diff options
Diffstat (limited to 'host/lib')
112 files changed, 7086 insertions, 443 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..f578b6c95 --- /dev/null +++ b/host/lib/convert/convert_unpack_sc12.cpp @@ -0,0 +1,152 @@ +// +// 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; +}; + +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; +    } + +    void operator()(const input_type &inputs, const output_type &outputs, const size_t nsamps) +    { +        const item32_sc12_3x *input = reinterpret_cast<const item32_sc12_3x *>(size_t(inputs[0]) & ~0x3); +        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 +        const size_t head_samps = size_t(inputs[0]) & 0x3; +        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 +        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..0ef53db0a 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 @@ -23,6 +23,7 @@  #include <boost/weak_ptr.hpp>  #include <boost/thread/mutex.hpp>  #include <boost/foreach.hpp> +#include <cstdlib>  #include <iostream>  using namespace uhd; @@ -59,6 +60,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 +147,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 +161,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 +255,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{ 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..197e257da 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,14 +42,51 @@ 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; + +/*! + * 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; +}; +  /*!   * 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.   */  //! helper function: handles all async callbacks -static void LIBUSB_CALL libusb_async_cb(libusb_transfer *lut){ -    *(static_cast<bool *>(lut->user_data)) = true; +static void LIBUSB_CALL libusb_async_cb(libusb_transfer *lut) +{ +    lut_result_t *r = (lut_result_t *)lut->user_data; +    r->completed = 1; +    r->status = lut->status; +    r->actual_length = lut->actual_length;  }  /*! @@ -61,7 +104,8 @@ static void LIBUSB_CALL libusb_async_cb(libusb_transfer *lut){   * \param completed a reference to the completed flag   * \return true for completion, false for timeout   */ -UHD_INLINE bool wait_for_completion(libusb_context *ctx, const double timeout, bool &completed){ +UHD_INLINE bool wait_for_completion(libusb_context *ctx, const double timeout, int &completed) +{      //already completed by a previous call?      if (completed) return true; @@ -69,7 +113,7 @@ UHD_INLINE bool wait_for_completion(libusb_context *ctx, const double timeout, b      timeval tv;      tv.tv_sec = 0;      tv.tv_usec = 0; -    libusb_handle_events_timeout(ctx, &tv); +    libusb_handle_events_timeout_completed(ctx, &tv, &completed);      if (completed) return true;      //finish the rest with a timeout loop @@ -78,73 +122,54 @@ UHD_INLINE bool wait_for_completion(libusb_context *ctx, const double timeout, b          timeval tv;          tv.tv_sec = 0;          tv.tv_usec = 10000; /*10ms*/ -        libusb_handle_events_timeout(ctx, &tv); +        libusb_handle_events_timeout_completed(ctx, &tv, &completed);      }      return completed;  }  /*********************************************************************** - * 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); -    } - -    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); -        } -        return managed_recv_buffer::sptr(); -    } - -    bool completed; - -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){_release_cb(this);} -    void release(void){ -        completed = false; -        _lut->length = size(); -        UHD_ASSERT_THROW(libusb_submit_transfer(_lut) == 0); +    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, _frame_size); +    template <typename buffer_type> +    UHD_INLINE typename buffer_type::sptr get_new(const double timeout) +    { +        if (wait_for_completion(_ctx, timeout, result.completed)) +        { +            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_send_buffer::sptr(); +        return typename buffer_type::sptr();      } -    bool completed; +    lut_result_t result;  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 +178,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 +213,170 @@ 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_zero_copy_single(void) +    {          libusb_context *ctx = libusb::session::get_global_session()->get_context();          //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; +        int completed = 0;          wait_for_completion(ctx, 0.01, completed);          //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 l(_mutex); +            if (_enqueued.empty()) +            { +                _cond.timed_wait(l, 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); -    } +        buff = front->get_new<buffer_type>(timeout); -    size_t get_num_recv_frames(void) const { return _num_recv_frames; } -    size_t get_num_send_frames(void) const { return _num_send_frames; } +        boost::mutex::scoped_lock l(_mutex); +        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..1d05e159c --- /dev/null +++ b/host/lib/usrp/b200/b200_iface.cpp @@ -0,0 +1,592 @@ +// +// 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; + +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); +        fx3_control_read(B200_VREQ_EEPROM_READ, +                         0, offset | (boost::uint16_t(addr) << 8), +                         (unsigned char*) &recv_bytes[0], +                         num_bytes); +        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; + +        unsigned char out_buff[64]; +        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, sizeof(out_buff)); +            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..1247d1f86 --- /dev/null +++ b/host/lib/usrp/b200/b200_iface.hpp @@ -0,0 +1,70 @@ +// +// 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" + +class 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; +}; + + +#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..0da388b93 --- /dev/null +++ b/host/lib/usrp/b200/b200_impl.cpp @@ -0,0 +1,892 @@ +// +// 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; + +const boost::uint16_t B200_VENDOR_ID  = 0x2500; +const boost::uint16_t B200_PRODUCT_ID = 0x0020; +const boost::uint16_t INIT_PRODUCT_ID = 0x00f0; + +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->get_manufacturer() != "Ettus Research LLC") +        { +            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 +    std::vector<usb_device_handle::sptr> device_list = +        usb_device_handle::get_device_list(B200_VENDOR_ID, B200_PRODUCT_ID); + +    //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::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..eced4a539 --- /dev/null +++ b/host/lib/usrp/b200/b200_impl.hpp @@ -0,0 +1,196 @@ +// +// 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 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"; +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::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; +    void 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..d643ef855 --- /dev/null +++ b/host/lib/usrp/b200/b200_io_impl.cpp @@ -0,0 +1,378 @@ +// +// 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); +} + +void 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; +    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 *>()); +        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; +    } +} + +/*********************************************************************** + * 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..5298fd213 --- /dev/null +++ b/host/lib/usrp/cores/radio_ctrl_core_3000.cpp @@ -0,0 +1,313 @@ +// +// 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: + +    /******************************************************************* +     * 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 +            { +                UHD_ASSERT_THROW(_resp_queue.pop_with_timed_wait(resp_buff, _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; +    } + +    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(boost::shared_ptr<void> 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; +    boost::shared_ptr<void> _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; +    struct resp_buff_type +    { +        boost::uint32_t data[8]; +    }; +    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..a49ca2a4b --- /dev/null +++ b/host/lib/usrp/cores/radio_ctrl_core_3000.hpp @@ -0,0 +1,58 @@ +// +// 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/types/time_spec.hpp> +#include <uhd/transport/zero_copy.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include <uhd/types/wb_iface.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(boost::shared_ptr<void> 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..9db29e65a 100644 --- a/host/lib/usrp/dboard/db_sbx_common.cpp +++ b/host/lib/usrp/dboard/db_sbx_common.cpp @@ -32,6 +32,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 +115,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 +135,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 +149,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 +167,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 +182,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)); diff --git a/host/lib/usrp/dboard/db_sbx_common.hpp b/host/lib/usrp/dboard/db_sbx_common.hpp index 2a0e83115..4f3a2eeaa 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)) @@ -213,6 +214,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_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;  } | 
