aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib')
-rw-r--r--host/lib/CMakeLists.txt3
-rw-r--r--host/lib/convert/CMakeLists.txt14
-rw-r--r--host/lib/convert/convert_common.hpp38
-rw-r--r--host/lib/convert/convert_fc32_item32.cpp113
-rw-r--r--host/lib/convert/convert_orc.orc1
-rw-r--r--host/lib/convert/convert_pack_sc12.cpp144
-rw-r--r--host/lib/convert/convert_unpack_sc12.cpp205
-rw-r--r--host/lib/convert/convert_with_orc.cpp4
-rw-r--r--host/lib/convert/convert_with_tables.cpp30
-rw-r--r--host/lib/convert/sse2_fc32_to_sc8.cpp6
-rw-r--r--host/lib/convert/sse2_fc64_to_sc8.cpp18
-rw-r--r--host/lib/convert/sse2_sc8_to_fc32.cpp6
-rw-r--r--host/lib/convert/sse2_sc8_to_fc64.cpp6
-rw-r--r--host/lib/ic_reg_maps/CMakeLists.txt5
-rw-r--r--host/lib/ic_reg_maps/gen_max2870_regs.py133
-rw-r--r--host/lib/stream.cpp30
-rw-r--r--host/lib/transport/CMakeLists.txt7
-rw-r--r--host/lib/transport/gen_vrt_if_packet.py143
-rw-r--r--host/lib/transport/libusb1_base.cpp56
-rw-r--r--host/lib/transport/libusb1_base.hpp4
-rw-r--r--host/lib/transport/libusb1_control.cpp5
-rw-r--r--host/lib/transport/libusb1_zero_copy.cpp398
-rw-r--r--host/lib/transport/super_recv_packet_handler.hpp53
-rw-r--r--host/lib/transport/super_send_packet_handler.hpp41
-rw-r--r--host/lib/types/CMakeLists.txt3
-rw-r--r--host/lib/types/serial.cpp78
-rw-r--r--host/lib/types/wb_iface.cpp56
-rw-r--r--host/lib/usrp/CMakeLists.txt3
-rw-r--r--host/lib/usrp/b100/CMakeLists.txt3
-rw-r--r--host/lib/usrp/b100/b100_impl.cpp34
-rw-r--r--host/lib/usrp/b100/b100_impl.hpp26
-rw-r--r--host/lib/usrp/b100/dboard_iface.cpp8
-rw-r--r--host/lib/usrp/b100/io_impl.cpp8
-rw-r--r--host/lib/usrp/b100/usb_zero_copy_wrapper.cpp (renamed from host/lib/transport/usb_zero_copy_wrapper.cpp)22
-rw-r--r--host/lib/usrp/b200/CMakeLists.txt34
-rw-r--r--host/lib/usrp/b200/b200_iface.cpp608
-rw-r--r--host/lib/usrp/b200/b200_iface.hpp84
-rw-r--r--host/lib/usrp/b200/b200_impl.cpp895
-rw-r--r--host/lib/usrp/b200/b200_impl.hpp192
-rw-r--r--host/lib/usrp/b200/b200_io_impl.cpp396
-rw-r--r--host/lib/usrp/b200/b200_regs.hpp122
-rw-r--r--host/lib/usrp/b200/b200_uart.cpp117
-rw-r--r--host/lib/usrp/b200/b200_uart.hpp36
-rw-r--r--host/lib/usrp/common/CMakeLists.txt4
-rw-r--r--host/lib/usrp/common/ad9361_ctrl.cpp173
-rw-r--r--host/lib/usrp/common/ad9361_ctrl.hpp128
-rw-r--r--host/lib/usrp/common/ad9361_transaction.h108
-rw-r--r--host/lib/usrp/common/adf4001_ctrl.cpp151
-rw-r--r--host/lib/usrp/common/adf4001_ctrl.hpp142
-rw-r--r--host/lib/usrp/common/fifo_ctrl_excelsior.hpp5
-rw-r--r--host/lib/usrp/common/fx2_ctrl.cpp8
-rw-r--r--host/lib/usrp/common/recv_packet_demuxer.cpp30
-rw-r--r--host/lib/usrp/common/recv_packet_demuxer_3000.hpp127
-rw-r--r--host/lib/usrp/cores/CMakeLists.txt10
-rw-r--r--host/lib/usrp/cores/gpio_core_200.cpp32
-rw-r--r--host/lib/usrp/cores/gpio_core_200.hpp18
-rw-r--r--host/lib/usrp/cores/i2c_core_100.cpp4
-rw-r--r--host/lib/usrp/cores/i2c_core_100.hpp4
-rw-r--r--host/lib/usrp/cores/i2c_core_100_wb32.cpp152
-rw-r--r--host/lib/usrp/cores/i2c_core_100_wb32.hpp37
-rw-r--r--host/lib/usrp/cores/i2c_core_200.cpp4
-rw-r--r--host/lib/usrp/cores/i2c_core_200.hpp4
-rw-r--r--host/lib/usrp/cores/radio_ctrl_core_3000.cpp344
-rw-r--r--host/lib/usrp/cores/radio_ctrl_core_3000.hpp59
-rw-r--r--host/lib/usrp/cores/rx_dsp_core_200.hpp4
-rw-r--r--host/lib/usrp/cores/rx_dsp_core_3000.cpp209
-rw-r--r--host/lib/usrp/cores/rx_dsp_core_3000.hpp58
-rw-r--r--host/lib/usrp/cores/rx_frontend_core_200.cpp2
-rw-r--r--host/lib/usrp/cores/rx_frontend_core_200.hpp4
-rw-r--r--host/lib/usrp/cores/rx_vita_core_3000.cpp153
-rw-r--r--host/lib/usrp/cores/rx_vita_core_3000.hpp59
-rw-r--r--host/lib/usrp/cores/spi_core_100.hpp4
-rw-r--r--host/lib/usrp/cores/spi_core_3000.cpp96
-rw-r--r--host/lib/usrp/cores/spi_core_3000.hpp39
-rw-r--r--host/lib/usrp/cores/time64_core_200.hpp4
-rw-r--r--host/lib/usrp/cores/time_core_3000.cpp118
-rw-r--r--host/lib/usrp/cores/time_core_3000.hpp58
-rw-r--r--host/lib/usrp/cores/tx_dsp_core_200.hpp4
-rw-r--r--host/lib/usrp/cores/tx_dsp_core_3000.cpp186
-rw-r--r--host/lib/usrp/cores/tx_dsp_core_3000.hpp54
-rw-r--r--host/lib/usrp/cores/tx_frontend_core_200.cpp2
-rw-r--r--host/lib/usrp/cores/tx_frontend_core_200.hpp4
-rw-r--r--host/lib/usrp/cores/tx_vita_core_3000.cpp107
-rw-r--r--host/lib/usrp/cores/tx_vita_core_3000.hpp49
-rw-r--r--host/lib/usrp/cores/user_settings_core_200.cpp2
-rw-r--r--host/lib/usrp/cores/user_settings_core_200.hpp4
-rw-r--r--host/lib/usrp/cores/wb_iface.hpp60
-rw-r--r--host/lib/usrp/dboard/CMakeLists.txt1
-rw-r--r--host/lib/usrp/dboard/db_cbx.cpp212
-rw-r--r--host/lib/usrp/dboard/db_sbx_common.cpp155
-rw-r--r--host/lib/usrp/dboard/db_sbx_common.hpp53
-rw-r--r--host/lib/usrp/dboard/db_sbx_version3.cpp118
-rw-r--r--host/lib/usrp/dboard/db_sbx_version4.cpp120
-rw-r--r--host/lib/usrp/dboard_iface.cpp7
-rw-r--r--host/lib/usrp/e100/dboard_iface.cpp8
-rw-r--r--host/lib/usrp/e100/e100_ctrl.cpp4
-rw-r--r--host/lib/usrp/e100/e100_impl.hpp2
-rw-r--r--host/lib/usrp/e100/io_impl.cpp3
-rw-r--r--host/lib/usrp/gps_ctrl.cpp144
-rw-r--r--host/lib/usrp/mboard_eeprom.cpp65
-rw-r--r--host/lib/usrp/multi_usrp.cpp161
-rw-r--r--host/lib/usrp/usrp1/dboard_iface.cpp8
-rw-r--r--host/lib/usrp/usrp1/io_impl.cpp11
-rw-r--r--host/lib/usrp/usrp1/usrp1_iface.cpp4
-rw-r--r--host/lib/usrp/usrp1/usrp1_iface.hpp7
-rw-r--r--host/lib/usrp/usrp2/dboard_iface.cpp8
-rw-r--r--host/lib/usrp/usrp2/io_impl.cpp3
-rw-r--r--host/lib/usrp/usrp2/usrp2_fifo_ctrl.hpp5
-rw-r--r--host/lib/usrp/usrp2/usrp2_iface.cpp4
-rw-r--r--host/lib/usrp/usrp2/usrp2_iface.hpp7
-rw-r--r--host/lib/usrp/usrp2/usrp2_impl.hpp4
-rw-r--r--host/lib/utils/CMakeLists.txt8
-rw-r--r--host/lib/utils/images.cpp2
-rw-r--r--host/lib/utils/paths.cpp9
-rw-r--r--host/lib/utils/tasks.cpp99
115 files changed, 7588 insertions, 656 deletions
diff --git a/host/lib/CMakeLists.txt b/host/lib/CMakeLists.txt
index a37a8ab85..4ca06af9a 100644
--- a/host/lib/CMakeLists.txt
+++ b/host/lib/CMakeLists.txt
@@ -1,5 +1,5 @@
#
-# Copyright 2010-2011,2013 Ettus Research LLC
+# Copyright 2010-2013 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
@@ -88,6 +88,7 @@ CONFIGURE_FILE(
LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/deprecated.cpp
${CMAKE_CURRENT_SOURCE_DIR}/device.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/stream.cpp
${CMAKE_CURRENT_SOURCE_DIR}/exception.cpp
${CMAKE_CURRENT_SOURCE_DIR}/property_tree.cpp
${CMAKE_CURRENT_BINARY_DIR}/version.cpp
diff --git a/host/lib/convert/CMakeLists.txt b/host/lib/convert/CMakeLists.txt
index 0d9d0983f..00e129b78 100644
--- a/host/lib/convert/CMakeLists.txt
+++ b/host/lib/convert/CMakeLists.txt
@@ -1,5 +1,5 @@
#
-# Copyright 2011-2012 Ettus Research LLC
+# Copyright 2011-2013 Ettus Research LLC
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -24,12 +24,11 @@ MESSAGE(STATUS "")
########################################################################
# Look for Orc support
########################################################################
-FIND_PACKAGE(PkgConfig)
-IF(PKG_CONFIG_FOUND)
-PKG_CHECK_MODULES(ORC "orc-0.4 > 0.4.11")
-ENDIF(PKG_CONFIG_FOUND)
+FIND_PACKAGE(ORC)
-FIND_PROGRAM(ORCC_EXECUTABLE orcc)
+IF(NOT ORCC_EXECUTABLE)
+ FIND_PROGRAM(ORCC_EXECUTABLE orcc)
+ENDIF()
LIBUHD_REGISTER_COMPONENT("ORC" ENABLE_ORC ON "ENABLE_LIBUHD;ORC_FOUND;ORCC_EXECUTABLE" OFF)
@@ -122,4 +121,7 @@ LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/convert_with_tables.cpp
${CMAKE_CURRENT_SOURCE_DIR}/convert_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/convert_item32.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/convert_pack_sc12.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/convert_unpack_sc12.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/convert_fc32_item32.cpp
)
diff --git a/host/lib/convert/convert_common.hpp b/host/lib/convert/convert_common.hpp
index 933978a8f..ceaa1151c 100644
--- a/host/lib/convert/convert_common.hpp
+++ b/host/lib/convert/convert_common.hpp
@@ -1,5 +1,5 @@
//
-// Copyright 2011-2012 Ettus Research LLC
+// Copyright 2011-2013 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
@@ -150,10 +150,10 @@ UHD_INLINE void item32_sc16_to_xx(
template <typename T> UHD_INLINE item32_t xx_to_item32_sc8_x1(
const std::complex<T> &in0, const std::complex<T> &in1, const double scale_factor
){
- boost::uint8_t real0 = boost::int8_t(in0.real()*float(scale_factor));
- boost::uint8_t imag0 = boost::int8_t(in0.imag()*float(scale_factor));
- boost::uint8_t real1 = boost::int8_t(in1.real()*float(scale_factor));
- boost::uint8_t imag1 = boost::int8_t(in1.imag()*float(scale_factor));
+ boost::uint8_t real1 = boost::int8_t(in0.real()*float(scale_factor));
+ boost::uint8_t imag1 = boost::int8_t(in0.imag()*float(scale_factor));
+ boost::uint8_t real0 = boost::int8_t(in1.real()*float(scale_factor));
+ boost::uint8_t imag0 = boost::int8_t(in1.imag()*float(scale_factor));
return
(item32_t(real0) << 8) | (item32_t(imag0) << 0) |
(item32_t(real1) << 24) | (item32_t(imag1) << 16)
@@ -163,10 +163,10 @@ template <typename T> UHD_INLINE item32_t xx_to_item32_sc8_x1(
template <> UHD_INLINE item32_t xx_to_item32_sc8_x1(
const sc16_t &in0, const sc16_t &in1, const double
){
- boost::uint8_t real0 = boost::int8_t(in0.real());
- boost::uint8_t imag0 = boost::int8_t(in0.imag());
- boost::uint8_t real1 = boost::int8_t(in1.real());
- boost::uint8_t imag1 = boost::int8_t(in1.imag());
+ boost::uint8_t real1 = boost::int8_t(in0.real());
+ boost::uint8_t imag1 = boost::int8_t(in0.imag());
+ boost::uint8_t real0 = boost::int8_t(in1.real());
+ boost::uint8_t imag0 = boost::int8_t(in1.imag());
return
(item32_t(real0) << 8) | (item32_t(imag0) << 0) |
(item32_t(real1) << 24) | (item32_t(imag1) << 16)
@@ -176,10 +176,10 @@ template <> UHD_INLINE item32_t xx_to_item32_sc8_x1(
template <> UHD_INLINE item32_t xx_to_item32_sc8_x1(
const sc8_t &in0, const sc8_t &in1, const double
){
- boost::uint8_t real0 = boost::int8_t(in0.real());
- boost::uint8_t imag0 = boost::int8_t(in0.imag());
- boost::uint8_t real1 = boost::int8_t(in1.real());
- boost::uint8_t imag1 = boost::int8_t(in1.imag());
+ boost::uint8_t real1 = boost::int8_t(in0.real());
+ boost::uint8_t imag1 = boost::int8_t(in0.imag());
+ boost::uint8_t real0 = boost::int8_t(in1.real());
+ boost::uint8_t imag0 = boost::int8_t(in1.imag());
return
(item32_t(real0) << 8) | (item32_t(imag0) << 0) |
(item32_t(real1) << 24) | (item32_t(imag1) << 16)
@@ -211,11 +211,11 @@ UHD_INLINE void xx_to_item32_sc8(
template <typename T> UHD_INLINE void item32_sc8_x1_to_xx(
const item32_t item, std::complex<T> &out0, std::complex<T> &out1, const double scale_factor
){
- out0 = std::complex<T>(
+ out1 = std::complex<T>(
T(boost::int8_t(item >> 8)*float(scale_factor)),
T(boost::int8_t(item >> 0)*float(scale_factor))
);
- out1 = std::complex<T>(
+ out0 = std::complex<T>(
T(boost::int8_t(item >> 24)*float(scale_factor)),
T(boost::int8_t(item >> 16)*float(scale_factor))
);
@@ -224,11 +224,11 @@ template <typename T> UHD_INLINE void item32_sc8_x1_to_xx(
template <> UHD_INLINE void item32_sc8_x1_to_xx(
const item32_t item, sc16_t &out0, sc16_t &out1, const double
){
- out0 = sc16_t(
+ out1 = sc16_t(
boost::int16_t(boost::int8_t(item >> 8)),
boost::int16_t(boost::int8_t(item >> 0))
);
- out1 = sc16_t(
+ out0 = sc16_t(
boost::int16_t(boost::int8_t(item >> 24)),
boost::int16_t(boost::int8_t(item >> 16))
);
@@ -237,11 +237,11 @@ template <> UHD_INLINE void item32_sc8_x1_to_xx(
template <> UHD_INLINE void item32_sc8_x1_to_xx(
const item32_t item, sc8_t &out0, sc8_t &out1, const double
){
- out0 = sc8_t(
+ out1 = sc8_t(
boost::int8_t(boost::int8_t(item >> 8)),
boost::int8_t(boost::int8_t(item >> 0))
);
- out1 = sc8_t(
+ out0 = sc8_t(
boost::int8_t(boost::int8_t(item >> 24)),
boost::int8_t(boost::int8_t(item >> 16))
);
diff --git a/host/lib/convert/convert_fc32_item32.cpp b/host/lib/convert/convert_fc32_item32.cpp
new file mode 100644
index 000000000..29bfefd46
--- /dev/null
+++ b/host/lib/convert/convert_fc32_item32.cpp
@@ -0,0 +1,113 @@
+//
+// Copyright 2013 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 "convert_common.hpp"
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/msg.hpp>
+#include <boost/math/special_functions/round.hpp>
+#include <vector>
+
+using namespace uhd::convert;
+
+typedef boost::uint32_t (*to32_type)(boost::uint32_t);
+
+template <typename type, to32_type tohost>
+struct convert_fc32_item32_1_to_star_1 : public converter
+{
+ convert_fc32_item32_1_to_star_1(void)
+ {
+ //NOP
+ }
+
+ void set_scalar(const double scalar)
+ {
+ _scalar = scalar;
+ }
+
+ void operator()(const input_type &inputs, const output_type &outputs, const size_t nsamps)
+ {
+ const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]);
+ std::complex<type> *output = reinterpret_cast<std::complex<type> *>(outputs[0]);
+
+ size_t i = 0;
+ for (size_t o = 0; o < nsamps; o++)
+ {
+ const item32_t i32 = tohost(input[i++]);
+ const item32_t q32 = tohost(input[i++]);
+ const float i_f32 = reinterpret_cast<const float &>(i32);
+ const float q_f32 = reinterpret_cast<const float &>(q32);
+ output[o] = std::complex<type>(type(i_f32*_scalar), type(q_f32*_scalar));
+ }
+ }
+
+ double _scalar;
+};
+
+template <typename type, to32_type towire>
+struct convert_star_1_to_fc32_item32_1 : public converter
+{
+ convert_star_1_to_fc32_item32_1(void)
+ {
+ //NOP
+ }
+
+ void set_scalar(const double scalar)
+ {
+ _scalar = scalar;
+ }
+
+ void operator()(const input_type &inputs, const output_type &outputs, const size_t nsamps)
+ {
+ const std::complex<type> *input = reinterpret_cast<const std::complex<type> *>(inputs[0]);
+ item32_t *output = reinterpret_cast<item32_t *>(outputs[0]);
+
+ size_t o = 0;
+ for (size_t i = 0; i < nsamps; i++)
+ {
+ const float i_f32 = type(input[i].real()*_scalar);
+ const float q_f32 = type(input[i].imag()*_scalar);
+ const item32_t i32 = towire(reinterpret_cast<const item32_t &>(i_f32));
+ const item32_t q32 = towire(reinterpret_cast<const item32_t &>(q_f32));
+ output[o++] = i32; output[o++] = q32;
+ }
+ }
+
+ double _scalar;
+};
+
+#define __make_registrations(itype, otype, fcn, type, conv) \
+static converter::sptr make_convert_ ## itype ## _1_ ## otype ## _1(void) \
+{ \
+ return converter::sptr(new fcn<type, conv>()); \
+} \
+UHD_STATIC_BLOCK(register_convert_ ## itype ## _1_ ## otype ## _1) \
+{ \
+ uhd::convert::id_type id; \
+ id.num_inputs = 1; id.num_outputs = 1; \
+ id.input_format = #itype; id.output_format = #otype; \
+ uhd::convert::register_converter(id, &make_convert_ ## itype ## _1_ ## otype ## _1, PRIORITY_GENERAL); \
+}
+
+__make_registrations(fc32_item32_le, fc32, convert_fc32_item32_1_to_star_1, float, uhd::wtohx)
+__make_registrations(fc32_item32_be, fc32, convert_fc32_item32_1_to_star_1, float, uhd::ntohx)
+__make_registrations(fc32_item32_le, fc64, convert_fc32_item32_1_to_star_1, double, uhd::wtohx)
+__make_registrations(fc32_item32_be, fc64, convert_fc32_item32_1_to_star_1, double, uhd::ntohx)
+
+__make_registrations(fc32, fc32_item32_le, convert_star_1_to_fc32_item32_1, float, uhd::wtohx)
+__make_registrations(fc32, fc32_item32_be, convert_star_1_to_fc32_item32_1, float, uhd::ntohx)
+__make_registrations(fc64, fc32_item32_le, convert_star_1_to_fc32_item32_1, double, uhd::wtohx)
+__make_registrations(fc64, fc32_item32_be, convert_star_1_to_fc32_item32_1, double, uhd::ntohx)
diff --git a/host/lib/convert/convert_orc.orc b/host/lib/convert/convert_orc.orc
index f7075606e..ffb298f26 100644
--- a/host/lib/convert/convert_orc.orc
+++ b/host/lib/convert/convert_orc.orc
@@ -75,6 +75,5 @@ swapl dst, src
.floatparam 4 scalar
x2 mulf tmp, src, scalar
x2 convfl tmp, tmp
-swaplq tmp, tmp
x2 convlw tmp2, tmp
x2 convwb dst, tmp2
diff --git a/host/lib/convert/convert_pack_sc12.cpp b/host/lib/convert/convert_pack_sc12.cpp
new file mode 100644
index 000000000..680814994
--- /dev/null
+++ b/host/lib/convert/convert_pack_sc12.cpp
@@ -0,0 +1,144 @@
+//
+// Copyright 2013 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 "convert_common.hpp"
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/msg.hpp>
+#include <boost/math/special_functions/round.hpp>
+#include <vector>
+
+using namespace uhd::convert;
+
+typedef boost::uint32_t (*towire32_type)(boost::uint32_t);
+
+struct item32_sc12_3x
+{
+ item32_t line0;
+ item32_t line1;
+ item32_t line2;
+};
+
+template <typename type, towire32_type towire>
+void convert_star_4_to_sc12_item32_3
+(
+ const std::complex<type> &in0,
+ const std::complex<type> &in1,
+ const std::complex<type> &in2,
+ const std::complex<type> &in3,
+ item32_sc12_3x &output,
+ const double scalar
+)
+{
+ const item32_t i0 = boost::int32_t(type(in0.real()*scalar)) & 0xfff;
+ const item32_t q0 = boost::int32_t(type(in0.imag()*scalar)) & 0xfff;
+
+ const item32_t i1 = boost::int32_t(type(in1.real()*scalar)) & 0xfff;
+ const item32_t q1 = boost::int32_t(type(in1.imag()*scalar)) & 0xfff;
+
+ const item32_t i2 = boost::int32_t(type(in2.real()*scalar)) & 0xfff;
+ const item32_t q2 = boost::int32_t(type(in2.imag()*scalar)) & 0xfff;
+
+ const item32_t i3 = boost::int32_t(type(in3.real()*scalar)) & 0xfff;
+ const item32_t q3 = boost::int32_t(type(in3.imag()*scalar)) & 0xfff;
+
+ const item32_t line0 = (i0 << 20) | (q0 << 8) | (i1 >> 4);
+ const item32_t line1 = (i1 << 28) | (q1 << 16) | (i2 << 4) | (q2 >> 8);
+ const item32_t line2 = (q2 << 24) | (i3 << 12) | (q3);
+
+ output.line0 = towire(line0);
+ output.line1 = towire(line1);
+ output.line2 = towire(line2);
+}
+
+template <typename type, towire32_type towire>
+struct convert_star_1_to_sc12_item32_1 : public converter
+{
+ convert_star_1_to_sc12_item32_1(void)
+ {
+ //NOP
+ }
+
+ void set_scalar(const double scalar)
+ {
+ _scalar = scalar;
+ }
+
+ void operator()(const input_type &inputs, const output_type &outputs, const size_t nsamps)
+ {
+ const std::complex<type> *input = reinterpret_cast<const std::complex<type> *>(inputs[0]);
+ item32_sc12_3x *output = reinterpret_cast<item32_sc12_3x *>(size_t(outputs[0]) & ~0x3);
+
+ //helper variables
+ size_t i = 0, o = 0;
+
+ //handle the head case
+ const size_t head_samps = size_t(outputs[0]) & 0x3;
+ switch (head_samps)
+ {
+ case 0: break; //no head
+ case 1: convert_star_4_to_sc12_item32_3<type, towire>(0, 0, 0, input[0], output[o++], _scalar); break;
+ case 2: convert_star_4_to_sc12_item32_3<type, towire>(0, 0, input[0], input[1], output[o++], _scalar); break;
+ case 3: convert_star_4_to_sc12_item32_3<type, towire>(0, input[0], input[1], input[2], output[o++], _scalar); break;
+ }
+ i += head_samps;
+
+ //convert the body
+ while (i+3 < nsamps)
+ {
+ convert_star_4_to_sc12_item32_3<type, towire>(input[i+0], input[i+1], input[i+2], input[i+3], output[o], _scalar);
+ o++; i += 4;
+ }
+
+ //handle the tail case
+ const size_t tail_samps = nsamps - i;
+ switch (tail_samps)
+ {
+ case 0: break; //no tail
+ case 1: convert_star_4_to_sc12_item32_3<type, towire>(input[i+0], 0, 0, 0, output[o], _scalar); break;
+ case 2: convert_star_4_to_sc12_item32_3<type, towire>(input[i+0], input[i+1], 0, 0, output[o], _scalar); break;
+ case 3: convert_star_4_to_sc12_item32_3<type, towire>(input[i+0], input[i+1], input[i+2], 0, output[o], _scalar); break;
+ }
+ }
+
+ double _scalar;
+};
+
+static converter::sptr make_convert_fc32_1_to_sc12_item32_le_1(void)
+{
+ return converter::sptr(new convert_star_1_to_sc12_item32_1<float, uhd::wtohx>());
+}
+
+static converter::sptr make_convert_fc32_1_to_sc12_item32_be_1(void)
+{
+ return converter::sptr(new convert_star_1_to_sc12_item32_1<float, uhd::ntohx>());
+}
+
+UHD_STATIC_BLOCK(register_convert_pack_sc12)
+{
+ //uhd::convert::register_bytes_per_item("sc12", 3/*bytes*/); //registered in unpack
+
+ uhd::convert::id_type id;
+ id.num_inputs = 1;
+ id.num_outputs = 1;
+ id.input_format = "fc32";
+
+ id.output_format = "sc12_item32_le";
+ uhd::convert::register_converter(id, &make_convert_fc32_1_to_sc12_item32_le_1, PRIORITY_GENERAL);
+
+ id.output_format = "sc12_item32_be";
+ uhd::convert::register_converter(id, &make_convert_fc32_1_to_sc12_item32_be_1, PRIORITY_GENERAL);
+}
diff --git a/host/lib/convert/convert_unpack_sc12.cpp b/host/lib/convert/convert_unpack_sc12.cpp
new file mode 100644
index 000000000..e98ab73f1
--- /dev/null
+++ b/host/lib/convert/convert_unpack_sc12.cpp
@@ -0,0 +1,205 @@
+//
+// Copyright 2013 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 "convert_common.hpp"
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/msg.hpp>
+#include <boost/math/special_functions/round.hpp>
+#include <vector>
+
+using namespace uhd::convert;
+
+typedef boost::uint32_t (*tohost32_type)(boost::uint32_t);
+
+struct item32_sc12_3x
+{
+ item32_t line0;
+ item32_t line1;
+ item32_t line2;
+};
+
+/*
+ * convert_sc12_item32_3_to_star_4 takes in 3 lines with 32 bit each
+ * and converts them 4 samples of type 'std::complex<type>'.
+ * The structure of the 3 lines is as follows:
+ * _ _ _ _ _ _ _ _
+ * |_ _ _1_ _ _|_ _|
+ * |_2_ _ _|_ _ _3_|
+ * |_ _|_ _ _4_ _ _|
+ *
+ * The numbers mark the position of one complex sample.
+ */
+template <typename type, tohost32_type tohost>
+void convert_sc12_item32_3_to_star_4
+(
+ const item32_sc12_3x &input,
+ std::complex<type> &out0,
+ std::complex<type> &out1,
+ std::complex<type> &out2,
+ std::complex<type> &out3,
+ const double scalar
+)
+{
+ //step 0: extract the lines from the input buffer
+ const item32_t line0 = tohost(input.line0);
+ const item32_t line1 = tohost(input.line1);
+ const item32_t line2 = tohost(input.line2);
+ const boost::uint64_t line01 = (boost::uint64_t(line0) << 32) | line1;
+ const boost::uint64_t line12 = (boost::uint64_t(line1) << 32) | line2;
+
+ //step 1: shift out and mask off the individual numbers
+ const type i0 = type(boost::int16_t(line0 >> 16)*scalar);
+ const type q0 = type(boost::int16_t(line0 >> 4)*scalar);
+
+ const type i1 = type(boost::int16_t(line01 >> 24)*scalar);
+ const type q1 = type(boost::int16_t(line1 >> 12)*scalar);
+
+ const type i2 = type(boost::int16_t(line1 >> 0)*scalar);
+ const type q2 = type(boost::int16_t(line12 >> 20)*scalar);
+
+ const type i3 = type(boost::int16_t(line2 >> 8)*scalar);
+ const type q3 = type(boost::int16_t(line2 << 4)*scalar);
+
+ //step 2: load the outputs
+ out0 = std::complex<type>(i0, q0);
+ out1 = std::complex<type>(i1, q1);
+ out2 = std::complex<type>(i2, q2);
+ out3 = std::complex<type>(i3, q3);
+}
+
+template <typename type, tohost32_type tohost>
+struct convert_sc12_item32_1_to_star_1 : public converter
+{
+ convert_sc12_item32_1_to_star_1(void)
+ {
+ //NOP
+ }
+
+ void set_scalar(const double scalar)
+ {
+ const int unpack_growth = 16;
+ _scalar = scalar/unpack_growth;
+ }
+
+ /*
+ * This converter takes in 24 bits complex samples, 12 bits I and 12 bits Q, and converts them to type 'std::complex<type>'.
+ * 'type' is usually 'float'.
+ * For the converter to work correctly the used managed_buffer which holds all samples of one packet has to be 32 bits aligned.
+ * We assume 32 bits to be one line. This said the converter must be aware where it is supposed to start within 3 lines.
+ *
+ */
+ void operator()(const input_type &inputs, const output_type &outputs, const size_t nsamps)
+ {
+ /*
+ * Looking at the line structure above we can identify 4 cases.
+ * Each corresponds to the start of a different sample within a 3 line block.
+ * head_samps derives the number of samples left within one block.
+ * Then the number of bytes the converter has to rewind are calculated.
+ */
+ const size_t head_samps = size_t(inputs[0]) & 0x3;
+ size_t rewind = 0;
+ switch(head_samps)
+ {
+ case 0: break;
+ case 1: rewind = 9; break;
+ case 2: rewind = 6; break;
+ case 3: rewind = 3; break;
+ }
+
+ /*
+ * The pointer *input now points to the head of a 3 line block.
+ */
+ const item32_sc12_3x *input = reinterpret_cast<const item32_sc12_3x *>(size_t(inputs[0]) - rewind);
+ std::complex<type> *output = reinterpret_cast<std::complex<type> *>(outputs[0]);
+
+ //helper variables
+ std::complex<type> dummy0, dummy1, dummy2;
+ size_t i = 0, o = 0;
+
+ /*
+ * handle the head case
+ * head_samps holds the number of samples left in a block.
+ * The 3 line converter is called for the whole block and already processed samples are dumped.
+ * We don't run into the risk of a SIGSEGV because input will always point to valid memory within a managed_buffer.
+ * Furthermore the bytes in a buffer remain unchanged after they have been copied into it.
+ */
+ switch (head_samps)
+ {
+ case 0: break; //no head
+ case 1: convert_sc12_item32_3_to_star_4<type, tohost>(input[i++], dummy0, dummy1, dummy2, output[0], _scalar); break;
+ case 2: convert_sc12_item32_3_to_star_4<type, tohost>(input[i++], dummy0, dummy1, output[0], output[1], _scalar); break;
+ case 3: convert_sc12_item32_3_to_star_4<type, tohost>(input[i++], dummy0, output[0], output[1], output[2], _scalar); break;
+ }
+ o += head_samps;
+
+ //convert the body
+ while (o+3 < nsamps)
+ {
+ convert_sc12_item32_3_to_star_4<type, tohost>(input[i], output[o+0], output[o+1], output[o+2], output[o+3], _scalar);
+ i++; o += 4;
+ }
+
+ /*
+ * handle the tail case
+ * The converter can be called with any number of samples to be converted.
+ * This can end up in only a part of a block to be converted in one call.
+ * We never have to worry about SIGSEGVs here as long as we end in the middle of a managed_buffer.
+ * If we are at the end of managed_buffer there are 2 precautions to prevent SIGSEGVs.
+ * Firstly only a read operation is performed.
+ * Secondly managed_buffers allocate a fixed size memory which is always larger than the actually used size.
+ * e.g. The current sample maximum is 2000 samples in a packet over USB.
+ * With sc12 samples a packet consists of 6000kb but managed_buffers allocate 16kb each.
+ * Thus we don't run into problems here either.
+ */
+ const size_t tail_samps = nsamps - o;
+ switch (tail_samps)
+ {
+ case 0: break; //no tail
+ case 1: convert_sc12_item32_3_to_star_4<type, tohost>(input[i], output[o+0], dummy0, dummy1, dummy2, _scalar); break;
+ case 2: convert_sc12_item32_3_to_star_4<type, tohost>(input[i], output[o+0], output[o+1], dummy1, dummy2, _scalar); break;
+ case 3: convert_sc12_item32_3_to_star_4<type, tohost>(input[i], output[o+0], output[o+1], output[o+2], dummy2, _scalar); break;
+ }
+ }
+
+ double _scalar;
+};
+
+static converter::sptr make_convert_sc12_item32_le_1_to_fc32_1(void)
+{
+ return converter::sptr(new convert_sc12_item32_1_to_star_1<float, uhd::wtohx>());
+}
+
+static converter::sptr make_convert_sc12_item32_be_1_to_fc32_1(void)
+{
+ return converter::sptr(new convert_sc12_item32_1_to_star_1<float, uhd::ntohx>());
+}
+
+UHD_STATIC_BLOCK(register_convert_unpack_sc12)
+{
+ uhd::convert::register_bytes_per_item("sc12", 3/*bytes*/);
+
+ uhd::convert::id_type id;
+ id.num_inputs = 1;
+ id.num_outputs = 1;
+ id.output_format = "fc32";
+
+ id.input_format = "sc12_item32_le";
+ uhd::convert::register_converter(id, &make_convert_sc12_item32_le_1_to_fc32_1, PRIORITY_GENERAL);
+
+ id.input_format = "sc12_item32_be";
+ uhd::convert::register_converter(id, &make_convert_sc12_item32_be_1_to_fc32_1, PRIORITY_GENERAL);
+}
diff --git a/host/lib/convert/convert_with_orc.cpp b/host/lib/convert/convert_with_orc.cpp
index e44c8ca73..19755fa44 100644
--- a/host/lib/convert/convert_with_orc.cpp
+++ b/host/lib/convert/convert_with_orc.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2011 Ettus Research LLC
+// Copyright 2011-2013 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
@@ -57,9 +57,9 @@ DECLARE_CONVERTER(sc16_item32_le, 1, sc16, 1, PRIORITY_LIBORC){
DECLARE_CONVERTER(fc32, 1, sc8_item32_be, 1, PRIORITY_LIBORC){
_convert_fc32_1_to_sc8_1_nswap_orc(outputs[0], inputs[0], scale_factor, nsamps);
- _convert_swap_byte_pairs_orc(outputs[0], outputs[0], (nsamps + 1)/2);
}
DECLARE_CONVERTER(fc32, 1, sc8_item32_le, 1, PRIORITY_LIBORC){
_convert_fc32_1_to_sc8_1_nswap_orc(outputs[0], inputs[0], scale_factor, nsamps);
+ _convert_swap_byte_pairs_orc(outputs[0], outputs[0], (nsamps + 1)/2);
}
diff --git a/host/lib/convert/convert_with_tables.cpp b/host/lib/convert/convert_with_tables.cpp
index cd7773d4b..4d295fa01 100644
--- a/host/lib/convert/convert_with_tables.cpp
+++ b/host/lib/convert/convert_with_tables.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2011-2012 Ettus Research LLC
+// Copyright 2011-2013 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
@@ -59,16 +59,16 @@ public:
item32_t lookup(const sc16_t &in0, const sc16_t &in1){
if (swap){ //hope this compiles out, its a template constant
return
- (item32_t(_table[boost::uint16_t(in0.real())]) << 16) |
- (item32_t(_table[boost::uint16_t(in0.imag())]) << 24) |
- (item32_t(_table[boost::uint16_t(in1.real())]) << 0) |
- (item32_t(_table[boost::uint16_t(in1.imag())]) << 8) ;
+ (item32_t(_table[boost::uint16_t(in1.real())]) << 16) |
+ (item32_t(_table[boost::uint16_t(in1.imag())]) << 24) |
+ (item32_t(_table[boost::uint16_t(in0.real())]) << 0) |
+ (item32_t(_table[boost::uint16_t(in0.imag())]) << 8) ;
}
return
- (item32_t(_table[boost::uint16_t(in0.real())]) << 8) |
- (item32_t(_table[boost::uint16_t(in0.imag())]) << 0) |
- (item32_t(_table[boost::uint16_t(in1.real())]) << 24) |
- (item32_t(_table[boost::uint16_t(in1.imag())]) << 16) ;
+ (item32_t(_table[boost::uint16_t(in1.real())]) << 8) |
+ (item32_t(_table[boost::uint16_t(in1.imag())]) << 0) |
+ (item32_t(_table[boost::uint16_t(in0.real())]) << 24) |
+ (item32_t(_table[boost::uint16_t(in0.imag())]) << 16) ;
}
private:
@@ -196,27 +196,27 @@ static converter::sptr make_convert_sc16_item32_le_1_to_fc64_1(void){
}
static converter::sptr make_convert_sc8_item32_be_1_to_fc32_1(void){
- return converter::sptr(new convert_sc8_item32_1_to_fcxx_1<float, uhd::ntohx, SHIFT_PAIR1>());
+ return converter::sptr(new convert_sc8_item32_1_to_fcxx_1<float, uhd::ntohx, SHIFT_PAIR0>());
}
static converter::sptr make_convert_sc8_item32_be_1_to_fc64_1(void){
- return converter::sptr(new convert_sc8_item32_1_to_fcxx_1<double, uhd::ntohx, SHIFT_PAIR1>());
+ return converter::sptr(new convert_sc8_item32_1_to_fcxx_1<double, uhd::ntohx, SHIFT_PAIR0>());
}
static converter::sptr make_convert_sc8_item32_le_1_to_fc32_1(void){
- return converter::sptr(new convert_sc8_item32_1_to_fcxx_1<float, uhd::wtohx, SHIFT_PAIR0>());
+ return converter::sptr(new convert_sc8_item32_1_to_fcxx_1<float, uhd::wtohx, SHIFT_PAIR1>());
}
static converter::sptr make_convert_sc8_item32_le_1_to_fc64_1(void){
- return converter::sptr(new convert_sc8_item32_1_to_fcxx_1<double, uhd::wtohx, SHIFT_PAIR0>());
+ return converter::sptr(new convert_sc8_item32_1_to_fcxx_1<double, uhd::wtohx, SHIFT_PAIR1>());
}
static converter::sptr make_convert_sc8_item32_be_1_to_sc16_1(void){
- return converter::sptr(new convert_sc8_item32_1_to_fcxx_1<s16_t, uhd::ntohx, SHIFT_PAIR1>());
+ return converter::sptr(new convert_sc8_item32_1_to_fcxx_1<s16_t, uhd::ntohx, SHIFT_PAIR0>());
}
static converter::sptr make_convert_sc8_item32_le_1_to_sc16_1(void){
- return converter::sptr(new convert_sc8_item32_1_to_fcxx_1<s16_t, uhd::wtohx, SHIFT_PAIR0>());
+ return converter::sptr(new convert_sc8_item32_1_to_fcxx_1<s16_t, uhd::wtohx, SHIFT_PAIR1>());
}
static converter::sptr make_convert_sc16_1_to_sc8_item32_be_1(void){
diff --git a/host/lib/convert/sse2_fc32_to_sc8.cpp b/host/lib/convert/sse2_fc32_to_sc8.cpp
index dd884640d..36aa68b0e 100644
--- a/host/lib/convert/sse2_fc32_to_sc8.cpp
+++ b/host/lib/convert/sse2_fc32_to_sc8.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2012 Ettus Research LLC
+// Copyright 2012-2013 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -47,7 +47,7 @@ DECLARE_CONVERTER(fc32, 1, sc8_item32_be, 1, PRIORITY_SIMD){
item32_t *output = reinterpret_cast<item32_t *>(outputs[0]);
const __m128 scalar = _mm_set_ps1(float(scale_factor));
- const int shuf = _MM_SHUFFLE(1, 0, 3, 2);
+ const int shuf = _MM_SHUFFLE(3, 2, 1, 0);
#define convert_fc32_1_to_sc8_item32_1_bswap_guts(_al_) \
for (size_t j = 0; i+7 < nsamps; i+=8, j+=4){ \
@@ -83,7 +83,7 @@ DECLARE_CONVERTER(fc32, 1, sc8_item32_le, 1, PRIORITY_SIMD){
item32_t *output = reinterpret_cast<item32_t *>(outputs[0]);
const __m128 scalar = _mm_set_ps1(float(scale_factor));
- const int shuf = _MM_SHUFFLE(2, 3, 0, 1);
+ const int shuf = _MM_SHUFFLE(0, 1, 2, 3);
#define convert_fc32_1_to_sc8_item32_1_nswap_guts(_al_) \
for (size_t j = 0; i+7 < nsamps; i+=8, j+=4){ \
diff --git a/host/lib/convert/sse2_fc64_to_sc8.cpp b/host/lib/convert/sse2_fc64_to_sc8.cpp
index bf3719e13..82a8e0bb0 100644
--- a/host/lib/convert/sse2_fc64_to_sc8.cpp
+++ b/host/lib/convert/sse2_fc64_to_sc8.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2012 Ettus Research LLC
+// Copyright 2012-2013 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
@@ -59,10 +59,10 @@ DECLARE_CONVERTER(fc64, 1, sc8_item32_be, 1, PRIORITY_SIMD){
\
/* interleave */ \
const __m128i tmpi = pack_sc8_item32_4x( \
- pack_sc32_4x(tmp0, tmp1, scalar), \
- pack_sc32_4x(tmp2, tmp3, scalar), \
- pack_sc32_4x(tmp4, tmp5, scalar), \
- pack_sc32_4x(tmp6, tmp7, scalar) \
+ pack_sc32_4x(tmp1, tmp0, scalar), \
+ pack_sc32_4x(tmp3, tmp2, scalar), \
+ pack_sc32_4x(tmp5, tmp4, scalar), \
+ pack_sc32_4x(tmp7, tmp6, scalar) \
); \
\
/* store to output */ \
@@ -103,10 +103,10 @@ DECLARE_CONVERTER(fc64, 1, sc8_item32_le, 1, PRIORITY_SIMD){
\
/* interleave */ \
__m128i tmpi = pack_sc8_item32_4x( \
- pack_sc32_4x(tmp1, tmp0, scalar), \
- pack_sc32_4x(tmp3, tmp2, scalar), \
- pack_sc32_4x(tmp5, tmp4, scalar), \
- pack_sc32_4x(tmp7, tmp6, scalar) \
+ pack_sc32_4x(tmp0, tmp1, scalar), \
+ pack_sc32_4x(tmp2, tmp3, scalar), \
+ pack_sc32_4x(tmp4, tmp5, scalar), \
+ pack_sc32_4x(tmp6, tmp7, scalar) \
); \
tmpi = _mm_or_si128(_mm_srli_epi16(tmpi, 8), _mm_slli_epi16(tmpi, 8)); /*byteswap*/\
\
diff --git a/host/lib/convert/sse2_sc8_to_fc32.cpp b/host/lib/convert/sse2_sc8_to_fc32.cpp
index c0e561814..724af0225 100644
--- a/host/lib/convert/sse2_sc8_to_fc32.cpp
+++ b/host/lib/convert/sse2_sc8_to_fc32.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2012 Ettus Research LLC
+// Copyright 2012-2013 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
@@ -48,7 +48,7 @@ DECLARE_CONVERTER(sc8_item32_be, 1, fc32, 1, PRIORITY_SIMD){
fc32_t *output = reinterpret_cast<fc32_t *>(outputs[0]);
const __m128 scalar = _mm_set_ps1(float(scale_factor)/(1 << 24));
- const int shuf = _MM_SHUFFLE(1, 0, 3, 2);
+ const int shuf = _MM_SHUFFLE(3, 2, 1, 0);
size_t i = 0, j = 0;
fc32_t dummy;
@@ -92,7 +92,7 @@ DECLARE_CONVERTER(sc8_item32_le, 1, fc32, 1, PRIORITY_SIMD){
fc32_t *output = reinterpret_cast<fc32_t *>(outputs[0]);
const __m128 scalar = _mm_set_ps1(float(scale_factor)/(1 << 24));
- const int shuf = _MM_SHUFFLE(2, 3, 0, 1);
+ const int shuf = _MM_SHUFFLE(0, 1, 2, 3);
size_t i = 0, j = 0;
fc32_t dummy;
diff --git a/host/lib/convert/sse2_sc8_to_fc64.cpp b/host/lib/convert/sse2_sc8_to_fc64.cpp
index ef9c0fdb4..94d8911f6 100644
--- a/host/lib/convert/sse2_sc8_to_fc64.cpp
+++ b/host/lib/convert/sse2_sc8_to_fc64.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2012 Ettus Research LLC
+// Copyright 2012-2013 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
@@ -77,7 +77,7 @@ DECLARE_CONVERTER(sc8_item32_be, 1, fc64, 1, PRIORITY_SIMD){
\
/* unpack */ \
__m128d tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; \
- unpack_sc32_8x(tmpi, tmp1, tmp0, tmp3, tmp2, tmp5, tmp4, tmp7, tmp6, scalar); \
+ unpack_sc32_8x(tmpi, tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, scalar); \
\
/* store to output */ \
_mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+0), tmp0); \
@@ -125,7 +125,7 @@ DECLARE_CONVERTER(sc8_item32_le, 1, fc64, 1, PRIORITY_SIMD){
/* unpack */ \
__m128d tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; \
tmpi = _mm_or_si128(_mm_srli_epi16(tmpi, 8), _mm_slli_epi16(tmpi, 8)); /*byteswap*/\
- unpack_sc32_8x(tmpi, tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, scalar); \
+ unpack_sc32_8x(tmpi, tmp1, tmp0, tmp3, tmp2, tmp5, tmp4, tmp7, tmp6, scalar); \
\
/* store to output */ \
_mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+0), tmp0); \
diff --git a/host/lib/ic_reg_maps/CMakeLists.txt b/host/lib/ic_reg_maps/CMakeLists.txt
index dc2ab7847..b6e121db3 100644
--- a/host/lib/ic_reg_maps/CMakeLists.txt
+++ b/host/lib/ic_reg_maps/CMakeLists.txt
@@ -33,6 +33,11 @@ LIBUHD_PYTHON_GEN_SOURCE(
)
LIBUHD_PYTHON_GEN_SOURCE(
+ ${CMAKE_CURRENT_SOURCE_DIR}/gen_max2870_regs.py
+ ${CMAKE_CURRENT_BINARY_DIR}/max2870_regs.hpp
+)
+
+LIBUHD_PYTHON_GEN_SOURCE(
${CMAKE_CURRENT_SOURCE_DIR}/gen_adf4360_regs.py
${CMAKE_CURRENT_BINARY_DIR}/adf4360_regs.hpp
)
diff --git a/host/lib/ic_reg_maps/gen_max2870_regs.py b/host/lib/ic_reg_maps/gen_max2870_regs.py
new file mode 100644
index 000000000..f26c27281
--- /dev/null
+++ b/host/lib/ic_reg_maps/gen_max2870_regs.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python
+#
+# Copyright 2013 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/>.
+#
+
+########################################################################
+# Template for raw text data describing registers
+# name addr[bit range inclusive] default optional enums
+########################################################################
+
+REGS_TMPL="""\
+########################################################################
+## Address 0x00
+## Divider control
+## Write-only, default = 0x007D0000
+########################################################################
+int_n_mode 0x00[31] 0 frac_n, int_n
+int_16_bit 0x00[15:30] 0x007D ##Integer divider: 16-65535 in int-N mode, 19-4091 in frac-N mode.
+frac_12_bit 0x00[3:14] 0 ##Frac divider: 0-4095
+########################################################################
+## Address 0x01
+## Charge pump control
+## Write-only, default = 0x2000FFF9
+########################################################################
+cpoc 0x01[31] 0 disabled, enabled
+cpl 0x01[29:30] 1 disabled, enabled, res1, res2
+cpt 0x01[27:28] 0 normal, reserved, force_source, force_sink
+phase_12_bit 0x01[15:26] 1 ##sets phase shift
+mod_12_bit 0x01[3:14] 0xFFF ##VCO frac modulus
+########################################################################
+## Address 0x02
+## Misc. control
+## Write-only, default = 0x00004042
+########################################################################
+lds 0x02[31] 0 slow, fast
+low_noise_and_spur 0x02[29:30] 3 low_noise, reserved, low_spur_1, low_spur_2
+muxout 0x02[26:28] 1 tri_state, high, low, rdiv, ndiv, ald, dld, res7
+reference_doubler 0x02[25] 0 disabled, enabled
+reference_divide_by_2 0x02[24] 0 disabled, enabled
+r_counter_10_bit 0x02[14:23] 1 ##R divider value, 1-1023
+double_buffer 0x02[13] 0 disabled, enabled
+#set $current_setting_enums = ', '.join(map(lambda x: '_'.join(("%0.2fma"%(1.631/5.1 * (1.+x))).split('.')), range(0,16)))
+charge_pump_current 0x02[9:12] 7 $current_setting_enums
+ldf 0x02[8] 0 frac_n, int_n
+ldp 0x02[7] 0 10ns, 6ns
+pd_polarity 0x02[6] 1 negative, positive
+power_down 0x02[5] 0 normal, shutdown
+cp_three_state 0x02[4] 0 disabled, enabled
+counter_reset 0x02[3] 0 normal, reset
+########################################################################
+## Address 0x03
+## VCO control
+## Write-only, default = 0x0000000B
+########################################################################
+vco 0x03[26:31] 0 ##VCO subband selection, used when VAS disabledd
+vas 0x03[25] 0 enabled, disabled ##VCO autoselect
+retune 0x03[24] 1 disabled, enabled
+clock_div_mode 0x03[15:16] 0 clock_divider_off, fast_lock, phase, reserved
+clock_divider_12_bit 0x03[3:14] 1 ##clock divider, 1-4095
+########################################################################
+## Address 0x04
+## RF output control
+## Write-only, default = 0x6180B23C
+########################################################################
+res4 0x04[26:31] 0x18
+bs_msb 0x04[24:25] 0 ##Band select MSBs
+feedback_select 0x04[23] 1 divided, fundamental
+rf_divider_select 0x04[20:22] 0 div1, div2, div4, div8, div16, div32, div64, div128
+band_select_clock_div 0x04[12:19] 0
+aux_output_select 0x04[9] 1 divided, fundamental
+aux_output_enable 0x04[8] 0 disabled, enabled
+aux_output_power 0x04[6:7] 0 m4dBm, m1dBm, 2dBm, 5dBm
+rf_output_enable 0x04[5] 1 disabled, enabled
+output_power 0x04[3:4] 3 m4dBm, m1dBm, 2dBm, 5dBm
+########################################################################
+## Address 0x05
+## Misc
+## Write only, default = 0x00400005
+########################################################################
+f01 0x05[24] 1 frac_n, auto
+ld_pin_mode 0x05[22:23] 1 low, dld, ald, high
+mux_sdo 0x05[18] 0 normal, sdo
+"""
+
+########################################################################
+# Template for methods in the body of the struct
+########################################################################
+BODY_TMPL="""\
+enum addr_t{
+ ADDR_R0 = 0,
+ ADDR_R1 = 1,
+ ADDR_R2 = 2,
+ ADDR_R3 = 3,
+ ADDR_R4 = 4,
+ ADDR_R5 = 5
+};
+
+boost::uint32_t get_reg(boost::uint8_t addr){
+ boost::uint32_t reg = addr & 0x7;
+ switch(addr){
+ #for $addr in range(5+1)
+ case $addr:
+ #for $reg in filter(lambda r: r.get_addr() == addr, $regs)
+ reg |= (boost::uint32_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift();
+ #end for
+ break;
+ #end for
+ }
+ return reg;
+}
+"""
+
+if __name__ == '__main__':
+ import common; common.generate(
+ name='max2870_regs',
+ regs_tmpl=REGS_TMPL,
+ body_tmpl=BODY_TMPL,
+ file=__file__,
+ )
+
diff --git a/host/lib/stream.cpp b/host/lib/stream.cpp
new file mode 100644
index 000000000..9fafad9ec
--- /dev/null
+++ b/host/lib/stream.cpp
@@ -0,0 +1,30 @@
+//
+// Copyright 2013 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/stream.hpp>
+
+using namespace uhd;
+
+rx_streamer::~rx_streamer(void)
+{
+ //empty
+}
+
+tx_streamer::~tx_streamer(void)
+{
+ //empty
+}
diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt
index 6524a8412..963edcf85 100644
--- a/host/lib/transport/CMakeLists.txt
+++ b/host/lib/transport/CMakeLists.txt
@@ -1,5 +1,5 @@
#
-# Copyright 2010-2011 Ettus Research LLC
+# Copyright 2010-2013 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
@@ -37,6 +37,10 @@ IF(ENABLE_USB)
${CMAKE_CURRENT_SOURCE_DIR}/libusb1_base.cpp
${CMAKE_CURRENT_SOURCE_DIR}/libusb1_base.hpp
)
+ SET_SOURCE_FILES_PROPERTIES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/libusb1_zero_copy.cpp
+ PROPERTIES COMPILE_DEFINITIONS "${LIBUSB_DEFINITIONS}"
+ )
ELSE(ENABLE_USB)
LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/usb_dummy_impl.cpp
@@ -118,5 +122,4 @@ LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/buffer_pool.cpp
${CMAKE_CURRENT_SOURCE_DIR}/if_addrs.cpp
${CMAKE_CURRENT_SOURCE_DIR}/udp_simple.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/usb_zero_copy_wrapper.cpp
)
diff --git a/host/lib/transport/gen_vrt_if_packet.py b/host/lib/transport/gen_vrt_if_packet.py
index e28ce3aae..98f6804ae 100644
--- a/host/lib/transport/gen_vrt_if_packet.py
+++ b/host/lib/transport/gen_vrt_if_packet.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
#
-# Copyright 2010-2011 Ettus Research LLC
+# Copyright 2010-2013 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
@@ -48,12 +48,14 @@ TMPL_TEXT = """
using namespace uhd;
using namespace uhd::transport;
+using namespace uhd::transport::vrt;
typedef size_t pred_type;
typedef std::vector<pred_type> pred_table_type;
#define pred_table_index(hdr) ((hdr >> 20) & 0x1ff)
-static pred_table_type get_pred_unpack_table(void){
+static pred_table_type get_pred_unpack_table(void)
+{
pred_table_type table(1 << 9, 0); //only 9 bits useful here (20-28)
for (size_t i = 0; i < table.size(); i++){
boost::uint32_t vrt_hdr_word = i << 20;
@@ -74,13 +76,45 @@ static const pred_table_type pred_unpack_table(get_pred_unpack_table());
//maps num empty bytes to trailer bits
static const size_t occ_table[] = {0, 2, 1, 3};
+const boost::uint32_t VRLP = ('V' << 24) | ('R' << 16) | ('L' << 8) | ('P' << 0);
+const boost::uint32_t VEND = ('V' << 24) | ('E' << 16) | ('N' << 8) | ('D' << 0);
+
+UHD_INLINE static boost::uint32_t chdr_to_vrt(const boost::uint32_t chdr, if_packet_info_t &info)
+{
+ const boost::uint32_t bytes = chdr & 0xffff;
+ boost::uint32_t vrt = (bytes + 3)/4;
+ info.packet_count = (chdr >> 16) & 0xfff;
+ vrt |= ((chdr >> 31) & 0x1) << 30; //context packet
+ vrt |= ((chdr >> 29) & 0x1) << 20; //has tsf
+ vrt |= ((chdr >> 28) & 0x1) << 24; //has eob
+ vrt |= (0x1) << 28; //has sid (always)
+ return vrt;
+}
+
+UHD_INLINE static boost::uint32_t vrt_to_chdr(const boost::uint32_t vrt, const if_packet_info_t &info)
+{
+ const boost::uint32_t words32 = vrt & 0xffff;
+ int bytes_rem = info.num_payload_bytes % 4;
+ if (bytes_rem != 0) bytes_rem -= 4; //adjust for round up
+ boost::uint32_t chdr = (words32 * 4) + bytes_rem;
+ chdr |= (info.packet_count & 0xfff) << 16;
+ chdr |= ((vrt >> 30) & 0x1) << 31; //context packet
+ chdr |= ((vrt >> 20) & 0x1) << 29; //has tsf
+ chdr |= ((vrt >> 24) & 0x1) << 28; //has eob
+ return chdr;
+}
+
########################################################################
#def gen_code($XE_MACRO, $suffix)
########################################################################
-void vrt::if_hdr_pack_$(suffix)(
+/***********************************************************************
+ * interal impl of packing VRT IF header only
+ **********************************************************************/
+UHD_INLINE void __if_hdr_pack_$(suffix)(
boost::uint32_t *packet_buff,
- if_packet_info_t &if_packet_info
+ if_packet_info_t &if_packet_info,
+ boost::uint32_t &vrt_hdr_word32
){
boost::uint32_t vrt_hdr_flags = 0;
@@ -154,31 +188,33 @@ void vrt::if_hdr_pack_$(suffix)(
}
//fill in complete header word
- packet_buff[0] = $(XE_MACRO)(boost::uint32_t(0
+ vrt_hdr_word32 = boost::uint32_t(0
| (if_packet_info.packet_type << 29)
| vrt_hdr_flags
| ((if_packet_info.packet_count & 0xf) << 16)
| (if_packet_info.num_packet_words32 & 0xffff)
- ));
+ );
}
-void vrt::if_hdr_unpack_$(suffix)(
+/***********************************************************************
+ * interal impl of unpacking VRT IF header only
+ **********************************************************************/
+UHD_INLINE void __if_hdr_unpack_$(suffix)(
const boost::uint32_t *packet_buff,
- if_packet_info_t &if_packet_info
+ if_packet_info_t &if_packet_info,
+ const boost::uint32_t vrt_hdr_word32
){
- //extract vrt header
- boost::uint32_t vrt_hdr_word = $(XE_MACRO)(packet_buff[0]);
- size_t packet_words32 = vrt_hdr_word & 0xffff;
+ const size_t packet_words32 = vrt_hdr_word32 & 0xffff;
//failure case
if (if_packet_info.num_packet_words32 < packet_words32)
throw uhd::value_error("bad vrt header or packet fragment");
//extract fields from the header
- if_packet_info.packet_type = if_packet_info_t::packet_type_t(vrt_hdr_word >> 29);
- if_packet_info.packet_count = (vrt_hdr_word >> 16) & 0xf;
+ if_packet_info.packet_type = if_packet_info_t::packet_type_t(vrt_hdr_word32 >> 29);
+ if_packet_info.packet_count = (vrt_hdr_word32 >> 16) & 0xf;
- const pred_type pred = pred_unpack_table[pred_table_index(vrt_hdr_word)];
+ const pred_type pred = pred_unpack_table[pred_table_index(vrt_hdr_word32)];
size_t empty_bytes = 0;
@@ -259,6 +295,85 @@ void vrt::if_hdr_unpack_$(suffix)(
}
}
+/***********************************************************************
+ * link layer + VRT IF packing
+ **********************************************************************/
+void vrt::if_hdr_pack_$(suffix)(
+ boost::uint32_t *packet_buff,
+ if_packet_info_t &if_packet_info
+){
+ boost::uint32_t vrt_hdr_word32 = 0;
+ switch (if_packet_info.link_type)
+ {
+ case if_packet_info_t::LINK_TYPE_NONE:
+ __if_hdr_pack_$(suffix)(packet_buff, if_packet_info, vrt_hdr_word32);
+ packet_buff[0] = $(XE_MACRO)(vrt_hdr_word32);
+ break;
+
+ case if_packet_info_t::LINK_TYPE_CHDR:
+ {
+ __if_hdr_pack_$(suffix)(packet_buff, if_packet_info, vrt_hdr_word32);
+ const boost::uint32_t chdr = vrt_to_chdr(vrt_hdr_word32, if_packet_info);
+ packet_buff[0] = $(XE_MACRO)(chdr);
+ break;
+ }
+
+ case if_packet_info_t::LINK_TYPE_VRLP:
+ __if_hdr_pack_$(suffix)(packet_buff+2, if_packet_info, vrt_hdr_word32);
+ if_packet_info.num_header_words32 += 2;
+ if_packet_info.num_packet_words32 += 3;
+ packet_buff[0] = $(XE_MACRO)(VRLP);
+ packet_buff[1] = $(XE_MACRO)(boost::uint32_t(
+ (if_packet_info.num_packet_words32 & 0xfffff) |
+ ((if_packet_info.packet_count & 0xfff) << 20)
+ ));
+ packet_buff[2] = $(XE_MACRO)(vrt_hdr_word32);
+ packet_buff[if_packet_info.num_packet_words32-1] = $(XE_MACRO)(VEND);
+ break;
+ }
+}
+
+/***********************************************************************
+ * link layer + VRT IF unpacking
+ **********************************************************************/
+void vrt::if_hdr_unpack_$(suffix)(
+ const boost::uint32_t *packet_buff,
+ if_packet_info_t &if_packet_info
+){
+ boost::uint32_t vrt_hdr_word32 = 0;
+ switch (if_packet_info.link_type)
+ {
+ case if_packet_info_t::LINK_TYPE_NONE:
+ vrt_hdr_word32 = $(XE_MACRO)(packet_buff[0]);
+ __if_hdr_unpack_$(suffix)(packet_buff, if_packet_info, vrt_hdr_word32);
+ break;
+
+ case if_packet_info_t::LINK_TYPE_CHDR:
+ {
+ const boost::uint32_t chdr = $(XE_MACRO)(packet_buff[0]);
+ vrt_hdr_word32 = chdr_to_vrt(chdr, if_packet_info);
+ size_t packet_count = if_packet_info.packet_count;
+ __if_hdr_unpack_$(suffix)(packet_buff, if_packet_info, vrt_hdr_word32);
+ if_packet_info.num_payload_bytes -= (~chdr + 1) & 0x3;
+ if_packet_info.packet_count = packet_count;
+ break;
+ }
+
+ case if_packet_info_t::LINK_TYPE_VRLP:
+ {
+ if ($(XE_MACRO)(packet_buff[0]) != VRLP) throw uhd::value_error("bad vrl header VRLP");
+ const boost::uint32_t vrl_hdr = $(XE_MACRO)(packet_buff[1]);
+ vrt_hdr_word32 = $(XE_MACRO)(packet_buff[2]);
+ if (if_packet_info.num_packet_words32 < (vrl_hdr & 0xfffff)) throw uhd::value_error("bad vrl header or packet fragment");
+ if ($(XE_MACRO)(packet_buff[(vrl_hdr & 0xfffff)-1]) != VEND) throw uhd::value_error("bad vrl trailer VEND");
+ __if_hdr_unpack_$(suffix)(packet_buff+2, if_packet_info, vrt_hdr_word32);
+ if_packet_info.num_header_words32 += 2; //add vrl header
+ if_packet_info.packet_count = (vrl_hdr >> 20) & 0xfff;
+ break;
+ }
+ }
+}
+
########################################################################
#end def
########################################################################
diff --git a/host/lib/transport/libusb1_base.cpp b/host/lib/transport/libusb1_base.cpp
index d4ec874f1..8bd0f4354 100644
--- a/host/lib/transport/libusb1_base.cpp
+++ b/host/lib/transport/libusb1_base.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2010-2011 Ettus Research LLC
+// Copyright 2010-2013 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -19,10 +19,13 @@
#include <uhd/exception.hpp>
#include <uhd/utils/msg.hpp>
#include <uhd/utils/log.hpp>
+#include <uhd/utils/tasks.hpp>
#include <uhd/types/dict.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/foreach.hpp>
+#include <boost/bind.hpp>
+#include <cstdlib>
#include <iostream>
using namespace uhd;
@@ -36,9 +39,11 @@ public:
libusb_session_impl(void){
UHD_ASSERT_THROW(libusb_init(&_context) == 0);
libusb_set_debug(_context, debug_level);
+ task_handler = task::make(boost::bind(&libusb_session_impl::libusb_event_handler_task, this, _context));
}
~libusb_session_impl(void){
+ task_handler.reset();
libusb_exit(_context);
}
@@ -48,6 +53,21 @@ public:
private:
libusb_context *_context;
+ task::sptr task_handler;
+
+ /*
+ * Task to handle libusb events. There should only be one thread per libusb_context handling events.
+ * Using more than one thread can result in excessive CPU usage in kernel space (presumably from locking/waiting).
+ * The libusb documentation says it is safe, which it is, but it neglects to state the cost in CPU usage.
+ * Just don't do it!
+ */
+ UHD_INLINE void libusb_event_handler_task(libusb_context *context)
+ {
+ timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = 100000;
+ libusb_handle_events_timeout(context, &tv);
+ }
};
libusb::session::sptr libusb::session::get_global_session(void){
@@ -59,6 +79,15 @@ libusb::session::sptr libusb::session::get_global_session(void){
//create a new global session
sptr new_global_session(new libusb_session_impl());
global_session = new_global_session;
+
+ //set logging if envvar is set
+ const char *level_string = getenv("LIBUSB_DEBUG_LEVEL");
+ if (level_string != NULL)
+ {
+ const int level = int(level_string[0] - '0'); //easy conversion to integer
+ if (level >= 0 and level <= 3) libusb_set_debug(new_global_session->get_context(), level);
+ }
+
return new_global_session;
}
@@ -137,8 +166,13 @@ public:
return _desc;
}
- std::string get_ascii_serial(void) const{
- if (this->get().iSerialNumber == 0) return "";
+ std::string get_ascii_property(const std::string &what) const
+ {
+ boost::uint8_t off = 0;
+ if (what == "serial") off = this->get().iSerialNumber;
+ if (what == "product") off = this->get().iProduct;
+ if (what == "manufacturer") off = this->get().iManufacturer;
+ if (off == 0) return "";
libusb::device_handle::sptr handle(
libusb::device_handle::get_cached_handle(_dev)
@@ -146,7 +180,7 @@ public:
unsigned char buff[512];
ssize_t ret = libusb_get_string_descriptor_ascii(
- handle->get(), this->get().iSerialNumber, buff, sizeof(buff)
+ handle->get(), off, buff, sizeof(buff)
);
if (ret < 0) return ""; //on error, just return empty string
@@ -240,7 +274,15 @@ public:
}
std::string get_serial(void) const{
- return libusb::device_descriptor::make(this->get_device())->get_ascii_serial();
+ return libusb::device_descriptor::make(this->get_device())->get_ascii_property("serial");
+ }
+
+ std::string get_manufacturer() const{
+ return libusb::device_descriptor::make(this->get_device())->get_ascii_property("manufacturer");
+ }
+
+ std::string get_product() const{
+ return libusb::device_descriptor::make(this->get_device())->get_ascii_property("product");
}
boost::uint16_t get_vendor_id(void) const{
@@ -251,6 +293,10 @@ public:
return libusb::device_descriptor::make(this->get_device())->get().idProduct;
}
+ bool firmware_loaded() {
+ return (get_manufacturer() == "Ettus Research LLC");
+ }
+
private:
libusb::device::sptr _dev; //always keep a reference to device
};
diff --git a/host/lib/transport/libusb1_base.hpp b/host/lib/transport/libusb1_base.hpp
index 04c1d6574..7dab07fda 100644
--- a/host/lib/transport/libusb1_base.hpp
+++ b/host/lib/transport/libusb1_base.hpp
@@ -1,5 +1,5 @@
//
-// Copyright 2010 Ettus Research LLC
+// Copyright 2010-2013 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
@@ -102,7 +102,7 @@ namespace libusb {
//! get the underlying device descriptor
virtual const libusb_device_descriptor &get(void) const = 0;
- virtual std::string get_ascii_serial(void) const = 0;
+ virtual std::string get_ascii_property(const std::string &what) const = 0;
};
/*!
diff --git a/host/lib/transport/libusb1_control.cpp b/host/lib/transport/libusb1_control.cpp
index 3d9b38785..c1b8fe6df 100644
--- a/host/lib/transport/libusb1_control.cpp
+++ b/host/lib/transport/libusb1_control.cpp
@@ -21,8 +21,6 @@
using namespace uhd::transport;
-const int libusb_timeout = 0;
-
/***********************************************************************
* libusb-1.0 implementation of USB control transport
**********************************************************************/
@@ -39,7 +37,8 @@ public:
boost::uint16_t value,
boost::uint16_t index,
unsigned char *buff,
- boost::uint16_t length
+ boost::uint16_t length,
+ boost::int32_t libusb_timeout = 0
){
boost::mutex::scoped_lock lock(_mutex);
return libusb_control_transfer(_handle->get(),
diff --git a/host/lib/transport/libusb1_zero_copy.cpp b/host/lib/transport/libusb1_zero_copy.cpp
index 28bff9709..2d18e1623 100644
--- a/host/lib/transport/libusb1_zero_copy.cpp
+++ b/host/lib/transport/libusb1_zero_copy.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2010-2012 Ettus Research LLC
+// Copyright 2010-2013 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
@@ -18,11 +18,17 @@
#include "libusb1_base.hpp"
#include <uhd/transport/usb_zero_copy.hpp>
#include <uhd/transport/buffer_pool.hpp>
+#include <uhd/transport/bounded_buffer.hpp>
#include <uhd/utils/msg.hpp>
#include <uhd/exception.hpp>
#include <boost/foreach.hpp>
+#include <boost/format.hpp>
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
#include <boost/make_shared.hpp>
-#include <boost/thread/thread.hpp>
+#include <boost/circular_buffer.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/condition_variable.hpp>
#include <list>
using namespace uhd;
@@ -36,115 +42,128 @@ static const size_t DEFAULT_XFER_SIZE = 32*512; //bytes
#define LIBUSB_CALL
#endif /*LIBUSB_CALL*/
+//! libusb_handle_events_timeout_completed is only in newer API
+#ifndef HAVE_LIBUSB_HANDLE_EVENTS_TIMEOUT_COMPLETED
+ #define libusb_handle_events_timeout_completed(ctx, tx, completed) \
+ libusb_handle_events_timeout(ctx, tx)
+#endif
+
+//! libusb_error_name is only in newer API
+#ifndef HAVE_LIBUSB_ERROR_NAME
+ #define libusb_error_name(code) \
+ str(boost::format("LIBUSB_ERROR_CODE %d") % code)
+#endif
+
+//! type for sharing the release queue with managed buffers
+class libusb_zero_copy_mb;
+typedef boost::shared_ptr<bounded_buffer<libusb_zero_copy_mb *> > mb_queue_sptr;
+
/*!
- * All libusb callback functions should be marked with the LIBUSB_CALL macro
- * to ensure that they are compiled with the same calling convention as libusb.
+ * The libusb docs state that status and actual length can only be read in the callback.
+ * Therefore, this struct is intended to store data seen from the callback function.
*/
+struct lut_result_t
+{
+ lut_result_t(void)
+ {
+ completed = 0;
+ status = LIBUSB_TRANSFER_COMPLETED;
+ actual_length = 0;
+ }
+ int completed;
+ libusb_transfer_status status;
+ int actual_length;
+ boost::mutex mut;
+ boost::condition_variable usb_transfer_complete;
+};
-//! helper function: handles all async callbacks
-static void LIBUSB_CALL libusb_async_cb(libusb_transfer *lut){
- *(static_cast<bool *>(lut->user_data)) = true;
-}
+// Created to be used as an argument to boost::condition_variable::timed_wait() function
+struct lut_result_completed {
+ const lut_result_t& _result;
+ lut_result_completed(const lut_result_t& result):_result(result) {}
+ bool operator()() const {return (_result.completed ? true : false);}
+};
/*!
- * Wait for a managed buffer to become complete.
- *
- * This routine processes async events until the transaction completes.
- * We must call the libusb handle events in a loop because the handler
- * may complete managed buffers other than the one we are waiting on.
- *
- * We cannot determine if handle events timed out or processed an event.
- * Therefore, the timeout condition is handled by using boost system time.
- *
- * \param ctx the libusb context structure
- * \param timeout the wait timeout in seconds
- * \param completed a reference to the completed flag
- * \return true for completion, false for timeout
+ * All libusb callback functions should be marked with the LIBUSB_CALL macro
+ * to ensure that they are compiled with the same calling convention as libusb.
*/
-UHD_INLINE bool wait_for_completion(libusb_context *ctx, const double timeout, bool &completed){
- //already completed by a previous call?
- if (completed) return true;
-
- //perform a non-blocking event handle
- timeval tv;
- tv.tv_sec = 0;
- tv.tv_usec = 0;
- libusb_handle_events_timeout(ctx, &tv);
- if (completed) return true;
-
- //finish the rest with a timeout loop
- const boost::system_time timeout_time = boost::get_system_time() + boost::posix_time::microseconds(long(timeout*1000000));
- while (not completed and (boost::get_system_time() < timeout_time)){
- timeval tv;
- tv.tv_sec = 0;
- tv.tv_usec = 10000; /*10ms*/
- libusb_handle_events_timeout(ctx, &tv);
- }
- return completed;
+//! helper function: handles all async callbacks
+static void LIBUSB_CALL libusb_async_cb(libusb_transfer *lut)
+{
+ lut_result_t *r = (lut_result_t *)lut->user_data;
+ boost::lock_guard<boost::mutex> lock(r->mut);
+ r->status = lut->status;
+ r->actual_length = lut->actual_length;
+ r->completed = 1;
+ r->usb_transfer_complete.notify_one(); // wake up thread waiting in wait_for_completion() member function below
}
/***********************************************************************
- * Reusable managed receiver buffer:
+ * Reusable managed buffer:
* - Associated with a particular libusb transfer struct.
* - Submits the transfer to libusb in the release method.
**********************************************************************/
-class libusb_zero_copy_mrb : public managed_recv_buffer{
+class libusb_zero_copy_mb : public managed_buffer
+{
public:
- libusb_zero_copy_mrb(libusb_transfer *lut, const size_t frame_size):
+ libusb_zero_copy_mb(libusb_transfer *lut, const size_t frame_size, boost::function<void(libusb_zero_copy_mb *)> release_cb, const bool is_recv, const std::string &name):
+ _release_cb(release_cb), _is_recv(is_recv), _name(name),
_ctx(libusb::session::get_global_session()->get_context()),
_lut(lut), _frame_size(frame_size) { /* NOP */ }
- void release(void){
- completed = false;
- _lut->length = _frame_size; //always reset length
- UHD_ASSERT_THROW(libusb_submit_transfer(_lut) == 0);
+ void release(void){_release_cb(this);}
+
+ UHD_INLINE void submit(void)
+ {
+ _lut->length = (_is_recv)? _frame_size : size(); //always set length
+ const int ret = libusb_submit_transfer(_lut);
+ if (ret != 0) throw uhd::runtime_error(str(boost::format(
+ "usb %s submit failed: %s") % _name % libusb_error_name(ret)));
}
- sptr get_new(const double timeout, size_t &index){
- if (wait_for_completion(_ctx, timeout, completed)){
- index++;
- return make(this, _lut->buffer, _lut->actual_length);
+ template <typename buffer_type>
+ UHD_INLINE typename buffer_type::sptr get_new(const double timeout)
+ {
+ if (wait_for_completion(timeout))
+ {
+ if (result.status != LIBUSB_TRANSFER_COMPLETED) throw uhd::runtime_error(str(boost::format(
+ "usb %s transfer status: %d") % _name % int(result.status)));
+ result.completed = 0;
+ return make(reinterpret_cast<buffer_type *>(this), _lut->buffer, (_is_recv)? result.actual_length : _frame_size);
}
- return managed_recv_buffer::sptr();
+ return typename buffer_type::sptr();
}
- bool completed;
+ // This is public because it is accessed from the libusb_zero_copy_single constructor
+ lut_result_t result;
-private:
- libusb_context *_ctx;
- libusb_transfer *_lut;
- const size_t _frame_size;
-};
-
-/***********************************************************************
- * Reusable managed send buffer:
- * - Associated with a particular libusb transfer struct.
- * - Submits the transfer to libusb in the commit method.
- **********************************************************************/
-class libusb_zero_copy_msb : public managed_send_buffer{
-public:
- libusb_zero_copy_msb(libusb_transfer *lut, const size_t frame_size):
- _ctx(libusb::session::get_global_session()->get_context()),
- _lut(lut), _frame_size(frame_size) { completed = true; }
-
- void release(void){
- completed = false;
- _lut->length = size();
- UHD_ASSERT_THROW(libusb_submit_transfer(_lut) == 0);
- }
-
- sptr get_new(const double timeout, size_t &index){
- if (wait_for_completion(_ctx, timeout, completed)){
- index++;
- return make(this, _lut->buffer, _frame_size);
+ /*!
+ * Wait for a managed buffer to become complete.
+ *
+ * \param timeout the wait timeout in seconds. A negative value will wait forever.
+ * \return true for completion, false for timeout
+ */
+ UHD_INLINE bool wait_for_completion(const double timeout)
+ {
+ boost::unique_lock<boost::mutex> lock(result.mut);
+ if (!result.completed) {
+ if (timeout < 0.0) {
+ result.usb_transfer_complete.wait(lock);
+ } else {
+ const boost::system_time timeout_time = boost::get_system_time() + boost::posix_time::microseconds(long(timeout*1000000));
+ result.usb_transfer_complete.timed_wait(lock, timeout_time, lut_result_completed(result));
+ }
}
- return managed_send_buffer::sptr();
+ return result.completed;
}
- bool completed;
-
private:
+
+ boost::function<void(libusb_zero_copy_mb *)> _release_cb;
+ const bool _is_recv;
+ const std::string _name;
libusb_context *_ctx;
libusb_transfer *_lut;
const size_t _frame_size;
@@ -153,39 +172,33 @@ private:
/***********************************************************************
* USB zero_copy device class
**********************************************************************/
-class libusb_zero_copy_impl : public usb_zero_copy{
+class libusb_zero_copy_single
+{
public:
-
- libusb_zero_copy_impl(
+ libusb_zero_copy_single(
libusb::device_handle::sptr handle,
- const size_t recv_interface,
- const size_t recv_endpoint,
- const size_t send_interface,
- const size_t send_endpoint,
- const device_addr_t &hints
+ const size_t interface, const size_t endpoint,
+ const size_t num_frames, const size_t frame_size
):
_handle(handle),
- _recv_frame_size(size_t(hints.cast<double>("recv_frame_size", DEFAULT_XFER_SIZE))),
- _num_recv_frames(size_t(hints.cast<double>("num_recv_frames", DEFAULT_NUM_XFERS))),
- _send_frame_size(size_t(hints.cast<double>("send_frame_size", DEFAULT_XFER_SIZE))),
- _num_send_frames(size_t(hints.cast<double>("num_send_frames", DEFAULT_NUM_XFERS))),
- _recv_buffer_pool(buffer_pool::make(_num_recv_frames, _recv_frame_size)),
- _send_buffer_pool(buffer_pool::make(_num_send_frames, _send_frame_size)),
- _next_recv_buff_index(0),
- _next_send_buff_index(0)
+ _num_frames(num_frames),
+ _frame_size(frame_size),
+ _buffer_pool(buffer_pool::make(_num_frames, _frame_size)),
+ _enqueued(_num_frames), _released(_num_frames)
{
- _handle->claim_interface(recv_interface);
- _handle->claim_interface(send_interface);
+ const bool is_recv = (endpoint & 0x80) != 0;
+ const std::string name = str(boost::format("%s%d") % ((is_recv)? "rx" : "tx") % int(endpoint & 0x7f));
+ _handle->claim_interface(interface);
//flush the buffers out of the recv endpoint
//limit the flushing to at most one second
- for (size_t i = 0; i < 100; i++)
+ if (is_recv) for (size_t i = 0; i < 100; i++)
{
unsigned char buff[512];
int transfered = 0;
const int status = libusb_bulk_transfer(
_handle->get(), // dev_handle
- (recv_endpoint & 0x7f) | 0x80, // endpoint
+ endpoint, // endpoint
static_cast<unsigned char *>(buff),
sizeof(buff),
&transfered, //bytes xfered
@@ -194,102 +207,169 @@ public:
if (status == LIBUSB_ERROR_TIMEOUT) break;
}
- //allocate libusb transfer structs and managed receive buffers
- for (size_t i = 0; i < get_num_recv_frames(); i++){
-
+ //allocate libusb transfer structs and managed buffers
+ for (size_t i = 0; i < get_num_frames(); i++)
+ {
libusb_transfer *lut = libusb_alloc_transfer(0);
UHD_ASSERT_THROW(lut != NULL);
- _mrb_pool.push_back(boost::make_shared<libusb_zero_copy_mrb>(lut, this->get_recv_frame_size()));
+ _mb_pool.push_back(boost::make_shared<libusb_zero_copy_mb>(
+ lut, this->get_frame_size(), boost::bind(&libusb_zero_copy_single::enqueue_damn_buffer, this, _1), is_recv, name
+ ));
libusb_fill_bulk_transfer(
lut, // transfer
_handle->get(), // dev_handle
- (recv_endpoint & 0x7f) | 0x80, // endpoint
- static_cast<unsigned char *>(_recv_buffer_pool->at(i)), // buffer
- this->get_recv_frame_size(), // length
+ endpoint, // endpoint
+ static_cast<unsigned char *>(_buffer_pool->at(i)), // buffer
+ this->get_frame_size(), // length
libusb_transfer_cb_fn(&libusb_async_cb), // callback
- static_cast<void *>(&_mrb_pool.back()->completed), // user_data
+ static_cast<void *>(&_mb_pool.back()->result), // user_data
0 // timeout (ms)
);
_all_luts.push_back(lut);
- _mrb_pool.back()->release();
}
- //allocate libusb transfer structs and managed send buffers
- for (size_t i = 0; i < get_num_send_frames(); i++){
-
- libusb_transfer *lut = libusb_alloc_transfer(0);
- UHD_ASSERT_THROW(lut != NULL);
-
- _msb_pool.push_back(boost::make_shared<libusb_zero_copy_msb>(lut, this->get_send_frame_size()));
-
- libusb_fill_bulk_transfer(
- lut, // transfer
- _handle->get(), // dev_handle
- (send_endpoint & 0x7f) | 0x00, // endpoint
- static_cast<unsigned char *>(_send_buffer_pool->at(i)), // buffer
- this->get_send_frame_size(), // length
- libusb_transfer_cb_fn(&libusb_async_cb), // callback
- static_cast<void *>(&_msb_pool.back()->completed), // user_data
- 0 // timeout
- );
-
- _all_luts.push_back(lut);
+ //initial release for all buffers
+ for (size_t i = 0; i < get_num_frames(); i++)
+ {
+ libusb_zero_copy_mb &mb = *(_mb_pool[i]);
+ if (is_recv) mb.release();
+ else
+ {
+ mb.result.completed = 1;
+ _enqueued.push_back(&mb);
+ }
}
}
- ~libusb_zero_copy_impl(void){
- libusb_context *ctx = libusb::session::get_global_session()->get_context();
-
+ ~libusb_zero_copy_single(void)
+ {
//cancel all transfers
- BOOST_FOREACH(libusb_transfer *lut, _all_luts){
+ BOOST_FOREACH(libusb_transfer *lut, _all_luts)
+ {
libusb_cancel_transfer(lut);
}
//process all transfers until timeout occurs
- bool completed = false;
- wait_for_completion(ctx, 0.01, completed);
+ BOOST_FOREACH(libusb_zero_copy_mb *mb, _enqueued)
+ {
+ mb->wait_for_completion(0.01);
+ }
//free all transfers
- BOOST_FOREACH(libusb_transfer *lut, _all_luts){
+ BOOST_FOREACH(libusb_transfer *lut, _all_luts)
+ {
libusb_free_transfer(lut);
}
-
}
- managed_recv_buffer::sptr get_recv_buff(double timeout){
- if (_next_recv_buff_index == _num_recv_frames) _next_recv_buff_index = 0;
- return _mrb_pool[_next_recv_buff_index]->get_new(timeout, _next_recv_buff_index);
- }
+ template <typename buffer_type>
+ UHD_INLINE typename buffer_type::sptr get_buff(double timeout)
+ {
+ typename buffer_type::sptr buff;
+ libusb_zero_copy_mb *front = NULL;
+ boost::mutex::scoped_lock lock(_mutex);
+ if (_enqueued.empty())
+ {
+ _cond.timed_wait(lock, boost::posix_time::microseconds(long(timeout*1e6)));
+ }
+ if (_enqueued.empty()) return buff;
+ front = _enqueued.front();
- managed_send_buffer::sptr get_send_buff(double timeout){
- if (_next_send_buff_index == _num_send_frames) _next_send_buff_index = 0;
- return _msb_pool[_next_send_buff_index]->get_new(timeout, _next_send_buff_index);
- }
+ lock.unlock();
+ buff = front->get_new<buffer_type>(timeout);
+ lock.lock();
- size_t get_num_recv_frames(void) const { return _num_recv_frames; }
- size_t get_num_send_frames(void) const { return _num_send_frames; }
+ if (buff) _enqueued.pop_front();
+ this->submit_what_we_can();
+ return buff;
+ }
- size_t get_recv_frame_size(void) const { return _recv_frame_size; }
- size_t get_send_frame_size(void) const { return _send_frame_size; }
+ UHD_INLINE size_t get_num_frames(void) const { return _num_frames; }
+ UHD_INLINE size_t get_frame_size(void) const { return _frame_size; }
private:
libusb::device_handle::sptr _handle;
- const size_t _recv_frame_size, _num_recv_frames;
- const size_t _send_frame_size, _num_send_frames;
+ const size_t _num_frames, _frame_size;
//! Storage for transfer related objects
- buffer_pool::sptr _recv_buffer_pool, _send_buffer_pool;
- std::vector<boost::shared_ptr<libusb_zero_copy_mrb> > _mrb_pool;
- std::vector<boost::shared_ptr<libusb_zero_copy_msb> > _msb_pool;
- size_t _next_recv_buff_index, _next_send_buff_index;
+ buffer_pool::sptr _buffer_pool;
+ std::vector<boost::shared_ptr<libusb_zero_copy_mb> > _mb_pool;
+
+ boost::mutex _mutex;
+ boost::condition_variable _cond;
+
+ //! why 2 queues? there is room in the future to have > N buffers but only N in flight
+ boost::circular_buffer<libusb_zero_copy_mb *> _enqueued, _released;
+
+ void enqueue_damn_buffer(libusb_zero_copy_mb *mb)
+ {
+ boost::mutex::scoped_lock l(_mutex);
+ _released.push_back(mb);
+ this->submit_what_we_can();
+ l.unlock();
+ _cond.notify_one();
+ }
+
+ void submit_what_we_can(void)
+ {
+ while (not _released.empty() and not _enqueued.full())
+ {
+ _released.front()->submit();
+ _enqueued.push_back(_released.front());
+ _released.pop_front();
+ }
+ }
//! a list of all transfer structs we allocated
std::list<libusb_transfer *> _all_luts;
+};
+
+/***********************************************************************
+ * USB zero_copy device class
+ **********************************************************************/
+struct libusb_zero_copy_impl : usb_zero_copy
+{
+ libusb_zero_copy_impl(
+ libusb::device_handle::sptr handle,
+ const size_t recv_interface,
+ const size_t recv_endpoint,
+ const size_t send_interface,
+ const size_t send_endpoint,
+ const device_addr_t &hints
+ ){
+ _recv_impl.reset(new libusb_zero_copy_single(
+ handle, recv_interface, (recv_endpoint & 0x7f) | 0x80,
+ size_t(hints.cast<double>("num_recv_frames", DEFAULT_NUM_XFERS)),
+ size_t(hints.cast<double>("recv_frame_size", DEFAULT_XFER_SIZE))));
+ _send_impl.reset(new libusb_zero_copy_single(
+ handle, send_interface, (send_endpoint & 0x7f) | 0x00,
+ size_t(hints.cast<double>("num_send_frames", DEFAULT_NUM_XFERS)),
+ size_t(hints.cast<double>("send_frame_size", DEFAULT_XFER_SIZE))));
+ }
+
+ managed_recv_buffer::sptr get_recv_buff(double timeout)
+ {
+ boost::mutex::scoped_lock l(_recv_mutex);
+ return _recv_impl->get_buff<managed_recv_buffer>(timeout);
+ }
+
+ managed_send_buffer::sptr get_send_buff(double timeout)
+ {
+ boost::mutex::scoped_lock l(_send_mutex);
+ return _send_impl->get_buff<managed_send_buffer>(timeout);
+ }
+
+ size_t get_num_recv_frames(void) const { return _recv_impl->get_num_frames(); }
+ size_t get_num_send_frames(void) const { return _send_impl->get_num_frames(); }
+ size_t get_recv_frame_size(void) const { return _recv_impl->get_frame_size(); }
+ size_t get_send_frame_size(void) const { return _send_impl->get_frame_size(); }
+ boost::shared_ptr<libusb_zero_copy_single> _recv_impl, _send_impl;
+ boost::mutex _recv_mutex, _send_mutex;
};
/***********************************************************************
diff --git a/host/lib/transport/super_recv_packet_handler.hpp b/host/lib/transport/super_recv_packet_handler.hpp
index 5a75d5f0d..688228e49 100644
--- a/host/lib/transport/super_recv_packet_handler.hpp
+++ b/host/lib/transport/super_recv_packet_handler.hpp
@@ -63,6 +63,8 @@ static inline void handle_overflow_nop(void){}
class recv_packet_handler{
public:
typedef boost::function<managed_recv_buffer::sptr(double)> get_buff_type;
+ typedef boost::function<void(const size_t)> handle_flowctrl_type;
+ typedef boost::function<void(const stream_cmd_t&)> issue_stream_cmd_type;
typedef void(*vrt_unpacker_type)(const boost::uint32_t *, vrt::if_packet_info_t &);
//typedef boost::function<void(const boost::uint32_t *, vrt::if_packet_info_t &)> vrt_unpacker_type;
@@ -139,6 +141,19 @@ public:
_props.at(xport_chan).get_buff = get_buff;
}
+ /*!
+ * Set the function to handle flow control
+ * \param xport_chan which transport channel
+ * \param handle_flowctrl the callback function
+ */
+ void set_xport_handle_flowctrl(const size_t xport_chan, const handle_flowctrl_type &handle_flowctrl, const size_t update_window, const bool do_init = false)
+ {
+ _props.at(xport_chan).handle_flowctrl = handle_flowctrl;
+ //we need the window size to be within the 0xfff (max 12 bit seq)
+ _props.at(xport_chan).fc_update_window = std::min<size_t>(update_window, 0xfff);
+ if (do_init) handle_flowctrl(0);
+ }
+
//! Set the conversion routine for all channels
void set_converter(const uhd::convert::id_type &id){
_num_outputs = id.num_outputs;
@@ -158,6 +173,21 @@ public:
_converter->set_scalar(scale_factor);
}
+ //! Set the callback to issue stream commands
+ void set_issue_stream_cmd(const size_t xport_chan, const issue_stream_cmd_type &issue_stream_cmd)
+ {
+ _props.at(xport_chan).issue_stream_cmd = issue_stream_cmd;
+ }
+
+ //! Overload call to issue stream commands
+ void issue_stream_cmd(const stream_cmd_t &stream_cmd)
+ {
+ for (size_t i = 0; i < _props.size(); i++)
+ {
+ if (_props[i].issue_stream_cmd) _props[i].issue_stream_cmd(stream_cmd);
+ }
+ }
+
/*******************************************************************
* Receive:
* The entry point for the fast-path receive calls.
@@ -219,8 +249,11 @@ private:
handle_overflow(&handle_overflow_nop)
{}
get_buff_type get_buff;
+ issue_stream_cmd_type issue_stream_cmd;
size_t packet_count;
handle_overflow_type handle_overflow;
+ handle_flowctrl_type handle_flowctrl;
+ size_t fc_update_window;
};
std::vector<xport_chan_props_type> _props;
size_t _num_outputs;
@@ -302,6 +335,15 @@ private:
info.time = time_spec_t::from_ticks(info.ifpi.tsf, _tick_rate); //assumes has_tsf is true
info.copy_buff = reinterpret_cast<const char *>(info.vrt_hdr + info.ifpi.num_header_words32);
+ //handle flow control
+ if (_props[index].handle_flowctrl)
+ {
+ if ((info.ifpi.packet_count % _props[index].fc_update_window/2) == 0)
+ {
+ _props[index].handle_flowctrl(info.ifpi.packet_count);
+ }
+ }
+
//--------------------------------------------------------------
//-- Determine return conditions:
//-- The order of these checks is HOLY.
@@ -314,8 +356,9 @@ private:
//2) check for sequence errors
#ifndef SRPH_DONT_CHECK_SEQUENCE
+ const size_t seq_mask = (info.ifpi.link_type == vrt::if_packet_info_t::LINK_TYPE_NONE)? 0xf : 0xfff;
const size_t expected_packet_count = _props[index].packet_count;
- _props[index].packet_count = (info.ifpi.packet_count + 1)%16;
+ _props[index].packet_count = (info.ifpi.packet_count + 1) & seq_mask;
if (expected_packet_count != info.ifpi.packet_count){
return PACKET_SEQUENCE_ERROR;
}
@@ -459,7 +502,7 @@ private:
curr_info.metadata.start_of_burst = false;
curr_info.metadata.end_of_burst = false;
curr_info.metadata.error_code = rx_metadata_t::ERROR_CODE_OVERFLOW;
- UHD_MSG(fastpath) << "O";
+ UHD_MSG(fastpath) << "D";
return;
}
@@ -479,6 +522,7 @@ private:
curr_info.metadata.start_of_burst = false;
curr_info.metadata.end_of_burst = false;
curr_info.metadata.error_code = rx_metadata_t::ERROR_CODE_ALIGNMENT;
+ _props[index].handle_overflow();
return;
}
@@ -622,6 +666,11 @@ public:
return recv_packet_handler::recv(buffs, nsamps_per_buff, metadata, timeout, one_packet);
}
+ void issue_stream_cmd(const stream_cmd_t &stream_cmd)
+ {
+ return recv_packet_handler::issue_stream_cmd(stream_cmd);
+ }
+
private:
size_t _max_num_samps;
};
diff --git a/host/lib/transport/super_send_packet_handler.hpp b/host/lib/transport/super_send_packet_handler.hpp
index 726742327..41f030ea6 100644
--- a/host/lib/transport/super_send_packet_handler.hpp
+++ b/host/lib/transport/super_send_packet_handler.hpp
@@ -47,6 +47,7 @@ namespace uhd{ namespace transport{ namespace sph{
class send_packet_handler{
public:
typedef boost::function<managed_send_buffer::sptr(double)> get_buff_type;
+ typedef boost::function<bool(uhd::async_metadata_t &, const double)> async_receiver_type;
typedef void(*vrt_packer_type)(boost::uint32_t *, vrt::if_packet_info_t &);
//typedef boost::function<void(boost::uint32_t *, vrt::if_packet_info_t &)> vrt_packer_type;
@@ -57,6 +58,7 @@ public:
send_packet_handler(const size_t size = 1):
_next_packet_seq(0)
{
+ this->set_enable_trailer(true);
this->resize(size);
}
@@ -96,6 +98,11 @@ public:
_props.at(xport_chan).sid = sid;
}
+ void set_enable_trailer(const bool enable)
+ {
+ _has_tlr = enable;
+ }
+
//! Set the rate of ticks per second
void set_tick_rate(const double rate){
_tick_rate = rate;
@@ -138,6 +145,21 @@ public:
_converter->set_scalar(scale_factor);
}
+ //! Set the callback to get async messages
+ void set_async_receiver(const async_receiver_type &async_receiver)
+ {
+ _async_receiver = async_receiver;
+ }
+
+ //! Overload call to get async metadata
+ bool recv_async_msg(
+ uhd::async_metadata_t &async_metadata, double timeout = 0.1
+ ){
+ if (_async_receiver) return _async_receiver(async_metadata, timeout);
+ boost::this_thread::sleep(boost::posix_time::microseconds(long(timeout*1e6)));
+ return false;
+ }
+
/*******************************************************************
* Send:
* The entry point for the fast-path send calls.
@@ -154,7 +176,7 @@ public:
if_packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_DATA;
//if_packet_info.has_sid = false; //set per channel
if_packet_info.has_cid = false;
- if_packet_info.has_tlr = true;
+ if_packet_info.has_tlr = _has_tlr;
if_packet_info.has_tsi = false;
if_packet_info.has_tsf = metadata.has_time_spec;
if_packet_info.tsf = metadata.time_spec.to_ticks(_tick_rate);
@@ -165,9 +187,12 @@ public:
//TODO remove this code when sample counts of zero are supported by hardware
#ifndef SSPH_DONT_PAD_TO_ONE
- if (nsamps_per_buff == 0) return send_one_packet(
- _zero_buffs, 1, if_packet_info, timeout
- ) & 0x0;
+ static const boost::uint64_t zero = 0;
+ _zero_buffs.resize(buffs.size(), &zero);
+
+ if (nsamps_per_buff == 0) return send_one_packet(
+ _zero_buffs, 1, if_packet_info, timeout
+ ) & 0x0;
#endif
return send_one_packet(buffs, nsamps_per_buff, if_packet_info, timeout);
@@ -228,6 +253,8 @@ private:
size_t _max_samples_per_packet;
std::vector<const void *> _zero_buffs;
size_t _next_packet_seq;
+ bool _has_tlr;
+ async_receiver_type _async_receiver;
/*******************************************************************
* Send a single packet:
@@ -337,6 +364,12 @@ public:
return send_packet_handler::send(buffs, nsamps_per_buff, metadata, timeout);
}
+ bool recv_async_msg(
+ uhd::async_metadata_t &async_metadata, double timeout = 0.1
+ ){
+ return send_packet_handler::recv_async_msg(async_metadata, timeout);
+ }
+
private:
size_t _max_num_samps;
};
diff --git a/host/lib/types/CMakeLists.txt b/host/lib/types/CMakeLists.txt
index 2ca0faef7..b69c8e487 100644
--- a/host/lib/types/CMakeLists.txt
+++ b/host/lib/types/CMakeLists.txt
@@ -1,5 +1,5 @@
#
-# Copyright 2011 Ettus Research LLC
+# Copyright 2011-2013 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
@@ -88,4 +88,5 @@ LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/time_spec.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tune.cpp
${CMAKE_CURRENT_SOURCE_DIR}/types.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/wb_iface.cpp
)
diff --git a/host/lib/types/serial.cpp b/host/lib/types/serial.cpp
index 9e9d32954..9b8336dd8 100644
--- a/host/lib/types/serial.cpp
+++ b/host/lib/types/serial.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2011 Ettus Research LLC
+// Copyright 2011-2013 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -21,6 +21,21 @@
using namespace uhd;
+i2c_iface::~i2c_iface(void)
+{
+ //empty
+}
+
+spi_iface::~spi_iface(void)
+{
+ //empty
+}
+
+uart_iface::~uart_iface(void)
+{
+ //empty
+}
+
spi_config_t::spi_config_t(edge_t edge):
mosi_edge(edge),
miso_edge(edge)
@@ -29,8 +44,8 @@ spi_config_t::spi_config_t(edge_t edge):
}
void i2c_iface::write_eeprom(
- boost::uint8_t addr,
- boost::uint8_t offset,
+ boost::uint16_t addr,
+ boost::uint16_t offset,
const byte_vector_t &bytes
){
for (size_t i = 0; i < bytes.size(); i++){
@@ -42,8 +57,8 @@ void i2c_iface::write_eeprom(
}
byte_vector_t i2c_iface::read_eeprom(
- boost::uint8_t addr,
- boost::uint8_t offset,
+ boost::uint16_t addr,
+ boost::uint16_t offset,
size_t num_bytes
){
byte_vector_t bytes;
@@ -55,6 +70,59 @@ byte_vector_t i2c_iface::read_eeprom(
return bytes;
}
+struct eeprom16_impl : i2c_iface
+{
+ eeprom16_impl(i2c_iface* internal)
+ {
+ _internal = internal;
+ }
+ i2c_iface* _internal;
+
+ byte_vector_t read_i2c(
+ boost::uint16_t addr,
+ size_t num_bytes
+ ){
+ return _internal->read_i2c(addr, num_bytes);
+ }
+
+ void write_i2c(
+ boost::uint16_t addr,
+ const byte_vector_t &bytes
+ ){
+ return _internal->write_i2c(addr, bytes);
+ }
+
+ byte_vector_t read_eeprom(
+ boost::uint16_t addr,
+ boost::uint16_t offset,
+ size_t num_bytes
+ ){
+ byte_vector_t cmd = boost::assign::list_of(offset >> 8)(offset & 0xff);
+ this->write_i2c(addr, cmd);
+ return this->read_i2c(addr, num_bytes);
+ }
+
+ void write_eeprom(
+ boost::uint16_t addr,
+ boost::uint16_t offset,
+ const byte_vector_t &bytes
+ ){
+ for (size_t i = 0; i < bytes.size(); i++)
+ {
+ //write a byte at a time, its easy that way
+ boost::uint16_t offset_i = offset+i;
+ byte_vector_t cmd = boost::assign::list_of(offset_i >> 8)(offset_i & 0xff)(bytes[i]);
+ this->write_i2c(addr, cmd);
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10)); //worst case write
+ }
+ }
+};
+
+i2c_iface::sptr i2c_iface::eeprom16(void)
+{
+ return i2c_iface::sptr(new eeprom16_impl(this));
+}
+
boost::uint32_t spi_iface::read_spi(
int which_slave,
const spi_config_t &config,
diff --git a/host/lib/types/wb_iface.cpp b/host/lib/types/wb_iface.cpp
new file mode 100644
index 000000000..6edfdfe2f
--- /dev/null
+++ b/host/lib/types/wb_iface.cpp
@@ -0,0 +1,56 @@
+//
+// Copyright 2013 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/types/wb_iface.hpp>
+#include <uhd/exception.hpp>
+
+using namespace uhd;
+
+wb_iface::~wb_iface(void)
+{
+ //NOP
+}
+
+void wb_iface::poke64(const wb_iface::wb_addr_type, const boost::uint64_t)
+{
+ throw uhd::not_implemented_error("poke64 not implemented");
+}
+
+boost::uint64_t wb_iface::peek64(const wb_iface::wb_addr_type)
+{
+ throw uhd::not_implemented_error("peek64 not implemented");
+}
+
+void wb_iface::poke32(const wb_iface::wb_addr_type, const boost::uint32_t)
+{
+ throw uhd::not_implemented_error("poke32 not implemented");
+}
+
+boost::uint32_t wb_iface::peek32(const wb_iface::wb_addr_type)
+{
+ throw uhd::not_implemented_error("peek32 not implemented");
+}
+
+void wb_iface::poke16(const wb_iface::wb_addr_type, const boost::uint16_t)
+{
+ throw uhd::not_implemented_error("poke16 not implemented");
+}
+
+boost::uint16_t wb_iface::peek16(const wb_iface::wb_addr_type)
+{
+ throw uhd::not_implemented_error("peek16 not implemented");
+}
diff --git a/host/lib/usrp/CMakeLists.txt b/host/lib/usrp/CMakeLists.txt
index 8ae379f73..f8c817df5 100644
--- a/host/lib/usrp/CMakeLists.txt
+++ b/host/lib/usrp/CMakeLists.txt
@@ -1,5 +1,5 @@
#
-# Copyright 2010-2011 Ettus Research LLC
+# Copyright 2010-2013 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
@@ -37,3 +37,4 @@ INCLUDE_SUBDIRECTORY(usrp1)
INCLUDE_SUBDIRECTORY(usrp2)
INCLUDE_SUBDIRECTORY(b100)
INCLUDE_SUBDIRECTORY(e100)
+INCLUDE_SUBDIRECTORY(b200)
diff --git a/host/lib/usrp/b100/CMakeLists.txt b/host/lib/usrp/b100/CMakeLists.txt
index d2c33b512..bcc5ac74d 100644
--- a/host/lib/usrp/b100/CMakeLists.txt
+++ b/host/lib/usrp/b100/CMakeLists.txt
@@ -1,5 +1,5 @@
#
-# Copyright 2011-2012 Ettus Research LLC
+# Copyright 2011-2013 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
@@ -31,5 +31,6 @@ IF(ENABLE_B100)
${CMAKE_CURRENT_SOURCE_DIR}/codec_ctrl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/dboard_iface.cpp
${CMAKE_CURRENT_SOURCE_DIR}/io_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/usb_zero_copy_wrapper.cpp
)
ENDIF(ENABLE_B100)
diff --git a/host/lib/usrp/b100/b100_impl.cpp b/host/lib/usrp/b100/b100_impl.cpp
index 138d328aa..305ba42a7 100644
--- a/host/lib/usrp/b100/b100_impl.cpp
+++ b/host/lib/usrp/b100/b100_impl.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2012 Ettus Research LLC
+// Copyright 2012-2013 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
@@ -188,12 +188,27 @@ b100_impl::b100_impl(const device_addr_t &device_addr){
ctrl_xport_args["send_frame_size"] = "512";
ctrl_xport_args["num_send_frames"] = "16";
- _ctrl_transport = usb_zero_copy::make(
- handle,
- 4, 8, //interface, endpoint
- 3, 4, //interface, endpoint
- ctrl_xport_args
- );
+ //try to open ctrl transport
+ //this could fail with libusb_submit_transfer under some conditions
+ try{
+ _ctrl_transport = usb_zero_copy::make(
+ handle,
+ 4, 8, //interface, endpoint
+ 3, 4, //interface, endpoint
+ ctrl_xport_args
+ );
+ }
+ //try reset once in the case of failure
+ catch(const uhd::exception &ex){
+ if (initialization_count > 1) throw;
+ UHD_MSG(warning) <<
+ "The control endpoint was left in a bad state.\n"
+ "Attempting endpoint re-enumeration...\n" << ex.what() << std::endl;
+ _fifo_ctrl.reset();
+ _ctrl_transport.reset();
+ _fx2_ctrl->usrp_fx2_reset();
+ goto b100_impl_constructor_begin;
+ }
this->enable_gpif(true);
////////////////////////////////////////////////////////////////////
@@ -245,7 +260,7 @@ b100_impl::b100_impl(const device_addr_t &device_addr){
const size_t rx_lut_size = size_t(data_xport_args.cast<double>("recv_frame_size", 0.0));
_fifo_ctrl->poke32(TOREG(SR_PADDER+0), rx_lut_size/sizeof(boost::uint32_t));
- _data_transport = usb_zero_copy::make_wrapper(
+ _data_transport = usb_zero_copy_make_wrapper(
usb_zero_copy::make(
handle, // identifier
2, 6, // IN interface, endpoint
@@ -474,7 +489,7 @@ b100_impl::b100_impl(const device_addr_t &device_addr){
}
//initialize io handling
- _recv_demuxer = recv_packet_demuxer::make(_data_transport, _rx_dsps.size(), B100_RX_SID_BASE);
+ _recv_demuxer.reset(new recv_packet_demuxer_3000(_data_transport));
//allocate streamer weak ptrs containers
_rx_streamers.resize(_rx_dsps.size());
@@ -518,6 +533,7 @@ void b100_impl::check_fw_compat(void){
"%s"
) % B100_FW_COMPAT_NUM % fw_compat_num % print_images_error()));
}
+ _tree->create<std::string>("/mboards/0/fw_version").set(str(boost::format("%u.0") % fw_compat_num));
}
void b100_impl::check_fpga_compat(void){
diff --git a/host/lib/usrp/b100/b100_impl.hpp b/host/lib/usrp/b100/b100_impl.hpp
index 68d7043a1..ab83c80a3 100644
--- a/host/lib/usrp/b100/b100_impl.hpp
+++ b/host/lib/usrp/b100/b100_impl.hpp
@@ -1,5 +1,5 @@
//
-// Copyright 2011-2012 Ettus Research LLC
+// Copyright 2011-2013 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
@@ -29,7 +29,7 @@
#include "time64_core_200.hpp"
#include "fifo_ctrl_excelsior.hpp"
#include "user_settings_core_200.hpp"
-#include "recv_packet_demuxer.hpp"
+#include "recv_packet_demuxer_3000.hpp"
#include <uhd/device.hpp>
#include <uhd/property_tree.hpp>
#include <uhd/types/dict.hpp>
@@ -67,13 +67,31 @@ static const std::string B100_EEPROM_MAP_KEY = "B100";
//! Make a b100 dboard interface
uhd::usrp::dboard_iface::sptr make_b100_dboard_iface(
- wb_iface::sptr wb_iface,
+ uhd::wb_iface::sptr wb_iface,
uhd::i2c_iface::sptr i2c_iface,
uhd::spi_iface::sptr spi_iface,
b100_clock_ctrl::sptr clock,
b100_codec_ctrl::sptr codec
);
+/*!
+ * Make a wrapper around a zero copy implementation.
+ * The wrapper performs the following functions:
+ * - Pad commits to the frame boundary
+ * - Extract multiple packets on recv
+ *
+ * When enable multiple receive packets is set to true,
+ * the implementation inspects the vita length on transfers,
+ * and may split a single transfer into multiple managed buffers.
+ *
+ * \param usb_zc a usb zero copy interface object
+ * \param usb_frame_boundary bytes per frame
+ * \return a new zero copy wrapper object
+ */
+uhd::transport::zero_copy_if::sptr usb_zero_copy_make_wrapper(
+ uhd::transport::zero_copy_if::sptr usb_zc, size_t usb_frame_boundary = 512
+);
+
//! Implementation guts
class b100_impl : public uhd::device {
public:
@@ -105,7 +123,7 @@ private:
//transports
uhd::transport::zero_copy_if::sptr _ctrl_transport;
uhd::transport::zero_copy_if::sptr _data_transport;
- uhd::usrp::recv_packet_demuxer::sptr _recv_demuxer;
+ boost::shared_ptr<uhd::usrp::recv_packet_demuxer_3000> _recv_demuxer;
//dboard stuff
uhd::usrp::dboard_manager::sptr _dboard_manager;
diff --git a/host/lib/usrp/b100/dboard_iface.cpp b/host/lib/usrp/b100/dboard_iface.cpp
index 25604da72..efbba1c4c 100644
--- a/host/lib/usrp/b100/dboard_iface.cpp
+++ b/host/lib/usrp/b100/dboard_iface.cpp
@@ -73,8 +73,8 @@ public:
void set_gpio_debug(unit_t, int);
boost::uint16_t read_gpio(unit_t);
- void write_i2c(boost::uint8_t, const byte_vector_t &);
- byte_vector_t read_i2c(boost::uint8_t, size_t);
+ void write_i2c(boost::uint16_t, const byte_vector_t &);
+ byte_vector_t read_i2c(boost::uint16_t, size_t);
void write_spi(
unit_t unit,
@@ -219,11 +219,11 @@ boost::uint32_t b100_dboard_iface::read_write_spi(
/***********************************************************************
* I2C
**********************************************************************/
-void b100_dboard_iface::write_i2c(boost::uint8_t addr, const byte_vector_t &bytes){
+void b100_dboard_iface::write_i2c(boost::uint16_t addr, const byte_vector_t &bytes){
return _i2c_iface->write_i2c(addr, bytes);
}
-byte_vector_t b100_dboard_iface::read_i2c(boost::uint8_t addr, size_t num_bytes){
+byte_vector_t b100_dboard_iface::read_i2c(boost::uint16_t addr, size_t num_bytes){
return _i2c_iface->read_i2c(addr, num_bytes);
}
diff --git a/host/lib/usrp/b100/io_impl.cpp b/host/lib/usrp/b100/io_impl.cpp
index 723756dcc..86edb4ed6 100644
--- a/host/lib/usrp/b100/io_impl.cpp
+++ b/host/lib/usrp/b100/io_impl.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2011-2012 Ettus Research LLC
+// Copyright 2011-2013 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
@@ -158,12 +158,15 @@ rx_streamer::sptr b100_impl::get_rx_stream(const uhd::stream_args_t &args_){
const size_t dsp = args.channels[chan_i];
_rx_dsps[dsp]->set_nsamps_per_packet(spp); //seems to be a good place to set this
_rx_dsps[dsp]->setup(args);
+ _recv_demuxer->realloc_sid(B100_RX_SID_BASE + dsp);
my_streamer->set_xport_chan_get_buff(chan_i, boost::bind(
- &recv_packet_demuxer::get_recv_buff, _recv_demuxer, dsp, _1
+ &recv_packet_demuxer_3000::get_recv_buff, _recv_demuxer, B100_RX_SID_BASE + dsp, _1
), true /*flush*/);
my_streamer->set_overflow_handler(chan_i, boost::bind(
&rx_dsp_core_200::handle_overflow, _rx_dsps[dsp]
));
+ my_streamer->set_issue_stream_cmd(chan_i, boost::bind(
+ &rx_dsp_core_200::issue_stream_command, _rx_dsps[dsp], _1));
_rx_streamers[dsp] = my_streamer; //store weak pointer
}
@@ -217,6 +220,7 @@ tx_streamer::sptr b100_impl::get_tx_stream(const uhd::stream_args_t &args_){
my_streamer->set_xport_chan_get_buff(chan_i, boost::bind(
&zero_copy_if::get_send_buff, _data_transport, _1
));
+ my_streamer->set_async_receiver(boost::bind(&fifo_ctrl_excelsior::pop_async_msg, _fifo_ctrl, _1, _2));
_tx_streamers[dsp] = my_streamer; //store weak pointer
}
diff --git a/host/lib/transport/usb_zero_copy_wrapper.cpp b/host/lib/usrp/b100/usb_zero_copy_wrapper.cpp
index d04244ca9..2096e4ef4 100644
--- a/host/lib/transport/usb_zero_copy_wrapper.cpp
+++ b/host/lib/usrp/b100/usb_zero_copy_wrapper.cpp
@@ -81,7 +81,7 @@ private:
**********************************************************************/
class usb_zero_copy_wrapper_msb : public managed_send_buffer{
public:
- usb_zero_copy_wrapper_msb(const usb_zero_copy::sptr internal, const size_t fragmentation_size):
+ usb_zero_copy_wrapper_msb(const zero_copy_if::sptr internal, const size_t fragmentation_size):
_internal(internal), _fragmentation_size(fragmentation_size)
{
_ok_to_auto_flush = false;
@@ -131,7 +131,7 @@ public:
}
private:
- usb_zero_copy::sptr _internal;
+ zero_copy_if::sptr _internal;
const size_t _fragmentation_size;
managed_send_buffer::sptr _last_send_buff;
size_t _bytes_in_buffer;
@@ -164,7 +164,7 @@ private:
**********************************************************************/
class usb_zero_copy_wrapper : public usb_zero_copy{
public:
- usb_zero_copy_wrapper(sptr usb_zc, const size_t frame_boundary):
+ usb_zero_copy_wrapper(zero_copy_if::sptr usb_zc, const size_t frame_boundary):
_internal_zc(usb_zc),
_frame_boundary(frame_boundary),
_next_recv_buff_index(0)
@@ -176,6 +176,14 @@ public:
}
managed_recv_buffer::sptr get_recv_buff(double timeout){
+ //lazy flush mechanism - negative timeout
+ if (timeout < 0.0)
+ {
+ _last_recv_buff.reset();
+ while (_internal_zc->get_recv_buff()){}
+ return managed_recv_buffer::sptr();
+ }
+
//attempt to get a managed recv buffer
if (not _last_recv_buff){
_last_recv_buff = _internal_zc->get_recv_buff(timeout);
@@ -210,7 +218,7 @@ public:
}
private:
- sptr _internal_zc;
+ zero_copy_if::sptr _internal_zc;
size_t _frame_boundary;
std::vector<boost::shared_ptr<usb_zero_copy_wrapper_mrb> > _mrb_pool;
boost::shared_ptr<usb_zero_copy_wrapper_msb> _the_only_msb;
@@ -224,8 +232,8 @@ private:
/***********************************************************************
* USB zero copy wrapper factory function
**********************************************************************/
-usb_zero_copy::sptr usb_zero_copy::make_wrapper(
- sptr usb_zc, size_t usb_frame_boundary
+zero_copy_if::sptr usb_zero_copy_make_wrapper(
+ zero_copy_if::sptr usb_zc, size_t usb_frame_boundary
){
- return sptr(new usb_zero_copy_wrapper(usb_zc, usb_frame_boundary));
+ return zero_copy_if::sptr(new usb_zero_copy_wrapper(usb_zc, usb_frame_boundary));
}
diff --git a/host/lib/usrp/b200/CMakeLists.txt b/host/lib/usrp/b200/CMakeLists.txt
new file mode 100644
index 000000000..3d8aad052
--- /dev/null
+++ b/host/lib/usrp/b200/CMakeLists.txt
@@ -0,0 +1,34 @@
+#
+# Copyright 2012-2013 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/>.
+#
+
+########################################################################
+# This file included, use CMake directory variables
+########################################################################
+
+########################################################################
+# Conditionally configure the B100 support
+########################################################################
+LIBUHD_REGISTER_COMPONENT("B200" ENABLE_B200 ON "ENABLE_LIBUHD;ENABLE_USB" OFF)
+
+IF(ENABLE_B200)
+ LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/b200_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/b200_iface.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/b200_io_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/b200_uart.cpp
+ )
+ENDIF(ENABLE_B200)
diff --git a/host/lib/usrp/b200/b200_iface.cpp b/host/lib/usrp/b200/b200_iface.cpp
new file mode 100644
index 000000000..5c512c1d9
--- /dev/null
+++ b/host/lib/usrp/b200/b200_iface.cpp
@@ -0,0 +1,608 @@
+//
+// Copyright 2012-2013 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 "b200_iface.hpp"
+
+#include <uhd/utils/msg.hpp>
+#include <uhd/exception.hpp>
+#include <boost/functional/hash.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/format.hpp>
+#include <fstream>
+#include <string>
+#include <vector>
+#include <cstring>
+#include <iomanip>
+#include <libusb.h>
+
+using namespace uhd;
+using namespace uhd::transport;
+
+static const bool load_img_msg = true;
+
+const static boost::uint8_t FX3_FIRMWARE_LOAD = 0xA0;
+const static boost::uint8_t VRT_VENDOR_OUT = (LIBUSB_REQUEST_TYPE_VENDOR
+ | LIBUSB_ENDPOINT_OUT);
+const static boost::uint8_t VRT_VENDOR_IN = (LIBUSB_REQUEST_TYPE_VENDOR
+ | LIBUSB_ENDPOINT_IN);
+const static boost::uint8_t B200_VREQ_FPGA_START = 0x02;
+const static boost::uint8_t B200_VREQ_FPGA_DATA = 0x12;
+const static boost::uint8_t B200_VREQ_GET_COMPAT = 0x15;
+const static boost::uint8_t B200_VREQ_SET_FPGA_HASH = 0x1C;
+const static boost::uint8_t B200_VREQ_GET_FPGA_HASH = 0x1D;
+const static boost::uint8_t B200_VREQ_SET_FW_HASH = 0x1E;
+const static boost::uint8_t B200_VREQ_GET_FW_HASH = 0x1F;
+const static boost::uint8_t B200_VREQ_LOOP = 0x22;
+const static boost::uint8_t B200_VREQ_SPI_WRITE = 0x32;
+const static boost::uint8_t B200_VREQ_SPI_READ = 0x42;
+const static boost::uint8_t B200_VREQ_FPGA_CONFIG = 0x55;
+const static boost::uint8_t B200_VREQ_FPGA_RESET = 0x62;
+const static boost::uint8_t B200_VREQ_GPIF_RESET = 0x72;
+const static boost::uint8_t B200_VREQ_GET_USB = 0x80;
+const static boost::uint8_t B200_VREQ_GET_STATUS = 0x83;
+const static boost::uint8_t B200_VREQ_AD9361_CTRL_WRITE = 0x90;
+const static boost::uint8_t B200_VREQ_AD9361_CTRL_READ = 0x91;
+const static boost::uint8_t B200_VREQ_FX3_RESET = 0x99;
+const static boost::uint8_t B200_VREQ_EEPROM_WRITE = 0xBA;
+const static boost::uint8_t B200_VREQ_EEPROM_READ = 0xBB;
+
+const static boost::uint8_t FX3_STATE_FPGA_READY = 0x01;
+const static boost::uint8_t FX3_STATE_CONFIGURING_FPGA = 0x02;
+const static boost::uint8_t FX3_STATE_BUSY = 0x03;
+const static boost::uint8_t FX3_STATE_RUNNING = 0x04;
+const static boost::uint8_t FX3_STATE_UNCONFIGURED = 0x05;
+const static boost::uint8_t FX3_STATE_ERROR = 0x06;
+
+const static int VREQ_MAX_SIZE_USB2 = 64;
+const static int VREQ_MAX_SIZE_USB3 = 512;
+const static int VREQ_DEFAULT_SIZE = VREQ_MAX_SIZE_USB2;
+const static int VREQ_MAX_SIZE = VREQ_MAX_SIZE_USB3;
+
+typedef boost::uint32_t hash_type;
+
+
+/***********************************************************************
+ * Helper Functions
+ **********************************************************************/
+/*!
+ * Create a file hash
+ * The hash will be used to identify the loaded firmware and fpga image
+ * \param filename file used to generate hash value
+ * \return hash value in a size_t type
+ */
+static hash_type generate_hash(const char *filename)
+{
+ std::ifstream file(filename);
+ if (not file){
+ throw uhd::io_error(std::string("cannot open input file ") + filename);
+ }
+
+ size_t hash = 0;
+
+ char ch;
+ long long count = 0;
+ while (file.get(ch)) {
+ count++;
+ boost::hash_combine(hash, ch);
+ }
+
+ if (count == 0){
+ throw uhd::io_error(std::string("empty input file ") + filename);
+ }
+
+ if (not file.eof()){
+ throw uhd::io_error(std::string("file error ") + filename);
+ }
+
+ file.close();
+ return hash_type(hash);
+}
+
+
+/*!
+ * Verify checksum of a Intel HEX record
+ * \param record a line from an Intel HEX file
+ * \return true if record is valid, false otherwise
+ */
+bool checksum(std::string *record) {
+
+ size_t len = record->length();
+ unsigned int i;
+ unsigned char sum = 0;
+ unsigned int val;
+
+ for (i = 1; i < len; i += 2) {
+ std::istringstream(record->substr(i, 2)) >> std::hex >> val;
+ sum += val;
+ }
+
+ if (sum == 0)
+ return true;
+ else
+ return false;
+}
+
+
+/*!
+ * Parse Intel HEX record
+ *
+ * \param record a line from an Intel HEX file
+ * \param len output length of record
+ * \param addr output address
+ * \param type output type
+ * \param data output data
+ * \return true if record is sucessfully read, false on error
+ */
+bool parse_record(std::string *record, boost::uint16_t &len, \
+ boost::uint16_t &addr, boost::uint16_t &type, unsigned char* data) {
+
+ unsigned int i;
+ std::string _data;
+ unsigned int val;
+
+ if (record->substr(0, 1) != ":")
+ return false;
+
+ std::istringstream(record->substr(1, 2)) >> std::hex >> len;
+ std::istringstream(record->substr(3, 4)) >> std::hex >> addr;
+ std::istringstream(record->substr(7, 2)) >> std::hex >> type;
+
+ for (i = 0; i < len; i++) {
+ std::istringstream(record->substr(9 + 2 * i, 2)) >> std::hex >> val;
+ data[i] = (unsigned char) val;
+ }
+
+ return true;
+}
+
+
+/***********************************************************************
+ * The implementation class
+ **********************************************************************/
+class b200_iface_impl : public b200_iface{
+public:
+
+ b200_iface_impl(usb_control::sptr usb_ctrl):
+ _usb_ctrl(usb_ctrl)
+ {
+ //NOP
+ }
+
+
+ int fx3_control_write(boost::uint8_t request,
+ boost::uint16_t value,
+ boost::uint16_t index,
+ unsigned char *buff,
+ boost::uint16_t length,
+ boost::int32_t timeout = 0)
+ {
+ return _usb_ctrl->submit(VRT_VENDOR_OUT, // bmReqeustType
+ request, // bRequest
+ value, // wValue
+ index, // wIndex
+ buff, // data
+ length, // wLength
+ timeout); // timeout
+ }
+
+
+ int fx3_control_read(boost::uint8_t request,
+ boost::uint16_t value,
+ boost::uint16_t index,
+ unsigned char *buff,
+ boost::uint16_t length,
+ boost::int32_t timeout = 0)
+ {
+ return _usb_ctrl->submit(VRT_VENDOR_IN, // bmReqeustType
+ request, // bRequest
+ value, // wValue
+ index, // wIndex
+ buff, // data
+ length, // wLength
+ timeout); // timeout
+ }
+
+
+ void write_i2c(boost::uint16_t addr, const byte_vector_t &bytes)
+ {
+ throw uhd::not_implemented_error("b200 write i2c");
+ }
+
+
+ byte_vector_t read_i2c(boost::uint16_t addr, size_t num_bytes)
+ {
+ throw uhd::not_implemented_error("b200 read i2c");
+ }
+
+ void write_eeprom(boost::uint16_t addr, boost::uint16_t offset,
+ const byte_vector_t &bytes)
+ {
+ fx3_control_write(B200_VREQ_EEPROM_WRITE,
+ 0, offset | (boost::uint16_t(addr) << 8),
+ (unsigned char *) &bytes[0],
+ bytes.size());
+ }
+
+ byte_vector_t read_eeprom(
+ boost::uint16_t addr,
+ boost::uint16_t offset,
+ size_t num_bytes
+ ){
+ byte_vector_t recv_bytes(num_bytes);
+ int bytes_read = fx3_control_read(B200_VREQ_EEPROM_READ,
+ 0, offset | (boost::uint16_t(addr) << 8),
+ (unsigned char*) &recv_bytes[0],
+ num_bytes);
+ if (bytes_read != num_bytes)
+ throw uhd::io_error("Failed to read data from EEPROM.");
+ return recv_bytes;
+ }
+
+ void transact_spi(
+ unsigned char *tx_data,
+ size_t num_tx_bits,
+ unsigned char *rx_data,
+ size_t num_rx_bits
+ ){
+ int ret = 0;
+ boost::uint16_t tx_length = num_tx_bits / 8;
+
+ if(tx_data[0] & 0x80) {
+ ret = fx3_control_write(B200_VREQ_SPI_WRITE, 0x00, \
+ 0x00, tx_data, tx_length);
+ } else {
+ ret = fx3_control_write(B200_VREQ_SPI_READ, 0x00, \
+ 0x00, tx_data, tx_length);
+ }
+
+ if(ret < 0) {
+ throw uhd::io_error("transact_spi: fx3_control_write failed!");
+ }
+
+
+ if(num_rx_bits) {
+ boost::uint16_t total_length = num_rx_bits / 8;
+
+ ret = fx3_control_read(B200_VREQ_LOOP, 0x00, \
+ 0x00, rx_data, total_length);
+
+ if(ret < 0) {
+ throw uhd::io_error("transact_spi: readback failed!");
+ }
+ }
+ }
+
+ void ad9361_transact(const unsigned char in_buff[64], unsigned char out_buff[64]) {
+ fx3_control_write(B200_VREQ_AD9361_CTRL_WRITE, 0x00, 0x00, (unsigned char *)in_buff, 64);
+ int ret = 0;
+ for (size_t i = 0; i < 30; i++)
+ {
+ ret = fx3_control_read(B200_VREQ_AD9361_CTRL_READ, 0x00, 0x00, out_buff, 64, 1000);
+ if (ret == 64) return;
+ }
+ throw uhd::io_error(str(boost::format("ad9361_transact failed with usb error: %d") % ret));
+ }
+
+
+ void load_firmware(const std::string filestring, bool force = false)
+ {
+ const char *filename = filestring.c_str();
+
+ /* Fields used in each USB control transfer. */
+ boost::uint16_t len = 0;
+ boost::uint16_t type = 0;
+ boost::uint16_t lower_address_bits = 0x0000;
+ unsigned char data[512];
+
+ /* Can be set by the Intel HEX record 0x04, used for all 0x00 records
+ * thereafter. Note this field takes the place of the 'index' parameter in
+ * libusb calls, and is necessary for FX3's 32-bit addressing. */
+ boost::uint16_t upper_address_bits = 0x0000;
+
+ std::ifstream file;
+ file.open(filename, std::ifstream::in);
+
+ if(!file.good()) {
+ throw uhd::io_error("fx3_load_firmware: cannot open firmware input file");
+ }
+
+ if (load_img_msg) UHD_MSG(status) << "Loading firmware image: " \
+ << filestring << "..." << std::flush;
+
+ while (!file.eof()) {
+ boost::int32_t ret = 0;
+ std::string record;
+ file >> record;
+
+ /* Check for valid Intel HEX record. */
+ if (!checksum(&record) || !parse_record(&record, len, \
+ lower_address_bits, type, data)) {
+ throw uhd::io_error("fx3_load_firmware: bad intel hex record checksum");
+ }
+
+ /* Type 0x00: Data. */
+ if (type == 0x00) {
+ ret = fx3_control_write(FX3_FIRMWARE_LOAD, \
+ lower_address_bits, upper_address_bits, data, len);
+
+ if (ret < 0) {
+ throw uhd::io_error("usrp_load_firmware: usrp_control_write failed");
+ }
+ }
+
+ /* Type 0x01: EOF. */
+ else if (type == 0x01) {
+ if (lower_address_bits != 0x0000 || len != 0 ) {
+ throw uhd::io_error("fx3_load_firmware: For EOF record, address must be 0, length must be 0.");
+ }
+
+ //TODO
+ //usrp_set_firmware_hash(hash); //set hash before reset
+
+ /* Successful termination! */
+ file.close();
+
+ /* Let the system settle. */
+ boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
+ return;
+ }
+
+ /* Type 0x04: Extended Linear Address Record. */
+ else if (type == 0x04) {
+ if (lower_address_bits != 0x0000 || len != 2 ) {
+ throw uhd::io_error("fx3_load_firmware: For ELA record, address must be 0, length must be 2.");
+ }
+
+ upper_address_bits = ((boost::uint16_t)((data[0] & 0x00FF) << 8))\
+ + ((boost::uint16_t)(data[1] & 0x00FF));
+ }
+
+ /* Type 0x05: Start Linear Address Record. */
+ else if (type == 0x05) {
+ if (lower_address_bits != 0x0000 || len != 4 ) {
+ throw uhd::io_error("fx3_load_firmware: For SLA record, address must be 0, length must be 4.");
+ }
+
+ /* The firmware load is complete. We now need to tell the CPU
+ * to jump to an execution address start point, now contained within
+ * the data field. Parse these address bits out, and then push the
+ * instruction. */
+ upper_address_bits = ((boost::uint16_t)((data[0] & 0x00FF) << 8))\
+ + ((boost::uint16_t)(data[1] & 0x00FF));
+ lower_address_bits = ((boost::uint16_t)((data[2] & 0x00FF) << 8))\
+ + ((boost::uint16_t)(data[3] & 0x00FF));
+
+ fx3_control_write(FX3_FIRMWARE_LOAD, lower_address_bits, \
+ upper_address_bits, 0, 0);
+
+ if (load_img_msg) UHD_MSG(status) << " done" << std::endl;
+ }
+
+ /* If we receive an unknown record type, error out. */
+ else {
+ throw uhd::io_error("fx3_load_firmware: unsupported record type.");
+ }
+ }
+
+ /* There was no valid EOF. */
+ throw uhd::io_error("fx3_load_firmware: No EOF record found.");
+ }
+
+
+ void reset_fx3(void) {
+ unsigned char data[4];
+ memset(data, 0x00, sizeof(data));
+
+ fx3_control_write(B200_VREQ_FX3_RESET, 0x00, 0x00, data, 4);
+ }
+
+ void reset_gpif(void) {
+ unsigned char data[4];
+ memset(data, 0x00, sizeof(data));
+
+ fx3_control_write(B200_VREQ_GPIF_RESET, 0x00, 0x00, data, 4);
+ }
+
+ void set_fpga_reset_pin(const bool reset)
+ {
+ unsigned char data[4];
+ memset(data, (reset)? 0xFF : 0x00, sizeof(data));
+
+ UHD_THROW_INVALID_CODE_PATH();
+
+ fx3_control_write(B200_VREQ_FPGA_RESET, 0x00, 0x00, data, 4);
+ }
+
+ boost::uint8_t get_usb_speed(void) {
+
+ unsigned char rx_data[1];
+
+ fx3_control_read(B200_VREQ_GET_USB, 0x00, 0x00, rx_data, 1);
+
+ return boost::lexical_cast<boost::uint8_t>(rx_data[0]);
+ }
+
+
+ boost::uint8_t get_fx3_status(void) {
+
+ unsigned char rx_data[1];
+
+ fx3_control_read(B200_VREQ_GET_STATUS, 0x00, 0x00, &rx_data[0], 1);
+
+ return boost::lexical_cast<boost::uint8_t>(rx_data[0]);
+ }
+
+ boost::uint16_t get_compat_num(void) {
+
+ unsigned char rx_data[2];
+
+ fx3_control_read(B200_VREQ_GET_COMPAT , 0x00, 0x00, rx_data, 2);
+
+ boost::uint16_t compat = 0x0000;
+ compat |= (((uint16_t) rx_data[0]) << 8);
+ compat |= (rx_data[1] & 0x00FF);
+
+ return compat;
+ }
+
+ void usrp_get_firmware_hash(hash_type &hash) {
+ fx3_control_read(B200_VREQ_GET_FW_HASH, 0x00, 0x00,
+ (unsigned char*) &hash, 4, 500);
+ }
+
+ void usrp_set_firmware_hash(hash_type hash) {
+ fx3_control_write(B200_VREQ_SET_FW_HASH, 0x00, 0x00,
+ (unsigned char*) &hash, 4);
+ }
+
+ void usrp_get_fpga_hash(hash_type &hash) {
+ fx3_control_read(B200_VREQ_GET_FPGA_HASH, 0x00, 0x00,
+ (unsigned char*) &hash, 4, 500);
+ }
+
+ void usrp_set_fpga_hash(hash_type hash) {
+ fx3_control_write(B200_VREQ_SET_FPGA_HASH, 0x00, 0x00,
+ (unsigned char*) &hash, 4);
+ }
+
+ boost::uint32_t load_fpga(const std::string filestring) {
+
+ boost::uint8_t fx3_state = 0;
+ boost::uint32_t wait_count;
+
+ const char *filename = filestring.c_str();
+
+ hash_type hash = generate_hash(filename);
+ hash_type loaded_hash; usrp_get_fpga_hash(loaded_hash);
+ if (hash == loaded_hash) return 0;
+
+ int transfer_size = VREQ_DEFAULT_SIZE;
+ int current_usb_speed = get_usb_speed();
+ if (current_usb_speed == 3)
+ transfer_size = VREQ_MAX_SIZE_USB3;
+ else if (current_usb_speed != 2)
+ throw uhd::io_error("load_fpga: get_usb_speed returned invalid USB speed (not 2 or 3)");
+
+ UHD_ASSERT_THROW(transfer_size <= VREQ_MAX_SIZE);
+
+ unsigned char out_buff[VREQ_MAX_SIZE];
+ memset(out_buff, 0x00, sizeof(out_buff));
+ fx3_control_write(B200_VREQ_FPGA_CONFIG, 0, 0, out_buff, 1, 1000);
+
+ size_t file_size = 0;
+ {
+ std::ifstream file(filename, std::ios::in | std::ios::binary | std::ios::ate);
+ file_size = file.tellg();
+ }
+
+ std::ifstream file;
+ file.open(filename, std::ios::in | std::ios::binary);
+
+ if(!file.good()) {
+ throw uhd::io_error("load_fpga: cannot open FPGA input file.");
+ }
+
+ wait_count = 0;
+ do {
+ fx3_state = get_fx3_status();
+
+ if((wait_count >= 500) || (fx3_state == FX3_STATE_ERROR)) {
+ return fx3_state;
+ }
+
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+
+ wait_count++;
+ } while(fx3_state != FX3_STATE_FPGA_READY);
+
+ if (load_img_msg) UHD_MSG(status) << "Loading FPGA image: " \
+ << filestring << "..." << std::flush;
+
+ fx3_control_write(B200_VREQ_FPGA_START, 0, 0, out_buff, 1, 1000);
+
+ wait_count = 0;
+ do {
+ fx3_state = get_fx3_status();
+
+ if((wait_count >= 1000) || (fx3_state == FX3_STATE_ERROR)) {
+ return fx3_state;
+ }
+
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+
+ wait_count++;
+ } while(fx3_state != FX3_STATE_CONFIGURING_FPGA);
+
+ size_t bytes_sent = 0;
+ while(!file.eof()) {
+ file.read((char *) out_buff, transfer_size);
+ const std::streamsize n = file.gcount();
+ if(n == 0) continue;
+
+ boost::uint16_t transfer_count = boost::uint16_t(n);
+
+ /* Send the data to the device. */
+ fx3_control_write(B200_VREQ_FPGA_DATA, 0, 0, out_buff, transfer_count, 5000);
+
+ if (load_img_msg)
+ {
+ if (bytes_sent == 0) UHD_MSG(status) << " 0%" << std::flush;
+ const size_t percent_before = size_t((bytes_sent*100)/file_size);
+ bytes_sent += transfer_count;
+ const size_t percent_after = size_t((bytes_sent*100)/file_size);
+ if (percent_before/10 != percent_after/10)
+ {
+ UHD_MSG(status) << "\b\b\b\b" << std::setw(3) << percent_after << "%" << std::flush;
+ }
+ }
+ }
+
+ file.close();
+
+ wait_count = 0;
+ do {
+ fx3_state = get_fx3_status();
+
+ if((wait_count >= 500) || (fx3_state == FX3_STATE_ERROR)) {
+ return fx3_state;
+ }
+
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+
+ wait_count++;
+ } while(fx3_state != FX3_STATE_RUNNING);
+
+ usrp_set_fpga_hash(hash);
+
+ if (load_img_msg) UHD_MSG(status) << "\b\b\b\b done" << std::endl;
+
+ return 0;
+ }
+
+private:
+ usb_control::sptr _usb_ctrl;
+};
+
+/***********************************************************************
+ * Make an instance of the implementation
+ **********************************************************************/
+b200_iface::sptr b200_iface::make(usb_control::sptr usb_ctrl)
+{
+ return sptr(new b200_iface_impl(usb_ctrl));
+}
diff --git a/host/lib/usrp/b200/b200_iface.hpp b/host/lib/usrp/b200/b200_iface.hpp
new file mode 100644
index 000000000..5b6eeede4
--- /dev/null
+++ b/host/lib/usrp/b200/b200_iface.hpp
@@ -0,0 +1,84 @@
+//
+// Copyright 2012-2013 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_B200_IFACE_HPP
+#define INCLUDED_B200_IFACE_HPP
+
+#include <stdint.h>
+#include <uhd/transport/usb_control.hpp>
+#include <uhd/types/serial.hpp> //i2c iface
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+#include "ad9361_ctrl.hpp"
+
+const static boost::uint16_t B200_VENDOR_ID = 0x2500;
+const static boost::uint16_t B200_PRODUCT_ID = 0x0020;
+const static boost::uint16_t FX3_VID = 0x04b4;
+const static boost::uint16_t FX3_DEFAULT_PID = 0x00f3;
+const static boost::uint16_t FX3_REENUM_PID = 0x00f0;
+
+static const std::string B200_FW_FILE_NAME = "usrp_b200_fw.hex";
+static const std::string B200_FPGA_FILE_NAME = "usrp_b200_fpga.bin";
+static const std::string B210_FPGA_FILE_NAME = "usrp_b210_fpga.bin";
+
+class UHD_API b200_iface: boost::noncopyable, public virtual uhd::i2c_iface,
+ public ad9361_ctrl_iface_type {
+public:
+ typedef boost::shared_ptr<b200_iface> sptr;
+
+ /*!
+ * Make a b200 interface object from a control transport
+ * \param usb_ctrl a USB control transport
+ * \return a new b200 interface object
+ */
+ static sptr make(uhd::transport::usb_control::sptr usb_ctrl);
+
+ //! query the device USB speed (2, 3)
+ virtual boost::uint8_t get_usb_speed(void) = 0;
+
+ //! get the current status of the FX3
+ virtual boost::uint8_t get_fx3_status(void) = 0;
+
+ //! get the current status of the FX3
+ virtual boost::uint16_t get_compat_num(void) = 0;
+
+ //! load a firmware image
+ virtual void load_firmware(const std::string filestring, bool force=false) = 0;
+
+ //! reset the FX3
+ virtual void reset_fx3(void) = 0;
+
+ //! reset the GPIF state machine
+ virtual void reset_gpif(void) = 0;
+
+ //! set the FPGA_RESET line
+ virtual void set_fpga_reset_pin(const bool reset) = 0;
+
+ //! load an FPGA image
+ virtual boost::uint32_t load_fpga(const std::string filestring) = 0;
+
+ //! send SPI through the FX3
+ virtual void transact_spi( unsigned char *tx_data, size_t num_tx_bits, \
+ unsigned char *rx_data, size_t num_rx_bits) = 0;
+
+ virtual void write_eeprom(boost::uint16_t addr, boost::uint16_t offset, const uhd::byte_vector_t &bytes) = 0;
+
+ virtual uhd::byte_vector_t read_eeprom(boost::uint16_t addr, boost::uint16_t offset, size_t num_bytes) = 0;
+};
+
+
+#endif /* INCLUDED_B200_IFACE_HPP */
diff --git a/host/lib/usrp/b200/b200_impl.cpp b/host/lib/usrp/b200/b200_impl.cpp
new file mode 100644
index 000000000..132b1198d
--- /dev/null
+++ b/host/lib/usrp/b200/b200_impl.cpp
@@ -0,0 +1,895 @@
+//
+// Copyright 2012-2013 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 "b200_impl.hpp"
+#include "b200_regs.hpp"
+#include <uhd/transport/usb_control.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/utils/static.hpp>
+#include <uhd/utils/images.hpp>
+#include <uhd/utils/safe_call.hpp>
+#include <uhd/usrp/dboard_eeprom.hpp>
+#include <boost/format.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/functional/hash.hpp>
+#include <cstdio>
+#include <ctime>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+
+static const boost::posix_time::milliseconds REENUMERATION_TIMEOUT_MS(3000);
+
+//! mapping of frontend to radio perif index
+static const size_t FE1 = 1;
+static const size_t FE2 = 0;
+
+/***********************************************************************
+ * Discovery
+ **********************************************************************/
+static device_addrs_t b200_find(const device_addr_t &hint)
+{
+ device_addrs_t b200_addrs;
+
+ //return an empty list of addresses when type is set to non-b200
+ if (hint.has_key("type") and hint["type"] != "b200") return b200_addrs;
+
+ //Return an empty list of addresses when an address is specified,
+ //since an address is intended for a different, non-USB, device.
+ if (hint.has_key("addr")) return b200_addrs;
+
+ unsigned int vid, pid;
+
+ if(hint.has_key("vid") && hint.has_key("pid") && hint.has_key("type") && hint["type"] == "b200") {
+ sscanf(hint.get("vid").c_str(), "%x", &vid);
+ sscanf(hint.get("pid").c_str(), "%x", &pid);
+ } else {
+ vid = B200_VENDOR_ID;
+ pid = B200_PRODUCT_ID;
+ }
+
+ // Important note:
+ // The get device list calls are nested inside the for loop.
+ // This allows the usb guts to decontruct when not in use,
+ // so that re-enumeration after fw load can occur successfully.
+ // This requirement is a courtesy of libusb1.0 on windows.
+
+ //find the usrps and load firmware
+ size_t found = 0;
+ BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) {
+ //extract the firmware path for the b200
+ std::string b200_fw_image;
+ try{
+ b200_fw_image = find_image_path(hint.get("fw", B200_FW_FILE_NAME));
+ }
+ catch(...){
+ UHD_MSG(warning) << boost::format(
+ "Could not locate B200 firmware.\n"
+ "Please install the images package. %s\n"
+ ) % print_images_error();
+ return b200_addrs;
+ }
+ UHD_LOG << "the firmware image: " << b200_fw_image << std::endl;
+
+ usb_control::sptr control;
+ try{control = usb_control::make(handle, 0);}
+ catch(const uhd::exception &){continue;} //ignore claimed
+
+ //check if fw was already loaded
+ if (!(handle->firmware_loaded()))
+ {
+ b200_iface::make(control)->load_firmware(b200_fw_image);
+ }
+
+ found++;
+ }
+
+ const boost::system_time timeout_time = boost::get_system_time() + REENUMERATION_TIMEOUT_MS;
+
+ //search for the device until found or timeout
+ while (boost::get_system_time() < timeout_time and b200_addrs.empty() and found != 0)
+ {
+ BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid))
+ {
+ usb_control::sptr control;
+ try{control = usb_control::make(handle, 0);}
+ catch(const uhd::exception &){continue;} //ignore claimed
+
+ b200_iface::sptr iface = b200_iface::make(control);
+ const mboard_eeprom_t mb_eeprom = mboard_eeprom_t(*iface, "B200");
+
+ device_addr_t new_addr;
+ new_addr["type"] = "b200";
+ new_addr["name"] = mb_eeprom["name"];
+ new_addr["serial"] = handle->get_serial();
+ //this is a found b200 when the hint serial and name match or blank
+ if (
+ (not hint.has_key("name") or hint["name"] == new_addr["name"]) and
+ (not hint.has_key("serial") or hint["serial"] == new_addr["serial"])
+ ){
+ b200_addrs.push_back(new_addr);
+ }
+ }
+ }
+
+ return b200_addrs;
+}
+
+/***********************************************************************
+ * Make
+ **********************************************************************/
+static device::sptr b200_make(const device_addr_t &device_addr)
+{
+ return device::sptr(new b200_impl(device_addr));
+}
+
+UHD_STATIC_BLOCK(register_b200_device)
+{
+ device::register_device(&b200_find, &b200_make);
+}
+
+/***********************************************************************
+ * Structors
+ **********************************************************************/
+b200_impl::b200_impl(const device_addr_t &device_addr)
+{
+ _tree = property_tree::make();
+ const fs_path mb_path = "/mboards/0";
+
+ //try to match the given device address with something on the USB bus
+ uint16_t vid = B200_VENDOR_ID;
+ uint16_t pid = B200_PRODUCT_ID;
+ if (device_addr.has_key("vid"))
+ sscanf(device_addr.get("vid").c_str(), "%x", &vid);
+ if (device_addr.has_key("pid"))
+ sscanf(device_addr.get("pid").c_str(), "%x", &pid);
+
+ std::vector<usb_device_handle::sptr> device_list =
+ usb_device_handle::get_device_list(vid, pid);
+
+ //locate the matching handle in the device list
+ usb_device_handle::sptr handle;
+ BOOST_FOREACH(usb_device_handle::sptr dev_handle, device_list) {
+ if (dev_handle->get_serial() == device_addr["serial"]){
+ handle = dev_handle;
+ break;
+ }
+ }
+ UHD_ASSERT_THROW(handle.get() != NULL); //better be found
+
+ //create control objects
+ usb_control::sptr control = usb_control::make(handle, 0);
+ _iface = b200_iface::make(control);
+ this->check_fw_compat(); //check after making
+
+ ////////////////////////////////////////////////////////////////////
+ // setup the mboard eeprom
+ ////////////////////////////////////////////////////////////////////
+ const mboard_eeprom_t mb_eeprom(*_iface, "B200");
+ _tree->create<mboard_eeprom_t>(mb_path / "eeprom")
+ .set(mb_eeprom)
+ .subscribe(boost::bind(&b200_impl::set_mb_eeprom, this, _1));
+
+ ////////////////////////////////////////////////////////////////////
+ // Load the FPGA image, then reset GPIF
+ ////////////////////////////////////////////////////////////////////
+ std::string default_file_name;
+ std::string product_name = "B200?";
+ if (not mb_eeprom["product"].empty())
+ {
+ switch (boost::lexical_cast<boost::uint16_t>(mb_eeprom["product"]))
+ {
+ case 0x0001:
+ case 0x7737:
+ product_name = "B200";
+ default_file_name = B200_FPGA_FILE_NAME;
+ break;
+ case 0x7738:
+ case 0x0002:
+ product_name = "B210";
+ default_file_name = B210_FPGA_FILE_NAME;
+ break;
+ default: UHD_MSG(error) << "B200 unknown product code: " << mb_eeprom["product"] << std::endl;
+ }
+ }
+ if (default_file_name.empty())
+ {
+ UHD_ASSERT_THROW(device_addr.has_key("fpga"));
+ }
+
+ //extract the FPGA path for the B200
+ std::string b200_fpga_image = find_image_path(
+ device_addr.has_key("fpga")? device_addr["fpga"] : default_file_name
+ );
+
+ boost::uint32_t status = _iface->load_fpga(b200_fpga_image);
+
+ if(status != 0) {
+ throw uhd::runtime_error(str(boost::format("fx3 is in state %1%") % status));
+ }
+
+ _iface->reset_gpif();
+
+ ////////////////////////////////////////////////////////////////////
+ // Create control transport
+ ////////////////////////////////////////////////////////////////////
+ boost::uint8_t usb_speed = _iface->get_usb_speed();
+ UHD_MSG(status) << "Operating over USB " << (int) usb_speed << "." << std::endl;
+ const std::string min_frame_size = (usb_speed == 3) ? "1024" : "512";
+
+ device_addr_t ctrl_xport_args;
+ ctrl_xport_args["recv_frame_size"] = min_frame_size;
+ ctrl_xport_args["num_recv_frames"] = "16";
+ ctrl_xport_args["send_frame_size"] = min_frame_size;
+ ctrl_xport_args["num_send_frames"] = "16";
+
+ _ctrl_transport = usb_zero_copy::make(
+ handle,
+ 4, 8, //interface, endpoint
+ 3, 4, //interface, endpoint
+ ctrl_xport_args
+ );
+ while (_ctrl_transport->get_recv_buff(0.0)){} //flush ctrl xport
+
+ ////////////////////////////////////////////////////////////////////
+ // Async task structure
+ ////////////////////////////////////////////////////////////////////
+ _async_task_data.reset(new AsyncTaskData());
+ _async_task_data->async_md.reset(new async_md_type(1000/*messages deep*/));
+ _async_task = uhd::msg_task::make(boost::bind(&b200_impl::handle_async_task, this, _ctrl_transport, _async_task_data));
+
+ ////////////////////////////////////////////////////////////////////
+ // Local control endpoint
+ ////////////////////////////////////////////////////////////////////
+ _local_ctrl = radio_ctrl_core_3000::make(false/*lilE*/, _ctrl_transport, zero_copy_if::sptr()/*null*/, B200_LOCAL_CTRL_SID);
+ _local_ctrl->hold_task(_async_task);
+ _async_task_data->local_ctrl = _local_ctrl; //weak
+ this->check_fpga_compat();
+
+ /* Initialize the GPIOs, set the default bandsels to the lower range. Note
+ * that calling update_bandsel calls update_gpio_state(). */
+ _gpio_state = gpio_state();
+ update_bandsel("RX", 800e6);
+ update_bandsel("TX", 850e6);
+
+ ////////////////////////////////////////////////////////////////////
+ // Create the GPSDO control
+ ////////////////////////////////////////////////////////////////////
+ _async_task_data->gpsdo_uart = b200_uart::make(_ctrl_transport, B200_TX_GPS_UART_SID);
+ _async_task_data->gpsdo_uart->set_baud_divider(B200_BUS_CLOCK_RATE/115200);
+ _async_task_data->gpsdo_uart->write_uart("\n"); //cause the baud and response to be setup
+ boost::this_thread::sleep(boost::posix_time::seconds(1)); //allow for a little propagation
+
+ if ((_local_ctrl->peek32(RB32_CORE_STATUS) & 0xff) != B200_GPSDO_ST_NONE)
+ {
+ UHD_MSG(status) << "Detecting internal GPSDO.... " << std::flush;
+ try
+ {
+ _gps = gps_ctrl::make(_async_task_data->gpsdo_uart);
+ }
+ catch(std::exception &e)
+ {
+ UHD_MSG(error) << "An error occurred making GPSDO control: " << e.what() << std::endl;
+ }
+ if (_gps and _gps->gps_detected())
+ {
+ //UHD_MSG(status) << "found" << std::endl;
+ BOOST_FOREACH(const std::string &name, _gps->get_sensors())
+ {
+ _tree->create<sensor_value_t>(mb_path / "sensors" / name)
+ .publish(boost::bind(&gps_ctrl::get_sensor, _gps, name));
+ }
+ }
+ else
+ {
+ UHD_MSG(status) << "not found" << std::endl;
+ _local_ctrl->poke32(TOREG(SR_CORE_GPSDO_ST), B200_GPSDO_ST_NONE);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////
+ // Initialize the properties tree
+ ////////////////////////////////////////////////////////////////////
+ _tree->create<std::string>("/name").set("B-Series Device");
+ _tree->create<std::string>(mb_path / "name").set(product_name);
+ _tree->create<std::string>(mb_path / "codename").set("Sasquatch");
+
+ ////////////////////////////////////////////////////////////////////
+ // Create data transport
+ // This happens after FPGA ctrl instantiated so any junk that might
+ // be in the FPGAs buffers doesn't get pulled into the transport
+ // before being cleared.
+ ////////////////////////////////////////////////////////////////////
+ device_addr_t data_xport_args;
+ data_xport_args["recv_frame_size"] = device_addr.get("recv_frame_size", "8192");
+ data_xport_args["num_recv_frames"] = device_addr.get("num_recv_frames", "16");
+ data_xport_args["send_frame_size"] = device_addr.get("send_frame_size", "8192");
+ data_xport_args["num_send_frames"] = device_addr.get("num_send_frames", "16");
+
+ _data_transport = usb_zero_copy::make(
+ handle, // identifier
+ 2, 6, // IN interface, endpoint
+ 1, 2, // OUT interface, endpoint
+ data_xport_args // param hints
+ );
+ while (_data_transport->get_recv_buff(0.0)){} //flush ctrl xport
+ _demux.reset(new recv_packet_demuxer_3000(_data_transport));
+
+ ////////////////////////////////////////////////////////////////////
+ // Init codec - turns on clocks
+ ////////////////////////////////////////////////////////////////////
+ UHD_MSG(status) << "Initialize CODEC control..." << std::endl;
+ _codec_ctrl = ad9361_ctrl::make(_iface);
+ this->reset_codec_dcm();
+
+ ////////////////////////////////////////////////////////////////////
+ // create codec control objects
+ ////////////////////////////////////////////////////////////////////
+ {
+ const fs_path codec_path = mb_path / ("rx_codecs") / "A";
+ _tree->create<std::string>(codec_path / "name").set(product_name+" RX dual ADC");
+ _tree->create<int>(codec_path / "gains"); //empty cuz gains are in frontend
+ }
+ {
+ const fs_path codec_path = mb_path / ("tx_codecs") / "A";
+ _tree->create<std::string>(codec_path / "name").set(product_name+" TX dual DAC");
+ _tree->create<int>(codec_path / "gains"); //empty cuz gains are in frontend
+ }
+
+ ////////////////////////////////////////////////////////////////////
+ // create clock control objects
+ ////////////////////////////////////////////////////////////////////
+ _tree->create<double>(mb_path / "tick_rate")
+ .coerce(boost::bind(&b200_impl::set_tick_rate, this, _1))
+ .publish(boost::bind(&b200_impl::get_tick_rate, this))
+ .subscribe(boost::bind(&b200_impl::update_tick_rate, this, _1));
+ _tree->create<time_spec_t>(mb_path / "time" / "cmd");
+
+ ////////////////////////////////////////////////////////////////////
+ // and do the misc mboard sensors
+ ////////////////////////////////////////////////////////////////////
+ _tree->create<sensor_value_t>(mb_path / "sensors" / "ref_locked")
+ .publish(boost::bind(&b200_impl::get_ref_locked, this));
+
+ ////////////////////////////////////////////////////////////////////
+ // create frontend mapping
+ ////////////////////////////////////////////////////////////////////
+ _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec")
+ .set(subdev_spec_t())
+ .subscribe(boost::bind(&b200_impl::update_rx_subdev_spec, this, _1));
+ _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec")
+ .set(subdev_spec_t())
+ .subscribe(boost::bind(&b200_impl::update_tx_subdev_spec, this, _1));
+
+ ////////////////////////////////////////////////////////////////////
+ // setup radio control
+ ////////////////////////////////////////////////////////////////////
+ UHD_MSG(status) << "Initialize Radio control..." << std::endl;
+ const size_t num_radio_chains = ((_local_ctrl->peek32(RB32_CORE_STATUS) >> 8) & 0xff);
+ UHD_ASSERT_THROW(num_radio_chains > 0);
+ UHD_ASSERT_THROW(num_radio_chains <= 2);
+ _radio_perifs.resize(num_radio_chains);
+ for (size_t i = 0; i < _radio_perifs.size(); i++) this->setup_radio(i);
+
+ //now test each radio module's connection to the codec interface
+ _codec_ctrl->data_port_loopback(true);
+ BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs)
+ {
+ this->codec_loopback_self_test(perif.ctrl);
+ }
+ _codec_ctrl->data_port_loopback(false);
+
+ ////////////////////////////////////////////////////////////////////
+ // create time and clock control objects
+ ////////////////////////////////////////////////////////////////////
+ _spi_iface = spi_core_3000::make(_local_ctrl, TOREG(SR_CORE_SPI), RB32_CORE_SPI);
+ _spi_iface->set_divider(B200_BUS_CLOCK_RATE/ADF4001_SPI_RATE);
+ _adf4001_iface = boost::shared_ptr<adf4001_ctrl>(new adf4001_ctrl(_spi_iface, ADF4001_SLAVENO));
+
+ //register time now and pps onto available radio cores
+ _tree->create<time_spec_t>(mb_path / "time" / "now")
+ .publish(boost::bind(&time_core_3000::get_time_now, _radio_perifs[0].time64));
+ _tree->create<time_spec_t>(mb_path / "time" / "pps")
+ .publish(boost::bind(&time_core_3000::get_time_last_pps, _radio_perifs[0].time64));
+ for (size_t i = 0; i < _radio_perifs.size(); i++)
+ {
+ _tree->access<time_spec_t>(mb_path / "time" / "now")
+ .subscribe(boost::bind(&time_core_3000::set_time_now, _radio_perifs[i].time64, _1));
+ _tree->access<time_spec_t>(mb_path / "time" / "pps")
+ .subscribe(boost::bind(&time_core_3000::set_time_next_pps, _radio_perifs[i].time64, _1));
+ }
+
+ //setup time source props
+ _tree->create<std::string>(mb_path / "time_source" / "value")
+ .subscribe(boost::bind(&b200_impl::update_time_source, this, _1));
+ static const std::vector<std::string> time_sources = boost::assign::list_of("none")("external")("gpsdo");
+ _tree->create<std::vector<std::string> >(mb_path / "time_source" / "options").set(time_sources);
+ //setup reference source props
+ _tree->create<std::string>(mb_path / "clock_source" / "value")
+ .subscribe(boost::bind(&b200_impl::update_clock_source, this, _1));
+ static const std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("gpsdo");
+ _tree->create<std::vector<std::string> >(mb_path / "clock_source" / "options").set(clock_sources);
+
+ ////////////////////////////////////////////////////////////////////
+ // dboard eeproms but not really
+ ////////////////////////////////////////////////////////////////////
+ dboard_eeprom_t db_eeprom;
+ _tree->create<dboard_eeprom_t>(mb_path / "dboards" / "A" / "rx_eeprom").set(db_eeprom);
+ _tree->create<dboard_eeprom_t>(mb_path / "dboards" / "A" / "tx_eeprom").set(db_eeprom);
+ _tree->create<dboard_eeprom_t>(mb_path / "dboards" / "A" / "gdb_eeprom").set(db_eeprom);
+
+ ////////////////////////////////////////////////////////////////////
+ // do some post-init tasks
+ ////////////////////////////////////////////////////////////////////
+
+ //init the clock rate to something reasonable
+ _tree->access<double>(mb_path / "tick_rate").set(
+ device_addr.cast<double>("master_clock_rate", B200_DEFAULT_TICK_RATE));
+
+ //subdev spec contains full width of selections
+ subdev_spec_t rx_spec, tx_spec;
+ BOOST_FOREACH(const std::string &fe, _tree->list(mb_path / "dboards" / "A" / "rx_frontends"))
+ {
+ rx_spec.push_back(subdev_spec_pair_t("A", fe));
+ }
+ BOOST_FOREACH(const std::string &fe, _tree->list(mb_path / "dboards" / "A" / "tx_frontends"))
+ {
+ tx_spec.push_back(subdev_spec_pair_t("A", fe));
+ }
+ _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(rx_spec);
+ _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(tx_spec);
+
+ //init to internal clock and time source
+ _tree->access<std::string>(mb_path / "clock_source/value").set("internal");
+ _tree->access<std::string>(mb_path / "time_source/value").set("none");
+
+ //GPS installed: use external ref, time, and init time spec
+ if (_gps and _gps->gps_detected())
+ {
+ UHD_MSG(status) << "Setting references to the internal GPSDO" << std::endl;
+ _tree->access<std::string>(mb_path / "time_source" / "value").set("gpsdo");
+ _tree->access<std::string>(mb_path / "clock_source" / "value").set("gpsdo");
+ UHD_MSG(status) << "Initializing time to the internal GPSDO" << std::endl;
+ const time_t tp = time_t(_gps->get_sensor("gps_time").to_int()+1);
+ _tree->access<time_spec_t>(mb_path / "time" / "pps").set(time_spec_t(tp));
+ }
+
+}
+
+b200_impl::~b200_impl(void)
+{
+ UHD_SAFE_CALL
+ (
+ _async_task.reset();
+ )
+}
+
+/***********************************************************************
+ * setup radio control objects
+ **********************************************************************/
+
+void b200_impl::setup_radio(const size_t dspno)
+{
+ radio_perifs_t &perif = _radio_perifs[dspno];
+ const fs_path mb_path = "/mboards/0";
+
+ ////////////////////////////////////////////////////////////////////
+ // radio control
+ ////////////////////////////////////////////////////////////////////
+ const boost::uint32_t sid = (dspno == 0)? B200_CTRL0_MSG_SID : B200_CTRL1_MSG_SID;
+ perif.ctrl = radio_ctrl_core_3000::make(false/*lilE*/, _ctrl_transport, zero_copy_if::sptr()/*null*/, sid);
+ perif.ctrl->hold_task(_async_task);
+ _async_task_data->radio_ctrl[dspno] = perif.ctrl; //weak
+ _tree->access<time_spec_t>(mb_path / "time" / "cmd")
+ .subscribe(boost::bind(&radio_ctrl_core_3000::set_time, perif.ctrl, _1));
+ this->register_loopback_self_test(perif.ctrl);
+ perif.atr = gpio_core_200_32wo::make(perif.ctrl, TOREG(SR_ATR));
+
+ ////////////////////////////////////////////////////////////////////
+ // create rx dsp control objects
+ ////////////////////////////////////////////////////////////////////
+ perif.framer = rx_vita_core_3000::make(perif.ctrl, TOREG(SR_RX_CTRL));
+ perif.ddc = rx_dsp_core_3000::make(perif.ctrl, TOREG(SR_RX_DSP));
+ perif.ddc->set_link_rate(10e9/8); //whatever
+ _tree->access<double>(mb_path / "tick_rate")
+ .subscribe(boost::bind(&rx_vita_core_3000::set_tick_rate, perif.framer, _1))
+ .subscribe(boost::bind(&rx_dsp_core_3000::set_tick_rate, perif.ddc, _1));
+ const fs_path rx_dsp_path = mb_path / "rx_dsps" / str(boost::format("%u") % dspno);
+ _tree->create<meta_range_t>(rx_dsp_path / "rate" / "range")
+ .publish(boost::bind(&rx_dsp_core_3000::get_host_rates, perif.ddc));
+ _tree->create<double>(rx_dsp_path / "rate" / "value")
+ .coerce(boost::bind(&rx_dsp_core_3000::set_host_rate, perif.ddc, _1))
+ .subscribe(boost::bind(&b200_impl::update_rx_samp_rate, this, dspno, _1))
+ .set(1e6);
+ _tree->create<double>(rx_dsp_path / "freq" / "value")
+ .coerce(boost::bind(&rx_dsp_core_3000::set_freq, perif.ddc, _1))
+ .set(0.0);
+ _tree->create<meta_range_t>(rx_dsp_path / "freq" / "range")
+ .publish(boost::bind(&rx_dsp_core_3000::get_freq_range, perif.ddc));
+ _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd")
+ .subscribe(boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1));
+
+ ////////////////////////////////////////////////////////////////////
+ // create tx dsp control objects
+ ////////////////////////////////////////////////////////////////////
+ perif.deframer = tx_vita_core_3000::make(perif.ctrl, TOREG(SR_TX_CTRL));
+ perif.duc = tx_dsp_core_3000::make(perif.ctrl, TOREG(SR_TX_DSP));
+ perif.duc->set_link_rate(10e9/8); //whatever
+ _tree->access<double>(mb_path / "tick_rate")
+ .subscribe(boost::bind(&tx_vita_core_3000::set_tick_rate, perif.deframer, _1))
+ .subscribe(boost::bind(&tx_dsp_core_3000::set_tick_rate, perif.duc, _1));
+ const fs_path tx_dsp_path = mb_path / "tx_dsps" / str(boost::format("%u") % dspno);
+ _tree->create<meta_range_t>(tx_dsp_path / "rate" / "range")
+ .publish(boost::bind(&tx_dsp_core_3000::get_host_rates, perif.duc));
+ _tree->create<double>(tx_dsp_path / "rate" / "value")
+ .coerce(boost::bind(&tx_dsp_core_3000::set_host_rate, perif.duc, _1))
+ .subscribe(boost::bind(&b200_impl::update_tx_samp_rate, this, dspno, _1))
+ .set(1e6);
+ _tree->create<double>(tx_dsp_path / "freq" / "value")
+ .coerce(boost::bind(&tx_dsp_core_3000::set_freq, perif.duc, _1))
+ .set(0.0);
+ _tree->create<meta_range_t>(tx_dsp_path / "freq" / "range")
+ .publish(boost::bind(&tx_dsp_core_3000::get_freq_range, perif.duc));
+
+ ////////////////////////////////////////////////////////////////////
+ // create time control objects
+ ////////////////////////////////////////////////////////////////////
+ time_core_3000::readback_bases_type time64_rb_bases;
+ time64_rb_bases.rb_now = RB64_TIME_NOW;
+ time64_rb_bases.rb_pps = RB64_TIME_PPS;
+ perif.time64 = time_core_3000::make(perif.ctrl, TOREG(SR_TIME), time64_rb_bases);
+
+ ////////////////////////////////////////////////////////////////////
+ // create RF frontend interfacing
+ ////////////////////////////////////////////////////////////////////
+ for(size_t direction = 0; direction < 2; direction++)
+ {
+ const std::string x = direction? "rx" : "tx";
+ const std::string key = std::string((direction? "RX" : "TX")) + std::string(((dspno == FE1)? "1" : "2"));
+ const fs_path rf_fe_path = mb_path / "dboards" / "A" / (x+"_frontends") / (dspno? "B" : "A");
+
+ _tree->create<std::string>(rf_fe_path / "name").set("FE-"+key);
+ _tree->create<int>(rf_fe_path / "sensors"); //empty TODO
+ BOOST_FOREACH(const std::string &name, ad9361_ctrl::get_gain_names(key))
+ {
+ _tree->create<meta_range_t>(rf_fe_path / "gains" / name / "range")
+ .set(ad9361_ctrl::get_gain_range(key));
+
+ _tree->create<double>(rf_fe_path / "gains" / name / "value")
+ .coerce(boost::bind(&ad9361_ctrl::set_gain, _codec_ctrl, key, _1))
+ .set(0.0);
+ }
+ _tree->create<std::string>(rf_fe_path / "connection").set("IQ");
+ _tree->create<bool>(rf_fe_path / "enabled").set(true);
+ _tree->create<bool>(rf_fe_path / "use_lo_offset").set(false);
+ _tree->create<double>(rf_fe_path / "bandwidth" / "value")
+ .coerce(boost::bind(&ad9361_ctrl::set_bw_filter, _codec_ctrl, key, _1))
+ .set(40e6);
+ _tree->create<meta_range_t>(rf_fe_path / "bandwidth" / "range")
+ .publish(boost::bind(&ad9361_ctrl::get_bw_filter_range, key));
+ _tree->create<double>(rf_fe_path / "freq" / "value")
+ .set(0.0)
+ .coerce(boost::bind(&ad9361_ctrl::tune, _codec_ctrl, key, _1))
+ .subscribe(boost::bind(&b200_impl::update_bandsel, this, key, _1));
+ _tree->create<meta_range_t>(rf_fe_path / "freq" / "range")
+ .publish(boost::bind(&ad9361_ctrl::get_rf_freq_range));
+
+ //setup antenna stuff
+ if (key[0] == 'R')
+ {
+ static const std::vector<std::string> ants = boost::assign::list_of("TX/RX")("RX2");
+ _tree->create<std::vector<std::string> >(rf_fe_path / "antenna" / "options").set(ants);
+ _tree->create<std::string>(rf_fe_path / "antenna" / "value")
+ .subscribe(boost::bind(&b200_impl::update_antenna_sel, this, dspno, _1))
+ .set("RX2");
+ }
+ if (key[0] == 'T')
+ {
+ static const std::vector<std::string> ants(1, "TX/RX");
+ _tree->create<std::vector<std::string> >(rf_fe_path / "antenna" / "options").set(ants);
+ _tree->create<std::string>(rf_fe_path / "antenna" / "value").set("TX/RX");
+ }
+
+ }
+}
+
+/***********************************************************************
+ * loopback tests
+ **********************************************************************/
+
+void b200_impl::register_loopback_self_test(wb_iface::sptr iface)
+{
+ bool test_fail = false;
+ UHD_MSG(status) << "Performing register loopback test... " << std::flush;
+ size_t hash = time(NULL);
+ for (size_t i = 0; i < 100; i++)
+ {
+ boost::hash_combine(hash, i);
+ iface->poke32(TOREG(SR_TEST), boost::uint32_t(hash));
+ test_fail = iface->peek32(RB32_TEST) != boost::uint32_t(hash);
+ if (test_fail) break; //exit loop on any failure
+ }
+ UHD_MSG(status) << ((test_fail)? "fail" : "pass") << std::endl;
+}
+
+void b200_impl::codec_loopback_self_test(wb_iface::sptr iface)
+{
+ bool test_fail = false;
+ UHD_MSG(status) << "Performing CODEC loopback test... " << std::flush;
+ size_t hash = size_t(time(NULL));
+ for (size_t i = 0; i < 100; i++)
+ {
+ boost::hash_combine(hash, i);
+ const boost::uint32_t word32 = boost::uint32_t(hash) & 0xfff0fff0;
+ iface->poke32(TOREG(SR_CODEC_IDLE), word32);
+ iface->peek64(RB64_CODEC_READBACK); //enough idleness for loopback to propagate
+ const boost::uint64_t rb_word64 = iface->peek64(RB64_CODEC_READBACK);
+ const boost::uint32_t rb_tx = boost::uint32_t(rb_word64 >> 32);
+ const boost::uint32_t rb_rx = boost::uint32_t(rb_word64 & 0xffffffff);
+ test_fail = word32 != rb_tx or word32 != rb_rx;
+ if (test_fail) break; //exit loop on any failure
+ }
+ UHD_MSG(status) << ((test_fail)? "fail" : "pass") << std::endl;
+
+ /* Zero out the idle data. */
+ iface->poke32(TOREG(SR_CODEC_IDLE), 0);
+}
+
+/***********************************************************************
+ * Sample and tick rate comprehension below
+ **********************************************************************/
+double b200_impl::set_tick_rate(const double rate)
+{
+ UHD_MSG(status) << "Asking for clock rate " << rate/1e6 << " MHz\n";
+ _tick_rate = _codec_ctrl->set_clock_rate(rate);
+ UHD_MSG(status) << "Actually got clock rate " << _tick_rate/1e6 << " MHz\n";
+
+ //reset after clock rate change
+ this->reset_codec_dcm();
+
+ BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs)
+ {
+ perif.time64->set_tick_rate(_tick_rate);
+ perif.time64->self_test();
+ }
+ return _tick_rate;
+}
+
+/***********************************************************************
+ * compat checks
+ **********************************************************************/
+
+void b200_impl::check_fw_compat(void)
+{
+ boost::uint16_t compat_num = _iface->get_compat_num();
+ boost::uint32_t compat_major = (boost::uint32_t) (compat_num >> 8);
+ boost::uint32_t compat_minor = (boost::uint32_t) (compat_num & 0xFF);
+
+ if (compat_major != B200_FW_COMPAT_NUM_MAJOR){
+ throw uhd::runtime_error(str(boost::format(
+ "Expected firmware compatibility number 0x%x, but got 0x%x.%x:\n"
+ "The firmware build is not compatible with the host code build.\n"
+ "%s"
+ ) % int(B200_FW_COMPAT_NUM_MAJOR) % compat_major % compat_minor
+ % print_images_error()));
+ }
+ _tree->create<std::string>("/mboards/0/fw_version").set(str(boost::format("%u.%u")
+ % compat_major % compat_minor));
+}
+
+void b200_impl::check_fpga_compat(void)
+{
+ const boost::uint64_t compat = _local_ctrl->peek64(0);
+ const boost::uint32_t signature = boost::uint32_t(compat >> 32);
+ const boost::uint16_t compat_major = boost::uint16_t(compat >> 16);
+ const boost::uint16_t compat_minor = boost::uint16_t(compat & 0xffff);
+ if (signature != 0xACE0BA5E) throw uhd::runtime_error(
+ "b200::check_fpga_compat signature register readback failed");
+
+ if (compat_major != B200_FPGA_COMPAT_NUM){
+ throw uhd::runtime_error(str(boost::format(
+ "Expected FPGA compatibility number 0x%x, but got 0x%x.%x:\n"
+ "The FPGA build is not compatible with the host code build.\n"
+ "%s"
+ ) % int(B200_FPGA_COMPAT_NUM) % compat_major % compat_minor
+ % print_images_error()));
+ }
+ _tree->create<std::string>("/mboards/0/fpga_version").set(str(boost::format("%u.%u")
+ % compat_major % compat_minor));
+}
+
+void b200_impl::set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &mb_eeprom)
+{
+ mb_eeprom.commit(*_iface, "B200");
+}
+
+
+/***********************************************************************
+ * Reference time and clock
+ **********************************************************************/
+
+void b200_impl::update_clock_source(const std::string &source)
+{
+ if (source == "internal"){
+ _adf4001_iface->set_lock_to_ext_ref(false);
+ }
+ else if ((source == "external")
+ or (source == "gpsdo")){
+
+ _adf4001_iface->set_lock_to_ext_ref(true);
+ } else {
+ throw uhd::key_error("update_clock_source: unknown source: " + source);
+ }
+
+ _gpio_state.ref_sel = (source == "gpsdo")? 1 : 0;
+ this->update_gpio_state();
+}
+
+void b200_impl::update_time_source(const std::string &source)
+{
+ if (source == "none"){}
+ else if (source == "external"){}
+ else if (source == "gpsdo"){}
+ else throw uhd::key_error("update_time_source: unknown source: " + source);
+ _local_ctrl->poke32(TOREG(SR_CORE_PPS_SEL), (source == "external")? 1 : 0);
+}
+
+/***********************************************************************
+ * GPIO setup
+ **********************************************************************/
+
+void b200_impl::update_bandsel(const std::string& which, double freq)
+{
+ if(which[0] == 'R') {
+ if(freq < 2.2e9) {
+ _gpio_state.rx_bandsel_a = 0;
+ _gpio_state.rx_bandsel_b = 0;
+ _gpio_state.rx_bandsel_c = 1;
+ } else if((freq >= 2.2e9) && (freq < 4e9)) {
+ _gpio_state.rx_bandsel_a = 0;
+ _gpio_state.rx_bandsel_b = 1;
+ _gpio_state.rx_bandsel_c = 0;
+ } else if((freq >= 4e9) && (freq <= 6e9)) {
+ _gpio_state.rx_bandsel_a = 1;
+ _gpio_state.rx_bandsel_b = 0;
+ _gpio_state.rx_bandsel_c = 0;
+ } else {
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+ } else if(which[0] == 'T') {
+ if(freq < 2.5e9) {
+ _gpio_state.tx_bandsel_a = 0;
+ _gpio_state.tx_bandsel_b = 1;
+ } else if((freq >= 2.5e9) && (freq <= 6e9)) {
+ _gpio_state.tx_bandsel_a = 1;
+ _gpio_state.tx_bandsel_b = 0;
+ } else {
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+ } else {
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+
+ update_gpio_state();
+}
+
+void b200_impl::update_gpio_state(void)
+{
+ const boost::uint32_t misc_word = 0
+ | (_gpio_state.tx_bandsel_a << 7)
+ | (_gpio_state.tx_bandsel_b << 6)
+ | (_gpio_state.rx_bandsel_a << 5)
+ | (_gpio_state.rx_bandsel_b << 4)
+ | (_gpio_state.rx_bandsel_c << 3)
+ | (_gpio_state.codec_arst << 2)
+ | (_gpio_state.mimo << 1)
+ | (_gpio_state.ref_sel << 0)
+ ;
+
+ _local_ctrl->poke32(TOREG(RB32_CORE_MISC), misc_word);
+}
+
+void b200_impl::reset_codec_dcm(void)
+{
+ _gpio_state.codec_arst = 1;
+ this->update_gpio_state();
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+ _gpio_state.codec_arst = 0;
+ this->update_gpio_state();
+}
+
+void b200_impl::update_atrs(void)
+{
+ if (_radio_perifs.size() > FE1 and _radio_perifs[FE1].atr)
+ {
+ radio_perifs_t &perif = _radio_perifs[FE1];
+ const bool enb_rx = bool(perif.rx_streamer.lock());
+ const bool enb_tx = bool(perif.tx_streamer.lock());
+ const bool is_rx2 = perif.ant_rx2;
+ const size_t rxonly = (enb_rx)? ((is_rx2)? STATE_RX1_RX2 : STATE_RX1_TXRX) : STATE_OFF;
+ const size_t txonly = (enb_tx)? (STATE_TX1_TXRX) : STATE_OFF;
+ size_t fd = STATE_OFF;
+ if (enb_rx and enb_tx) fd = STATE_FDX1_TXRX;
+ if (enb_rx and not enb_tx) fd = rxonly;
+ if (not enb_rx and enb_tx) fd = txonly;
+ gpio_core_200_32wo::sptr atr = perif.atr;
+ atr->set_atr_reg(dboard_iface::ATR_REG_IDLE, STATE_OFF);
+ atr->set_atr_reg(dboard_iface::ATR_REG_RX_ONLY, rxonly);
+ atr->set_atr_reg(dboard_iface::ATR_REG_TX_ONLY, txonly);
+ atr->set_atr_reg(dboard_iface::ATR_REG_FULL_DUPLEX, fd);
+ }
+ if (_radio_perifs.size() > FE2 and _radio_perifs[FE2].atr)
+ {
+ radio_perifs_t &perif = _radio_perifs[FE2];
+ const bool enb_rx = bool(perif.rx_streamer.lock());
+ const bool enb_tx = bool(perif.tx_streamer.lock());
+ const bool is_rx2 = perif.ant_rx2;
+ const size_t rxonly = (enb_rx)? ((is_rx2)? STATE_RX2_RX2 : STATE_RX2_TXRX) : STATE_OFF;
+ const size_t txonly = (enb_tx)? (STATE_TX2_TXRX) : STATE_OFF;
+ size_t fd = STATE_OFF;
+ if (enb_rx and enb_tx) fd = STATE_FDX2_TXRX;
+ if (enb_rx and not enb_tx) fd = rxonly;
+ if (not enb_rx and enb_tx) fd = txonly;
+ gpio_core_200_32wo::sptr atr = perif.atr;
+ atr->set_atr_reg(dboard_iface::ATR_REG_IDLE, STATE_OFF);
+ atr->set_atr_reg(dboard_iface::ATR_REG_RX_ONLY, rxonly);
+ atr->set_atr_reg(dboard_iface::ATR_REG_TX_ONLY, txonly);
+ atr->set_atr_reg(dboard_iface::ATR_REG_FULL_DUPLEX, fd);
+ }
+}
+
+void b200_impl::update_antenna_sel(const size_t which, const std::string &ant)
+{
+ if (ant != "TX/RX" and ant != "RX2") throw uhd::value_error("b200: unknown RX antenna option: " + ant);
+ _radio_perifs[which].ant_rx2 = (ant == "RX2");
+ this->update_atrs();
+}
+
+void b200_impl::update_enables(void)
+{
+ //extract settings from state variables
+ const bool enb_tx1 = (_radio_perifs.size() > FE1) and bool(_radio_perifs[FE1].tx_streamer.lock());
+ const bool enb_rx1 = (_radio_perifs.size() > FE1) and bool(_radio_perifs[FE1].rx_streamer.lock());
+ const bool enb_tx2 = (_radio_perifs.size() > FE2) and bool(_radio_perifs[FE2].tx_streamer.lock());
+ const bool enb_rx2 = (_radio_perifs.size() > FE2) and bool(_radio_perifs[FE2].rx_streamer.lock());
+ const size_t num_rx = (enb_rx1?1:0) + (enb_rx2?1:0);
+ const size_t num_tx = (enb_tx1?1:0) + (enb_tx2?1:0);
+ const bool mimo = num_rx == 2 or num_tx == 2;
+
+ //setup the active chains in the codec
+ _codec_ctrl->set_active_chains(enb_tx1, enb_tx2, enb_rx1, enb_rx2);
+ if ((num_rx + num_tx) == 0) _codec_ctrl->set_active_chains(true, false, true, false); //enable something
+ this->reset_codec_dcm(); //set_active_chains could cause a clock rate change - reset dcm
+
+ //figure out if mimo is enabled based on new state
+ _gpio_state.mimo = (mimo)? 1 : 0;
+ update_gpio_state();
+
+ //atrs change based on enables
+ this->update_atrs();
+}
+
+sensor_value_t b200_impl::get_ref_locked(void)
+{
+ const bool lock = (_local_ctrl->peek32(RB32_CORE_MISC) & 0x1) == 0x1;
+ return sensor_value_t("Ref", lock, "locked", "unlocked");
+}
diff --git a/host/lib/usrp/b200/b200_impl.hpp b/host/lib/usrp/b200/b200_impl.hpp
new file mode 100644
index 000000000..362c45347
--- /dev/null
+++ b/host/lib/usrp/b200/b200_impl.hpp
@@ -0,0 +1,192 @@
+//
+// Copyright 2012-2013 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_B200_IMPL_HPP
+#define INCLUDED_B200_IMPL_HPP
+
+#include "b200_iface.hpp"
+#include "b200_uart.hpp"
+#include "ad9361_ctrl.hpp"
+#include "adf4001_ctrl.hpp"
+#include "rx_vita_core_3000.hpp"
+#include "tx_vita_core_3000.hpp"
+#include "time_core_3000.hpp"
+#include "gpio_core_200.hpp"
+#include "radio_ctrl_core_3000.hpp"
+#include "rx_dsp_core_3000.hpp"
+#include "tx_dsp_core_3000.hpp"
+#include <uhd/device.hpp>
+#include <uhd/property_tree.hpp>
+#include <uhd/utils/pimpl.hpp>
+#include <uhd/utils/tasks.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/types/sensors.hpp>
+#include <uhd/types/clock_config.hpp>
+#include <uhd/types/stream_cmd.hpp>
+#include <uhd/usrp/mboard_eeprom.hpp>
+#include <uhd/usrp/subdev_spec.hpp>
+#include <uhd/usrp/gps_ctrl.hpp>
+#include <uhd/transport/usb_zero_copy.hpp>
+#include <uhd/transport/bounded_buffer.hpp>
+#include <boost/weak_ptr.hpp>
+#include "recv_packet_demuxer_3000.hpp"
+static const boost::uint8_t B200_FW_COMPAT_NUM_MAJOR = 0x03;
+static const boost::uint8_t B200_FW_COMPAT_NUM_MINOR = 0x00;
+static const boost::uint16_t B200_FPGA_COMPAT_NUM = 0x02;
+static const double B200_LINK_RATE_BPS = (5e9)/8; //practical link rate (5 Gbps)
+static const double B200_BUS_CLOCK_RATE = 100e6;
+static const double B200_DEFAULT_TICK_RATE = 32e6;
+static const boost::uint32_t B200_GPSDO_ST_NONE = 0x83;
+
+#define FLIP_SID(sid) (((sid)<<16)|((sid)>>16))
+
+static const boost::uint32_t B200_CTRL0_MSG_SID = 0x00000010;
+static const boost::uint32_t B200_RESP0_MSG_SID = FLIP_SID(B200_CTRL0_MSG_SID);
+
+static const boost::uint32_t B200_CTRL1_MSG_SID = 0x00000020;
+static const boost::uint32_t B200_RESP1_MSG_SID = FLIP_SID(B200_CTRL1_MSG_SID);
+
+static const boost::uint32_t B200_TX_DATA0_SID = 0x00000050;
+static const boost::uint32_t B200_TX_MSG0_SID = FLIP_SID(B200_TX_DATA0_SID);
+
+static const boost::uint32_t B200_TX_DATA1_SID = 0x00000060;
+static const boost::uint32_t B200_TX_MSG1_SID = FLIP_SID(B200_TX_DATA1_SID);
+
+static const boost::uint32_t B200_RX_DATA0_SID = 0x000000A0;
+static const boost::uint32_t B200_RX_DATA1_SID = 0x000000B0;
+
+static const boost::uint32_t B200_TX_GPS_UART_SID = 0x00000030;
+static const boost::uint32_t B200_RX_GPS_UART_SID = FLIP_SID(B200_TX_GPS_UART_SID);
+
+static const boost::uint32_t B200_LOCAL_CTRL_SID = 0x00000040;
+static const boost::uint32_t B200_LOCAL_RESP_SID = FLIP_SID(B200_LOCAL_CTRL_SID);
+
+/***********************************************************************
+ * The B200 Capability Constants
+ **********************************************************************/
+
+//! Implementation guts
+struct b200_impl : public uhd::device
+{
+ //structors
+ b200_impl(const uhd::device_addr_t &);
+ ~b200_impl(void);
+
+ //the io interface
+ uhd::rx_streamer::sptr get_rx_stream(const uhd::stream_args_t &args);
+ uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &args);
+ bool recv_async_msg(uhd::async_metadata_t &, double);
+
+ uhd::property_tree::sptr _tree;
+
+ //controllers
+ b200_iface::sptr _iface;
+ radio_ctrl_core_3000::sptr _local_ctrl;
+ ad9361_ctrl::sptr _codec_ctrl;
+ spi_core_3000::sptr _spi_iface;
+ boost::shared_ptr<uhd::usrp::adf4001_ctrl> _adf4001_iface;
+ uhd::gps_ctrl::sptr _gps;
+
+ //transports
+ uhd::transport::zero_copy_if::sptr _data_transport;
+ uhd::transport::zero_copy_if::sptr _ctrl_transport;
+ boost::shared_ptr<uhd::usrp::recv_packet_demuxer_3000> _demux;
+
+ //device properties interface
+ uhd::property_tree::sptr get_tree(void) const
+ {
+ return _tree;
+ }
+
+ boost::weak_ptr<uhd::rx_streamer> _rx_streamer;
+ boost::weak_ptr<uhd::tx_streamer> _tx_streamer;
+
+ //async ctrl + msgs
+ uhd::msg_task::sptr _async_task;
+ typedef uhd::transport::bounded_buffer<uhd::async_metadata_t> async_md_type;
+ struct AsyncTaskData
+ {
+ boost::shared_ptr<async_md_type> async_md;
+ boost::weak_ptr<radio_ctrl_core_3000> local_ctrl;
+ boost::weak_ptr<radio_ctrl_core_3000> radio_ctrl[2];
+ b200_uart::sptr gpsdo_uart;
+ };
+ boost::shared_ptr<AsyncTaskData> _async_task_data;
+ boost::optional<uhd::msg_task::msg_type_t> handle_async_task(uhd::transport::zero_copy_if::sptr, boost::shared_ptr<AsyncTaskData>);
+
+ void register_loopback_self_test(uhd::wb_iface::sptr iface);
+ void codec_loopback_self_test(uhd::wb_iface::sptr iface);
+ void set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &);
+ void check_fw_compat(void);
+ void check_fpga_compat(void);
+ void update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &);
+ void update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &);
+ void update_time_source(const std::string &);
+ void update_clock_source(const std::string &);
+ void update_bandsel(const std::string& which, double freq);
+ void update_antenna_sel(const size_t which, const std::string &ant);
+ uhd::sensor_value_t get_ref_locked(void);
+
+ //perifs in the radio core
+ struct radio_perifs_t
+ {
+ radio_ctrl_core_3000::sptr ctrl;
+ gpio_core_200_32wo::sptr atr;
+ time_core_3000::sptr time64;
+ rx_vita_core_3000::sptr framer;
+ rx_dsp_core_3000::sptr ddc;
+ tx_vita_core_3000::sptr deframer;
+ tx_dsp_core_3000::sptr duc;
+ boost::weak_ptr<uhd::rx_streamer> rx_streamer;
+ boost::weak_ptr<uhd::tx_streamer> tx_streamer;
+ bool ant_rx2;
+ };
+ std::vector<radio_perifs_t> _radio_perifs;
+ void setup_radio(const size_t which_radio);
+ void handle_overflow(const size_t index);
+
+ struct gpio_state {
+ boost::uint32_t tx_bandsel_a, tx_bandsel_b, rx_bandsel_a, rx_bandsel_b, rx_bandsel_c, codec_arst, mimo, ref_sel;
+
+ gpio_state() {
+ tx_bandsel_a = 0;
+ tx_bandsel_b = 0;
+ rx_bandsel_a = 0;
+ rx_bandsel_b = 0;
+ rx_bandsel_c = 0;
+ codec_arst = 0;
+ mimo = 0;
+ ref_sel = 0;
+ }
+ } _gpio_state;
+
+ void update_gpio_state(void);
+ void reset_codec_dcm(void);
+
+ void update_enables(void);
+ void update_atrs(void);
+
+ void update_tick_rate(const double);
+ void update_rx_samp_rate(const size_t, const double);
+ void update_tx_samp_rate(const size_t, const double);
+
+ double _tick_rate;
+ double get_tick_rate(void){return _tick_rate;}
+ double set_tick_rate(const double rate);
+};
+
+#endif /* INCLUDED_B200_IMPL_HPP */
diff --git a/host/lib/usrp/b200/b200_io_impl.cpp b/host/lib/usrp/b200/b200_io_impl.cpp
new file mode 100644
index 000000000..4fe90bd4a
--- /dev/null
+++ b/host/lib/usrp/b200/b200_io_impl.cpp
@@ -0,0 +1,396 @@
+//
+// Copyright 2012-2013 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 "b200_regs.hpp"
+#include "b200_impl.hpp"
+#include "validate_subdev_spec.hpp"
+#include "../../transport/super_recv_packet_handler.hpp"
+#include "../../transport/super_send_packet_handler.hpp"
+#include "async_packet_handler.hpp"
+#include <boost/bind.hpp>
+#include <boost/make_shared.hpp>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+
+/***********************************************************************
+ * update streamer rates
+ **********************************************************************/
+void b200_impl::update_tick_rate(const double rate)
+{
+ BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs)
+ {
+ boost::shared_ptr<sph::recv_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<sph::recv_packet_streamer>(perif.rx_streamer.lock());
+ if (my_streamer) my_streamer->set_tick_rate(rate);
+ perif.framer->set_tick_rate(_tick_rate);
+ }
+ BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs)
+ {
+ boost::shared_ptr<sph::send_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<sph::send_packet_streamer>(perif.tx_streamer.lock());
+ if (my_streamer) my_streamer->set_tick_rate(rate);
+ perif.deframer->set_tick_rate(_tick_rate);
+ }
+}
+
+void b200_impl::update_rx_samp_rate(const size_t dspno, const double rate)
+{
+ boost::shared_ptr<sph::recv_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_radio_perifs[dspno].rx_streamer.lock());
+ if (not my_streamer) return;
+ my_streamer->set_samp_rate(rate);
+ const double adj = _radio_perifs[dspno].ddc->get_scaling_adjustment();
+ my_streamer->set_scale_factor(adj);
+}
+
+void b200_impl::update_tx_samp_rate(const size_t dspno, const double rate)
+{
+ boost::shared_ptr<sph::send_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<sph::send_packet_streamer>(_radio_perifs[dspno].tx_streamer.lock());
+ if (not my_streamer) return;
+ my_streamer->set_samp_rate(rate);
+ const double adj = _radio_perifs[dspno].duc->get_scaling_adjustment();
+ my_streamer->set_scale_factor(adj);
+}
+
+/***********************************************************************
+ * frontend selection
+ **********************************************************************/
+void b200_impl::update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &spec)
+{
+ //sanity checking
+ if (spec.size()) validate_subdev_spec(_tree, spec, "rx");
+ UHD_ASSERT_THROW(spec.size() <= _radio_perifs.size());
+
+ if (spec.size() > 0)
+ {
+ UHD_ASSERT_THROW(spec[0].db_name == "A");
+ UHD_ASSERT_THROW(spec[0].sd_name == "A");
+ }
+ if (spec.size() > 1)
+ {
+ //TODO we can support swapping at a later date, only this combo is supported
+ UHD_ASSERT_THROW(spec[1].db_name == "A");
+ UHD_ASSERT_THROW(spec[1].sd_name == "B");
+ }
+
+ this->update_enables();
+}
+
+void b200_impl::update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec)
+{
+ //sanity checking
+ if (spec.size()) validate_subdev_spec(_tree, spec, "tx");
+ UHD_ASSERT_THROW(spec.size() <= _radio_perifs.size());
+
+ if (spec.size() > 0)
+ {
+ UHD_ASSERT_THROW(spec[0].db_name == "A");
+ UHD_ASSERT_THROW(spec[0].sd_name == "A");
+ }
+ if (spec.size() > 1)
+ {
+ //TODO we can support swapping at a later date, only this combo is supported
+ UHD_ASSERT_THROW(spec[1].db_name == "A");
+ UHD_ASSERT_THROW(spec[1].sd_name == "B");
+ }
+
+ this->update_enables();
+}
+
+static void b200_if_hdr_unpack_le(
+ const boost::uint32_t *packet_buff,
+ vrt::if_packet_info_t &if_packet_info
+){
+ if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR;
+ return vrt::if_hdr_unpack_le(packet_buff, if_packet_info);
+}
+
+static void b200_if_hdr_pack_le(
+ boost::uint32_t *packet_buff,
+ vrt::if_packet_info_t &if_packet_info
+){
+ if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR;
+ return vrt::if_hdr_pack_le(packet_buff, if_packet_info);
+}
+
+/***********************************************************************
+ * Async Data
+ **********************************************************************/
+bool b200_impl::recv_async_msg(
+ async_metadata_t &async_metadata, double timeout
+){
+ return _async_task_data->async_md->pop_with_timed_wait(async_metadata, timeout);
+}
+
+/*
+ * This method is constantly called in a msg_task loop.
+ * Incoming messages are dispatched in to the hosts radio_ctrl_cores.
+ * The radio_ctrl_core queues are accessed via a weak_ptr to them, stored in AsyncTaskData.
+ * During shutdown the radio_ctrl_core dtor's are called.
+ * An empty peek32(0) is sent out to flush pending async messages.
+ * The response to those messages can't be delivered to the ctrl_core queues anymore
+ * because the shared pointer corresponding to the weak_ptrs is no longer valid.
+ * Those stranded messages are put into a dump_queue implemented in msg_task.
+ * A radio_ctrl_core can search for missing messages there.
+ */
+boost::optional<uhd::msg_task::msg_type_t> b200_impl::handle_async_task(
+ uhd::transport::zero_copy_if::sptr xport,
+ boost::shared_ptr<AsyncTaskData> data
+)
+{
+ managed_recv_buffer::sptr buff = xport->get_recv_buff();
+ if (not buff or buff->size() < 8)
+ return NULL;
+
+ const boost::uint32_t sid = uhd::wtohx(buff->cast<const boost::uint32_t *>()[1]);
+ switch (sid) {
+
+ //if the packet is a control response
+ case B200_RESP0_MSG_SID:
+ case B200_RESP1_MSG_SID:
+ case B200_LOCAL_RESP_SID:
+ {
+ radio_ctrl_core_3000::sptr ctrl;
+ if (sid == B200_RESP0_MSG_SID) ctrl = data->radio_ctrl[0].lock();
+ if (sid == B200_RESP1_MSG_SID) ctrl = data->radio_ctrl[1].lock();
+ if (sid == B200_LOCAL_RESP_SID) ctrl = data->local_ctrl.lock();
+ if (ctrl){
+ ctrl->push_response(buff->cast<const boost::uint32_t *>());
+ }
+ else{
+ return std::make_pair(sid, uhd::msg_task::buff_to_vector(buff->cast<boost::uint8_t *>(), buff->size() ) );
+ }
+ break;
+ }
+
+ //if the packet is a uart message
+ case B200_RX_GPS_UART_SID:
+ {
+ data->gpsdo_uart->handle_uart_packet(buff);
+ break;
+ }
+
+ //or maybe the packet is a TX async message
+ case B200_TX_MSG0_SID:
+ case B200_TX_MSG1_SID:
+ {
+ const size_t i = (sid == B200_TX_MSG0_SID)? 0 : 1;
+
+ //extract packet info
+ vrt::if_packet_info_t if_packet_info;
+ if_packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t);
+ const boost::uint32_t *packet_buff = buff->cast<const boost::uint32_t *>();
+
+ //unpacking can fail
+ try
+ {
+ b200_if_hdr_unpack_le(packet_buff, if_packet_info);
+ }
+ catch(const std::exception &ex)
+ {
+ UHD_MSG(error) << "Error parsing ctrl packet: " << ex.what() << std::endl;
+ break;
+ }
+
+ //fill in the async metadata
+ async_metadata_t metadata;
+ load_metadata_from_buff(uhd::wtohx<boost::uint32_t>, metadata, if_packet_info, packet_buff, _tick_rate, i);
+ data->async_md->push_with_pop_on_full(metadata);
+ standard_async_msg_prints(metadata);
+ break;
+ }
+
+ //doh!
+ default:
+ UHD_MSG(error) << "Got a ctrl packet with unknown SID " << sid << std::endl;
+ }
+ return NULL;
+}
+
+/***********************************************************************
+ * Receive streamer
+ **********************************************************************/
+rx_streamer::sptr b200_impl::get_rx_stream(const uhd::stream_args_t &args_)
+{
+ stream_args_t args = args_;
+
+ //setup defaults for unspecified values
+ if (args.otw_format.empty()) args.otw_format = "sc16";
+ args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels;
+
+ boost::shared_ptr<sph::recv_packet_streamer> my_streamer;
+ for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++)
+ {
+ const size_t chan = args.channels[stream_i];
+ radio_perifs_t &perif = _radio_perifs[chan];
+ if (args.otw_format == "sc16") perif.ctrl->poke32(TOREG(SR_RX_FMT), 0);
+ if (args.otw_format == "sc12") perif.ctrl->poke32(TOREG(SR_RX_FMT), 1);
+ if (args.otw_format == "fc32") perif.ctrl->poke32(TOREG(SR_RX_FMT), 2);
+ if (args.otw_format == "sc8") perif.ctrl->poke32(TOREG(SR_RX_FMT), 3);
+ const boost::uint32_t sid = chan?B200_RX_DATA1_SID:B200_RX_DATA0_SID;
+
+ //calculate packet size
+ static const size_t hdr_size = 0
+ + vrt::max_if_hdr_words32*sizeof(boost::uint32_t)
+ //+ sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer
+ - sizeof(vrt::if_packet_info_t().cid) //no class id ever used
+ - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used
+ ;
+ const size_t bpp = _data_transport->get_recv_frame_size() - hdr_size;
+ const size_t bpi = convert::get_bytes_per_item(args.otw_format);
+ size_t spp = unsigned(args.args.cast<double>("spp", bpp/bpi));
+ spp = std::min<size_t>(2000, spp); //magic maximum for framing at full rate
+
+ //make the new streamer given the samples per packet
+ if (not my_streamer) my_streamer = boost::make_shared<sph::recv_packet_streamer>(spp);
+ my_streamer->resize(args.channels.size());
+
+ //init some streamer stuff
+ my_streamer->set_vrt_unpacker(&b200_if_hdr_unpack_le);
+
+ //set the converter
+ uhd::convert::id_type id;
+ id.input_format = args.otw_format + "_item32_le";
+ id.num_inputs = 1;
+ id.output_format = args.cpu_format;
+ id.num_outputs = 1;
+ my_streamer->set_converter(id);
+
+ perif.framer->clear();
+ perif.framer->set_nsamps_per_packet(spp);
+ perif.framer->set_sid(sid);
+ perif.framer->setup(args);
+ perif.ddc->setup(args);
+ _demux->realloc_sid(sid);
+ my_streamer->set_xport_chan_get_buff(stream_i, boost::bind(
+ &recv_packet_demuxer_3000::get_recv_buff, _demux, sid, _1
+ ), true /*flush*/);
+ my_streamer->set_overflow_handler(stream_i, boost::bind(
+ &b200_impl::handle_overflow, this, chan
+ ));
+ my_streamer->set_issue_stream_cmd(stream_i, boost::bind(
+ &rx_vita_core_3000::issue_stream_command, perif.framer, _1
+ ));
+ perif.rx_streamer = my_streamer; //store weak pointer
+
+ //sets all tick and samp rates on this streamer
+ this->update_tick_rate(this->get_tick_rate());
+ _tree->access<double>(str(boost::format("/mboards/0/rx_dsps/%u/rate/value") % chan)).update();
+ }
+ this->update_enables();
+
+ return my_streamer;
+}
+
+void b200_impl::handle_overflow(const size_t i)
+{
+ boost::shared_ptr<sph::recv_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_radio_perifs[i].rx_streamer.lock());
+ if (my_streamer->get_num_channels() == 2) //MIMO time
+ {
+ //find out if we were in continuous mode before stopping
+ const bool in_continuous_streaming_mode = _radio_perifs[i].framer->in_continuous_streaming_mode();
+ //stop streaming
+ my_streamer->issue_stream_cmd(stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS);
+ //flush demux
+ _demux->realloc_sid(B200_RX_DATA0_SID);
+ _demux->realloc_sid(B200_RX_DATA1_SID);
+ //flush actual transport
+ while (_data_transport->get_recv_buff(0.001)){}
+ //restart streaming
+ if (in_continuous_streaming_mode)
+ {
+ stream_cmd_t stream_cmd(stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
+ stream_cmd.stream_now = false;
+ stream_cmd.time_spec = _radio_perifs[i].time64->get_time_now() + time_spec_t(0.01);
+ my_streamer->issue_stream_cmd(stream_cmd);
+ }
+ }
+ else _radio_perifs[i].framer->handle_overflow();
+}
+
+/***********************************************************************
+ * Transmit streamer
+ **********************************************************************/
+tx_streamer::sptr b200_impl::get_tx_stream(const uhd::stream_args_t &args_)
+{
+ stream_args_t args = args_;
+
+ //setup defaults for unspecified values
+ if (args.otw_format.empty()) args.otw_format = "sc16";
+ args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels;
+
+ boost::shared_ptr<sph::send_packet_streamer> my_streamer;
+ for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++)
+ {
+ const size_t chan = args.channels[stream_i];
+ radio_perifs_t &perif = _radio_perifs[chan];
+ if (args.otw_format == "sc16") perif.ctrl->poke32(TOREG(SR_TX_FMT), 0);
+ if (args.otw_format == "sc12") perif.ctrl->poke32(TOREG(SR_TX_FMT), 1);
+ if (args.otw_format == "fc32") perif.ctrl->poke32(TOREG(SR_TX_FMT), 2);
+ if (args.otw_format == "sc8") perif.ctrl->poke32(TOREG(SR_TX_FMT), 3);
+
+ //calculate packet size
+ static const size_t hdr_size = 0
+ + vrt::max_if_hdr_words32*sizeof(boost::uint32_t)
+ //+ sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer
+ - sizeof(vrt::if_packet_info_t().cid) //no class id ever used
+ - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used
+ ;
+ static const size_t bpp = _data_transport->get_send_frame_size() - hdr_size;
+ const size_t spp = bpp/convert::get_bytes_per_item(args.otw_format);
+
+ //make the new streamer given the samples per packet
+ if (not my_streamer) my_streamer = boost::make_shared<sph::send_packet_streamer>(spp);
+ my_streamer->resize(args.channels.size());
+
+ //init some streamer stuff
+ my_streamer->set_vrt_packer(&b200_if_hdr_pack_le);
+
+ //set the converter
+ uhd::convert::id_type id;
+ id.input_format = args.cpu_format;
+ id.num_inputs = 1;
+ id.output_format = args.otw_format + "_item32_le";
+ id.num_outputs = 1;
+ my_streamer->set_converter(id);
+
+ perif.deframer->clear();
+ perif.deframer->setup(args);
+ perif.duc->setup(args);
+
+ my_streamer->set_xport_chan_get_buff(stream_i, boost::bind(
+ &zero_copy_if::get_send_buff, _data_transport, _1
+ ));
+ my_streamer->set_async_receiver(boost::bind(
+ &async_md_type::pop_with_timed_wait, _async_task_data->async_md, _1, _2
+ ));
+ my_streamer->set_xport_chan_sid(stream_i, true, chan?B200_TX_DATA1_SID:B200_TX_DATA0_SID);
+ my_streamer->set_enable_trailer(false); //TODO not implemented trailer support yet
+ perif.tx_streamer = my_streamer; //store weak pointer
+
+ //sets all tick and samp rates on this streamer
+ this->update_tick_rate(this->get_tick_rate());
+ _tree->access<double>(str(boost::format("/mboards/0/tx_dsps/%u/rate/value") % chan)).update();
+ }
+ this->update_enables();
+
+ return my_streamer;
+}
diff --git a/host/lib/usrp/b200/b200_regs.hpp b/host/lib/usrp/b200/b200_regs.hpp
new file mode 100644
index 000000000..c64066b27
--- /dev/null
+++ b/host/lib/usrp/b200/b200_regs.hpp
@@ -0,0 +1,122 @@
+//
+// Copyright 2012-2013 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_B200_REGS_HPP
+#define INCLUDED_B200_REGS_HPP
+
+#include <boost/cstdint.hpp>
+
+#define TOREG(x) ((x)*4)
+
+#define localparam static const int
+
+localparam SR_CORE_SPI = 8;
+localparam SR_CORE_MISC = 16;
+localparam SR_CORE_COMPAT = 24;
+localparam SR_CORE_GPSDO_ST = 40;
+localparam SR_CORE_PPS_SEL = 48;
+localparam RB32_CORE_SPI = 8;
+localparam RB32_CORE_MISC = 16;
+localparam RB32_CORE_STATUS = 20;
+
+localparam SR_SPI = 8;
+localparam SR_ATR = 12;
+localparam SR_TEST = 21;
+localparam SR_CODEC_IDLE = 22;
+localparam SR_READBACK = 32;
+localparam SR_TX_CTRL = 64;
+localparam SR_RX_CTRL = 96;
+localparam SR_RX_DSP = 144;
+localparam SR_TX_DSP = 184;
+localparam SR_TIME = 128;
+localparam SR_RX_FMT = 136;
+localparam SR_TX_FMT = 138;
+
+localparam RB32_TEST = 0;
+localparam RB64_TIME_NOW = 8;
+localparam RB64_TIME_PPS = 16;
+localparam RB64_CODEC_READBACK = 24;
+
+//pll constants
+static const int ADF4001_SLAVENO = (1 << 1);
+static const double ADF4001_SPI_RATE = 10e3; //slow for large time constant on spi lines
+
+/* ATR Control Bits */
+static const boost::uint32_t TX_ENABLE1 = (1 << 7);
+static const boost::uint32_t SFDX1_RX = (1 << 6);
+static const boost::uint32_t SFDX1_TX = (1 << 5);
+static const boost::uint32_t SRX1_RX = (1 << 4);
+static const boost::uint32_t SRX1_TX = (1 << 3);
+static const boost::uint32_t LED_RX1 = (1 << 2);
+static const boost::uint32_t LED_TXRX_RX1 = (1 << 1);
+static const boost::uint32_t LED_TXRX_TX1 = (1 << 0);
+
+static const boost::uint32_t TX_ENABLE2 = (1 << 7);
+static const boost::uint32_t SFDX2_RX = (1 << 6);
+static const boost::uint32_t SFDX2_TX = (1 << 5);
+static const boost::uint32_t SRX2_RX = (1 << 4);
+static const boost::uint32_t SRX2_TX = (1 << 3);
+static const boost::uint32_t LED_RX2 = (1 << 2);
+static const boost::uint32_t LED_TXRX_RX2 = (1 << 1);
+static const boost::uint32_t LED_TXRX_TX2 = (1 << 0);
+
+
+/* ATR State Definitions. */
+static const boost::uint32_t STATE_OFF = 0x00;
+
+///////////////////////// side 1 ///////////////////////////////////
+static const boost::uint32_t STATE_RX1_RX2 = (SFDX1_RX
+ | SFDX1_TX
+ | LED_RX1);
+
+static const boost::uint32_t STATE_RX1_TXRX = (SRX1_RX
+ | SRX1_TX
+ | LED_TXRX_RX1);
+
+static const boost::uint32_t STATE_FDX1_TXRX = (TX_ENABLE1
+ | SFDX1_RX
+ | SFDX1_TX
+ | LED_TXRX_TX1
+ | LED_RX1);
+
+static const boost::uint32_t STATE_TX1_TXRX = (TX_ENABLE1
+ | SFDX1_RX
+ | SFDX1_TX
+ | LED_TXRX_TX1);
+
+///////////////////////// side 2 ///////////////////////////////////
+static const boost::uint32_t STATE_RX2_RX2 = (SFDX2_RX
+ | SRX2_TX
+ | LED_RX2);
+
+static const boost::uint32_t STATE_RX2_TXRX = (SRX2_TX
+ | SRX2_RX
+ | LED_TXRX_RX2);
+
+static const boost::uint32_t STATE_FDX2_TXRX = (TX_ENABLE2
+ | SFDX2_RX
+ | SFDX2_TX
+ | LED_TXRX_TX2
+ | LED_RX2);
+
+static const boost::uint32_t STATE_TX2_TXRX = (TX_ENABLE2
+ | SFDX2_RX
+ | SFDX2_TX
+ | LED_TXRX_TX2);
+
+
+#endif /* INCLUDED_B200_REGS_HPP */
diff --git a/host/lib/usrp/b200/b200_uart.cpp b/host/lib/usrp/b200/b200_uart.cpp
new file mode 100644
index 000000000..4682a79b9
--- /dev/null
+++ b/host/lib/usrp/b200/b200_uart.cpp
@@ -0,0 +1,117 @@
+//
+// Copyright 2013 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 "b200_uart.hpp"
+#include <uhd/transport/bounded_buffer.hpp>
+#include <uhd/transport/vrt_if_packet.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/types/time_spec.hpp>
+#include <uhd/exception.hpp>
+
+using namespace uhd;
+using namespace uhd::transport;
+
+struct b200_uart_impl : b200_uart
+{
+ b200_uart_impl(zero_copy_if::sptr xport, const boost::uint32_t sid):
+ _xport(xport),
+ _sid(sid),
+ _count(0),
+ _char_queue(4096)
+ {
+ //this default baud divider is over 9000
+ this->set_baud_divider(9001);
+ }
+
+ void send_char(const char ch)
+ {
+ managed_send_buffer::sptr buff = _xport->get_send_buff();
+ UHD_ASSERT_THROW(bool(buff));
+
+ vrt::if_packet_info_t packet_info;
+ packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR;
+ packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_CONTEXT;
+ packet_info.num_payload_words32 = 2;
+ packet_info.num_payload_bytes = packet_info.num_payload_words32*sizeof(boost::uint32_t);
+ packet_info.packet_count = _count++;
+ packet_info.sob = false;
+ packet_info.eob = false;
+ packet_info.sid = _sid;
+ packet_info.has_sid = true;
+ packet_info.has_cid = false;
+ packet_info.has_tsi = false;
+ packet_info.has_tsf = false;
+ packet_info.has_tlr = false;
+
+ boost::uint32_t *packet_buff = buff->cast<boost::uint32_t *>();
+ vrt::if_hdr_pack_le(packet_buff, packet_info);
+ packet_buff[packet_info.num_header_words32+0] = uhd::htowx(boost::uint32_t(_baud_div));
+ packet_buff[packet_info.num_header_words32+1] = uhd::htowx(boost::uint32_t(ch));
+ buff->commit(packet_info.num_packet_words32*sizeof(boost::uint32_t));
+ }
+
+ void write_uart(const std::string &buff)
+ {
+ for (size_t i = 0; i < buff.size(); i++)
+ {
+ if (buff[i] == '\n') this->send_char('\r');
+ this->send_char(buff[i]);
+ }
+ }
+
+ std::string read_uart(double timeout)
+ {
+ std::string line;
+ char ch = '\0';
+ while (_char_queue.pop_with_timed_wait(ch, timeout))
+ {
+ if (ch == '\r') continue;
+ line += std::string(&ch, 1);
+ if (ch == '\n') return line;
+ }
+ return line;
+ }
+
+ void handle_uart_packet(managed_recv_buffer::sptr buff)
+ {
+ const boost::uint32_t *packet_buff = buff->cast<const boost::uint32_t *>();
+ vrt::if_packet_info_t packet_info;
+ packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR;
+ packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t);
+ vrt::if_hdr_unpack_le(packet_buff, packet_info);
+ const char ch = char(uhd::wtohx(packet_buff[packet_info.num_header_words32+1]));
+ _char_queue.push_with_pop_on_full(ch);
+ }
+
+ void set_baud_divider(const double baud_div)
+ {
+ _baud_div = size_t(baud_div + 0.5);
+ }
+
+ const zero_copy_if::sptr _xport;
+ const boost::uint32_t _sid;
+ size_t _count;
+ size_t _baud_div;
+ bounded_buffer<char> _char_queue;
+};
+
+
+b200_uart::sptr b200_uart::make(zero_copy_if::sptr xport, const boost::uint32_t sid)
+{
+ return b200_uart::sptr(new b200_uart_impl(xport, sid));
+}
diff --git a/host/lib/usrp/b200/b200_uart.hpp b/host/lib/usrp/b200/b200_uart.hpp
new file mode 100644
index 000000000..1c8e44ddc
--- /dev/null
+++ b/host/lib/usrp/b200/b200_uart.hpp
@@ -0,0 +1,36 @@
+//
+// Copyright 2013 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_B200_UART_HPP
+#define INCLUDED_B200_UART_HPP
+
+#include <uhd/transport/zero_copy.hpp>
+#include <uhd/types/serial.hpp> //uart iface
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+
+class b200_uart: boost::noncopyable, public uhd::uart_iface
+{
+public:
+ typedef boost::shared_ptr<b200_uart> sptr;
+ static sptr make(uhd::transport::zero_copy_if::sptr, const boost::uint32_t sid);
+ virtual void handle_uart_packet(uhd::transport::managed_recv_buffer::sptr buff) = 0;
+ virtual void set_baud_divider(const double baud_div) = 0;
+};
+
+
+#endif /* INCLUDED_B200_UART_HPP */
diff --git a/host/lib/usrp/common/CMakeLists.txt b/host/lib/usrp/common/CMakeLists.txt
index fa07e3d1d..1728b63f9 100644
--- a/host/lib/usrp/common/CMakeLists.txt
+++ b/host/lib/usrp/common/CMakeLists.txt
@@ -1,5 +1,5 @@
#
-# Copyright 2011-2012 Ettus Research LLC
+# Copyright 2011-2013 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
@@ -29,6 +29,8 @@ ENDIF(ENABLE_USB)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/adf4001_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/ad9361_ctrl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/apply_corrections.cpp
${CMAKE_CURRENT_SOURCE_DIR}/validate_subdev_spec.cpp
${CMAKE_CURRENT_SOURCE_DIR}/recv_packet_demuxer.cpp
diff --git a/host/lib/usrp/common/ad9361_ctrl.cpp b/host/lib/usrp/common/ad9361_ctrl.cpp
new file mode 100644
index 000000000..1afa2fbb7
--- /dev/null
+++ b/host/lib/usrp/common/ad9361_ctrl.cpp
@@ -0,0 +1,173 @@
+//
+// Copyright 2012-2013 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 "ad9361_ctrl.hpp"
+#include "ad9361_transaction.h"
+#include <uhd/exception.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhd/utils/msg.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/format.hpp>
+#include <cstring>
+
+//! compat strnlen for platforms that dont have it
+static size_t my_strnlen(const char *str, size_t max)
+{
+ const char *end = (const char *)std::memchr((const void *)str, 0, max);
+ if (end == NULL) return max;
+ return (size_t)(end - str);
+}
+
+using namespace uhd;
+
+struct ad9361_ctrl_impl : public ad9361_ctrl
+{
+ ad9361_ctrl_impl(ad9361_ctrl_iface_sptr iface):
+ _iface(iface), _seq(0)
+ {
+ ad9361_transaction_t request;
+
+ request.action = AD9361_ACTION_ECHO;
+ this->do_transaction(request);
+
+ request.action = AD9361_ACTION_INIT;
+ this->do_transaction(request);
+ }
+
+ double set_gain(const std::string &which, const double value)
+ {
+ ad9361_transaction_t request;
+
+ if (which == "RX1") request.action = AD9361_ACTION_SET_RX1_GAIN;
+ if (which == "RX2") request.action = AD9361_ACTION_SET_RX2_GAIN;
+ if (which == "TX1") request.action = AD9361_ACTION_SET_TX1_GAIN;
+ if (which == "TX2") request.action = AD9361_ACTION_SET_TX2_GAIN;
+
+ ad9361_double_pack(value, request.value.gain);
+ const ad9361_transaction_t reply = this->do_transaction(request);
+ return ad9361_double_unpack(reply.value.gain);
+ }
+
+ //! set a new clock rate, return the exact value
+ double set_clock_rate(const double rate)
+ {
+ //warning for known trouble rates
+ if (rate > 56e6) UHD_MSG(warning) << boost::format(
+ "The requested clock rate %f MHz may cause slow configuration.\n"
+ "The driver recommends a master clock rate less than %f MHz.\n"
+ ) % (rate/1e6) % 56.0 << std::endl;
+
+ //clip to known bounds
+ const meta_range_t clock_rate_range = ad9361_ctrl::get_clock_rate_range();
+ const double clipped_rate = clock_rate_range.clip(rate);
+
+ ad9361_transaction_t request;
+ request.action = AD9361_ACTION_SET_CLOCK_RATE;
+ ad9361_double_pack(clipped_rate, request.value.rate);
+ const ad9361_transaction_t reply = this->do_transaction(request);
+ return ad9361_double_unpack(reply.value.rate);
+ }
+
+ //! set which RX and TX chains/antennas are active
+ void set_active_chains(bool tx1, bool tx2, bool rx1, bool rx2)
+ {
+ boost::uint32_t mask = 0;
+ if (tx1) mask |= (1 << 0);
+ if (tx2) mask |= (1 << 1);
+ if (rx1) mask |= (1 << 2);
+ if (rx2) mask |= (1 << 3);
+
+ ad9361_transaction_t request;
+ request.action = AD9361_ACTION_SET_ACTIVE_CHAINS;
+ request.value.enable_mask = mask;
+ this->do_transaction(request);
+ }
+
+ //! tune the given frontend, return the exact value
+ double tune(const std::string &which, const double freq)
+ {
+ //clip to known bounds
+ const meta_range_t freq_range = ad9361_ctrl::get_rf_freq_range();
+ const double clipped_freq = freq_range.clip(freq);
+
+ ad9361_transaction_t request;
+
+ if (which[0] == 'R') request.action = AD9361_ACTION_SET_RX_FREQ;
+ if (which[0] == 'T') request.action = AD9361_ACTION_SET_TX_FREQ;
+
+ const double value = ad9361_ctrl::get_rf_freq_range().clip(clipped_freq);
+ ad9361_double_pack(value, request.value.freq);
+ const ad9361_transaction_t reply = this->do_transaction(request);
+ return ad9361_double_unpack(reply.value.freq);
+ }
+
+ //! turn on/off Catalina's data port loopback
+ void data_port_loopback(const bool on)
+ {
+ ad9361_transaction_t request;
+ request.action = AD9361_ACTION_SET_CODEC_LOOP;
+ request.value.codec_loop = on? 1 : 0;
+ this->do_transaction(request);
+ }
+
+ ad9361_transaction_t do_transaction(const ad9361_transaction_t &request)
+ {
+ boost::mutex::scoped_lock lock(_mutex);
+
+ //declare in/out buffers
+ unsigned char in_buff[64] = {};
+ unsigned char out_buff[64] = {};
+
+ //copy the input transaction
+ std::memcpy(in_buff, &request, sizeof(request));
+
+ //fill in other goodies
+ ad9361_transaction_t *in = (ad9361_transaction_t *)in_buff;
+ in->version = AD9361_TRANSACTION_VERSION;
+ in->sequence = _seq++;
+
+ //transact
+ _iface->ad9361_transact(in_buff, out_buff);
+ ad9361_transaction_t *out = (ad9361_transaction_t *)out_buff;
+
+ //sanity checks
+ UHD_ASSERT_THROW(out->version == in->version);
+ UHD_ASSERT_THROW(out->sequence == in->sequence);
+
+ //handle errors
+ const size_t len = my_strnlen(out->error_msg, AD9361_TRANSACTION_MAX_ERROR_MSG);
+ const std::string error_msg(out->error_msg, len);
+ if (not error_msg.empty()) throw uhd::runtime_error("ad9361 do transaction: " + error_msg);
+
+ //return result done!
+ return *out;
+ }
+
+ ad9361_ctrl_iface_sptr _iface;
+ size_t _seq;
+ boost::mutex _mutex;
+
+};
+
+
+/***********************************************************************
+ * Make an instance of the implementation
+ **********************************************************************/
+ad9361_ctrl::sptr ad9361_ctrl::make(ad9361_ctrl_iface_sptr iface)
+{
+ return sptr(new ad9361_ctrl_impl(iface));
+}
diff --git a/host/lib/usrp/common/ad9361_ctrl.hpp b/host/lib/usrp/common/ad9361_ctrl.hpp
new file mode 100644
index 000000000..fd8012764
--- /dev/null
+++ b/host/lib/usrp/common/ad9361_ctrl.hpp
@@ -0,0 +1,128 @@
+//
+// Copyright 2012-2013 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_AD9361_CTRL_HPP
+#define INCLUDED_AD9361_CTRL_HPP
+
+#include <uhd/transport/zero_copy.hpp>
+#include <uhd/types/serial.hpp>
+#include <uhd/types/ranges.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+#include <boost/function.hpp>
+#include <vector>
+#include <string>
+
+
+struct ad9361_ctrl_iface_type
+{
+ virtual void ad9361_transact(const unsigned char in_buff[64], unsigned char out_buff[64]) = 0;
+};
+typedef boost::shared_ptr<ad9361_ctrl_iface_type> ad9361_ctrl_iface_sptr;
+
+
+struct ad9361_ctrl_over_zc : ad9361_ctrl_iface_type
+{
+ ad9361_ctrl_over_zc(uhd::transport::zero_copy_if::sptr xport)
+ {
+ _xport = xport;
+ }
+
+ void ad9361_transact(const unsigned char in_buff[64], unsigned char out_buff[64])
+ {
+ {
+ uhd::transport::managed_send_buffer::sptr buff = _xport->get_send_buff(10.0);
+ if (not buff or buff->size() < 64) throw std::runtime_error("ad9361_ctrl_over_zc send timeout");
+ std::memcpy(buff->cast<void *>(), in_buff, 64);
+ buff->commit(64);
+ }
+ {
+ uhd::transport::managed_recv_buffer::sptr buff = _xport->get_recv_buff(10.0);
+ if (not buff or buff->size() < 64) throw std::runtime_error("ad9361_ctrl_over_zc recv timeout");
+ std::memcpy(out_buff, buff->cast<const void *>(), 64);
+ }
+ }
+
+ uhd::transport::zero_copy_if::sptr _xport;
+};
+
+
+class ad9361_ctrl : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<ad9361_ctrl> sptr;
+
+ //! make a new codec control object
+ static sptr make(ad9361_ctrl_iface_sptr iface);
+
+ //! Get a list of gain names for RX or TX
+ static std::vector<std::string> get_gain_names(const std::string &/*which*/)
+ {
+ return std::vector<std::string>(1, "PGA");
+ }
+
+ //! get the gain range for a particular gain element
+ static uhd::meta_range_t get_gain_range(const std::string &which)
+ {
+ if(which[0] == 'R') {
+ return uhd::meta_range_t(0.0, 73.0, 1.0);
+ } else {
+ return uhd::meta_range_t(0.0, 89.75, 0.25);
+ }
+ }
+
+ //! get the freq range for the frontend which
+ static uhd::meta_range_t get_rf_freq_range(void)
+ {
+ return uhd::meta_range_t(50e6, 6e9);
+ }
+
+ //! get the filter range for the frontend which
+ static uhd::meta_range_t get_bw_filter_range(const std::string &/*which*/)
+ {
+ return uhd::meta_range_t(200e3, 56e6);
+ }
+
+ //! get the clock rate range for the frontend
+ static uhd::meta_range_t get_clock_rate_range(void)
+ {
+ //return uhd::meta_range_t(220e3, 61.44e6);
+ return uhd::meta_range_t(5e6, 61.44e6); //5 MHz DCM low end
+ }
+
+ //! set the filter bandwidth for the frontend
+ double set_bw_filter(const std::string &/*which*/, const double /*bw*/)
+ {
+ return 56e6; //TODO
+ }
+
+ //! set the gain for a particular gain element
+ virtual double set_gain(const std::string &which, const double value) = 0;
+
+ //! set a new clock rate, return the exact value
+ virtual double set_clock_rate(const double rate) = 0;
+
+ //! set which RX and TX chains/antennas are active
+ virtual void set_active_chains(bool tx1, bool tx2, bool rx1, bool rx2) = 0;
+
+ //! tune the given frontend, return the exact value
+ virtual double tune(const std::string &which, const double value) = 0;
+
+ //! turn on/off Catalina's data port loopback
+ virtual void data_port_loopback(const bool on) = 0;
+};
+
+#endif /* INCLUDED_AD9361_CTRL_HPP */
diff --git a/host/lib/usrp/common/ad9361_transaction.h b/host/lib/usrp/common/ad9361_transaction.h
new file mode 100644
index 000000000..7cbad5908
--- /dev/null
+++ b/host/lib/usrp/common/ad9361_transaction.h
@@ -0,0 +1,108 @@
+//
+// Copyright 2013 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_AD9361_TRANSACTION_H
+#define INCLUDED_AD9361_TRANSACTION_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//various constants
+#define AD9361_TRANSACTION_VERSION 0x4
+#define AD9361_TRANSACTION_MAX_ERROR_MSG 40
+
+//action types
+#define AD9361_ACTION_ECHO 0
+#define AD9361_ACTION_INIT 1
+#define AD9361_ACTION_SET_RX1_GAIN 2
+#define AD9361_ACTION_SET_TX1_GAIN 3
+#define AD9361_ACTION_SET_RX2_GAIN 4
+#define AD9361_ACTION_SET_TX2_GAIN 5
+#define AD9361_ACTION_SET_RX_FREQ 6
+#define AD9361_ACTION_SET_TX_FREQ 7
+#define AD9361_ACTION_SET_CODEC_LOOP 8
+#define AD9361_ACTION_SET_CLOCK_RATE 9
+#define AD9361_ACTION_SET_ACTIVE_CHAINS 10
+
+typedef union
+{
+ double d;
+ uint32_t x[2];
+} ad9361_double_union_t;
+
+static inline void ad9361_double_pack(const double input, uint32_t output[2])
+{
+ ad9361_double_union_t p = {};
+ p.d = input;
+ output[0] = p.x[0];
+ output[1] = p.x[1];
+}
+
+static inline double ad9361_double_unpack(const uint32_t input[2])
+{
+ ad9361_double_union_t p = {};
+ p.x[0] = input[0];
+ p.x[1] = input[1];
+ return p.d;
+}
+
+typedef struct
+{
+ //version is expected to be AD9361_TRANSACTION_VERSION
+ //check otherwise for compatibility
+ uint32_t version;
+
+ //sequence number - increment every call for sanity
+ uint32_t sequence;
+
+ //action tells us what to do, see AD9361_ACTION_*
+ uint32_t action;
+
+ union
+ {
+ //enable mask for chains
+ uint32_t enable_mask;
+
+ //true to enable codec internal loopback
+ uint32_t codec_loop;
+
+ //freq holds request LO freq and result from tune
+ uint32_t freq[2];
+
+ //gain holds request gain and result from action
+ uint32_t gain[2];
+
+ //rate holds request clock rate and result from action
+ uint32_t rate[2];
+
+ } value;
+
+ //error message comes back as a reply -
+ //set to null string for no error \0
+ char error_msg[];
+
+} ad9361_transaction_t;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* INCLUDED_AD9361_TRANSACTION_H */
diff --git a/host/lib/usrp/common/adf4001_ctrl.cpp b/host/lib/usrp/common/adf4001_ctrl.cpp
new file mode 100644
index 000000000..46171c7ce
--- /dev/null
+++ b/host/lib/usrp/common/adf4001_ctrl.cpp
@@ -0,0 +1,151 @@
+//
+// Copyright 2013 Ettus Research LLC
+//
+// Original ADF4001 driver written by: bistromath
+// Mar 1, 2013
+//
+// Re-used and re-licensed with permission.
+//
+// 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 "adf4001_ctrl.hpp"
+
+#include <uhd/utils/msg.hpp>
+#include <iostream>
+#include <iomanip>
+
+using namespace uhd;
+using namespace uhd::usrp;
+
+adf4001_regs_t::adf4001_regs_t(void) {
+ ref_counter = 0;
+ n = 0;
+ charge_pump_current_1 = 0;
+ charge_pump_current_2 = 0;
+ anti_backlash_width = ANTI_BACKLASH_WIDTH_2_9NS;
+ lock_detect_precision = LOCK_DETECT_PRECISION_3CYC;
+ charge_pump_gain = CHARGE_PUMP_GAIN_1;
+ counter_reset = COUNTER_RESET_NORMAL;
+ power_down = POWER_DOWN_NORMAL;
+ muxout = MUXOUT_TRISTATE_OUT;
+ phase_detector_polarity = PHASE_DETECTOR_POLARITY_NEGATIVE;
+ charge_pump_mode = CHARGE_PUMP_TRISTATE;
+ fastlock_mode = FASTLOCK_MODE_DISABLED;
+ timer_counter_control = TIMEOUT_3CYC;
+}
+
+
+boost::uint32_t adf4001_regs_t::get_reg(boost::uint8_t addr) {
+ boost::uint32_t reg = 0;
+ switch (addr) {
+ case 0:
+ reg |= (boost::uint32_t(ref_counter) & 0x003FFF) << 2;
+ reg |= (boost::uint32_t(anti_backlash_width) & 0x000003) << 16;
+ reg |= (boost::uint32_t(lock_detect_precision) & 0x000001) << 20;
+ break;
+ case 1:
+ reg |= (boost::uint32_t(n) & 0x001FFF) << 8;
+ reg |= (boost::uint32_t(charge_pump_gain) & 0x000001) << 21;
+ break;
+ case 2:
+ reg |= (boost::uint32_t(counter_reset) & 0x000001) << 2;
+ reg |= (boost::uint32_t(power_down) & 0x000001) << 3;
+ reg |= (boost::uint32_t(muxout) & 0x000007) << 4;
+ reg |= (boost::uint32_t(phase_detector_polarity) & 0x000001) << 7;
+ reg |= (boost::uint32_t(charge_pump_mode) & 0x000001) << 8;
+ reg |= (boost::uint32_t(fastlock_mode) & 0x000003) << 9;
+ reg |= (boost::uint32_t(timer_counter_control) & 0x00000F) << 11;
+ reg |= (boost::uint32_t(charge_pump_current_1) & 0x000007) << 15;
+ reg |= (boost::uint32_t(charge_pump_current_2) & 0x000007) << 18;
+ reg |= (boost::uint32_t(power_down) & 0x000002) << 21;
+ break;
+ case 3:
+ reg |= (boost::uint32_t(counter_reset) & 0x000001) << 2;
+ reg |= (boost::uint32_t(power_down) & 0x000001) << 3;
+ reg |= (boost::uint32_t(muxout) & 0x000007) << 4;
+ reg |= (boost::uint32_t(phase_detector_polarity) & 0x000001) << 7;
+ reg |= (boost::uint32_t(charge_pump_mode) & 0x000001) << 8;
+ reg |= (boost::uint32_t(fastlock_mode) & 0x000003) << 9;
+ reg |= (boost::uint32_t(timer_counter_control) & 0x00000F) << 11;
+ reg |= (boost::uint32_t(charge_pump_current_1) & 0x000007) << 15;
+ reg |= (boost::uint32_t(charge_pump_current_2) & 0x000007) << 18;
+ reg |= (boost::uint32_t(power_down) & 0x000002) << 21;
+ break;
+ default:
+ break;
+ }
+
+ reg |= (boost::uint32_t(addr) & 0x03);
+
+ return reg;
+}
+
+
+adf4001_ctrl::adf4001_ctrl(spi_core_3000::sptr _spi, int slaveno):
+ spi_iface(_spi),
+ slaveno(slaveno)
+ {
+
+ spi_config.mosi_edge = spi_config_t::EDGE_RISE;
+
+ //set defaults
+ adf4001_regs.ref_counter = 1;
+ adf4001_regs.n = 4;
+ adf4001_regs.charge_pump_current_1 = 7;
+ adf4001_regs.charge_pump_current_2 = 7;
+ adf4001_regs.muxout = adf4001_regs_t::MUXOUT_DLD;
+ adf4001_regs.counter_reset = adf4001_regs_t::COUNTER_RESET_NORMAL;
+ adf4001_regs.phase_detector_polarity = adf4001_regs_t::PHASE_DETECTOR_POLARITY_POSITIVE;
+ adf4001_regs.charge_pump_mode = adf4001_regs_t::CHARGE_PUMP_TRISTATE;
+
+ //everything else should be defaults
+
+ program_regs();
+}
+
+void adf4001_ctrl::set_lock_to_ext_ref(bool external) {
+ if(external) {
+ adf4001_regs.charge_pump_mode = adf4001_regs_t::CHARGE_PUMP_NORMAL;
+ } else {
+ adf4001_regs.charge_pump_mode = adf4001_regs_t::CHARGE_PUMP_TRISTATE;
+ }
+
+ program_regs();
+}
+
+void adf4001_ctrl::program_regs(void) {
+ //no control over CE, only LE, therefore we use the initialization latch method
+ write_reg(3);
+ boost::this_thread::sleep(boost::posix_time::microseconds(1));
+
+ //write R counter latch (0)
+ write_reg(0);
+ boost::this_thread::sleep(boost::posix_time::microseconds(1));
+
+ //write N counter latch (1)
+ write_reg(1);
+ boost::this_thread::sleep(boost::posix_time::microseconds(1));
+}
+
+
+void adf4001_ctrl::write_reg(boost::uint8_t addr) {
+ boost::uint32_t reg = adf4001_regs.get_reg(addr); //load the reg data
+
+ spi_iface->transact_spi(slaveno,
+ spi_config,
+ reg,
+ 24,
+ false);
+}
diff --git a/host/lib/usrp/common/adf4001_ctrl.hpp b/host/lib/usrp/common/adf4001_ctrl.hpp
new file mode 100644
index 000000000..a16cff3fa
--- /dev/null
+++ b/host/lib/usrp/common/adf4001_ctrl.hpp
@@ -0,0 +1,142 @@
+//
+// Copyright 2013 Ettus Research LLC
+//
+// Original ADF4001 driver written by: bistromath
+// Mar 1, 2013
+//
+// Re-used and re-licensed with permission.
+//
+// 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_USRP_COMMON_ADF4001_HPP
+#define INCLUDED_LIBUHD_USRP_COMMON_ADF4001_HPP
+
+#include "spi_core_3000.hpp"
+#include <uhd/types/serial.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/thread/thread.hpp>
+
+namespace uhd { namespace usrp {
+
+class adf4001_regs_t {
+public:
+
+ /* Function prototypes */
+ boost::uint32_t get_reg(boost::uint8_t addr);
+ adf4001_regs_t(void);
+
+ /* Register values / addresses */
+ boost::uint16_t ref_counter; //14 bits
+ boost::uint16_t n; //13 bits
+ boost::uint8_t charge_pump_current_1; //3 bits
+ boost::uint8_t charge_pump_current_2; //3 bits
+
+ enum anti_backlash_width_t {
+ ANTI_BACKLASH_WIDTH_2_9NS = 0,
+ ANTI_BACKLASH_WIDTH_1_3NS = 1,
+ ANTI_BACKLASH_WIDTH_6_0NS = 2,
+ ANTI_BACKLASH_WIDTH_2_9NS_WAT = 3
+ };
+ anti_backlash_width_t anti_backlash_width;
+
+ enum lock_detect_precision_t {
+ LOCK_DETECT_PRECISION_3CYC = 0,
+ LOCK_DETECT_PRECISION_5CYC = 1
+ };
+ lock_detect_precision_t lock_detect_precision;
+ enum charge_pump_gain_t {
+ CHARGE_PUMP_GAIN_1 = 0,
+ CHARGE_PUMP_GAIN_2 = 1
+ };
+ charge_pump_gain_t charge_pump_gain;
+ enum counter_reset_t {
+ COUNTER_RESET_NORMAL = 0,
+ COUNTER_RESET_RESET = 1
+ };
+ counter_reset_t counter_reset;
+ enum power_down_t {
+ POWER_DOWN_NORMAL = 0,
+ POWER_DOWN_ASYNC = 1,
+ POWER_DOWN_SYNC = 3
+ };
+ power_down_t power_down;
+ enum muxout_t {
+ MUXOUT_TRISTATE_OUT = 0,
+ MUXOUT_DLD = 1,
+ MUXOUT_NDIV = 2,
+ MUXOUT_AVDD = 3,
+ MUXOUT_RDIV = 4,
+ MUXOUT_NCH_OD_ALD = 5,
+ MUXOUT_SDO = 6,
+ MUXOUT_GND = 7
+ };
+ muxout_t muxout;
+ enum phase_detector_polarity_t {
+ PHASE_DETECTOR_POLARITY_NEGATIVE = 0,
+ PHASE_DETECTOR_POLARITY_POSITIVE = 1
+ };
+ phase_detector_polarity_t phase_detector_polarity;
+ enum charge_pump_mode_t {
+ CHARGE_PUMP_NORMAL = 0,
+ CHARGE_PUMP_TRISTATE = 1
+ };
+ charge_pump_mode_t charge_pump_mode;
+ enum fastlock_mode_t {
+ FASTLOCK_MODE_DISABLED = 0,
+ FASTLOCK_MODE_1 = 1,
+ FASTLOCK_MODE_2 = 2
+ };
+ fastlock_mode_t fastlock_mode;
+ enum timer_counter_control_t {
+ TIMEOUT_3CYC = 0,
+ TIMEOUT_7CYC = 1,
+ TIMEOUT_11CYC = 2,
+ TIMEOUT_15CYC = 3,
+ TIMEOUT_19CYC = 4,
+ TIMEOUT_23CYC = 5,
+ TIMEOUT_27CYC = 6,
+ TIMEOUT_31CYC = 7,
+ TIMEOUT_35CYC = 8,
+ TIMEOUT_39CYC = 9,
+ TIMEOUT_43CYC = 10,
+ TIMEOUT_47CYC = 11,
+ TIMEOUT_51CYC = 12,
+ TIMEOUT_55CYC = 13,
+ TIMEOUT_59CYC = 14,
+ TIMEOUT_63CYC = 15,
+ };
+ timer_counter_control_t timer_counter_control;
+};
+
+
+class adf4001_ctrl {
+public:
+
+ adf4001_ctrl(spi_core_3000::sptr _spi, int slaveno);
+ void set_lock_to_ext_ref(bool external);
+
+private:
+ spi_core_3000::sptr spi_iface;
+ int slaveno;
+ spi_config_t spi_config;
+ adf4001_regs_t adf4001_regs;
+
+ void program_regs(void);
+ void write_reg(boost::uint8_t addr);
+};
+
+}}
+
+#endif
diff --git a/host/lib/usrp/common/fifo_ctrl_excelsior.hpp b/host/lib/usrp/common/fifo_ctrl_excelsior.hpp
index c3ef65a2c..bd7777ffa 100644
--- a/host/lib/usrp/common/fifo_ctrl_excelsior.hpp
+++ b/host/lib/usrp/common/fifo_ctrl_excelsior.hpp
@@ -24,7 +24,7 @@
#include <uhd/transport/zero_copy.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/utility.hpp>
-#include "wb_iface.hpp"
+#include <uhd/types/wb_iface.hpp>
#include <string>
@@ -40,7 +40,8 @@ struct fifo_ctrl_excelsior_config
/*!
* Provide access to peek, poke, spi, and async messages.
*/
-class fifo_ctrl_excelsior : public wb_iface, public uhd::spi_iface{
+class fifo_ctrl_excelsior : public uhd::wb_iface, public uhd::spi_iface
+{
public:
typedef boost::shared_ptr<fifo_ctrl_excelsior> sptr;
diff --git a/host/lib/usrp/common/fx2_ctrl.cpp b/host/lib/usrp/common/fx2_ctrl.cpp
index 1f9cb84b3..6111efea9 100644
--- a/host/lib/usrp/common/fx2_ctrl.cpp
+++ b/host/lib/usrp/common/fx2_ctrl.cpp
@@ -411,8 +411,8 @@ public:
}
byte_vector_t read_eeprom(
- boost::uint8_t addr,
- boost::uint8_t offset,
+ boost::uint16_t addr,
+ boost::uint16_t offset,
size_t num_bytes
){
this->write_i2c(addr, byte_vector_t(1, offset));
@@ -432,7 +432,7 @@ public:
static const bool iface_debug = false;
static const size_t max_i2c_data_bytes = 64;
- void write_i2c(boost::uint8_t addr, const byte_vector_t &bytes)
+ void write_i2c(boost::uint16_t addr, const byte_vector_t &bytes)
{
UHD_ASSERT_THROW(bytes.size() < max_i2c_data_bytes);
@@ -442,7 +442,7 @@ public:
uhd::runtime_error("USRP: failed i2c write");
}
- byte_vector_t read_i2c(boost::uint8_t addr, size_t num_bytes)
+ byte_vector_t read_i2c(boost::uint16_t addr, size_t num_bytes)
{
UHD_ASSERT_THROW(num_bytes < max_i2c_data_bytes);
diff --git a/host/lib/usrp/common/recv_packet_demuxer.cpp b/host/lib/usrp/common/recv_packet_demuxer.cpp
index f2cfe3bb0..fe606213c 100644
--- a/host/lib/usrp/common/recv_packet_demuxer.cpp
+++ b/host/lib/usrp/common/recv_packet_demuxer.cpp
@@ -19,6 +19,8 @@
#include <uhd/utils/msg.hpp>
#include <uhd/utils/byteswap.hpp>
#include <boost/thread/mutex.hpp>
+#include <uhd/transport/vrt_if_packet.hpp>
+#include <uhd/types/metadata.hpp>
#include <queue>
#include <deque>
#include <vector>
@@ -27,6 +29,19 @@ using namespace uhd;
using namespace uhd::usrp;
using namespace uhd::transport;
+struct recv_pkt_demux_mrb : public managed_recv_buffer
+{
+public:
+ recv_pkt_demux_mrb(void){/*NOP*/}
+
+ void release(void)
+ {
+ delete this;
+ }
+
+ boost::uint32_t buff[10];
+};
+
static UHD_INLINE boost::uint32_t extract_sid(managed_recv_buffer::sptr &buff){
//ASSUME that the data is in little endian format
return uhd::wtohx(buff->cast<const boost::uint32_t *>()[1]);
@@ -66,7 +81,20 @@ public:
//otherwise queue and try again
if (rx_index < _queues.size()) _queues[rx_index].wrapper.push(buff);
- else UHD_MSG(error) << "Got a data packet with unknown SID " << extract_sid(buff) << std::endl;
+ else
+ {
+ UHD_MSG(error) << "Got a data packet with unknown SID " << extract_sid(buff) << std::endl;
+ recv_pkt_demux_mrb *mrb = new recv_pkt_demux_mrb();
+ vrt::if_packet_info_t info;
+ info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_DATA;
+ info.num_payload_words32 = 1;
+ info.num_payload_bytes = info.num_payload_words32*sizeof(boost::uint32_t);
+ info.has_sid = true;
+ info.sid = _sid_base + index;
+ vrt::if_hdr_pack_le(mrb->buff, info);
+ mrb->buff[info.num_header_words32] = rx_metadata_t::ERROR_CODE_OVERFLOW;
+ return mrb->make(mrb, mrb->buff, info.num_packet_words32*sizeof(boost::uint32_t));
+ }
}
}
diff --git a/host/lib/usrp/common/recv_packet_demuxer_3000.hpp b/host/lib/usrp/common/recv_packet_demuxer_3000.hpp
new file mode 100644
index 000000000..4fb6c4604
--- /dev/null
+++ b/host/lib/usrp/common/recv_packet_demuxer_3000.hpp
@@ -0,0 +1,127 @@
+//
+// Copyright 2013 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_USRP_COMMON_RECV_PACKET_DEMUXER_3000_HPP
+#define INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_3000_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/transport/zero_copy.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/thread.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/atomic.hpp>
+#include <uhd/types/time_spec.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <queue>
+#include <map>
+
+namespace uhd{ namespace usrp{
+
+ struct recv_packet_demuxer_3000
+ {
+ recv_packet_demuxer_3000(transport::zero_copy_if::sptr xport):
+ _xport(xport)
+ {/*NOP*/}
+
+ transport::managed_recv_buffer::sptr get_recv_buff(const boost::uint32_t sid, const double timeout)
+ {
+ const time_spec_t exit_time = time_spec_t(timeout) + time_spec_t::get_system_time();
+ transport::managed_recv_buffer::sptr buff;
+ buff = _internal_get_recv_buff(sid, timeout);
+ while (not buff) //loop until timeout
+ {
+ const time_spec_t delta = exit_time - time_spec_t::get_system_time();
+ const double new_timeout = delta.get_real_secs();
+ if (new_timeout < 0.0) break;
+ buff = _internal_get_recv_buff(sid, new_timeout);
+ }
+ return buff;
+ }
+
+ transport::managed_recv_buffer::sptr _internal_get_recv_buff(const boost::uint32_t sid, const double timeout)
+ {
+ transport::managed_recv_buffer::sptr buff;
+
+ //----------------------------------------------------------
+ //-- Check the queue to see if we already have a buffer
+ //----------------------------------------------------------
+ {
+ boost::mutex::scoped_lock l(mutex);
+ queue_type_t &queue = _queues[sid];
+ if (not queue.empty())
+ {
+ buff = queue.front();
+ queue.front().reset();
+ queue.pop();
+ return buff;
+ }
+ }
+
+ //----------------------------------------------------------
+ //-- Try to claim the transport or wait patiently
+ //----------------------------------------------------------
+ if (_claimed.cas(1, 0))
+ {
+ boost::mutex::scoped_lock l(mutex);
+ cond.timed_wait(l, boost::posix_time::microseconds(long(timeout*1e6)));
+ }
+
+ //----------------------------------------------------------
+ //-- Wait on the transport for input buffers
+ //----------------------------------------------------------
+ else
+ {
+ buff = _xport->get_recv_buff(timeout);
+ if (buff)
+ {
+ const boost::uint32_t new_sid = uhd::wtohx(buff->cast<const boost::uint32_t *>()[1]);
+ if (new_sid != sid)
+ {
+ boost::mutex::scoped_lock l(mutex);
+ if (_queues.count(new_sid) == 0) UHD_MSG(error)
+ << "recv packet demuxer unexpected sid 0x" << std::hex << new_sid << std::dec
+ << std::endl;
+ else _queues[new_sid].push(buff);
+ buff.reset();
+ }
+ }
+ _claimed.write(0);
+ cond.notify_all();
+ }
+ return buff;
+ }
+
+ void realloc_sid(const boost::uint32_t sid)
+ {
+ boost::mutex::scoped_lock l(mutex);
+ while(not _queues[sid].empty()) //allocated and clears if already allocated
+ {
+ _queues[sid].pop();
+ }
+ }
+
+ typedef std::queue<transport::managed_recv_buffer::sptr> queue_type_t;
+ std::map<boost::uint32_t, queue_type_t> _queues;
+ transport::zero_copy_if::sptr _xport;
+ uhd::atomic_uint32_t _claimed;
+ boost::condition_variable cond;
+ boost::mutex mutex;
+ };
+
+}} //namespace uhd::usrp
+
+#endif /* INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_3000_HPP */
diff --git a/host/lib/usrp/cores/CMakeLists.txt b/host/lib/usrp/cores/CMakeLists.txt
index 3192b0774..f28ae040f 100644
--- a/host/lib/usrp/cores/CMakeLists.txt
+++ b/host/lib/usrp/cores/CMakeLists.txt
@@ -1,5 +1,5 @@
#
-# Copyright 2011-2012 Ettus Research LLC
+# Copyright 2011-2013 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
@@ -32,4 +32,12 @@ LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/rx_frontend_core_200.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tx_frontend_core_200.cpp
${CMAKE_CURRENT_SOURCE_DIR}/user_settings_core_200.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/rx_vita_core_3000.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/tx_vita_core_3000.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/time_core_3000.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/spi_core_3000.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/i2c_core_100_wb32.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/rx_dsp_core_3000.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/tx_dsp_core_3000.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/radio_ctrl_core_3000.cpp
)
diff --git a/host/lib/usrp/cores/gpio_core_200.cpp b/host/lib/usrp/cores/gpio_core_200.cpp
index cdab70b8d..51c23aa4b 100644
--- a/host/lib/usrp/cores/gpio_core_200.cpp
+++ b/host/lib/usrp/cores/gpio_core_200.cpp
@@ -104,3 +104,35 @@ private:
gpio_core_200::sptr gpio_core_200::make(wb_iface::sptr iface, const size_t base, const size_t rb_addr){
return sptr(new gpio_core_200_impl(iface, base, rb_addr));
}
+
+class gpio_core_200_32wo_impl : public gpio_core_200_32wo{
+public:
+ gpio_core_200_32wo_impl(wb_iface::sptr iface, const size_t base):
+ _iface(iface), _base(base)
+ {
+ _iface->poke32(REG_GPIO_DDR, 0xffffffff);
+ }
+
+ void set_atr_reg(const atr_reg_t atr, const boost::uint32_t value){
+ if (atr == dboard_iface::ATR_REG_IDLE) _iface->poke32(REG_GPIO_IDLE, value);
+ if (atr == dboard_iface::ATR_REG_TX_ONLY) _iface->poke32(REG_GPIO_TX_ONLY, value);
+ if (atr == dboard_iface::ATR_REG_RX_ONLY) _iface->poke32(REG_GPIO_RX_ONLY, value);
+ if (atr == dboard_iface::ATR_REG_FULL_DUPLEX) _iface->poke32(REG_GPIO_BOTH, value);
+ }
+
+ void set_all_regs(const boost::uint32_t value){
+ this->set_atr_reg(dboard_iface::ATR_REG_IDLE, value);
+ this->set_atr_reg(dboard_iface::ATR_REG_TX_ONLY, value);
+ this->set_atr_reg(dboard_iface::ATR_REG_RX_ONLY, value);
+ this->set_atr_reg(dboard_iface::ATR_REG_FULL_DUPLEX, value);
+ }
+
+private:
+ wb_iface::sptr _iface;
+ const size_t _base;
+
+};
+
+gpio_core_200_32wo::sptr gpio_core_200_32wo::make(wb_iface::sptr iface, const size_t base){
+ return sptr(new gpio_core_200_32wo_impl(iface, base));
+}
diff --git a/host/lib/usrp/cores/gpio_core_200.hpp b/host/lib/usrp/cores/gpio_core_200.hpp
index 278575874..15fe5f2dd 100644
--- a/host/lib/usrp/cores/gpio_core_200.hpp
+++ b/host/lib/usrp/cores/gpio_core_200.hpp
@@ -23,7 +23,7 @@
#include <boost/cstdint.hpp>
#include <boost/utility.hpp>
#include <boost/shared_ptr.hpp>
-#include "wb_iface.hpp"
+#include <uhd/types/wb_iface.hpp>
class gpio_core_200 : boost::noncopyable{
public:
@@ -33,7 +33,7 @@ public:
typedef uhd::usrp::dboard_iface::atr_reg_t atr_reg_t;
//! makes a new GPIO core from iface and slave base
- static sptr make(wb_iface::sptr iface, const size_t base, const size_t rb_addr);
+ static sptr make(uhd::wb_iface::sptr iface, const size_t base, const size_t rb_addr);
//! 1 = ATR
virtual void set_pin_ctrl(const unit_t unit, const boost::uint16_t value) = 0;
@@ -49,4 +49,18 @@ public:
};
+//! Simple wrapper for 32 bit write only
+class gpio_core_200_32wo : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<gpio_core_200_32wo> sptr;
+
+ typedef uhd::usrp::dboard_iface::atr_reg_t atr_reg_t;
+
+ static sptr make(uhd::wb_iface::sptr iface, const size_t);
+
+ virtual void set_atr_reg(const atr_reg_t atr, const boost::uint32_t value) = 0;
+
+ virtual void set_all_regs(const boost::uint32_t value) = 0;
+};
+
#endif /* INCLUDED_LIBUHD_USRP_GPIO_CORE_200_HPP */
diff --git a/host/lib/usrp/cores/i2c_core_100.cpp b/host/lib/usrp/cores/i2c_core_100.cpp
index ceeb3f518..9e8a226f2 100644
--- a/host/lib/usrp/cores/i2c_core_100.cpp
+++ b/host/lib/usrp/cores/i2c_core_100.cpp
@@ -70,7 +70,7 @@ public:
}
void write_i2c(
- boost::uint8_t addr,
+ boost::uint16_t addr,
const byte_vector_t &bytes
){
_iface->poke16(REG_I2C_DATA, (addr << 1) | 0); //addr and read bit (0)
@@ -93,7 +93,7 @@ public:
}
byte_vector_t read_i2c(
- boost::uint8_t addr,
+ boost::uint16_t addr,
size_t num_bytes
){
byte_vector_t bytes;
diff --git a/host/lib/usrp/cores/i2c_core_100.hpp b/host/lib/usrp/cores/i2c_core_100.hpp
index f7a5ae4f7..4e7a2874b 100644
--- a/host/lib/usrp/cores/i2c_core_100.hpp
+++ b/host/lib/usrp/cores/i2c_core_100.hpp
@@ -22,14 +22,14 @@
#include <uhd/types/serial.hpp>
#include <boost/utility.hpp>
#include <boost/shared_ptr.hpp>
-#include "wb_iface.hpp"
+#include <uhd/types/wb_iface.hpp>
class i2c_core_100 : boost::noncopyable, public uhd::i2c_iface{
public:
typedef boost::shared_ptr<i2c_core_100> sptr;
//! makes a new i2c core from iface and slave base
- static sptr make(wb_iface::sptr iface, const size_t base);
+ static sptr make(uhd::wb_iface::sptr iface, const size_t base);
};
#endif /* INCLUDED_LIBUHD_USRP_I2C_CORE_100_HPP */
diff --git a/host/lib/usrp/cores/i2c_core_100_wb32.cpp b/host/lib/usrp/cores/i2c_core_100_wb32.cpp
new file mode 100644
index 000000000..df6e6ff72
--- /dev/null
+++ b/host/lib/usrp/cores/i2c_core_100_wb32.cpp
@@ -0,0 +1,152 @@
+//
+// Copyright 2011-2013 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 "i2c_core_100_wb32.hpp"
+#include <uhd/exception.hpp>
+#include <uhd/utils/msg.hpp>
+#include <boost/thread/thread.hpp> //sleep
+
+#define REG_I2C_PRESCALER_LO _base + 0
+#define REG_I2C_PRESCALER_HI _base + 4
+#define REG_I2C_CTRL _base + 8
+#define REG_I2C_DATA _base + 12
+#define REG_I2C_CMD_STATUS _base + 16
+
+//
+// STA, STO, RD, WR, and IACK bits are cleared automatically
+//
+
+#define I2C_CTRL_EN (1 << 7) // core enable
+#define I2C_CTRL_IE (1 << 6) // interrupt enable
+
+#define I2C_CMD_START (1 << 7) // generate (repeated) start condition
+#define I2C_CMD_STOP (1 << 6) // generate stop condition
+#define I2C_CMD_RD (1 << 5) // read from slave
+#define I2C_CMD_WR (1 << 4) // write to slave
+#define I2C_CMD_NACK (1 << 3) // when a rcvr, send ACK (ACK=0) or NACK (ACK=1)
+#define I2C_CMD_RSVD_2 (1 << 2) // reserved
+#define I2C_CMD_RSVD_1 (1 << 1) // reserved
+#define I2C_CMD_IACK (1 << 0) // set to clear pending interrupt
+
+#define I2C_ST_RXACK (1 << 7) // Received acknowledgement from slave (1 = NAK, 0 = ACK)
+#define I2C_ST_BUSY (1 << 6) // 1 after START signal detected; 0 after STOP signal detected
+#define I2C_ST_AL (1 << 5) // Arbitration lost. 1 when core lost arbitration
+#define I2C_ST_RSVD_4 (1 << 4) // reserved
+#define I2C_ST_RSVD_3 (1 << 3) // reserved
+#define I2C_ST_RSVD_2 (1 << 2) // reserved
+#define I2C_ST_TIP (1 << 1) // Transfer-in-progress
+#define I2C_ST_IP (1 << 0) // Interrupt pending
+
+using namespace uhd;
+
+class i2c_core_100_wb32_wb32_impl : public i2c_core_100_wb32{
+public:
+ i2c_core_100_wb32_wb32_impl(wb_iface::sptr iface, const size_t base):
+ _iface(iface), _base(base)
+ {
+ //init I2C FPGA interface.
+ _iface->poke32(REG_I2C_CTRL, 0x0000);
+ _iface->poke32(REG_I2C_CTRL, I2C_CTRL_EN); //enable I2C core
+ }
+
+ void set_clock_rate(const double rate)
+ {
+ static const boost::uint32_t i2c_datarate = 400000;
+ boost::uint16_t prescaler = rate / (i2c_datarate*5) - 1;
+ _iface->poke32(REG_I2C_PRESCALER_LO, prescaler & 0xFF);
+ _iface->poke32(REG_I2C_PRESCALER_HI, (prescaler >> 8) & 0xFF);
+ }
+
+ void write_i2c(
+ boost::uint16_t addr,
+ const byte_vector_t &bytes
+ ){
+ _iface->poke32(REG_I2C_DATA, (addr << 1) | 0); //addr and read bit (0)
+ _iface->poke32(REG_I2C_CMD_STATUS, I2C_CMD_WR | I2C_CMD_START | (bytes.size() == 0 ? I2C_CMD_STOP : 0));
+
+ //wait for previous transfer to complete
+ if (not wait_chk_ack()) {
+ _iface->poke32(REG_I2C_CMD_STATUS, I2C_CMD_STOP);
+ return;
+ }
+
+ for (size_t i = 0; i < bytes.size(); i++) {
+ _iface->poke32(REG_I2C_DATA, bytes[i]);
+ _iface->poke32(REG_I2C_CMD_STATUS, I2C_CMD_WR | ((i == (bytes.size() - 1)) ? I2C_CMD_STOP : 0));
+ if(!wait_chk_ack()) {
+ _iface->poke32(REG_I2C_CMD_STATUS, I2C_CMD_STOP);
+ return;
+ }
+ }
+ }
+
+ byte_vector_t read_i2c(
+ boost::uint16_t addr,
+ size_t num_bytes
+ ){
+ byte_vector_t bytes;
+ if (num_bytes == 0) return bytes;
+
+ while (_iface->peek32(REG_I2C_CMD_STATUS) & I2C_ST_BUSY){
+ /* NOP */
+ }
+
+ _iface->poke32(REG_I2C_DATA, (addr << 1) | 1); //addr and read bit (1)
+ _iface->poke32(REG_I2C_CMD_STATUS, I2C_CMD_WR | I2C_CMD_START);
+ //wait for previous transfer to complete
+ if (not wait_chk_ack()) {
+ _iface->poke32(REG_I2C_CMD_STATUS, I2C_CMD_STOP);
+ }
+ for (size_t i = 0; i < num_bytes; i++) {
+ _iface->poke32(REG_I2C_CMD_STATUS, I2C_CMD_RD | ((num_bytes == i+1) ? (I2C_CMD_STOP | I2C_CMD_NACK) : 0));
+ i2c_wait();
+ bytes.push_back(boost::uint8_t(_iface->peek32(REG_I2C_DATA)));
+ }
+ return bytes;
+ }
+
+ //override read_eeprom so we can write once, read all N bytes
+ //the default implementation calls read i2c once per byte
+ byte_vector_t read_eeprom(boost::uint16_t addr, boost::uint16_t offset, size_t num_bytes)
+ {
+ this->write_i2c(addr, byte_vector_t(1, offset));
+ return this->read_i2c(addr, num_bytes);
+ }
+
+private:
+ void i2c_wait(void) {
+ for (size_t i = 0; i < 10; i++)
+ {
+ if ((_iface->peek32(REG_I2C_CMD_STATUS) & I2C_ST_TIP) == 0) return;
+ boost::this_thread::sleep(boost::posix_time::milliseconds(1));
+ }
+ UHD_MSG(error) << "i2c_core_100_wb32: i2c_wait timeout" << std::endl;
+ }
+
+ bool wait_chk_ack(void){
+ i2c_wait();
+ return (_iface->peek32(REG_I2C_CMD_STATUS) & I2C_ST_RXACK) == 0;
+ }
+
+ wb_iface::sptr _iface;
+ const size_t _base;
+};
+
+i2c_core_100_wb32::sptr i2c_core_100_wb32::make(wb_iface::sptr iface, const size_t base)
+{
+ return sptr(new i2c_core_100_wb32_wb32_impl(iface, base));
+}
diff --git a/host/lib/usrp/cores/i2c_core_100_wb32.hpp b/host/lib/usrp/cores/i2c_core_100_wb32.hpp
new file mode 100644
index 000000000..b5912ba9a
--- /dev/null
+++ b/host/lib/usrp/cores/i2c_core_100_wb32.hpp
@@ -0,0 +1,37 @@
+//
+// Copyright 2011-2013 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_USRP_I2C_CORE_100_WB32_HPP
+#define INCLUDED_LIBUHD_USRP_I2C_CORE_100_WB32_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/types/serial.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include <uhd/types/wb_iface.hpp>
+
+class i2c_core_100_wb32 : boost::noncopyable, public uhd::i2c_iface{
+public:
+ typedef boost::shared_ptr<i2c_core_100_wb32> sptr;
+
+ //! makes a new i2c core from iface and slave base
+ static sptr make(uhd::wb_iface::sptr iface, const size_t base);
+
+ virtual void set_clock_rate(const double rate) = 0;
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_I2C_CORE_100_WB32_HPP */
diff --git a/host/lib/usrp/cores/i2c_core_200.cpp b/host/lib/usrp/cores/i2c_core_200.cpp
index 1b882c54a..6010ac5a2 100644
--- a/host/lib/usrp/cores/i2c_core_200.cpp
+++ b/host/lib/usrp/cores/i2c_core_200.cpp
@@ -73,7 +73,7 @@ public:
}
void write_i2c(
- boost::uint8_t addr,
+ boost::uint16_t addr,
const byte_vector_t &bytes
){
this->poke(REG_I2C_WR_DATA, (addr << 1) | 0); //addr and read bit (0)
@@ -96,7 +96,7 @@ public:
}
byte_vector_t read_i2c(
- boost::uint8_t addr,
+ boost::uint16_t addr,
size_t num_bytes
){
byte_vector_t bytes;
diff --git a/host/lib/usrp/cores/i2c_core_200.hpp b/host/lib/usrp/cores/i2c_core_200.hpp
index 508855985..1b20455d3 100644
--- a/host/lib/usrp/cores/i2c_core_200.hpp
+++ b/host/lib/usrp/cores/i2c_core_200.hpp
@@ -22,14 +22,14 @@
#include <uhd/types/serial.hpp>
#include <boost/utility.hpp>
#include <boost/shared_ptr.hpp>
-#include "wb_iface.hpp"
+#include <uhd/types/wb_iface.hpp>
class i2c_core_200 : boost::noncopyable, public uhd::i2c_iface{
public:
typedef boost::shared_ptr<i2c_core_200> sptr;
//! makes a new i2c core from iface and slave base
- static sptr make(wb_iface::sptr iface, const size_t base, const size_t readback);
+ static sptr make(uhd::wb_iface::sptr iface, const size_t base, const size_t readback);
};
#endif /* INCLUDED_LIBUHD_USRP_I2C_CORE_200_HPP */
diff --git a/host/lib/usrp/cores/radio_ctrl_core_3000.cpp b/host/lib/usrp/cores/radio_ctrl_core_3000.cpp
new file mode 100644
index 000000000..0d6e1c665
--- /dev/null
+++ b/host/lib/usrp/cores/radio_ctrl_core_3000.cpp
@@ -0,0 +1,344 @@
+//
+// Copyright 2012-2013 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 "radio_ctrl_core_3000.hpp"
+#include "async_packet_handler.hpp"
+#include <uhd/exception.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/safe_call.hpp>
+#include <uhd/transport/bounded_buffer.hpp>
+#include <uhd/transport/vrt_if_packet.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/format.hpp>
+#include <boost/bind.hpp>
+#include <queue>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+
+static const double ACK_TIMEOUT = 2.0; //supposed to be worst case practical timeout
+static const double MASSIVE_TIMEOUT = 10.0; //for when we wait on a timed command
+static const size_t SR_READBACK = 32;
+
+class radio_ctrl_core_3000_impl: public radio_ctrl_core_3000
+{
+public:
+
+ radio_ctrl_core_3000_impl(const bool big_endian,
+ uhd::transport::zero_copy_if::sptr ctrl_xport,
+ uhd::transport::zero_copy_if::sptr resp_xport,
+ const boost::uint32_t sid, const std::string &name) :
+ _link_type(vrt::if_packet_info_t::LINK_TYPE_CHDR), _packet_type(
+ vrt::if_packet_info_t::PACKET_TYPE_CONTEXT), _bige(
+ big_endian), _ctrl_xport(ctrl_xport), _resp_xport(
+ resp_xport), _sid(sid), _name(name), _seq_out(0), _timeout(
+ ACK_TIMEOUT), _resp_queue(128/*max response msgs*/), _resp_queue_size(
+ _resp_xport ? _resp_xport->get_num_recv_frames() : 3)
+ {
+ UHD_LOG<< "radio_ctrl_core_3000_impl() " << _name << std::endl;
+ if (resp_xport)
+ {
+ while (resp_xport->get_recv_buff(0.0)) {} //flush
+ }
+ this->set_time(uhd::time_spec_t(0.0));
+ this->set_tick_rate(1.0); //something possible but bogus
+ }
+
+ ~radio_ctrl_core_3000_impl(void)
+ {
+ UHD_LOG << "~radio_ctrl_core_3000_impl() " << _name << std::endl;
+ _timeout = ACK_TIMEOUT; //reset timeout to something small
+ UHD_SAFE_CALL(
+ this->peek32(0);//dummy peek with the purpose of ack'ing all packets
+ _async_task.reset();//now its ok to release the task
+ )
+ }
+
+ /*******************************************************************
+ * Peek and poke 32 bit implementation
+ ******************************************************************/
+ void poke32(const wb_addr_type addr, const boost::uint32_t data)
+ {
+ boost::mutex::scoped_lock lock(_mutex);
+ UHD_LOGV(always) << _name << std::hex << " addr 0x" << addr << " data 0x" << data << std::dec << std::endl;
+
+ this->send_pkt(addr/4, data);
+ this->wait_for_ack(false);
+ }
+
+ boost::uint32_t peek32(const wb_addr_type addr)
+ {
+ boost::mutex::scoped_lock lock(_mutex);
+ UHD_LOGV(always) << _name << std::hex << " addr 0x" << addr << std::dec << std::endl;
+ this->send_pkt(SR_READBACK, addr/8);
+ this->wait_for_ack(false);
+
+ this->send_pkt(0);
+ const boost::uint64_t res = this->wait_for_ack(true);
+ const boost::uint32_t lo = boost::uint32_t(res & 0xffffffff);
+ const boost::uint32_t hi = boost::uint32_t(res >> 32);
+ return ((addr/4) & 0x1)? hi : lo;
+ }
+
+ boost::uint64_t peek64(const wb_addr_type addr)
+ {
+ boost::mutex::scoped_lock lock(_mutex);
+ UHD_LOGV(always) << _name << std::hex << " addr 0x" << addr << std::dec << std::endl;
+
+ this->send_pkt(SR_READBACK, addr/8);
+ this->wait_for_ack(false);
+
+ this->send_pkt(0);
+ return this->wait_for_ack(true);
+ }
+
+ /*******************************************************************
+ * Update methods for time
+ ******************************************************************/
+ void set_time(const uhd::time_spec_t &time)
+ {
+ boost::mutex::scoped_lock lock(_mutex);
+ _time = time;
+ _use_time = _time != uhd::time_spec_t(0.0);
+ if (_use_time) _timeout = MASSIVE_TIMEOUT; //permanently sets larger timeout
+ }
+
+ void set_tick_rate(const double rate)
+ {
+ boost::mutex::scoped_lock lock(_mutex);
+ _tick_rate = rate;
+ }
+
+private:
+ // This is the buffer type for messages in radio control core.
+ struct resp_buff_type
+ {
+ boost::uint32_t data[8];
+ };
+
+ /*******************************************************************
+ * Primary control and interaction private methods
+ ******************************************************************/
+ UHD_INLINE void send_pkt(const boost::uint32_t addr, const boost::uint32_t data = 0)
+ {
+ managed_send_buffer::sptr buff = _ctrl_xport->get_send_buff(0.0);
+ if (not buff) {
+ throw uhd::runtime_error("fifo ctrl timed out getting a send buffer");
+ }
+ boost::uint32_t *pkt = buff->cast<boost::uint32_t *>();
+
+ //load packet info
+ vrt::if_packet_info_t packet_info;
+ packet_info.link_type = _link_type;
+ packet_info.packet_type = _packet_type;
+ packet_info.num_payload_words32 = 2;
+ packet_info.num_payload_bytes = packet_info.num_payload_words32*sizeof(boost::uint32_t);
+ packet_info.packet_count = _seq_out;
+ packet_info.tsf = _time.to_ticks(_tick_rate);
+ packet_info.sob = false;
+ packet_info.eob = false;
+ packet_info.sid = _sid;
+ packet_info.has_sid = true;
+ packet_info.has_cid = false;
+ packet_info.has_tsi = false;
+ packet_info.has_tsf = _use_time;
+ packet_info.has_tlr = false;
+
+ //load header
+ if (_bige) vrt::if_hdr_pack_be(pkt, packet_info);
+ else vrt::if_hdr_pack_le(pkt, packet_info);
+
+ //load payload
+ pkt[packet_info.num_header_words32+0] = (_bige)? uhd::htonx(addr) : uhd::htowx(addr);
+ pkt[packet_info.num_header_words32+1] = (_bige)? uhd::htonx(data) : uhd::htowx(data);
+ //UHD_MSG(status) << boost::format("0x%08x, 0x%08x\n") % addr % data;
+ //send the buffer over the interface
+ _outstanding_seqs.push(_seq_out);
+ buff->commit(sizeof(boost::uint32_t)*(packet_info.num_packet_words32));
+
+ _seq_out++;//inc seq for next call
+ }
+
+ UHD_INLINE boost::uint64_t wait_for_ack(const bool readback)
+ {
+ while (readback or (_outstanding_seqs.size() >= _resp_queue_size))
+ {
+ UHD_LOGV(always) << _name << " wait_for_ack: " << "readback = " << readback << " outstanding_seqs.size() " << _outstanding_seqs.size() << std::endl;
+ //get seq to ack from outstanding packets list
+ UHD_ASSERT_THROW(not _outstanding_seqs.empty());
+ const size_t seq_to_ack = _outstanding_seqs.front();
+ _outstanding_seqs.pop();
+
+ //parse the packet
+ vrt::if_packet_info_t packet_info;
+ resp_buff_type resp_buff;
+ boost::uint32_t const *pkt = NULL;
+ managed_recv_buffer::sptr buff;
+
+ //get buffer from response endpoint - or die in timeout
+ if (_resp_xport)
+ {
+ buff = _resp_xport->get_recv_buff(_timeout);
+ try
+ {
+ UHD_ASSERT_THROW(bool(buff));
+ UHD_ASSERT_THROW(bool(buff->size()));
+ }
+ catch(const std::exception &ex)
+ {
+ throw uhd::io_error(str(boost::format("Radio ctrl (%s) no response packet - %s") % _name % ex.what()));
+ }
+ pkt = buff->cast<const boost::uint32_t *>();
+ packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t);
+ }
+
+ //get buffer from response endpoint - or die in timeout
+ else
+ {
+ /*
+ * Couldn't get message with haste.
+ * Now check both possible queues for messages.
+ * Messages should come in on _resp_queue,
+ * but could end up in dump_queue.
+ * If we don't get a message --> Die in timeout.
+ */
+ double accum_timeout = 0.0;
+ const double short_timeout = 0.005; // == 5ms
+ while(not (_resp_queue.pop_with_haste(resp_buff)
+ || check_dump_queue(resp_buff)
+ || _resp_queue.pop_with_timed_wait(resp_buff, short_timeout)
+ )){
+ /*
+ * If a message couldn't be received within a given timeout
+ * --> throw AssertionError!
+ */
+ accum_timeout += short_timeout;
+ UHD_ASSERT_THROW(accum_timeout < _timeout);
+ }
+
+ pkt = resp_buff.data;
+ packet_info.num_packet_words32 = sizeof(resp_buff)/sizeof(boost::uint32_t);
+ }
+
+ //parse the buffer
+ try
+ {
+ packet_info.link_type = _link_type;
+ if (_bige) vrt::if_hdr_unpack_be(pkt, packet_info);
+ else vrt::if_hdr_unpack_le(pkt, packet_info);
+ }
+ catch(const std::exception &ex)
+ {
+ UHD_MSG(error) << "Radio ctrl bad VITA packet: " << ex.what() << std::endl;
+ UHD_VAR(buff->size());
+ UHD_MSG(status) << std::hex << pkt[0] << std::dec << std::endl;
+ UHD_MSG(status) << std::hex << pkt[1] << std::dec << std::endl;
+ UHD_MSG(status) << std::hex << pkt[2] << std::dec << std::endl;
+ UHD_MSG(status) << std::hex << pkt[3] << std::dec << std::endl;
+ }
+
+ //check the buffer
+ try
+ {
+ UHD_ASSERT_THROW(packet_info.has_sid);
+ UHD_ASSERT_THROW(packet_info.sid == boost::uint32_t((_sid >> 16) | (_sid << 16)));
+ UHD_ASSERT_THROW(packet_info.packet_count == (seq_to_ack & 0xfff));
+ UHD_ASSERT_THROW(packet_info.num_payload_words32 == 2);
+ UHD_ASSERT_THROW(packet_info.packet_type == _packet_type);
+ }
+ catch(const std::exception &ex)
+ {
+ throw uhd::io_error(str(boost::format("Radio ctrl (%s) packet parse error - %s") % _name % ex.what()));
+ }
+
+ //return the readback value
+ if (readback and _outstanding_seqs.empty())
+ {
+ const boost::uint64_t hi = (_bige)? uhd::ntohx(pkt[packet_info.num_header_words32+0]) : uhd::wtohx(pkt[packet_info.num_header_words32+0]);
+ const boost::uint64_t lo = (_bige)? uhd::ntohx(pkt[packet_info.num_header_words32+1]) : uhd::wtohx(pkt[packet_info.num_header_words32+1]);
+ return ((hi << 32) | lo);
+ }
+ }
+
+ return 0;
+ }
+
+ /*
+ * If ctrl_core waits for a message that didn't arrive it can search for it in the dump queue.
+ * This actually happens during shutdown.
+ * handle_async_task can't access radio_ctrl_cores queue anymore thus it returns the corresponding message.
+ * msg_task class implements a dump_queue to store such messages.
+ * With check_dump_queue we can check if a message we are waiting for got stranded there.
+ * If a message got stuck we get it here and push it onto our own message_queue.
+ */
+ bool check_dump_queue(resp_buff_type b) {
+ boost::uint32_t recv_sid = (((_sid)<<16)|((_sid)>>16));
+ uhd::msg_task::msg_payload_t msg;
+ do{
+ msg = _async_task->get_msg_from_dump_queue(recv_sid);
+ }
+ while(msg.size() < 8 && msg.size() != 0);
+
+ if(msg.size() >= 8) {
+ memcpy(b.data, &msg.front(), 8);
+ return true;
+ }
+ return false;
+ }
+
+ void push_response(const boost::uint32_t *buff)
+ {
+ resp_buff_type resp_buff;
+ std::memcpy(resp_buff.data, buff, sizeof(resp_buff));
+ _resp_queue.push_with_haste(resp_buff);
+ }
+
+ void hold_task(uhd::msg_task::sptr task)
+ {
+ _async_task = task;
+ }
+
+ const vrt::if_packet_info_t::link_type_t _link_type;
+ const vrt::if_packet_info_t::packet_type_t _packet_type;
+ const bool _bige;
+ const uhd::transport::zero_copy_if::sptr _ctrl_xport;
+ const uhd::transport::zero_copy_if::sptr _resp_xport;
+ uhd::msg_task::sptr _async_task;
+ const boost::uint32_t _sid;
+ const std::string _name;
+ boost::mutex _mutex;
+ size_t _seq_out;
+ uhd::time_spec_t _time;
+ bool _use_time;
+ double _tick_rate;
+ double _timeout;
+ std::queue<size_t> _outstanding_seqs;
+ bounded_buffer<resp_buff_type> _resp_queue;
+ const size_t _resp_queue_size;
+};
+
+radio_ctrl_core_3000::sptr radio_ctrl_core_3000::make(const bool big_endian,
+ zero_copy_if::sptr ctrl_xport, zero_copy_if::sptr resp_xport,
+ const boost::uint32_t sid, const std::string &name)
+{
+ return sptr(
+ new radio_ctrl_core_3000_impl(big_endian, ctrl_xport, resp_xport,
+ sid, name));
+}
diff --git a/host/lib/usrp/cores/radio_ctrl_core_3000.hpp b/host/lib/usrp/cores/radio_ctrl_core_3000.hpp
new file mode 100644
index 000000000..51a307c10
--- /dev/null
+++ b/host/lib/usrp/cores/radio_ctrl_core_3000.hpp
@@ -0,0 +1,59 @@
+//
+// Copyright 2012-2013 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_USRP_RADIO_CTRL_3000_HPP
+#define INCLUDED_LIBUHD_USRP_RADIO_CTRL_3000_HPP
+
+#include <uhd/utils/msg_task.hpp>
+#include <uhd/types/time_spec.hpp>
+#include <uhd/transport/zero_copy.hpp>
+#include <uhd/types/wb_iface.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+#include <string>
+
+/*!
+ * Provide access to peek, poke for the radio ctrl module
+ */
+class radio_ctrl_core_3000 : public uhd::wb_iface
+{
+public:
+ typedef boost::shared_ptr<radio_ctrl_core_3000> sptr;
+
+ //! Make a new control object
+ static sptr make(
+ const bool big_endian,
+ uhd::transport::zero_copy_if::sptr ctrl_xport,
+ uhd::transport::zero_copy_if::sptr resp_xport,
+ const boost::uint32_t sid,
+ const std::string &name = "0"
+ );
+
+ //! Hold a ref to a task thats feeding push response
+ virtual void hold_task(uhd::msg_task::sptr task) = 0;
+
+ //! Push a response externall (resp_xport is NULL)
+ virtual void push_response(const boost::uint32_t *buff) = 0;
+
+ //! Set the command time that will activate
+ virtual void set_time(const uhd::time_spec_t &time) = 0;
+
+ //! Set the tick rate (converting time into ticks)
+ virtual void set_tick_rate(const double rate) = 0;
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_RADIO_CTRL_3000_HPP */
diff --git a/host/lib/usrp/cores/rx_dsp_core_200.hpp b/host/lib/usrp/cores/rx_dsp_core_200.hpp
index b01f751e9..3937df9e8 100644
--- a/host/lib/usrp/cores/rx_dsp_core_200.hpp
+++ b/host/lib/usrp/cores/rx_dsp_core_200.hpp
@@ -24,7 +24,7 @@
#include <boost/utility.hpp>
#include <boost/shared_ptr.hpp>
#include <uhd/types/stream_cmd.hpp>
-#include "wb_iface.hpp"
+#include <uhd/types/wb_iface.hpp>
#include <string>
class rx_dsp_core_200 : boost::noncopyable{
@@ -32,7 +32,7 @@ public:
typedef boost::shared_ptr<rx_dsp_core_200> sptr;
static sptr make(
- wb_iface::sptr iface,
+ uhd::wb_iface::sptr iface,
const size_t dsp_base, const size_t ctrl_base,
const boost::uint32_t sid, const bool lingering_packet = false
);
diff --git a/host/lib/usrp/cores/rx_dsp_core_3000.cpp b/host/lib/usrp/cores/rx_dsp_core_3000.cpp
new file mode 100644
index 000000000..7b3324f74
--- /dev/null
+++ b/host/lib/usrp/cores/rx_dsp_core_3000.cpp
@@ -0,0 +1,209 @@
+//
+// Copyright 2011-2013 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 "rx_dsp_core_3000.hpp"
+#include <uhd/types/dict.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/safe_call.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/thread/thread.hpp> //thread sleep
+#include <boost/math/special_functions/round.hpp>
+#include <boost/math/special_functions/sign.hpp>
+#include <algorithm>
+#include <cmath>
+
+#define REG_DSP_RX_FREQ _dsp_base + 0
+#define REG_DSP_RX_SCALE_IQ _dsp_base + 4
+#define REG_DSP_RX_DECIM _dsp_base + 8
+#define REG_DSP_RX_MUX _dsp_base + 12
+
+#define FLAG_DSP_RX_MUX_SWAP_IQ (1 << 0)
+#define FLAG_DSP_RX_MUX_REAL_MODE (1 << 1)
+
+template <class T> T ceil_log2(T num){
+ return std::ceil(std::log(num)/std::log(T(2)));
+}
+
+using namespace uhd;
+
+class rx_dsp_core_3000_impl : public rx_dsp_core_3000{
+public:
+ rx_dsp_core_3000_impl(
+ wb_iface::sptr iface,
+ const size_t dsp_base
+ ):
+ _iface(iface), _dsp_base(dsp_base)
+ {
+ //init to something so update method has reasonable defaults
+ _scaling_adjustment = 1.0;
+ _dsp_extra_scaling = 1.0;
+ this->set_tick_rate(1.0);
+ }
+
+ ~rx_dsp_core_3000_impl(void)
+ {
+ UHD_SAFE_CALL
+ (
+ //NOP
+ )
+ }
+
+ void set_mux(const std::string &mode, const bool fe_swapped){
+ static const uhd::dict<std::string, boost::uint32_t> mode_to_mux = boost::assign::map_list_of
+ ("IQ", 0)
+ ("QI", FLAG_DSP_RX_MUX_SWAP_IQ)
+ ("I", FLAG_DSP_RX_MUX_REAL_MODE)
+ ("Q", FLAG_DSP_RX_MUX_SWAP_IQ | FLAG_DSP_RX_MUX_REAL_MODE)
+ ;
+ _iface->poke32(REG_DSP_RX_MUX, mode_to_mux[mode] ^ (fe_swapped? FLAG_DSP_RX_MUX_SWAP_IQ : 0));
+ }
+
+ void set_tick_rate(const double rate){
+ _tick_rate = rate;
+ }
+
+ void set_link_rate(const double rate){
+ //_link_rate = rate/sizeof(boost::uint32_t); //in samps/s
+ _link_rate = rate/sizeof(boost::uint16_t); //in samps/s (allows for 8sc)
+ }
+
+ uhd::meta_range_t get_host_rates(void){
+ meta_range_t range;
+ for (int rate = 512; rate > 256; rate -= 4){
+ range.push_back(range_t(_tick_rate/rate));
+ }
+ for (int rate = 256; rate > 128; rate -= 2){
+ range.push_back(range_t(_tick_rate/rate));
+ }
+ for (int rate = 128; rate >= int(std::ceil(_tick_rate/_link_rate)); rate -= 1){
+ range.push_back(range_t(_tick_rate/rate));
+ }
+ return range;
+ }
+
+ double set_host_rate(const double rate){
+ const size_t decim_rate = boost::math::iround(_tick_rate/this->get_host_rates().clip(rate, true));
+ size_t decim = decim_rate;
+
+ //determine which half-band filters are activated
+ int hb0 = 0, hb1 = 0;
+ if (decim % 2 == 0){
+ hb0 = 1;
+ decim /= 2;
+ }
+ if (decim % 2 == 0){
+ hb1 = 1;
+ decim /= 2;
+ }
+
+ _iface->poke32(REG_DSP_RX_DECIM, (hb1 << 9) | (hb0 << 8) | (decim & 0xff));
+
+ if (decim > 1 and hb0 == 0 and hb1 == 0)
+ {
+ UHD_MSG(warning) << boost::format(
+ "The requested decimation is odd; the user should expect CIC rolloff.\n"
+ "Select an even decimation to ensure that a halfband filter is enabled.\n"
+ "decimation = dsp_rate/samp_rate -> %d = (%f MHz)/(%f MHz)\n"
+ ) % decim_rate % (_tick_rate/1e6) % (rate/1e6);
+ }
+
+ // Calculate CIC decimation (i.e., without halfband decimators)
+ // Calculate closest multiplier constant to reverse gain absent scale multipliers
+ const double rate_pow = std::pow(double(decim & 0xff), 4);
+ _scaling_adjustment = std::pow(2, ceil_log2(rate_pow))/(1.65*rate_pow);
+ this->update_scalar();
+
+ return _tick_rate/decim_rate;
+ }
+
+ void update_scalar(void){
+ const double factor = 1.0 + std::max(ceil_log2(_scaling_adjustment), 0.0);
+ const double target_scalar = (1 << 17)*_scaling_adjustment/_dsp_extra_scaling/factor;
+ const boost::int32_t actual_scalar = boost::math::iround(target_scalar);
+ _fxpt_scalar_correction = target_scalar/actual_scalar*factor; //should be small
+ _iface->poke32(REG_DSP_RX_SCALE_IQ, actual_scalar);
+ }
+
+ double get_scaling_adjustment(void){
+ return _fxpt_scalar_correction*_host_extra_scaling/32767.;
+ }
+
+ double set_freq(const double freq_){
+ //correct for outside of rate (wrap around)
+ double freq = std::fmod(freq_, _tick_rate);
+ if (std::abs(freq) > _tick_rate/2.0)
+ freq -= boost::math::sign(freq)*_tick_rate;
+
+ //calculate the freq register word (signed)
+ UHD_ASSERT_THROW(std::abs(freq) <= _tick_rate/2.0);
+ static const double scale_factor = std::pow(2.0, 32);
+ const boost::int32_t freq_word = boost::int32_t(boost::math::round((freq / _tick_rate) * scale_factor));
+
+ //update the actual frequency
+ const double actual_freq = (double(freq_word) / scale_factor) * _tick_rate;
+
+ _iface->poke32(REG_DSP_RX_FREQ, boost::uint32_t(freq_word));
+
+ return actual_freq;
+ }
+
+ uhd::meta_range_t get_freq_range(void){
+ return uhd::meta_range_t(-_tick_rate/2, +_tick_rate/2, _tick_rate/std::pow(2.0, 32));
+ }
+
+ void setup(const uhd::stream_args_t &stream_args){
+
+ if (stream_args.otw_format == "sc16"){
+ _dsp_extra_scaling = 1.0;
+ _host_extra_scaling = 1.0;
+ }
+ else if (stream_args.otw_format == "sc8"){
+ double peak = stream_args.args.cast<double>("peak", 1.0);
+ peak = std::max(peak, 1.0/256);
+ _host_extra_scaling = peak*256;
+ _dsp_extra_scaling = peak;
+ }
+ else if (stream_args.otw_format == "sc12"){
+ double peak = stream_args.args.cast<double>("peak", 1.0);
+ peak = std::max(peak, 1.0/16);
+ _host_extra_scaling = peak*16;
+ _dsp_extra_scaling = peak;
+ }
+ else if (stream_args.otw_format == "fc32"){
+ _host_extra_scaling = 1.0;
+ _dsp_extra_scaling = 1.0;
+ }
+ else throw uhd::value_error("USRP RX cannot handle requested wire format: " + stream_args.otw_format);
+
+ _host_extra_scaling *= stream_args.args.cast<double>("fullscale", 1.0);
+
+ this->update_scalar();
+ }
+
+private:
+ wb_iface::sptr _iface;
+ const size_t _dsp_base;
+ double _tick_rate, _link_rate;
+ double _scaling_adjustment, _dsp_extra_scaling, _host_extra_scaling, _fxpt_scalar_correction;
+};
+
+rx_dsp_core_3000::sptr rx_dsp_core_3000::make(wb_iface::sptr iface, const size_t dsp_base)
+{
+ return sptr(new rx_dsp_core_3000_impl(iface, dsp_base));
+}
diff --git a/host/lib/usrp/cores/rx_dsp_core_3000.hpp b/host/lib/usrp/cores/rx_dsp_core_3000.hpp
new file mode 100644
index 000000000..02e5587a2
--- /dev/null
+++ b/host/lib/usrp/cores/rx_dsp_core_3000.hpp
@@ -0,0 +1,58 @@
+//
+// Copyright 2011-2013 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_USRP_RX_DSP_CORE_3000_HPP
+#define INCLUDED_LIBUHD_USRP_RX_DSP_CORE_3000_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/stream.hpp>
+#include <uhd/types/ranges.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include <uhd/types/stream_cmd.hpp>
+#include <uhd/types/wb_iface.hpp>
+#include <string>
+
+class rx_dsp_core_3000 : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<rx_dsp_core_3000> sptr;
+
+ static sptr make(
+ uhd::wb_iface::sptr iface,
+ const size_t dsp_base
+ );
+
+ virtual void set_mux(const std::string &mode, const bool fe_swapped = false) = 0;
+
+ virtual void set_tick_rate(const double rate) = 0;
+
+ virtual void set_link_rate(const double rate) = 0;
+
+ virtual double set_host_rate(const double rate) = 0;
+
+ virtual uhd::meta_range_t get_host_rates(void) = 0;
+
+ virtual double get_scaling_adjustment(void) = 0;
+
+ virtual uhd::meta_range_t get_freq_range(void) = 0;
+
+ virtual double set_freq(const double freq) = 0;
+
+ virtual void setup(const uhd::stream_args_t &stream_args) = 0;
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_RX_DSP_CORE_3000_HPP */
diff --git a/host/lib/usrp/cores/rx_frontend_core_200.cpp b/host/lib/usrp/cores/rx_frontend_core_200.cpp
index 1813758da..09b36c1a6 100644
--- a/host/lib/usrp/cores/rx_frontend_core_200.cpp
+++ b/host/lib/usrp/cores/rx_frontend_core_200.cpp
@@ -18,6 +18,8 @@
#include "rx_frontend_core_200.hpp"
#include <boost/math/special_functions/round.hpp>
+using namespace uhd;
+
#define REG_RX_FE_SWAP_IQ _base + 0 //lower bit
#define REG_RX_FE_MAG_CORRECTION _base + 4 //18 bits
#define REG_RX_FE_PHASE_CORRECTION _base + 8 //18 bits
diff --git a/host/lib/usrp/cores/rx_frontend_core_200.hpp b/host/lib/usrp/cores/rx_frontend_core_200.hpp
index 5755424c8..8327aef8b 100644
--- a/host/lib/usrp/cores/rx_frontend_core_200.hpp
+++ b/host/lib/usrp/cores/rx_frontend_core_200.hpp
@@ -21,7 +21,7 @@
#include <uhd/config.hpp>
#include <boost/utility.hpp>
#include <boost/shared_ptr.hpp>
-#include "wb_iface.hpp"
+#include <uhd/types/wb_iface.hpp>
#include <complex>
#include <string>
@@ -29,7 +29,7 @@ class rx_frontend_core_200 : boost::noncopyable{
public:
typedef boost::shared_ptr<rx_frontend_core_200> sptr;
- static sptr make(wb_iface::sptr iface, const size_t base);
+ static sptr make(uhd::wb_iface::sptr iface, const size_t base);
virtual void set_mux(const bool swap) = 0;
diff --git a/host/lib/usrp/cores/rx_vita_core_3000.cpp b/host/lib/usrp/cores/rx_vita_core_3000.cpp
new file mode 100644
index 000000000..aad137ea3
--- /dev/null
+++ b/host/lib/usrp/cores/rx_vita_core_3000.cpp
@@ -0,0 +1,153 @@
+//
+// Copyright 2013 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 "rx_vita_core_3000.hpp"
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/safe_call.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/tuple/tuple.hpp>
+
+#define REG_FRAMER_MAXLEN _base + 4*4 + 0
+#define REG_FRAMER_SID _base + 4*4 + 4
+
+#define REG_CTRL_CMD _base + 0
+#define REG_CTRL_TIME_HI _base + 4
+#define REG_CTRL_TIME_LO _base + 8
+
+#define REG_FC_WINDOW _base + 6*4 + 0
+#define REG_FC_ENABLE _base + 6*4 + 4
+
+using namespace uhd;
+
+struct rx_vita_core_3000_impl : rx_vita_core_3000
+{
+ rx_vita_core_3000_impl(
+ wb_iface::sptr iface,
+ const size_t base
+ ):
+ _iface(iface),
+ _base(base),
+ _continuous_streaming(false),
+ _is_setup(false)
+ {
+ this->set_tick_rate(1); //init to non zero
+ this->set_nsamps_per_packet(100); //init to non zero
+ this->clear();
+ }
+
+ ~rx_vita_core_3000_impl(void)
+ {
+ UHD_SAFE_CALL
+ (
+ this->clear();
+ )
+ }
+
+ void configure_flow_control(const size_t window_size)
+ {
+ _iface->poke32(REG_FC_WINDOW, window_size-1);
+ _iface->poke32(REG_FC_ENABLE, window_size?1:0);
+ }
+
+ void clear(void)
+ {
+ this->configure_flow_control(0); //disable fc
+ }
+
+ void set_nsamps_per_packet(const size_t nsamps)
+ {
+ _iface->poke32(REG_FRAMER_MAXLEN, nsamps);
+ }
+
+ void issue_stream_command(const uhd::stream_cmd_t &stream_cmd)
+ {
+ if (not _is_setup)
+ {
+ //UHD_MSG(warning) << "rx vita core 3000 issue stream command - not setup yet!";
+ return;
+ }
+ UHD_ASSERT_THROW(stream_cmd.num_samps <= 0x0fffffff);
+ _continuous_streaming = stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
+
+ //setup the mode to instruction flags
+ typedef boost::tuple<bool, bool, bool, bool> inst_t;
+ static const uhd::dict<stream_cmd_t::stream_mode_t, inst_t> mode_to_inst = boost::assign::map_list_of
+ //reload, chain, samps, stop
+ (stream_cmd_t::STREAM_MODE_START_CONTINUOUS, inst_t(true, true, false, false))
+ (stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS, inst_t(false, false, false, true))
+ (stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE, inst_t(false, false, true, false))
+ (stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE, inst_t(false, true, true, false))
+ ;
+
+ //setup the instruction flag values
+ bool inst_reload, inst_chain, inst_samps, inst_stop;
+ boost::tie(inst_reload, inst_chain, inst_samps, inst_stop) = mode_to_inst[stream_cmd.stream_mode];
+
+ //calculate the word from flags and length
+ boost::uint32_t cmd_word = 0;
+ cmd_word |= boost::uint32_t((stream_cmd.stream_now)? 1 : 0) << 31;
+ cmd_word |= boost::uint32_t((inst_chain)? 1 : 0) << 30;
+ cmd_word |= boost::uint32_t((inst_reload)? 1 : 0) << 29;
+ cmd_word |= boost::uint32_t((inst_stop)? 1 : 0) << 28;
+ cmd_word |= (inst_samps)? stream_cmd.num_samps : ((inst_stop)? 0 : 1);
+
+ //issue the stream command
+ _iface->poke32(REG_CTRL_CMD, cmd_word);
+ const boost::uint64_t ticks = (stream_cmd.stream_now)? 0 : stream_cmd.time_spec.to_ticks(_tick_rate);
+ _iface->poke32(REG_CTRL_TIME_HI, boost::uint32_t(ticks >> 32));
+ _iface->poke32(REG_CTRL_TIME_LO, boost::uint32_t(ticks >> 0)); //latches the command
+ }
+
+ void set_tick_rate(const double rate)
+ {
+ _tick_rate = rate;
+ }
+
+ void set_sid(const boost::uint32_t sid)
+ {
+ _iface->poke32(REG_FRAMER_SID, sid);
+ }
+
+ void handle_overflow(void)
+ {
+ if (_continuous_streaming) this->issue_stream_command(stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
+ }
+
+ void setup(const uhd::stream_args_t &)
+ {
+ _is_setup = true;
+ }
+
+ bool in_continuous_streaming_mode(void)
+ {
+ return _continuous_streaming;
+ }
+
+ wb_iface::sptr _iface;
+ const size_t _base;
+ double _tick_rate;
+ bool _continuous_streaming;
+ bool _is_setup;
+};
+
+rx_vita_core_3000::sptr rx_vita_core_3000::make(
+ wb_iface::sptr iface,
+ const size_t base
+)
+{
+ return rx_vita_core_3000::sptr(new rx_vita_core_3000_impl(iface, base));
+}
diff --git a/host/lib/usrp/cores/rx_vita_core_3000.hpp b/host/lib/usrp/cores/rx_vita_core_3000.hpp
new file mode 100644
index 000000000..577510728
--- /dev/null
+++ b/host/lib/usrp/cores/rx_vita_core_3000.hpp
@@ -0,0 +1,59 @@
+//
+// Copyright 2013 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_USRP_RX_VITA_CORE_3000_HPP
+#define INCLUDED_LIBUHD_USRP_RX_VITA_CORE_3000_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/stream.hpp>
+#include <uhd/types/ranges.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include <uhd/types/stream_cmd.hpp>
+#include <uhd/types/wb_iface.hpp>
+#include <string>
+
+class rx_vita_core_3000 : boost::noncopyable
+{
+public:
+ typedef boost::shared_ptr<rx_vita_core_3000> sptr;
+
+ static sptr make(
+ uhd::wb_iface::sptr iface,
+ const size_t base
+ );
+
+ virtual void clear(void) = 0;
+
+ virtual void set_nsamps_per_packet(const size_t nsamps) = 0;
+
+ virtual void issue_stream_command(const uhd::stream_cmd_t &stream_cmd) = 0;
+
+ virtual void set_tick_rate(const double rate) = 0;
+
+ virtual void set_sid(const boost::uint32_t sid) = 0;
+
+ virtual void handle_overflow(void) = 0;
+
+ virtual void setup(const uhd::stream_args_t &stream_args) = 0;
+
+ virtual void configure_flow_control(const size_t window_size) = 0;
+
+ virtual bool in_continuous_streaming_mode(void) = 0;
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_RX_VITA_CORE_3000_HPP */
diff --git a/host/lib/usrp/cores/spi_core_100.hpp b/host/lib/usrp/cores/spi_core_100.hpp
index 87d328aaa..ce53c0b86 100644
--- a/host/lib/usrp/cores/spi_core_100.hpp
+++ b/host/lib/usrp/cores/spi_core_100.hpp
@@ -22,14 +22,14 @@
#include <uhd/types/serial.hpp>
#include <boost/utility.hpp>
#include <boost/shared_ptr.hpp>
-#include "wb_iface.hpp"
+#include <uhd/types/wb_iface.hpp>
class spi_core_100 : boost::noncopyable, public uhd::spi_iface{
public:
typedef boost::shared_ptr<spi_core_100> sptr;
//! makes a new spi core from iface and slave base
- static sptr make(wb_iface::sptr iface, const size_t base);
+ static sptr make(uhd::wb_iface::sptr iface, const size_t base);
};
#endif /* INCLUDED_LIBUHD_USRP_SPI_CORE_100_HPP */
diff --git a/host/lib/usrp/cores/spi_core_3000.cpp b/host/lib/usrp/cores/spi_core_3000.cpp
new file mode 100644
index 000000000..b7503064a
--- /dev/null
+++ b/host/lib/usrp/cores/spi_core_3000.cpp
@@ -0,0 +1,96 @@
+//
+// Copyright 2013 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 "spi_core_3000.hpp"
+#include <uhd/exception.hpp>
+#include <uhd/utils/msg.hpp>
+#include <boost/thread/thread.hpp> //sleep
+
+#define SPI_DIV _base + 0
+#define SPI_CTRL _base + 4
+#define SPI_DATA _base + 8
+
+using namespace uhd;
+
+class spi_core_3000_impl : public spi_core_3000
+{
+public:
+ spi_core_3000_impl(wb_iface::sptr iface, const size_t base, const size_t readback):
+ _iface(iface), _base(base), _readback(readback), _ctrl_word_cache(0)
+ {
+ this->set_divider(30);
+ }
+
+ boost::uint32_t transact_spi(
+ int which_slave,
+ const spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits,
+ bool readback
+ ){
+ boost::mutex::scoped_lock lock(_mutex);
+
+ //load control word
+ boost::uint32_t ctrl_word = 0;
+ ctrl_word |= ((which_slave & 0xffffff) << 0);
+ ctrl_word |= ((num_bits & 0x3f) << 24);
+ if (config.mosi_edge == spi_config_t::EDGE_FALL) ctrl_word |= (1 << 31);
+ if (config.miso_edge == spi_config_t::EDGE_RISE) ctrl_word |= (1 << 30);
+
+ //load data word (must be in upper bits)
+ const boost::uint32_t data_out = data << (32 - num_bits);
+
+ //conditionally send control word
+ if (_ctrl_word_cache != ctrl_word)
+ {
+ _iface->poke32(SPI_DIV, _div);
+ _iface->poke32(SPI_CTRL, ctrl_word);
+ _ctrl_word_cache = ctrl_word;
+ }
+
+ //send data word
+ _iface->poke32(SPI_DATA, data_out);
+
+ //conditional readback
+ if (readback)
+ {
+ return _iface->peek32(_readback);
+ }
+
+ return 0;
+ }
+
+ void set_divider(const double div)
+ {
+ _div = size_t((div/2) - 0.5);
+ }
+
+private:
+
+ wb_iface::sptr _iface;
+ const size_t _base;
+ const size_t _readback;
+ boost::uint32_t _ctrl_word_cache;
+ boost::mutex _mutex;
+ size_t _div;
+};
+
+spi_core_3000::sptr spi_core_3000::make(wb_iface::sptr iface, const size_t base, const size_t readback)
+{
+ return sptr(new spi_core_3000_impl(iface, base, readback));
+}
+
diff --git a/host/lib/usrp/cores/spi_core_3000.hpp b/host/lib/usrp/cores/spi_core_3000.hpp
new file mode 100644
index 000000000..923efed3d
--- /dev/null
+++ b/host/lib/usrp/cores/spi_core_3000.hpp
@@ -0,0 +1,39 @@
+//
+// Copyright 2013 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_USRP_SPI_CORE_3000_HPP
+#define INCLUDED_LIBUHD_USRP_SPI_CORE_3000_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/types/serial.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include <uhd/types/wb_iface.hpp>
+
+class spi_core_3000 : boost::noncopyable, public uhd::spi_iface
+{
+public:
+ typedef boost::shared_ptr<spi_core_3000> sptr;
+
+ //! makes a new spi core from iface and slave base
+ static sptr make(uhd::wb_iface::sptr iface, const size_t base, const size_t readback);
+
+ //! Set the spi clock divider to something usable
+ virtual void set_divider(const double div) = 0;
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_SPI_CORE_3000_HPP */
diff --git a/host/lib/usrp/cores/time64_core_200.hpp b/host/lib/usrp/cores/time64_core_200.hpp
index 315f2ba67..e211ce040 100644
--- a/host/lib/usrp/cores/time64_core_200.hpp
+++ b/host/lib/usrp/cores/time64_core_200.hpp
@@ -22,7 +22,7 @@
#include <uhd/types/time_spec.hpp>
#include <boost/utility.hpp>
#include <boost/shared_ptr.hpp>
-#include "wb_iface.hpp"
+#include <uhd/types/wb_iface.hpp>
#include <string>
#include <vector>
@@ -37,7 +37,7 @@ public:
//! makes a new time64 core from iface and slave base
static sptr make(
- wb_iface::sptr iface, const size_t base,
+ uhd::wb_iface::sptr iface, const size_t base,
const readback_bases_type &readback_bases,
const size_t mimo_delay_cycles = 0 // 0 means no-mimo
);
diff --git a/host/lib/usrp/cores/time_core_3000.cpp b/host/lib/usrp/cores/time_core_3000.cpp
new file mode 100644
index 000000000..45ff55271
--- /dev/null
+++ b/host/lib/usrp/cores/time_core_3000.cpp
@@ -0,0 +1,118 @@
+//
+// Copyright 2013 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 "time_core_3000.hpp"
+#include <uhd/utils/safe_call.hpp>
+#include <uhd/utils/msg.hpp>
+#include <boost/thread/thread.hpp>
+
+#define REG_TIME_HI _base + 0
+#define REG_TIME_LO _base + 4
+#define REG_TIME_CTRL _base + 8
+
+#define CTRL_LATCH_TIME_PPS (1 << 1)
+#define CTRL_LATCH_TIME_NOW (1 << 0)
+
+using namespace uhd;
+
+struct time_core_3000_impl : time_core_3000
+{
+ time_core_3000_impl(
+ wb_iface::sptr iface, const size_t base,
+ const readback_bases_type &readback_bases
+ ):
+ _iface(iface),
+ _base(base),
+ _readback_bases(readback_bases)
+ {
+ this->set_tick_rate(1); //init to non zero
+ }
+
+ ~time_core_3000_impl(void)
+ {
+ UHD_SAFE_CALL
+ (
+ //NOP
+ )
+ }
+
+ void set_tick_rate(const double rate)
+ {
+ _tick_rate = rate;
+ }
+
+ void self_test(void)
+ {
+ const size_t sleep_millis = 100;
+ UHD_MSG(status) << "Performing timer loopback test... " << std::flush;
+ const time_spec_t time0 = this->get_time_now();
+ boost::this_thread::sleep(boost::posix_time::milliseconds(sleep_millis));
+ const time_spec_t time1 = this->get_time_now();
+ const double approx_secs = (time1 - time0).get_real_secs();
+ const bool test_fail = (approx_secs > 0.15) or (approx_secs < 0.05);
+ UHD_MSG(status) << ((test_fail)? " fail" : "pass") << std::endl;
+
+ //useful warning for debugging actual rate
+ const size_t ticks_elapsed = _tick_rate*approx_secs;
+ const size_t appox_rate = ticks_elapsed/(sleep_millis/1e3);
+ if (test_fail) UHD_MSG(warning)
+ << "Expecting clock rate: " << (_tick_rate/1e6) << " MHz\n"
+ << "Appoximate clock rate: " << (appox_rate/1e6) << " MHz\n"
+ << std::endl;
+ }
+
+ uhd::time_spec_t get_time_now(void)
+ {
+ const boost::uint64_t ticks = _iface->peek64(_readback_bases.rb_now);
+ return time_spec_t::from_ticks(ticks, _tick_rate);
+ }
+
+ uhd::time_spec_t get_time_last_pps(void)
+ {
+ const boost::uint64_t ticks = _iface->peek64(_readback_bases.rb_pps);
+ return time_spec_t::from_ticks(ticks, _tick_rate);
+ }
+
+ void set_time_now(const uhd::time_spec_t &time)
+ {
+ const boost::uint64_t ticks = time.to_ticks(_tick_rate);
+ _iface->poke32(REG_TIME_HI, boost::uint32_t(ticks >> 32));
+ _iface->poke32(REG_TIME_LO, boost::uint32_t(ticks >> 0));
+ _iface->poke32(REG_TIME_CTRL, CTRL_LATCH_TIME_NOW);
+ }
+
+ void set_time_next_pps(const uhd::time_spec_t &time)
+ {
+ const boost::uint64_t ticks = time.to_ticks(_tick_rate);
+ _iface->poke32(REG_TIME_HI, boost::uint32_t(ticks >> 32));
+ _iface->poke32(REG_TIME_LO, boost::uint32_t(ticks >> 0));
+ _iface->poke32(REG_TIME_CTRL, CTRL_LATCH_TIME_PPS);
+ }
+
+ wb_iface::sptr _iface;
+ const size_t _base;
+ const readback_bases_type _readback_bases;
+ double _tick_rate;
+};
+
+time_core_3000::sptr time_core_3000::make(
+ wb_iface::sptr iface, const size_t base,
+ const readback_bases_type &readback_bases
+)
+{
+ return time_core_3000::sptr(new time_core_3000_impl(iface, base, readback_bases));
+}
diff --git a/host/lib/usrp/cores/time_core_3000.hpp b/host/lib/usrp/cores/time_core_3000.hpp
new file mode 100644
index 000000000..fad408810
--- /dev/null
+++ b/host/lib/usrp/cores/time_core_3000.hpp
@@ -0,0 +1,58 @@
+//
+// Copyright 2013 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_USRP_TIME_CORE_3000_HPP
+#define INCLUDED_LIBUHD_USRP_TIME_CORE_3000_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/types/time_spec.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include <uhd/types/wb_iface.hpp>
+
+class time_core_3000 : boost::noncopyable
+{
+public:
+ typedef boost::shared_ptr<time_core_3000> sptr;
+
+ struct readback_bases_type
+ {
+ size_t rb_now;
+ size_t rb_pps;
+ };
+
+ //! makes a new time core from iface and slave base
+ static sptr make(
+ uhd::wb_iface::sptr iface, const size_t base,
+ const readback_bases_type &readback_bases
+ );
+
+ virtual void self_test(void) = 0;
+
+ virtual void set_tick_rate(const double rate) = 0;
+
+ virtual uhd::time_spec_t get_time_now(void) = 0;
+
+ virtual uhd::time_spec_t get_time_last_pps(void) = 0;
+
+ virtual void set_time_now(const uhd::time_spec_t &time) = 0;
+
+ virtual void set_time_next_pps(const uhd::time_spec_t &time) = 0;
+
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_TIME_CORE_3000_HPP */
diff --git a/host/lib/usrp/cores/tx_dsp_core_200.hpp b/host/lib/usrp/cores/tx_dsp_core_200.hpp
index 0e1cfb6bc..ce3d1dbdd 100644
--- a/host/lib/usrp/cores/tx_dsp_core_200.hpp
+++ b/host/lib/usrp/cores/tx_dsp_core_200.hpp
@@ -23,14 +23,14 @@
#include <uhd/types/ranges.hpp>
#include <boost/utility.hpp>
#include <boost/shared_ptr.hpp>
-#include "wb_iface.hpp"
+#include <uhd/types/wb_iface.hpp>
class tx_dsp_core_200 : boost::noncopyable{
public:
typedef boost::shared_ptr<tx_dsp_core_200> sptr;
static sptr make(
- wb_iface::sptr iface,
+ uhd::wb_iface::sptr iface,
const size_t dsp_base, const size_t ctrl_base,
const boost::uint32_t sid
);
diff --git a/host/lib/usrp/cores/tx_dsp_core_3000.cpp b/host/lib/usrp/cores/tx_dsp_core_3000.cpp
new file mode 100644
index 000000000..feb749cd9
--- /dev/null
+++ b/host/lib/usrp/cores/tx_dsp_core_3000.cpp
@@ -0,0 +1,186 @@
+//
+// Copyright 2011-2013 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 "tx_dsp_core_3000.hpp"
+#include <uhd/types/dict.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/math/special_functions/round.hpp>
+#include <boost/math/special_functions/sign.hpp>
+#include <boost/thread/thread.hpp> //sleep
+#include <algorithm>
+#include <cmath>
+
+#define REG_DSP_TX_FREQ _dsp_base + 0
+#define REG_DSP_TX_SCALE_IQ _dsp_base + 4
+#define REG_DSP_TX_INTERP _dsp_base + 8
+
+template <class T> T ceil_log2(T num){
+ return std::ceil(std::log(num)/std::log(T(2)));
+}
+
+using namespace uhd;
+
+class tx_dsp_core_3000_impl : public tx_dsp_core_3000{
+public:
+ tx_dsp_core_3000_impl(
+ wb_iface::sptr iface,
+ const size_t dsp_base
+ ):
+ _iface(iface), _dsp_base(dsp_base)
+ {
+ //init to something so update method has reasonable defaults
+ _scaling_adjustment = 1.0;
+ _dsp_extra_scaling = 1.0;
+ this->set_tick_rate(1.0);
+ }
+
+ void set_tick_rate(const double rate){
+ _tick_rate = rate;
+ }
+
+ void set_link_rate(const double rate){
+ //_link_rate = rate/sizeof(boost::uint32_t); //in samps/s
+ _link_rate = rate/sizeof(boost::uint16_t); //in samps/s (allows for 8sc)
+ }
+
+ uhd::meta_range_t get_host_rates(void){
+ meta_range_t range;
+ for (int rate = 512; rate > 256; rate -= 4){
+ range.push_back(range_t(_tick_rate/rate));
+ }
+ for (int rate = 256; rate > 128; rate -= 2){
+ range.push_back(range_t(_tick_rate/rate));
+ }
+ for (int rate = 128; rate >= int(std::ceil(_tick_rate/_link_rate)); rate -= 1){
+ range.push_back(range_t(_tick_rate/rate));
+ }
+ return range;
+ }
+
+ double set_host_rate(const double rate){
+ const size_t interp_rate = boost::math::iround(_tick_rate/this->get_host_rates().clip(rate, true));
+ size_t interp = interp_rate;
+
+ //determine which half-band filters are activated
+ int hb0 = 0, hb1 = 0;
+ if (interp % 2 == 0){
+ hb0 = 1;
+ interp /= 2;
+ }
+ if (interp % 2 == 0){
+ hb1 = 1;
+ interp /= 2;
+ }
+
+ _iface->poke32(REG_DSP_TX_INTERP, (hb1 << 9) | (hb0 << 8) | (interp & 0xff));
+
+ if (interp > 1 and hb0 == 0 and hb1 == 0)
+ {
+ UHD_MSG(warning) << boost::format(
+ "The requested interpolation is odd; the user should expect CIC rolloff.\n"
+ "Select an even interpolation to ensure that a halfband filter is enabled.\n"
+ "interpolation = dsp_rate/samp_rate -> %d = (%f MHz)/(%f MHz)\n"
+ ) % interp_rate % (_tick_rate/1e6) % (rate/1e6);
+ }
+
+ // Calculate CIC interpolation (i.e., without halfband interpolators)
+ // Calculate closest multiplier constant to reverse gain absent scale multipliers
+ const double rate_pow = std::pow(double(interp & 0xff), 3);
+ _scaling_adjustment = std::pow(2, ceil_log2(rate_pow))/(1.65*rate_pow);
+ this->update_scalar();
+
+ return _tick_rate/interp_rate;
+ }
+
+ void update_scalar(void){
+ const double factor = 1.0 + std::max(ceil_log2(_scaling_adjustment), 0.0);
+ const double target_scalar = (1 << 17)*_scaling_adjustment/_dsp_extra_scaling/factor;
+ const boost::int32_t actual_scalar = boost::math::iround(target_scalar);
+ _fxpt_scalar_correction = target_scalar/actual_scalar*factor; //should be small
+ _iface->poke32(REG_DSP_TX_SCALE_IQ, actual_scalar);
+ }
+
+ double get_scaling_adjustment(void){
+ return _fxpt_scalar_correction*_host_extra_scaling*32767.;
+ }
+
+ double set_freq(const double freq_){
+ //correct for outside of rate (wrap around)
+ double freq = std::fmod(freq_, _tick_rate);
+ if (std::abs(freq) > _tick_rate/2.0)
+ freq -= boost::math::sign(freq)*_tick_rate;
+
+ //calculate the freq register word (signed)
+ UHD_ASSERT_THROW(std::abs(freq) <= _tick_rate/2.0);
+ static const double scale_factor = std::pow(2.0, 32);
+ const boost::int32_t freq_word = boost::int32_t(boost::math::round((freq / _tick_rate) * scale_factor));
+
+ //update the actual frequency
+ const double actual_freq = (double(freq_word) / scale_factor) * _tick_rate;
+
+ _iface->poke32(REG_DSP_TX_FREQ, boost::uint32_t(freq_word));
+
+ return actual_freq;
+ }
+
+ uhd::meta_range_t get_freq_range(void){
+ return uhd::meta_range_t(-_tick_rate/2, +_tick_rate/2, _tick_rate/std::pow(2.0, 32));
+ }
+
+ void setup(const uhd::stream_args_t &stream_args){
+
+ if (stream_args.otw_format == "sc16"){
+ _dsp_extra_scaling = 1.0;
+ _host_extra_scaling = 1.0;
+ }
+ else if (stream_args.otw_format == "sc8"){
+ double peak = stream_args.args.cast<double>("peak", 1.0);
+ peak = std::max(peak, 1.0/256);
+ _host_extra_scaling = 1.0/peak/256;
+ _dsp_extra_scaling = 1.0/peak;
+ }
+ else if (stream_args.otw_format == "sc12"){
+ double peak = stream_args.args.cast<double>("peak", 1.0);
+ peak = std::max(peak, 1.0/16);
+ _host_extra_scaling = 1.0/peak/16;
+ _dsp_extra_scaling = 1.0/peak;
+ }
+ else if (stream_args.otw_format == "fc32"){
+ _host_extra_scaling = 1.0;
+ _dsp_extra_scaling = 1.0;
+ }
+ else throw uhd::value_error("USRP TX cannot handle requested wire format: " + stream_args.otw_format);
+
+ _host_extra_scaling /= stream_args.args.cast<double>("fullscale", 1.0);
+
+ this->update_scalar();
+ }
+
+private:
+ wb_iface::sptr _iface;
+ const size_t _dsp_base;
+ double _tick_rate, _link_rate;
+ double _scaling_adjustment, _dsp_extra_scaling, _host_extra_scaling, _fxpt_scalar_correction;
+};
+
+tx_dsp_core_3000::sptr tx_dsp_core_3000::make(wb_iface::sptr iface, const size_t dsp_base)
+{
+ return sptr(new tx_dsp_core_3000_impl(iface, dsp_base));
+}
diff --git a/host/lib/usrp/cores/tx_dsp_core_3000.hpp b/host/lib/usrp/cores/tx_dsp_core_3000.hpp
new file mode 100644
index 000000000..6f725b836
--- /dev/null
+++ b/host/lib/usrp/cores/tx_dsp_core_3000.hpp
@@ -0,0 +1,54 @@
+//
+// Copyright 2011-2013 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_USRP_TX_DSP_CORE_3000_HPP
+#define INCLUDED_LIBUHD_USRP_TX_DSP_CORE_3000_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/stream.hpp>
+#include <uhd/types/ranges.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include <uhd/types/wb_iface.hpp>
+
+class tx_dsp_core_3000 : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<tx_dsp_core_3000> sptr;
+
+ static sptr make(
+ uhd::wb_iface::sptr iface,
+ const size_t dsp_base
+ );
+
+ virtual void set_tick_rate(const double rate) = 0;
+
+ virtual void set_link_rate(const double rate) = 0;
+
+ virtual double set_host_rate(const double rate) = 0;
+
+ virtual uhd::meta_range_t get_host_rates(void) = 0;
+
+ virtual double get_scaling_adjustment(void) = 0;
+
+ virtual uhd::meta_range_t get_freq_range(void) = 0;
+
+ virtual double set_freq(const double freq) = 0;
+
+ virtual void setup(const uhd::stream_args_t &stream_args) = 0;
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_TX_DSP_CORE_3000_HPP */
diff --git a/host/lib/usrp/cores/tx_frontend_core_200.cpp b/host/lib/usrp/cores/tx_frontend_core_200.cpp
index e35874173..d701027e5 100644
--- a/host/lib/usrp/cores/tx_frontend_core_200.cpp
+++ b/host/lib/usrp/cores/tx_frontend_core_200.cpp
@@ -21,6 +21,8 @@
#include <boost/assign/list_of.hpp>
#include <boost/math/special_functions/round.hpp>
+using namespace uhd;
+
#define REG_TX_FE_DC_OFFSET_I _base + 0 //24 bits
#define REG_TX_FE_DC_OFFSET_Q _base + 4 //24 bits
#define REG_TX_FE_MAG_CORRECTION _base + 8 //18 bits
diff --git a/host/lib/usrp/cores/tx_frontend_core_200.hpp b/host/lib/usrp/cores/tx_frontend_core_200.hpp
index 8ee0f3e6d..7d09b39d2 100644
--- a/host/lib/usrp/cores/tx_frontend_core_200.hpp
+++ b/host/lib/usrp/cores/tx_frontend_core_200.hpp
@@ -21,7 +21,7 @@
#include <uhd/config.hpp>
#include <boost/utility.hpp>
#include <boost/shared_ptr.hpp>
-#include "wb_iface.hpp"
+#include <uhd/types/wb_iface.hpp>
#include <complex>
#include <string>
@@ -29,7 +29,7 @@ class tx_frontend_core_200 : boost::noncopyable{
public:
typedef boost::shared_ptr<tx_frontend_core_200> sptr;
- static sptr make(wb_iface::sptr iface, const size_t base);
+ static sptr make(uhd::wb_iface::sptr iface, const size_t base);
virtual void set_mux(const std::string &mode) = 0;
diff --git a/host/lib/usrp/cores/tx_vita_core_3000.cpp b/host/lib/usrp/cores/tx_vita_core_3000.cpp
new file mode 100644
index 000000000..38eb6afb5
--- /dev/null
+++ b/host/lib/usrp/cores/tx_vita_core_3000.cpp
@@ -0,0 +1,107 @@
+//
+// Copyright 2013 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 "tx_vita_core_3000.hpp"
+#include <uhd/utils/safe_call.hpp>
+
+#define REG_CTRL_ERROR_POLICY _base + 0
+#define REG_DEFRAMER_CYCLE_FC_UPS _base + 2*4 + 0
+#define REG_DEFRAMER_PACKET_FC_UPS _base + 2*4 + 4
+
+using namespace uhd;
+
+struct tx_vita_core_3000_impl : tx_vita_core_3000
+{
+ tx_vita_core_3000_impl(
+ wb_iface::sptr iface,
+ const size_t base
+ ):
+ _iface(iface),
+ _base(base)
+ {
+ this->set_tick_rate(1); //init to non zero
+ this->set_underflow_policy("next_packet");
+ this->clear();
+ }
+
+ ~tx_vita_core_3000_impl(void)
+ {
+ UHD_SAFE_CALL
+ (
+ this->clear();
+ )
+ }
+
+ void clear(void)
+ {
+ this->configure_flow_control(0, 0);
+ this->set_underflow_policy(_policy); //clears the seq
+ }
+
+ void set_tick_rate(const double rate)
+ {
+ _tick_rate = rate;
+ }
+
+ void set_underflow_policy(const std::string &policy)
+ {
+ if (policy == "next_packet")
+ {
+ _iface->poke32(REG_CTRL_ERROR_POLICY, (1 << 1));
+ }
+ else if (policy == "next_burst")
+ {
+ _iface->poke32(REG_CTRL_ERROR_POLICY, (1 << 2));
+ }
+ else if (policy == "wait")
+ {
+ _iface->poke32(REG_CTRL_ERROR_POLICY, (1 << 0));
+ }
+ else throw uhd::value_error("USRP TX cannot handle requested underflow policy: " + policy);
+ _policy = policy;
+ }
+
+ void setup(const uhd::stream_args_t &stream_args)
+ {
+ if (stream_args.args.has_key("underflow_policy"))
+ {
+ this->set_underflow_policy(stream_args.args["underflow_policy"]);
+ }
+ }
+
+ void configure_flow_control(const size_t cycs_per_up, const size_t pkts_per_up)
+ {
+ if (cycs_per_up == 0) _iface->poke32(REG_DEFRAMER_CYCLE_FC_UPS, 0);
+ else _iface->poke32(REG_DEFRAMER_CYCLE_FC_UPS, (1 << 31) | ((cycs_per_up) & 0xffffff));
+
+ if (pkts_per_up == 0) _iface->poke32(REG_DEFRAMER_PACKET_FC_UPS, 0);
+ else _iface->poke32(REG_DEFRAMER_PACKET_FC_UPS, (1 << 31) | ((pkts_per_up) & 0xffff));
+ }
+
+ wb_iface::sptr _iface;
+ const size_t _base;
+ double _tick_rate;
+ std::string _policy;
+};
+
+tx_vita_core_3000::sptr tx_vita_core_3000::make(
+ wb_iface::sptr iface,
+ const size_t base
+)
+{
+ return tx_vita_core_3000::sptr(new tx_vita_core_3000_impl(iface, base));
+}
diff --git a/host/lib/usrp/cores/tx_vita_core_3000.hpp b/host/lib/usrp/cores/tx_vita_core_3000.hpp
new file mode 100644
index 000000000..d4677a3e3
--- /dev/null
+++ b/host/lib/usrp/cores/tx_vita_core_3000.hpp
@@ -0,0 +1,49 @@
+//
+// Copyright 2013 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_USRP_TX_VITA_CORE_3000_HPP
+#define INCLUDED_LIBUHD_USRP_TX_VITA_CORE_3000_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/stream.hpp>
+#include <uhd/types/ranges.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include <uhd/types/stream_cmd.hpp>
+#include <uhd/types/wb_iface.hpp>
+#include <string>
+
+class tx_vita_core_3000 : boost::noncopyable
+{
+public:
+ typedef boost::shared_ptr<tx_vita_core_3000> sptr;
+
+ static sptr make(
+ uhd::wb_iface::sptr iface,
+ const size_t base
+ );
+
+ virtual void clear(void) = 0;
+
+ virtual void set_tick_rate(const double rate) = 0;
+
+ virtual void setup(const uhd::stream_args_t &stream_args) = 0;
+
+ virtual void configure_flow_control(const size_t cycs_per_up, const size_t pkts_per_up) = 0;
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_TX_VITA_CORE_3000_HPP */
diff --git a/host/lib/usrp/cores/user_settings_core_200.cpp b/host/lib/usrp/cores/user_settings_core_200.cpp
index d262631b1..391725edc 100644
--- a/host/lib/usrp/cores/user_settings_core_200.cpp
+++ b/host/lib/usrp/cores/user_settings_core_200.cpp
@@ -17,6 +17,8 @@
#include "user_settings_core_200.hpp"
+using namespace uhd;
+
#define REG_USER_ADDR _base + 0
#define REG_USER_DATA _base + 4
diff --git a/host/lib/usrp/cores/user_settings_core_200.hpp b/host/lib/usrp/cores/user_settings_core_200.hpp
index 1f5d13de7..f5fca2ce6 100644
--- a/host/lib/usrp/cores/user_settings_core_200.hpp
+++ b/host/lib/usrp/cores/user_settings_core_200.hpp
@@ -21,14 +21,14 @@
#include <uhd/config.hpp>
#include <boost/utility.hpp>
#include <boost/shared_ptr.hpp>
-#include "wb_iface.hpp"
+#include <uhd/types/wb_iface.hpp>
class user_settings_core_200 : boost::noncopyable{
public:
typedef boost::shared_ptr<user_settings_core_200> sptr;
typedef std::pair<boost::uint8_t, boost::uint32_t> user_reg_t;
- static sptr make(wb_iface::sptr iface, const size_t base);
+ static sptr make(uhd::wb_iface::sptr iface, const size_t base);
virtual void set_reg(const user_reg_t &reg) = 0;
};
diff --git a/host/lib/usrp/cores/wb_iface.hpp b/host/lib/usrp/cores/wb_iface.hpp
deleted file mode 100644
index 982594b21..000000000
--- a/host/lib/usrp/cores/wb_iface.hpp
+++ /dev/null
@@ -1,60 +0,0 @@
-//
-// Copyright 2011 Ettus Research LLC
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-//
-
-#ifndef INCLUDED_LIBUHD_USRP_WB_IFACE_HPP
-#define INCLUDED_LIBUHD_USRP_WB_IFACE_HPP
-
-#include <uhd/config.hpp>
-#include <boost/cstdint.hpp>
-#include <boost/shared_ptr.hpp>
-
-class wb_iface{
-public:
- typedef boost::shared_ptr<wb_iface> sptr;
- typedef boost::uint32_t wb_addr_type;
-
- /*!
- * Write a register (32 bits)
- * \param addr the address
- * \param data the 32bit data
- */
- virtual void poke32(wb_addr_type addr, boost::uint32_t data) = 0;
-
- /*!
- * Read a register (32 bits)
- * \param addr the address
- * \return the 32bit data
- */
- virtual boost::uint32_t peek32(wb_addr_type addr) = 0;
-
- /*!
- * Write a register (16 bits)
- * \param addr the address
- * \param data the 16bit data
- */
- virtual void poke16(wb_addr_type addr, boost::uint16_t data) = 0;
-
- /*!
- * Read a register (16 bits)
- * \param addr the address
- * \return the 16bit data
- */
- virtual boost::uint16_t peek16(wb_addr_type addr) = 0;
-
-};
-
-#endif /* INCLUDED_LIBUHD_USRP_WB_IFACE_HPP */
diff --git a/host/lib/usrp/dboard/CMakeLists.txt b/host/lib/usrp/dboard/CMakeLists.txt
index b000c7f33..9e8653608 100644
--- a/host/lib/usrp/dboard/CMakeLists.txt
+++ b/host/lib/usrp/dboard/CMakeLists.txt
@@ -26,6 +26,7 @@ LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/db_sbx_common.cpp
${CMAKE_CURRENT_SOURCE_DIR}/db_sbx_version3.cpp
${CMAKE_CURRENT_SOURCE_DIR}/db_sbx_version4.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/db_cbx.cpp
${CMAKE_CURRENT_SOURCE_DIR}/db_wbx_common.cpp
${CMAKE_CURRENT_SOURCE_DIR}/db_wbx_version2.cpp
${CMAKE_CURRENT_SOURCE_DIR}/db_wbx_version3.cpp
diff --git a/host/lib/usrp/dboard/db_cbx.cpp b/host/lib/usrp/dboard/db_cbx.cpp
new file mode 100644
index 000000000..04399e64e
--- /dev/null
+++ b/host/lib/usrp/dboard/db_cbx.cpp
@@ -0,0 +1,212 @@
+//
+// Copyright 2011-2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+
+#include "max2870_regs.hpp"
+#include "db_sbx_common.hpp"
+
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace boost::assign;
+
+/***********************************************************************
+ * Structors
+ **********************************************************************/
+sbx_xcvr::cbx::cbx(sbx_xcvr *_self_sbx_xcvr) {
+ //register the handle to our base CBX class
+ self_base = _self_sbx_xcvr;
+}
+
+
+sbx_xcvr::cbx::~cbx(void){
+ /* NOP */
+}
+
+
+/***********************************************************************
+ * Tuning
+ **********************************************************************/
+double sbx_xcvr::cbx::set_lo_freq(dboard_iface::unit_t unit, double target_freq) {
+ UHD_LOGV(often) << boost::format(
+ "CBX tune: target frequency %f Mhz"
+ ) % (target_freq/1e6) << std::endl;
+
+ //clip the input
+ target_freq = cbx_freq_range.clip(target_freq);
+
+ //map mode setting to valid integer divider (N) values
+ static const uhd::range_t int_n_mode_div_range(16,4095,1);
+ static const uhd::range_t frac_n_mode_div_range(19,4091,1);
+
+ //map rf divider select output dividers to enums
+ static const uhd::dict<int, max2870_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of
+ (1, max2870_regs_t::RF_DIVIDER_SELECT_DIV1)
+ (2, max2870_regs_t::RF_DIVIDER_SELECT_DIV2)
+ (4, max2870_regs_t::RF_DIVIDER_SELECT_DIV4)
+ (8, max2870_regs_t::RF_DIVIDER_SELECT_DIV8)
+ (16, max2870_regs_t::RF_DIVIDER_SELECT_DIV16)
+ (32, max2870_regs_t::RF_DIVIDER_SELECT_DIV32)
+ (64, max2870_regs_t::RF_DIVIDER_SELECT_DIV64)
+ (128, max2870_regs_t::RF_DIVIDER_SELECT_DIV128)
+ ;
+
+ double actual_freq, pfd_freq;
+ double ref_freq = self_base->get_iface()->get_clock_rate(unit);
+ max2870_regs_t::int_n_mode_t int_n_mode;
+ int R=0, BS=0, N=0, FRAC=0, MOD=4095;
+ int RFdiv = 1;
+ max2870_regs_t::reference_divide_by_2_t T = max2870_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED;
+ max2870_regs_t::reference_doubler_t D = max2870_regs_t::REFERENCE_DOUBLER_DISABLED;
+
+ //Reference doubler for 50% duty cycle
+ // if ref_freq < 12.5MHz enable regs.reference_divide_by_2
+ //NOTE: MAX2870 goes down to 10MHz ref vs. 12.5MHz on ADF4351
+ if(ref_freq <= 10.0e6) D = max2870_regs_t::REFERENCE_DOUBLER_ENABLED;
+
+ //increase RF divider until acceptable VCO frequency
+ double vco_freq = target_freq;
+ //NOTE: MIN freq for MAX2870 VCO is 3GHz vs. 2.2GHz on ADF4351
+ while (vco_freq < 3e9) {
+ vco_freq *= 2;
+ RFdiv *= 2;
+ }
+
+ /*
+ * The goal here is to loop though possible R dividers,
+ * band select clock dividers, N (int) dividers, and FRAC
+ * (frac) dividers.
+ *
+ * Calculate the N and F dividers for each set of values.
+ * The loop exits when it meets all of the constraints.
+ * The resulting loop values are loaded into the registers.
+ *
+ * from pg.21
+ *
+ * f_pfd = f_ref*(1+D)/(R*(1+T))
+ * f_vco = (N + (FRAC/MOD))*f_pfd
+ * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD
+ * f_rf = f_vco/RFdiv
+ */
+ for(R = 1; R <= 1023; R+=1){
+ //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T)
+ pfd_freq = ref_freq*(1+D)/(R*(1+T));
+
+ //keep the PFD frequency at or below 25MHz
+ if (pfd_freq > 25e6) continue;
+
+ //ignore fractional part of tuning
+ N = int(vco_freq/pfd_freq);
+
+ //Fractional-N calculation
+ FRAC = int((vco_freq/pfd_freq - N)*MOD);
+
+ //are we in int-N or frac-N mode?
+ int_n_mode = (FRAC == 0) ? max2870_regs_t::INT_N_MODE_INT_N : max2870_regs_t::INT_N_MODE_FRAC_N;
+
+ //keep N within int divider requirements
+ if(int_n_mode == max2870_regs_t::INT_N_MODE_INT_N) {
+ if(N < int_n_mode_div_range.start()) continue;
+ if(N > int_n_mode_div_range.stop()) continue;
+ } else {
+ if(N < frac_n_mode_div_range.start()) continue;
+ if(N > frac_n_mode_div_range.stop()) continue;
+ }
+
+ //keep pfd freq low enough to achieve 50kHz BS clock
+ BS = std::ceil(pfd_freq / 50e3);
+ if(BS <= 1023) break;
+ }
+
+ UHD_ASSERT_THROW(R <= 1023);
+
+ //Reference divide-by-2 for 50% duty cycle
+ // if R even, move one divide by 2 to to regs.reference_divide_by_2
+ if(R % 2 == 0){
+ T = max2870_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED;
+ R /= 2;
+ }
+
+ //actual frequency calculation
+ actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv);
+
+ UHD_LOGV(often)
+ << boost::format("CBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl
+ << boost::format("CBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d"
+ ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl
+ << boost::format("CBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f"
+ ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl;
+
+ //load the register values
+ max2870_regs_t regs;
+
+ if ((unit == dboard_iface::UNIT_TX) and (actual_freq == sbx_tx_lo_2dbm.clip(actual_freq)))
+ regs.output_power = max2870_regs_t::OUTPUT_POWER_2DBM;
+ else
+ regs.output_power = max2870_regs_t::OUTPUT_POWER_5DBM;
+
+ //set frac/int CPL mode
+ max2870_regs_t::cpl_t cpl;
+ max2870_regs_t::ldf_t ldf;
+ max2870_regs_t::cpoc_t cpoc;
+ if(int_n_mode == max2870_regs_t::INT_N_MODE_INT_N) {
+ cpl = max2870_regs_t::CPL_DISABLED;
+ cpoc = max2870_regs_t::CPOC_ENABLED;
+ ldf = max2870_regs_t::LDF_INT_N;
+ } else {
+ cpl = max2870_regs_t::CPL_ENABLED;
+ ldf = max2870_regs_t::LDF_FRAC_N;
+ cpoc = max2870_regs_t::CPOC_DISABLED;
+ }
+
+ regs.frac_12_bit = FRAC;
+ regs.int_16_bit = N;
+ regs.mod_12_bit = MOD;
+ regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD)));
+ regs.feedback_select = (target_freq >= 3.0e9) ? max2870_regs_t::FEEDBACK_SELECT_DIVIDED : max2870_regs_t::FEEDBACK_SELECT_FUNDAMENTAL;
+ regs.r_counter_10_bit = R;
+ regs.reference_divide_by_2 = T;
+ regs.reference_doubler = D;
+ regs.band_select_clock_div = BS;
+ UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv));
+ regs.rf_divider_select = rfdivsel_to_enum[RFdiv];
+ regs.int_n_mode = int_n_mode;
+ regs.cpl = cpl;
+ regs.ldf = ldf;
+ regs.cpoc = cpoc;
+
+ //write the registers
+ //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0)
+ int addr;
+
+ for(addr=5; addr>=0; addr--){
+ UHD_LOGV(often) << boost::format(
+ "CBX SPI Reg (0x%02x): 0x%08x"
+ ) % addr % regs.get_reg(addr) << std::endl;
+ self_base->get_iface()->write_spi(
+ unit, spi_config_t::EDGE_RISE,
+ regs.get_reg(addr), 32
+ );
+ }
+
+ //return the actual frequency
+ UHD_LOGV(often) << boost::format(
+ "CBX tune: actual frequency %f Mhz"
+ ) % (actual_freq/1e6) << std::endl;
+ return actual_freq;
+}
+
diff --git a/host/lib/usrp/dboard/db_sbx_common.cpp b/host/lib/usrp/dboard/db_sbx_common.cpp
index 728cb17e9..5b713c6d7 100644
--- a/host/lib/usrp/dboard/db_sbx_common.cpp
+++ b/host/lib/usrp/dboard/db_sbx_common.cpp
@@ -21,6 +21,137 @@ using namespace uhd;
using namespace uhd::usrp;
using namespace boost::assign;
+/***********************************************************************
+ * ADF 4350/4351 Tuning Utility
+ **********************************************************************/
+sbx_xcvr::sbx_versionx::adf435x_tuning_settings sbx_xcvr::sbx_versionx::_tune_adf435x_synth(
+ double target_freq,
+ double ref_freq,
+ const adf435x_tuning_constraints& constraints,
+ double& actual_freq)
+{
+ //Default invalid value for actual_freq
+ actual_freq = 0;
+
+ double pfd_freq = 0;
+ boost::uint16_t R = 0, BS = 0, N = 0, FRAC = 0, MOD = 0;
+ boost::uint16_t RFdiv = static_cast<boost::uint16_t>(constraints.rf_divider_range.start());
+ bool D = false, T = false;
+
+ //Reference doubler for 50% duty cycle
+ //If ref_freq < 12.5MHz enable the reference doubler
+ D = (ref_freq <= constraints.ref_doubler_threshold);
+
+ static const double MIN_VCO_FREQ = 2.2e9;
+ static const double MAX_VCO_FREQ = 4.4e9;
+
+ //increase RF divider until acceptable VCO frequency
+ double vco_freq = target_freq;
+ while (vco_freq < MIN_VCO_FREQ && RFdiv < static_cast<boost::uint16_t>(constraints.rf_divider_range.stop())) {
+ vco_freq *= 2;
+ RFdiv *= 2;
+ }
+
+ /*
+ * The goal here is to loop though possible R dividers,
+ * band select clock dividers, N (int) dividers, and FRAC
+ * (frac) dividers.
+ *
+ * Calculate the N and F dividers for each set of values.
+ * The loop exits when it meets all of the constraints.
+ * The resulting loop values are loaded into the registers.
+ *
+ * from pg.21
+ *
+ * f_pfd = f_ref*(1+D)/(R*(1+T))
+ * f_vco = (N + (FRAC/MOD))*f_pfd
+ * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD
+ * f_rf = f_vco/RFdiv)
+ * f_actual = f_rf/2
+ */
+ for(R = 1; R <= 1023; R+=1){
+ //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T)
+ pfd_freq = ref_freq*(D?2:1)/(R*(T?2:1));
+
+ //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth)
+ if (pfd_freq > constraints.pfd_freq_max) continue;
+
+ //ignore fractional part of tuning
+ //N is computed from target_freq and not vco_freq because the feedback
+ //mode is set to FEEDBACK_SELECT_DIVIDED
+ N = boost::uint16_t(std::floor(target_freq/pfd_freq));
+
+ //keep N > minimum int divider requirement
+ if (N < static_cast<boost::uint16_t>(constraints.int_range.start())) continue;
+
+ for(BS=1; BS <= 255; BS+=1){
+ //keep the band select frequency at or below band_sel_freq_max
+ //constraint on band select clock
+ if (pfd_freq/BS > constraints.band_sel_freq_max) continue;
+ goto done_loop;
+ }
+ } done_loop:
+
+ //Fractional-N calculation
+ MOD = 4095; //max fractional accuracy
+ //N is computed from target_freq and not vco_freq because the feedback
+ //mode is set to FEEDBACK_SELECT_DIVIDED
+ FRAC = static_cast<boost::uint16_t>((target_freq/pfd_freq - N)*MOD);
+ if (constraints.force_frac0) {
+ if (FRAC > (MOD / 2)) { //Round integer such that actual freq is closest to target
+ N++;
+ }
+ FRAC = 0;
+ }
+
+ //Reference divide-by-2 for 50% duty cycle
+ // if R even, move one divide by 2 to to regs.reference_divide_by_2
+ if(R % 2 == 0) {
+ T = true;
+ R /= 2;
+ }
+
+ //Typical phase resync time documented in data sheet pg.24
+ static const double PHASE_RESYNC_TIME = 400e-6;
+
+ //actual frequency calculation
+ actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(D?2:1)/(R*(T?2:1)));
+
+ //load the settings
+ adf435x_tuning_settings settings;
+ settings.frac_12_bit = FRAC;
+ settings.int_16_bit = N;
+ settings.mod_12_bit = MOD;
+ settings.clock_divider_12_bit = std::max<boost::uint16_t>(1, std::ceil(PHASE_RESYNC_TIME*pfd_freq/MOD));
+ settings.r_counter_10_bit = R;
+ settings.r_divide_by_2_en = T;
+ settings.r_doubler_en = D;
+ settings.band_select_clock_div = BS;
+ settings.rf_divider = RFdiv;
+ settings.feedback_after_divider = true;
+
+ UHD_LOGV(often)
+ << boost::format("ADF 435X Frequencies (MHz): REQUESTED=%0.9f, ACTUAL=%0.9f"
+ ) % (target_freq/1e6) % (actual_freq/1e6) << std::endl
+ << boost::format("ADF 435X Intermediates (MHz): VCO=%0.2f, PFD=%0.2f, BAND=%0.2f, REF=%0.2f"
+ ) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) % (ref_freq/1e6) << std::endl
+ << boost::format("ADF 435X Settings: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d"
+ ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl;
+
+ UHD_ASSERT_THROW((settings.frac_12_bit & ((boost::uint16_t)~0xFFF)) == 0);
+ UHD_ASSERT_THROW((settings.mod_12_bit & ((boost::uint16_t)~0xFFF)) == 0);
+ UHD_ASSERT_THROW((settings.clock_divider_12_bit & ((boost::uint16_t)~0xFFF)) == 0);
+ UHD_ASSERT_THROW((settings.r_counter_10_bit & ((boost::uint16_t)~0x3FF)) == 0);
+
+ UHD_ASSERT_THROW(vco_freq >= MIN_VCO_FREQ and vco_freq <= MAX_VCO_FREQ);
+ UHD_ASSERT_THROW(settings.rf_divider >= static_cast<boost::uint16_t>(constraints.rf_divider_range.start()));
+ UHD_ASSERT_THROW(settings.rf_divider <= static_cast<boost::uint16_t>(constraints.rf_divider_range.stop()));
+ UHD_ASSERT_THROW(settings.int_16_bit >= static_cast<boost::uint16_t>(constraints.int_range.start()));
+ UHD_ASSERT_THROW(settings.int_16_bit <= static_cast<boost::uint16_t>(constraints.int_range.stop()));
+
+ return settings;
+}
+
/***********************************************************************
* Register the SBX dboard (min freq, max freq, rx div2, tx div2)
@@ -32,6 +163,7 @@ static dboard_base::sptr make_sbx(dboard_base::ctor_args_t args){
UHD_STATIC_BLOCK(reg_sbx_dboards){
dboard_manager::register_dboard(0x0054, 0x0055, &make_sbx, "SBX");
dboard_manager::register_dboard(0x0065, 0x0064, &make_sbx, "SBX v4");
+ dboard_manager::register_dboard(0x0067, 0x0066, &make_sbx, "CBX");
}
@@ -114,9 +246,15 @@ sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){
switch(get_rx_id().to_uint16()) {
case 0x054:
db_actual = sbx_versionx_sptr(new sbx_version3(this));
+ freq_range = sbx_freq_range;
break;
case 0x065:
db_actual = sbx_versionx_sptr(new sbx_version4(this));
+ freq_range = sbx_freq_range;
+ break;
+ case 0x067:
+ db_actual = sbx_versionx_sptr(new cbx(this));
+ freq_range = cbx_freq_range;
break;
default:
/* We didn't recognize the version of the board... */
@@ -128,7 +266,8 @@ sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){
////////////////////////////////////////////////////////////////////
if(get_rx_id() == 0x054) this->get_rx_subtree()->create<std::string>("name").set("SBXv3 RX");
else if(get_rx_id() == 0x065) this->get_rx_subtree()->create<std::string>("name").set("SBXv4 RX");
- else this->get_rx_subtree()->create<std::string>("name").set("SBX RX");
+ else if(get_rx_id() == 0x067) this->get_rx_subtree()->create<std::string>("name").set("CBX RX");
+ else this->get_rx_subtree()->create<std::string>("name").set("SBX/CBX RX");
this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked")
.publish(boost::bind(&sbx_xcvr::get_locked, this, dboard_iface::UNIT_RX));
@@ -141,8 +280,8 @@ sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){
}
this->get_rx_subtree()->create<double>("freq/value")
.coerce(boost::bind(&sbx_xcvr::set_lo_freq, this, dboard_iface::UNIT_RX, _1))
- .set((sbx_freq_range.start() + sbx_freq_range.stop())/2.0);
- this->get_rx_subtree()->create<meta_range_t>("freq/range").set(sbx_freq_range);
+ .set((freq_range.start() + freq_range.stop())/2.0);
+ this->get_rx_subtree()->create<meta_range_t>("freq/range").set(freq_range);
this->get_rx_subtree()->create<std::string>("antenna/value")
.subscribe(boost::bind(&sbx_xcvr::set_rx_ant, this, _1))
.set("RX2");
@@ -159,8 +298,9 @@ sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){
// Register TX properties
////////////////////////////////////////////////////////////////////
if(get_tx_id() == 0x055) this->get_tx_subtree()->create<std::string>("name").set("SBXv3 TX");
- else if(get_tx_id() == 0x067) this->get_tx_subtree()->create<std::string>("name").set("SBXv4 TX");
- else this->get_tx_subtree()->create<std::string>("name").set("SBX TX");
+ else if(get_tx_id() == 0x064) this->get_tx_subtree()->create<std::string>("name").set("SBXv4 TX");
+ else if(get_tx_id() == 0x066) this->get_tx_subtree()->create<std::string>("name").set("CBX TX");
+ else this->get_tx_subtree()->create<std::string>("name").set("SBX/CBX TX");
this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked")
.publish(boost::bind(&sbx_xcvr::get_locked, this, dboard_iface::UNIT_TX));
@@ -173,8 +313,8 @@ sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){
}
this->get_tx_subtree()->create<double>("freq/value")
.coerce(boost::bind(&sbx_xcvr::set_lo_freq, this, dboard_iface::UNIT_TX, _1))
- .set((sbx_freq_range.start() + sbx_freq_range.stop())/2.0);
- this->get_tx_subtree()->create<meta_range_t>("freq/range").set(sbx_freq_range);
+ .set((freq_range.start() + freq_range.stop())/2.0);
+ this->get_tx_subtree()->create<meta_range_t>("freq/range").set(freq_range);
this->get_tx_subtree()->create<std::string>("antenna/value")
.subscribe(boost::bind(&sbx_xcvr::set_tx_ant, this, _1))
.set(sbx_tx_antennas.at(0));
@@ -353,4 +493,3 @@ void sbx_xcvr::flash_leds(void) {
this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO));
this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO));
}
-
diff --git a/host/lib/usrp/dboard/db_sbx_common.hpp b/host/lib/usrp/dboard/db_sbx_common.hpp
index 2a0e83115..e9bb2434c 100644
--- a/host/lib/usrp/dboard/db_sbx_common.hpp
+++ b/host/lib/usrp/dboard/db_sbx_common.hpp
@@ -100,6 +100,7 @@ using namespace boost::assign;
* The SBX dboard constants
**********************************************************************/
static const freq_range_t sbx_freq_range(400e6, 4.4e9);
+static const freq_range_t cbx_freq_range(1200e6, 6.0e9);
static const freq_range_t sbx_tx_lo_2dbm = list_of
(range_t(0.35e9, 0.37e9))
@@ -180,6 +181,34 @@ protected:
~sbx_versionx(void) {}
virtual double set_lo_freq(dboard_iface::unit_t unit, double target_freq) = 0;
+ protected:
+ struct adf435x_tuning_constraints {
+ bool force_frac0;
+ double ref_doubler_threshold;
+ double pfd_freq_max;
+ double band_sel_freq_max;
+ uhd::range_t rf_divider_range;
+ uhd::range_t int_range;
+ };
+
+ struct adf435x_tuning_settings {
+ boost::uint16_t frac_12_bit;
+ boost::uint16_t int_16_bit;
+ boost::uint16_t mod_12_bit;
+ boost::uint16_t r_counter_10_bit;
+ bool r_doubler_en;
+ bool r_divide_by_2_en;
+ boost::uint16_t clock_divider_12_bit;
+ boost::uint8_t band_select_clock_div;
+ boost::uint16_t rf_divider;
+ bool feedback_after_divider;
+ };
+
+ adf435x_tuning_settings _tune_adf435x_synth(
+ double target_freq,
+ double ref_freq,
+ const adf435x_tuning_constraints& constraints,
+ double& actual_freq);
};
/*!
@@ -213,6 +242,30 @@ protected:
};
/*!
+ * CBX daughterboard
+ *
+ * The only driver difference between SBX and CBX is the MAX2870 vs. ADF435x.
+ * There is also no LO filter switching required, but the GPIO is left blank
+ * so we don't worry about it.
+ */
+ class cbx : public sbx_versionx {
+ public:
+ cbx(sbx_xcvr *_self_sbx_xcvr);
+ ~cbx(void);
+
+ double set_lo_freq(dboard_iface::unit_t unit, double target_freq);
+
+ /*! This is the registered instance of the wrapper class, sbx_base. */
+ sbx_xcvr *self_base;
+ };
+
+ /*!
+ * Frequency range of the daughterboard; this is set in the constructor
+ * to correspond either to SBX or CBX.
+ */
+ freq_range_t freq_range;
+
+ /*!
* Handle to the version-specific implementation of the SBX.
*
* Since many of this class's functions are dependent on the version of the
diff --git a/host/lib/usrp/dboard/db_sbx_version3.cpp b/host/lib/usrp/dboard/db_sbx_version3.cpp
index 2765d530c..b0c9cd18f 100644
--- a/host/lib/usrp/dboard/db_sbx_version3.cpp
+++ b/host/lib/usrp/dboard/db_sbx_version3.cpp
@@ -63,85 +63,21 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar
(16, adf4350_regs_t::RF_DIVIDER_SELECT_DIV16)
;
- double actual_freq, pfd_freq;
- double ref_freq = self_base->get_iface()->get_clock_rate(unit);
- int R=0, BS=0, N=0, FRAC=0, MOD=0;
- int RFdiv = 1;
- adf4350_regs_t::reference_divide_by_2_t T = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED;
- adf4350_regs_t::reference_doubler_t D = adf4350_regs_t::REFERENCE_DOUBLER_DISABLED;
-
- //Reference doubler for 50% duty cycle
- // if ref_freq < 12.5MHz enable regs.reference_divide_by_2
- if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED;
-
- //increase RF divider until acceptable VCO frequency
- double vco_freq = target_freq;
- while (vco_freq < 2.2e9) {
- vco_freq *= 2;
- RFdiv *= 2;
- }
-
//use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler)
adf4350_regs_t::prescaler_t prescaler = target_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5;
- /*
- * The goal here is to loop though possible R dividers,
- * band select clock dividers, N (int) dividers, and FRAC
- * (frac) dividers.
- *
- * Calculate the N and F dividers for each set of values.
- * The loop exits when it meets all of the constraints.
- * The resulting loop values are loaded into the registers.
- *
- * from pg.21
- *
- * f_pfd = f_ref*(1+D)/(R*(1+T))
- * f_vco = (N + (FRAC/MOD))*f_pfd
- * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD
- * f_rf = f_vco/RFdiv)
- * f_actual = f_rf/2
- */
- for(R = 1; R <= 1023; R+=1){
- //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T)
- pfd_freq = ref_freq*(1+D)/(R*(1+T));
-
- //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth)
- if (pfd_freq > 25e6) continue;
-
- //ignore fractional part of tuning
- N = int(std::floor(target_freq/pfd_freq));
-
- //keep N > minimum int divider requirement
- if (N < prescaler_to_min_int_div[prescaler]) continue;
-
- for(BS=1; BS <= 255; BS+=1){
- //keep the band select frequency at or below 100KHz
- //constraint on band select clock
- if (pfd_freq/BS > 100e3) continue;
- goto done_loop;
- }
- } done_loop:
-
- //Fractional-N calculation
- MOD = 4095; //max fractional accuracy
- FRAC = int((target_freq/pfd_freq - N)*MOD);
-
- //Reference divide-by-2 for 50% duty cycle
- // if R even, move one divide by 2 to to regs.reference_divide_by_2
- if(R % 2 == 0){
- T = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED;
- R /= 2;
- }
-
- //actual frequency calculation
- actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T))));
+ adf435x_tuning_constraints tuning_constraints;
+ tuning_constraints.force_frac0 = false;
+ tuning_constraints.band_sel_freq_max = 100e3;
+ tuning_constraints.ref_doubler_threshold = 12.5e6;
+ tuning_constraints.int_range = uhd::range_t(prescaler_to_min_int_div[prescaler], 4095); //INT is a 12-bit field
+ tuning_constraints.pfd_freq_max = 25e6;
+ tuning_constraints.rf_divider_range = uhd::range_t(1, 16);
- UHD_LOGV(often)
- << boost::format("SBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl
- << boost::format("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d"
- ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl
- << boost::format("SBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f"
- ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl;
+ double actual_freq;
+ adf435x_tuning_settings tuning_settings = _tune_adf435x_synth(
+ target_freq, self_base->get_iface()->get_clock_rate(unit),
+ tuning_constraints, actual_freq);
//load the register values
adf4350_regs_t regs;
@@ -151,19 +87,25 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar
else
regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM;
- regs.frac_12_bit = FRAC;
- regs.int_16_bit = N;
- regs.mod_12_bit = MOD;
- regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD)));
- regs.feedback_select = adf4350_regs_t::FEEDBACK_SELECT_DIVIDED;
- regs.clock_div_mode = adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE;
- regs.prescaler = prescaler;
- regs.r_counter_10_bit = R;
- regs.reference_divide_by_2 = T;
- regs.reference_doubler = D;
- regs.band_select_clock_div = BS;
- UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv));
- regs.rf_divider_select = rfdivsel_to_enum[RFdiv];
+ regs.frac_12_bit = tuning_settings.frac_12_bit;
+ regs.int_16_bit = tuning_settings.int_16_bit;
+ regs.mod_12_bit = tuning_settings.mod_12_bit;
+ regs.clock_divider_12_bit = tuning_settings.clock_divider_12_bit;
+ regs.feedback_select = tuning_settings.feedback_after_divider ?
+ adf4350_regs_t::FEEDBACK_SELECT_DIVIDED :
+ adf4350_regs_t::FEEDBACK_SELECT_FUNDAMENTAL;
+ regs.clock_div_mode = adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE;
+ regs.prescaler = prescaler;
+ regs.r_counter_10_bit = tuning_settings.r_counter_10_bit;
+ regs.reference_divide_by_2 = tuning_settings.r_divide_by_2_en ?
+ adf4350_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED :
+ adf4350_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED;
+ regs.reference_doubler = tuning_settings.r_doubler_en ?
+ adf4350_regs_t::REFERENCE_DOUBLER_ENABLED :
+ adf4350_regs_t::REFERENCE_DOUBLER_DISABLED;
+ regs.band_select_clock_div = tuning_settings.band_select_clock_div;
+ UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(tuning_settings.rf_divider));
+ regs.rf_divider_select = rfdivsel_to_enum[tuning_settings.rf_divider];
//reset the N and R counter
regs.counter_reset = adf4350_regs_t::COUNTER_RESET_ENABLED;
diff --git a/host/lib/usrp/dboard/db_sbx_version4.cpp b/host/lib/usrp/dboard/db_sbx_version4.cpp
index 27fd68b05..8d95b0655 100644
--- a/host/lib/usrp/dboard/db_sbx_version4.cpp
+++ b/host/lib/usrp/dboard/db_sbx_version4.cpp
@@ -66,85 +66,21 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar
(64, adf4351_regs_t::RF_DIVIDER_SELECT_DIV64)
;
- double actual_freq, pfd_freq;
- double ref_freq = self_base->get_iface()->get_clock_rate(unit);
- int R=0, BS=0, N=0, FRAC=0, MOD=0;
- int RFdiv = 1;
- adf4351_regs_t::reference_divide_by_2_t T = adf4351_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED;
- adf4351_regs_t::reference_doubler_t D = adf4351_regs_t::REFERENCE_DOUBLER_DISABLED;
-
- //Reference doubler for 50% duty cycle
- // if ref_freq < 12.5MHz enable regs.reference_divide_by_2
- if(ref_freq <= 12.5e6) D = adf4351_regs_t::REFERENCE_DOUBLER_ENABLED;
-
- //increase RF divider until acceptable VCO frequency
- double vco_freq = target_freq;
- while (vco_freq < 2.2e9) {
- vco_freq *= 2;
- RFdiv *= 2;
- }
-
//use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler)
- adf4351_regs_t::prescaler_t prescaler = target_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5;
-
- /*
- * The goal here is to loop though possible R dividers,
- * band select clock dividers, N (int) dividers, and FRAC
- * (frac) dividers.
- *
- * Calculate the N and F dividers for each set of values.
- * The loop exits when it meets all of the constraints.
- * The resulting loop values are loaded into the registers.
- *
- * from pg.21
- *
- * f_pfd = f_ref*(1+D)/(R*(1+T))
- * f_vco = (N + (FRAC/MOD))*f_pfd
- * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD
- * f_rf = f_vco/RFdiv)
- * f_actual = f_rf/2
- */
- for(R = 1; R <= 1023; R+=1){
- //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T)
- pfd_freq = ref_freq*(1+D)/(R*(1+T));
-
- //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth)
- if (pfd_freq > 25e6) continue;
-
- //ignore fractional part of tuning
- N = int(std::floor(vco_freq/pfd_freq));
-
- //keep N > minimum int divider requirement
- if (N < prescaler_to_min_int_div[prescaler]) continue;
-
- for(BS=1; BS <= 255; BS+=1){
- //keep the band select frequency at or below 100KHz
- //constraint on band select clock
- if (pfd_freq/BS > 100e3) continue;
- goto done_loop;
- }
- } done_loop:
-
- //Fractional-N calculation
- MOD = 4095; //max fractional accuracy
- FRAC = int((target_freq/pfd_freq - N)*MOD);
-
- //Reference divide-by-2 for 50% duty cycle
- // if R even, move one divide by 2 to to regs.reference_divide_by_2
- if(R % 2 == 0){
- T = adf4351_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED;
- R /= 2;
- }
+ adf4351_regs_t::prescaler_t prescaler = target_freq > 3.6e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5;
- //actual frequency calculation
- actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T))));
+ adf435x_tuning_constraints tuning_constraints;
+ tuning_constraints.force_frac0 = false;
+ tuning_constraints.band_sel_freq_max = 100e3;
+ tuning_constraints.ref_doubler_threshold = 12.5e6;
+ tuning_constraints.int_range = uhd::range_t(prescaler_to_min_int_div[prescaler], 4095); //INT is a 12-bit field
+ tuning_constraints.pfd_freq_max = 25e6;
+ tuning_constraints.rf_divider_range = uhd::range_t(1, 64);
- UHD_LOGV(often)
- << boost::format("SBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl
- << boost::format("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d"
- ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl
- << boost::format("SBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f"
- ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl;
+ double actual_freq;
+ adf435x_tuning_settings tuning_settings = _tune_adf435x_synth(
+ target_freq, self_base->get_iface()->get_clock_rate(unit),
+ tuning_constraints, actual_freq);
//load the register values
adf4351_regs_t regs;
@@ -154,19 +90,25 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar
else
regs.output_power = adf4351_regs_t::OUTPUT_POWER_5DBM;
- regs.frac_12_bit = FRAC;
- regs.int_16_bit = N;
- regs.mod_12_bit = MOD;
- regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD)));
- regs.feedback_select = adf4351_regs_t::FEEDBACK_SELECT_DIVIDED;
- regs.clock_div_mode = adf4351_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE;
- regs.prescaler = prescaler;
- regs.r_counter_10_bit = R;
- regs.reference_divide_by_2 = T;
- regs.reference_doubler = D;
- regs.band_select_clock_div = BS;
- UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv));
- regs.rf_divider_select = rfdivsel_to_enum[RFdiv];
+ regs.frac_12_bit = tuning_settings.frac_12_bit;
+ regs.int_16_bit = tuning_settings.int_16_bit;
+ regs.mod_12_bit = tuning_settings.mod_12_bit;
+ regs.clock_divider_12_bit = tuning_settings.clock_divider_12_bit;
+ regs.feedback_select = tuning_settings.feedback_after_divider ?
+ adf4351_regs_t::FEEDBACK_SELECT_DIVIDED :
+ adf4351_regs_t::FEEDBACK_SELECT_FUNDAMENTAL;
+ regs.clock_div_mode = adf4351_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE;
+ regs.prescaler = prescaler;
+ regs.r_counter_10_bit = tuning_settings.r_counter_10_bit;
+ regs.reference_divide_by_2 = tuning_settings.r_divide_by_2_en ?
+ adf4351_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED :
+ adf4351_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED;
+ regs.reference_doubler = tuning_settings.r_doubler_en ?
+ adf4351_regs_t::REFERENCE_DOUBLER_ENABLED :
+ adf4351_regs_t::REFERENCE_DOUBLER_DISABLED;
+ regs.band_select_clock_div = tuning_settings.band_select_clock_div;
+ UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(tuning_settings.rf_divider));
+ regs.rf_divider_select = rfdivsel_to_enum[tuning_settings.rf_divider];
//reset the N and R counter
regs.counter_reset = adf4351_regs_t::COUNTER_RESET_ENABLED;
diff --git a/host/lib/usrp/dboard_iface.cpp b/host/lib/usrp/dboard_iface.cpp
index 5cc5ea470..6be50130a 100644
--- a/host/lib/usrp/dboard_iface.cpp
+++ b/host/lib/usrp/dboard_iface.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2010 Ettus Research LLC
+// Copyright 2010-2013 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
@@ -31,6 +31,11 @@ dboard_iface::dboard_iface(void){
_impl = UHD_PIMPL_MAKE(impl, ());
}
+dboard_iface::~dboard_iface(void)
+{
+ //empty
+}
+
template <typename T>
static T shadow_it(T &shadow, const T &value, const T &mask){
shadow = (shadow & ~mask) | (value & mask);
diff --git a/host/lib/usrp/e100/dboard_iface.cpp b/host/lib/usrp/e100/dboard_iface.cpp
index 532b2dc9e..07d0049c8 100644
--- a/host/lib/usrp/e100/dboard_iface.cpp
+++ b/host/lib/usrp/e100/dboard_iface.cpp
@@ -73,8 +73,8 @@ public:
void set_gpio_debug(unit_t, int);
boost::uint16_t read_gpio(unit_t);
- void write_i2c(boost::uint8_t, const byte_vector_t &);
- byte_vector_t read_i2c(boost::uint8_t, size_t);
+ void write_i2c(boost::uint16_t, const byte_vector_t &);
+ byte_vector_t read_i2c(boost::uint16_t, size_t);
void write_spi(
unit_t unit,
@@ -219,11 +219,11 @@ boost::uint32_t e100_dboard_iface::read_write_spi(
/***********************************************************************
* I2C
**********************************************************************/
-void e100_dboard_iface::write_i2c(boost::uint8_t addr, const byte_vector_t &bytes){
+void e100_dboard_iface::write_i2c(boost::uint16_t addr, const byte_vector_t &bytes){
return _i2c_iface->write_i2c(addr, bytes);
}
-byte_vector_t e100_dboard_iface::read_i2c(boost::uint8_t addr, size_t num_bytes){
+byte_vector_t e100_dboard_iface::read_i2c(boost::uint16_t addr, size_t num_bytes){
return _i2c_iface->read_i2c(addr, num_bytes);
}
diff --git a/host/lib/usrp/e100/e100_ctrl.cpp b/host/lib/usrp/e100/e100_ctrl.cpp
index c9c86c8af..cdbbff6dd 100644
--- a/host/lib/usrp/e100/e100_ctrl.cpp
+++ b/host/lib/usrp/e100/e100_ctrl.cpp
@@ -144,7 +144,7 @@ public:
::close(_node_fd);
}
- void write_i2c(boost::uint8_t addr, const byte_vector_t &bytes){
+ void write_i2c(boost::uint16_t addr, const byte_vector_t &bytes){
byte_vector_t rw_bytes(bytes);
//setup the message
@@ -163,7 +163,7 @@ public:
UHD_ASSERT_THROW(::ioctl(_node_fd, I2C_RDWR, &data) >= 0);
}
- byte_vector_t read_i2c(boost::uint8_t addr, size_t num_bytes){
+ byte_vector_t read_i2c(boost::uint16_t addr, size_t num_bytes){
byte_vector_t bytes(num_bytes);
//setup the message
diff --git a/host/lib/usrp/e100/e100_impl.hpp b/host/lib/usrp/e100/e100_impl.hpp
index 6f64d4b80..813f9cc8f 100644
--- a/host/lib/usrp/e100/e100_impl.hpp
+++ b/host/lib/usrp/e100/e100_impl.hpp
@@ -61,7 +61,7 @@ extern void e100_load_fpga(const std::string &bin_file);
//! Make an e100 dboard interface
uhd::usrp::dboard_iface::sptr make_e100_dboard_iface(
- wb_iface::sptr wb_iface,
+ uhd::wb_iface::sptr wb_iface,
uhd::i2c_iface::sptr i2c_iface,
uhd::spi_iface::sptr spi_iface,
e100_clock_ctrl::sptr clock,
diff --git a/host/lib/usrp/e100/io_impl.cpp b/host/lib/usrp/e100/io_impl.cpp
index e34620444..bf04a5871 100644
--- a/host/lib/usrp/e100/io_impl.cpp
+++ b/host/lib/usrp/e100/io_impl.cpp
@@ -166,6 +166,8 @@ rx_streamer::sptr e100_impl::get_rx_stream(const uhd::stream_args_t &args_){
my_streamer->set_overflow_handler(chan_i, boost::bind(
&rx_dsp_core_200::handle_overflow, _rx_dsps[dsp]
));
+ my_streamer->set_issue_stream_cmd(chan_i, boost::bind(
+ &rx_dsp_core_200::issue_stream_command, _rx_dsps[dsp], _1));
_rx_streamers[dsp] = my_streamer; //store weak pointer
}
@@ -220,6 +222,7 @@ tx_streamer::sptr e100_impl::get_tx_stream(const uhd::stream_args_t &args_){
my_streamer->set_xport_chan_get_buff(chan_i, boost::bind(
&zero_copy_if::get_send_buff, _data_transport, _1
));
+ my_streamer->set_async_receiver(boost::bind(&fifo_ctrl_excelsior::pop_async_msg, _fifo_ctrl, _1, _2));
_tx_streamers[dsp] = my_streamer; //store weak pointer
}
diff --git a/host/lib/usrp/gps_ctrl.cpp b/host/lib/usrp/gps_ctrl.cpp
index f3bdded60..c3af75faa 100644
--- a/host/lib/usrp/gps_ctrl.cpp
+++ b/host/lib/usrp/gps_ctrl.cpp
@@ -26,6 +26,10 @@
#include <boost/thread/thread.hpp>
#include <boost/tokenizer.hpp>
#include <boost/format.hpp>
+#include <boost/regex.hpp>
+
+#include "boost/tuple/tuple.hpp"
+#include "boost/foreach.hpp"
using namespace uhd;
using namespace boost::gregorian;
@@ -38,14 +42,69 @@ using namespace boost::this_thread;
*/
class gps_ctrl_impl : public gps_ctrl{
+private:
+ std::map<std::string, boost::tuple<std::string, boost::system_time, bool> > sensors;
+
+ std::string get_cached_sensor(const std::string sensor, const int freshness, const bool once, const bool touch=true) {
+ boost::system_time time = boost::get_system_time();
+ try {
+ // this is nasty ...
+ //std::cout << boost::format("Requested %s - seen? ") % sensor << sensors[sensor].get<2>() << " once? " << once << std::endl;
+ if(time - sensors[sensor].get<1>() < milliseconds(freshness) && (!once or !sensors[sensor].get<2>())) {
+ sensors[sensor] = boost::make_tuple(sensors[sensor].get<0>(), sensors[sensor].get<1>(), touch);
+ return sensors[sensor].get<0>();
+ } else {
+ return update_cached_sensors(sensor);
+ }
+ } catch(std::exception &e) {
+ UHD_MSG(warning) << "get_cached_sensor: " << e.what();
+ }
+ return std::string();
+ }
+
+ std::string update_cached_sensors(const std::string sensor) {
+ if(not gps_detected() || (gps_type != GPS_TYPE_JACKSON_LABS)) {
+ UHD_MSG(error) << "get_stat(): unsupported GPS or no GPS detected";
+ return std::string();
+ }
+
+ std::string msg = _recv();
+ static const boost::regex status_regex("\\d\\d-\\d\\d-\\d\\d");
+ boost::system_time time = boost::get_system_time();
+ if(msg.size() < 6)
+ return std::string();
+
+ std::string nmea = msg.substr(1,5);
+ const std::list<std::string> list = boost::assign::list_of("GPGGA")("GPRMC");
+ BOOST_FOREACH(std::string key, list) {
+ // beginning matches one of the NMEA keys
+ if(!nmea.compare(key)) {
+ sensors[key] = boost::make_tuple(msg, time, !sensor.compare(key));
+ // if this was what we're looking for return it
+ return (!sensor.compare(key))? msg : std::string();
+ }
+ }
+
+ //We're still here so it's not one of the NMEA strings from above
+ if(boost::regex_search(msg, status_regex, boost::regex_constants::match_continuous)) {
+ trim(msg);
+ sensors["SERVO"] = boost::make_tuple(msg, time, false);
+ if(!sensor.compare("SERVO"))
+ return msg;
+ else
+ return std::string();
+ }
+ return std::string();
+ }
public:
gps_ctrl_impl(uart_iface::sptr uart){
_uart = uart;
+
std::string reply;
bool i_heard_some_nmea = false, i_heard_something_weird = false;
gps_type = GPS_TYPE_NONE;
-
+
//first we look for a Jackson Labs Firefly (since that's what we provide...)
_flush(); //get whatever junk is in the rx buffer right now, and throw it away
_send("HAAAY GUYYYYS\n"); //to elicit a response from the Firefly
@@ -60,7 +119,7 @@ public:
if(reply.find("Command Error") != std::string::npos) {
gps_type = GPS_TYPE_JACKSON_LABS;
break;
- }
+ }
else if(reply.substr(0, 3) == "$GP") i_heard_some_nmea = true; //but keep looking for that "Command Error" response
else if(reply.length() != 0) i_heard_something_weird = true; //probably wrong baud rate
sleep(milliseconds(GPS_TIMEOUT_DELAY_MS));
@@ -99,7 +158,8 @@ public:
("gps_gpgga")
("gps_gprmc")
("gps_time")
- ("gps_locked");
+ ("gps_locked")
+ ("gps_servo");
return ret;
}
@@ -108,7 +168,7 @@ public:
or key == "gps_gprmc") {
return sensor_value_t(
boost::to_upper_copy(key),
- get_nmea(boost::to_upper_copy(key.substr(4,8))),
+ get_cached_sensor(boost::to_upper_copy(key.substr(4,8)), GPS_NMEA_NORMAL_FRESHNESS, false, false),
"");
}
else if(key == "gps_time") {
@@ -117,6 +177,9 @@ public:
else if(key == "gps_locked") {
return sensor_value_t("GPS lock status", locked(), "locked", "unlocked");
}
+ else if(key == "gps_servo") {
+ return sensor_value_t("GPS servo status", get_servo(), "");
+ }
else {
throw uhd::value_error("gps ctrl get_sensor unknown key: " + key);
}
@@ -138,24 +201,25 @@ private:
sleep(milliseconds(FIREFLY_STUPID_DELAY_MS));
_send("GPS:GPRMC 1\n");
sleep(milliseconds(FIREFLY_STUPID_DELAY_MS));
+ _send("SERV:TRAC 0\n");
+ sleep(milliseconds(FIREFLY_STUPID_DELAY_MS));
}
-
+
//retrieve a raw NMEA sentence
std::string get_nmea(std::string msgtype) {
- msgtype.insert(0, "$");
std::string reply;
- if(not gps_detected()) {
- UHD_MSG(error) << "get_nmea(): unsupported GPS or no GPS detected";
- return std::string();
- }
-
- _flush(); //flush all input before waiting for a message
const boost::system_time comm_timeout = boost::get_system_time() + milliseconds(GPS_COMM_TIMEOUT_MS);
while(boost::get_system_time() < comm_timeout) {
- reply = _recv();
- if(reply.substr(0, 6) == msgtype)
- return reply;
+ if(!msgtype.compare("GPRMC")) {
+ reply = get_cached_sensor(msgtype, GPS_NMEA_FRESHNESS, true);
+ }
+ else {
+ reply = get_cached_sensor(msgtype, GPS_NMEA_LOW_FRESHNESS, false);
+ }
+ if(reply.size()) {
+ if(reply.substr(1, 5) == msgtype) return reply;
+ }
boost::this_thread::sleep(milliseconds(GPS_TIMEOUT_DELAY_MS));
}
throw uhd::value_error(str(boost::format("get_nmea(): no %s message found") % msgtype));
@@ -176,9 +240,10 @@ private:
}
ptime get_time(void) {
+ _flush();
int error_cnt = 0;
ptime gps_time;
- while(error_cnt < 3) {
+ while(error_cnt < 2) {
try {
std::string reply = get_nmea("GPRMC");
@@ -188,29 +253,30 @@ private:
if(datestr.size() == 0 or timestr.size() == 0) {
throw uhd::value_error(str(boost::format("Invalid response \"%s\"") % reply));
}
-
+
//just trust me on this one
- gps_time = ptime( date(
+ gps_time = ptime( date(
greg_year(boost::lexical_cast<int>(datestr.substr(4, 2)) + 2000),
- greg_month(boost::lexical_cast<int>(datestr.substr(2, 2))),
- greg_day(boost::lexical_cast<int>(datestr.substr(0, 2)))
+ greg_month(boost::lexical_cast<int>(datestr.substr(2, 2))),
+ greg_day(boost::lexical_cast<int>(datestr.substr(0, 2)))
),
hours( boost::lexical_cast<int>(timestr.substr(0, 2)))
+ minutes(boost::lexical_cast<int>(timestr.substr(2, 2)))
+ seconds(boost::lexical_cast<int>(timestr.substr(4, 2)))
);
return gps_time;
-
+
} catch(std::exception &e) {
UHD_MSG(warning) << "get_time: " << e.what();
+ _flush();
error_cnt++;
}
}
throw uhd::value_error("Timeout after no valid message found");
-
+
return gps_time; //keep gcc from complaining
}
-
+
time_t get_epoch_time(void) {
return (get_time() - from_time_t(0)).total_seconds();
}
@@ -223,7 +289,7 @@ private:
int error_cnt = 0;
while(error_cnt < 3) {
try {
- std::string reply = get_nmea("GPGGA");
+ std::string reply = get_cached_sensor("GPGGA", GPS_LOCK_FRESHNESS, false, false);
if(reply.size() <= 1) return false;
return (get_token(reply, 6) != "0");
@@ -236,6 +302,29 @@ private:
return false;
}
+ std::string get_servo(void) {
+
+ //enable servo reporting
+ _send("SERV:TRAC 1\n");
+ sleep(milliseconds(FIREFLY_STUPID_DELAY_MS));
+
+ std::string reply;
+
+ const boost::system_time comm_timeout = boost::get_system_time() + milliseconds(GPS_COMM_TIMEOUT_MS);
+ while(boost::get_system_time() < comm_timeout) {
+ reply = get_cached_sensor("SERVO", GPS_NMEA_LOW_FRESHNESS, false);
+ if(reply.size())
+ {
+ //disable it before leaving function
+ _send("SERV:TRAC 0\n");
+ return reply;
+ }
+ boost::this_thread::sleep(milliseconds(GPS_TIMEOUT_DELAY_MS));
+ }
+ throw uhd::value_error("get_stat(): no servo message found");
+ return std::string();
+ }
+
uart_iface::sptr _uart;
void _flush(void){
@@ -258,7 +347,12 @@ private:
GPS_TYPE_NONE
} gps_type;
- static const int GPS_COMM_TIMEOUT_MS = 1500;
+ static const int GPS_COMM_TIMEOUT_MS = 1300;
+ static const int GPS_NMEA_FRESHNESS = 10;
+ static const int GPS_NMEA_LOW_FRESHNESS = 2500;
+ static const int GPS_NMEA_NORMAL_FRESHNESS = 1000;
+ static const int GPS_SERVO_FRESHNESS = 2500;
+ static const int GPS_LOCK_FRESHNESS = 2500;
static const int GPS_TIMEOUT_DELAY_MS = 200;
static const int FIREFLY_STUPID_DELAY_MS = 200;
};
diff --git a/host/lib/usrp/mboard_eeprom.cpp b/host/lib/usrp/mboard_eeprom.cpp
index 1f4abc27e..dc25379f9 100644
--- a/host/lib/usrp/mboard_eeprom.cpp
+++ b/host/lib/usrp/mboard_eeprom.cpp
@@ -359,6 +359,69 @@ static void store_b100(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface){
}
/***********************************************************************
+ * Implementation of B200 load/store
+ **********************************************************************/
+/* On the B200, this field indicates the slave address. From the FX3, this
+ * address is always 0. */
+static const boost::uint8_t B200_EEPROM_SLAVE_ADDR = 0x04;
+
+//use char array so we dont need to attribute packed
+struct b200_eeprom_map{
+ unsigned char _r[220];
+ unsigned char revision[2];
+ unsigned char product[2];
+ unsigned char name[NAME_MAX_LEN];
+ unsigned char serial[SERIAL_LEN];
+};
+
+static void load_b200(mboard_eeprom_t &mb_eeprom, i2c_iface &iface){
+ //extract the revision number
+ mb_eeprom["revision"] = uint16_bytes_to_string(
+ iface.read_eeprom(B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, revision), 2)
+ );
+
+ //extract the product code
+ mb_eeprom["product"] = uint16_bytes_to_string(
+ iface.read_eeprom(B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, product), 2)
+ );
+
+ //extract the serial
+ mb_eeprom["serial"] = bytes_to_string(iface.read_eeprom(
+ B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, serial), SERIAL_LEN
+ ));
+
+ //extract the name
+ mb_eeprom["name"] = bytes_to_string(iface.read_eeprom(
+ B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, name), NAME_MAX_LEN
+ ));
+}
+
+static void store_b200(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface){
+ //parse the revision number
+ if (mb_eeprom.has_key("revision")) iface.write_eeprom(
+ B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, revision),
+ string_to_uint16_bytes(mb_eeprom["revision"])
+ );
+
+ //parse the product code
+ if (mb_eeprom.has_key("product")) iface.write_eeprom(
+ B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, product),
+ string_to_uint16_bytes(mb_eeprom["product"])
+ );
+
+ //store the serial
+ if (mb_eeprom.has_key("serial")) iface.write_eeprom(
+ B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, serial),
+ string_to_bytes(mb_eeprom["serial"], SERIAL_LEN)
+ );
+
+ //store the name
+ if (mb_eeprom.has_key("name")) iface.write_eeprom(
+ B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, name),
+ string_to_bytes(mb_eeprom["name"], NAME_MAX_LEN)
+ );
+}
+/***********************************************************************
* Implementation of E100 load/store
**********************************************************************/
static const boost::uint8_t E100_EEPROM_ADDR = 0x51;
@@ -451,6 +514,7 @@ mboard_eeprom_t::mboard_eeprom_t(i2c_iface &iface, const std::string &which){
if (which == "N100") load_n100(*this, iface);
if (which == "B000") load_b000(*this, iface);
if (which == "B100") load_b100(*this, iface);
+ if (which == "B200") load_b200(*this, iface);
if (which == "E100") load_e100(*this, iface);
}
@@ -458,5 +522,6 @@ void mboard_eeprom_t::commit(i2c_iface &iface, const std::string &which) const{
if (which == "N100") store_n100(*this, iface);
if (which == "B000") store_b000(*this, iface);
if (which == "B100") store_b100(*this, iface);
+ if (which == "B200") store_b200(*this, iface);
if (which == "E100") store_e100(*this, iface);
}
diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp
index eba3157a9..26ce1ccdd 100644
--- a/host/lib/usrp/multi_usrp.cpp
+++ b/host/lib/usrp/multi_usrp.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2010-2012 Ettus Research LLC
+// Copyright 2010-2013 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -19,7 +19,7 @@
#include <uhd/usrp/multi_usrp.hpp>
#include <uhd/utils/msg.hpp>
#include <uhd/exception.hpp>
-#include <uhd/utils/msg.hpp>
+#include <uhd/utils/log.hpp>
#include <uhd/utils/gain_group.hpp>
#include <uhd/usrp/dboard_id.hpp>
#include <uhd/usrp/mboard_eeprom.hpp>
@@ -140,6 +140,14 @@ static tune_result_t tune_xx_subdev_and_dsp(
}
//------------------------------------------------------------------
+ //-- poke the tune request args into the dboard
+ //------------------------------------------------------------------
+ if (rf_fe_subtree->exists("tune_args"))
+ {
+ rf_fe_subtree->access<device_addr_t>("tune_args").set(tune_request.args);
+ }
+
+ //------------------------------------------------------------------
//-- set the RF frequency depending upon the policy
//------------------------------------------------------------------
double target_rf_freq = 0.0;
@@ -537,8 +545,25 @@ public:
}
}
- subdev_spec_t get_rx_subdev_spec(size_t mboard){
- return _tree->access<subdev_spec_t>(mb_root(mboard) / "rx_subdev_spec").get();
+ subdev_spec_t get_rx_subdev_spec(size_t mboard)
+ {
+ subdev_spec_t spec = _tree->access<subdev_spec_t>(mb_root(mboard) / "rx_subdev_spec").get();
+ if (spec.empty())
+ {
+ try
+ {
+ const std::string db_name = _tree->list(mb_root(mboard) / "dboards").at(0);
+ const std::string fe_name = _tree->list(mb_root(mboard) / "dboards" / db_name / "rx_frontends").at(0);
+ spec.push_back(subdev_spec_pair_t(db_name, fe_name));
+ _tree->access<subdev_spec_t>(mb_root(mboard) / "rx_subdev_spec").set(spec);
+ }
+ catch(const std::exception &e)
+ {
+ throw uhd::index_error(str(boost::format("multi_usrp::get_rx_subdev_spec(%u) failed to make default spec - %s") % mboard % e.what()));
+ }
+ UHD_MSG(status) << "Selecting default RX front end spec: " << spec.to_pp_string() << std::endl;
+ }
+ return spec;
}
size_t get_rx_num_channels(void){
@@ -689,8 +714,25 @@ public:
}
}
- subdev_spec_t get_tx_subdev_spec(size_t mboard){
- return _tree->access<subdev_spec_t>(mb_root(mboard) / "tx_subdev_spec").get();
+ subdev_spec_t get_tx_subdev_spec(size_t mboard)
+ {
+ subdev_spec_t spec = _tree->access<subdev_spec_t>(mb_root(mboard) / "tx_subdev_spec").get();
+ if (spec.empty())
+ {
+ try
+ {
+ const std::string db_name = _tree->list(mb_root(mboard) / "dboards").at(0);
+ const std::string fe_name = _tree->list(mb_root(mboard) / "dboards" / db_name / "tx_frontends").at(0);
+ spec.push_back(subdev_spec_pair_t(db_name, fe_name));
+ _tree->access<subdev_spec_t>(mb_root(mboard) / "tx_subdev_spec").set(spec);
+ }
+ catch(const std::exception &e)
+ {
+ throw uhd::index_error(str(boost::format("multi_usrp::get_tx_subdev_spec(%u) failed to make default spec - %s") % mboard % e.what()));
+ }
+ UHD_MSG(status) << "Selecting default TX front end spec: " << spec.to_pp_string() << std::endl;
+ }
+ return spec;
}
size_t get_tx_num_channels(void){
@@ -835,6 +877,10 @@ private:
if (mcp.chan < sss) break;
mcp.chan -= sss;
}
+ if (mcp.mboard >= get_num_mboards())
+ {
+ throw uhd::index_error(str(boost::format("multi_usrp: RX channel %u out of range for configured RX frontends") % chan));
+ }
return mcp;
}
@@ -846,48 +892,108 @@ private:
if (mcp.chan < sss) break;
mcp.chan -= sss;
}
+ if (mcp.mboard >= get_num_mboards())
+ {
+ throw uhd::index_error(str(boost::format("multi_usrp: TX channel %u out of range for configured TX frontends") % chan));
+ }
return mcp;
}
- fs_path mb_root(const size_t mboard){
- const std::string name = _tree->list("/mboards").at(mboard);
- return "/mboards/" + name;
+ fs_path mb_root(const size_t mboard)
+ {
+ try
+ {
+ const std::string name = _tree->list("/mboards").at(mboard);
+ return "/mboards/" + name;
+ }
+ catch(const std::exception &e)
+ {
+ throw uhd::index_error(str(boost::format("multi_usrp::mb_root(%u) - %s") % mboard % e.what()));
+ }
}
- fs_path rx_dsp_root(const size_t chan){
+ fs_path rx_dsp_root(const size_t chan)
+ {
mboard_chan_pair mcp = rx_chan_to_mcp(chan);
- const std::string name = _tree->list(mb_root(mcp.mboard) / "rx_dsps").at(mcp.chan);
- return mb_root(mcp.mboard) / "rx_dsps" / name;
+ try
+ {
+ const std::string name = _tree->list(mb_root(mcp.mboard) / "rx_dsps").at(mcp.chan);
+ return mb_root(mcp.mboard) / "rx_dsps" / name;
+ }
+ catch(const std::exception &e)
+ {
+ throw uhd::index_error(str(boost::format("multi_usrp::rx_dsp_root(%u) - mcp(%u) - %s") % chan % mcp.chan % e.what()));
+ }
}
- fs_path tx_dsp_root(const size_t chan){
+ fs_path tx_dsp_root(const size_t chan)
+ {
mboard_chan_pair mcp = tx_chan_to_mcp(chan);
- const std::string name = _tree->list(mb_root(mcp.mboard) / "tx_dsps").at(mcp.chan);
- return mb_root(mcp.mboard) / "tx_dsps" / name;
+ try
+ {
+ const std::string name = _tree->list(mb_root(mcp.mboard) / "tx_dsps").at(mcp.chan);
+ return mb_root(mcp.mboard) / "tx_dsps" / name;
+ }
+ catch(const std::exception &e)
+ {
+ throw uhd::index_error(str(boost::format("multi_usrp::tx_dsp_root(%u) - mcp(%u) - %s") % chan % mcp.chan % e.what()));
+ }
}
- fs_path rx_fe_root(const size_t chan){
+ fs_path rx_fe_root(const size_t chan)
+ {
mboard_chan_pair mcp = rx_chan_to_mcp(chan);
- const subdev_spec_pair_t spec = get_rx_subdev_spec(mcp.mboard).at(mcp.chan);
- return mb_root(mcp.mboard) / "rx_frontends" / spec.db_name;
+ try
+ {
+ const subdev_spec_pair_t spec = get_rx_subdev_spec(mcp.mboard).at(mcp.chan);
+ return mb_root(mcp.mboard) / "rx_frontends" / spec.db_name;
+ }
+ catch(const std::exception &e)
+ {
+ throw uhd::index_error(str(boost::format("multi_usrp::rx_fe_root(%u) - mcp(%u) - %s") % chan % mcp.chan % e.what()));
+ }
}
- fs_path tx_fe_root(const size_t chan){
+ fs_path tx_fe_root(const size_t chan)
+ {
mboard_chan_pair mcp = tx_chan_to_mcp(chan);
- const subdev_spec_pair_t spec = get_tx_subdev_spec(mcp.mboard).at(mcp.chan);
- return mb_root(mcp.mboard) / "tx_frontends" / spec.db_name;
+ try
+ {
+ const subdev_spec_pair_t spec = get_tx_subdev_spec(mcp.mboard).at(mcp.chan);
+ return mb_root(mcp.mboard) / "tx_frontends" / spec.db_name;
+ }
+ catch(const std::exception &e)
+ {
+ throw uhd::index_error(str(boost::format("multi_usrp::tx_fe_root(%u) - mcp(%u) - %s") % chan % mcp.chan % e.what()));
+ }
}
- fs_path rx_rf_fe_root(const size_t chan){
+ fs_path rx_rf_fe_root(const size_t chan)
+ {
mboard_chan_pair mcp = rx_chan_to_mcp(chan);
- const subdev_spec_pair_t spec = get_rx_subdev_spec(mcp.mboard).at(mcp.chan);
- return mb_root(mcp.mboard) / "dboards" / spec.db_name / "rx_frontends" / spec.sd_name;
+ try
+ {
+ const subdev_spec_pair_t spec = get_rx_subdev_spec(mcp.mboard).at(mcp.chan);
+ return mb_root(mcp.mboard) / "dboards" / spec.db_name / "rx_frontends" / spec.sd_name;
+ }
+ catch(const std::exception &e)
+ {
+ throw uhd::index_error(str(boost::format("multi_usrp::rx_rf_fe_root(%u) - mcp(%u) - %s") % chan % mcp.chan % e.what()));
+ }
}
- fs_path tx_rf_fe_root(const size_t chan){
+ fs_path tx_rf_fe_root(const size_t chan)
+ {
mboard_chan_pair mcp = tx_chan_to_mcp(chan);
- const subdev_spec_pair_t spec = get_tx_subdev_spec(mcp.mboard).at(mcp.chan);
- return mb_root(mcp.mboard) / "dboards" / spec.db_name / "tx_frontends" / spec.sd_name;
+ try
+ {
+ const subdev_spec_pair_t spec = get_tx_subdev_spec(mcp.mboard).at(mcp.chan);
+ return mb_root(mcp.mboard) / "dboards" / spec.db_name / "tx_frontends" / spec.sd_name;
+ }
+ catch(const std::exception &e)
+ {
+ throw uhd::index_error(str(boost::format("multi_usrp::tx_rf_fe_root(%u) - mcp(%u) - %s") % chan % mcp.chan % e.what()));
+ }
}
gain_group::sptr rx_gain_group(size_t chan){
@@ -921,5 +1027,6 @@ private:
* The Make Function
**********************************************************************/
multi_usrp::sptr multi_usrp::make(const device_addr_t &dev_addr){
+ UHD_LOG << "multi_usrp::make with args " << dev_addr.to_pp_string() << std::endl;
return sptr(new multi_usrp_impl(dev_addr));
}
diff --git a/host/lib/usrp/usrp1/dboard_iface.cpp b/host/lib/usrp/usrp1/dboard_iface.cpp
index 39850d5d1..4c3141d9e 100644
--- a/host/lib/usrp/usrp1/dboard_iface.cpp
+++ b/host/lib/usrp/usrp1/dboard_iface.cpp
@@ -113,8 +113,8 @@ public:
void set_gpio_debug(unit_t, int);
boost::uint16_t read_gpio(unit_t);
- void write_i2c(boost::uint8_t, const byte_vector_t &);
- byte_vector_t read_i2c(boost::uint8_t, size_t);
+ void write_i2c(boost::uint16_t, const byte_vector_t &);
+ byte_vector_t read_i2c(boost::uint16_t, size_t);
void write_spi(unit_t unit,
const spi_config_t &config,
@@ -386,13 +386,13 @@ boost::uint32_t usrp1_dboard_iface::read_write_spi(unit_t unit,
/***********************************************************************
* I2C
**********************************************************************/
-void usrp1_dboard_iface::write_i2c(boost::uint8_t addr,
+void usrp1_dboard_iface::write_i2c(boost::uint16_t addr,
const byte_vector_t &bytes)
{
return _iface->write_i2c(addr, bytes);
}
-byte_vector_t usrp1_dboard_iface::read_i2c(boost::uint8_t addr,
+byte_vector_t usrp1_dboard_iface::read_i2c(boost::uint16_t addr,
size_t num_bytes)
{
return _iface->read_i2c(addr, num_bytes);
diff --git a/host/lib/usrp/usrp1/io_impl.cpp b/host/lib/usrp/usrp1/io_impl.cpp
index 8940a92bb..d384eb13f 100644
--- a/host/lib/usrp/usrp1/io_impl.cpp
+++ b/host/lib/usrp/usrp1/io_impl.cpp
@@ -356,6 +356,11 @@ public:
return _stc->recv_post(metadata, num_samps_recvd);
}
+ void issue_stream_cmd(const stream_cmd_t &stream_cmd)
+ {
+ _stc->issue_stream_cmd(stream_cmd);
+ }
+
private:
size_t _max_num_samps;
soft_time_ctrl::sptr _stc;
@@ -410,6 +415,12 @@ public:
return num_samps_sent;
}
+ bool recv_async_msg(
+ async_metadata_t &async_metadata, double timeout = 0.1
+ ){
+ return _stc->get_async_queue().pop_with_timed_wait(async_metadata, timeout);
+ }
+
private:
size_t _max_num_samps;
soft_time_ctrl::sptr _stc;
diff --git a/host/lib/usrp/usrp1/usrp1_iface.cpp b/host/lib/usrp/usrp1/usrp1_iface.cpp
index 16b747e45..9301721aa 100644
--- a/host/lib/usrp/usrp1/usrp1_iface.cpp
+++ b/host/lib/usrp/usrp1/usrp1_iface.cpp
@@ -104,11 +104,11 @@ public:
/*******************************************************************
* I2C
******************************************************************/
- void write_i2c(boost::uint8_t addr, const byte_vector_t &bytes){
+ void write_i2c(boost::uint16_t addr, const byte_vector_t &bytes){
return _ctrl_transport->write_i2c(addr, bytes);
}
- byte_vector_t read_i2c(boost::uint8_t addr, size_t num_bytes){
+ byte_vector_t read_i2c(boost::uint16_t addr, size_t num_bytes){
return _ctrl_transport->read_i2c(addr, num_bytes);
}
diff --git a/host/lib/usrp/usrp1/usrp1_iface.hpp b/host/lib/usrp/usrp1/usrp1_iface.hpp
index 4612d7912..7fc943190 100644
--- a/host/lib/usrp/usrp1/usrp1_iface.hpp
+++ b/host/lib/usrp/usrp1/usrp1_iface.hpp
@@ -1,5 +1,5 @@
//
-// Copyright 2010-2011 Ettus Research LLC
+// Copyright 2010-2013 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -19,7 +19,7 @@
#define INCLUDED_USRP1_IFACE_HPP
#include "fx2_ctrl.hpp"
-#include "wb_iface.hpp"
+#include <uhd/types/wb_iface.hpp>
#include <uhd/types/serial.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/utility.hpp>
@@ -42,7 +42,8 @@
* Provides a set of functions to implementation layer.
* Including spi, peek, poke, control...
*/
-class usrp1_iface : public wb_iface, public uhd::i2c_iface, public uhd::spi_iface, boost::noncopyable{
+class usrp1_iface : public uhd::wb_iface, public uhd::i2c_iface, public uhd::spi_iface, boost::noncopyable
+{
public:
typedef boost::shared_ptr<usrp1_iface> sptr;
diff --git a/host/lib/usrp/usrp2/dboard_iface.cpp b/host/lib/usrp/usrp2/dboard_iface.cpp
index edd9ef242..8f2d0f0dc 100644
--- a/host/lib/usrp/usrp2/dboard_iface.cpp
+++ b/host/lib/usrp/usrp2/dboard_iface.cpp
@@ -60,8 +60,8 @@ public:
void set_gpio_debug(unit_t, int);
boost::uint16_t read_gpio(unit_t);
- void write_i2c(boost::uint8_t, const byte_vector_t &);
- byte_vector_t read_i2c(boost::uint8_t, size_t);
+ void write_i2c(boost::uint16_t, const byte_vector_t &);
+ byte_vector_t read_i2c(boost::uint16_t, size_t);
void set_clock_rate(unit_t, double);
double get_clock_rate(unit_t);
@@ -229,11 +229,11 @@ boost::uint32_t usrp2_dboard_iface::read_write_spi(
/***********************************************************************
* I2C
**********************************************************************/
-void usrp2_dboard_iface::write_i2c(boost::uint8_t addr, const byte_vector_t &bytes){
+void usrp2_dboard_iface::write_i2c(boost::uint16_t addr, const byte_vector_t &bytes){
return _i2c_iface->write_i2c(addr, bytes);
}
-byte_vector_t usrp2_dboard_iface::read_i2c(boost::uint8_t addr, size_t num_bytes){
+byte_vector_t usrp2_dboard_iface::read_i2c(boost::uint16_t addr, size_t num_bytes){
return _i2c_iface->read_i2c(addr, num_bytes);
}
diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp
index e06cf8f6f..9ee6abed0 100644
--- a/host/lib/usrp/usrp2/io_impl.cpp
+++ b/host/lib/usrp/usrp2/io_impl.cpp
@@ -467,6 +467,8 @@ rx_streamer::sptr usrp2_impl::get_rx_stream(const uhd::stream_args_t &args_){
my_streamer->set_xport_chan_get_buff(chan_i, boost::bind(
&zero_copy_if::get_recv_buff, _mbc[mb].rx_dsp_xports[dsp], _1
), true /*flush*/);
+ my_streamer->set_issue_stream_cmd(chan_i, boost::bind(
+ &rx_dsp_core_200::issue_stream_command, _mbc[mb].rx_dsps[dsp], _1));
_mbc[mb].rx_streamers[dsp] = my_streamer; //store weak pointer
break;
}
@@ -536,6 +538,7 @@ tx_streamer::sptr usrp2_impl::get_tx_stream(const uhd::stream_args_t &args_){
my_streamer->set_xport_chan_get_buff(chan_i, boost::bind(
&usrp2_impl::io_impl::get_send_buff, _io_impl.get(), abs, _1
));
+ my_streamer->set_async_receiver(boost::bind(&bounded_buffer<async_metadata_t>::pop_with_timed_wait, &(_io_impl->async_msg_fifo), _1, _2));
_mbc[mb].tx_streamers[dsp] = my_streamer; //store weak pointer
break;
}
diff --git a/host/lib/usrp/usrp2/usrp2_fifo_ctrl.hpp b/host/lib/usrp/usrp2/usrp2_fifo_ctrl.hpp
index b48d05aa2..13dfb5b46 100644
--- a/host/lib/usrp/usrp2/usrp2_fifo_ctrl.hpp
+++ b/host/lib/usrp/usrp2/usrp2_fifo_ctrl.hpp
@@ -23,14 +23,15 @@
#include <uhd/transport/zero_copy.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/utility.hpp>
-#include "wb_iface.hpp"
+#include <uhd/types/wb_iface.hpp>
#include <string>
/*!
* The usrp2 FIFO control class:
* Provide high-speed peek/poke interface.
*/
-class usrp2_fifo_ctrl : public wb_iface, public uhd::spi_iface{
+class usrp2_fifo_ctrl : public uhd::wb_iface, public uhd::spi_iface
+{
public:
typedef boost::shared_ptr<usrp2_fifo_ctrl> sptr;
diff --git a/host/lib/usrp/usrp2/usrp2_iface.cpp b/host/lib/usrp/usrp2/usrp2_iface.cpp
index 8804433e7..3b230ca69 100644
--- a/host/lib/usrp/usrp2/usrp2_iface.cpp
+++ b/host/lib/usrp/usrp2/usrp2_iface.cpp
@@ -240,7 +240,7 @@ public:
/***********************************************************************
* I2C
**********************************************************************/
- void write_i2c(boost::uint8_t addr, const byte_vector_t &buf){
+ void write_i2c(boost::uint16_t addr, const byte_vector_t &buf){
//setup the out data
usrp2_ctrl_data_t out_data = usrp2_ctrl_data_t();
out_data.id = htonl(USRP2_CTRL_ID_WRITE_THESE_I2C_VALUES_BRO);
@@ -258,7 +258,7 @@ public:
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){
+ byte_vector_t read_i2c(boost::uint16_t addr, size_t num_bytes){
//setup the out data
usrp2_ctrl_data_t out_data = usrp2_ctrl_data_t();
out_data.id = htonl(USRP2_CTRL_ID_DO_AN_I2C_READ_FOR_ME_BRO);
diff --git a/host/lib/usrp/usrp2/usrp2_iface.hpp b/host/lib/usrp/usrp2/usrp2_iface.hpp
index ed4de02d5..a01f2ccfa 100644
--- a/host/lib/usrp/usrp2/usrp2_iface.hpp
+++ b/host/lib/usrp/usrp2/usrp2_iface.hpp
@@ -1,5 +1,5 @@
//
-// Copyright 2010-2012 Ettus Research LLC
+// Copyright 2010-2013 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
@@ -25,7 +25,7 @@
#include <boost/utility.hpp>
#include <boost/function.hpp>
#include "usrp2_regs.hpp"
-#include "wb_iface.hpp"
+#include <uhd/types/wb_iface.hpp>
#include <string>
/*!
@@ -33,7 +33,8 @@
* Provides a set of functions to implementation layer.
* Including spi, peek, poke, control...
*/
-class usrp2_iface : public wb_iface, public uhd::spi_iface, public uhd::i2c_iface{
+class usrp2_iface : public uhd::wb_iface, public uhd::spi_iface, public uhd::i2c_iface
+{
public:
typedef boost::shared_ptr<usrp2_iface> sptr;
/*!
diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp
index a6c0d87cf..f9988287f 100644
--- a/host/lib/usrp/usrp2/usrp2_impl.hpp
+++ b/host/lib/usrp/usrp2/usrp2_impl.hpp
@@ -57,7 +57,7 @@ static const std::string USRP2_EEPROM_MAP_KEY = "N100";
//! Make a usrp2 dboard interface.
uhd::usrp::dboard_iface::sptr make_usrp2_dboard_iface(
- wb_iface::sptr wb_iface,
+ uhd::wb_iface::sptr wb_iface,
uhd::i2c_iface::sptr i2c_iface,
uhd::spi_iface::sptr spi_iface,
usrp2_clock_ctrl::sptr clk_ctrl
@@ -84,7 +84,7 @@ private:
usrp2_iface::sptr iface;
usrp2_fifo_ctrl::sptr fifo_ctrl;
uhd::spi_iface::sptr spiface;
- wb_iface::sptr wbiface;
+ uhd::wb_iface::sptr wbiface;
usrp2_clock_ctrl::sptr clock;
usrp2_codec_ctrl::sptr codec;
uhd::gps_ctrl::sptr gps;
diff --git a/host/lib/utils/CMakeLists.txt b/host/lib/utils/CMakeLists.txt
index 95105f917..a28e1f9ef 100644
--- a/host/lib/utils/CMakeLists.txt
+++ b/host/lib/utils/CMakeLists.txt
@@ -115,14 +115,14 @@ SET_SOURCE_FILES_PROPERTIES(
########################################################################
# Define UHD_PKG_DATA_PATH for paths.cpp
########################################################################
-FILE(TO_NATIVE_PATH ${CMAKE_INSTALL_PREFIX}/${PKG_DATA_DIR} UHD_PKG_DATA_PATH)
-STRING(REPLACE "\\" "\\\\" UHD_PKG_DATA_PATH ${UHD_PKG_DATA_PATH})
-MESSAGE(STATUS "Full package data directory: ${UHD_PKG_DATA_PATH}")
+FILE(TO_NATIVE_PATH ${CMAKE_INSTALL_PREFIX} UHD_PKG_PATH)
+STRING(REPLACE "\\" "\\\\" UHD_PKG_PATH ${UHD_PKG_PATH})
SET_SOURCE_FILES_PROPERTIES(
${CMAKE_CURRENT_SOURCE_DIR}/paths.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/images.cpp
PROPERTIES COMPILE_DEFINITIONS
- "UHD_PKG_DATA_PATH=\"${UHD_PKG_DATA_PATH}\""
+ "UHD_PKG_PATH=\"${UHD_PKG_PATH}\";UHD_LIB_DIR=\"lib${LIB_SUFFIX}\""
)
########################################################################
diff --git a/host/lib/utils/images.cpp b/host/lib/utils/images.cpp
index 251cadeaa..1ba2f81e6 100644
--- a/host/lib/utils/images.cpp
+++ b/host/lib/utils/images.cpp
@@ -42,7 +42,7 @@ std::string uhd::find_image_path(const std::string &image_name){
}
std::string uhd::find_images_downloader(void){
- return fs::path((fs::path(get_pkg_data_path()) / "utils" / "uhd_images_downloader.py")).string();
+ return fs::path(fs::path(uhd::get_pkg_path()) / UHD_LIB_DIR / "uhd" / "utils" / "uhd_images_downloader.py").string();
}
std::string uhd::print_images_error(void){
diff --git a/host/lib/utils/paths.cpp b/host/lib/utils/paths.cpp
index 6e0dd13bd..25cade693 100644
--- a/host/lib/utils/paths.cpp
+++ b/host/lib/utils/paths.cpp
@@ -72,20 +72,21 @@ static std::vector<fs::path> get_env_paths(const std::string &var_name){
/***********************************************************************
* Get a list of special purpose paths
**********************************************************************/
-std::string uhd::get_pkg_data_path(void)
+std::string uhd::get_pkg_path(void)
{
- return get_env_var("UHD_PKG_DATA_PATH", UHD_PKG_DATA_PATH);
+ return get_env_var("UHD_PKG_PATH", UHD_PKG_PATH);
}
std::vector<fs::path> get_image_paths(void){
std::vector<fs::path> paths = get_env_paths("UHD_IMAGE_PATH");
- paths.push_back(fs::path(uhd::get_pkg_data_path()) / "images");
+ paths.push_back(fs::path(uhd::get_pkg_path()) / "share" / "uhd" / "images");
return paths;
}
std::vector<fs::path> get_module_paths(void){
std::vector<fs::path> paths = get_env_paths("UHD_MODULE_PATH");
- paths.push_back(fs::path(uhd::get_pkg_data_path()) / "modules");
+ paths.push_back(fs::path(uhd::get_pkg_path()) / UHD_LIB_DIR / "uhd" / "modules");
+ paths.push_back(fs::path(uhd::get_pkg_path()) / "share" / "uhd" / "modules");
return paths;
}
diff --git a/host/lib/utils/tasks.cpp b/host/lib/utils/tasks.cpp
index 1f735de06..08c32a5fb 100644
--- a/host/lib/utils/tasks.cpp
+++ b/host/lib/utils/tasks.cpp
@@ -16,11 +16,13 @@
//
#include <uhd/utils/tasks.hpp>
+#include <uhd/utils/msg_task.hpp>
#include <uhd/utils/msg.hpp>
#include <boost/thread/thread.hpp>
#include <boost/thread/barrier.hpp>
#include <exception>
#include <iostream>
+#include <vector>
using namespace uhd;
@@ -80,3 +82,100 @@ private:
task::sptr task::make(const task_fcn_type &task_fcn){
return task::sptr(new task_impl(task_fcn));
}
+
+/*
+ * During shutdown pointers to queues for radio_ctrl_core might not be available anymore.
+ * msg_task_impl provides a dump_queue for such messages.
+ * ctrl_cores can check this queue for stranded messages.
+ */
+
+class msg_task_impl : public msg_task{
+public:
+
+ msg_task_impl(const task_fcn_type &task_fcn):
+ _spawn_barrier(2)
+ {
+ _thread_group.create_thread(boost::bind(&msg_task_impl::task_loop, this, task_fcn));
+ _spawn_barrier.wait();
+ }
+
+ ~msg_task_impl(void){
+ _running = false;
+ _thread_group.interrupt_all();
+ _thread_group.join_all();
+ }
+
+ /*
+ * Returns the first message for the given SID.
+ * This way a radio_ctrl_core doesn't have to die in timeout but can check for stranded messages here.
+ * This might happen during shutdown when dtors are called.
+ * See also: comments in b200_io_impl->handle_async_task
+ */
+ msg_payload_t get_msg_from_dump_queue(boost::uint32_t sid)
+ {
+ boost::mutex::scoped_lock lock(_mutex);
+ msg_payload_t b;
+ for (size_t i = 0; i < _dump_queue.size(); i++) {
+ if (sid == _dump_queue[i].first) {
+ b = _dump_queue[i].second;
+ _dump_queue.erase(_dump_queue.begin() + i);
+ break;
+ }
+ }
+ return b;
+ }
+
+private:
+
+ void task_loop(const task_fcn_type &task_fcn){
+ _running = true;
+ _spawn_barrier.wait();
+
+ try{
+ while (_running){
+ boost::optional<msg_type_t> buff = task_fcn();
+ if(buff != boost::none){
+ /*
+ * If a message gets stranded it is returned by task_fcn and then pushed to the dump_queue.
+ * This way ctrl_cores can check dump_queue for missing messages.
+ */
+ boost::mutex::scoped_lock lock(_mutex);
+ _dump_queue.push_back(buff.get() );
+ }
+ }
+ }
+ catch(const boost::thread_interrupted &){
+ //this is an ok way to exit the task loop
+ }
+ catch(const std::exception &e){
+ do_error_msg(e.what());
+ }
+ catch(...){
+ //FIXME
+ //Unfortunately, this is also an ok way to end a task,
+ //because on some systems boost throws uncatchables.
+ }
+ }
+
+ void do_error_msg(const std::string &msg){
+ UHD_MSG(error)
+ << "An unexpected exception was caught in a task loop." << std::endl
+ << "The task loop will now exit, things may not work." << std::endl
+ << msg << std::endl
+ ;
+ }
+
+ boost::mutex _mutex;
+ boost::thread_group _thread_group;
+ boost::barrier _spawn_barrier;
+ bool _running;
+
+ /*
+ * This queue holds stranded messages until a radio_ctrl_core grabs them via 'get_msg_from_dump_queue'.
+ */
+ std::vector <msg_type_t> _dump_queue;
+};
+
+msg_task::sptr msg_task::make(const task_fcn_type &task_fcn){
+ return msg_task::sptr(new msg_task_impl(task_fcn));
+}