diff options
Diffstat (limited to 'host/lib')
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 ®) = 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)); +} |