diff options
Diffstat (limited to 'host/lib')
81 files changed, 4864 insertions, 4968 deletions
| diff --git a/host/lib/CMakeLists.txt b/host/lib/CMakeLists.txt index 2b0210e6e..7a76ebd53 100644 --- a/host/lib/CMakeLists.txt +++ b/host/lib/CMakeLists.txt @@ -86,11 +86,11 @@ CONFIGURE_FILE(  # Append to the list of sources for lib uhd  ########################################################################  LIBUHD_APPEND_SOURCES( +    ${CMAKE_CURRENT_SOURCE_DIR}/deprecated.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/device.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/exception.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/property_tree.cpp      ${CMAKE_CURRENT_BINARY_DIR}/version.cpp -    ${CMAKE_CURRENT_SOURCE_DIR}/wax.cpp  )  ######################################################################## diff --git a/host/lib/convert/CMakeLists.txt b/host/lib/convert/CMakeLists.txt index b260cb247..4cc421884 100644 --- a/host/lib/convert/CMakeLists.txt +++ b/host/lib/convert/CMakeLists.txt @@ -112,17 +112,6 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})  INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})  LIBUHD_PYTHON_GEN_SOURCE( -    ${CMAKE_CURRENT_SOURCE_DIR}/gen_convert_pred.py -    ${CMAKE_CURRENT_BINARY_DIR}/convert_pred.hpp -) - -INCLUDE(AddFileDependencies) -ADD_FILE_DEPENDENCIES( -    ${CMAKE_CURRENT_SOURCE_DIR}/convert_impl.cpp -    ${CMAKE_CURRENT_BINARY_DIR}/convert_pred.hpp -) - -LIBUHD_PYTHON_GEN_SOURCE(      ${CMAKE_CURRENT_SOURCE_DIR}/gen_convert_general.py      ${CMAKE_CURRENT_BINARY_DIR}/convert_general.cpp  ) diff --git a/host/lib/convert/convert_common.hpp b/host/lib/convert/convert_common.hpp index 7f513b124..34fb848c3 100644 --- a/host/lib/convert/convert_common.hpp +++ b/host/lib/convert/convert_common.hpp @@ -23,43 +23,58 @@  #include <boost/cstdint.hpp>  #include <complex> -#define DECLARE_CONVERTER(fcn, prio) \ +#define _DECLARE_CONVERTER(fcn, in_form, num_in, out_form, num_out, prio) \      static void fcn( \          const uhd::convert::input_type &inputs, \          const uhd::convert::output_type &outputs, \ -        size_t nsamps, double scale_factor \ +        const size_t nsamps, const double scale_factor \      ); \ -    UHD_STATIC_BLOCK(register_##fcn##_##prio){ \ -        uhd::convert::register_converter(#fcn, fcn, prio); \ +    UHD_STATIC_BLOCK(__register_##fcn##_##prio){ \ +        uhd::convert::id_type id; \ +        id.input_format = #in_form; \ +        id.num_inputs = num_in; \ +        id.output_format = #out_form; \ +        id.num_outputs = num_out; \ +        uhd::convert::register_converter(id, fcn, prio); \      } \      static void fcn( \          const uhd::convert::input_type &inputs, \          const uhd::convert::output_type &outputs, \ -        size_t nsamps, double scale_factor \ +        const size_t nsamps, const double scale_factor \      ) +#define DECLARE_CONVERTER(in_form, num_in, out_form, num_out, prio) \ +    _DECLARE_CONVERTER(__convert_##in_form##_##num_in##_##out_form##_##num_out, in_form, num_in, out_form, num_out, prio) +  /***********************************************************************   * Typedefs   **********************************************************************/  typedef std::complex<double>         fc64_t;  typedef std::complex<float>          fc32_t; +typedef std::complex<boost::int32_t> sc32_t;  typedef std::complex<boost::int16_t> sc16_t;  typedef std::complex<boost::int8_t>  sc8_t; +typedef double                       f64_t; +typedef float                        f32_t; +typedef boost::int32_t               s32_t; +typedef boost::int16_t               s16_t; +typedef boost::int8_t                s8_t; +  typedef boost::uint32_t              item32_t;  /*********************************************************************** - * Convert complex short buffer to items32 + * Convert complex short buffer to items32 sc16   **********************************************************************/ -static UHD_INLINE item32_t sc16_to_item32(sc16_t num, double){ +static UHD_INLINE item32_t sc16_to_item32_sc16(sc16_t num, double){      boost::uint16_t real = num.real();      boost::uint16_t imag = num.imag();      return (item32_t(real) << 16) | (item32_t(imag) << 0);  }  /*********************************************************************** - * Convert items32 buffer to complex short + * Convert items32 sc16 buffer to complex short   **********************************************************************/ -static UHD_INLINE sc16_t item32_to_sc16(item32_t item, double){ +static UHD_INLINE sc16_t item32_sc16_to_sc16(item32_t item, double){      return sc16_t(          boost::int16_t(item >> 16),          boost::int16_t(item >> 0) @@ -67,41 +82,83 @@ static UHD_INLINE sc16_t item32_to_sc16(item32_t item, double){  }  /*********************************************************************** - * Convert complex float buffer to items32 (no swap) + * Convert complex float buffer to items32 sc16   **********************************************************************/ -static UHD_INLINE item32_t fc32_to_item32(fc32_t num, float scale_factor){ -    boost::uint16_t real = boost::int16_t(num.real()*scale_factor); -    boost::uint16_t imag = boost::int16_t(num.imag()*scale_factor); +static UHD_INLINE item32_t fc32_to_item32_sc16(fc32_t num, double scale_factor){ +    boost::uint16_t real = boost::int16_t(num.real()*float(scale_factor)); +    boost::uint16_t imag = boost::int16_t(num.imag()*float(scale_factor));      return (item32_t(real) << 16) | (item32_t(imag) << 0);  }  /*********************************************************************** - * Convert items32 buffer to complex float + * Convert items32 sc16 buffer to complex float   **********************************************************************/ -static UHD_INLINE fc32_t item32_to_fc32(item32_t item, float scale_factor){ +static UHD_INLINE fc32_t item32_sc16_to_fc32(item32_t item, double scale_factor){      return fc32_t( -        float(boost::int16_t(item >> 16)*scale_factor), -        float(boost::int16_t(item >> 0)*scale_factor) +        float(boost::int16_t(item >> 16)*float(scale_factor)), +        float(boost::int16_t(item >> 0)*float(scale_factor))      );  }  /*********************************************************************** - * Convert complex double buffer to items32 (no swap) + * Convert complex double buffer to items32 sc16   **********************************************************************/ -static UHD_INLINE item32_t fc64_to_item32(fc64_t num, double scale_factor){ +static UHD_INLINE item32_t fc64_to_item32_sc16(fc64_t num, double scale_factor){      boost::uint16_t real = boost::int16_t(num.real()*scale_factor);      boost::uint16_t imag = boost::int16_t(num.imag()*scale_factor);      return (item32_t(real) << 16) | (item32_t(imag) << 0);  }  /*********************************************************************** - * Convert items32 buffer to complex double + * Convert items32 sc16 buffer to complex double   **********************************************************************/ -static UHD_INLINE fc64_t item32_to_fc64(item32_t item, double scale_factor){ +static UHD_INLINE fc64_t item32_sc16_to_fc64(item32_t item, double scale_factor){      return fc64_t(          float(boost::int16_t(item >> 16)*scale_factor),          float(boost::int16_t(item >> 0)*scale_factor)      );  } +/*********************************************************************** + * Convert items32 sc8 buffer to complex short + **********************************************************************/ +static UHD_INLINE void item32_sc8_to_sc16(item32_t item, sc16_t &out0, sc16_t &out1, double){ +    out0 = sc16_t( +        boost::int8_t(item >> 8), +        boost::int8_t(item >> 0) +    ); +    out1 = sc16_t( +        boost::int8_t(item >> 24), +        boost::int8_t(item >> 16) +    ); +} + +/*********************************************************************** + * Convert items32 sc8 buffer to complex float + **********************************************************************/ +static UHD_INLINE void item32_sc8_to_fc32(item32_t item, fc32_t &out0, fc32_t &out1, double scale_factor){ +    out0 = fc32_t( +        float(boost::int8_t(item >> 8)*float(scale_factor)), +        float(boost::int8_t(item >> 0)*float(scale_factor)) +    ); +    out1 = fc32_t( +        float(boost::int8_t(item >> 24)*float(scale_factor)), +        float(boost::int8_t(item >> 16)*float(scale_factor)) +    ); +} + +/*********************************************************************** + * Convert items32 sc8 buffer to complex double + **********************************************************************/ +static UHD_INLINE void item32_sc8_to_fc64(item32_t item, fc64_t &out0, fc64_t &out1, double scale_factor){ +    out0 = fc64_t( +        float(boost::int8_t(item >> 8)*scale_factor), +        float(boost::int8_t(item >> 0)*scale_factor) +    ); +    out1 = fc64_t( +        float(boost::int8_t(item >> 24)*scale_factor), +        float(boost::int8_t(item >> 16)*scale_factor) +    ); +} +  #endif /* INCLUDED_LIBUHD_CONVERT_COMMON_HPP */ diff --git a/host/lib/convert/convert_fc32_with_sse2.cpp b/host/lib/convert/convert_fc32_with_sse2.cpp index 676e1561c..24a939d6c 100644 --- a/host/lib/convert/convert_fc32_with_sse2.cpp +++ b/host/lib/convert/convert_fc32_with_sse2.cpp @@ -21,7 +21,7 @@  using namespace uhd::convert; -DECLARE_CONVERTER(convert_fc32_1_to_item32_1_nswap, PRIORITY_CUSTOM){ +DECLARE_CONVERTER(fc32, 1, sc16_item32_le, 1, PRIORITY_SIMD){      const fc32_t *input = reinterpret_cast<const fc32_t *>(inputs[0]);      item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); @@ -51,7 +51,7 @@ DECLARE_CONVERTER(convert_fc32_1_to_item32_1_nswap, PRIORITY_CUSTOM){      //dispatch according to alignment      switch (size_t(input) & 0xf){      case 0x8: -        output[i] = fc32_to_item32(input[i], float(scale_factor)); i++; +        output[i] = fc32_to_item32_sc16(input[i], float(scale_factor)); i++;      case 0x0:          convert_fc32_1_to_item32_1_nswap_guts(_)          break; @@ -60,11 +60,11 @@ DECLARE_CONVERTER(convert_fc32_1_to_item32_1_nswap, PRIORITY_CUSTOM){      //convert remainder      for (; i < nsamps; i++){ -        output[i] = fc32_to_item32(input[i], float(scale_factor)); +        output[i] = fc32_to_item32_sc16(input[i], float(scale_factor));      }  } -DECLARE_CONVERTER(convert_fc32_1_to_item32_1_bswap, PRIORITY_CUSTOM){ +DECLARE_CONVERTER(fc32, 1, sc16_item32_be, 1, PRIORITY_SIMD){      const fc32_t *input = reinterpret_cast<const fc32_t *>(inputs[0]);      item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); @@ -93,7 +93,7 @@ DECLARE_CONVERTER(convert_fc32_1_to_item32_1_bswap, PRIORITY_CUSTOM){      //dispatch according to alignment      switch (size_t(input) & 0xf){      case 0x8: -        output[i] = uhd::byteswap(fc32_to_item32(input[i], float(scale_factor))); i++; +        output[i] = uhd::byteswap(fc32_to_item32_sc16(input[i], float(scale_factor))); i++;      case 0x0:          convert_fc32_1_to_item32_1_bswap_guts(_)          break; @@ -102,11 +102,11 @@ DECLARE_CONVERTER(convert_fc32_1_to_item32_1_bswap, PRIORITY_CUSTOM){      //convert remainder      for (; i < nsamps; i++){ -        output[i] = uhd::byteswap(fc32_to_item32(input[i], float(scale_factor))); +        output[i] = uhd::byteswap(fc32_to_item32_sc16(input[i], float(scale_factor)));      }  } -DECLARE_CONVERTER(convert_item32_1_to_fc32_1_nswap, PRIORITY_CUSTOM){ +DECLARE_CONVERTER(sc16_item32_le, 1, fc32, 1, PRIORITY_SIMD){      const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]);      fc32_t *output = reinterpret_cast<fc32_t *>(outputs[0]); @@ -138,7 +138,7 @@ DECLARE_CONVERTER(convert_item32_1_to_fc32_1_nswap, PRIORITY_CUSTOM){      //dispatch according to alignment      switch (size_t(output) & 0xf){      case 0x8: -        output[i] = item32_to_fc32(input[i], float(scale_factor)); i++; +        output[i] = item32_sc16_to_fc32(input[i], float(scale_factor)); i++;      case 0x0:          convert_item32_1_to_fc32_1_nswap_guts(_)          break; @@ -147,11 +147,11 @@ DECLARE_CONVERTER(convert_item32_1_to_fc32_1_nswap, PRIORITY_CUSTOM){      //convert remainder      for (; i < nsamps; i++){ -        output[i] = item32_to_fc32(input[i], float(scale_factor)); +        output[i] = item32_sc16_to_fc32(input[i], float(scale_factor));      }  } -DECLARE_CONVERTER(convert_item32_1_to_fc32_1_bswap, PRIORITY_CUSTOM){ +DECLARE_CONVERTER(sc16_item32_be, 1, fc32, 1, PRIORITY_SIMD){      const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]);      fc32_t *output = reinterpret_cast<fc32_t *>(outputs[0]); @@ -182,7 +182,7 @@ DECLARE_CONVERTER(convert_item32_1_to_fc32_1_bswap, PRIORITY_CUSTOM){      //dispatch according to alignment      switch (size_t(output) & 0xf){      case 0x8: -        output[i] = item32_to_fc32(uhd::byteswap(input[i]), float(scale_factor)); i++; +        output[i] = item32_sc16_to_fc32(uhd::byteswap(input[i]), float(scale_factor)); i++;      case 0x0:          convert_item32_1_to_fc32_1_bswap_guts(_)          break; @@ -191,6 +191,6 @@ DECLARE_CONVERTER(convert_item32_1_to_fc32_1_bswap, PRIORITY_CUSTOM){      //convert remainder      for (; i < nsamps; i++){ -        output[i] = item32_to_fc32(uhd::byteswap(input[i]), float(scale_factor)); +        output[i] = item32_sc16_to_fc32(uhd::byteswap(input[i]), float(scale_factor));      }  } diff --git a/host/lib/convert/convert_fc64_with_sse2.cpp b/host/lib/convert/convert_fc64_with_sse2.cpp index 4d28396a4..837bb584e 100644 --- a/host/lib/convert/convert_fc64_with_sse2.cpp +++ b/host/lib/convert/convert_fc64_with_sse2.cpp @@ -21,7 +21,7 @@  using namespace uhd::convert; -DECLARE_CONVERTER(convert_fc64_1_to_item32_1_nswap, PRIORITY_CUSTOM){ +DECLARE_CONVERTER(fc64, 1, sc16_item32_le, 1, PRIORITY_SIMD){      const fc64_t *input = reinterpret_cast<const fc64_t *>(inputs[0]);      item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); @@ -64,11 +64,11 @@ DECLARE_CONVERTER(convert_fc64_1_to_item32_1_nswap, PRIORITY_CUSTOM){      //convert remainder      for (; i < nsamps; i++){ -        output[i] = fc64_to_item32(input[i], scale_factor); +        output[i] = fc64_to_item32_sc16(input[i], scale_factor);      }  } -DECLARE_CONVERTER(convert_fc64_1_to_item32_1_bswap, PRIORITY_CUSTOM){ +DECLARE_CONVERTER(fc64, 1, sc16_item32_be, 1, PRIORITY_SIMD){      const fc64_t *input = reinterpret_cast<const fc64_t *>(inputs[0]);      item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); @@ -110,11 +110,11 @@ DECLARE_CONVERTER(convert_fc64_1_to_item32_1_bswap, PRIORITY_CUSTOM){      //convert remainder      for (; i < nsamps; i++){ -        output[i] = uhd::byteswap(fc64_to_item32(input[i], scale_factor)); +        output[i] = uhd::byteswap(fc64_to_item32_sc16(input[i], scale_factor));      }  } -DECLARE_CONVERTER(convert_item32_1_to_fc64_1_nswap, PRIORITY_CUSTOM){ +DECLARE_CONVERTER(sc16_item32_le, 1, fc64, 1, PRIORITY_SIMD){      const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]);      fc64_t *output = reinterpret_cast<fc64_t *>(outputs[0]); @@ -159,11 +159,11 @@ DECLARE_CONVERTER(convert_item32_1_to_fc64_1_nswap, PRIORITY_CUSTOM){      //convert remainder      for (; i < nsamps; i++){ -        output[i] = item32_to_fc64(input[i], scale_factor); +        output[i] = item32_sc16_to_fc64(input[i], scale_factor);      }  } -DECLARE_CONVERTER(convert_item32_1_to_fc64_1_bswap, PRIORITY_CUSTOM){ +DECLARE_CONVERTER(sc16_item32_be, 1, fc64, 1, PRIORITY_SIMD){      const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]);      fc64_t *output = reinterpret_cast<fc64_t *>(outputs[0]); @@ -207,6 +207,6 @@ DECLARE_CONVERTER(convert_item32_1_to_fc64_1_bswap, PRIORITY_CUSTOM){      //convert remainder      for (; i < nsamps; i++){ -        output[i] = item32_to_fc64(uhd::byteswap(input[i]), scale_factor); +        output[i] = item32_sc16_to_fc64(uhd::byteswap(input[i]), scale_factor);      }  } diff --git a/host/lib/convert/convert_impl.cpp b/host/lib/convert/convert_impl.cpp index 9b2cdcdc9..df03b44f9 100644 --- a/host/lib/convert/convert_impl.cpp +++ b/host/lib/convert/convert_impl.cpp @@ -18,11 +18,37 @@  #include <uhd/convert.hpp>  #include <uhd/utils/log.hpp>  #include <uhd/utils/static.hpp> +#include <uhd/types/dict.hpp>  #include <uhd/exception.hpp> +#include <boost/cstdint.hpp> +#include <boost/format.hpp> +#include <complex>  using namespace uhd; -#include "convert_pred.hpp" +bool convert::operator==(const convert::id_type &lhs, const convert::id_type &rhs){ +    return true +        and (lhs.input_format  == rhs.input_format) +        and (lhs.num_inputs    == rhs.num_inputs) +        and (lhs.output_format == rhs.output_format) +        and (lhs.num_outputs   == rhs.num_outputs) +    ; +} + +std::string convert::id_type::to_pp_string(void) const{ +    return str(boost::format( +        "conversion ID\n" +        "  Input format: %s\n" +        "  Num inputs: %d\n" +        "  Output format: %s\n" +        "  Num outputs: %d\n" +    ) +        % this->input_format +        % this->num_inputs +        % this->output_format +        % this->num_outputs +    ); +}  /***********************************************************************   * Define types for the function tables @@ -30,56 +56,34 @@ using namespace uhd;  struct fcn_table_entry_type{      convert::priority_type prio;      convert::function_type fcn; -    fcn_table_entry_type(void) -    : prio(convert::PRIORITY_EMPTY), fcn(NULL){ -        /* NOP */ -    }  }; -typedef std::vector<fcn_table_entry_type> fcn_table_type;  /***********************************************************************   * Setup the table registry   **********************************************************************/ -UHD_SINGLETON_FCN(fcn_table_type, get_cpu_to_otw_table); -UHD_SINGLETON_FCN(fcn_table_type, get_otw_to_cpu_table); - -fcn_table_type &get_table(dir_type dir){ -    switch(dir){ -    case DIR_OTW_TO_CPU: return get_otw_to_cpu_table(); -    case DIR_CPU_TO_OTW: return get_cpu_to_otw_table(); -    } -    UHD_THROW_INVALID_CODE_PATH(); -} +typedef uhd::dict<convert::id_type, fcn_table_entry_type> fcn_table_type; +UHD_SINGLETON_FCN(fcn_table_type, get_table);  /***********************************************************************   * The registry functions   **********************************************************************/  void uhd::convert::register_converter( -    const std::string &markup, +    const id_type &id,      function_type fcn,      priority_type prio  ){ -    //extract the predicate and direction from the markup -    dir_type dir; -    pred_type pred = make_pred(markup, dir); -      //get a reference to the function table -    fcn_table_type &table = get_table(dir); - -    //resize the table so that its at least pred+1 -    if (table.size() <= pred) table.resize(pred+1); +    fcn_table_type &table = get_table();      //register the function if higher priority -    if (table[pred].prio < prio){ -        table[pred].fcn = fcn; -        table[pred].prio = prio; +    if (not table.has_key(id) or table[id].prio < prio){ +        table[id].fcn = fcn; +        table[id].prio = prio;      }      //----------------------------------------------------------------// -    UHD_LOGV(always) << "register_converter: " << markup << std::endl +    UHD_LOGV(always) << "register_converter: " << id.to_pp_string() << std::endl          << "    prio: " << prio << std::endl -        << "    pred: " << pred << std::endl -        << "    dir: " << dir << std::endl          << std::endl      ;      //----------------------------------------------------------------// @@ -88,22 +92,50 @@ void uhd::convert::register_converter(  /***********************************************************************   * The converter functions   **********************************************************************/ -const convert::function_type &convert::get_converter_cpu_to_otw( -    const io_type_t &io_type, -    const otw_type_t &otw_type, -    size_t num_input_buffs, -    size_t num_output_buffs -){ -    pred_type pred = make_pred(io_type, otw_type, num_input_buffs, num_output_buffs); -    return get_cpu_to_otw_table().at(pred).fcn; +convert::function_type convert::get_converter(const id_type &id){ +    if (get_table().has_key(id)) return get_table()[id].fcn; +    throw uhd::key_error("Cannot find a conversion routine for " + id.to_pp_string());  } -const convert::function_type &convert::get_converter_otw_to_cpu( -    const io_type_t &io_type, -    const otw_type_t &otw_type, -    size_t num_input_buffs, -    size_t num_output_buffs +/*********************************************************************** + * Mappings for item format to byte size for all items we can + **********************************************************************/ +typedef uhd::dict<std::string, size_t> item_size_type; +UHD_SINGLETON_FCN(item_size_type, get_item_size_table); + +void register_bytes_per_item( +    const std::string &format, const size_t size  ){ -    pred_type pred = make_pred(io_type, otw_type, num_input_buffs, num_output_buffs); -    return get_otw_to_cpu_table().at(pred).fcn; +    get_item_size_table()[format] = size; +} + +size_t convert::get_bytes_per_item(const std::string &format){ +    if (get_item_size_table().has_key(format)) return get_item_size_table()[format]; + +    //OK. I am sorry about this. +    //We didnt find a match, so lets find a match for the first term. +    //This is partially a hack because of the way I append strings. +    //But as long as life is kind, we can keep this. +    const size_t pos = format.find("_"); +    if (pos != std::string::npos){ +        return get_bytes_per_item(format.substr(0, pos)); +    } + +    throw uhd::key_error("Cannot find an item size:\n" + format); +} + +UHD_STATIC_BLOCK(convert_register_item_sizes){ +    //register standard complex types +    get_item_size_table()["fc64"] = sizeof(std::complex<double>); +    get_item_size_table()["fc32"] = sizeof(std::complex<float>); +    get_item_size_table()["sc32"] = sizeof(std::complex<boost::int32_t>); +    get_item_size_table()["sc16"] = sizeof(std::complex<boost::int16_t>); +    get_item_size_table()["sc8"] = sizeof(std::complex<boost::int8_t>); + +    //register standard real types +    get_item_size_table()["f64"] = sizeof(double); +    get_item_size_table()["f32"] = sizeof(float); +    get_item_size_table()["s32"] = sizeof(boost::int32_t); +    get_item_size_table()["s16"] = sizeof(boost::int16_t); +    get_item_size_table()["s8"] = sizeof(boost::int8_t);  } diff --git a/host/lib/convert/convert_with_neon.cpp b/host/lib/convert/convert_with_neon.cpp index e5f08cad9..c7ad62104 100644 --- a/host/lib/convert/convert_with_neon.cpp +++ b/host/lib/convert/convert_with_neon.cpp @@ -20,7 +20,7 @@  using namespace uhd::convert; -DECLARE_CONVERTER(convert_fc32_1_to_item32_1_nswap, PRIORITY_CUSTOM){ +DECLARE_CONVERTER(fc32, 1, sc16_item32_le, 1, PRIORITY_SIMD){      const fc32_t *input = reinterpret_cast<const fc32_t *>(inputs[0]);      item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); @@ -37,10 +37,10 @@ DECLARE_CONVERTER(convert_fc32_1_to_item32_1_nswap, PRIORITY_CUSTOM){      }      for (; i < nsamps; i++) -        output[i] = fc32_to_item32(input[i], float(scale_factor)); +        output[i] = fc32_to_item32_sc16(input[i], scale_factor);  } -DECLARE_CONVERTER(convert_item32_1_to_fc32_1_nswap, PRIORITY_CUSTOM){ +DECLARE_CONVERTER(sc16_item32_le, 1, fc32, 1, PRIORITY_SIMD){      const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]);      fc32_t *output = reinterpret_cast<fc32_t *>(outputs[0]); @@ -57,5 +57,5 @@ DECLARE_CONVERTER(convert_item32_1_to_fc32_1_nswap, PRIORITY_CUSTOM){      }      for (; i < nsamps; i++) -        output[i] = item32_to_fc32(input[i], float(scale_factor)); +        output[i] = item32_sc16_to_fc32(input[i], scale_factor);  } diff --git a/host/lib/convert/convert_with_orc.cpp b/host/lib/convert/convert_with_orc.cpp index 844c2595c..0c46bcf1e 100644 --- a/host/lib/convert/convert_with_orc.cpp +++ b/host/lib/convert/convert_with_orc.cpp @@ -29,26 +29,26 @@ extern void _convert_sc16_1_to_item32_1_nswap_orc(void *, const void *, float, i  extern void _convert_item32_1_to_sc16_1_nswap_orc(void *, const void *, float, int);  } -DECLARE_CONVERTER(convert_fc32_1_to_item32_1_nswap, PRIORITY_LIBORC){ +DECLARE_CONVERTER(fc32, 1, sc16_item32_le, 1, PRIORITY_LIBORC){      _convert_fc32_1_to_item32_1_nswap_orc(outputs[0], inputs[0], scale_factor, nsamps);  } -DECLARE_CONVERTER(convert_fc32_1_to_item32_1_bswap, PRIORITY_LIBORC){ +DECLARE_CONVERTER(fc32, 1, sc16_item32_be, 1, PRIORITY_LIBORC){      _convert_fc32_1_to_item32_1_bswap_orc(outputs[0], inputs[0], scale_factor, nsamps);  } -DECLARE_CONVERTER(convert_item32_1_to_fc32_1_nswap, PRIORITY_LIBORC){ +DECLARE_CONVERTER(sc16_item32_le, 1, fc32, 1, PRIORITY_LIBORC){      _convert_item32_1_to_fc32_1_nswap_orc(outputs[0], inputs[0], scale_factor, nsamps);  } -DECLARE_CONVERTER(convert_item32_1_to_fc32_1_bswap, PRIORITY_LIBORC){ +DECLARE_CONVERTER(sc16_item32_be, 1, fc32, 1, PRIORITY_LIBORC){      _convert_item32_1_to_fc32_1_bswap_orc(outputs[0], inputs[0], scale_factor, nsamps);  } -DECLARE_CONVERTER(convert_sc16_1_to_item32_1_nswap, PRIORITY_LIBORC){ +DECLARE_CONVERTER(sc16, 1, sc16_item32_le, 1, PRIORITY_LIBORC){      _convert_sc16_1_to_item32_1_nswap_orc(outputs[0], inputs[0], scale_factor, nsamps);  } -DECLARE_CONVERTER(convert_item32_1_to_sc16_1_nswap, PRIORITY_LIBORC){ +DECLARE_CONVERTER(sc16_item32_le, 1, sc16, 1, PRIORITY_LIBORC){      _convert_item32_1_to_sc16_1_nswap_orc(outputs[0], inputs[0], scale_factor, nsamps);  } diff --git a/host/lib/convert/gen_convert_general.py b/host/lib/convert/gen_convert_general.py index 8c3138bda..52b4212b4 100644 --- a/host/lib/convert/gen_convert_general.py +++ b/host/lib/convert/gen_convert_general.py @@ -28,48 +28,127 @@ TMPL_HEADER = """  using namespace uhd::convert;  """ -TMPL_CONV_TO_FROM_ITEM32_1 = """ -DECLARE_CONVERTER(convert_$(cpu_type)_1_to_item32_1_$(swap), PRIORITY_GENERAL){ +TMPL_CONV_GEN2_ITEM32 = """ +DECLARE_CONVERTER(item32, 1, sc16_item32_$(end), 1, PRIORITY_GENERAL){ +    const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]); +    item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); + +    if (scale_factor == 0){} //avoids unused warning + +    for (size_t i = 0; i < nsamps; i++){ +        output[i] = $(to_wire)(input[i]); +    } +} + +DECLARE_CONVERTER(sc16_item32_$(end), 1, item32, 1, PRIORITY_GENERAL){ +    const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]); +    item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); + +    if (scale_factor == 0){} //avoids unused warning + +    for (size_t i = 0; i < nsamps; i++){ +        output[i] = $(to_host)(input[i]); +    } +} +""" + +TMPL_CONV_GEN2_COMPLEX = """ +DECLARE_CONVERTER($(cpu_type), 1, sc16_item32_$(end), 1, PRIORITY_GENERAL){      const $(cpu_type)_t *input = reinterpret_cast<const $(cpu_type)_t *>(inputs[0]);      item32_t *output = reinterpret_cast<item32_t *>(outputs[0]);      for (size_t i = 0; i < nsamps; i++){ -        output[i] = $(swap_fcn)($(cpu_type)_to_item32(input[i], float(scale_factor))); +        output[i] = $(to_wire)($(cpu_type)_to_item32_sc16(input[i], scale_factor));      }  } -DECLARE_CONVERTER(convert_item32_1_to_$(cpu_type)_1_$(swap), PRIORITY_GENERAL){ +DECLARE_CONVERTER(sc16_item32_$(end), 1, $(cpu_type), 1, PRIORITY_GENERAL){      const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]);      $(cpu_type)_t *output = reinterpret_cast<$(cpu_type)_t *>(outputs[0]);      for (size_t i = 0; i < nsamps; i++){ -        output[i] = item32_to_$(cpu_type)($(swap_fcn)(input[i]), float(scale_factor)); +        output[i] = item32_sc16_to_$(cpu_type)($(to_host)(input[i]), scale_factor); +    } +} + +DECLARE_CONVERTER(sc8_item32_$(end), 1, $(cpu_type), 1, PRIORITY_GENERAL){ +    const item32_t *input = reinterpret_cast<const item32_t *>(size_t(inputs[0]) & ~0x3); +    $(cpu_type)_t *output = reinterpret_cast<$(cpu_type)_t *>(outputs[0]); +    $(cpu_type)_t dummy; +    size_t num_samps = nsamps; + +    if ((size_t(inputs[0]) & 0x3) != 0){ +        const item32_t item0 = $(to_host)(*input++); +        item32_sc8_to_$(cpu_type)(item0, dummy, *output++, scale_factor); +        num_samps--; +    } + +    const size_t num_pairs = num_samps/2; +    for (size_t i = 0, j = 0; i < num_pairs; i++, j+=2){ +        const item32_t item_i = $(to_host)(input[i]); +        item32_sc8_to_$(cpu_type)(item_i, output[j], output[j+1], scale_factor); +    } + +    if (num_samps != num_pairs*2){ +        const item32_t item_n = $(to_host)(input[num_pairs]); +        item32_sc8_to_$(cpu_type)(item_n, output[num_samps-1], dummy, scale_factor);      }  }  """ -TMPL_CONV_TO_FROM_ITEM32_X = """ -DECLARE_CONVERTER(convert_$(cpu_type)_$(width)_to_item32_1_$(swap), PRIORITY_GENERAL){ + +TMPL_CONV_USRP1_COMPLEX = """ +DECLARE_CONVERTER($(cpu_type), $(width), sc16_item16_usrp1, 1, PRIORITY_GENERAL){      #for $w in range($width)      const $(cpu_type)_t *input$(w) = reinterpret_cast<const $(cpu_type)_t *>(inputs[$(w)]);      #end for -    item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); +    boost::uint16_t *output = reinterpret_cast<boost::uint16_t *>(outputs[0]); + +    if (scale_factor == 0){} //avoids unused warning      for (size_t i = 0, j = 0; i < nsamps; i++){          #for $w in range($width) -        output[j++] = $(swap_fcn)($(cpu_type)_to_item32(input$(w)[i], float(scale_factor))); +        output[j++] = $(to_wire)(boost::int16_t(input$(w)[i].real()$(do_scale))); +        output[j++] = $(to_wire)(boost::int16_t(input$(w)[i].imag()$(do_scale)));          #end for      }  } -DECLARE_CONVERTER(convert_item32_1_to_$(cpu_type)_$(width)_$(swap), PRIORITY_GENERAL){ -    const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]); +DECLARE_CONVERTER(sc16_item16_usrp1, 1, $(cpu_type), $(width), PRIORITY_GENERAL){ +    const boost::uint16_t *input = reinterpret_cast<const boost::uint16_t *>(inputs[0]); +    #for $w in range($width) +    $(cpu_type)_t *output$(w) = reinterpret_cast<$(cpu_type)_t *>(outputs[$(w)]); +    #end for + +    if (scale_factor == 0){} //avoids unused warning + +    for (size_t i = 0, j = 0; i < nsamps; i++){ +        #for $w in range($width) +        output$(w)[i] = $(cpu_type)_t( +            boost::int16_t($(to_host)(input[j+0]))$(do_scale), +            boost::int16_t($(to_host)(input[j+1]))$(do_scale) +        ); +        j += 2; +        #end for +    } +} + +DECLARE_CONVERTER(sc8_item16_usrp1, 1, $(cpu_type), $(width), PRIORITY_GENERAL){ +    const boost::uint16_t *input = reinterpret_cast<const boost::uint16_t *>(inputs[0]);      #for $w in range($width)      $(cpu_type)_t *output$(w) = reinterpret_cast<$(cpu_type)_t *>(outputs[$(w)]);      #end for +    if (scale_factor == 0){} //avoids unused warning +      for (size_t i = 0, j = 0; i < nsamps; i++){          #for $w in range($width) -        output$(w)[i] = item32_to_$(cpu_type)($(swap_fcn)(input[j++]), float(scale_factor)); +        { +        const boost::uint16_t num = $(to_host)(input[j++]); +        output$(w)[i] = $(cpu_type)_t( +            boost::int8_t(num)$(do_scale), +            boost::int8_t(num >> 8)$(do_scale) +        ); +        }          #end for      }  } @@ -83,11 +162,32 @@ if __name__ == '__main__':      import sys, os      file = os.path.basename(__file__)      output = parse_tmpl(TMPL_HEADER, file=file) -    for width in 1, 2, 3, 4: -        for swap, swap_fcn in (('nswap', ''), ('bswap', 'uhd::byteswap')): -            for cpu_type in 'fc64', 'fc32', 'sc16': -                output += parse_tmpl( -                    TMPL_CONV_TO_FROM_ITEM32_1 if width == 1 else TMPL_CONV_TO_FROM_ITEM32_X, -                    width=width, swap=swap, swap_fcn=swap_fcn, cpu_type=cpu_type -                ) + +    #generate complex converters for all gen2 platforms +    for end, to_host, to_wire in ( +        ('be', 'uhd::ntohx', 'uhd::htonx'), +        ('le', 'uhd::wtohx', 'uhd::htowx'), +    ): +        for cpu_type in 'fc64', 'fc32', 'sc16': +            output += parse_tmpl( +                TMPL_CONV_GEN2_COMPLEX, +                end=end, to_host=to_host, to_wire=to_wire, cpu_type=cpu_type +            ) +        output += parse_tmpl( +                TMPL_CONV_GEN2_ITEM32, +                end=end, to_host=to_host, to_wire=to_wire +            ) + +    #generate complex converters for usrp1 format +    for width in 1, 2, 4: +        for cpu_type, do_scale in ( +            ('fc64', '*scale_factor'), +            ('fc32', '*float(scale_factor)'), +            ('sc16', ''), +        ): +            output += parse_tmpl( +                TMPL_CONV_USRP1_COMPLEX, +                width=width, to_host='uhd::wtohx', to_wire='uhd::htowx', +                cpu_type=cpu_type, do_scale=do_scale +            )      open(sys.argv[1], 'w').write(output) diff --git a/host/lib/convert/gen_convert_pred.py b/host/lib/convert/gen_convert_pred.py deleted file mode 100644 index 360fbcf44..000000000 --- a/host/lib/convert/gen_convert_pred.py +++ /dev/null @@ -1,185 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2011-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/>. -# - -TMPL_TEXT = """ -#import time -/*********************************************************************** - * This file was generated by $file on $time.strftime("%c") - **********************************************************************/ -\#include <uhd/exception.hpp> -\#include <boost/tokenizer.hpp> -\#include <boost/lexical_cast.hpp> -\#include <boost/detail/endian.hpp> -\#include <boost/cstdint.hpp> -\#include <string> -\#include <vector> - -typedef size_t pred_type; -typedef std::vector<pred_type> pred_vector_type; - -enum dir_type{ -    DIR_OTW_TO_CPU = 0, -    DIR_CPU_TO_OTW = 1 -}; - -struct pred_error : uhd::value_error{ -    pred_error(const std::string &what): -        uhd::value_error("convert::make_pred: " + what) -    { -        /* NOP */ -    } -}; - -pred_type make_pred(const std::string &markup, dir_type &dir){ -    pred_type pred = 0; - -    try{ -        boost::tokenizer<boost::char_separator<char> > tokenizer(markup, boost::char_separator<char>("_")); -        std::vector<std::string> tokens(tokenizer.begin(), tokenizer.end()); -        //token 0 is <convert> -        std::string inp_type = tokens.at(1); -        std::string num_inps = tokens.at(2); -        //token 3 is <to> -        std::string out_type = tokens.at(4); -        std::string num_outs = tokens.at(5); -        std::string swap_type = tokens.at(6); - -        std::string cpu_type, otw_type; -        if (inp_type.find("item") == std::string::npos){ -            cpu_type = inp_type; -            otw_type = out_type; -            dir = DIR_CPU_TO_OTW; -        } -        else{ -            cpu_type = out_type; -            otw_type = inp_type; -            dir = DIR_OTW_TO_CPU; -        } - -        if      (cpu_type == "fc64") pred |= $ph.fc64_p; -        else if (cpu_type == "fc32") pred |= $ph.fc32_p; -        else if (cpu_type == "sc16") pred |= $ph.sc16_p; -        else if (cpu_type == "sc8")  pred |= $ph.sc8_p; -        else throw pred_error("unhandled io type " + cpu_type); - -        if (otw_type == "item32") pred |= $ph.item32_p; -        else throw pred_error("unhandled otw type " + otw_type); - -        int num_inputs = boost::lexical_cast<int>(num_inps); -        int num_outputs = boost::lexical_cast<int>(num_outs); - -        switch(num_inputs*num_outputs){ //FIXME treated as one value -        case 1: pred |= $ph.chan1_p; break; -        case 2: pred |= $ph.chan2_p; break; -        case 3: pred |= $ph.chan3_p; break; -        case 4: pred |= $ph.chan4_p; break; -        default: throw pred_error("unhandled number of channels"); -        } - -        if      (swap_type == "bswap") pred |= $ph.bswap_p; -        else if (swap_type == "nswap") pred |= $ph.nswap_p; -        else throw pred_error("unhandled swap type"); - -    } -    catch(...){ -        throw pred_error("could not parse markup: " + markup); -    } - -    return pred; -} - -#define pred_table_wildcard pred_type(~0) -#define pred_table_max_size size_t(128) -#define pred_table_index(e) (pred_type(e) & 0x7f) - -static pred_vector_type get_pred_byte_order_table(void){ -    pred_vector_type table(pred_table_max_size, pred_table_wildcard); -    \#ifdef BOOST_BIG_ENDIAN -    table[pred_table_index(otw_type_t::BO_BIG_ENDIAN)]    = $ph.nswap_p; -    table[pred_table_index(otw_type_t::BO_LITTLE_ENDIAN)] = $ph.bswap_p; -    \#else -    table[pred_table_index(otw_type_t::BO_BIG_ENDIAN)]    = $ph.bswap_p; -    table[pred_table_index(otw_type_t::BO_LITTLE_ENDIAN)] = $ph.nswap_p; -    \#endif -    table[pred_table_index(otw_type_t::BO_NATIVE)]        = $ph.nswap_p; -    return table; -} - -static pred_vector_type get_pred_io_type_table(void){ -    pred_vector_type table(pred_table_max_size, pred_table_wildcard); -    table[pred_table_index(io_type_t::COMPLEX_FLOAT64)]    = $ph.fc64_p; -    table[pred_table_index(io_type_t::COMPLEX_FLOAT32)]    = $ph.fc32_p; -    table[pred_table_index(io_type_t::COMPLEX_INT16)]      = $ph.sc16_p; -    return table; -} - -static pred_vector_type get_pred_num_io_table(void){ -    pred_vector_type table(pred_table_max_size, pred_table_wildcard); -    table[1] = $ph.chan1_p; -    table[2] = $ph.chan2_p; -    table[3] = $ph.chan3_p; -    table[4] = $ph.chan4_p; -    return table; -} - -UHD_INLINE pred_type make_pred( -    const io_type_t &io_type, -    const otw_type_t &otw_type, -    size_t num_inputs, -    size_t num_outputs -){ -    pred_type pred = $ph.item32_p; //only item32 supported as of now - -    static const pred_vector_type pred_byte_order_table(get_pred_byte_order_table()); -    pred |= pred_byte_order_table[pred_table_index(otw_type.byteorder)]; - -    static const pred_vector_type pred_io_type_table(get_pred_io_type_table()); -    pred |= pred_io_type_table[pred_table_index(io_type.tid)]; - -    static const pred_vector_type pred_num_io_table(get_pred_num_io_table()); -    pred |= pred_num_io_table[pred_table_index(num_inputs*num_outputs)]; - -    if (pred == pred_table_wildcard) throw pred_error( -        "unhanded input combination for make_pred()" -    ); - -    return pred; -} -""" - -def parse_tmpl(_tmpl_text, **kwargs): -    from Cheetah.Template import Template -    return str(Template(_tmpl_text, kwargs)) - -class ph: -    bswap_p  = 0b00001 -    nswap_p  = 0b00000 -    item32_p = 0b00000 -    sc8_p    = 0b00000 -    sc16_p   = 0b00010 -    fc32_p   = 0b00100 -    fc64_p   = 0b00110 -    chan1_p  = 0b00000 -    chan2_p  = 0b01000 -    chan3_p  = 0b10000 -    chan4_p  = 0b11000 - -if __name__ == '__main__': -    import sys, os -    file = os.path.basename(__file__) -    open(sys.argv[1], 'w').write(parse_tmpl(TMPL_TEXT, file=file, ph=ph)) diff --git a/host/lib/deprecated.cpp b/host/lib/deprecated.cpp new file mode 100644 index 000000000..0fc751eeb --- /dev/null +++ b/host/lib/deprecated.cpp @@ -0,0 +1,81 @@ +//---------------------------------------------------------------------- +//-- deprecated interfaces below, to be removed when the API is changed +//---------------------------------------------------------------------- + +#include <uhd/types/otw_type.hpp> +#include <uhd/types/io_type.hpp> +#include <boost/cstdint.hpp> +#include <stdexcept> +#include <complex> +#include <vector> + +using namespace uhd; + +/*********************************************************************** + * otw type + **********************************************************************/ +size_t otw_type_t::get_sample_size(void) const{ +    return (this->width * 2) / 8; +} + +otw_type_t::otw_type_t(void): +    width(0), +    shift(0), +    byteorder(BO_NATIVE) +{ +    /* NOP */ +} + +/*********************************************************************** + * io type + **********************************************************************/ +static std::vector<size_t> get_tid_size_table(void){ +    std::vector<size_t> table(128, 0); +    table[size_t(io_type_t::COMPLEX_FLOAT64)] = sizeof(std::complex<double>); +    table[size_t(io_type_t::COMPLEX_FLOAT32)] = sizeof(std::complex<float>); +    table[size_t(io_type_t::COMPLEX_INT16)]   = sizeof(std::complex<boost::int16_t>); +    table[size_t(io_type_t::COMPLEX_INT8)]    = sizeof(std::complex<boost::int8_t>); +    return table; +} + +static const std::vector<size_t> tid_size_table(get_tid_size_table()); + +io_type_t::io_type_t(tid_t tid): +    size(tid_size_table[size_t(tid) & 0x7f]), tid(tid) +{ +    /* NOP */ +} + +io_type_t::io_type_t(size_t size): +    size(size), tid(CUSTOM_TYPE) +{ +    /* NOP */ +} + +#include <uhd/types/clock_config.hpp> + +using namespace uhd; + +clock_config_t clock_config_t::external(void){ +    clock_config_t clock_config; +    clock_config.ref_source = clock_config_t::REF_SMA; +    clock_config.pps_source = clock_config_t::PPS_SMA; +    clock_config.pps_polarity = clock_config_t::PPS_POS; +    return clock_config; +} + +clock_config_t clock_config_t::internal(void){ +    clock_config_t clock_config; +    clock_config.ref_source = clock_config_t::REF_INT; +    clock_config.pps_source = clock_config_t::PPS_SMA; +    clock_config.pps_polarity = clock_config_t::PPS_POS; +    return clock_config; +} + +clock_config_t::clock_config_t(void): +    ref_source(REF_INT), +    pps_source(PPS_SMA), +    pps_polarity(PPS_POS) +{ +    /* NOP */ +} diff --git a/host/lib/ic_reg_maps/CMakeLists.txt b/host/lib/ic_reg_maps/CMakeLists.txt index 46cf5a06a..dc2ab7847 100644 --- a/host/lib/ic_reg_maps/CMakeLists.txt +++ b/host/lib/ic_reg_maps/CMakeLists.txt @@ -28,6 +28,11 @@ LIBUHD_PYTHON_GEN_SOURCE(  )  LIBUHD_PYTHON_GEN_SOURCE( +    ${CMAKE_CURRENT_SOURCE_DIR}/gen_adf4351_regs.py +    ${CMAKE_CURRENT_BINARY_DIR}/adf4351_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_adf4350_regs.py b/host/lib/ic_reg_maps/gen_adf4350_regs.py index bccdb2edf..fce2f569b 100755 --- a/host/lib/ic_reg_maps/gen_adf4350_regs.py +++ b/host/lib/ic_reg_maps/gen_adf4350_regs.py @@ -51,6 +51,7 @@ reference_divide_by_2   2[24]       1       disabled, enabled  reference_doubler       2[25]       0       disabled, enabled  muxout                  2[26:28]    1       3state, dvdd, dgnd, rdiv, ndiv, analog_ld, dld, reserved  low_noise_and_spur      2[29:30]    3       low_noise, reserved0, reserved1, low_spur +##reserved              2[31]       0  ########################################################################  ## address 3  ######################################################################## diff --git a/host/lib/ic_reg_maps/gen_adf4351_regs.py b/host/lib/ic_reg_maps/gen_adf4351_regs.py new file mode 100755 index 000000000..607b2979d --- /dev/null +++ b/host/lib/ic_reg_maps/gen_adf4351_regs.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python +# +# Copyright 2010 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. +# + +######################################################################## +# Template for raw text data describing registers +# name addr[bit range inclusive] default optional enums +######################################################################## +REGS_TMPL="""\ +######################################################################## +## address 0 +######################################################################## +frac_12_bit             0[3:14]     0 +int_16_bit              0[15:30]    0x23 +##reserved              0[31]       0 +######################################################################## +## address 1 +######################################################################## +mod_12_bit              1[3:14]     0xfff +phase_12_bit            1[15:26]    0 +prescaler               1[27]       0       4_5, 8_9 +phase_adjust            1[28]       0 +##reserved              1[29:31]    0 +######################################################################## +## address 2 +######################################################################## +counter_reset           2[3]        0       disabled, enabled +cp_three_state          2[4]        0       disabled, enabled +power_down              2[5]        0       disabled, enabled +pd_polarity             2[6]        1       negative, positive +ldp                     2[7]        0       10ns, 6ns +ldf                     2[8]        0       frac_n, int_n +#set $current_setting_enums = ', '.join(map(lambda x: '_'.join(("%0.2fma"%(round(x*31.27 + 31.27)/100)).split('.')), range(0,16))) +charge_pump_current     2[9:12]     5       $current_setting_enums +double_buffer           2[13]       0       disabled, enabled +r_counter_10_bit        2[14:23]    0 +reference_divide_by_2   2[24]       1       disabled, enabled +reference_doubler       2[25]       0       disabled, enabled +muxout                  2[26:28]    1       3state, dvdd, dgnd, rdiv, ndiv, analog_ld, dld, reserved +low_noise_and_spur      2[29:30]    3       low_noise, reserved0, reserved1, low_spur +##reserved              2[31]       0 +######################################################################## +## address 3 +######################################################################## +clock_divider_12_bit    3[3:14]     0 +clock_div_mode          3[15:16]    0       clock_divider_off, fast_lock, resync_enable, reserved +##reserved              3[17]       0 +cycle_slip_reduction    3[18]       0       disabled, enabled +##reserved              3[19:20]    0 +charge_cancel           3[21]       0 +anti_backlash_pulse     3[22]       0       6ns, 3ns +band_select_mode        3[23]       0       low, high +##reserved              3[24:31]    0 +######################################################################## +## address 4 +######################################################################## +output_power            4[3:4]      3       m4dbm, m1dbm, 2dbm, 5dbm +rf_output_enable        4[5]        1       disabled, enabled +aux_output_power        4[6:7]      0       m4dbm, m1dbm, 2dbm, 5dbm +aux_output_enable       4[8]        0       disabled, enabled +aux_output_select       4[9]        1       divided, fundamental +mute_till_lock_detect   4[10]       0       mute_disabled, mute_enabled +vco_power_down          4[11]       0       vco_powered_up, vco_powered_down +band_select_clock_div   4[12:19]    0 +rf_divider_select       4[20:22]    5       div1, div2, div4, div8, div16, div32, div64 +feedback_select         4[23]       1       divided, fundamental +##reserved              4[24:31]    0 +######################################################################## +## address 5 +######################################################################## +##reserved              5[3:18]     0 +##reserved              5[19:20]    0 +##reserved              5[21]       0 +ld_pin_mode             5[22:23]    1       low0, dld, low, high +##reserved              5[24:31]    0 +""" + +######################################################################## +# 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 = 0; +    switch(addr){ +    #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs))) +    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; +} + +void set_reg(boost::uint8_t addr, boost::uint32_t reg){ +    switch(addr){ +    #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs))) +    case $addr: +        #for $reg in filter(lambda r: r.get_addr() == addr, $regs) +        $reg.get_name() = $(reg.get_type())((reg >> $reg.get_shift()) & $reg.get_mask()); +        #end for +        break; +    #end for +    } +} +""" + +if __name__ == '__main__': +    import common; common.generate( +        name='adf4351_regs', +        regs_tmpl=REGS_TMPL, +        body_tmpl=BODY_TMPL, +        file=__file__, +    ) diff --git a/host/lib/transport/gen_vrt_if_packet.py b/host/lib/transport/gen_vrt_if_packet.py index 5f048d8c7..245a7ddbd 100755 --- a/host/lib/transport/gen_vrt_if_packet.py +++ b/host/lib/transport/gen_vrt_if_packet.py @@ -70,6 +70,10 @@ static pred_table_type get_pred_unpack_table(void){  static const pred_table_type pred_unpack_table(get_pred_unpack_table()); +//maps trailer bits to num empty bytes +//maps num empty bytes to trailer bits +static const size_t occ_table[] = {0, 2, 1, 3}; +  ########################################################################  #def gen_code($XE_MACRO, $suffix)  ######################################################################## @@ -122,14 +126,6 @@ void vrt::if_hdr_pack_$(suffix)(              #set $num_header_words += 1              #set $flags |= (0x1 << 20);          #end if -        ########## Trailer ########## -        #if $pred & $tlr_p -            //packet_buff[$num_header_words+if_packet_info.num_payload_words32] = $(XE_MACRO)(if_packet_info.tlr); -            #set $flags |= (0x1 << 26); -            #set $num_trailer_words = 1; -        #else -            #set $num_trailer_words = 0; -        #end if          ########## Burst Flags ##########          #if $pred & $eob_p              #set $flags |= (0x1 << 24); @@ -137,6 +133,18 @@ void vrt::if_hdr_pack_$(suffix)(          #if $pred & $sob_p              #set $flags |= (0x1 << 25);          #end if +        ########## Trailer ########## +        #if $pred & $tlr_p +            { +                const size_t empty_bytes = if_packet_info.num_payload_words32*sizeof(boost::uint32_t) - if_packet_info.num_payload_bytes; +                if_packet_info.tlr |= (0x3 << 22) | (occ_table[empty_bytes & 0x3] << 10); +            } +            packet_buff[$num_header_words+if_packet_info.num_payload_words32] = $(XE_MACRO)(if_packet_info.tlr); +            #set $flags |= (0x1 << 26); +            #set $num_trailer_words = 1; +        #else +            #set $num_trailer_words = 0; +        #end if          ########## Variables ##########              if_packet_info.num_header_words32 = $num_header_words;              if_packet_info.num_packet_words32 = $($num_header_words + $num_trailer_words) + if_packet_info.num_payload_words32; @@ -172,6 +180,8 @@ void vrt::if_hdr_unpack_$(suffix)(      const pred_type pred = pred_unpack_table[pred_table_index(vrt_hdr_word)]; +    size_t empty_bytes = 0; +      switch(pred){      #for $pred in range(2**7)      case $pred: @@ -211,15 +221,6 @@ void vrt::if_hdr_unpack_$(suffix)(          #else              if_packet_info.has_tsf = false;          #end if -        ########## Trailer ########## -        #if $pred & $tlr_p -            if_packet_info.has_tlr = true; -            if_packet_info.tlr = $(XE_MACRO)(packet_buff[packet_words32-1]); -            #set $num_trailer_words = 1; -        #else -            if_packet_info.has_tlr = false; -            #set $num_trailer_words = 0; -        #end if          ########## Burst Flags ##########          #if $pred & $eob_p              if_packet_info.eob = true; @@ -231,12 +232,28 @@ void vrt::if_hdr_unpack_$(suffix)(          #else              if_packet_info.sob = false;          #end if +        ########## Trailer ########## +        #if $pred & $tlr_p +            if_packet_info.has_tlr = true; +            if_packet_info.tlr = $(XE_MACRO)(packet_buff[packet_words32-1]); +            #set $num_trailer_words = 1; +            { +                const int indicators = (if_packet_info.tlr >> 20) & (if_packet_info.tlr >> 8); +                if ((indicators & (1 << 0)) != 0) if_packet_info.eob = true; +                if ((indicators & (1 << 1)) != 0) if_packet_info.sob = true; +                empty_bytes = occ_table[(indicators >> 2) & 0x3]; +            } +        #else +            if_packet_info.has_tlr = false; +            #set $num_trailer_words = 0; +        #end if          ########## Variables ##########              //another failure case              if (packet_words32 < $($num_header_words + $num_trailer_words))                  throw uhd::value_error("bad vrt header or invalid packet length");              if_packet_info.num_header_words32 = $num_header_words;              if_packet_info.num_payload_words32 = packet_words32 - $($num_header_words + $num_trailer_words); +            if_packet_info.num_payload_bytes = if_packet_info.num_payload_words32*sizeof(boost::uint32_t) - empty_bytes;          break;      #end for      } diff --git a/host/lib/transport/super_recv_packet_handler.hpp b/host/lib/transport/super_recv_packet_handler.hpp index 541c588e6..57aae96b1 100644 --- a/host/lib/transport/super_recv_packet_handler.hpp +++ b/host/lib/transport/super_recv_packet_handler.hpp @@ -21,15 +21,12 @@  #include <uhd/config.hpp>  #include <uhd/exception.hpp>  #include <uhd/convert.hpp> -#include <uhd/device.hpp> +#include <uhd/stream.hpp>  #include <uhd/utils/msg.hpp>  #include <uhd/utils/byteswap.hpp> -#include <uhd/types/io_type.hpp> -#include <uhd/types/otw_type.hpp>  #include <uhd/types/metadata.hpp>  #include <uhd/transport/vrt_if_packet.hpp>  #include <uhd/transport/zero_copy.hpp> -#include <boost/thread/mutex.hpp>  #include <boost/dynamic_bitset.hpp>  #include <boost/foreach.hpp>  #include <boost/function.hpp> @@ -124,24 +121,12 @@ public:          _props.at(xport_chan).get_buff = get_buff;      } -    /*! -     * Setup the conversion functions (homogeneous across transports). -     * Here, we load a table of converters for all possible io types. -     * This makes the converter look-up an O(1) operation. -     * \param otw_type the channel data type -     * \param width the streams per channel (usually 1) -     */ -    void set_converter(const uhd::otw_type_t &otw_type, const size_t width = 1){ -        _io_buffs.resize(width); -        _converters.resize(128); -        for (size_t io_type = 0; io_type < _converters.size(); io_type++){ -            try{ -                _converters[io_type] = uhd::convert::get_converter_otw_to_cpu( -                    io_type_t::tid_t(io_type), otw_type, 1, width -                ); -            }catch(const uhd::value_error &){} //we expect this, not all io_types valid... -        } -        _bytes_per_item = otw_type.get_sample_size(); +    //! Set the conversion routine for all channels +    void set_converter(const uhd::convert::id_type &id){ +        _io_buffs.resize(id.num_outputs); +        _converter = uhd::convert::get_converter(id); +        _bytes_per_otw_item = uhd::convert::get_bytes_per_item(id.input_format); +        _bytes_per_cpu_item = uhd::convert::get_bytes_per_item(id.output_format);      }      //! Set the transport channel's overflow handler @@ -149,11 +134,6 @@ public:          _props.at(xport_chan).handle_overflow = handle_overflow;      } -    //! Get a scoped lock object for this instance -    boost::mutex::scoped_lock get_scoped_lock(void){ -        return boost::mutex::scoped_lock(_mutex); -    } -      //! Set the scale factor used in float conversion      void set_scale_factor(const double scale_factor){          _scale_factor = scale_factor; @@ -165,15 +145,12 @@ public:       * Dispatch into combinations of single packet receive calls.       ******************************************************************/      UHD_INLINE size_t recv( -        const uhd::device::recv_buffs_type &buffs, +        const uhd::rx_streamer::buffs_type &buffs,          const size_t nsamps_per_buff,          uhd::rx_metadata_t &metadata, -        const uhd::io_type_t &io_type, -        uhd::device::recv_mode_t recv_mode, -        double timeout +        const double timeout, +        const bool one_packet      ){ -        boost::mutex::scoped_lock lock(_mutex); -          //handle metadata queued from a previous receive          if (_queue_error_for_next_call){              _queue_error_for_next_call = false; @@ -183,48 +160,34 @@ public:              if (_queue_metadata.error_code != rx_metadata_t::ERROR_CODE_TIMEOUT) return 0;          } -        switch(recv_mode){ +        size_t accum_num_samps = recv_one_packet( +            buffs, nsamps_per_buff, metadata, timeout +        ); -        //////////////////////////////////////////////////////////////// -        case uhd::device::RECV_MODE_ONE_PACKET:{ -        //////////////////////////////////////////////////////////////// -            return recv_one_packet(buffs, nsamps_per_buff, metadata, io_type, timeout); -        } - -        //////////////////////////////////////////////////////////////// -        case uhd::device::RECV_MODE_FULL_BUFF:{ -        //////////////////////////////////////////////////////////////// -            size_t accum_num_samps = recv_one_packet( -                buffs, nsamps_per_buff, metadata, io_type, timeout -            ); +        if (one_packet) return accum_num_samps; -            //first recv had an error code set, return immediately -            if (metadata.error_code != rx_metadata_t::ERROR_CODE_NONE) return accum_num_samps; +        //first recv had an error code set, return immediately +        if (metadata.error_code != rx_metadata_t::ERROR_CODE_NONE) return accum_num_samps; -            //loop until buffer is filled or error code -            while(accum_num_samps < nsamps_per_buff){ -                size_t num_samps = recv_one_packet( -                    buffs, nsamps_per_buff - accum_num_samps, _queue_metadata, -                    io_type, timeout, accum_num_samps*io_type.size -                ); +        //loop until buffer is filled or error code +        while(accum_num_samps < nsamps_per_buff){ +            size_t num_samps = recv_one_packet( +                buffs, nsamps_per_buff - accum_num_samps, _queue_metadata, +                timeout, accum_num_samps*_bytes_per_cpu_item +            ); -                //metadata had an error code set, store for next call and return -                if (_queue_metadata.error_code != rx_metadata_t::ERROR_CODE_NONE){ -                    _queue_error_for_next_call = true; -                    break; -                } -                accum_num_samps += num_samps; +            //metadata had an error code set, store for next call and return +            if (_queue_metadata.error_code != rx_metadata_t::ERROR_CODE_NONE){ +                _queue_error_for_next_call = true; +                break;              } -            return accum_num_samps; +            accum_num_samps += num_samps;          } - -        default: throw uhd::value_error("unknown recv mode"); -        }//switch(recv_mode) +        return accum_num_samps;      }  private: -    boost::mutex _mutex;      vrt_unpacker_type _vrt_unpacker;      size_t _header_offset_words32;      double _tick_rate, _samp_rate; @@ -242,8 +205,9 @@ private:      };      std::vector<xport_chan_props_type> _props;      std::vector<void *> _io_buffs; //used in conversion -    size_t _bytes_per_item; //used in conversion -    std::vector<uhd::convert::function_type> _converters; //used in conversion +    size_t _bytes_per_otw_item; //used in conversion +    size_t _bytes_per_cpu_item; //used in conversion +    uhd::convert::function_type _converter; //used in conversion      double _scale_factor;      //! information stored for a received buffer @@ -363,7 +327,7 @@ private:              info.alignment_time = info[index].time;              info.indexes_todo.set();              info.indexes_todo.reset(index); -            info.data_bytes_to_copy = info[index].ifpi.num_payload_words32*sizeof(boost::uint32_t); +            info.data_bytes_to_copy = info[index].ifpi.num_payload_bytes;          }          //if the sequence id matches: @@ -471,7 +435,7 @@ private:                  std::swap(curr_info, next_info); //save progress from curr -> next                  curr_info.metadata.has_time_spec = prev_info.metadata.has_time_spec;                  curr_info.metadata.time_spec = prev_info.metadata.time_spec + time_spec_t(0, -                    prev_info[index].ifpi.num_payload_words32*sizeof(boost::uint32_t)/_bytes_per_item, _samp_rate); +                    prev_info[index].ifpi.num_payload_words32*sizeof(boost::uint32_t)/_bytes_per_otw_item, _samp_rate);                  curr_info.metadata.more_fragments = false;                  curr_info.metadata.fragment_offset = 0;                  curr_info.metadata.start_of_burst = false; @@ -507,13 +471,8 @@ private:          curr_info.metadata.time_spec = curr_info[0].time;          curr_info.metadata.more_fragments = false;          curr_info.metadata.fragment_offset = 0; -        /* TODO SOB on RX not supported in hardware -        static const int tlr_sob_flags = (1 << 21) | (1 << 9); //enable and indicator bits -        curr_info.metadata.start_of_burst = curr_info[0].ifpi.has_tlr and (int(curr_info[0].ifpi.tlr & tlr_sob_flags) != 0); -        */ -        curr_info.metadata.start_of_burst = false; -        static const int tlr_eob_flags = (1 << 20) | (1 << 8); //enable and indicator bits -        curr_info.metadata.end_of_burst   = curr_info[0].ifpi.has_tlr and (int(curr_info[0].ifpi.tlr & tlr_eob_flags) != 0); +        curr_info.metadata.start_of_burst = curr_info[0].ifpi.sob; +        curr_info.metadata.end_of_burst = curr_info[0].ifpi.eob;          curr_info.metadata.error_code = rx_metadata_t::ERROR_CODE_NONE;      } @@ -525,11 +484,10 @@ private:       * Then copy-convert available data into the user's IO buffers.       ******************************************************************/      UHD_INLINE size_t recv_one_packet( -        const uhd::device::recv_buffs_type &buffs, +        const uhd::rx_streamer::buffs_type &buffs,          const size_t nsamps_per_buff,          uhd::rx_metadata_t &metadata, -        const uhd::io_type_t &io_type, -        double timeout, +        const double timeout,          const size_t buffer_offset_bytes = 0      ){          //get the next buffer if the current one has expired @@ -551,9 +509,9 @@ private:          metadata.time_spec += time_spec_t(0, info.fragment_offset_in_samps, _samp_rate);          //extract the number of samples available to copy -        const size_t nsamps_available = info.data_bytes_to_copy/_bytes_per_item; +        const size_t nsamps_available = info.data_bytes_to_copy/_bytes_per_otw_item;          const size_t nsamps_to_copy = std::min(nsamps_per_buff*_io_buffs.size(), nsamps_available); -        const size_t bytes_to_copy = nsamps_to_copy*_bytes_per_item; +        const size_t bytes_to_copy = nsamps_to_copy*_bytes_per_otw_item;          const size_t nsamps_to_copy_per_io_buff = nsamps_to_copy/_io_buffs.size();          size_t buff_index = 0; @@ -565,7 +523,9 @@ private:              }              //copy-convert the samples from the recv buffer -            _converters[io_type.tid](buff_info.copy_buff, _io_buffs, nsamps_to_copy_per_io_buff, _scale_factor); +            if (nsamps_to_copy_per_io_buff != 0) _converter( +                buff_info.copy_buff, _io_buffs, nsamps_to_copy_per_io_buff, _scale_factor +            );              //update the rx copy buffer to reflect the bytes copied              buff_info.copy_buff += bytes_to_copy; @@ -589,6 +549,34 @@ private:      }  }; +class recv_packet_streamer : public recv_packet_handler, public rx_streamer{ +public: +    recv_packet_streamer(const size_t max_num_samps){ +        _max_num_samps = max_num_samps; +    } + +    size_t get_num_channels(void) const{ +        return this->size(); +    } + +    size_t get_max_num_samps(void) const{ +        return _max_num_samps; +    } + +    size_t recv( +        const rx_streamer::buffs_type &buffs, +        const size_t nsamps_per_buff, +        uhd::rx_metadata_t &metadata, +        const double timeout, +        const bool one_packet +    ){ +        return recv_packet_handler::recv(buffs, nsamps_per_buff, metadata, timeout, one_packet); +    } + +private: +    size_t _max_num_samps; +}; +  }}} //namespace  #endif /* INCLUDED_LIBUHD_TRANSPORT_SUPER_RECV_PACKET_HANDLER_HPP */ diff --git a/host/lib/transport/super_send_packet_handler.hpp b/host/lib/transport/super_send_packet_handler.hpp index a99adcb5f..c3ffcc861 100644 --- a/host/lib/transport/super_send_packet_handler.hpp +++ b/host/lib/transport/super_send_packet_handler.hpp @@ -21,16 +21,13 @@  #include <uhd/config.hpp>  #include <uhd/exception.hpp>  #include <uhd/convert.hpp> -#include <uhd/device.hpp> +#include <uhd/stream.hpp>  #include <uhd/utils/msg.hpp>  #include <uhd/utils/byteswap.hpp> -#include <uhd/types/io_type.hpp> -#include <uhd/types/otw_type.hpp>  #include <uhd/types/metadata.hpp>  #include <uhd/transport/vrt_if_packet.hpp>  #include <uhd/transport/zero_copy.hpp>  #include <boost/thread/thread_time.hpp> -#include <boost/thread/mutex.hpp>  #include <boost/foreach.hpp>  #include <boost/function.hpp>  #include <iostream> @@ -100,24 +97,12 @@ public:          _props.at(xport_chan).get_buff = get_buff;      } -    /*! -     * Setup the conversion functions (homogeneous across transports). -     * Here, we load a table of converters for all possible io types. -     * This makes the converter look-up an O(1) operation. -     * \param otw_type the channel data type -     * \param width the streams per channel (usually 1) -     */ -    void set_converter(const uhd::otw_type_t &otw_type, const size_t width = 1){ -        _io_buffs.resize(width); -        _converters.resize(128); -        for (size_t io_type = 0; io_type < _converters.size(); io_type++){ -            try{ -                _converters[io_type] = uhd::convert::get_converter_cpu_to_otw( -                    io_type_t::tid_t(io_type), otw_type, 1, width -                ); -            }catch(const uhd::value_error &){} //we expect this, not all io_types valid... -        } -        _bytes_per_item = otw_type.get_sample_size(); +    //! Set the conversion routine for all channels +    void set_converter(const uhd::convert::id_type &id){ +        _io_buffs.resize(id.num_inputs); +        _converter = uhd::convert::get_converter(id); +        _bytes_per_otw_item = uhd::convert::get_bytes_per_item(id.output_format); +        _bytes_per_cpu_item = uhd::convert::get_bytes_per_item(id.input_format);      }      /*! @@ -129,11 +114,6 @@ public:          _max_samples_per_packet = num_samps;      } -    //! Get a scoped lock object for this instance -    boost::mutex::scoped_lock get_scoped_lock(void){ -        return boost::mutex::scoped_lock(_mutex); -    } -      //! Set the scale factor used in float conversion      void set_scale_factor(const double scale_factor){          _scale_factor = scale_factor; @@ -145,15 +125,11 @@ public:       * Dispatch into combinations of single packet send calls.       ******************************************************************/      UHD_INLINE size_t send( -        const uhd::device::send_buffs_type &buffs, +        const uhd::tx_streamer::buffs_type &buffs,          const size_t nsamps_per_buff,          const uhd::tx_metadata_t &metadata, -        const uhd::io_type_t &io_type, -        uhd::device::send_mode_t send_mode, -        double timeout +        const double timeout      ){ -        boost::mutex::scoped_lock lock(_mutex); -          //translate the metadata to vrt if packet info          vrt::if_packet_info_t if_packet_info;          if_packet_info.has_sid = false; @@ -166,74 +142,56 @@ public:          if_packet_info.sob     = metadata.start_of_burst;          if_packet_info.eob     = metadata.end_of_burst; -        if (nsamps_per_buff <= _max_samples_per_packet) send_mode = uhd::device::SEND_MODE_ONE_PACKET; -        switch(send_mode){ - -        //////////////////////////////////////////////////////////////// -        case uhd::device::SEND_MODE_ONE_PACKET:{ -        //////////////////////////////////////////////////////////////// +        if (nsamps_per_buff <= _max_samples_per_packet){              //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, io_type, timeout +                _zero_buffs, 1, if_packet_info, timeout              ) & 0x0;              #endif -            return send_one_packet( -                buffs, -                std::min(nsamps_per_buff, _max_samples_per_packet), -                if_packet_info, io_type, timeout -            ); +            return send_one_packet(buffs, nsamps_per_buff, if_packet_info, timeout);          } +        size_t total_num_samps_sent = 0; -        //////////////////////////////////////////////////////////////// -        case uhd::device::SEND_MODE_FULL_BUFF:{ -        //////////////////////////////////////////////////////////////// -            size_t total_num_samps_sent = 0; +        //false until final fragment +        if_packet_info.eob = false; -            //false until final fragment -            if_packet_info.eob = false; +        const size_t num_fragments = (nsamps_per_buff-1)/_max_samples_per_packet; +        const size_t final_length = ((nsamps_per_buff-1)%_max_samples_per_packet)+1; -            const size_t num_fragments = (nsamps_per_buff-1)/_max_samples_per_packet; -            const size_t final_length = ((nsamps_per_buff-1)%_max_samples_per_packet)+1; +        //loop through the following fragment indexes +        for (size_t i = 0; i < num_fragments; i++){ -            //loop through the following fragment indexes -            for (size_t i = 0; i < num_fragments; i++){ - -                //send a fragment with the helper function -                const size_t num_samps_sent = send_one_packet( -                    buffs, _max_samples_per_packet, -                    if_packet_info, io_type, timeout, -                    total_num_samps_sent*io_type.size -                ); -                total_num_samps_sent += num_samps_sent; -                if (num_samps_sent == 0) return total_num_samps_sent; - -                //setup metadata for the next fragment -                const time_spec_t time_spec = metadata.time_spec + time_spec_t(0, total_num_samps_sent, _samp_rate); -                if_packet_info.tsi = boost::uint32_t(time_spec.get_full_secs()); -                if_packet_info.tsf = boost::uint64_t(time_spec.get_tick_count(_tick_rate)); -                if_packet_info.sob = false; +            //send a fragment with the helper function +            const size_t num_samps_sent = send_one_packet( +                buffs, _max_samples_per_packet, +                if_packet_info, timeout, +                total_num_samps_sent*_bytes_per_cpu_item +            ); +            total_num_samps_sent += num_samps_sent; +            if (num_samps_sent == 0) return total_num_samps_sent; -            } +            //setup metadata for the next fragment +            const time_spec_t time_spec = metadata.time_spec + time_spec_t(0, total_num_samps_sent, _samp_rate); +            if_packet_info.tsi = boost::uint32_t(time_spec.get_full_secs()); +            if_packet_info.tsf = boost::uint64_t(time_spec.get_tick_count(_tick_rate)); +            if_packet_info.sob = false; -            //send the final fragment with the helper function -            if_packet_info.eob = metadata.end_of_burst; -            return total_num_samps_sent + send_one_packet( -                buffs, final_length, -                if_packet_info, io_type, timeout, -                total_num_samps_sent*io_type.size -            );          } -        default: throw uhd::value_error("unknown send mode"); -        }//switch(send_mode) +        //send the final fragment with the helper function +        if_packet_info.eob = metadata.end_of_burst; +        return total_num_samps_sent + send_one_packet( +            buffs, final_length, +            if_packet_info, timeout, +            total_num_samps_sent*_bytes_per_cpu_item +        );      }  private: -    boost::mutex _mutex;      vrt_packer_type _vrt_packer;      size_t _header_offset_words32;      double _tick_rate, _samp_rate; @@ -242,8 +200,9 @@ private:      };      std::vector<xport_chan_props_type> _props;      std::vector<const void *> _io_buffs; //used in conversion -    size_t _bytes_per_item; //used in conversion -    std::vector<uhd::convert::function_type> _converters; //used in conversion +    size_t _bytes_per_otw_item; //used in conversion +    size_t _bytes_per_cpu_item; //used in conversion +    uhd::convert::function_type _converter; //used in conversion      size_t _max_samples_per_packet;      std::vector<const void *> _zero_buffs;      size_t _next_packet_seq; @@ -253,15 +212,15 @@ private:       * Send a single packet:       ******************************************************************/      UHD_INLINE size_t send_one_packet( -        const uhd::device::send_buffs_type &buffs, +        const uhd::tx_streamer::buffs_type &buffs,          const size_t nsamps_per_buff,          vrt::if_packet_info_t &if_packet_info, -        const uhd::io_type_t &io_type, -        double timeout, +        const double timeout,          const size_t buffer_offset_bytes = 0      ){          //load the rest of the if_packet_info in here -        if_packet_info.num_payload_words32 = (nsamps_per_buff*_io_buffs.size()*_bytes_per_item)/sizeof(boost::uint32_t); +        if_packet_info.num_payload_bytes = nsamps_per_buff*_io_buffs.size()*_bytes_per_otw_item; +        if_packet_info.num_payload_words32 = (if_packet_info.num_payload_bytes + 3/*round up*/)/sizeof(boost::uint32_t);          if_packet_info.packet_count = _next_packet_seq;          size_t buff_index = 0; @@ -280,7 +239,9 @@ private:              otw_mem += if_packet_info.num_header_words32;              //copy-convert the samples into the send buffer -            _converters[io_type.tid](_io_buffs, otw_mem, nsamps_per_buff, _scale_factor); +            if (nsamps_per_buff != 0) _converter( +                _io_buffs, otw_mem, nsamps_per_buff, _scale_factor +            );              //commit the samples to the zero-copy interface              size_t num_bytes_total = (_header_offset_words32+if_packet_info.num_packet_words32)*sizeof(boost::uint32_t); @@ -292,6 +253,34 @@ private:      }  }; +class send_packet_streamer : public send_packet_handler, public tx_streamer{ +public: +    send_packet_streamer(const size_t max_num_samps){ +        _max_num_samps = max_num_samps; +        this->set_max_samples_per_packet(_max_num_samps); +    } + +    size_t get_num_channels(void) const{ +        return this->size(); +    } + +    size_t get_max_num_samps(void) const{ +        return _max_num_samps; +    } + +    size_t send( +        const tx_streamer::buffs_type &buffs, +        const size_t nsamps_per_buff, +        const uhd::tx_metadata_t &metadata, +        const double timeout +    ){ +        return send_packet_handler::send(buffs, nsamps_per_buff, metadata, timeout); +    } + +private: +    size_t _max_num_samps; +}; +  }}} //namespace  #endif /* INCLUDED_LIBUHD_TRANSPORT_SUPER_SEND_PACKET_HANDLER_HPP */ diff --git a/host/lib/types/CMakeLists.txt b/host/lib/types/CMakeLists.txt index 957dfd345..2ca0faef7 100644 --- a/host/lib/types/CMakeLists.txt +++ b/host/lib/types/CMakeLists.txt @@ -80,7 +80,6 @@ SET_SOURCE_FILES_PROPERTIES(  # This file included, use CMake directory variables  ########################################################################  LIBUHD_APPEND_SOURCES( -    ${CMAKE_CURRENT_SOURCE_DIR}/clock_config.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/device_addr.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/mac_addr.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/ranges.cpp diff --git a/host/lib/types/clock_config.cpp b/host/lib/types/clock_config.cpp deleted file mode 100644 index c150c5cc3..000000000 --- a/host/lib/types/clock_config.cpp +++ /dev/null @@ -1,44 +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/>. -// - -#include <uhd/types/clock_config.hpp> - -using namespace uhd; - -clock_config_t clock_config_t::external(void){ -    clock_config_t clock_config; -    clock_config.ref_source = clock_config_t::REF_SMA; -    clock_config.pps_source = clock_config_t::PPS_SMA; -    clock_config.pps_polarity = clock_config_t::PPS_POS; -    return clock_config; -} - -clock_config_t clock_config_t::internal(void){ -    clock_config_t clock_config; -    clock_config.ref_source = clock_config_t::REF_INT; -    clock_config.pps_source = clock_config_t::PPS_SMA; -    clock_config.pps_polarity = clock_config_t::PPS_POS; -    return clock_config; -} - -clock_config_t::clock_config_t(void): -    ref_source(REF_INT), -    pps_source(PPS_SMA), -    pps_polarity(PPS_POS) -{ -    /* NOP */ -} diff --git a/host/lib/types/types.cpp b/host/lib/types/types.cpp index 7c65d2997..1a3e7e860 100644 --- a/host/lib/types/types.cpp +++ b/host/lib/types/types.cpp @@ -17,12 +17,6 @@  #include <uhd/types/stream_cmd.hpp>  #include <uhd/types/metadata.hpp> -#include <uhd/types/otw_type.hpp> -#include <uhd/types/io_type.hpp> -#include <boost/cstdint.hpp> -#include <stdexcept> -#include <complex> -#include <vector>  using namespace uhd; @@ -48,44 +42,3 @@ tx_metadata_t::tx_metadata_t(void):  {      /* NOP */  } - -/*********************************************************************** - * otw type - **********************************************************************/ -size_t otw_type_t::get_sample_size(void) const{ -    return (this->width * 2) / 8; -} - -otw_type_t::otw_type_t(void): -    width(0), -    shift(0), -    byteorder(BO_NATIVE) -{ -    /* NOP */ -} - -/*********************************************************************** - * io type - **********************************************************************/ -static std::vector<size_t> get_tid_size_table(void){ -    std::vector<size_t> table(128, 0); -    table[size_t(io_type_t::COMPLEX_FLOAT64)] = sizeof(std::complex<double>); -    table[size_t(io_type_t::COMPLEX_FLOAT32)] = sizeof(std::complex<float>); -    table[size_t(io_type_t::COMPLEX_INT16)]   = sizeof(std::complex<boost::int16_t>); -    table[size_t(io_type_t::COMPLEX_INT8)]    = sizeof(std::complex<boost::int8_t>); -    return table; -} - -static const std::vector<size_t> tid_size_table(get_tid_size_table()); - -io_type_t::io_type_t(tid_t tid): -    size(tid_size_table[size_t(tid) & 0x7f]), tid(tid) -{ -    /* NOP */ -} - -io_type_t::io_type_t(size_t size): -    size(size), tid(CUSTOM_TYPE) -{ -    /* NOP */ -} diff --git a/host/lib/usrp/b100/b100_impl.cpp b/host/lib/usrp/b100/b100_impl.cpp index 98c5cab8c..c506559be 100644 --- a/host/lib/usrp/b100/b100_impl.cpp +++ b/host/lib/usrp/b100/b100_impl.cpp @@ -139,6 +139,7 @@ UHD_STATIC_BLOCK(register_b100_device){   * Structors   **********************************************************************/  b100_impl::b100_impl(const device_addr_t &device_addr){ +    _tree = property_tree::make();      //extract the FPGA path for the B100      std::string b100_fpga_image = find_image_path( @@ -226,7 +227,6 @@ b100_impl::b100_impl(const device_addr_t &device_addr){      ////////////////////////////////////////////////////////////////////      // Initialize the properties tree      //////////////////////////////////////////////////////////////////// -    _tree = property_tree::make();      _tree->create<std::string>("/name").set("B-Series Device");      const fs_path mb_path = "/mboards/0";      _tree->create<std::string>(mb_path / "name").set("B100 (B-Hundo)"); @@ -276,12 +276,31 @@ b100_impl::b100_impl(const device_addr_t &device_addr){      ////////////////////////////////////////////////////////////////////      _rx_fe = rx_frontend_core_200::make(_fpga_ctrl, B100_REG_SR_ADDR(B100_SR_RX_FRONT));      _tx_fe = tx_frontend_core_200::make(_fpga_ctrl, B100_REG_SR_ADDR(B100_SR_TX_FRONT)); -    //TODO lots of properties to expose here for frontends +      _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec")          .subscribe(boost::bind(&b100_impl::update_rx_subdev_spec, this, _1));      _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec")          .subscribe(boost::bind(&b100_impl::update_tx_subdev_spec, this, _1)); +    const fs_path rx_fe_path = mb_path / "rx_frontends" / "A"; +    const fs_path tx_fe_path = mb_path / "tx_frontends" / "A"; + +    _tree->create<std::complex<double> >(rx_fe_path / "dc_offset" / "value") +        .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, _rx_fe, _1)) +        .set(std::complex<double>(0.0, 0.0)); +    _tree->create<bool>(rx_fe_path / "dc_offset" / "enable") +        .subscribe(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, _rx_fe, _1)) +        .set(true); +    _tree->create<std::complex<double> >(rx_fe_path / "iq_balance" / "value") +        .subscribe(boost::bind(&rx_frontend_core_200::set_iq_balance, _rx_fe, _1)) +        .set(std::complex<double>(0.0, 0.0)); +    _tree->create<std::complex<double> >(tx_fe_path / "dc_offset" / "value") +        .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, _tx_fe, _1)) +        .set(std::complex<double>(0.0, 0.0)); +    _tree->create<std::complex<double> >(tx_fe_path / "iq_balance" / "value") +        .subscribe(boost::bind(&tx_frontend_core_200::set_iq_balance, _tx_fe, _1)) +        .set(std::complex<double>(0.0, 0.0)); +      ////////////////////////////////////////////////////////////////////      // create rx dsp control objects      //////////////////////////////////////////////////////////////////// @@ -296,9 +315,12 @@ b100_impl::b100_impl(const device_addr_t &device_addr){          _tree->access<double>(mb_path / "tick_rate")              .subscribe(boost::bind(&rx_dsp_core_200::set_tick_rate, _rx_dsps[dspno], _1));          fs_path rx_dsp_path = mb_path / str(boost::format("rx_dsps/%u") % dspno); +        _tree->create<meta_range_t>(rx_dsp_path / "rate/range") +            .publish(boost::bind(&rx_dsp_core_200::get_host_rates, _rx_dsps[dspno]));          _tree->create<double>(rx_dsp_path / "rate/value") +            .set(1e6) //some default              .coerce(boost::bind(&rx_dsp_core_200::set_host_rate, _rx_dsps[dspno], _1)) -            .subscribe(boost::bind(&b100_impl::update_rx_samp_rate, this, _1)); +            .subscribe(boost::bind(&b100_impl::update_rx_samp_rate, this, dspno, _1));          _tree->create<double>(rx_dsp_path / "freq/value")              .coerce(boost::bind(&rx_dsp_core_200::set_freq, _rx_dsps[dspno], _1));          _tree->create<meta_range_t>(rx_dsp_path / "freq/range") @@ -316,9 +338,12 @@ b100_impl::b100_impl(const device_addr_t &device_addr){      _tx_dsp->set_link_rate(B100_LINK_RATE_BPS);      _tree->access<double>(mb_path / "tick_rate")          .subscribe(boost::bind(&tx_dsp_core_200::set_tick_rate, _tx_dsp, _1)); +    _tree->create<meta_range_t>(mb_path / "tx_dsps/0/rate/range") +        .publish(boost::bind(&tx_dsp_core_200::get_host_rates, _tx_dsp));      _tree->create<double>(mb_path / "tx_dsps/0/rate/value") +        .set(1e6) //some default          .coerce(boost::bind(&tx_dsp_core_200::set_host_rate, _tx_dsp, _1)) -        .subscribe(boost::bind(&b100_impl::update_tx_samp_rate, this, _1)); +        .subscribe(boost::bind(&b100_impl::update_tx_samp_rate, this, 0, _1));      _tree->create<double>(mb_path / "tx_dsps/0/freq/value")          .coerce(boost::bind(&tx_dsp_core_200::set_freq, _tx_dsp, _1));      _tree->create<meta_range_t>(mb_path / "tx_dsps/0/freq/range") @@ -379,22 +404,9 @@ b100_impl::b100_impl(const device_addr_t &device_addr){      _dboard_iface = make_b100_dboard_iface(_fpga_ctrl, _fpga_i2c_ctrl, _fpga_spi_ctrl, _clock_ctrl, _codec_ctrl);      _tree->create<dboard_iface::sptr>(mb_path / "dboards/A/iface").set(_dboard_iface);      _dboard_manager = dboard_manager::make( -        rx_db_eeprom.id, -        ((gdb_eeprom.id == dboard_id_t::none())? tx_db_eeprom : gdb_eeprom).id, -        _dboard_iface +        rx_db_eeprom.id, tx_db_eeprom.id, gdb_eeprom.id, +        _dboard_iface, _tree->subtree(mb_path / "dboards/A")      ); -    BOOST_FOREACH(const std::string &name, _dboard_manager->get_rx_subdev_names()){ -        dboard_manager::populate_prop_tree_from_subdev( -            _tree->subtree(mb_path / "dboards/A/rx_frontends" / name), -            _dboard_manager->get_rx_subdev(name) -        ); -    } -    BOOST_FOREACH(const std::string &name, _dboard_manager->get_tx_subdev_names()){ -        dboard_manager::populate_prop_tree_from_subdev( -            _tree->subtree(mb_path / "dboards/A/tx_frontends" / name), -            _dboard_manager->get_tx_subdev(name) -        ); -    }      //initialize io handling      this->io_init(); @@ -402,19 +414,13 @@ b100_impl::b100_impl(const device_addr_t &device_addr){      ////////////////////////////////////////////////////////////////////      // do some post-init tasks      //////////////////////////////////////////////////////////////////// -    _tree->access<double>(mb_path / "tick_rate").update() //update and then subscribe the clock callback -        .subscribe(boost::bind(&b100_clock_ctrl::set_fpga_clock_rate, _clock_ctrl, _1)); +    this->update_rates(); -    //and now that the tick rate is set, init the host rates to something -    BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "rx_dsps")){ -        _tree->access<double>(mb_path / "rx_dsps" / name / "rate" / "value").set(1e6); -    } -    BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "tx_dsps")){ -        _tree->access<double>(mb_path / "tx_dsps" / name / "rate" / "value").set(1e6); -    } +    _tree->access<double>(mb_path / "tick_rate") //now subscribe the clock rate setter +        .subscribe(boost::bind(&b100_clock_ctrl::set_fpga_clock_rate, _clock_ctrl, _1)); -    _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(subdev_spec_t("A:"+_dboard_manager->get_rx_subdev_names()[0])); -    _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(subdev_spec_t("A:"+_dboard_manager->get_tx_subdev_names()[0])); +    _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(subdev_spec_t("A:" + _tree->list(mb_path / "dboards/A/rx_frontends").at(0))); +    _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(subdev_spec_t("A:" + _tree->list(mb_path / "dboards/A/tx_frontends").at(0)));      _tree->access<std::string>(mb_path / "clock_source/value").set("internal");      _tree->access<std::string>(mb_path / "time_source/value").set("none");  } @@ -438,13 +444,19 @@ void b100_impl::check_fw_compat(void){  }  void b100_impl::check_fpga_compat(void){ -    const boost::uint16_t fpga_compat_num = _fpga_ctrl->peek16(B100_REG_MISC_COMPAT); -    if (fpga_compat_num != B100_FPGA_COMPAT_NUM){ +    const boost::uint32_t fpga_compat_num = _fpga_ctrl->peek32(B100_REG_RB_COMPAT); +    boost::uint16_t fpga_major = fpga_compat_num >> 16, fpga_minor = fpga_compat_num & 0xffff; +    if (fpga_major == 0){ //old version scheme +        fpga_major = fpga_minor; +        fpga_minor = 0; +    } +    if (fpga_major != B100_FPGA_COMPAT_NUM){          throw uhd::runtime_error(str(boost::format( -            "Expected FPGA compatibility number 0x%x, but got 0x%x:\n" +            "Expected FPGA compatibility number %d, but got %d:\n"              "The FPGA build is not compatible with the host code build." -        ) % B100_FPGA_COMPAT_NUM % fpga_compat_num)); +        ) % int(B100_FPGA_COMPAT_NUM) % fpga_major));      } +    _tree->create<std::string>("/mboards/0/fpga_version").set(str(boost::format("%u.%u") % fpga_major % fpga_minor));  }  double b100_impl::update_rx_codec_gain(const double gain){ diff --git a/host/lib/usrp/b100/b100_impl.hpp b/host/lib/usrp/b100/b100_impl.hpp index 5ec3dde7b..0984260be 100644 --- a/host/lib/usrp/b100/b100_impl.hpp +++ b/host/lib/usrp/b100/b100_impl.hpp @@ -33,7 +33,6 @@  #include <uhd/property_tree.hpp>  #include <uhd/utils/pimpl.hpp>  #include <uhd/types/dict.hpp> -#include <uhd/types/otw_type.hpp>  #include <uhd/types/sensors.hpp>  #include <uhd/types/clock_config.hpp>  #include <uhd/types/stream_cmd.hpp> @@ -42,12 +41,13 @@  #include <uhd/usrp/dboard_eeprom.hpp>  #include <uhd/usrp/dboard_manager.hpp>  #include <uhd/transport/usb_zero_copy.hpp> +#include <boost/weak_ptr.hpp>  static const double          B100_LINK_RATE_BPS = 256e6/8; //pratical link rate (< 480 Mbps)  static const std::string     B100_FW_FILE_NAME = "usrp_b100_fw.ihx";  static const std::string     B100_FPGA_FILE_NAME = "usrp_b100_fpga.bin";  static const boost::uint16_t B100_FW_COMPAT_NUM = 0x02; -static const boost::uint16_t B100_FPGA_COMPAT_NUM = 0x05; +static const boost::uint16_t B100_FPGA_COMPAT_NUM = 0x08;  static const boost::uint32_t B100_RX_SID_BASE = 2;  static const boost::uint32_t B100_TX_ASYNC_SID = 1;  static const double          B100_DEFAULT_TICK_RATE = 64e6; @@ -69,17 +69,8 @@ public:      ~b100_impl(void);      //the io interface -    size_t send(const send_buffs_type &, -                size_t, -                const uhd::tx_metadata_t &, -                const uhd::io_type_t &, -                send_mode_t, double); -    size_t recv(const recv_buffs_type &, -                size_t, uhd::rx_metadata_t &, -                const uhd::io_type_t &, -                recv_mode_t, double); -    size_t get_max_send_samps_per_packet(void) const; -    size_t get_max_recv_samps_per_packet(void) const; +    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);  private: @@ -106,7 +97,6 @@ private:      uhd::usrp::dboard_iface::sptr _dboard_iface;      //handle io stuff -    uhd::otw_type_t _rx_otw_type, _tx_otw_type;      UHD_PIMPL_DECL(io_impl) _io_impl;      void io_init(void); @@ -115,14 +105,18 @@ private:          return _tree;      } +    std::vector<boost::weak_ptr<uhd::rx_streamer> > _rx_streamers; +    std::vector<boost::weak_ptr<uhd::tx_streamer> > _tx_streamers; +      void check_fw_compat(void);      void check_fpga_compat(void);      double update_rx_codec_gain(const double); //sets A and B at once      void set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &);      void set_db_eeprom(const std::string &, const uhd::usrp::dboard_eeprom_t &);      void update_tick_rate(const double rate); -    void update_rx_samp_rate(const double rate); -    void update_tx_samp_rate(const double rate); +    void update_rx_samp_rate(const size_t, const double rate); +    void update_tx_samp_rate(const size_t, const double rate); +    void update_rates(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_clock_source(const std::string &); diff --git a/host/lib/usrp/b100/b100_regs.hpp b/host/lib/usrp/b100/b100_regs.hpp index 5e24f9937..491e16eef 100644 --- a/host/lib/usrp/b100/b100_regs.hpp +++ b/host/lib/usrp/b100/b100_regs.hpp @@ -31,7 +31,6 @@  #define B100_REG_MISC_RX_LEN     B100_REG_MISC_BASE + 10  #define B100_REG_MISC_TX_LEN     B100_REG_MISC_BASE + 12  #define B100_REG_MISC_XFER_RATE  B100_REG_MISC_BASE + 14 -#define B100_REG_MISC_COMPAT     B100_REG_MISC_BASE + 16  /////////////////////////////////////////////////////  // Slave 1 -- UART @@ -61,43 +60,6 @@  #define B100_REG_I2C_BASE B100_REG_SLAVE(3) -//////////////////////////////////////////////// -// Slave 4 -- GPIO - -#define B100_REG_GPIO_BASE B100_REG_SLAVE(4) - -#define B100_REG_GPIO_RX_IO      B100_REG_GPIO_BASE + 0 -#define B100_REG_GPIO_TX_IO      B100_REG_GPIO_BASE + 2 -#define B100_REG_GPIO_RX_DDR     B100_REG_GPIO_BASE + 4 -#define B100_REG_GPIO_TX_DDR     B100_REG_GPIO_BASE + 6 -#define B100_REG_GPIO_RX_SEL     B100_REG_GPIO_BASE + 8 -#define B100_REG_GPIO_TX_SEL     B100_REG_GPIO_BASE + 10 -#define B100_REG_GPIO_RX_DBG     B100_REG_GPIO_BASE + 12 -#define B100_REG_GPIO_TX_DBG     B100_REG_GPIO_BASE + 14 - -//possible bit values for sel when dbg is 0: -#define GPIO_SEL_SW    0 // if pin is an output, set by software in the io reg -#define GPIO_SEL_ATR   1 // if pin is an output, set by ATR logic - -//possible bit values for sel when dbg is 1: -#define GPIO_SEL_DEBUG_0   0 // if pin is an output, debug lines from FPGA fabric -#define GPIO_SEL_DEBUG_1   1 // if pin is an output, debug lines from FPGA fabric - -/////////////////////////////////////////////////// -// Slave 6 -- ATR Controller -//   16 regs - -#define B100_REG_ATR_BASE  B100_REG_SLAVE(6) - -#define	B100_REG_ATR_IDLE_RXSIDE  B100_REG_ATR_BASE + 0 -#define	B100_REG_ATR_IDLE_TXSIDE  B100_REG_ATR_BASE + 2 -#define B100_REG_ATR_INTX_RXSIDE  B100_REG_ATR_BASE + 4 -#define B100_REG_ATR_INTX_TXSIDE  B100_REG_ATR_BASE + 6 -#define	B100_REG_ATR_INRX_RXSIDE  B100_REG_ATR_BASE + 8 -#define	B100_REG_ATR_INRX_TXSIDE  B100_REG_ATR_BASE + 10 -#define	B100_REG_ATR_FULL_RXSIDE  B100_REG_ATR_BASE + 12 -#define	B100_REG_ATR_FULL_TXSIDE  B100_REG_ATR_BASE + 14 -  ///////////////////////////////////////////////////  // Slave 7 -- Readback Mux 32 @@ -108,6 +70,8 @@  #define B100_REG_RB_TIME_PPS_SECS   B100_REG_RB_MUX_32_BASE + 8  #define B100_REG_RB_TIME_PPS_TICKS  B100_REG_RB_MUX_32_BASE + 12  #define B100_REG_RB_MISC_TEST32     B100_REG_RB_MUX_32_BASE + 16 +#define B100_REG_RB_COMPAT          B100_REG_RB_MUX_32_BASE + 24 +#define B100_REG_RB_GPIO            B100_REG_RB_MUX_32_BASE + 28  ////////////////////////////////////////////////////  // Slaves 8 & 9 -- Settings Bus @@ -132,6 +96,8 @@  #define B100_SR_CLEAR_TX_FIFO 62 // 1 reg  #define B100_SR_GLOBAL_RESET 63  // 1 reg +#define B100_SR_GPIO 128 +  #define B100_REG_SR_ADDR(n) (B100_REG_SLAVE(8) + (4*(n)))  #define B100_REG_SR_MISC_TEST32        B100_REG_SR_ADDR(B100_SR_REG_TEST32) diff --git a/host/lib/usrp/b100/dboard_iface.cpp b/host/lib/usrp/b100/dboard_iface.cpp index 229215a4e..39ad5c5ac 100644 --- a/host/lib/usrp/b100/dboard_iface.cpp +++ b/host/lib/usrp/b100/dboard_iface.cpp @@ -15,7 +15,7 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // -#include "wb_iface.hpp" +#include "gpio_core_200.hpp"  #include <uhd/types/serial.hpp>  #include "b100_regs.hpp"  #include "clock_ctrl.hpp" @@ -45,13 +45,11 @@ public:          _spi_iface = spi_iface;          _clock = clock;          _codec = codec; +        _gpio = gpio_core_200::make(_wb_iface, B100_REG_SR_ADDR(B100_SR_GPIO), B100_REG_RB_GPIO);          //init the clock rate shadows          this->set_clock_rate(UNIT_RX, _clock->get_fpga_clock_rate());          this->set_clock_rate(UNIT_TX, _clock->get_fpga_clock_rate()); - -        _wb_iface->poke16(B100_REG_GPIO_RX_DBG, 0); -        _wb_iface->poke16(B100_REG_GPIO_TX_DBG, 0);      }      ~b100_dboard_iface(void){ @@ -104,6 +102,7 @@ private:      spi_iface::sptr _spi_iface;      b100_clock_ctrl::sptr _clock;      b100_codec_ctrl::sptr _codec; +    gpio_core_200::sptr _gpio;  };  /*********************************************************************** @@ -160,77 +159,27 @@ double b100_dboard_iface::get_codec_rate(unit_t){   * GPIO   **********************************************************************/  void b100_dboard_iface::_set_pin_ctrl(unit_t unit, boost::uint16_t value){ -    UHD_ASSERT_THROW(GPIO_SEL_ATR == 1); //make this assumption -    switch(unit){ -    case UNIT_RX: _wb_iface->poke16(B100_REG_GPIO_RX_SEL, value); return; -    case UNIT_TX: _wb_iface->poke16(B100_REG_GPIO_TX_SEL, value); return; -    } +    return _gpio->set_pin_ctrl(unit, value);  }  void b100_dboard_iface::_set_gpio_ddr(unit_t unit, boost::uint16_t value){ -    switch(unit){ -    case UNIT_RX: _wb_iface->poke16(B100_REG_GPIO_RX_DDR, value); return; -    case UNIT_TX: _wb_iface->poke16(B100_REG_GPIO_TX_DDR, value); return; -    } +    return _gpio->set_gpio_ddr(unit, value);  }  void b100_dboard_iface::_set_gpio_out(unit_t unit, boost::uint16_t value){ -    switch(unit){ -    case UNIT_RX: _wb_iface->poke16(B100_REG_GPIO_RX_IO, value); return; -    case UNIT_TX: _wb_iface->poke16(B100_REG_GPIO_TX_IO, value); return; -    } +    return _gpio->set_gpio_out(unit, value);  }  boost::uint16_t b100_dboard_iface::read_gpio(unit_t unit){ -    switch(unit){ -    case UNIT_RX: return _wb_iface->peek16(B100_REG_GPIO_RX_IO); -    case UNIT_TX: return _wb_iface->peek16(B100_REG_GPIO_TX_IO); -    default: UHD_THROW_INVALID_CODE_PATH(); -    } +    return _gpio->read_gpio(unit);  }  void b100_dboard_iface::_set_atr_reg(unit_t unit, atr_reg_t atr, boost::uint16_t value){ -    //define mapping of unit to atr regs to register address -    static const uhd::dict< -        unit_t, uhd::dict<atr_reg_t, boost::uint32_t> -    > unit_to_atr_to_addr = map_list_of -        (UNIT_RX, map_list_of -            (ATR_REG_IDLE,        B100_REG_ATR_IDLE_RXSIDE) -            (ATR_REG_TX_ONLY,     B100_REG_ATR_INTX_RXSIDE) -            (ATR_REG_RX_ONLY,     B100_REG_ATR_INRX_RXSIDE) -            (ATR_REG_FULL_DUPLEX, B100_REG_ATR_FULL_RXSIDE) -        ) -        (UNIT_TX, map_list_of -            (ATR_REG_IDLE,        B100_REG_ATR_IDLE_TXSIDE) -            (ATR_REG_TX_ONLY,     B100_REG_ATR_INTX_TXSIDE) -            (ATR_REG_RX_ONLY,     B100_REG_ATR_INRX_TXSIDE) -            (ATR_REG_FULL_DUPLEX, B100_REG_ATR_FULL_TXSIDE) -        ) -    ; -    _wb_iface->poke16(unit_to_atr_to_addr[unit][atr], value); +    return _gpio->set_atr_reg(unit, atr, value);  } -void b100_dboard_iface::set_gpio_debug(unit_t unit, int which){ -    //set this unit to all outputs -    this->set_gpio_ddr(unit, 0xffff); - -    //calculate the debug selections -    boost::uint32_t dbg_sels = 0x0; -    int sel = (which == 0)? GPIO_SEL_DEBUG_0 : GPIO_SEL_DEBUG_1; -    for(size_t i = 0; i < 16; i++) dbg_sels |= sel << i; - -    //set the debug on and which debug selection -    switch(unit){ -    case UNIT_RX: -        _wb_iface->poke16(B100_REG_GPIO_RX_DBG, 0xffff); -        _wb_iface->poke16(B100_REG_GPIO_RX_SEL, dbg_sels); -        return; - -    case UNIT_TX: -        _wb_iface->poke16(B100_REG_GPIO_TX_DBG, 0xffff); -        _wb_iface->poke16(B100_REG_GPIO_TX_SEL, dbg_sels); -        return; -    } +void b100_dboard_iface::set_gpio_debug(unit_t, int){ +    throw uhd::not_implemented_error("no set_gpio_debug implemented");  }  /*********************************************************************** diff --git a/host/lib/usrp/b100/io_impl.cpp b/host/lib/usrp/b100/io_impl.cpp index d2eee4f7c..abbd9864f 100644 --- a/host/lib/usrp/b100/io_impl.cpp +++ b/host/lib/usrp/b100/io_impl.cpp @@ -30,6 +30,7 @@  #include <boost/thread.hpp>  #include <uhd/utils/msg.hpp>  #include <uhd/utils/log.hpp> +#include <boost/make_shared.hpp>  using namespace uhd;  using namespace uhd::usrp; @@ -46,8 +47,6 @@ struct b100_impl::io_impl{      zero_copy_if::sptr data_transport;      bounded_buffer<async_metadata_t> async_msg_fifo;      recv_packet_demuxer::sptr demuxer; -    sph::recv_packet_handler recv_handler; -    sph::send_packet_handler send_handler;  };  /*********************************************************************** @@ -55,16 +54,6 @@ struct b100_impl::io_impl{   **********************************************************************/  void b100_impl::io_init(void){ -    //setup rx otw type -    _rx_otw_type.width = 16; -    _rx_otw_type.shift = 0; -    _rx_otw_type.byteorder = uhd::otw_type_t::BO_LITTLE_ENDIAN; - -    //setup tx otw type -    _tx_otw_type.width = 16; -    _tx_otw_type.shift = 0; -    _tx_otw_type.byteorder = uhd::otw_type_t::BO_LITTLE_ENDIAN; -      //clear state machines      _fpga_ctrl->poke32(B100_REG_CLEAR_RX, 0);      _fpga_ctrl->poke32(B100_REG_CLEAR_TX, 0); @@ -72,19 +61,16 @@ void b100_impl::io_init(void){      //set the expected packet size in USB frames      _fpga_ctrl->poke32(B100_REG_MISC_RX_LEN, 4); +    //allocate streamer weak ptrs containers +    _rx_streamers.resize(_rx_dsps.size()); +    _tx_streamers.resize(1/*known to be 1 dsp*/); +      //create new io impl      _io_impl = UHD_PIMPL_MAKE(io_impl, ());      _io_impl->demuxer = recv_packet_demuxer::make(_data_transport, _rx_dsps.size(), B100_RX_SID_BASE);      //now its safe to register the async callback      _fpga_ctrl->set_async_cb(boost::bind(&b100_impl::handle_async_message, this, _1)); - -    //init some handler stuff -    _io_impl->recv_handler.set_vrt_unpacker(&vrt::if_hdr_unpack_le); -    _io_impl->recv_handler.set_converter(_rx_otw_type); -    _io_impl->send_handler.set_vrt_packer(&vrt::if_hdr_pack_le); -    _io_impl->send_handler.set_converter(_tx_otw_type); -    _io_impl->send_handler.set_max_samples_per_packet(get_max_send_samps_per_packet());  }  void b100_impl::handle_async_message(managed_recv_buffer::sptr rbuf){ @@ -120,27 +106,54 @@ void b100_impl::handle_async_message(managed_recv_buffer::sptr rbuf){      else UHD_MSG(error) << "Unknown async packet" << std::endl;  } +void b100_impl::update_rates(void){ +    const fs_path mb_path = "/mboards/0"; +    _tree->access<double>(mb_path / "tick_rate").update(); + +    //and now that the tick rate is set, init the host rates to something +    BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "rx_dsps")){ +        _tree->access<double>(mb_path / "rx_dsps" / name / "rate" / "value").update(); +    } +    BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "tx_dsps")){ +        _tree->access<double>(mb_path / "tx_dsps" / name / "rate" / "value").update(); +    } +} +  void b100_impl::update_tick_rate(const double rate){ -    boost::mutex::scoped_lock recv_lock = _io_impl->recv_handler.get_scoped_lock(); -    _io_impl->recv_handler.set_tick_rate(rate); -    boost::mutex::scoped_lock send_lock = _io_impl->send_handler.get_scoped_lock(); -    _io_impl->send_handler.set_tick_rate(rate); +    //update the tick rate on all existing streamers -> thread safe +    for (size_t i = 0; i < _rx_streamers.size(); i++){ +        boost::shared_ptr<sph::recv_packet_streamer> my_streamer = +            boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_rx_streamers[i].lock()); +        if (my_streamer.get() == NULL) continue; +        my_streamer->set_tick_rate(rate); +    } +    for (size_t i = 0; i < _tx_streamers.size(); i++){ +        boost::shared_ptr<sph::send_packet_streamer> my_streamer = +            boost::dynamic_pointer_cast<sph::send_packet_streamer>(_tx_streamers[i].lock()); +        if (my_streamer.get() == NULL) continue; +        my_streamer->set_tick_rate(rate); +    }  } -void b100_impl::update_rx_samp_rate(const double rate){ -    boost::mutex::scoped_lock recv_lock = _io_impl->recv_handler.get_scoped_lock(); -    _io_impl->recv_handler.set_samp_rate(rate); -    const double adj = _rx_dsps.front()->get_scaling_adjustment(); -    _io_impl->recv_handler.set_scale_factor(adj/32767.); +void b100_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>(_rx_streamers[dspno].lock()); +    if (my_streamer.get() == NULL) return; + +    my_streamer->set_samp_rate(rate); +    const double adj = _rx_dsps[dspno]->get_scaling_adjustment(); +    my_streamer->set_scale_factor(adj);  } -void b100_impl::update_tx_samp_rate(const double rate){ -    boost::mutex::scoped_lock send_lock = _io_impl->send_handler.get_scoped_lock(); -    _io_impl->send_handler.set_samp_rate(rate); +void b100_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>(_tx_streamers[dspno].lock()); +    if (my_streamer.get() == NULL) return; + +    my_streamer->set_samp_rate(rate);  }  void b100_impl::update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){ -    boost::mutex::scoped_lock recv_lock = _io_impl->recv_handler.get_scoped_lock();      fs_path root = "/mboards/0/dboards";      //sanity checking @@ -154,22 +167,9 @@ void b100_impl::update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){          _rx_dsps[i]->set_mux(conn, fe_swapped);      }      _rx_fe->set_mux(fe_swapped); - -    //resize for the new occupancy -    _io_impl->recv_handler.resize(spec.size()); - -    //bind new callbacks for the handler -    for (size_t i = 0; i < _io_impl->recv_handler.size(); i++){ -        _rx_dsps[i]->set_nsamps_per_packet(get_max_recv_samps_per_packet()); //seems to be a good place to set this -        _io_impl->recv_handler.set_xport_chan_get_buff(i, boost::bind( -            &recv_packet_demuxer::get_recv_buff, _io_impl->demuxer, i, _1 -        )); -        _io_impl->recv_handler.set_overflow_handler(i, boost::bind(&rx_dsp_core_200::handle_overflow, _rx_dsps[i])); -    }  }  void b100_impl::update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){ -    boost::mutex::scoped_lock send_lock = _io_impl->send_handler.get_scoped_lock();      fs_path root = "/mboards/0/dboards";      //sanity checking @@ -178,16 +178,6 @@ void b100_impl::update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){      //set the mux for this spec      const std::string conn = _tree->access<std::string>(root / spec[0].db_name / "tx_frontends" / spec[0].sd_name / "connection").get();      _tx_fe->set_mux(conn); - -    //resize for the new occupancy -    _io_impl->send_handler.resize(spec.size()); - -    //bind new callbacks for the handler -    for (size_t i = 0; i < _io_impl->send_handler.size(); i++){ -        _io_impl->send_handler.set_xport_chan_get_buff(i, boost::bind( -            &zero_copy_if::get_send_buff, _data_transport, _1 -        )); -    }  }  /*********************************************************************** @@ -201,50 +191,109 @@ bool b100_impl::recv_async_msg(  }  /*********************************************************************** - * Send Data + * Receive streamer   **********************************************************************/ -size_t b100_impl::get_max_send_samps_per_packet(void) const { +rx_streamer::sptr b100_impl::get_rx_stream(const uhd::stream_args_t &args_){ +    stream_args_t args = args_; + +    //setup defaults for unspecified values +    args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format; +    args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; +    const unsigned sc8_scalar = unsigned(args.args.cast<double>("scalar", 0x400)); + +    //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      ; -    static const size_t bpp = 2048 - hdr_size; -    return bpp / _tx_otw_type.get_sample_size(); -} +    const size_t bpp = 2048 - hdr_size; //limited by FPGA pkt buffer size +    const size_t spp = bpp/convert::get_bytes_per_item(args.otw_format); + +    //make the new streamer given the samples per packet +    boost::shared_ptr<sph::recv_packet_streamer> my_streamer = boost::make_shared<sph::recv_packet_streamer>(spp); + +    //init some streamer stuff +    my_streamer->resize(args.channels.size()); +    my_streamer->set_vrt_unpacker(&vrt::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); + +    //bind callbacks for the handler +    for (size_t chan_i = 0; chan_i < args.channels.size(); chan_i++){ +        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]->set_format(args.otw_format, sc8_scalar); +        my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( +            &recv_packet_demuxer::get_recv_buff, _io_impl->demuxer, dsp, _1 +        )); +        my_streamer->set_overflow_handler(chan_i, boost::bind( +            &rx_dsp_core_200::handle_overflow, _rx_dsps[dsp] +        )); +        _rx_streamers[dsp] = my_streamer; //store weak pointer +    } -size_t b100_impl::send( -    const send_buffs_type &buffs, size_t nsamps_per_buff, -    const tx_metadata_t &metadata, const io_type_t &io_type, -    send_mode_t send_mode, double timeout -){ -    return _io_impl->send_handler.send( -        buffs, nsamps_per_buff, -        metadata, io_type, -        send_mode, timeout -    ); +    //sets all tick and samp rates on this streamer +    this->update_rates(); + +    return my_streamer;  }  /*********************************************************************** - * Receive Data + * Transmit streamer   **********************************************************************/ -size_t b100_impl::get_max_recv_samps_per_packet(void) const { +tx_streamer::sptr b100_impl::get_tx_stream(const uhd::stream_args_t &args_){ +    stream_args_t args = args_; + +    //setup defaults for unspecified values +    args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format; +    args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; + +    if (args.otw_format != "sc16"){ +        throw uhd::value_error("USRP TX cannot handle requested wire format: " + args.otw_format); +    } + +    //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      ; -    size_t bpp = 2048 - hdr_size; //limited by FPGA pkt buffer size -    return bpp/_rx_otw_type.get_sample_size(); -} +    static const size_t bpp = 2048 - hdr_size; +    const size_t spp = bpp/convert::get_bytes_per_item(args.otw_format); + +    //make the new streamer given the samples per packet +    boost::shared_ptr<sph::send_packet_streamer> my_streamer = boost::make_shared<sph::send_packet_streamer>(spp); + +    //init some streamer stuff +    my_streamer->resize(args.channels.size()); +    my_streamer->set_vrt_packer(&vrt::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); + +    //bind callbacks for the handler +    for (size_t chan_i = 0; chan_i < args.channels.size(); chan_i++){ +        const size_t dsp = args.channels[chan_i]; +        UHD_ASSERT_THROW(dsp == 0); //always 0 +        my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( +            &zero_copy_if::get_send_buff, _data_transport, _1 +        )); +        _tx_streamers[dsp] = my_streamer; //store weak pointer +    } -size_t b100_impl::recv( -    const recv_buffs_type &buffs, size_t nsamps_per_buff, -    rx_metadata_t &metadata, const io_type_t &io_type, -    recv_mode_t recv_mode, double timeout -){ -    return _io_impl->recv_handler.recv( -        buffs, nsamps_per_buff, -        metadata, io_type, -        recv_mode, timeout -    ); +    //sets all tick and samp rates on this streamer +    this->update_rates(); + +    return my_streamer;  } diff --git a/host/lib/usrp/cores/gpio_core_200.cpp b/host/lib/usrp/cores/gpio_core_200.cpp index 8639b1851..d756097ff 100644 --- a/host/lib/usrp/cores/gpio_core_200.cpp +++ b/host/lib/usrp/cores/gpio_core_200.cpp @@ -23,15 +23,14 @@  #define REG_GPIO_TX_ONLY       _base + 8  #define REG_GPIO_BOTH          _base + 12  #define REG_GPIO_DDR           _base + 16 -#define REG_GPIO_READ          _base + 0 //any address will readback  using namespace uhd;  using namespace usrp;  class gpio_core_200_impl : public gpio_core_200{  public: -    gpio_core_200_impl(wb_iface::sptr iface, const size_t base): -        _iface(iface), _base(base) { /* NOP */ } +    gpio_core_200_impl(wb_iface::sptr iface, const size_t base, const size_t rb_addr): +        _iface(iface), _base(base), _rb_addr(rb_addr) { /* NOP */ }      void set_pin_ctrl(const unit_t unit, const boost::uint16_t value){          _pin_ctrl[unit] = value; //shadow @@ -57,12 +56,13 @@ public:      }      boost::uint16_t read_gpio(const unit_t unit){ -        return boost::uint16_t(_iface->peek32(REG_GPIO_READ) >> unit2shit(unit)); +        return boost::uint16_t(_iface->peek32(_rb_addr) >> unit2shit(unit));      }  private:      wb_iface::sptr _iface;      const size_t _base; +    const size_t _rb_addr;      uhd::dict<unit_t, boost::uint16_t> _pin_ctrl, _gpio_out, _gpio_ddr;      uhd::dict<unit_t, uhd::dict<atr_reg_t, boost::uint16_t> > _atr_regs; @@ -95,6 +95,6 @@ private:  }; -gpio_core_200::sptr gpio_core_200::make(wb_iface::sptr iface, const size_t base){ -    return sptr(new gpio_core_200_impl(iface, base)); +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));  } diff --git a/host/lib/usrp/cores/gpio_core_200.hpp b/host/lib/usrp/cores/gpio_core_200.hpp index 7ff2af649..278575874 100644 --- a/host/lib/usrp/cores/gpio_core_200.hpp +++ b/host/lib/usrp/cores/gpio_core_200.hpp @@ -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); +    static sptr make(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; diff --git a/host/lib/usrp/cores/rx_dsp_core_200.cpp b/host/lib/usrp/cores/rx_dsp_core_200.cpp index d562c64db..b97f9c58e 100644 --- a/host/lib/usrp/cores/rx_dsp_core_200.cpp +++ b/host/lib/usrp/cores/rx_dsp_core_200.cpp @@ -42,6 +42,7 @@  #define REG_RX_CTRL_VRT_TLR        _ctrl_base + 24  #define REG_RX_CTRL_NSAMPS_PP      _ctrl_base + 28  #define REG_RX_CTRL_NCHANNELS      _ctrl_base + 32 +#define REG_RX_CTRL_FORMAT         _ctrl_base + 36  template <class T> T ceil_log2(T num){      return std::ceil(std::log(num)/std::log(T(2))); @@ -129,15 +130,26 @@ public:      }      void set_link_rate(const double rate){ -        _link_rate = rate/sizeof(boost::uint32_t); //in samps/s +        //_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){ -        size_t decim_rate = uhd::clip<size_t>( -            boost::math::iround(_tick_rate/rate), size_t(std::ceil(_tick_rate/_link_rate)), 512 -        ); -        if (decim_rate > 128) decim_rate &= ~0x1; //CIC up to 128, have to use 1 HB -        if (decim_rate > 256) decim_rate &= ~0x3; //CIC up to 128, have to use 2 HB +        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 @@ -162,7 +174,7 @@ public:      }      double get_scaling_adjustment(void){ -        return _scaling_adjustment; +        return _scaling_adjustment/_fxpt_scale_adj;      }      double set_freq(const double freq_){ @@ -192,12 +204,28 @@ public:          if (_continuous_streaming) issue_stream_command(stream_cmd_t::STREAM_MODE_START_CONTINUOUS);      } +    void set_format(const std::string &format, const unsigned scale){ +        unsigned format_word = 0; +        if (format == "sc16"){ +            format_word = 0; +            _fxpt_scale_adj = 32767.; +        } +        else if (format == "sc8"){ +            format_word = (1 << 18); +            _fxpt_scale_adj = 32767./scale; +        } +        else throw uhd::value_error("USRP RX cannot handle requested wire format: " + format); + +        const unsigned scale_word = scale & 0x3ffff; //18 bits; +        _iface->poke32(REG_RX_CTRL_FORMAT, format_word | scale_word); +    } +  private:      wb_iface::sptr _iface;      const size_t _dsp_base, _ctrl_base;      double _tick_rate, _link_rate;      bool _continuous_streaming; -    double _scaling_adjustment; +    double _scaling_adjustment, _fxpt_scale_adj;  };  rx_dsp_core_200::sptr rx_dsp_core_200::make(wb_iface::sptr iface, const size_t dsp_base, const size_t ctrl_base, const boost::uint32_t sid, const bool lingering_packet){ diff --git a/host/lib/usrp/cores/rx_dsp_core_200.hpp b/host/lib/usrp/cores/rx_dsp_core_200.hpp index 391cc8441..89b8c1f51 100644 --- a/host/lib/usrp/cores/rx_dsp_core_200.hpp +++ b/host/lib/usrp/cores/rx_dsp_core_200.hpp @@ -48,6 +48,8 @@ public:      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; @@ -56,6 +58,7 @@ public:      virtual void handle_overflow(void) = 0; +    virtual void set_format(const std::string &format, const unsigned scale) = 0;  };  #endif /* INCLUDED_LIBUHD_USRP_RX_DSP_CORE_200_HPP */ diff --git a/host/lib/usrp/cores/rx_frontend_core_200.cpp b/host/lib/usrp/cores/rx_frontend_core_200.cpp index 0e8220b49..d42022947 100644 --- a/host/lib/usrp/cores/rx_frontend_core_200.cpp +++ b/host/lib/usrp/cores/rx_frontend_core_200.cpp @@ -24,6 +24,9 @@  #define REG_RX_FE_OFFSET_I            _base + 12 //18 bits  #define REG_RX_FE_OFFSET_Q            _base + 16 //18 bits +#define OFFSET_FIXED (1ul << 31) +#define OFFSET_SET   (1ul << 30) +  static boost::uint32_t fs_to_bits(const double num, const size_t bits){      return boost::int32_t(boost::math::round(num * (1 << (bits-1))));  } @@ -41,17 +44,32 @@ public:          _iface->poke32(REG_RX_FE_SWAP_IQ, swap? 1 : 0);      } -    void set_offset(const std::complex<double> &off){ -        _iface->poke32(REG_RX_FE_OFFSET_I, fs_to_bits(off.real(), 24)); -        _iface->poke32(REG_RX_FE_OFFSET_Q, fs_to_bits(off.imag(), 24)); +    void set_dc_offset_auto(const bool enb){ +        this->set_dc_offset(enb? 0 : OFFSET_FIXED); +    } + +    std::complex<double> set_dc_offset(const std::complex<double> &off){ +        static const double scaler = double(1ul << 29); +        _i_dc_off = boost::math::iround(off.real()*scaler); +        _q_dc_off = boost::math::iround(off.imag()*scaler); + +        this->set_dc_offset(OFFSET_SET | OFFSET_FIXED); + +        return std::complex<double>(_i_dc_off/scaler, _q_dc_off/scaler); +    } + +    void set_dc_offset(const boost::uint32_t flags){ +        _iface->poke32(REG_RX_FE_OFFSET_I, flags | _i_dc_off); +        _iface->poke32(REG_RX_FE_OFFSET_Q, flags | _q_dc_off);      } -    void set_correction(const std::complex<double> &cor){ +    void set_iq_balance(const std::complex<double> &cor){          _iface->poke32(REG_RX_FE_MAG_CORRECTION, fs_to_bits(std::abs(cor), 18));          _iface->poke32(REG_RX_FE_PHASE_CORRECTION, fs_to_bits(std::atan2(cor.real(), cor.imag()), 18));      }  private: +    boost::int32_t _i_dc_off, _q_dc_off;      wb_iface::sptr _iface;      const size_t _base;  }; diff --git a/host/lib/usrp/cores/rx_frontend_core_200.hpp b/host/lib/usrp/cores/rx_frontend_core_200.hpp index a950e2bb7..5755424c8 100644 --- a/host/lib/usrp/cores/rx_frontend_core_200.hpp +++ b/host/lib/usrp/cores/rx_frontend_core_200.hpp @@ -33,9 +33,11 @@ public:      virtual void set_mux(const bool swap) = 0; -    virtual void set_offset(const std::complex<double> &off) = 0; +    virtual void set_dc_offset_auto(const bool enb) = 0; -    virtual void set_correction(const std::complex<double> &cor) = 0; +    virtual std::complex<double> set_dc_offset(const std::complex<double> &off) = 0; + +    virtual void set_iq_balance(const std::complex<double> &cor) = 0;  }; diff --git a/host/lib/usrp/cores/tx_dsp_core_200.cpp b/host/lib/usrp/cores/tx_dsp_core_200.cpp index 04e9f5da4..9d90d30cc 100644 --- a/host/lib/usrp/cores/tx_dsp_core_200.cpp +++ b/host/lib/usrp/cores/tx_dsp_core_200.cpp @@ -70,15 +70,26 @@ public:      }      void set_link_rate(const double rate){ -        _link_rate = rate/sizeof(boost::uint32_t); //in samps/s +        //_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){ -        size_t interp_rate = uhd::clip<size_t>( -            boost::math::iround(_tick_rate/rate), size_t(std::ceil(_tick_rate/_link_rate)), 512 -        ); -        if (interp_rate > 128) interp_rate &= ~0x1; //CIC up to 128, have to use 1 HB -        if (interp_rate > 256) interp_rate &= ~0x3; //CIC up to 128, have to use 2 HB +        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 diff --git a/host/lib/usrp/cores/tx_dsp_core_200.hpp b/host/lib/usrp/cores/tx_dsp_core_200.hpp index 65f822558..e6be63557 100644 --- a/host/lib/usrp/cores/tx_dsp_core_200.hpp +++ b/host/lib/usrp/cores/tx_dsp_core_200.hpp @@ -40,6 +40,8 @@ public:      virtual double set_host_rate(const double rate) = 0; +    virtual uhd::meta_range_t get_host_rates(void) = 0; +      virtual uhd::meta_range_t get_freq_range(void) = 0;      virtual double set_freq(const double freq) = 0; diff --git a/host/lib/usrp/cores/tx_frontend_core_200.cpp b/host/lib/usrp/cores/tx_frontend_core_200.cpp index a7568a81e..327e8d344 100644 --- a/host/lib/usrp/cores/tx_frontend_core_200.cpp +++ b/host/lib/usrp/cores/tx_frontend_core_200.cpp @@ -50,12 +50,18 @@ public:          _iface->poke32(REG_TX_FE_MUX, mode_to_mux[mode]);      } -    void set_dc_offset(const std::complex<double> &off){ -        _iface->poke32(REG_TX_FE_DC_OFFSET_I, fs_to_bits(off.real(), 24)); -        _iface->poke32(REG_TX_FE_DC_OFFSET_Q, fs_to_bits(off.imag(), 24)); +    std::complex<double> set_dc_offset(const std::complex<double> &off){ +        static const double scaler = double(1ul << 23); +        const boost::int32_t i_dc_off = boost::math::iround(off.real()*scaler); +        const boost::int32_t q_dc_off = boost::math::iround(off.imag()*scaler); + +        _iface->poke32(REG_TX_FE_DC_OFFSET_I, i_dc_off); +        _iface->poke32(REG_TX_FE_DC_OFFSET_Q, q_dc_off); + +        return std::complex<double>(i_dc_off/scaler, q_dc_off/scaler);      } -    void set_correction(const std::complex<double> &cor){ +    void set_iq_balance(const std::complex<double> &cor){          _iface->poke32(REG_TX_FE_MAG_CORRECTION, fs_to_bits(std::abs(cor), 18));          _iface->poke32(REG_TX_FE_PHASE_CORRECTION, fs_to_bits(std::atan2(cor.real(), cor.imag()), 18));      } diff --git a/host/lib/usrp/cores/tx_frontend_core_200.hpp b/host/lib/usrp/cores/tx_frontend_core_200.hpp index 9e4a7bc79..8ee0f3e6d 100644 --- a/host/lib/usrp/cores/tx_frontend_core_200.hpp +++ b/host/lib/usrp/cores/tx_frontend_core_200.hpp @@ -33,9 +33,9 @@ public:      virtual void set_mux(const std::string &mode) = 0; -    virtual void set_dc_offset(const std::complex<double> &off) = 0; +    virtual std::complex<double> set_dc_offset(const std::complex<double> &off) = 0; -    virtual void set_correction(const std::complex<double> &cor) = 0; +    virtual void set_iq_balance(const std::complex<double> &cor) = 0;  }; diff --git a/host/lib/usrp/dboard/CMakeLists.txt b/host/lib/usrp/dboard/CMakeLists.txt index c7b46e7c4..b000c7f33 100644 --- a/host/lib/usrp/dboard/CMakeLists.txt +++ b/host/lib/usrp/dboard/CMakeLists.txt @@ -23,8 +23,13 @@ LIBUHD_APPEND_SOURCES(      ${CMAKE_CURRENT_SOURCE_DIR}/db_basic_and_lf.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/db_rfx.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/db_xcvr2450.cpp -    ${CMAKE_CURRENT_SOURCE_DIR}/db_sbx.cpp +    ${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_wbx_common.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/db_wbx_version2.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/db_wbx_version3.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/db_wbx_version4.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/db_wbx_simple.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/db_dbsrx.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/db_unknown.cpp diff --git a/host/lib/usrp/dboard/db_basic_and_lf.cpp b/host/lib/usrp/dboard/db_basic_and_lf.cpp index 86d86dda0..53429a8c7 100644 --- a/host/lib/usrp/dboard/db_basic_and_lf.cpp +++ b/host/lib/usrp/dboard/db_basic_and_lf.cpp @@ -23,6 +23,7 @@  #include <uhd/usrp/dboard_base.hpp>  #include <uhd/usrp/dboard_manager.hpp>  #include <boost/assign/list_of.hpp> +#include <boost/bind.hpp>  #include <boost/format.hpp>  using namespace uhd; @@ -48,9 +49,6 @@ public:      basic_rx(ctor_args_t args, double max_freq);      ~basic_rx(void); -    void rx_get(const wax::obj &key, wax::obj &val); -    void rx_set(const wax::obj &key, const wax::obj &val); -  private:      double _max_freq;  }; @@ -60,18 +58,15 @@ public:      basic_tx(ctor_args_t args, double max_freq);      ~basic_tx(void); -    void tx_get(const wax::obj &key, wax::obj &val); -    void tx_set(const wax::obj &key, const wax::obj &val); -  private:      double _max_freq;  }; -static const uhd::dict<std::string, subdev_conn_t> sd_name_to_conn = map_list_of -    ("AB", SUBDEV_CONN_COMPLEX_IQ) -    ("BA", SUBDEV_CONN_COMPLEX_QI) -    ("A",  SUBDEV_CONN_REAL_I) -    ("B",  SUBDEV_CONN_REAL_Q) +static const uhd::dict<std::string, std::string> sd_name_to_conn = map_list_of +    ("AB", "IQ") +    ("BA", "QI") +    ("A",  "I") +    ("B",  "Q")  ;  /*********************************************************************** @@ -105,222 +100,95 @@ UHD_STATIC_BLOCK(reg_basic_and_lf_dboards){   **********************************************************************/  basic_rx::basic_rx(ctor_args_t args, double max_freq) : rx_dboard_base(args){      _max_freq = max_freq; +    //this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true); +    //////////////////////////////////////////////////////////////////// +    // Register properties +    //////////////////////////////////////////////////////////////////// +    this->get_rx_subtree()->create<std::string>("name").set( +        std::string(str(boost::format("%s - %s") +            % get_rx_id().to_pp_string() +            % get_subdev_name() +        ))); +    this->get_rx_subtree()->create<int>("gains"); //phony property so this dir exists +    this->get_rx_subtree()->create<double>("freq/value") +        .set(double(0.0)); +    this->get_rx_subtree()->create<meta_range_t>("freq/range") +        .set(freq_range_t(-_max_freq, +_max_freq)); +    this->get_rx_subtree()->create<std::string>("antenna/value") +        .set(""); +    this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options") +        .set(list_of("")); +    this->get_rx_subtree()->create<int>("sensors"); //phony property so this dir exists +    this->get_rx_subtree()->create<std::string>("connection") +        .set(sd_name_to_conn[get_subdev_name()]); +    this->get_rx_subtree()->create<bool>("enabled") +        .set(true); //always enabled +    this->get_rx_subtree()->create<bool>("use_lo_offset") +        .set(false); +    this->get_rx_subtree()->create<double>("bandwidth/value") +        .set(subdev_bandwidth_scalar[get_subdev_name()]*_max_freq); +    this->get_rx_subtree()->create<meta_range_t>("bandwidth/range") +        .set(freq_range_t(subdev_bandwidth_scalar[get_subdev_name()]*_max_freq, subdev_bandwidth_scalar[get_subdev_name()]*_max_freq)); +     +    //disable RX dboard clock by default +    this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, false); +      //set GPIOs to output 0x0000 to decrease noise pickup      this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, 0x0000);      this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0xFFFF);      this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, 0x0000); -    //this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true);  }  basic_rx::~basic_rx(void){      /* NOP */  } -void basic_rx::rx_get(const wax::obj &key_, wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ -    case SUBDEV_PROP_NAME: -        val = std::string(str(boost::format("%s - %s") -            % get_rx_id().to_pp_string() -            % get_subdev_name() -        )); -        return; - -    case SUBDEV_PROP_OTHERS: -        val = prop_names_t(); //empty -        return; - -    case SUBDEV_PROP_GAIN: -        val = double(0); -        return; - -    case SUBDEV_PROP_GAIN_RANGE: -        val = gain_range_t(0, 0, 0); -        return; - -    case SUBDEV_PROP_GAIN_NAMES: -        val = prop_names_t(); //empty -        return; - -    case SUBDEV_PROP_FREQ: -        val = double(0); -        return; - -    case SUBDEV_PROP_FREQ_RANGE: -        val = freq_range_t(-_max_freq, +_max_freq); -        return; - -    case SUBDEV_PROP_ANTENNA: -        val = std::string(""); -        return; - -    case SUBDEV_PROP_ANTENNA_NAMES: -        val = prop_names_t(1, ""); //vector of 1 empty string -        return; - -    case SUBDEV_PROP_SENSOR_NAMES: -        val = std::vector<std::string>(); //empty -        return; - -    case SUBDEV_PROP_CONNECTION: -        val = sd_name_to_conn[get_subdev_name()]; -        return; - -    case SUBDEV_PROP_ENABLED: -        val = true; //always enabled -        return; - -    case SUBDEV_PROP_USE_LO_OFFSET: -        val = false; -        return; - -    case SUBDEV_PROP_BANDWIDTH: -        val = subdev_bandwidth_scalar[get_subdev_name()]*_max_freq; -        return; - -    default: UHD_THROW_PROP_GET_ERROR(); -    } -} - -void basic_rx::rx_set(const wax::obj &key_, const wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ - -    case SUBDEV_PROP_GAIN: -        UHD_ASSERT_THROW(val.as<double>() == double(0)); -        return; - -    case SUBDEV_PROP_ANTENNA: -        if (val.as<std::string>().empty()) return; -        throw uhd::value_error("no selectable antennas on this board"); - -    case SUBDEV_PROP_FREQ: -        return; // it wont do you much good, but you can set it - -    case SUBDEV_PROP_ENABLED: -        return; //always enabled - -    case SUBDEV_PROP_BANDWIDTH: -        UHD_MSG(warning) << boost::format( -            "%s: No tunable bandwidth, fixed filtered to %0.2fMHz" -        ) % get_rx_id().to_pp_string() % _max_freq; -        return; - -    default: UHD_THROW_PROP_SET_ERROR(); -    } -} -  /***********************************************************************   * Basic and LF TX dboard   **********************************************************************/  basic_tx::basic_tx(ctor_args_t args, double max_freq) : tx_dboard_base(args){      _max_freq = max_freq;      //this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true); -} -basic_tx::~basic_tx(void){ -    /* NOP */ -} - -void basic_tx::tx_get(const wax::obj &key_, wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ -    case SUBDEV_PROP_NAME: -        val = std::string(str(boost::format("%s - %s") -            % get_tx_id().to_pp_string() +    //////////////////////////////////////////////////////////////////// +    // Register properties +    //////////////////////////////////////////////////////////////////// +    this->get_tx_subtree()->create<std::string>("name").set( +        std::string(str(boost::format("%s - %s") +            % get_rx_id().to_pp_string()              % get_subdev_name() -        )); -        return; - -    case SUBDEV_PROP_OTHERS: -        val = prop_names_t(); //empty -        return; - -    case SUBDEV_PROP_GAIN: -        val = double(0); -        return; - -    case SUBDEV_PROP_GAIN_RANGE: -        val = gain_range_t(0, 0, 0); -        return; - -    case SUBDEV_PROP_GAIN_NAMES: -        val = prop_names_t(); //empty -        return; - -    case SUBDEV_PROP_FREQ: -        val = double(0); -        return; - -    case SUBDEV_PROP_FREQ_RANGE: -        val = freq_range_t(-_max_freq, +_max_freq); -        return; - -    case SUBDEV_PROP_ANTENNA: -        val = std::string(""); -        return; - -    case SUBDEV_PROP_ANTENNA_NAMES: -        val = prop_names_t(1, ""); //vector of 1 empty string -        return; - -    case SUBDEV_PROP_SENSOR_NAMES: -        val = std::vector<std::string>(); //empty -        return; - -    case SUBDEV_PROP_CONNECTION: -        val = sd_name_to_conn[get_subdev_name()]; -        return; - -    case SUBDEV_PROP_ENABLED: -        val = true; //always enabled -        return; - -    case SUBDEV_PROP_USE_LO_OFFSET: -        val = false; -        return; - -    case SUBDEV_PROP_BANDWIDTH: -        val = subdev_bandwidth_scalar[get_subdev_name()]*_max_freq; -        return; +        ))); +    this->get_tx_subtree()->create<int>("gains"); //phony property so this dir exists +    this->get_tx_subtree()->create<double>("freq/value") +        .set(double(0.0)); +    this->get_tx_subtree()->create<meta_range_t>("freq/range") +        .set(freq_range_t(-_max_freq, +_max_freq)); +    this->get_tx_subtree()->create<std::string>("antenna/value") +        .set(""); +    this->get_tx_subtree()->create<std::vector<std::string> >("antenna/options") +        .set(list_of("")); +    this->get_tx_subtree()->create<int>("sensors"); //phony property so this dir exists +    this->get_tx_subtree()->create<std::string>("connection") +        .set(sd_name_to_conn[get_subdev_name()]); +    this->get_tx_subtree()->create<bool>("enabled") +        .set(true); //always enabled +    this->get_tx_subtree()->create<bool>("use_lo_offset") +        .set(false); +    this->get_tx_subtree()->create<double>("bandwidth/value") +        .set(subdev_bandwidth_scalar[get_subdev_name()]*_max_freq); +    this->get_tx_subtree()->create<meta_range_t>("bandwidth/range") +        .set(freq_range_t(subdev_bandwidth_scalar[get_subdev_name()]*_max_freq, subdev_bandwidth_scalar[get_subdev_name()]*_max_freq)); +     +    //disable TX dboard clock by default +    this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, false); -    default: UHD_THROW_PROP_GET_ERROR(); -    } +    //set GPIOs to output 0x0000 to decrease noise pickup +    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, 0x0000); +    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, 0xFFFF); +    this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, 0x0000);  } -void basic_tx::tx_set(const wax::obj &key_, const wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ - -    case SUBDEV_PROP_GAIN: -        UHD_ASSERT_THROW(val.as<double>() == double(0)); -        return; - -    case SUBDEV_PROP_ANTENNA: -        if (val.as<std::string>().empty()) return; -        throw uhd::value_error("no selectable antennas on this board"); - -    case SUBDEV_PROP_FREQ: -        return; // it wont do you much good, but you can set it - -    case SUBDEV_PROP_ENABLED: -        return; //always enabled - -    case SUBDEV_PROP_BANDWIDTH: -        UHD_MSG(warning) << boost::format( -            "%s: No tunable bandwidth, fixed filtered to %0.2fMHz" -        ) % get_tx_id().to_pp_string() % _max_freq; -        return; - -    default: UHD_THROW_PROP_SET_ERROR(); -    } +basic_tx::~basic_tx(void){ +    /* NOP */  } diff --git a/host/lib/usrp/dboard/db_dbsrx.cpp b/host/lib/usrp/dboard/db_dbsrx.cpp index c65c52590..7a90467ef 100644 --- a/host/lib/usrp/dboard/db_dbsrx.cpp +++ b/host/lib/usrp/dboard/db_dbsrx.cpp @@ -46,9 +46,12 @@ using namespace boost::assign;   **********************************************************************/  static const freq_range_t dbsrx_freq_range(0.8e9, 2.4e9); +//Multiplied by 2.0 for conversion to complex bandpass from lowpass +static const freq_range_t dbsrx_bandwidth_range(2.0*4.0e6, 2.0*33.0e6); +  static const freq_range_t dbsrx_pfd_freq_range(0.15e6, 2.01e6); -static const prop_names_t dbsrx_antennas = list_of("J3"); +static const std::vector<std::string> dbsrx_antennas = list_of("J3");  static const uhd::dict<std::string, gain_range_t> dbsrx_gain_ranges = map_list_of      ("GC1", gain_range_t(0, 56, 0.5)) @@ -63,9 +66,6 @@ public:      dbsrx(ctor_args_t args);      ~dbsrx(void); -    void rx_get(const wax::obj &key, wax::obj &val); -    void rx_set(const wax::obj &key, const wax::obj &val); -  private:      double _lo_freq;      double _bandwidth; @@ -76,9 +76,9 @@ private:          return (this->get_iface()->get_special_props().mangle_i2c_addrs)? 0x65 : 0x67;      }; -    void set_lo_freq(double target_freq); -    void set_gain(double gain, const std::string &name); -    void set_bandwidth(double bandwidth); +    double set_lo_freq(double target_freq); +    double set_gain(double gain, const std::string &name); +    double set_bandwidth(double bandwidth);      void send_reg(boost::uint8_t start_reg, boost::uint8_t stop_reg){          start_reg = boost::uint8_t(uhd::clip(int(start_reg), 0x0, 0x5)); @@ -136,10 +136,10 @@ private:      }      /*! -     * Is the LO locked? -     * \return true for locked +     * Get the lock detect status of the LO. +     * \return sensor for locked       */ -    bool get_locked(void){ +    sensor_value_t get_locked(void){          read_reg(0x0, 0x0);          //mask and return lock detect @@ -149,9 +149,8 @@ private:              "DBSRX: locked %d"          ) % locked << std::endl; -        return locked; +        return sensor_value_t("LO", locked, "locked", "unlocked");      } -  };  /*********************************************************************** @@ -190,30 +189,58 @@ dbsrx::dbsrx(ctor_args_t args) : rx_dboard_base(args){                  "Please see the daughterboard app notes"                   ) % this->get_rx_id().to_pp_string(); +    //send initial register settings +    this->send_reg(0x0, 0x5); + +    //set defaults for LO, gains, and filter bandwidth +    _bandwidth = 33e6; + +    //////////////////////////////////////////////////////////////////// +    // Register properties +    //////////////////////////////////////////////////////////////////// +    this->get_rx_subtree()->create<std::string>("name") +        .set(get_rx_id().to_pp_string()); +    this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") +        .publish(boost::bind(&dbsrx::get_locked, this)); +    BOOST_FOREACH(const std::string &name, dbsrx_gain_ranges.keys()){ +        this->get_rx_subtree()->create<double>("gains/"+name+"/value") +            .coerce(boost::bind(&dbsrx::set_gain, this, _1, name)) +            .set(dbsrx_gain_ranges[name].start()); +        this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range") +            .set(dbsrx_gain_ranges[name]); +    } +    this->get_rx_subtree()->create<double>("freq/value") +        .coerce(boost::bind(&dbsrx::set_lo_freq, this, _1)) +        .set(dbsrx_freq_range.start()); +    this->get_rx_subtree()->create<meta_range_t>("freq/range") +        .set(dbsrx_freq_range); +    this->get_rx_subtree()->create<std::string>("antenna/value") +        .set(dbsrx_antennas.at(0)); +    this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options") +        .set(dbsrx_antennas); +    this->get_rx_subtree()->create<std::string>("connection") +        .set("IQ"); +    this->get_rx_subtree()->create<bool>("enabled") +        .set(true); //always enabled +    this->get_rx_subtree()->create<bool>("use_lo_offset") +        .set(false); +    this->get_rx_subtree()->create<double>("bandwidth/value") +        .coerce(boost::bind(&dbsrx::set_bandwidth, this, _1)) +        .set(2.0*_bandwidth); //_bandwidth in lowpass, convert to complex bandpass +    this->get_rx_subtree()->create<meta_range_t>("bandwidth/range") +        .set(dbsrx_bandwidth_range); +      //enable only the clocks we need      this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true);      //set the gpio directions and atr controls (identically)      this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, 0x0); // All unused in atr      if (this->get_iface()->get_special_props().soft_clock_divider){ -        this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x1); // GPIO0 is clock +        this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x1); // GPIO0 is clock when on USRP1      }      else{          this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x0); // All Inputs      } - -    //send initial register settings -    this->send_reg(0x0, 0x5); - -    //set defaults for LO, gains, and filter bandwidth -    _bandwidth = 33e6; -    set_lo_freq(dbsrx_freq_range.start()); - -    BOOST_FOREACH(const std::string &name, dbsrx_gain_ranges.keys()){ -        set_gain(dbsrx_gain_ranges[name].start(), name); -    } - -    set_bandwidth(33e6); // default bandwidth from datasheet  }  dbsrx::~dbsrx(void){ @@ -223,7 +250,7 @@ dbsrx::~dbsrx(void){  /***********************************************************************   * Tuning   **********************************************************************/ -void dbsrx::set_lo_freq(double target_freq){ +double dbsrx::set_lo_freq(double target_freq){      target_freq = dbsrx_freq_range.clip(target_freq);      double actual_freq=0.0, pfd_freq=0.0, ref_clock=0.0; @@ -398,6 +425,8 @@ void dbsrx::set_lo_freq(double target_freq){      if (update_filter_settings) set_bandwidth(_bandwidth);      get_locked(); + +    return _lo_freq;  }  /*********************************************************************** @@ -456,7 +485,7 @@ static double gain_to_gc1_rfvga_dac(double &gain){      return dac_volts;  } -void dbsrx::set_gain(double gain, const std::string &name){ +double dbsrx::set_gain(double gain, const std::string &name){      assert_has(dbsrx_gain_ranges.keys(), name, "dbsrx gain name");      if (name == "GC2"){          _max2118_write_regs.gc2 = gain_to_gc2_vga_reg(gain); @@ -468,14 +497,19 @@ void dbsrx::set_gain(double gain, const std::string &name){      }      else UHD_THROW_INVALID_CODE_PATH();      _gains[name] = gain; + +    return gain;  }  /***********************************************************************   * Bandwidth Handling   **********************************************************************/ -void dbsrx::set_bandwidth(double bandwidth){ +double dbsrx::set_bandwidth(double bandwidth){ +    //convert complex bandpass to lowpass bandwidth +    bandwidth = bandwidth/2.0; +      //clip the input -    bandwidth = uhd::clip<double>(bandwidth, 4e6, 33e6); +    bandwidth = dbsrx_bandwidth_range.clip(bandwidth);      double ref_clock = this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX); @@ -492,109 +526,7 @@ void dbsrx::set_bandwidth(double bandwidth){      ) % (_bandwidth/1e6) % int(_max2118_write_regs.m_divider) % int(_max2118_write_regs.f_dac) << std::endl;      this->send_reg(0x3, 0x4); -} - -/*********************************************************************** - * RX Get and Set - **********************************************************************/ -void dbsrx::rx_get(const wax::obj &key_, wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ -    case SUBDEV_PROP_NAME: -        val = get_rx_id().to_pp_string(); -        return; - -    case SUBDEV_PROP_OTHERS: -        val = prop_names_t(); //empty -        return; - -    case SUBDEV_PROP_GAIN: -        assert_has(_gains.keys(), key.name, "dbsrx gain name"); -        val = _gains[key.name]; -        return; - -    case SUBDEV_PROP_GAIN_RANGE: -        assert_has(dbsrx_gain_ranges.keys(), key.name, "dbsrx gain name"); -        val = dbsrx_gain_ranges[key.name]; -        return; - -    case SUBDEV_PROP_GAIN_NAMES: -        val = prop_names_t(dbsrx_gain_ranges.keys()); -        return; - -    case SUBDEV_PROP_FREQ: -        val = _lo_freq; -        return; - -    case SUBDEV_PROP_FREQ_RANGE: -        val = dbsrx_freq_range; -        return; - -    case SUBDEV_PROP_ANTENNA: -        val = dbsrx_antennas.at(0); -        return; - -    case SUBDEV_PROP_ANTENNA_NAMES: -        val = dbsrx_antennas; -        return; - -    case SUBDEV_PROP_CONNECTION: -        val = SUBDEV_CONN_COMPLEX_IQ; -        return; - -    case SUBDEV_PROP_ENABLED: -        val = true; //always enabled -        return; - -    case SUBDEV_PROP_USE_LO_OFFSET: -        val = false; -        return; - -    case SUBDEV_PROP_SENSOR: -        UHD_ASSERT_THROW(key.name == "lo_locked"); -        val = sensor_value_t("LO", this->get_locked(), "locked", "unlocked"); -        return; - -    case SUBDEV_PROP_SENSOR_NAMES: -        val = prop_names_t(1, "lo_locked"); -        return; - -    case SUBDEV_PROP_BANDWIDTH: -        val = 2*_bandwidth; //_bandwidth is low-pass, we want complex double-sided -        return; - -    default: UHD_THROW_PROP_GET_ERROR(); -    } -} - -void dbsrx::rx_set(const wax::obj &key_, const wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ - -    case SUBDEV_PROP_FREQ: -        this->set_lo_freq(val.as<double>()); -        return; - -    case SUBDEV_PROP_ANTENNA: -        assert_has(dbsrx_antennas, val.as<std::string>(), "DBSRX antenna name"); -        return; -    case SUBDEV_PROP_GAIN: -        this->set_gain(val.as<double>(), key.name); -        return; - -    case SUBDEV_PROP_ENABLED: -        return; //always enabled - -    case SUBDEV_PROP_BANDWIDTH: -        this->set_bandwidth(val.as<double>()/2.0); //complex double-sided, we want low-pass -        return; - -    default: UHD_THROW_PROP_SET_ERROR(); -    } +    //convert lowpass back to complex bandpass bandwidth +    return 2.0*_bandwidth;  } - diff --git a/host/lib/usrp/dboard/db_dbsrx2.cpp b/host/lib/usrp/dboard/db_dbsrx2.cpp index f19236907..954d7083d 100644 --- a/host/lib/usrp/dboard/db_dbsrx2.cpp +++ b/host/lib/usrp/dboard/db_dbsrx2.cpp @@ -42,9 +42,12 @@ using namespace boost::assign;   **********************************************************************/  static const freq_range_t dbsrx2_freq_range(0.8e9, 2.4e9); +//Multiplied by 2.0 for conversion to complex bandpass from lowpass +static const freq_range_t dbsrx2_bandwidth_range(2.0*4.0e6, 2.0*40.0e6); +  static const int dbsrx2_ref_divider = 4; // Hitachi HMC426 divider (U7) -static const prop_names_t dbsrx2_antennas = list_of("J3"); +static const std::vector<std::string> dbsrx2_antennas = list_of("J3");  static const uhd::dict<std::string, gain_range_t> dbsrx2_gain_ranges = map_list_of      ("GC1", gain_range_t(0, 73, 0.05)) @@ -59,9 +62,6 @@ public:      dbsrx2(ctor_args_t args);      ~dbsrx2(void); -    void rx_get(const wax::obj &key, wax::obj &val); -    void rx_set(const wax::obj &key, const wax::obj &val); -  private:      double _lo_freq;      double _bandwidth; @@ -72,9 +72,9 @@ private:          return (this->get_iface()->get_special_props().mangle_i2c_addrs)? 0x60 : 0x61;      } -    void set_lo_freq(double target_freq); -    void set_gain(double gain, const std::string &name); -    void set_bandwidth(double bandwidth); +    double set_lo_freq(double target_freq); +    double set_gain(double gain, const std::string &name); +    double set_bandwidth(double bandwidth);      void send_reg(boost::uint8_t start_reg, boost::uint8_t stop_reg){          start_reg = boost::uint8_t(uhd::clip(int(start_reg), 0x0, 0xB)); @@ -146,10 +146,10 @@ private:      }      /*! -     * Is the LO locked? -     * \return true for locked +     * Get the lock detect status of the LO. +     * \return sensor for locked       */ -    bool get_locked(void){ +    sensor_value_t get_locked(void){          read_reg(0xC, 0xD);          //mask and return lock detect @@ -159,9 +159,8 @@ private:              "DBSRX2 locked: %d"          ) % locked << std::endl; -        return locked; +        return sensor_value_t("LO", locked, "locked", "unlocked");      } -  };  /*********************************************************************** @@ -182,28 +181,53 @@ UHD_STATIC_BLOCK(reg_dbsrx2_dboard){   * Structors   **********************************************************************/  dbsrx2::dbsrx2(ctor_args_t args) : rx_dboard_base(args){ -    //enable only the clocks we need -    this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true); - -    //set the gpio directions and atr controls (identically) -    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, 0x0); // All unused in atr -    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x0); // All Inputs -      //send initial register settings      send_reg(0x0, 0xB);      //for (boost::uint8_t addr=0; addr<=12; addr++) this->send_reg(addr, addr); -    //set defaults for LO, gains -    set_lo_freq(dbsrx2_freq_range.start()); +    //////////////////////////////////////////////////////////////////// +    // Register properties +    //////////////////////////////////////////////////////////////////// +    this->get_rx_subtree()->create<std::string>("name") +        .set(get_rx_id().to_pp_string()); +    this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") +        .publish(boost::bind(&dbsrx2::get_locked, this));      BOOST_FOREACH(const std::string &name, dbsrx2_gain_ranges.keys()){ -        set_gain(dbsrx2_gain_ranges[name].start(), name); +        this->get_rx_subtree()->create<double>("gains/"+name+"/value") +            .coerce(boost::bind(&dbsrx2::set_gain, this, _1, name)) +            .set(dbsrx2_gain_ranges[name].start()); +        this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range") +            .set(dbsrx2_gain_ranges[name]);      } +    this->get_rx_subtree()->create<double>("freq/value") +        .coerce(boost::bind(&dbsrx2::set_lo_freq, this, _1)) +        .set(dbsrx2_freq_range.start()); +    this->get_rx_subtree()->create<meta_range_t>("freq/range") +        .set(dbsrx2_freq_range); +    this->get_rx_subtree()->create<std::string>("antenna/value") +        .set(dbsrx2_antennas.at(0)); +    this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options") +        .set(dbsrx2_antennas); +    this->get_rx_subtree()->create<std::string>("connection") +        .set("QI"); +    this->get_rx_subtree()->create<bool>("enabled") +        .set(true); //always enabled +    this->get_rx_subtree()->create<bool>("use_lo_offset") +        .set(false); +    this->get_rx_subtree()->create<double>("bandwidth/value") +        .coerce(boost::bind(&dbsrx2::set_bandwidth, this, _1)) +        .set(2.0*40.0e6); //bandwidth in lowpass, convert to complex bandpass +    this->get_rx_subtree()->create<meta_range_t>("bandwidth/range") +        .set(dbsrx2_bandwidth_range); -    set_bandwidth(40e6); // default bandwidth from datasheet -    get_locked(); +    //enable only the clocks we need +    this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true); + +    //set the gpio directions and atr controls (identically) +    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, 0x0); // All unused in atr +    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x0); // All Inputs -    _max2112_write_regs.bbg = boost::math::iround(dbsrx2_gain_ranges["BBG"].start()); -    send_reg(0x9, 0x9); +    get_locked();  }  dbsrx2::~dbsrx2(void){ @@ -213,8 +237,8 @@ dbsrx2::~dbsrx2(void){  /***********************************************************************   * Tuning   **********************************************************************/ -void dbsrx2::set_lo_freq(double target_freq){ -    //target_freq = uhd::clip(target_freq, dbsrx2_freq_range.min, dbsrx2_freq_range.max); +double dbsrx2::set_lo_freq(double target_freq){ +    //target_freq = dbsrx2_freq_range.clip(target_freq);      //variables used in the calculation below      int scaler = target_freq > 1125e6 ? 2 : 4; @@ -257,6 +281,7 @@ void dbsrx2::set_lo_freq(double target_freq){      //FIXME: probably unnecessary to call get_locked here      //get_locked(); +    return _lo_freq;  }  /*********************************************************************** @@ -309,7 +334,7 @@ static double gain_to_gc1_rfvga_dac(double &gain){      return dac_volts;  } -void dbsrx2::set_gain(double gain, const std::string &name){ +double dbsrx2::set_gain(double gain, const std::string &name){      assert_has(dbsrx2_gain_ranges.keys(), name, "dbsrx2 gain name");      if (name == "BBG"){          _max2112_write_regs.bbg = gain_to_bbg_vga_reg(gain); @@ -321,14 +346,19 @@ void dbsrx2::set_gain(double gain, const std::string &name){      }      else UHD_THROW_INVALID_CODE_PATH();      _gains[name] = gain; + +    return gain;  }  /***********************************************************************   * Bandwidth Handling   **********************************************************************/ -void dbsrx2::set_bandwidth(double bandwidth){ +double dbsrx2::set_bandwidth(double bandwidth){ +    //convert complex bandpass to lowpass bandwidth +    bandwidth = bandwidth/2.0; +      //clip the input -    bandwidth = uhd::clip<double>(bandwidth, 4e6, 40e6); +    bandwidth = dbsrx2_bandwidth_range.clip(bandwidth);      _max2112_write_regs.lp = int((bandwidth/1e6 - 4)/0.29 + 12);      _bandwidth = double(4 + (_max2112_write_regs.lp - 12) * 0.29)*1e6; @@ -339,105 +369,7 @@ void dbsrx2::set_bandwidth(double bandwidth){          << std::endl;      this->send_reg(0x8, 0x8); -} -/*********************************************************************** - * RX Get and Set - **********************************************************************/ -void dbsrx2::rx_get(const wax::obj &key_, wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ -    case SUBDEV_PROP_NAME: -        val = get_rx_id().to_pp_string(); -        return; - -    case SUBDEV_PROP_OTHERS: -        val = prop_names_t(); //empty -        return; - -    case SUBDEV_PROP_GAIN: -        assert_has(_gains.keys(), key.name, "dbsrx2 gain name"); -        val = _gains[key.name]; -        return; - -    case SUBDEV_PROP_GAIN_RANGE: -        assert_has(dbsrx2_gain_ranges.keys(), key.name, "dbsrx2 gain name"); -        val = dbsrx2_gain_ranges[key.name]; -        return; - -    case SUBDEV_PROP_GAIN_NAMES: -        val = prop_names_t(dbsrx2_gain_ranges.keys()); -        return; - -    case SUBDEV_PROP_FREQ: -        val = _lo_freq; -        return; - -    case SUBDEV_PROP_FREQ_RANGE: -        val = dbsrx2_freq_range; -        return; - -    case SUBDEV_PROP_ANTENNA: -        val = std::string("J3"); -        return; - -    case SUBDEV_PROP_ANTENNA_NAMES: -        val = dbsrx2_antennas; -        return; - -    case SUBDEV_PROP_CONNECTION: -        val = SUBDEV_CONN_COMPLEX_QI; -        return; - -    case SUBDEV_PROP_ENABLED: -        val = true; //always enabled -        return; - -    case SUBDEV_PROP_USE_LO_OFFSET: -        val = false; -        return; - -    case SUBDEV_PROP_SENSOR: -        UHD_ASSERT_THROW(key.name == "lo_locked"); -        val = sensor_value_t("LO", this->get_locked(), "locked", "unlocked"); -        return; - -    case SUBDEV_PROP_SENSOR_NAMES: -        val = prop_names_t(1, "lo_locked"); -        return; - -    case SUBDEV_PROP_BANDWIDTH: -        val = _bandwidth; -        return; - -    default: UHD_THROW_PROP_GET_ERROR(); -    } +    //convert lowpass back to complex bandpass bandwidth +    return 2.0*_bandwidth;  } - -void dbsrx2::rx_set(const wax::obj &key_, const wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ - -    case SUBDEV_PROP_FREQ: -        this->set_lo_freq(val.as<double>()); -        return; - -    case SUBDEV_PROP_GAIN: -        this->set_gain(val.as<double>(), key.name); -        return; - -    case SUBDEV_PROP_ENABLED: -        return; //always enabled - -    case SUBDEV_PROP_BANDWIDTH: -        this->set_bandwidth(val.as<double>()); -        return; - -    default: UHD_THROW_PROP_SET_ERROR(); -    } -} - diff --git a/host/lib/usrp/dboard/db_rfx.cpp b/host/lib/usrp/dboard/db_rfx.cpp index 14129ef72..3896534cd 100644 --- a/host/lib/usrp/dboard/db_rfx.cpp +++ b/host/lib/usrp/dboard/db_rfx.cpp @@ -45,6 +45,7 @@  #include <uhd/usrp/dboard_base.hpp>  #include <uhd/usrp/dboard_manager.hpp>  #include <boost/assign/list_of.hpp> +#include <boost/bind.hpp>  #include <boost/format.hpp>  #include <boost/math/special_functions/round.hpp> @@ -55,11 +56,9 @@ using namespace boost::assign;  /***********************************************************************   * The RFX Series constants   **********************************************************************/ -static const prop_names_t rfx_tx_antennas = list_of("TX/RX"); +static const std::vector<std::string> rfx_tx_antennas = list_of("TX/RX"); -static const prop_names_t rfx_rx_antennas = list_of("TX/RX")("RX2"); - -static const uhd::dict<std::string, gain_range_t> rfx_tx_gain_ranges; //empty +static const std::vector<std::string> rfx_rx_antennas = list_of("TX/RX")("RX2");  static const uhd::dict<std::string, gain_range_t> rfx_rx_gain_ranges = map_list_of      ("PGA0", gain_range_t(0, 70, 0.022)) @@ -81,27 +80,17 @@ public:      );      ~rfx_xcvr(void); -    void rx_get(const wax::obj &key, wax::obj &val); -    void rx_set(const wax::obj &key, const wax::obj &val); - -    void tx_get(const wax::obj &key, wax::obj &val); -    void tx_set(const wax::obj &key, const wax::obj &val); -  private:      const freq_range_t _freq_range;      const uhd::dict<std::string, gain_range_t> _rx_gain_ranges;      const uhd::dict<dboard_iface::unit_t, bool> _div2; -    double       _rx_lo_freq, _tx_lo_freq;      std::string  _rx_ant;      uhd::dict<std::string, double> _rx_gains;      boost::uint16_t _power_up; -    void set_rx_lo_freq(double freq); -    void set_tx_lo_freq(double freq);      void set_rx_ant(const std::string &ant);      void set_tx_ant(const std::string &ant); -    void set_rx_gain(double gain, const std::string &name); -    void set_tx_gain(double gain, const std::string &name); +    double set_rx_gain(double gain, const std::string &name);      /*!       * Set the LO frequency for the particular dboard unit. @@ -114,17 +103,18 @@ private:      /*!       * Get the lock detect status of the LO.       * \param unit which unit rx or tx -     * \return true for locked +     * \return sensor for locked       */ -    bool get_locked(dboard_iface::unit_t unit){ -        return (this->get_iface()->read_gpio(unit) & LOCKDET_MASK) != 0; +    sensor_value_t get_locked(dboard_iface::unit_t unit){ +        const bool locked = (this->get_iface()->read_gpio(unit) & LOCKDET_MASK) != 0; +        return sensor_value_t("LO", locked, "locked", "unlocked");      }      /*!       * Read the RSSI from the aux adc -     * \return the rssi in dB +     * \return the rssi sensor in dBm       */ -    double get_rssi(void){ +    sensor_value_t get_rssi(void){          //RSSI from VAGC vs RF Power, Fig 34, pg 13          double max_power = -3.0; @@ -133,7 +123,8 @@ private:          static const double rssi_dyn_range = 60;          //calculate the rssi from the voltage          double voltage = this->get_iface()->read_aux_adc(dboard_iface::UNIT_RX, dboard_iface::AUX_ADC_B); -        return max_power - rssi_dyn_range*(voltage - min_v)/(max_v - min_v); +        const double rssi = max_power - rssi_dyn_range*(voltage - min_v)/(max_v - min_v); +        return sensor_value_t("RSSI", rssi, "dBm");      }  }; @@ -192,6 +183,59 @@ rfx_xcvr::rfx_xcvr(      ),      _power_up((get_rx_id() == 0x0024 && get_tx_id() == 0x0028) ? POWER_IO : 0)  { +    //////////////////////////////////////////////////////////////////// +    // Register RX properties +    //////////////////////////////////////////////////////////////////// +    this->get_rx_subtree()->create<std::string>("name").set("RFX RX"); +    this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") +        .publish(boost::bind(&rfx_xcvr::get_locked, this, dboard_iface::UNIT_RX)); +    this->get_rx_subtree()->create<sensor_value_t>("sensors/rssi") +        .publish(boost::bind(&rfx_xcvr::get_rssi, this)); +    BOOST_FOREACH(const std::string &name, _rx_gain_ranges.keys()){ +        this->get_rx_subtree()->create<double>("gains/"+name+"/value") +            .coerce(boost::bind(&rfx_xcvr::set_rx_gain, this, _1, name)) +            .set(_rx_gain_ranges[name].start()); +        this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range") +            .set(_rx_gain_ranges[name]); +    } +    this->get_rx_subtree()->create<double>("freq/value") +        .coerce(boost::bind(&rfx_xcvr::set_lo_freq, this, dboard_iface::UNIT_RX, _1)) +        .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(&rfx_xcvr::set_rx_ant, this, _1)) +        .set("RX2"); +    this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options") +        .set(rfx_rx_antennas); +    this->get_rx_subtree()->create<std::string>("connection").set("QI"); +    this->get_rx_subtree()->create<bool>("enabled").set(true); //always enabled +    this->get_rx_subtree()->create<bool>("use_lo_offset").set(false); +    this->get_rx_subtree()->create<double>("bandwidth/value").set(2*20.0e6); //20MHz low-pass, we want complex double-sided +    this->get_rx_subtree()->create<meta_range_t>("bandwidth/range") +        .set(freq_range_t(2*20.0e6, 2*20.0e6)); + +    //////////////////////////////////////////////////////////////////// +    // Register TX properties +    //////////////////////////////////////////////////////////////////// +    this->get_tx_subtree()->create<std::string>("name").set("RFX TX"); +    this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked") +        .publish(boost::bind(&rfx_xcvr::get_locked, this, dboard_iface::UNIT_TX)); +    this->get_tx_subtree()->create<int>("gains"); //phony property so this dir exists +    this->get_tx_subtree()->create<double>("freq/value") +        .coerce(boost::bind(&rfx_xcvr::set_lo_freq, this, dboard_iface::UNIT_TX, _1)) +        .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(&rfx_xcvr::set_tx_ant, this, _1)).set(rfx_tx_antennas.at(0)); +    this->get_tx_subtree()->create<std::vector<std::string> >("antenna/options") +        .set(rfx_tx_antennas); +    this->get_tx_subtree()->create<std::string>("connection").set("IQ"); +    this->get_tx_subtree()->create<bool>("enabled").set(true); //always enabled +    this->get_tx_subtree()->create<bool>("use_lo_offset").set(true); +    this->get_tx_subtree()->create<double>("bandwidth/value").set(2*20.0e6); //20MHz low-pass, we want complex double-sided +    this->get_tx_subtree()->create<meta_range_t>("bandwidth/range") +        .set(freq_range_t(2*20.0e6, 2*20.0e6)); +      //enable the clocks that we need      this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true);      this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true); @@ -213,15 +257,6 @@ rfx_xcvr::rfx_xcvr(      this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE,        _power_up | ANT_XX | MIXER_DIS);      this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY,     _power_up | ANT_XX | MIXER_DIS);      this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, _power_up | ANT_RX2| MIXER_ENB); - -    //set some default values -    set_rx_lo_freq((_freq_range.start() + _freq_range.stop())/2.0); -    set_tx_lo_freq((_freq_range.start() + _freq_range.stop())/2.0); -    set_rx_ant("RX2"); - -    BOOST_FOREACH(const std::string &name, _rx_gain_ranges.keys()){ -        set_rx_gain(_rx_gain_ranges[name].start(), name); -    }  }  rfx_xcvr::~rfx_xcvr(void){ @@ -267,20 +302,16 @@ static double rx_pga0_gain_to_dac_volts(double &gain, double range){      return dac_volts;  } -void rfx_xcvr::set_tx_gain(double, const std::string &name){ -    assert_has(rfx_tx_gain_ranges.keys(), name, "rfx tx gain name"); -    UHD_THROW_INVALID_CODE_PATH(); //no gains to set -} - -void rfx_xcvr::set_rx_gain(double gain, const std::string &name){ +double rfx_xcvr::set_rx_gain(double gain, const std::string &name){      assert_has(_rx_gain_ranges.keys(), name, "rfx rx gain name");      if(name == "PGA0"){          double dac_volts = rx_pga0_gain_to_dac_volts(gain,                                 (_rx_gain_ranges["PGA0"].stop() - _rx_gain_ranges["PGA0"].start())); -        _rx_gains[name] = gain;          //write the new voltage to the aux dac          this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, dboard_iface::AUX_DAC_A, dac_volts); + +        return gain;      }      else UHD_THROW_INVALID_CODE_PATH();  } @@ -288,14 +319,6 @@ void rfx_xcvr::set_rx_gain(double gain, const std::string &name){  /***********************************************************************   * Tuning   **********************************************************************/ -void rfx_xcvr::set_rx_lo_freq(double freq){ -    _rx_lo_freq = set_lo_freq(dboard_iface::UNIT_RX, freq); -} - -void rfx_xcvr::set_tx_lo_freq(double freq){ -    _tx_lo_freq = set_lo_freq(dboard_iface::UNIT_TX, freq); -} -  double rfx_xcvr::set_lo_freq(      dboard_iface::unit_t unit,      double target_freq @@ -408,214 +431,3 @@ double rfx_xcvr::set_lo_freq(      ) % (actual_freq/1e6) << std::endl;      return actual_freq;  } - -/*********************************************************************** - * RX Get and Set - **********************************************************************/ -void rfx_xcvr::rx_get(const wax::obj &key_, wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ -    case SUBDEV_PROP_NAME: -        val = get_rx_id().to_pp_string(); -        return; - -    case SUBDEV_PROP_OTHERS: -        val = prop_names_t(); //empty -        return; - -    case SUBDEV_PROP_GAIN: -        assert_has(_rx_gains.keys(), key.name, "rfx rx gain name"); -        val = _rx_gains[key.name]; -        return; - -    case SUBDEV_PROP_GAIN_RANGE: -        assert_has(_rx_gain_ranges.keys(), key.name, "rfx rx gain name"); -        val = _rx_gain_ranges[key.name]; -        return; - -    case SUBDEV_PROP_GAIN_NAMES: -        val = prop_names_t(_rx_gain_ranges.keys()); -        return; - -    case SUBDEV_PROP_FREQ: -        val = _rx_lo_freq; -        return; - -    case SUBDEV_PROP_FREQ_RANGE: -        val = _freq_range; -        return; - -    case SUBDEV_PROP_ANTENNA: -        val = _rx_ant; -        return; - -    case SUBDEV_PROP_ANTENNA_NAMES: -        val = rfx_rx_antennas; -        return; - -    case SUBDEV_PROP_CONNECTION: -        val = SUBDEV_CONN_COMPLEX_QI; -        return; - -    case SUBDEV_PROP_ENABLED: -        val = true; //always enabled -        return; - -    case SUBDEV_PROP_USE_LO_OFFSET: -        val = false; -        return; - -    case SUBDEV_PROP_SENSOR: -        if (key.name == "lo_locked") -            val = sensor_value_t("LO", this->get_locked(dboard_iface::UNIT_RX), "locked", "unlocked"); -        else if ((key.name == "rssi") and (get_rx_id() != 0x0024)) -            val = sensor_value_t("RSSI", this->get_rssi(), "dBm"); -        else -            UHD_THROW_INVALID_CODE_PATH(); -        return; - -    case SUBDEV_PROP_SENSOR_NAMES:{ -            prop_names_t names = list_of("lo_locked"); -            if (get_rx_id() != 0x0024) names.push_back("rssi"); -            val = names; -        } -        return; - -    case SUBDEV_PROP_BANDWIDTH: -        val = 2*20.0e6; //20MHz low-pass, we want complex double-sided -        return; - -    default: UHD_THROW_PROP_GET_ERROR(); -    } -} - -void rfx_xcvr::rx_set(const wax::obj &key_, const wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ - -    case SUBDEV_PROP_FREQ: -        this->set_rx_lo_freq(val.as<double>()); -        return; - -    case SUBDEV_PROP_GAIN: -        this->set_rx_gain(val.as<double>(), key.name); -        return; - -    case SUBDEV_PROP_ANTENNA: -        this->set_rx_ant(val.as<std::string>()); -        return; - -    case SUBDEV_PROP_ENABLED: -        return; //always enabled - -    case SUBDEV_PROP_BANDWIDTH: -        UHD_MSG(warning) << "RFX: No tunable bandwidth, fixed filtered to 40MHz"; -        return; - -    default: UHD_THROW_PROP_SET_ERROR(); -    } -} - -/*********************************************************************** - * TX Get and Set - **********************************************************************/ -void rfx_xcvr::tx_get(const wax::obj &key_, wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ -    case SUBDEV_PROP_NAME: -        val = get_tx_id().to_pp_string(); -        return; - -    case SUBDEV_PROP_OTHERS: -        val = prop_names_t(); //empty -        return; - -    case SUBDEV_PROP_GAIN: -    case SUBDEV_PROP_GAIN_RANGE: -        assert_has(rfx_tx_gain_ranges.keys(), key.name, "rfx tx gain name"); -        //no controllable tx gains, will not get here -        return; - -    case SUBDEV_PROP_GAIN_NAMES: -        val = prop_names_t(rfx_tx_gain_ranges.keys()); -        return; - -    case SUBDEV_PROP_FREQ: -        val = _tx_lo_freq; -        return; - -    case SUBDEV_PROP_FREQ_RANGE: -        val = _freq_range; -        return; - -    case SUBDEV_PROP_ANTENNA: -        val = std::string("TX/RX"); -        return; - -    case SUBDEV_PROP_ANTENNA_NAMES: -        val = rfx_tx_antennas; -        return; - -    case SUBDEV_PROP_CONNECTION: -        val = SUBDEV_CONN_COMPLEX_IQ; -        return; - -    case SUBDEV_PROP_ENABLED: -        val = true; //always enabled -        return; - -    case SUBDEV_PROP_USE_LO_OFFSET: -        val = true; -        return; - -    case SUBDEV_PROP_SENSOR: -        UHD_ASSERT_THROW(key.name == "lo_locked"); -        val = sensor_value_t("LO", this->get_locked(dboard_iface::UNIT_TX), "locked", "unlocked"); -        return; - -    case SUBDEV_PROP_SENSOR_NAMES: -        val = prop_names_t(1, "lo_locked"); -        return; - -    case SUBDEV_PROP_BANDWIDTH: -        val = 2*20.0e6; //20MHz low-pass, we want complex double-sided -        return; - -    default: UHD_THROW_PROP_GET_ERROR(); -    } -} - -void rfx_xcvr::tx_set(const wax::obj &key_, const wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ - -    case SUBDEV_PROP_FREQ: -        this->set_tx_lo_freq(val.as<double>()); -        return; - -    case SUBDEV_PROP_GAIN: -        this->set_tx_gain(val.as<double>(), key.name); -        return; - -    case SUBDEV_PROP_ANTENNA: -        this->set_tx_ant(val.as<std::string>()); -        return; - -    case SUBDEV_PROP_ENABLED: -        return; //always enabled - -    case SUBDEV_PROP_BANDWIDTH: -        UHD_MSG(warning) << "RFX: No tunable bandwidth, fixed filtered to 40MHz"; -        return; - -    default: UHD_THROW_PROP_SET_ERROR(); -    } -} diff --git a/host/lib/usrp/dboard/db_sbx.cpp b/host/lib/usrp/dboard/db_sbx.cpp deleted file mode 100644 index 40dbd286d..000000000 --- a/host/lib/usrp/dboard/db_sbx.cpp +++ /dev/null @@ -1,785 +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/>. -// - -// Common IO Pins -#define LO_LPF_EN       (1 << 15) -#define ADF4350_CE      (1 << 3) -#define ADF4350_PDBRF   (1 << 2) -#define ADF4350_MUXOUT  (1 << 1)                // INPUT!!! -#define LOCKDET_MASK    (1 << 0)                // INPUT!!! - -// TX IO Pins -#define TRSW            (1 << 14)               // 0 = TX, 1 = RX  -#define TX_LED_TXRX     (1 << 7)                // LED for TX Antenna Selection TX/RX -#define TX_LED_LD       (1 << 6)                // LED for TX Lock Detect -#define DIS_POWER_TX    (1 << 5)                // on UNIT_TX, 0 powers up TX -#define TX_ENABLE       (1 << 4)                // on UNIT_TX, 0 disables TX Mixer - -// RX IO Pins -#define LNASW           (1 << 14)               // 0 = TX/RX, 1 = RX2 -#define RX_LED_RX1RX2   (1 << 7)                // LED for RX Antenna Selection RX1/RX2 -#define RX_LED_LD       (1 << 6)                // LED for RX Lock Detect -#define DIS_POWER_RX    (1 << 5)                // on UNIT_RX, 0 powers up RX -#define RX_DISABLE      (1 << 4)                // on UNIT_RX, 1 disables RX Mixer and Baseband - -// RX Attenuator Pins -#define RX_ATTN_SHIFT   8                       // lsb of RX Attenuator Control -#define RX_ATTN_MASK    (63 << RX_ATTN_SHIFT)   // valid bits of RX Attenuator Control - -// TX Attenuator Pins -#define TX_ATTN_SHIFT   8                       // lsb of RX Attenuator Control -#define TX_ATTN_MASK    (63 << TX_ATTN_SHIFT)   // valid bits of RX Attenuator Control - -// Mixer functions -#define TX_MIXER_ENB    (ADF4350_PDBRF) -#define TX_MIXER_DIS    0 - -#define RX_MIXER_ENB    (ADF4350_PDBRF) -#define RX_MIXER_DIS    0 - -// Pin functions -#define TX_LED_IO       (TX_LED_TXRX|TX_LED_LD)     // LED gpio lines, pull down for LED -#define TXIO_MASK       (LO_LPF_EN|TRSW|ADF4350_CE|ADF4350_PDBRF|TX_ATTN_MASK|DIS_POWER_TX|TX_ENABLE) - -#define RX_LED_IO       (RX_LED_RX1RX2|RX_LED_LD)   // LED gpio lines, pull down for LED -#define RXIO_MASK       (LO_LPF_EN|LNASW|ADF4350_CE|ADF4350_PDBRF|RX_ATTN_MASK|DIS_POWER_RX|RX_DISABLE) - -// Power functions -#define TX_POWER_UP     (ADF4350_CE|TX_ENABLE) -#define TX_POWER_DOWN   (DIS_POWER_TX) - -#define RX_POWER_UP     (ADF4350_CE) -#define RX_POWER_DOWN   (DIS_POWER_RX) - -// Antenna constants -#define ANT_TX          TRSW                    //the tx line is transmitting -#define ANT_RX          0                       //the tx line is receiving -#define ANT_TXRX        0                       //the rx line is on txrx -#define ANT_RX2         LNASW                   //the rx line in on rx2 -#define ANT_XX          LNASW                   //dont care how the antenna is set - -#include "adf4350_regs.hpp" -#include <uhd/types/dict.hpp> -#include <uhd/types/ranges.hpp> -#include <uhd/types/sensors.hpp> -#include <uhd/utils/assert_has.hpp> -#include <uhd/utils/log.hpp> -#include <uhd/utils/static.hpp> -#include <uhd/utils/algorithm.hpp> -#include <uhd/utils/msg.hpp> -#include <uhd/usrp/dboard_base.hpp> -#include <uhd/usrp/dboard_manager.hpp> -#include <boost/assign/list_of.hpp> -#include <boost/format.hpp> -#include <boost/math/special_functions/round.hpp> -#include <boost/thread.hpp> - -using namespace uhd; -using namespace uhd::usrp; -using namespace boost::assign; - -/*********************************************************************** - * The SBX dboard constants - **********************************************************************/ -static const freq_range_t sbx_freq_range(400e6, 4.4e9); - -static const freq_range_t sbx_tx_lo_2dbm = list_of -    (range_t(0.35e9, 0.37e9)) -; - -static const freq_range_t sbx_enable_tx_lo_filter = list_of -    (range_t(0.4e9, 1.5e9)) -; - -static const freq_range_t sbx_enable_rx_lo_filter = list_of -    (range_t(0.4e9, 1.5e9)) -; - -static const prop_names_t sbx_tx_antennas = list_of("TX/RX"); - -static const prop_names_t sbx_rx_antennas = list_of("TX/RX")("RX2"); - -static const uhd::dict<std::string, gain_range_t> sbx_tx_gain_ranges = map_list_of -    ("PGA0", gain_range_t(0, 31.5, double(0.5))) -; - -static const uhd::dict<std::string, gain_range_t> sbx_rx_gain_ranges = map_list_of -    ("PGA0", gain_range_t(0, 31.5, double(0.5))) -; - -/*********************************************************************** - * The SBX dboard - **********************************************************************/ -class sbx_xcvr : public xcvr_dboard_base{ -public: -    sbx_xcvr(ctor_args_t args); -    ~sbx_xcvr(void); - -    void rx_get(const wax::obj &key, wax::obj &val); -    void rx_set(const wax::obj &key, const wax::obj &val); - -    void tx_get(const wax::obj &key, wax::obj &val); -    void tx_set(const wax::obj &key, const wax::obj &val); - -private: -    uhd::dict<std::string, double> _tx_gains, _rx_gains; -    double       _rx_lo_freq, _tx_lo_freq; -    std::string  _tx_ant, _rx_ant; - -    void set_rx_lo_freq(double freq); -    void set_tx_lo_freq(double freq); -    void set_rx_ant(const std::string &ant); -    void set_tx_ant(const std::string &ant); -    void set_rx_gain(double gain, const std::string &name); -    void set_tx_gain(double gain, const std::string &name); - -    void update_atr(void); - -    /*! -     * Set the LO frequency for the particular dboard unit. -     * \param unit which unit rx or tx -     * \param target_freq the desired frequency in Hz -     * \return the actual frequency in Hz -     */ -    double set_lo_freq(dboard_iface::unit_t unit, double target_freq); - -    /*! -     * Get the lock detect status of the LO. -     * \param unit which unit rx or tx -     * \return true for locked -     */ -    bool get_locked(dboard_iface::unit_t unit){ -        return (this->get_iface()->read_gpio(unit) & LOCKDET_MASK) != 0; -    } - -    /*! -     * Flash the LEDs -     */ -    void flash_leds(void) { -        //Remove LED gpios from ATR control temporarily and set to outputs -        this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, TXIO_MASK); -        this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, RXIO_MASK); -        this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|RX_LED_IO)); -        this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); - -        /* -        //flash All LEDs -        for (int i = 0; i < 3; i++) { -            this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_IO, RX_LED_IO); -            this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_IO, TX_LED_IO); - -            boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -            this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, 0, RX_LED_IO); -            this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, 0, TX_LED_IO); - -            boost::this_thread::sleep(boost::posix_time::milliseconds(100)); -        } -        */ - -        this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_LD, TX_LED_IO); -        boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -        this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_TXRX|TX_LED_LD, TX_LED_IO); -        boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -        this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_LD, RX_LED_IO); -        boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -        this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_RX1RX2|RX_LED_LD, RX_LED_IO); -        boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -        this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_LD, RX_LED_IO); -        boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -        this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, 0, RX_LED_IO); -        boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -        this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_LD, TX_LED_IO); -        boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -        this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, 0, TX_LED_IO); -        boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -        /* -        //flash All LEDs -        for (int i = 0; i < 3; i++) { -            this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, 0, RX_LED_IO); -            this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, 0, TX_LED_IO); - -            boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -            this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_IO, RX_LED_IO); -            this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_IO, TX_LED_IO); - -            boost::this_thread::sleep(boost::posix_time::milliseconds(100)); -        } -        */ -        //Put LED gpios back in ATR control and update atr -        this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO)); -        this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); -        this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO)); -        this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); -    } - -}; - -/*********************************************************************** - * Register the SBX dboard (min freq, max freq, rx div2, tx div2) - **********************************************************************/ -static dboard_base::sptr make_sbx(dboard_base::ctor_args_t args){ -    return dboard_base::sptr(new sbx_xcvr(args)); -} - -UHD_STATIC_BLOCK(reg_sbx_dboards){ -    dboard_manager::register_dboard(0x0054, 0x0055, &make_sbx, "SBX"); -} - -/*********************************************************************** - * Structors - **********************************************************************/ -sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){ - -    //enable the clocks that we need -    this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true); -    this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true); - -    //set the gpio directions and atr controls (identically) -    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO)); -    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); -    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO)); -    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); - -    //flash LEDs -    flash_leds(); - -    UHD_LOGV(often) << boost::format( -        "SBX GPIO Direction: RX: 0x%08x, TX: 0x%08x" -    ) % RXIO_MASK % TXIO_MASK << std::endl; - -    //set some default values -    set_rx_lo_freq((sbx_freq_range.start() + sbx_freq_range.stop())/2.0); -    set_tx_lo_freq((sbx_freq_range.start() + sbx_freq_range.stop())/2.0); -    set_rx_ant("RX2"); - -    BOOST_FOREACH(const std::string &name, sbx_tx_gain_ranges.keys()){ -        set_tx_gain(sbx_tx_gain_ranges[name].start(), name); -    } -    BOOST_FOREACH(const std::string &name, sbx_rx_gain_ranges.keys()){ -        set_rx_gain(sbx_rx_gain_ranges[name].start(), name); -    } -} - -sbx_xcvr::~sbx_xcvr(void){ -    /* NOP */ -} - -/*********************************************************************** - * Gain Handling - **********************************************************************/ -static int rx_pga0_gain_to_iobits(double &gain){ -    //clip the input -    gain = sbx_rx_gain_ranges["PGA0"].clip(gain); - -    //convert to attenuation and update iobits for atr -    double attn = sbx_rx_gain_ranges["PGA0"].stop() - gain; - -    //calculate the RX attenuation -    int attn_code = int(floor(attn*2)); -    int iobits = ((~attn_code) << RX_ATTN_SHIFT) & RX_ATTN_MASK; - -     -    UHD_LOGV(often) << boost::format( -        "SBX TX Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x" -    ) % attn % attn_code % (iobits & RX_ATTN_MASK) % RX_ATTN_MASK << std::endl; - -    //the actual gain setting -    gain = sbx_rx_gain_ranges["PGA0"].stop() - double(attn_code)/2; - -    return iobits; -} - -static int tx_pga0_gain_to_iobits(double &gain){ -    //clip the input -    gain = sbx_tx_gain_ranges["PGA0"].clip(gain); - -    //convert to attenuation and update iobits for atr -    double attn = sbx_tx_gain_ranges["PGA0"].stop() - gain; - -    //calculate the TX attenuation -    int attn_code = int(floor(attn*2)); -    int iobits = ((~attn_code) << TX_ATTN_SHIFT) & TX_ATTN_MASK; - -     -    UHD_LOGV(often) << boost::format( -        "SBX TX Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x" -    ) % attn % attn_code % (iobits & TX_ATTN_MASK) % TX_ATTN_MASK << std::endl; - -    //the actual gain setting -    gain = sbx_tx_gain_ranges["PGA0"].stop() - double(attn_code)/2; - -    return iobits; -} - -void sbx_xcvr::set_tx_gain(double gain, const std::string &name){ -    assert_has(sbx_tx_gain_ranges.keys(), name, "sbx tx gain name"); -    if(name == "PGA0"){ -        tx_pga0_gain_to_iobits(gain); -        _tx_gains[name] = gain; - -        //write the new gain to atr regs -        update_atr(); -    } -    else UHD_THROW_INVALID_CODE_PATH(); -} - -void sbx_xcvr::set_rx_gain(double gain, const std::string &name){ -    assert_has(sbx_rx_gain_ranges.keys(), name, "sbx rx gain name"); -    if(name == "PGA0"){ -        rx_pga0_gain_to_iobits(gain); -        _rx_gains[name] = gain; - -        //write the new gain to atr regs -        update_atr(); -    } -    else UHD_THROW_INVALID_CODE_PATH(); -} - -/*********************************************************************** - * Antenna Handling - **********************************************************************/ -void sbx_xcvr::update_atr(void){ -    //calculate atr pins -    int rx_pga0_iobits = rx_pga0_gain_to_iobits(_rx_gains["PGA0"]); -    int tx_pga0_iobits = tx_pga0_gain_to_iobits(_tx_gains["PGA0"]); -    int rx_lo_lpf_en = (_rx_lo_freq == sbx_enable_rx_lo_filter.clip(_rx_lo_freq)) ? LO_LPF_EN : 0; -    int tx_lo_lpf_en = (_tx_lo_freq == sbx_enable_tx_lo_filter.clip(_tx_lo_freq)) ? LO_LPF_EN : 0; -    int rx_ld_led = get_locked(dboard_iface::UNIT_RX) ? 0 : RX_LED_LD; -    int tx_ld_led = get_locked(dboard_iface::UNIT_TX) ? 0 : TX_LED_LD; -    int rx_ant_led = _rx_ant == "TX/RX" ? RX_LED_RX1RX2 : 0; -    int tx_ant_led = _rx_ant == "TX/RX" ? 0 : TX_LED_TXRX; - -    //setup the tx atr (this does not change with antenna) -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE, -        tx_pga0_iobits | tx_lo_lpf_en | tx_ld_led | tx_ant_led | TX_POWER_UP | ANT_XX | TX_MIXER_DIS); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY, -        tx_pga0_iobits | tx_lo_lpf_en | tx_ld_led | tx_ant_led | TX_POWER_UP | ANT_TX | TX_MIXER_ENB); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, -        tx_pga0_iobits | tx_lo_lpf_en | tx_ld_led | tx_ant_led | TX_POWER_UP | ANT_TX | TX_MIXER_ENB); - -    //setup the rx atr (this does not change with antenna) -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, -        rx_pga0_iobits | rx_lo_lpf_en | rx_ld_led | rx_ant_led | RX_POWER_UP | ANT_XX | RX_MIXER_DIS); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, -        rx_pga0_iobits | rx_lo_lpf_en | rx_ld_led | rx_ant_led | RX_POWER_UP | ANT_RX2 | RX_MIXER_DIS); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, -        rx_pga0_iobits | rx_lo_lpf_en | rx_ld_led | rx_ant_led | RX_POWER_UP | ANT_RX2 | RX_MIXER_ENB); - -    //set the atr regs that change with antenna setting -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY, -        tx_pga0_iobits | tx_lo_lpf_en | tx_ld_led | tx_ant_led | TX_POWER_UP | TX_MIXER_DIS | -            ((_rx_ant == "TX/RX")? ANT_RX : ANT_TX)); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, -        rx_pga0_iobits | rx_lo_lpf_en | rx_ld_led | rx_ant_led | RX_POWER_UP | RX_MIXER_ENB |  -            ((_rx_ant == "TX/RX")? ANT_TXRX : ANT_RX2)); - -    UHD_LOGV(often) << boost::format( -        "SBX RXONLY ATR REG: 0x%08x" -    ) % (rx_pga0_iobits | RX_POWER_UP | RX_MIXER_ENB | ((_rx_ant == "TX/RX")? ANT_TXRX : ANT_RX2)) << std::endl; -} - -void sbx_xcvr::set_rx_ant(const std::string &ant){ -    //validate input -    assert_has(sbx_rx_antennas, ant, "sbx rx antenna name"); - -    //shadow the setting -    _rx_ant = ant; - -    //write the new antenna setting to atr regs -    update_atr(); -} - -void sbx_xcvr::set_tx_ant(const std::string &ant){ -    assert_has(sbx_tx_antennas, ant, "sbx tx antenna name"); -    //only one antenna option, do nothing -} - -/*********************************************************************** - * Tuning - **********************************************************************/ -void sbx_xcvr::set_rx_lo_freq(double freq){ -    _rx_lo_freq = set_lo_freq(dboard_iface::UNIT_RX, freq); -} - -void sbx_xcvr::set_tx_lo_freq(double freq){ -    _tx_lo_freq = set_lo_freq(dboard_iface::UNIT_TX, freq); -} - -double sbx_xcvr::set_lo_freq( -    dboard_iface::unit_t unit, -    double target_freq -){ -    UHD_LOGV(often) << boost::format( -        "SBX tune: target frequency %f Mhz" -    ) % (target_freq/1e6) << std::endl; - -    //clip the input -    target_freq = sbx_freq_range.clip(target_freq); - -    //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) -    static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of -        (0,23) //adf4350_regs_t::PRESCALER_4_5 -        (1,75) //adf4350_regs_t::PRESCALER_8_9 -    ; - -    //map rf divider select output dividers to enums -    static const uhd::dict<int, adf4350_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of -        (1,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV1) -        (2,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV2) -        (4,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV4) -        (8,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV8) -        (16, adf4350_regs_t::RF_DIVIDER_SELECT_DIV16) -    ; - -    double actual_freq, pfd_freq; -    double ref_freq = this->get_iface()->get_clock_rate(unit); -    int R=0, BS=0, N=0, FRAC=0, MOD=0; -    int RFdiv = 1; -    adf4350_regs_t::reference_divide_by_2_t T     = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; -    adf4350_regs_t::reference_doubler_t     D     = adf4350_regs_t::REFERENCE_DOUBLER_DISABLED;     - -    //Reference doubler for 50% duty cycle -    // if ref_freq < 12.5MHz enable regs.reference_divide_by_2 -    if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED; - -    //increase RF divider until acceptable VCO frequency -    //start with target_freq*2 because mixer has divide by 2 -    double vco_freq = target_freq; -    while (vco_freq < 2.2e9) { -        vco_freq *= 2; -        RFdiv *= 2; -    } - -    //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) -    adf4350_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; - -    /* -     * The goal here is to loop though possible R dividers, -     * band select clock dividers, N (int) dividers, and FRAC  -     * (frac) dividers. -     * -     * Calculate the N and F dividers for each set of values. -     * The loop exists when it meets all of the constraints. -     * The resulting loop values are loaded into the registers. -     * -     * from pg.21 -     * -     * f_pfd = f_ref*(1+D)/(R*(1+T)) -     * f_vco = (N + (FRAC/MOD))*f_pfd -     *    N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD -     * f_rf = f_vco/RFdiv) -     * f_actual = f_rf/2 -     */ -    for(R = 1; R <= 1023; R+=1){ -        //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T) -        pfd_freq = ref_freq*(1+D)/(R*(1+T)); - -        //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth) -        if (pfd_freq > 25e6) continue; - -        //ignore fractional part of tuning -        N = int(std::floor(vco_freq/pfd_freq)); - -        //keep N > minimum int divider requirement -        if (N < prescaler_to_min_int_div[prescaler]) continue; - -        for(BS=1; BS <= 255; BS+=1){ -            //keep the band select frequency at or below 100KHz -            //constraint on band select clock -            if (pfd_freq/BS > 100e3) continue; -            goto done_loop; -        } -    } done_loop: - -    //Fractional-N calculation -    MOD = 4095; //max fractional accuracy -    FRAC = int((vco_freq/pfd_freq - N)*MOD); - -    //Reference divide-by-2 for 50% duty cycle -    // if R even, move one divide by 2 to to regs.reference_divide_by_2 -    if(R % 2 == 0){ -        T = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED; -        R /= 2; -    } - -    //actual frequency calculation -    actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv); - -    UHD_LOGV(often) -        << boost::format("SBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl -        << boost::format("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%d" -            ) % R % BS % N % FRAC % MOD % T % D % RFdiv % get_locked(unit)<< std::endl -        << boost::format("SBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" -            ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; - -    //load the register values -    adf4350_regs_t regs; - -    if ((unit == dboard_iface::UNIT_TX) and (actual_freq == sbx_tx_lo_2dbm.clip(actual_freq)))  -        regs.output_power = adf4350_regs_t::OUTPUT_POWER_2DBM; -    else -        regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM; - -    regs.frac_12_bit = FRAC; -    regs.int_16_bit = N; -    regs.mod_12_bit = MOD; -    regs.prescaler = prescaler; -    regs.r_counter_10_bit = R; -    regs.reference_divide_by_2 = T; -    regs.reference_doubler = D; -    regs.band_select_clock_div = BS; -    UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); -    regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; - -    //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( -            "SBX SPI Reg (0x%02x): 0x%08x" -        ) % addr % regs.get_reg(addr) << std::endl; -        this->get_iface()->write_spi( -            unit, spi_config_t::EDGE_RISE, -            regs.get_reg(addr), 32 -        ); -    } - -    //return the actual frequency -    UHD_LOGV(often) << boost::format( -        "SBX tune: actual frequency %f Mhz" -    ) % (actual_freq/1e6) << std::endl; -    return actual_freq; -} - -/*********************************************************************** - * RX Get and Set - **********************************************************************/ -void sbx_xcvr::rx_get(const wax::obj &key_, wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ -    case SUBDEV_PROP_NAME: -        val = get_rx_id().to_pp_string(); -        return; - -    case SUBDEV_PROP_OTHERS: -        val = prop_names_t(); //empty -        return; - -    case SUBDEV_PROP_GAIN: -        assert_has(_rx_gains.keys(), key.name, "sbx rx gain name"); -        val = _rx_gains[key.name]; -        return; - -    case SUBDEV_PROP_GAIN_RANGE: -        assert_has(sbx_rx_gain_ranges.keys(), key.name, "sbx rx gain name"); -        val = sbx_rx_gain_ranges[key.name]; -        return; - -    case SUBDEV_PROP_GAIN_NAMES: -        val = prop_names_t(sbx_rx_gain_ranges.keys()); -        return; - -    case SUBDEV_PROP_FREQ: -        val = _rx_lo_freq; -        return; - -    case SUBDEV_PROP_FREQ_RANGE: -        val = sbx_freq_range; -        return; - -    case SUBDEV_PROP_ANTENNA: -        val = _rx_ant; -        return; - -    case SUBDEV_PROP_ANTENNA_NAMES: -        val = sbx_rx_antennas; -        return; - -    case SUBDEV_PROP_CONNECTION: -        val = SUBDEV_CONN_COMPLEX_IQ; -        return; - -    case SUBDEV_PROP_USE_LO_OFFSET: -        val = false; -        return; - -    case SUBDEV_PROP_ENABLED: -        val = true; //always enabled -        return; - -    case SUBDEV_PROP_SENSOR: -        UHD_ASSERT_THROW(key.name == "lo_locked"); -        val = sensor_value_t("LO", this->get_locked(dboard_iface::UNIT_RX), "locked", "unlocked"); -        return; - -    case SUBDEV_PROP_SENSOR_NAMES: -        val = prop_names_t(1, "lo_locked"); -        return; - -    case SUBDEV_PROP_BANDWIDTH: -        val = 2*20.0e6; //20MHz low-pass, we want complex double-sided -        return; - -    default: UHD_THROW_PROP_GET_ERROR(); -    } -} - -void sbx_xcvr::rx_set(const wax::obj &key_, const wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ - -    case SUBDEV_PROP_FREQ: -        this->set_rx_lo_freq(val.as<double>()); -        return; - -    case SUBDEV_PROP_GAIN: -        this->set_rx_gain(val.as<double>(), key.name); -        return; - -    case SUBDEV_PROP_ANTENNA: -        this->set_rx_ant(val.as<std::string>()); -        return; - -    case SUBDEV_PROP_ENABLED: -        return; //always enabled - -    case SUBDEV_PROP_BANDWIDTH: -        UHD_MSG(warning) << "SBX: No tunable bandwidth, fixed filtered to 40MHz"; -        return; - -    default: UHD_THROW_PROP_SET_ERROR(); -    } -} - -/*********************************************************************** - * TX Get and Set - **********************************************************************/ -void sbx_xcvr::tx_get(const wax::obj &key_, wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ -    case SUBDEV_PROP_NAME: -        val = get_tx_id().to_pp_string(); -        return; - -    case SUBDEV_PROP_OTHERS: -        val = prop_names_t(); //empty -        return; - -    case SUBDEV_PROP_GAIN: -        assert_has(_tx_gains.keys(), key.name, "sbx tx gain name"); -        val = _tx_gains[key.name]; -        return; - -    case SUBDEV_PROP_GAIN_RANGE: -        assert_has(sbx_tx_gain_ranges.keys(), key.name, "sbx tx gain name"); -        val = sbx_tx_gain_ranges[key.name]; -        return; - -    case SUBDEV_PROP_GAIN_NAMES: -        val = prop_names_t(sbx_tx_gain_ranges.keys()); -        return; - -    case SUBDEV_PROP_FREQ: -        val = _tx_lo_freq; -        return; - -    case SUBDEV_PROP_FREQ_RANGE: -        val = sbx_freq_range; -        return; - -    case SUBDEV_PROP_ANTENNA: -        val = std::string("TX/RX"); -        return; - -    case SUBDEV_PROP_ANTENNA_NAMES: -        val = sbx_tx_antennas; -        return; - -    case SUBDEV_PROP_CONNECTION: -        val = SUBDEV_CONN_COMPLEX_QI; -        return; - -    case SUBDEV_PROP_USE_LO_OFFSET: -        val = false; -        return; - -    case SUBDEV_PROP_ENABLED: -        val = true; //always enabled -        return; - -    case SUBDEV_PROP_SENSOR: -        UHD_ASSERT_THROW(key.name == "lo_locked"); -        val = sensor_value_t("LO", this->get_locked(dboard_iface::UNIT_TX), "locked", "unlocked"); -        return; - -    case SUBDEV_PROP_SENSOR_NAMES: -        val = prop_names_t(1, "lo_locked"); -        return; - -    case SUBDEV_PROP_BANDWIDTH: -        val = 2*20.0e6; //20MHz low-pass, we want complex double-sided -        return; - -    default: UHD_THROW_PROP_GET_ERROR(); -    } -} - -void sbx_xcvr::tx_set(const wax::obj &key_, const wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ - -    case SUBDEV_PROP_FREQ: -        this->set_tx_lo_freq(val.as<double>()); -        return; - -    case SUBDEV_PROP_GAIN: -        this->set_tx_gain(val.as<double>(), key.name); -        return; - -    case SUBDEV_PROP_ANTENNA: -        this->set_tx_ant(val.as<std::string>()); -        return; - -    case SUBDEV_PROP_ENABLED: -        return; //always enabled - -    case SUBDEV_PROP_BANDWIDTH: -        UHD_MSG(warning) << "SBX: No tunable bandwidth, fixed filtered to 40MHz"; -        return; - -    default: UHD_THROW_PROP_SET_ERROR(); -    } -} diff --git a/host/lib/usrp/dboard/db_sbx_common.cpp b/host/lib/usrp/dboard/db_sbx_common.cpp new file mode 100644 index 000000000..27b930f81 --- /dev/null +++ b/host/lib/usrp/dboard/db_sbx_common.cpp @@ -0,0 +1,353 @@ +// +// 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/>. +// + +#include "db_sbx_common.hpp" +#include "adf4350_regs.hpp" + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign; + + +/*********************************************************************** + * Register the SBX dboard (min freq, max freq, rx div2, tx div2) + **********************************************************************/ +static dboard_base::sptr make_sbx(dboard_base::ctor_args_t args){ +    return dboard_base::sptr(new sbx_xcvr(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"); +} + + +/*********************************************************************** + * Gain Handling + **********************************************************************/ +static int rx_pga0_gain_to_iobits(double &gain){ +    //clip the input +    gain = sbx_rx_gain_ranges["PGA0"].clip(gain); + +    //convert to attenuation and update iobits for atr +    double attn = sbx_rx_gain_ranges["PGA0"].stop() - gain; + +    //calculate the RX attenuation +    int attn_code = int(floor(attn*2)); +    int iobits = ((~attn_code) << RX_ATTN_SHIFT) & RX_ATTN_MASK; + +    UHD_LOGV(often) << boost::format( +        "SBX TX Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x" +    ) % attn % attn_code % (iobits & RX_ATTN_MASK) % RX_ATTN_MASK << std::endl; + +    //the actual gain setting +    gain = sbx_rx_gain_ranges["PGA0"].stop() - double(attn_code)/2; + +    return iobits; +} + +static int tx_pga0_gain_to_iobits(double &gain){ +    //clip the input +    gain = sbx_tx_gain_ranges["PGA0"].clip(gain); + +    //convert to attenuation and update iobits for atr +    double attn = sbx_tx_gain_ranges["PGA0"].stop() - gain; + +    //calculate the TX attenuation +    int attn_code = int(floor(attn*2)); +    int iobits = ((~attn_code) << TX_ATTN_SHIFT) & TX_ATTN_MASK; + +    UHD_LOGV(often) << boost::format( +        "SBX TX Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x" +    ) % attn % attn_code % (iobits & TX_ATTN_MASK) % TX_ATTN_MASK << std::endl; + +    //the actual gain setting +    gain = sbx_tx_gain_ranges["PGA0"].stop() - double(attn_code)/2; + +    return iobits; +} + +double sbx_xcvr::set_tx_gain(double gain, const std::string &name){ +    assert_has(sbx_tx_gain_ranges.keys(), name, "sbx tx gain name"); +    if(name == "PGA0"){ +        tx_pga0_gain_to_iobits(gain); +        _tx_gains[name] = gain; + +        //write the new gain to atr regs +        update_atr(); +    } +    else UHD_THROW_INVALID_CODE_PATH(); +    return _tx_gains[name]; +} + +double sbx_xcvr::set_rx_gain(double gain, const std::string &name){ +    assert_has(sbx_rx_gain_ranges.keys(), name, "sbx rx gain name"); +    if(name == "PGA0"){ +        rx_pga0_gain_to_iobits(gain); +        _rx_gains[name] = gain; + +        //write the new gain to atr regs +        update_atr(); +    } +    else UHD_THROW_INVALID_CODE_PATH(); +    return _rx_gains[name]; +} + + +/*********************************************************************** + * Structors + **********************************************************************/ +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)); +            break; +        case 0x065: +            db_actual = sbx_versionx_sptr(new sbx_version4(this)); +            break; +        default: +            /* We didn't recognize the version of the board... */ +            UHD_THROW_INVALID_CODE_PATH(); +    } + +    //////////////////////////////////////////////////////////////////// +    // Register RX properties +    //////////////////////////////////////////////////////////////////// +    this->get_rx_subtree()->create<std::string>("name").set("SBX RX"); +    this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") +        .publish(boost::bind(&sbx_xcvr::get_locked, this, dboard_iface::UNIT_RX)); +    BOOST_FOREACH(const std::string &name, sbx_rx_gain_ranges.keys()){ +        this->get_rx_subtree()->create<double>("gains/"+name+"/value") +            .coerce(boost::bind(&sbx_xcvr::set_rx_gain, this, _1, name)) +            .set(sbx_rx_gain_ranges[name].start()); +        this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range") +            .set(sbx_rx_gain_ranges[name]); +    } +    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); +    this->get_rx_subtree()->create<std::string>("antenna/value") +        .subscribe(boost::bind(&sbx_xcvr::set_rx_ant, this, _1)) +        .set("RX2"); +    this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options") +        .set(sbx_rx_antennas); +    this->get_rx_subtree()->create<std::string>("connection").set("IQ"); +    this->get_rx_subtree()->create<bool>("enabled").set(true); //always enabled +    this->get_rx_subtree()->create<bool>("use_lo_offset").set(false); +    this->get_rx_subtree()->create<double>("bandwidth/value").set(2*20.0e6); //20MHz low-pass, we want complex double-sided +    this->get_rx_subtree()->create<meta_range_t>("bandwidth/range") +        .set(freq_range_t(2*20.0e6, 2*20.0e6)); + +    //////////////////////////////////////////////////////////////////// +    // Register TX properties +    //////////////////////////////////////////////////////////////////// +    this->get_tx_subtree()->create<std::string>("name").set("SBX TX"); +    this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked") +        .publish(boost::bind(&sbx_xcvr::get_locked, this, dboard_iface::UNIT_TX)); +    BOOST_FOREACH(const std::string &name, sbx_tx_gain_ranges.keys()){ +        this->get_tx_subtree()->create<double>("gains/"+name+"/value") +            .coerce(boost::bind(&sbx_xcvr::set_tx_gain, this, _1, name)) +            .set(sbx_tx_gain_ranges[name].start()); +        this->get_tx_subtree()->create<meta_range_t>("gains/"+name+"/range") +            .set(sbx_tx_gain_ranges[name]); +    } +    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); +    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)); +    this->get_tx_subtree()->create<std::vector<std::string> >("antenna/options") +        .set(sbx_tx_antennas); +    this->get_tx_subtree()->create<std::string>("connection").set("QI"); +    this->get_tx_subtree()->create<bool>("enabled").set(true); //always enabled +    this->get_tx_subtree()->create<bool>("use_lo_offset").set(false); +    this->get_tx_subtree()->create<double>("bandwidth/value").set(2*20.0e6); //20MHz low-pass, we want complex double-sided +    this->get_tx_subtree()->create<meta_range_t>("bandwidth/range") +        .set(freq_range_t(2*20.0e6, 2*20.0e6)); + +    //enable the clocks that we need +    this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true); +    this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true); + +    //set the gpio directions and atr controls (identically) +    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO)); +    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); +    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO)); +    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); + +    //flash LEDs +    flash_leds(); + +    UHD_LOGV(often) << boost::format( +        "SBX GPIO Direction: RX: 0x%08x, TX: 0x%08x" +    ) % RXIO_MASK % TXIO_MASK << std::endl; +} + +sbx_xcvr::~sbx_xcvr(void){ +    /* NOP */ +} + +/*********************************************************************** + * Antenna Handling + **********************************************************************/ +void sbx_xcvr::update_atr(void){ +    //calculate atr pins +    int rx_pga0_iobits = rx_pga0_gain_to_iobits(_rx_gains["PGA0"]); +    int tx_pga0_iobits = tx_pga0_gain_to_iobits(_tx_gains["PGA0"]); +    int rx_lo_lpf_en = (_rx_lo_freq == sbx_enable_rx_lo_filter.clip(_rx_lo_freq)) ? LO_LPF_EN : 0; +    int tx_lo_lpf_en = (_tx_lo_freq == sbx_enable_tx_lo_filter.clip(_tx_lo_freq)) ? LO_LPF_EN : 0; +    int rx_ld_led = get_locked(dboard_iface::UNIT_RX).to_bool() ? 0 : RX_LED_LD; +    int tx_ld_led = get_locked(dboard_iface::UNIT_TX).to_bool() ? 0 : TX_LED_LD; +    int rx_ant_led = _rx_ant == "TX/RX" ? RX_LED_RX1RX2 : 0; +    int tx_ant_led = _rx_ant == "TX/RX" ? 0 : TX_LED_TXRX; + +    //setup the tx atr (this does not change with antenna) +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE, +        tx_pga0_iobits | tx_lo_lpf_en | tx_ld_led | tx_ant_led | TX_POWER_UP | ANT_XX | TX_MIXER_DIS); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY, +        tx_pga0_iobits | tx_lo_lpf_en | tx_ld_led | tx_ant_led | TX_POWER_UP | ANT_TX | TX_MIXER_ENB); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, +        tx_pga0_iobits | tx_lo_lpf_en | tx_ld_led | tx_ant_led | TX_POWER_UP | ANT_TX | TX_MIXER_ENB); + +    //setup the rx atr (this does not change with antenna) +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, +        rx_pga0_iobits | rx_lo_lpf_en | rx_ld_led | rx_ant_led | RX_POWER_UP | ANT_XX | RX_MIXER_DIS); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, +        rx_pga0_iobits | rx_lo_lpf_en | rx_ld_led | rx_ant_led | RX_POWER_UP | ANT_RX2 | RX_MIXER_DIS); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, +        rx_pga0_iobits | rx_lo_lpf_en | rx_ld_led | rx_ant_led | RX_POWER_UP | ANT_RX2 | RX_MIXER_ENB); + +    //set the atr regs that change with antenna setting +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY, +        tx_pga0_iobits | tx_lo_lpf_en | tx_ld_led | tx_ant_led | TX_POWER_UP | TX_MIXER_DIS | +            ((_rx_ant == "TX/RX")? ANT_RX : ANT_TX)); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, +        rx_pga0_iobits | rx_lo_lpf_en | rx_ld_led | rx_ant_led | RX_POWER_UP | RX_MIXER_ENB |  +            ((_rx_ant == "TX/RX")? ANT_TXRX : ANT_RX2)); + +    UHD_LOGV(often) << boost::format( +        "SBX RXONLY ATR REG: 0x%08x" +    ) % (rx_pga0_iobits | RX_POWER_UP | RX_MIXER_ENB | ((_rx_ant == "TX/RX")? ANT_TXRX : ANT_RX2)) << std::endl; +} + +void sbx_xcvr::set_rx_ant(const std::string &ant){ +    //validate input +    assert_has(sbx_rx_antennas, ant, "sbx rx antenna name"); + +    //shadow the setting +    _rx_ant = ant; + +    //write the new antenna setting to atr regs +    update_atr(); +} + +void sbx_xcvr::set_tx_ant(const std::string &ant){ +    assert_has(sbx_tx_antennas, ant, "sbx tx antenna name"); +    //only one antenna option, do nothing +} + +/*********************************************************************** + * Tuning + **********************************************************************/ +void sbx_xcvr::set_rx_lo_freq(double freq){ +    _rx_lo_freq = db_actual->set_lo_freq(dboard_iface::UNIT_RX, freq); +} + +void sbx_xcvr::set_tx_lo_freq(double freq){ +    _tx_lo_freq = db_actual->set_lo_freq(dboard_iface::UNIT_TX, freq); +} + +double sbx_xcvr::set_lo_freq(dboard_iface::unit_t unit, double target_freq) { +    return db_actual->set_lo_freq(unit, target_freq); +} + +sensor_value_t sbx_xcvr::get_locked(dboard_iface::unit_t unit) { +    const bool locked = (this->get_iface()->read_gpio(unit) & LOCKDET_MASK) != 0; +    return sensor_value_t("LO", locked, "locked", "unlocked"); +} + + +void sbx_xcvr::flash_leds(void) { +    //Remove LED gpios from ATR control temporarily and set to outputs +    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, TXIO_MASK); +    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, RXIO_MASK); +    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|RX_LED_IO)); +    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); + +    /* +    //flash All LEDs +    for (int i = 0; i < 3; i++) { +        this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_IO, RX_LED_IO); +        this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_IO, TX_LED_IO); + +        boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + +        this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, 0, RX_LED_IO); +        this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, 0, TX_LED_IO); + +        boost::this_thread::sleep(boost::posix_time::milliseconds(100)); +    } +    */ + +    this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_LD, TX_LED_IO); +    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + +    this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_TXRX|TX_LED_LD, TX_LED_IO); +    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + +    this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_LD, RX_LED_IO); +    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + +    this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_RX1RX2|RX_LED_LD, RX_LED_IO); +    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + +    this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_LD, RX_LED_IO); +    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + +    this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, 0, RX_LED_IO); +    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + +    this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_LD, TX_LED_IO); +    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + +    this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, 0, TX_LED_IO); +    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + +    /* +    //flash All LEDs +    for (int i = 0; i < 3; i++) { +        this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, 0, RX_LED_IO); +        this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, 0, TX_LED_IO); + +        boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + +        this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_IO, RX_LED_IO); +        this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_IO, TX_LED_IO); + +        boost::this_thread::sleep(boost::posix_time::milliseconds(100)); +    } +    */ +    //Put LED gpios back in ATR control and update atr +    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO)); +    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); +    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO)); +    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); +} + diff --git a/host/lib/usrp/dboard/db_sbx_common.hpp b/host/lib/usrp/dboard/db_sbx_common.hpp new file mode 100644 index 000000000..c90cce456 --- /dev/null +++ b/host/lib/usrp/dboard/db_sbx_common.hpp @@ -0,0 +1,227 @@ +// +// 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/>. +// + + + +// Common IO Pins +#define LO_LPF_EN       (1 << 15) +#define SYNTH_CE        (1 << 3) +#define SYNTH_PDBRF     (1 << 2) +#define SYNTH_MUXOUT    (1 << 1)                // INPUT!!! +#define LOCKDET_MASK    (1 << 0)                // INPUT!!! + +// TX IO Pins +#define TRSW            (1 << 14)               // 0 = TX, 1 = RX  +#define TX_LED_TXRX     (1 << 7)                // LED for TX Antenna Selection TX/RX +#define TX_LED_LD       (1 << 6)                // LED for TX Lock Detect +#define DIS_POWER_TX    (1 << 5)                // on UNIT_TX, 0 powers up TX +#define TX_ENABLE       (1 << 4)                // on UNIT_TX, 0 disables TX Mixer + +// RX IO Pins +#define LNASW           (1 << 14)               // 0 = TX/RX, 1 = RX2 +#define RX_LED_RX1RX2   (1 << 7)                // LED for RX Antenna Selection RX1/RX2 +#define RX_LED_LD       (1 << 6)                // LED for RX Lock Detect +#define DIS_POWER_RX    (1 << 5)                // on UNIT_RX, 0 powers up RX +#define RX_DISABLE      (1 << 4)                // on UNIT_RX, 1 disables RX Mixer and Baseband + +// RX Attenuator Pins +#define RX_ATTN_SHIFT   8                       // lsb of RX Attenuator Control +#define RX_ATTN_MASK    (63 << RX_ATTN_SHIFT)   // valid bits of RX Attenuator Control + +// TX Attenuator Pins +#define TX_ATTN_SHIFT   8                       // lsb of RX Attenuator Control +#define TX_ATTN_MASK    (63 << TX_ATTN_SHIFT)   // valid bits of RX Attenuator Control + +// Mixer functions +#define TX_MIXER_ENB    (SYNTH_PDBRF) +#define TX_MIXER_DIS    0 + +#define RX_MIXER_ENB    (SYNTH_PDBRF) +#define RX_MIXER_DIS    0 + +// Pin functions +#define TX_LED_IO       (TX_LED_TXRX|TX_LED_LD)     // LED gpio lines, pull down for LED +#define TXIO_MASK       (LO_LPF_EN|TRSW|SYNTH_CE|SYNTH_PDBRF|TX_ATTN_MASK|DIS_POWER_TX|TX_ENABLE) + +#define RX_LED_IO       (RX_LED_RX1RX2|RX_LED_LD)   // LED gpio lines, pull down for LED +#define RXIO_MASK       (LO_LPF_EN|LNASW|SYNTH_CE|SYNTH_PDBRF|RX_ATTN_MASK|DIS_POWER_RX|RX_DISABLE) + +// Power functions +#define TX_POWER_UP     (SYNTH_CE|TX_ENABLE) +#define TX_POWER_DOWN   (DIS_POWER_TX) + +#define RX_POWER_UP     (SYNTH_CE) +#define RX_POWER_DOWN   (DIS_POWER_RX) + +// Antenna constants +#define ANT_TX          TRSW                    //the tx line is transmitting +#define ANT_RX          0                       //the tx line is receiving +#define ANT_TXRX        0                       //the rx line is on txrx +#define ANT_RX2         LNASW                   //the rx line in on rx2 +#define ANT_XX          LNASW                   //dont care how the antenna is set + + +#include <uhd/types/dict.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/types/sensors.hpp> +#include <uhd/utils/assert_has.hpp> +#include <uhd/utils/log.hpp> +#include <uhd/utils/static.hpp> +#include <uhd/utils/algorithm.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/usrp/dboard_base.hpp> +#include <uhd/usrp/dboard_manager.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/format.hpp> +#include <boost/math/special_functions/round.hpp> +#include <boost/thread.hpp> + + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign; + + +/*********************************************************************** + * The SBX dboard constants + **********************************************************************/ +static const freq_range_t sbx_freq_range(400e6, 4.4e9); + +static const freq_range_t sbx_tx_lo_2dbm = list_of +    (range_t(0.35e9, 0.37e9)) +; + +static const freq_range_t sbx_enable_tx_lo_filter = list_of +    (range_t(0.4e9, 1.5e9)) +; + +static const freq_range_t sbx_enable_rx_lo_filter = list_of +    (range_t(0.4e9, 1.5e9)) +; + +static const std::vector<std::string> sbx_tx_antennas = list_of("TX/RX"); + +static const std::vector<std::string> sbx_rx_antennas = list_of("TX/RX")("RX2"); + +static const uhd::dict<std::string, gain_range_t> sbx_tx_gain_ranges = map_list_of +    ("PGA0", gain_range_t(0, 31.5, double(0.5))) +; + +static const uhd::dict<std::string, gain_range_t> sbx_rx_gain_ranges = map_list_of +    ("PGA0", gain_range_t(0, 31.5, double(0.5))) +; + +/*********************************************************************** + * The SBX dboard + **********************************************************************/ +class sbx_xcvr : public xcvr_dboard_base{ +public: +    sbx_xcvr(ctor_args_t args); +    ~sbx_xcvr(void); + +protected: + +    uhd::dict<std::string, double> _tx_gains, _rx_gains; +    double       _rx_lo_freq, _tx_lo_freq; +    std::string  _tx_ant, _rx_ant; + +    void set_rx_lo_freq(double freq); +    void set_tx_lo_freq(double freq); +    void set_rx_ant(const std::string &ant); +    void set_tx_ant(const std::string &ant); +    double set_rx_gain(double gain, const std::string &name); +    double set_tx_gain(double gain, const std::string &name); + +    void update_atr(void); + +    /*! +     * Set the LO frequency for the particular dboard unit. +     * \param unit which unit rx or tx +     * \param target_freq the desired frequency in Hz +     * \return the actual frequency in Hz +     */ +    virtual double set_lo_freq(dboard_iface::unit_t unit, double target_freq); + +    /*! +     * Get the lock detect status of the LO. +     * \param unit which unit rx or tx +     * \return true for locked +     */ +    sensor_value_t get_locked(dboard_iface::unit_t unit); + +    /*! +     * Flash the LEDs +     */ +    void flash_leds(void); + +    /*! +     * Version-agnostic ABC that wraps version-specific implementations of the +     * WBX base daughterboard. +     * +     * This class is an abstract base class, and thus is impossible to +     * instantiate. +     */ +    class sbx_versionx { +    public: +        sbx_versionx() {} +        ~sbx_versionx(void) {} + +        virtual double set_lo_freq(dboard_iface::unit_t unit, double target_freq) = 0; +    }; + +    /*! +     * Version 3 of the SBX Daughterboard +     */ +    class sbx_version3 : public sbx_versionx { +    public: +        sbx_version3(sbx_xcvr *_self_sbx_xcvr); +        ~sbx_version3(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; +    }; + +    /*! +     * Version 4 of the SBX Daughterboard +     * +     * The only difference in the fourth revision is the ADF4351 vs the ADF4350. +     */ +    class sbx_version4 : public sbx_versionx { +    public: +        sbx_version4(sbx_xcvr *_self_sbx_xcvr); +        ~sbx_version4(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; +    }; + +    /*! +     * Handle to the version-specific implementation of the SBX. +     * +     * Since many of this class's functions are dependent on the version of the +     * SBX board, this class will instantiate an object of the appropriate +     * sbx_version* subclass, and invoke any relevant functions through that +     * object.  This pointer is set to the proper object at construction time. +     */ +    typedef boost::shared_ptr<sbx_versionx> sbx_versionx_sptr; +    sbx_versionx_sptr db_actual; +}; + diff --git a/host/lib/usrp/dboard/db_sbx_version3.cpp b/host/lib/usrp/dboard/db_sbx_version3.cpp new file mode 100644 index 000000000..6e20d5882 --- /dev/null +++ b/host/lib/usrp/dboard/db_sbx_version3.cpp @@ -0,0 +1,186 @@ +// +// 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/>. +// + + +#include "adf4350_regs.hpp" +#include "db_sbx_common.hpp" + + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign; + +/*********************************************************************** + * Structors + **********************************************************************/ +sbx_xcvr::sbx_version3::sbx_version3(sbx_xcvr *_self_sbx_xcvr) { +    //register the handle to our base SBX class +    self_base = _self_sbx_xcvr; +} + +sbx_xcvr::sbx_version3::~sbx_version3(void){ +    /* NOP */ +} + + +/*********************************************************************** + * Tuning + **********************************************************************/ +double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double target_freq) { +    UHD_LOGV(often) << boost::format( +        "SBX tune: target frequency %f Mhz" +    ) % (target_freq/1e6) << std::endl; + +    //clip the input +    target_freq = sbx_freq_range.clip(target_freq); + +    //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) +    static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of +        (0,23) //adf4350_regs_t::PRESCALER_4_5 +        (1,75) //adf4350_regs_t::PRESCALER_8_9 +    ; + +    //map rf divider select output dividers to enums +    static const uhd::dict<int, adf4350_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of +        (1,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV1) +        (2,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV2) +        (4,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV4) +        (8,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV8) +        (16, adf4350_regs_t::RF_DIVIDER_SELECT_DIV16) +    ; + +    double actual_freq, pfd_freq; +    double ref_freq = self_base->get_iface()->get_clock_rate(unit); +    int R=0, BS=0, N=0, FRAC=0, MOD=0; +    int RFdiv = 1; +    adf4350_regs_t::reference_divide_by_2_t T     = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; +    adf4350_regs_t::reference_doubler_t     D     = adf4350_regs_t::REFERENCE_DOUBLER_DISABLED;     + +    //Reference doubler for 50% duty cycle +    // if ref_freq < 12.5MHz enable regs.reference_divide_by_2 +    if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED; + +    //increase RF divider until acceptable VCO frequency +    //start with target_freq*2 because mixer has divide by 2 +    double vco_freq = target_freq; +    while (vco_freq < 2.2e9) { +        vco_freq *= 2; +        RFdiv *= 2; +    } + +    //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) +    adf4350_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; + +    /* +     * The goal here is to loop though possible R dividers, +     * band select clock dividers, N (int) dividers, and FRAC  +     * (frac) dividers. +     * +     * Calculate the N and F dividers for each set of values. +     * The loop exists when it meets all of the constraints. +     * The resulting loop values are loaded into the registers. +     * +     * from pg.21 +     * +     * f_pfd = f_ref*(1+D)/(R*(1+T)) +     * f_vco = (N + (FRAC/MOD))*f_pfd +     *    N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD +     * f_rf = f_vco/RFdiv) +     * f_actual = f_rf/2 +     */ +    for(R = 1; R <= 1023; R+=1){ +        //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T) +        pfd_freq = ref_freq*(1+D)/(R*(1+T)); + +        //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth) +        if (pfd_freq > 25e6) continue; + +        //ignore fractional part of tuning +        N = int(std::floor(vco_freq/pfd_freq)); + +        //keep N > minimum int divider requirement +        if (N < prescaler_to_min_int_div[prescaler]) continue; + +        for(BS=1; BS <= 255; BS+=1){ +            //keep the band select frequency at or below 100KHz +            //constraint on band select clock +            if (pfd_freq/BS > 100e3) continue; +            goto done_loop; +        } +    } done_loop: + +    //Fractional-N calculation +    MOD = 4095; //max fractional accuracy +    FRAC = int((vco_freq/pfd_freq - N)*MOD); + +    //Reference divide-by-2 for 50% duty cycle +    // if R even, move one divide by 2 to to regs.reference_divide_by_2 +    if(R % 2 == 0){ +        T = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED; +        R /= 2; +    } + +    //actual frequency calculation +    actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv); + +    UHD_LOGV(often) +        << boost::format("SBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl +        << boost::format("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s" +            ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl +        << boost::format("SBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" +            ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; + +    //load the register values +    adf4350_regs_t regs; + +    if ((unit == dboard_iface::UNIT_TX) and (actual_freq == sbx_tx_lo_2dbm.clip(actual_freq)))  +        regs.output_power = adf4350_regs_t::OUTPUT_POWER_2DBM; +    else +        regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM; + +    regs.frac_12_bit = FRAC; +    regs.int_16_bit = N; +    regs.mod_12_bit = MOD; +    regs.prescaler = prescaler; +    regs.r_counter_10_bit = R; +    regs.reference_divide_by_2 = T; +    regs.reference_doubler = D; +    regs.band_select_clock_div = BS; +    UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); +    regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; + +    //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( +            "SBX 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( +        "SBX tune: actual frequency %f Mhz" +    ) % (actual_freq/1e6) << std::endl; +    return actual_freq; +} + diff --git a/host/lib/usrp/dboard/db_sbx_version4.cpp b/host/lib/usrp/dboard/db_sbx_version4.cpp new file mode 100644 index 000000000..d22e83b51 --- /dev/null +++ b/host/lib/usrp/dboard/db_sbx_version4.cpp @@ -0,0 +1,189 @@ +// +// 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/>. +// + + +#include "adf4351_regs.hpp" +#include "db_sbx_common.hpp" + + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign; + +/*********************************************************************** + * Structors + **********************************************************************/ +sbx_xcvr::sbx_version4::sbx_version4(sbx_xcvr *_self_sbx_xcvr) { +    //register the handle to our base SBX class +    self_base = _self_sbx_xcvr; +} + + +sbx_xcvr::sbx_version4::~sbx_version4(void){ +    /* NOP */ +} + + +/*********************************************************************** + * Tuning + **********************************************************************/ +double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double target_freq) { +    UHD_LOGV(often) << boost::format( +        "SBX tune: target frequency %f Mhz" +    ) % (target_freq/1e6) << std::endl; + +    //clip the input +    target_freq = sbx_freq_range.clip(target_freq); + +    //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) +    static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of +        (0,23) //adf4351_regs_t::PRESCALER_4_5 +        (1,75) //adf4351_regs_t::PRESCALER_8_9 +    ; + +    //map rf divider select output dividers to enums +    static const uhd::dict<int, adf4351_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of +        (1,  adf4351_regs_t::RF_DIVIDER_SELECT_DIV1) +        (2,  adf4351_regs_t::RF_DIVIDER_SELECT_DIV2) +        (4,  adf4351_regs_t::RF_DIVIDER_SELECT_DIV4) +        (8,  adf4351_regs_t::RF_DIVIDER_SELECT_DIV8) +        (16, adf4351_regs_t::RF_DIVIDER_SELECT_DIV16) +        (32, adf4351_regs_t::RF_DIVIDER_SELECT_DIV16) +        (64, adf4351_regs_t::RF_DIVIDER_SELECT_DIV16) +    ; + +    double actual_freq, pfd_freq; +    double ref_freq = self_base->get_iface()->get_clock_rate(unit); +    int R=0, BS=0, N=0, FRAC=0, MOD=0; +    int RFdiv = 1; +    adf4351_regs_t::reference_divide_by_2_t T     = adf4351_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; +    adf4351_regs_t::reference_doubler_t     D     = adf4351_regs_t::REFERENCE_DOUBLER_DISABLED;     + +    //Reference doubler for 50% duty cycle +    // if ref_freq < 12.5MHz enable regs.reference_divide_by_2 +    if(ref_freq <= 12.5e6) D = adf4351_regs_t::REFERENCE_DOUBLER_ENABLED; + +    //increase RF divider until acceptable VCO frequency +    //start with target_freq*2 because mixer has divide by 2 +    double vco_freq = target_freq; +    while (vco_freq < 2.2e9) { +        vco_freq *= 2; +        RFdiv *= 2; +    } + +    //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) +    adf4351_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; + +    /* +     * The goal here is to loop though possible R dividers, +     * band select clock dividers, N (int) dividers, and FRAC  +     * (frac) dividers. +     * +     * Calculate the N and F dividers for each set of values. +     * The loop exists when it meets all of the constraints. +     * The resulting loop values are loaded into the registers. +     * +     * from pg.21 +     * +     * f_pfd = f_ref*(1+D)/(R*(1+T)) +     * f_vco = (N + (FRAC/MOD))*f_pfd +     *    N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD +     * f_rf = f_vco/RFdiv) +     * f_actual = f_rf/2 +     */ +    for(R = 1; R <= 1023; R+=1){ +        //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T) +        pfd_freq = ref_freq*(1+D)/(R*(1+T)); + +        //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth) +        if (pfd_freq > 25e6) continue; + +        //ignore fractional part of tuning +        N = int(std::floor(vco_freq/pfd_freq)); + +        //keep N > minimum int divider requirement +        if (N < prescaler_to_min_int_div[prescaler]) continue; + +        for(BS=1; BS <= 255; BS+=1){ +            //keep the band select frequency at or below 100KHz +            //constraint on band select clock +            if (pfd_freq/BS > 100e3) continue; +            goto done_loop; +        } +    } done_loop: + +    //Fractional-N calculation +    MOD = 4095; //max fractional accuracy +    FRAC = int((vco_freq/pfd_freq - N)*MOD); + +    //Reference divide-by-2 for 50% duty cycle +    // if R even, move one divide by 2 to to regs.reference_divide_by_2 +    if(R % 2 == 0){ +        T = adf4351_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED; +        R /= 2; +    } + +    //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("SBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl +        << boost::format("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s" +            ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl +        << boost::format("SBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" +            ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; + +    //load the register values +    adf4351_regs_t regs; + +    if ((unit == dboard_iface::UNIT_TX) and (actual_freq == sbx_tx_lo_2dbm.clip(actual_freq)))  +        regs.output_power = adf4351_regs_t::OUTPUT_POWER_2DBM; +    else +        regs.output_power = adf4351_regs_t::OUTPUT_POWER_5DBM; + +    regs.frac_12_bit = FRAC; +    regs.int_16_bit = N; +    regs.mod_12_bit = MOD; +    regs.prescaler = prescaler; +    regs.r_counter_10_bit = R; +    regs.reference_divide_by_2 = T; +    regs.reference_doubler = D; +    regs.band_select_clock_div = BS; +    UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); +    regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; + +    //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( +            "SBX 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( +        "SBX tune: actual frequency %f Mhz" +    ) % (actual_freq/1e6) << std::endl; +    return actual_freq; +} + diff --git a/host/lib/usrp/dboard/db_tvrx.cpp b/host/lib/usrp/dboard/db_tvrx.cpp index 907d798dd..dfa617e77 100644 --- a/host/lib/usrp/dboard/db_tvrx.cpp +++ b/host/lib/usrp/dboard/db_tvrx.cpp @@ -57,7 +57,7 @@ using namespace boost::assign;   **********************************************************************/  static const freq_range_t tvrx_freq_range(50e6, 860e6); -static const prop_names_t tvrx_antennas = list_of("RX"); +static const std::vector<std::string> tvrx_antennas = list_of("RX");  static const uhd::dict<std::string, freq_range_t> tvrx_freq_ranges = map_list_of      ("VHFLO", freq_range_t(50e6, 158e6)) @@ -136,9 +136,6 @@ public:      tvrx(ctor_args_t args);      ~tvrx(void); -    void rx_get(const wax::obj &key, wax::obj &val); -    void rx_set(const wax::obj &key, const wax::obj &val); -  private:      uhd::dict<std::string, double> _gains;      double _lo_freq; @@ -147,8 +144,8 @@ private:          return (this->get_iface()->get_special_props().mangle_i2c_addrs)? 0x61 : 0x60; //ok really? we could rename that call      }; -    void set_gain(double gain, const std::string &name); -    void set_freq(double freq); +    double set_gain(double gain, const std::string &name); +    double set_freq(double freq);      void update_regs(void){          byte_vector_t regs_vector(4); @@ -185,6 +182,39 @@ UHD_STATIC_BLOCK(reg_tvrx_dboard){   * Structors   **********************************************************************/  tvrx::tvrx(ctor_args_t args) : rx_dboard_base(args){ +    //////////////////////////////////////////////////////////////////// +    // Register properties +    //////////////////////////////////////////////////////////////////// +    this->get_rx_subtree()->create<std::string>("name") +        .set(get_rx_id().to_pp_string()); +    BOOST_FOREACH(const std::string &name, get_tvrx_gain_ranges().keys()){ +        this->get_rx_subtree()->create<double>("gains/"+name+"/value") +            .coerce(boost::bind(&tvrx::set_gain, this, _1, name)) +            .set(get_tvrx_gain_ranges()[name].start()); +        this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range") +            .set(get_tvrx_gain_ranges()[name]); +    } +    this->get_rx_subtree()->create<double>("freq/value") +        .coerce(boost::bind(&tvrx::set_freq, this, _1)) +        .set(tvrx_freq_range.start()); +    this->get_rx_subtree()->create<meta_range_t>("freq/range") +        .set(tvrx_freq_range); +    this->get_rx_subtree()->create<std::string>("antenna/value") +        .set(tvrx_antennas.at(0)); +    this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options") +        .set(tvrx_antennas); +    this->get_rx_subtree()->create<std::string>("connection") +        .set("I"); +    this->get_rx_subtree()->create<bool>("enabled") +        .set(true); //always enabled +    this->get_rx_subtree()->create<bool>("use_lo_offset") +        .set(false); +    this->get_rx_subtree()->create<double>("bandwidth/value") +        .set(6.0e6); +    this->get_rx_subtree()->create<meta_range_t>("bandwidth/range") +        .set(freq_range_t(6.0e6, 6.0e6)); + +    //enable only the clocks we need      this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true);      //set the gpio directions and atr controls (identically) @@ -317,7 +347,7 @@ static double if_gain_to_voltage(double gain){      return dac_volts;  } -void tvrx::set_gain(double gain, const std::string &name){ +double tvrx::set_gain(double gain, const std::string &name){      assert_has(get_tvrx_gain_ranges().keys(), name, "tvrx gain name");      if (name == "RF"){          this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, dboard_iface::AUX_DAC_B, rf_gain_to_voltage(gain, _lo_freq)); @@ -327,6 +357,8 @@ void tvrx::set_gain(double gain, const std::string &name){      }      else UHD_THROW_INVALID_CODE_PATH();      _gains[name] = gain; + +    return gain;  }  /*! @@ -334,7 +366,7 @@ void tvrx::set_gain(double gain, const std::string &name){   * \param freq the requested frequency   */ -void tvrx::set_freq(double freq) { +double tvrx::set_freq(double freq) {      freq = tvrx_freq_range.clip(freq);      std::string prev_band = get_band(_lo_freq - tvrx_if_freq);      std::string new_band = get_band(freq); @@ -367,131 +399,6 @@ void tvrx::set_freq(double freq) {      UHD_LOGV(often) << boost::format("set_freq: target LO: %f f_ref: %f divisor: %i actual LO: %f") % target_lo_freq % f_ref % divisor % actual_lo_freq << std::endl;      _lo_freq = actual_lo_freq; //for rx props -} - -/*********************************************************************** - * Get the alias frequency of frequency freq when sampled at fs. - * \param freq the frequency of interest - * \param fs the sample rate - * \return the alias frequency - **********************************************************************/ - -static double get_alias(double freq, double fs) { -    double alias; -    freq = fmod(freq, fs); -    if(freq >= (fs/2)) { -        alias = freq - fs; -    } else { -        alias = freq; -    } -    return alias; -} -/*********************************************************************** - * RX Get and Set - **********************************************************************/ -void tvrx::rx_get(const wax::obj &key_, wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); -    double codec_rate; - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ -    case SUBDEV_PROP_NAME: -        val = get_rx_id().to_pp_string(); -        return; - -    case SUBDEV_PROP_OTHERS: -        val = prop_names_t(); //empty -        return; - -    case SUBDEV_PROP_GAIN: -        assert_has(_gains.keys(), key.name, "tvrx gain name"); -        val = _gains[key.name]; -        return; - -    case SUBDEV_PROP_GAIN_RANGE: -        assert_has(get_tvrx_gain_ranges().keys(), key.name, "tvrx gain name"); -        val = get_tvrx_gain_ranges()[key.name]; -        return; - -    case SUBDEV_PROP_GAIN_NAMES: -        val = prop_names_t(get_tvrx_gain_ranges().keys()); -        return; - -    case SUBDEV_PROP_FREQ: -    /* -     * so here we have to do some magic. because the TVRX uses a relatively high IF, -     * we have to watch the sample rate to see if the IF will be aliased -     * or if it will fall within Nyquist. -     */ -        codec_rate = this->get_iface()->get_codec_rate(dboard_iface::UNIT_RX); -        val = (_lo_freq - tvrx_if_freq) + get_alias(tvrx_if_freq, codec_rate); -        UHD_LOGV(often) -            << "Getting TVRX freq..." << std::endl -            << "\tCodec rate: " << codec_rate << std::endl -            << "\tLO freq: " << _lo_freq << std::endl -            << "\tIF freq: " << tvrx_if_freq << std::endl -            << "\tAlias freq: " << get_alias(tvrx_if_freq, codec_rate) << std::endl -            << "\tCalculated freq: " << val.as<double>() << std::endl; -        return; - -    case SUBDEV_PROP_FREQ_RANGE: -        val = tvrx_freq_range; -        return; - -    case SUBDEV_PROP_ANTENNA: -        val = tvrx_antennas.front(); //there's only one -        return; - -    case SUBDEV_PROP_ANTENNA_NAMES: -        val = tvrx_antennas; -        return; - -    case SUBDEV_PROP_CONNECTION: -        val = SUBDEV_CONN_REAL_I; -        return; - -    case SUBDEV_PROP_ENABLED: -        val = true; //always enabled -        return; - -    case SUBDEV_PROP_USE_LO_OFFSET: -        val = false; -        return; - -    case SUBDEV_PROP_BANDWIDTH: -        val = 6.0e6; -        return; - -    case SUBDEV_PROP_SENSOR_NAMES: -        val = std::vector<std::string>(); //empty -        return; - -    default: UHD_THROW_PROP_GET_ERROR(); -    } -} - -void tvrx::rx_set(const wax::obj &key_, const wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ -    case SUBDEV_PROP_GAIN: -        this->set_gain(val.as<double>(), key.name); -        return; - -    case SUBDEV_PROP_FREQ: -        this->set_freq(val.as<double>()); -        return; - -    case SUBDEV_PROP_ENABLED: -        return; //always enabled - -    case SUBDEV_PROP_BANDWIDTH: -        UHD_MSG(warning) << "TVRX: No tunable bandwidth, fixed filtered to 6MHz"; -        return; - -    default: UHD_THROW_PROP_SET_ERROR(); -    } +    return _lo_freq;  } - diff --git a/host/lib/usrp/dboard/db_tvrx2.cpp b/host/lib/usrp/dboard/db_tvrx2.cpp index 23f203b8c..628221527 100644 --- a/host/lib/usrp/dboard/db_tvrx2.cpp +++ b/host/lib/usrp/dboard/db_tvrx2.cpp @@ -704,14 +704,22 @@ static const std::vector<tvrx2_tda18272_freq_map_t> tvrx2_tda18272_freq_map = li  static const freq_range_t tvrx2_freq_range(42e6, 870e6); +static const freq_range_t tvrx2_bandwidth_range = list_of +    (range_t(1.7e6)) +    (range_t(6.0e6)) +    (range_t(7.0e6)) +    (range_t(8.0e6)) +    (range_t(10.0e6)) +; +  static const uhd::dict<std::string, std::string> tvrx2_sd_name_to_antennas = map_list_of      ("RX1", "J100")      ("RX2", "J140")  ; -static const uhd::dict<std::string, subdev_conn_t> tvrx2_sd_name_to_conn = map_list_of -    ("RX1",  SUBDEV_CONN_REAL_Q) -    ("RX2",  SUBDEV_CONN_REAL_I) +static const uhd::dict<std::string, std::string> tvrx2_sd_name_to_conn = map_list_of +    ("RX1",  "Q") +    ("RX2",  "I")  ;  static const uhd::dict<std::string, boost::uint8_t> tvrx2_sd_name_to_i2c_addr = map_list_of @@ -745,9 +753,6 @@ public:      tvrx2(ctor_args_t args);      ~tvrx2(void); -    void rx_get(const wax::obj &key, wax::obj &val); -    void rx_set(const wax::obj &key, const wax::obj &val); -  private:      double _freq_scalar;      double _lo_freq; @@ -760,12 +765,11 @@ private:      bool _enabled; -    void set_enabled(void); -    void set_disabled(void); +    bool set_enabled(bool); -    void set_lo_freq(double target_freq); -    void set_gain(double gain, const std::string &name); -    void set_bandwidth(double bandwidth); +    double set_lo_freq(double target_freq); +    double set_gain(double gain, const std::string &name); +    double set_bandwidth(double bandwidth);      void set_scaled_rf_freq(double rf_freq);      double get_scaled_rf_freq(void); @@ -825,10 +829,10 @@ private:      }      /*! -     * Is the LO locked? -     * \return true for locked +     * Get the lock detect status of the LO. +     * \return sensor for locked       */ -    bool get_locked(void){ +    sensor_value_t get_locked(void){          read_reg(0x5, 0x5);          //return lock detect @@ -838,14 +842,15 @@ private:              "TVRX2 (%s): locked %d"          ) % (get_subdev_name()) % locked << std::endl; -        return locked; +        return sensor_value_t("LO", locked, "locked", "unlocked");      }      /*!       * Read the RSSI from the registers -     * \return the rssi in dB(m?) FIXME +     * Read the RSSI from the aux adc +     * \return the rssi sensor in dB(m?) FIXME       */ -    double get_rssi(void){ +    sensor_value_t get_rssi(void){          //Launch RSSI calculation with MSM statemachine          _tda18272hnm_regs.set_reg(0x19, 0x80); //set MSM_byte_1 for rssi calculation          _tda18272hnm_regs.set_reg(0x1A, 0x01); //set MSM_byte_2 for launching rssi calculation @@ -859,14 +864,16 @@ private:          //calculate the rssi from the voltage          double rssi_dBuV = 40.0 + double(((110.0 - 40.0)/128.0) * _tda18272hnm_regs.get_reg(0x7)); -        return rssi_dBuV - 107.0; //convert to dBm in 50ohm environment ( -108.8 if 75ohm ) FIXME +        double rssi =  rssi_dBuV - 107.0; //convert to dBm in 50ohm environment ( -108.8 if 75ohm ) FIXME + +        return sensor_value_t("RSSI", rssi, "dBm");      }      /*!       * Read the Temperature from the registers       * \return the temp in degC       */ -    double get_temp(void){ +    sensor_value_t get_temp(void){          //Enable Temperature reading          _tda18272hnm_regs.tm_on = tda18272hnm_regs_t::TM_ON_SENSOR_ON;          send_reg(0x4, 0x4); @@ -882,7 +889,7 @@ private:          _tda18272hnm_regs.tm_on = tda18272hnm_regs_t::TM_ON_SENSOR_OFF;          send_reg(0x4, 0x4); -        return (double(_tda18272hnm_regs.tm_d)); +        return sensor_value_t("TEMP", double(_tda18272hnm_regs.tm_d), "degC");      }  }; @@ -930,24 +937,64 @@ tvrx2::tvrx2(ctor_args_t args) : rx_dboard_base(args){          ( 7, tvrx2_tda18272_rfcal_coeffs_t(10) )      ; -    //set the gpio directions and atr controls (identically) -    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, 0); // All unused in atr -    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, OUTPUT_MASK); // Set outputs +    //set defaults for LO, gains, and filter bandwidth +    _bandwidth = 10e6; + +    _if_freq = 12.5e6; -    double ref_clock=0.0; +    _enabled = false; -    //configure ref_clock +    //send initial register settings +    //this->read_reg(0x0, 0x43); +    //this->send_reg(0x0, 0x43); -    /* -    std::vector<double> clock_rates = this->get_iface()->get_clock_rates(dboard_iface::UNIT_RX); -    BOOST_FOREACH(ref_clock, uhd::sorted(clock_rates)){ -        if (ref_clock < 16.0e6) continue;  -        if (ref_clock >= 16.0e6) break;  +    //send magic xtal_cal_dac setting +    send_reg(0x65, 0x65); + +    //////////////////////////////////////////////////////////////////// +    // Register properties +    //////////////////////////////////////////////////////////////////// +    this->get_rx_subtree()->create<std::string>("name") +        .set(get_rx_id().to_pp_string()); +    this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") +        .publish(boost::bind(&tvrx2::get_locked, this)); +    this->get_rx_subtree()->create<sensor_value_t>("sensors/rssi") +        .publish(boost::bind(&tvrx2::get_rssi, this)); +    this->get_rx_subtree()->create<sensor_value_t>("sensors/temperature") +        .publish(boost::bind(&tvrx2::get_temp, this)); +    BOOST_FOREACH(const std::string &name, tvrx2_gain_ranges.keys()){ +        this->get_rx_subtree()->create<double>("gains/"+name+"/value") +            .coerce(boost::bind(&tvrx2::set_gain, this, _1, name)); +        this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range") +            .set(tvrx2_gain_ranges[name]);      } -    this->get_iface()->set_clock_rate(dboard_iface::UNIT_RX, ref_clock); -    */ +    this->get_rx_subtree()->create<double>("freq/value") +        .coerce(boost::bind(&tvrx2::set_lo_freq, this, _1)); +    this->get_rx_subtree()->create<meta_range_t>("freq/range") +        .set(tvrx2_freq_range); +    this->get_rx_subtree()->create<std::string>("antenna/value") +        .set(tvrx2_sd_name_to_antennas[get_subdev_name()]); +    this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options") +        .set(list_of(tvrx2_sd_name_to_antennas[get_subdev_name()])); +    this->get_rx_subtree()->create<std::string>("connection") +        .set(tvrx2_sd_name_to_conn[get_subdev_name()]); +    this->get_rx_subtree()->create<bool>("enabled") +        .coerce(boost::bind(&tvrx2::set_enabled, this, _1)) +        .set(_enabled); +    this->get_rx_subtree()->create<bool>("use_lo_offset") +        .set(false); +    this->get_rx_subtree()->create<double>("bandwidth/value") +        .coerce(boost::bind(&tvrx2::set_bandwidth, this, _1)) +        .set(_bandwidth); +    this->get_rx_subtree()->create<meta_range_t>("bandwidth/range") +        .set(tvrx2_bandwidth_range); -    ref_clock = this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX); +    //set the gpio directions and atr controls (identically) +    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, 0); // All unused in atr +    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, OUTPUT_MASK); // Set outputs + +    //configure ref_clock +    double ref_clock = this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX);      if (ref_clock == 64.0e6) {          this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, REFCLOCK_DIV4); @@ -978,22 +1025,6 @@ tvrx2::tvrx2(ctor_args_t args) : rx_dboard_base(args){          "TVRX2 (%s): Refclock %f Hz, scalar = %f"      ) % (get_subdev_name()) % (this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX)) % _freq_scalar << std::endl; -    //set defaults for LO, gains, and filter bandwidth -    _bandwidth = 10e6; - -    _if_freq = 12.5e6; - -    _lo_freq = tvrx2_freq_range.start(); - -    _enabled = false; - -    //send initial register settings -    //this->read_reg(0x0, 0x43); -    //this->send_reg(0x0, 0x43); - -    //send magic xtal_cal_dac setting -    send_reg(0x65, 0x65); -      _tda18272hnm_regs.irq_polarity = tda18272hnm_regs_t::IRQ_POLARITY_RAISED_VCC;      _tda18272hnm_regs.irq_clear = tda18272hnm_regs_t::IRQ_CLEAR_TRUE;      send_reg(0x37, 0x37); @@ -1013,39 +1044,47 @@ tvrx2::tvrx2(ctor_args_t args) : rx_dboard_base(args){      transition_0();  } -void tvrx2::set_enabled(void){ -    //setup tuner parameters -    transition_1(); +bool tvrx2::set_enabled(bool enable){ +    if (enable == _enabled) return _enabled; -    transition_2(int(tvrx2_freq_range.start())); +    if (enable and not _enabled){ +        //setup tuner parameters +        transition_1(); -    test_rf_filter_robustness(); +        transition_2(int(tvrx2_freq_range.start())); -    BOOST_FOREACH(const std::string &name, tvrx2_gain_ranges.keys()){ -        set_gain(tvrx2_gain_ranges[name].start(), name); -    } +        test_rf_filter_robustness(); + +        BOOST_FOREACH(const std::string &name, tvrx2_gain_ranges.keys()){ +            this->get_rx_subtree()->access<double>("gains/"+name+"/value") +                .set(tvrx2_gain_ranges[name].start()); +        } + +        this->get_rx_subtree()->access<double>("bandwidth/value") +            .set(_bandwidth); // default bandwidth from datasheet + +        //transition_2 equivalent +        this->get_rx_subtree()->access<double>("freq/value") +            .set(tvrx2_freq_range.start()); -    set_bandwidth(_bandwidth); // default bandwidth from datasheet +        //enter standby mode +        transition_3(); +        _enabled = true; -    //transition_2 equivalent -    set_lo_freq(tvrx2_freq_range.start()); +    } else { +        //enter standby mode +        transition_3(); +        _enabled = false; +    } -    //enter standby mode -    transition_3(); -    _enabled = true; +    return _enabled;  }  tvrx2::~tvrx2(void){      UHD_LOGV(often) << boost::format(          "TVRX2 (%s): Called Destructor"      ) % (get_subdev_name()) << std::endl; -    UHD_SAFE_CALL(if (_enabled) set_disabled();) -} - -void tvrx2::set_disabled(void){ -    //enter standby mode -    transition_3(); -    _enabled = false; +    UHD_SAFE_CALL(if (_enabled) set_enabled(false);)  } @@ -1682,7 +1721,7 @@ void tvrx2::wait_irq(void){  /***********************************************************************   * Tuning   **********************************************************************/ -void tvrx2::set_lo_freq(double target_freq){ +double tvrx2::set_lo_freq(double target_freq){      //target_freq = std::clip(target_freq, tvrx2_freq_range.min, tvrx2_freq_range.max);      read_reg(0x6, 0x6); @@ -1711,7 +1750,9 @@ void tvrx2::set_lo_freq(double target_freq){      UHD_LOGV(often) << boost::format(          "\nTVRX2 (%s): RSSI = %f dBm\n" -    ) % (get_subdev_name()) % (get_rssi()) << std::endl; +    ) % (get_subdev_name()) % (get_rssi().to_real()) << std::endl; + +    return _lo_freq;  }  /*********************************************************************** @@ -1741,7 +1782,7 @@ static double gain_to_if_gain_dac(double &gain){      return dac_volts;  } -void tvrx2::set_gain(double gain, const std::string &name){ +double tvrx2::set_gain(double gain, const std::string &name){      assert_has(tvrx2_gain_ranges.keys(), name, "tvrx2 gain name");      if (name == "IF"){ @@ -1752,6 +1793,8 @@ void tvrx2::set_gain(double gain, const std::string &name){      //shadow gain setting      _gains[name] = gain; + +    return gain;  }  /*********************************************************************** @@ -1780,7 +1823,10 @@ static tda18272hnm_regs_t::lp_fc_t bandwidth_to_lp_fc_reg(double &bandwidth){      UHD_THROW_INVALID_CODE_PATH();  } -void tvrx2::set_bandwidth(double bandwidth){ +double tvrx2::set_bandwidth(double bandwidth){ +    //clip the input +    bandwidth = tvrx2_bandwidth_range.clip(bandwidth); +      //compute low pass cutoff frequency setting      _tda18272hnm_regs.lp_fc = bandwidth_to_lp_fc_reg(bandwidth); @@ -1793,119 +1839,6 @@ void tvrx2::set_bandwidth(double bandwidth){      UHD_LOGV(often) << boost::format(          "TVRX2 (%s) Bandwidth (lp_fc): %f Hz, reg: %d"      ) % (get_subdev_name()) % _bandwidth % (int(_tda18272hnm_regs.lp_fc)) << std::endl; -} - -/*********************************************************************** - * RX Get and Set - **********************************************************************/ -void tvrx2::rx_get(const wax::obj &key_, wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ -    case SUBDEV_PROP_NAME: -        val = get_rx_id().to_pp_string(); -        return; - -    case SUBDEV_PROP_OTHERS: -        val = prop_names_t(); //empty -        return; - -    case SUBDEV_PROP_GAIN: -        assert_has(_gains.keys(), key.name, "tvrx2 gain name"); -        val = _gains[key.name]; -        return; - -    case SUBDEV_PROP_GAIN_RANGE: -        assert_has(tvrx2_gain_ranges.keys(), key.name, "tvrx2 gain name"); -        val = tvrx2_gain_ranges[key.name]; -        return; - -    case SUBDEV_PROP_GAIN_NAMES: -        val = prop_names_t(tvrx2_gain_ranges.keys()); -        return; - -    case SUBDEV_PROP_FREQ: -        val = _lo_freq; -        return; - -    case SUBDEV_PROP_FREQ_RANGE: -        val = tvrx2_freq_range; -        return; - -    case SUBDEV_PROP_ANTENNA: -        val = tvrx2_sd_name_to_antennas[get_subdev_name()]; -        return; - -    case SUBDEV_PROP_ANTENNA_NAMES: -        val = prop_names_t(1, tvrx2_sd_name_to_antennas[get_subdev_name()]); -        return; - -    case SUBDEV_PROP_CONNECTION: -        val = tvrx2_sd_name_to_conn[get_subdev_name()]; -        return; - -    case SUBDEV_PROP_ENABLED: -        val = _enabled; -        return; - -    case SUBDEV_PROP_USE_LO_OFFSET: -        val = false; -        return; - -    case SUBDEV_PROP_SENSOR: -        if (key.name == "lo_locked") -            val = sensor_value_t("LO", this->get_locked(), "locked", "unlocked"); -        else if (key.name == "rssi") -            val = sensor_value_t("RSSI", this->get_rssi(), "dBm"); -        else if (key.name == "temperature") -            val = sensor_value_t("TEMP", this->get_temp(), "degC"); -        else -            UHD_THROW_INVALID_CODE_PATH(); -        return; - -    case SUBDEV_PROP_SENSOR_NAMES:{ -            prop_names_t names = list_of("lo_locked")("rssi")("temperature"); -            val = names; -        } -        return; -    case SUBDEV_PROP_BANDWIDTH: -        val = _bandwidth; -        return; - -    default: UHD_THROW_PROP_GET_ERROR(); -    } +    return _bandwidth;  } - -void tvrx2::rx_set(const wax::obj &key_, const wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ - -    case SUBDEV_PROP_FREQ: -        this->set_lo_freq(val.as<double>()); -        return; - -    case SUBDEV_PROP_GAIN: -        this->set_gain( val.as<double>(), key.name); -        return; - -    case SUBDEV_PROP_ANTENNA: -        return; - -    case SUBDEV_PROP_ENABLED: -        if ((val.as<bool>())) this->set_enabled(); -        else if (not (val.as<bool>())) this->set_disabled(); - -        return; - -    case SUBDEV_PROP_BANDWIDTH: -        this->set_bandwidth(val.as<double>()); -        return; - -    default: UHD_THROW_PROP_SET_ERROR(); -    } -} - diff --git a/host/lib/usrp/dboard/db_unknown.cpp b/host/lib/usrp/dboard/db_unknown.cpp index 3a11f1e5b..2ed50cd91 100644 --- a/host/lib/usrp/dboard/db_unknown.cpp +++ b/host/lib/usrp/dboard/db_unknown.cpp @@ -64,17 +64,11 @@ static void warn_if_old_rfx(const dboard_id_t &dboard_id, const std::string &xx)  class unknown_rx : public rx_dboard_base{  public:      unknown_rx(ctor_args_t args); - -    void rx_get(const wax::obj &key, wax::obj &val); -    void rx_set(const wax::obj &key, const wax::obj &val);  };  class unknown_tx : public tx_dboard_base{  public:      unknown_tx(ctor_args_t args); - -    void tx_get(const wax::obj &key, wax::obj &val); -    void tx_set(const wax::obj &key, const wax::obj &val);  };  /*********************************************************************** @@ -98,99 +92,35 @@ UHD_STATIC_BLOCK(reg_unknown_dboards){   **********************************************************************/  unknown_rx::unknown_rx(ctor_args_t args) : rx_dboard_base(args){      warn_if_old_rfx(this->get_rx_id(), "RX"); -} - -void unknown_rx::rx_get(const wax::obj &key_, wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ -    case SUBDEV_PROP_NAME: -        val = "Unknown - " + get_rx_id().to_pp_string(); -        return; - -    case SUBDEV_PROP_OTHERS: -        val = prop_names_t(); //empty -        return; - -    case SUBDEV_PROP_GAIN: -        val = double(0); -        return; - -    case SUBDEV_PROP_GAIN_RANGE: -        val = gain_range_t(0, 0, 0); -        return; - -    case SUBDEV_PROP_GAIN_NAMES: -        val = prop_names_t(); //empty -        return; - -    case SUBDEV_PROP_FREQ: -        val = double(0); -        return; - -    case SUBDEV_PROP_FREQ_RANGE: -        val = freq_range_t(0.0, 0.0); -        return; - -    case SUBDEV_PROP_ANTENNA: -        val = std::string(""); -        return; - -    case SUBDEV_PROP_ANTENNA_NAMES: -        val = prop_names_t(1, ""); //vector of 1 empty string -        return; - -    case SUBDEV_PROP_SENSOR_NAMES: -        val = std::vector<std::string>(); //empty -        return; - -    case SUBDEV_PROP_CONNECTION: -        val = SUBDEV_CONN_COMPLEX_IQ; -        return; - -    case SUBDEV_PROP_ENABLED: -        val = true; //always enabled -        return; - -    case SUBDEV_PROP_USE_LO_OFFSET: -        val = false; -        return; - -    case SUBDEV_PROP_BANDWIDTH: -        val = 0.0; -        return; - -    default: UHD_THROW_PROP_GET_ERROR(); -    } -} - -void unknown_rx::rx_set(const wax::obj &key_, const wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ - -    case SUBDEV_PROP_GAIN: -        UHD_ASSERT_THROW(val.as<double>() == double(0)); -        return; - -    case SUBDEV_PROP_ANTENNA: -        if (val.as<std::string>().empty()) return; -        throw uhd::value_error("Unknown Daughterboard: No selectable antenna"); - -    case SUBDEV_PROP_FREQ: -        return; // it wont do you much good, but you can set it - -    case SUBDEV_PROP_ENABLED: -        return; //always enabled - -    case SUBDEV_PROP_BANDWIDTH: -        UHD_MSG(warning) << "Unknown Daughterboard: No tunable bandwidth, fixed filtered to 0.0MHz"; -        return; -    default: UHD_THROW_PROP_SET_ERROR(); -    } +    //////////////////////////////////////////////////////////////////// +    // Register properties +    //////////////////////////////////////////////////////////////////// +    this->get_rx_subtree()->create<std::string>("name").set( +        std::string(str(boost::format("%s - %s") +            % get_rx_id().to_pp_string() +            % get_subdev_name() +        ))); +    this->get_rx_subtree()->create<int>("gains"); //phony property so this dir exists +    this->get_rx_subtree()->create<double>("freq/value") +        .set(double(0.0)); +    this->get_rx_subtree()->create<meta_range_t>("freq/range") +        .set(freq_range_t(double(0.0), double(0.0))); +    this->get_rx_subtree()->create<std::string>("antenna/value") +        .set(""); +    this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options") +        .set(list_of("")); +    this->get_rx_subtree()->create<int>("sensors"); //phony property so this dir exists +    this->get_rx_subtree()->create<std::string>("connection") +        .set("IQ"); +    this->get_rx_subtree()->create<bool>("enabled") +        .set(true); //always enabled +    this->get_rx_subtree()->create<bool>("use_lo_offset") +        .set(false); +    this->get_rx_subtree()->create<double>("bandwidth/value") +        .set(double(0.0)); +    this->get_rx_subtree()->create<meta_range_t>("bandwidth/range") +        .set(freq_range_t(0.0, 0.0));  }  /*********************************************************************** @@ -198,97 +128,33 @@ void unknown_rx::rx_set(const wax::obj &key_, const wax::obj &val){   **********************************************************************/  unknown_tx::unknown_tx(ctor_args_t args) : tx_dboard_base(args){      warn_if_old_rfx(this->get_tx_id(), "TX"); -} - -void unknown_tx::tx_get(const wax::obj &key_, wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ -    case SUBDEV_PROP_NAME: -        val = "Unknown - " + get_tx_id().to_pp_string(); -        return; - -    case SUBDEV_PROP_OTHERS: -        val = prop_names_t(); //empty -        return; - -    case SUBDEV_PROP_GAIN: -        val = double(0); -        return; - -    case SUBDEV_PROP_GAIN_RANGE: -        val = gain_range_t(0, 0, 0); -        return; - -    case SUBDEV_PROP_GAIN_NAMES: -        val = prop_names_t(); //empty -        return; - -    case SUBDEV_PROP_FREQ: -        val = double(0); -        return; - -    case SUBDEV_PROP_FREQ_RANGE: -        val = freq_range_t(0.0, 0.0); -        return; - -    case SUBDEV_PROP_ANTENNA: -        val = std::string(""); -        return; - -    case SUBDEV_PROP_ANTENNA_NAMES: -        val = prop_names_t(1, ""); //vector of 1 empty string -        return; - -    case SUBDEV_PROP_SENSOR_NAMES: -        val = std::vector<std::string>(); //empty -        return; - -    case SUBDEV_PROP_CONNECTION: -        val = SUBDEV_CONN_COMPLEX_IQ; -        return; -    case SUBDEV_PROP_ENABLED: -        val = true; //always enabled -        return; - -    case SUBDEV_PROP_USE_LO_OFFSET: -        val = false; -        return; - -    case SUBDEV_PROP_BANDWIDTH: -        val = 0.0; -        return; - -    default: UHD_THROW_PROP_GET_ERROR(); -    } -} - -void unknown_tx::tx_set(const wax::obj &key_, const wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ - -    case SUBDEV_PROP_GAIN: -        UHD_ASSERT_THROW(val.as<double>() == double(0)); -        return; - -    case SUBDEV_PROP_ANTENNA: -        if (val.as<std::string>().empty()) return; -        throw uhd::value_error("Unknown Daughterboard: No selectable antenna"); - -    case SUBDEV_PROP_FREQ: -        return; // it wont do you much good, but you can set it - -    case SUBDEV_PROP_ENABLED: -        return; //always enabled - -    case SUBDEV_PROP_BANDWIDTH: -        UHD_MSG(warning) << "Unknown Daughterboard: No tunable bandwidth, fixed filtered to 0.0MHz"; -        return; - -    default: UHD_THROW_PROP_SET_ERROR(); -    } +    //////////////////////////////////////////////////////////////////// +    // Register properties +    //////////////////////////////////////////////////////////////////// +    this->get_tx_subtree()->create<std::string>("name").set( +        std::string(str(boost::format("%s - %s") +            % get_tx_id().to_pp_string() +            % get_subdev_name() +        ))); +    this->get_tx_subtree()->create<int>("gains"); //phony property so this dir exists +    this->get_tx_subtree()->create<double>("freq/value") +        .set(double(0.0)); +    this->get_tx_subtree()->create<meta_range_t>("freq/range") +        .set(freq_range_t(double(0.0), double(0.0))); +    this->get_tx_subtree()->create<std::string>("antenna/value") +        .set(""); +    this->get_tx_subtree()->create<std::vector<std::string> >("antenna/options") +        .set(list_of("")); +    this->get_tx_subtree()->create<int>("sensors"); //phony property so this dir exists +    this->get_tx_subtree()->create<std::string>("connection") +        .set("IQ"); +    this->get_tx_subtree()->create<bool>("enabled") +        .set(true); //always enabled +    this->get_tx_subtree()->create<bool>("use_lo_offset") +        .set(false); +    this->get_tx_subtree()->create<double>("bandwidth/value") +        .set(double(0.0)); +    this->get_tx_subtree()->create<meta_range_t>("bandwidth/range") +        .set(freq_range_t(0.0, 0.0));  } diff --git a/host/lib/usrp/dboard/db_wbx_common.cpp b/host/lib/usrp/dboard/db_wbx_common.cpp index c7f8ba55d..daf6dbc98 100644 --- a/host/lib/usrp/dboard/db_wbx_common.cpp +++ b/host/lib/usrp/dboard/db_wbx_common.cpp @@ -15,151 +15,22 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // -// Common IO Pins -#define ADF4350_CE      (1 << 3) -#define ADF4350_PDBRF   (1 << 2) -#define ADF4350_MUXOUT  (1 << 1)                // INPUT!!! -#define LOCKDET_MASK    (1 << 0)                // INPUT!!! - -// TX IO Pins -#define TX_PUP_5V       (1 << 7)                // enables 5.0V power supply -#define TX_PUP_3V       (1 << 6)                // enables 3.3V supply -#define TXMOD_EN        (1 << 4)                // on UNIT_TX, 1 enables TX Modulator - -// RX IO Pins -#define RX_PUP_5V       (1 << 7)                // enables 5.0V power supply -#define RX_PUP_3V       (1 << 6)                // enables 3.3V supply -#define RXBB_PDB        (1 << 4)                // on UNIT_RX, 1 powers up RX baseband - -// RX Attenuator Pins -#define RX_ATTN_SHIFT   8                       // lsb of RX Attenuator Control -#define RX_ATTN_MASK    (63 << RX_ATTN_SHIFT)      // valid bits of RX Attenuator Control - -// TX Attenuator Pins (v3 only) -#define TX_ATTN_16      (1 << 14) -#define TX_ATTN_8       (1 << 5) -#define TX_ATTN_4       (1 << 4) -#define TX_ATTN_2       (1 << 3) -#define TX_ATTN_1       (1 << 1) -#define TX_ATTN_MASK    (TX_ATTN_16|TX_ATTN_8|TX_ATTN_4|TX_ATTN_2|TX_ATTN_1)      // valid bits of TX Attenuator Control - -// Mixer functions -#define TX_MIXER_ENB    (TXMOD_EN|ADF4350_PDBRF)    // for v3, TXMOD_EN tied to ADF4350_PDBRF rather than separate -#define TX_MIXER_DIS    0 - -#define RX_MIXER_ENB    (RXBB_PDB|ADF4350_PDBRF) -#define RX_MIXER_DIS    0 - -// Power functions -#define TX_POWER_UP     (TX_PUP_5V|TX_PUP_3V) // high enables power supply -#define TX_POWER_DOWN   0 - -#define RX_POWER_UP     (RX_PUP_5V|RX_PUP_3V|ADF4350_CE) // high enables power supply -#define RX_POWER_DOWN   0 -  #include "db_wbx_common.hpp"  #include "adf4350_regs.hpp" -#include <uhd/utils/log.hpp>  #include <uhd/types/dict.hpp>  #include <uhd/types/ranges.hpp>  #include <uhd/types/sensors.hpp>  #include <uhd/utils/assert_has.hpp>  #include <uhd/utils/algorithm.hpp>  #include <uhd/utils/msg.hpp> -#include <uhd/usrp/dboard_base.hpp> -#include <boost/assign/list_of.hpp> -#include <boost/format.hpp> -#include <boost/math/special_functions/round.hpp>  using namespace uhd;  using namespace uhd::usrp;  using namespace boost::assign; -/*********************************************************************** - * The WBX Common dboard constants - **********************************************************************/ -static const uhd::dict<std::string, gain_range_t> wbx_tx_gain_ranges = map_list_of -    ("PGA0", gain_range_t(0, 25, 0.05)) -; - -static const uhd::dict<std::string, gain_range_t> wbx_v3_tx_gain_ranges = map_list_of -    ("PGA0", gain_range_t(0, 31, 1.0)) -; - -static const uhd::dict<std::string, gain_range_t> wbx_rx_gain_ranges = map_list_of -    ("PGA0", gain_range_t(0, 31.5, 0.5)) -; - -/*********************************************************************** - * WBX Common Implementation - **********************************************************************/ -wbx_base::wbx_base(ctor_args_t args) : xcvr_dboard_base(args){ - -    //enable the clocks that we need -    this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true); -    this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true); - -    //v3 has different io bits for attenuator control -    int v3_iobits = is_v3() ? TX_ATTN_MASK : ADF4350_CE; -    int v3_tx_mod = is_v3() ? ADF4350_PDBRF : TXMOD_EN|ADF4350_PDBRF; - -    //set the gpio directions and atr controls -    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, v3_tx_mod); -    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, RXBB_PDB|ADF4350_PDBRF); -    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, TX_PUP_5V|TX_PUP_3V|v3_tx_mod|v3_iobits); -    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, RX_PUP_5V|RX_PUP_3V|ADF4350_CE|RXBB_PDB|ADF4350_PDBRF|RX_ATTN_MASK); - -    //setup ATR for the mixer enables (always enabled to prevent phase slip between bursts) -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE,        v3_tx_mod, TX_MIXER_DIS | v3_tx_mod); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY,     v3_tx_mod, TX_MIXER_DIS | v3_tx_mod); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY,     v3_tx_mod, TX_MIXER_DIS | v3_tx_mod); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, v3_tx_mod, TX_MIXER_DIS | v3_tx_mod); - -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE,        RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY,     RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY,     RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); - -    //set some default values -    if (is_v3()) { -        BOOST_FOREACH(const std::string &name, wbx_v3_tx_gain_ranges.keys()){ -            set_tx_gain(wbx_v3_tx_gain_ranges[name].start(), name); -        } -    } -    else { -        BOOST_FOREACH(const std::string &name, wbx_tx_gain_ranges.keys()){ -            set_tx_gain(wbx_tx_gain_ranges[name].start(), name); -        } -    } - -    BOOST_FOREACH(const std::string &name, wbx_rx_gain_ranges.keys()){ -        set_rx_gain(wbx_rx_gain_ranges[name].start(), name); -    } -    set_rx_enabled(false); -    set_tx_enabled(false); -} - -wbx_base::~wbx_base(void){ -    /* NOP */ -} - -/*********************************************************************** - * Enables - **********************************************************************/ -void wbx_base::set_rx_enabled(bool enb){ -    this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, -        (enb)? RX_POWER_UP : RX_POWER_DOWN, RX_POWER_UP | RX_POWER_DOWN -    ); -} - -void wbx_base::set_tx_enabled(bool enb){ -    this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, -        (enb)? TX_POWER_UP | ADF4350_CE : TX_POWER_DOWN, TX_POWER_UP | TX_POWER_DOWN | (is_v3() ? 0 : ADF4350_CE) -    ); -}  /*********************************************************************** - * Gain Handling + * Gain-related functions   **********************************************************************/  static int rx_pga0_gain_to_iobits(double &gain){      //clip the input @@ -182,82 +53,84 @@ static int rx_pga0_gain_to_iobits(double &gain){      return iobits;  } -//v3 TX gains -static int tx_pga0_gain_to_iobits(double &gain){ -    //clip the input -    gain = wbx_v3_tx_gain_ranges["PGA0"].clip(gain); - -    //convert to attenuation -    double attn = wbx_v3_tx_gain_ranges["PGA0"].stop() - gain; -    //calculate the attenuation -    int attn_code = boost::math::iround(attn); -    int iobits = ( -            (attn_code & 16 ? 0 : TX_ATTN_16) | -            (attn_code &  8 ? 0 : TX_ATTN_8) | -            (attn_code &  4 ? 0 : TX_ATTN_4) | -            (attn_code &  2 ? 0 : TX_ATTN_2) | -            (attn_code &  1 ? 0 : TX_ATTN_1)  -        ) & TX_ATTN_MASK; +/*********************************************************************** + * WBX Common Implementation + **********************************************************************/ +wbx_base::wbx_base(ctor_args_t args) : xcvr_dboard_base(args){ -    UHD_LOGV(often) << boost::format( -        "WBX TX Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x" -    ) % attn % attn_code % (iobits & TX_ATTN_MASK) % TX_ATTN_MASK << std::endl; +    //enable the clocks that we need +    this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true); +    this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true); -    //the actual gain setting -    gain = wbx_v3_tx_gain_ranges["PGA0"].stop() - double(attn_code); +    //////////////////////////////////////////////////////////////////// +    // Register RX properties +    //////////////////////////////////////////////////////////////////// +    this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") +        .publish(boost::bind(&wbx_base::get_locked, this, dboard_iface::UNIT_RX)); +    BOOST_FOREACH(const std::string &name, wbx_rx_gain_ranges.keys()){ +        this->get_rx_subtree()->create<double>("gains/"+name+"/value") +            .coerce(boost::bind(&wbx_base::set_rx_gain, this, _1, name)) +            .set(wbx_rx_gain_ranges[name].start()); +        this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range") +            .set(wbx_rx_gain_ranges[name]); +    } +    this->get_rx_subtree()->create<std::string>("connection").set("IQ"); +    this->get_rx_subtree()->create<bool>("enabled") +        .subscribe(boost::bind(&wbx_base::set_rx_enabled, this, _1)) +        .set(true); //start enabled +    this->get_rx_subtree()->create<bool>("use_lo_offset").set(false); +    this->get_rx_subtree()->create<double>("bandwidth/value").set(2*20.0e6); //20MHz low-pass, we want complex double-sided +    this->get_rx_subtree()->create<meta_range_t>("bandwidth/range") +        .set(freq_range_t(2*20.0e6, 2*20.0e6)); + +    //////////////////////////////////////////////////////////////////// +    // Register TX properties +    //////////////////////////////////////////////////////////////////// +    this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked") +        .publish(boost::bind(&wbx_base::get_locked, this, dboard_iface::UNIT_TX)); +    this->get_tx_subtree()->create<std::string>("connection").set("IQ"); +    this->get_tx_subtree()->create<bool>("use_lo_offset").set(false); +    this->get_tx_subtree()->create<double>("bandwidth/value").set(2*20.0e6); //20MHz low-pass, we want complex double-sided +    this->get_tx_subtree()->create<meta_range_t>("bandwidth/range") +        .set(freq_range_t(2*20.0e6, 2*20.0e6)); + +    // instantiate subclass foo +    switch(get_rx_id().to_uint16()) { +        case 0x053: +            db_actual = wbx_versionx_sptr(new wbx_version2(this)); +            return; +        case 0x057: +            db_actual = wbx_versionx_sptr(new wbx_version3(this)); +            return; +        case 0x063: +            db_actual = wbx_versionx_sptr(new wbx_version4(this)); +            return; +        default: +            /* We didn't recognize the version of the board... */ +            UHD_THROW_INVALID_CODE_PATH(); +    } -    return iobits;  } -//Pre v3 TX gains -static double tx_pga0_gain_to_dac_volts(double &gain){ -    //clip the input -    gain = wbx_tx_gain_ranges["PGA0"].clip(gain); - -    //voltage level constants -    static const double max_volts = 0.5, min_volts = 1.4; -    static const double slope = (max_volts-min_volts)/wbx_tx_gain_ranges["PGA0"].stop(); - -    //calculate the voltage for the aux dac -    double dac_volts = gain*slope + min_volts; -    UHD_LOGV(often) << boost::format( -        "WBX TX Gain: %f dB, dac_volts: %f V" -    ) % gain % dac_volts << std::endl; - -    //the actual gain setting -    gain = (dac_volts - min_volts)/slope; - -    return dac_volts; +wbx_base::~wbx_base(void){ +    /* NOP */  } -void wbx_base::set_tx_gain(double gain, const std::string &name){ -    if (is_v3()) { -        assert_has(wbx_v3_tx_gain_ranges.keys(), name, "wbx tx gain name"); -        if(name == "PGA0"){ -            boost::uint16_t io_bits = tx_pga0_gain_to_iobits(gain); -            _tx_gains[name] = gain; - -            //write the new gain to tx gpio outputs -            this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, io_bits, TX_ATTN_MASK); -        } -        else UHD_THROW_INVALID_CODE_PATH(); -    } -    else { -        assert_has(wbx_tx_gain_ranges.keys(), name, "wbx tx gain name"); -        if(name == "PGA0"){ -            double dac_volts = tx_pga0_gain_to_dac_volts(gain); -            _tx_gains[name] = gain; - -            //write the new voltage to the aux dac -            this->get_iface()->write_aux_dac(dboard_iface::UNIT_TX, dboard_iface::AUX_DAC_A, dac_volts); -        } -        else UHD_THROW_INVALID_CODE_PATH(); -    } +/*********************************************************************** + * Enables + **********************************************************************/ +void wbx_base::set_rx_enabled(bool enb){ +    this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, +        (enb)? RX_POWER_UP : RX_POWER_DOWN, RX_POWER_UP | RX_POWER_DOWN +    );  } -void wbx_base::set_rx_gain(double gain, const std::string &name){ +/*********************************************************************** + * Gain Handling + **********************************************************************/ +double wbx_base::set_rx_gain(double gain, const std::string &name){      assert_has(wbx_rx_gain_ranges.keys(), name, "wbx rx gain name");      if(name == "PGA0"){          boost::uint16_t io_bits = rx_pga0_gain_to_iobits(gain); @@ -267,390 +140,17 @@ void wbx_base::set_rx_gain(double gain, const std::string &name){          this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, io_bits, RX_ATTN_MASK);      }      else UHD_THROW_INVALID_CODE_PATH(); +    return _rx_gains[name]; //returned shadowed  }  /***********************************************************************   * Tuning   **********************************************************************/ -double wbx_base::set_lo_freq( -    dboard_iface::unit_t unit, -    double target_freq -){ -    UHD_LOGV(often) << boost::format( -        "WBX tune: target frequency %f Mhz" -    ) % (target_freq/1e6) << std::endl; - -    //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) -    static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of -        (0,23) //adf4350_regs_t::PRESCALER_4_5 -        (1,75) //adf4350_regs_t::PRESCALER_8_9 -    ; - -    //map rf divider select output dividers to enums -    static const uhd::dict<int, adf4350_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of -        (1,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV1) -        (2,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV2) -        (4,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV4) -        (8,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV8) -        (16, adf4350_regs_t::RF_DIVIDER_SELECT_DIV16) -    ; - -    double actual_freq, pfd_freq; -    double ref_freq = this->get_iface()->get_clock_rate(unit); -    int R=0, BS=0, N=0, FRAC=0, MOD=0; -    int RFdiv = 1; -    adf4350_regs_t::reference_divide_by_2_t T     = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; -    adf4350_regs_t::reference_doubler_t     D     = adf4350_regs_t::REFERENCE_DOUBLER_DISABLED;     - -    //Reference doubler for 50% duty cycle -    // if ref_freq < 12.5MHz enable regs.reference_divide_by_2 -    if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED; - -    //increase RF divider until acceptable VCO frequency -    //start with target_freq*2 because mixer has divide by 2 -    double vco_freq = target_freq*2; -    while (vco_freq < 2.2e9) { -        vco_freq *= 2; -        RFdiv *= 2; -    } - -    //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) -    adf4350_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; - -    /* -     * The goal here is to loop though possible R dividers, -     * band select clock dividers, N (int) dividers, and FRAC  -     * (frac) dividers. -     * -     * Calculate the N and F dividers for each set of values. -     * The loop exists when it meets all of the constraints. -     * The resulting loop values are loaded into the registers. -     * -     * from pg.21 -     * -     * f_pfd = f_ref*(1+D)/(R*(1+T)) -     * f_vco = (N + (FRAC/MOD))*f_pfd -     *    N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD -     * f_rf = f_vco/RFdiv) -     * f_actual = f_rf/2 -     */ -    for(R = 1; R <= 1023; R+=1){ -        //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T) -        pfd_freq = ref_freq*(1+D)/(R*(1+T)); - -        //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth) -        if (pfd_freq > 25e6) continue; - -        //ignore fractional part of tuning -        N = int(std::floor(vco_freq/pfd_freq)); - -        //keep N > minimum int divider requirement -        if (N < prescaler_to_min_int_div[prescaler]) continue; - -        for(BS=1; BS <= 255; BS+=1){ -            //keep the band select frequency at or below 100KHz -            //constraint on band select clock -            if (pfd_freq/BS > 100e3) continue; -            goto done_loop; -        } -    } done_loop: - -    //Fractional-N calculation -    MOD = 4095; //max fractional accuracy -    FRAC = int((vco_freq/pfd_freq - N)*MOD); - -    //Reference divide-by-2 for 50% duty cycle -    // if R even, move one divide by 2 to to regs.reference_divide_by_2 -    if(R % 2 == 0){ -        T = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED; -        R /= 2; -    } - -    //actual frequency calculation -    actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv/2); - - -    UHD_LOGV(often) -        << boost::format("WBX 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("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%d" -            ) % R % BS % N % FRAC % MOD % T % D % RFdiv % get_locked(unit)<< std::endl -        << boost::format("WBX 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 -    adf4350_regs_t regs; - -    regs.frac_12_bit = FRAC; -    regs.int_16_bit = N; -    regs.mod_12_bit = MOD; -    regs.prescaler = prescaler; -    regs.r_counter_10_bit = R; -    regs.reference_divide_by_2 = T; -    regs.reference_doubler = D; -    regs.band_select_clock_div = BS; -    UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); -    regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; - -    if (unit == dboard_iface::UNIT_RX) { -        freq_range_t rx_lo_5dbm = list_of -            (range_t(0.05e9, 1.4e9)) -        ; - -        freq_range_t rx_lo_2dbm = list_of -            (range_t(1.4e9, 2.2e9)) -        ; - -        if (actual_freq == rx_lo_5dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM; - -        if (actual_freq == rx_lo_2dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_2DBM; - -    } else if (unit == dboard_iface::UNIT_TX) { -        freq_range_t tx_lo_5dbm = list_of -            (range_t(0.05e9, 1.7e9)) -            (range_t(1.9e9, 2.2e9)) -        ; - -        freq_range_t tx_lo_m1dbm = list_of -            (range_t(1.7e9, 1.9e9)) -        ; - -        if (actual_freq == tx_lo_5dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM; - -        if (actual_freq == tx_lo_m1dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_M1DBM; - -    } - -    //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( -            "WBX SPI Reg (0x%02x): 0x%08x" -        ) % addr % regs.get_reg(addr) << std::endl; -        this->get_iface()->write_spi( -            unit, spi_config_t::EDGE_RISE, -            regs.get_reg(addr), 32 -        ); -    } - -    //return the actual frequency -    UHD_LOGV(often) << boost::format( -        "WBX tune: actual frequency %f Mhz" -    ) % (actual_freq/1e6) << std::endl; -    return actual_freq; +double wbx_base::set_lo_freq(dboard_iface::unit_t unit, double target_freq) { +    return db_actual->set_lo_freq(unit, target_freq);  } -bool wbx_base::get_locked(dboard_iface::unit_t unit){ -    return (this->get_iface()->read_gpio(unit) & LOCKDET_MASK) != 0; -} - -bool wbx_base::is_v3(void){ -    return get_rx_id().to_uint16() == 0x057; -} - -/*********************************************************************** - * RX Get and Set - **********************************************************************/ -void wbx_base::rx_get(const wax::obj &key_, wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ -    case SUBDEV_PROP_NAME: -        val = get_rx_id().to_pp_string(); -        return; - -    case SUBDEV_PROP_OTHERS: -        val = prop_names_t(); //empty -        return; - -    case SUBDEV_PROP_GAIN: -        assert_has(_rx_gains.keys(), key.name, "wbx rx gain name"); -        val = _rx_gains[key.name]; -        return; - -    case SUBDEV_PROP_GAIN_RANGE: -        assert_has(wbx_rx_gain_ranges.keys(), key.name, "wbx rx gain name"); -        val = wbx_rx_gain_ranges[key.name]; -        return; - -    case SUBDEV_PROP_GAIN_NAMES: -        val = prop_names_t(wbx_rx_gain_ranges.keys()); -        return; - -    case SUBDEV_PROP_FREQ: -        val = 0.0; -        return; - -    case SUBDEV_PROP_FREQ_RANGE: -        val = freq_range_t(0.0, 0.0, 0.0);; -        return; - -    case SUBDEV_PROP_ANTENNA: -        val = std::string(""); -        return; - -    case SUBDEV_PROP_ANTENNA_NAMES: -        val = prop_names_t(1, ""); -        return; - -    case SUBDEV_PROP_CONNECTION: -        val = SUBDEV_CONN_COMPLEX_IQ; -        return; - -    case SUBDEV_PROP_ENABLED: -        val = _rx_enabled; -        return; - -    case SUBDEV_PROP_USE_LO_OFFSET: -        val = false; -        return; - -    case SUBDEV_PROP_SENSOR: -        UHD_ASSERT_THROW(key.name == "lo_locked"); -        val = sensor_value_t("LO", this->get_locked(dboard_iface::UNIT_RX), "locked", "unlocked"); -        return; - -    case SUBDEV_PROP_SENSOR_NAMES: -        val = prop_names_t(1, "lo_locked"); -        return; - -    case SUBDEV_PROP_BANDWIDTH: -        val = 2*20.0e6; //20MHz low-pass, we want complex double-sided -        return; - -    default: UHD_THROW_PROP_GET_ERROR(); -    } -} - -void wbx_base::rx_set(const wax::obj &key_, const wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ - -    case SUBDEV_PROP_GAIN: -        this->set_rx_gain(val.as<double>(), key.name); -        return; - -    case SUBDEV_PROP_ENABLED: -        _rx_enabled = val.as<bool>(); -        this->set_rx_enabled(_rx_enabled); -        return; - -    case SUBDEV_PROP_BANDWIDTH: -        UHD_MSG(warning) << "WBX: No tunable bandwidth, fixed filtered to 40MHz"; -        return; - -    default: UHD_THROW_PROP_SET_ERROR(); -    } -} - -/*********************************************************************** - * TX Get and Set - **********************************************************************/ -void wbx_base::tx_get(const wax::obj &key_, wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ -    case SUBDEV_PROP_NAME: -        val = get_tx_id().to_pp_string(); -        return; - -    case SUBDEV_PROP_OTHERS: -        val = prop_names_t(); //empty -        return; - -    case SUBDEV_PROP_GAIN: -        assert_has(_tx_gains.keys(), key.name, "wbx tx gain name"); -        val = _tx_gains[key.name]; -        return; - -    case SUBDEV_PROP_GAIN_RANGE: -        if (is_v3()) { -            assert_has(wbx_v3_tx_gain_ranges.keys(), key.name, "wbx tx gain name"); -            val = wbx_v3_tx_gain_ranges[key.name]; -        } -        else { -            assert_has(wbx_tx_gain_ranges.keys(), key.name, "wbx tx gain name"); -            val = wbx_tx_gain_ranges[key.name]; -        } -        return; - -    case SUBDEV_PROP_GAIN_NAMES: -        if (is_v3()) -            val = prop_names_t(wbx_v3_tx_gain_ranges.keys()); -        else -            val = prop_names_t(wbx_tx_gain_ranges.keys()); -        return; - -    case SUBDEV_PROP_FREQ: -        val = 0.0; -        return; - -    case SUBDEV_PROP_FREQ_RANGE: -        val = freq_range_t(0.0, 0.0, 0.0); -        return; - -    case SUBDEV_PROP_ANTENNA: -        val = std::string(""); -        return; - -    case SUBDEV_PROP_ANTENNA_NAMES: -        val = prop_names_t(1, ""); -        return; - -    case SUBDEV_PROP_CONNECTION: -        val = SUBDEV_CONN_COMPLEX_IQ; -        return; - -    case SUBDEV_PROP_ENABLED: -        val = _tx_enabled; -        return; - -    case SUBDEV_PROP_USE_LO_OFFSET: -        val = false; -        return; - -    case SUBDEV_PROP_SENSOR: -        UHD_ASSERT_THROW(key.name == "lo_locked"); -        val = sensor_value_t("LO", this->get_locked(dboard_iface::UNIT_TX), "locked", "unlocked"); -        return; - -    case SUBDEV_PROP_SENSOR_NAMES: -        val = prop_names_t(1, "lo_locked"); -        return; - -    case SUBDEV_PROP_BANDWIDTH: -        val = 2*20.0e6; //20MHz low-pass, we want complex double-sided -        return; - -    default: UHD_THROW_PROP_GET_ERROR(); -    } -} - -void wbx_base::tx_set(const wax::obj &key_, const wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ - -    case SUBDEV_PROP_GAIN: -        this->set_tx_gain(val.as<double>(), key.name); -        return; - -    case SUBDEV_PROP_ENABLED: -        _tx_enabled = val.as<bool>(); -        this->set_tx_enabled(_tx_enabled); -        return; - -    case SUBDEV_PROP_BANDWIDTH: -        UHD_MSG(warning) << "WBX: No tunable bandwidth, fixed filtered to 40MHz"; -        return; - -    default: UHD_THROW_PROP_SET_ERROR(); -    } +sensor_value_t wbx_base::get_locked(dboard_iface::unit_t unit){ +    const bool locked = (this->get_iface()->read_gpio(unit) & LOCKDET_MASK) != 0; +    return sensor_value_t("LO", locked, "locked", "unlocked");  } diff --git a/host/lib/usrp/dboard/db_wbx_common.hpp b/host/lib/usrp/dboard/db_wbx_common.hpp index 5d33ddce9..e7eb3f31a 100644 --- a/host/lib/usrp/dboard/db_wbx_common.hpp +++ b/host/lib/usrp/dboard/db_wbx_common.hpp @@ -18,14 +18,75 @@  #ifndef INCLUDED_LIBUHD_USRP_DBOARD_DB_WBX_COMMON_HPP  #define INCLUDED_LIBUHD_USRP_DBOARD_DB_WBX_COMMON_HPP -#include "adf4350_regs.hpp" +// Common IO Pins +#define ADF4350_CE      (1 << 3) +#define ADF4350_PDBRF   (1 << 2) +#define ADF4350_MUXOUT  (1 << 1)                // INPUT!!! +#define ADF4351_CE      (1 << 3) +#define ADF4351_PDBRF   (1 << 2) +#define ADF4351_MUXOUT  (1 << 1)                // INPUT!!! + +#define LOCKDET_MASK    (1 << 0)                // INPUT!!! + +// TX IO Pins +#define TX_PUP_5V       (1 << 7)                // enables 5.0V power supply +#define TX_PUP_3V       (1 << 6)                // enables 3.3V supply +#define TXMOD_EN        (1 << 4)                // on UNIT_TX, 1 enables TX Modulator + +// RX IO Pins +#define RX_PUP_5V       (1 << 7)                // enables 5.0V power supply +#define RX_PUP_3V       (1 << 6)                // enables 3.3V supply +#define RXBB_PDB        (1 << 4)                // on UNIT_RX, 1 powers up RX baseband + +// RX Attenuator Pins +#define RX_ATTN_SHIFT   8                       // lsb of RX Attenuator Control +#define RX_ATTN_MASK    (63 << RX_ATTN_SHIFT)      // valid bits of RX Attenuator Control + +// TX Attenuator Pins (v3 only) +#define TX_ATTN_16      (1 << 14) +#define TX_ATTN_8       (1 << 5) +#define TX_ATTN_4       (1 << 4) +#define TX_ATTN_2       (1 << 3) +#define TX_ATTN_1       (1 << 1) +#define TX_ATTN_MASK    (TX_ATTN_16|TX_ATTN_8|TX_ATTN_4|TX_ATTN_2|TX_ATTN_1)      // valid bits of TX Attenuator Control + +// Mixer functions +#define TX_MIXER_ENB    (TXMOD_EN|ADF4350_PDBRF)    // for v3, TXMOD_EN tied to ADF4350_PDBRF rather than separate +#define TX_MIXER_DIS    0 + +#define RX_MIXER_ENB    (RXBB_PDB|ADF4350_PDBRF) +#define RX_MIXER_DIS    0 + +// Power functions +#define TX_POWER_UP     (TX_PUP_5V|TX_PUP_3V) // high enables power supply +#define TX_POWER_DOWN   0 + +#define RX_POWER_UP     (RX_PUP_5V|RX_PUP_3V|ADF4350_CE) // high enables power supply +#define RX_POWER_DOWN   0 + +  #include <uhd/types/dict.hpp>  #include <uhd/types/ranges.hpp> -#include <uhd/utils/props.hpp> +#include <uhd/types/sensors.hpp> +#include <uhd/utils/log.hpp> +#include <uhd/utils/static.hpp>  #include <uhd/usrp/dboard_base.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/format.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/math/special_functions/round.hpp> +#include <boost/bind.hpp>  namespace uhd{ namespace usrp{ + +/*********************************************************************** + * The WBX Common dboard constants + **********************************************************************/ +static const uhd::dict<std::string, gain_range_t> wbx_rx_gain_ranges = boost::assign::map_list_of +    ("PGA0", gain_range_t(0, 31.5, 0.5)); + +  /***********************************************************************   * The WBX dboard base class   **********************************************************************/ @@ -35,17 +96,9 @@ public:      virtual ~wbx_base(void);  protected: -    virtual void set_rx_gain(double gain, const std::string &name); -    virtual void set_tx_gain(double gain, const std::string &name); +    virtual double set_rx_gain(double gain, const std::string &name);      virtual void set_rx_enabled(bool enb); -    virtual void set_tx_enabled(bool enb); - -    void rx_get(const wax::obj &key, wax::obj &val); -    void rx_set(const wax::obj &key, const wax::obj &val); - -    void tx_get(const wax::obj &key, wax::obj &val); -    void tx_set(const wax::obj &key, const wax::obj &val);      /*!       * Set the LO frequency for the particular dboard unit. @@ -57,22 +110,103 @@ protected:      /*!       * Get the lock detect status of the LO. +     * +     * This operation is identical for all versions of the WBX board.       * \param unit which unit rx or tx       * \return true for locked       */ -    virtual bool get_locked(dboard_iface::unit_t unit); +    virtual sensor_value_t get_locked(dboard_iface::unit_t unit);      /*! -     * Detect if this a v3 WBX -     * \return true for locked +     * Version-agnostic ABC that wraps version-specific implementations of the +     * WBX base daughterboard. +     * +     * This class is an abstract base class, and thus is impossible to +     * instantiate. +     */ +    class wbx_versionx { +    public: +        wbx_versionx() {} +        ~wbx_versionx(void) {} + +        virtual double set_tx_gain(double gain, const std::string &name) = 0; +        virtual void set_tx_enabled(bool enb) = 0; +        virtual double set_lo_freq(dboard_iface::unit_t unit, double target_freq) = 0; + +        /*! This is the registered instance of the wrapper class, wbx_base. */ +        wbx_base *self_base; + +        property_tree::sptr get_rx_subtree(void){ +            return self_base->get_rx_subtree(); +        } + +        property_tree::sptr get_tx_subtree(void){ +            return self_base->get_tx_subtree(); +        } +    }; + + +    /*! +     * Version 2 of the WBX Daughterboard +     * +     * Basically the original release of the DB. +     */ +    class wbx_version2 : public wbx_versionx { +    public: +        wbx_version2(wbx_base *_self_wbx_base); +        ~wbx_version2(void); + +        double set_tx_gain(double gain, const std::string &name); +        void set_tx_enabled(bool enb); +        double set_lo_freq(dboard_iface::unit_t unit, double target_freq); +    }; + +    /*! +     * Version 3 of the WBX Daughterboard +     * +     * Fixed a problem with the AGC from Version 2. +     */ +    class wbx_version3 : public wbx_versionx { +    public: +        wbx_version3(wbx_base *_self_wbx_base); +        ~wbx_version3(void); + +        double set_tx_gain(double gain, const std::string &name); +        void set_tx_enabled(bool enb); +        double set_lo_freq(dboard_iface::unit_t unit, double target_freq); +    }; + +    /*! +     * Version 4 of the WBX Daughterboard +     * +     * Upgrades the Frequnecy Synthensizer from ADF4350 to ADF4351.       */ -    virtual bool is_v3(void); +    class wbx_version4 : public wbx_versionx { +    public: +        wbx_version4(wbx_base *_self_wbx_base); +        ~wbx_version4(void); + +        double set_tx_gain(double gain, const std::string &name); +        void set_tx_enabled(bool enb); +        double set_lo_freq(dboard_iface::unit_t unit, double target_freq); +    }; + +    /*! +     * Handle to the version-specific implementation of the WBX. +     * +     * Since many of this class's functions are dependent on the version of the +     * WBX board, this class will instantiate an object of the appropriate +     * wbx_version_* subclass, and invoke any relevant functions through that +     * object.  This pointer is set to the proper object at construction time. +     */ +    typedef boost::shared_ptr<wbx_versionx> wbx_versionx_sptr; +    wbx_versionx_sptr db_actual; -private:      uhd::dict<std::string, double> _tx_gains, _rx_gains;      bool _rx_enabled, _tx_enabled;  }; +  }} //namespace uhd::usrp  #endif /* INCLUDED_LIBUHD_USRP_DBOARD_DB_WBX_COMMON_HPP */ diff --git a/host/lib/usrp/dboard/db_wbx_simple.cpp b/host/lib/usrp/dboard/db_wbx_simple.cpp index 990bacbc8..f46ea70d1 100644 --- a/host/lib/usrp/dboard/db_wbx_simple.cpp +++ b/host/lib/usrp/dboard/db_wbx_simple.cpp @@ -32,14 +32,13 @@ using namespace uhd;  using namespace uhd::usrp;  using namespace boost::assign; +  /***********************************************************************   * The WBX Simple dboard constants   **********************************************************************/ -static const freq_range_t wbx_freq_range(68.75e6, 2.2e9); - -static const prop_names_t wbx_tx_antennas = list_of("TX/RX"); +static const std::vector<std::string> wbx_tx_antennas = list_of("TX/RX"); -static const prop_names_t wbx_rx_antennas = list_of("TX/RX")("RX2"); +static const std::vector<std::string> wbx_rx_antennas = list_of("TX/RX")("RX2");  /***********************************************************************   * The WBX simple implementation @@ -49,17 +48,7 @@ public:      wbx_simple(ctor_args_t args);      ~wbx_simple(void); -    void rx_get(const wax::obj &key, wax::obj &val); -    void rx_set(const wax::obj &key, const wax::obj &val); - -    void tx_get(const wax::obj &key, wax::obj &val); -    void tx_set(const wax::obj &key, const wax::obj &val); -  private: -    void set_rx_lo_freq(double freq); -    void set_tx_lo_freq(double freq); -    double _rx_lo_freq, _tx_lo_freq; -      void set_rx_ant(const std::string &ant);      void set_tx_ant(const std::string &ant);      std::string _rx_ant; @@ -72,11 +61,16 @@ static dboard_base::sptr make_wbx_simple(dboard_base::ctor_args_t args){      return dboard_base::sptr(new wbx_simple(args));  } +/*********************************************************************** + * ID Numbers for WBX daughterboard combinations. + **********************************************************************/  UHD_STATIC_BLOCK(reg_wbx_simple_dboards){      dboard_manager::register_dboard(0x0053, 0x0052, &make_wbx_simple, "WBX");      dboard_manager::register_dboard(0x0053, 0x004f, &make_wbx_simple, "WBX + Simple GDB");      dboard_manager::register_dboard(0x0057, 0x0056, &make_wbx_simple, "WBX v3");      dboard_manager::register_dboard(0x0057, 0x004f, &make_wbx_simple, "WBX v3 + Simple GDB"); +    dboard_manager::register_dboard(0x0063, 0x0062, &make_wbx_simple, "WBX v4"); +    dboard_manager::register_dboard(0x0063, 0x004f, &make_wbx_simple, "WBX v4 + Simple GDB");  }  /*********************************************************************** @@ -84,6 +78,28 @@ UHD_STATIC_BLOCK(reg_wbx_simple_dboards){   **********************************************************************/  wbx_simple::wbx_simple(ctor_args_t args) : wbx_base(args){ +    //////////////////////////////////////////////////////////////////// +    // Register RX properties +    //////////////////////////////////////////////////////////////////// +    this->get_rx_subtree()->access<std::string>("name").set( +        this->get_rx_subtree()->access<std::string>("name").get() + " + Simple GDB"); +    this->get_rx_subtree()->create<std::string>("antenna/value") +        .subscribe(boost::bind(&wbx_simple::set_rx_ant, this, _1)) +        .set("RX2"); +    this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options") +        .set(wbx_rx_antennas); + +    //////////////////////////////////////////////////////////////////// +    // Register TX properties +    //////////////////////////////////////////////////////////////////// +    this->get_tx_subtree()->access<std::string>("name").set( +        this->get_tx_subtree()->access<std::string>("name").get() + " + Simple GDB"); +    this->get_tx_subtree()->create<std::string>("antenna/value") +        .subscribe(boost::bind(&wbx_simple::set_tx_ant, this, _1)) +        .set(wbx_tx_antennas.at(0)); +    this->get_tx_subtree()->create<std::vector<std::string> >("antenna/options") +        .set(wbx_tx_antennas); +      //set the gpio directions and atr controls (antenna switches all under ATR)      this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, ANTSW_IO, ANTSW_IO);      this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, ANTSW_IO, ANTSW_IO); @@ -99,11 +115,6 @@ wbx_simple::wbx_simple(ctor_args_t args) : wbx_base(args){      this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE,        ANT_TXRX, ANTSW_IO);      this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY,     ANT_RX2, ANTSW_IO);      this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, ANT_RX2, ANTSW_IO); - -    //set some default values -    set_rx_lo_freq((wbx_freq_range.start() + wbx_freq_range.stop())/2.0); -    set_tx_lo_freq((wbx_freq_range.start() + wbx_freq_range.stop())/2.0); -    set_rx_ant("RX2");  }  wbx_simple::~wbx_simple(void){ @@ -128,128 +139,3 @@ void wbx_simple::set_tx_ant(const std::string &ant){      assert_has(wbx_tx_antennas, ant, "wbx tx antenna name");      //only one antenna option, do nothing  } - -/*********************************************************************** - * Tuning - **********************************************************************/ -void wbx_simple::set_rx_lo_freq(double freq){ -    _rx_lo_freq = set_lo_freq(dboard_iface::UNIT_RX, wbx_freq_range.clip(freq)); -} - -void wbx_simple::set_tx_lo_freq(double freq){ -    _tx_lo_freq = set_lo_freq(dboard_iface::UNIT_TX, wbx_freq_range.clip(freq)); -} - -/*********************************************************************** - * RX Get and Set - **********************************************************************/ -void wbx_simple::rx_get(const wax::obj &key_, wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ -    case SUBDEV_PROP_NAME: -        if (is_v3()) -            val = std::string("WBX v3 RX + Simple GDB"); -        else -            val = std::string("WBX RX + Simple GDB"); -        return; - -    case SUBDEV_PROP_FREQ: -        val = _rx_lo_freq; -        return; - -    case SUBDEV_PROP_FREQ_RANGE: -        val = wbx_freq_range; -        return; - -    case SUBDEV_PROP_ANTENNA: -        val = _rx_ant; -        return; - -    case SUBDEV_PROP_ANTENNA_NAMES: -        val = wbx_rx_antennas; -        return; - -    default: -        //call into the base class for other properties -        wbx_base::rx_get(key_, val); -    } -} - -void wbx_simple::rx_set(const wax::obj &key_, const wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ - -    case SUBDEV_PROP_FREQ: -        this->set_rx_lo_freq(val.as<double>()); -        return; - -    case SUBDEV_PROP_ANTENNA: -        this->set_rx_ant(val.as<std::string>()); -        return; - -    default: -        //call into the base class for other properties -        wbx_base::rx_set(key_, val); -    } -} - -/*********************************************************************** - * TX Get and Set - **********************************************************************/ -void wbx_simple::tx_get(const wax::obj &key_, wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ -    case SUBDEV_PROP_NAME: -        if (is_v3()) -            val = std::string("WBX v3 TX + Simple GDB"); -        else -            val = std::string("WBX TX + Simple GDB"); -        return; - -    case SUBDEV_PROP_FREQ: -        val = _tx_lo_freq; -        return; - -    case SUBDEV_PROP_FREQ_RANGE: -        val = wbx_freq_range; -        return; - -    case SUBDEV_PROP_ANTENNA: -        val = std::string("TX/RX"); -        return; - -    case SUBDEV_PROP_ANTENNA_NAMES: -        val = wbx_tx_antennas; -        return; - -    default: -        //call into the base class for other properties -        wbx_base::tx_get(key_, val); -    } -} - -void wbx_simple::tx_set(const wax::obj &key_, const wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ - -    case SUBDEV_PROP_FREQ: -        this->set_tx_lo_freq(val.as<double>()); -        return; - -    case SUBDEV_PROP_ANTENNA: -        this->set_tx_ant(val.as<std::string>()); -        return; - -    default: -        //call into the base class for other properties -        wbx_base::tx_set(key_, val); -    } -} diff --git a/host/lib/usrp/dboard/db_wbx_version2.cpp b/host/lib/usrp/dboard/db_wbx_version2.cpp new file mode 100644 index 000000000..e25cb2e4c --- /dev/null +++ b/host/lib/usrp/dboard/db_wbx_version2.cpp @@ -0,0 +1,326 @@ +// +// 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/>. +// + +#include "db_wbx_common.hpp" +#include "adf4350_regs.hpp" +#include <uhd/utils/log.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/types/sensors.hpp> +#include <uhd/utils/assert_has.hpp> +#include <uhd/utils/algorithm.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/usrp/dboard_base.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/format.hpp> +#include <boost/math/special_functions/round.hpp> + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign; + + +/*********************************************************************** + * WBX Version 2 Constants + **********************************************************************/ +static const uhd::dict<std::string, gain_range_t> wbx_v2_tx_gain_ranges = map_list_of +    ("PGA0", gain_range_t(0, 25, 0.05)) +; + +static const freq_range_t wbx_v2_freq_range(68.75e6, 2.2e9); + +/*********************************************************************** + * Gain-related functions + **********************************************************************/ +static double tx_pga0_gain_to_dac_volts(double &gain){ +    //clip the input +    gain = wbx_v2_tx_gain_ranges["PGA0"].clip(gain); + +    //voltage level constants +    static const double max_volts = 0.5, min_volts = 1.4; +    static const double slope = (max_volts-min_volts)/wbx_v2_tx_gain_ranges["PGA0"].stop(); + +    //calculate the voltage for the aux dac +    double dac_volts = gain*slope + min_volts; + +    UHD_LOGV(often) << boost::format( +        "WBX TX Gain: %f dB, dac_volts: %f V" +    ) % gain % dac_volts << std::endl; + +    //the actual gain setting +    gain = (dac_volts - min_volts)/slope; + +    return dac_volts; +} + + +/*********************************************************************** + * WBX Version 2 Implementation + **********************************************************************/ +wbx_base::wbx_version2::wbx_version2(wbx_base *_self_wbx_base) { +    //register our handle on the primary wbx_base instance +    self_base = _self_wbx_base; + +    //////////////////////////////////////////////////////////////////// +    // Register RX properties +    //////////////////////////////////////////////////////////////////// +    this->get_rx_subtree()->create<std::string>("name").set("WBX RX v2"); +    this->get_rx_subtree()->create<double>("freq/value") +         .coerce(boost::bind(&wbx_base::wbx_version2::set_lo_freq, this, dboard_iface::UNIT_RX, _1)) +         .set((wbx_v2_freq_range.start() + wbx_v2_freq_range.stop())/2.0); +    this->get_rx_subtree()->create<meta_range_t>("freq/range").set(wbx_v2_freq_range); + +    //////////////////////////////////////////////////////////////////// +    // Register TX properties +    //////////////////////////////////////////////////////////////////// +    this->get_tx_subtree()->create<std::string>("name").set("WBX TX v2"); +    BOOST_FOREACH(const std::string &name, wbx_v2_tx_gain_ranges.keys()){ +        self_base->get_tx_subtree()->create<double>("gains/"+name+"/value") +            .coerce(boost::bind(&wbx_base::wbx_version2::set_tx_gain, this, _1, name)) +            .set(wbx_v2_tx_gain_ranges[name].start()); +        self_base->get_tx_subtree()->create<meta_range_t>("gains/"+name+"/range") +            .set(wbx_v2_tx_gain_ranges[name]); +    } +    this->get_tx_subtree()->create<double>("freq/value") +         .coerce(boost::bind(&wbx_base::wbx_version2::set_lo_freq, this, dboard_iface::UNIT_TX, _1)) +         .set((wbx_v2_freq_range.start() + wbx_v2_freq_range.stop())/2.0); +    this->get_tx_subtree()->create<meta_range_t>("freq/range").set(wbx_v2_freq_range); +    this->get_tx_subtree()->create<bool>("enabled") +        .subscribe(boost::bind(&wbx_base::wbx_version2::set_tx_enabled, this, _1)) +        .set(true); //start enabled + +    //set attenuator control bits +    int v2_iobits = ADF4350_CE; +    int v2_tx_mod = TXMOD_EN|ADF4350_PDBRF; + +    //set the gpio directions and atr controls +    self_base->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, v2_tx_mod); +    self_base->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, RXBB_PDB|ADF4350_PDBRF); +    self_base->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, TX_PUP_5V|TX_PUP_3V|v2_tx_mod|v2_iobits); +    self_base->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, RX_PUP_5V|RX_PUP_3V|ADF4350_CE|RXBB_PDB|ADF4350_PDBRF|RX_ATTN_MASK); + +    //setup ATR for the mixer enables (always enabled to prevent phase slip between bursts) +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE,        v2_tx_mod, TX_MIXER_DIS | v2_tx_mod); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY,     v2_tx_mod, TX_MIXER_DIS | v2_tx_mod); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY,     v2_tx_mod, TX_MIXER_DIS | v2_tx_mod); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, v2_tx_mod, TX_MIXER_DIS | v2_tx_mod); + +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE,        RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY,     RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY,     RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); +} + +wbx_base::wbx_version2::~wbx_version2(void){ +    /* NOP */ +} + +/*********************************************************************** + * Enables + **********************************************************************/ +void wbx_base::wbx_version2::set_tx_enabled(bool enb){ +    self_base->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, +        (enb)? TX_POWER_UP | ADF4350_CE : TX_POWER_DOWN, TX_POWER_UP | TX_POWER_DOWN | ADF4350_CE); +} + + +/*********************************************************************** + * Gain Handling + **********************************************************************/ +double wbx_base::wbx_version2::set_tx_gain(double gain, const std::string &name){ +    assert_has(wbx_v2_tx_gain_ranges.keys(), name, "wbx tx gain name"); +    if(name == "PGA0"){ +        double dac_volts = tx_pga0_gain_to_dac_volts(gain); +        self_base->_tx_gains[name] = gain; + +        //write the new voltage to the aux dac +        self_base->get_iface()->write_aux_dac(dboard_iface::UNIT_TX, dboard_iface::AUX_DAC_A, dac_volts); +    } +    else UHD_THROW_INVALID_CODE_PATH(); +    return self_base->_tx_gains[name]; //shadowed +} + + +/*********************************************************************** + * Tuning + **********************************************************************/ +double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double target_freq) { +    UHD_LOGV(often) << boost::format( +        "WBX tune: target frequency %f Mhz" +    ) % (target_freq/1e6) << std::endl; + +    //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) +    static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of +        (0,23) //adf4350_regs_t::PRESCALER_4_5 +        (1,75) //adf4350_regs_t::PRESCALER_8_9 +    ; + +    //map rf divider select output dividers to enums +    static const uhd::dict<int, adf4350_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of +        (1,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV1) +        (2,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV2) +        (4,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV4) +        (8,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV8) +        (16, adf4350_regs_t::RF_DIVIDER_SELECT_DIV16) +    ; + +    double actual_freq, pfd_freq; +    double ref_freq = self_base->get_iface()->get_clock_rate(unit); +    int R=0, BS=0, N=0, FRAC=0, MOD=0; +    int RFdiv = 1; +    adf4350_regs_t::reference_divide_by_2_t T     = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; +    adf4350_regs_t::reference_doubler_t     D     = adf4350_regs_t::REFERENCE_DOUBLER_DISABLED;     + +    //Reference doubler for 50% duty cycle +    // if ref_freq < 12.5MHz enable regs.reference_divide_by_2 +    if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED; + +    //increase RF divider until acceptable VCO frequency +    //start with target_freq*2 because mixer has divide by 2 +    double vco_freq = target_freq*2; +    while (vco_freq < 2.2e9) { +        vco_freq *= 2; +        RFdiv *= 2; +    } + +    //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) +    adf4350_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; + +    /* +     * The goal here is to loop though possible R dividers, +     * band select clock dividers, N (int) dividers, and FRAC  +     * (frac) dividers. +     * +     * Calculate the N and F dividers for each set of values. +     * The loop exists when it meets all of the constraints. +     * The resulting loop values are loaded into the registers. +     * +     * from pg.21 +     * +     * f_pfd = f_ref*(1+D)/(R*(1+T)) +     * f_vco = (N + (FRAC/MOD))*f_pfd +     *    N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD +     * f_rf = f_vco/RFdiv) +     * f_actual = f_rf/2 +     */ +    for(R = 1; R <= 1023; R+=1){ +        //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T) +        pfd_freq = ref_freq*(1+D)/(R*(1+T)); + +        //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth) +        if (pfd_freq > 25e6) continue; + +        //ignore fractional part of tuning +        N = int(std::floor(vco_freq/pfd_freq)); + +        //keep N > minimum int divider requirement +        if (N < prescaler_to_min_int_div[prescaler]) continue; + +        for(BS=1; BS <= 255; BS+=1){ +            //keep the band select frequency at or below 100KHz +            //constraint on band select clock +            if (pfd_freq/BS > 100e3) continue; +            goto done_loop; +        } +    } done_loop: + +    //Fractional-N calculation +    MOD = 4095; //max fractional accuracy +    FRAC = int((vco_freq/pfd_freq - N)*MOD); + +    //Reference divide-by-2 for 50% duty cycle +    // if R even, move one divide by 2 to to regs.reference_divide_by_2 +    if(R % 2 == 0){ +        T = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED; +        R /= 2; +    } + +    //actual frequency calculation +    actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv/2); + + +    UHD_LOGV(often) +        << boost::format("WBX 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("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s" +            ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl +        << boost::format("WBX 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 +    adf4350_regs_t regs; + +    regs.frac_12_bit = FRAC; +    regs.int_16_bit = N; +    regs.mod_12_bit = MOD; +    regs.prescaler = prescaler; +    regs.r_counter_10_bit = R; +    regs.reference_divide_by_2 = T; +    regs.reference_doubler = D; +    regs.band_select_clock_div = BS; +    UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); +    regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; + +    if (unit == dboard_iface::UNIT_RX) { +        freq_range_t rx_lo_5dbm = list_of +            (range_t(0.05e9, 1.4e9)) +        ; + +        freq_range_t rx_lo_2dbm = list_of +            (range_t(1.4e9, 2.2e9)) +        ; + +        if (actual_freq == rx_lo_5dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM; + +        if (actual_freq == rx_lo_2dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_2DBM; + +    } else if (unit == dboard_iface::UNIT_TX) { +        freq_range_t tx_lo_5dbm = list_of +            (range_t(0.05e9, 1.7e9)) +            (range_t(1.9e9, 2.2e9)) +        ; + +        freq_range_t tx_lo_m1dbm = list_of +            (range_t(1.7e9, 1.9e9)) +        ; + +        if (actual_freq == tx_lo_5dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM; + +        if (actual_freq == tx_lo_m1dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_M1DBM; + +    } + +    //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( +            "WBX 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( +        "WBX tune: actual frequency %f Mhz" +    ) % (actual_freq/1e6) << std::endl; +    return actual_freq; +} diff --git a/host/lib/usrp/dboard/db_wbx_version3.cpp b/host/lib/usrp/dboard/db_wbx_version3.cpp new file mode 100644 index 000000000..70981ce94 --- /dev/null +++ b/host/lib/usrp/dboard/db_wbx_version3.cpp @@ -0,0 +1,333 @@ +// +// 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/>. +// + +#include "db_wbx_common.hpp" +#include "adf4350_regs.hpp" +#include <uhd/utils/log.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/types/sensors.hpp> +#include <uhd/utils/assert_has.hpp> +#include <uhd/utils/algorithm.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/usrp/dboard_base.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/format.hpp> +#include <boost/math/special_functions/round.hpp> + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign; + + +/*********************************************************************** + * WBX Version 3 Constants + **********************************************************************/ +static const uhd::dict<std::string, gain_range_t> wbx_v3_tx_gain_ranges = map_list_of +    ("PGA0", gain_range_t(0, 31, 1.0)) +; + +static const freq_range_t wbx_v3_freq_range(68.75e6, 2.2e9); + +/*********************************************************************** + * Gain-related functions + **********************************************************************/ +static int tx_pga0_gain_to_iobits(double &gain){ +    //clip the input +    gain = wbx_v3_tx_gain_ranges["PGA0"].clip(gain); + +    //convert to attenuation +    double attn = wbx_v3_tx_gain_ranges["PGA0"].stop() - gain; + +    //calculate the attenuation +    int attn_code = boost::math::iround(attn); +    int iobits = ( +            (attn_code & 16 ? 0 : TX_ATTN_16) | +            (attn_code &  8 ? 0 : TX_ATTN_8) | +            (attn_code &  4 ? 0 : TX_ATTN_4) | +            (attn_code &  2 ? 0 : TX_ATTN_2) | +            (attn_code &  1 ? 0 : TX_ATTN_1)  +        ) & TX_ATTN_MASK; + +    UHD_LOGV(often) << boost::format( +        "WBX TX Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x" +    ) % attn % attn_code % (iobits & TX_ATTN_MASK) % TX_ATTN_MASK << std::endl; + +    //the actual gain setting +    gain = wbx_v3_tx_gain_ranges["PGA0"].stop() - double(attn_code); + +    return iobits; +} + + +/*********************************************************************** + * WBX Common Implementation + **********************************************************************/ +wbx_base::wbx_version3::wbx_version3(wbx_base *_self_wbx_base) { +    //register our handle on the primary wbx_base instance +    self_base = _self_wbx_base; + +    //////////////////////////////////////////////////////////////////// +    // Register RX properties +    //////////////////////////////////////////////////////////////////// +    this->get_rx_subtree()->create<std::string>("name").set("WBX RX v3"); +    this->get_rx_subtree()->create<double>("freq/value") +         .coerce(boost::bind(&wbx_base::wbx_version3::set_lo_freq, this, dboard_iface::UNIT_RX, _1)) +         .set((wbx_v3_freq_range.start() + wbx_v3_freq_range.stop())/2.0); +    this->get_rx_subtree()->create<meta_range_t>("freq/range").set(wbx_v3_freq_range); + +    //////////////////////////////////////////////////////////////////// +    // Register TX properties +    //////////////////////////////////////////////////////////////////// +    this->get_tx_subtree()->create<std::string>("name").set("WBX TX v3"); +    BOOST_FOREACH(const std::string &name, wbx_v3_tx_gain_ranges.keys()){ +        self_base->get_tx_subtree()->create<double>("gains/"+name+"/value") +            .coerce(boost::bind(&wbx_base::wbx_version3::set_tx_gain, this, _1, name)) +            .set(wbx_v3_tx_gain_ranges[name].start()); +        self_base->get_tx_subtree()->create<meta_range_t>("gains/"+name+"/range") +            .set(wbx_v3_tx_gain_ranges[name]); +    } +    this->get_tx_subtree()->create<double>("freq/value") +         .coerce(boost::bind(&wbx_base::wbx_version3::set_lo_freq, this, dboard_iface::UNIT_TX, _1)) +         .set((wbx_v3_freq_range.start() + wbx_v3_freq_range.stop())/2.0); +    this->get_tx_subtree()->create<meta_range_t>("freq/range").set(wbx_v3_freq_range); +    this->get_tx_subtree()->create<bool>("enabled") +        .subscribe(boost::bind(&wbx_base::wbx_version3::set_tx_enabled, this, _1)) +        .set(true); //start enabled + +    //set attenuator control bits +    int v3_iobits = TX_ATTN_MASK; +    int v3_tx_mod = ADF4350_PDBRF; + +    //set the gpio directions and atr controls +    self_base->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, v3_tx_mod); +    self_base->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, RXBB_PDB|ADF4350_PDBRF); +    self_base->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, TX_PUP_5V|TX_PUP_3V|v3_tx_mod|v3_iobits); +    self_base->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, RX_PUP_5V|RX_PUP_3V|ADF4350_CE|RXBB_PDB|ADF4350_PDBRF|RX_ATTN_MASK); + +    //setup ATR for the mixer enables (always enabled to prevent phase slip between bursts) +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE,        v3_tx_mod, TX_MIXER_DIS | v3_tx_mod); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY,     v3_tx_mod, TX_MIXER_DIS | v3_tx_mod); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY,     v3_tx_mod, TX_MIXER_DIS | v3_tx_mod); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, v3_tx_mod, TX_MIXER_DIS | v3_tx_mod); + +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE,        RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY,     RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY,     RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); +} + +wbx_base::wbx_version3::~wbx_version3(void){ +    /* NOP */ +} + + +/*********************************************************************** + * Enables + **********************************************************************/ +void wbx_base::wbx_version3::set_tx_enabled(bool enb){ +    self_base->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, +        (enb)? TX_POWER_UP | ADF4350_CE : TX_POWER_DOWN, TX_POWER_UP | TX_POWER_DOWN | 0); +} + + +/*********************************************************************** + * Gain Handling + **********************************************************************/ +double wbx_base::wbx_version3::set_tx_gain(double gain, const std::string &name){ +    assert_has(wbx_v3_tx_gain_ranges.keys(), name, "wbx tx gain name"); +    if(name == "PGA0"){ +        boost::uint16_t io_bits = tx_pga0_gain_to_iobits(gain); +        self_base->_tx_gains[name] = gain; + +        //write the new gain to tx gpio outputs +        self_base->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, io_bits, TX_ATTN_MASK); +    } +    else UHD_THROW_INVALID_CODE_PATH(); +    return self_base->_tx_gains[name]; //shadow +} + + +/*********************************************************************** + * Tuning + **********************************************************************/ +double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double target_freq) { +    UHD_LOGV(often) << boost::format( +        "WBX tune: target frequency %f Mhz" +    ) % (target_freq/1e6) << std::endl; + +    //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) +    static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of +        (0,23) //adf4350_regs_t::PRESCALER_4_5 +        (1,75) //adf4350_regs_t::PRESCALER_8_9 +    ; + +    //map rf divider select output dividers to enums +    static const uhd::dict<int, adf4350_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of +        (1,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV1) +        (2,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV2) +        (4,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV4) +        (8,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV8) +        (16, adf4350_regs_t::RF_DIVIDER_SELECT_DIV16) +    ; + +    double actual_freq, pfd_freq; +    double ref_freq = self_base->get_iface()->get_clock_rate(unit); +    int R=0, BS=0, N=0, FRAC=0, MOD=0; +    int RFdiv = 1; +    adf4350_regs_t::reference_divide_by_2_t T     = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; +    adf4350_regs_t::reference_doubler_t     D     = adf4350_regs_t::REFERENCE_DOUBLER_DISABLED;     + +    //Reference doubler for 50% duty cycle +    // if ref_freq < 12.5MHz enable regs.reference_divide_by_2 +    if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED; + +    //increase RF divider until acceptable VCO frequency +    //start with target_freq*2 because mixer has divide by 2 +    double vco_freq = target_freq*2; +    while (vco_freq < 2.2e9) { +        vco_freq *= 2; +        RFdiv *= 2; +    } + +    //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) +    adf4350_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; + +    /* +     * The goal here is to loop though possible R dividers, +     * band select clock dividers, N (int) dividers, and FRAC  +     * (frac) dividers. +     * +     * Calculate the N and F dividers for each set of values. +     * The loop exists when it meets all of the constraints. +     * The resulting loop values are loaded into the registers. +     * +     * from pg.21 +     * +     * f_pfd = f_ref*(1+D)/(R*(1+T)) +     * f_vco = (N + (FRAC/MOD))*f_pfd +     *    N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD +     * f_rf = f_vco/RFdiv) +     * f_actual = f_rf/2 +     */ +    for(R = 1; R <= 1023; R+=1){ +        //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T) +        pfd_freq = ref_freq*(1+D)/(R*(1+T)); + +        //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth) +        if (pfd_freq > 25e6) continue; + +        //ignore fractional part of tuning +        N = int(std::floor(vco_freq/pfd_freq)); + +        //keep N > minimum int divider requirement +        if (N < prescaler_to_min_int_div[prescaler]) continue; + +        for(BS=1; BS <= 255; BS+=1){ +            //keep the band select frequency at or below 100KHz +            //constraint on band select clock +            if (pfd_freq/BS > 100e3) continue; +            goto done_loop; +        } +    } done_loop: + +    //Fractional-N calculation +    MOD = 4095; //max fractional accuracy +    FRAC = int((vco_freq/pfd_freq - N)*MOD); + +    //Reference divide-by-2 for 50% duty cycle +    // if R even, move one divide by 2 to to regs.reference_divide_by_2 +    if(R % 2 == 0){ +        T = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED; +        R /= 2; +    } + +    //actual frequency calculation +    actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv/2); + + +    UHD_LOGV(often) +        << boost::format("WBX 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("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s" +            ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl +        << boost::format("WBX 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 +    adf4350_regs_t regs; + +    regs.frac_12_bit = FRAC; +    regs.int_16_bit = N; +    regs.mod_12_bit = MOD; +    regs.prescaler = prescaler; +    regs.r_counter_10_bit = R; +    regs.reference_divide_by_2 = T; +    regs.reference_doubler = D; +    regs.band_select_clock_div = BS; +    UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); +    regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; + +    if (unit == dboard_iface::UNIT_RX) { +        freq_range_t rx_lo_5dbm = list_of +            (range_t(0.05e9, 1.4e9)) +        ; + +        freq_range_t rx_lo_2dbm = list_of +            (range_t(1.4e9, 2.2e9)) +        ; + +        if (actual_freq == rx_lo_5dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM; + +        if (actual_freq == rx_lo_2dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_2DBM; + +    } else if (unit == dboard_iface::UNIT_TX) { +        freq_range_t tx_lo_5dbm = list_of +            (range_t(0.05e9, 1.7e9)) +            (range_t(1.9e9, 2.2e9)) +        ; + +        freq_range_t tx_lo_m1dbm = list_of +            (range_t(1.7e9, 1.9e9)) +        ; + +        if (actual_freq == tx_lo_5dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM; + +        if (actual_freq == tx_lo_m1dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_M1DBM; + +    } + +    //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( +            "WBX 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( +        "WBX tune: actual frequency %f Mhz" +    ) % (actual_freq/1e6) << std::endl; +    return actual_freq; +} diff --git a/host/lib/usrp/dboard/db_wbx_version4.cpp b/host/lib/usrp/dboard/db_wbx_version4.cpp new file mode 100644 index 000000000..faaf9e3fd --- /dev/null +++ b/host/lib/usrp/dboard/db_wbx_version4.cpp @@ -0,0 +1,336 @@ +// +// 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/>. +// + +#include "db_wbx_common.hpp" +#include "adf4351_regs.hpp" +#include <uhd/utils/log.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/types/sensors.hpp> +#include <uhd/utils/assert_has.hpp> +#include <uhd/utils/algorithm.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/usrp/dboard_base.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/format.hpp> +#include <boost/math/special_functions/round.hpp> + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign; + + +/*********************************************************************** + * WBX Version 3 Constants + **********************************************************************/ +static const uhd::dict<std::string, gain_range_t> wbx_v4_tx_gain_ranges = map_list_of +    ("PGA0", gain_range_t(0, 31, 1.0)) +; + +static const freq_range_t wbx_v4_freq_range(50.0e6, 2.2e9); + + +/*********************************************************************** + * Gain-related functions + **********************************************************************/ +static int tx_pga0_gain_to_iobits(double &gain){ +    //clip the input +    gain = wbx_v4_tx_gain_ranges["PGA0"].clip(gain); + +    //convert to attenuation +    double attn = wbx_v4_tx_gain_ranges["PGA0"].stop() - gain; + +    //calculate the attenuation +    int attn_code = boost::math::iround(attn); +    int iobits = ( +            (attn_code & 16 ? 0 : TX_ATTN_16) | +            (attn_code &  8 ? 0 : TX_ATTN_8) | +            (attn_code &  4 ? 0 : TX_ATTN_4) | +            (attn_code &  2 ? 0 : TX_ATTN_2) | +            (attn_code &  1 ? 0 : TX_ATTN_1)  +        ) & TX_ATTN_MASK; + +    UHD_LOGV(often) << boost::format( +        "WBX TX Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x" +    ) % attn % attn_code % (iobits & TX_ATTN_MASK) % TX_ATTN_MASK << std::endl; + +    //the actual gain setting +    gain = wbx_v4_tx_gain_ranges["PGA0"].stop() - double(attn_code); + +    return iobits; +} + + +/*********************************************************************** + * WBX Common Implementation + **********************************************************************/ +wbx_base::wbx_version4::wbx_version4(wbx_base *_self_wbx_base) { +    //register our handle on the primary wbx_base instance +    self_base = _self_wbx_base; + +    //////////////////////////////////////////////////////////////////// +    // Register RX properties +    //////////////////////////////////////////////////////////////////// +    this->get_rx_subtree()->create<std::string>("name").set("WBX RX v4"); +    this->get_rx_subtree()->create<double>("freq/value") +         .coerce(boost::bind(&wbx_base::wbx_version4::set_lo_freq, this, dboard_iface::UNIT_RX, _1)) +         .set((wbx_v4_freq_range.start() + wbx_v4_freq_range.stop())/2.0); +    this->get_rx_subtree()->create<meta_range_t>("freq/range").set(wbx_v4_freq_range); + +    //////////////////////////////////////////////////////////////////// +    // Register TX properties +    //////////////////////////////////////////////////////////////////// +    this->get_tx_subtree()->create<std::string>("name").set("WBX TX v4"); +    BOOST_FOREACH(const std::string &name, wbx_v4_tx_gain_ranges.keys()){ +        self_base->get_tx_subtree()->create<double>("gains/"+name+"/value") +            .coerce(boost::bind(&wbx_base::wbx_version4::set_tx_gain, this, _1, name)) +            .set(wbx_v4_tx_gain_ranges[name].start()); +        self_base->get_tx_subtree()->create<meta_range_t>("gains/"+name+"/range") +            .set(wbx_v4_tx_gain_ranges[name]); +    } +    this->get_tx_subtree()->create<double>("freq/value") +         .coerce(boost::bind(&wbx_base::wbx_version4::set_lo_freq, this, dboard_iface::UNIT_TX, _1)) +         .set((wbx_v4_freq_range.start() + wbx_v4_freq_range.stop())/2.0); +    this->get_tx_subtree()->create<meta_range_t>("freq/range").set(wbx_v4_freq_range); +    this->get_tx_subtree()->create<bool>("enabled") +        .subscribe(boost::bind(&wbx_base::wbx_version4::set_tx_enabled, this, _1)) +        .set(true); //start enabled + +    //set attenuator control bits +    int v4_iobits = TX_ATTN_MASK; +    int v4_tx_mod = ADF4351_PDBRF; + +    //set the gpio directions and atr controls +    self_base->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, v4_tx_mod); +    self_base->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, RXBB_PDB|ADF4351_PDBRF); +    self_base->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, TX_PUP_5V|TX_PUP_3V|v4_tx_mod|v4_iobits); +    self_base->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, RX_PUP_5V|RX_PUP_3V|ADF4351_CE|RXBB_PDB|ADF4351_PDBRF|RX_ATTN_MASK); + +    //setup ATR for the mixer enables (always enabled to prevent phase slip between bursts) +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE,        v4_tx_mod, TX_MIXER_DIS | v4_tx_mod); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY,     v4_tx_mod, TX_MIXER_DIS | v4_tx_mod); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY,     v4_tx_mod, TX_MIXER_DIS | v4_tx_mod); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, v4_tx_mod, TX_MIXER_DIS | v4_tx_mod); + +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE,        RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY,     RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY,     RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); +} + +wbx_base::wbx_version4::~wbx_version4(void){ +    /* NOP */ +} + + +/*********************************************************************** + * Enables + **********************************************************************/ +void wbx_base::wbx_version4::set_tx_enabled(bool enb) { +    self_base->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, +        (enb)? TX_POWER_UP | ADF4351_CE : TX_POWER_DOWN, TX_POWER_UP | TX_POWER_DOWN | 0); +} + + +/*********************************************************************** + * Gain Handling + **********************************************************************/ +double wbx_base::wbx_version4::set_tx_gain(double gain, const std::string &name) { +    assert_has(wbx_v4_tx_gain_ranges.keys(), name, "wbx tx gain name"); +    if(name == "PGA0"){ +        boost::uint16_t io_bits = tx_pga0_gain_to_iobits(gain); +        self_base->_tx_gains[name] = gain; + +        //write the new gain to tx gpio outputs +        self_base->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, io_bits, TX_ATTN_MASK); +    } +    else UHD_THROW_INVALID_CODE_PATH(); +    return self_base->_tx_gains[name]; +} + + +/*********************************************************************** + * Tuning + **********************************************************************/ +double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double target_freq) { +    UHD_LOGV(often) << boost::format( +        "WBX tune: target frequency %f Mhz" +    ) % (target_freq/1e6) << std::endl; + +    //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) +    static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of +        (0,23) //adf4351_regs_t::PRESCALER_4_5 +        (1,75) //adf4351_regs_t::PRESCALER_8_9 +    ; + +    //map rf divider select output dividers to enums +    static const uhd::dict<int, adf4351_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of +        (1,  adf4351_regs_t::RF_DIVIDER_SELECT_DIV1) +        (2,  adf4351_regs_t::RF_DIVIDER_SELECT_DIV2) +        (4,  adf4351_regs_t::RF_DIVIDER_SELECT_DIV4) +        (8,  adf4351_regs_t::RF_DIVIDER_SELECT_DIV8) +        (16, adf4351_regs_t::RF_DIVIDER_SELECT_DIV16) +        (32, adf4351_regs_t::RF_DIVIDER_SELECT_DIV32) +        (64, adf4351_regs_t::RF_DIVIDER_SELECT_DIV64) +    ; + +    double actual_freq, pfd_freq; +    double ref_freq = self_base->get_iface()->get_clock_rate(unit); +    int R=0, BS=0, N=0, FRAC=0, MOD=0; +    int RFdiv = 1; +    adf4351_regs_t::reference_divide_by_2_t T     = adf4351_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; +    adf4351_regs_t::reference_doubler_t     D     = adf4351_regs_t::REFERENCE_DOUBLER_DISABLED;     + +    //Reference doubler for 50% duty cycle +    // if ref_freq < 12.5MHz enable regs.reference_divide_by_2 +    if(ref_freq <= 12.5e6) D = adf4351_regs_t::REFERENCE_DOUBLER_ENABLED; + +    //increase RF divider until acceptable VCO frequency +    //start with target_freq*2 because mixer has divide by 2 +    double vco_freq = target_freq*2; +    while (vco_freq < 2.2e9) { +        vco_freq *= 2; +        RFdiv *= 2; +    } + +    //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) +    adf4351_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; + +    /* +     * The goal here is to loop though possible R dividers, +     * band select clock dividers, N (int) dividers, and FRAC  +     * (frac) dividers. +     * +     * Calculate the N and F dividers for each set of values. +     * The loop exits when it meets all of the constraints. +     * The resulting loop values are loaded into the registers. +     * +     * from pg.21 +     * +     * f_pfd = f_ref*(1+D)/(R*(1+T)) +     * f_vco = (N + (FRAC/MOD))*f_pfd +     *    N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD +     * f_rf = f_vco/RFdiv) +     * f_actual = f_rf/2 +     */ +    for(R = 1; R <= 1023; R+=1){ +        //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T) +        pfd_freq = ref_freq*(1+D)/(R*(1+T)); + +        //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth) +        if (pfd_freq > 25e6) continue; + +        //ignore fractional part of tuning +        N = int(std::floor(vco_freq/pfd_freq)); + +        //keep N > minimum int divider requirement +        if (N < prescaler_to_min_int_div[prescaler]) continue; + +        for(BS=1; BS <= 255; BS+=1){ +            //keep the band select frequency at or below 100KHz +            //constraint on band select clock +            if (pfd_freq/BS > 100e3) continue; +            goto done_loop; +        } +    } done_loop: + +    //Fractional-N calculation +    MOD = 4095; //max fractional accuracy +    FRAC = int((vco_freq/pfd_freq - N)*MOD); + +    //Reference divide-by-2 for 50% duty cycle +    // if R even, move one divide by 2 to to regs.reference_divide_by_2 +    if(R % 2 == 0){ +        T = adf4351_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED; +        R /= 2; +    } + +    //actual frequency calculation +    actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv/2); + + +    UHD_LOGV(often) +        << boost::format("WBX 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("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s" +            ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl +        << boost::format("WBX 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 +    adf4351_regs_t regs; + +    regs.frac_12_bit = FRAC; +    regs.int_16_bit = N; +    regs.mod_12_bit = MOD; +    regs.prescaler = prescaler; +    regs.r_counter_10_bit = R; +    regs.reference_divide_by_2 = T; +    regs.reference_doubler = D; +    regs.band_select_clock_div = BS; +    UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); +    regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; + +    if (unit == dboard_iface::UNIT_RX) { +        freq_range_t rx_lo_5dbm = list_of +            (range_t(0.05e9, 1.4e9)) +        ; + +        freq_range_t rx_lo_2dbm = list_of +            (range_t(1.4e9, 2.2e9)) +        ; + +        if (actual_freq == rx_lo_5dbm.clip(actual_freq)) regs.output_power = adf4351_regs_t::OUTPUT_POWER_5DBM; + +        if (actual_freq == rx_lo_2dbm.clip(actual_freq)) regs.output_power = adf4351_regs_t::OUTPUT_POWER_2DBM; + +    } else if (unit == dboard_iface::UNIT_TX) { +        freq_range_t tx_lo_5dbm = list_of +            (range_t(0.05e9, 1.7e9)) +            (range_t(1.9e9, 2.2e9)) +        ; + +        freq_range_t tx_lo_m1dbm = list_of +            (range_t(1.7e9, 1.9e9)) +        ; + +        if (actual_freq == tx_lo_5dbm.clip(actual_freq)) regs.output_power = adf4351_regs_t::OUTPUT_POWER_5DBM; + +        if (actual_freq == tx_lo_m1dbm.clip(actual_freq)) regs.output_power = adf4351_regs_t::OUTPUT_POWER_M1DBM; + +    } + +    //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( +            "WBX 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( +        "WBX tune: actual frequency %f Mhz" +    ) % (actual_freq/1e6) << std::endl; +    return actual_freq; +} diff --git a/host/lib/usrp/dboard/db_xcvr2450.cpp b/host/lib/usrp/dboard/db_xcvr2450.cpp index bfd4421b8..cf1637335 100644 --- a/host/lib/usrp/dboard/db_xcvr2450.cpp +++ b/host/lib/usrp/dboard/db_xcvr2450.cpp @@ -76,7 +76,22 @@ static const freq_range_t xcvr_freq_range = list_of      (range_t(4.9e9, 6.0e9))  ; -static const prop_names_t xcvr_antennas = list_of("J1")("J2"); +//Multiplied by 2.0 for conversion to complex bandpass from lowpass +static const freq_range_t xcvr_tx_bandwidth_range = list_of +    (range_t(2.0*12e6)) +    (range_t(2.0*18e6)) +    (range_t(2.0*24e6)) +; + +//Multiplied by 2.0 for conversion to complex bandpass from lowpass +static const freq_range_t xcvr_rx_bandwidth_range = list_of +    (range_t(2.0*0.9*7.5e6, 2.0*1.1*7.5e6)) +    (range_t(2.0*0.9*9.5e6, 2.0*1.1*9.5e6)) +    (range_t(2.0*0.9*14e6,  2.0*1.1*14e6)) +    (range_t(2.0*0.9*18e6,  2.0*1.1*18e6)) +; + +static const std::vector<std::string> xcvr_antennas = list_of("J1")("J2");  static const uhd::dict<std::string, gain_range_t> xcvr_tx_gain_ranges = map_list_of      ("VGA", gain_range_t(0, 30, 0.5)) @@ -99,12 +114,6 @@ public:      xcvr2450(ctor_args_t args);      ~xcvr2450(void); -    void rx_get(const wax::obj &key, wax::obj &val); -    void rx_set(const wax::obj &key, const wax::obj &val); - -    void tx_get(const wax::obj &key, wax::obj &val); -    void tx_set(const wax::obj &key, const wax::obj &val); -  private:      double _lo_freq;      double _rx_bandwidth, _tx_bandwidth; @@ -113,14 +122,14 @@ private:      int _ad9515div;      max2829_regs_t _max2829_regs; -    void set_lo_freq(double target_freq); -    void set_lo_freq_core(double target_freq); +    double set_lo_freq(double target_freq); +    double set_lo_freq_core(double target_freq);      void set_tx_ant(const std::string &ant);      void set_rx_ant(const std::string &ant); -    void set_tx_gain(double gain, const std::string &name); -    void set_rx_gain(double gain, const std::string &name); -    void set_rx_bandwidth(double bandwidth); -    void set_tx_bandwidth(double bandwidth); +    double set_tx_gain(double gain, const std::string &name); +    double set_rx_gain(double gain, const std::string &name); +    double set_rx_bandwidth(double bandwidth); +    double set_tx_bandwidth(double bandwidth);      void update_atr(void);      void spi_reset(void); @@ -139,18 +148,19 @@ private:      static bool is_highband(double freq){return freq > 3e9;}      /*! -     * Is the LO locked? -     * \return true for locked +     * Get the lock detect status of the LO. +     * \return sensor for locked       */ -    bool get_locked(void){ -        return (this->get_iface()->read_gpio(dboard_iface::UNIT_RX) & LOCKDET_RXIO) != 0; +    sensor_value_t get_locked(void){ +        const bool locked = (this->get_iface()->read_gpio(dboard_iface::UNIT_RX) & LOCKDET_RXIO) != 0; +        return sensor_value_t("LO", locked, "locked", "unlocked");      }      /*!       * Read the RSSI from the aux adc -     * \return the rssi in dB +     * \return the rssi sensor in dBm       */ -    double get_rssi(void){ +    sensor_value_t get_rssi(void){          //*FIXME* RSSI depends on LNA Gain Setting (datasheet pg 16 top middle chart)          double max_power = 0.0;          switch(_max2829_regs.rx_lna_gain){ @@ -165,7 +175,8 @@ private:          static const double rssi_dyn_range = 60;          //calculate the rssi from the voltage          double voltage = this->get_iface()->read_aux_adc(dboard_iface::UNIT_RX, dboard_iface::AUX_ADC_B); -        return max_power - rssi_dyn_range*(voltage - min_v)/(max_v - min_v); +        double rssi = max_power - rssi_dyn_range*(voltage - min_v)/(max_v - min_v); +        return sensor_value_t("RSSI", rssi, "dBm");      }  }; @@ -185,15 +196,6 @@ UHD_STATIC_BLOCK(reg_xcvr2450_dboard){   * Structors   **********************************************************************/  xcvr2450::xcvr2450(ctor_args_t args) : xcvr_dboard_base(args){ -    //enable only the clocks we need -    this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true); - -    //set the gpio directions and atr controls (identically) -    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, TXIO_MASK); -    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, RXIO_MASK); -    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, TXIO_MASK); -    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, RXIO_MASK); -      spi_reset(); //prepare the spi      _rx_bandwidth = 9.5e6; @@ -222,16 +224,88 @@ xcvr2450::xcvr2450(ctor_args_t args) : xcvr_dboard_base(args){          this->send_reg(reg);      } -    //set defaults for LO, gains, antennas -    set_lo_freq(2.45e9); -    set_rx_ant(xcvr_antennas.at(0)); -    set_tx_ant(xcvr_antennas.at(1)); -    BOOST_FOREACH(const std::string &name, xcvr_tx_gain_ranges.keys()){ -        set_tx_gain(xcvr_tx_gain_ranges[name].start(), name); -    } +    //////////////////////////////////////////////////////////////////// +    // Register RX properties +    //////////////////////////////////////////////////////////////////// +    this->get_rx_subtree()->create<std::string>("name") +        .set(get_rx_id().to_pp_string()); +    this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") +        .publish(boost::bind(&xcvr2450::get_locked, this)); +    this->get_rx_subtree()->create<sensor_value_t>("sensors/rssi") +        .publish(boost::bind(&xcvr2450::get_rssi, this));      BOOST_FOREACH(const std::string &name, xcvr_rx_gain_ranges.keys()){ -        set_rx_gain(xcvr_rx_gain_ranges[name].start(), name); +        this->get_rx_subtree()->create<double>("gains/"+name+"/value") +            .coerce(boost::bind(&xcvr2450::set_rx_gain, this, _1, name)) +            .set(xcvr_rx_gain_ranges[name].start()); +        this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range") +            .set(xcvr_rx_gain_ranges[name]);      } +    this->get_rx_subtree()->create<double>("freq/value") +        .coerce(boost::bind(&xcvr2450::set_lo_freq, this, _1)) +        .set(double(2.45e9)); +    this->get_rx_subtree()->create<meta_range_t>("freq/range") +        .set(xcvr_freq_range); +    this->get_rx_subtree()->create<std::string>("antenna/value") +        .subscribe(boost::bind(&xcvr2450::set_rx_ant, this, _1)) +        .set(xcvr_antennas.at(0)); +    this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options") +        .set(xcvr_antennas); +    this->get_rx_subtree()->create<std::string>("connection") +        .set("IQ"); +    this->get_rx_subtree()->create<bool>("enabled") +        .set(true); //always enabled +    this->get_rx_subtree()->create<bool>("use_lo_offset") +        .set(false); +    this->get_rx_subtree()->create<double>("bandwidth/value") +        .coerce(boost::bind(&xcvr2450::set_rx_bandwidth, this, _1)) //complex bandpass bandwidth  +        .set(2.0*_rx_bandwidth); //_rx_bandwidth in lowpass, convert to complex bandpass +    this->get_rx_subtree()->create<meta_range_t>("bandwidth/range") +        .set(xcvr_rx_bandwidth_range); + +    //////////////////////////////////////////////////////////////////// +    // Register TX properties +    //////////////////////////////////////////////////////////////////// +    this->get_tx_subtree()->create<std::string>("name") +        .set(get_tx_id().to_pp_string()); +    this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked") +        .publish(boost::bind(&xcvr2450::get_locked, this)); +    BOOST_FOREACH(const std::string &name, xcvr_tx_gain_ranges.keys()){ +        this->get_tx_subtree()->create<double>("gains/"+name+"/value") +            .coerce(boost::bind(&xcvr2450::set_tx_gain, this, _1, name)) +            .set(xcvr_tx_gain_ranges[name].start()); +        this->get_tx_subtree()->create<meta_range_t>("gains/"+name+"/range") +            .set(xcvr_tx_gain_ranges[name]); +    } +    this->get_tx_subtree()->create<double>("freq/value") +        .coerce(boost::bind(&xcvr2450::set_lo_freq, this, _1)) +        .set(double(2.45e9)); +    this->get_tx_subtree()->create<meta_range_t>("freq/range") +        .set(xcvr_freq_range); +    this->get_tx_subtree()->create<std::string>("antenna/value") +        .subscribe(boost::bind(&xcvr2450::set_tx_ant, this, _1)) +        .set(xcvr_antennas.at(1)); +    this->get_tx_subtree()->create<std::vector<std::string> >("antenna/options") +        .set(xcvr_antennas); +    this->get_tx_subtree()->create<std::string>("connection") +        .set("IQ"); +    this->get_tx_subtree()->create<bool>("enabled") +        .set(true); //always enabled +    this->get_tx_subtree()->create<bool>("use_lo_offset") +        .set(true); +    this->get_tx_subtree()->create<double>("bandwidth/value") +        .coerce(boost::bind(&xcvr2450::set_tx_bandwidth, this, _1)) //complex bandpass bandwidth +        .set(2.0*_tx_bandwidth); //_tx_bandwidth in lowpass, convert to complex bandpass +    this->get_tx_subtree()->create<meta_range_t>("bandwidth/range") +        .set(xcvr_tx_bandwidth_range); + +    //enable only the clocks we need +    this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true); + +    //set the gpio directions and atr controls (identically) +    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, TXIO_MASK); +    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, RXIO_MASK); +    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, TXIO_MASK); +    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, RXIO_MASK);  }  xcvr2450::~xcvr2450(void){ @@ -249,6 +323,9 @@ void xcvr2450::spi_reset(void){      boost::this_thread::sleep(boost::posix_time::milliseconds(10));  } +/*********************************************************************** + * Update ATR regs which change with Antenna or Freq + **********************************************************************/  void xcvr2450::update_atr(void){      //calculate tx atr pins      int band_sel   = (xcvr2450::is_highband(_lo_freq))? HB_PA_TXIO : LB_PA_TXIO; @@ -274,17 +351,19 @@ void xcvr2450::update_atr(void){  /***********************************************************************   * Tuning   **********************************************************************/ -void xcvr2450::set_lo_freq(double target_freq){ +double xcvr2450::set_lo_freq(double target_freq){      //tune the LO and sleep a bit for lock      //if not locked, try some carrier offsets +    double actual = 0.0;      for (double offset = 0.0; offset <= 3e6; offset+=1e6){ -        this->set_lo_freq_core(target_freq + offset); +        actual = this->set_lo_freq_core(target_freq + offset);          boost::this_thread::sleep(boost::posix_time::milliseconds(50)); -        if (this->get_locked()) return; +        if (this->get_locked().to_bool()) break;      } +    return actual;  } -void xcvr2450::set_lo_freq_core(double target_freq){ +double xcvr2450::set_lo_freq_core(double target_freq){      //clip the input to the range      target_freq = xcvr_freq_range.clip(target_freq); @@ -348,6 +427,8 @@ void xcvr2450::set_lo_freq_core(double target_freq){      this->send_reg(0x5);      _max2829_regs.vco_bandswitch = max2829_regs_t::VCO_BANDSWITCH_AUTOMATIC;;      this->send_reg(0x5); + +    return _lo_freq;  }  /*********************************************************************** @@ -441,7 +522,7 @@ static int gain_to_rx_lna_reg(double &gain){      return reg;  } -void xcvr2450::set_tx_gain(double gain, const std::string &name){ +double xcvr2450::set_tx_gain(double gain, const std::string &name){      assert_has(xcvr_tx_gain_ranges.keys(), name, "xcvr tx gain name");      if (name == "VGA"){          _max2829_regs.tx_vga_gain = gain_to_tx_vga_reg(gain); @@ -453,9 +534,11 @@ void xcvr2450::set_tx_gain(double gain, const std::string &name){      }      else UHD_THROW_INVALID_CODE_PATH();      _tx_gains[name] = gain; + +    return gain;  } -void xcvr2450::set_rx_gain(double gain, const std::string &name){ +double xcvr2450::set_rx_gain(double gain, const std::string &name){      assert_has(xcvr_rx_gain_ranges.keys(), name, "xcvr rx gain name");      if (name == "VGA"){          _max2829_regs.rx_vga_gain = gain_to_rx_vga_reg(gain); @@ -467,6 +550,8 @@ void xcvr2450::set_rx_gain(double gain, const std::string &name){      }      else UHD_THROW_INVALID_CODE_PATH();      _rx_gains[name] = gain; + +    return gain;  } @@ -541,9 +626,12 @@ static max2829_regs_t::rx_lpf_coarse_adj_t bandwidth_to_rx_lpf_coarse_reg(double      UHD_THROW_INVALID_CODE_PATH();  } -void xcvr2450::set_rx_bandwidth(double bandwidth){ +double xcvr2450::set_rx_bandwidth(double bandwidth){      double requested_bandwidth = bandwidth; +    //convert complex bandpass to lowpass bandwidth +    bandwidth = bandwidth/2.0; +      //compute coarse low pass cutoff frequency setting      _max2829_regs.rx_lpf_coarse_adj = bandwidth_to_rx_lpf_coarse_reg(bandwidth); @@ -559,9 +647,14 @@ void xcvr2450::set_rx_bandwidth(double bandwidth){      UHD_LOGV(often) << boost::format(          "XCVR2450 RX Bandwidth (lp_fc): %f Hz, coarse reg: %d, fine reg: %d"      ) % _rx_bandwidth % (int(_max2829_regs.rx_lpf_coarse_adj)) % (int(_max2829_regs.rx_lpf_fine_adj)) << std::endl; + +    return 2.0*_rx_bandwidth;  } -void xcvr2450::set_tx_bandwidth(double bandwidth){ +double xcvr2450::set_tx_bandwidth(double bandwidth){ +    //convert complex bandpass to lowpass bandwidth +    bandwidth = bandwidth/2.0; +      //compute coarse low pass cutoff frequency setting      _max2829_regs.tx_lpf_coarse_adj = bandwidth_to_tx_lpf_coarse_reg(bandwidth); @@ -574,219 +667,7 @@ void xcvr2450::set_tx_bandwidth(double bandwidth){      UHD_LOGV(often) << boost::format(          "XCVR2450 TX Bandwidth (lp_fc): %f Hz, coarse reg: %d"      ) % _tx_bandwidth % (int(_max2829_regs.tx_lpf_coarse_adj)) << std::endl; -} - - -/*********************************************************************** - * RX Get and Set - **********************************************************************/ -void xcvr2450::rx_get(const wax::obj &key_, wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ -    case SUBDEV_PROP_NAME: -        val = get_rx_id().to_pp_string(); -        return; - -    case SUBDEV_PROP_OTHERS: -        val = prop_names_t(); //empty -        return; - -    case SUBDEV_PROP_GAIN: -        assert_has(_rx_gains.keys(), key.name, "xcvr rx gain name"); -        val = _rx_gains[key.name]; -        return; - -    case SUBDEV_PROP_GAIN_RANGE: -        assert_has(xcvr_rx_gain_ranges.keys(), key.name, "xcvr rx gain name"); -        val = xcvr_rx_gain_ranges[key.name]; -        return; - -    case SUBDEV_PROP_GAIN_NAMES: -        val = prop_names_t(xcvr_rx_gain_ranges.keys()); -        return; - -    case SUBDEV_PROP_FREQ: -        val = _lo_freq; -        return; - -    case SUBDEV_PROP_FREQ_RANGE: -        val = xcvr_freq_range; -        return; - -    case SUBDEV_PROP_ANTENNA: -        val = _rx_ant; -        return; - -    case SUBDEV_PROP_ANTENNA_NAMES: -        val = xcvr_antennas; -        return; - -    case SUBDEV_PROP_CONNECTION: -        val = SUBDEV_CONN_COMPLEX_IQ; -        return; - -    case SUBDEV_PROP_ENABLED: -        val = true; //always enabled -        return; - -    case SUBDEV_PROP_USE_LO_OFFSET: -        val = false; -        return; - -    case SUBDEV_PROP_SENSOR: -        if (key.name == "lo_locked") -            val = sensor_value_t("LO", this->get_locked(), "locked", "unlocked"); -        else if (key.name == "rssi") -            val = sensor_value_t("RSSI", this->get_rssi(), "dBm"); -        else -            UHD_THROW_INVALID_CODE_PATH(); -        return; - -    case SUBDEV_PROP_SENSOR_NAMES:{ -            prop_names_t names = list_of("lo_locked")("rssi"); -            val = names; -        } -        return; - -    case SUBDEV_PROP_BANDWIDTH: -        val = 2*_rx_bandwidth; //_tx_bandwidth is low-pass, we want complex double-sided -        return; - -    default: UHD_THROW_PROP_GET_ERROR(); -    } -} - -void xcvr2450::rx_set(const wax::obj &key_, const wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ - -    case SUBDEV_PROP_FREQ: -        this->set_lo_freq(val.as<double>()); -        return; - -    case SUBDEV_PROP_GAIN: -        this->set_rx_gain(val.as<double>(), key.name); -        return; - -    case SUBDEV_PROP_ANTENNA: -        this->set_rx_ant(val.as<std::string>()); -        return; - -    case SUBDEV_PROP_BANDWIDTH: -        this->set_rx_bandwidth(val.as<double>()/2.0); //complex double-sided, we want low-pass -        return; -    case SUBDEV_PROP_ENABLED: -        return; //always enabled - -    default: UHD_THROW_PROP_SET_ERROR(); -    } -} - -/*********************************************************************** - * TX Get and Set - **********************************************************************/ -void xcvr2450::tx_get(const wax::obj &key_, wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ -    case SUBDEV_PROP_NAME: -        val = get_tx_id().to_pp_string(); -        return; - -    case SUBDEV_PROP_OTHERS: -        val = prop_names_t(); //empty -        return; - -    case SUBDEV_PROP_GAIN: -        assert_has(_tx_gains.keys(), key.name, "xcvr tx gain name"); -        val = _tx_gains[key.name]; -        return; - -    case SUBDEV_PROP_GAIN_RANGE: -        assert_has(xcvr_tx_gain_ranges.keys(), key.name, "xcvr tx gain name"); -        val = xcvr_tx_gain_ranges[key.name]; -        return; - -    case SUBDEV_PROP_GAIN_NAMES: -        val = prop_names_t(xcvr_tx_gain_ranges.keys()); -        return; - -    case SUBDEV_PROP_FREQ: -        val = _lo_freq; -        return; - -    case SUBDEV_PROP_FREQ_RANGE: -        val = xcvr_freq_range; -        return; - -    case SUBDEV_PROP_ANTENNA: -        val = _tx_ant; -        return; - -    case SUBDEV_PROP_ANTENNA_NAMES: -        val = xcvr_antennas; -        return; - -    case SUBDEV_PROP_CONNECTION: -        val = SUBDEV_CONN_COMPLEX_QI; -        return; - -    case SUBDEV_PROP_ENABLED: -        val = true; //always enabled -        return; - -    case SUBDEV_PROP_USE_LO_OFFSET: -        val = false; -        return; - -    case SUBDEV_PROP_SENSOR: -        UHD_ASSERT_THROW(key.name == "lo_locked"); -        val = sensor_value_t("LO", this->get_locked(), "locked", "unlocked"); -        return; - -    case SUBDEV_PROP_SENSOR_NAMES: -        val = prop_names_t(1, "lo_locked"); -        return; - -    case SUBDEV_PROP_BANDWIDTH: -        val = 2*_tx_bandwidth; //_tx_bandwidth is low-pass, we want complex double-sided -        return; - -    default: UHD_THROW_PROP_GET_ERROR(); -    } -} - -void xcvr2450::tx_set(const wax::obj &key_, const wax::obj &val){ -    named_prop_t key = named_prop_t::extract(key_); - -    //handle the get request conditioned on the key -    switch(key.as<subdev_prop_t>()){ - -    case SUBDEV_PROP_FREQ: -        set_lo_freq(val.as<double>()); -        return; - -    case SUBDEV_PROP_GAIN: -        this->set_tx_gain(val.as<double>(), key.name); -        return; - -    case SUBDEV_PROP_BANDWIDTH: -        this->set_tx_bandwidth(val.as<double>()/2.0); //complex double-sided, we want low-pass -        return; - -    case SUBDEV_PROP_ANTENNA: -        this->set_tx_ant(val.as<std::string>()); -        return; - -    case SUBDEV_PROP_ENABLED: -        return; //always enabled - -    default: UHD_THROW_PROP_SET_ERROR(); -    } +    //convert lowpass back to complex bandpass bandwidth +    return 2.0*_tx_bandwidth;  } diff --git a/host/lib/usrp/dboard_base.cpp b/host/lib/usrp/dboard_base.cpp index e14c9d144..fe14c02b9 100644 --- a/host/lib/usrp/dboard_base.cpp +++ b/host/lib/usrp/dboard_base.cpp @@ -20,6 +20,7 @@  #include <boost/format.hpp>  #include <stdexcept> +using namespace uhd;  using namespace uhd::usrp;  /*********************************************************************** @@ -34,10 +35,6 @@ dboard_base::dboard_base(ctor_args_t args){      _impl->args = *static_cast<dboard_ctor_args_t *>(args);  } -dboard_base::~dboard_base(void){ -   /* NOP */ -} -  std::string dboard_base::get_subdev_name(void){      return _impl->args.sd_name;  } @@ -54,6 +51,14 @@ dboard_id_t dboard_base::get_tx_id(void){      return _impl->args.tx_id;  } +property_tree::sptr dboard_base::get_rx_subtree(void){ +    return _impl->args.rx_subtree; +} + +property_tree::sptr dboard_base::get_tx_subtree(void){ +    return _impl->args.tx_subtree; +} +  /***********************************************************************   * xcvr dboard dboard_base class   **********************************************************************/ @@ -70,10 +75,6 @@ xcvr_dboard_base::xcvr_dboard_base(ctor_args_t args) : dboard_base(args){      }  } -xcvr_dboard_base::~xcvr_dboard_base(void){ -    /* NOP */ -} -  /***********************************************************************   * rx dboard dboard_base class   **********************************************************************/ @@ -86,18 +87,6 @@ rx_dboard_base::rx_dboard_base(ctor_args_t args) : dboard_base(args){      }  } -rx_dboard_base::~rx_dboard_base(void){ -    /* NOP */ -} - -void rx_dboard_base::tx_get(const wax::obj &, wax::obj &){ -    throw uhd::runtime_error("cannot call tx_get on a rx dboard"); -} - -void rx_dboard_base::tx_set(const wax::obj &, const wax::obj &){ -    throw uhd::runtime_error("cannot call tx_set on a rx dboard"); -} -  /***********************************************************************   * tx dboard dboard_base class   **********************************************************************/ @@ -109,15 +98,3 @@ tx_dboard_base::tx_dboard_base(ctor_args_t args) : dboard_base(args){          ) % get_rx_id().to_pp_string() % dboard_id_t::none().to_pp_string()));      }  } - -tx_dboard_base::~tx_dboard_base(void){ -    /* NOP */ -} - -void tx_dboard_base::rx_get(const wax::obj &, wax::obj &){ -    throw uhd::runtime_error("cannot call rx_get on a tx dboard"); -} - -void tx_dboard_base::rx_set(const wax::obj &, const wax::obj &){ -    throw uhd::runtime_error("cannot call rx_set on a tx dboard"); -} diff --git a/host/lib/usrp/dboard_ctor_args.hpp b/host/lib/usrp/dboard_ctor_args.hpp index 708f2ea08..99c071ff8 100644 --- a/host/lib/usrp/dboard_ctor_args.hpp +++ b/host/lib/usrp/dboard_ctor_args.hpp @@ -18,6 +18,7 @@  #ifndef INCLUDED_LIBUHD_USRP_DBOARD_CTOR_ARGS_HPP  #define INCLUDED_LIBUHD_USRP_DBOARD_CTOR_ARGS_HPP +#include <uhd/property_tree.hpp>  #include <uhd/usrp/dboard_id.hpp>  #include <uhd/usrp/dboard_base.hpp>  #include <uhd/usrp/dboard_iface.hpp> @@ -29,6 +30,7 @@ namespace uhd{ namespace usrp{          std::string               sd_name;          dboard_iface::sptr        db_iface;          dboard_id_t               rx_id, tx_id; +        property_tree::sptr       rx_subtree, tx_subtree;      };  }} //namespace diff --git a/host/lib/usrp/dboard_manager.cpp b/host/lib/usrp/dboard_manager.cpp index 0326c28ce..816fba0c4 100644 --- a/host/lib/usrp/dboard_manager.cpp +++ b/host/lib/usrp/dboard_manager.cpp @@ -79,7 +79,7 @@ bool operator==(const dboard_key_t &lhs, const dboard_key_t &rhs){   * storage and registering for dboards   **********************************************************************/  //dboard registry tuple: dboard constructor, canonical name, subdev names -typedef boost::tuple<dboard_manager::dboard_ctor_t, std::string, prop_names_t> args_t; +typedef boost::tuple<dboard_manager::dboard_ctor_t, std::string, std::vector<std::string> > args_t;  //map a dboard id to a dboard constructor  typedef uhd::dict<dboard_key_t, args_t> id_to_args_map_t; @@ -89,7 +89,7 @@ static void register_dboard_key(      const dboard_key_t &dboard_key,      dboard_manager::dboard_ctor_t dboard_ctor,      const std::string &name, -    const prop_names_t &subdev_names +    const std::vector<std::string> &subdev_names  ){      UHD_LOGV(always) << "registering: " << name << std::endl;      if (get_id_to_args_map().has_key(dboard_key)){ @@ -110,7 +110,7 @@ void dboard_manager::register_dboard(      const dboard_id_t &dboard_id,      dboard_ctor_t dboard_ctor,      const std::string &name, -    const prop_names_t &subdev_names +    const std::vector<std::string> &subdev_names  ){      register_dboard_key(dboard_key_t(dboard_id), dboard_ctor, name, subdev_names);  } @@ -120,7 +120,7 @@ void dboard_manager::register_dboard(      const dboard_id_t &tx_dboard_id,      dboard_ctor_t dboard_ctor,      const std::string &name, -    const prop_names_t &subdev_names +    const std::vector<std::string> &subdev_names  ){      register_dboard_key(dboard_key_t(rx_dboard_id, tx_dboard_id), dboard_ctor, name, subdev_names);  } @@ -144,46 +144,6 @@ std::string dboard_id_t::to_pp_string(void) const{  }  /*********************************************************************** - * internal helper classes - **********************************************************************/ -/*! - * A special wax proxy object that forwards calls to a subdev. - * A sptr to an instance will be used in the properties structure.  - */ -class subdev_proxy : boost::noncopyable, public wax::obj{ -public: -    typedef boost::shared_ptr<subdev_proxy> sptr; -    enum type_t{RX_TYPE, TX_TYPE}; - -    //structors -    subdev_proxy(dboard_base::sptr subdev, type_t type): -        _subdev(subdev), _type(type) -    { -        /* NOP */ -    } - -private: -    dboard_base::sptr   _subdev; -    type_t              _type; - -    //forward the get calls to the rx or tx -    void get(const wax::obj &key, wax::obj &val){ -        switch(_type){ -        case RX_TYPE: return _subdev->rx_get(key, val); -        case TX_TYPE: return _subdev->tx_get(key, val); -        } -    } - -    //forward the set calls to the rx or tx -    void set(const wax::obj &key, const wax::obj &val){ -        switch(_type){ -        case RX_TYPE: return _subdev->rx_set(key, val); -        case TX_TYPE: return _subdev->tx_set(key, val); -        } -    } -}; - -/***********************************************************************   * dboard manager implementation class   **********************************************************************/  class dboard_manager_impl : public dboard_manager{ @@ -192,23 +152,18 @@ public:      dboard_manager_impl(          dboard_id_t rx_dboard_id,          dboard_id_t tx_dboard_id, -        dboard_iface::sptr iface +        dboard_iface::sptr iface, +        property_tree::sptr subtree      );      ~dboard_manager_impl(void); -    //dboard_iface -    prop_names_t get_rx_subdev_names(void); -    prop_names_t get_tx_subdev_names(void); -    wax::obj get_rx_subdev(const std::string &subdev_name); -    wax::obj get_tx_subdev(const std::string &subdev_name); -  private: -    void init(dboard_id_t, dboard_id_t); +    void init(dboard_id_t, dboard_id_t, property_tree::sptr);      //list of rx and tx dboards in this dboard_manager      //each dboard here is actually a subdevice proxy      //the subdevice proxy is internal to the cpp file -    uhd::dict<std::string, subdev_proxy::sptr> _rx_dboards; -    uhd::dict<std::string, subdev_proxy::sptr> _tx_dboards; +    uhd::dict<std::string, dboard_base::sptr> _rx_dboards; +    uhd::dict<std::string, dboard_base::sptr> _tx_dboards;      dboard_iface::sptr _iface;      void set_nice_dboard_if(void);  }; @@ -219,10 +174,16 @@ private:  dboard_manager::sptr dboard_manager::make(      dboard_id_t rx_dboard_id,      dboard_id_t tx_dboard_id, -    dboard_iface::sptr iface +    dboard_id_t gdboard_id, +    dboard_iface::sptr iface, +    property_tree::sptr subtree  ){      return dboard_manager::sptr( -        new dboard_manager_impl(rx_dboard_id, tx_dboard_id, iface) +        new dboard_manager_impl( +            rx_dboard_id, +            (gdboard_id == dboard_id_t::none())? tx_dboard_id : gdboard_id, +            iface, subtree +        )      );  } @@ -232,21 +193,22 @@ dboard_manager::sptr dboard_manager::make(  dboard_manager_impl::dboard_manager_impl(      dboard_id_t rx_dboard_id,      dboard_id_t tx_dboard_id, -    dboard_iface::sptr iface +    dboard_iface::sptr iface, +    property_tree::sptr subtree  ):      _iface(iface)  {      try{ -        this->init(rx_dboard_id, tx_dboard_id); +        this->init(rx_dboard_id, tx_dboard_id, subtree);      }      catch(const std::exception &e){          UHD_MSG(error) << "The daughterboard manager encountered a recoverable error in init" << std::endl << e.what(); -        this->init(dboard_id_t::none(), dboard_id_t::none()); +        this->init(dboard_id_t::none(), dboard_id_t::none(), subtree);      }  }  void dboard_manager_impl::init( -    dboard_id_t rx_dboard_id, dboard_id_t tx_dboard_id +    dboard_id_t rx_dboard_id, dboard_id_t tx_dboard_id, property_tree::sptr subtree  ){      //find the dboard key matches for the dboard ids      dboard_key_t rx_dboard_key, tx_dboard_key, xcvr_dboard_key; @@ -283,7 +245,7 @@ void dboard_manager_impl::init(      if (xcvr_dboard_key.is_xcvr()){          //extract data for the xcvr dboard key -        dboard_ctor_t dboard_ctor; std::string name; prop_names_t subdevs; +        dboard_ctor_t dboard_ctor; std::string name; std::vector<std::string> subdevs;          boost::tie(dboard_ctor, name, subdevs) = get_id_to_args_map()[xcvr_dboard_key];          //create the xcvr object for each subdevice @@ -291,15 +253,11 @@ void dboard_manager_impl::init(              db_ctor_args.sd_name = subdev;              db_ctor_args.rx_id = rx_dboard_id;              db_ctor_args.tx_id = tx_dboard_id; +            db_ctor_args.rx_subtree = subtree->subtree("rx_frontends/" + subdev); +            db_ctor_args.tx_subtree = subtree->subtree("tx_frontends/" + subdev);              dboard_base::sptr xcvr_dboard = dboard_ctor(&db_ctor_args); -            //create a rx proxy for this xcvr board -            _rx_dboards[subdev] = subdev_proxy::sptr( -                new subdev_proxy(xcvr_dboard, subdev_proxy::RX_TYPE) -            ); -            //create a tx proxy for this xcvr board -            _tx_dboards[subdev] = subdev_proxy::sptr( -                new subdev_proxy(xcvr_dboard, subdev_proxy::TX_TYPE) -            ); +            _rx_dboards[subdev] = xcvr_dboard; +            _tx_dboards[subdev] = xcvr_dboard;          }      } @@ -312,7 +270,7 @@ void dboard_manager_impl::init(          }          //extract data for the rx dboard key -        dboard_ctor_t rx_dboard_ctor; std::string rx_name; prop_names_t rx_subdevs; +        dboard_ctor_t rx_dboard_ctor; std::string rx_name; std::vector<std::string> rx_subdevs;          boost::tie(rx_dboard_ctor, rx_name, rx_subdevs) = get_id_to_args_map()[rx_dboard_key];          //make the rx subdevs @@ -320,11 +278,9 @@ void dboard_manager_impl::init(              db_ctor_args.sd_name = subdev;              db_ctor_args.rx_id = rx_dboard_id;              db_ctor_args.tx_id = dboard_id_t::none(); -            dboard_base::sptr rx_dboard = rx_dboard_ctor(&db_ctor_args); -            //create a rx proxy for this rx board -            _rx_dboards[subdev] = subdev_proxy::sptr( -                new subdev_proxy(rx_dboard, subdev_proxy::RX_TYPE) -            ); +            db_ctor_args.rx_subtree = subtree->subtree("rx_frontends/" + subdev); +            db_ctor_args.tx_subtree = property_tree::sptr(); //null +            _rx_dboards[subdev] = rx_dboard_ctor(&db_ctor_args);          }          //force the tx key to the unknown board for bad combinations @@ -333,7 +289,7 @@ void dboard_manager_impl::init(          }          //extract data for the tx dboard key -        dboard_ctor_t tx_dboard_ctor; std::string tx_name; prop_names_t tx_subdevs; +        dboard_ctor_t tx_dboard_ctor; std::string tx_name; std::vector<std::string> tx_subdevs;          boost::tie(tx_dboard_ctor, tx_name, tx_subdevs) = get_id_to_args_map()[tx_dboard_key];          //make the tx subdevs @@ -341,11 +297,9 @@ void dboard_manager_impl::init(              db_ctor_args.sd_name = subdev;              db_ctor_args.rx_id = dboard_id_t::none();              db_ctor_args.tx_id = tx_dboard_id; -            dboard_base::sptr tx_dboard = tx_dboard_ctor(&db_ctor_args); -            //create a tx proxy for this tx board -            _tx_dboards[subdev] = subdev_proxy::sptr( -                new subdev_proxy(tx_dboard, subdev_proxy::TX_TYPE) -            ); +            db_ctor_args.rx_subtree = property_tree::sptr(); //null +            db_ctor_args.tx_subtree = subtree->subtree("tx_frontends/" + subdev); +            _tx_dboards[subdev] = tx_dboard_ctor(&db_ctor_args);          }      }  } @@ -354,30 +308,6 @@ dboard_manager_impl::~dboard_manager_impl(void){UHD_SAFE_CALL(      set_nice_dboard_if();  )} -prop_names_t dboard_manager_impl::get_rx_subdev_names(void){ -    return _rx_dboards.keys(); -} - -prop_names_t dboard_manager_impl::get_tx_subdev_names(void){ -    return _tx_dboards.keys(); -} - -wax::obj dboard_manager_impl::get_rx_subdev(const std::string &subdev_name){ -    if (not _rx_dboards.has_key(subdev_name)) throw uhd::key_error( -        str(boost::format("Unknown rx subdev name %s") % subdev_name) -    ); -    //get a link to the rx subdev proxy -    return _rx_dboards[subdev_name]->get_link(); -} - -wax::obj dboard_manager_impl::get_tx_subdev(const std::string &subdev_name){ -    if (not _tx_dboards.has_key(subdev_name)) throw uhd::key_error( -        str(boost::format("Unknown tx subdev name %s") % subdev_name) -    ); -    //get a link to the tx subdev proxy -    return _tx_dboards[subdev_name]->get_link(); -} -  void dboard_manager_impl::set_nice_dboard_if(void){      //make a list of possible unit types      std::vector<dboard_iface::unit_t> units = boost::assign::list_of @@ -392,137 +322,4 @@ void dboard_manager_impl::set_nice_dboard_if(void){          _iface->set_pin_ctrl(unit, 0x0000); //all gpio          _iface->set_clock_enabled(unit, false); //clock off      } - -    //disable all rx subdevices -    BOOST_FOREACH(const std::string &sd_name, this->get_rx_subdev_names()){ -        this->get_rx_subdev(sd_name)[SUBDEV_PROP_ENABLED] = false; -    } - -    //disable all tx subdevices -    BOOST_FOREACH(const std::string &sd_name, this->get_tx_subdev_names()){ -        this->get_tx_subdev(sd_name)[SUBDEV_PROP_ENABLED] = false; -    } -} - -/*********************************************************************** - * Populate a properties tree from a subdev waxy object - **********************************************************************/ -#include <uhd/types/ranges.hpp> -#include <uhd/types/sensors.hpp> - -static sensor_value_t get_sensor(wax::obj subdev, const std::string &name){ -    return subdev[named_prop_t(SUBDEV_PROP_SENSOR, name)].as<sensor_value_t>(); -} - -static void set_gain(wax::obj subdev, const std::string &name, const double gain){ -    subdev[named_prop_t(SUBDEV_PROP_GAIN, name)] = gain; -} - -static double get_gain(wax::obj subdev, const std::string &name){ -    return subdev[named_prop_t(SUBDEV_PROP_GAIN, name)].as<double>(); -} - -static meta_range_t get_gain_range(wax::obj subdev, const std::string &name){ -    return subdev[named_prop_t(SUBDEV_PROP_GAIN_RANGE, name)].as<meta_range_t>(); -} - -static void set_freq(wax::obj subdev, const double freq){ -    subdev[SUBDEV_PROP_FREQ] = freq; -} - -static double get_freq(wax::obj subdev){ -    return subdev[SUBDEV_PROP_FREQ].as<double>(); -} - -static meta_range_t get_freq_range(wax::obj subdev){ -    return subdev[SUBDEV_PROP_FREQ_RANGE].as<meta_range_t>(); -} - -static void set_ant(wax::obj subdev, const std::string &ant){ -    subdev[SUBDEV_PROP_ANTENNA] = ant; -} - -static std::string get_ant(wax::obj subdev){ -    return subdev[SUBDEV_PROP_ANTENNA].as<std::string>(); -} - -static std::vector<std::string> get_ants(wax::obj subdev){ -    return subdev[SUBDEV_PROP_ANTENNA_NAMES].as<std::vector<std::string> >(); -} - -static std::string get_conn(wax::obj subdev){ -    switch(subdev[SUBDEV_PROP_CONNECTION].as<subdev_conn_t>()){ -    case SUBDEV_CONN_COMPLEX_IQ: return "IQ"; -    case SUBDEV_CONN_COMPLEX_QI: return "QI"; -    case SUBDEV_CONN_REAL_I: return "I"; -    case SUBDEV_CONN_REAL_Q: return "Q"; -    } -    UHD_THROW_INVALID_CODE_PATH(); -} - -static bool get_use_lo_off(wax::obj subdev){ -    return subdev[SUBDEV_PROP_USE_LO_OFFSET].as<bool>(); -} - -static bool get_set_enb(wax::obj subdev, const bool enb){ -    subdev[SUBDEV_PROP_ENABLED] = enb; -    return subdev[SUBDEV_PROP_ENABLED].as<bool>(); -} - -static void set_bw(wax::obj subdev, const double freq){ -    subdev[SUBDEV_PROP_BANDWIDTH] = freq; -} - -static double get_bw(wax::obj subdev){ -    return subdev[SUBDEV_PROP_BANDWIDTH].as<double>(); -} - -void dboard_manager::populate_prop_tree_from_subdev( -    property_tree::sptr subtree, wax::obj subdev -){ -    subtree->create<std::string>("name").set(subdev[SUBDEV_PROP_NAME].as<std::string>()); - -    const prop_names_t sensor_names = subdev[SUBDEV_PROP_SENSOR_NAMES].as<prop_names_t>(); -    subtree->create<int>("sensors"); //phony property so this dir exists -    BOOST_FOREACH(const std::string &name, sensor_names){ -        subtree->create<sensor_value_t>("sensors/" + name) -            .publish(boost::bind(&get_sensor, subdev, name)); -    } - -    const prop_names_t gain_names = subdev[SUBDEV_PROP_GAIN_NAMES].as<prop_names_t>(); -    subtree->create<int>("gains"); //phony property so this dir exists -    BOOST_FOREACH(const std::string &name, gain_names){ -        subtree->create<double>("gains/" + name + "/value") -            .publish(boost::bind(&get_gain, subdev, name)) -            .subscribe(boost::bind(&set_gain, subdev, name, _1)); -        subtree->create<meta_range_t>("gains/" + name + "/range") -            .publish(boost::bind(&get_gain_range, subdev, name)); -    } - -    subtree->create<double>("freq/value") -        .publish(boost::bind(&get_freq, subdev)) -        .subscribe(boost::bind(&set_freq, subdev, _1)); - -    subtree->create<meta_range_t>("freq/range") -        .publish(boost::bind(&get_freq_range, subdev)); - -    subtree->create<std::string>("antenna/value") -        .publish(boost::bind(&get_ant, subdev)) -        .subscribe(boost::bind(&set_ant, subdev, _1)); - -    subtree->create<std::vector<std::string> >("antenna/options") -        .publish(boost::bind(&get_ants, subdev)); - -    subtree->create<std::string>("connection") -        .publish(boost::bind(&get_conn, subdev)); - -    subtree->create<bool>("enabled") -        .coerce(boost::bind(&get_set_enb, subdev, _1)); - -    subtree->create<bool>("use_lo_offset") -        .publish(boost::bind(&get_use_lo_off, subdev)); - -    subtree->create<double>("bandwidth/value") -        .publish(boost::bind(&get_bw, subdev)) -        .subscribe(boost::bind(&set_bw, subdev, _1));  } diff --git a/host/lib/usrp/e100/dboard_iface.cpp b/host/lib/usrp/e100/dboard_iface.cpp index d45577bd9..6afc7bc48 100644 --- a/host/lib/usrp/e100/dboard_iface.cpp +++ b/host/lib/usrp/e100/dboard_iface.cpp @@ -15,7 +15,7 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // -#include "wb_iface.hpp" +#include "gpio_core_200.hpp"  #include <uhd/types/serial.hpp>  #include "e100_regs.hpp"  #include "clock_ctrl.hpp" @@ -45,13 +45,11 @@ public:          _spi_iface = spi_iface;          _clock = clock;          _codec = codec; +        _gpio = gpio_core_200::make(_wb_iface, E100_REG_SR_ADDR(UE_SR_GPIO), E100_REG_RB_GPIO);          //init the clock rate shadows          this->set_clock_rate(UNIT_RX, _clock->get_fpga_clock_rate());          this->set_clock_rate(UNIT_TX, _clock->get_fpga_clock_rate()); - -        _wb_iface->poke16(E100_REG_GPIO_RX_DBG, 0); -        _wb_iface->poke16(E100_REG_GPIO_TX_DBG, 0);      }      ~e100_dboard_iface(void){ @@ -104,6 +102,7 @@ private:      spi_iface::sptr _spi_iface;      e100_clock_ctrl::sptr _clock;      e100_codec_ctrl::sptr _codec; +    gpio_core_200::sptr _gpio;  };  /*********************************************************************** @@ -160,77 +159,27 @@ double e100_dboard_iface::get_codec_rate(unit_t){   * GPIO   **********************************************************************/  void e100_dboard_iface::_set_pin_ctrl(unit_t unit, boost::uint16_t value){ -    UHD_ASSERT_THROW(GPIO_SEL_ATR == 1); //make this assumption -    switch(unit){ -    case UNIT_RX: _wb_iface->poke16(E100_REG_GPIO_RX_SEL, value); return; -    case UNIT_TX: _wb_iface->poke16(E100_REG_GPIO_TX_SEL, value); return; -    } +    return _gpio->set_pin_ctrl(unit, value);  }  void e100_dboard_iface::_set_gpio_ddr(unit_t unit, boost::uint16_t value){ -    switch(unit){ -    case UNIT_RX: _wb_iface->poke16(E100_REG_GPIO_RX_DDR, value); return; -    case UNIT_TX: _wb_iface->poke16(E100_REG_GPIO_TX_DDR, value); return; -    } +    return _gpio->set_gpio_ddr(unit, value);  }  void e100_dboard_iface::_set_gpio_out(unit_t unit, boost::uint16_t value){ -    switch(unit){ -    case UNIT_RX: _wb_iface->poke16(E100_REG_GPIO_RX_IO, value); return; -    case UNIT_TX: _wb_iface->poke16(E100_REG_GPIO_TX_IO, value); return; -    } +    return _gpio->set_gpio_out(unit, value);  }  boost::uint16_t e100_dboard_iface::read_gpio(unit_t unit){ -    switch(unit){ -    case UNIT_RX: return _wb_iface->peek16(E100_REG_GPIO_RX_IO); -    case UNIT_TX: return _wb_iface->peek16(E100_REG_GPIO_TX_IO); -    default: UHD_THROW_INVALID_CODE_PATH(); -    } +    return _gpio->read_gpio(unit);  }  void e100_dboard_iface::_set_atr_reg(unit_t unit, atr_reg_t atr, boost::uint16_t value){ -    //define mapping of unit to atr regs to register address -    static const uhd::dict< -        unit_t, uhd::dict<atr_reg_t, boost::uint32_t> -    > unit_to_atr_to_addr = map_list_of -        (UNIT_RX, map_list_of -            (ATR_REG_IDLE,        E100_REG_ATR_IDLE_RXSIDE) -            (ATR_REG_TX_ONLY,     E100_REG_ATR_INTX_RXSIDE) -            (ATR_REG_RX_ONLY,     E100_REG_ATR_INRX_RXSIDE) -            (ATR_REG_FULL_DUPLEX, E100_REG_ATR_FULL_RXSIDE) -        ) -        (UNIT_TX, map_list_of -            (ATR_REG_IDLE,        E100_REG_ATR_IDLE_TXSIDE) -            (ATR_REG_TX_ONLY,     E100_REG_ATR_INTX_TXSIDE) -            (ATR_REG_RX_ONLY,     E100_REG_ATR_INRX_TXSIDE) -            (ATR_REG_FULL_DUPLEX, E100_REG_ATR_FULL_TXSIDE) -        ) -    ; -    _wb_iface->poke16(unit_to_atr_to_addr[unit][atr], value); +    return _gpio->set_atr_reg(unit, atr, value);  } -void e100_dboard_iface::set_gpio_debug(unit_t unit, int which){ -    //set this unit to all outputs -    this->set_gpio_ddr(unit, 0xffff); - -    //calculate the debug selections -    boost::uint32_t dbg_sels = 0x0; -    int sel = (which == 0)? GPIO_SEL_DEBUG_0 : GPIO_SEL_DEBUG_1; -    for(size_t i = 0; i < 16; i++) dbg_sels |= sel << i; - -    //set the debug on and which debug selection -    switch(unit){ -    case UNIT_RX: -        _wb_iface->poke16(E100_REG_GPIO_RX_DBG, 0xffff); -        _wb_iface->poke16(E100_REG_GPIO_RX_SEL, dbg_sels); -        return; - -    case UNIT_TX: -        _wb_iface->poke16(E100_REG_GPIO_TX_DBG, 0xffff); -        _wb_iface->poke16(E100_REG_GPIO_TX_SEL, dbg_sels); -        return; -    } +void e100_dboard_iface::set_gpio_debug(unit_t, int){ +    throw uhd::not_implemented_error("no set_gpio_debug implemented");  }  /*********************************************************************** diff --git a/host/lib/usrp/e100/e100_impl.cpp b/host/lib/usrp/e100/e100_impl.cpp index 564a05a7e..c0a8f46f3 100644 --- a/host/lib/usrp/e100/e100_impl.cpp +++ b/host/lib/usrp/e100/e100_impl.cpp @@ -106,6 +106,7 @@ UHD_STATIC_BLOCK(register_e100_device){   * Structors   **********************************************************************/  e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ +    _tree = property_tree::make();      //setup the main interface into fpga      const std::string node = device_addr["node"]; @@ -167,15 +168,7 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){      );      //check that the compatibility is correct -    const boost::uint16_t fpga_compat_num = _fpga_ctrl->peek16(E100_REG_MISC_COMPAT); -    if (fpga_compat_num != E100_FPGA_COMPAT_NUM){ -        throw uhd::runtime_error(str(boost::format( -            "\nPlease update the FPGA image for your device.\n" -            "See the application notes for USRP E-Series for instructions.\n" -            "Expected FPGA compatibility number 0x%x, but got 0x%x:\n" -            "The FPGA build is not compatible with the host code build." -        ) % E100_FPGA_COMPAT_NUM % fpga_compat_num)); -    } +    this->check_fpga_compat();      ////////////////////////////////////////////////////////////////////      // Create controller objects @@ -187,7 +180,6 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){      ////////////////////////////////////////////////////////////////////      // Initialize the properties tree      //////////////////////////////////////////////////////////////////// -    _tree = property_tree::make();      _tree->create<std::string>("/name").set("E-Series Device");      const fs_path mb_path = "/mboards/0";      _tree->create<std::string>(mb_path / "name").set(str(boost::format("%s (euewanee)") % model)); @@ -250,12 +242,31 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){      ////////////////////////////////////////////////////////////////////      _rx_fe = rx_frontend_core_200::make(_fpga_ctrl, E100_REG_SR_ADDR(UE_SR_RX_FRONT));      _tx_fe = tx_frontend_core_200::make(_fpga_ctrl, E100_REG_SR_ADDR(UE_SR_TX_FRONT)); -    //TODO lots of properties to expose here for frontends +      _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec")          .subscribe(boost::bind(&e100_impl::update_rx_subdev_spec, this, _1));      _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec")          .subscribe(boost::bind(&e100_impl::update_tx_subdev_spec, this, _1)); +    const fs_path rx_fe_path = mb_path / "rx_frontends" / "A"; +    const fs_path tx_fe_path = mb_path / "tx_frontends" / "A"; + +    _tree->create<std::complex<double> >(rx_fe_path / "dc_offset" / "value") +        .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, _rx_fe, _1)) +        .set(std::complex<double>(0.0, 0.0)); +    _tree->create<bool>(rx_fe_path / "dc_offset" / "enable") +        .subscribe(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, _rx_fe, _1)) +        .set(true); +    _tree->create<std::complex<double> >(rx_fe_path / "iq_balance" / "value") +        .subscribe(boost::bind(&rx_frontend_core_200::set_iq_balance, _rx_fe, _1)) +        .set(std::complex<double>(0.0, 0.0)); +    _tree->create<std::complex<double> >(tx_fe_path / "dc_offset" / "value") +        .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, _tx_fe, _1)) +        .set(std::complex<double>(0.0, 0.0)); +    _tree->create<std::complex<double> >(tx_fe_path / "iq_balance" / "value") +        .subscribe(boost::bind(&tx_frontend_core_200::set_iq_balance, _tx_fe, _1)) +        .set(std::complex<double>(0.0, 0.0)); +      ////////////////////////////////////////////////////////////////////      // create rx dsp control objects      //////////////////////////////////////////////////////////////////// @@ -270,9 +281,12 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){          _tree->access<double>(mb_path / "tick_rate")              .subscribe(boost::bind(&rx_dsp_core_200::set_tick_rate, _rx_dsps[dspno], _1));          fs_path rx_dsp_path = mb_path / str(boost::format("rx_dsps/%u") % dspno); +        _tree->create<meta_range_t>(rx_dsp_path / "rate/range") +            .publish(boost::bind(&rx_dsp_core_200::get_host_rates, _rx_dsps[dspno]));          _tree->create<double>(rx_dsp_path / "rate/value") +            .set(1e6) //some default              .coerce(boost::bind(&rx_dsp_core_200::set_host_rate, _rx_dsps[dspno], _1)) -            .subscribe(boost::bind(&e100_impl::update_rx_samp_rate, this, _1)); +            .subscribe(boost::bind(&e100_impl::update_rx_samp_rate, this, dspno, _1));          _tree->create<double>(rx_dsp_path / "freq/value")              .coerce(boost::bind(&rx_dsp_core_200::set_freq, _rx_dsps[dspno], _1));          _tree->create<meta_range_t>(rx_dsp_path / "freq/range") @@ -290,9 +304,12 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){      _tx_dsp->set_link_rate(E100_TX_LINK_RATE_BPS);      _tree->access<double>(mb_path / "tick_rate")          .subscribe(boost::bind(&tx_dsp_core_200::set_tick_rate, _tx_dsp, _1)); +    _tree->create<meta_range_t>(mb_path / "tx_dsps/0/rate/range") +        .publish(boost::bind(&tx_dsp_core_200::get_host_rates, _tx_dsp));      _tree->create<double>(mb_path / "tx_dsps/0/rate/value") +        .set(1e6) //some default          .coerce(boost::bind(&tx_dsp_core_200::set_host_rate, _tx_dsp, _1)) -        .subscribe(boost::bind(&e100_impl::update_tx_samp_rate, this, _1)); +        .subscribe(boost::bind(&e100_impl::update_tx_samp_rate, this, 0, _1));      _tree->create<double>(mb_path / "tx_dsps/0/freq/value")          .coerce(boost::bind(&tx_dsp_core_200::set_freq, _tx_dsp, _1));      _tree->create<meta_range_t>(mb_path / "tx_dsps/0/freq/range") @@ -353,22 +370,9 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){      _dboard_iface = make_e100_dboard_iface(_fpga_ctrl, _fpga_i2c_ctrl, _fpga_spi_ctrl, _clock_ctrl, _codec_ctrl);      _tree->create<dboard_iface::sptr>(mb_path / "dboards/A/iface").set(_dboard_iface);      _dboard_manager = dboard_manager::make( -        rx_db_eeprom.id, -        ((gdb_eeprom.id == dboard_id_t::none())? tx_db_eeprom : gdb_eeprom).id, -        _dboard_iface +        rx_db_eeprom.id, tx_db_eeprom.id, gdb_eeprom.id, +        _dboard_iface, _tree->subtree(mb_path / "dboards/A")      ); -    BOOST_FOREACH(const std::string &name, _dboard_manager->get_rx_subdev_names()){ -        dboard_manager::populate_prop_tree_from_subdev( -            _tree->subtree(mb_path / "dboards/A/rx_frontends" / name), -            _dboard_manager->get_rx_subdev(name) -        ); -    } -    BOOST_FOREACH(const std::string &name, _dboard_manager->get_tx_subdev_names()){ -        dboard_manager::populate_prop_tree_from_subdev( -            _tree->subtree(mb_path / "dboards/A/tx_frontends" / name), -            _dboard_manager->get_tx_subdev(name) -        ); -    }      //initialize io handling      this->io_init(); @@ -376,19 +380,13 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){      ////////////////////////////////////////////////////////////////////      // do some post-init tasks      //////////////////////////////////////////////////////////////////// -    _tree->access<double>(mb_path / "tick_rate").update() //update and then subscribe the clock callback -        .subscribe(boost::bind(&e100_clock_ctrl::set_fpga_clock_rate, _clock_ctrl, _1)); +    this->update_rates(); -    //and now that the tick rate is set, init the host rates to something -    BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "rx_dsps")){ -        _tree->access<double>(mb_path / "rx_dsps" / name / "rate" / "value").set(1e6); -    } -    BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "tx_dsps")){ -        _tree->access<double>(mb_path / "tx_dsps" / name / "rate" / "value").set(1e6); -    } +    _tree->access<double>(mb_path / "tick_rate") //now subscribe the clock rate setter +        .subscribe(boost::bind(&e100_clock_ctrl::set_fpga_clock_rate, _clock_ctrl, _1)); -    _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(subdev_spec_t("A:"+_dboard_manager->get_rx_subdev_names()[0])); -    _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(subdev_spec_t("A:"+_dboard_manager->get_tx_subdev_names()[0])); +    _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(subdev_spec_t("A:" + _tree->list(mb_path / "dboards/A/rx_frontends").at(0))); +    _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(subdev_spec_t("A:" + _tree->list(mb_path / "dboards/A/tx_frontends").at(0)));      _tree->access<std::string>(mb_path / "clock_source/value").set("internal");      _tree->access<std::string>(mb_path / "time_source/value").set("none"); @@ -436,3 +434,19 @@ sensor_value_t e100_impl::get_ref_locked(void){      const bool lock = _clock_ctrl->get_locked();      return sensor_value_t("Ref", lock, "locked", "unlocked");  } + +void e100_impl::check_fpga_compat(void){ +    const boost::uint32_t fpga_compat_num = _fpga_ctrl->peek32(E100_REG_RB_COMPAT); +    boost::uint16_t fpga_major = fpga_compat_num >> 16, fpga_minor = fpga_compat_num & 0xffff; +    if (fpga_major == 0){ //old version scheme +        fpga_major = fpga_minor; +        fpga_minor = 0; +    } +    if (fpga_major != E100_FPGA_COMPAT_NUM){ +        throw uhd::runtime_error(str(boost::format( +            "Expected FPGA compatibility number %d, but got %d:\n" +            "The FPGA build is not compatible with the host code build." +        ) % int(E100_FPGA_COMPAT_NUM) % fpga_major)); +    } +    _tree->create<std::string>("/mboards/0/fpga_version").set(str(boost::format("%u.%u") % fpga_major % fpga_minor)); +} diff --git a/host/lib/usrp/e100/e100_impl.hpp b/host/lib/usrp/e100/e100_impl.hpp index 4b2ec5ee0..f3e481b93 100644 --- a/host/lib/usrp/e100/e100_impl.hpp +++ b/host/lib/usrp/e100/e100_impl.hpp @@ -33,11 +33,10 @@  #include <uhd/usrp/mboard_eeprom.hpp>  #include <uhd/usrp/gps_ctrl.hpp>  #include <uhd/types/sensors.hpp> -#include <uhd/types/otw_type.hpp> -#include <uhd/types/clock_config.hpp>  #include <uhd/types/stream_cmd.hpp>  #include <uhd/usrp/dboard_manager.hpp>  #include <uhd/transport/zero_copy.hpp> +#include <boost/weak_ptr.hpp>  #ifndef INCLUDED_E100_IMPL_HPP  #define INCLUDED_E100_IMPL_HPP @@ -49,7 +48,7 @@ static const double          E100_RX_LINK_RATE_BPS = 166e6/3/2*2;  static const double          E100_TX_LINK_RATE_BPS = 166e6/3/1*2;  static const std::string     E100_I2C_DEV_NODE = "/dev/i2c-3";  static const std::string     E100_UART_DEV_NODE = "/dev/ttyO0"; -static const boost::uint16_t E100_FPGA_COMPAT_NUM = 0x06; +static const boost::uint16_t E100_FPGA_COMPAT_NUM = 0x08;  static const boost::uint32_t E100_RX_SID_BASE = 2;  static const boost::uint32_t E100_TX_ASYNC_SID = 1;  static const double          E100_DEFAULT_CLOCK_RATE = 64e6; @@ -78,11 +77,9 @@ public:      ~e100_impl(void);      //the io interface -    size_t send(const send_buffs_type &, size_t, const uhd::tx_metadata_t &, const uhd::io_type_t &, send_mode_t, double); -    size_t recv(const recv_buffs_type &, size_t, uhd::rx_metadata_t &, const uhd::io_type_t &, recv_mode_t, double); +    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); -    size_t get_max_send_samps_per_packet(void) const; -    size_t get_max_recv_samps_per_packet(void) const;  private:      uhd::property_tree::sptr _tree; @@ -110,7 +107,6 @@ private:      uhd::usrp::dboard_iface::sptr _dboard_iface;      //handle io stuff -    uhd::otw_type_t _rx_otw_type, _tx_otw_type;      UHD_PIMPL_DECL(io_impl) _io_impl;      void io_init(void); @@ -119,16 +115,21 @@ private:          return _tree;      } +    std::vector<boost::weak_ptr<uhd::rx_streamer> > _rx_streamers; +    std::vector<boost::weak_ptr<uhd::tx_streamer> > _tx_streamers; +      double update_rx_codec_gain(const double); //sets A and B at once      void set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &);      void set_db_eeprom(const std::string &, const uhd::usrp::dboard_eeprom_t &);      void update_tick_rate(const double rate); -    void update_rx_samp_rate(const double rate); -    void update_tx_samp_rate(const double rate); +    void update_rx_samp_rate(const size_t, const double rate); +    void update_tx_samp_rate(const size_t, const double rate); +    void update_rates(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_clock_source(const std::string &);      uhd::sensor_value_t get_ref_locked(void); +    void check_fpga_compat(void);  }; diff --git a/host/lib/usrp/e100/e100_regs.hpp b/host/lib/usrp/e100/e100_regs.hpp index 28ef707dc..f24f5895b 100644 --- a/host/lib/usrp/e100/e100_regs.hpp +++ b/host/lib/usrp/e100/e100_regs.hpp @@ -31,7 +31,6 @@  #define E100_REG_MISC_RX_LEN     E100_REG_MISC_BASE + 10  #define E100_REG_MISC_TX_LEN     E100_REG_MISC_BASE + 12  #define E100_REG_MISC_XFER_RATE  E100_REG_MISC_BASE + 14 -#define E100_REG_MISC_COMPAT     E100_REG_MISC_BASE + 16  /////////////////////////////////////////////////////  // Slave 1 -- UART @@ -67,43 +66,6 @@  #define E100_REG_ERR_BUFF E100_REG_SLAVE(5) -//////////////////////////////////////////////// -// Slave 4 -- GPIO - -#define E100_REG_GPIO_BASE E100_REG_SLAVE(4) - -#define E100_REG_GPIO_RX_IO      E100_REG_GPIO_BASE + 0 -#define E100_REG_GPIO_TX_IO      E100_REG_GPIO_BASE + 2 -#define E100_REG_GPIO_RX_DDR     E100_REG_GPIO_BASE + 4 -#define E100_REG_GPIO_TX_DDR     E100_REG_GPIO_BASE + 6 -#define E100_REG_GPIO_RX_SEL     E100_REG_GPIO_BASE + 8 -#define E100_REG_GPIO_TX_SEL     E100_REG_GPIO_BASE + 10 -#define E100_REG_GPIO_RX_DBG     E100_REG_GPIO_BASE + 12 -#define E100_REG_GPIO_TX_DBG     E100_REG_GPIO_BASE + 14 - -//possible bit values for sel when dbg is 0: -#define GPIO_SEL_SW    0 // if pin is an output, set by software in the io reg -#define GPIO_SEL_ATR   1 // if pin is an output, set by ATR logic - -//possible bit values for sel when dbg is 1: -#define GPIO_SEL_DEBUG_0   0 // if pin is an output, debug lines from FPGA fabric -#define GPIO_SEL_DEBUG_1   1 // if pin is an output, debug lines from FPGA fabric - -/////////////////////////////////////////////////// -// Slave 6 -- ATR Controller -//   16 regs - -#define E100_REG_ATR_BASE  E100_REG_SLAVE(6) - -#define	E100_REG_ATR_IDLE_RXSIDE  E100_REG_ATR_BASE + 0 -#define	E100_REG_ATR_IDLE_TXSIDE  E100_REG_ATR_BASE + 2 -#define E100_REG_ATR_INTX_RXSIDE  E100_REG_ATR_BASE + 4 -#define E100_REG_ATR_INTX_TXSIDE  E100_REG_ATR_BASE + 6 -#define	E100_REG_ATR_INRX_RXSIDE  E100_REG_ATR_BASE + 8 -#define	E100_REG_ATR_INRX_TXSIDE  E100_REG_ATR_BASE + 10 -#define	E100_REG_ATR_FULL_RXSIDE  E100_REG_ATR_BASE + 12 -#define	E100_REG_ATR_FULL_TXSIDE  E100_REG_ATR_BASE + 14 -  ///////////////////////////////////////////////////  // Slave 7 -- Readback Mux 32 @@ -115,6 +77,8 @@  #define E100_REG_RB_TIME_PPS_TICKS  E100_REG_RB_MUX_32_BASE + 12  #define E100_REG_RB_MISC_TEST32     E100_REG_RB_MUX_32_BASE + 16  #define E100_REG_RB_ERR_STATUS      E100_REG_RB_MUX_32_BASE + 20 +#define E100_REG_RB_COMPAT          E100_REG_RB_MUX_32_BASE + 24 +#define E100_REG_RB_GPIO            E100_REG_RB_MUX_32_BASE + 28  ////////////////////////////////////////////////////  // Slave 8 -- Settings Bus @@ -141,6 +105,8 @@  #define UE_SR_CLEAR_TX_FIFO 62 // 1 reg  #define UE_SR_GLOBAL_RESET 63  // 1 reg +#define UE_SR_GPIO 128 +  #define E100_REG_SR_ADDR(n) (E100_REG_SLAVE(8) + (4*(n)))  #define E100_REG_SR_MISC_TEST32        E100_REG_SR_ADDR(UE_SR_REG_TEST32) diff --git a/host/lib/usrp/e100/io_impl.cpp b/host/lib/usrp/e100/io_impl.cpp index 0b81c1a86..3b0828b45 100644 --- a/host/lib/usrp/e100/io_impl.cpp +++ b/host/lib/usrp/e100/io_impl.cpp @@ -35,6 +35,7 @@  #include <fcntl.h> //open, close  #include <sstream>  #include <fstream> +#include <boost/make_shared.hpp>  using namespace uhd;  using namespace uhd::usrp; @@ -60,10 +61,6 @@ struct e100_impl::io_impl{      //which is after the states and booty which may hold managed buffers.      recv_packet_demuxer::sptr demuxer; -    //state management for the vrt packet handler code -    sph::recv_packet_handler recv_handler; -    sph::send_packet_handler send_handler; -      //a pirate's life is the life for me!      void recv_pirate_loop(          spi_iface::sptr //keep a sptr to iface which shares gpio147 @@ -159,16 +156,6 @@ void e100_impl::io_impl::handle_irq(void){   **********************************************************************/  void e100_impl::io_init(void){ -    //setup rx otw type -    _rx_otw_type.width = 16; -    _rx_otw_type.shift = 0; -    _rx_otw_type.byteorder = uhd::otw_type_t::BO_LITTLE_ENDIAN; - -    //setup tx otw type -    _tx_otw_type.width = 16; -    _tx_otw_type.shift = 0; -    _tx_otw_type.byteorder = uhd::otw_type_t::BO_LITTLE_ENDIAN; -      //create new io impl      _io_impl = UHD_PIMPL_MAKE(io_impl, ());      _io_impl->demuxer = recv_packet_demuxer::make(_data_transport, _rx_dsps.size(), E100_RX_SID_BASE); @@ -178,6 +165,10 @@ void e100_impl::io_init(void){      _fpga_ctrl->poke32(E100_REG_CLEAR_RX, 0);      _fpga_ctrl->poke32(E100_REG_CLEAR_TX, 0); +    //allocate streamer weak ptrs containers +    _rx_streamers.resize(_rx_dsps.size()); +    _tx_streamers.resize(1/*known to be 1 dsp*/); +      //prepare the async msg buffer for incoming messages      _fpga_ctrl->poke32(E100_REG_SR_ERR_CTRL, 1 << 0); //clear      while ((_fpga_ctrl->peek32(E100_REG_RB_ERR_STATUS) & (1 << 2)) == 0){} //wait for idle @@ -187,37 +178,58 @@ void e100_impl::io_init(void){      _io_impl->pirate_task = task::make(boost::bind(          &e100_impl::io_impl::recv_pirate_loop, _io_impl.get(), _aux_spi_iface      )); - -    //init some handler stuff -    _io_impl->recv_handler.set_vrt_unpacker(&vrt::if_hdr_unpack_le); -    _io_impl->recv_handler.set_converter(_rx_otw_type); -    _io_impl->send_handler.set_vrt_packer(&vrt::if_hdr_pack_le); -    _io_impl->send_handler.set_converter(_tx_otw_type); -    _io_impl->send_handler.set_max_samples_per_packet(get_max_send_samps_per_packet());  }  void e100_impl::update_tick_rate(const double rate){      _io_impl->tick_rate = rate; -    boost::mutex::scoped_lock recv_lock = _io_impl->recv_handler.get_scoped_lock(); -    _io_impl->recv_handler.set_tick_rate(rate); -    boost::mutex::scoped_lock send_lock = _io_impl->send_handler.get_scoped_lock(); -    _io_impl->send_handler.set_tick_rate(rate); + +    //update the tick rate on all existing streamers -> thread safe +    for (size_t i = 0; i < _rx_streamers.size(); i++){ +        boost::shared_ptr<sph::recv_packet_streamer> my_streamer = +            boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_rx_streamers[i].lock()); +        if (my_streamer.get() == NULL) continue; +        my_streamer->set_tick_rate(rate); +    } +    for (size_t i = 0; i < _tx_streamers.size(); i++){ +        boost::shared_ptr<sph::send_packet_streamer> my_streamer = +            boost::dynamic_pointer_cast<sph::send_packet_streamer>(_tx_streamers[i].lock()); +        if (my_streamer.get() == NULL) continue; +        my_streamer->set_tick_rate(rate); +    }  } -void e100_impl::update_rx_samp_rate(const double rate){ -    boost::mutex::scoped_lock recv_lock = _io_impl->recv_handler.get_scoped_lock(); -    _io_impl->recv_handler.set_samp_rate(rate); -    const double adj = _rx_dsps.front()->get_scaling_adjustment(); -    _io_impl->recv_handler.set_scale_factor(adj/32767.); +void e100_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>(_rx_streamers[dspno].lock()); +    if (my_streamer.get() == NULL) return; + +    my_streamer->set_samp_rate(rate); +    const double adj = _rx_dsps[dspno]->get_scaling_adjustment(); +    my_streamer->set_scale_factor(adj); +} + +void e100_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>(_tx_streamers[dspno].lock()); +    if (my_streamer.get() == NULL) return; + +    my_streamer->set_samp_rate(rate);  } -void e100_impl::update_tx_samp_rate(const double rate){ -    boost::mutex::scoped_lock send_lock = _io_impl->send_handler.get_scoped_lock(); -    _io_impl->send_handler.set_samp_rate(rate); +void e100_impl::update_rates(void){ +    const fs_path mb_path = "/mboards/0"; +    _tree->access<double>(mb_path / "tick_rate").update(); + +    //and now that the tick rate is set, init the host rates to something +    BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "rx_dsps")){ +        _tree->access<double>(mb_path / "rx_dsps" / name / "rate" / "value").update(); +    } +    BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "tx_dsps")){ +        _tree->access<double>(mb_path / "tx_dsps" / name / "rate" / "value").update(); +    }  }  void e100_impl::update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){ -    boost::mutex::scoped_lock recv_lock = _io_impl->recv_handler.get_scoped_lock();      fs_path root = "/mboards/0/dboards";      //sanity checking @@ -231,22 +243,9 @@ void e100_impl::update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){          _rx_dsps[i]->set_mux(conn, fe_swapped);      }      _rx_fe->set_mux(fe_swapped); - -    //resize for the new occupancy -    _io_impl->recv_handler.resize(spec.size()); - -    //bind new callbacks for the handler -    for (size_t i = 0; i < _io_impl->recv_handler.size(); i++){ -        _rx_dsps[i]->set_nsamps_per_packet(get_max_recv_samps_per_packet()); //seems to be a good place to set this -        _io_impl->recv_handler.set_xport_chan_get_buff(i, boost::bind( -            &recv_packet_demuxer::get_recv_buff, _io_impl->demuxer, i, _1 -        )); -        _io_impl->recv_handler.set_overflow_handler(i, boost::bind(&rx_dsp_core_200::handle_overflow, _rx_dsps[i])); -    }  }  void e100_impl::update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){ -    boost::mutex::scoped_lock send_lock = _io_impl->send_handler.get_scoped_lock();      fs_path root = "/mboards/0/dboards";      //sanity checking @@ -255,73 +254,122 @@ void e100_impl::update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){      //set the mux for this spec      const std::string conn = _tree->access<std::string>(root / spec[0].db_name / "tx_frontends" / spec[0].sd_name / "connection").get();      _tx_fe->set_mux(conn); +} -    //resize for the new occupancy -    _io_impl->send_handler.resize(spec.size()); - -    //bind new callbacks for the handler -    for (size_t i = 0; i < _io_impl->send_handler.size(); i++){ -        _io_impl->send_handler.set_xport_chan_get_buff(i, boost::bind( -            &zero_copy_if::get_send_buff, _data_transport, _1 -        )); -    } +/*********************************************************************** + * Async Recv + **********************************************************************/ +bool e100_impl::recv_async_msg( +    async_metadata_t &async_metadata, double timeout +){ +    boost::this_thread::disable_interruption di; //disable because the wait can throw +    return _io_impl->async_msg_fifo.pop_with_timed_wait(async_metadata, timeout);  }  /*********************************************************************** - * Data Send + * Receive streamer   **********************************************************************/ -size_t e100_impl::get_max_send_samps_per_packet(void) const{ +rx_streamer::sptr e100_impl::get_rx_stream(const uhd::stream_args_t &args_){ +    stream_args_t args = args_; + +    //setup defaults for unspecified values +    args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format; +    args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; +    const unsigned sc8_scalar = unsigned(args.args.cast<double>("scalar", 0x400)); + +    //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      ; -    size_t bpp = _data_transport->get_send_frame_size() - hdr_size; -    return bpp/_tx_otw_type.get_sample_size(); -} +    const size_t bpp = _data_transport->get_recv_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 +    boost::shared_ptr<sph::recv_packet_streamer> my_streamer = boost::make_shared<sph::recv_packet_streamer>(spp); + +    //init some streamer stuff +    my_streamer->resize(args.channels.size()); +    my_streamer->set_vrt_unpacker(&vrt::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); + +    //bind callbacks for the handler +    for (size_t chan_i = 0; chan_i < args.channels.size(); chan_i++){ +        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]->set_format(args.otw_format, sc8_scalar); +        my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( +            &recv_packet_demuxer::get_recv_buff, _io_impl->demuxer, dsp, _1 +        )); +        my_streamer->set_overflow_handler(chan_i, boost::bind( +            &rx_dsp_core_200::handle_overflow, _rx_dsps[dsp] +        )); +        _rx_streamers[dsp] = my_streamer; //store weak pointer +    } -size_t e100_impl::send( -    const send_buffs_type &buffs, size_t nsamps_per_buff, -    const tx_metadata_t &metadata, const io_type_t &io_type, -    send_mode_t send_mode, double timeout -){ -    return _io_impl->send_handler.send( -        buffs, nsamps_per_buff, -        metadata, io_type, -        send_mode, timeout -    ); +    //sets all tick and samp rates on this streamer +    this->update_rates(); + +    return my_streamer;  }  /*********************************************************************** - * Data Recv + * Transmit streamer   **********************************************************************/ -size_t e100_impl::get_max_recv_samps_per_packet(void) const{ +tx_streamer::sptr e100_impl::get_tx_stream(const uhd::stream_args_t &args_){ +    stream_args_t args = args_; + +    //setup defaults for unspecified values +    args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format; +    args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; + +    if (args.otw_format != "sc16"){ +        throw uhd::value_error("USRP TX cannot handle requested wire format: " + args.otw_format); +    } + +    //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      ; -    size_t bpp = _data_transport->get_recv_frame_size() - hdr_size; -    return bpp/_rx_otw_type.get_sample_size(); -} +    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 +    boost::shared_ptr<sph::send_packet_streamer> my_streamer = boost::make_shared<sph::send_packet_streamer>(spp); + +    //init some streamer stuff +    my_streamer->resize(args.channels.size()); +    my_streamer->set_vrt_packer(&vrt::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); + +    //bind callbacks for the handler +    for (size_t chan_i = 0; chan_i < args.channels.size(); chan_i++){ +        const size_t dsp = args.channels[chan_i]; +        UHD_ASSERT_THROW(dsp == 0); //always 0 +        my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( +            &zero_copy_if::get_send_buff, _data_transport, _1 +        )); +        _tx_streamers[dsp] = my_streamer; //store weak pointer +    } -size_t e100_impl::recv( -    const recv_buffs_type &buffs, size_t nsamps_per_buff, -    rx_metadata_t &metadata, const io_type_t &io_type, -    recv_mode_t recv_mode, double timeout -){ -    return _io_impl->recv_handler.recv( -        buffs, nsamps_per_buff, -        metadata, io_type, -        recv_mode, timeout -    ); -} +    //sets all tick and samp rates on this streamer +    this->update_rates(); -/*********************************************************************** - * Async Recv - **********************************************************************/ -bool e100_impl::recv_async_msg( -    async_metadata_t &async_metadata, double timeout -){ -    boost::this_thread::disable_interruption di; //disable because the wait can throw -    return _io_impl->async_msg_fifo.pop_with_timed_wait(async_metadata, timeout); +    return my_streamer;  } diff --git a/host/lib/usrp/gps_ctrl.cpp b/host/lib/usrp/gps_ctrl.cpp index c645d2948..45fa1f39e 100644 --- a/host/lib/usrp/gps_ctrl.cpp +++ b/host/lib/usrp/gps_ctrl.cpp @@ -17,7 +17,6 @@  #include <uhd/usrp/gps_ctrl.hpp>  #include <uhd/utils/msg.hpp> -#include <uhd/utils/props.hpp>  #include <uhd/exception.hpp>  #include <uhd/types/sensors.hpp>  #include <boost/algorithm/string.hpp> @@ -121,7 +120,7 @@ public:          return sensor_value_t("GPS lock status", locked(), "locked", "unlocked");      }      else { -        UHD_THROW_PROP_GET_ERROR(); +        throw uhd::value_error("gps ctrl get_sensor unknown key: " + key);      }    } diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp index 73699dc81..1110f5ebd 100644 --- a/host/lib/usrp/multi_usrp.cpp +++ b/host/lib/usrp/multi_usrp.cpp @@ -17,7 +17,6 @@  #include <uhd/property_tree.hpp>  #include <uhd/usrp/multi_usrp.hpp> -#include <uhd/usrp/mboard_iface.hpp>  #include <uhd/utils/msg.hpp>  #include <uhd/exception.hpp>  #include <uhd/utils/msg.hpp> @@ -348,6 +347,14 @@ public:          return true;      } +    void set_command_time(const time_spec_t &, size_t){ +        throw uhd::not_implemented_error("Not implemented yet, but we have a very good idea of how to do it."); +    } + +    void clear_command_time(size_t){ +        throw uhd::not_implemented_error("Not implemented yet, but we have a very good idea of how to do it."); +    } +      void issue_stream_cmd(const stream_cmd_t &stream_cmd, size_t chan){          if (chan != ALL_CHANS){              _tree->access<stream_cmd_t>(rx_dsp_root(chan) / "stream_cmd").set(stream_cmd); @@ -359,34 +366,64 @@ public:      }      void set_clock_config(const clock_config_t &clock_config, size_t mboard){ +        //set the reference source... +        std::string clock_source; +        switch(clock_config.ref_source){ +        case clock_config_t::REF_INT: clock_source = "internal"; break; +        case clock_config_t::PPS_SMA: clock_source = "external"; break; +        case clock_config_t::PPS_MIMO: clock_source = "mimo"; break; +        default: clock_source = "unknown"; +        } +        this->set_clock_source(clock_source, mboard); + +        //set the time source +        std::string time_source; +        switch(clock_config.pps_source){ +        case clock_config_t::PPS_INT: time_source = "internal"; break; +        case clock_config_t::PPS_SMA: time_source = "external"; break; +        case clock_config_t::PPS_MIMO: time_source = "mimo"; break; +        default: time_source = "unknown"; +        } +        if (time_source == "external" and clock_config.pps_polarity == clock_config_t::PPS_NEG) time_source = "_external_"; +        this->set_time_source(time_source, mboard); +    } + +    void set_time_source(const std::string &source, const size_t mboard){          if (mboard != ALL_MBOARDS){ -            //set the reference source... -            std::string clock_source; -            switch(clock_config.ref_source){ -            case clock_config_t::REF_INT: clock_source = "internal"; break; -            case clock_config_t::PPS_SMA: clock_source = "external"; break; -            case clock_config_t::PPS_MIMO: clock_source = "mimo"; break; -            default: clock_source = "unknown"; -            } -            _tree->access<std::string>(mb_root(mboard) / "clock_source" / "value").set(clock_source); - -            //set the time source -            std::string time_source; -            switch(clock_config.pps_source){ -            case clock_config_t::PPS_INT: time_source = "internal"; break; -            case clock_config_t::PPS_SMA: time_source = "external"; break; -            case clock_config_t::PPS_MIMO: time_source = "mimo"; break; -            default: time_source = "unknown"; -            } -            if (clock_source == "external" and clock_config.pps_polarity == clock_config_t::PPS_NEG) time_source = "_external_"; -            _tree->access<std::string>(mb_root(mboard) / "time_source" / "value").set(time_source); +            _tree->access<std::string>(mb_root(mboard) / "time_source" / "value").set(source);              return;          }          for (size_t m = 0; m < get_num_mboards(); m++){ -            set_clock_config(clock_config, m); +            return this->set_time_source(source, m);          }      } +    std::string get_time_source(const size_t mboard){ +        return _tree->access<std::string>(mb_root(mboard) / "time_source" / "value").get(); +    } + +    std::vector<std::string> get_time_sources(const size_t mboard){ +        return _tree->access<std::vector<std::string> >(mb_root(mboard) / "time_source" / "options").get(); +    } + +    void set_clock_source(const std::string &source, const size_t mboard){ +        if (mboard != ALL_MBOARDS){ +            _tree->access<std::string>(mb_root(mboard) / "clock_source" / "value").set(source); +            return; +        } +        for (size_t m = 0; m < get_num_mboards(); m++){ +            return this->set_clock_source(source, m); +        } +    } + +    std::string get_clock_source(const size_t mboard){ +        return _tree->access<std::string>(mb_root(mboard) / "clock_source" / "value").get(); +    } + +    std::vector<std::string> get_clock_sources(const size_t mboard){ +        return _tree->access<std::vector<std::string> >(mb_root(mboard) / "clock_source" / "options").get(); +    } +      size_t get_num_mboards(void){          return _tree->list("/mboards").size();      } @@ -399,10 +436,6 @@ public:          return _tree->list(mb_root(mboard) / "sensors");      } -    mboard_iface::sptr get_mboard_iface(size_t){ -        return mboard_iface::sptr(); //not implemented -    } -      /*******************************************************************       * RX methods       ******************************************************************/ @@ -447,6 +480,10 @@ public:          return _tree->access<double>(rx_dsp_root(chan) / "rate" / "value").get();      } +    meta_range_t get_rx_rates(size_t chan){ +        return _tree->access<meta_range_t>(rx_dsp_root(chan) / "rate" / "range").get(); +    } +      tune_result_t set_rx_freq(const tune_request_t &tune_request, size_t chan){          tune_result_t r = tune_xx_subdev_and_dsp(RX_SIGN, _tree->subtree(rx_dsp_root(chan)), _tree->subtree(rx_rf_fe_root(chan)), tune_request);          do_tune_freq_warning_message(tune_request.target_freq, get_rx_freq(chan), "RX"); @@ -501,6 +538,10 @@ public:          return _tree->access<double>(rx_rf_fe_root(chan) / "bandwidth" / "value").get();      } +    meta_range_t get_rx_bandwidth_range(size_t chan){ +        return _tree->access<meta_range_t>(rx_rf_fe_root(chan) / "bandwidth" / "range").get(); +    } +      dboard_iface::sptr get_rx_dboard_iface(size_t chan){          return _tree->access<dboard_iface::sptr>(rx_rf_fe_root(chan).branch_path().branch_path() / "iface").get();      } @@ -513,6 +554,36 @@ public:          return _tree->list(rx_rf_fe_root(chan) / "sensors");      } +    void set_rx_dc_offset(const bool enb, size_t chan){ +        if (chan != ALL_CHANS){ +            _tree->access<bool>(rx_fe_root(chan) / "dc_offset" / "enable").set(enb); +            return; +        } +        for (size_t c = 0; c < get_rx_num_channels(); c++){ +            this->set_rx_dc_offset(enb, c); +        } +    } + +    void set_rx_dc_offset(const std::complex<double> &offset, size_t chan){ +        if (chan != ALL_CHANS){ +            _tree->access<std::complex<double> >(rx_fe_root(chan) / "dc_offset" / "value").set(offset); +            return; +        } +        for (size_t c = 0; c < get_rx_num_channels(); c++){ +            this->set_rx_dc_offset(offset, c); +        } +    } + +    void set_rx_iq_balance(const std::complex<double> &offset, size_t chan){ +        if (chan != ALL_CHANS){ +            _tree->access<std::complex<double> >(rx_fe_root(chan) / "iq_balance" / "value").set(offset); +            return; +        } +        for (size_t c = 0; c < get_rx_num_channels(); c++){ +            this->set_rx_iq_balance(offset, c); +        } +    } +      /*******************************************************************       * TX methods       ******************************************************************/ @@ -557,6 +628,10 @@ public:          return _tree->access<double>(tx_dsp_root(chan) / "rate" / "value").get();      } +    meta_range_t get_tx_rates(size_t chan){ +        return _tree->access<meta_range_t>(tx_dsp_root(chan) / "rate" / "range").get(); +    } +      tune_result_t set_tx_freq(const tune_request_t &tune_request, size_t chan){          tune_result_t r = tune_xx_subdev_and_dsp(TX_SIGN, _tree->subtree(tx_dsp_root(chan)), _tree->subtree(tx_rf_fe_root(chan)), tune_request);          do_tune_freq_warning_message(tune_request.target_freq, get_tx_freq(chan), "TX"); @@ -611,6 +686,10 @@ public:          return _tree->access<double>(tx_rf_fe_root(chan) / "bandwidth" / "value").get();      } +    meta_range_t get_tx_bandwidth_range(size_t chan){ +        return _tree->access<meta_range_t>(tx_rf_fe_root(chan) / "bandwidth" / "range").get(); +    } +      dboard_iface::sptr get_tx_dboard_iface(size_t chan){          return _tree->access<dboard_iface::sptr>(tx_rf_fe_root(chan).branch_path().branch_path() / "iface").get();      } @@ -623,6 +702,26 @@ public:          return _tree->list(tx_rf_fe_root(chan) / "sensors");      } +    void set_tx_dc_offset(const std::complex<double> &offset, size_t chan){ +        if (chan != ALL_CHANS){ +            _tree->access<std::complex<double> >(tx_fe_root(chan) / "dc_offset" / "value").set(offset); +            return; +        } +        for (size_t c = 0; c < get_tx_num_channels(); c++){ +            this->set_tx_dc_offset(offset, c); +        } +    } + +    void set_tx_iq_balance(const std::complex<double> &offset, size_t chan){ +        if (chan != ALL_CHANS){ +            _tree->access<std::complex<double> >(tx_fe_root(chan) / "iq_balance" / "value").set(offset); +            return; +        } +        for (size_t c = 0; c < get_tx_num_channels(); c++){ +            this->set_tx_iq_balance(offset, c); +        } +    } +  private:      device::sptr _dev;      property_tree::sptr _tree; @@ -671,6 +770,18 @@ private:          return mb_root(mcp.mboard) / "tx_dsps" / name;      } +    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; +    } + +    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; +    } +      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); diff --git a/host/lib/usrp/usrp1/dboard_iface.cpp b/host/lib/usrp/usrp1/dboard_iface.cpp index 449ec64fe..34bbe1893 100644 --- a/host/lib/usrp/usrp1/dboard_iface.cpp +++ b/host/lib/usrp/usrp1/dboard_iface.cpp @@ -39,7 +39,7 @@ public:      usrp1_dboard_iface(usrp1_iface::sptr iface,                         usrp1_codec_ctrl::sptr codec,                         usrp1_impl::dboard_slot_t dboard_slot, -                       const double master_clock_rate, +                       const double &master_clock_rate,                         const dboard_id_t &rx_dboard_id      ):          _dboard_slot(dboard_slot), @@ -49,10 +49,8 @@ public:          _iface = iface;          _codec = codec; -        //init the clock rate shadows -        this->set_clock_rate(UNIT_RX, this->get_clock_rates(UNIT_RX).front()); -        this->set_clock_rate(UNIT_TX, this->get_clock_rates(UNIT_TX).front()); -         +        _dbsrx_classic_div = 1; +          //yes this is evil but it's necessary for TVRX to work on USRP1          if(_rx_dboard_id == tvrx_id) _codec->bypass_adc_buffers(false);          //else _codec->bypass_adc_buffers(false); //don't think this is necessary @@ -103,9 +101,9 @@ public:  private:      usrp1_iface::sptr _iface;      usrp1_codec_ctrl::sptr _codec; -    uhd::dict<unit_t, double> _clock_rates; +    unsigned _dbsrx_classic_div;      const usrp1_impl::dboard_slot_t _dboard_slot; -    const double _master_clock_rate; +    const double &_master_clock_rate;      const dboard_id_t _rx_dboard_id;  }; @@ -115,7 +113,7 @@ private:  dboard_iface::sptr usrp1_impl::make_dboard_iface(usrp1_iface::sptr iface,                                             usrp1_codec_ctrl::sptr codec,                                             usrp1_impl::dboard_slot_t dboard_slot, -                                           const double master_clock_rate, +                                           const double &master_clock_rate,                                             const dboard_id_t &rx_dboard_id  ){      return dboard_iface::sptr(new usrp1_dboard_iface( @@ -137,17 +135,16 @@ static const dboard_id_t dbsrx_classic_id(0x0002);  void usrp1_dboard_iface::set_clock_rate(unit_t unit, double rate)  {      assert_has(this->get_clock_rates(unit), rate, "dboard clock rate"); -    _clock_rates[unit] = rate;      if (unit == UNIT_RX && _rx_dboard_id == dbsrx_classic_id){ -        size_t divider = size_t(_master_clock_rate/rate); +        _dbsrx_classic_div = size_t(_master_clock_rate/rate);          switch(_dboard_slot){          case usrp1_impl::DBOARD_SLOT_A: -            _iface->poke32(FR_RX_A_REFCLK, (divider & 0x7f) | 0x80); +            _iface->poke32(FR_RX_A_REFCLK, (_dbsrx_classic_div & 0x7f) | 0x80);              break;          case usrp1_impl::DBOARD_SLOT_B: -            _iface->poke32(FR_RX_B_REFCLK, (divider & 0x7f) | 0x80); +            _iface->poke32(FR_RX_B_REFCLK, (_dbsrx_classic_div & 0x7f) | 0x80);              break;          }      } @@ -168,7 +165,10 @@ std::vector<double> usrp1_dboard_iface::get_clock_rates(unit_t unit)  double usrp1_dboard_iface::get_clock_rate(unit_t unit)  { -    return _clock_rates[unit]; +    if (unit == UNIT_RX && _rx_dboard_id == dbsrx_classic_id){ +        return _master_clock_rate/_dbsrx_classic_div; +    } +    return _master_clock_rate;  }  void usrp1_dboard_iface::set_clock_enabled(unit_t, bool) diff --git a/host/lib/usrp/usrp1/io_impl.cpp b/host/lib/usrp/usrp1/io_impl.cpp index de325ea5d..937706fdd 100644 --- a/host/lib/usrp/usrp1/io_impl.cpp +++ b/host/lib/usrp/usrp1/io_impl.cpp @@ -22,6 +22,7 @@  #include "../../transport/super_send_packet_handler.hpp"  #include "usrp1_calc_mux.hpp"  #include "fpga_regs_standard.h" +#include "fpga_regs_common.h"  #include "usrp_commands.h"  #include "usrp1_impl.hpp"  #include <uhd/utils/msg.hpp> @@ -33,6 +34,7 @@  #include <boost/thread/thread.hpp>  #include <boost/bind.hpp>  #include <boost/format.hpp> +#include <boost/make_shared.hpp>  using namespace uhd;  using namespace uhd::usrp; @@ -109,6 +111,7 @@ static void usrp1_bs_vrt_unpacker(  ){      if_packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_DATA;      if_packet_info.num_payload_words32 = if_packet_info.num_packet_words32; +    if_packet_info.num_payload_bytes = if_packet_info.num_packet_words32*sizeof(boost::uint32_t);      if_packet_info.num_header_words32 = 0;      if_packet_info.packet_count = 0;      if_packet_info.sob = false; @@ -138,10 +141,6 @@ struct usrp1_impl::io_impl{      zero_copy_if::sptr data_transport; -    //state management for the vrt packet handler code -    sph::recv_packet_handler recv_handler; -    sph::send_packet_handler send_handler; -      //wrapper around the actual send buffer interface      //all of this to ensure only aligned lengths are committed      //NOTE: you must commit before getting a new buffer @@ -219,13 +218,6 @@ void usrp1_impl::io_impl::flush_send_buff(void){   * Initialize internals within this file   **********************************************************************/  void usrp1_impl::io_init(void){ -    _rx_otw_type.width = 16; -    _rx_otw_type.shift = 0; -    _rx_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; - -    _tx_otw_type.width = 16; -    _tx_otw_type.shift = 0; -    _tx_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN;      _io_impl = UHD_PIMPL_MAKE(io_impl, (_data_transport)); @@ -234,18 +226,6 @@ void usrp1_impl::io_init(void){          &usrp1_impl::vandal_conquest_loop, this      )); -    //init some handler stuff -    _io_impl->recv_handler.set_tick_rate(_master_clock_rate); -    _io_impl->recv_handler.set_vrt_unpacker(&usrp1_bs_vrt_unpacker); -    _io_impl->recv_handler.set_xport_chan_get_buff(0, boost::bind( -        &uhd::transport::zero_copy_if::get_recv_buff, _io_impl->data_transport, _1 -    )); -    _io_impl->send_handler.set_tick_rate(_master_clock_rate); -    _io_impl->send_handler.set_vrt_packer(&usrp1_bs_vrt_packer); -    _io_impl->send_handler.set_xport_chan_get_buff(0, boost::bind( -        &usrp1_impl::io_impl::get_send_buff, _io_impl.get(), _1 -    )); -      //init as disabled, then call the real function (uses restore)      this->enable_rx(false);      this->enable_tx(false); @@ -325,17 +305,109 @@ void usrp1_impl::vandal_conquest_loop(void){  }  /*********************************************************************** + * RX streamer wrapper that talks to soft time control + **********************************************************************/ +class usrp1_recv_packet_streamer : public sph::recv_packet_handler, public rx_streamer{ +public: +    usrp1_recv_packet_streamer(const size_t max_num_samps, soft_time_ctrl::sptr stc){ +        _max_num_samps = max_num_samps; +        _stc = stc; +    } + +    size_t get_num_channels(void) const{ +        return this->size(); +    } + +    size_t get_max_num_samps(void) const{ +        return _max_num_samps; +    } + +    size_t recv( +        const rx_streamer::buffs_type &buffs, +        const size_t nsamps_per_buff, +        uhd::rx_metadata_t &metadata, +        const double timeout, +        const bool one_packet +    ){ +        //interleave a "soft" inline message into the receive stream: +        if (_stc->get_inline_queue().pop_with_haste(metadata)) return 0; + +        size_t num_samps_recvd = sph::recv_packet_handler::recv( +            buffs, nsamps_per_buff, metadata, timeout, one_packet +        ); + +        return _stc->recv_post(metadata, num_samps_recvd); +    } + +private: +    size_t _max_num_samps; +    soft_time_ctrl::sptr _stc; +}; + +/*********************************************************************** + * TX streamer wrapper that talks to soft time control + **********************************************************************/ +class usrp1_send_packet_streamer : public sph::send_packet_handler, public tx_streamer{ +public: +    usrp1_send_packet_streamer(const size_t max_num_samps, soft_time_ctrl::sptr stc, boost::function<void(bool)> tx_enb_fcn){ +        _max_num_samps = max_num_samps; +        this->set_max_samples_per_packet(_max_num_samps); +        _stc = stc; +        _tx_enb_fcn = tx_enb_fcn; +    } + +    size_t get_num_channels(void) const{ +        return this->size(); +    } + +    size_t get_max_num_samps(void) const{ +        return _max_num_samps; +    } + +    size_t send( +        const tx_streamer::buffs_type &buffs, +        const size_t nsamps_per_buff, +        const uhd::tx_metadata_t &metadata, +        const double timeout_ +    ){ +        double timeout = timeout_; //rw copy +        _stc->send_pre(metadata, timeout); + +        _tx_enb_fcn(true); //always enable (it will do the right thing) +        size_t num_samps_sent = sph::send_packet_handler::send( +            buffs, nsamps_per_buff, metadata, timeout +        ); + +        //handle eob flag (commit the buffer, //disable the DACs) +        //check num samps sent to avoid flush on incomplete/timeout +        if (metadata.end_of_burst and num_samps_sent == nsamps_per_buff){ +            async_metadata_t metadata; +            metadata.channel = 0; +            metadata.has_time_spec = true; +            metadata.time_spec = _stc->get_time(); +            metadata.event_code = async_metadata_t::EVENT_CODE_BURST_ACK; +            _stc->get_async_queue().push_with_pop_on_full(metadata); +            _tx_enb_fcn(false); +        } + +        return num_samps_sent; +    } + +private: +    size_t _max_num_samps; +    soft_time_ctrl::sptr _stc; +    boost::function<void(bool)> _tx_enb_fcn; +}; + +/***********************************************************************   * Properties callback methods below   **********************************************************************/  void usrp1_impl::update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){ -    boost::mutex::scoped_lock lock = _io_impl->recv_handler.get_scoped_lock();      //sanity checking      validate_subdev_spec(_tree, spec, "rx");      _rx_subdev_spec = spec; //shadow -    //_io_impl->recv_handler.resize(spec.size()); //always 1 -    _io_impl->recv_handler.set_converter(_rx_otw_type, spec.size());      //set the mux and set the number of rx channels      std::vector<mapping_pair_t> mapping; @@ -351,14 +423,11 @@ void usrp1_impl::update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){  }  void usrp1_impl::update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){ -    boost::mutex::scoped_lock lock = _io_impl->send_handler.get_scoped_lock();      //sanity checking      validate_subdev_spec(_tree, spec, "tx");      _tx_subdev_spec = spec; //shadow -    //_io_impl->send_handler.resize(spec.size()); //always 1 -    _io_impl->send_handler.set_converter(_tx_otw_type, spec.size());      //set the mux and set the number of tx channels      std::vector<mapping_pair_t> mapping; @@ -371,41 +440,94 @@ void usrp1_impl::update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){      bool s = this->disable_tx();      _iface->poke32(FR_TX_MUX, calc_tx_mux(mapping));      this->restore_tx(s); +} -    //if the spec changes size, so does the max samples per packet... -    _io_impl->send_handler.set_max_samples_per_packet(get_max_send_samps_per_packet()); +void usrp1_impl::update_tick_rate(const double rate){ +    //updating this variable should: +    //update dboard iface -> it has a reference +    //update dsp freq bounds -> publisher +    _master_clock_rate = rate;  } -double usrp1_impl::update_rx_samp_rate(const double samp_rate){ -    boost::mutex::scoped_lock lock = _io_impl->recv_handler.get_scoped_lock(); +uhd::meta_range_t usrp1_impl::get_rx_dsp_host_rates(void){ +    meta_range_t range; +    const size_t div = this->has_rx_halfband()? 2 : 1; +    for (int rate = 256; rate >= 4; rate -= div){ +        range.push_back(range_t(_master_clock_rate/rate)); +    } +    return range; +} -    const size_t rate = uhd::clip<size_t>( -        boost::math::iround(_master_clock_rate / samp_rate), size_t(std::ceil(_master_clock_rate / 8e6)), 256 -    ); +uhd::meta_range_t usrp1_impl::get_tx_dsp_host_rates(void){ +    meta_range_t range; +    const size_t div = this->has_tx_halfband()? 2 : 1; +    for (int rate = 256; rate >= 8; rate -= div){ +        range.push_back(range_t(_master_clock_rate/rate)); +    } +    return range; +} -    bool s = this->disable_rx(); -    _iface->poke32(FR_DECIM_RATE, rate/2 - 1); -    this->restore_rx(s); +double usrp1_impl::update_rx_samp_rate(size_t dspno, const double samp_rate){ + +    const size_t div = this->has_rx_halfband()? 2 : 1; +    const size_t rate = boost::math::iround(_master_clock_rate/this->get_rx_dsp_host_rates().clip(samp_rate, true)); + +    if (rate < 8 and this->has_rx_halfband()) UHD_MSG(warning) << +        "USRP1 cannot achieve decimations below 8 when the half-band filter is present.\n" +        "The usrp1_fpga_4rx.rbf file is a special FPGA image without RX half-band filters.\n" +        "To load this image, set the device address key/value pair: fpga=usrp1_fpga_4rx.rbf\n" +    << std::endl; + +    if (dspno == 0){ //only care if dsp0 is set since its homogeneous +        bool s = this->disable_rx(); +        _iface->poke32(FR_RX_SAMPLE_RATE_DIV, div - 1); +        _iface->poke32(FR_DECIM_RATE, rate/div - 1); +        this->restore_rx(s); + +        //update the streamer if created +        boost::shared_ptr<usrp1_recv_packet_streamer> my_streamer = +            boost::dynamic_pointer_cast<usrp1_recv_packet_streamer>(_rx_streamer.lock()); +        if (my_streamer.get() != NULL){ +            my_streamer->set_samp_rate(_master_clock_rate / rate); +        } +    } -    _io_impl->recv_handler.set_samp_rate(_master_clock_rate / rate);      return _master_clock_rate / rate;  } -double usrp1_impl::update_tx_samp_rate(const double samp_rate){ -    boost::mutex::scoped_lock lock = _io_impl->send_handler.get_scoped_lock(); +double usrp1_impl::update_tx_samp_rate(size_t dspno, const double samp_rate){ -    const size_t rate = uhd::clip<size_t>( -        boost::math::iround(_master_clock_rate / samp_rate), size_t(std::ceil(_master_clock_rate / 8e6)), 256 -    ); +    const size_t div = this->has_tx_halfband()? 2 : 1; +    const size_t rate = boost::math::iround(_master_clock_rate/this->get_tx_dsp_host_rates().clip(samp_rate, true)); -    bool s = this->disable_tx(); -    _iface->poke32(FR_INTERP_RATE, rate/2 - 1); -    this->restore_tx(s); +    if (dspno == 0){ //only care if dsp0 is set since its homogeneous +        bool s = this->disable_tx(); +        _iface->poke32(FR_TX_SAMPLE_RATE_DIV, div - 1); +        _iface->poke32(FR_INTERP_RATE, rate/div - 1); +        this->restore_tx(s); + +        //update the streamer if created +        boost::shared_ptr<usrp1_send_packet_streamer> my_streamer = +            boost::dynamic_pointer_cast<usrp1_send_packet_streamer>(_tx_streamer.lock()); +        if (my_streamer.get() != NULL){ +            my_streamer->set_samp_rate(_master_clock_rate / rate); +        } +    } -    _io_impl->send_handler.set_samp_rate(_master_clock_rate / rate);      return _master_clock_rate / rate;  } +void usrp1_impl::update_rates(void){ +    const fs_path mb_path = "/mboards/0"; +    this->update_tick_rate(_master_clock_rate); +    BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "rx_dsps")){ +        _tree->access<double>(mb_path / "rx_dsps" / name / "rate" / "value").update(); +    } +    BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "tx_dsps")){ +        _tree->access<double>(mb_path / "tx_dsps" / name / "rate" / "value").update(); +    } +} +  double usrp1_impl::update_rx_dsp_freq(const size_t dspno, const double freq_){      //correct for outside of rate (wrap around) @@ -443,67 +565,120 @@ bool usrp1_impl::recv_async_msg(  }  /*********************************************************************** - * Data send + helper functions + * Receive streamer   **********************************************************************/ -size_t usrp1_impl::get_max_send_samps_per_packet(void) const { -    return (_data_transport->get_send_frame_size() - alignment_padding) -        / _tx_otw_type.get_sample_size() -        / _tx_subdev_spec.size() -    ; -} +rx_streamer::sptr usrp1_impl::get_rx_stream(const uhd::stream_args_t &args_){ +    stream_args_t args = args_; + +    //setup defaults for unspecified values +    args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format; +    args.channels.clear(); //NOTE: we have no choice about the channel mapping +    for (size_t ch = 0; ch < _rx_subdev_spec.size(); ch++){ +        args.channels.push_back(ch); +    } -size_t usrp1_impl::send( -    const send_buffs_type &buffs, size_t nsamps_per_buff, -    const tx_metadata_t &metadata, const io_type_t &io_type, -    send_mode_t send_mode, double timeout -){ -    if (_soft_time_ctrl->send_pre(metadata, timeout)) return 0; +    if (args.otw_format == "sc16"){ +        _iface->poke32(FR_RX_FORMAT, 0 +            | (0 << bmFR_RX_FORMAT_SHIFT_SHIFT) +            | (16 << bmFR_RX_FORMAT_WIDTH_SHIFT) +            | bmFR_RX_FORMAT_WANT_Q +        ); +    } +    else if (args.otw_format == "sc8"){ +        _iface->poke32(FR_RX_FORMAT, 0 +            | (8 << bmFR_RX_FORMAT_SHIFT_SHIFT) +            | (8 << bmFR_RX_FORMAT_WIDTH_SHIFT) +            | bmFR_RX_FORMAT_WANT_Q +        ); +    } +    else{ +        throw uhd::value_error("USRP1 RX cannot handle requested wire format: " + args.otw_format); +    } -    this->tx_stream_on_off(true); //always enable (it will do the right thing) -    size_t num_samps_sent = _io_impl->send_handler.send( -        buffs, nsamps_per_buff, -        metadata, io_type, -        send_mode, timeout -    ); +    //calculate packet size +    const size_t bpp = _data_transport->get_recv_frame_size()/args.channels.size(); +    const size_t spp = bpp/convert::get_bytes_per_item(args.otw_format); -    //handle eob flag (commit the buffer, /*disable the DACs*/) -    //check num samps sent to avoid flush on incomplete/timeout -    if (metadata.end_of_burst and num_samps_sent == nsamps_per_buff){ -        async_metadata_t metadata; -        metadata.channel = 0; -        metadata.has_time_spec = true; -        metadata.time_spec = _soft_time_ctrl->get_time(); -        metadata.event_code = async_metadata_t::EVENT_CODE_BURST_ACK; -        _soft_time_ctrl->get_async_queue().push_with_pop_on_full(metadata); -        this->tx_stream_on_off(false); -    } +    //make the new streamer given the samples per packet +    boost::shared_ptr<usrp1_recv_packet_streamer> my_streamer = +        boost::make_shared<usrp1_recv_packet_streamer>(spp, _soft_time_ctrl); + +    //special scale factor change for sc8 +    if (args.otw_format == "sc8") +        my_streamer->set_scale_factor(1.0/127); + +    //init some streamer stuff +    my_streamer->set_tick_rate(_master_clock_rate); +    my_streamer->set_vrt_unpacker(&usrp1_bs_vrt_unpacker); +    my_streamer->set_xport_chan_get_buff(0, boost::bind( +        &uhd::transport::zero_copy_if::get_recv_buff, _io_impl->data_transport, _1 +    )); + +    //set the converter +    uhd::convert::id_type id; +    id.input_format = args.otw_format + "_item16_usrp1"; +    id.num_inputs = 1; +    id.output_format = args.cpu_format; +    id.num_outputs = args.channels.size(); +    my_streamer->set_converter(id); -    return num_samps_sent; +    //save as weak ptr for update access +    _rx_streamer = my_streamer; + +    //sets all tick and samp rates on this streamer +    this->update_rates(); + +    return my_streamer;  }  /*********************************************************************** - * Data recv + helper functions + * Transmit streamer   **********************************************************************/ -size_t usrp1_impl::get_max_recv_samps_per_packet(void) const { -    return _data_transport->get_recv_frame_size() -        / _rx_otw_type.get_sample_size() -        / _rx_subdev_spec.size() -    ; -} +tx_streamer::sptr usrp1_impl::get_tx_stream(const uhd::stream_args_t &args_){ +    stream_args_t args = args_; + +    //setup defaults for unspecified values +    args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format; +    args.channels.clear(); //NOTE: we have no choice about the channel mapping +    for (size_t ch = 0; ch < _tx_subdev_spec.size(); ch++){ +        args.channels.push_back(ch); +    } -size_t usrp1_impl::recv( -    const recv_buffs_type &buffs, size_t nsamps_per_buff, -    rx_metadata_t &metadata, const io_type_t &io_type, -    recv_mode_t recv_mode, double timeout -){ -    //interleave a "soft" inline message into the receive stream: -    if (_soft_time_ctrl->get_inline_queue().pop_with_haste(metadata)) return 0; +    if (args.otw_format != "sc16"){ +        throw uhd::value_error("USRP1 TX cannot handle requested wire format: " + args.otw_format); +    } -    size_t num_samps_recvd = _io_impl->recv_handler.recv( -        buffs, nsamps_per_buff, -        metadata, io_type, -        recv_mode, timeout -    ); +    _iface->poke32(FR_TX_FORMAT, bmFR_TX_FORMAT_16_IQ); + +    //calculate packet size +    const size_t bpp = _data_transport->get_send_frame_size()/args.channels.size(); +    const size_t spp = bpp/convert::get_bytes_per_item(args.otw_format); + +    //make the new streamer given the samples per packet +    boost::function<void(bool)> tx_fcn = boost::bind(&usrp1_impl::tx_stream_on_off, this, _1); +    boost::shared_ptr<usrp1_send_packet_streamer> my_streamer = +        boost::make_shared<usrp1_send_packet_streamer>(spp, _soft_time_ctrl, tx_fcn); + +    //init some streamer stuff +    my_streamer->set_tick_rate(_master_clock_rate); +    my_streamer->set_vrt_packer(&usrp1_bs_vrt_packer); +    my_streamer->set_xport_chan_get_buff(0, boost::bind( +        &usrp1_impl::io_impl::get_send_buff, _io_impl.get(), _1 +    )); + +    //set the converter +    uhd::convert::id_type id; +    id.input_format = args.cpu_format; +    id.num_inputs = args.channels.size(); +    id.output_format = args.otw_format + "_item16_usrp1"; +    id.num_outputs = 1; +    my_streamer->set_converter(id); + +    //save as weak ptr for update access +    _tx_streamer = my_streamer; + +    //sets all tick and samp rates on this streamer +    this->update_rates(); -    return _soft_time_ctrl->recv_post(metadata, num_samps_recvd); +    return my_streamer;  } diff --git a/host/lib/usrp/usrp1/soft_time_ctrl.cpp b/host/lib/usrp/usrp1/soft_time_ctrl.cpp index 78481c3ff..b8af8af06 100644 --- a/host/lib/usrp/usrp1/soft_time_ctrl.cpp +++ b/host/lib/usrp/usrp1/soft_time_ctrl.cpp @@ -131,8 +131,8 @@ public:      /*******************************************************************       * Transmit control       ******************************************************************/ -    bool send_pre(const tx_metadata_t &md, double &timeout){ -        if (not md.has_time_spec) return false; +    void send_pre(const tx_metadata_t &md, double &timeout){ +        if (not md.has_time_spec) return;          boost::mutex::scoped_lock lock(_update_mutex); @@ -146,12 +146,11 @@ public:              metadata.time_spec = this->time_now();              metadata.event_code = async_metadata_t::EVENT_CODE_TIME_ERROR;              _async_msg_queue.push_with_pop_on_full(metadata); -            return true; +            return;          }          timeout -= (time_at - time_now()).get_real_secs();          sleep_until_time(lock, time_at); -        return false;      }      /******************************************************************* diff --git a/host/lib/usrp/usrp1/soft_time_ctrl.hpp b/host/lib/usrp/usrp1/soft_time_ctrl.hpp index e91aaf6a2..b92b51252 100644 --- a/host/lib/usrp/usrp1/soft_time_ctrl.hpp +++ b/host/lib/usrp/usrp1/soft_time_ctrl.hpp @@ -57,7 +57,7 @@ public:      virtual size_t recv_post(rx_metadata_t &md, const size_t nsamps) = 0;      //! Call before the internal send function -    virtual bool send_pre(const tx_metadata_t &md, double &timeout) = 0; +    virtual void send_pre(const tx_metadata_t &md, double &timeout) = 0;      //! Issue a stream command to receive      virtual void issue_stream_cmd(const stream_cmd_t &cmd) = 0; diff --git a/host/lib/usrp/usrp1/usrp1_calc_mux.hpp b/host/lib/usrp/usrp1/usrp1_calc_mux.hpp index 31c190db0..d86a7a809 100644 --- a/host/lib/usrp/usrp1/usrp1_calc_mux.hpp +++ b/host/lib/usrp/usrp1/usrp1_calc_mux.hpp @@ -37,7 +37,7 @@ typedef std::pair<std::string, std::string> mapping_pair_t;   *    to account for the reversal in the type conversion routines.   **********************************************************************/  static int calc_rx_mux_pair(int adc_for_i, int adc_for_q){ -    return (adc_for_i << 2) | (adc_for_q << 0); //shift reversal here +    return (adc_for_i << 0) | (adc_for_q << 2);  }  /*! @@ -98,7 +98,7 @@ static boost::uint32_t calc_rx_mux(const std::vector<mapping_pair_t> &mapping){   *    to account for the reversal in the type conversion routines.   **********************************************************************/  static int calc_tx_mux_pair(int chn_for_i, int chn_for_q){ -    return (chn_for_i << 4) | (chn_for_q << 0); //shift reversal here +    return (chn_for_i << 0) | (chn_for_q << 4);  }  /*! diff --git a/host/lib/usrp/usrp1/usrp1_impl.cpp b/host/lib/usrp/usrp1/usrp1_impl.cpp index fe4541d38..4be5a3a2b 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.cpp +++ b/host/lib/usrp/usrp1/usrp1_impl.cpp @@ -33,6 +33,7 @@  #include <boost/filesystem.hpp>  #include <boost/thread/thread.hpp>  #include <boost/lexical_cast.hpp> +#include <boost/math/special_functions/round.hpp>  #include <cstdio>  using namespace uhd; @@ -187,21 +188,6 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){      // Normal mode with no loopback or Rx counting      _iface->poke32(FR_MODE, 0x00000000);      _iface->poke32(FR_DEBUG_EN, 0x00000000); -    _iface->poke32(FR_RX_SAMPLE_RATE_DIV, 0x00000001); //divide by 2 -    _iface->poke32(FR_TX_SAMPLE_RATE_DIV, 0x00000001); //divide by 2 -    _iface->poke32(FR_DC_OFFSET_CL_EN, 0x0000000f); - -    // Reset offset correction registers -    _iface->poke32(FR_ADC_OFFSET_0, 0x00000000); -    _iface->poke32(FR_ADC_OFFSET_1, 0x00000000); -    _iface->poke32(FR_ADC_OFFSET_2, 0x00000000); -    _iface->poke32(FR_ADC_OFFSET_3, 0x00000000); - -    // Set default for RX format to 16-bit I&Q and no half-band filter bypass -    _iface->poke32(FR_RX_FORMAT, 0x00000300); - -    // Set default for TX format to 16-bit I&Q -    _iface->poke32(FR_TX_FORMAT, 0x00000000);      UHD_LOG          << "USRP1 Capabilities" << std::endl @@ -233,14 +219,25 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){      // create clock control objects      ////////////////////////////////////////////////////////////////////      _master_clock_rate = 64e6; -    try{ -        if (not mb_eeprom["mcr"].empty()) +    if (device_addr.has_key("mcr")){ +        try{ +            _master_clock_rate = boost::lexical_cast<double>(device_addr["mcr"]); +        } +        catch(const std::exception &e){ +            UHD_MSG(error) << "Error parsing FPGA clock rate from device address: " << e.what() << std::endl; +        } +    } +    else if (not mb_eeprom["mcr"].empty()){ +        try{              _master_clock_rate = boost::lexical_cast<double>(mb_eeprom["mcr"]); -    }catch(const std::exception &e){ -        UHD_MSG(error) << "Error parsing FPGA clock rate from EEPROM: " << e.what() << std::endl; +        } +        catch(const std::exception &e){ +            UHD_MSG(error) << "Error parsing FPGA clock rate from EEPROM: " << e.what() << std::endl; +        }      }      UHD_MSG(status) << boost::format("Using FPGA clock rate of %fMHz...") % (_master_clock_rate/1e6) << std::endl; -    _tree->create<double>(mb_path / "tick_rate").set(_master_clock_rate); +    _tree->create<double>(mb_path / "tick_rate") +        .subscribe(boost::bind(&usrp1_impl::update_tick_rate, this, _1));      ////////////////////////////////////////////////////////////////////      // create codec control objects @@ -274,18 +271,31 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){      _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec")          .subscribe(boost::bind(&usrp1_impl::update_tx_subdev_spec, this, _1)); +    BOOST_FOREACH(const std::string &db, _dbc.keys()){ +        const fs_path rx_fe_path = mb_path / "rx_frontends" / db; +        _tree->create<std::complex<double> >(rx_fe_path / "dc_offset" / "value") +            .coerce(boost::bind(&usrp1_impl::set_rx_dc_offset, this, db, _1)) +            .set(std::complex<double>(0.0, 0.0)); +        _tree->create<bool>(rx_fe_path / "dc_offset" / "enable") +            .subscribe(boost::bind(&usrp1_impl::set_enb_rx_dc_offset, this, db, _1)) +            .set(true); +    } +      ////////////////////////////////////////////////////////////////////      // create rx dsp control objects      ////////////////////////////////////////////////////////////////////      _tree->create<int>(mb_path / "rx_dsps"); //dummy in case we have none      for (size_t dspno = 0; dspno < get_num_ddcs(); dspno++){          fs_path rx_dsp_path = mb_path / str(boost::format("rx_dsps/%u") % dspno); +        _tree->create<meta_range_t>(rx_dsp_path / "rate/range") +            .publish(boost::bind(&usrp1_impl::get_rx_dsp_host_rates, this));          _tree->create<double>(rx_dsp_path / "rate/value") -            .coerce(boost::bind(&usrp1_impl::update_rx_samp_rate, this, _1)); +            .set(1e6) //some default rate +            .coerce(boost::bind(&usrp1_impl::update_rx_samp_rate, this, dspno, _1));          _tree->create<double>(rx_dsp_path / "freq/value")              .coerce(boost::bind(&usrp1_impl::update_rx_dsp_freq, this, dspno, _1));          _tree->create<meta_range_t>(rx_dsp_path / "freq/range") -            .set(meta_range_t(-_master_clock_rate/2, +_master_clock_rate/2)); +            .publish(boost::bind(&usrp1_impl::get_rx_dsp_freq_range, this));          _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd");          if (dspno == 0){              //only subscribe the callback for dspno 0 since it will stream all dsps @@ -300,12 +310,15 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){      _tree->create<int>(mb_path / "tx_dsps"); //dummy in case we have none      for (size_t dspno = 0; dspno < get_num_ducs(); dspno++){          fs_path tx_dsp_path = mb_path / str(boost::format("tx_dsps/%u") % dspno); +        _tree->create<meta_range_t>(tx_dsp_path / "rate/range") +            .publish(boost::bind(&usrp1_impl::get_tx_dsp_host_rates, this));          _tree->create<double>(tx_dsp_path / "rate/value") -            .coerce(boost::bind(&usrp1_impl::update_tx_samp_rate, this, _1)); +            .set(1e6) //some default rate +            .coerce(boost::bind(&usrp1_impl::update_tx_samp_rate, this, dspno, _1));          _tree->create<double>(tx_dsp_path / "freq/value")              .coerce(boost::bind(&usrp1_impl::update_tx_dsp_freq, this, dspno, _1)); -        _tree->create<meta_range_t>(tx_dsp_path / "freq/range") //magic scalar comes from codec control: -            .set(meta_range_t(-_master_clock_rate*0.6875, +_master_clock_rate*0.6875)); +        _tree->create<meta_range_t>(tx_dsp_path / "freq/range") +            .publish(boost::bind(&usrp1_impl::get_tx_dsp_freq_range, this));      }      //////////////////////////////////////////////////////////////////// @@ -350,29 +363,16 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){          );          _tree->create<dboard_iface::sptr>(mb_path / "dboards" / db/ "iface").set(_dbc[db].dboard_iface);          _dbc[db].dboard_manager = dboard_manager::make( -            rx_db_eeprom.id, -            ((gdb_eeprom.id == dboard_id_t::none())? tx_db_eeprom : gdb_eeprom).id, -            _dbc[db].dboard_iface +            rx_db_eeprom.id, tx_db_eeprom.id, gdb_eeprom.id, +            _dbc[db].dboard_iface, _tree->subtree(mb_path / "dboards" / db)          ); -        BOOST_FOREACH(const std::string &name, _dbc[db].dboard_manager->get_rx_subdev_names()){ -            dboard_manager::populate_prop_tree_from_subdev( -                _tree->subtree(mb_path / "dboards" / db/ "rx_frontends" / name), -                _dbc[db].dboard_manager->get_rx_subdev(name) -            ); -        } -        BOOST_FOREACH(const std::string &name, _dbc[db].dboard_manager->get_tx_subdev_names()){ -            dboard_manager::populate_prop_tree_from_subdev( -                _tree->subtree(mb_path / "dboards" / db/ "tx_frontends" / name), -                _dbc[db].dboard_manager->get_tx_subdev(name) -            ); -        }          //init the subdev specs if we have a dboard (wont leave this loop empty)          if (rx_db_eeprom.id != dboard_id_t::none() or _rx_subdev_spec.empty()){ -            _rx_subdev_spec = subdev_spec_t(db + ":" + _dbc[db].dboard_manager->get_rx_subdev_names()[0]); +            _rx_subdev_spec = subdev_spec_t(db + ":" + _tree->list(mb_path / "dboards" / db / "rx_frontends").at(0));          }          if (tx_db_eeprom.id != dboard_id_t::none() or _tx_subdev_spec.empty()){ -            _tx_subdev_spec = subdev_spec_t(db + ":" + _dbc[db].dboard_manager->get_tx_subdev_names()[0]); +            _tx_subdev_spec = subdev_spec_t(db + ":" + _tree->list(mb_path / "dboards" / db / "tx_frontends").at(0));          }      } @@ -382,14 +382,7 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){      ////////////////////////////////////////////////////////////////////      // do some post-init tasks      //////////////////////////////////////////////////////////////////// -    //and now that the tick rate is set, init the host rates to something -    BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "rx_dsps")){ -        _tree->access<double>(mb_path / "rx_dsps" / name / "rate" / "value").set(1e6); -    } -    BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "tx_dsps")){ -        _tree->access<double>(mb_path / "tx_dsps" / name / "rate" / "value").set(1e6); -    } - +    this->update_rates();      if (_tree->list(mb_path / "rx_dsps").size() > 0)          _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(_rx_subdev_spec);      if (_tree->list(mb_path / "tx_dsps").size() > 0) @@ -456,3 +449,36 @@ double usrp1_impl::update_rx_codec_gain(const std::string &db, const double gain      _dbc[db].codec->set_rx_pga_gain(gain, 'B');      return _dbc[db].codec->get_rx_pga_gain('A');  } + +uhd::meta_range_t usrp1_impl::get_rx_dsp_freq_range(void){ +    return meta_range_t(-_master_clock_rate/2, +_master_clock_rate/2); +} + +uhd::meta_range_t usrp1_impl::get_tx_dsp_freq_range(void){ +    //magic scalar comes from codec control: +    return meta_range_t(-_master_clock_rate*0.6875, +_master_clock_rate*0.6875); +} + +void usrp1_impl::set_enb_rx_dc_offset(const std::string &db, const bool enb){ +    const size_t shift = (db == "A")? 0 : 2; +    _rx_dc_offset_shadow &= ~(0x3 << shift); //clear bits +    _rx_dc_offset_shadow &= ((enb)? 0x3 : 0x0) << shift; +    _iface->poke32(FR_DC_OFFSET_CL_EN, _rx_dc_offset_shadow & 0xf); +} + +std::complex<double> usrp1_impl::set_rx_dc_offset(const std::string &db, const std::complex<double> &offset){ +    const boost::int32_t i_off = boost::math::iround(offset.real() * (1ul << 31)); +    const boost::int32_t q_off = boost::math::iround(offset.imag() * (1ul << 31)); + +    if (db == "A"){ +        _iface->poke32(FR_ADC_OFFSET_0, i_off); +        _iface->poke32(FR_ADC_OFFSET_1, q_off); +    } + +    if (db == "B"){ +        _iface->poke32(FR_ADC_OFFSET_2, i_off); +        _iface->poke32(FR_ADC_OFFSET_3, q_off); +    } + +    return std::complex<double>(double(i_off) * (1ul << 31), double(q_off) * (1ul << 31)); +} diff --git a/host/lib/usrp/usrp1/usrp1_impl.hpp b/host/lib/usrp/usrp1/usrp1_impl.hpp index 68ce31a54..99bb01c76 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.hpp +++ b/host/lib/usrp/usrp1/usrp1_impl.hpp @@ -31,6 +31,7 @@  #include <uhd/usrp/dboard_eeprom.hpp>  #include <uhd/usrp/dboard_manager.hpp>  #include <uhd/transport/usb_zero_copy.hpp> +#include <complex>  #ifndef INCLUDED_USRP1_IMPL_HPP  #define INCLUDED_USRP1_IMPL_HPP @@ -55,21 +56,8 @@ public:      ~usrp1_impl(void);      //the io interface -    size_t send(const send_buffs_type &, -                size_t, -                const uhd::tx_metadata_t &, -                const uhd::io_type_t &, -                send_mode_t, double); - -    size_t recv(const recv_buffs_type &, -                size_t, uhd::rx_metadata_t &, -                const uhd::io_type_t &, -                recv_mode_t, double); - -    size_t get_max_send_samps_per_packet(void) const; - -    size_t get_max_recv_samps_per_packet(void) const; - +    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);  private: @@ -94,21 +82,34 @@ private:      double _master_clock_rate; //clock rate shadow +    //weak pointers to streamers for update purposes +    boost::weak_ptr<uhd::rx_streamer> _rx_streamer; +    boost::weak_ptr<uhd::tx_streamer> _tx_streamer; +      void set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &);      void set_db_eeprom(const std::string &, const std::string &, const uhd::usrp::dboard_eeprom_t &);      double update_rx_codec_gain(const std::string &, const double); //sets A and B at once      void update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &);      void update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &); -    double update_rx_samp_rate(const double); -    double update_tx_samp_rate(const double); +    double update_rx_samp_rate(size_t dspno, const double); +    double update_tx_samp_rate(size_t dspno, const double); +    void update_rates(void);      double update_rx_dsp_freq(const size_t, const double);      double update_tx_dsp_freq(const size_t, const double); +    void update_tick_rate(const double rate); +    uhd::meta_range_t get_rx_dsp_freq_range(void); +    uhd::meta_range_t get_tx_dsp_freq_range(void); +    uhd::meta_range_t get_rx_dsp_host_rates(void); +    uhd::meta_range_t get_tx_dsp_host_rates(void); +    size_t _rx_dc_offset_shadow; +    void set_enb_rx_dc_offset(const std::string &db, const bool); +    std::complex<double> set_rx_dc_offset(const std::string &db, const std::complex<double> &);      static uhd::usrp::dboard_iface::sptr make_dboard_iface(          usrp1_iface::sptr,          usrp1_codec_ctrl::sptr,          dboard_slot_t, -        const double, +        const double &,          const uhd::usrp::dboard_id_t &      ); @@ -119,8 +120,7 @@ private:      void tx_stream_on_off(bool);      void handle_overrun(size_t); -    //otw types -    uhd::otw_type_t _rx_otw_type, _tx_otw_type; +    //channel mapping shadows      uhd::usrp::subdev_spec_t _rx_subdev_spec, _tx_subdev_spec;      //capabilities diff --git a/host/lib/usrp/usrp2/dboard_iface.cpp b/host/lib/usrp/usrp2/dboard_iface.cpp index c31fc52b7..bc510c8a1 100644 --- a/host/lib/usrp/usrp2/dboard_iface.cpp +++ b/host/lib/usrp/usrp2/dboard_iface.cpp @@ -107,7 +107,7 @@ usrp2_dboard_iface::usrp2_dboard_iface(  ){      _iface = iface;      _clock_ctrl = clock_ctrl; -    _gpio = gpio_core_200::make(_iface, GPIO_BASE); +    _gpio = gpio_core_200::make(_iface, U2_REG_SR_ADDR(SR_GPIO), U2_REG_GPIO_RB);      //reset the aux dacs      _dac_regs[UNIT_RX] = ad5623_regs_t(); diff --git a/host/lib/usrp/usrp2/fw_common.h b/host/lib/usrp/usrp2/fw_common.h index 7ad06f33f..62ba2d792 100644 --- a/host/lib/usrp/usrp2/fw_common.h +++ b/host/lib/usrp/usrp2/fw_common.h @@ -30,7 +30,7 @@ extern "C" {  #endif  //fpga and firmware compatibility numbers -#define USRP2_FPGA_COMPAT_NUM 7 +#define USRP2_FPGA_COMPAT_NUM 8  #define USRP2_FW_COMPAT_NUM 11  #define USRP2_FW_VER_MINOR 0 diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp index 70331e536..8018875ec 100644 --- a/host/lib/usrp/usrp2/io_impl.cpp +++ b/host/lib/usrp/usrp2/io_impl.cpp @@ -31,6 +31,7 @@  #include <boost/format.hpp>  #include <boost/bind.hpp>  #include <boost/thread/mutex.hpp> +#include <boost/make_shared.hpp>  #include <iostream>  using namespace uhd; @@ -158,10 +159,6 @@ struct usrp2_impl::io_impl{      std::vector<zero_copy_if::sptr> tx_xports;      std::vector<flow_control_monitor::sptr> fc_mons; -    //state management for the vrt packet handler code -    sph::recv_packet_handler recv_handler; -    sph::send_packet_handler send_handler; -      //methods and variables for the pirate crew      void recv_pirate_loop(zero_copy_if::sptr, size_t);      std::list<task::sptr> pirate_tasks; @@ -237,17 +234,6 @@ void usrp2_impl::io_impl::recv_pirate_loop(   * Helper Functions   **********************************************************************/  void usrp2_impl::io_init(void){ - -    //setup rx otw type -    _rx_otw_type.width = 16; -    _rx_otw_type.shift = 0; -    _rx_otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN; - -    //setup tx otw type -    _tx_otw_type.width = 16; -    _tx_otw_type.shift = 0; -    _tx_otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN; -      //create new io impl      _io_impl = UHD_PIMPL_MAKE(io_impl, ()); @@ -260,6 +246,12 @@ void usrp2_impl::io_init(void){          )));      } +    //allocate streamer weak ptrs containers +    BOOST_FOREACH(const std::string &mb, _mbc.keys()){ +        _mbc[mb].rx_streamers.resize(_mbc[mb].rx_dsps.size()); +        _mbc[mb].tx_streamers.resize(1/*known to be 1 dsp*/); +    } +      //create a new pirate thread for each zc if (yarr!!)      size_t index = 0;      BOOST_FOREACH(const std::string &mb, _mbc.keys()){ @@ -269,58 +261,62 @@ void usrp2_impl::io_init(void){              _mbc[mb].tx_dsp_xport, index++          )));      } - -    //init some handler stuff -    _io_impl->recv_handler.set_vrt_unpacker(&vrt::if_hdr_unpack_be); -    _io_impl->recv_handler.set_converter(_rx_otw_type); -    _io_impl->send_handler.set_vrt_packer(&vrt::if_hdr_pack_be, vrt_send_header_offset_words32); -    _io_impl->send_handler.set_converter(_tx_otw_type); -    _io_impl->send_handler.set_max_samples_per_packet(get_max_send_samps_per_packet()); - -    //set the packet threshold to be an entire socket buffer's worth -    const size_t packets_per_sock_buff = size_t(50e6/_mbc[_mbc.keys().front()].rx_dsp_xports[0]->get_recv_frame_size()); -    _io_impl->recv_handler.set_alignment_failure_threshold(packets_per_sock_buff);  }  void usrp2_impl::update_tick_rate(const double rate){ -    _io_impl->tick_rate = rate; -    boost::mutex::scoped_lock recv_lock = _io_impl->recv_handler.get_scoped_lock(); -    _io_impl->recv_handler.set_tick_rate(rate); -    boost::mutex::scoped_lock send_lock = _io_impl->send_handler.get_scoped_lock(); -    _io_impl->send_handler.set_tick_rate(rate); +    _io_impl->tick_rate = rate; //shadow for async msg + +    //update the tick rate on all existing streamers -> thread safe +    BOOST_FOREACH(const std::string &mb, _mbc.keys()){ +        for (size_t i = 0; i < _mbc[mb].rx_streamers.size(); i++){ +            boost::shared_ptr<sph::recv_packet_streamer> my_streamer = +                boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_mbc[mb].rx_streamers[i].lock()); +            if (my_streamer.get() == NULL) continue; +            my_streamer->set_tick_rate(rate); +        } +        for (size_t i = 0; i < _mbc[mb].tx_streamers.size(); i++){ +            boost::shared_ptr<sph::send_packet_streamer> my_streamer = +                boost::dynamic_pointer_cast<sph::send_packet_streamer>(_mbc[mb].tx_streamers[i].lock()); +            if (my_streamer.get() == NULL) continue; +            my_streamer->set_tick_rate(rate); +        } +    }  } -void usrp2_impl::update_rx_samp_rate(const double rate){ -    boost::mutex::scoped_lock recv_lock = _io_impl->recv_handler.get_scoped_lock(); -    _io_impl->recv_handler.set_samp_rate(rate); -    const double adj = _mbc[_mbc.keys().front()].rx_dsps.front()->get_scaling_adjustment(); -    _io_impl->recv_handler.set_scale_factor(adj/32767.); +void usrp2_impl::update_rx_samp_rate(const std::string &mb, const size_t dsp, const double rate){ +    boost::shared_ptr<sph::recv_packet_streamer> my_streamer = +        boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_mbc[mb].rx_streamers[dsp].lock()); +    if (my_streamer.get() == NULL) return; + +    my_streamer->set_samp_rate(rate); +    const double adj = _mbc[mb].rx_dsps[dsp]->get_scaling_adjustment(); +    my_streamer->set_scale_factor(adj);  } -void usrp2_impl::update_tx_samp_rate(const double rate){ -    boost::mutex::scoped_lock send_lock = _io_impl->send_handler.get_scoped_lock(); -    _io_impl->send_handler.set_samp_rate(rate); +void usrp2_impl::update_tx_samp_rate(const std::string &mb, const size_t dsp, const double rate){ +    boost::shared_ptr<sph::send_packet_streamer> my_streamer = +        boost::dynamic_pointer_cast<sph::send_packet_streamer>(_mbc[mb].tx_streamers[dsp].lock()); +    if (my_streamer.get() == NULL) return; + +    my_streamer->set_samp_rate(rate);  } -static subdev_spec_t replace_zero_in_spec(const std::string &type, const subdev_spec_t &spec){ -    subdev_spec_t new_spec; -    BOOST_FOREACH(const subdev_spec_pair_t &pair, spec){ -        if (pair.db_name == "0"){ -            UHD_MSG(warning) -                << boost::format("In the %s subdevice specification: %s") % type % spec.to_string() << std::endl -                << "Accepting dboard slot name \"0\" for backward compatibility." << std::endl -                << "The official name of the dboard slot on USRP2/N-Series is \"A\"." << std::endl -            ; -            new_spec.push_back(subdev_spec_pair_t("A", pair.sd_name)); +void usrp2_impl::update_rates(void){ +    BOOST_FOREACH(const std::string &mb, _mbc.keys()){ +        fs_path root = "/mboards/" + mb; +        _tree->access<double>(root / "tick_rate").update(); + +        //and now that the tick rate is set, init the host rates to something +        BOOST_FOREACH(const std::string &name, _tree->list(root / "rx_dsps")){ +            _tree->access<double>(root / "rx_dsps" / name / "rate" / "value").update(); +        } +        BOOST_FOREACH(const std::string &name, _tree->list(root / "tx_dsps")){ +            _tree->access<double>(root / "tx_dsps" / name / "rate" / "value").update();          } -        else new_spec.push_back(pair);      } -    return new_spec;  } -subdev_spec_t usrp2_impl::update_rx_subdev_spec(const std::string &which_mb, const subdev_spec_t &spec_){ -    const subdev_spec_t spec = replace_zero_in_spec("RX", spec_); -    boost::mutex::scoped_lock recv_lock = _io_impl->recv_handler.get_scoped_lock(); +void usrp2_impl::update_rx_subdev_spec(const std::string &which_mb, const subdev_spec_t &spec){      fs_path root = "/mboards/" + which_mb + "/dboards";      //sanity checking @@ -339,24 +335,9 @@ subdev_spec_t usrp2_impl::update_rx_subdev_spec(const std::string &which_mb, con      _mbc[which_mb].rx_chan_occ = spec.size();      size_t nchan = 0;      BOOST_FOREACH(const std::string &mb, _mbc.keys()) nchan += _mbc[mb].rx_chan_occ; -    _io_impl->recv_handler.resize(nchan); - -    //bind new callbacks for the handler -    size_t chan = 0; -    BOOST_FOREACH(const std::string &mb, _mbc.keys()){ -        for (size_t dsp = 0; dsp < _mbc[mb].rx_chan_occ; dsp++){ -            _mbc[mb].rx_dsps[dsp]->set_nsamps_per_packet(get_max_recv_samps_per_packet()); //seems to be a good place to set this -            _io_impl->recv_handler.set_xport_chan_get_buff(chan++, boost::bind( -                &zero_copy_if::get_recv_buff, _mbc[mb].rx_dsp_xports[dsp], _1 -            )); -        } -    } -    return spec;  } -subdev_spec_t usrp2_impl::update_tx_subdev_spec(const std::string &which_mb, const subdev_spec_t &spec_){ -    const subdev_spec_t spec = replace_zero_in_spec("TX", spec_); -    boost::mutex::scoped_lock send_lock = _io_impl->send_handler.get_scoped_lock(); +void usrp2_impl::update_tx_subdev_spec(const std::string &which_mb, const subdev_spec_t &spec){      fs_path root = "/mboards/" + which_mb + "/dboards";      //sanity checking @@ -370,18 +351,6 @@ subdev_spec_t usrp2_impl::update_tx_subdev_spec(const std::string &which_mb, con      _mbc[which_mb].tx_chan_occ = spec.size();      size_t nchan = 0;      BOOST_FOREACH(const std::string &mb, _mbc.keys()) nchan += _mbc[mb].tx_chan_occ; -    _io_impl->send_handler.resize(nchan); - -    //bind new callbacks for the handler -    size_t chan = 0, i = 0; -    BOOST_FOREACH(const std::string &mb, _mbc.keys()){ -        for (size_t dsp = 0; dsp < _mbc[mb].tx_chan_occ; dsp++){ -            _io_impl->send_handler.set_xport_chan_get_buff(chan++, boost::bind( -                &usrp2_impl::io_impl::get_send_buff, _io_impl.get(), i++, _1 -            )); -        } -    } -    return spec;  }  /*********************************************************************** @@ -395,51 +364,128 @@ bool usrp2_impl::recv_async_msg(  }  /*********************************************************************** - * Send Data + * Receive streamer   **********************************************************************/ -size_t usrp2_impl::get_max_send_samps_per_packet(void) const{ +rx_streamer::sptr usrp2_impl::get_rx_stream(const uhd::stream_args_t &args_){ +    stream_args_t args = args_; + +    //setup defaults for unspecified values +    args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format; +    args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; +    const unsigned sc8_scalar = unsigned(args.args.cast<double>("scalar", 0x400)); + +    //calculate packet size      static const size_t hdr_size = 0          + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) -        + vrt_send_header_offset_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      ; -    const size_t bpp = _mbc[_mbc.keys().front()].tx_dsp_xport->get_send_frame_size() - hdr_size; -    return bpp/_tx_otw_type.get_sample_size(); -} +    const size_t bpp = _mbc[_mbc.keys().front()].rx_dsp_xports[0]->get_recv_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 +    boost::shared_ptr<sph::recv_packet_streamer> my_streamer = boost::make_shared<sph::recv_packet_streamer>(spp); + +    //init some streamer stuff +    my_streamer->resize(args.channels.size()); +    my_streamer->set_vrt_unpacker(&vrt::if_hdr_unpack_be); + +    //set the converter +    uhd::convert::id_type id; +    id.input_format = args.otw_format + "_item32_be"; +    id.num_inputs = 1; +    id.output_format = args.cpu_format; +    id.num_outputs = 1; +    my_streamer->set_converter(id); + +    //bind callbacks for the handler +    for (size_t chan_i = 0; chan_i < args.channels.size(); chan_i++){ +        const size_t chan = args.channels[chan_i]; +        size_t num_chan_so_far = 0; +        BOOST_FOREACH(const std::string &mb, _mbc.keys()){ +            num_chan_so_far += _mbc[mb].rx_chan_occ; +            if (chan < num_chan_so_far){ +                const size_t dsp = chan + _mbc[mb].rx_chan_occ - num_chan_so_far; +                _mbc[mb].rx_dsps[dsp]->set_nsamps_per_packet(spp); //seems to be a good place to set this +                _mbc[mb].rx_dsps[dsp]->set_format(args.otw_format, sc8_scalar); +                my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( +                    &zero_copy_if::get_recv_buff, _mbc[mb].rx_dsp_xports[dsp], _1 +                )); +                _mbc[mb].rx_streamers[dsp] = my_streamer; //store weak pointer +                break; +            } +        } +    } -size_t usrp2_impl::send( -    const send_buffs_type &buffs, size_t nsamps_per_buff, -    const tx_metadata_t &metadata, const io_type_t &io_type, -    send_mode_t send_mode, double timeout -){ -    return _io_impl->send_handler.send( -        buffs, nsamps_per_buff, -        metadata, io_type, -        send_mode, timeout -    ); +    //set the packet threshold to be an entire socket buffer's worth +    const size_t packets_per_sock_buff = size_t(50e6/_mbc[_mbc.keys().front()].rx_dsp_xports[0]->get_recv_frame_size()); +    my_streamer->set_alignment_failure_threshold(packets_per_sock_buff); + +    //sets all tick and samp rates on this streamer +    this->update_rates(); + +    return my_streamer;  }  /*********************************************************************** - * Receive Data + * Transmit streamer   **********************************************************************/ -size_t usrp2_impl::get_max_recv_samps_per_packet(void) const{ +tx_streamer::sptr usrp2_impl::get_tx_stream(const uhd::stream_args_t &args_){ +    stream_args_t args = args_; + +    //setup defaults for unspecified values +    args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format; +    args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; + +    if (args.otw_format != "sc16"){ +        throw uhd::value_error("USRP TX cannot handle requested wire format: " + args.otw_format); +    } + +    //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 +        + vrt_send_header_offset_words32*sizeof(boost::uint32_t)          - sizeof(vrt::if_packet_info_t().cid) //no class id ever used      ; -    const size_t bpp = _mbc[_mbc.keys().front()].rx_dsp_xports[0]->get_recv_frame_size() - hdr_size; -    return bpp/_rx_otw_type.get_sample_size(); -} +    const size_t bpp = _mbc[_mbc.keys().front()].tx_dsp_xport->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 +    boost::shared_ptr<sph::send_packet_streamer> my_streamer = boost::make_shared<sph::send_packet_streamer>(spp); + +    //init some streamer stuff +    my_streamer->resize(args.channels.size()); +    my_streamer->set_vrt_packer(&vrt::if_hdr_pack_be, vrt_send_header_offset_words32); + +    //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_be"; +    id.num_outputs = 1; +    my_streamer->set_converter(id); + +    //bind callbacks for the handler +    for (size_t chan_i = 0; chan_i < args.channels.size(); chan_i++){ +        const size_t chan = args.channels[chan_i]; +        size_t num_chan_so_far = 0; +        size_t abs = 0; +        BOOST_FOREACH(const std::string &mb, _mbc.keys()){ +            num_chan_so_far += _mbc[mb].tx_chan_occ; +            if (chan < num_chan_so_far){ +                const size_t dsp = chan + _mbc[mb].tx_chan_occ - num_chan_so_far; +                my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( +                    &usrp2_impl::io_impl::get_send_buff, _io_impl.get(), abs, _1 +                )); +                _mbc[mb].tx_streamers[dsp] = my_streamer; //store weak pointer +                break; +            } +            abs += 1; //assume 1 tx dsp +        } +    } -size_t usrp2_impl::recv( -    const recv_buffs_type &buffs, size_t nsamps_per_buff, -    rx_metadata_t &metadata, const io_type_t &io_type, -    recv_mode_t recv_mode, double timeout -){ -    return _io_impl->recv_handler.recv( -        buffs, nsamps_per_buff, -        metadata, io_type, -        recv_mode, timeout -    ); +    //sets all tick and samp rates on this streamer +    this->update_rates(); + +    return my_streamer;  } diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index 2b541bcf0..bb3bfc7e4 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -456,11 +456,30 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){          _mbc[mb].tx_fe = tx_frontend_core_200::make(              _mbc[mb].iface, U2_REG_SR_ADDR(SR_TX_FRONT)          ); -        //TODO lots of properties to expose here for frontends +          _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec") -            .coerce(boost::bind(&usrp2_impl::update_rx_subdev_spec, this, mb, _1)); +            .subscribe(boost::bind(&usrp2_impl::update_rx_subdev_spec, this, mb, _1));          _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec") -            .coerce(boost::bind(&usrp2_impl::update_tx_subdev_spec, this, mb, _1)); +            .subscribe(boost::bind(&usrp2_impl::update_tx_subdev_spec, this, mb, _1)); + +        const fs_path rx_fe_path = mb_path / "rx_frontends" / "A"; +        const fs_path tx_fe_path = mb_path / "tx_frontends" / "A"; + +        _tree->create<std::complex<double> >(rx_fe_path / "dc_offset" / "value") +            .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, _mbc[mb].rx_fe, _1)) +            .set(std::complex<double>(0.0, 0.0)); +        _tree->create<bool>(rx_fe_path / "dc_offset" / "enable") +            .subscribe(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, _mbc[mb].rx_fe, _1)) +            .set(true); +        _tree->create<std::complex<double> >(rx_fe_path / "iq_balance" / "value") +            .subscribe(boost::bind(&rx_frontend_core_200::set_iq_balance, _mbc[mb].rx_fe, _1)) +            .set(std::complex<double>(0.0, 0.0)); +        _tree->create<std::complex<double> >(tx_fe_path / "dc_offset" / "value") +            .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, _mbc[mb].tx_fe, _1)) +            .set(std::complex<double>(0.0, 0.0)); +        _tree->create<std::complex<double> >(tx_fe_path / "iq_balance" / "value") +            .subscribe(boost::bind(&tx_frontend_core_200::set_iq_balance, _mbc[mb].tx_fe, _1)) +            .set(std::complex<double>(0.0, 0.0));          ////////////////////////////////////////////////////////////////          // create rx dsp control objects @@ -480,9 +499,12 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){              _mbc[mb].rx_dsp_xports[dspno]->get_recv_buff(0.01).get(); //recv with timeout for lingering              _mbc[mb].rx_dsp_xports[dspno]->get_recv_buff(0.01).get(); //recv with timeout for expected              fs_path rx_dsp_path = mb_path / str(boost::format("rx_dsps/%u") % dspno); +            _tree->create<meta_range_t>(rx_dsp_path / "rate/range") +                .publish(boost::bind(&rx_dsp_core_200::get_host_rates, _mbc[mb].rx_dsps[dspno]));              _tree->create<double>(rx_dsp_path / "rate/value") +                .set(1e6) //some default                  .coerce(boost::bind(&rx_dsp_core_200::set_host_rate, _mbc[mb].rx_dsps[dspno], _1)) -                .subscribe(boost::bind(&usrp2_impl::update_rx_samp_rate, this, _1)); +                .subscribe(boost::bind(&usrp2_impl::update_rx_samp_rate, this, mb, dspno, _1));              _tree->create<double>(rx_dsp_path / "freq/value")                  .coerce(boost::bind(&rx_dsp_core_200::set_freq, _mbc[mb].rx_dsps[dspno], _1));              _tree->create<meta_range_t>(rx_dsp_path / "freq/range") @@ -500,9 +522,12 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){          _mbc[mb].tx_dsp->set_link_rate(USRP2_LINK_RATE_BPS);          _tree->access<double>(mb_path / "tick_rate")              .subscribe(boost::bind(&tx_dsp_core_200::set_tick_rate, _mbc[mb].tx_dsp, _1)); +        _tree->create<meta_range_t>(mb_path / "tx_dsps/0/rate/range") +            .publish(boost::bind(&tx_dsp_core_200::get_host_rates, _mbc[mb].tx_dsp));          _tree->create<double>(mb_path / "tx_dsps/0/rate/value") +            .set(1e6) //some default              .coerce(boost::bind(&tx_dsp_core_200::set_host_rate, _mbc[mb].tx_dsp, _1)) -            .subscribe(boost::bind(&usrp2_impl::update_tx_samp_rate, this, _1)); +            .subscribe(boost::bind(&usrp2_impl::update_tx_samp_rate, this, mb, 0, _1));          _tree->create<double>(mb_path / "tx_dsps/0/freq/value")              .coerce(boost::bind(&usrp2_impl::set_tx_dsp_freq, this, mb, _1));          _tree->create<meta_range_t>(mb_path / "tx_dsps/0/freq/range") @@ -572,42 +597,21 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){          _mbc[mb].dboard_iface = make_usrp2_dboard_iface(_mbc[mb].iface, _mbc[mb].clock);          _tree->create<dboard_iface::sptr>(mb_path / "dboards/A/iface").set(_mbc[mb].dboard_iface);          _mbc[mb].dboard_manager = dboard_manager::make( -            rx_db_eeprom.id, -            ((gdb_eeprom.id == dboard_id_t::none())? tx_db_eeprom : gdb_eeprom).id, -            _mbc[mb].dboard_iface +            rx_db_eeprom.id, tx_db_eeprom.id, gdb_eeprom.id, +            _mbc[mb].dboard_iface, _tree->subtree(mb_path / "dboards/A")          ); -        BOOST_FOREACH(const std::string &name, _mbc[mb].dboard_manager->get_rx_subdev_names()){ -            dboard_manager::populate_prop_tree_from_subdev( -                _tree->subtree(mb_path / "dboards/A/rx_frontends" / name), -                _mbc[mb].dboard_manager->get_rx_subdev(name) -            ); -        } -        BOOST_FOREACH(const std::string &name, _mbc[mb].dboard_manager->get_tx_subdev_names()){ -            dboard_manager::populate_prop_tree_from_subdev( -                _tree->subtree(mb_path / "dboards/A/tx_frontends" / name), -                _mbc[mb].dboard_manager->get_tx_subdev(name) -            ); -        }      }      //initialize io handling      this->io_init();      //do some post-init tasks +    this->update_rates();      BOOST_FOREACH(const std::string &mb, _mbc.keys()){          fs_path root = "/mboards/" + mb; -        _tree->access<double>(root / "tick_rate").update(); - -        //and now that the tick rate is set, init the host rates to something -        BOOST_FOREACH(const std::string &name, _tree->list(root / "rx_dsps")){ -            _tree->access<double>(root / "rx_dsps" / name / "rate" / "value").set(1e6); -        } -        BOOST_FOREACH(const std::string &name, _tree->list(root / "tx_dsps")){ -            _tree->access<double>(root / "tx_dsps" / name / "rate" / "value").set(1e6); -        } -        _tree->access<subdev_spec_t>(root / "rx_subdev_spec").set(subdev_spec_t("A:"+_mbc[mb].dboard_manager->get_rx_subdev_names()[0])); -        _tree->access<subdev_spec_t>(root / "tx_subdev_spec").set(subdev_spec_t("A:"+_mbc[mb].dboard_manager->get_tx_subdev_names()[0])); +        _tree->access<subdev_spec_t>(root / "rx_subdev_spec").set(subdev_spec_t("A:" + _tree->list(root / "dboards/A/rx_frontends").at(0))); +        _tree->access<subdev_spec_t>(root / "tx_subdev_spec").set(subdev_spec_t("A:" + _tree->list(root / "dboards/A/tx_frontends").at(0)));          _tree->access<std::string>(root / "clock_source/value").set("internal");          _tree->access<std::string>(root / "time_source/value").set("none"); diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index 6f133f411..31a390af7 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -31,7 +31,6 @@  #include <uhd/device.hpp>  #include <uhd/utils/pimpl.hpp>  #include <uhd/types/dict.hpp> -#include <uhd/types/otw_type.hpp>  #include <uhd/types/stream_cmd.hpp>  #include <uhd/types/clock_config.hpp>  #include <uhd/usrp/dboard_eeprom.hpp> @@ -73,18 +72,8 @@ public:      ~usrp2_impl(void);      //the io interface -    size_t send( -        const send_buffs_type &, size_t, -        const uhd::tx_metadata_t &, const uhd::io_type_t &, -        uhd::device::send_mode_t, double -    ); -    size_t recv( -        const recv_buffs_type &, size_t, -        uhd::rx_metadata_t &, const uhd::io_type_t &, -        uhd::device::recv_mode_t, double -    ); -    size_t get_max_send_samps_per_packet(void) const; -    size_t get_max_recv_samps_per_packet(void) const; +    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);  private: @@ -97,6 +86,8 @@ private:          rx_frontend_core_200::sptr rx_fe;          tx_frontend_core_200::sptr tx_fe;          std::vector<rx_dsp_core_200::sptr> rx_dsps; +        std::vector<boost::weak_ptr<uhd::rx_streamer> > rx_streamers; +        std::vector<boost::weak_ptr<uhd::tx_streamer> > tx_streamers;          tx_dsp_core_200::sptr tx_dsp;          time64_core_200::sptr time64;          std::vector<uhd::transport::zero_copy_if::sptr> rx_dsp_xports; @@ -120,15 +111,15 @@ private:      }      //io impl methods and members -    uhd::otw_type_t _rx_otw_type, _tx_otw_type;      UHD_PIMPL_DECL(io_impl) _io_impl;      void io_init(void);      void update_tick_rate(const double rate); -    void update_rx_samp_rate(const double rate); -    void update_tx_samp_rate(const double rate); +    void update_rx_samp_rate(const std::string &, const size_t, const double rate); +    void update_tx_samp_rate(const std::string &, const size_t, const double rate); +    void update_rates(void);      //update spec methods are coercers until we only accept db_name == A -    uhd::usrp::subdev_spec_t update_rx_subdev_spec(const std::string &, const uhd::usrp::subdev_spec_t &); -    uhd::usrp::subdev_spec_t update_tx_subdev_spec(const std::string &, const uhd::usrp::subdev_spec_t &); +    void update_rx_subdev_spec(const std::string &, const uhd::usrp::subdev_spec_t &); +    void update_tx_subdev_spec(const std::string &, const uhd::usrp::subdev_spec_t &);      double set_tx_dsp_freq(const std::string &, const double);      uhd::meta_range_t get_tx_dsp_freq_range(const std::string &);      void update_clock_source(const std::string &, const std::string &); diff --git a/host/lib/usrp/usrp2/usrp2_regs.hpp b/host/lib/usrp/usrp2/usrp2_regs.hpp index 8839997f1..179a930c6 100644 --- a/host/lib/usrp/usrp2/usrp2_regs.hpp +++ b/host/lib/usrp/usrp2/usrp2_regs.hpp @@ -50,6 +50,7 @@  #define SR_TX_CTRL  144   // 6  #define SR_TX_DSP   160   // 5 +#define SR_GPIO     184  #define SR_UDP_SM   192   // 64  #define U2_REG_SR_ADDR(sr) (SETTING_REGS_BASE + (4 * (sr))) @@ -95,6 +96,7 @@  // Readback regs  ////////////////////////////////////////////////  #define U2_REG_STATUS READBACK_BASE + 4*8 +#define U2_REG_GPIO_RB READBACK_BASE + 4*9  #define U2_REG_TIME64_SECS_RB_IMM READBACK_BASE + 4*10  #define U2_REG_TIME64_TICKS_RB_IMM READBACK_BASE + 4*11  #define U2_REG_COMPAT_NUM_RB READBACK_BASE + 4*12 diff --git a/host/lib/utils/CMakeLists.txt b/host/lib/utils/CMakeLists.txt index fd3249099..19dde9a56 100644 --- a/host/lib/utils/CMakeLists.txt +++ b/host/lib/utils/CMakeLists.txt @@ -134,7 +134,6 @@ LIBUHD_APPEND_SOURCES(      ${CMAKE_CURRENT_SOURCE_DIR}/log.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/msg.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/paths.cpp -    ${CMAKE_CURRENT_SOURCE_DIR}/props.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/static.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/tasks.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/thread_priority.cpp diff --git a/host/lib/utils/props.cpp b/host/lib/utils/props.cpp deleted file mode 100644 index fc9f8e63f..000000000 --- a/host/lib/utils/props.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// -// Copyright 2010 Ettus Research LLC -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program.  If not, see <http://www.gnu.org/licenses/>. -// - -#include <uhd/utils/props.hpp> - -using namespace uhd; - -named_prop_t::named_prop_t( -    const wax::obj &key, -    const std::string &name -): -    key(key), -    name(name) -{ -    /* NOP */ -} - -named_prop_t named_prop_t::extract( -    const wax::obj &key, -    const std::string &name -){ -    if (key.type() == typeid(named_prop_t)){ -        return key.as<named_prop_t>(); -    } -    return named_prop_t(key, name); -} diff --git a/host/lib/wax.cpp b/host/lib/wax.cpp deleted file mode 100644 index 5f658acd8..000000000 --- a/host/lib/wax.cpp +++ /dev/null @@ -1,151 +0,0 @@ -// -// Copyright 2010-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/>. -// - -#include <uhd/wax.hpp> -#include <uhd/exception.hpp> -#include <boost/format.hpp> -#include <stdexcept> - -/*! - * The link args for internal use within this cpp file: - * - * It contains a link (in this case a pointer) to a wax object. - * Only the methods in this file may create or parse link args. - * The get_link method is the creator of a link args object. - * The [] operator will resolve the link and make the [] call. - * - * TODO: register the link args with the wax obj that it links to. - * That way, if the obj destructs, the link can be invalidated. - * The operator() will throw, rather than dereferencing bad memory. - */ -class link_args_t{ -public: -    link_args_t(const wax::obj *obj_ptr) : _obj_ptr(obj_ptr){ -        /* NOP */ -    } -    wax::obj & operator()(void) const{ -        //recursively resolve link args to get at original pointer -        if (_obj_ptr->type() == typeid(link_args_t)){ -            return _obj_ptr->as<link_args_t>()(); -        } -        return *const_cast<wax::obj *>(_obj_ptr); -    } -private: -    const wax::obj *_obj_ptr; -}; - -/*! - * The proxy args for internal use within this cpp file: - * - * It contains a link and a key for setting/getting a property. - * Only the methods in this file may create or parse proxy args. - * Class methods have special handling for the case when the - * wax obj contains an instance of the proxy args. - */ -class proxy_args_t{ -public: -    proxy_args_t(const wax::obj *obj_ptr, const wax::obj &key) : _key(key){ -        _obj_link = obj_ptr->get_link(); -    } -    wax::obj & operator()(void) const{ -        return _obj_link.as<link_args_t>()(); -    } -    const wax::obj & key(void) const{ -        return _key; -    } -private: -    wax::obj _obj_link; -    const wax::obj _key; -}; - -/*********************************************************************** - * Structors - **********************************************************************/ -wax::obj::obj(void){ -    /* NOP */ -} - -wax::obj::obj(const obj &o){ -    _contents = o._contents; -} - -wax::obj::~obj(void){ -    /* NOP */ -} - -/*********************************************************************** - * Special Operators - **********************************************************************/ -wax::obj wax::obj::operator[](const obj &key){ -    if (_contents.type() == typeid(proxy_args_t)){ -        obj val = resolve(); -        //check if its a special link and call -        if (val.type() == typeid(link_args_t)){ -            return val.as<link_args_t>()()[key]; -        } -        //unknown obj -        throw uhd::type_error("cannot use [] on non wax::obj link"); -    } -    else{ -        return proxy_args_t(this, key); -    } -} - -wax::obj & wax::obj::operator=(const obj &val){ -    if (_contents.type() == typeid(proxy_args_t)){ -        proxy_args_t proxy_args = boost::any_cast<proxy_args_t>(_contents); -        proxy_args().set(proxy_args.key(), val); -    } -    else{ -        _contents = val._contents; -    } -    return *this; -} - -/*********************************************************************** - * Public Methods - **********************************************************************/ -wax::obj wax::obj::get_link(void) const{ -    return link_args_t(this); -} - -const std::type_info & wax::obj::type(void) const{ -    return resolve().type(); -} - -/*********************************************************************** - * Private Methods - **********************************************************************/ -boost::any wax::obj::resolve(void) const{ -    if (_contents.type() == typeid(proxy_args_t)){ -        obj val; -        proxy_args_t proxy_args = boost::any_cast<proxy_args_t>(_contents); -        proxy_args().get(proxy_args.key(), val); -        return val.resolve(); -    } -    else{ -        return _contents; -    } -} - -void wax::obj::get(const obj &, obj &){ -    throw uhd::type_error("Cannot call get on wax obj base class"); -} - -void wax::obj::set(const obj &, const obj &){ -    throw uhd::type_error("Cannot call set on wax obj base class"); -} | 
