diff options
Diffstat (limited to 'host/lib')
155 files changed, 12453 insertions, 3818 deletions
diff --git a/host/lib/CMakeLists.txt b/host/lib/CMakeLists.txt index d8c6fad70..f74af1f29 100644 --- a/host/lib/CMakeLists.txt +++ b/host/lib/CMakeLists.txt @@ -89,12 +89,19 @@ CONFIGURE_FILE( LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/deprecated.cpp ${CMAKE_CURRENT_SOURCE_DIR}/device.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/image_loader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/stream.cpp ${CMAKE_CURRENT_SOURCE_DIR}/exception.cpp ${CMAKE_CURRENT_SOURCE_DIR}/property_tree.cpp ${CMAKE_CURRENT_BINARY_DIR}/version.cpp ) +IF(ENABLE_C_API) + LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/error_c.cpp + ) +ENDIF(ENABLE_C_API) + ######################################################################## # Add DLL resource file to Windows build ######################################################################## @@ -103,8 +110,24 @@ IF(MSVC) IF(UHD_VERSION_DEVEL) SET(RC_TRIMMED_VERSION_PATCH "999") ENDIF(UHD_VERSION_DEVEL) + + # Allow a custom .rc template file to be used + IF(CUSTOM_RC_FILE) + IF(IS_ABSOLUTE "${CUSTOM_RC_FILE}") + SET(UHD_RC_IN "${CUSTOM_RC_FILE}") + ELSE() + SET(UHD_RC_IN "${CMAKE_BINARY_DIR}/${CUSTOM_RC_FILE}") + ENDIF(IS_ABSOLUTE "${CUSTOM_RC_FILE}") + MESSAGE(STATUS "") + MESSAGE(STATUS "Using custom RC template: ${UHD_RC_IN}") + MESSAGE(STATUS "") + ELSE() + SET(UHD_RC_IN "${CMAKE_CURRENT_SOURCE_DIR}/uhd.rc.in") + ENDIF(CUSTOM_RC_FILE) + SET(UHD_RC_IN ${UHD_RC_IN} CACHE STRING "uhd.rc template filepath") + CONFIGURE_FILE( - ${CMAKE_CURRENT_SOURCE_DIR}/uhd.rc.in + ${UHD_RC_IN} ${CMAKE_CURRENT_BINARY_DIR}/uhd.rc @ONLY) diff --git a/host/lib/convert/CMakeLists.txt b/host/lib/convert/CMakeLists.txt index 5204c29ea..024c2260b 100644 --- a/host/lib/convert/CMakeLists.txt +++ b/host/lib/convert/CMakeLists.txt @@ -22,40 +22,6 @@ INCLUDE(CheckIncludeFileCXX) MESSAGE(STATUS "") ######################################################################## -# Look for Orc support -######################################################################## -FIND_PACKAGE(ORC) - -IF(NOT ORCC_EXECUTABLE) - FIND_PROGRAM(ORCC_EXECUTABLE orcc) -ENDIF() - -LIBUHD_REGISTER_COMPONENT("ORC" ENABLE_ORC ON "ENABLE_LIBUHD;ORC_FOUND;ORCC_EXECUTABLE" OFF) - -IF(ENABLE_ORC) - INCLUDE_DIRECTORIES(${ORC_INCLUDE_DIRS}) - LINK_DIRECTORIES(${ORC_LIBRARY_DIRS}) - ENABLE_LANGUAGE(C) - - SET(orcc_src ${CMAKE_CURRENT_SOURCE_DIR}/convert_orc.orc) - - GET_FILENAME_COMPONENT(orc_file_name_we ${orcc_src} NAME_WE) - SET(orcc_gen ${CMAKE_CURRENT_BINARY_DIR}/${orc_file_name_we}.c) - MESSAGE(STATUS "Orc found, enabling Orc support.") - ADD_CUSTOM_COMMAND( - COMMAND ${ORCC_EXECUTABLE} --implementation -o ${orcc_gen} ${orcc_src} - DEPENDS ${orcc_src} OUTPUT ${orcc_gen} - ) - LIBUHD_APPEND_SOURCES(${orcc_gen}) - LIBUHD_APPEND_SOURCES( - ${CMAKE_CURRENT_SOURCE_DIR}/convert_with_orc.cpp - ) - LIBUHD_APPEND_LIBS(${ORC_LIBRARIES}) -ELSE(ENABLE_ORC) - MESSAGE(STATUS "Orc not found, disabling orc support.") -ENDIF(ENABLE_ORC) - -######################################################################## # Check for SSE2 SIMD headers ######################################################################## IF(CMAKE_COMPILER_IS_GNUCXX) diff --git a/host/lib/convert/convert_common.hpp b/host/lib/convert/convert_common.hpp index 6c2ea9fec..6e73e9436 100644 --- a/host/lib/convert/convert_common.hpp +++ b/host/lib/convert/convert_common.hpp @@ -65,11 +65,10 @@ static const int PRIORITY_GENERAL = 0; static const int PRIORITY_EMPTY = -1; #ifdef __ARM_NEON__ -static const int PRIORITY_LIBORC = 3; -static const int PRIORITY_SIMD = 2; //neon conversions could be implemented better, orc wins +static const int PRIORITY_SIMD = 2; static const int PRIORITY_TABLE = 1; //tables require large cache, so they are slower on arm #else -static const int PRIORITY_LIBORC = 2; +// We used to have ORC, too, so SIMD is 3 static const int PRIORITY_SIMD = 3; static const int PRIORITY_TABLE = 1; #endif @@ -87,6 +86,7 @@ typedef float f32_t; typedef boost::int32_t s32_t; typedef boost::int16_t s16_t; typedef boost::int8_t s8_t; +typedef boost::uint8_t u8_t; typedef boost::uint32_t item32_t; diff --git a/host/lib/convert/convert_impl.cpp b/host/lib/convert/convert_impl.cpp index 329e94a4d..d90bb9c94 100644 --- a/host/lib/convert/convert_impl.cpp +++ b/host/lib/convert/convert_impl.cpp @@ -43,10 +43,10 @@ bool convert::operator==(const convert::id_type &lhs, const convert::id_type &rh 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" + " Input format: %s\n" + " Num inputs: %d\n" " Output format: %s\n" - " Num outputs: %d\n" + " Num outputs: %d\n" ) % this->input_format % this->num_inputs @@ -55,6 +55,15 @@ std::string convert::id_type::to_pp_string(void) const{ ); } +std::string convert::id_type::to_string(void) const{ + return str(boost::format("%s (%d) -> %s (%d)") + % this->input_format + % this->num_inputs + % this->output_format + % this->num_outputs + ); +} + /*********************************************************************** * Setup the table registry **********************************************************************/ @@ -92,7 +101,15 @@ convert::function_type convert::get_converter( //find a matching priority priority_type best_prio = -1; BOOST_FOREACH(priority_type prio_i, get_table()[id].keys()){ - if (prio_i == prio) return get_table()[id][prio]; + if (prio_i == prio) { + //----------------------------------------------------------------// + UHD_LOGV(always) << "get_converter: For converter ID: " << id.to_pp_string() << std::endl + << "Using prio: " << prio << std::endl + << std::endl + ; + //----------------------------------------------------------------// + return get_table()[id][prio]; + } best_prio = std::max(best_prio, prio_i); } @@ -100,6 +117,13 @@ convert::function_type convert::get_converter( if (prio != -1) throw uhd::key_error( "Cannot find a conversion routine [with prio] for " + id.to_pp_string()); + //----------------------------------------------------------------// + UHD_LOGV(always) << "get_converter: For converter ID: " << id.to_pp_string() << std::endl + << "Using prio: " << best_prio << std::endl + << std::endl + ; + //----------------------------------------------------------------// + //otherwise, return best prio return get_table()[id][best_prio]; } @@ -148,6 +172,7 @@ UHD_STATIC_BLOCK(convert_register_item_sizes){ convert::register_bytes_per_item("s32", sizeof(boost::int32_t)); convert::register_bytes_per_item("s16", sizeof(boost::int16_t)); convert::register_bytes_per_item("s8", sizeof(boost::int8_t)); + convert::register_bytes_per_item("u8", sizeof(boost::uint8_t)); //register VITA types convert::register_bytes_per_item("item32", sizeof(boost::int32_t)); diff --git a/host/lib/convert/convert_orc.orc b/host/lib/convert/convert_orc.orc deleted file mode 100644 index ffb298f26..000000000 --- a/host/lib/convert/convert_orc.orc +++ /dev/null @@ -1,79 +0,0 @@ -.function _convert_fc32_1_to_item32_1_nswap_orc -.source 8 src -.dest 4 dst -.floatparam 4 scalar -.temp 8 scaled -.temp 8 converted -.temp 4 short -x2 mulf scaled, src, scalar -x2 convfl converted, scaled -x2 convlw short, converted -swapl short, short -x2 swapw dst, short - -.function _convert_fc32_1_to_item32_1_bswap_orc -.source 8 src -.dest 4 dst -.floatparam 4 scalar -.temp 8 scaled -.temp 8 converted -.temp 4 short -x2 mulf scaled, src, scalar -x2 convfl converted, scaled -x2 convlw short, converted -x2 swapw dst, short - -.function _convert_item32_1_to_fc32_1_nswap_orc -.source 4 src -.dest 8 dst -.floatparam 4 scalar -.temp 4 tmp1 -.temp 8 tmp2 -x2 swapw tmp1, src -swapl tmp1, tmp1 -x2 convswl tmp2, tmp1 -x2 convlf tmp2, tmp2 -x2 mulf dst, tmp2, scalar - -.function _convert_item32_1_to_fc32_1_bswap_orc -.source 4 src -.dest 8 dst -.floatparam 4 scalar -.temp 4 tmp1 -.temp 8 tmp2 -x2 swapw tmp1, src -x2 convswl tmp2, tmp1 -x2 convlf tmp2, tmp2 -x2 mulf dst, tmp2, scalar - -.function _convert_sc16_1_to_item32_1_nswap_orc -.source 4 src -.dest 4 dst -.temp 4 tmp -.floatparam 4 scalar -swapl tmp, src -x2 swapw dst, tmp - -.function _convert_item32_1_to_sc16_1_nswap_orc -.source 4 src -.dest 4 dst -.floatparam 4 scalar -.temp 4 tmp -x2 swapw tmp, src -swapl dst, tmp - -.function _convert_swap_byte_pairs_orc -.source 4 src -.dest 4 dst -swapl dst, src - -.function _convert_fc32_1_to_sc8_1_nswap_orc -.source 8 src -.dest 2 dst -.temp 8 tmp -.temp 4 tmp2 -.floatparam 4 scalar -x2 mulf tmp, src, scalar -x2 convfl tmp, tmp -x2 convlw tmp2, tmp -x2 convwb dst, tmp2 diff --git a/host/lib/convert/convert_with_orc.cpp b/host/lib/convert/convert_with_orc.cpp deleted file mode 100644 index 19755fa44..000000000 --- a/host/lib/convert/convert_with_orc.cpp +++ /dev/null @@ -1,65 +0,0 @@ -// -// Copyright 2011-2013 Ettus Research LLC -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. -// - -#include "convert_common.hpp" -#include <uhd/utils/byteswap.hpp> - -using namespace uhd::convert; - -extern "C" { -extern void _convert_fc32_1_to_item32_1_nswap_orc(void *, const void *, float, int); -extern void _convert_fc32_1_to_item32_1_bswap_orc(void *, const void *, float, int); -extern void _convert_item32_1_to_fc32_1_nswap_orc(void *, const void *, float, int); -extern void _convert_item32_1_to_fc32_1_bswap_orc(void *, const void *, float, int); -extern void _convert_sc16_1_to_item32_1_nswap_orc(void *, const void *, float, int); -extern void _convert_item32_1_to_sc16_1_nswap_orc(void *, const void *, float, int); -extern void _convert_fc32_1_to_sc8_1_nswap_orc(void *, const void *, float, int); -extern void _convert_swap_byte_pairs_orc(void *, const void *, int); -} - -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(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(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(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(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(sc16_item32_le, 1, sc16, 1, PRIORITY_LIBORC){ - _convert_item32_1_to_sc16_1_nswap_orc(outputs[0], inputs[0], scale_factor, nsamps); -} - -DECLARE_CONVERTER(fc32, 1, sc8_item32_be, 1, PRIORITY_LIBORC){ - _convert_fc32_1_to_sc8_1_nswap_orc(outputs[0], inputs[0], scale_factor, nsamps); -} - -DECLARE_CONVERTER(fc32, 1, sc8_item32_le, 1, PRIORITY_LIBORC){ - _convert_fc32_1_to_sc8_1_nswap_orc(outputs[0], inputs[0], scale_factor, nsamps); - _convert_swap_byte_pairs_orc(outputs[0], outputs[0], (nsamps + 1)/2); -} diff --git a/host/lib/convert/gen_convert_general.py b/host/lib/convert/gen_convert_general.py index b0790755a..4f9eeb747 100644 --- a/host/lib/convert/gen_convert_general.py +++ b/host/lib/convert/gen_convert_general.py @@ -17,92 +17,147 @@ # TMPL_HEADER = """ -#import time +<% + import time +%> /*********************************************************************** - * This file was generated by $file on $time.strftime("%c") + * This file was generated by ${file} on ${time.strftime("%c")} **********************************************************************/ -\#include "convert_common.hpp" -\#include <uhd/utils/byteswap.hpp> +#include "convert_common.hpp" +#include <uhd/utils/byteswap.hpp> using namespace uhd::convert; + + +// item32 -> item32: Just a memcpy. No scaling possible. +DECLARE_CONVERTER(item32, 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]); + + memcpy(output, input, nsamps * sizeof(item32_t)); +} """ TMPL_CONV_GEN2_ITEM32 = """ -DECLARE_CONVERTER(item32, 1, sc16_item32_$(end), 1, PRIORITY_GENERAL){ +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]); - for (size_t i = 0; i < nsamps; i++){ - output[i] = $(to_wire)(input[i]); - } -} + for (size_t i = 0; i < nsamps; i++) {{ + output[i] = {to_wire}(input[i]); + }} +}} -DECLARE_CONVERTER(sc16_item32_$(end), 1, item32, 1, PRIORITY_GENERAL){ +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]); - for (size_t i = 0; i < nsamps; i++){ - output[i] = $(to_host)(input[i]); - } -} + for (size_t i = 0; i < nsamps; i++) {{ + output[i] = {to_host}(input[i]); + }} +}} +""" + +TMPL_CONV_U8 = """ +DECLARE_CONVERTER(u8, 1, u8_item32_{end}, 1, PRIORITY_GENERAL) {{ + const boost::uint32_t *input = reinterpret_cast<const boost::uint32_t *>(inputs[0]); + boost::uint32_t *output = reinterpret_cast<boost::uint32_t *>(outputs[0]); + + // 1) Copy all the 4-byte tuples + size_t n_words = nsamps / 4; + for (size_t i = 0; i < n_words; i++) {{ + output[i] = {to_wire}(input[i]); + }} + // 2) If nsamps was not a multiple of 4, copy the rest by hand + size_t bytes_left = nsamps % 4; + if (bytes_left) {{ + const u8_t *last_input_word = reinterpret_cast<const u8_t *>(&input[n_words]); + u8_t *last_output_word = reinterpret_cast<u8_t *>(&output[n_words]); + for (size_t k = 0; k < bytes_left; k++) {{ + last_output_word[k] = last_input_word[k]; + }} + output[n_words] = {to_wire}(output[n_words]); + }} +}} + +DECLARE_CONVERTER(u8_item32_{end}, 1, u8, 1, PRIORITY_GENERAL) {{ + const boost::uint32_t *input = reinterpret_cast<const boost::uint32_t *>(inputs[0]); + boost::uint32_t *output = reinterpret_cast<boost::uint32_t *>(outputs[0]); + + // 1) Copy all the 4-byte tuples + size_t n_words = nsamps / 4; + for (size_t i = 0; i < n_words; i++) {{ + output[i] = {to_host}(input[i]); + }} + // 2) If nsamps was not a multiple of 4, copy the rest by hand + size_t bytes_left = nsamps % 4; + if (bytes_left) {{ + boost::uint32_t last_input_word = {to_host}(input[n_words]); + const u8_t *last_input_word_ptr = reinterpret_cast<const u8_t *>(&last_input_word); + u8_t *last_output_word = reinterpret_cast<u8_t *>(&output[n_words]); + for (size_t k = 0; k < bytes_left; k++) {{ + last_output_word[k] = last_input_word_ptr[k]; + }} + }} +}} """ 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 +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}]); + % endfor boost::uint16_t *output = reinterpret_cast<boost::uint16_t *>(outputs[0]); for (size_t i = 0, j = 0; i < nsamps; i++){ - #for $w in range($width) - output[j++] = $(to_wire)(boost::uint16_t(boost::int16_t(input$(w)[i].real()$(do_scale)))); - output[j++] = $(to_wire)(boost::uint16_t(boost::int16_t(input$(w)[i].imag()$(do_scale)))); - #end for + % for w in range(width): + output[j++] = ${to_wire}(boost::uint16_t(boost::int16_t(input${w}[i].real()${do_scale}))); + output[j++] = ${to_wire}(boost::uint16_t(boost::int16_t(input${w}[i].imag()${do_scale}))); + % endfor } } -DECLARE_CONVERTER(sc16_item16_usrp1, 1, $(cpu_type), $(width), PRIORITY_GENERAL){ +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 + % for w in range(width): + ${cpu_type}_t *output${w} = reinterpret_cast<${cpu_type}_t *>(outputs[${w}]); + % endfor 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) + % 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 + % endfor } } -DECLARE_CONVERTER(sc8_item16_usrp1, 1, $(cpu_type), $(width), PRIORITY_GENERAL){ +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 + % for w in range(width): + ${cpu_type}_t *output${w} = reinterpret_cast<${cpu_type}_t *>(outputs[${w}]); + % endfor for (size_t i = 0, j = 0; i < nsamps; i++){ - #for $w in range($width) + % for w in range(width): { - 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) + 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 + % endfor } } """ def parse_tmpl(_tmpl_text, **kwargs): - from Cheetah.Template import Template - return str(Template(_tmpl_text, kwargs)) + from mako.template import Template + return Template(_tmpl_text).render(**kwargs) if __name__ == '__main__': import sys, os @@ -114,12 +169,19 @@ if __name__ == '__main__': ('be', 'uhd::ntohx', 'uhd::htonx'), ('le', 'uhd::wtohx', 'uhd::htowx'), ): - output += parse_tmpl( - TMPL_CONV_GEN2_ITEM32, + output += TMPL_CONV_GEN2_ITEM32.format( end=end, to_host=to_host, to_wire=to_wire - ) + ) + #generate raw (u8) converters: + for end, to_host, to_wire in ( + ('be', 'uhd::ntohx', 'uhd::htonx'), + ('le', 'uhd::wtohx', 'uhd::htowx'), + ): + output += TMPL_CONV_U8.format( + end=end, to_host=to_host, to_wire=to_wire + ) - #generate complex converters for usrp1 format + #generate complex converters for usrp1 format (requires Cheetah) for width in 1, 2, 4: for cpu_type, do_scale in ( ('fc64', '*scale_factor'), diff --git a/host/lib/error_c.cpp b/host/lib/error_c.cpp new file mode 100644 index 000000000..3ce63a81d --- /dev/null +++ b/host/lib/error_c.cpp @@ -0,0 +1,76 @@ +// +// Copyright 2015 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/error.h> +#include <uhd/exception.hpp> +#include <uhd/utils/static.hpp> + +#include <boost/thread/mutex.hpp> + +#include <cstring> + +#define MAP_TO_ERROR(exception_type, error_type) \ + if (dynamic_cast<const uhd::exception_type*>(e)) return error_type; + +uhd_error error_from_uhd_exception(const uhd::exception* e){ + MAP_TO_ERROR(index_error, UHD_ERROR_INDEX) + MAP_TO_ERROR(key_error, UHD_ERROR_KEY) + MAP_TO_ERROR(not_implemented_error, UHD_ERROR_NOT_IMPLEMENTED) + MAP_TO_ERROR(usb_error, UHD_ERROR_USB) + MAP_TO_ERROR(io_error, UHD_ERROR_IO) + MAP_TO_ERROR(os_error, UHD_ERROR_OS) + MAP_TO_ERROR(assertion_error, UHD_ERROR_ASSERTION) + MAP_TO_ERROR(lookup_error, UHD_ERROR_LOOKUP) + MAP_TO_ERROR(type_error, UHD_ERROR_TYPE) + MAP_TO_ERROR(value_error, UHD_ERROR_VALUE) + MAP_TO_ERROR(runtime_error, UHD_ERROR_RUNTIME) + MAP_TO_ERROR(environment_error, UHD_ERROR_ENVIRONMENT) + MAP_TO_ERROR(system_error, UHD_ERROR_SYSTEM) + + return UHD_ERROR_EXCEPT; +} + +// Store the error string in a single place in library +UHD_SINGLETON_FCN(std::string, _c_global_error_string) + +static boost::mutex _error_c_mutex; + +const std::string& get_c_global_error_string(){ + boost::mutex::scoped_lock lock(_error_c_mutex); + return _c_global_error_string(); +} + +void set_c_global_error_string( + const std::string &msg +){ + boost::mutex::scoped_lock lock(_error_c_mutex); + _c_global_error_string() = msg; +} + +uhd_error uhd_get_last_error( + char* error_out, + size_t strbuffer_len +){ + try{ + memset(error_out, '\0', strbuffer_len); + strncpy(error_out, _c_global_error_string().c_str(), strbuffer_len); + } + catch(...){ + return UHD_ERROR_UNKNOWN; + } + return UHD_ERROR_NONE; +} diff --git a/host/lib/exception.cpp b/host/lib/exception.cpp index ea056bd3b..a9e36fd15 100644 --- a/host/lib/exception.cpp +++ b/host/lib/exception.cpp @@ -43,3 +43,8 @@ make_exception_impl("EnvironmentError", environment_error, exception) make_exception_impl("IOError", io_error, environment_error) make_exception_impl("OSError", os_error, environment_error) make_exception_impl("SystemError", system_error, exception) + +usb_error::usb_error(int code, const std::string &what): + runtime_error(str(boost::format("%s %d: %s") % "USBError" % code % what)), _code(code) {} +usb_error *usb_error::dynamic_clone(void) const{return new usb_error(*this);} \ +void usb_error::dynamic_throw(void) const{throw *this;} diff --git a/host/lib/ic_reg_maps/common.py b/host/lib/ic_reg_maps/common.py index 24f5bf8be..5c0cfc109 100644..100755 --- a/host/lib/ic_reg_maps/common.py +++ b/host/lib/ic_reg_maps/common.py @@ -1,5 +1,5 @@ # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-2011,2015 Ettus Research LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -18,105 +18,99 @@ import re import sys import math -from Cheetah.Template import Template +from mako.template import Template -COMMON_TMPL = """\ -#import time +COMMON_TMPL = """<% import time %>\ /*********************************************************************** - * This file was generated by $file on $time.strftime("%c") + * This file was generated by ${file} on ${time.strftime("%c")} **********************************************************************/ -\#ifndef INCLUDED_$(name.upper())_HPP -\#define INCLUDED_$(name.upper())_HPP +#ifndef INCLUDED_${name.upper()}_HPP +#define INCLUDED_${name.upper()}_HPP -\#include <uhd/config.hpp> -\#include <uhd/exception.hpp> -\#include <boost/cstdint.hpp> -\#include <set> +#include <uhd/config.hpp> +#include <uhd/exception.hpp> +#include <boost/cstdint.hpp> +#include <set> -class $(name)_t{ +class ${name}_t{ public: - #for $reg in $regs - #if $reg.get_enums() - enum $reg.get_type(){ - #for $i, $enum in enumerate($reg.get_enums()) - #set $end_comma = ',' if $i < len($reg.get_enums())-1 else '' - $(reg.get_name().upper())_$(enum[0].upper()) = $enum[1]$end_comma - #end for + % for reg in regs: + % if reg.get_enums(): + enum ${reg.get_type()}{ + % for i,enum in enumerate(reg.get_enums()): + ${reg.get_name().upper()}_${enum[0].upper()} = ${enum[1]}<% comma = ',' if i != (len(reg.get_enums())-1) else '' %>${comma} + % endfor }; - #end if - $reg.get_type() $reg.get_name(); - #end for + % endif + ${reg.get_type()} ${reg.get_name()}; + % endfor - $(name)_t(void){ + ${name}_t(void){ _state = NULL; - #for $reg in $regs - $reg.get_name() = $reg.get_default(); - #end for + % for reg in regs: + ${reg.get_name()} = ${reg.get_default()}; + % endfor } - ~$(name)_t(void){ + ~${name}_t(void){ delete _state; } - $body + ${body} void save_state(void){ - if (_state == NULL) _state = new $(name)_t(); - #for $reg in $regs - _state->$reg.get_name() = this->$reg.get_name(); - #end for + if (_state == NULL) _state = new ${name}_t(); + % for reg in regs: + _state->${reg.get_name()} = this->${reg.get_name()}; + % endfor } template<typename T> std::set<T> get_changed_addrs(void){ if (_state == NULL) throw uhd::runtime_error("no saved state"); //check each register for changes std::set<T> addrs; - #for $reg in $regs - if(_state->$reg.get_name() != this->$reg.get_name()){ - addrs.insert($reg.get_addr()); + % for reg in regs: + if(_state->${reg.get_name()} != this->${reg.get_name()}){ + addrs.insert(${reg.get_addr()}); } - #end for + % endfor return addrs; } - #for $mreg in $mregs - $mreg.get_type() get_$(mreg.get_name())(void){ - return - #set $shift = 0 - #for $reg in $mreg.get_regs() - ($(mreg.get_type())($reg.get_name() & $reg.get_mask()) << $shift) | - #set $shift = $shift + $reg.get_bit_width() - #end for + % for mreg in mregs: + ${mreg.get_type()} get_${mreg.get_name()}(void){ + return <% shift = 0 %> + % for reg in mreg.get_regs(): + (${mreg.get_type()}(${reg.get_name()} & ${reg.get_mask()}) << ${shift}) |<% shift = shift + reg.get_bit_width() %> + % endfor 0; } - void set_$(mreg.get_name())($mreg.get_type() reg){ - #set $shift = 0 - #for $reg in $mreg.get_regs() - $reg.get_name() = (reg >> $shift) & $reg.get_mask(); - #set $shift = $shift + $reg.get_bit_width() - #end for + void set_${mreg.get_name()}(${mreg.get_type()} reg){<% shift = 0 %> + % for reg in mreg.get_regs(): + ${reg.get_name()} = (reg >> ${shift}) & ${reg.get_mask()};<% shift = shift + reg.get_bit_width() %> + % endfor } - #end for + % endfor private: - $(name)_t *_state; + ${name}_t *_state; }; -\#endif /* INCLUDED_$(name.upper())_HPP */ +#endif /* INCLUDED_${name.upper()}_HPP */ """ def parse_tmpl(_tmpl_text, **kwargs): - return str(Template(_tmpl_text, kwargs)) + return Template(_tmpl_text).render(**kwargs) def to_num(arg): return int(eval(arg)) class reg: def __init__(self, reg_des): try: self.parse(reg_des) - except Exception, e: - raise Exception, 'Error parsing register description: "%s"\nWhat: %s'%(reg_des, e) + except Exception as e: + raise Exception('Error parsing register description: "%s"\nWhat: %s'%(reg_des, e)) def parse(self, reg_des): x = re.match('^(\w*)\s*(\w*)\[(.*)\]\s*(\w*)\s*(.*)$', reg_des) @@ -133,7 +127,8 @@ class reg: self._enums = list() if enums: enum_val = 0 - for enum_str in map(str.strip, enums.split(',')): + for enum_str_unstripped in enums.split(','): + enum_str = enum_str_unstripped.strip() if '=' in enum_str: enum_name, enum_val = enum_str.split('=') enum_val = to_num(enum_val) @@ -146,7 +141,7 @@ class reg: def get_name(self): return self._name def get_default(self): for key, val in self.get_enums(): - if val == self._default: return str.upper('%s_%s'%(self.get_name(), key)) + if val == self._default: return ('%s_%s'%(self.get_name(), key)).upper() return self._default def get_type(self): if self.get_enums(): return '%s_t'%self.get_name() @@ -158,14 +153,14 @@ class reg: class mreg: def __init__(self, mreg_des, regs): try: self.parse(mreg_des, regs) - except Exception, e: - raise Exception, 'Error parsing meta register description: "%s"\nWhat: %s'%(mreg_des, e) + except Exception as e: + raise Exception('Error parsing meta register description: "%s"\nWhat: %s'%(mreg_des, e)) def parse(self, mreg_des, regs): x = re.match('^~(\w*)\s+(.*)\s*$', mreg_des) self._name, reg_names = x.groups() regs_dict = dict([(reg.get_name(), reg) for reg in regs]) - self._regs = [regs_dict[reg_name] for reg_name in map(str.strip, reg_names.split(','))] + self._regs = [regs_dict[reg_name.strip()] for reg_name in reg_names.split(',')] def get_name(self): return self._name def get_regs(self): return self._regs diff --git a/host/lib/ic_reg_maps/gen_ad5623_regs.py b/host/lib/ic_reg_maps/gen_ad5623_regs.py index e653921ba..8b70a9f0a 100755 --- a/host/lib/ic_reg_maps/gen_ad5623_regs.py +++ b/host/lib/ic_reg_maps/gen_ad5623_regs.py @@ -32,9 +32,9 @@ cmd 0[19:21] 0 wr_input_n, up_dac_n, wr_input_n_up_a BODY_TMPL="""\ boost::uint32_t get_reg(void){ boost::uint32_t reg = 0; - #for $reg in filter(lambda r: r.get_addr() == 0, $regs) - reg |= (boost::uint32_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % for reg in filter(lambda r: r.get_addr() == 0, regs): + reg |= (boost::uint32_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor return reg; } """ diff --git a/host/lib/ic_reg_maps/gen_ad7922_regs.py b/host/lib/ic_reg_maps/gen_ad7922_regs.py index 5cec1924a..c77991182 100755 --- a/host/lib/ic_reg_maps/gen_ad7922_regs.py +++ b/host/lib/ic_reg_maps/gen_ad7922_regs.py @@ -32,16 +32,16 @@ chn 0[13] 0 BODY_TMPL="""\ boost::uint16_t get_reg(void){ boost::uint16_t reg = 0; - #for $reg in filter(lambda r: r.get_addr() == 0, $regs) - reg |= (boost::uint32_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % for reg in filter(lambda r: r.get_addr() == 0, regs): + reg |= (boost::uint32_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor return reg; } void set_reg(boost::uint16_t reg){ - #for $reg in filter(lambda r: r.get_addr() == 0, $regs) - $reg.get_name() = $(reg.get_type())((reg >> $reg.get_shift()) & $reg.get_mask()); - #end for + % for reg in filter(lambda r: r.get_addr() == 0, regs): + ${reg.get_name()} = ${reg.get_type()}((reg >> ${reg.get_shift()}) & ${reg.get_mask()}); + % endfor } """ diff --git a/host/lib/ic_reg_maps/gen_ad9510_regs.py b/host/lib/ic_reg_maps/gen_ad9510_regs.py index 6c1e612cc..9f194b5c9 100755 --- a/host/lib/ic_reg_maps/gen_ad9510_regs.py +++ b/host/lib/ic_reg_maps/gen_ad9510_regs.py @@ -54,25 +54,25 @@ lock_detect_disable 0xD[6] 0 enb, dis ######################################################################## ## fine delay adjust ######################################################################## -#for $i, $o in ((5, 0), (6, 4)) -delay_control_out$i $hex(0x34+$o)[0] 0 -ramp_current_out$i $hex(0x35+$o)[0:2] 0 200ua, 400ua, 600ua, 800ua, 1000ua, 1200ua, 1400ua, 1600ua -ramp_capacitor_out$i $hex(0x35+$o)[3:5] 0 4caps=0, 3caps=1, 2caps=3, 1cap=7 -delay_fine_adjust_out$i $hex(0x36+$o)[1:5] 0 -#end for +% for i, o in ((5, 0), (6, 4)): +delay_control_out${i} ${hex(0x34+o)}[0] 0 +ramp_current_out${i} ${hex(0x35+o)}[0:2] 0 200ua, 400ua, 600ua, 800ua, 1000ua, 1200ua, 1400ua, 1600ua +ramp_capacitor_out${i} ${hex(0x35+o)}[3:5] 0 4caps=0, 3caps=1, 2caps=3, 1cap=7 +delay_fine_adjust_out${i} ${hex(0x36+o)}[1:5] 0 +% endfor ######################################################################## ## outputs ######################################################################## -#for $i, $o in ((0, 0), (1, 1), (2, 2), (3, 3)) -power_down_lvpecl_out$i $hex(0x3C+$o)[0:1] 0 normal, test, safe_pd, total_pd -output_level_lvpecl_out$i $hex(0x3C+$o)[2:3] 2 500mv, 340mv, 810mv, 660mv -#end for -#for $i, $o in ((4, 0), (5, 1), (6, 2), (7, 3)) -power_down_lvds_cmos_out$i $hex(0x40+$o)[0] 0 -output_level_lvds_out$i $hex(0x40+$o)[1:2] 1 1_75ma, 3_5ma, 5_25ma, 7ma -lvds_cmos_select_out$i $hex(0x40+$o)[3] 1 lvds, cmos -inverted_cmos_driver_out$i $hex(0x40+$o)[4] 0 dis, enb -#end for +% for i, o in ((0, 0), (1, 1), (2, 2), (3, 3)): +power_down_lvpecl_out${i} ${hex(0x3C+o)}[0:1] 0 normal, test, safe_pd, total_pd +output_level_lvpecl_out${i} ${hex(0x3C+o)}[2:3] 2 500mv, 340mv, 810mv, 660mv +% endfor +% for i, o in ((4, 0), (5, 1), (6, 2), (7, 3)): +power_down_lvds_cmos_out${i} ${hex(0x40+o)}[0] 0 +output_level_lvds_out${i} ${hex(0x40+o)}[1:2] 1 1_75ma, 3_5ma, 5_25ma, 7ma +lvds_cmos_select_out${i} ${hex(0x40+o)}[3] 1 lvds, cmos +inverted_cmos_driver_out${i} ${hex(0x40+o)}[4] 0 dis, enb +% endfor clock_select 0x45[0] 1 clk2_drives, clk1_drives clk1_power_down 0x45[1] 0 clk2_power_down 0x45[2] 0 @@ -82,15 +82,15 @@ all_clock_inputs_pd 0x45[5] 0 ######################################################################## ## dividers ######################################################################## -#for $i, $o in ((0, 0), (1, 2), (2, 4), (3, 6), (4, 8), (5, 10), (6, 12), (7, 14)) -divider_high_cycles_out$i $hex(0x48+$o)[0:3] 0 -divider_low_cycles_out$i $hex(0x48+$o)[4:7] 0 -phase_offset_out$i $hex(0x49+$o)[0:3] 0 -start_out$i $hex(0x49+$o)[4] 0 -force_out$i $hex(0x49+$o)[5] 0 -nosync_out$i $hex(0x49+$o)[6] 0 -bypass_divider_out$i $hex(0x49+$o)[7] 0 -#end for +% for i, o in ((0, 0), (1, 2), (2, 4), (3, 6), (4, 8), (5, 10), (6, 12), (7, 14)): +divider_high_cycles_out${i} ${hex(0x48+o)}[0:3] 0 +divider_low_cycles_out${i} ${hex(0x48+o)}[4:7] 0 +phase_offset_out${i} ${hex(0x49+o)}[0:3] 0 +start_out${i} ${hex(0x49+o)}[4] 0 +force_out${i} ${hex(0x49+o)}[5] 0 +nosync_out${i} ${hex(0x49+o)}[6] 0 +bypass_divider_out${i} ${hex(0x49+o)}[7] 0 +% endfor ######################################################################## ## function ######################################################################## @@ -110,13 +110,13 @@ BODY_TMPL="""\ boost::uint8_t get_reg(boost::uint16_t addr){ boost::uint8_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 + % 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()}; + % endfor break; - #end for + % endfor } return reg; } diff --git a/host/lib/ic_reg_maps/gen_ad9522_regs.py b/host/lib/ic_reg_maps/gen_ad9522_regs.py index 1512da811..cc906b76c 100755 --- a/host/lib/ic_reg_maps/gen_ad9522_regs.py +++ b/host/lib/ic_reg_maps/gen_ad9522_regs.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-2011,2015 Ettus Research LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -88,39 +88,38 @@ ref2_freq_gt_thresh 0x01F[2] 0 ref1_freq_gt_thresh 0x01F[1] 0 digital_lock_detect 0x01F[0] 0 ######################################################################## -#for $i in range(12) -#set $addr = ($i + 0x0F0) -out$(i)_format $(addr)[7] 0 lvds, cmos -out$(i)_cmos_configuration $(addr)[6:5] 3 off, a_on, b_on, ab_on -out$(i)_polarity $(addr)[4:3] 0 lvds_a_non_b_inv=0, lvds_a_inv_b_non=1, cmos_ab_non=0, cmos_ab_inv=1, cmos_a_non_b_inv=2, cmos_a_inv_b_non=3 -out$(i)_lvds_diff_voltage $(addr)[2:1] 1 1_75ma, 3_5ma, 5_25ma, 7_0ma -out$(i)_lvds_power_down $(addr)[0] 0 -#end for +% for i in range(12): +<% addr = (i + 0x0F0) %>\ +out${i}_format ${addr}[7] 0 lvds, cmos +out${i}_cmos_configuration ${addr}[6:5] 3 off, a_on, b_on, ab_on +out${i}_polarity ${addr}[4:3] 0 lvds_a_non_b_inv=0, lvds_a_inv_b_non=1, cmos_ab_non=0, cmos_ab_inv=1, cmos_a_non_b_inv=2, cmos_a_inv_b_non=3 +out${i}_lvds_diff_voltage ${addr}[2:1] 1 1_75ma, 3_5ma, 5_25ma, 7_0ma +out${i}_lvds_power_down ${addr}[0] 0 +% endfor ######################################################################## -#for $i in reversed(range(8)) -csdld_en_out_$i 0x0FC[$i] 0 ignore, async -#end for +% for i in reversed(range(8)): +csdld_en_out_${i} 0x0FC[${i}] 0 ignore, async +% endfor ######################################################################## -#for $i in reversed(range(4)) -csdld_en_out_$(8 + $i) 0x0FD[$i] 0 ignore, async -#end for +% for i in reversed(range(4)): +csdld_en_out_${8 + i} 0x0FD[${i}] 0 ignore, async +% endfor ######################################################################## -#set $default_val = 0x7 -#for $i in range(4) -#set $addr0 = hex($i*3 + 0x190) -#set $addr1 = hex($i*3 + 0x191) -#set $addr2 = hex($i*3 + 0x192) -divider$(i)_low_cycles $(addr0)[7:4] $default_val -divider$(i)_high_cycles $(addr0)[3:0] $default_val -divider$(i)_bypass $(addr1)[7] 0 -divider$(i)_ignore_sync $(addr1)[6] 0 -divider$(i)_force_high $(addr1)[5] 0 -divider$(i)_start_high $(addr1)[4] 0 -divider$(i)_phase_offset $(addr1)[3:0] 0 -channel$(i)_power_down $(addr2)[2] 0 -disable_divider$(i)_ddc $(addr2)[0] 0 -#set $default_val /= 2 -#end for +% for i in range(4): +<% default_val = int(0x7 / (2**i)) %>\ +<% addr0 = hex(i*3 + 0x190) %>\ +<% addr1 = hex(i*3 + 0x191) %>\ +<% addr2 = hex(i*3 + 0x192) %>\ +divider${i}_low_cycles ${addr0}[7:4] ${default_val} +divider${i}_high_cycles ${addr0}[3:0] ${default_val} +divider${i}_bypass ${addr1}[7] 0 +divider${i}_ignore_sync ${addr1}[6] 0 +divider${i}_force_high ${addr1}[5] 0 +divider${i}_start_high ${addr1}[4] 0 +divider${i}_phase_offset ${addr1}[3:0] 0 +channel${i}_power_down ${addr2}[2] 0 +disable_divider${i}_ddc ${addr2}[0] 0 +% endfor ######################################################################## vco_divider 0x1E0[2:0] 2 div2, div3, div4, div5, div6, static, div1 power_down_clock_input_sel 0x1E1[4] 0 @@ -145,13 +144,13 @@ BODY_TMPL="""\ boost::uint32_t get_reg(boost::uint16_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::uint8_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % 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::uint8_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } if (addr == 0){ //mirror 4 bits in register 0 reg |= ((reg >> 7) & 0x1) << 0; @@ -164,13 +163,13 @@ boost::uint32_t get_reg(boost::uint16_t addr){ void set_reg(boost::uint16_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 + % 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()}); + % endfor break; - #end for + % endfor } } diff --git a/host/lib/ic_reg_maps/gen_ad9777_regs.py b/host/lib/ic_reg_maps/gen_ad9777_regs.py index 47b61cf44..514283409 100755 --- a/host/lib/ic_reg_maps/gen_ad9777_regs.py +++ b/host/lib/ic_reg_maps/gen_ad9777_regs.py @@ -91,13 +91,13 @@ BODY_TMPL="""\ boost::uint8_t get_reg(boost::uint8_t addr){ boost::uint8_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::uint8_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % 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::uint8_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } return reg; } diff --git a/host/lib/ic_reg_maps/gen_ad9862_regs.py b/host/lib/ic_reg_maps/gen_ad9862_regs.py index 00340224c..022d97c16 100755 --- a/host/lib/ic_reg_maps/gen_ad9862_regs.py +++ b/host/lib/ic_reg_maps/gen_ad9862_regs.py @@ -41,10 +41,10 @@ all_rx_pd 1[0] 0 ######################################################################## ## Rx A and B ######################################################################## -#for $x, $i in (('a', 2), ('b', 3)) -byp_buffer_$x $(i)[7] 0 -rx_pga_$x $(i)[0:4] 0 -#end for +% for x, i in (('a', 2), ('b', 3)): +byp_buffer_${x} ${i}[7] 0 +rx_pga_${x} ${i}[0:4] 0 +% endfor ######################################################################## ## Rx Misc ######################################################################## @@ -76,13 +76,13 @@ tx_analog_pd 8[0:2] 0 none=0, txb=4, txa=2, both=7 ######################################################################## ## Tx Offset and Gain ######################################################################## -#for $x, $i, $j, $k in (('a', 10, 11, 14), ('b', 12, 13, 15)) -dac_$(x)_offset_1_0 $(i)[6:7] 0 -dac_$(x)_offset_dir $(i)[0] 0 neg_diff, pos_dif -dac_$(x)_offset_9_2 $(j)[0:7] 0 -dac_$(x)_coarse_gain $(k)[6:7] 0 -dac_$(x)_fine_gain $(k)[0:5] 0 -#end for +% for x, i, j, k in (('a', 10, 11, 14), ('b', 12, 13, 15)): +dac_${x}_offset_1_0 ${i}[6:7] 0 +dac_${x}_offset_dir ${i}[0] 0 neg_diff, pos_dif +dac_${x}_offset_9_2 ${j}[0:7] 0 +dac_${x}_coarse_gain ${k}[6:7] 0 +dac_${x}_fine_gain ${k}[0:5] 0 +% endfor tx_pga_gain 16[0:7] 0 ######################################################################## ## Tx Misc @@ -139,20 +139,20 @@ dis1 25[0] 0 enb, dis ######################################################################## ## Aux ADC ######################################################################## -#for $x, $i in (('a2', 26), ('a1', 28), ('b2', 30), ('b1', 32)) -aux_adc_$(x)_1_0 $(i)[6:7] 0 -aux_adc_$(x)_9_2 $int(1+$i)[0:7] 0 -#end for +% for x, i in (('a2', 26), ('a1', 28), ('b2', 30), ('b1', 32)): +aux_adc_${x}_1_0 ${i}[6:7] 0 +aux_adc_${x}_9_2 ${int(1+i)}[0:7] 0 +% endfor ######################################################################## ## Aux ADC Control ######################################################################## aux_spi 34[7] 0 dis, enb sel_bnota 34[6] 0 adc_a, adc_b -#for $x, $i in (('b', 5), ('a', 2)) -refsel_$(x) 34[$i] 0 external, internal -select_$(x) 34[$int($i-1)] 0 aux_adc2, aux_adc1 -start_$(x) 34[$int($i-2)] 0 -#end for +% for x, i in (('b', 5), ('a', 2)): +refsel_${x} 34[${i}] 0 external, internal +select_${x} 34[${int(i-1)}] 0 aux_adc2, aux_adc1 +start_${x} 34[${int(i-2)}] 0 +% endfor ######################################################################## ## Aux ADC Clock ######################################################################## @@ -160,9 +160,9 @@ clk_4 35[0] 0 1_2, 1_4 ######################################################################## ## Aux DAC ######################################################################## -#for $x, $i in (('a', 36), ('b', 37), ('c', 38)) -aux_dac_$x $(i)[0:7] 0 -#end for +% for x, i in (('a', 36), ('b', 37), ('c', 38)): +aux_dac_${x} ${i}[0:7] 0 +% endfor ######################################################################## ## Aux DAC Update ######################################################################## @@ -205,26 +205,26 @@ BODY_TMPL=""" boost::uint8_t get_reg(boost::uint8_t addr){ boost::uint8_t reg = 0; switch(addr){ - #for $addr in range(0, 63+1) - case $addr: - #for $reg in filter(lambda r: r.get_addr() == addr, $regs) - reg |= (boost::uint16_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % for addr in range(0, 63+1): + case ${addr}: + % for reg in filter(lambda r: r.get_addr() == addr, regs): + reg |= (boost::uint16_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } return reg; } void set_reg(boost::uint8_t addr, boost::uint16_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 + % 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()}); + % endfor break; - #end for + % endfor } } diff --git a/host/lib/ic_reg_maps/gen_adf4350_regs.py b/host/lib/ic_reg_maps/gen_adf4350_regs.py index fce2f569b..644654dee 100755 --- a/host/lib/ic_reg_maps/gen_adf4350_regs.py +++ b/host/lib/ic_reg_maps/gen_adf4350_regs.py @@ -43,8 +43,8 @@ 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 +<% 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 @@ -101,13 +101,13 @@ enum addr_t{ boost::uint32_t get_reg(boost::uint8_t addr){ boost::uint32_t reg = addr & 0x7; switch(addr){ - #for $addr in range(5+1) - case $addr: - #for $reg in filter(lambda r: r.get_addr() == addr, $regs) - reg |= (boost::uint32_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % for addr in range(5+1): + case ${addr}: + % for reg in filter(lambda r: r.get_addr() == addr, regs): + reg |= (boost::uint32_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } return reg; } diff --git a/host/lib/ic_reg_maps/gen_adf4351_regs.py b/host/lib/ic_reg_maps/gen_adf4351_regs.py index 4b0ef788c..6699e5137 100755 --- a/host/lib/ic_reg_maps/gen_adf4351_regs.py +++ b/host/lib/ic_reg_maps/gen_adf4351_regs.py @@ -44,8 +44,8 @@ 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 +<% 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 @@ -105,13 +105,13 @@ enum addr_t{ boost::uint32_t get_reg(boost::uint8_t addr){ boost::uint32_t reg = addr & 0x7; switch(addr){ - #for $addr in range(5+1) - case $addr: - #for $reg in filter(lambda r: r.get_addr() == addr, $regs) - reg |= (boost::uint32_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % for addr in range(5+1): + case ${addr}: + % for reg in filter(lambda r: r.get_addr() == addr, regs): + reg |= (boost::uint32_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } return reg; } diff --git a/host/lib/ic_reg_maps/gen_adf4360_regs.py b/host/lib/ic_reg_maps/gen_adf4360_regs.py index 3fd8707a7..921f014ff 100755 --- a/host/lib/ic_reg_maps/gen_adf4360_regs.py +++ b/host/lib/ic_reg_maps/gen_adf4360_regs.py @@ -32,9 +32,9 @@ charge_pump_output 0[9] 0 normal, 3state cp_gain_0 0[10] 0 set1, set2 mute_till_ld 0[11] 0 dis, enb output_power_level 0[12:13] 0 3_5ma, 5_0ma, 7_5ma, 11_0ma -#set $current_setting_enums = ', '.join(map(lambda x: x+"ma", "0_31 0_62 0_93 1_25 1_56 1_87 2_18 2_50".split())) -current_setting1 0[14:16] 0 $current_setting_enums -current_setting2 0[17:19] 0 $current_setting_enums +<% current_setting_enums = ', '.join(map(lambda x: x+"ma", "0_31 0_62 0_93 1_25 1_56 1_87 2_18 2_50".split())) %>\ +current_setting1 0[14:16] 0 ${current_setting_enums} +current_setting2 0[17:19] 0 ${current_setting_enums} power_down 0[20:21] 0 normal_op=0, async_pd=1, sync_pd=3 prescaler_value 0[22:23] 0 8_9, 16_17, 32_33 ######################################################################## @@ -68,13 +68,13 @@ enum addr_t{ boost::uint32_t get_reg(addr_t addr){ boost::uint32_t reg = addr & 0x3; 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 + % 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()}; + % endfor break; - #end for + % endfor } return reg; } diff --git a/host/lib/ic_reg_maps/gen_ads62p44_regs.py b/host/lib/ic_reg_maps/gen_ads62p44_regs.py index f0a84d940..df5c0c66c 100755 --- a/host/lib/ic_reg_maps/gen_ads62p44_regs.py +++ b/host/lib/ic_reg_maps/gen_ads62p44_regs.py @@ -95,13 +95,13 @@ BODY_TMPL="""\ boost::uint8_t get_reg(boost::uint8_t addr){ boost::uint8_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::uint8_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % 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::uint8_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } return reg; } diff --git a/host/lib/ic_reg_maps/gen_ads62p48_regs.py b/host/lib/ic_reg_maps/gen_ads62p48_regs.py index c38ce8ff1..fa5668d4f 100644..100755 --- a/host/lib/ic_reg_maps/gen_ads62p48_regs.py +++ b/host/lib/ic_reg_maps/gen_ads62p48_regs.py @@ -55,13 +55,13 @@ BODY_TMPL="""\ boost::uint8_t get_reg(boost::uint8_t addr){ boost::uint8_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::uint8_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % 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::uint8_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } return reg; } diff --git a/host/lib/ic_reg_maps/gen_lmk04816_regs.py b/host/lib/ic_reg_maps/gen_lmk04816_regs.py index e89a82671..d432ac706 100644..100755 --- a/host/lib/ic_reg_maps/gen_lmk04816_regs.py +++ b/host/lib/ic_reg_maps/gen_lmk04816_regs.py @@ -1,4 +1,4 @@ -#Copyright 2010 Ettus Research LLC +#Copyright 2010,2015 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 @@ -26,7 +26,7 @@ address0 0[0:4] 0 CLKout0_1_DIV 0[5:15] 25 CLKout0_1_HS 0[16] 0 RESET 0[17] 0 no_reset, reset -CLKout0_1_DDLY 0[18:27] 0 five +CLKout0_1_DDLY 0[18:27] 0 CLKout0_ADLY_SEL 0[28] 0 d_pd, d_ev_x, d_odd_y, d_both CLKout1_ADLY_SEL 0[29] 0 d_pd, d_ev_x, d_odd_y, d_both Required_0 0[30] 0 @@ -94,15 +94,15 @@ CLKout10_11_PD 5[31] 1 normal, power_down ######################################################################## ## address 6 ######################################################################## -#set $CLKoutX_TYPE_ENUMS = "p_down=0, LVDS=1, LVPECL_700mVpp=2, LVPECL_1200mVpp=3, LVPECL_1600mVpp=4, LVPECL_200mVpp=5, LVCMOS=6, LVCMOS_IN=7, LVCMOS_NN=8, LVCMOS_II=9, LVCMOS_LN=10, LVCMOS_LI=11, LVCMOS_NL=12, LVCMOS_IL=13, LVCMOS_LL=1" +<% CLKoutX_TYPE_ENUMS = "p_down=0, LVDS=1, LVPECL_700mVpp=2, LVPECL_1200mVpp=3, LVPECL_1600mVpp=4, LVPECL_200mVpp=5, LVCMOS=6, LVCMOS_IN=7, LVCMOS_NN=8, LVCMOS_II=9, LVCMOS_LN=10, LVCMOS_LI=11, LVCMOS_NL=12, LVCMOS_IL=13, LVCMOS_LL=1" %>\ address6 6[0:4] 6 CLKout0_1_ADLY 6[5:9] 0 Required_6_10 6[10] 0 CLKout2_3_ADLY 6[11:15] 0 -CLKout0_TYPE 6[16:19] 0 $(CLKoutX_TYPE_ENUMS) -CLKout1_TYPE 6[20:23] 0 $(CLKoutX_TYPE_ENUMS) -CLKout2_TYPE 6[24:27] 0 $(CLKoutX_TYPE_ENUMS) -CLKout3_TYPE 6[28:31] 0 $(CLKoutX_TYPE_ENUMS) +CLKout0_TYPE 6[16:19] 0 ${CLKoutX_TYPE_ENUMS} +CLKout1_TYPE 6[20:23] 0 ${CLKoutX_TYPE_ENUMS} +CLKout2_TYPE 6[24:27] 0 ${CLKoutX_TYPE_ENUMS} +CLKout3_TYPE 6[28:31] 0 ${CLKoutX_TYPE_ENUMS} ######################################################################## ## address 7 ######################################################################## @@ -110,21 +110,21 @@ address7 7[0:4] 7 CLKout4_5_ADLY 7[5:9] 0 Required_7_10 7[10] 0 CLKout6_7_ADLY 7[11:15] 0 -CLKout4_TYPE 7[16:19] 0 $(CLKoutX_TYPE_ENUMS) -CLKout5_TYPE 7[20:23] 0 $(CLKoutX_TYPE_ENUMS) -CLKout6_TYPE 7[24:27] 0 $(CLKoutX_TYPE_ENUMS) -CLKout7_TYPE 7[28:31] 0 $(CLKoutX_TYPE_ENUMS) +CLKout4_TYPE 7[16:19] 0 ${CLKoutX_TYPE_ENUMS} +CLKout5_TYPE 7[20:23] 0 ${CLKoutX_TYPE_ENUMS} +CLKout6_TYPE 7[24:27] 0 ${CLKoutX_TYPE_ENUMS} +CLKout7_TYPE 7[28:31] 0 ${CLKoutX_TYPE_ENUMS} ######################################################################## ## address 8 ######################################################################## address8 8[0:4] 8 CLKout8_9_ADLY 8[5:9] 0 Required_8_10 8[10] 0 -CLKout10_11_ADLY 8[11:15] 0 $(CLKoutX_TYPE_ENUMS) -CLKout8_TYPE 8[16:19] 0 $(CLKoutX_TYPE_ENUMS) -CLKout9_TYPE 8[20:23] 0 $(CLKoutX_TYPE_ENUMS) -CLKout10_TYPE 8[24:27] 0 $(CLKoutX_TYPE_ENUMS) -CLKout11_TYPE 8[28:31] 0 $(CLKoutX_TYPE_ENUMS) +CLKout10_11_ADLY 8[11:15] 0 ${CLKoutX_TYPE_ENUMS} +CLKout8_TYPE 8[16:19] 0 ${CLKoutX_TYPE_ENUMS} +CLKout9_TYPE 8[20:23] 0 ${CLKoutX_TYPE_ENUMS} +CLKout10_TYPE 8[24:27] 0 ${CLKoutX_TYPE_ENUMS} +CLKout11_TYPE 8[28:31] 0 ${CLKoutX_TYPE_ENUMS} ######################################################################## ## address 9 ######################################################################## @@ -378,22 +378,18 @@ BODY_TMPL = """\ boost::uint32_t get_reg(int 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 + % 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()}; + % endfor break; - #end for + % endfor } return reg; } """ - - - - if __name__ == '__main__': import common; common.generate( name='lmk04816_regs', @@ -402,6 +398,3 @@ if __name__ == '__main__': file=__file__, ) - - - diff --git a/host/lib/ic_reg_maps/gen_max2112_regs.py b/host/lib/ic_reg_maps/gen_max2112_regs.py index c2fc4e3e2..be760ec2e 100755 --- a/host/lib/ic_reg_maps/gen_max2112_regs.py +++ b/host/lib/ic_reg_maps/gen_max2112_regs.py @@ -53,13 +53,14 @@ f_divider_lsb 4[0:7] 0x84 ######################################################################## ## XTAL-Divider R-Divider (5) Write ######################################################################## -#set $xtal_divider_names = ', '.join(map(lambda x: 'div' + str(x), range(1,9))) -xtal_divider 5[5:7] 0 $xtal_divider_names +<% xtal_divider_names = ', '.join(map(lambda x: 'div' + str(x), range(1,9))) %>\ +xtal_divider 5[5:7] 0 ${xtal_divider_names} r_divider 5[0:4] 1 ######################################################################## ## PLL (6) Write ######################################################################## -d24 6[7] 1 div2, div4 ## div2 for LO <= 1125M, div4 > 1125M +## div2 for LO <= 1125M, div4 > 1125M +d24 6[7] 1 div2, div4 cps 6[6] 1 i_cp_from_icp, i_cp_from_vas icp 6[5] 0 i_cp_600ua, i_cp_1200ua ##reserved 6[0:4] 0 @@ -73,7 +74,8 @@ ade 7[0] 1 disabled, enabled ######################################################################## ## LPF (8) Write ######################################################################## -lp 8[0:7] 0x4B ## map(lambda x: "%0.2f"%((4e6 + (x - 12) * 290e3)/1e6), range(255)) in MHz +## map(lambda x: "%0.2f"%((4e6 + (x - 12) * 290e3)/1e6), range(255)) in MHz +lp 8[0:7] 0x4B ######################################################################## ## Control (9) Write ######################################################################## @@ -81,7 +83,8 @@ stby 9[7] 0 normal, disable_sig_and_synth ##reserved 9[6] 0 pwdn 9[5] 0 normal, invalid ##reserved 9[4] 0 -bbg 9[0:3] 0 ## Baseband Gain in dB +## Baseband Gain in dB +bbg 9[0:3] 0 ######################################################################## ## Shutdown (0xA) Write ######################################################################## @@ -118,7 +121,8 @@ ld 0xC[4] 0 unlocked, locked ######################################################################## ## Status Byte-2 (0xD) Read ######################################################################## -vcosbr 0xD[3:7] 0 ## vco band readback +## vco band readback +vcosbr 0xD[3:7] 0 adc 0xD[0:2] 0 ool0, lock0, vaslock0, vaslock1, vaslock2, vaslock3, lock1, ool1 """ @@ -129,41 +133,30 @@ BODY_TMPL="""\ boost::uint8_t get_reg(boost::uint8_t addr){ boost::uint8_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::uint8_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % 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::uint8_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } return boost::uint8_t(reg); } void set_reg(boost::uint8_t addr, boost::uint8_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 + % 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()}); + % endfor break; - #end for + % endfor } } """ -SPLIT_REGS_HELPER_TMPL="""\ -#for $divname in ['n','f'] -void set_$(divname)_divider(boost::uint32_t $divname){ - #for $regname in sorted(map(lambda r: r.get_name(), filter(lambda r: r.get_name().find(divname + '_divider') == 0, $regs))) - #end for -} -#end for -""" - #$regname = boost::uint8_t($divname & $regs[regname].get_mask()); - #$divname = boost::uint32_t($divname >> $regs[regname].get_shift()); - if __name__ == '__main__': import common; common.generate( name='max2112_write_regs', diff --git a/host/lib/ic_reg_maps/gen_max2118_regs.py b/host/lib/ic_reg_maps/gen_max2118_regs.py index 506fbaec8..01d7615de 100755 --- a/host/lib/ic_reg_maps/gen_max2118_regs.py +++ b/host/lib/ic_reg_maps/gen_max2118_regs.py @@ -38,28 +38,31 @@ n_divider_lsb 1[0:7] 0xB6 ######################################################################## ## R, Charge Pump, and VCO (2) Write ######################################################################## -#set $r_divider_names = ', '.join(map(lambda x: 'div' + str(2**(x+1)), range(0,8))) -r_divider 2[5:7] 1 $r_divider_names -#set $cp_current_bias = ', '.join(map(lambda x: 'i_cp_%dua'%(50*2**x), range(0,4))) -cp_current 2[3:4] 3 $cp_current_bias +<% r_divider_names = ', '.join(map(lambda x: 'div' + str(2**(x+1)), range(0,8))) %>\ +r_divider 2[5:7] 1 ${r_divider_names} +<% cp_current_bias = ', '.join(map(lambda x: 'i_cp_%dua'%(50*2**x), range(0,4))) %>\ +cp_current 2[3:4] 3 ${cp_current_bias} osc_band 2[0:2] 5 ######################################################################## ## I/Q Filter DAC (3) Write ######################################################################## ##unused 3[7] 0 -f_dac 3[0:6] 0x7F ## filter tuning dac, depends on m +## filter tuning dac, depends on m +f_dac 3[0:6] 0x7F ######################################################################## ## LPF Divider DAC (4) Write ######################################################################## adl_vco_adc_latch 4[7] 0 disabled, enabled ade_vco_ade_read 4[6] 0 disabled, enabled dl_output_drive 4[5] 0 iq_590m_vpp, iq_1_vpp -m_divider 4[0:4] 2 ## filter tuning counter +## filter tuning counter +m_divider 4[0:4] 2 ######################################################################## ## GC2 and Diag (5) Write ######################################################################## diag 5[5:7] 0 normal, cp_i_source, cp_i_sink, cp_high_z, unused, n_and_filt, r_and_gc2, m_div -gc2 5[0:4] 0x1F ## Step Size: 0-1: 0dB, 2-22: 1dB, 23-31: 0.5dB +## Step Size: 0-1: 0dB, 2-22: 1dB, 23-31: 0.5dB +gc2 5[0:4] 0x1F """ ######################################################################## @@ -71,11 +74,13 @@ READ_REGS_TMPL="""\ ## Status (0) Read ######################################################################## pwr 0[6] 0 not_reset, reset -adc 0[2:4] 0 ## VCO tuning voltage, Lock Status +## VCO tuning voltage, Lock Status +adc 0[2:4] 0 ######################################################################## ## I/Q Filter DAC (1) Read ######################################################################## -filter_dac 1[0:6] 0 ## I/Q Filter tuning DAC, current +## I/Q Filter tuning DAC, current +filter_dac 1[0:6] 0 """ ######################################################################## @@ -85,26 +90,26 @@ BODY_TMPL="""\ boost::uint8_t get_reg(boost::uint8_t addr){ boost::uint8_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::uint8_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % 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::uint8_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } return boost::uint8_t(reg); } void set_reg(boost::uint8_t addr, boost::uint8_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 + % 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()}); + % endfor break; - #end for + % endfor } } """ diff --git a/host/lib/ic_reg_maps/gen_max2829_regs.py b/host/lib/ic_reg_maps/gen_max2829_regs.py index 383131c18..dbcb68ec9 100755 --- a/host/lib/ic_reg_maps/gen_max2829_regs.py +++ b/host/lib/ic_reg_maps/gen_max2829_regs.py @@ -112,13 +112,13 @@ BODY_TMPL="""\ boost::uint32_t get_reg(boost::uint8_t addr){ boost::uint16_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::uint16_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % 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::uint16_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } return (boost::uint32_t(reg) << 4) | (addr & 0xf); } diff --git a/host/lib/ic_reg_maps/gen_max2870_regs.py b/host/lib/ic_reg_maps/gen_max2870_regs.py index f26c27281..af4e3c786 100644..100755 --- a/host/lib/ic_reg_maps/gen_max2870_regs.py +++ b/host/lib/ic_reg_maps/gen_max2870_regs.py @@ -28,8 +28,10 @@ REGS_TMPL="""\ ## Write-only, default = 0x007D0000 ######################################################################## int_n_mode 0x00[31] 0 frac_n, int_n -int_16_bit 0x00[15:30] 0x007D ##Integer divider: 16-65535 in int-N mode, 19-4091 in frac-N mode. -frac_12_bit 0x00[3:14] 0 ##Frac divider: 0-4095 +## Integer divider: 16-65535 in int-N mode, 19-4091 in frac-N mode. +int_16_bit 0x00[15:30] 0x007D +## Frac divider: 0-4095 +frac_12_bit 0x00[3:14] 0 ######################################################################## ## Address 0x01 ## Charge pump control @@ -38,8 +40,10 @@ frac_12_bit 0x00[3:14] 0 ##Frac divider: 0-4095 cpoc 0x01[31] 0 disabled, enabled cpl 0x01[29:30] 1 disabled, enabled, res1, res2 cpt 0x01[27:28] 0 normal, reserved, force_source, force_sink -phase_12_bit 0x01[15:26] 1 ##sets phase shift -mod_12_bit 0x01[3:14] 0xFFF ##VCO frac modulus +## sets phase shift +phase_12_bit 0x01[15:26] 1 +## VCO frac modulus +mod_12_bit 0x01[3:14] 0xFFF ######################################################################## ## Address 0x02 ## Misc. control @@ -50,10 +54,11 @@ low_noise_and_spur 0x02[29:30] 3 low_noise, reserved, low_spur_1, muxout 0x02[26:28] 1 tri_state, high, low, rdiv, ndiv, ald, dld, res7 reference_doubler 0x02[25] 0 disabled, enabled reference_divide_by_2 0x02[24] 0 disabled, enabled -r_counter_10_bit 0x02[14:23] 1 ##R divider value, 1-1023 +## R divider value, 1-1023 +r_counter_10_bit 0x02[14:23] 1 double_buffer 0x02[13] 0 disabled, enabled -#set $current_setting_enums = ', '.join(map(lambda x: '_'.join(("%0.2fma"%(1.631/5.1 * (1.+x))).split('.')), range(0,16))) -charge_pump_current 0x02[9:12] 7 $current_setting_enums +<% current_setting_enums = ', '.join(map(lambda x: '_'.join(("%0.2fma"%(1.631/5.1 * (1.+x))).split('.')), range(0,16))) %>\ +charge_pump_current 0x02[9:12] 7 ${current_setting_enums} ldf 0x02[8] 0 frac_n, int_n ldp 0x02[7] 0 10ns, 6ns pd_polarity 0x02[6] 1 negative, positive @@ -65,18 +70,22 @@ counter_reset 0x02[3] 0 normal, reset ## VCO control ## Write-only, default = 0x0000000B ######################################################################## -vco 0x03[26:31] 0 ##VCO subband selection, used when VAS disabledd -vas 0x03[25] 0 enabled, disabled ##VCO autoselect +## VCO subband selection, used when VAS disabledd +vco 0x03[26:31] 0 +## VCO autoselect +vas 0x03[25] 0 enabled, disabled retune 0x03[24] 1 disabled, enabled clock_div_mode 0x03[15:16] 0 clock_divider_off, fast_lock, phase, reserved -clock_divider_12_bit 0x03[3:14] 1 ##clock divider, 1-4095 +## clock divider, 1-4095 +clock_divider_12_bit 0x03[3:14] 1 ######################################################################## ## Address 0x04 ## RF output control ## Write-only, default = 0x6180B23C ######################################################################## res4 0x04[26:31] 0x18 -bs_msb 0x04[24:25] 0 ##Band select MSBs +## Band select MSBs +bs_msb 0x04[24:25] 0 feedback_select 0x04[23] 1 divided, fundamental rf_divider_select 0x04[20:22] 0 div1, div2, div4, div8, div16, div32, div64, div128 band_select_clock_div 0x04[12:19] 0 @@ -111,13 +120,13 @@ enum addr_t{ boost::uint32_t get_reg(boost::uint8_t addr){ boost::uint32_t reg = addr & 0x7; switch(addr){ - #for $addr in range(5+1) - case $addr: - #for $reg in filter(lambda r: r.get_addr() == addr, $regs) - reg |= (boost::uint32_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % for addr in range(5+1): + case ${addr}: + % for reg in filter(lambda r: r.get_addr() == addr, regs): + reg |= (boost::uint32_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } return reg; } diff --git a/host/lib/ic_reg_maps/gen_max2871_regs.py b/host/lib/ic_reg_maps/gen_max2871_regs.py index 338a019d8..f591c1636 100644..100755 --- a/host/lib/ic_reg_maps/gen_max2871_regs.py +++ b/host/lib/ic_reg_maps/gen_max2871_regs.py @@ -28,8 +28,10 @@ REGS_TMPL="""\ ## Write-only, default = 0x007D0000 ######################################################################## int_n_mode 0x00[31] 0 frac_n, int_n -int_16_bit 0x00[15:30] 0x007D ##Integer divider: 16-65535 in int-N mode, 19-4091 in frac-N mode. -frac_12_bit 0x00[3:14] 0 ##Frac divider: 0-4095 +## Integer divider: 16-65535 in int-N mode, 19-4091 in frac-N mode. +int_16_bit 0x00[15:30] 0x007D +## Frac divider: 0-4095 +frac_12_bit 0x00[3:14] 0 ######################################################################## ## Address 0x01 ## Charge pump control @@ -38,8 +40,10 @@ frac_12_bit 0x00[3:14] 0 ##Frac divider: 0-4095 res1 0x01[31] 0 cpl 0x01[29:30] 1 disabled, enabled, res1, res2 cpt 0x01[27:28] 0 normal, reserved, force_source, force_sink -phase_12_bit 0x01[15:26] 1 ##sets phase shift -mod_12_bit 0x01[3:14] 0xFFF ##VCO frac modulus +## sets phase shift +phase_12_bit 0x01[15:26] 1 +## VCO frac modulus +mod_12_bit 0x01[3:14] 0xFFF ######################################################################## ## Address 0x02 ## Misc. control @@ -50,10 +54,11 @@ low_noise_and_spur 0x02[29:30] 3 low_noise, reserved, low_spur_1, muxout 0x02[26:28] 0x6 tri_state, high, low, rdiv, ndiv, ald, dld, sync, res8, res9, res10, res11, spi, res13, res14, res15 reference_doubler 0x02[25] 0 disabled, enabled reference_divide_by_2 0x02[24] 0 disabled, enabled -r_counter_10_bit 0x02[14:23] 1 ##R divider value, 1-1023 +## R divider value, 1-1023 +r_counter_10_bit 0x02[14:23] 1 double_buffer 0x02[13] 0 disabled, enabled -#set $current_setting_enums = ', '.join(map(lambda x: '_'.join(("%0.2fma"%(1.631/5.1 * (1.+x))).split('.')), range(0,16))) -charge_pump_current 0x02[9:12] 7 $current_setting_enums +<% current_setting_enums = ', '.join(map(lambda x: '_'.join(("%0.2fma"%(1.631/5.1 * (1.+x))).split('.')), range(0,16))) %>\ +charge_pump_current 0x02[9:12] 7 ${current_setting_enums} ldf 0x02[8] 0 frac_n, int_n ldp 0x02[7] 0 10ns, 6ns pd_polarity 0x02[6] 1 negative, positive @@ -65,14 +70,17 @@ counter_reset 0x02[3] 0 normal, reset ## VCO control ## Write-only, default = 0x0000000B ######################################################################## -vco 0x03[26:31] 0 ##VCO subband selection, used when VAS disabledd -shutdown_vas 0x03[25] 0 enabled, disabled ##VCO autoselect +## VCO subband selection, used when VAS disabledd +vco 0x03[26:31] 0 +## VCO autoselect +shutdown_vas 0x03[25] 0 enabled, disabled retune 0x03[24] 1 disabled, enabled res3 0x3[19:23] 0 csm 0x3[18] 0 disabled, enabled mutedel 0x3[17] 0 disabled, enabled clock_div_mode 0x03[15:16] 0 clock_divider_off, fast_lock, phase, reserved -clock_divider_12_bit 0x03[3:14] 1 ##clock divider, 1-4095 +## clock divider, 1-4095 +clock_divider_12_bit 0x03[3:14] 1 ######################################################################## ## Address 0x04 ## RF output control @@ -82,7 +90,8 @@ res4 0x04[29:31] 0x3 shutdown_ldo 0x04[28] 0 enabled, disabled shutdown_div 0x04[27] 0 enabled, disabled shutdown_ref 0x04[26] 0 enabled, disabled -bs_msb 0x04[24:25] 0 ##Band select MSBs +## Band select MSBs +bs_msb 0x04[24:25] 0 feedback_select 0x04[23] 1 divided, fundamental rf_divider_select 0x04[20:22] 0 div1, div2, div4, div8, div16, div32, div64, div128 band_select_clock_div 0x04[12:19] 0 @@ -124,13 +133,13 @@ enum addr_t{ boost::uint32_t get_reg(boost::uint8_t addr){ boost::uint32_t reg = addr & 0x7; switch(addr){ - #for $addr in range(5+1) - case $addr: - #for $reg in filter(lambda r: r.get_addr() == addr, $regs) - reg |= (boost::uint32_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % for addr in range(5+1): + case ${addr}: + % for reg in filter(lambda r: r.get_addr() == addr, regs): + reg |= (boost::uint32_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } return reg; } diff --git a/host/lib/ic_reg_maps/gen_tda18272hnm_regs.py b/host/lib/ic_reg_maps/gen_tda18272hnm_regs.py index 677a201de..308d7d524 100755 --- a/host/lib/ic_reg_maps/gen_tda18272hnm_regs.py +++ b/host/lib/ic_reg_maps/gen_tda18272hnm_regs.py @@ -28,7 +28,7 @@ REGS_TMPL="""\ ######################################################################## ## ID_byte_1 (0x00) Read ######################################################################## -##reserved as 1 0x00[7] 1 +## reserved as 1 0x00[7] 1 ident_14_8 0x00[0:6] 0 ######################################################################## ## ID_byte_2 (0x01) Read @@ -43,23 +43,25 @@ minor_rev 0x02[0:3] 0 ######################################################################## ## Thermo_byte_1 (0x03) Read ######################################################################## -##reserved 0x03[7] 0 -tm_d 0x03[0:6] 0 ## 22-127deg C junction temp +## reserved 0x03[7] 0 +## 22-127deg C junction temp +tm_d 0x03[0:6] 0 ######################################################################## ## Thermo_byte_2 (0x04) Write ######################################################################## -##reserved 0x04[1:7] 0 +## reserved 0x04[1:7] 0 tm_on 0x04[0] 0 sensor_off, sensor_on ######################################################################## ## Power_state_byte_1 (0x05) Read ######################################################################## -##reserved 0x05[2:7] 0 +## reserved 0x05[2:7] 0 por 0x05[1] 0 read, reset lo_lock 0x05[0] 0 unlocked, locked ######################################################################## -## Power_state_byte_2 (0x06) Read/Write ## Standby modes +## Standby modes +## Power_state_byte_2 (0x06) Read/Write ######################################################################## -##reserved 0x06[4:7] 0 +## reserved 0x06[4:7] 0 sm 0x06[3] 0 normal, standby sm_pll 0x06[2] 0 on, off sm_lna 0x06[1] 0 on, off @@ -68,14 +70,15 @@ sm_lna 0x06[1] 0 on, off ######################################################################## ## Input_Power_Level_byte (0x07) Read ######################################################################## -##reserved 0x07[7] 0 -power_level 0x07[0:6] 0 ## 40dB_Vrms to 110dB_Vrms +## reserved 0x07[7] 0 +## 40dB_Vrms to 110dB_Vrms +power_level 0x07[0:6] 0 ## Trigger power level calculation with MSM_byte_1 and MSM_byte_2 ######################################################################## ## IRQ_status (0x08) Read/Write ######################################################################## irq_status 0x08[7] 0 cleared, set -##reserved 0x08[6] 0 +## reserved 0x08[6] 0 irq_xtalcal_end 0x08[5] 0 false, true irq_rssi_end 0x08[4] 0 false, true irq_localc_end 0x08[3] 0 false, true @@ -86,7 +89,7 @@ irq_rccal_end 0x08[0] 0 false, true ## IRQ_enable (0x09) Read/Write ######################################################################## irq_enable 0x09[7] 1 false, true -##reserved 0x09[6] 0 +## reserved 0x09[6] 0 irq_xtalcal_enable 0x09[5] 0 false, true irq_rssi_enable 0x09[4] 0 false, true irq_localc_enable 0x09[3] 0 false, true @@ -97,7 +100,7 @@ irq_rccal_enable 0x09[0] 0 false, true ## IRQ_clear (0x0a) Read/Write ######################################################################## irq_clear 0x0a[7] 0 false, true -##reserved 0x0a[6] 0 +## reserved 0x0a[6] 0 irq_xtalcal_clear 0x0a[5] 0 false, true irq_rssi_clear 0x0a[4] 0 false, true irq_localc_clear 0x0a[3] 0 false, true @@ -108,7 +111,7 @@ irq_rccal_clear 0x0a[0] 0 false, true ## IRQ_set (0x0b) Read ######################################################################## irq_set 0x0b[7] 0 false, true -##reserved 0x0b[6] 0 +## reserved 0x0b[6] 0 irq_xtalcal_set 0x0b[5] 0 false, true irq_rssi_set 0x0b[4] 0 false, true irq_localc_set 0x0b[3] 0 false, true @@ -120,12 +123,12 @@ irq_rccal_set 0x0b[0] 0 false, true ######################################################################## lt_enable 0x0c[7] 0 agc1_6_15db 0x0c[6] 1 -##reserved 0x0c[4:5] 0 +## reserved 0x0c[4:5] 0 agc1_top 0x0c[0:3] 0 ######################################################################## ## AGC2_byte_1 (0x0d) Read ######################################################################## -##reserved 0x0d[5:7] 0 +## reserved 0x0d[5:7] 0 agc2_top 0x0d[0:4] 0xf ######################################################################## ## AGCK_byte_1 (0x0e) Read/Write @@ -141,24 +144,25 @@ agck_mode 0x0e[0:1] 1 analog_tv=1, digital_tv=2 pd_rfagc_adapt 0x0f[7] 0 on, off rfagc_adapt_top 0x0f[5:6] 0 rfagc_low_bw 0x0f[4] 1 -rf_atten_3db 0x0f[3] 0 0db, 3db ## FIXME +## FIXME +rf_atten_3db 0x0f[3] 0 0db, 3db agc3_top 0x0f[0:2] 1 ######################################################################## ## IR_MIXER_byte_1 (0x10) Read/Write ######################################################################## -##reserved 0x10[4:7] 0 +## reserved 0x10[4:7] 0 agc4_top 0x10[0:3] 1 ######################################################################## ## AGC5_byte_1 (0x11) Read/Write ######################################################################## -##reserved 0x11[7] 0 +## reserved 0x11[7] 0 agcs_do_step_assym 0x11[5:6] 2 agc5_hpf 0x11[4] 1 off, on agc5_top 0x11[0:3] 1 ######################################################################## ## IF_AGC_byte (0x12) Read/Write ######################################################################## -##reserved 0x12[3:7] 0 +## reserved 0x12[3:7] 0 if_level 0x12[0:2] 0 0_5vpp=7, 0_6vpp=6, 0_7vpp=5, 0_85vpp=4, 0_8vpp=3, 1_0vpp=2, 1_25vpp=1, 2_0vpp=0 ######################################################################## ## IF_byte_1 (0x13) Read/Write @@ -172,18 +176,19 @@ lp_fc 0x13[0:2] 3 1_7mhz=4, 6_0mhz=0, 7_0mhz=1, 8_0mhz=2, ######################################################################## i2c_clock_mode 0x14[7] 0 digital_clock 0x14[6] 1 spread_off, spread_on -##reserved 0x14[5] 0 +## reserved 0x14[5] 0 xtalosc_anareg_en 0x14[4] 0 -##reserved 0x14[2:3] 0 +## reserved 0x14[2:3] 0 xtout 0x14[0:1] 0 no=0, 16mhz=3 ######################################################################## ## IF_Frequency_byte (0x15) Read/Write ######################################################################## -if_freq 0x15[0:7] 0 ## IF frequency = if_freq*50 (kHz) +## IF frequency = if_freq*50 (kHz) +if_freq 0x15[0:7] 0 ######################################################################## ## RF_Frequency_byte_1 (0x16) Read/Write ######################################################################## -##reserved 0x16[4:7] 0 +## reserved 0x16[4:7] 0 rf_freq_19_16 0x16[0:3] 0 ######################################################################## ## RF_Frequency_byte_2 (0x17) Read/Write @@ -209,7 +214,7 @@ calc_pll 0x19[0] 0 ######################################################################## ## MSM_byte_2 (0x1a) Read ######################################################################## -##reserved 0x1a[2:7] 0 +## reserved 0x1a[2:7] 0 xtalcal_launch 0x1a[1] 0 msm_launch 0x1a[0] 0 ######################################################################## @@ -227,11 +232,11 @@ psm_lodriver 0x1b[0:1] 0 dcc_bypass 0x1c[7] 0 dcc_slow 0x1c[6] 0 dcc_psm 0x1c[5] 0 -##reserved 0x1c[0:4] 0 +## reserved 0x1c[0:4] 0 ######################################################################## ## FLO_Max_byte (0x1d) Read ######################################################################## -##reserved 0x1d[6:7] 0 +## reserved 0x1d[6:7] 0 fmax_lo 0x1d[0:5] 0xA ######################################################################## ## IR_Cal_byte_1 (0x1e) Read @@ -249,12 +254,12 @@ ir_freqlow 0x1f[0:4] 0 ######################################################################## ## IR_Cal_byte_3 (0x20) Read ######################################################################## -##reserved 0x20[5:7] 0 +## reserved 0x20[5:7] 0 ir_freqmid 0x20[0:4] 0 ######################################################################## ## IR_Cal_byte_4 (0x21) Read ######################################################################## -##reserved 0x21[5:7] 0 +## reserved 0x21[5:7] 0 coarse_ir_freqhigh 0x21[4] 0 ir_freqhigh 0x21[0:3] 0 ######################################################################## @@ -270,8 +275,9 @@ agc_ovld_timer 0x22[0:1] 0 ######################################################################## ir_mixer_loop_off 0x23[7] 0 ir_mixer_do_step 0x23[5:6] 0 -##reserved 0x23[2:4] 0 -hi_pass 0x23[1] 0 disable, enable ## FIXME Logic Unclear +## reserved 0x23[2:4] 0 +## FIXME Logic Unclear +hi_pass 0x23[1] 0 disable, enable if_notch 0x23[0] 1 on, off ######################################################################## ## AGC1_byte_2 (0x24) Read @@ -285,9 +291,9 @@ agc1_gain 0x24[0:3] 8 ######################################################################## agc5_loop_off 0x25[7] 0 agc5_do_step 0x25[5:6] 0 -##reserved 0x25[4] 0 +## reserved 0x25[4] 0 force_agc5_gain 0x25[3] 0 -##reserved 0x25[2] 0 +## reserved 0x25[2] 0 agc5_gain 0x25[0:1] 2 ######################################################################## ## RF_Cal_byte_1 (0x26) Read @@ -335,7 +341,7 @@ rfcal_freq11 0x2b[0:1] 0 ## RF_Filter_byte_1 (0x2c) Read ######################################################################## rf_filter_bypass 0x2c[7] 0 -##reserved as 0 0x2c[6] 0 +## reserved as 0 0x2c[6] 0 agc2_loop_off 0x2c[5] 0 force_agc2_gain 0x2c[4] 0 rf_filter_gv 0x2c[2:3] 2 @@ -353,12 +359,12 @@ gain_taper 0x2e[0:5] 0 ## RF_Band_Pass_Filter_byte (0x2f) Read ######################################################################## rf_bpf_bypass 0x2f[7] 0 -##reserved 0x2f[3:6] 0 +## reserved 0x2f[3:6] 0 rf_bpf 0x2f[0:2] 0 ######################################################################## ## CP_Current_byte (0x30) Read ######################################################################## -##reserved 0x30[7] 0 +## reserved 0x30[7] 0 n_cp_current 0x30[0:6] 0x68 ######################################################################## ## AGC_Det_Out_byte (0x31) Read @@ -374,24 +380,26 @@ do_agc1 0x31[0] 0 ######################################################################## ## RF_AGC_Gain_byte_1 (0x32) Read ######################################################################## -#set $lna_gain_names = ', '.join(map(lambda x: {0: '', 1: 'm'}[3*x-12 < 0] + str(abs(3*x-12)) + 'db=' + str(x), range(0,10))) -##reserved 0x32[6:7] 0 +## reserved 0x32[6:7] 0 agc2_gain_read 0x32[4:5] 3 m11db, m8db, m5db, m2db -agc1_gain_read 0x32[0:3] 9 $lna_gain_names +<% lna_gain_names = ', '.join(map(lambda x: {0: '', 1: 'm'}[3*x-12 < 0] + str(abs(3*x-12)) + 'db=' + str(x), range(0,10))) %>\ +agc1_gain_read 0x32[0:3] 9 ${lna_gain_names} ######################################################################## ## RF_AGC_Gain_byte_2 (0x33) Read ######################################################################## -#set $top_agc3_read_names = ', '.join(map(lambda x: str(int(round(1.92*x+94))) + 'dbuvrms=' + str(x), range(0,8))) -##reserved 0x33[3:7] 0 -top_agc3_read 0x33[0:2] 0 $top_agc3_read_names +## reserved 0x33[3:7] 0 +<% top_agc3_read_names = ', '.join(map(lambda x: str(int(round(1.92*x+94))) + 'dbuvrms=' + str(x), range(0,8))) %>\ +top_agc3_read 0x33[0:2] 0 ${top_agc3_read_names} ######################################################################## ## IF_AGC_Gain_byte (0x34) Read ######################################################################## -#set $lpf_gain_names = ', '.join(map(lambda x: str(3*x) + 'db=' + str(x), range(0,4))) -#set $ir_mixer_names = ', '.join(map(lambda x: str(3*x+2) + 'db=' + str(x), range(0,5))) -##reserved 0x34[5:7] 0 -agc5_gain_read 0x34[3:4] 3 $lpf_gain_names -agc4_gain_read 0x34[0:2] 4 $ir_mixer_names +## reserved 0x34[5:7] 0 +<% + lpf_gain_names = ', '.join(map(lambda x: str(3*x) + 'db=' + str(x), range(0,4))) + ir_mixer_names = ', '.join(map(lambda x: str(3*x+2) + 'db=' + str(x), range(0,5))) +%>\ +agc5_gain_read 0x34[3:4] 3 ${lpf_gain_names} +agc4_gain_read 0x34[0:2] 4 ${ir_mixer_names} ######################################################################## ## Power_byte_1 (0x35) Read ######################################################################## @@ -399,9 +407,9 @@ rssi 0x35[0:7] 0 ######################################################################## ## Power_byte_2 (0x36) Read ######################################################################## -##reserved 0x36[6:7] 0 +## reserved 0x36[6:7] 0 rssi_av 0x36[5] 0 -##reserved 0x36[4] 0 +## reserved 0x36[4] 0 rssi_cap_reset_en 0x36[3] 1 rssi_cap_val 0x36[2] 1 rssi_ck_speed 0x36[1] 0 @@ -479,39 +487,30 @@ BODY_TMPL="""\ boost::uint8_t get_reg(boost::uint8_t addr){ boost::uint8_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::uint8_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % 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::uint8_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } return boost::uint8_t(reg); } void set_reg(boost::uint8_t addr, boost::uint8_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 + % 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()}); + % endfor break; - #end for + % endfor } } """ -SPLIT_REGS_HELPER_TMPL="""\ -#for $divname in ['n','f'] -void set_$(divname)_divider(boost::uint32_t $divname){ - #for $regname in sorted(map(lambda r: r.get_name(), filter(lambda r: r.get_name().find(divname + '_divider') == 0, $regs))) - #end for -} -#end for -""" - if __name__ == '__main__': import common; common.generate( name='tda18272hnm_regs', diff --git a/host/lib/ic_reg_maps/gen_tuner_4937di5_regs.py b/host/lib/ic_reg_maps/gen_tuner_4937di5_regs.py index 73f7aa3db..9b8e1958f 100644..100755 --- a/host/lib/ic_reg_maps/gen_tuner_4937di5_regs.py +++ b/host/lib/ic_reg_maps/gen_tuner_4937di5_regs.py @@ -53,13 +53,13 @@ BODY_TMPL="""\ boost::uint8_t get_reg(boost::uint8_t addr){ boost::uint8_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::uint8_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % 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::uint8_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } return boost::uint8_t(reg); } diff --git a/host/lib/image_loader.cpp b/host/lib/image_loader.cpp new file mode 100644 index 000000000..91dd325dd --- /dev/null +++ b/host/lib/image_loader.cpp @@ -0,0 +1,87 @@ +// +// Copyright 2014-2015 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 <iostream> +#include <map> +#include <utility> + +#include <boost/filesystem.hpp> +#include <boost/foreach.hpp> +#include <boost/format.hpp> + +#include <uhd/exception.hpp> +#include <uhd/image_loader.hpp> +#include <uhd/utils/log.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/static.hpp> + +namespace fs = boost::filesystem; + +typedef std::map<std::string, uhd::image_loader::loader_fcn_t> loader_fcn_map_t; +typedef std::map<std::string, std::string> string_map_t; + +// Nice typedefs for iterating over std::map +typedef std::pair<std::string, uhd::image_loader::loader_fcn_t> loader_fcn_pair_t; +typedef std::pair<std::string, std::string> string_pair_t; + +UHD_SINGLETON_FCN(loader_fcn_map_t, get_image_loaders); +UHD_SINGLETON_FCN(string_map_t, get_recovery_strings); + +/* + * Registration + */ +void uhd::image_loader::register_image_loader(const std::string &device_type, + const loader_fcn_t &loader_fcn, + const std::string &recovery_instructions){ + UHD_LOGV(always) << "Registering image loader and recovery instructions for " + << device_type << std::endl; + + get_image_loaders().insert(loader_fcn_pair_t(device_type, loader_fcn)); + get_recovery_strings().insert(string_pair_t(device_type, recovery_instructions)); +} + +/* + * Actual loading + */ +bool uhd::image_loader::load(const uhd::image_loader::image_loader_args_t &image_loader_args){ + + // If "type=foo" given in args, see if we have an image loader for that + if(image_loader_args.args.has_key("type")){ + std::string type = image_loader_args.args.get("type"); + if(get_image_loaders().find(type) == get_image_loaders().end()){ + throw uhd::runtime_error(str(boost::format("There is no image loader registered for given type \"%s\".") + % type)); + } + else return get_image_loaders().at(type)(image_loader_args); + } + else{ + BOOST_FOREACH(const loader_fcn_pair_t &loader_fcn_pair, get_image_loaders()){ + if(loader_fcn_pair.second(image_loader_args)) return true; + } + return false; + } +} + +/* + * Get recovery instructions for particular device + */ +std::string uhd::image_loader::get_recovery_instructions(const std::string &device_type){ + if(get_recovery_strings().count(device_type) == 0){ + return "A firmware or FPGA loading process was interrupted by the user. This can leave your device in a non-working state."; + } + else return get_recovery_strings().at(device_type); +} diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt index 5920f3d78..6abc399b4 100644 --- a/host/lib/transport/CMakeLists.txt +++ b/host/lib/transport/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2013 Ettus Research LLC +# Copyright 2010-2013,2015 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 @@ -30,7 +30,7 @@ INCLUDE_SUBDIRECTORY(nirio) MESSAGE(STATUS "") FIND_PACKAGE(USB1) -LIBUHD_REGISTER_COMPONENT("USB" ENABLE_USB ON "ENABLE_LIBUHD;LIBUSB_FOUND" OFF) +LIBUHD_REGISTER_COMPONENT("USB" ENABLE_USB ON "ENABLE_LIBUHD;LIBUSB_FOUND" OFF OFF) IF(ENABLE_USB) MESSAGE(STATUS "USB support enabled via libusb.") @@ -129,6 +129,7 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/if_addrs.cpp ${CMAKE_CURRENT_SOURCE_DIR}/udp_simple.cpp ${CMAKE_CURRENT_SOURCE_DIR}/nirio_zero_copy.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/chdr.cpp ) # Verbose Debug output for send/recv diff --git a/host/lib/transport/chdr.cpp b/host/lib/transport/chdr.cpp new file mode 100644 index 000000000..632887e56 --- /dev/null +++ b/host/lib/transport/chdr.cpp @@ -0,0 +1,182 @@ +// +// Copyright 2014 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/transport/chdr.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/exception.hpp> + +//define the endian macros to convert integers +#ifdef BOOST_BIG_ENDIAN + #define BE_MACRO(x) (x) + #define LE_MACRO(x) uhd::byteswap(x) +#else + #define BE_MACRO(x) uhd::byteswap(x) + #define LE_MACRO(x) (x) +#endif + +using namespace uhd::transport::vrt; + +static const boost::uint32_t HDR_FLAG_TSF = (1 << 29); +static const boost::uint32_t HDR_FLAG_EOB = (1 << 28); +static const boost::uint32_t HDR_FLAG_ERROR = (1 << 28); + +/***************************************************************************/ +/* Packing */ +/***************************************************************************/ +/*! Translate the contents of \p if_packet_info into a 32-Bit word and return it. + */ +UHD_INLINE boost::uint32_t _hdr_pack_chdr( + if_packet_info_t &if_packet_info +) { + // Set fields in if_packet_info + if_packet_info.num_header_words32 = 2 + (if_packet_info.has_tsf ? 2 : 0); + if_packet_info.num_packet_words32 = + if_packet_info.num_header_words32 + + if_packet_info.num_payload_words32; + + boost::uint16_t pkt_length = + if_packet_info.num_payload_bytes + (4 * if_packet_info.num_header_words32); + boost::uint32_t chdr = 0 + // 2 Bits: Packet type + | (if_packet_info.packet_type << 30) + // 1 Bit: Has time + | (if_packet_info.has_tsf ? HDR_FLAG_TSF : 0) + // 1 Bit: EOB or Error + | ((if_packet_info.eob or if_packet_info.error) ? HDR_FLAG_EOB : 0) + // 12 Bits: Sequence number + | ((if_packet_info.packet_count & 0xFFF) << 16) + // 16 Bits: Total packet length + | pkt_length; + return chdr; +} + +void chdr::if_hdr_pack_be( + boost::uint32_t *packet_buff, + if_packet_info_t &if_packet_info +) { + // Write header and update if_packet_info + packet_buff[0] = BE_MACRO(_hdr_pack_chdr(if_packet_info)); + + // Write SID + packet_buff[1] = BE_MACRO(if_packet_info.sid); + + // Write time + if (if_packet_info.has_tsf) { + packet_buff[2] = BE_MACRO(boost::uint32_t(if_packet_info.tsf >> 32)); + packet_buff[3] = BE_MACRO(boost::uint32_t(if_packet_info.tsf >> 0)); + } +} + +void chdr::if_hdr_pack_le( + boost::uint32_t *packet_buff, + if_packet_info_t &if_packet_info +) { + // Write header and update if_packet_info + packet_buff[0] = LE_MACRO(_hdr_pack_chdr(if_packet_info)); + + // Write SID + packet_buff[1] = LE_MACRO(if_packet_info.sid); + + // Write time + if (if_packet_info.has_tsf) { + packet_buff[2] = LE_MACRO(boost::uint32_t(if_packet_info.tsf >> 32)); + packet_buff[3] = LE_MACRO(boost::uint32_t(if_packet_info.tsf >> 0)); + } +} + + +/***************************************************************************/ +/* Unpacking */ +/***************************************************************************/ +UHD_INLINE void _hdr_unpack_chdr( + const boost::uint32_t chdr, + if_packet_info_t &if_packet_info +) { + // Set constant members + if_packet_info.link_type = if_packet_info_t::LINK_TYPE_CHDR; + if_packet_info.has_cid = false; + if_packet_info.has_sid = true; + if_packet_info.has_tsi = false; + if_packet_info.has_tlr = false; + if_packet_info.sob = false; + + // Set configurable members + if_packet_info.has_tsf = (chdr & HDR_FLAG_TSF) > 0; + if_packet_info.packet_type = if_packet_info_t::packet_type_t((chdr >> 30) & 0x3); + if_packet_info.eob = (if_packet_info.packet_type == if_packet_info_t::PACKET_TYPE_DATA) + && ((chdr & HDR_FLAG_EOB) > 0); + if_packet_info.error = (if_packet_info.packet_type == if_packet_info_t::PACKET_TYPE_RESP) + && ((chdr & HDR_FLAG_ERROR) > 0); + if_packet_info.packet_count = (chdr >> 16) & 0xFFF; + + // Set packet length variables + if (if_packet_info.has_tsf) { + if_packet_info.num_header_words32 = 4; + } else { + if_packet_info.num_header_words32 = 2; + } + size_t pkt_size_bytes = (chdr & 0xFFFF); + size_t pkt_size_word32 = (pkt_size_bytes / 4) + ((pkt_size_bytes % 4) ? 1 : 0); + // Check lengths match: + if (pkt_size_word32 < if_packet_info.num_header_words32) { + throw uhd::value_error("Bad CHDR or invalid packet length"); + } + if (if_packet_info.num_packet_words32 < pkt_size_word32) { + throw uhd::value_error("Bad CHDR or packet fragment"); + } + if_packet_info.num_payload_bytes = pkt_size_bytes - (4 * if_packet_info.num_header_words32); + if_packet_info.num_payload_words32 = pkt_size_word32 - if_packet_info.num_header_words32; +} + +void chdr::if_hdr_unpack_be( + const boost::uint32_t *packet_buff, + if_packet_info_t &if_packet_info +) { + // Read header and update if_packet_info + boost::uint32_t chdr = BE_MACRO(packet_buff[0]); + _hdr_unpack_chdr(chdr, if_packet_info); + + // Read SID + if_packet_info.sid = BE_MACRO(packet_buff[1]); + + // Read time (has_tsf was updated earlier) + if (if_packet_info.has_tsf) { + if_packet_info.tsf = 0 + | boost::uint64_t(BE_MACRO(packet_buff[2])) << 32 + | BE_MACRO(packet_buff[3]); + } +} + +void chdr::if_hdr_unpack_le( + const boost::uint32_t *packet_buff, + if_packet_info_t &if_packet_info +) { + // Read header and update if_packet_info + boost::uint32_t chdr = LE_MACRO(packet_buff[0]); + _hdr_unpack_chdr(chdr, if_packet_info); + + // Read SID + if_packet_info.sid = LE_MACRO(packet_buff[1]); + + // Read time (has_tsf was updated earlier) + if (if_packet_info.has_tsf) { + if_packet_info.tsf = 0 + | boost::uint64_t(LE_MACRO(packet_buff[2])) << 32 + | LE_MACRO(packet_buff[3]); + } +} + diff --git a/host/lib/transport/gen_vrt_if_packet.py b/host/lib/transport/gen_vrt_if_packet.py index 98f6804ae..6723e3a4b 100644 --- a/host/lib/transport/gen_vrt_if_packet.py +++ b/host/lib/transport/gen_vrt_if_packet.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright 2010-2013 Ettus Research LLC +# Copyright 2010-2013,2015 Ettus Research LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -25,26 +25,25 @@ metatdata into vrt headers and vrt headers into metadata. The generated code infers jump tables to speed-up the parsing time. """ -TMPL_TEXT = """ -#import time +TMPL_TEXT = """<% import time %> /*********************************************************************** - * This file was generated by $file on $time.strftime("%c") + * This file was generated by ${file} on ${time.strftime("%c")} **********************************************************************/ -\#include <uhd/exception.hpp> -\#include <uhd/transport/vrt_if_packet.hpp> -\#include <uhd/utils/byteswap.hpp> -\#include <boost/detail/endian.hpp> -\#include <vector> +#include <uhd/exception.hpp> +#include <uhd/transport/vrt_if_packet.hpp> +#include <uhd/utils/byteswap.hpp> +#include <boost/detail/endian.hpp> +#include <vector> //define the endian macros to convert integers -\#ifdef BOOST_BIG_ENDIAN - \#define BE_MACRO(x) (x) - \#define LE_MACRO(x) uhd::byteswap(x) -\#else - \#define BE_MACRO(x) uhd::byteswap(x) - \#define LE_MACRO(x) (x) -\#endif +#ifdef BOOST_BIG_ENDIAN + #define BE_MACRO(x) (x) + #define LE_MACRO(x) uhd::byteswap(x) +#else + #define BE_MACRO(x) uhd::byteswap(x) + #define LE_MACRO(x) (x) +#endif using namespace uhd; using namespace uhd::transport; @@ -59,13 +58,13 @@ static pred_table_type get_pred_unpack_table(void) pred_table_type table(1 << 9, 0); //only 9 bits useful here (20-28) for (size_t i = 0; i < table.size(); i++){ boost::uint32_t vrt_hdr_word = i << 20; - if(vrt_hdr_word & $hex(0x1 << 28)) table[i] |= $hex($sid_p); - if(vrt_hdr_word & $hex(0x1 << 27)) table[i] |= $hex($cid_p); - if(vrt_hdr_word & $hex(0x3 << 22)) table[i] |= $hex($tsi_p); - if(vrt_hdr_word & $hex(0x3 << 20)) table[i] |= $hex($tsf_p); - if(vrt_hdr_word & $hex(0x1 << 26)) table[i] |= $hex($tlr_p); - if(vrt_hdr_word & $hex(0x1 << 24)) table[i] |= $hex($eob_p); - if(vrt_hdr_word & $hex(0x1 << 25)) table[i] |= $hex($sob_p); + if(vrt_hdr_word & ${hex(0x1 << 28)}) table[i] |= ${hex(sid_p)}; + if(vrt_hdr_word & ${hex(0x1 << 27)}) table[i] |= ${hex(cid_p)}; + if(vrt_hdr_word & ${hex(0x3 << 22)}) table[i] |= ${hex(tsi_p)}; + if(vrt_hdr_word & ${hex(0x3 << 20)}) table[i] |= ${hex(tsf_p)}; + if(vrt_hdr_word & ${hex(0x1 << 26)}) table[i] |= ${hex(tlr_p)}; + if(vrt_hdr_word & ${hex(0x1 << 24)}) table[i] |= ${hex(eob_p)}; + if(vrt_hdr_word & ${hex(0x1 << 25)}) table[i] |= ${hex(sob_p)}; } return table; } @@ -105,13 +104,12 @@ UHD_INLINE static boost::uint32_t vrt_to_chdr(const boost::uint32_t vrt, const i } ######################################################################## -#def gen_code($XE_MACRO, $suffix) +<%def name="gen_code(XE_MACRO, suffix)"> ######################################################################## - /*********************************************************************** - * interal impl of packing VRT IF header only + * internal impl of packing VRT IF header only **********************************************************************/ -UHD_INLINE void __if_hdr_pack_$(suffix)( +UHD_INLINE void __if_hdr_pack_${suffix}( boost::uint32_t *packet_buff, if_packet_info_t &if_packet_info, boost::uint32_t &vrt_hdr_word32 @@ -119,72 +117,53 @@ UHD_INLINE void __if_hdr_pack_$(suffix)( boost::uint32_t vrt_hdr_flags = 0; pred_type pred = 0; - if (if_packet_info.has_sid) pred |= $hex($sid_p); - if (if_packet_info.has_cid) pred |= $hex($cid_p); - if (if_packet_info.has_tsi) pred |= $hex($tsi_p); - if (if_packet_info.has_tsf) pred |= $hex($tsf_p); - if (if_packet_info.has_tlr) pred |= $hex($tlr_p); - if (if_packet_info.eob) pred |= $hex($eob_p); - if (if_packet_info.sob) pred |= $hex($sob_p); + if (if_packet_info.has_sid) pred |= ${hex(sid_p)}; + if (if_packet_info.has_cid) pred |= ${hex(cid_p)}; + if (if_packet_info.has_tsi) pred |= ${hex(tsi_p)}; + if (if_packet_info.has_tsf) pred |= ${hex(tsf_p)}; + if (if_packet_info.has_tlr) pred |= ${hex(tlr_p)}; + if (if_packet_info.eob) pred |= ${hex(eob_p)}; + if (if_packet_info.sob) pred |= ${hex(sob_p)}; switch(pred){ - #for $pred in range(2**7) - case $pred: - #set $num_header_words = 1 - #set $flags = 0 + % for pred in range(2**7): + case ${pred}:<% num_header_words = 1 %><% flags = 0 %> ########## Stream ID ########## - #if $pred & $sid_p - packet_buff[$num_header_words] = $(XE_MACRO)(if_packet_info.sid); - #set $num_header_words += 1 - #set $flags |= (0x1 << 28); - #end if + % if pred & sid_p: + packet_buff[${num_header_words}] = ${XE_MACRO}(if_packet_info.sid);<% num_header_words += 1 %><% flags |= (0x1 << 28) %> + % endif ########## Class ID ########## - #if $pred & $cid_p - packet_buff[$num_header_words] = 0; //not implemented - #set $num_header_words += 1 - packet_buff[$num_header_words] = 0; //not implemented - #set $num_header_words += 1 - #set $flags |= (0x1 << 27); - #end if + % if pred & cid_p: + packet_buff[${num_header_words}] = 0; //not implemented<% num_header_words += 1 %> + packet_buff[${num_header_words}] = 0; //not implemented<% num_header_words += 1 %><% flags |= (0x1 << 27) %> + % endif ########## Integer Time ########## - #if $pred & $tsi_p - packet_buff[$num_header_words] = $(XE_MACRO)(if_packet_info.tsi); - #set $num_header_words += 1 - #set $flags |= (0x3 << 22); - #end if + % if pred & tsi_p: + packet_buff[${num_header_words}] = ${XE_MACRO}(if_packet_info.tsi);<% num_header_words += 1 %><% flags |= (0x3 << 22) %> + % endif ########## Fractional Time ########## - #if $pred & $tsf_p - packet_buff[$num_header_words] = $(XE_MACRO)(boost::uint32_t(if_packet_info.tsf >> 32)); - #set $num_header_words += 1 - packet_buff[$num_header_words] = $(XE_MACRO)(boost::uint32_t(if_packet_info.tsf >> 0)); - #set $num_header_words += 1 - #set $flags |= (0x1 << 20); - #end if + % if pred & tsf_p: + packet_buff[${num_header_words}] = ${XE_MACRO}(boost::uint32_t(if_packet_info.tsf >> 32));<% num_header_words += 1 %> + packet_buff[${num_header_words}] = ${XE_MACRO}(boost::uint32_t(if_packet_info.tsf >> 0));<% num_header_words += 1 %><% flags |= (0x1 << 20) %> + % endif ########## Burst Flags ########## - #if $pred & $eob_p - #set $flags |= (0x1 << 24); - #end if - #if $pred & $sob_p - #set $flags |= (0x1 << 25); - #end if +<% if pred & eob_p: flags |= (0x1 << 24) %><% if pred & sob_p: flags |= (0x1 << 25) %> ########## Trailer ########## - #if $pred & $tlr_p + % 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 + packet_buff[${num_header_words}+if_packet_info.num_payload_words32] = ${XE_MACRO}(if_packet_info.tlr);<% flags |= (0x1 << 26) %><% num_trailer_words = 1 %> + % else: +<% num_trailer_words = 0 %> + % endif ########## 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; - vrt_hdr_flags = $hex($flags); + if_packet_info.num_header_words32 = ${num_header_words}; + if_packet_info.num_packet_words32 = ${num_header_words + num_trailer_words} + if_packet_info.num_payload_words32; + vrt_hdr_flags = ${hex(flags)}; break; - #end for + % endfor } //fill in complete header word @@ -197,9 +176,9 @@ UHD_INLINE void __if_hdr_pack_$(suffix)( } /*********************************************************************** - * interal impl of unpacking VRT IF header only + * internal impl of unpacking VRT IF header only **********************************************************************/ -UHD_INLINE void __if_hdr_unpack_$(suffix)( +UHD_INLINE void __if_hdr_unpack_${suffix}( const boost::uint32_t *packet_buff, if_packet_info_t &if_packet_info, const boost::uint32_t vrt_hdr_word32 @@ -219,86 +198,78 @@ UHD_INLINE void __if_hdr_unpack_$(suffix)( size_t empty_bytes = 0; switch(pred){ - #for $pred in range(2**7) - case $pred: - #set $has_time_spec = False - #set $num_header_words = 1 + % for pred in range(2**7): + case ${pred}:<% has_time_spec = False %><% num_header_words = 1 %> ########## Stream ID ########## - #if $pred & $sid_p + % if pred & sid_p: if_packet_info.has_sid = true; - if_packet_info.sid = $(XE_MACRO)(packet_buff[$num_header_words]); - #set $num_header_words += 1 - #else + if_packet_info.sid = ${XE_MACRO}(packet_buff[${num_header_words}]);<% num_header_words += 1 %> + % else: if_packet_info.has_sid = false; - #end if + % endif ########## Class ID ########## - #if $pred & $cid_p + % if pred & cid_p: if_packet_info.has_cid = true; - if_packet_info.cid = 0; //not implemented - #set $num_header_words += 2 - #else + if_packet_info.cid = 0; //not implemented<% num_header_words += 2 %> + % else: if_packet_info.has_cid = false; - #end if + % endif ########## Integer Time ########## - #if $pred & $tsi_p + % if pred & tsi_p: if_packet_info.has_tsi = true; - if_packet_info.tsi = $(XE_MACRO)(packet_buff[$num_header_words]); - #set $num_header_words += 1 - #else + if_packet_info.tsi = ${XE_MACRO}(packet_buff[${num_header_words}]); +<% num_header_words += 1 %> + % else: if_packet_info.has_tsi = false; - #end if + % endif ########## Fractional Time ########## - #if $pred & $tsf_p + % if pred & tsf_p: if_packet_info.has_tsf = true; - if_packet_info.tsf = boost::uint64_t($(XE_MACRO)(packet_buff[$num_header_words])) << 32; - #set $num_header_words += 1 - if_packet_info.tsf |= $(XE_MACRO)(packet_buff[$num_header_words]); - #set $num_header_words += 1 - #else + if_packet_info.tsf = boost::uint64_t(${XE_MACRO}(packet_buff[${num_header_words}])) << 32;<% num_header_words += 1 %> + if_packet_info.tsf |= ${XE_MACRO}(packet_buff[${num_header_words}]);<% num_header_words += 1 %> + % else: if_packet_info.has_tsf = false; - #end if + % endif ########## Burst Flags ########## - #if $pred & $eob_p + % if pred & eob_p: if_packet_info.eob = true; - #else + % else: if_packet_info.eob = false; - #end if - #if $pred & $sob_p + % endif + % if pred & sob_p: if_packet_info.sob = true; - #else + % else: if_packet_info.sob = false; - #end if + % endif ########## Trailer ########## - #if $pred & $tlr_p + % 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; + if_packet_info.tlr = ${XE_MACRO}(packet_buff[packet_words32-1]);<% 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 + % else: + if_packet_info.has_tlr = false;<% num_trailer_words = 0 %> + % endif ########## Variables ########## //another failure case - if (packet_words32 < $($num_header_words + $num_trailer_words)) + 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_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 + % endfor } } /*********************************************************************** * link layer + VRT IF packing **********************************************************************/ -void vrt::if_hdr_pack_$(suffix)( +void vrt::if_hdr_pack_${suffix}( boost::uint32_t *packet_buff, if_packet_info_t &if_packet_info ){ @@ -306,29 +277,29 @@ void vrt::if_hdr_pack_$(suffix)( switch (if_packet_info.link_type) { case if_packet_info_t::LINK_TYPE_NONE: - __if_hdr_pack_$(suffix)(packet_buff, if_packet_info, vrt_hdr_word32); - packet_buff[0] = $(XE_MACRO)(vrt_hdr_word32); + __if_hdr_pack_${suffix}(packet_buff, if_packet_info, vrt_hdr_word32); + packet_buff[0] = ${XE_MACRO}(vrt_hdr_word32); break; case if_packet_info_t::LINK_TYPE_CHDR: { - __if_hdr_pack_$(suffix)(packet_buff, if_packet_info, vrt_hdr_word32); + __if_hdr_pack_${suffix}(packet_buff, if_packet_info, vrt_hdr_word32); const boost::uint32_t chdr = vrt_to_chdr(vrt_hdr_word32, if_packet_info); - packet_buff[0] = $(XE_MACRO)(chdr); + packet_buff[0] = ${XE_MACRO}(chdr); break; } case if_packet_info_t::LINK_TYPE_VRLP: - __if_hdr_pack_$(suffix)(packet_buff+2, if_packet_info, vrt_hdr_word32); + __if_hdr_pack_${suffix}(packet_buff+2, if_packet_info, vrt_hdr_word32); if_packet_info.num_header_words32 += 2; if_packet_info.num_packet_words32 += 3; - packet_buff[0] = $(XE_MACRO)(VRLP); - packet_buff[1] = $(XE_MACRO)(boost::uint32_t( + packet_buff[0] = ${XE_MACRO}(VRLP); + packet_buff[1] = ${XE_MACRO}(boost::uint32_t( (if_packet_info.num_packet_words32 & 0xfffff) | ((if_packet_info.packet_count & 0xfff) << 20) )); - packet_buff[2] = $(XE_MACRO)(vrt_hdr_word32); - packet_buff[if_packet_info.num_packet_words32-1] = $(XE_MACRO)(VEND); + packet_buff[2] = ${XE_MACRO}(vrt_hdr_word32); + packet_buff[if_packet_info.num_packet_words32-1] = ${XE_MACRO}(VEND); break; } } @@ -336,7 +307,7 @@ void vrt::if_hdr_pack_$(suffix)( /*********************************************************************** * link layer + VRT IF unpacking **********************************************************************/ -void vrt::if_hdr_unpack_$(suffix)( +void vrt::if_hdr_unpack_${suffix}( const boost::uint32_t *packet_buff, if_packet_info_t &if_packet_info ){ @@ -344,16 +315,16 @@ void vrt::if_hdr_unpack_$(suffix)( switch (if_packet_info.link_type) { case if_packet_info_t::LINK_TYPE_NONE: - vrt_hdr_word32 = $(XE_MACRO)(packet_buff[0]); - __if_hdr_unpack_$(suffix)(packet_buff, if_packet_info, vrt_hdr_word32); + vrt_hdr_word32 = ${XE_MACRO}(packet_buff[0]); + __if_hdr_unpack_${suffix}(packet_buff, if_packet_info, vrt_hdr_word32); break; case if_packet_info_t::LINK_TYPE_CHDR: { - const boost::uint32_t chdr = $(XE_MACRO)(packet_buff[0]); + const boost::uint32_t chdr = ${XE_MACRO}(packet_buff[0]); vrt_hdr_word32 = chdr_to_vrt(chdr, if_packet_info); size_t packet_count = if_packet_info.packet_count; - __if_hdr_unpack_$(suffix)(packet_buff, if_packet_info, vrt_hdr_word32); + __if_hdr_unpack_${suffix}(packet_buff, if_packet_info, vrt_hdr_word32); if_packet_info.num_payload_bytes -= (~chdr + 1) & 0x3; if_packet_info.packet_count = packet_count; break; @@ -361,12 +332,12 @@ void vrt::if_hdr_unpack_$(suffix)( case if_packet_info_t::LINK_TYPE_VRLP: { - if ($(XE_MACRO)(packet_buff[0]) != VRLP) throw uhd::value_error("bad vrl header VRLP"); - const boost::uint32_t vrl_hdr = $(XE_MACRO)(packet_buff[1]); - vrt_hdr_word32 = $(XE_MACRO)(packet_buff[2]); + if (${XE_MACRO}(packet_buff[0]) != VRLP) throw uhd::value_error("bad vrl header VRLP"); + const boost::uint32_t vrl_hdr = ${XE_MACRO}(packet_buff[1]); + vrt_hdr_word32 = ${XE_MACRO}(packet_buff[2]); if (if_packet_info.num_packet_words32 < (vrl_hdr & 0xfffff)) throw uhd::value_error("bad vrl header or packet fragment"); - if ($(XE_MACRO)(packet_buff[(vrl_hdr & 0xfffff)-1]) != VEND) throw uhd::value_error("bad vrl trailer VEND"); - __if_hdr_unpack_$(suffix)(packet_buff+2, if_packet_info, vrt_hdr_word32); + if (${XE_MACRO}(packet_buff[(vrl_hdr & 0xfffff)-1]) != VEND) throw uhd::value_error("bad vrl trailer VEND"); + __if_hdr_unpack_${suffix}(packet_buff+2, if_packet_info, vrt_hdr_word32); if_packet_info.num_header_words32 += 2; //add vrl header if_packet_info.packet_count = (vrl_hdr >> 20) & 0xfff; break; @@ -375,16 +346,16 @@ void vrt::if_hdr_unpack_$(suffix)( } ######################################################################## -#end def +</%def> ######################################################################## -$gen_code("BE_MACRO", "be") -$gen_code("LE_MACRO", "le") +${gen_code("BE_MACRO", "be")} +${gen_code("LE_MACRO", "le")} """ def parse_tmpl(_tmpl_text, **kwargs): - from Cheetah.Template import Template - return str(Template(_tmpl_text, kwargs)) + from mako.template import Template + return Template(_tmpl_text).render(**kwargs) if __name__ == '__main__': import sys diff --git a/host/lib/transport/libusb1_base.cpp b/host/lib/transport/libusb1_base.cpp index ae33cc036..f92117a9e 100644 --- a/host/lib/transport/libusb1_base.cpp +++ b/host/lib/transport/libusb1_base.cpp @@ -71,7 +71,18 @@ private: timeval tv; tv.tv_sec = 0; tv.tv_usec = 100000; - libusb_handle_events_timeout(context, &tv); + int ret = libusb_handle_events_timeout(context, &tv); + switch (ret) + { + case LIBUSB_SUCCESS: + case LIBUSB_ERROR_TIMEOUT: + break; + case LIBUSB_ERROR_NO_DEVICE: + throw uhd::io_error(libusb_strerror(LIBUSB_ERROR_NO_DEVICE)); + default: + UHD_MSG(error) << __FUNCTION__ << ": " << libusb_strerror((libusb_error)ret) << std::endl; + break; + } } }; @@ -251,6 +262,21 @@ public: _claimed.push_back(interface); } + void clear_endpoints(unsigned char recv_endpoint, unsigned char send_endpoint) + { + int ret; + ret = libusb_clear_halt(this->get(), recv_endpoint | 0x80); + UHD_LOG << "usb device handle: recv endpoint clear: " << libusb_error_name(ret) << std::endl; + ret = libusb_clear_halt(this->get(), send_endpoint | 0x00); + UHD_LOG << "usb device handle: send endpoint clear: " << libusb_error_name(ret) << std::endl; + } + + void reset_device(void) + { + int ret = libusb_reset_device(this->get()); + UHD_LOG << "usb device handle: dev Reset: " << libusb_error_name(ret) << std::endl; + } + private: libusb::device::sptr _dev; //always keep a reference to device libusb_device_handle *_handle; @@ -345,15 +371,21 @@ libusb::special_handle::sptr libusb::special_handle::make(device::sptr dev){ std::vector<usb_device_handle::sptr> usb_device_handle::get_device_list( boost::uint16_t vid, boost::uint16_t pid ){ - std::vector<usb_device_handle::sptr> handles; + return usb_device_handle::get_device_list(std::vector<usb_device_handle::vid_pid_pair_t>(1,usb_device_handle::vid_pid_pair_t(vid,pid))); +} +std::vector<usb_device_handle::sptr> usb_device_handle::get_device_list(const std::vector<usb_device_handle::vid_pid_pair_t>& vid_pid_pair_list) +{ + std::vector<usb_device_handle::sptr> handles; libusb::device_list::sptr dev_list = libusb::device_list::make(); - for (size_t i = 0; i < dev_list->size(); i++){ - usb_device_handle::sptr handle = libusb::special_handle::make(dev_list->at(i)); - if (handle->get_vendor_id() == vid and handle->get_product_id() == pid){ - handles.push_back(handle); - } + for(size_t iter = 0; iter < vid_pid_pair_list.size(); ++iter) + { + for (size_t i = 0; i < dev_list->size(); i++){ + usb_device_handle::sptr handle = libusb::special_handle::make(dev_list->at(i)); + if (handle->get_vendor_id() == vid_pid_pair_list[iter].first and handle->get_product_id() == vid_pid_pair_list[iter].second){ + handles.push_back(handle); + } + } } - return handles; } diff --git a/host/lib/transport/libusb1_base.hpp b/host/lib/transport/libusb1_base.hpp index b00946614..2ff1291d9 100644 --- a/host/lib/transport/libusb1_base.hpp +++ b/host/lib/transport/libusb1_base.hpp @@ -24,6 +24,29 @@ #include <uhd/transport/usb_device_handle.hpp> #include <libusb.h> +//! Define LIBUSB_CALL when its missing (non-windows) +#ifndef LIBUSB_CALL + #define LIBUSB_CALL +#endif /*LIBUSB_CALL*/ + +//! libusb_handle_events_timeout_completed is only in newer API +#ifndef HAVE_LIBUSB_HANDLE_EVENTS_TIMEOUT_COMPLETED + #define libusb_handle_events_timeout_completed(ctx, tx, completed) \ + libusb_handle_events_timeout(ctx, tx) +#endif /* HAVE_LIBUSB_HANDLE_EVENTS_TIMEOUT_COMPLETED */ + +//! libusb_error_name is only in newer API +#ifndef HAVE_LIBUSB_ERROR_NAME + #define libusb_error_name(code) \ + str(boost::format("LIBUSB_ERROR_CODE %d") % code) +#endif /* HAVE_LIBUSB_ERROR_NAME */ + +//! libusb_strerror is only in newer API +#ifndef HAVE_LIBUSB_STRERROR + #define libusb_strerror(code) \ + libusb_error_name(code) +#endif /* HAVE_LIBUSB_STRERROR */ + /*********************************************************************** * Libusb object oriented smart pointer wrappers: * The following wrappers provide allocation and automatic deallocation @@ -135,6 +158,10 @@ namespace libusb { * Control interface: 0 */ virtual void claim_interface(int) = 0; + + virtual void clear_endpoints(unsigned char recv_endpoint, unsigned char send_endpoint) = 0; + + virtual void reset_device(void) = 0; }; /*! diff --git a/host/lib/transport/libusb1_zero_copy.cpp b/host/lib/transport/libusb1_zero_copy.cpp index 1ac02d16f..b67b36d0a 100644 --- a/host/lib/transport/libusb1_zero_copy.cpp +++ b/host/lib/transport/libusb1_zero_copy.cpp @@ -43,23 +43,6 @@ using namespace uhd::transport; static const size_t DEFAULT_NUM_XFERS = 16; //num xfers static const size_t DEFAULT_XFER_SIZE = 32*512; //bytes -//! Define LIBUSB_CALL when its missing (non-windows) -#ifndef LIBUSB_CALL - #define LIBUSB_CALL -#endif /*LIBUSB_CALL*/ - -//! libusb_handle_events_timeout_completed is only in newer API -#ifndef HAVE_LIBUSB_HANDLE_EVENTS_TIMEOUT_COMPLETED - #define libusb_handle_events_timeout_completed(ctx, tx, completed) \ - libusb_handle_events_timeout(ctx, tx) -#endif - -//! libusb_error_name is only in newer API -#ifndef HAVE_LIBUSB_ERROR_NAME - #define libusb_error_name(code) \ - str(boost::format("LIBUSB_ERROR_CODE %d") % code) -#endif - //! type for sharing the release queue with managed buffers class libusb_zero_copy_mb; typedef boost::shared_ptr<bounded_buffer<libusb_zero_copy_mb *> > mb_queue_sptr; @@ -155,7 +138,8 @@ public: result.is_recv = _is_recv; #endif const int ret = libusb_submit_transfer(_lut); - if (ret != 0) throw uhd::runtime_error(str(boost::format( + if (ret != LIBUSB_SUCCESS) + throw uhd::usb_error(ret, str(boost::format( "usb %s submit failed: %s") % _name % libusb_error_name(ret))); } @@ -164,8 +148,9 @@ public: { if (wait_for_completion(timeout)) { - if (result.status != LIBUSB_TRANSFER_COMPLETED) throw uhd::runtime_error(str(boost::format( - "usb %s transfer status: %d") % _name % int(result.status))); + if (result.status != LIBUSB_TRANSFER_COMPLETED) + throw uhd::runtime_error(str(boost::format("usb %s transfer status: %d") + % _name % libusb_error_name(result.status))); result.completed = 0; return make(reinterpret_cast<buffer_type *>(this), _lut->buffer, (_is_recv)? result.actual_length : _frame_size); } @@ -219,7 +204,8 @@ public: _num_frames(num_frames), _frame_size(frame_size), _buffer_pool(buffer_pool::make(_num_frames, _frame_size)), - _enqueued(_num_frames), _released(_num_frames) + _enqueued(_num_frames), _released(_num_frames), + _status(STATUS_RUNNING) { const bool is_recv = (endpoint & 0x80) != 0; const std::string name = str(boost::format("%s%d") % ((is_recv)? "rx" : "tx") % int(endpoint & 0x7f)); @@ -304,18 +290,24 @@ public: UHD_INLINE typename buffer_type::sptr get_buff(double timeout) { typename buffer_type::sptr buff; - libusb_zero_copy_mb *front = NULL; - boost::mutex::scoped_lock lock(_mutex); + + if (_status == STATUS_ERROR) + return buff; + + // Serialize access to buffers + boost::mutex::scoped_lock get_buff_lock(_get_buff_mutex); + + boost::mutex::scoped_lock queue_lock(_queue_mutex); if (_enqueued.empty()) { - _cond.timed_wait(lock, boost::posix_time::microseconds(long(timeout*1e6))); + _buff_ready_cond.timed_wait(queue_lock, boost::posix_time::microseconds(long(timeout*1e6))); } if (_enqueued.empty()) return buff; - front = _enqueued.front(); + libusb_zero_copy_mb *front = _enqueued.front(); - lock.unlock(); + queue_lock.unlock(); buff = front->get_new<buffer_type>(timeout); - lock.lock(); + queue_lock.lock(); if (buff) _enqueued.pop_front(); this->submit_what_we_can(); @@ -333,28 +325,39 @@ private: buffer_pool::sptr _buffer_pool; std::vector<boost::shared_ptr<libusb_zero_copy_mb> > _mb_pool; - boost::mutex _mutex; - boost::condition_variable _cond; + boost::mutex _queue_mutex; + boost::condition_variable _buff_ready_cond; + boost::mutex _get_buff_mutex; //! why 2 queues? there is room in the future to have > N buffers but only N in flight boost::circular_buffer<libusb_zero_copy_mb *> _enqueued, _released; + enum {STATUS_RUNNING, STATUS_ERROR} _status; + void enqueue_buffer(libusb_zero_copy_mb *mb) { - boost::mutex::scoped_lock l(_mutex); + boost::mutex::scoped_lock l(_queue_mutex); _released.push_back(mb); this->submit_what_we_can(); - l.unlock(); - _cond.notify_one(); + _buff_ready_cond.notify_one(); } void submit_what_we_can(void) { + if (_status == STATUS_ERROR) + return; while (not _released.empty() and not _enqueued.full()) { - _released.front()->submit(); - _enqueued.push_back(_released.front()); - _released.pop_front(); + try { + _released.front()->submit(); + _enqueued.push_back(_released.front()); + _released.pop_front(); + } + catch (uhd::usb_error& e) + { + _status = STATUS_ERROR; + throw e; + } } } diff --git a/host/lib/transport/nirio/lvbitx/process-lvbitx.py b/host/lib/transport/nirio/lvbitx/process-lvbitx.py index ab9625608..7887c3997 100755 --- a/host/lib/transport/nirio/lvbitx/process-lvbitx.py +++ b/host/lib/transport/nirio/lvbitx/process-lvbitx.py @@ -1,6 +1,6 @@ #!/usr/bin/python # -# Copyright 2013-2014 Ettus Research LLC +# Copyright 2013-2015 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 @@ -35,7 +35,7 @@ parser.add_option("--output-src-path", type="string", dest="output_src_path", he # Args if (len(args) < 1): - print 'ERROR: Please specify the input LVBITX file name' + print('ERROR: Please specify the input LVBITX file name') sys.exit(1) lvbitx_filename = args[0] @@ -44,16 +44,16 @@ autogen_src_path = os.path.abspath(options.output_src_path) if (options.output_s class_name = os.path.splitext(os.path.basename(input_filename))[0] if (not os.path.isfile(input_filename)): - print 'ERROR: FPGA File ' + input_filename + ' could not be accessed or is not a file.' + print('ERROR: FPGA File ' + input_filename + ' could not be accessed or is not a file.') sys.exit(1) if (options.merge_bin is not None and not os.path.isfile(os.path.abspath(options.merge_bin))): - print 'ERROR: FPGA Bin File ' + options.merge_bin + ' could not be accessed or is not a file.' + print('ERROR: FPGA Bin File ' + options.merge_bin + ' could not be accessed or is not a file.') sys.exit(1) if (not os.path.exists(autogen_src_path)): - print 'ERROR: Output path ' + autogen_src_path + ' could not be accessed.' + print('ERROR: Output path ' + autogen_src_path + ' could not be accessed.') sys.exit(1) if (options.output_lvbitx_path is not None and input_filename == os.path.join(autogen_src_path, class_name + '.lvbitx')): - print 'ERROR: Input and output LVBITX files were the same. Choose a difference input file or output path.' + print('ERROR: Input and output LVBITX files were the same. Choose a difference input file or output path.') sys.exit(1) # Get XML Tree Node @@ -122,7 +122,7 @@ def map_SubType_to_ScalarType(SubType): elif SubType == 'U64': ScalarType = 'RIO_SCALAR_TYPE_UQ' else: - print 'ERROR: No corresponding nirio_scalar_type_t value for SubType ' + SubType + ' .' + print('ERROR: No corresponding nirio_scalar_type_t value for SubType ' + SubType + ' .') sys.exit(1) return ScalarType; @@ -187,7 +187,7 @@ codegen_transform['lvbitx_signature'] = str.upper(root.find('SignatureRegister') # Write BIN file bitstream = base64.b64decode(root.find('Bitstream').text) if (options.output_lvbitx_path is not None and hashlib.md5(bitstream).hexdigest() != root.find('BitstreamMD5').text): - print 'ERROR: The MD5 sum for the output LVBITX was incorrect. Make sure that the bitstream in the input LVBITX or BIN file is valid.' + print('ERROR: The MD5 sum for the output LVBITX was incorrect. Make sure that the bitstream in the input LVBITX or BIN file is valid.') sys.exit(1) if (options.output_bin): fpga_bin_file = open(os.path.join(options.output_lvbitx_path, class_name + '.bin'), 'w') diff --git a/host/lib/transport/nirio/nirio_driver_iface_linux.cpp b/host/lib/transport/nirio/nirio_driver_iface_linux.cpp index 4afc1579f..4d983de17 100644 --- a/host/lib/transport/nirio/nirio_driver_iface_linux.cpp +++ b/host/lib/transport/nirio/nirio_driver_iface_linux.cpp @@ -25,16 +25,6 @@ namespace nirio_driver_iface { -struct nirio_ioctl_block_t -{ - uint64_t in_buf; - uint64_t out_buf; - uint32_t in_buf_len; - uint32_t out_buf_len; - uint32_t bytes_returned; - uint32_t padding; -}; - nirio_status rio_open( const std::string& device_path, rio_dev_handle_t& device_handle) diff --git a/host/lib/transport/nirio/niriok_proxy_impl_v1.cpp b/host/lib/transport/nirio/niriok_proxy_impl_v1.cpp index f0ffe2ffc..444e98f1b 100644 --- a/host/lib/transport/nirio/niriok_proxy_impl_v1.cpp +++ b/host/lib/transport/nirio/niriok_proxy_impl_v1.cpp @@ -26,8 +26,397 @@ #pragma GCC diagnostic ignored "-Wmissing-field-initializers" #endif +// CTL_CODE macro for non-win OSes +#ifndef UHD_PLATFORM_WIN32 + #define CTL_CODE(a,controlCode,b,c) (controlCode) +#endif + +const uint32_t NIRIO_IOCTL_BASE = 0x800; + +const uint32_t NIRIO_IOCTL_SYNCOP = + CTL_CODE(FILE_DEVICE_UNKNOWN, + NIRIO_IOCTL_BASE + 4, + METHOD_OUT_DIRECT, + FILE_READ_DATA | FILE_WRITE_DATA); + ///< The synchronous operation code. Note: We + /// must use METHOD_OUT_DIRECT on the syncOp() + /// IOCTL to ensure the contents of the output + /// block are available in the kernel. + +const uint32_t NIRIO_IOCTL_GET_IFACE_NUM = + CTL_CODE(FILE_DEVICE_UNKNOWN, + NIRIO_IOCTL_BASE + 6, + METHOD_BUFFERED, + FILE_READ_DATA); ///< Get the interface number for a device + +const uint32_t NIRIO_IOCTL_GET_SESSION = + CTL_CODE(FILE_DEVICE_UNKNOWN, + NIRIO_IOCTL_BASE + 8, + METHOD_BUFFERED, + FILE_READ_ACCESS); ///< Gets a previously opened session to a device + +const uint32_t NIRIO_IOCTL_POST_OPEN = + CTL_CODE(FILE_DEVICE_UNKNOWN, + NIRIO_IOCTL_BASE + 9, + METHOD_BUFFERED, + FILE_READ_ACCESS); ///< Called after opening a session + +const uint32_t NIRIO_IOCTL_PRE_CLOSE = + CTL_CODE(FILE_DEVICE_UNKNOWN, + NIRIO_IOCTL_BASE + 10, + METHOD_BUFFERED, + FILE_READ_ACCESS); ///< Called before closing a session + namespace uhd { namespace niusrprio { + // ------------------------------- + // Function Codes: defined as integers rather than enums because they + // are going to be carried accross boundaries so size matters + + struct NIRIO_FUNC + { + static const uint32_t GET32 = 0x00000001; + static const uint32_t SET32 = 0x00000002; + static const uint32_t SET_DRIVER_CONFIG = 0x00000007; + static const uint32_t FIFO = 0x00000008; + static const uint32_t IO = 0x0000000A; + static const uint32_t FIFO_STOP_ALL = 0x0000000C; + static const uint32_t ADD_RESOURCE = 0x0000000D; + static const uint32_t GET_STRING = 0x0000000E; + static const uint32_t SET_STRING = 0x0000000F; + static const uint32_t DOWNLOAD = 0x00000013; + static const uint32_t RESET = 0x00000014; + }; + + struct NIRIO_RESOURCE + { + static const uint32_t INPUT_FIFO = 0xD0000001; + static const uint32_t OUTPUT_FIFO = 0xD0000002; + }; + + struct NIRIO_FIFO + { + static const uint32_t CONFIGURE = 0x80000001; + static const uint32_t START = 0x80000002; + static const uint32_t STOP = 0x80000003; + static const uint32_t READ = 0x80000004; + static const uint32_t WRITE = 0x80000005; + static const uint32_t WAIT = 0x80000006; + static const uint32_t GRANT = 0x80000007; + }; + + struct NIRIO_IO + { + static const uint32_t POKE64 = 0xA0000005; + static const uint32_t POKE32 = 0xA0000006; + static const uint32_t POKE16 = 0xA0000007; + static const uint32_t POKE8 = 0xA0000008; + static const uint32_t PEEK64 = 0xA0000009; + static const uint32_t PEEK32 = 0xA000000A; + static const uint32_t PEEK16 = 0xA000000B; + static const uint32_t PEEK8 = 0xA000000C; + static const uint32_t READ_BLOCK = 0xA000000D; + static const uint32_t WRITE_BLOCK = 0xA000000E; + static const uint32_t GET_IO_WINDOW = 0xA000000F; + static const uint32_t GET_IO_WINDOW_SIZE = 0xA0000010; + }; + + struct nirio_ioctl_packet_t { + nirio_ioctl_packet_t(void* const _outBuf, const uint32_t _outSize, const int32_t _statusCode) + { + outBuf._64BitField = 0; + outBuf.pointer = _outBuf; + outSize = _outSize; + statusCode = _statusCode; + }; + + union { + void* pointer; + uint64_t _64BitField; + } outBuf; + + uint32_t outSize; + int32_t statusCode; + }; + + struct nirio_syncop_in_params_t + { + uint32_t function; + uint32_t subfunction; + + union + { + struct + { + uint32_t attribute; + uint32_t value; + } attribute32; + + struct + { + uint32_t attribute; + uint64_t value; + } attribute64; + + struct + { + uint32_t attribute; + } attributeStr; + + struct + { + uint32_t attribute; + } download; + + union + { + struct + { + uint32_t reserved_field_0_0_0; + } reserved_field_0_0; + struct + { + uint32_t reserved_field_0_1_0; + uint32_t reserved_field_0_1_1; + } reserved_field_0_1; + struct + { + uint32_t reserved_field_0_2_0; + } reserved_field_0_2; + } reserved_field_0; + + union + { + struct + { + uint32_t channel; + uint32_t baseAddress; + uint32_t depthInSamples; + uint32_t version; + } fifo; + struct + { + uint32_t channel; + uint32_t baseAddress; + uint32_t depthInSamples; + uint32_t version; + uint32_t scalarType; + uint32_t bitWidth; + } fifoWithDataType; + struct + { + uint64_t rangeBaseAddress; + uint32_t rangeSizeInBytes; + uint32_t rangeAttribute; + } atomic; // obsolete + } add; + + struct + { + uint32_t channel; + + union + { + struct + { + uint32_t requestedDepth; + uint8_t requiresActuals; + } config; + struct + { + uint32_t timeout; + } read; + struct + { + uint32_t timeout; + uint32_t scalarType; + uint32_t bitWidth; + } readWithDataType; + struct + { + uint32_t timeout; + } write; + struct + { + uint32_t timeout; + uint32_t scalarType; + uint32_t bitWidth; + } writeWithDataType; + struct + { + uint32_t elementsRequested; + uint32_t scalarType; + uint32_t bitWidth; + uint32_t timeout; + uint8_t output; + } wait; + struct + { + uint32_t elements; + } grant; + } op; + } fifo; + + struct + { + uint64_t reserved_field_1_0; + uint32_t reserved_field_1_1; + uint32_t reserved_field_1_2; + } reserved_field_1; // Obsolete + + struct + { + uint32_t offset; + union + { + uint64_t value64; + uint32_t value32; + uint16_t value16; + uint8_t value8; + } value; + union + { + uint32_t sizeToMap; + } memoryMappedIoWindow; + } io; + + struct + { + uint32_t reserved_field_2_0; + uint32_t reserved_field_2_1; + } reserved_field_2; + + struct + { + uint32_t reserved_field_3_0; + } reserved_field_3; + + union + { + struct + { + uint32_t reserved_field_4_0; + int32_t reserved_field_4_1; + } wait; + } reserved_field_4; + + } params; + + uint32_t inbufByteLen; + + union + { + const void* pointer; + uint64_t _64BitField; + } inbuf; + }; + + static inline void init_syncop_in_params(nirio_syncop_in_params_t& param, const void* const buf, const uint32_t len) + { + param.inbuf._64BitField = 0; + param.inbuf.pointer = buf; + param.inbufByteLen = len; + } + + struct nirio_syncop_out_params_t + { + union + { + struct + { + uint32_t value; + } attribute32; + + struct + { + uint64_t value; + } attribute64; + + union + { + struct + { + uint32_t reserved_field_0_0; + } enable; + } reserved_field_0; + + struct + { + union + { + struct + { + uint32_t actualDepth; + uint32_t actualSize; + } config; + struct + { + uint32_t numberRead; + uint32_t numberRemaining; + } read; + struct + { + uint32_t numberRemaining; + } write; + struct + { + union + { + void* pointer; + uint64_t _64BitField; + } elements; + } wait; + } op; + } fifo; + + struct + { + union + { + union + { + uint64_t value64; + uint32_t value32; + uint16_t value16; + uint8_t value8; + } value; + union + { + void* memoryMappedAddress; + uint64_t _64BitField; + } memoryMappedIoWindow; + union + { + uint32_t size; + } memoryMappedIoWindowSize; + }; + } io; + + uint32_t stringLength; + + struct + { + uint32_t reserved_field_1_0; + } reserved_field_1; + + } params; + + uint32_t outbufByteLen; + + union + { + void* pointer; + uint64_t _64BitField; + } outbuf; + }; + + static inline void init_syncop_out_params(nirio_syncop_out_params_t& param, void* buf, uint32_t len) + { + param.outbuf._64BitField = 0; + param.outbuf.pointer = buf; + param.outbufByteLen = len; + } + //------------------------------------------------------- // niriok_proxy_impl_v1 //------------------------------------------------------- @@ -55,11 +444,11 @@ namespace uhd { namespace niusrprio interface_path, _device_handle), status); if (nirio_status_not_fatal(status)) { nirio_status_chain(nirio_driver_iface::rio_ioctl(_device_handle, - nirio_driver_iface::NIRIO_IOCTL_POST_OPEN, + NIRIO_IOCTL_POST_OPEN, NULL, 0, NULL, 0), status); nirio_ioctl_packet_t out(&_interface_num, sizeof(_interface_num), 0); nirio_status_chain(nirio_driver_iface::rio_ioctl(_device_handle, - nirio_driver_iface::NIRIO_IOCTL_GET_IFACE_NUM, + NIRIO_IOCTL_GET_IFACE_NUM, NULL, 0, &out, sizeof(out)), status); @@ -83,7 +472,7 @@ namespace uhd { namespace niusrprio if(nirio_driver_iface::rio_isopen(_device_handle)) { nirio_driver_iface::rio_ioctl( - _device_handle, nirio_driver_iface::NIRIO_IOCTL_PRE_CLOSE, NULL, 0, NULL, 0); + _device_handle, NIRIO_IOCTL_PRE_CLOSE, NULL, 0, NULL, 0); nirio_driver_iface::rio_close(_device_handle); } } @@ -100,18 +489,6 @@ namespace uhd { namespace niusrprio return sync_operation(&in, sizeof(in), &out, sizeof(out)); } - nirio_status niriok_proxy_impl_v1::get_cached_session( - uint32_t& session) - { - READER_LOCK - - nirio_ioctl_packet_t out(&session, sizeof(session), 0); - return nirio_driver_iface::rio_ioctl(_device_handle, - nirio_driver_iface::NIRIO_IOCTL_GET_SESSION, - NULL, 0, - &out, sizeof(out)); - } - nirio_status niriok_proxy_impl_v1::get_version( nirio_version_t type, uint32_t& major, @@ -150,7 +527,7 @@ namespace uhd { namespace niusrprio nirio_ioctl_packet_t out(readBuffer, readBufferLength, 0); nirio_status ioctl_status = nirio_driver_iface::rio_ioctl(_device_handle, - nirio_driver_iface::NIRIO_IOCTL_SYNCOP, + NIRIO_IOCTL_SYNCOP, writeBuffer, writeBufferLength, &out, sizeof(out)); if (nirio_status_fatal(ioctl_status)) return ioctl_status; @@ -282,14 +659,14 @@ namespace uhd { namespace niusrprio nirio_status niriok_proxy_impl_v1::add_fifo_resource(const nirio_fifo_info_t& fifo_info) { - niriok_proxy_impl_v1::nirio_syncop_in_params_t in = {}; - niriok_proxy_impl_v1::nirio_syncop_out_params_t out = {}; + nirio_syncop_in_params_t in = {}; + nirio_syncop_out_params_t out = {}; - in.function = niriok_proxy_impl_v1::NIRIO_FUNC::ADD_RESOURCE; + in.function = NIRIO_FUNC::ADD_RESOURCE; if (fifo_info.direction == OUTPUT_FIFO) - in.subfunction = niriok_proxy_impl_v1::NIRIO_RESOURCE::OUTPUT_FIFO; + in.subfunction = NIRIO_RESOURCE::OUTPUT_FIFO; else - in.subfunction = niriok_proxy_impl_v1::NIRIO_RESOURCE::INPUT_FIFO; + in.subfunction = NIRIO_RESOURCE::INPUT_FIFO; in.params.add.fifoWithDataType.channel = fifo_info.channel; in.params.add.fifoWithDataType.baseAddress = fifo_info.base_addr; @@ -307,7 +684,7 @@ namespace uhd { namespace niusrprio nirio_syncop_in_params_t in = {}; nirio_syncop_out_params_t out = {}; - in.function = niriok_proxy_impl_v1::NIRIO_FUNC::SET_DRIVER_CONFIG; + in.function = NIRIO_FUNC::SET_DRIVER_CONFIG; in.subfunction = 0; return sync_operation(&in, sizeof(in), &out, sizeof(out)); diff --git a/host/lib/transport/nirio/niriok_proxy_impl_v2.cpp b/host/lib/transport/nirio/niriok_proxy_impl_v2.cpp index 748db5cf8..f75de01e1 100644 --- a/host/lib/transport/nirio/niriok_proxy_impl_v2.cpp +++ b/host/lib/transport/nirio/niriok_proxy_impl_v2.cpp @@ -27,9 +27,263 @@ #pragma GCC diagnostic ignored "-Wmissing-field-initializers" #endif +#define IOCTL_TRANSPORT_GET32 IOCTL(0, 0, IOCTL_ACCESS_READ) +#define IOCTL_TRANSPORT_SET32 IOCTL(0, 1, IOCTL_ACCESS_WRITE) +#define IOCTL_TRANSPORT_GET_STRING IOCTL(0, 2, IOCTL_ACCESS_READ) +#define IOCTL_TRANSPORT_SET_STRING IOCTL(0, 3, IOCTL_ACCESS_WRITE) +#define IOCTL_TRANSPORT_RESET IOCTL(1, 1, IOCTL_ACCESS_WRITE) +#define IOCTL_TRANSPORT_ADD_INPUT_FIFO_RESOURCE IOCTL(2, 0, IOCTL_ACCESS_ANY) +#define IOCTL_TRANSPORT_ADD_OUTPUT_FIFO_RESOURCE IOCTL(2, 1, IOCTL_ACCESS_ANY) +#define IOCTL_TRANSPORT_SET_DEVICE_CONFIG IOCTL(2, 3, IOCTL_ACCESS_WRITE) +#define IOCTL_TRANSPORT_FIFO_CONFIG IOCTL(4, 0, IOCTL_ACCESS_ANY) +#define IOCTL_TRANSPORT_FIFO_START IOCTL(4, 1, IOCTL_ACCESS_ANY) +#define IOCTL_TRANSPORT_FIFO_STOP IOCTL(4, 2, IOCTL_ACCESS_ANY) +#define IOCTL_TRANSPORT_FIFO_READ IOCTL(4, 3, IOCTL_ACCESS_READ) +#define IOCTL_TRANSPORT_FIFO_WRITE IOCTL(4, 4, IOCTL_ACCESS_WRITE) +#define IOCTL_TRANSPORT_FIFO_WAIT IOCTL(4, 5, IOCTL_ACCESS_ANY) +#define IOCTL_TRANSPORT_FIFO_GRANT IOCTL(4, 6, IOCTL_ACCESS_ANY) +#define IOCTL_TRANSPORT_FIFO_STOP_ALL IOCTL(4, 7, IOCTL_ACCESS_ANY) +#define IOCTL_TRANSPORT_PEEK64 IOCTL(5, 2, IOCTL_ACCESS_READ) +#define IOCTL_TRANSPORT_PEEK32 IOCTL(5, 3, IOCTL_ACCESS_READ) +#define IOCTL_TRANSPORT_POKE64 IOCTL(5, 6, IOCTL_ACCESS_WRITE) +#define IOCTL_TRANSPORT_POKE32 IOCTL(5, 7, IOCTL_ACCESS_WRITE) +#define IOCTL_TRANSPORT_POST_OPEN IOCTL(8, 0, IOCTL_ACCESS_ANY) +#define IOCTL_TRANSPORT_PRE_CLOSE IOCTL(8, 1, IOCTL_ACCESS_ANY) + namespace uhd { namespace niusrprio { //------------------------------------------------------- + // ioctl param typedefs + //------------------------------------------------------- + typedef struct { + nirio_scalar_type_t scalarType; + nirio_u32_t bitWidth; + nirio_i32_t integerWordLength; + } nirio_fifo_data_type_t; + + typedef struct in_transport_get32 + { + nirio_device_attribute32_t attribute; + int32_t status; + } in_transport_get32_t; + typedef struct out_transport_get32 + { + uint32_t retVal__; + int32_t status; + } out_transport_get32_t; + typedef struct in_transport_set32 + { + nirio_device_attribute32_t attribute; + uint32_t value; + int32_t status; + } in_transport_set32_t; + typedef struct out_transport_set32 + { + int32_t status; + } out_transport_set32_t; + typedef struct out_transport_get_string + { + uint32_t stringLen; + int32_t status; + } out_transport_get_string_t; + typedef struct out_transport_set_string + { + int32_t status; + } out_transport_set_string_t; + typedef struct in_transport_reset + { + int32_t status; + } in_transport_reset_t; + typedef struct out_transport_reset + { + int32_t status; + } out_transport_reset_t; + typedef struct in_transport_add_input_fifo_resource + { + uint32_t channel; + uint32_t baseAddress; + uint32_t depthInSamples; + nirio_fifo_data_type_t dataType; + uint32_t version; + int32_t status; + } in_transport_add_input_fifo_resource_t; + typedef struct out_transport_addInputFifo_resource + { + int32_t status; + } out_transport_add_input_fifo_resource_t; + typedef struct in_transport_addOutputFifo_resource + { + uint32_t channel; + uint32_t baseAddress; + uint32_t depthInSamples; + nirio_fifo_data_type_t dataType; + uint32_t version; + int32_t status; + } in_transport_add_output_fifo_resource_t; + typedef struct out_transport_addOutputFifo_resource + { + int32_t status; + } out_transport_add_output_fifo_resource_t; + typedef struct in_transport_setDevice_config + { + uint32_t attribute; + int32_t status; + } in_transport_set_device_config_t; + typedef struct out_transport_setDevice_config + { + int32_t status; + } out_transport_set_device_config_t; + typedef struct in_transport_fifo_config + { + uint32_t channel; + aligned_uint64_t requestedDepth; + int32_t status; + } in_transport_fifo_config_t; + typedef struct out_transport_fifo_config + { + aligned_uint64_t actualDepth; + aligned_uint64_t actualSize; + int32_t status; + } out_transport_fifo_config_t; + typedef struct in_transport_fifo_start + { + uint32_t channel; + int32_t status; + } in_transport_fifo_start_t; + typedef struct out_transport_fifo_start + { + int32_t status; + } out_transport_fifo_start_t; + typedef struct in_transport_fifo_stop + { + uint32_t channel; + int32_t status; + } in_transport_fifo_stop_t; + typedef struct out_transport_fifo_stop + { + int32_t status; + } out_transport_fifo_stop_t; + typedef struct in_transport_fifo_read + { + uint32_t channel; + aligned_uint64_t buf; + uint32_t numberElements; + nirio_fifo_data_type_t dataType; + uint32_t timeout; + int32_t status; + } in_transport_fifo_read_t; + typedef struct out_transport_fifo_read + { + uint32_t read; + uint32_t remaining; + int32_t status; + } out_transport_fifo_read_t; + typedef struct in_transport_fifo_write + { + uint32_t channel; + aligned_uint64_t buf; + uint32_t numberElements; + nirio_fifo_data_type_t dataType; + uint32_t timeout; + int32_t status; + } in_transport_fifo_write_t; + typedef struct out_transport_fifo_write + { + uint32_t remaining; + int32_t status; + } out_transport_fifo_write_t; + typedef struct in_transport_fifo_wait + { + uint32_t channel; + aligned_uint64_t elementsRequested; + nirio_fifo_data_type_t dataType; + bool output; + uint32_t timeout; + int32_t status; + } in_transport_fifo_wait_t; + typedef struct out_transport_fifo_wait + { + aligned_uint64_t elements; + aligned_uint64_t elementsAcquired; + aligned_uint64_t elementsRemaining; + int32_t status; + } out_transport_fifo_wait_t; + typedef struct in_transport_fifo_grant + { + uint32_t channel; + aligned_uint64_t elements; + int32_t status; + } in_transport_fifo_grant_t; + typedef struct out_transport_fifo_grant + { + int32_t status; + } out_transport_fifo_grant_t; + typedef struct in_transport_fifoStop_all + { + int32_t status; + } in_transport_fifo_stop_all_t; + typedef struct out_transport_fifoStop_all + { + int32_t status; + } out_transport_fifo_stop_all_t; + typedef struct in_transport_peek64 + { + uint32_t offset; + int32_t status; + } in_transport_peek64_t; + typedef struct out_transport_peek64 + { + aligned_uint64_t retVal__; + int32_t status; + } out_transport_peek64_t; + typedef struct in_transport_peek32 + { + uint32_t offset; + int32_t status; + } in_transport_peek32_t; + typedef struct out_transport_peek32 + { + uint32_t retVal__; + int32_t status; + } out_transport_peek32_t; + typedef struct in_transport_poke64 + { + uint32_t offset; + aligned_uint64_t value; + int32_t status; + } in_transport_poke64_t; + typedef struct out_transport_poke64 + { + int32_t status; + } out_transport_poke64_t; + typedef struct in_transport_poke32 + { + uint32_t offset; + uint32_t value; + int32_t status; + } in_transport_poke32_t; + typedef struct out_transport_poke32 + { + int32_t status; + } out_transport_poke32_t; + typedef struct in_transport_post_open + { + int32_t status; + } in_transport_post_open_t; + typedef struct out_transport_post_open + { + int32_t status; + } out_transport_post_open_t; + typedef struct in_transport_pre_close + { + int32_t status; + } in_transport_pre_close_t; + typedef struct out_transport_pre_close + { + int32_t status; + } out_transport_pre_close_t; + + //------------------------------------------------------- // niriok_proxy_impl_v2 //------------------------------------------------------- niriok_proxy_impl_v2::niriok_proxy_impl_v2() @@ -110,18 +364,6 @@ namespace uhd { namespace niusrprio return out.status; } - nirio_status niriok_proxy_impl_v2::get_cached_session( - uint32_t& session) - { - READER_LOCK - - nirio_ioctl_packet_t out(&session, sizeof(session), 0); - return nirio_driver_iface::rio_ioctl(_device_handle, - nirio_driver_iface::NIRIO_IOCTL_GET_SESSION, - NULL, 0, - &out, sizeof(out)); - } - nirio_status niriok_proxy_impl_v2::get_version( nirio_version_t type, uint32_t& major, @@ -580,7 +822,7 @@ namespace uhd { namespace niusrprio out_transport_fifo_read_t out = {}; in.channel = channel; - in.buf = reinterpret_cast<tAlignedU64>(buffer); + in.buf = reinterpret_cast<aligned_uint64_t>(buffer); in.numberElements = elements_to_read; in.dataType.scalarType = map_int_to_scalar_type(scalar_type); in.dataType.bitWidth = bit_width; @@ -620,7 +862,7 @@ namespace uhd { namespace niusrprio out_transport_fifo_write_t out = {}; in.channel = channel; - in.buf = reinterpret_cast<tAlignedU64>(buffer); + in.buf = reinterpret_cast<aligned_uint64_t>(buffer); in.numberElements = elements_to_write; in.dataType.scalarType = map_int_to_scalar_type(scalar_type); in.dataType.bitWidth = bit_width; diff --git a/host/lib/transport/nirio/rpc/rpc_client.cpp b/host/lib/transport/nirio/rpc/rpc_client.cpp index cf8e9c1a9..bbaf9f235 100644 --- a/host/lib/transport/nirio/rpc/rpc_client.cpp +++ b/host/lib/transport/nirio/rpc/rpc_client.cpp @@ -52,7 +52,7 @@ rpc_client::rpc_client ( //- address_configured: Only return addresses if a non-loopback address is configured for the system. //- numeric_host: No name resolution should be attempted for host //- numeric_service: No name resolution should be attempted for service - tcp::resolver::query::flags query_flags; + tcp::resolver::query::flags query_flags(tcp::resolver::query::passive); tcp::resolver::query query(tcp::v4(), server, port, query_flags); tcp::resolver::iterator iterator = resolver.resolve(query); diff --git a/host/lib/transport/super_recv_packet_handler.hpp b/host/lib/transport/super_recv_packet_handler.hpp index 5c84327a4..0f1f7ff3a 100644 --- a/host/lib/transport/super_recv_packet_handler.hpp +++ b/host/lib/transport/super_recv_packet_handler.hpp @@ -147,7 +147,7 @@ public: */ void set_xport_chan_get_buff(const size_t xport_chan, const get_buff_type &get_buff, const bool flush = false){ if (flush){ - while (get_buff(0.0)); + while (get_buff(0.0)) {}; } _props.at(xport_chan).get_buff = get_buff; } @@ -560,6 +560,11 @@ private: _props[index].handle_overflow(); curr_info.metadata = metadata; UHD_MSG(fastpath) << "O"; + + // Not sending flow control would cause timeouts due to source flow control locking up + if(_props[index].handle_flowctrl) { + _props[index].handle_flowctrl(next_info[index].ifpi.packet_count); + } } return; diff --git a/host/lib/transport/udp_zero_copy.cpp b/host/lib/transport/udp_zero_copy.cpp index adc7d5585..70fb5b552 100644 --- a/host/lib/transport/udp_zero_copy.cpp +++ b/host/lib/transport/udp_zero_copy.cpp @@ -87,7 +87,10 @@ public: if (wait_for_recv_ready(_sock_fd, timeout)){ _len = ::recv(_sock_fd, (char *)_mem, _frame_size, 0); - UHD_ASSERT_THROW(_len > 0); // TODO: Handle case of recv error + if (_len == 0) + throw uhd::io_error("socket closed"); + if (_len < 0) + throw uhd::io_error(str(boost::format("recv error on socket: %s") % strerror(errno))); index++; //advances the caller's buffer return make(this, _mem, size_t(_len)); } @@ -126,6 +129,10 @@ public: boost::this_thread::sleep(boost::posix_time::microseconds(1)); continue; //try to send again } + if (ret == -1) + { + throw uhd::io_error(str(boost::format("send error on socket: %s") % strerror(errno))); + } UHD_ASSERT_THROW(ret == ssize_t(size())); } _claimer.release(); diff --git a/host/lib/types/CMakeLists.txt b/host/lib/types/CMakeLists.txt index f19043c1e..ebb788183 100644 --- a/host/lib/types/CMakeLists.txt +++ b/host/lib/types/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2011-2013 Ettus Research LLC +# Copyright 2011-2013,2015 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 @@ -86,8 +86,22 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/ranges.cpp ${CMAKE_CURRENT_SOURCE_DIR}/sensors.cpp ${CMAKE_CURRENT_SOURCE_DIR}/serial.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/sid.cpp ${CMAKE_CURRENT_SOURCE_DIR}/time_spec.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tune.cpp ${CMAKE_CURRENT_SOURCE_DIR}/types.cpp ${CMAKE_CURRENT_SOURCE_DIR}/wb_iface.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/byte_vector.cpp ) + +IF(ENABLE_C_API) + LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/metadata_c.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ranges_c.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/sensors_c.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/string_vector_c.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tune_c.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/usrp_info_c.cpp + ) +ENDIF() diff --git a/host/lib/types/byte_vector.cpp b/host/lib/types/byte_vector.cpp new file mode 100644 index 000000000..071cdb8cb --- /dev/null +++ b/host/lib/types/byte_vector.cpp @@ -0,0 +1,42 @@ +// +// Copyright 2015 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 <boost/foreach.hpp> + +#include <uhd/types/byte_vector.hpp> + +namespace uhd{ + +std::string bytes_to_string(const byte_vector_t &bytes){ + std::string out; + BOOST_FOREACH(boost::uint8_t byte, bytes){ + if (byte < 32 or byte > 127) return out; + out += byte; + } + return out; +} + +byte_vector_t string_to_bytes(const std::string &str, size_t max_length){ + byte_vector_t bytes; + for (size_t i = 0; i < std::min(str.size(), max_length); i++){ + bytes.push_back(str[i]); + } + if (bytes.size() < max_length - 1) bytes.push_back('\0'); + return bytes; +} + +} /* namespace uhd */ diff --git a/host/lib/types/filters.cpp b/host/lib/types/filters.cpp new file mode 100644 index 000000000..4ee06491f --- /dev/null +++ b/host/lib/types/filters.cpp @@ -0,0 +1,74 @@ +// +// Copyright 2015 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/filters.hpp> + +using namespace uhd; + +std::ostream& uhd::operator<<(std::ostream& os, filter_info_base& f) +{ + return os << f.to_pp_string(); +} + +std::string filter_info_base::to_pp_string() +{ + std::ostringstream os; + os << "[filter_info_base]" << std::endl; + switch(_type){ + case ANALOG_LOW_PASS: + os << "type: " << "Analog Low-pass" << std::endl; + break; + case ANALOG_BAND_PASS: + os << "type: " << "Analog Band-pass" << std::endl; + break; + case DIGITAL_I16: + os << "type: " << "Digital (i16)" << std::endl; + break; + case DIGITAL_FIR_I16: + os << "type: " << "Digital FIR (i16)" << std::endl; + break; + default: + os << "type: " << "Unknown type!" << std::endl; + break; + } + + os << "bypass enable: " << _bypass << std::endl + <<"position index: " << _position_index << std::endl; + + std::string str = os.str(); + return str; +} + +std::string analog_filter_base::to_pp_string() +{ + std::ostringstream os; + os << filter_info_base::to_pp_string() << + "\t[analog_filter_base]" << std::endl << + "\tdesc: " << _analog_type << std::endl; + return std::string(os.str()); + +} + +std::string analog_filter_lp::to_pp_string() +{ + std::ostringstream os; + os << analog_filter_base::to_pp_string() << + "\t\t[analog_filter_lp]" << std::endl << + "\t\tcutoff: " << _cutoff << std::endl << + "\t\trolloff: " << _rolloff << std::endl; + return std::string(os.str()); +} diff --git a/host/lib/types/metadata_c.cpp b/host/lib/types/metadata_c.cpp new file mode 100644 index 000000000..96f43d140 --- /dev/null +++ b/host/lib/types/metadata_c.cpp @@ -0,0 +1,315 @@ +// +// Copyright 2015 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/metadata.h> + +#include <uhd/types/time_spec.hpp> + +#include <string.h> + +/* + * RX metadata + */ + +uhd_error uhd_rx_metadata_make( + uhd_rx_metadata_handle* handle +){ + UHD_SAFE_C( + *handle = new uhd_rx_metadata_t; + ) +} + +uhd_error uhd_rx_metadata_free( + uhd_rx_metadata_handle* handle +){ + UHD_SAFE_C( + delete *handle; + *handle = NULL; + ) +} + +uhd_error uhd_rx_metadata_has_time_spec( + uhd_rx_metadata_handle h, + bool *result_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *result_out = h->rx_metadata_cpp.has_time_spec; + ) +} + +uhd_error uhd_rx_metadata_time_spec( + uhd_rx_metadata_handle h, + time_t *full_secs_out, + double *frac_secs_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::time_spec_t time_spec_cpp = h->rx_metadata_cpp.time_spec; + *full_secs_out = time_spec_cpp.get_full_secs(); + *frac_secs_out = time_spec_cpp.get_frac_secs(); + ) +} + +uhd_error uhd_rx_metadata_more_fragments( + uhd_rx_metadata_handle h, + bool *result_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *result_out = h->rx_metadata_cpp.more_fragments; + ) +} + +uhd_error uhd_rx_metadata_fragment_offset( + uhd_rx_metadata_handle h, + size_t *fragment_offset_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *fragment_offset_out = h->rx_metadata_cpp.fragment_offset; + ) +} + +uhd_error uhd_rx_metadata_start_of_burst( + uhd_rx_metadata_handle h, + bool *result_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *result_out = h->rx_metadata_cpp.start_of_burst; + ) +} + +uhd_error uhd_rx_metadata_end_of_burst( + uhd_rx_metadata_handle h, + bool *result_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *result_out = h->rx_metadata_cpp.end_of_burst; + ) +} + +uhd_error uhd_rx_metadata_out_of_sequence( + uhd_rx_metadata_handle h, + bool *result_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *result_out = h->rx_metadata_cpp.out_of_sequence; + ) +} + +uhd_error uhd_rx_metadata_to_pp_string( + uhd_rx_metadata_handle h, + char* pp_string_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string pp_string_cpp = h->rx_metadata_cpp.to_pp_string(); + memset(pp_string_out, '\0', strbuffer_len); + strncpy(pp_string_out, pp_string_cpp.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_rx_metadata_error_code( + uhd_rx_metadata_handle h, + uhd_rx_metadata_error_code_t *error_code_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *error_code_out = uhd_rx_metadata_error_code_t(h->rx_metadata_cpp.error_code); + ) +} + +uhd_error uhd_rx_metadata_strerror( + uhd_rx_metadata_handle h, + char* strerror_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string strerror_cpp = h->rx_metadata_cpp.strerror(); + memset(strerror_out, '\0', strbuffer_len); + strncpy(strerror_out, strerror_cpp.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_rx_metadata_last_error( + uhd_rx_metadata_handle h, + char* error_out, + size_t strbuffer_len +){ + UHD_SAFE_C( + memset(error_out, '\0', strbuffer_len); + strncpy(error_out, h->last_error.c_str(), strbuffer_len); + ) +} + +/* + * TX metadata + */ + +uhd_error uhd_tx_metadata_make( + uhd_tx_metadata_handle* handle, + bool has_time_spec, + time_t full_secs, + double frac_secs, + bool start_of_burst, + bool end_of_burst +){ + UHD_SAFE_C( + *handle = new uhd_tx_metadata_t; + (*handle)->tx_metadata_cpp.has_time_spec = has_time_spec; + if(has_time_spec){ + (*handle)->tx_metadata_cpp.time_spec = uhd::time_spec_t(full_secs, frac_secs); + } + (*handle)->tx_metadata_cpp.start_of_burst = start_of_burst; + (*handle)->tx_metadata_cpp.end_of_burst = end_of_burst; + ) +} + +uhd_error uhd_tx_metadata_free( + uhd_tx_metadata_handle* handle +){ + UHD_SAFE_C( + delete *handle; + *handle = NULL; + ) +} + +uhd_error uhd_tx_metadata_has_time_spec( + uhd_tx_metadata_handle h, + bool *result_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *result_out = h->tx_metadata_cpp.has_time_spec; + ) +} + +uhd_error uhd_tx_metadata_time_spec( + uhd_tx_metadata_handle h, + time_t *full_secs_out, + double *frac_secs_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::time_spec_t time_spec_cpp = h->tx_metadata_cpp.time_spec; + *full_secs_out = time_spec_cpp.get_full_secs(); + *frac_secs_out = time_spec_cpp.get_frac_secs(); + ) +} + +uhd_error uhd_tx_metadata_start_of_burst( + uhd_tx_metadata_handle h, + bool *result_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *result_out = h->tx_metadata_cpp.start_of_burst; + ) +} + +uhd_error uhd_tx_metadata_end_of_burst( + uhd_tx_metadata_handle h, + bool *result_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *result_out = h->tx_metadata_cpp.end_of_burst; + ) +} + +uhd_error uhd_tx_metadata_last_error( + uhd_tx_metadata_handle h, + char* error_out, + size_t strbuffer_len +){ + UHD_SAFE_C( + memset(error_out, '\0', strbuffer_len); + strncpy(error_out, h->last_error.c_str(), strbuffer_len); + ) +} + +/* + * Async metadata + */ + +uhd_error uhd_async_metadata_make( + uhd_async_metadata_handle* handle +){ + UHD_SAFE_C( + *handle = new uhd_async_metadata_t; + ) +} + +uhd_error uhd_async_metadata_free( + uhd_async_metadata_handle* handle +){ + UHD_SAFE_C( + delete *handle; + *handle = NULL; + ) +} + +uhd_error uhd_async_metadata_channel( + uhd_async_metadata_handle h, + size_t *channel_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *channel_out = h->async_metadata_cpp.channel; + ) +} + +uhd_error uhd_async_metadata_has_time_spec( + uhd_async_metadata_handle h, + bool *result_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *result_out = h->async_metadata_cpp.has_time_spec; + ) +} + +uhd_error uhd_async_metadata_time_spec( + uhd_async_metadata_handle h, + time_t *full_secs_out, + double *frac_secs_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::time_spec_t time_spec_cpp = h->async_metadata_cpp.time_spec; + *full_secs_out = time_spec_cpp.get_full_secs(); + *frac_secs_out = time_spec_cpp.get_frac_secs(); + ) +} + +uhd_error uhd_async_metadata_event_code( + uhd_async_metadata_handle h, + uhd_async_metadata_event_code_t *event_code_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *event_code_out = uhd_async_metadata_event_code_t(h->async_metadata_cpp.event_code); + ) +} + +uhd_error uhd_async_metadata_user_payload( + uhd_async_metadata_handle h, + uint32_t user_payload_out[4] +){ + UHD_SAFE_C_SAVE_ERROR(h, + memcpy(user_payload_out, h->async_metadata_cpp.user_payload, 4*sizeof(uint32_t)); + ) +} + +uhd_error uhd_async_metadata_last_error( + uhd_async_metadata_handle h, + char* error_out, + size_t strbuffer_len +){ + UHD_SAFE_C( + memset(error_out, '\0', strbuffer_len); + strncpy(error_out, h->last_error.c_str(), strbuffer_len); + ) +} diff --git a/host/lib/types/ranges_c.cpp b/host/lib/types/ranges_c.cpp new file mode 100644 index 000000000..0c0df24ce --- /dev/null +++ b/host/lib/types/ranges_c.cpp @@ -0,0 +1,162 @@ +// +// Copyright 2015 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/ranges.h> + +#include <string.h> + +/* + * uhd::range_t + */ +uhd_error uhd_range_to_pp_string( + const uhd_range_t *range, + char* pp_string_out, + size_t strbuffer_len +){ + UHD_SAFE_C( + uhd::range_t range_cpp = uhd_range_c_to_cpp(range); + std::string pp_string_cpp = range_cpp.to_pp_string(); + + memset(pp_string_out, '\0', strbuffer_len); + strncpy(pp_string_out, pp_string_cpp.c_str(), strbuffer_len); + ) +} + +uhd::range_t uhd_range_c_to_cpp( + const uhd_range_t *range_c +){ + return uhd::range_t(range_c->start, range_c->stop, range_c->step); +} + +void uhd_range_cpp_to_c( + const uhd::range_t &range_cpp, + uhd_range_t *range_c +){ + range_c->start = range_cpp.start(); + range_c->stop = range_cpp.stop(); + range_c->step = range_cpp.step(); +} + +/* + * uhd::meta_range_t + */ +uhd_error uhd_meta_range_make( + uhd_meta_range_handle* h +){ + UHD_SAFE_C( + (*h) = new uhd_meta_range_t; + ) +} + +uhd_error uhd_meta_range_free( + uhd_meta_range_handle* h +){ + UHD_SAFE_C( + delete (*h); + (*h) = NULL; + ) +} + +uhd_error uhd_meta_range_start( + uhd_meta_range_handle h, + double *start_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *start_out = h->meta_range_cpp.start(); + ) +} + +uhd_error uhd_meta_range_stop( + uhd_meta_range_handle h, + double *stop_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *stop_out = h->meta_range_cpp.stop(); + ) +} + +uhd_error uhd_meta_range_step( + uhd_meta_range_handle h, + double *step_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *step_out = h->meta_range_cpp.step(); + ) +} + +uhd_error uhd_meta_range_clip( + uhd_meta_range_handle h, + double value, + bool clip_step, + double *result_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *result_out = h->meta_range_cpp.clip(value, clip_step); + ) +} + +uhd_error uhd_meta_range_size( + uhd_meta_range_handle h, + size_t *size_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *size_out = h->meta_range_cpp.size(); + ) +} + +uhd_error uhd_meta_range_push_back( + uhd_meta_range_handle h, + const uhd_range_t *range +){ + UHD_SAFE_C_SAVE_ERROR(h, + h->meta_range_cpp.push_back(uhd_range_c_to_cpp(range)); + ) +} + +uhd_error uhd_meta_range_at( + uhd_meta_range_handle h, + size_t num, + uhd_range_t *range_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd_range_cpp_to_c(h->meta_range_cpp.at(num), + range_out); + ) +} + +uhd_error uhd_meta_range_to_pp_string( + uhd_meta_range_handle h, + char* pp_string_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string pp_string_cpp = h->meta_range_cpp.to_pp_string(); + memset(pp_string_out, '\0', strbuffer_len); + strncpy(pp_string_out, pp_string_cpp.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_meta_range_last_error( + uhd_meta_range_handle h, + char* error_out, + size_t strbuffer_len +){ + UHD_SAFE_C( + memset(error_out, '\0', strbuffer_len); + strncpy(error_out, h->last_error.c_str(), strbuffer_len); + ) +} diff --git a/host/lib/types/sensors_c.cpp b/host/lib/types/sensors_c.cpp new file mode 100644 index 000000000..f1976c102 --- /dev/null +++ b/host/lib/types/sensors_c.cpp @@ -0,0 +1,228 @@ +// +// Copyright 2015 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/sensors.h> + +#include <boost/lexical_cast.hpp> + +#include <stdexcept> +#include <string.h> +#include <string> + +uhd_error uhd_sensor_value_make_from_bool( + uhd_sensor_value_handle* h, + const char* name, + bool value, + const char* utrue, + const char* ufalse +){ + try{ + *h = new uhd_sensor_value_t; + } + catch(...){ + return UHD_ERROR_UNKNOWN; + } + + UHD_SAFE_C_SAVE_ERROR((*h), + (*h)->sensor_value_cpp = new uhd::sensor_value_t(name, + value, + utrue, + ufalse); + ) +} + +uhd_error uhd_sensor_value_make_from_int( + uhd_sensor_value_handle* h, + const char* name, + int value, + const char* unit, + const char* formatter +){ + try{ + *h = new uhd_sensor_value_t; + } + catch(...){ + return UHD_ERROR_UNKNOWN; + } + + UHD_SAFE_C_SAVE_ERROR((*h), + std::string fmt(formatter); + if(fmt.empty()){ + (*h)->sensor_value_cpp = new uhd::sensor_value_t(name, + value, + unit); + } + else{ + (*h)->sensor_value_cpp = new uhd::sensor_value_t(name, + value, + unit, + fmt); + } + ) +} + +uhd_error uhd_sensor_value_make_from_realnum( + uhd_sensor_value_handle* h, + const char* name, + double value, + const char* unit, + const char* formatter +){ + try{ + *h = new uhd_sensor_value_t; + } + catch(...){ + return UHD_ERROR_UNKNOWN; + } + + UHD_SAFE_C_SAVE_ERROR((*h), + std::string fmt(formatter); + if(fmt.empty()){ + (*h)->sensor_value_cpp = new uhd::sensor_value_t(name, + value, + unit); + } + else{ + (*h)->sensor_value_cpp = new uhd::sensor_value_t(name, + value, + unit, + fmt); + } + ) +} + +uhd_error uhd_sensor_value_make_from_string( + uhd_sensor_value_handle* h, + const char* name, + const char* value, + const char* unit +){ + try{ + *h = new uhd_sensor_value_t; + } + catch(...){ + return UHD_ERROR_UNKNOWN; + } + + UHD_SAFE_C_SAVE_ERROR((*h), + (*h)->sensor_value_cpp = new uhd::sensor_value_t(name, + value, + unit); + ) +} + +uhd_error uhd_sensor_value_free( + uhd_sensor_value_handle *h +){ + UHD_SAFE_C( + delete (*h)->sensor_value_cpp; + delete *h; + *h = NULL; + ) +} + +uhd_error uhd_sensor_value_to_bool( + uhd_sensor_value_handle h, + bool *value_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *value_out = h->sensor_value_cpp->to_bool(); + ) +} + +uhd_error uhd_sensor_value_to_int( + uhd_sensor_value_handle h, + int *value_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *value_out = h->sensor_value_cpp->to_int(); + ) +} + +uhd_error uhd_sensor_value_to_realnum( + uhd_sensor_value_handle h, + double *value_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *value_out = h->sensor_value_cpp->to_real(); + ) +} + +uhd_error uhd_sensor_value_name( + uhd_sensor_value_handle h, + char* name_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + memset(name_out, '\0', strbuffer_len); + strncpy(name_out, h->sensor_value_cpp->name.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_sensor_value_value( + uhd_sensor_value_handle h, + char* value_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + memset(value_out, '\0', strbuffer_len); + strncpy(value_out, h->sensor_value_cpp->value.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_sensor_value_unit( + uhd_sensor_value_handle h, + char* unit_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + memset(unit_out, '\0', strbuffer_len); + strncpy(unit_out, h->sensor_value_cpp->unit.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_sensor_value_data_type( + uhd_sensor_value_handle h, + uhd_sensor_value_data_type_t *data_type_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *data_type_out = uhd_sensor_value_data_type_t(h->sensor_value_cpp->type); + ) +} + +uhd_error uhd_sensor_value_to_pp_string( + uhd_sensor_value_handle h, + char* pp_string_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string pp_string_cpp = h->sensor_value_cpp->to_pp_string(); + memset(pp_string_out, '\0', strbuffer_len); + strncpy(pp_string_out, pp_string_cpp.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_sensor_value_last_error( + uhd_sensor_value_handle h, + char* error_out, + size_t strbuffer_len +){ + UHD_SAFE_C( + memset(error_out, '\0', strbuffer_len); + strncpy(error_out, h->last_error.c_str(), strbuffer_len); + ) +} diff --git a/host/lib/types/sid.cpp b/host/lib/types/sid.cpp new file mode 100644 index 000000000..2fc3781cf --- /dev/null +++ b/host/lib/types/sid.cpp @@ -0,0 +1,153 @@ +// +// Copyright 2014 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 <boost/format.hpp> +#include <boost/regex.hpp> +#include <boost/lexical_cast.hpp> +#include <uhd/exception.hpp> +#include <uhd/types/sid.hpp> +#include <uhd/utils/cast.hpp> + +using namespace uhd; + +sid_t::sid_t() + : _sid(0x0000), _set(false) +{ +} + +sid_t::sid_t(boost::uint32_t sid) + : _sid(sid), _set(true) +{ +} + +sid_t::sid_t(boost::uint8_t src_addr, boost::uint8_t src_ep, boost::uint8_t dst_addr, boost::uint8_t dst_ep) + : _sid(0x0000), _set(true) +{ + set_src_addr(src_addr); + set_src_endpoint(src_ep); + set_dst_addr(dst_addr); + set_dst_endpoint(dst_ep); +} + +sid_t::sid_t(const std::string &sid_str) + : _sid(0x0000), _set(false) +{ + set_from_str(sid_str); +} + +std::string sid_t::to_pp_string() const +{ + if (not _set) { + return "x.x>x.x"; + } + return str(boost::format("%d.%d>%d.%d") + % get_src_addr() + % get_src_endpoint() + % get_dst_addr() + % get_dst_endpoint() + ); +} + +std::string sid_t::to_pp_string_hex() const +{ + if (not _set) { + return "xx:xx>xx:xx"; + } + return str(boost::format("%02x:%02x>%02x:%02x") + % get_src_addr() + % get_src_endpoint() + % get_dst_addr() + % get_dst_endpoint() + ); +} + + +void sid_t::set_sid(boost::uint32_t new_sid) +{ + _set = true; + _sid = new_sid; +} + +void sid_t::set_from_str(const std::string &sid_str) +{ + const std::string dec_regex = "(\\d{1,3})\\.(\\d{1,3})[.:/><](\\d{1,3})\\.(\\d{1,3})"; + const std::string hex_regex = "([[:xdigit:]]{2}):([[:xdigit:]]{2})[.:/><]([[:xdigit:]]{2}):([[:xdigit:]]{2})"; + + boost::cmatch matches; + if (boost::regex_match(sid_str.c_str(), matches, boost::regex(dec_regex))) { + set_src_addr(boost::lexical_cast<size_t>(matches[1])); + set_src_endpoint(boost::lexical_cast<size_t>(matches[2])); + set_dst_addr(boost::lexical_cast<size_t>(matches[3])); + set_dst_endpoint(boost::lexical_cast<size_t>(matches[4])); + return; + } + + if (boost::regex_match(sid_str.c_str(), matches, boost::regex(hex_regex))) { + set_src_addr(uhd::cast::hexstr_cast<size_t>(matches[1])); + set_src_endpoint(uhd::cast::hexstr_cast<size_t>(matches[2])); + set_dst_addr(uhd::cast::hexstr_cast<size_t>(matches[3])); + set_dst_endpoint(uhd::cast::hexstr_cast<size_t>(matches[4])); + return; + } + + throw uhd::value_error(str(boost::format("Invalid SID representation: %s") % sid_str)); +} + +void sid_t::set_src(boost::uint32_t new_addr) { + set_sid((_sid & 0x0000FFFF) | ((new_addr & 0xFFFF) << 16)); +} + +void sid_t::set_dst(boost::uint32_t new_addr) { + set_sid((_sid & 0xFFFF0000) | (new_addr & 0xFFFF)); +} + +void sid_t::set_src_addr(boost::uint32_t new_addr) { + set_sid((_sid & 0x00FFFFFF) | ((new_addr & 0xFF) << 24)); +} + +void sid_t::set_src_endpoint(boost::uint32_t new_addr) { + set_sid((_sid & 0xFF00FFFF) | ((new_addr & 0xFF) << 16)); +} + +void sid_t::set_dst_addr(boost::uint32_t new_addr) { + set_sid((_sid & 0xFFFF00FF) | ((new_addr & 0xFF) << 8)); +} + +void sid_t::set_dst_endpoint(boost::uint32_t new_addr) { + set_sid((_sid & 0xFFFFFF00) | ((new_addr & 0xFF) << 0)); +} + +void sid_t::set_dst_xbarport(boost::uint32_t new_xbarport) +{ + set_sid((_sid & 0xFFFFFF0F) | ((new_xbarport & 0xF) << 4)); +} + +void sid_t::set_dst_blockport(boost::uint32_t new_blockport) +{ + set_sid((_sid & 0xFFFFFFF0) | ((new_blockport & 0xF) << 0)); +} + +sid_t sid_t::reversed() +{ + return sid_t((get_dst() << 16) | get_src()); +} + +void sid_t::reverse() +{ + set_sid((get_dst() << 16) | get_src()); +} + diff --git a/host/lib/types/string_vector_c.cpp b/host/lib/types/string_vector_c.cpp new file mode 100644 index 000000000..b50c7cdff --- /dev/null +++ b/host/lib/types/string_vector_c.cpp @@ -0,0 +1,80 @@ +// +// Copyright 2015 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/string_vector.h> + +#include <string.h> + +uhd_error uhd_string_vector_make( + uhd_string_vector_handle *h +){ + UHD_SAFE_C( + (*h) = new uhd_string_vector_t; + ) +} + +uhd_error uhd_string_vector_free( + uhd_string_vector_handle *h +){ + UHD_SAFE_C( + delete (*h); + (*h) = NULL; + ) +} + +uhd_error uhd_string_vector_push_back( + uhd_string_vector_handle *h, + const char* value +){ + UHD_SAFE_C_SAVE_ERROR((*h), + (*h)->string_vector_cpp.push_back(value); + ) +} + +uhd_error uhd_string_vector_at( + uhd_string_vector_handle h, + size_t index, + char* value_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + memset(value_out, '\0', strbuffer_len); + + const std::string& value_cpp = h->string_vector_cpp.at(index); + strncpy(value_out, value_cpp.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_string_vector_size( + uhd_string_vector_handle h, + size_t *size_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *size_out = h->string_vector_cpp.size(); + ) +} + +uhd_error uhd_string_vector_last_error( + uhd_string_vector_handle h, + char* error_out, + size_t strbuffer_len +){ + UHD_SAFE_C( + memset(error_out, '\0', strbuffer_len); + strncpy(error_out, h->last_error.c_str(), strbuffer_len); + ) +} diff --git a/host/lib/types/tune_c.cpp b/host/lib/types/tune_c.cpp new file mode 100644 index 000000000..c62935cb8 --- /dev/null +++ b/host/lib/types/tune_c.cpp @@ -0,0 +1,76 @@ +// +// Copyright 2015 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/tune_request.h> +#include <uhd/types/tune_result.h> + +#include <boost/format.hpp> + +#include <cstdlib> +#include <cstring> +#include <iostream> + +/* + * Tune request + */ + +uhd::tune_request_t uhd_tune_request_c_to_cpp(uhd_tune_request_t *tune_request_c){ + uhd::tune_request_t tune_request_cpp; + + tune_request_cpp.target_freq = tune_request_c->target_freq; + tune_request_cpp.rf_freq_policy = uhd::tune_request_t::policy_t(tune_request_c->rf_freq_policy); + tune_request_cpp.rf_freq = tune_request_c->rf_freq; + tune_request_cpp.dsp_freq_policy = uhd::tune_request_t::policy_t(tune_request_c->dsp_freq_policy); + tune_request_cpp.dsp_freq = tune_request_c->dsp_freq; + + std::string args_cpp = (tune_request_c->args) ? tune_request_c->args : std::string(""); + tune_request_cpp.args = uhd::device_addr_t(args_cpp); + + return tune_request_cpp; +} + +/* + * Tune result + */ + +void uhd_tune_result_to_pp_string(uhd_tune_result_t *tune_result_c, + char* pp_string_out, size_t strbuffer_len){ + std::string pp_string_cpp = uhd_tune_result_c_to_cpp(tune_result_c).to_pp_string(); + memset(pp_string_out, '\0', strbuffer_len); + strncpy(pp_string_out, pp_string_cpp.c_str(), strbuffer_len); +} + +uhd::tune_result_t uhd_tune_result_c_to_cpp(uhd_tune_result_t *tune_result_c){ + uhd::tune_result_t tune_result_cpp; + + tune_result_cpp.clipped_rf_freq = tune_result_c->clipped_rf_freq; + tune_result_cpp.target_rf_freq = tune_result_c->target_rf_freq; + tune_result_cpp.actual_rf_freq = tune_result_c->actual_rf_freq; + tune_result_cpp.target_dsp_freq = tune_result_c->target_dsp_freq; + tune_result_cpp.actual_dsp_freq = tune_result_c->actual_dsp_freq; + + return tune_result_cpp; +} + +void uhd_tune_result_cpp_to_c(const uhd::tune_result_t &tune_result_cpp, + uhd_tune_result_t *tune_result_c){ + tune_result_c->clipped_rf_freq = tune_result_cpp.clipped_rf_freq; + tune_result_c->target_rf_freq = tune_result_cpp.target_rf_freq; + tune_result_c->actual_rf_freq = tune_result_cpp.actual_rf_freq; + tune_result_c->target_dsp_freq = tune_result_cpp.target_dsp_freq; + tune_result_c->actual_dsp_freq = tune_result_cpp.actual_dsp_freq; +} diff --git a/host/lib/types/usrp_info_c.cpp b/host/lib/types/usrp_info_c.cpp new file mode 100644 index 000000000..77354d901 --- /dev/null +++ b/host/lib/types/usrp_info_c.cpp @@ -0,0 +1,44 @@ +// +// Copyright 2015 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/usrp_info.h> + +uhd_error uhd_usrp_rx_info_free(uhd_usrp_rx_info_t *rx_info){ + free(rx_info->mboard_id); + free(rx_info->mboard_name); + free(rx_info->mboard_serial); + free(rx_info->rx_id); + free(rx_info->rx_subdev_name); + free(rx_info->rx_subdev_spec); + free(rx_info->rx_serial); + free(rx_info->rx_antenna); + + return UHD_ERROR_NONE; +} + +uhd_error uhd_usrp_tx_info_free(uhd_usrp_tx_info_t *tx_info){ + free(tx_info->mboard_id); + free(tx_info->mboard_name); + free(tx_info->mboard_serial); + free(tx_info->tx_id); + free(tx_info->tx_subdev_name); + free(tx_info->tx_subdev_spec); + free(tx_info->tx_serial); + free(tx_info->tx_antenna); + + return UHD_ERROR_NONE; +} diff --git a/host/lib/usrp/CMakeLists.txt b/host/lib/usrp/CMakeLists.txt index f6788b5ef..5c9592970 100644 --- a/host/lib/usrp/CMakeLists.txt +++ b/host/lib/usrp/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2013 Ettus Research LLC +# Copyright 2010-2015 Ettus Research LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -18,6 +18,10 @@ ######################################################################## # This file included, use CMake directory variables ######################################################################## +find_package(GPSD) + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/dboard_base.cpp ${CMAKE_CURRENT_SOURCE_DIR}/dboard_eeprom.cpp @@ -30,6 +34,24 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/subdev_spec.cpp ) +IF(ENABLE_C_API) + LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/dboard_eeprom_c.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/mboard_eeprom_c.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/subdev_spec_c.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/usrp_c.cpp + ) +ENDIF(ENABLE_C_API) + +LIBUHD_REGISTER_COMPONENT("GPSD" ENABLE_GPSD OFF "ENABLE_LIBUHD;ENABLE_GPSD;LIBGPS_FOUND" OFF OFF) + +IF(ENABLE_GPSD) + LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/gpsd_iface.cpp + ) + LIBUHD_APPEND_LIBS(${LIBGPS_LIBRARY}) +ENDIF(ENABLE_GPSD) + INCLUDE_SUBDIRECTORY(cores) INCLUDE_SUBDIRECTORY(dboard) INCLUDE_SUBDIRECTORY(common) diff --git a/host/lib/usrp/b100/CMakeLists.txt b/host/lib/usrp/b100/CMakeLists.txt index bcc5ac74d..1558cd974 100644 --- a/host/lib/usrp/b100/CMakeLists.txt +++ b/host/lib/usrp/b100/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2011-2013 Ettus Research LLC +# Copyright 2011-2013,2015 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 @@ -22,7 +22,7 @@ ######################################################################## # Conditionally configure the B100 support ######################################################################## -LIBUHD_REGISTER_COMPONENT("B100" ENABLE_B100 ON "ENABLE_LIBUHD;ENABLE_USB" OFF) +LIBUHD_REGISTER_COMPONENT("B100" ENABLE_B100 ON "ENABLE_LIBUHD;ENABLE_USB" OFF OFF) IF(ENABLE_B100) LIBUHD_APPEND_SOURCES( diff --git a/host/lib/usrp/b200/CMakeLists.txt b/host/lib/usrp/b200/CMakeLists.txt index ce89b5d80..76710dc65 100644 --- a/host/lib/usrp/b200/CMakeLists.txt +++ b/host/lib/usrp/b200/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2012-2013 Ettus Research LLC +# Copyright 2012-2013,2015 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 @@ -22,10 +22,11 @@ ######################################################################## # Conditionally configure the B200 support ######################################################################## -LIBUHD_REGISTER_COMPONENT("B200" ENABLE_B200 ON "ENABLE_LIBUHD;ENABLE_USB" OFF) +LIBUHD_REGISTER_COMPONENT("B200" ENABLE_B200 ON "ENABLE_LIBUHD;ENABLE_USB" OFF OFF) IF(ENABLE_B200) LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/b200_image_loader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/b200_impl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/b200_iface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/b200_io_impl.cpp diff --git a/host/lib/usrp/b200/b200_iface.cpp b/host/lib/usrp/b200/b200_iface.cpp index 270d3bb4b..4754a6357 100644 --- a/host/lib/usrp/b200/b200_iface.cpp +++ b/host/lib/usrp/b200/b200_iface.cpp @@ -511,7 +511,7 @@ public: throw uhd::io_error((boost::format("Short write on set FPGA hash (expecting: %d, returned: %d)") % bytes_to_send % ret).str()); } - boost::uint32_t load_fpga(const std::string filestring) { + boost::uint32_t load_fpga(const std::string filestring, bool force) { boost::uint8_t fx3_state = 0; boost::uint32_t wait_count; @@ -522,7 +522,7 @@ public: hash_type hash = generate_hash(filename); hash_type loaded_hash; usrp_get_fpga_hash(loaded_hash); - if (hash == loaded_hash) return 0; + if (hash == loaded_hash and !force) return 0; // Establish default largest possible control request transfer size based on operating USB speed int transfer_size = VREQ_DEFAULT_SIZE; diff --git a/host/lib/usrp/b200/b200_iface.hpp b/host/lib/usrp/b200/b200_iface.hpp index 1d123439a..19ec561fa 100644 --- a/host/lib/usrp/b200/b200_iface.hpp +++ b/host/lib/usrp/b200/b200_iface.hpp @@ -1,5 +1,5 @@ // -// Copyright 2012-2013,2015 Ettus Research LLC +// Copyright 2012-2013 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -27,40 +27,55 @@ #include <boost/utility.hpp> #include "ad9361_ctrl.hpp" -enum b200_type_t { +enum b200_product_t { B200, - B210 + B210, + B205 }; +// These are actual USB PIDs (not Ettus Product IDs) const static boost::uint16_t B200_VENDOR_ID = 0x2500; const static boost::uint16_t B200_VENDOR_NI_ID = 0x3923; const static boost::uint16_t B200_PRODUCT_ID = 0x0020; +const static boost::uint16_t B205_PRODUCT_ID = 0x0021; const static boost::uint16_t B200_PRODUCT_NI_ID = 0x7813; const static boost::uint16_t B210_PRODUCT_NI_ID = 0x7814; const static boost::uint16_t FX3_VID = 0x04b4; const static boost::uint16_t FX3_DEFAULT_PID = 0x00f3; const static boost::uint16_t FX3_REENUM_PID = 0x00f0; +//! Map the USB PID to the product (only for PIDs that map to a single product) +static const uhd::dict<boost::uint16_t, b200_product_t> B2XX_PID_TO_PRODUCT = boost::assign::map_list_of + (B200_PRODUCT_NI_ID, B200) + (B210_PRODUCT_NI_ID, B210) + (B205_PRODUCT_ID, B205) +; + static const std::string B200_FW_FILE_NAME = "usrp_b200_fw.hex"; -//! Map the product ID (in the EEPROM) to a device type -static const uhd::dict<boost::uint16_t, b200_type_t> B2X0_PRODUCT_ID = boost::assign::map_list_of +//! Map the EEPROM product ID codes to the product +static const uhd::dict<boost::uint16_t, b200_product_t> B2XX_PRODUCT_ID = boost::assign::map_list_of (0x0001, B200) (0x7737, B200) (B200_PRODUCT_NI_ID, B200) (0x0002, B210) (0x7738, B210) (B210_PRODUCT_NI_ID, B210) + (0x0003, B205) + (0x7739, B205) ; -static const uhd::dict<b200_type_t, std::string> B2X0_STR_NAMES = boost::assign::map_list_of + +static const uhd::dict<b200_product_t, std::string> B2XX_STR_NAMES = boost::assign::map_list_of (B200, "B200") (B210, "B210") + (B205, "B200mini") ; -static const uhd::dict<b200_type_t, std::string> B2X0_FPGA_FILE_NAME = boost::assign::map_list_of +static const uhd::dict<b200_product_t, std::string> B2XX_FPGA_FILE_NAME = boost::assign::map_list_of (B200, "usrp_b200_fpga.bin") (B210, "usrp_b210_fpga.bin") + (B205, "usrp_b200mini_fpga.bin") ; @@ -97,7 +112,7 @@ public: virtual void set_fpga_reset_pin(const bool reset) = 0; //! load an FPGA image - virtual boost::uint32_t load_fpga(const std::string filestring) = 0; + virtual boost::uint32_t load_fpga(const std::string filestring, bool force=false) = 0; virtual void write_eeprom(boost::uint16_t addr, boost::uint16_t offset, const uhd::byte_vector_t &bytes) = 0; diff --git a/host/lib/usrp/b200/b200_image_loader.cpp b/host/lib/usrp/b200/b200_image_loader.cpp new file mode 100644 index 000000000..e8fb8afea --- /dev/null +++ b/host/lib/usrp/b200/b200_image_loader.cpp @@ -0,0 +1,150 @@ +// +// Copyright 2014-2015 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 <boost/assign.hpp> +#include <boost/foreach.hpp> +#include <boost/lexical_cast.hpp> + +#include <uhd/exception.hpp> +#include <uhd/image_loader.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/usrp/mboard_eeprom.hpp> +#include <uhd/utils/paths.hpp> +#include <uhd/utils/static.hpp> + +#include "b200_iface.hpp" +#include "b200_impl.hpp" + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; + +namespace uhd{ + +static b200_iface::sptr get_b200_iface(const image_loader::image_loader_args_t& image_loader_args, + mboard_eeprom_t &mb_eeprom, usb_device_handle::sptr& handle, + bool user_specified){ + + std::vector<usb_device_handle::sptr> dev_handles = get_b200_device_handles(image_loader_args.args); + std::vector<usb_device_handle::sptr> applicable_dev_handles; + b200_iface::sptr iface; + mboard_eeprom_t eeprom; // Internal use + + if(dev_handles.size() > 0){ + BOOST_FOREACH(usb_device_handle::sptr dev_handle, dev_handles){ + if(dev_handle->firmware_loaded()){ + iface = b200_iface::make(usb_control::make(dev_handle,0)); + eeprom = mboard_eeprom_t(*iface, "B200"); + if(user_specified){ + if(image_loader_args.args.has_key("serial") and + eeprom.get("serial") != image_loader_args.args.get("serial")){ + continue; + } + if(image_loader_args.args.has_key("name") and + eeprom.get("name") != image_loader_args.args.get("name")){ + continue; + } + applicable_dev_handles.push_back(dev_handle); + } + else applicable_dev_handles.push_back(dev_handle); + } + } + + // At this point, we should have a single B2XX + if(applicable_dev_handles.size() == 1){ + mb_eeprom = eeprom; + handle = applicable_dev_handles[0]; + return iface; + } + else if(applicable_dev_handles.size() > 1){ + std::string err_msg = "Could not resolve given args to a single B2XX device.\n" + "Applicable devices:\n"; + + BOOST_FOREACH(usb_device_handle::sptr dev_handle, applicable_dev_handles){ + eeprom = mboard_eeprom_t(*b200_iface::make(usb_control::make(dev_handle,0)), "B200"); + err_msg += str(boost::format(" * %s (serial=%s)\n") + % B2XX_STR_NAMES.get(get_b200_product(dev_handle, mb_eeprom), "B2XX") + % mb_eeprom.get("serial")); + } + + err_msg += "\nSpecify one of these devices with the given args to load an image onto it."; + + throw uhd::runtime_error(err_msg); + } + } + + // No applicable devices found, return empty sptr so we can exit + iface.reset(); + mb_eeprom = mboard_eeprom_t(); + return iface; +} + +static bool b200_image_loader(const image_loader::image_loader_args_t &image_loader_args){ + if(!image_loader_args.load_fpga) + return false; + + bool user_specified = (image_loader_args.args.has_key("serial") or + image_loader_args.args.has_key("name")); + + // See if a B2x0 with the given args is found + mboard_eeprom_t mb_eeprom; + usb_device_handle::sptr handle; + b200_iface::sptr iface = get_b200_iface(image_loader_args, mb_eeprom, handle, user_specified); + if(!iface) return false; // No initialized B2x0 found + + std::string fpga_path; + if(image_loader_args.fpga_path == ""){ + /* + * Normally, we can auto-generate the FPGA filename from what's in the EEPROM, + * but if the applicable value is not in the EEPROM, the user must give a specific + * filename for us to use. + */ + std::string product = mb_eeprom.get("product"); + if(not B2XX_PRODUCT_ID.has_key(boost::lexical_cast<boost::uint16_t>(product))){ + if(user_specified){ + // The user specified a bad device but expects us to know what it is + throw uhd::runtime_error("Could not determine model. You must manually specify an FPGA image filename."); + } + else{ + return false; + } + } + else{ + fpga_path = find_image_path(B2XX_FPGA_FILE_NAME.get(get_b200_product(handle, mb_eeprom))); + } + } + else fpga_path = image_loader_args.fpga_path; + + std::cout << boost::format("Unit: USRP %s (%s)") + % B2XX_STR_NAMES.get(get_b200_product(handle, mb_eeprom), "B2XX") + % mb_eeprom.get("serial") + << std::endl; + + iface->load_fpga(fpga_path, true); + + return true; +} + +UHD_STATIC_BLOCK(register_b200_image_loader){ + std::string recovery_instructions = "This device is likely in an unusable state. Power-cycle the\n" + "device, and the firmware/FPGA will be reloaded the next time\n" + "UHD uses the device."; + + image_loader::register_image_loader("b200", b200_image_loader, recovery_instructions); +} + +} /* namespace uhd */ diff --git a/host/lib/usrp/b200/b200_impl.cpp b/host/lib/usrp/b200/b200_impl.cpp index 0409adf30..6bd49e013 100644 --- a/host/lib/usrp/b200/b200_impl.cpp +++ b/host/lib/usrp/b200/b200_impl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2012-2014 Ettus Research LLC +// Copyright 2012-2015 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -37,20 +37,23 @@ #include <ctime> #include <cmath> +#include "../../transport/libusb1_base.hpp" + using namespace uhd; using namespace uhd::usrp; using namespace uhd::transport; static const boost::posix_time::milliseconds REENUMERATION_TIMEOUT_MS(3000); +// B200 + B210: class b200_ad9361_client_t : public ad9361_params { public: ~b200_ad9361_client_t() {} double get_band_edge(frequency_band_t band) { switch (band) { - case AD9361_RX_BAND0: return 2.2e9; - case AD9361_RX_BAND1: return 4.0e9; - case AD9361_TX_BAND0: return 2.5e9; + case AD9361_RX_BAND0: return 2.2e9; // Port C + case AD9361_RX_BAND1: return 4.0e9; // Port B + case AD9361_TX_BAND0: return 2.5e9; // Port B default: return 0; } } @@ -70,25 +73,92 @@ public: } }; +// B205 +class b205_ad9361_client_t : public ad9361_params { +public: + ~b205_ad9361_client_t() {} + double get_band_edge(frequency_band_t band) { + switch (band) { + case AD9361_RX_BAND0: return 0; // Set these all to + case AD9361_RX_BAND1: return 0; // zero, so RF port A + case AD9361_TX_BAND0: return 0; // is used all the time + default: return 0; // On both Rx and Tx + } + } + clocking_mode_t get_clocking_mode() { + return AD9361_XTAL_N_CLK_PATH; + } + digital_interface_mode_t get_digital_interface_mode() { + return AD9361_DDR_FDD_LVCMOS; + } + digital_interface_delays_t get_digital_interface_timing() { + digital_interface_delays_t delays; + delays.rx_clk_delay = 0; + delays.rx_data_delay = 0x6; + delays.tx_clk_delay = 0; + delays.tx_data_delay = 0xF; + return delays; + } +}; + +/*********************************************************************** + * Helpers + **********************************************************************/ +std::string check_option_valid( + const std::string &name, + const std::vector<std::string> &valid_options, + const std::string &option +) { + if (std::find(valid_options.begin(), valid_options.end(), option) == valid_options.end()) { + throw uhd::runtime_error(str( + boost::format("Invalid option chosen for: %s") + % name + )); + } + + return option; +} + /*********************************************************************** * Discovery **********************************************************************/ //! Look up the type of B-Series device we're currently running. -// If the product ID stored in mb_eeprom is invalid, throws a -// uhd::runtime_error. -static b200_type_t get_b200_type(const mboard_eeprom_t &mb_eeprom) +// Throws a uhd::runtime_error if the USB PID and the product ID stored +// in the MB EEPROM are invalid, +b200_product_t get_b200_product(const usb_device_handle::sptr& handle, const mboard_eeprom_t &mb_eeprom) { + // Try USB PID first + boost::uint16_t product_id = handle->get_product_id(); + if (B2XX_PID_TO_PRODUCT.has_key(product_id)) + return B2XX_PID_TO_PRODUCT[product_id]; + + // Try EEPROM product ID code second if (mb_eeprom["product"].empty()) { throw uhd::runtime_error("B200: Missing product ID on EEPROM."); } - boost::uint16_t product_id = boost::lexical_cast<boost::uint16_t>(mb_eeprom["product"]); - if (not B2X0_PRODUCT_ID.has_key(product_id)) { + product_id = boost::lexical_cast<boost::uint16_t>(mb_eeprom["product"]); + if (not B2XX_PRODUCT_ID.has_key(product_id)) { throw uhd::runtime_error(str( boost::format("B200 unknown product code: 0x%04x") % product_id )); } - return B2X0_PRODUCT_ID[product_id]; + return B2XX_PRODUCT_ID[product_id]; +} + +std::vector<usb_device_handle::sptr> get_b200_device_handles(const device_addr_t &hint) +{ + std::vector<usb_device_handle::vid_pid_pair_t> vid_pid_pair_list; + + if(hint.has_key("vid") && hint.has_key("pid") && hint.has_key("type") && hint["type"] == "b200") { + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("vid")), + uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("pid")))); + } else { + vid_pid_pair_list = b200_vid_pid_pairs; + } + + //find the usrps and load firmware + return usb_device_handle::get_device_list(vid_pid_pair_list); } static device_addrs_t b200_find(const device_addr_t &hint) @@ -104,25 +174,13 @@ static device_addrs_t b200_find(const device_addr_t &hint) if (hint_i.has_key("addr") || hint_i.has_key("resource")) return b200_addrs; } - boost::uint16_t vid, pid; - - if(hint.has_key("vid") && hint.has_key("pid") && hint.has_key("type") && hint["type"] == "b200") { - vid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("vid")); - pid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("pid")); - } else { - vid = B200_VENDOR_ID; - pid = B200_PRODUCT_ID; - } - // Important note: // The get device list calls are nested inside the for loop. // This allows the usb guts to decontruct when not in use, // so that re-enumeration after fw load can occur successfully. // This requirement is a courtesy of libusb1.0 on windows. - - //find the usrps and load firmware size_t found = 0; - BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) { + BOOST_FOREACH(usb_device_handle::sptr handle, get_b200_device_handles(hint)) { //extract the firmware path for the b200 std::string b200_fw_image; try{ @@ -153,7 +211,7 @@ static device_addrs_t b200_find(const device_addr_t &hint) //search for the device until found or timeout while (boost::get_system_time() < timeout_time and b200_addrs.empty() and found != 0) { - BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) + BOOST_FOREACH(usb_device_handle::sptr handle, get_b200_device_handles(hint)) { usb_control::sptr control; try{control = usb_control::make(handle, 0);} @@ -168,9 +226,10 @@ static device_addrs_t b200_find(const device_addr_t &hint) new_addr["serial"] = handle->get_serial(); try { // Turn the 16-Bit product ID into a string representation - new_addr["product"] = B2X0_STR_NAMES[get_b200_type(mb_eeprom)]; + new_addr["product"] = B2XX_STR_NAMES[get_b200_product(handle, mb_eeprom)]; } catch (const uhd::runtime_error &e) { // No problem if this fails -- this is just device discovery, after all. + new_addr["product"] = "B2??"; } //this is a found b200 when the hint serial and name match or blank @@ -191,7 +250,24 @@ static device_addrs_t b200_find(const device_addr_t &hint) **********************************************************************/ static device::sptr b200_make(const device_addr_t &device_addr) { - return device::sptr(new b200_impl(device_addr)); + uhd::transport::usb_device_handle::sptr handle; + + // We try twice, because the first time, the link might be in a bad state + // and we might need to reset the link, but if that didn't help, trying + // a third time is pointless. + try { + return device::sptr(new b200_impl(device_addr, handle)); + } + catch (const uhd::usb_error &e) { + libusb::device_handle::sptr dev_handle(libusb::device_handle::get_cached_handle( + boost::static_pointer_cast<libusb::special_handle>(handle)->get_device() + )); + dev_handle->clear_endpoints(B200_USB_CTRL_RECV_ENDPOINT, B200_USB_CTRL_SEND_ENDPOINT); + dev_handle->clear_endpoints(B200_USB_DATA_RECV_ENDPOINT, B200_USB_DATA_SEND_ENDPOINT); + dev_handle->reset_device(); + } + + return device::sptr(new b200_impl(device_addr, handle)); } UHD_STATIC_BLOCK(register_b200_device) @@ -202,8 +278,10 @@ UHD_STATIC_BLOCK(register_b200_device) /*********************************************************************** * Structors **********************************************************************/ -b200_impl::b200_impl(const device_addr_t &device_addr) : +b200_impl::b200_impl(const uhd::device_addr_t& device_addr, usb_device_handle::sptr &handle) : + _product(B200), // Some safe value _revision(0), + _time_source(UNKNOWN), _tick_rate(0.0) // Forces a clock initialization at startup { _tree = property_tree::make(); @@ -213,16 +291,54 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : //try to match the given device address with something on the USB bus boost::uint16_t vid = B200_VENDOR_ID; boost::uint16_t pid = B200_PRODUCT_ID; + bool specified_vid = false; + bool specified_pid = false; + if (device_addr.has_key("vid")) + { vid = uhd::cast::hexstr_cast<boost::uint16_t>(device_addr.get("vid")); + specified_vid = true; + } + if (device_addr.has_key("pid")) + { pid = uhd::cast::hexstr_cast<boost::uint16_t>(device_addr.get("pid")); + specified_pid = true; + } - std::vector<usb_device_handle::sptr> device_list = - usb_device_handle::get_device_list(vid, pid); + std::vector<usb_device_handle::vid_pid_pair_t> vid_pid_pair_list;//search list for devices. + + // Search only for specified VID and PID if both specified + if (specified_vid && specified_pid) + { + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(vid,pid)); + } + // Search for all supported PIDs limited to specified VID if only VID specified + else if (specified_vid) + { + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(vid, B200_PRODUCT_ID)); + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(vid, B205_PRODUCT_ID)); + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(vid, B200_PRODUCT_NI_ID)); + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(vid, B210_PRODUCT_NI_ID)); + } + // Search for all supported VIDs limited to specified PID if only PID specified + else if (specified_pid) + { + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID,pid)); + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID,pid)); + } + // Search for all supported devices if neither VID nor PID specified + else + { + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID, B200_PRODUCT_ID)); + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID, B205_PRODUCT_ID)); + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID, B200_PRODUCT_NI_ID)); + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID, B210_PRODUCT_NI_ID)); + } + + std::vector<usb_device_handle::sptr> device_list = usb_device_handle::get_device_list(vid_pid_pair_list); //locate the matching handle in the device list - usb_device_handle::sptr handle; BOOST_FOREACH(usb_device_handle::sptr dev_handle, device_list) { if (dev_handle->get_serial() == device_addr["serial"]){ handle = dev_handle; @@ -251,9 +367,9 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : std::string product_name; try { // This will throw if the product ID is invalid: - _b200_type = get_b200_type(mb_eeprom); - default_file_name = B2X0_FPGA_FILE_NAME.get(_b200_type); - product_name = B2X0_STR_NAMES.get(_b200_type); + _product = get_b200_product(handle, mb_eeprom); + default_file_name = B2XX_FPGA_FILE_NAME.get(_product); + product_name = B2XX_STR_NAMES.get(_product); } catch (const uhd::runtime_error &e) { // The only reason we may let this pass is if the user specified // the FPGA file name: @@ -262,12 +378,15 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : } // In this case, we must provide a default product name: product_name = "B200?"; - _b200_type = B200; } if (not mb_eeprom["revision"].empty()) { _revision = boost::lexical_cast<size_t>(mb_eeprom["revision"]); } + UHD_MSG(status) << "Detected Device: " << B2XX_STR_NAMES[_product] << std::endl; + + _gpsdo_capable = (_product != B205); + //////////////////////////////////////////////////////////////////// // Set up frontend mapping //////////////////////////////////////////////////////////////////// @@ -285,7 +404,7 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : _fe2 = 0; _gpio_state.swap_atr = 1; // Unswapped setup: - if (_b200_type == B200 and _revision >= 5) { + if (_product == B205 or (_product == B200 and _revision >= 5)) { _fe1 = 0; //map radio0 to FE1 _fe2 = 1; //map radio1 to FE2 _gpio_state.swap_atr = 0; // ATRs for radio0 are mapped to FE1 @@ -320,10 +439,11 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : ctrl_xport_args["send_frame_size"] = min_frame_size; ctrl_xport_args["num_send_frames"] = "16"; + // This may throw a uhd::usb_error, which will be caught by b200_make(). _ctrl_transport = usb_zero_copy::make( handle, - 4, 8, //interface, endpoint - 3, 4, //interface, endpoint + B200_USB_CTRL_RECV_INTERFACE, B200_USB_CTRL_RECV_ENDPOINT, //interface, endpoint + B200_USB_CTRL_SEND_INTERFACE, B200_USB_CTRL_SEND_ENDPOINT, //interface, endpoint ctrl_xport_args ); while (_ctrl_transport->get_recv_buff(0.0)){} //flush ctrl xport @@ -352,34 +472,37 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : //////////////////////////////////////////////////////////////////// // Create the GPSDO control //////////////////////////////////////////////////////////////////// - _async_task_data->gpsdo_uart = b200_uart::make(_ctrl_transport, B200_TX_GPS_UART_SID); - _async_task_data->gpsdo_uart->set_baud_divider(B200_BUS_CLOCK_RATE/115200); - _async_task_data->gpsdo_uart->write_uart("\n"); //cause the baud and response to be setup - boost::this_thread::sleep(boost::posix_time::seconds(1)); //allow for a little propagation - - if ((_local_ctrl->peek32(RB32_CORE_STATUS) & 0xff) != B200_GPSDO_ST_NONE) + if (_gpsdo_capable) { - UHD_MSG(status) << "Detecting internal GPSDO.... " << std::flush; - try - { - _gps = gps_ctrl::make(_async_task_data->gpsdo_uart); - } - catch(std::exception &e) - { - UHD_MSG(error) << "An error occurred making GPSDO control: " << e.what() << std::endl; - } - if (_gps and _gps->gps_detected()) + _async_task_data->gpsdo_uart = b200_uart::make(_ctrl_transport, B200_TX_GPS_UART_SID); + _async_task_data->gpsdo_uart->set_baud_divider(B200_BUS_CLOCK_RATE/115200); + _async_task_data->gpsdo_uart->write_uart("\n"); //cause the baud and response to be setup + boost::this_thread::sleep(boost::posix_time::seconds(1)); //allow for a little propagation + + if ((_local_ctrl->peek32(RB32_CORE_STATUS) & 0xff) != B200_GPSDO_ST_NONE) { - //UHD_MSG(status) << "found" << std::endl; - BOOST_FOREACH(const std::string &name, _gps->get_sensors()) + UHD_MSG(status) << "Detecting internal GPSDO.... " << std::flush; + try { - _tree->create<sensor_value_t>(mb_path / "sensors" / name) - .publish(boost::bind(&gps_ctrl::get_sensor, _gps, name)); + _gps = gps_ctrl::make(_async_task_data->gpsdo_uart); + } + catch(std::exception &e) + { + UHD_MSG(error) << "An error occurred making GPSDO control: " << e.what() << std::endl; + } + if (_gps and _gps->gps_detected()) + { + //UHD_MSG(status) << "found" << std::endl; + BOOST_FOREACH(const std::string &name, _gps->get_sensors()) + { + _tree->create<sensor_value_t>(mb_path / "sensors" / name) + .publish(boost::bind(&gps_ctrl::get_sensor, _gps, name)); + } + } + else + { + _local_ctrl->poke32(TOREG(SR_CORE_GPSDO_ST), B200_GPSDO_ST_NONE); } - } - else - { - _local_ctrl->poke32(TOREG(SR_CORE_GPSDO_ST), B200_GPSDO_ST_NONE); } } @@ -388,7 +511,7 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : //////////////////////////////////////////////////////////////////// _tree->create<std::string>("/name").set("B-Series Device"); _tree->create<std::string>(mb_path / "name").set(product_name); - _tree->create<std::string>(mb_path / "codename").set("Sasquatch"); + _tree->create<std::string>(mb_path / "codename").set((_product == B205) ? "Pixie" : "Sasquatch"); //////////////////////////////////////////////////////////////////// // Create data transport @@ -402,10 +525,11 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : data_xport_args["send_frame_size"] = device_addr.get("send_frame_size", "8192"); data_xport_args["num_send_frames"] = device_addr.get("num_send_frames", "16"); + // This may throw a uhd::usb_error, which will be caught by b200_make(). _data_transport = usb_zero_copy::make( handle, // identifier - 2, 6, // IN interface, endpoint - 1, 2, // OUT interface, endpoint + B200_USB_DATA_RECV_INTERFACE, B200_USB_DATA_RECV_ENDPOINT, //interface, endpoint + B200_USB_DATA_SEND_INTERFACE, B200_USB_DATA_SEND_ENDPOINT, //interface, endpoint data_xport_args // param hints ); while (_data_transport->get_recv_buff(0.0)){} //flush ctrl xport @@ -415,13 +539,20 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : // create time and clock control objects //////////////////////////////////////////////////////////////////// _spi_iface = b200_local_spi_core::make(_local_ctrl); - _adf4001_iface = boost::make_shared<b200_ref_pll_ctrl>(_spi_iface); + if (_product != B205) { + _adf4001_iface = boost::make_shared<b200_ref_pll_ctrl>(_spi_iface); + } //////////////////////////////////////////////////////////////////// // Init codec - turns on clocks //////////////////////////////////////////////////////////////////// UHD_MSG(status) << "Initialize CODEC control..." << std::endl; - ad9361_params::sptr client_settings = boost::make_shared<b200_ad9361_client_t>(); + ad9361_params::sptr client_settings; + if (_product == B205) { + client_settings = boost::make_shared<b205_ad9361_client_t>(); + } else { + client_settings = boost::make_shared<b200_ad9361_client_t>(); + } _codec_ctrl = ad9361_ctrl::make_spi(client_settings, _spi_iface, AD9361_SLAVENO); this->reset_codec_dcm(); @@ -447,6 +578,7 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : .publish(boost::bind(&b200_impl::get_tick_rate, this)) .subscribe(boost::bind(&b200_impl::update_tick_rate, this, _1)); _tree->create<time_spec_t>(mb_path / "time" / "cmd"); + _tree->create<bool>(mb_path / "auto_tick_rate").set(false); //////////////////////////////////////////////////////////////////// // and do the misc mboard sensors @@ -477,15 +609,17 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : UHD_ASSERT_THROW(num_radio_chains > 0); UHD_ASSERT_THROW(num_radio_chains <= 2); _radio_perifs.resize(num_radio_chains); - for (size_t i = 0; i < _radio_perifs.size(); i++) this->setup_radio(i); + _codec_mgr = ad936x_manager::make(_codec_ctrl, num_radio_chains); + _codec_mgr->init_codec(); + for (size_t i = 0; i < _radio_perifs.size(); i++) + this->setup_radio(i); + //now test each radio module's connection to the codec interface - _codec_ctrl->data_port_loopback(true); BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs) { - this->codec_loopback_self_test(perif.ctrl); + _codec_mgr->loopback_self_test(perif.ctrl, TOREG(SR_CODEC_IDLE), RB64_CODEC_READBACK); } - _codec_ctrl->data_port_loopback(false); //register time now and pps onto available radio cores _tree->create<time_spec_t>(mb_path / "time" / "now") @@ -501,15 +635,36 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : } //setup time source props + static const std::vector<std::string> time_sources = (_gpsdo_capable) ? + boost::assign::list_of("none")("internal")("external")("gpsdo") : + boost::assign::list_of("none")("internal")("external") ; + _tree->create<std::vector<std::string> >(mb_path / "time_source" / "options") + .set(time_sources); _tree->create<std::string>(mb_path / "time_source" / "value") + .coerce(boost::bind(&check_option_valid, "time source", time_sources, _1)) .subscribe(boost::bind(&b200_impl::update_time_source, this, _1)); - static const std::vector<std::string> time_sources = boost::assign::list_of("none")("internal")("external")("gpsdo"); - _tree->create<std::vector<std::string> >(mb_path / "time_source" / "options").set(time_sources); //setup reference source props + static const std::vector<std::string> clock_sources = (_gpsdo_capable) ? + boost::assign::list_of("internal")("external")("gpsdo") : + boost::assign::list_of("internal")("external") ; + _tree->create<std::vector<std::string> >(mb_path / "clock_source" / "options") + .set(clock_sources); _tree->create<std::string>(mb_path / "clock_source" / "value") + .coerce(boost::bind(&check_option_valid, "clock source", clock_sources, _1)) .subscribe(boost::bind(&b200_impl::update_clock_source, this, _1)); - static const std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("gpsdo"); - _tree->create<std::vector<std::string> >(mb_path / "clock_source" / "options").set(clock_sources); + + //////////////////////////////////////////////////////////////////// + // front panel gpio + //////////////////////////////////////////////////////////////////// + _radio_perifs[0].fp_gpio = gpio_core_200::make(_radio_perifs[0].ctrl, TOREG(SR_FP_GPIO), RB32_FP_GPIO); + BOOST_FOREACH(const gpio_attr_map_t::value_type attr, gpio_attr_map) + { + _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / attr.second) + .set(0) + .subscribe(boost::bind(&b200_impl::set_fp_gpio, this, _radio_perifs[0].fp_gpio, attr.first, _1)); + } + _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / "READBACK") + .publish(boost::bind(&b200_impl::get_fp_gpio, this, _radio_perifs[0].fp_gpio)); //////////////////////////////////////////////////////////////////// // dboard eeproms but not really @@ -524,7 +679,7 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : //////////////////////////////////////////////////////////////////// //init the clock rate to something reasonable - double default_tick_rate = device_addr.cast<double>("master_clock_rate", B200_DEFAULT_TICK_RATE); + double default_tick_rate = device_addr.cast<double>("master_clock_rate", ad936x_manager::DEFAULT_TICK_RATE); _tree->access<double>(mb_path / "tick_rate").set(default_tick_rate); //subdev spec contains full width of selections @@ -542,27 +697,17 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : //init to internal clock and time source _tree->access<std::string>(mb_path / "clock_source/value").set("internal"); - _tree->access<std::string>(mb_path / "time_source/value").set("none"); + _tree->access<std::string>(mb_path / "time_source/value").set("internal"); // Set the DSP chains to some safe value for (size_t i = 0; i < _radio_perifs.size(); i++) { - _radio_perifs[i].ddc->set_host_rate(default_tick_rate / B200_DEFAULT_DECIM); - _radio_perifs[i].duc->set_host_rate(default_tick_rate / B200_DEFAULT_INTERP); + _radio_perifs[i].ddc->set_host_rate(default_tick_rate / ad936x_manager::DEFAULT_DECIM); + _radio_perifs[i].duc->set_host_rate(default_tick_rate / ad936x_manager::DEFAULT_INTERP); } - - //GPS installed: use external ref, time, and init time spec - if (_gps and _gps->gps_detected()) - { - UHD_MSG(status) << "Setting references to the internal GPSDO" << std::endl; - _tree->access<std::string>(mb_path / "time_source" / "value").set("gpsdo"); - _tree->access<std::string>(mb_path / "clock_source" / "value").set("gpsdo"); - UHD_MSG(status) << "Initializing time to the internal GPSDO" << std::endl; - const time_t tp = time_t(_gps->get_sensor("gps_time").to_int()+1); - _tree->access<time_spec_t>(mb_path / "time" / "pps").set(time_spec_t(tp)); - } else { - //init to internal clock and time source - _tree->access<std::string>(mb_path / "clock_source/value").set("internal"); - _tree->access<std::string>(mb_path / "time_source/value").set("internal"); + // We can automatically choose a master clock rate, but not if the user specifies one + _tree->access<bool>(mb_path / "auto_tick_rate").set(not device_addr.has_key("master_clock_rate")); + if (not device_addr.has_key("master_clock_rate")) { + UHD_MSG(status) << "Setting master clock rate selection to 'automatic'." << std::endl; } } @@ -585,10 +730,18 @@ void b200_impl::setup_radio(const size_t dspno) const fs_path mb_path = "/mboards/0"; //////////////////////////////////////////////////////////////////// + // Set up transport + //////////////////////////////////////////////////////////////////// + const boost::uint32_t sid = (dspno == 0) ? B200_CTRL0_MSG_SID : B200_CTRL1_MSG_SID; + + //////////////////////////////////////////////////////////////////// // radio control //////////////////////////////////////////////////////////////////// - const boost::uint32_t sid = (dspno == 0)? B200_CTRL0_MSG_SID : B200_CTRL1_MSG_SID; - perif.ctrl = radio_ctrl_core_3000::make(false/*lilE*/, _ctrl_transport, zero_copy_if::sptr()/*null*/, sid); + perif.ctrl = radio_ctrl_core_3000::make( + false/*lilE*/, + _ctrl_transport, + zero_copy_if::sptr()/*null*/, + sid); perif.ctrl->hold_task(_async_task); _async_task_data->radio_ctrl[dspno] = perif.ctrl; //weak _tree->access<time_spec_t>(mb_path / "time" / "cmd") @@ -596,121 +749,96 @@ void b200_impl::setup_radio(const size_t dspno) _tree->access<double>(mb_path / "tick_rate") .subscribe(boost::bind(&radio_ctrl_core_3000::set_tick_rate, perif.ctrl, _1)); this->register_loopback_self_test(perif.ctrl); - perif.atr = gpio_core_200_32wo::make(perif.ctrl, TOREG(SR_ATR)); //////////////////////////////////////////////////////////////////// - // create rx dsp control objects + // Set up peripherals //////////////////////////////////////////////////////////////////// + perif.atr = gpio_core_200_32wo::make(perif.ctrl, TOREG(SR_ATR)); + // create rx dsp control objects perif.framer = rx_vita_core_3000::make(perif.ctrl, TOREG(SR_RX_CTRL)); perif.ddc = rx_dsp_core_3000::make(perif.ctrl, TOREG(SR_RX_DSP), true /*is_b200?*/); perif.ddc->set_link_rate(10e9/8); //whatever perif.ddc->set_mux("IQ", false, dspno == 1 ? true : false, dspno == 1 ? true : false); + perif.ddc->set_freq(rx_dsp_core_3000::DEFAULT_CORDIC_FREQ); + perif.deframer = tx_vita_core_3000::make(perif.ctrl, TOREG(SR_TX_CTRL)); + perif.duc = tx_dsp_core_3000::make(perif.ctrl, TOREG(SR_TX_DSP)); + perif.duc->set_link_rate(10e9/8); //whatever + perif.duc->set_freq(tx_dsp_core_3000::DEFAULT_CORDIC_FREQ); + + //////////////////////////////////////////////////////////////////// + // create time control objects + //////////////////////////////////////////////////////////////////// + time_core_3000::readback_bases_type time64_rb_bases; + time64_rb_bases.rb_now = RB64_TIME_NOW; + time64_rb_bases.rb_pps = RB64_TIME_PPS; + perif.time64 = time_core_3000::make(perif.ctrl, TOREG(SR_TIME), time64_rb_bases); + + //////////////////////////////////////////////////////////////////// + // connect rx dsp control objects + //////////////////////////////////////////////////////////////////// _tree->access<double>(mb_path / "tick_rate") .subscribe(boost::bind(&rx_vita_core_3000::set_tick_rate, perif.framer, _1)) .subscribe(boost::bind(&rx_dsp_core_3000::set_tick_rate, perif.ddc, _1)); const fs_path rx_dsp_path = mb_path / "rx_dsps" / dspno; - _tree->create<meta_range_t>(rx_dsp_path / "rate" / "range") - .publish(boost::bind(&rx_dsp_core_3000::get_host_rates, perif.ddc)); - _tree->create<double>(rx_dsp_path / "rate" / "value") - .set(0.0) // We can only load a sensible value after the tick rate was set - .coerce(boost::bind(&rx_dsp_core_3000::set_host_rate, perif.ddc, _1)) + perif.ddc->populate_subtree(_tree->subtree(rx_dsp_path)); + _tree->access<double>(rx_dsp_path / "rate" / "value") + .coerce(boost::bind(&b200_impl::coerce_rx_samp_rate, this, perif.ddc, dspno, _1)) .subscribe(boost::bind(&b200_impl::update_rx_samp_rate, this, dspno, _1)) ; - _tree->create<double>(rx_dsp_path / "freq" / "value") - .coerce(boost::bind(&rx_dsp_core_3000::set_freq, perif.ddc, _1)) - .set(0.0); - _tree->create<meta_range_t>(rx_dsp_path / "freq" / "range") - .publish(boost::bind(&rx_dsp_core_3000::get_freq_range, perif.ddc)); _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd") .subscribe(boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1)); //////////////////////////////////////////////////////////////////// // create tx dsp control objects //////////////////////////////////////////////////////////////////// - perif.deframer = tx_vita_core_3000::make(perif.ctrl, TOREG(SR_TX_CTRL)); - perif.duc = tx_dsp_core_3000::make(perif.ctrl, TOREG(SR_TX_DSP)); - perif.duc->set_link_rate(10e9/8); //whatever _tree->access<double>(mb_path / "tick_rate") .subscribe(boost::bind(&tx_vita_core_3000::set_tick_rate, perif.deframer, _1)) .subscribe(boost::bind(&tx_dsp_core_3000::set_tick_rate, perif.duc, _1)); const fs_path tx_dsp_path = mb_path / "tx_dsps" / dspno; - _tree->create<meta_range_t>(tx_dsp_path / "rate" / "range") - .publish(boost::bind(&tx_dsp_core_3000::get_host_rates, perif.duc)); - _tree->create<double>(tx_dsp_path / "rate" / "value") - .set(0.0) // We can only load a sensible value after the tick rate was set - .coerce(boost::bind(&tx_dsp_core_3000::set_host_rate, perif.duc, _1)) + perif.duc->populate_subtree(_tree->subtree(tx_dsp_path)); + _tree->access<double>(tx_dsp_path / "rate" / "value") + .coerce(boost::bind(&b200_impl::coerce_tx_samp_rate, this, perif.duc, dspno, _1)) .subscribe(boost::bind(&b200_impl::update_tx_samp_rate, this, dspno, _1)) ; - _tree->create<double>(tx_dsp_path / "freq" / "value") - .coerce(boost::bind(&tx_dsp_core_3000::set_freq, perif.duc, _1)) - .set(0.0); - _tree->create<meta_range_t>(tx_dsp_path / "freq" / "range") - .publish(boost::bind(&tx_dsp_core_3000::get_freq_range, perif.duc)); - - //////////////////////////////////////////////////////////////////// - // create time control objects - //////////////////////////////////////////////////////////////////// - time_core_3000::readback_bases_type time64_rb_bases; - time64_rb_bases.rb_now = RB64_TIME_NOW; - time64_rb_bases.rb_pps = RB64_TIME_PPS; - perif.time64 = time_core_3000::make(perif.ctrl, TOREG(SR_TIME), time64_rb_bases); //////////////////////////////////////////////////////////////////// // create RF frontend interfacing //////////////////////////////////////////////////////////////////// - for(size_t direction = 0; direction < 2; direction++) - { - const std::string x = direction? "rx" : "tx"; - const std::string key = std::string((direction? "RX" : "TX")) + std::string(((dspno == _fe1)? "1" : "2")); - const fs_path rf_fe_path = mb_path / "dboards" / "A" / (x+"_frontends") / (dspno? "B" : "A"); - - _tree->create<std::string>(rf_fe_path / "name").set("FE-"+key); - _tree->create<int>(rf_fe_path / "sensors"); + static const std::vector<direction_t> dirs = boost::assign::list_of(RX_DIRECTION)(TX_DIRECTION); + BOOST_FOREACH(direction_t dir, dirs) { + const std::string x = (dir == RX_DIRECTION) ? "rx" : "tx"; + const std::string key = std::string(((dir == RX_DIRECTION) ? "RX" : "TX")) + std::string(((dspno == _fe1) ? "1" : "2")); + const fs_path rf_fe_path + = mb_path / "dboards" / "A" / (x + "_frontends") / (dspno ? "B" : "A"); + + // This will connect all the AD936x-specific items + _codec_mgr->populate_frontend_subtree( + _tree->subtree(rf_fe_path), key, dir + ); + + // Now connect all the b200_impl-specific items _tree->create<sensor_value_t>(rf_fe_path / "sensors" / "lo_locked") - .publish(boost::bind(&b200_impl::get_fe_pll_locked, this, x == "tx")); - BOOST_FOREACH(const std::string &name, ad9361_ctrl::get_gain_names(key)) - { - _tree->create<meta_range_t>(rf_fe_path / "gains" / name / "range") - .set(ad9361_ctrl::get_gain_range(key)); - - _tree->create<double>(rf_fe_path / "gains" / name / "value") - .coerce(boost::bind(&ad9361_ctrl::set_gain, _codec_ctrl, key, _1)) - .set(x == "rx" ? B200_DEFAULT_RX_GAIN : B200_DEFAULT_TX_GAIN); - } - _tree->create<std::string>(rf_fe_path / "connection").set("IQ"); - _tree->create<bool>(rf_fe_path / "enabled").set(true); - _tree->create<bool>(rf_fe_path / "use_lo_offset").set(false); - _tree->create<double>(rf_fe_path / "bandwidth" / "value") - .coerce(boost::bind(&ad9361_ctrl::set_bw_filter, _codec_ctrl, key, _1)) - .set(40e6); - _tree->create<meta_range_t>(rf_fe_path / "bandwidth" / "range") - .publish(boost::bind(&ad9361_ctrl::get_bw_filter_range, key)); - _tree->create<double>(rf_fe_path / "freq" / "value") - .publish(boost::bind(&ad9361_ctrl::get_freq, _codec_ctrl, key)) - .coerce(boost::bind(&ad9361_ctrl::tune, _codec_ctrl, key, _1)) + .publish(boost::bind(&b200_impl::get_fe_pll_locked, this, dir == TX_DIRECTION)) + ; + _tree->access<double>(rf_fe_path / "freq" / "value") .subscribe(boost::bind(&b200_impl::update_bandsel, this, key, _1)) - .set(B200_DEFAULT_FREQ); - _tree->create<meta_range_t>(rf_fe_path / "freq" / "range") - .publish(boost::bind(&ad9361_ctrl::get_rf_freq_range)); - - //setup RX related stuff - if (key[0] == 'R') + ; + if (dir == RX_DIRECTION) { static const std::vector<std::string> ants = boost::assign::list_of("TX/RX")("RX2"); _tree->create<std::vector<std::string> >(rf_fe_path / "antenna" / "options").set(ants); _tree->create<std::string>(rf_fe_path / "antenna" / "value") .subscribe(boost::bind(&b200_impl::update_antenna_sel, this, dspno, _1)) - .set("RX2"); - _tree->create<sensor_value_t>(rf_fe_path / "sensors" / "rssi") - .publish(boost::bind(&ad9361_ctrl::get_rssi, _codec_ctrl, key)); + .set("RX2") + ; + } - if (key[0] == 'T') + else if (dir == TX_DIRECTION) { static const std::vector<std::string> ants(1, "TX/RX"); _tree->create<std::vector<std::string> >(rf_fe_path / "antenna" / "options").set(ants); _tree->create<std::string>(rf_fe_path / "antenna" / "value").set("TX/RX"); } - } } @@ -733,34 +861,10 @@ void b200_impl::register_loopback_self_test(wb_iface::sptr iface) UHD_MSG(status) << ((test_fail)? "fail" : "pass") << std::endl; } -void b200_impl::codec_loopback_self_test(wb_iface::sptr iface) -{ - UHD_MSG(status) << "Performing CODEC loopback test... " << std::flush; - size_t hash = size_t(time(NULL)); - for (size_t i = 0; i < 100; i++) - { - boost::hash_combine(hash, i); - const boost::uint32_t word32 = boost::uint32_t(hash) & 0xfff0fff0; - iface->poke32(TOREG(SR_CODEC_IDLE), word32); - iface->peek64(RB64_CODEC_READBACK); //enough idleness for loopback to propagate - const boost::uint64_t rb_word64 = iface->peek64(RB64_CODEC_READBACK); - const boost::uint32_t rb_tx = boost::uint32_t(rb_word64 >> 32); - const boost::uint32_t rb_rx = boost::uint32_t(rb_word64 & 0xffffffff); - bool test_fail = word32 != rb_tx or word32 != rb_rx; - if (test_fail) { - UHD_MSG(status) << "fail" << std::endl; - throw uhd::runtime_error("CODEC loopback test failed."); - } - } - UHD_MSG(status) << "pass" << std::endl; - /* Zero out the idle data. */ - iface->poke32(TOREG(SR_CODEC_IDLE), 0); -} - /*********************************************************************** * Sample and tick rate comprehension below **********************************************************************/ -void b200_impl::enforce_tick_rate_limits(size_t chan_count, double tick_rate, const char* direction /*= NULL*/) +void b200_impl::enforce_tick_rate_limits(size_t chan_count, double tick_rate, const std::string &direction /*= ""*/) { const size_t max_chans = 2; if (chan_count > max_chans) @@ -768,7 +872,7 @@ void b200_impl::enforce_tick_rate_limits(size_t chan_count, double tick_rate, co throw uhd::value_error(boost::str( boost::format("cannot not setup %d %s channels (maximum is %d)") % chan_count - % (direction ? direction : "data") + % (direction.empty() ? "data" : direction) % max_chans )); } @@ -782,20 +886,26 @@ void b200_impl::enforce_tick_rate_limits(size_t chan_count, double tick_rate, co % (tick_rate/1e6) % (max_tick_rate/1e6) % chan_count - % (direction ? direction : "data") + % (direction.empty() ? "data" : direction) )); } } } -double b200_impl::set_tick_rate(const double rate) +double b200_impl::set_tick_rate(const double new_tick_rate) { - UHD_MSG(status) << (boost::format("Asking for clock rate %.6f MHz\n") % (rate/1e6)); - - check_tick_rate_with_current_streamers(rate); // Defined in b200_io_impl.cpp + UHD_MSG(status) << (boost::format("Asking for clock rate %.6f MHz... ") % (new_tick_rate/1e6)) << std::flush; + check_tick_rate_with_current_streamers(new_tick_rate); // Defined in b200_io_impl.cpp + + // Make sure the clock rate is actually changed before doing + // the full Monty of setting regs and loopback tests etc. + if (std::abs(new_tick_rate - _tick_rate) < 1.0) { + UHD_MSG(status) << "OK" << std::endl; + return _tick_rate; + } - _tick_rate = _codec_ctrl->set_clock_rate(rate); - UHD_MSG(status) << (boost::format("Actually got clock rate %.6f MHz\n") % (_tick_rate/1e6)); + _tick_rate = _codec_ctrl->set_clock_rate(new_tick_rate); + UHD_MSG(status) << std::endl << (boost::format("Actually got clock rate %.6f MHz.") % (_tick_rate/1e6)) << std::endl; //reset after clock rate change this->reset_codec_dcm(); @@ -839,12 +949,14 @@ void b200_impl::check_fpga_compat(void) if (signature != 0xACE0BA5E) throw uhd::runtime_error( "b200::check_fpga_compat signature register readback failed"); - if (compat_major != B200_FPGA_COMPAT_NUM){ + const boost::uint16_t expected = (_product == B205 ? B205_FPGA_COMPAT_NUM : B200_FPGA_COMPAT_NUM); + if (compat_major != expected) + { 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.\n" "%s" - ) % int(B200_FPGA_COMPAT_NUM) % compat_major % print_utility_error("uhd_images_downloader.py"))); + ) % int(expected) % compat_major % print_utility_error("uhd_images_downloader.py"))); } _tree->create<std::string>("/mboards/0/fpga_version").set(str(boost::format("%u.%u") % compat_major % compat_minor)); @@ -856,41 +968,118 @@ void b200_impl::set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &mb_eeprom) } +boost::uint32_t b200_impl::get_fp_gpio(gpio_core_200::sptr gpio) +{ + return boost::uint32_t(gpio->read_gpio(dboard_iface::UNIT_RX)); +} + +void b200_impl::set_fp_gpio(gpio_core_200::sptr gpio, const gpio_attr_t attr, const boost::uint32_t value) +{ + switch (attr) + { + case GPIO_CTRL: return gpio->set_pin_ctrl(dboard_iface::UNIT_RX, value); + case GPIO_DDR: return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value); + case GPIO_OUT: return gpio->set_gpio_out(dboard_iface::UNIT_RX, value); + case GPIO_ATR_0X: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value); + case GPIO_ATR_RX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value); + case GPIO_ATR_TX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value); + case GPIO_ATR_XX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, value); + default: UHD_THROW_INVALID_CODE_PATH(); + } +} + /*********************************************************************** * Reference time and clock **********************************************************************/ void b200_impl::update_clock_source(const std::string &source) { - // present the PLL with a valid 10 MHz signal before switching its reference - _gpio_state.ref_sel = (source == "gpsdo")? 1 : 0; - this->update_gpio_state(); + // For B205, ref_sel selects whether or not to lock to the external clock source + if (_product == B205) + { + if (source == "external" and _time_source == EXTERNAL) + { + throw uhd::value_error("external reference cannot be both a clock source and a time source"); + } - if (source == "internal"){ - _adf4001_iface->set_lock_to_ext_ref(false); + if (source == "internal") + { + if (_gpio_state.ref_sel != 0) + { + _gpio_state.ref_sel = 0; + this->update_gpio_state(); + } + } + else if (source == "external") + { + if (_gpio_state.ref_sel != 1) + { + _gpio_state.ref_sel = 1; + this->update_gpio_state(); + } + } + else + { + throw uhd::key_error("update_clock_source: unknown source: " + source); + } + return; } - else if ((source == "external") - or (source == "gpsdo")){ + // For all other devices, ref_sel selects the external or gpsdo clock source + // and the ADF4001 selects whether to lock to it or not + if (source == "internal") + { + _adf4001_iface->set_lock_to_ext_ref(false); + } + else if (source == "external") + { + if (_gpio_state.ref_sel != 0) + { + _gpio_state.ref_sel = 0; + this->update_gpio_state(); + } _adf4001_iface->set_lock_to_ext_ref(true); - } else { + } + else if (_gps and source == "gpsdo") + { + if (_gpio_state.ref_sel != 1) + { + _gpio_state.ref_sel = 1; + this->update_gpio_state(); + } + _adf4001_iface->set_lock_to_ext_ref(true); + } + else + { throw uhd::key_error("update_clock_source: unknown source: " + source); } } void b200_impl::update_time_source(const std::string &source) { - boost::uint32_t value = 0; + if (_product == B205 and source == "external" and _gpio_state.ref_sel == 1) + { + throw uhd::value_error("external reference cannot be both a time source and a clock source"); + } + + // We assume source is valid for this device (if it's gone through + // the prop three, then it definitely is thanks to our coercer) + time_source_t value; if (source == "none") - value = 3; + value = NONE; else if (source == "internal") - value = 2; + value = INTERNAL; else if (source == "external") - value = 1; - else if (source == "gpsdo") - value = 0; - else throw uhd::key_error("update_time_source: unknown source: " + source); - _local_ctrl->poke32(TOREG(SR_CORE_PPS_SEL), value); + value = EXTERNAL; + else if (_gps and source == "gpsdo") + value = GPSDO; + else + throw uhd::key_error("update_time_source: unknown source: " + source); + if (_time_source != value) + { + _local_ctrl->poke32(TOREG(SR_CORE_PPS_SEL), value); + _time_source = value; + } } /*********************************************************************** @@ -899,6 +1088,11 @@ void b200_impl::update_time_source(const std::string &source) void b200_impl::update_bandsel(const std::string& which, double freq) { + // B205 does not have bandsels + if (_product == B205) { + return; + } + if(which[0] == 'R') { if(freq < 2.2e9) { _gpio_state.rx_bandsel_a = 0; diff --git a/host/lib/usrp/b200/b200_impl.hpp b/host/lib/usrp/b200/b200_impl.hpp index 65796d1a4..b291f8e5c 100644 --- a/host/lib/usrp/b200/b200_impl.hpp +++ b/host/lib/usrp/b200/b200_impl.hpp @@ -1,5 +1,5 @@ // -// Copyright 2012-2013 Ettus Research LLC +// Copyright 2012-2015 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 @@ -22,6 +22,7 @@ #include "b200_uart.hpp" #include "b200_cores.hpp" #include "ad9361_ctrl.hpp" +#include "ad936x_manager.hpp" #include "adf4001_ctrl.hpp" #include "rx_vita_core_3000.hpp" #include "tx_vita_core_3000.hpp" @@ -43,18 +44,14 @@ #include <uhd/usrp/gps_ctrl.hpp> #include <uhd/transport/usb_zero_copy.hpp> #include <uhd/transport/bounded_buffer.hpp> +#include <boost/assign.hpp> #include <boost/weak_ptr.hpp> #include "recv_packet_demuxer_3000.hpp" -static const boost::uint8_t B200_FW_COMPAT_NUM_MAJOR = 7; +static const boost::uint8_t B200_FW_COMPAT_NUM_MAJOR = 8; static const boost::uint8_t B200_FW_COMPAT_NUM_MINOR = 0; -static const boost::uint16_t B200_FPGA_COMPAT_NUM = 8; +static const boost::uint16_t B200_FPGA_COMPAT_NUM = 10; +static const boost::uint16_t B205_FPGA_COMPAT_NUM = 1; static const double B200_BUS_CLOCK_RATE = 100e6; -static const double B200_DEFAULT_TICK_RATE = 32e6; -static const double B200_DEFAULT_FREQ = 100e6; // Hz -static const double B200_DEFAULT_DECIM = 128; -static const double B200_DEFAULT_INTERP = 128; -static const double B200_DEFAULT_RX_GAIN = 0; // dB -static const double B200_DEFAULT_TX_GAIN = 0; // dB static const boost::uint32_t B200_GPSDO_ST_NONE = 0x83; static const size_t B200_MAX_RATE_USB2 = 53248000; // bytes/s static const size_t B200_MAX_RATE_USB3 = 500000000; // bytes/s @@ -82,32 +79,60 @@ static const boost::uint32_t B200_RX_GPS_UART_SID = FLIP_SID(B200_TX_GPS_UART_SI static const boost::uint32_t B200_LOCAL_CTRL_SID = 0x00000040; static const boost::uint32_t B200_LOCAL_RESP_SID = FLIP_SID(B200_LOCAL_CTRL_SID); -/*********************************************************************** - * The B200 Capability Constants - **********************************************************************/ +static const unsigned char B200_USB_CTRL_RECV_INTERFACE = 4; +static const unsigned char B200_USB_CTRL_RECV_ENDPOINT = 8; +static const unsigned char B200_USB_CTRL_SEND_INTERFACE = 3; +static const unsigned char B200_USB_CTRL_SEND_ENDPOINT = 4; + +static const unsigned char B200_USB_DATA_RECV_INTERFACE = 2; +static const unsigned char B200_USB_DATA_RECV_ENDPOINT = 6; +static const unsigned char B200_USB_DATA_SEND_INTERFACE = 1; +static const unsigned char B200_USB_DATA_SEND_ENDPOINT = 2; + +/* + * VID/PID pairs for all B2xx products + */ +static std::vector<uhd::transport::usb_device_handle::vid_pid_pair_t> b200_vid_pid_pairs = + boost::assign::list_of + (uhd::transport::usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID, B200_PRODUCT_ID)) + (uhd::transport::usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID, B205_PRODUCT_ID)) + (uhd::transport::usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID, B200_PRODUCT_NI_ID)) + (uhd::transport::usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID, B210_PRODUCT_NI_ID)) + ; + +b200_product_t get_b200_product(const uhd::transport::usb_device_handle::sptr& handle, const uhd::usrp::mboard_eeprom_t &mb_eeprom); +std::vector<uhd::transport::usb_device_handle::sptr> get_b200_device_handles(const uhd::device_addr_t &hint); //! Implementation guts class b200_impl : public uhd::device { public: //structors - b200_impl(const uhd::device_addr_t &); + b200_impl(const uhd::device_addr_t &, uhd::transport::usb_device_handle::sptr &handle); ~b200_impl(void); //the io interface uhd::rx_streamer::sptr get_rx_stream(const uhd::stream_args_t &args); uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &args); bool recv_async_msg(uhd::async_metadata_t &, double); - void check_streamer_args(const uhd::stream_args_t &args, double tick_rate, const char* direction = NULL); + + //! Check that the combination of stream args and tick rate are valid. + // + // Basically figures out the arguments for enforce_tick_rate_limits() + // and calls said method. If arguments are invalid, throws a + // uhd::value_error. + void check_streamer_args(const uhd::stream_args_t &args, double tick_rate, const std::string &direction = ""); private: - b200_type_t _b200_type; - size_t _revision; + b200_product_t _product; + size_t _revision; + bool _gpsdo_capable; //controllers b200_iface::sptr _iface; radio_ctrl_core_3000::sptr _local_ctrl; uhd::usrp::ad9361_ctrl::sptr _codec_ctrl; + uhd::usrp::ad936x_manager::sptr _codec_mgr; b200_local_spi_core::sptr _spi_iface; boost::shared_ptr<uhd::usrp::adf4001_ctrl> _adf4001_iface; uhd::gps_ctrl::sptr _gps; @@ -136,7 +161,6 @@ private: boost::optional<uhd::msg_task::msg_type_t> handle_async_task(uhd::transport::zero_copy_if::sptr, boost::shared_ptr<AsyncTaskData>); void register_loopback_self_test(uhd::wb_iface::sptr iface); - void codec_loopback_self_test(uhd::wb_iface::sptr iface); void set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &); void check_fw_compat(void); void check_fpga_compat(void); @@ -154,6 +178,7 @@ private: { radio_ctrl_core_3000::sptr ctrl; gpio_core_200_32wo::sptr atr; + gpio_core_200::sptr fp_gpio; time_core_3000::sptr time64; rx_vita_core_3000::sptr framer; rx_dsp_core_3000::sptr ddc; @@ -194,20 +219,71 @@ private: } } _gpio_state; + enum time_source_t {GPSDO=0,EXTERNAL=1,INTERNAL=2,NONE=3,UNKNOWN=4} _time_source; + void update_gpio_state(void); void reset_codec_dcm(void); void update_enables(void); void update_atrs(void); + boost::uint32_t get_fp_gpio(gpio_core_200::sptr); + void set_fp_gpio(gpio_core_200::sptr, const gpio_attr_t, const boost::uint32_t); + double _tick_rate; double get_tick_rate(void){return _tick_rate;} double set_tick_rate(const double rate); + + /*! \brief Choose a tick rate (master clock rate) that works well for the given sampling rate. + * + * This function will try and choose a master clock rate automatically. + * See the function definition for details on the algorithm. + * + * The chosen tick rate is the largest multiple of two that is smaler + * than the max tick rate. + * The base rate is either given explicitly, or is the lcm() of the tx + * and rx sampling rates. In that case, it reads the rates directly + * from the property tree. It also tries to guess the number of channels + * (for the max possible tick rate) by checking the available streamers. + * This value, too, can explicitly be given. + * + * \param rate If this is given, it will be used as a minimum rate, or + * argument to lcm(). + * \param tree_dsp_path The sampling rate from this property tree path + * will be ignored. + * \param num_chans If given, specifies the number of channels. + */ + void set_auto_tick_rate( + const double rate=0, + const uhd::fs_path &tree_dsp_path="", + size_t num_chans=0 + ); + void update_tick_rate(const double); - void enforce_tick_rate_limits(size_t chan_count, double tick_rate, const char* direction = NULL); + + /*! Check if \p tick_rate works with \p chan_count channels. + * + * Throws a uhd::value_error if not. + */ + void enforce_tick_rate_limits(size_t chan_count, double tick_rate, const std::string &direction = ""); void check_tick_rate_with_current_streamers(double rate); + /*! Return the max number of channels on active rx_streamer or tx_streamer objects associated with this device. + * + * \param direction Set to "TX" to only check tx_streamers, "RX" to only check + * rx_streamers. Any other value will check if \e any active + * streamers are available. + * \return Return the number of tx streamers (direction=="TX"), the number of rx + * streamers (direction=="RX") or the total number of streamers. + */ + size_t max_chan_count(const std::string &direction=""); + + //! Coercer, attached to the "rate/value" property on the rx dsps. + double coerce_rx_samp_rate(rx_dsp_core_3000::sptr, size_t, const double); void update_rx_samp_rate(const size_t, const double); + + //! Coercer, attached to the "rate/value" property on the tx dsps. + double coerce_tx_samp_rate(tx_dsp_core_3000::sptr, size_t, const double); void update_tx_samp_rate(const size_t, const double); }; diff --git a/host/lib/usrp/b200/b200_io_impl.cpp b/host/lib/usrp/b200/b200_io_impl.cpp index 1e11e7ff6..4aa1a46af 100644 --- a/host/lib/usrp/b200/b200_io_impl.cpp +++ b/host/lib/usrp/b200/b200_io_impl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2012-2013 Ettus Research LLC +// Copyright 2012-2014 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -21,8 +21,10 @@ #include "../../transport/super_recv_packet_handler.hpp" #include "../../transport/super_send_packet_handler.hpp" #include "async_packet_handler.hpp" +#include <uhd/utils/math.hpp> #include <boost/bind.hpp> #include <boost/make_shared.hpp> +#include <boost/math/common_factor.hpp> #include <set> using namespace uhd; @@ -34,30 +36,32 @@ using namespace uhd::transport; **********************************************************************/ void b200_impl::check_tick_rate_with_current_streamers(double rate) { - size_t max_tx_chan_count = 0, max_rx_chan_count = 0; + // Defined in b200_impl.cpp + enforce_tick_rate_limits(max_chan_count("RX"), rate, "RX"); + enforce_tick_rate_limits(max_chan_count("TX"), rate, "TX"); +} + +// direction can either be "TX", "RX", or empty (default) +size_t b200_impl::max_chan_count(const std::string &direction /* = "" */) +{ + size_t max_count = 0; BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs) { - { + if ((direction == "RX" or direction.empty()) and not perif.rx_streamer.expired()) { boost::shared_ptr<sph::recv_packet_streamer> rx_streamer = boost::dynamic_pointer_cast<sph::recv_packet_streamer>(perif.rx_streamer.lock()); - if (rx_streamer) - max_rx_chan_count = std::max(max_rx_chan_count, rx_streamer->get_num_channels()); + max_count = std::max(max_count, rx_streamer->get_num_channels()); } - - { + if ((direction == "TX" or direction.empty()) and not perif.tx_streamer.expired()) { boost::shared_ptr<sph::send_packet_streamer> tx_streamer = boost::dynamic_pointer_cast<sph::send_packet_streamer>(perif.tx_streamer.lock()); - if (tx_streamer) - max_tx_chan_count = std::max(max_tx_chan_count, tx_streamer->get_num_channels()); + max_count = std::max(max_count, tx_streamer->get_num_channels()); } } - - // Defined in b200_impl.cpp - enforce_tick_rate_limits(max_rx_chan_count, rate, "RX"); - enforce_tick_rate_limits(max_tx_chan_count, rate, "TX"); + return max_count; } -void b200_impl::check_streamer_args(const uhd::stream_args_t &args, double tick_rate, const char* direction /*= NULL*/) +void b200_impl::check_streamer_args(const uhd::stream_args_t &args, double tick_rate, const std::string &direction /*= ""*/) { std::set<size_t> chans_set; for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++) @@ -69,33 +73,115 @@ void b200_impl::check_streamer_args(const uhd::stream_args_t &args, double tick_ enforce_tick_rate_limits(chans_set.size(), tick_rate, direction); // Defined in b200_impl.cpp } -void b200_impl::update_tick_rate(const double rate) +void b200_impl::set_auto_tick_rate( + const double rate, + const fs_path &tree_dsp_path, + size_t num_chans +) { + if (num_chans == 0) { // Divine them + num_chans = std::max(size_t(1), max_chan_count()); + } + const double max_tick_rate = ad9361_device_t::AD9361_MAX_CLOCK_RATE/num_chans; + if (rate != 0.0 and + (uhd::math::fp_compare::fp_compare_delta<double>(rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) > + uhd::math::fp_compare::fp_compare_delta<double>(max_tick_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ))) { + throw uhd::value_error(str( + boost::format("Requested sampling rate (%.2f Msps) exceeds maximum tick rate of %.2f MHz.") + % (rate / 1e6) % (max_tick_rate / 1e6) + )); + } + + // See also the doxygen documentation for these steps in b200_impl.hpp + // Step 1: Obtain LCM and max rate from all relevant dsps + boost::uint32_t lcm_rate = (rate == 0) ? 1 : static_cast<boost::uint32_t>(floor(rate + 0.5)); + for (int i = 0; i < 2; i++) { // Loop through rx and tx + std::string dir = (i == 0) ? "tx" : "rx"; + // We have no way of knowing which DSPs are used, so we check them all. + BOOST_FOREACH(const std::string &dsp_no, _tree->list(str(boost::format("/mboards/0/%s_dsps") % dir))) { + fs_path dsp_path = str(boost::format("/mboards/0/%s_dsps/%s") % dir % dsp_no); + if (dsp_path == tree_dsp_path) { + continue; + } + double this_dsp_rate = _tree->access<double>(dsp_path / "rate/value").get(); + // Check if the user selected something completely unreasonable: + if (uhd::math::fp_compare::fp_compare_delta<double>(this_dsp_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) > + uhd::math::fp_compare::fp_compare_delta<double>(max_tick_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ)) { + throw uhd::value_error(str( + boost::format("Requested sampling rate (%.2f Msps) exceeds maximum tick rate of %.2f MHz.") + % (this_dsp_rate / 1e6) % (max_tick_rate / 1e6) + )); + } + // If this_dsp_rate == 0.0, the sampling rate for this DSP hasn't been set, so + // we don't take that into consideration. + if (this_dsp_rate == 0.0) { + continue; + } + lcm_rate = boost::math::lcm<boost::uint32_t>( + lcm_rate, + static_cast<boost::uint32_t>(floor(this_dsp_rate + 0.5)) + ); + } + } + if (lcm_rate == 1) { + // In this case, no one has ever set a sampling rate. + return; + } + + double base_rate = static_cast<double>(lcm_rate); + try { + // Step 2: Get a good tick rate value + const double new_rate = _codec_mgr->get_auto_tick_rate(base_rate, num_chans); + // Step 3: Set the new tick rate value (if any change) + if (!uhd::math::frequencies_are_equal(_tree->access<double>("/mboards/0/tick_rate").get(), new_rate)) { + _tree->access<double>("/mboards/0/tick_rate").set(new_rate); + } + } catch (const uhd::value_error &e) { + UHD_MSG(warning) + << "Cannot automatically determine an appropriate tick rate for these sampling rates." << std::endl + << "Consider using different sampling rates, or manually specify a suitable master clock rate." << std::endl; + return; // Let the others handle this + } +} + +void b200_impl::update_tick_rate(const double new_tick_rate) { - check_tick_rate_with_current_streamers(rate); + check_tick_rate_with_current_streamers(new_tick_rate); BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs) { boost::shared_ptr<sph::recv_packet_streamer> my_streamer = boost::dynamic_pointer_cast<sph::recv_packet_streamer>(perif.rx_streamer.lock()); - if (my_streamer) my_streamer->set_tick_rate(rate); - perif.framer->set_tick_rate(_tick_rate); + if (my_streamer) my_streamer->set_tick_rate(new_tick_rate); + perif.framer->set_tick_rate(new_tick_rate); } BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs) { boost::shared_ptr<sph::send_packet_streamer> my_streamer = boost::dynamic_pointer_cast<sph::send_packet_streamer>(perif.tx_streamer.lock()); - if (my_streamer) my_streamer->set_tick_rate(rate); - perif.deframer->set_tick_rate(_tick_rate); + if (my_streamer) my_streamer->set_tick_rate(new_tick_rate); + perif.deframer->set_tick_rate(new_tick_rate); } } -#define CHECK_BANDWIDTH(dir) \ - if (rate > _codec_ctrl->get_bw_filter_range(dir).stop()) { \ - UHD_MSG(warning) \ - << "Selected " << dir << " bandwidth (" << (rate/1e6) << " MHz) exceeds\n" \ - << "analog frontend filter bandwidth (" << (_codec_ctrl->get_bw_filter_range(dir).stop()/1e6) << " MHz)." \ - << std::endl; \ +#define CHECK_RATE_AND_THROW(rate) \ + if (uhd::math::fp_compare::fp_compare_delta<double>(rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) > \ + uhd::math::fp_compare::fp_compare_delta<double>(ad9361_device_t::AD9361_MAX_CLOCK_RATE, uhd::math::FREQ_COMPARISON_DELTA_HZ)) { \ + throw uhd::value_error(str( \ + boost::format("Requested sampling rate (%.2f Msps) exceeds maximum tick rate.") \ + % (rate / 1e6) \ + )); \ + } + +double b200_impl::coerce_rx_samp_rate(rx_dsp_core_3000::sptr ddc, size_t dspno, const double rx_rate) +{ + // Have to set tick rate first, or the ddc will change the requested rate based on default tick rate + if (_tree->access<bool>("/mboards/0/auto_tick_rate").get()) { + CHECK_RATE_AND_THROW(rx_rate); + const std::string dsp_path = (boost::format("/mboards/0/rx_dsps/%s") % dspno).str(); + set_auto_tick_rate(rx_rate, dsp_path); } + return ddc->set_host_rate(rx_rate); +} void b200_impl::update_rx_samp_rate(const size_t dspno, const double rate) { @@ -105,7 +191,18 @@ void b200_impl::update_rx_samp_rate(const size_t dspno, const double rate) my_streamer->set_samp_rate(rate); const double adj = _radio_perifs[dspno].ddc->get_scaling_adjustment(); my_streamer->set_scale_factor(adj); - CHECK_BANDWIDTH("Rx"); + _codec_mgr->check_bandwidth(rate, "Rx"); +} + +double b200_impl::coerce_tx_samp_rate(tx_dsp_core_3000::sptr duc, size_t dspno, const double tx_rate) +{ + // Have to set tick rate first, or the duc will change the requested rate based on default tick rate + if (_tree->access<bool>("/mboards/0/auto_tick_rate").get()) { + CHECK_RATE_AND_THROW(tx_rate); + const std::string dsp_path = (boost::format("/mboards/0/tx_dsps/%s") % dspno).str(); + set_auto_tick_rate(tx_rate, dsp_path); + } + return duc->set_host_rate(tx_rate); } void b200_impl::update_tx_samp_rate(const size_t dspno, const double rate) @@ -116,7 +213,7 @@ void b200_impl::update_tx_samp_rate(const size_t dspno, const double rate) my_streamer->set_samp_rate(rate); const double adj = _radio_perifs[dspno].duc->get_scaling_adjustment(); my_streamer->set_scale_factor(adj); - CHECK_BANDWIDTH("Tx"); + _codec_mgr->check_bandwidth(rate, "Tx"); } /*********************************************************************** @@ -131,7 +228,7 @@ uhd::usrp::subdev_spec_t b200_impl::coerce_subdev_spec(const uhd::usrp::subdev_s // // Any other spec is probably illegal and will be caught by // validate_subdev_spec(). - if (spec.size() and _b200_type == B200 and spec[0].sd_name == "B") { + if (spec.size() and (_product == B200 or _product == B205) and spec[0].sd_name == "B") { spec[0].sd_name = "A"; } return spec; @@ -276,6 +373,9 @@ rx_streamer::sptr b200_impl::get_rx_stream(const uhd::stream_args_t &args_) if (args.otw_format.empty()) args.otw_format = "sc16"; args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; + if (_tree->access<bool>("/mboards/0/auto_tick_rate").get()) { + set_auto_tick_rate(0, "", args.channels.size()); + } check_streamer_args(args, this->get_tick_rate(), "RX"); boost::shared_ptr<sph::recv_packet_streamer> my_streamer; @@ -383,7 +483,10 @@ tx_streamer::sptr b200_impl::get_tx_stream(const uhd::stream_args_t &args_) if (args.otw_format.empty()) args.otw_format = "sc16"; args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; - check_streamer_args(args, this->get_tick_rate(), "TX"); + if (_tree->access<bool>("/mboards/0/auto_tick_rate").get()) { + set_auto_tick_rate(0, "", args.channels.size()); + } + check_streamer_args(args, this->get_tick_rate(), "RX"); boost::shared_ptr<sph::send_packet_streamer> my_streamer; for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++) diff --git a/host/lib/usrp/b200/b200_regs.hpp b/host/lib/usrp/b200/b200_regs.hpp index 900651f94..8f2dd03f3 100644 --- a/host/lib/usrp/b200/b200_regs.hpp +++ b/host/lib/usrp/b200/b200_regs.hpp @@ -46,11 +46,13 @@ localparam SR_TX_DSP = 184; localparam SR_TIME = 128; localparam SR_RX_FMT = 136; localparam SR_TX_FMT = 138; +localparam SR_FP_GPIO = 200; localparam RB32_TEST = 0; localparam RB64_TIME_NOW = 8; localparam RB64_TIME_PPS = 16; localparam RB64_CODEC_READBACK = 24; +localparam RB32_FP_GPIO = 32; //pll constants static const int AD9361_SLAVENO = (1 << 0); diff --git a/host/lib/usrp/common/CMakeLists.txt b/host/lib/usrp/common/CMakeLists.txt index 129cc569b..e63a09935 100644 --- a/host/lib/usrp/common/CMakeLists.txt +++ b/host/lib/usrp/common/CMakeLists.txt @@ -33,6 +33,7 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/adf4001_ctrl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/adf435x_common.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ad9361_ctrl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ad936x_manager.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ad9361_driver/ad9361_device.cpp ${CMAKE_CURRENT_SOURCE_DIR}/apply_corrections.cpp ${CMAKE_CURRENT_SOURCE_DIR}/validate_subdev_spec.cpp diff --git a/host/lib/usrp/common/ad9361_ctrl.cpp b/host/lib/usrp/common/ad9361_ctrl.cpp index 65e8e2df9..54f0fcdbf 100644 --- a/host/lib/usrp/common/ad9361_ctrl.cpp +++ b/host/lib/usrp/common/ad9361_ctrl.cpp @@ -16,7 +16,6 @@ // #include "ad9361_ctrl.hpp" -#include <uhd/exception.hpp> #include <uhd/types/ranges.hpp> #include <uhd/utils/msg.hpp> #include <uhd/types/serial.hpp> @@ -108,6 +107,27 @@ public: return _device.set_gain(direction, chain, value); } + void set_agc(const std::string &which, bool enable) + { + boost::lock_guard<boost::mutex> lock(_mutex); + + ad9361_device_t::chain_t chain =_get_chain_from_antenna(which); + _device.set_agc(chain, enable); + } + + void set_agc_mode(const std::string &which, const std::string &mode) + { + boost::lock_guard<boost::mutex> lock(_mutex); + ad9361_device_t::chain_t chain =_get_chain_from_antenna(which); + if(mode == "slow") { + _device.set_agc_mode(chain, ad9361_device_t::GAIN_MODE_SLOW_AGC); + } else if (mode == "fast"){ + _device.set_agc_mode(chain, ad9361_device_t::GAIN_MODE_FAST_AGC); + } else { + throw uhd::runtime_error("ad9361_ctrl got an invalid AGC option."); + } + } + //! set a new clock rate, return the exact value double set_clock_rate(const double rate) { @@ -175,6 +195,67 @@ public: return sensor_value_t("RSSI", _device.get_rssi(chain), "dB"); } + //! read the internal temp sensor. Average over 3 results + sensor_value_t get_temperature() + { + return sensor_value_t("temp", _device.get_average_temperature(), "C"); + } + + void set_dc_offset_auto(const std::string &which, const bool on) + { + boost::lock_guard<boost::mutex> lock(_mutex); + + ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); + _device.set_dc_offset_auto(direction,on); + } + + void set_iq_balance_auto(const std::string &which, const bool on) + { + boost::lock_guard<boost::mutex> lock(_mutex); + + ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); + _device.set_iq_balance_auto(direction,on); + } + + double set_bw_filter(const std::string &which, const double bw) + { + boost::lock_guard<boost::mutex> lock(_mutex); + + ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); + return _device.set_bw_filter(direction, bw); + } + + std::vector<std::string> get_filter_names(const std::string &which) + { + boost::lock_guard<boost::mutex> lock(_mutex); + + ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); + return _device.get_filter_names(direction); + } + + filter_info_base::sptr get_filter(const std::string &which, const std::string &filter_name) + { + boost::lock_guard<boost::mutex> lock(_mutex); + + ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); + ad9361_device_t::chain_t chain =_get_chain_from_antenna(which); + return _device.get_filter(direction, chain, filter_name); + } + + void set_filter(const std::string &which, const std::string &filter_name, const filter_info_base::sptr filter) + { + boost::lock_guard<boost::mutex> lock(_mutex); + + ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); + ad9361_device_t::chain_t chain = _get_chain_from_antenna(which); + _device.set_filter(direction, chain, filter_name, filter); + } + + void output_digital_test_tone(bool enb) + { + _device.digital_test_tone(enb); + } + private: static ad9361_device_t::direction_t _get_direction_from_antenna(const std::string& antenna) { diff --git a/host/lib/usrp/common/ad9361_ctrl.hpp b/host/lib/usrp/common/ad9361_ctrl.hpp index b7d7b8e26..5c438ee9c 100644 --- a/host/lib/usrp/common/ad9361_ctrl.hpp +++ b/host/lib/usrp/common/ad9361_ctrl.hpp @@ -22,15 +22,31 @@ #include <uhd/types/ranges.hpp> #include <uhd/types/serial.hpp> #include <uhd/types/sensors.hpp> +#include <uhd/exception.hpp> #include <boost/shared_ptr.hpp> #include <ad9361_device.h> #include <string> +#include <complex> +#include <uhd/types/filters.hpp> +#include <vector> namespace uhd { namespace usrp { -/*********************************************************************** - * AD9361 Control Interface - **********************************************************************/ +/*! AD936x Control Interface + * + * This is a convenient way to access the AD936x RF IC. + * It basically encodes knowledge of register values etc. into + * accessible API calls. + * + * \section ad936x_which The `which` parameter + * + * Many function calls require a `which` parameter to select + * the RF frontend. Valid values for `which` are: + * - RX1, RX2 + * - TX1, TX2 + * + * Frontend numbering is as designed by the AD9361. + */ class ad9361_ctrl : public boost::noncopyable { public: @@ -77,15 +93,18 @@ public: return uhd::meta_range_t(5e6, ad9361_device_t::AD9361_MAX_CLOCK_RATE); //5 MHz DCM low end } - //! set the filter bandwidth for the frontend - double set_bw_filter(const std::string &/*which*/, const double /*bw*/) - { - return 56e6; //TODO - } + //! set the filter bandwidth for the frontend's analog low pass + virtual double set_bw_filter(const std::string &/*which*/, const double /*bw*/) = 0; //! set the gain for a particular gain element virtual double set_gain(const std::string &which, const double value) = 0; + //! Enable or disable the AGC module + virtual void set_agc(const std::string &which, bool enable) = 0; + + //! configure the AGC module to slow or fast mode + virtual void set_agc_mode(const std::string &which, const std::string &mode) = 0; + //! set a new clock rate, return the exact value virtual double set_clock_rate(const double rate) = 0; @@ -95,14 +114,48 @@ public: //! tune the given frontend, return the exact value virtual double tune(const std::string &which, const double value) = 0; + //! set the DC offset for I and Q manually + void set_dc_offset(const std::string &, const std::complex<double>) + { + //This feature should not be used according to Analog Devices + throw uhd::runtime_error("ad9361_ctrl::set_dc_offset this feature is not supported on this device."); + } + + //! enable or disable the BB/RF DC tracking feature + virtual void set_dc_offset_auto(const std::string &which, const bool on) = 0; + + //! set the IQ correction value manually + void set_iq_balance(const std::string &, const std::complex<double>) + { + //This feature should not be used according to Analog Devices + throw uhd::runtime_error("ad9361_ctrl::set_iq_balance this feature is not supported on this device."); + } + + //! enable or disable the quadrature calibration + virtual void set_iq_balance_auto(const std::string &which, const bool on) = 0; + //! get the current frequency for the given frontend virtual double get_freq(const std::string &which) = 0; - //! turn on/off data port loopback + //! turn on/off Catalina's data port loopback virtual void data_port_loopback(const bool on) = 0; //! read internal RSSI sensor virtual sensor_value_t get_rssi(const std::string &which) = 0; + + //! read the internal temp sensor + virtual sensor_value_t get_temperature() = 0; + + //! List all available filters by name + virtual std::vector<std::string> get_filter_names(const std::string &which) = 0; + + //! Return a list of all filters + virtual filter_info_base::sptr get_filter(const std::string &which, const std::string &filter_name) = 0; + + //! Write back a filter + virtual void set_filter(const std::string &which, const std::string &filter_name, const filter_info_base::sptr) = 0; + + virtual void output_digital_test_tone(bool enb) = 0; }; }} diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_client.h b/host/lib/usrp/common/ad9361_driver/ad9361_client.h index 5e848d4c0..e9ea1404a 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_client.h +++ b/host/lib/usrp/common/ad9361_driver/ad9361_client.h @@ -1,5 +1,18 @@ // -// Copyright 2014 Ettus Research LLC +// Copyright 2014 Ettus Research +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. // #ifndef INCLUDED_AD9361_CLIENT_H diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp b/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp index db5de52d0..0a8a61575 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp +++ b/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp @@ -1,5 +1,18 @@ // -// Copyright 2014 Ettus Research LLC +// Copyright 2014 Ettus Research +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. // #include "ad9361_filter_taps.h" @@ -11,6 +24,7 @@ #include <cmath> #include <uhd/exception.hpp> #include <uhd/utils/log.hpp> +#include <uhd/utils/msg.hpp> #include <boost/cstdint.hpp> #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/thread/thread.hpp> @@ -78,16 +92,21 @@ int get_num_taps(int max_num_taps) { const double ad9361_device_t::AD9361_MAX_GAIN = 89.75; const double ad9361_device_t::AD9361_MAX_CLOCK_RATE = 61.44e6; +const double ad9361_device_t::AD9361_CAL_VALID_WINDOW = 100e6; // Max bandwdith is due to filter rolloff in analog filter stage const double ad9361_device_t::AD9361_RECOMMENDED_MAX_BANDWIDTH = 56e6; +/* Startup RF frequencies */ +const double ad9361_device_t::DEFAULT_RX_FREQ = 800e6; +const double ad9361_device_t::DEFAULT_TX_FREQ = 850e6; + /* Program either the RX or TX FIR filter. * * The process is the same for both filters, but the function must be told * how many taps are in the filter, and given a vector of the taps * themselves. */ -void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, boost::uint16_t *coeffs) +void ad9361_device_t::_program_fir_filter(direction_t direction, chain_t chain, int num_taps, boost::uint16_t *coeffs) { boost::uint16_t base; @@ -102,8 +121,20 @@ void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, b /* Encode number of filter taps for programming register */ boost::uint8_t reg_numtaps = (((num_taps / 16) - 1) & 0x07) << 5; + boost::uint8_t reg_chain = 0; + switch (chain) { + case CHAIN_1: + reg_chain = 0x01 << 3; + break; + case CHAIN_2: + reg_chain = 0x02 << 3; + break; + default: + reg_chain = 0x03 << 3; + } + /* Turn on the filter clock. */ - _io_iface->poke8(base + 5, reg_numtaps | 0x1a); + _io_iface->poke8(base + 5, reg_numtaps | reg_chain | 0x02); boost::this_thread::sleep(boost::posix_time::milliseconds(1)); /* Zero the unused taps just in case they have stale data */ @@ -112,7 +143,7 @@ void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, b _io_iface->poke8(base + 0, addr); _io_iface->poke8(base + 1, 0x0); _io_iface->poke8(base + 2, 0x0); - _io_iface->poke8(base + 5, reg_numtaps | 0x1e); + _io_iface->poke8(base + 5, reg_numtaps | reg_chain | (1 << 1) | (1 << 2)); _io_iface->poke8(base + 4, 0x00); _io_iface->poke8(base + 4, 0x00); } @@ -122,7 +153,7 @@ void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, b _io_iface->poke8(base + 0, addr); _io_iface->poke8(base + 1, (coeffs[addr]) & 0xff); _io_iface->poke8(base + 2, (coeffs[addr] >> 8) & 0xff); - _io_iface->poke8(base + 5, reg_numtaps | 0x1e); + _io_iface->poke8(base + 5, reg_numtaps | reg_chain | (1 << 1) | (1 << 2)); _io_iface->poke8(base + 4, 0x00); _io_iface->poke8(base + 4, 0x00); } @@ -133,9 +164,9 @@ void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, b before the clock stops. Wait 4 sample clock periods after setting D2 high while that data writes into the table" */ - _io_iface->poke8(base + 5, reg_numtaps | 0x1A); + _io_iface->poke8(base + 5, reg_numtaps | reg_chain | (1 << 1)); if (direction == RX) { - _io_iface->poke8(base + 5, reg_numtaps | 0x18); + _io_iface->poke8(base + 5, reg_numtaps | reg_chain ); /* Rx Gain, set to prevent digital overflow/saturation in filters 0:+6dB, 1:0dB, 2:-6dB, 3:-12dB page 35 of UG-671 */ @@ -144,7 +175,7 @@ void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, b /* Tx Gain. bit[0]. set to prevent digital overflow/saturation in filters 0: 0dB, 1:-6dB page 25 of UG-671 */ - _io_iface->poke8(base + 5, reg_numtaps | 0x18); + _io_iface->poke8(base + 5, reg_numtaps | reg_chain ); } } @@ -175,7 +206,7 @@ void ad9361_device_t::_setup_rx_fir(size_t num_taps, boost::int32_t decimation) } } - _program_fir_filter(RX, num_taps, coeffs.get()); + _program_fir_filter(RX, CHAIN_BOTH, num_taps, coeffs.get()); } /* Program the TX FIR Filter. */ @@ -207,7 +238,7 @@ void ad9361_device_t::_setup_tx_fir(size_t num_taps, boost::int32_t interpolatio } } - _program_fir_filter(TX, num_taps, coeffs.get()); + _program_fir_filter(TX, CHAIN_BOTH, num_taps, coeffs.get()); } /*********************************************************************** @@ -282,16 +313,24 @@ void ad9361_device_t::_calibrate_synth_charge_pumps() * * Note that the filter calibration depends heavily on the baseband * bandwidth, so this must be re-done after any change to the RX sample - * rate. */ -double ad9361_device_t::_calibrate_baseband_rx_analog_filter() + * rate. + * UG570 Page 33 states that this filter should be calibrated to 1.4 * bbbw*/ +double ad9361_device_t::_calibrate_baseband_rx_analog_filter(double req_rfbw) { - /* For filter tuning, baseband BW is half the complex BW, and must be - * between 28e6 and 0.2e6. */ - double bbbw = _baseband_bw / 2.0; + double bbbw = req_rfbw / 2.0; + if(bbbw > _baseband_bw / 2.0) + { + UHD_LOG << "baseband bandwidth too large for current sample rate. Setting bandwidth to: "<<_baseband_bw; + bbbw = _baseband_bw / 2.0; + } + + /* Baseband BW must be between 28e6 and 0.143e6. + * Max filter BW is 39.2 MHz. 39.2 / 1.4 = 28 + * Min filter BW is 200kHz. 200 / 1.4 = 143 */ if (bbbw > 28e6) { bbbw = 28e6; - } else if (bbbw < 0.20e6) { - bbbw = 0.20e6; + } else if (bbbw < 0.143e6) { + bbbw = 0.143e6; } double rxtune_clk = ((1.4 * bbbw * 2 * M_PI) / M_LN2); @@ -340,16 +379,25 @@ double ad9361_device_t::_calibrate_baseband_rx_analog_filter() * * Note that the filter calibration depends heavily on the baseband * bandwidth, so this must be re-done after any change to the TX sample - * rate. */ -double ad9361_device_t::_calibrate_baseband_tx_analog_filter() + * rate. + * UG570 Page 32 states that this filter should be calibrated to 1.6 * bbbw*/ +double ad9361_device_t::_calibrate_baseband_tx_analog_filter(double req_rfbw) { - /* For filter tuning, baseband BW is half the complex BW, and must be - * between 28e6 and 0.2e6. */ - double bbbw = _baseband_bw / 2.0; + double bbbw = req_rfbw / 2.0; + + if(bbbw > _baseband_bw / 2.0) + { + UHD_LOG << "baseband bandwidth too large for current sample rate. Setting bandwidth to: "<<_baseband_bw; + bbbw = _baseband_bw / 2.0; + } + + /* Baseband BW must be between 20e6 and 0.391e6. + * Max filter BW is 32 MHz. 32 / 1.6 = 20 + * Min filter BW is 625 kHz. 625 / 1.6 = 391 */ if (bbbw > 20e6) { bbbw = 20e6; - } else if (bbbw < 0.625e6) { - bbbw = 0.625e6; + } else if (bbbw < 0.391e6) { + bbbw = 0.391e6; } double txtune_clk = ((1.6 * bbbw * 2 * M_PI) / M_LN2); @@ -386,16 +434,25 @@ double ad9361_device_t::_calibrate_baseband_tx_analog_filter() /* Calibrate the secondary TX filter. * * This filter also depends on the TX sample rate, so if a rate change is - * made, the previous calibration will no longer be valid. */ -void ad9361_device_t::_calibrate_secondary_tx_filter() + * made, the previous calibration will no longer be valid. + * UG570 Page 32 states that this filter should be calibrated to 5 * bbbw*/ +double ad9361_device_t::_calibrate_secondary_tx_filter(double req_rfbw) { - /* For filter tuning, baseband BW is half the complex BW, and must be - * between 20e6 and 0.53e6. */ - double bbbw = _baseband_bw / 2.0; + double bbbw = req_rfbw / 2.0; + + if(bbbw > _baseband_bw / 2.0) + { + UHD_LOG << "baseband bandwidth too large for current sample rate. Setting bandwidth to: "<<_baseband_bw; + bbbw = _baseband_bw / 2.0; + } + + /* Baseband BW must be between 20e6 and 0.54e6. + * Max filter BW is 100 MHz. 100 / 5 = 20 + * Min filter BW is 2.7 MHz. 2.7 / 5 = 0.54 */ if (bbbw > 20e6) { bbbw = 20e6; - } else if (bbbw < 0.53e6) { - bbbw = 0.53e6; + } else if (bbbw < 0.54e6) { + bbbw = 0.54e6; } double bbbw_mhz = bbbw / 1e6; @@ -456,13 +513,17 @@ void ad9361_device_t::_calibrate_secondary_tx_filter() _io_iface->poke8(0x0d2, reg0d2); _io_iface->poke8(0x0d1, reg0d1); _io_iface->poke8(0x0d0, reg0d0); + + return bbbw; } /* Calibrate the RX TIAs. * * Note that the values in the TIA register, after calibration, vary with - * the RX gain settings. */ -void ad9361_device_t::_calibrate_rx_TIAs() + * the RX gain settings. + * We do not really program the BW here. Most settings are taken form the BB LPF registers + * UG570 page 33 states that this filter should be calibrated to 2.5 * bbbw */ +double ad9361_device_t::_calibrate_rx_TIAs(double req_rfbw) { boost::uint8_t reg1eb = _io_iface->peek8(0x1eb) & 0x3F; boost::uint8_t reg1ec = _io_iface->peek8(0x1ec) & 0x7F; @@ -473,13 +534,21 @@ void ad9361_device_t::_calibrate_rx_TIAs() boost::uint8_t reg1de = 0x00; boost::uint8_t reg1df = 0x00; - /* For calibration, baseband BW is half the complex BW, and must be - * between 28e6 and 0.2e6. */ - double bbbw = _baseband_bw / 2.0; - if (bbbw > 20e6) { - bbbw = 20e6; - } else if (bbbw < 0.20e6) { - bbbw = 0.20e6; + double bbbw = req_rfbw / 2.0; + + if(bbbw > _baseband_bw / 2.0) + { + UHD_LOG << "baseband bandwidth too large for current sample rate. Setting bandwidth to: "<<_baseband_bw; + bbbw = _baseband_bw / 2.0; + } + + /* Baseband BW must be between 28e6 and 0.4e6. + * Max filter BW is 70 MHz. 70 / 2.5 = 28 + * Min filter BW is 1 MHz. 1 / 2.5 = 0.4*/ + if (bbbw > 28e6) { + bbbw = 28e6; + } else if (bbbw < 0.40e6) { + bbbw = 0.40e6; } double ceil_bbbw_mhz = std::ceil(bbbw / 1e6); @@ -520,6 +589,8 @@ void ad9361_device_t::_calibrate_rx_TIAs() _io_iface->poke8(0x1df, reg1df); _io_iface->poke8(0x1dc, reg1dc); _io_iface->poke8(0x1de, reg1de); + + return bbbw; } /* Setup the AD9361 ADC. @@ -651,11 +722,12 @@ void ad9361_device_t::_setup_adc() } /* Calibrate the baseband DC offset. - * - * Note that this function is called from within the TX quadrature - * calibration function! */ + * Disables tracking + */ void ad9361_device_t::_calibrate_baseband_dc_offset() { + _io_iface->poke8(0x18b, 0x83); //Reset RF DC tracking flag + _io_iface->poke8(0x193, 0x3f); // Calibration settings _io_iface->poke8(0x190, 0x0f); // Set tracking coefficient //write_ad9361_reg(device, 0x190, /*0x0f*//*0xDF*/0x80*1 | 0x40*1 | (16+8/*+4*/)); // Set tracking coefficient: don't *4 counter, do decim /4, increased gain shift @@ -675,9 +747,8 @@ void ad9361_device_t::_calibrate_baseband_dc_offset() } /* Calibrate the RF DC offset. - * - * Note that this function is called from within the TX quadrature - * calibration function. */ + * Disables tracking + */ void ad9361_device_t::_calibrate_rf_dc_offset() { /* Some settings are frequency-dependent. */ @@ -692,7 +763,7 @@ void ad9361_device_t::_calibrate_rf_dc_offset() } _io_iface->poke8(0x185, 0x20); // RF DC Offset wait count - _io_iface->poke8(0x18b, 0x83); + _io_iface->poke8(0x18b, 0x83); // Disable tracking _io_iface->poke8(0x189, 0x30); /* Run the calibration! */ @@ -706,30 +777,70 @@ void ad9361_device_t::_calibrate_rf_dc_offset() count++; boost::this_thread::sleep(boost::posix_time::milliseconds(50)); } + + _io_iface->poke8(0x18b, 0x8d); // Enable RF DC tracking +} + +void ad9361_device_t::_configure_bb_dc_tracking() +{ + if (_use_dc_offset_tracking) + _io_iface->poke8(0x18b, 0xad); // Enable BB tracking + else + _io_iface->poke8(0x18b, 0x8d); // Disable BB tracking } -/* Start the RX quadrature calibration. +void ad9361_device_t::_configure_rx_iq_tracking() +{ + if (_use_iq_balance_tracking) + _io_iface->poke8(0x169, 0xcf); // Enable Rx IQ tracking + else + _io_iface->poke8(0x169, 0xc0); // Disable Rx IQ tracking +} + +/* Single shot Rx quadrature calibration * - * Note that we are using AD9361's 'tracking' feature for RX quadrature - * calibration, so once it starts it continues to free-run during operation. - * It should be re-run for large frequency changes. */ + * Procedure documented in "AD9361 Calibration Guide". Prior to calibration, + * state should be set to ALERT, FDD, and Dual Synth Mode. Rx quadrature + * tracking will be disabled, so run before or instead of enabling Rx + * quadrature tracking. + */ void ad9361_device_t::_calibrate_rx_quadrature() { /* Configure RX Quadrature calibration settings. */ _io_iface->poke8(0x168, 0x03); // Set tone level for cal _io_iface->poke8(0x16e, 0x25); // RX Gain index to use for cal _io_iface->poke8(0x16a, 0x75); // Set Kexp phase - _io_iface->poke8(0x16b, 0x15); // Set Kexp amplitude - _io_iface->poke8(0x169, 0xcf); // Continuous tracking mode - _io_iface->poke8(0x18b, 0xad); + _io_iface->poke8(0x16b, 0x95); // Set Kexp amplitude + _io_iface->poke8(0x057, 0x33); // Power down Tx mixer + _io_iface->poke8(0x169, 0xc0); // Disable tracking and free run mode + + /* Place Tx LO within passband of Rx spectrum */ + double current_tx_freq = _tx_freq; + _tune_helper(TX, _rx_freq + _rx_bb_lp_bw / 2.0); + + size_t count = 0; + _io_iface->poke8(0x016, 0x20); + while (_io_iface->peek8(0x016) & 0x20) { + if (count > 100) { + throw uhd::runtime_error("[ad9361_device_t] Rx Quadrature Calibration Failure"); + break; + } + count++; + boost::this_thread::sleep(boost::posix_time::milliseconds(5)); + } + + _io_iface->poke8(0x057, 0x30); // Re-enable Tx mixers + + _tune_helper(TX, current_tx_freq); } -/* TX quadtrature calibration routine. +/* TX quadrature calibration routine. * * The TX quadrature needs to be done twice, once for each TX chain, with * only one register change in between. Thus, this function enacts the * calibrations, and it is called from calibrate_tx_quadrature. */ void ad9361_device_t::_tx_quadrature_cal_routine() { + /* This is a weird process, but here is how it works: * 1) Read the calibrated NCO frequency bits out of 0A3. * 2) Write the two bits to the RX NCO freq part of 0A0. @@ -765,7 +876,7 @@ void ad9361_device_t::_tx_quadrature_cal_routine() { /* The gain table index used for calibration must be adjusted for the * mid-table to get a TIA index = 1 and LPF index = 0. */ - if ((_rx_freq >= 1300e6) && (_rx_freq < 4000e6)) { + if (_rx_freq < 1300e6) { _io_iface->poke8(0x0aa, 0x22); // Cal gain table index } else { _io_iface->poke8(0x0aa, 0x25); // Cal gain table index @@ -774,12 +885,6 @@ void ad9361_device_t::_tx_quadrature_cal_routine() { _io_iface->poke8(0x0a4, 0xf0); // Cal setting conut _io_iface->poke8(0x0ae, 0x00); // Cal LPF gain index (split mode) - /* First, calibrate the baseband DC offset. */ - _calibrate_baseband_dc_offset(); - - /* Second, calibrate the RF DC offset. */ - _calibrate_rf_dc_offset(); - /* Now, calibrate the TX quadrature! */ size_t count = 0; _io_iface->poke8(0x016, 0x10); @@ -794,9 +899,7 @@ void ad9361_device_t::_tx_quadrature_cal_routine() { } /* Run the TX quadrature calibration. - * - * Note that from within this function we are also triggering the baseband - * and RF DC calibrations. */ + */ void ad9361_device_t::_calibrate_tx_quadrature() { /* Make sure we are, in fact, in the ALERT state. If not, something is @@ -880,7 +983,7 @@ void ad9361_device_t::_program_mixer_gm_subtable() void ad9361_device_t::_program_gain_table() { /* Figure out which gain table we should be using for our current * frequency band. */ - boost::uint8_t (*gain_table)[5] = NULL; + boost::uint8_t (*gain_table)[3] = NULL; boost::uint8_t new_gain_table; if (_rx_freq < 1300e6) { gain_table = gain_table_sub_1300mhz; @@ -911,9 +1014,9 @@ void ad9361_device_t::_program_gain_table() { boost::uint8_t index = 0; for (; index < 77; index++) { _io_iface->poke8(0x130, index); - _io_iface->poke8(0x131, gain_table[index][1]); - _io_iface->poke8(0x132, gain_table[index][2]); - _io_iface->poke8(0x133, gain_table[index][3]); + _io_iface->poke8(0x131, gain_table[index][0]); + _io_iface->poke8(0x132, gain_table[index][1]); + _io_iface->poke8(0x133, gain_table[index][2]); _io_iface->poke8(0x137, 0x1E); _io_iface->poke8(0x134, 0x00); _io_iface->poke8(0x134, 0x00); @@ -939,28 +1042,58 @@ void ad9361_device_t::_program_gain_table() { /* Setup gain control registers. * - * This really only needs to be done once, at initialization. */ -void ad9361_device_t::_setup_gain_control() + * This really only needs to be done once, at initialization. + * If AGC is used the mode select bits (Reg 0x0FA) must be written manually */ +void ad9361_device_t::_setup_gain_control(bool agc) { - _io_iface->poke8(0x0FA, 0xE0); // Gain Control Mode Select - _io_iface->poke8(0x0FB, 0x08); // Table, Digital Gain, Man Gain Ctrl - _io_iface->poke8(0x0FC, 0x23); // Incr Step Size, ADC Overrange Size - _io_iface->poke8(0x0FD, 0x4C); // Max Full/LMT Gain Table Index - _io_iface->poke8(0x0FE, 0x44); // Decr Step Size, Peak Overload Time - _io_iface->poke8(0x100, 0x6F); // Max Digital Gain - _io_iface->poke8(0x104, 0x2F); // ADC Small Overload Threshold - _io_iface->poke8(0x105, 0x3A); // ADC Large Overload Threshold - _io_iface->poke8(0x107, 0x31); // Large LMT Overload Threshold - _io_iface->poke8(0x108, 0x39); // Small LMT Overload Threshold - _io_iface->poke8(0x109, 0x23); // Rx1 Full/LMT Gain Index - _io_iface->poke8(0x10A, 0x58); // Rx1 LPF Gain Index - _io_iface->poke8(0x10B, 0x00); // Rx1 Digital Gain Index - _io_iface->poke8(0x10C, 0x23); // Rx2 Full/LMT Gain Index - _io_iface->poke8(0x10D, 0x18); // Rx2 LPF Gain Index - _io_iface->poke8(0x10E, 0x00); // Rx2 Digital Gain Index - _io_iface->poke8(0x114, 0x30); // Low Power Threshold - _io_iface->poke8(0x11A, 0x27); // Initial LMT Gain Limit - _io_iface->poke8(0x081, 0x00); // Tx Symbol Gain Control + /* The AGC mode configuration should be good for all cases. + * However, non AGC configuration still used for backward compatibility. */ + if (agc) { + /*mode select bits must be written before hand!*/ + _io_iface->poke8(0x0FB, 0x08); // Table, Digital Gain, Man Gain Ctrl + _io_iface->poke8(0x0FC, 0x23); // Incr Step Size, ADC Overrange Size + _io_iface->poke8(0x0FD, 0x4C); // Max Full/LMT Gain Table Index + _io_iface->poke8(0x0FE, 0x44); // Decr Step Size, Peak Overload Time + _io_iface->poke8(0x100, 0x6F); // Max Digital Gain + _io_iface->poke8(0x101, 0x0A); // Max Digital Gain + _io_iface->poke8(0x103, 0x08); // Max Digital Gain + _io_iface->poke8(0x104, 0x2F); // ADC Small Overload Threshold + _io_iface->poke8(0x105, 0x3A); // ADC Large Overload Threshold + _io_iface->poke8(0x106, 0x22); // Max Digital Gain + _io_iface->poke8(0x107, 0x2B); // Large LMT Overload Threshold + _io_iface->poke8(0x108, 0x31); + _io_iface->poke8(0x111, 0x0A); + _io_iface->poke8(0x11A, 0x1C); + _io_iface->poke8(0x120, 0x0C); + _io_iface->poke8(0x121, 0x44); + _io_iface->poke8(0x122, 0x44); + _io_iface->poke8(0x123, 0x11); + _io_iface->poke8(0x124, 0xF5); + _io_iface->poke8(0x125, 0x3B); + _io_iface->poke8(0x128, 0x03); + _io_iface->poke8(0x129, 0x56); + _io_iface->poke8(0x12A, 0x22); + } else { + _io_iface->poke8(0x0FA, 0xE0); // Gain Control Mode Select + _io_iface->poke8(0x0FB, 0x08); // Table, Digital Gain, Man Gain Ctrl + _io_iface->poke8(0x0FC, 0x23); // Incr Step Size, ADC Overrange Size + _io_iface->poke8(0x0FD, 0x4C); // Max Full/LMT Gain Table Index + _io_iface->poke8(0x0FE, 0x44); // Decr Step Size, Peak Overload Time + _io_iface->poke8(0x100, 0x6F); // Max Digital Gain + _io_iface->poke8(0x104, 0x2F); // ADC Small Overload Threshold + _io_iface->poke8(0x105, 0x3A); // ADC Large Overload Threshold + _io_iface->poke8(0x107, 0x31); // Large LMT Overload Threshold + _io_iface->poke8(0x108, 0x39); // Small LMT Overload Threshold + _io_iface->poke8(0x109, 0x23); // Rx1 Full/LMT Gain Index + _io_iface->poke8(0x10A, 0x58); // Rx1 LPF Gain Index + _io_iface->poke8(0x10B, 0x00); // Rx1 Digital Gain Index + _io_iface->poke8(0x10C, 0x23); // Rx2 Full/LMT Gain Index + _io_iface->poke8(0x10D, 0x18); // Rx2 LPF Gain Index + _io_iface->poke8(0x10E, 0x00); // Rx2 Digital Gain Index + _io_iface->poke8(0x114, 0x30); // Low Power Threshold + _io_iface->poke8(0x11A, 0x27); // Initial LMT Gain Limit + _io_iface->poke8(0x081, 0x00); // Tx Symbol Gain Control + } } /* Setup the RX or TX synthesizers. @@ -1153,16 +1286,16 @@ double ad9361_device_t::_tune_helper(direction_t direction, const double value) /* Set band-specific settings. */ if (value < _client_params->get_band_edge(AD9361_RX_BAND0)) { - _regs.inputsel = (_regs.inputsel & 0xC0) | 0x30; + _regs.inputsel = (_regs.inputsel & 0xC0) | 0x30; // Port C, balanced } else if ((value >= _client_params->get_band_edge(AD9361_RX_BAND0)) && (value < _client_params->get_band_edge(AD9361_RX_BAND1))) { - _regs.inputsel = (_regs.inputsel & 0xC0) | 0x0C; + _regs.inputsel = (_regs.inputsel & 0xC0) | 0x0C; // Port B, balanced } else if ((value >= _client_params->get_band_edge(AD9361_RX_BAND1)) && (value <= 6e9)) { - _regs.inputsel = (_regs.inputsel & 0xC0) | 0x03; + _regs.inputsel = (_regs.inputsel & 0xC0) | 0x03; // Port A, balanced } else { throw uhd::runtime_error("[ad9361_device_t] [_tune_helper] INVALID_CODE_PATH"); } @@ -1257,6 +1390,7 @@ double ad9361_device_t::_setup_rates(const double rate) int divfactor = 0; _tfir_factor = 0; _rfir_factor = 0; + if (rate < 0.33e6) { // RX1 + RX2 enabled, 3, 2, 2, 4 _regs.rxfilt = B8(11101111); @@ -1397,8 +1531,8 @@ void ad9361_device_t::initialize() _regs.bbftune_mode = 0x1e; /* Initialize private VRQ fields. */ - _rx_freq = 0.0; - _tx_freq = 0.0; + _rx_freq = DEFAULT_RX_FREQ; + _tx_freq = DEFAULT_TX_FREQ; _req_rx_freq = 0.0; _req_tx_freq = 0.0; _baseband_bw = 0.0; @@ -1412,6 +1546,18 @@ void ad9361_device_t::initialize() _rx2_gain = 0; _tx1_gain = 0; _tx2_gain = 0; + _use_dc_offset_tracking = true; + _use_iq_balance_tracking = true; + _rx1_agc_mode = GAIN_MODE_SLOW_AGC; + _rx2_agc_mode = GAIN_MODE_SLOW_AGC; + _rx1_agc_enable = false; + _rx2_agc_enable = false; + _rx_analog_bw = 0; + _tx_analog_bw = 0; + _rx_tia_lp_bw = 0; + _tx_sec_lp_bw = 0; + _rx_bb_lp_bw = 0; + _tx_bb_lp_bw = 0; /* Reset the device. */ _io_iface->poke8(0x000, 0x01); @@ -1490,7 +1636,6 @@ void ad9361_device_t::initialize() _io_iface->poke8(0x019, 0x00); // AuxDAC2 Word[9:2] _io_iface->poke8(0x01A, 0x00); // AuxDAC1 Config and Word[1:0] _io_iface->poke8(0x01B, 0x00); // AuxDAC2 Config and Word[1:0] - _io_iface->poke8(0x022, 0x4A); // Invert Bypassed LNA _io_iface->poke8(0x023, 0xFF); // AuxDAC Manaul/Auto Control _io_iface->poke8(0x026, 0x00); // AuxDAC Manual Select Bit/GPO Manual Select _io_iface->poke8(0x030, 0x00); // AuxDAC1 Rx Delay @@ -1498,10 +1643,18 @@ void ad9361_device_t::initialize() _io_iface->poke8(0x032, 0x00); // AuxDAC2 Rx Delay _io_iface->poke8(0x033, 0x00); // AuxDAC2 Tx Delay + /* LNA bypass polarity inversion + * According to the register map, we should invert the bypass path to + * match LNA phase. Extensive testing, however, shows otherwise and that + * to align bypass and LNA phases, the bypass inversion switch should be + * turned off. + */ + _io_iface->poke8(0x022, 0x0A); + /* Setup AuxADC */ _io_iface->poke8(0x00B, 0x00); // Temp Sensor Setup (Offset) _io_iface->poke8(0x00C, 0x00); // Temp Sensor Setup (Temp Window) - _io_iface->poke8(0x00D, 0x03); // Temp Sensor Setup (Periodic Measure) + _io_iface->poke8(0x00D, 0x00); // Temp Sensor Setup (Manual Measure) _io_iface->poke8(0x00F, 0x04); // Temp Sensor Setup (Decimation) _io_iface->poke8(0x01C, 0x10); // AuxADC Setup (Clock Div) _io_iface->poke8(0x01D, 0x01); // AuxADC Setup (Decimation/Enable) @@ -1550,23 +1703,34 @@ void ad9361_device_t::initialize() _calibrate_synth_charge_pumps(); - _tune_helper(RX, 800e6); - _tune_helper(TX, 850e6); + _tune_helper(RX, _rx_freq); + _tune_helper(TX, _tx_freq); _program_mixer_gm_subtable(); _program_gain_table(); - _setup_gain_control(); + _setup_gain_control(false); - _calibrate_baseband_rx_analog_filter(); - _calibrate_baseband_tx_analog_filter(); - _calibrate_rx_TIAs(); - _calibrate_secondary_tx_filter(); + set_bw_filter(RX, _baseband_bw); + set_bw_filter(TX, _baseband_bw); _setup_adc(); - _calibrate_tx_quadrature(); + _calibrate_baseband_dc_offset(); + _calibrate_rf_dc_offset(); _calibrate_rx_quadrature(); + /* + * Rx BB DC and IQ tracking are both disabled by calibration at this + * point. Only issue commands if tracking needs to be turned on. + */ + if (_use_dc_offset_tracking) + _configure_bb_dc_tracking(); + if (_use_iq_balance_tracking) + _configure_rx_iq_tracking(); + + _last_rx_cal_freq = _rx_freq; + _last_tx_cal_freq = _tx_freq; + // cals done, set PPORT config switch (_client_params->get_digital_interface_mode()) { case AD9361_DDR_FDD_LVCMOS: { @@ -1680,19 +1844,30 @@ double ad9361_device_t::set_clock_rate(const double req_rate) _program_mixer_gm_subtable(); _program_gain_table(); - _setup_gain_control(); + _setup_gain_control(false); _reprogram_gains(); - _calibrate_baseband_rx_analog_filter(); - _calibrate_baseband_tx_analog_filter(); - _calibrate_rx_TIAs(); - _calibrate_secondary_tx_filter(); + set_bw_filter(RX, _baseband_bw); + set_bw_filter(TX, _baseband_bw); _setup_adc(); - _calibrate_tx_quadrature(); + _calibrate_baseband_dc_offset(); + _calibrate_rf_dc_offset(); _calibrate_rx_quadrature(); + /* + * Rx BB DC and IQ tracking are both disabled by calibration at this + * point. Only issue commands if tracking needs to be turned on. + */ + if (_use_dc_offset_tracking) + _configure_bb_dc_tracking(); + if (_use_iq_balance_tracking) + _configure_rx_iq_tracking(); + + _last_rx_cal_freq = _rx_freq; + _last_tx_cal_freq = _tx_freq; + // cals done, set PPORT config switch (_client_params->get_digital_interface_mode()) { case AD9361_DDR_FDD_LVCMOS: { @@ -1793,6 +1968,14 @@ void ad9361_device_t::set_active_chains(bool tx1, bool tx2, bool rx1, bool rx2) _io_iface->poke8(0x002, _regs.txfilt); _io_iface->poke8(0x003, _regs.rxfilt); + /* + * Last unconditional Tx calibration point. Any later Tx calibration will + * require user intervention (currently triggered by tuning difference that + * is > 100 MHz). Late calibration provides better performance. + */ + if (tx1 | tx2) + _calibrate_tx_quadrature(); + /* Put back into FDD state if necessary */ if (set_back_to_fdd) _io_iface->poke8(0x014, 0x21); @@ -1808,17 +1991,18 @@ void ad9361_device_t::set_active_chains(bool tx1, bool tx2, bool rx1, bool rx2) double ad9361_device_t::tune(direction_t direction, const double value) { boost::lock_guard<boost::recursive_mutex> lock(_mutex); + double last_cal_freq; if (direction == RX) { if (freq_is_nearly_equal(value, _req_rx_freq)) { return _rx_freq; } - + last_cal_freq = _last_rx_cal_freq; } else if (direction == TX) { if (freq_is_nearly_equal(value, _req_tx_freq)) { return _tx_freq; } - + last_cal_freq = _last_tx_cal_freq; } else { throw uhd::runtime_error("[ad9361_device_t] [tune] INVALID_CODE_PATH"); } @@ -1843,9 +2027,30 @@ double ad9361_device_t::tune(direction_t direction, const double value) /* Update the gain settings. */ _reprogram_gains(); - /* Run the calibration algorithms. */ - _calibrate_tx_quadrature(); - _calibrate_rx_quadrature(); + /* + * Only run the following calibrations if we are more than 100MHz away + * from the previous Tx or Rx calibration point. Leave out single shot + * Rx quadrature unless Rx quad-cal is disabled. + */ + if (std::abs(last_cal_freq - tune_freq) > AD9361_CAL_VALID_WINDOW) { + /* Run the calibration algorithms. */ + if (direction == RX) { + _calibrate_rf_dc_offset(); + if (!_use_iq_balance_tracking) + _calibrate_rx_quadrature(); + if (_use_dc_offset_tracking) + _configure_bb_dc_tracking(); + + _last_rx_cal_freq = tune_freq; + } else { + _calibrate_tx_quadrature(); + _last_tx_cal_freq = tune_freq; + } + + /* Rx IQ tracking can be disabled on Rx or Tx re-calibration */ + if (_use_iq_balance_tracking) + _configure_rx_iq_tracking(); + } /* If we were in the FDD state, return it now. */ if (not_in_alert) { @@ -1922,7 +2127,7 @@ double ad9361_device_t::set_gain(direction_t direction, chain_t chain, const dou } } -void ad9361_device_t::output_test_tone() +void ad9361_device_t::output_test_tone() // On RF side! { boost::lock_guard<boost::recursive_mutex> lock(_mutex); /* Output a 480 kHz tone at 800 MHz */ @@ -1932,6 +2137,12 @@ void ad9361_device_t::output_test_tone() _io_iface->poke8(0x3FE, 0x3F); } +void ad9361_device_t::digital_test_tone(bool enb) // Digital output +{ + boost::lock_guard<boost::recursive_mutex> lock(_mutex); + _io_iface->poke8(0x3F4, 0x02 | (enb ? 0x01 : 0x00)); +} + void ad9361_device_t::data_port_loopback(const bool loopback_enabled) { boost::lock_guard<boost::recursive_mutex> lock(_mutex); @@ -1960,4 +2171,663 @@ double ad9361_device_t::get_rssi(chain_t chain) return rssi; } +/* + * Returns the reading of the internal temperature sensor. + * One point calibration of the sensor was done according to datasheet + * leading to the given default constant correction factor. + */ +double ad9361_device_t::_get_temperature(const double cal_offset, const double timeout) +{ + //set 0x01D[0] to 1 to disable AuxADC GPIO reading + boost::uint8_t tmp = 0; + tmp = _io_iface->peek8(0x01D); + _io_iface->poke8(0x01D, (tmp | 0x01)); + _io_iface->poke8(0x00B, 0); //set offset to 0 + + _io_iface->poke8(0x00C, 0x01); //start reading, clears bit 0x00C[1] + boost::posix_time::ptime start_time = boost::posix_time::microsec_clock::local_time(); + boost::posix_time::time_duration elapsed; + //wait for valid data (toggle of bit 1 in 0x00C) + while(((_io_iface->peek8(0x00C) >> 1) & 0x01) == 0) { + boost::this_thread::sleep(boost::posix_time::microseconds(100)); + elapsed = boost::posix_time::microsec_clock::local_time() - start_time; + if(elapsed.total_milliseconds() > (timeout*1000)) + { + throw uhd::runtime_error("[ad9361_device_t] timeout while reading temperature"); + } + } + _io_iface->poke8(0x00C, 0x00); //clear read flag + + boost::uint8_t temp = _io_iface->peek8(0x00E); //read temperature. + double tmp_temp = temp/1.140f; //according to ADI driver + tmp_temp = tmp_temp + cal_offset; //Constant offset acquired by one point calibration. + + return tmp_temp; +} + +double ad9361_device_t::get_average_temperature(const double cal_offset, const size_t num_samples) +{ + double d_temp = 0; + for(size_t i = 0; i < num_samples; i++) { + double tmp_temp = _get_temperature(cal_offset); + d_temp += (tmp_temp/num_samples); + } + return d_temp; +} + +/* + * Enable/Disable DC offset tracking + * + * Only disable BB tracking while leaving static RF and BB DC calibrations enabled. + * According to correspondance from ADI, turning off Rx BB DC tracking clears the + * correction words so we don't need to be concerned with leaving the calibration + * in a bad state upon disabling. Testing also confirms this behavior. + * + * Note that Rx IQ tracking does not show similar state clearing behavior when + * disabled. + */ +void ad9361_device_t::set_dc_offset_auto(direction_t direction, const bool on) +{ + if (direction == RX) { + _use_dc_offset_tracking = on; + _configure_bb_dc_tracking(); + } else { + throw uhd::runtime_error("[ad9361_device_t] [set_dc_offset_auto] Tx DC tracking not supported"); + } +} + +/* + * Enable/Disable IQ balance tracking + * + * Run static Rx quadrature calibration after disabling quadrature tracking. + * This avoids the situation where a user might disable tracking when the loop + * is in a confused state (e.g. at or near saturation). Otherwise, the + * calibration setting could be forced to and left in a bad state. + */ +void ad9361_device_t::set_iq_balance_auto(direction_t direction, const bool on) +{ + if (direction == RX) { + _use_iq_balance_tracking = on; + _configure_rx_iq_tracking(); + if (!on) { + _io_iface->poke8(0x014, 0x05); // ALERT mode + _calibrate_rx_quadrature(); + _io_iface->poke8(0x014, 0x21); // FDD mode + } + } else { + throw uhd::runtime_error("[ad9361_device_t] [set_iq_balance_auto] Tx IQ tracking not supported"); + } +} + +/* Sets the RX gain mode to be used. + * If a transition from an AGC to an non AGC mode occurs (or vice versa) + * the gain configuration will be reloaded. */ +void ad9361_device_t::_setup_agc(chain_t chain, gain_mode_t gain_mode) +{ + boost::uint8_t gain_mode_reg = 0; + boost::uint8_t gain_mode_prev = 0; + boost::uint8_t gain_mode_bits_pos = 0; + + gain_mode_reg = _io_iface->peek8(0x0FA); + gain_mode_prev = (gain_mode_reg & 0x0F); + + if (chain == CHAIN_1) { + gain_mode_bits_pos = 0; + } else if (chain == CHAIN_2) { + gain_mode_bits_pos = 2; + } else + { + throw uhd::runtime_error("[ad9361_device_t] Wrong value for chain"); + } + + gain_mode_reg = (gain_mode_reg & (~(0x03<<gain_mode_bits_pos))); //clear mode bits + switch (gain_mode) { + case GAIN_MODE_MANUAL: + //leave bits cleared + break; + case GAIN_MODE_SLOW_AGC: + gain_mode_reg = (gain_mode_reg | (0x02<<gain_mode_bits_pos)); + break; + case GAIN_MODE_FAST_AGC: + gain_mode_reg = (gain_mode_reg | (0x01<<gain_mode_bits_pos)); + break; + default: + throw uhd::runtime_error("[ad9361_device_t] Gain mode does not exist"); + } + _io_iface->poke8(0x0FA, gain_mode_reg); + boost::uint8_t gain_mode_status = _io_iface->peek8(0x0FA); + gain_mode_status = (gain_mode_status & 0x0F); + /*Check if gain mode configuration needs to be reprogrammed*/ + if (((gain_mode_prev == 0) && (gain_mode_status != 0)) || ((gain_mode_prev != 0) && (gain_mode_status == 0))) { + if (gain_mode_status == 0) { + /*load manual mode config*/ + _setup_gain_control(false); + } else { + /*load agc mode config*/ + _setup_gain_control(true); + } + } +} + +void ad9361_device_t::set_agc(chain_t chain, bool enable) +{ + if(chain == CHAIN_1) { + _rx1_agc_enable = enable; + if(enable) { + _setup_agc(chain, _rx1_agc_mode); + } else { + _setup_agc(chain, GAIN_MODE_MANUAL); + } + } else if (chain == CHAIN_2){ + _rx2_agc_enable = enable; + if(enable) { + _setup_agc(chain, _rx2_agc_mode); + } else { + _setup_agc(chain, GAIN_MODE_MANUAL); + } + } else + { + throw uhd::runtime_error("[ad9361_device_t] Wrong value for chain"); + } +} + +void ad9361_device_t::set_agc_mode(chain_t chain, gain_mode_t gain_mode) +{ + if(chain == CHAIN_1) { + _rx1_agc_mode = gain_mode; + if(_rx1_agc_enable) { + _setup_agc(chain, _rx1_agc_mode); + } + } else if(chain == CHAIN_2){ + _rx2_agc_mode = gain_mode; + if(_rx2_agc_enable) { + _setup_agc(chain, _rx2_agc_mode); + } + } else + { + throw uhd::runtime_error("[ad9361_device_t] Wrong value for chain"); + } +} + +std::vector<std::string> ad9361_device_t::get_filter_names(direction_t direction) +{ + std::vector<std::string> ret; + if(direction == RX) { + for(std::map<std::string, filter_query_helper>::iterator it = _rx_filters.begin(); it != _rx_filters.end(); ++it) { + ret.push_back(it->first); + } + } else if (direction == TX) + { + for(std::map<std::string, filter_query_helper>::iterator it = _tx_filters.begin(); it != _tx_filters.end(); ++it) { + ret.push_back(it->first); + } + } + return ret; +} + +filter_info_base::sptr ad9361_device_t::get_filter(direction_t direction, chain_t chain, const std::string &name) +{ + if(direction == RX) { + if (not _rx_filters[name].get) + { + throw uhd::runtime_error("ad9361_device_t::get_filter this filter can not be read."); + } + return _rx_filters[name].get(direction, chain); + } else if (direction == TX) { + if (not _tx_filters[name].get) + { + throw uhd::runtime_error("ad9361_device_t::get_filter this filter can not be read."); + } + return _tx_filters[name].get(direction, chain); + } + + throw uhd::runtime_error("ad9361_device_t::get_filter wrong direction parameter."); +} + +void ad9361_device_t::set_filter(direction_t direction, chain_t chain, const std::string &name, filter_info_base::sptr filter) +{ + + if(direction == RX) { + if(not _rx_filters[name].set) + { + throw uhd::runtime_error("ad9361_device_t::set_filter this filter can not be written."); + } + _rx_filters[name].set(direction, chain, filter); + } else if (direction == TX) { + if(not _tx_filters[name].set) + { + throw uhd::runtime_error("ad9361_device_t::set_filter this filter can not be written."); + } + _tx_filters[name].set(direction, chain, filter); + } + +} + +double ad9361_device_t::set_bw_filter(direction_t direction, const double rf_bw) +{ + //both low pass filters are programmed to the same bw. However, their cutoffs will differ. + //Together they should create the requested bb bw. + double set_analog_bb_bw = 0; + if(direction == RX) + { + _rx_bb_lp_bw = _calibrate_baseband_rx_analog_filter(rf_bw); //returns bb bw + _rx_tia_lp_bw = _calibrate_rx_TIAs(rf_bw); + _rx_analog_bw = _rx_bb_lp_bw; + set_analog_bb_bw = _rx_analog_bw; + } else { + _tx_bb_lp_bw = _calibrate_baseband_tx_analog_filter(rf_bw); //returns bb bw + _tx_sec_lp_bw = _calibrate_secondary_tx_filter(rf_bw); + _tx_analog_bw = _tx_bb_lp_bw; + set_analog_bb_bw = _tx_analog_bw; + } + return (2.0 * set_analog_bb_bw); +} + +void ad9361_device_t::_set_fir_taps(direction_t direction, chain_t chain, const std::vector<boost::int16_t>& taps) +{ + size_t num_taps = taps.size(); + size_t num_taps_avail = _get_num_fir_taps(direction); + if(num_taps == num_taps_avail) + { + boost::scoped_array<boost::uint16_t> coeffs(new boost::uint16_t[num_taps_avail]); + for (size_t i = 0; i < num_taps_avail; i++) + { + coeffs[i] = boost::uint16_t(taps[i]); + } + _program_fir_filter(direction, chain, num_taps_avail, coeffs.get()); + } else if(num_taps < num_taps_avail){ + throw uhd::runtime_error("ad9361_device_t::_set_fir_taps not enough coefficients."); + } else { + throw uhd::runtime_error("ad9361_device_t::_set_fir_taps too many coefficients."); + } +} + +size_t ad9361_device_t::_get_num_fir_taps(direction_t direction) +{ + boost::uint8_t num = 0; + if(direction == RX) + num = _io_iface->peek8(0x0F5); + else + num = _io_iface->peek8(0x065); + num = ((num >> 5) & 0x07); + return ((num + 1) * 16); +} + +size_t ad9361_device_t::_get_fir_dec_int(direction_t direction) +{ + boost::uint8_t dec_int = 0; + if(direction == RX) + dec_int = _io_iface->peek8(0x003); + else + dec_int = _io_iface->peek8(0x002); + /* + * 0 = dec/int by 1 and bypass filter + * 1 = dec/int by 1 + * 2 = dec/int by 2 + * 3 = dec/int by 4 */ + dec_int = (dec_int & 0x03); + if(dec_int == 3) + { + return 4; + } + return dec_int; +} + +std::vector<boost::int16_t> ad9361_device_t::_get_fir_taps(direction_t direction, chain_t chain) +{ + int base; + size_t num_taps = _get_num_fir_taps(direction); + boost::uint8_t config; + boost::uint8_t reg_numtaps = (((num_taps / 16) - 1) & 0x07) << 5; + config = reg_numtaps | 0x02; //start the programming clock + + if(chain == CHAIN_1) + { + config = config | (1 << 3); + } else if (chain == CHAIN_2){ + config = config | (1 << 4); + } else { + throw uhd::runtime_error("[ad9361_device_t] Can not read both chains synchronously"); + } + + if(direction == RX) + { + base = 0xF0; + } else { + base = 0x60; + } + + _io_iface->poke8(base+5,config); + + std::vector<boost::int16_t> taps; + boost::uint8_t lower_val; + boost::uint8_t higher_val; + boost::uint16_t coeff; + for(size_t i = 0;i < num_taps;i++) + { + _io_iface->poke8(base,0x00+i); + lower_val = _io_iface->peek8(base+3); + higher_val = _io_iface->peek8(base+4); + coeff = ((higher_val << 8) | lower_val); + taps.push_back(boost::int16_t(coeff)); + } + + config = (config & (~(1 << 1))); //disable filter clock + _io_iface->poke8(base+5,config); + return taps; +} + +/* + * Returns either RX TIA LPF or TX Secondary LPF + * depending on the direction. + * See UG570 for details on used scaling factors. */ +filter_info_base::sptr ad9361_device_t::_get_filter_lp_tia_sec(direction_t direction) +{ + double cutoff = 0; + + if(direction == RX) + { + cutoff = 2.5 * _rx_tia_lp_bw; + } else { + cutoff = 5 * _tx_sec_lp_bw; + } + + filter_info_base::sptr lp(new analog_filter_lp(filter_info_base::ANALOG_LOW_PASS, false, 0, "single-pole", cutoff, 20)); + return lp; +} + +/* + * Returns RX/TX BB LPF. + * See UG570 for details on used scaling factors. */ +filter_info_base::sptr ad9361_device_t::_get_filter_lp_bb(direction_t direction) +{ + double cutoff = 0; + if(direction == RX) + { + cutoff = 1.4 * _rx_bb_lp_bw; + } else { + cutoff = 1.6 * _tx_bb_lp_bw; + } + + filter_info_base::sptr bb_lp(new analog_filter_lp(filter_info_base::ANALOG_LOW_PASS, false, 1, "third-order Butterworth", cutoff, 60)); + return bb_lp; +} + +/* + * For RX direction the DEC3 is returned. + * For TX direction the INT3 is returned. */ +filter_info_base::sptr ad9361_device_t::_get_filter_dec_int_3(direction_t direction) +{ + boost::uint8_t enable = 0; + double rate = _adcclock_freq; + double full_scale; + size_t dec = 0; + size_t interpol = 0; + filter_info_base::filter_type type = filter_info_base::DIGITAL_I16; + std::string name; + boost::int16_t taps_array_rx[] = {55, 83, 0, -393, -580, 0, 1914, 4041, 5120, 4041, 1914, 0, -580, -393, 0, 83, 55}; + boost::int16_t taps_array_tx[] = {36, -19, 0, -156, -12, 0, 479, 233, 0, -1215, -993, 0, 3569, 6277, 8192, 6277, 3569, 0, -993, -1215, 0, 223, 479, 0, -12, -156, 0, -19, 36}; + std::vector<boost::int16_t> taps; + + filter_info_base::sptr ret; + + if(direction == RX) + { + full_scale = 16384; + dec = 3; + interpol = 1; + + enable = _io_iface->peek8(0x003); + enable = ((enable >> 4) & 0x03); + taps.assign(taps_array_rx, taps_array_rx + sizeof(taps_array_rx) / sizeof(boost::int16_t) ); + + } else { + full_scale = 8192; + dec = 1; + interpol = 3; + + boost::uint8_t use_dac_clk_div = _io_iface->peek8(0x00A); + use_dac_clk_div = ((use_dac_clk_div >> 3) & 0x01); + if(use_dac_clk_div == 1) + { + rate = rate / 2; + } + + enable = _io_iface->peek8(0x002); + enable = ((enable >> 4) & 0x03); + if(enable == 2) //0 => int. by 1, 1 => int. by 2 (HB3), 2 => int. by 3 + { + rate /= 3; + } + + taps.assign(taps_array_tx, taps_array_tx + sizeof(taps_array_tx) / sizeof(boost::int16_t) ); + } + + ret = filter_info_base::sptr(new digital_filter_base<boost::int16_t>(type, (enable != 2) ? true : false, 2, rate, interpol, dec, full_scale, taps.size(), taps)); + return ret; +} + +filter_info_base::sptr ad9361_device_t::_get_filter_hb_3(direction_t direction) +{ + boost::uint8_t enable = 0; + double rate = _adcclock_freq; + double full_scale = 0; + size_t dec = 1; + size_t interpol = 1; + filter_info_base::filter_type type = filter_info_base::DIGITAL_I16; + boost::int16_t taps_array_rx[] = {1, 4, 6, 4, 1}; + boost::int16_t taps_array_tx[] = {1, 2, 1}; + std::vector<boost::int16_t> taps; + + if(direction == RX) + { + full_scale = 16; + dec = 2; + + enable = _io_iface->peek8(0x003); + enable = ((enable >> 4) & 0x03); + taps.assign(taps_array_rx, taps_array_rx + sizeof(taps_array_rx) / sizeof(boost::int16_t) ); + } else { + full_scale = 2; + interpol = 2; + + boost::uint8_t use_dac_clk_div = _io_iface->peek8(0x00A); + use_dac_clk_div = ((use_dac_clk_div >> 3) & 0x01); + if(use_dac_clk_div == 1) + { + rate = rate / 2; + } + + enable = _io_iface->peek8(0x002); + enable = ((enable >> 4) & 0x03); + if(enable == 1) + { + rate /= 2; + } + taps.assign(taps_array_tx, taps_array_tx + sizeof(taps_array_tx) / sizeof(boost::int16_t) ); + } + + filter_info_base::sptr hb = filter_info_base::sptr(new digital_filter_base<boost::int16_t>(type, (enable != 1) ? true : false, 2, rate, interpol, dec, full_scale, taps.size(), taps)); + return hb; +} + +filter_info_base::sptr ad9361_device_t::_get_filter_hb_2(direction_t direction) +{ + boost::uint8_t enable = 0; + double rate = _adcclock_freq; + double full_scale = 0; + size_t dec = 1; + size_t interpol = 1; + filter_info_base::filter_type type = filter_info_base::DIGITAL_I16; + boost::int16_t taps_array[] = {-9, 0, 73, 128, 73, 0, -9}; + std::vector<boost::int16_t> taps(taps_array, taps_array + sizeof(taps_array) / sizeof(boost::int16_t) ); + + digital_filter_base<boost::int16_t>::sptr hb_3 = boost::dynamic_pointer_cast<digital_filter_base<boost::int16_t> >(_get_filter_hb_3(direction)); + digital_filter_base<boost::int16_t>::sptr dec_int_3 = boost::dynamic_pointer_cast<digital_filter_base<boost::int16_t> >(_get_filter_dec_int_3(direction)); + + if(direction == RX) + { + full_scale = 256; + dec = 2; + enable = _io_iface->peek8(0x003); + } else { + full_scale = 128; + interpol = 2; + enable = _io_iface->peek8(0x002); + } + + enable = ((enable >> 3) & 0x01); + + if(!(hb_3->is_bypassed())) + { + if(direction == RX) + { + rate = hb_3->get_output_rate(); + }else if (direction == TX) { + rate = hb_3->get_input_rate(); + if(enable) + { + rate /= 2; + } + } + } else { //else dec3/int3 or none of them is used. + if(direction == RX) + { + rate = dec_int_3->get_output_rate(); + }else if (direction == TX) { + rate = dec_int_3->get_input_rate(); + if(enable) + { + rate /= 2; + } + } + } + + filter_info_base::sptr hb(new digital_filter_base<boost::int16_t>(type, (enable == 0) ? true : false, 3, rate, interpol, dec, full_scale, taps.size(), taps)); + return hb; +} + +filter_info_base::sptr ad9361_device_t::_get_filter_hb_1(direction_t direction) +{ + boost::uint8_t enable = 0; + double rate = 0; + double full_scale = 0; + size_t dec = 1; + size_t interpol = 1; + filter_info_base::filter_type type = filter_info_base::DIGITAL_I16; + + std::vector<boost::int16_t> taps; + boost::int16_t taps_rx_array[] = {-8, 0, 42, 0, -147, 0, 619, 1013, 619, 0, -147, 0, 42, 0, -8}; + boost::int16_t taps_tx_array[] = {-53, 0, 313, 0, -1155, 0, 4989, 8192, 4989, 0, -1155, 0, 313, 0, -53}; + + digital_filter_base<boost::int16_t>::sptr hb_2 = boost::dynamic_pointer_cast<digital_filter_base<boost::int16_t> >(_get_filter_hb_2(direction)); + + if(direction == RX) + { + full_scale = 2048; + dec = 2; + enable = _io_iface->peek8(0x003); + enable = ((enable >> 2) & 0x01); + rate = hb_2->get_output_rate(); + taps.assign(taps_rx_array, taps_rx_array + sizeof(taps_rx_array) / sizeof(boost::int16_t) ); + } else if (direction == TX) { + full_scale = 8192; + interpol = 2; + enable = _io_iface->peek8(0x002); + enable = ((enable >> 2) & 0x01); + rate = hb_2->get_input_rate(); + if(enable) + { + rate /= 2; + } + taps.assign(taps_tx_array, taps_tx_array + sizeof(taps_tx_array) / sizeof(boost::int16_t) ); + } + + filter_info_base::sptr hb(new digital_filter_base<boost::int16_t>(type, (enable == 0) ? true : false, 4, rate, interpol, dec, full_scale, taps.size(), taps)); + return hb; +} + +filter_info_base::sptr ad9361_device_t::_get_filter_fir(direction_t direction, chain_t chain) +{ + double rate = 0; + size_t dec = 1; + size_t interpol = 1; + size_t max_num_taps = 128; + boost::uint8_t enable = 1; + + digital_filter_base<boost::int16_t>::sptr hb_1 = boost::dynamic_pointer_cast<digital_filter_base<boost::int16_t> >(_get_filter_hb_1(direction)); + + if(direction == RX) + { + dec = _get_fir_dec_int(direction); + if(dec == 0) + { + enable = 0; + dec = 1; + } + interpol = 1; + rate = hb_1->get_output_rate(); + }else if (direction == TX) { + interpol = _get_fir_dec_int(direction); + if(interpol == 0) + { + enable = 0; + interpol = 1; + } + dec = 1; + rate = hb_1->get_input_rate(); + if(enable) + { + rate /= interpol; + } + } + max_num_taps = _get_num_fir_taps(direction); + + filter_info_base::sptr fir(new digital_filter_fir<boost::int16_t>(filter_info_base::DIGITAL_FIR_I16, (enable == 0) ? true : false, 5, rate, interpol, dec, 32767, max_num_taps, _get_fir_taps(direction, chain))); + + return fir; +} + +void ad9361_device_t::_set_filter_fir(direction_t direction, chain_t channel, filter_info_base::sptr filter) +{ + digital_filter_fir<boost::int16_t>::sptr fir = boost::dynamic_pointer_cast<digital_filter_fir<boost::int16_t> >(filter); + //only write taps. Ignore everything else for now + _set_fir_taps(direction, channel, fir->get_taps()); +} + +/* + * If BW of one of the analog filters gets overwritten manually, + * _tx_analog_bw and _rx_analog_bw are not valid any more! + * For useful data in those variables set_bw_filter method should be used + */ +void ad9361_device_t::_set_filter_lp_bb(direction_t direction, filter_info_base::sptr filter) +{ + analog_filter_lp::sptr lpf = boost::dynamic_pointer_cast<analog_filter_lp>(filter); + double bw = lpf->get_cutoff(); + if(direction == RX) + { + //remember: this function takes rf bw as its input and calibrated to 1.4 x the given value + _rx_bb_lp_bw = _calibrate_baseband_rx_analog_filter(2 * bw / 1.4); //returns bb bw + + } else { + //remember: this function takes rf bw as its input and calibrates to 1.6 x the given value + _tx_bb_lp_bw = _calibrate_baseband_tx_analog_filter(2 * bw / 1.6); + } +} + +void ad9361_device_t::_set_filter_lp_tia_sec(direction_t direction, filter_info_base::sptr filter) +{ + analog_filter_lp::sptr lpf = boost::dynamic_pointer_cast<analog_filter_lp>(filter); + double bw = lpf->get_cutoff(); + if(direction == RX) + { + //remember: this function takes rf bw as its input and calibrated to 2.5 x the given value + _rx_tia_lp_bw = _calibrate_rx_TIAs(2 * bw / 2.5); //returns bb bw + + } else { + //remember: this function takes rf bw as its input and calibrates to 5 x the given value + _tx_sec_lp_bw = _calibrate_secondary_tx_filter(2 * bw / 5); + } +} + }} diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_device.h b/host/lib/usrp/common/ad9361_driver/ad9361_device.h index 71ce78da7..66bc2e8b9 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_device.h +++ b/host/lib/usrp/common/ad9361_driver/ad9361_device.h @@ -1,5 +1,18 @@ // -// Copyright 2014 Ettus Research LLC +// Copyright 2014 Ettus Research +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. // #ifndef INCLUDED_AD9361_DEVICE_H @@ -8,6 +21,14 @@ #include <ad9361_client.h> #include <boost/noncopyable.hpp> #include <boost/thread/recursive_mutex.hpp> +#include <uhd/types/filters.hpp> +#include <uhd/types/sensors.hpp> +#include <complex> +#include <vector> +#include <map> +#include "boost/assign.hpp" +#include "boost/bind.hpp" +#include "boost/function.hpp" namespace uhd { namespace usrp { @@ -15,10 +36,41 @@ class ad9361_device_t : public boost::noncopyable { public: enum direction_t { RX, TX }; - enum chain_t { CHAIN_1, CHAIN_2 }; + enum gain_mode_t {GAIN_MODE_MANUAL, GAIN_MODE_SLOW_AGC, GAIN_MODE_FAST_AGC}; + enum chain_t { CHAIN_1, CHAIN_2, CHAIN_BOTH }; ad9361_device_t(ad9361_params::sptr client, ad9361_io::sptr io_iface) : - _client_params(client), _io_iface(io_iface) {} + _client_params(client), _io_iface(io_iface) { + + /* + * This Boost.Assign to_container() workaround is necessary because STL containers + * apparently confuse newer versions of MSVC. + * + * Source: http://www.boost.org/doc/libs/1_55_0/libs/assign/doc/#portability + */ + + _rx_filters = (boost::assign::map_list_of("LPF_TIA", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_lp_tia_sec, this, _1), + boost::bind(&ad9361_device_t::_set_filter_lp_tia_sec, this, _1, _3))) + ("LPF_BB", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_lp_bb, this, _1), + boost::bind(&ad9361_device_t::_set_filter_lp_bb, this, _1, _3))) + ("HB_3", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_3, this, _1), 0)) + ("DEC_3", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_dec_int_3, this, _1), 0)) + ("HB_2", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_2, this, _1), 0)) + ("HB_1", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_1, this, _1), 0)) + ("FIR_1", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_fir, this, _1, _2), + boost::bind(&ad9361_device_t::_set_filter_fir, this, _1, _2, _3)))).to_container(_rx_filters); + + _tx_filters = (boost::assign::map_list_of("LPF_SECONDARY", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_lp_tia_sec, this, _1), + boost::bind(&ad9361_device_t::_set_filter_lp_tia_sec, this, _1, _3))) + ("LPF_BB", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_lp_bb, this, _1), + boost::bind(&ad9361_device_t::_set_filter_lp_bb, this, _1, _3))) + ("HB_3", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_3, this, _1), 0)) + ("INT_3", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_dec_int_3, this, _1), 0)) + ("HB_2", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_2, this, _1), 0)) + ("HB_1", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_1, this, _1), 0)) + ("FIR_1", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_fir, this, _1, _2), + boost::bind(&ad9361_device_t::_set_filter_fir, this, _1, _2, _3)))).to_container(_tx_filters); + } /* Initialize the AD9361 codec. */ void initialize(); @@ -63,27 +115,66 @@ public: /* Make AD9361 output its test tone. */ void output_test_tone(); + void digital_test_tone(bool enb); // Digital output + /* Turn on/off AD9361's TX port --> RX port loopback. */ void data_port_loopback(const bool loopback_enabled); /* Read back the internal RSSI measurement data. */ double get_rssi(chain_t chain); + /*! Read the internal temperature sensor + *\param calibrate return raw sensor readings or apply calibration factor. + *\param num_samples number of measurements to average over + */ + double get_average_temperature(const double cal_offset = -30.0, const size_t num_samples = 3); + + /* Turn on/off AD9361's RX DC offset correction */ + void set_dc_offset_auto(direction_t direction, const bool on); + + /* Turn on/off AD9361's RX IQ imbalance correction */ + void set_iq_balance_auto(direction_t direction, const bool on); + + /* Configure AD9361's AGC module to use either fast or slow AGC mode. */ + void set_agc_mode(chain_t chain, gain_mode_t gain_mode); + + /* Enable AD9361's AGC gain mode. */ + void set_agc(chain_t chain, bool enable); + + /* Set bandwidth of AD9361's analog LP filters. + * Bandwidth should be RF bandwidth */ + double set_bw_filter(direction_t direction, const double rf_bw); + + /* + * Filter API implementation + * */ + filter_info_base::sptr get_filter(direction_t direction, chain_t chain, const std::string &name); + + void set_filter(direction_t direction, chain_t chain, const std::string &name, filter_info_base::sptr filter); + + std::vector<std::string> get_filter_names(direction_t direction); + //Constants static const double AD9361_MAX_GAIN; static const double AD9361_MAX_CLOCK_RATE; + static const double AD9361_CAL_VALID_WINDOW; static const double AD9361_RECOMMENDED_MAX_BANDWIDTH; + static const double DEFAULT_RX_FREQ; + static const double DEFAULT_TX_FREQ; private: //Methods void _program_fir_filter(direction_t direction, int num_taps, boost::uint16_t *coeffs); void _setup_tx_fir(size_t num_taps, boost::int32_t interpolation); void _setup_rx_fir(size_t num_taps, boost::int32_t decimation); + void _program_fir_filter(direction_t direction, chain_t chain, int num_taps, boost::uint16_t *coeffs); + void _setup_tx_fir(size_t num_taps); + void _setup_rx_fir(size_t num_taps); void _calibrate_lock_bbpll(); void _calibrate_synth_charge_pumps(); - double _calibrate_baseband_rx_analog_filter(); - double _calibrate_baseband_tx_analog_filter(); - void _calibrate_secondary_tx_filter(); - void _calibrate_rx_TIAs(); + double _calibrate_baseband_rx_analog_filter(double rfbw); + double _calibrate_baseband_tx_analog_filter(double rfbw); + double _calibrate_secondary_tx_filter(double rfbw); + double _calibrate_rx_TIAs(double rfbw); void _setup_adc(); void _calibrate_baseband_dc_offset(); void _calibrate_rf_dc_offset(); @@ -92,12 +183,30 @@ private: //Methods void _calibrate_tx_quadrature(); void _program_mixer_gm_subtable(); void _program_gain_table(); - void _setup_gain_control(); + void _setup_gain_control(bool use_agc); void _setup_synth(direction_t direction, double vcorate); double _tune_bbvco(const double rate); void _reprogram_gains(); double _tune_helper(direction_t direction, const double value); double _setup_rates(const double rate); + double _get_temperature(const double cal_offset, const double timeout = 0.1); + void _configure_bb_dc_tracking(); + void _configure_rx_iq_tracking(); + void _setup_agc(chain_t chain, gain_mode_t gain_mode); + void _set_fir_taps(direction_t direction, chain_t chain, const std::vector<boost::int16_t>& taps); + std::vector<boost::int16_t> _get_fir_taps(direction_t direction, chain_t chain); + size_t _get_num_fir_taps(direction_t direction); + size_t _get_fir_dec_int(direction_t direction); + filter_info_base::sptr _get_filter_lp_tia_sec(direction_t direction); + filter_info_base::sptr _get_filter_lp_bb(direction_t direction); + filter_info_base::sptr _get_filter_dec_int_3(direction_t direction); + filter_info_base::sptr _get_filter_hb_3(direction_t direction); + filter_info_base::sptr _get_filter_hb_2(direction_t direction); + filter_info_base::sptr _get_filter_hb_1(direction_t direction); + filter_info_base::sptr _get_filter_fir(direction_t direction, chain_t chain); + void _set_filter_fir(direction_t direction, chain_t channel, filter_info_base::sptr filter); + void _set_filter_lp_bb(direction_t direction, filter_info_base::sptr filter); + void _set_filter_lp_tia_sec(direction_t direction, filter_info_base::sptr filter); private: //Members typedef struct { @@ -110,11 +219,30 @@ private: //Members boost::uint8_t bbftune_mode; } chip_regs_t; + struct filter_query_helper + { + filter_query_helper( + boost::function<filter_info_base::sptr (direction_t, chain_t)> p_get, + boost::function<void (direction_t, chain_t, filter_info_base::sptr)> p_set + ) : get(p_get), set(p_set) { } + + filter_query_helper(){ } + + boost::function<filter_info_base::sptr (direction_t, chain_t)> get; + boost::function<void (direction_t, chain_t, filter_info_base::sptr)> set; + }; + + std::map<std::string, filter_query_helper> _rx_filters; + std::map<std::string, filter_query_helper> _tx_filters; + //Interfaces ad9361_params::sptr _client_params; ad9361_io::sptr _io_iface; //Intermediate state double _rx_freq, _tx_freq, _req_rx_freq, _req_tx_freq; + double _last_rx_cal_freq, _last_tx_cal_freq; + double _rx_analog_bw, _tx_analog_bw, _rx_bb_lp_bw, _tx_bb_lp_bw; + double _rx_tia_lp_bw, _tx_sec_lp_bw; //! Current baseband sampling rate (this is the actual rate the device is // is running at) double _baseband_bw; @@ -129,10 +257,14 @@ private: //Members double _rx1_gain, _rx2_gain, _tx1_gain, _tx2_gain; boost::int32_t _tfir_factor; boost::int32_t _rfir_factor; + gain_mode_t _rx1_agc_mode, _rx2_agc_mode; + bool _rx1_agc_enable, _rx2_agc_enable; //Register soft-copies chip_regs_t _regs; //Synchronization boost::recursive_mutex _mutex; + bool _use_dc_offset_tracking; + bool _use_iq_balance_tracking; }; }} //namespace diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_filter_taps.h b/host/lib/usrp/common/ad9361_driver/ad9361_filter_taps.h index a1a85a49a..97ff858fd 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_filter_taps.h +++ b/host/lib/usrp/common/ad9361_driver/ad9361_filter_taps.h @@ -1,5 +1,18 @@ // -// Copyright 2014 Ettus Research LLC +// Copyright 2014 Ettus Research +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. // #ifndef INCLUDED_AD9361_FILTER_TAPS_HPP diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_gain_tables.h b/host/lib/usrp/common/ad9361_driver/ad9361_gain_tables.h index 786029d6e..8cd958e23 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_gain_tables.h +++ b/host/lib/usrp/common/ad9361_driver/ad9361_gain_tables.h @@ -1,5 +1,18 @@ // -// Copyright 2014 Ettus Research LLC +// Copyright 2014 Ettus Research +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. // #ifndef INCLUDED_AD9361_GAIN_TABLES_HPP @@ -7,91 +20,91 @@ #include <boost/cstdint.hpp> -boost::uint8_t gain_table_sub_1300mhz[77][5] = { {0,0x00,0x00,0x20,1}, - {1,0x00,0x00,0x00,0}, {2,0x00,0x00,0x00,0}, {3,0x00,0x01,0x00,0}, - {4,0x00,0x02,0x00,0}, {5,0x00,0x03,0x00,0}, {6,0x00,0x04,0x00,0}, - {7,0x00,0x05,0x00,0}, {8,0x01,0x03,0x20,1}, {9,0x01,0x04,0x00,0}, - {10,0x01,0x05,0x00,0}, {11,0x01,0x06,0x00,0}, {12,0x01,0x07,0x00,0}, - {13,0x01,0x08,0x00,0}, {14,0x01,0x09,0x00,0}, {15,0x01,0x0A,0x00,0}, - {16,0x01,0x0B,0x00,0}, {17,0x01,0x0C,0x00,0}, {18,0x01,0x0D,0x00,0}, - {19,0x01,0x0E,0x00,0}, {20,0x02,0x09,0x20,1}, {21,0x02,0x0A,0x00,0}, - {22,0x02,0x0B,0x00,0}, {23,0x02,0x0C,0x00,0}, {24,0x02,0x0D,0x00,0}, - {25,0x02,0x0E,0x00,0}, {26,0x02,0x0F,0x00,0}, {27,0x02,0x10,0x00,0}, - {28,0x02,0x2B,0x20,1}, {29,0x02,0x2C,0x00,0}, {30,0x04,0x27,0x20,1}, - {31,0x04,0x28,0x00,0}, {32,0x04,0x29,0x00,0}, {33,0x04,0x2A,0x00,0}, - {34,0x04,0x2B,0x00,1}, {35,0x24,0x21,0x20,0}, {36,0x24,0x22,0x00,1}, - {37,0x44,0x20,0x20,0}, {38,0x44,0x21,0x00,0}, {39,0x44,0x22,0x00,0}, - {40,0x44,0x23,0x00,0}, {41,0x44,0x24,0x00,0}, {42,0x44,0x25,0x00,0}, - {43,0x44,0x26,0x00,0}, {44,0x44,0x27,0x00,0}, {45,0x44,0x28,0x00,0}, - {46,0x44,0x29,0x00,0}, {47,0x44,0x2A,0x00,0}, {48,0x44,0x2B,0x00,0}, - {49,0x44,0x2C,0x00,0}, {50,0x44,0x2D,0x00,0}, {51,0x44,0x2E,0x00,0}, - {52,0x44,0x2F,0x00,0}, {53,0x44,0x30,0x00,0}, {54,0x44,0x31,0x00,0}, - {55,0x64,0x2E,0x20,1}, {56,0x64,0x2F,0x00,0}, {57,0x64,0x30,0x00,0}, - {58,0x64,0x31,0x00,0}, {59,0x64,0x32,0x00,0}, {60,0x64,0x33,0x00,0}, - {61,0x64,0x34,0x00,0}, {62,0x64,0x35,0x00,0}, {63,0x64,0x36,0x00,0}, - {64,0x64,0x37,0x00,0}, {65,0x64,0x38,0x00,0}, {66,0x65,0x38,0x20,1}, - {67,0x66,0x38,0x20,1}, {68,0x67,0x38,0x20,1}, {69,0x68,0x38,0x20,1}, - {70,0x69,0x38,0x20,1}, {71,0x6A,0x38,0x20,1}, {72,0x6B,0x38,0x20,1}, - {73,0x6C,0x38,0x20,1}, {74,0x6D,0x38,0x20,1}, {75,0x6E,0x38,0x20,1}, - {76,0x6F,0x38,0x20,1}}; +boost::uint8_t gain_table_sub_1300mhz[77][3] = { +{ 0x00, 0x00, 0x20 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x00 }, { 0x00, 0x02, 0x00 }, { 0x00, 0x03, 0x00 }, +{ 0x00, 0x04, 0x00 }, { 0x00, 0x05, 0x00 }, { 0x01, 0x03, 0x20 }, +{ 0x01, 0x04, 0x00 }, { 0x01, 0x05, 0x00 }, { 0x01, 0x06, 0x00 }, +{ 0x01, 0x07, 0x00 }, { 0x01, 0x08, 0x00 }, { 0x01, 0x09, 0x00 }, +{ 0x01, 0x0A, 0x00 }, { 0x01, 0x0B, 0x00 }, { 0x01, 0x0C, 0x00 }, +{ 0x01, 0x0D, 0x00 }, { 0x01, 0x0E, 0x00 }, { 0x02, 0x09, 0x20 }, +{ 0x02, 0x0A, 0x00 }, { 0x02, 0x0B, 0x00 }, { 0x02, 0x0C, 0x00 }, +{ 0x02, 0x0D, 0x00 }, { 0x02, 0x0E, 0x00 }, { 0x02, 0x0F, 0x00 }, +{ 0x02, 0x10, 0x00 }, { 0x02, 0x2B, 0x20 }, { 0x02, 0x2C, 0x00 }, +{ 0x04, 0x28, 0x20 }, { 0x04, 0x29, 0x00 }, { 0x04, 0x2A, 0x00 }, +{ 0x04, 0x2B, 0x00 }, { 0x24, 0x20, 0x20 }, { 0x24, 0x21, 0x00 }, +{ 0x44, 0x20, 0x20 }, { 0x44, 0x21, 0x00 }, { 0x44, 0x22, 0x00 }, +{ 0x44, 0x23, 0x00 }, { 0x44, 0x24, 0x00 }, { 0x44, 0x25, 0x00 }, +{ 0x44, 0x26, 0x00 }, { 0x44, 0x27, 0x00 }, { 0x44, 0x28, 0x00 }, +{ 0x44, 0x29, 0x00 }, { 0x44, 0x2A, 0x00 }, { 0x44, 0x2B, 0x00 }, +{ 0x44, 0x2C, 0x00 }, { 0x44, 0x2D, 0x00 }, { 0x44, 0x2E, 0x00 }, +{ 0x44, 0x2F, 0x00 }, { 0x44, 0x30, 0x00 }, { 0x44, 0x31, 0x00 }, +{ 0x44, 0x32, 0x00 }, { 0x64, 0x2E, 0x20 }, { 0x64, 0x2F, 0x00 }, +{ 0x64, 0x30, 0x00 }, { 0x64, 0x31, 0x00 }, { 0x64, 0x32, 0x00 }, +{ 0x64, 0x33, 0x00 }, { 0x64, 0x34, 0x00 }, { 0x64, 0x35, 0x00 }, +{ 0x64, 0x36, 0x00 }, { 0x64, 0x37, 0x00 }, { 0x64, 0x38, 0x00 }, +{ 0x65, 0x38, 0x20 }, { 0x66, 0x38, 0x20 }, { 0x67, 0x38, 0x20 }, +{ 0x68, 0x38, 0x20 }, { 0x69, 0x38, 0x20 }, { 0x6A, 0x38, 0x20 }, +{ 0x6B, 0x38, 0x20 }, { 0x6C, 0x38, 0x20 }, { 0x6D, 0x38, 0x20 }, +{ 0x6E, 0x38, 0x20 }, { 0x6F, 0x38, 0x20 } }; -boost::uint8_t gain_table_1300mhz_to_4000mhz[77][5] = { {0,0x00,0x00,0x20,1}, - {1,0x00,0x00,0x00,0}, {2,0x00,0x00,0x00,0}, {3,0x00,0x01,0x00,0}, - {4,0x00,0x02,0x00,0}, {5,0x00,0x03,0x00,0}, {6,0x00,0x04,0x00,0}, - {7,0x00,0x05,0x00,0}, {8,0x01,0x03,0x20,1}, {9,0x01,0x04,0x00,0}, - {10,0x01,0x05,0x00,0}, {11,0x01,0x06,0x00,0}, {12,0x01,0x07,0x00,0}, - {13,0x01,0x08,0x00,0}, {14,0x01,0x09,0x00,0}, {15,0x01,0x0A,0x00,0}, - {16,0x01,0x0B,0x00,0}, {17,0x01,0x0C,0x00,0}, {18,0x01,0x0D,0x00,0}, - {19,0x01,0x0E,0x00,0}, {20,0x02,0x09,0x20,1}, {21,0x02,0x0A,0x00,0}, - {22,0x02,0x0B,0x00,0}, {23,0x02,0x0C,0x00,0}, {24,0x02,0x0D,0x00,0}, - {25,0x02,0x0E,0x00,0}, {26,0x02,0x0F,0x00,0}, {27,0x02,0x10,0x00,0}, - {28,0x02,0x2B,0x20,1}, {29,0x02,0x2C,0x00,0}, {30,0x04,0x28,0x20,1}, - {31,0x04,0x29,0x00,0}, {32,0x04,0x2A,0x00,0}, {33,0x04,0x2B,0x00,0}, - {34,0x24,0x20,0x20,0}, {35,0x24,0x21,0x00,1}, {36,0x44,0x20,0x20,0}, - {37,0x44,0x21,0x00,1}, {38,0x44,0x22,0x00,0}, {39,0x44,0x23,0x00,0}, - {40,0x44,0x24,0x00,0}, {41,0x44,0x25,0x00,0}, {42,0x44,0x26,0x00,0}, - {43,0x44,0x27,0x00,0}, {44,0x44,0x28,0x00,0}, {45,0x44,0x29,0x00,0}, - {46,0x44,0x2A,0x00,0}, {47,0x44,0x2B,0x00,0}, {48,0x44,0x2C,0x00,0}, - {49,0x44,0x2D,0x00,0}, {50,0x44,0x2E,0x00,0}, {51,0x44,0x2F,0x00,0}, - {52,0x44,0x30,0x00,0}, {53,0x44,0x31,0x00,0}, {54,0x44,0x32,0x00,0}, - {55,0x64,0x2E,0x20,1}, {56,0x64,0x2F,0x00,0}, {57,0x64,0x30,0x00,0}, - {58,0x64,0x31,0x00,0}, {59,0x64,0x32,0x00,0}, {60,0x64,0x33,0x00,0}, - {61,0x64,0x34,0x00,0}, {62,0x64,0x35,0x00,0}, {63,0x64,0x36,0x00,0}, - {64,0x64,0x37,0x00,0}, {65,0x64,0x38,0x00,0}, {66,0x65,0x38,0x20,1}, - {67,0x66,0x38,0x20,1}, {68,0x67,0x38,0x20,1}, {69,0x68,0x38,0x20,1}, - {70,0x69,0x38,0x20,1}, {71,0x6A,0x38,0x20,1}, {72,0x6B,0x38,0x20,1}, - {73,0x6C,0x38,0x20,1}, {74,0x6D,0x38,0x20,1}, {75,0x6E,0x38,0x20,1}, - {76,0x6F,0x38,0x20,1}}; +boost::uint8_t gain_table_1300mhz_to_4000mhz[77][3] = { +{ 0x00, 0x00, 0x20 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x00 }, { 0x00, 0x02, 0x00 }, { 0x00, 0x03, 0x00 }, +{ 0x00, 0x04, 0x00 }, { 0x00, 0x05, 0x00 }, { 0x01, 0x03, 0x20 }, +{ 0x01, 0x04, 0x00 }, { 0x01, 0x05, 0x00 }, { 0x01, 0x06, 0x00 }, +{ 0x01, 0x07, 0x00 }, { 0x01, 0x08, 0x00 }, { 0x01, 0x09, 0x00 }, +{ 0x01, 0x0A, 0x00 }, { 0x01, 0x0B, 0x00 }, { 0x01, 0x0C, 0x00 }, +{ 0x01, 0x0D, 0x00 }, { 0x01, 0x0E, 0x00 }, { 0x02, 0x09, 0x20 }, +{ 0x02, 0x0A, 0x00 }, { 0x02, 0x0B, 0x00 }, { 0x02, 0x0C, 0x00 }, +{ 0x02, 0x0D, 0x00 }, { 0x02, 0x0E, 0x00 }, { 0x02, 0x0F, 0x00 }, +{ 0x02, 0x10, 0x00 }, { 0x02, 0x2B, 0x20 }, { 0x02, 0x2C, 0x00 }, +{ 0x04, 0x27, 0x20 }, { 0x04, 0x28, 0x00 }, { 0x04, 0x29, 0x00 }, +{ 0x04, 0x2A, 0x00 }, { 0x04, 0x2B, 0x00 }, { 0x24, 0x21, 0x20 }, +{ 0x24, 0x22, 0x00 }, { 0x44, 0x20, 0x20 }, { 0x44, 0x21, 0x00 }, +{ 0x44, 0x22, 0x00 }, { 0x44, 0x23, 0x00 }, { 0x44, 0x24, 0x00 }, +{ 0x44, 0x25, 0x00 }, { 0x44, 0x26, 0x00 }, { 0x44, 0x27, 0x00 }, +{ 0x44, 0x28, 0x00 }, { 0x44, 0x29, 0x00 }, { 0x44, 0x2A, 0x00 }, +{ 0x44, 0x2B, 0x00 }, { 0x44, 0x2C, 0x00 }, { 0x44, 0x2D, 0x00 }, +{ 0x44, 0x2E, 0x00 }, { 0x44, 0x2F, 0x00 }, { 0x44, 0x30, 0x00 }, +{ 0x44, 0x31, 0x00 }, { 0x64, 0x2E, 0x20 }, { 0x64, 0x2F, 0x00 }, +{ 0x64, 0x30, 0x00 }, { 0x64, 0x31, 0x00 }, { 0x64, 0x32, 0x00 }, +{ 0x64, 0x33, 0x00 }, { 0x64, 0x34, 0x00 }, { 0x64, 0x35, 0x00 }, +{ 0x64, 0x36, 0x00 }, { 0x64, 0x37, 0x00 }, { 0x64, 0x38, 0x00 }, +{ 0x65, 0x38, 0x20 }, { 0x66, 0x38, 0x20 }, { 0x67, 0x38, 0x20 }, +{ 0x68, 0x38, 0x20 }, { 0x69, 0x38, 0x20 }, { 0x6A, 0x38, 0x20 }, +{ 0x6B, 0x38, 0x20 }, { 0x6C, 0x38, 0x20 }, { 0x6D, 0x38, 0x20 }, +{ 0x6E, 0x38, 0x20 }, { 0x6F, 0x38, 0x20 } }; -boost::uint8_t gain_table_4000mhz_to_6000mhz[77][5] = { {0,0x00,0x00,0x20,1}, - {1,0x00,0x00,0x00,0}, {2,0x00,0x00,0x00,0}, {3,0x00,0x00,0x00,0}, - {4,0x00,0x00,0x00,0}, {5,0x00,0x01,0x00,0}, {6,0x00,0x02,0x00,0}, - {7,0x00,0x03,0x00,0}, {8,0x01,0x01,0x20,1}, {9,0x01,0x02,0x00,0}, - {10,0x01,0x03,0x00,0}, {11,0x01,0x04,0x20,1}, {12,0x01,0x05,0x00,0}, - {13,0x01,0x06,0x00,0}, {14,0x01,0x07,0x00,0}, {15,0x01,0x08,0x00,0}, - {16,0x01,0x09,0x00,0}, {17,0x01,0x0A,0x00,0}, {18,0x01,0x0B,0x00,0}, - {19,0x01,0x0C,0x00,0}, {20,0x02,0x08,0x20,1}, {21,0x02,0x09,0x00,0}, - {22,0x02,0x0A,0x00,0}, {23,0x02,0x0B,0x20,1}, {24,0x02,0x0C,0x00,0}, - {25,0x02,0x0D,0x00,0}, {26,0x02,0x0E,0x00,0}, {27,0x02,0x0F,0x00,0}, - {28,0x02,0x2A,0x20,1}, {29,0x02,0x2B,0x00,0}, {30,0x04,0x27,0x20,1}, - {31,0x04,0x28,0x00,0}, {32,0x04,0x29,0x00,0}, {33,0x04,0x2A,0x00,0}, - {34,0x04,0x2B,0x00,0}, {35,0x04,0x2C,0x00,0}, {36,0x04,0x2D,0x00,0}, - {37,0x24,0x20,0x20,1}, {38,0x24,0x21,0x00,0}, {39,0x24,0x22,0x00,0}, - {40,0x44,0x20,0x20,1}, {41,0x44,0x21,0x00,0}, {42,0x44,0x22,0x00,0}, - {43,0x44,0x23,0x00,0}, {44,0x44,0x24,0x00,0}, {45,0x44,0x25,0x00,0}, - {46,0x44,0x26,0x00,0}, {47,0x44,0x27,0x00,0}, {48,0x44,0x28,0x00,0}, - {49,0x44,0x29,0x00,0}, {50,0x44,0x2A,0x00,0}, {51,0x44,0x2B,0x00,0}, - {52,0x44,0x2C,0x00,0}, {53,0x44,0x2D,0x00,0}, {54,0x44,0x2E,0x00,0}, - {55,0x64,0x2E,0x20,1}, {56,0x64,0x2F,0x00,0}, {57,0x64,0x30,0x00,0}, - {58,0x64,0x31,0x00,0}, {59,0x64,0x32,0x00,0}, {60,0x64,0x33,0x00,0}, - {61,0x64,0x34,0x00,0}, {62,0x64,0x35,0x00,0}, {63,0x64,0x36,0x00,0}, - {64,0x64,0x37,0x00,0}, {65,0x64,0x38,0x00,0}, {66,0x65,0x38,0x20,1}, - {67,0x66,0x38,0x20,1}, {68,0x67,0x38,0x20,1}, {69,0x68,0x38,0x20,1}, - {70,0x69,0x38,0x20,1}, {71,0x6A,0x38,0x20,1}, {72,0x6B,0x38,0x20,1}, - {73,0x6C,0x38,0x20,1}, {74,0x6D,0x38,0x20,1}, {75,0x6E,0x38,0x20,1}, - {76,0x6F,0x38,0x20,1}}; +boost::uint8_t gain_table_4000mhz_to_6000mhz[77][3] = { +{ 0x00, 0x00, 0x20 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, +{ 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x01, 0x00 }, +{ 0x00, 0x02, 0x00 }, { 0x00, 0x03, 0x00 }, { 0x01, 0x01, 0x20 }, +{ 0x01, 0x02, 0x00 }, { 0x01, 0x03, 0x00 }, { 0x01, 0x04, 0x20 }, +{ 0x01, 0x05, 0x00 }, { 0x01, 0x06, 0x00 }, { 0x01, 0x07, 0x00 }, +{ 0x01, 0x08, 0x00 }, { 0x01, 0x09, 0x00 }, { 0x01, 0x0A, 0x00 }, +{ 0x01, 0x0B, 0x00 }, { 0x01, 0x0C, 0x00 }, { 0x02, 0x08, 0x20 }, +{ 0x02, 0x09, 0x00 }, { 0x02, 0x0A, 0x00 }, { 0x02, 0x0B, 0x20 }, +{ 0x02, 0x0C, 0x00 }, { 0x02, 0x0D, 0x00 }, { 0x02, 0x0E, 0x00 }, +{ 0x02, 0x0F, 0x00 }, { 0x02, 0x2A, 0x20 }, { 0x02, 0x2B, 0x00 }, +{ 0x04, 0x27, 0x20 }, { 0x04, 0x28, 0x00 }, { 0x04, 0x29, 0x00 }, +{ 0x04, 0x2A, 0x00 }, { 0x04, 0x2B, 0x00 }, { 0x04, 0x2C, 0x00 }, +{ 0x04, 0x2D, 0x00 }, { 0x24, 0x20, 0x20 }, { 0x24, 0x21, 0x00 }, +{ 0x24, 0x22, 0x00 }, { 0x44, 0x20, 0x20 }, { 0x44, 0x21, 0x00 }, +{ 0x44, 0x22, 0x00 }, { 0x44, 0x23, 0x00 }, { 0x44, 0x24, 0x00 }, +{ 0x44, 0x25, 0x00 }, { 0x44, 0x26, 0x00 }, { 0x44, 0x27, 0x00 }, +{ 0x44, 0x28, 0x00 }, { 0x44, 0x29, 0x00 }, { 0x44, 0x2A, 0x00 }, +{ 0x44, 0x2B, 0x00 }, { 0x44, 0x2C, 0x00 }, { 0x44, 0x2D, 0x00 }, +{ 0x44, 0x2E, 0x00 }, { 0x64, 0x2E, 0x20 }, { 0x64, 0x2F, 0x00 }, +{ 0x64, 0x30, 0x00 }, { 0x64, 0x31, 0x00 }, { 0x64, 0x32, 0x00 }, +{ 0x64, 0x33, 0x00 }, { 0x64, 0x34, 0x00 }, { 0x64, 0x35, 0x00 }, +{ 0x64, 0x36, 0x00 }, { 0x64, 0x37, 0x00 }, { 0x64, 0x38, 0x00 }, +{ 0x65, 0x38, 0x20 }, { 0x66, 0x38, 0x20 }, { 0x67, 0x38, 0x20 }, +{ 0x68, 0x38, 0x20 }, { 0x69, 0x38, 0x20 }, { 0x6A, 0x38, 0x20 }, +{ 0x6B, 0x38, 0x20 }, { 0x6C, 0x38, 0x20 }, { 0x6D, 0x38, 0x20 }, +{ 0x6E, 0x38, 0x20 }, { 0x6F, 0x38, 0x20 } }; #endif /* INCLUDED_AD9361_GAIN_TABLES_HPP */ diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_synth_lut.h b/host/lib/usrp/common/ad9361_driver/ad9361_synth_lut.h index cb320e1f4..8155aae7c 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_synth_lut.h +++ b/host/lib/usrp/common/ad9361_driver/ad9361_synth_lut.h @@ -1,25 +1,38 @@ // -// Copyright 2014 Ettus Research LLC +// Copyright 2014 Ettus Research +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. // #ifndef INCLUDED_AD9361_SYNTH_LUT_HPP #define INCLUDED_AD9361_SYNTH_LUT_HPP -double vco_index[53] = {12605000000, 12245000000, 11906000000, 11588000000, - 11288000000, 11007000000, 10742000000, 10492000000, - 10258000000, 10036000000, 9827800000, 9631100000, - 9445300000, 9269800000, 9103600000, 8946300000, - 8797000000, 8655300000, 8520600000, 8392300000, - 8269900000, 8153100000, 8041400000, 7934400000, - 7831800000, 7733200000, 7638400000, 7547100000, - 7459000000, 7374000000, 7291900000, 7212400000, - 7135500000, 7061000000, 6988700000, 6918600000, - 6850600000, 6784600000, 6720500000, 6658200000, - 6597800000, 6539200000, 6482300000, 6427000000, - 6373400000, 6321400000, 6270900000, 6222000000, - 6174500000, 6128400000, 6083600000, 6040100000, - 5997700000}; +double vco_index[53] = {12605000000.0, 12245000000.0, 11906000000.0, 11588000000.0, + 11288000000.0, 11007000000.0, 10742000000.0, 10492000000.0, + 10258000000.0, 10036000000.0, 9827800000.0, 9631100000.0, + 9445300000.0, 9269800000.0, 9103600000.0, 8946300000.0, + 8797000000.0, 8655300000.0, 8520600000.0, 8392300000.0, + 8269900000.0, 8153100000.0, 8041400000.0, 7934400000.0, + 7831800000.0, 7733200000.0, 7638400000.0, 7547100000.0, + 7459000000.0, 7374000000.0, 7291900000.0, 7212400000.0, + 7135500000.0, 7061000000.0, 6988700000.0, 6918600000.0, + 6850600000.0, 6784600000.0, 6720500000.0, 6658200000.0, + 6597800000.0, 6539200000.0, 6482300000.0, 6427000000.0, + 6373400000.0, 6321400000.0, 6270900000.0, 6222000000.0, + 6174500000.0, 6128400000.0, 6083600000.0, 6040100000.0, + 5997700000.0}; int synth_cal_lut[53][12] = { {10, 0, 4, 0, 15, 8, 8, 13, 4, 13, 15, 9}, {10, 0, 4, 0, 15, 8, 9, 13, 4, 13, 15, 9}, diff --git a/host/lib/usrp/common/ad936x_manager.cpp b/host/lib/usrp/common/ad936x_manager.cpp new file mode 100644 index 000000000..8c8897803 --- /dev/null +++ b/host/lib/usrp/common/ad936x_manager.cpp @@ -0,0 +1,280 @@ +// +// Copyright 2015 Ettus Research +// +// 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 "ad936x_manager.hpp" +#include <uhd/utils/msg.hpp> +#include <boost/foreach.hpp> +#include <boost/functional/hash.hpp> + +using namespace uhd; +using namespace uhd::usrp; + +/**************************************************************************** + * Default values + ***************************************************************************/ +const double ad936x_manager::DEFAULT_GAIN = 0; +const double ad936x_manager::DEFAULT_BANDWIDTH = 56e6; +const double ad936x_manager::DEFAULT_TICK_RATE = 16e6; +const double ad936x_manager::DEFAULT_FREQ = 100e6; // Hz +const uint32_t ad936x_manager::DEFAULT_DECIM = 128; +const uint32_t ad936x_manager::DEFAULT_INTERP = 128; +const bool ad936x_manager::DEFAULT_AUTO_DC_OFFSET = true; +const bool ad936x_manager::DEFAULT_AUTO_IQ_BALANCE = true; +const bool ad936x_manager::DEFAULT_AGC_ENABLE = false; + +class ad936x_manager_impl : public ad936x_manager +{ + public: + /************************************************************************ + * Structor + ***********************************************************************/ + ad936x_manager_impl( + const ad9361_ctrl::sptr &codec_ctrl, + const size_t n_frontends + ) : _codec_ctrl(codec_ctrl), + _n_frontends(n_frontends) + { + if (_n_frontends < 1 or _n_frontends > 2) { + throw uhd::runtime_error(str( + boost::format("AD936x device can only have either 1 or 2 frontends, not %d.") + % _n_frontends + )); + } + for (size_t i = 1; i <= _n_frontends; i++) { + _rx_frontends.push_back(str(boost::format("RX%d") % i)); + _tx_frontends.push_back(str(boost::format("TX%d") % i)); + } + } + + /************************************************************************ + * API Calls + ***********************************************************************/ + void init_codec() + { + BOOST_FOREACH(const std::string &rx_fe, _rx_frontends) { + _codec_ctrl->set_gain(rx_fe, DEFAULT_GAIN); + _codec_ctrl->set_bw_filter(rx_fe, DEFAULT_BANDWIDTH); + _codec_ctrl->tune(rx_fe, DEFAULT_FREQ); + _codec_ctrl->set_dc_offset_auto(rx_fe, DEFAULT_AUTO_DC_OFFSET); + _codec_ctrl->set_iq_balance_auto(rx_fe, DEFAULT_AUTO_IQ_BALANCE); + _codec_ctrl->set_agc(rx_fe, DEFAULT_AGC_ENABLE); + } + BOOST_FOREACH(const std::string &tx_fe, _tx_frontends) { + _codec_ctrl->set_gain(tx_fe, DEFAULT_GAIN); + _codec_ctrl->set_bw_filter(tx_fe, DEFAULT_BANDWIDTH); + _codec_ctrl->tune(tx_fe, DEFAULT_FREQ); + } + } + + void loopback_self_test( + wb_iface::sptr iface, + wb_iface::wb_addr_type codec_idle_addr, + wb_iface::wb_addr_type codec_readback_addr + ) { + _codec_ctrl->data_port_loopback(true); + UHD_MSG(status) << "Performing CODEC loopback test... " << std::flush; + UHD_ASSERT_THROW(bool(iface)); + size_t hash = size_t(time(NULL)); + for (size_t i = 0; i < 100; i++) + { + boost::hash_combine(hash, i); + const boost::uint32_t word32 = boost::uint32_t(hash) & 0xfff0fff0; + iface->poke32(codec_idle_addr, word32); + // We do 2 peeks so we have enough idleness for loopback to propagate + iface->peek64(codec_readback_addr); + const boost::uint64_t rb_word64 = iface->peek64(codec_readback_addr); + const boost::uint32_t rb_tx = boost::uint32_t(rb_word64 >> 32); + const boost::uint32_t rb_rx = boost::uint32_t(rb_word64 & 0xffffffff); + bool test_fail = word32 != rb_tx or word32 != rb_rx; + if (test_fail) { + UHD_MSG(status) << "fail" << std::endl; + throw uhd::runtime_error("CODEC loopback test failed."); + } + } + UHD_MSG(status) << "pass" << std::endl; + /* Zero out the idle data. */ + iface->poke32(codec_idle_addr, 0); + _codec_ctrl->data_port_loopback(false); + } + + + double get_auto_tick_rate( + const double lcm_rate, + size_t num_chans + ) { + UHD_ASSERT_THROW(num_chans >= 1 and num_chans <= _n_frontends); + const uhd::meta_range_t rate_range = _codec_ctrl->get_clock_rate_range(); + const double min_tick_rate = rate_range.start(); + const double max_tick_rate = rate_range.stop() / num_chans; + + // Check if the requested rate is within available limits: + if (uhd::math::fp_compare::fp_compare_delta<double>(lcm_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) > + uhd::math::fp_compare::fp_compare_delta<double>(max_tick_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ)) { + throw uhd::value_error(str( + boost::format("[ad936x_manager] Cannot get determine a tick rate if sampling rate exceeds maximum tick rate (%f > %f)") + % lcm_rate % max_tick_rate + )); + } + + // **** Choose the new rate **** + // Rules for choosing the tick rate: + // Choose a rate that is a power of 2 larger than the sampling rate, + // but at least 4. Cannot exceed the max tick rate, of course, but must + // be larger than the minimum tick rate. + // An equation that does all that is: + // + // f_auto = r * 2^floor(log2(f_max/r)) + // = lcm_rate * multiplier + // + // where r is the base rate and f_max is the maximum tick rate. The case + // where floor() yields 1 must be caught. + // We use shifts here instead of 2^x because exp2() is not available in all compilers, + // also this guarantees no rounding issues. The type cast to int32_t serves as floor(): + int32_t multiplier = (1 << int32_t(uhd::math::log2(max_tick_rate / lcm_rate))); + if (multiplier == 2 and lcm_rate >= min_tick_rate) { + // Don't bother (see above) + multiplier = 1; + } + const double new_rate = lcm_rate * multiplier; + UHD_ASSERT_THROW( + uhd::math::fp_compare::fp_compare_delta<double>(new_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) >= + uhd::math::fp_compare::fp_compare_delta<double>(min_tick_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) + ); + UHD_ASSERT_THROW( + uhd::math::fp_compare::fp_compare_delta<double>(new_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) <= + uhd::math::fp_compare::fp_compare_delta<double>(max_tick_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) + ); + + return new_rate; + } + + bool check_bandwidth(double rate, const std::string dir) + { + if (rate > _codec_ctrl->get_bw_filter_range(dir).stop()) { + UHD_MSG(warning) + << "Selected " << dir << " bandwidth (" << (rate/1e6) << " MHz) exceeds\n" + << "analog frontend filter bandwidth (" << (_codec_ctrl->get_bw_filter_range(dir).stop()/1e6) << " MHz)." + << std::endl; + return false; + } + return true; + } + + void populate_frontend_subtree(uhd::property_tree::sptr subtree, const std::string &key, uhd::direction_t dir) + { + subtree->create<std::string>("name").set("FE-"+key); + + // Sensors + subtree->create<sensor_value_t>("sensors/temp") + .publish(boost::bind(&ad9361_ctrl::get_temperature, _codec_ctrl)) + ; + if (dir == RX_DIRECTION) { + subtree->create<sensor_value_t>("sensors/rssi") + .publish(boost::bind(&ad9361_ctrl::get_rssi, _codec_ctrl, key)) + ; + } + + // Gains + BOOST_FOREACH(const std::string &name, ad9361_ctrl::get_gain_names(key)) + { + subtree->create<meta_range_t>(uhd::fs_path("gains") / name / "range") + .set(ad9361_ctrl::get_gain_range(key)); + subtree->create<double>(uhd::fs_path("gains") / name / "value") + .set(ad936x_manager::DEFAULT_GAIN) + .coerce(boost::bind(&ad9361_ctrl::set_gain, _codec_ctrl, key, _1)) + ; + } + + // FE Settings + subtree->create<std::string>("connection").set("IQ"); + subtree->create<bool>("enabled").set(true); + subtree->create<bool>("use_lo_offset").set(false); + + // Analog Bandwidths + subtree->create<double>("bandwidth/value") + .set(ad936x_manager::DEFAULT_BANDWIDTH) + .coerce(boost::bind(&ad9361_ctrl::set_bw_filter, _codec_ctrl, key, _1)) + ; + subtree->create<meta_range_t>("bandwidth/range") + .publish(boost::bind(&ad9361_ctrl::get_bw_filter_range, key)) + ; + + // LO Tuning + subtree->create<meta_range_t>("freq/range") + .publish(boost::bind(&ad9361_ctrl::get_rf_freq_range)) + ; + subtree->create<double>("freq/value") + .publish(boost::bind(&ad9361_ctrl::get_freq, _codec_ctrl, key)) + .coerce(boost::bind(&ad9361_ctrl::tune, _codec_ctrl, key, _1)) + ; + + // Frontend corrections + if(dir == RX_DIRECTION) + { + subtree->create<bool>("dc_offset/enable" ) + .set(ad936x_manager::DEFAULT_AUTO_DC_OFFSET) + .subscribe(boost::bind(&ad9361_ctrl::set_dc_offset_auto, _codec_ctrl, key, _1)) + ; + subtree->create<bool>("iq_balance/enable" ) + .set(ad936x_manager::DEFAULT_AUTO_IQ_BALANCE) + .subscribe(boost::bind(&ad9361_ctrl::set_iq_balance_auto, _codec_ctrl, key, _1)) + ; + + // AGC setup + const std::list<std::string> mode_strings = boost::assign::list_of("slow")("fast"); + subtree->create<bool>("gain/agc/enable") + .set(DEFAULT_AGC_ENABLE) + .subscribe(boost::bind((&ad9361_ctrl::set_agc), _codec_ctrl, key, _1)) + ; + subtree->create<std::string>("gain/agc/mode/value") + .subscribe(boost::bind((&ad9361_ctrl::set_agc_mode), _codec_ctrl, key, _1)).set(mode_strings.front()) + ; + subtree->create< std::list<std::string> >("gain/agc/mode/options") + .set(mode_strings) + ; + } + + // Frontend filters + BOOST_FOREACH(const std::string &filter_name, _codec_ctrl->get_filter_names(key)) { + subtree->create<filter_info_base::sptr>(uhd::fs_path("filters") / filter_name / "value" ) + .publish(boost::bind(&ad9361_ctrl::get_filter, _codec_ctrl, key, filter_name)) + .subscribe(boost::bind(&ad9361_ctrl::set_filter, _codec_ctrl, key, filter_name, _1)); + } + } + + private: + //! Store a pointer to an actual AD936x control object + ad9361_ctrl::sptr _codec_ctrl; + + //! Do we have 1 or 2 frontends? + const size_t _n_frontends; + + //! List of valid RX frontend names (RX1, RX2) + std::vector<std::string> _rx_frontends; + //! List of valid TX frontend names (TX1, TX2) + std::vector<std::string> _tx_frontends; +}; /* class ad936x_manager_impl */ + +ad936x_manager::sptr ad936x_manager::make( + const ad9361_ctrl::sptr &codec_ctrl, + const size_t n_frontends +) { + return sptr( + new ad936x_manager_impl(codec_ctrl, n_frontends) + ); +} + diff --git a/host/lib/usrp/common/ad936x_manager.hpp b/host/lib/usrp/common/ad936x_manager.hpp new file mode 100644 index 000000000..9b4a351c6 --- /dev/null +++ b/host/lib/usrp/common/ad936x_manager.hpp @@ -0,0 +1,132 @@ +// +// Copyright 2015 Ettus Research +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_AD9361_MANAGER_HPP +#define INCLUDED_AD9361_MANAGER_HPP + +#include <uhd/types/wb_iface.hpp> +#include <uhd/utils/math.hpp> +#include <uhd/property_tree.hpp> +#include <uhd/types/direction.hpp> +#include <boost/shared_ptr.hpp> +#include "ad9361_ctrl.hpp" +#include <stdint.h> + +namespace uhd { namespace usrp { + +/*! AD936x Manager class + * + * This class performs higher (management) tasks on the AD936x. + * It requires a uhd::usrp::ad9361_ctrl object to do the actual + * register peeks/pokes etc. + */ +class ad936x_manager +{ +public: + typedef boost::shared_ptr<ad936x_manager> sptr; + + static const double DEFAULT_GAIN; + static const double DEFAULT_BANDWIDTH; + static const double DEFAULT_TICK_RATE; + static const double DEFAULT_FREQ; // Hz + static const uint32_t DEFAULT_DECIM; + static const uint32_t DEFAULT_INTERP; + static const bool DEFAULT_AUTO_DC_OFFSET; + static const bool DEFAULT_AUTO_IQ_BALANCE; + static const bool DEFAULT_AGC_ENABLE; + + /*! + * \param codec_ctrl The actual AD936x control object + * \param n_frontends Number of frontends (1 or 2) + */ + static sptr make( + const ad9361_ctrl::sptr &codec_ctrl, + const size_t n_frontends + ); + + virtual ~ad936x_manager(void) {}; + + /*! Put the AD936x into a default state. + * + * Sets gains, LOs, bandwidths, etc. according to the DEFAULT_* constants. + */ + virtual void init_codec(void) = 0; + + /*! Run a loopback self test. + * + * This will write data to the AD936x and read it back again. + * If this test fails, it generally means the interface is broken, + * so we assume it passes and throw otherwise. Running this requires + * a core that we can peek and poke the loopback values into. + * + * \param iface An interface to the associated radio control core + * \param iface The radio control core's address to write the loopback value + * \param iface The radio control core's readback address to read back the returned value + * + * \throws a uhd::runtime_error if the loopback value didn't match. + */ + virtual void loopback_self_test( + wb_iface::sptr iface, + wb_iface::wb_addr_type codec_idle_addr, + wb_iface::wb_addr_type codec_readback_addr + ) = 0; + + /*! Determine a tick rate that will work with a given sampling rate + * (assuming a DDC/DUC chain is also available elsewhere). + * + * Example: If we want to stream with a rate of 5 Msps, then the AD936x + * must run at an integer multiple of that. Although not strictly necessary, + * we always try and return a multiple of 2. Let's say we need those 5 Msps + * on two channels, then a good rate is 20 MHz, which is 4 times the sampling + * rate (thus we can use 2 halfbands elsewhere). + * If different rates are used on different channels, this can be particularly + * useful. The clock rate of the AD936x needs to be a multiple of the least + * common multiple of all the rates. Example: We want to transmit with 3 Msps + * and receive with 5 Msps. The LCM of this is 15 Msps, which is used as an + * argument for this function. A good rate is then 30 MHz, which is twice + * the LCM. + * + * \param lcm_rate Least Common Multiple of all the rates involved. + * \param num_chans The number of channels used for the stream. + * + * \returns a valid tick rate that can be used with the given rate + * \throws a uhd::value_error if \p lcm_rate exceeds the max tick rate + */ + virtual double get_auto_tick_rate( + const double lcm_rate, + size_t num_chans + ) = 0; + + /*! Check if a given sampling rate is within the available analog bandwidth. + * + * If not, outputs a warning message and returns false. + */ + virtual bool check_bandwidth(double rate, const std::string dir) = 0; + + /*! Populate the property tree for the device frontend + */ + virtual void populate_frontend_subtree( + uhd::property_tree::sptr subtree, + const std::string &key, + uhd::direction_t dir + ) = 0; + +}; /* class ad936x_manager */ + +}} /* namespace uhd::usrp */ + +#endif /* INCLUDED_AD9361_MANAGER_HPP */ diff --git a/host/lib/usrp/cores/gpio_core_200.hpp b/host/lib/usrp/cores/gpio_core_200.hpp index 164437f40..e22834fd9 100644 --- a/host/lib/usrp/cores/gpio_core_200.hpp +++ b/host/lib/usrp/cores/gpio_core_200.hpp @@ -1,5 +1,5 @@ // -// Copyright 2011,2014 Ettus Research LLC +// Copyright 2011,2014,2015 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 @@ -20,10 +20,34 @@ #include <uhd/config.hpp> #include <uhd/usrp/dboard_iface.hpp> +#include <boost/assign.hpp> #include <boost/cstdint.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> #include <uhd/types/wb_iface.hpp> +#include <map> + +typedef enum { + GPIO_CTRL, + GPIO_DDR, + GPIO_OUT, + GPIO_ATR_0X, + GPIO_ATR_RX, + GPIO_ATR_TX, + GPIO_ATR_XX +} gpio_attr_t; + +typedef std::map<gpio_attr_t,std::string> gpio_attr_map_t; +static const gpio_attr_map_t gpio_attr_map = + boost::assign::map_list_of + (GPIO_CTRL, "CTRL") + (GPIO_DDR, "DDR") + (GPIO_OUT, "OUT") + (GPIO_ATR_0X, "ATR_0X") + (GPIO_ATR_RX, "ATR_RX") + (GPIO_ATR_TX, "ATR_TX") + (GPIO_ATR_XX, "ATR_XX") +; class gpio_core_200 : boost::noncopyable{ public: diff --git a/host/lib/usrp/cores/rx_dsp_core_200.cpp b/host/lib/usrp/cores/rx_dsp_core_200.cpp index 6a36e8fa1..b899085c0 100644 --- a/host/lib/usrp/cores/rx_dsp_core_200.cpp +++ b/host/lib/usrp/cores/rx_dsp_core_200.cpp @@ -295,6 +295,7 @@ public: _iface->poke32(REG_RX_CTRL_FORMAT, format_word); } + private: wb_iface::sptr _iface; const size_t _dsp_base, _ctrl_base; diff --git a/host/lib/usrp/cores/rx_dsp_core_3000.cpp b/host/lib/usrp/cores/rx_dsp_core_3000.cpp index 8a131ffb4..18dabade0 100644 --- a/host/lib/usrp/cores/rx_dsp_core_3000.cpp +++ b/host/lib/usrp/cores/rx_dsp_core_3000.cpp @@ -46,6 +46,9 @@ template <class T> T ceil_log2(T num){ using namespace uhd; +const double rx_dsp_core_3000::DEFAULT_CORDIC_FREQ = 0.0; +const double rx_dsp_core_3000::DEFAULT_RATE = 1e6; + rx_dsp_core_3000::~rx_dsp_core_3000(void){ /* NOP */ } @@ -263,6 +266,24 @@ public: this->update_scalar(); } + void populate_subtree(property_tree::sptr subtree) + { + subtree->create<meta_range_t>("rate/range") + .publish(boost::bind(&rx_dsp_core_3000::get_host_rates, this)) + ; + subtree->create<double>("rate/value") + .set(DEFAULT_RATE) + .coerce(boost::bind(&rx_dsp_core_3000::set_host_rate, this, _1)) + ; + subtree->create<double>("freq/value") + .set(DEFAULT_CORDIC_FREQ) + .coerce(boost::bind(&rx_dsp_core_3000::set_freq, this, _1)) + ; + subtree->create<meta_range_t>("freq/range") + .publish(boost::bind(&rx_dsp_core_3000::get_freq_range, this)) + ; + } + private: wb_iface::sptr _iface; const size_t _dsp_base; diff --git a/host/lib/usrp/cores/rx_dsp_core_3000.hpp b/host/lib/usrp/cores/rx_dsp_core_3000.hpp index 89059e953..65801de1d 100644 --- a/host/lib/usrp/cores/rx_dsp_core_3000.hpp +++ b/host/lib/usrp/cores/rx_dsp_core_3000.hpp @@ -21,14 +21,18 @@ #include <uhd/config.hpp> #include <uhd/stream.hpp> #include <uhd/types/ranges.hpp> -#include <boost/utility.hpp> -#include <boost/shared_ptr.hpp> #include <uhd/types/stream_cmd.hpp> #include <uhd/types/wb_iface.hpp> +#include <uhd/property_tree.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> #include <string> class rx_dsp_core_3000 : boost::noncopyable{ public: + static const double DEFAULT_CORDIC_FREQ; + static const double DEFAULT_RATE; + typedef boost::shared_ptr<rx_dsp_core_3000> sptr; virtual ~rx_dsp_core_3000(void) = 0; @@ -56,6 +60,8 @@ public: virtual double set_freq(const double freq) = 0; virtual void setup(const uhd::stream_args_t &stream_args) = 0; + + virtual void populate_subtree(uhd::property_tree::sptr subtree) = 0; }; #endif /* INCLUDED_LIBUHD_USRP_RX_DSP_CORE_3000_HPP */ diff --git a/host/lib/usrp/cores/rx_frontend_core_200.cpp b/host/lib/usrp/cores/rx_frontend_core_200.cpp index b73896b57..7ac920553 100644 --- a/host/lib/usrp/cores/rx_frontend_core_200.cpp +++ b/host/lib/usrp/cores/rx_frontend_core_200.cpp @@ -17,6 +17,7 @@ #include "rx_frontend_core_200.hpp" #include <boost/math/special_functions/round.hpp> +#include <boost/bind.hpp> using namespace uhd; @@ -38,6 +39,10 @@ rx_frontend_core_200::~rx_frontend_core_200(void){ /* NOP */ } +const std::complex<double> rx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE = std::complex<double>(0.0, 0.0); +const bool rx_frontend_core_200::DEFAULT_DC_OFFSET_ENABLE = true; +const std::complex<double> rx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE = std::complex<double>(0.0, 0.0); + class rx_frontend_core_200_impl : public rx_frontend_core_200{ public: rx_frontend_core_200_impl(wb_iface::sptr iface, const size_t base): @@ -74,6 +79,22 @@ public: _iface->poke32(REG_RX_FE_PHASE_CORRECTION, fs_to_bits(cor.imag(), 18)); } + void populate_subtree(uhd::property_tree::sptr subtree) + { + subtree->create<std::complex<double> >("dc_offset/value") + .set(DEFAULT_DC_OFFSET_VALUE) + .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, this, _1)) + ; + subtree->create<bool>("dc_offset/enable") + .set(DEFAULT_DC_OFFSET_ENABLE) + .subscribe(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, this, _1)) + ; + subtree->create<std::complex<double> >("iq_balance/value") + .set(DEFAULT_IQ_BALANCE_VALUE) + .subscribe(boost::bind(&rx_frontend_core_200::set_iq_balance, this, _1)) + ; + } + private: boost::int32_t _i_dc_off, _q_dc_off; wb_iface::sptr _iface; diff --git a/host/lib/usrp/cores/rx_frontend_core_200.hpp b/host/lib/usrp/cores/rx_frontend_core_200.hpp index 9b18e2089..32ce77e00 100644 --- a/host/lib/usrp/cores/rx_frontend_core_200.hpp +++ b/host/lib/usrp/cores/rx_frontend_core_200.hpp @@ -19,14 +19,19 @@ #define INCLUDED_LIBUHD_USRP_TX_FRONTEND_CORE_200_HPP #include <uhd/config.hpp> +#include <uhd/types/wb_iface.hpp> +#include <uhd/property_tree.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> -#include <uhd/types/wb_iface.hpp> #include <complex> #include <string> class rx_frontend_core_200 : boost::noncopyable{ public: + static const std::complex<double> DEFAULT_DC_OFFSET_VALUE; + static const bool DEFAULT_DC_OFFSET_ENABLE; + static const std::complex<double> DEFAULT_IQ_BALANCE_VALUE; + typedef boost::shared_ptr<rx_frontend_core_200> sptr; virtual ~rx_frontend_core_200(void) = 0; @@ -41,6 +46,8 @@ public: virtual void set_iq_balance(const std::complex<double> &cor) = 0; + virtual void populate_subtree(uhd::property_tree::sptr subtree) = 0; + }; #endif /* INCLUDED_LIBUHD_USRP_TX_FRONTEND_CORE_200_HPP */ diff --git a/host/lib/usrp/cores/tx_dsp_core_3000.cpp b/host/lib/usrp/cores/tx_dsp_core_3000.cpp index 736205402..93b70435f 100644 --- a/host/lib/usrp/cores/tx_dsp_core_3000.cpp +++ b/host/lib/usrp/cores/tx_dsp_core_3000.cpp @@ -37,6 +37,9 @@ template <class T> T ceil_log2(T num){ using namespace uhd; +const double tx_dsp_core_3000::DEFAULT_CORDIC_FREQ = 0.0; +const double tx_dsp_core_3000::DEFAULT_RATE = 1e6; + tx_dsp_core_3000::~tx_dsp_core_3000(void){ /* NOP */ } @@ -200,6 +203,24 @@ public: this->update_scalar(); } + void populate_subtree(property_tree::sptr subtree) + { + subtree->create<meta_range_t>("rate/range") + .publish(boost::bind(&tx_dsp_core_3000::get_host_rates, this)) + ; + subtree->create<double>("rate/value") + .set(DEFAULT_RATE) + .coerce(boost::bind(&tx_dsp_core_3000::set_host_rate, this, _1)) + ; + subtree->create<double>("freq/value") + .set(DEFAULT_CORDIC_FREQ) + .coerce(boost::bind(&tx_dsp_core_3000::set_freq, this, _1)) + ; + subtree->create<meta_range_t>("freq/range") + .publish(boost::bind(&tx_dsp_core_3000::get_freq_range, this)) + ; + } + private: wb_iface::sptr _iface; const size_t _dsp_base; diff --git a/host/lib/usrp/cores/tx_dsp_core_3000.hpp b/host/lib/usrp/cores/tx_dsp_core_3000.hpp index a51cb2803..fbc43add6 100644 --- a/host/lib/usrp/cores/tx_dsp_core_3000.hpp +++ b/host/lib/usrp/cores/tx_dsp_core_3000.hpp @@ -21,12 +21,16 @@ #include <uhd/config.hpp> #include <uhd/stream.hpp> #include <uhd/types/ranges.hpp> +#include <uhd/types/wb_iface.hpp> +#include <uhd/property_tree.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> -#include <uhd/types/wb_iface.hpp> class tx_dsp_core_3000 : boost::noncopyable{ public: + static const double DEFAULT_CORDIC_FREQ; + static const double DEFAULT_RATE; + typedef boost::shared_ptr<tx_dsp_core_3000> sptr; virtual ~tx_dsp_core_3000(void) = 0; @@ -51,6 +55,8 @@ public: virtual double set_freq(const double freq) = 0; virtual void setup(const uhd::stream_args_t &stream_args) = 0; + + virtual void populate_subtree(uhd::property_tree::sptr subtree) = 0; }; #endif /* INCLUDED_LIBUHD_USRP_TX_DSP_CORE_3000_HPP */ diff --git a/host/lib/usrp/cores/tx_frontend_core_200.cpp b/host/lib/usrp/cores/tx_frontend_core_200.cpp index 7000f46bd..0fa028571 100644 --- a/host/lib/usrp/cores/tx_frontend_core_200.cpp +++ b/host/lib/usrp/cores/tx_frontend_core_200.cpp @@ -20,6 +20,7 @@ #include <uhd/exception.hpp> #include <boost/assign/list_of.hpp> #include <boost/math/special_functions/round.hpp> +#include <boost/bind.hpp> using namespace uhd; @@ -29,6 +30,9 @@ using namespace uhd; #define REG_TX_FE_PHASE_CORRECTION _base + 12 //18 bits #define REG_TX_FE_MUX _base + 16 //8 bits (std output = 0x10, reversed = 0x01) +const std::complex<double> tx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE = std::complex<double>(0.0, 0.0); +const std::complex<double> tx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE = std::complex<double>(0.0, 0.0); + 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)))); } @@ -71,6 +75,18 @@ public: _iface->poke32(REG_TX_FE_PHASE_CORRECTION, fs_to_bits(cor.imag(), 18)); } + void populate_subtree(uhd::property_tree::sptr subtree) + { + subtree->create< std::complex<double> >("dc_offset/value") + .set(DEFAULT_DC_OFFSET_VALUE) + .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, this, _1)) + ; + subtree->create< std::complex<double> >("iq_balance/value") + .set(DEFAULT_IQ_BALANCE_VALUE) + .subscribe(boost::bind(&tx_frontend_core_200::set_iq_balance, this, _1)) + ; + } + private: wb_iface::sptr _iface; const size_t _base; diff --git a/host/lib/usrp/cores/tx_frontend_core_200.hpp b/host/lib/usrp/cores/tx_frontend_core_200.hpp index 0b89ea818..912256329 100644 --- a/host/lib/usrp/cores/tx_frontend_core_200.hpp +++ b/host/lib/usrp/cores/tx_frontend_core_200.hpp @@ -19,9 +19,10 @@ #define INCLUDED_LIBUHD_USRP_RX_FRONTEND_CORE_200_HPP #include <uhd/config.hpp> +#include <uhd/types/wb_iface.hpp> +#include <uhd/property_tree.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> -#include <uhd/types/wb_iface.hpp> #include <complex> #include <string> @@ -29,6 +30,9 @@ class tx_frontend_core_200 : boost::noncopyable{ public: typedef boost::shared_ptr<tx_frontend_core_200> sptr; + static const std::complex<double> DEFAULT_DC_OFFSET_VALUE; + static const std::complex<double> DEFAULT_IQ_BALANCE_VALUE; + virtual ~tx_frontend_core_200(void) = 0; static sptr make(uhd::wb_iface::sptr iface, const size_t base); @@ -39,6 +43,8 @@ public: virtual void set_iq_balance(const std::complex<double> &cor) = 0; + virtual void populate_subtree(uhd::property_tree::sptr subtree) = 0; + }; #endif /* INCLUDED_LIBUHD_USRP_RX_FRONTEND_CORE_200_HPP */ diff --git a/host/lib/usrp/dboard/db_cbx.cpp b/host/lib/usrp/dboard/db_cbx.cpp index 8336117b8..daf9a8dfd 100644 --- a/host/lib/usrp/dboard/db_cbx.cpp +++ b/host/lib/usrp/dboard/db_cbx.cpp @@ -38,7 +38,7 @@ sbx_xcvr::cbx::~cbx(void){ /* NOP */ } -void sbx_xcvr::cbx::write_lo_regs(dboard_iface::unit_t unit, std::vector<boost::uint32_t> ®s) +void sbx_xcvr::cbx::write_lo_regs(dboard_iface::unit_t unit, const std::vector<boost::uint32_t> ®s) { BOOST_FOREACH(boost::uint32_t reg, regs) { diff --git a/host/lib/usrp/dboard/db_sbx_common.hpp b/host/lib/usrp/dboard/db_sbx_common.hpp index a08d22537..4800bbd83 100644 --- a/host/lib/usrp/dboard/db_sbx_common.hpp +++ b/host/lib/usrp/dboard/db_sbx_common.hpp @@ -1,5 +1,5 @@ // -// Copyright 2011-2014 Ettus Research LLC +// Copyright 2011-2015 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 @@ -225,7 +225,7 @@ protected: /*! This is the registered instance of the wrapper class, sbx_base. */ sbx_xcvr *self_base; private: - void write_lo_regs(dboard_iface::unit_t unit, std::vector<boost::uint32_t> ®s); + void write_lo_regs(dboard_iface::unit_t unit, const std::vector<boost::uint32_t> ®s); max287x_iface::sptr _txlo; max287x_iface::sptr _rxlo; }; diff --git a/host/lib/usrp/dboard_eeprom.cpp b/host/lib/usrp/dboard_eeprom.cpp index f2bee47a9..3b56ae19a 100644 --- a/host/lib/usrp/dboard_eeprom.cpp +++ b/host/lib/usrp/dboard_eeprom.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2011,2015 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 @@ -15,6 +15,7 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // +#include <uhd/types/byte_vector.hpp> #include <uhd/usrp/dboard_eeprom.hpp> #include <uhd/exception.hpp> #include <uhd/utils/log.hpp> @@ -27,30 +28,6 @@ using namespace uhd; using namespace uhd::usrp; -/*********************************************************************** - * Utility functions - **********************************************************************/ - -//! create a string from a byte vector, return empty if invalid ascii -static const std::string bytes_to_string(const byte_vector_t &bytes){ - std::string out; - BOOST_FOREACH(boost::uint8_t byte, bytes){ - if (byte < 32 or byte > 127) return out; - out += byte; - } - return out; -} - -//! create a byte vector from a string, null terminate unless max length -static const byte_vector_t string_to_bytes(const std::string &string, size_t max_length){ - byte_vector_t bytes; - for (size_t i = 0; i < std::min(string.size(), max_length); i++){ - bytes.push_back(string[i]); - } - if (bytes.size() < max_length - 1) bytes.push_back('\0'); - return bytes; -} - //////////////////////////////////////////////////////////////////////// // format of daughterboard EEPROM // 00: 0xDB code for ``I'm a daughterboard'' diff --git a/host/lib/usrp/dboard_eeprom_c.cpp b/host/lib/usrp/dboard_eeprom_c.cpp new file mode 100644 index 000000000..e3ef4933f --- /dev/null +++ b/host/lib/usrp/dboard_eeprom_c.cpp @@ -0,0 +1,109 @@ +// +// Copyright 2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/usrp/dboard_eeprom.h> +#include <uhd/error.h> + +#include <boost/lexical_cast.hpp> + +#include <string.h> + +uhd_error uhd_dboard_eeprom_make( + uhd_dboard_eeprom_handle* h +){ + UHD_SAFE_C( + *h = new uhd_dboard_eeprom_t; + ) +} + +uhd_error uhd_dboard_eeprom_free( + uhd_dboard_eeprom_handle* h +){ + UHD_SAFE_C( + delete *h; + *h = NULL; + ) +} + +uhd_error uhd_dboard_eeprom_get_id( + uhd_dboard_eeprom_handle h, + char* id_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string dboard_id_cpp = h->dboard_eeprom_cpp.id.to_string(); + strncpy(id_out, dboard_id_cpp.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_dboard_eeprom_set_id( + uhd_dboard_eeprom_handle h, + const char* id +){ + UHD_SAFE_C_SAVE_ERROR(h, + h->dboard_eeprom_cpp.id = uhd::usrp::dboard_id_t::from_string(id); + ) +} + +uhd_error uhd_dboard_eeprom_get_serial( + uhd_dboard_eeprom_handle h, + char* id_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string dboard_serial_cpp = h->dboard_eeprom_cpp.serial; + strncpy(id_out, dboard_serial_cpp.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_dboard_eeprom_set_serial( + uhd_dboard_eeprom_handle h, + const char* serial +){ + UHD_SAFE_C_SAVE_ERROR(h, + h->dboard_eeprom_cpp.serial = serial; + ) +} + +uhd_error uhd_dboard_eeprom_get_revision( + uhd_dboard_eeprom_handle h, + int* revision_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *revision_out = boost::lexical_cast<int>(h->dboard_eeprom_cpp.revision); + ) +} + +uhd_error uhd_dboard_eeprom_set_revision( + uhd_dboard_eeprom_handle h, + int revision +){ + UHD_SAFE_C_SAVE_ERROR(h, + h->dboard_eeprom_cpp.revision = boost::lexical_cast<std::string>(revision); + ) +} + +uhd_error uhd_dboard_eeprom_last_error( + uhd_dboard_eeprom_handle h, + char* error_out, + size_t strbuffer_len +){ + UHD_SAFE_C( + memset(error_out, '\0', strbuffer_len); + strncpy(error_out, h->last_error.c_str(), strbuffer_len); + ) +} diff --git a/host/lib/usrp/e100/CMakeLists.txt b/host/lib/usrp/e100/CMakeLists.txt index ac9d8c655..2a1e14eab 100644 --- a/host/lib/usrp/e100/CMakeLists.txt +++ b/host/lib/usrp/e100/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-2011,2015 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 @@ -22,7 +22,7 @@ ######################################################################## # Conditionally configure the USRP-E100 support ######################################################################## -LIBUHD_REGISTER_COMPONENT("E100" ENABLE_E100 OFF "ENABLE_LIBUHD;LINUX" OFF) +LIBUHD_REGISTER_COMPONENT("E100" ENABLE_E100 OFF "ENABLE_LIBUHD;LINUX" OFF OFF) IF(ENABLE_E100) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include) diff --git a/host/lib/usrp/e100/e100_impl.cpp b/host/lib/usrp/e100/e100_impl.cpp index ac419e0e0..6d3c08534 100644 --- a/host/lib/usrp/e100/e100_impl.cpp +++ b/host/lib/usrp/e100/e100_impl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2012,2014 Ettus Research LLC +// Copyright 2010-2012,2014-2015 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 @@ -28,6 +28,7 @@ #include <boost/functional/hash.hpp> #include <boost/assign/list_of.hpp> #include <fstream> +#include <iostream> #include <ctime> using namespace uhd; @@ -45,7 +46,7 @@ namespace fs = boost::filesystem; /*********************************************************************** * Discovery **********************************************************************/ -static device_addrs_t e100_find(const device_addr_t &hint){ +device_addrs_t e100_find(const device_addr_t &hint){ device_addrs_t e100_addrs; //return an empty list of addresses when type is set to non-usrp-e @@ -104,17 +105,10 @@ static const uhd::dict<std::string, std::string> model_to_fpga_file_name = boost ("E110", "usrp_e110_fpga.bin") ; -/*********************************************************************** - * Structors - **********************************************************************/ -e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ - _tree = property_tree::make(); - _type = device::USRP; - _ignore_cal_file = device_addr.has_key("ignore-cal-file"); - +std::string get_default_e1x0_fpga_image(const uhd::device_addr_t &device_addr){ //read the eeprom so we can determine the hardware - _dev_i2c_iface = e100_ctrl::make_dev_i2c_iface(E100_I2C_DEV_NODE); - const mboard_eeprom_t mb_eeprom(*_dev_i2c_iface, E100_EEPROM_MAP_KEY); + uhd::i2c_iface::sptr dev_i2c_iface = e100_ctrl::make_dev_i2c_iface(E100_I2C_DEV_NODE); + const mboard_eeprom_t mb_eeprom(*dev_i2c_iface, E100_EEPROM_MAP_KEY); //determine the model string for this device const std::string model = device_addr.get("model", mb_eeprom.get("model", "")); @@ -126,7 +120,21 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ ) % model)); //extract the fpga path and compute hash - const std::string default_fpga_file_name = model_to_fpga_file_name[model]; + return model_to_fpga_file_name[model]; +} + +/*********************************************************************** + * Structors + **********************************************************************/ +e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ + _tree = property_tree::make(); + _type = device::USRP; + _ignore_cal_file = device_addr.has_key("ignore-cal-file"); + + _dev_i2c_iface = e100_ctrl::make_dev_i2c_iface(E100_I2C_DEV_NODE); + const mboard_eeprom_t mb_eeprom(*_dev_i2c_iface, E100_EEPROM_MAP_KEY); + const std::string default_fpga_file_name = get_default_e1x0_fpga_image(device_addr); + const std::string model = device_addr["model"]; std::string e100_fpga_image; try{ e100_fpga_image = find_image_path(device_addr.get("fpga", default_fpga_file_name)); diff --git a/host/lib/usrp/e100/e100_impl.hpp b/host/lib/usrp/e100/e100_impl.hpp index 4efc21427..d00668224 100644 --- a/host/lib/usrp/e100/e100_impl.hpp +++ b/host/lib/usrp/e100/e100_impl.hpp @@ -29,6 +29,7 @@ #include "recv_packet_demuxer.hpp" #include <uhd/device.hpp> #include <uhd/property_tree.hpp> +#include <uhd/types/device_addr.hpp> #include <uhd/usrp/subdev_spec.hpp> #include <uhd/usrp/dboard_eeprom.hpp> #include <uhd/usrp/mboard_eeprom.hpp> @@ -68,6 +69,9 @@ uhd::usrp::dboard_iface::sptr make_e100_dboard_iface( e100_codec_ctrl::sptr codec ); +uhd::device_addrs_t e100_find(const uhd::device_addr_t &hint); +std::string get_default_e1x0_fpga_image(const uhd::device_addr_t &device_addr); + /*! * USRP-E100 implementation guts: * The implementation details are encapsulated here. diff --git a/host/lib/usrp/e100/fpga_downloader.cpp b/host/lib/usrp/e100/fpga_downloader.cpp index c9d77f560..9abde32f7 100644 --- a/host/lib/usrp/e100/fpga_downloader.cpp +++ b/host/lib/usrp/e100/fpga_downloader.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011,2014 Ettus Research LLC +// Copyright 2010-2011,2014-2015 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 @@ -17,8 +17,16 @@ #include <uhd/config.hpp> #ifdef UHD_DLL_EXPORTS +#include <boost/filesystem.hpp> +#include <boost/format.hpp> #include <uhd/exception.hpp> +#include <uhd/device.hpp> +#include <uhd/image_loader.hpp> +#include <uhd/types/device_addr.hpp> #include <uhd/utils/msg.hpp> +#include <uhd/utils/paths.hpp> +#include <uhd/utils/static.hpp> +#include "e100_impl.hpp" #else //special case when this file is externally included #include <stdexcept> #include <iostream> @@ -270,3 +278,34 @@ void e100_load_fpga(const std::string &bin_file){ } +#ifdef UHD_DLL_EXPORTS +namespace fs = boost::filesystem; + +static bool e100_image_loader(const uhd::image_loader::image_loader_args_t &image_loader_args){ + // Make sure this is an E1x0 + uhd::device_addrs_t devs = e100_find(uhd::device_addr_t()); + if(devs.size() == 0 or !image_loader_args.load_fpga) return false; + + std::string fpga_filename; + if(image_loader_args.fpga_path == ""){ + fpga_filename = uhd::find_image_path(get_default_e1x0_fpga_image(devs[0])); + } + else{ + if(not fs::exists(image_loader_args.fpga_path)){ + throw uhd::runtime_error(str(boost::format("The path \"%s\" does not exist.") + % image_loader_args.fpga_path)); + } + else fpga_filename = image_loader_args.fpga_path; + } + + e100_load_fpga(fpga_filename); + return true; +} + +UHD_STATIC_BLOCK(register_e100_image_loader){ + std::string recovery_instructions = "The default FPGA image will be loaded the next time " + "UHD uses this device."; + + uhd::image_loader::register_image_loader("e100", e100_image_loader, recovery_instructions); +} +#endif /* UHD_DLL_EXPORTS */ diff --git a/host/lib/usrp/e300/CMakeLists.txt b/host/lib/usrp/e300/CMakeLists.txt index 9ee9b5521..9c8aa29b9 100644 --- a/host/lib/usrp/e300/CMakeLists.txt +++ b/host/lib/usrp/e300/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2014 Ettus Research LLC +# Copyright 2013-2015 Ettus Research LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -24,7 +24,7 @@ ######################################################################## find_package(UDev) -LIBUHD_REGISTER_COMPONENT("E300" ENABLE_E300 OFF "ENABLE_LIBUHD" OFF) +LIBUHD_REGISTER_COMPONENT("E300" ENABLE_E300 OFF "ENABLE_LIBUHD" OFF OFF) IF(ENABLE_E300) LIST(APPEND E300_SOURCES @@ -39,17 +39,23 @@ IF(ENABLE_E300) ${CMAKE_CURRENT_SOURCE_DIR}/e300_i2c.cpp ${CMAKE_CURRENT_SOURCE_DIR}/e300_eeprom_manager.cpp ${CMAKE_CURRENT_SOURCE_DIR}/e300_common.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/e300_async_serial.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/e300_ublox_control_impl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/e300_remote_codec_ctrl.cpp ) LIBUHD_APPEND_SOURCES(${E300_SOURCES}) - IF(UDEV_FOUND) + IF(UDEV_FOUND AND NOT E300_FORCE_NETWORK) INCLUDE_DIRECTORIES(${UDEV_INCLUDE_DIR}) LIBUHD_APPEND_LIBS(${UDEV_LIBS}) SET_SOURCE_FILES_PROPERTIES( ${E300_SOURCES} PROPERTIES COMPILE_DEFINITIONS "E300_NATIVE=1" ) - ENDIF(UDEV_FOUND) + ENDIF(UDEV_FOUND AND NOT E300_FORCE_NETWORK) + + IF(ENABLE_GPSD) + SET_SOURCE_FILES_PROPERTIES( + ${CMAKE_CURRENT_SOURCE_DIR}/e300_impl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/e300_impl.hpp + PROPERTIES COMPILE_DEFINITIONS "E300_GPSD=1" + ) + ENDIF(ENABLE_GPSD) ENDIF(ENABLE_E300) diff --git a/host/lib/usrp/e300/e300_async_serial.cpp b/host/lib/usrp/e300/e300_async_serial.cpp deleted file mode 100644 index cdf18f7f7..000000000 --- a/host/lib/usrp/e300/e300_async_serial.cpp +++ /dev/null @@ -1,245 +0,0 @@ -// -// Copyright 2014 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 "e300_async_serial.hpp" - -namespace uhd { namespace usrp { namespace gps { - -async_serial::async_serial() - : _io(), - _port(_io), - _background_thread(), - _open(false), - _error(false) -{ -} - -async_serial::async_serial( - const std::string &node, - const size_t baud_rate, - boost::asio::serial_port_base::parity opt_parity, - boost::asio::serial_port_base::character_size opt_csize, - boost::asio::serial_port_base::flow_control opt_flow, - boost::asio::serial_port_base::stop_bits opt_stop) - : _io(), - _port(_io), - _background_thread(), - _open(false), - _error(false) -{ - open(node, baud_rate, opt_parity, opt_csize, opt_flow, opt_stop); -} - -void async_serial::open( - const std::string &node, - const size_t baud_rate, - boost::asio::serial_port_base::parity opt_parity, - boost::asio::serial_port_base::character_size opt_csize, - boost::asio::serial_port_base::flow_control opt_flow, - boost::asio::serial_port_base::stop_bits opt_stop) -{ - if(is_open()) - close(); - - _set_error_status(true); - _port.open(node); - _port.set_option( - boost::asio::serial_port_base::baud_rate(baud_rate)); - _port.set_option(opt_parity); - _port.set_option(opt_csize); - _port.set_option(opt_flow); - _port.set_option(opt_stop); - - _io.post(boost::bind(&async_serial::_do_read, this)); - - boost::thread t(boost::bind(&boost::asio::io_service::run, &_io)); - _background_thread.swap(t); - _set_error_status(false); - _open=true; -} - -bool async_serial::is_open() const -{ - return _open; -} - -bool async_serial::error_status() const -{ - boost::lock_guard<boost::mutex> l(_error_mutex); - return _error; -} - -void async_serial::close() -{ - if(!is_open()) - return; - - _open=false; - _io.post(boost::bind(&async_serial::_do_close, this)); - _background_thread.join(); - _io.reset(); - if(error_status()) - throw(boost::system::system_error(boost::system::error_code(), - "Error while closing the device")); -} - -void async_serial::write(const char *data, size_t size) -{ - { - boost::lock_guard<boost::mutex> l(_write_queue_mutex); - _write_queue.insert(_write_queue.end(), data, data+size); - } - _io.post(boost::bind(&async_serial::_do_write, this)); -} - -void async_serial::write(const std::vector<char> &data) -{ - { - boost::lock_guard<boost::mutex> l(_write_queue_mutex); - _write_queue.insert( - _write_queue.end(), - data.begin(), - data.end()); - } - _io.post(boost::bind(&async_serial::_do_write, this)); -} - -void async_serial::write_string(const std::string &s) -{ - { - boost::lock_guard<boost::mutex> l(_write_queue_mutex); - _write_queue.insert( - _write_queue.end(), - s.begin(), - s.end()); - } - _io.post(boost::bind(&async_serial::_do_write, this)); -} - -async_serial::~async_serial() -{ - if(is_open()) { - try { - close(); - } catch(...) { - //Don't throw from a destructor - } - } -} - -void async_serial::_do_read() -{ - _port.async_read_some(boost::asio::buffer( - _read_buffer,READ_BUFFER_SIZE), - boost::bind(&async_serial::_read_end, - this, - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred)); -} - -void async_serial::_read_end( - const boost::system::error_code& error, - size_t bytes_transferred) -{ - if(error) { - if(is_open()) { - _do_close(); - _set_error_status(true); - } - } else { - if(_callback) - _callback( - _read_buffer, - bytes_transferred); - _do_read(); - } -} - -void async_serial::_do_write() -{ - // if a write operation is already in progress, do nothing - if(_write_buffer == 0) { - boost::lock_guard<boost::mutex> l(_write_queue_mutex); - _write_buffer_size=_write_queue.size(); - _write_buffer.reset(new char[_write_queue.size()]); - std::copy(_write_queue.begin(),_write_queue.end(), - _write_buffer.get()); - _write_queue.clear(); - async_write( - _port, boost::asio::buffer(_write_buffer.get(), - _write_buffer_size), - boost::bind( - &async_serial::_write_end, - this, - boost::asio::placeholders::error)); - } -} - -void async_serial::_write_end(const boost::system::error_code& error) -{ - if(!error) { - boost::lock_guard<boost::mutex> l(_write_queue_mutex); - if(_write_queue.empty()) { - _write_buffer.reset(); - _write_buffer_size=0; - return; - } - _write_buffer_size = _write_queue.size(); - _write_buffer.reset(new char[_write_queue.size()]); - std::copy(_write_queue.begin(),_write_queue.end(), - _write_buffer.get()); - _write_queue.clear(); - async_write( - _port, - boost::asio::buffer(_write_buffer.get(), - _write_buffer_size), - boost::bind( - &async_serial::_write_end, - this, - boost::asio::placeholders::error)); - } else { - _set_error_status(true); - _do_close(); - } -} - -void async_serial::_do_close() -{ - boost::system::error_code ec; - _port.cancel(ec); - if(ec) - _set_error_status(true); - _port.close(ec); - if(ec) - _set_error_status(true); -} - -void async_serial::_set_error_status(const bool e) -{ - boost::lock_guard<boost::mutex> l(_error_mutex); - _error = e; -} - - -void async_serial::set_read_callback( - const boost::function<void (const char*, size_t)> &callback) -{ - _callback = callback; -} - - -}}} // namespace diff --git a/host/lib/usrp/e300/e300_async_serial.hpp b/host/lib/usrp/e300/e300_async_serial.hpp deleted file mode 100644 index fafc7de3d..000000000 --- a/host/lib/usrp/e300/e300_async_serial.hpp +++ /dev/null @@ -1,113 +0,0 @@ -// -// Copyright 2013-2014 Ettus Research LLC -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. -// - -#ifndef INCLUDED_ASYNC_SERIAL_HPP -#define INCLUDED_ASYNC_SERIAL_HPP - -#include <boost/asio.hpp> -#include <boost/bind.hpp> -#include <boost/thread.hpp> -#include <boost/utility.hpp> -#include <boost/function.hpp> -#include <boost/shared_array.hpp> - -namespace uhd { namespace usrp { namespace gps { - -class async_serial : private boost::noncopyable -{ -public: - async_serial(); - ~async_serial(); - - async_serial(const std::string &node, const size_t baud_rate, - boost::asio::serial_port_base::parity opt_parity= - boost::asio::serial_port_base::parity( - boost::asio::serial_port_base::parity::none), - boost::asio::serial_port_base::character_size opt_csize= - boost::asio::serial_port_base::character_size(8), - boost::asio::serial_port_base::flow_control opt_flow= - boost::asio::serial_port_base::flow_control( - boost::asio::serial_port_base::flow_control::none), - boost::asio::serial_port_base::stop_bits opt_stop= - boost::asio::serial_port_base::stop_bits( - boost::asio::serial_port_base::stop_bits::one)); - - void open(const std::string& node, const size_t baud_rate, - boost::asio::serial_port_base::parity opt_parity= - boost::asio::serial_port_base::parity( - boost::asio::serial_port_base::parity::none), - boost::asio::serial_port_base::character_size opt_csize= - boost::asio::serial_port_base::character_size(8), - boost::asio::serial_port_base::flow_control opt_flow= - boost::asio::serial_port_base::flow_control( - boost::asio::serial_port_base::flow_control::none), - boost::asio::serial_port_base::stop_bits opt_stop= - boost::asio::serial_port_base::stop_bits( - boost::asio::serial_port_base::stop_bits::one)); - - bool is_open(void) const; - - bool error_status(void) const; - - void close(void); - - void write(const char *data, const size_t size); - void write(const std::vector<char> &data); - - void write_string(const std::string &s); - - static const size_t READ_BUFFER_SIZE=512; - - void set_read_callback( - const boost::function<void (const char*, size_t)>& callback); - - void clear_callback(); - -private: // methods - void _do_read(); - - void _read_end( - const boost::system::error_code &error, - size_t bytes_transferred); - - void _do_write(); - - void _write_end(const boost::system::error_code &error); - - void _do_close(); - - void _set_error_status(const bool e); -private: // members - boost::asio::io_service _io; - boost::asio::serial_port _port; - boost::thread _background_thread; - bool _open; - bool _error; - mutable boost::mutex _error_mutex; - - std::vector<char> _write_queue; - boost::shared_array<char> _write_buffer; - size_t _write_buffer_size; - boost::mutex _write_queue_mutex; - char _read_buffer[READ_BUFFER_SIZE]; - - boost::function<void (const char*, size_t)> _callback; -}; - -}}} // namespace - -#endif //INCLUDED_ASYNC_SERIAL_HPP diff --git a/host/lib/usrp/e300/e300_common.cpp b/host/lib/usrp/e300/e300_common.cpp index db5b37055..216713bc6 100644 --- a/host/lib/usrp/e300/e300_common.cpp +++ b/host/lib/usrp/e300/e300_common.cpp @@ -1,5 +1,5 @@ // -// Copyright 2014 Ettus Research LLC +// Copyright 2014-2015 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 @@ -14,8 +14,12 @@ // 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/image_loader.hpp> #include <uhd/utils/msg.hpp> +#include <uhd/utils/paths.hpp> +#include <uhd/utils/static.hpp> +#include "e300_impl.hpp" #include "e300_fifo_config.hpp" #include "e300_fifo_config.hpp" @@ -23,7 +27,9 @@ #include <boost/filesystem.hpp> #include <fstream> +#include <string> +#ifdef E300_NATIVE namespace uhd { namespace usrp { namespace e300 { namespace common { @@ -54,6 +60,49 @@ void load_fpga_image(const std::string &path) UHD_MSG(status) << " done" << std::endl; } +static bool e300_image_loader(const image_loader::image_loader_args_t &image_loader_args) { + // Make sure this is an E3x0 and we don't want to use anything connected + uhd::device_addrs_t devs = e300_find(image_loader_args.args); + if(devs.size() == 0 or !image_loader_args.load_fpga) return false; + + std::string fpga_filename, idle_image; // idle_image never used, just needed for function + if(image_loader_args.fpga_path == "") { + get_e3x0_fpga_images(devs[0], fpga_filename, idle_image); + } + else { + if(not boost::filesystem::exists(image_loader_args.fpga_path)) { + throw uhd::runtime_error(str(boost::format("The path \"%s\" does not exist.") + % image_loader_args.fpga_path)); + } + else fpga_filename = image_loader_args.fpga_path; + } + + load_fpga_image(fpga_filename); + return true; +} + +UHD_STATIC_BLOCK(register_e300_image_loader) { + std::string recovery_instructions = "The default FPGA image will be loaded the next " + "time UHD uses this device."; + + image_loader::register_image_loader("e3x0", e300_image_loader, recovery_instructions); +} + +} + +}}} + +#else +namespace uhd { namespace usrp { namespace e300 { + +namespace common { + +void load_fpga_image(const std::string&) +{ + throw uhd::assertion_error("load_fpga_image() !E300_NATIVE"); +} + } }}} +#endif diff --git a/host/lib/usrp/e300/e300_defaults.hpp b/host/lib/usrp/e300/e300_defaults.hpp index d409062c5..267897e03 100644 --- a/host/lib/usrp/e300/e300_defaults.hpp +++ b/host/lib/usrp/e300/e300_defaults.hpp @@ -23,18 +23,13 @@ namespace uhd { namespace usrp { namespace e300 { static const double DEFAULT_TICK_RATE = 32e6; -static const double MAX_TICK_RATE = 50e6; -static const double MIN_TICK_RATE = 1e6; +static const double MIN_TICK_RATE = 10e6; static const double DEFAULT_TX_SAMP_RATE = 1.0e6; static const double DEFAULT_RX_SAMP_RATE = 1.0e6; static const double DEFAULT_DDC_FREQ = 0.0; static const double DEFAULT_DUC_FREQ = 0.0; -static const double DEFAULT_FE_GAIN = 0.0; -static const double DEFAULT_FE_FREQ = 1.0e9; -static const double DEFAULT_FE_BW = 56e6; - static const std::string DEFAULT_TIME_SRC = "internal"; static const std::string DEFAULT_CLOCK_SRC = "internal"; @@ -73,7 +68,7 @@ public: digital_interface_delays_t get_digital_interface_timing() { digital_interface_delays_t delays; delays.rx_clk_delay = 0; - delays.rx_data_delay = 0xF; + delays.rx_data_delay = 0x8; delays.tx_clk_delay = 0; delays.tx_data_delay = 0xF; return delays; diff --git a/host/lib/usrp/e300/e300_fpga_defs.hpp b/host/lib/usrp/e300/e300_fpga_defs.hpp index eea4d7f63..f514ec9e6 100644 --- a/host/lib/usrp/e300/e300_fpga_defs.hpp +++ b/host/lib/usrp/e300/e300_fpga_defs.hpp @@ -21,7 +21,7 @@ namespace uhd { namespace usrp { namespace e300 { namespace fpga { static const size_t NUM_RADIOS = 2; -static const boost::uint32_t COMPAT_MAJOR = 8; +static const boost::uint32_t COMPAT_MAJOR = 10; static const boost::uint32_t COMPAT_MINOR = 0; }}}} // namespace diff --git a/host/lib/usrp/e300/e300_impl.cpp b/host/lib/usrp/e300/e300_impl.cpp index a08168eab..6d66e83c0 100644 --- a/host/lib/usrp/e300/e300_impl.cpp +++ b/host/lib/usrp/e300/e300_impl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2013-2014 Ettus Research LLC +// Copyright 2013-2015 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 @@ -121,7 +121,7 @@ static bool is_loopback(const if_addrs_t &if_addrs) return if_addrs.inet == asio::ip::address_v4::loopback().to_string(); } -static device_addrs_t e300_find(const device_addr_t &multi_dev_hint) +device_addrs_t e300_find(const device_addr_t &multi_dev_hint) { // handle multi device discovery device_addrs_t hints = separate_device_addr(multi_dev_hint); @@ -268,6 +268,36 @@ static device::sptr e300_make(const device_addr_t &device_addr) return device::sptr(new e300_impl(device_addr)); } +// Common code used by e300_impl and e300_image_loader +void get_e3x0_fpga_images(const uhd::device_addr_t &device_addr, + std::string &fpga_image, + std::string &idle_image){ + const boost::uint16_t pid = boost::lexical_cast<boost::uint16_t>( + device_addr["product"]); + + //extract the FPGA path for the e300 + switch(e300_eeprom_manager::get_mb_type(pid)) { + case e300_eeprom_manager::USRP_E310_MB: + fpga_image = device_addr.cast<std::string>("fpga", + find_image_path(E310_FPGA_FILE_NAME)); + idle_image = find_image_path(E310_FPGA_IDLE_FILE_NAME); + break; + case e300_eeprom_manager::USRP_E300_MB: + fpga_image = device_addr.cast<std::string>("fpga", + find_image_path(E300_FPGA_FILE_NAME)); + idle_image = find_image_path(E300_FPGA_IDLE_FILE_NAME); + break; + case e300_eeprom_manager::UNKNOWN: + default: + UHD_MSG(warning) << "Unknown motherboard type, loading e300 image." + << std::endl; + fpga_image = device_addr.cast<std::string>("fpga", + find_image_path(E300_FPGA_FILE_NAME)); + idle_image = find_image_path(E300_FPGA_IDLE_FILE_NAME); + break; + } +} + /*********************************************************************** * Structors **********************************************************************/ @@ -286,33 +316,10 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr) if (_xport_path == AXI) { _do_not_reload = device_addr.has_key("no_reload_fpga"); if (not _do_not_reload) { - // Load FPGA image if provided via args - const boost::uint16_t pid = boost::lexical_cast<boost::uint16_t>( - device_addr["product"]); - std::string fpga_image; - - //extract the FPGA path for the e300 - switch(e300_eeprom_manager::get_mb_type(pid)) { - case e300_eeprom_manager::USRP_E310_MB: - fpga_image = device_addr.cast<std::string>("fpga", - find_image_path(E310_FPGA_FILE_NAME)); - _idle_image = find_image_path(E310_FPGA_IDLE_FILE_NAME); - break; - case e300_eeprom_manager::USRP_E300_MB: - fpga_image = device_addr.cast<std::string>("fpga", - find_image_path(E300_FPGA_FILE_NAME)); - _idle_image = find_image_path(E300_FPGA_IDLE_FILE_NAME); - break; - case e300_eeprom_manager::UNKNOWN: - default: - UHD_MSG(warning) << "Unknown motherboard type, loading e300 image." - << std::endl; - fpga_image = device_addr.cast<std::string>("fpga", - find_image_path(E300_FPGA_FILE_NAME)); - _idle_image = find_image_path(E300_FPGA_IDLE_FILE_NAME); - break; - } + get_e3x0_fpga_images(device_addr, + fpga_image, + _idle_image); common::load_fpga_image(fpga_image); } } @@ -387,18 +394,35 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr) // This is horrible ... why do I have to sleep here? boost::this_thread::sleep(boost::posix_time::milliseconds(100)); _eeprom_manager = boost::make_shared<e300_eeprom_manager>(i2c::make_i2cdev(E300_I2CDEV_DEVICE)); + _sensor_manager = e300_sensor_manager::make_local(_global_regs); } + _codec_mgr = ad936x_manager::make(_codec_ctrl, fpga::NUM_RADIOS); - UHD_MSG(status) << "Detecting internal GPSDO.... " << std::flush; - if (_xport_path == AXI) { - try { - _gps = gps::ublox::ubx::control::make("/dev/ttyPS1", 9600); - } catch (std::exception &e) { - UHD_MSG(error) << "An error occured making GPSDO control: " << e.what() << std::endl; +#ifdef E300_GPSD + UHD_MSG(status) << "Detecting internal GPSDO " << std::flush; + try { + if (_xport_path == AXI) + _gps = gpsd_iface::make("localhost", 2947); + else + _gps = gpsd_iface::make(device_addr["addr"], 2947); + } catch (std::exception &e) { + UHD_MSG(error) << "An error occured making GPSDd interface: " << e.what() << std::endl; + } + + if (_gps) { + for (size_t i = 0; i < _GPS_TIMEOUT; i++) + { + boost::this_thread::sleep(boost::posix_time::seconds(1)); + if (!_gps->gps_detected()) + std::cout << "." << std::flush; + else { + std::cout << ".... " << std::flush; + break; + } } - _sensor_manager = e300_sensor_manager::make_local(_gps, _global_regs); + UHD_MSG(status) << (_gps->gps_detected() ? "found" : "not found") << std::endl; } - UHD_MSG(status) << (_sensor_manager->get_gps_found() ? "found" : "not found") << std::endl; +#endif // Verify we can talk to the e300 core control registers ... UHD_MSG(status) << "Initializing core control..." << std::endl; @@ -443,6 +467,15 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr) _tree->create<sensor_value_t>(mb_path / "sensors" / name) .publish(boost::bind(&e300_sensor_manager::get_sensor, _sensor_manager, name)); } +#ifdef E300_GPSD + if (_gps) { + BOOST_FOREACH(const std::string &name, _gps->get_sensors()) + { + _tree->create<sensor_value_t>(mb_path / "sensors" / name) + .publish(boost::bind(&gpsd_iface::get_sensor, _gps, name)); + } + } +#endif //////////////////////////////////////////////////////////////////// // setup the mboard eeprom @@ -471,28 +504,23 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr) for(size_t instance = 0; instance < fpga::NUM_RADIOS; instance++) this->_setup_radio(instance); - _codec_ctrl->data_port_loopback(true); - // Radio 0 loopback through AD9361 - this->_codec_loopback_self_test(_radio_perifs[0].ctrl); + _codec_mgr->loopback_self_test(_radio_perifs[0].ctrl, radio::sr_addr(radio::CODEC_IDLE), radio::RB64_CODEC_READBACK); // Radio 1 loopback through AD9361 - this->_codec_loopback_self_test(_radio_perifs[1].ctrl); - - _codec_ctrl->data_port_loopback(false); + _codec_mgr->loopback_self_test(_radio_perifs[1].ctrl, radio::sr_addr(radio::CODEC_IDLE), radio::RB64_CODEC_READBACK); //////////////////////////////////////////////////////////////////// // internal gpios //////////////////////////////////////////////////////////////////// - gpio_core_200::sptr fp_gpio = gpio_core_200::make(_radio_perifs[0].ctrl, TOREG(SR_FP_GPIO), RB32_FP_GPIO); - const std::vector<std::string> gpio_attrs = boost::assign::list_of("CTRL")("DDR")("OUT")("ATR_0X")("ATR_RX")("ATR_TX")("ATR_XX"); - BOOST_FOREACH(const std::string &attr, gpio_attrs) + gpio_core_200::sptr fp_gpio = gpio_core_200::make(_radio_perifs[0].ctrl, radio::sr_addr(radio::FP_GPIO), radio::RB32_FP_GPIO); + BOOST_FOREACH(const gpio_attr_map_t::value_type attr, gpio_attr_map) { - _tree->create<boost::uint32_t>(mb_path / "gpio" / "INT0" / attr) - .subscribe(boost::bind(&e300_impl::_set_internal_gpio, this, fp_gpio, attr, _1)) + _tree->create<boost::uint32_t>(mb_path / "gpio" / "INT0" / attr.second) + .subscribe(boost::bind(&e300_impl::_set_internal_gpio, this, fp_gpio, attr.first, _1)) .set(0); } _tree->create<boost::uint8_t>(mb_path / "gpio" / "INT0" / "READBACK") - .publish(boost::bind(&e300_impl::_get_internal_gpio, this, fp_gpio, "READBACK")); + .publish(boost::bind(&e300_impl::_get_internal_gpio, this, fp_gpio)); //////////////////////////////////////////////////////////////////// @@ -510,7 +538,11 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr) _tree->create<std::string>(mb_path / "time_source" / "value") .subscribe(boost::bind(&e300_impl::_update_time_source, this, _1)) .set(e300::DEFAULT_TIME_SRC); +#ifdef E300_GPSD static const std::vector<std::string> time_sources = boost::assign::list_of("none")("internal")("external")("gpsdo"); +#else + static const std::vector<std::string> time_sources = boost::assign::list_of("none")("internal")("external"); +#endif _tree->create<std::vector<std::string> >(mb_path / "time_source" / "options").set(time_sources); //setup reference source props _tree->create<std::string>(mb_path / "clock_source" / "value") @@ -575,7 +607,7 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr) // init the clock rate to something reasonable _tree->access<double>(mb_path / "tick_rate").set( - device_addr.cast<double>("master_clock_rate", e300::DEFAULT_TICK_RATE)); + device_addr.cast<double>("master_clock_rate", ad936x_manager::DEFAULT_TICK_RATE)); // subdev spec contains full width of selections subdev_spec_t rx_spec, tx_spec; @@ -589,41 +621,37 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr) } _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(rx_spec); _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(tx_spec); - - UHD_MSG(status) << "Initializing time to the internal GPSDO" << std::endl; - const time_t tp = time_t(_sensor_manager->get_sensor("gps_time").to_int()+1); - _tree->access<time_spec_t>(mb_path / "time" / "pps").set(time_spec_t(tp)); - - // wait for time to be actually set - boost::this_thread::sleep(boost::posix_time::seconds(1)); } -boost::uint8_t e300_impl::_get_internal_gpio( - gpio_core_200::sptr gpio, - const std::string &) +boost::uint8_t e300_impl::_get_internal_gpio(gpio_core_200::sptr gpio) { return boost::uint32_t(gpio->read_gpio(dboard_iface::UNIT_RX)); } void e300_impl::_set_internal_gpio( gpio_core_200::sptr gpio, - const std::string &attr, + const gpio_attr_t attr, const boost::uint32_t value) { - if (attr == "CTRL") + switch (attr) + { + case GPIO_CTRL: return gpio->set_pin_ctrl(dboard_iface::UNIT_RX, value); - else if (attr == "DDR") + case GPIO_DDR: return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value); - else if (attr == "OUT") + case GPIO_OUT: return gpio->set_gpio_out(dboard_iface::UNIT_RX, value); - else if (attr == "ATR_0X") + case GPIO_ATR_0X: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value); - else if (attr == "ATR_RX") + case GPIO_ATR_RX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value); - else if (attr == "ATR_TX") + case GPIO_ATR_TX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value); - else if (attr == "ATR_XX") + case GPIO_ATR_XX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, value); + default: + UHD_THROW_INVALID_CODE_PATH(); + } } uhd::sensor_value_t e300_impl::_get_fe_pll_lock(const bool is_tx) @@ -665,6 +693,17 @@ void e300_impl::_enforce_tick_rate_limits( % direction )); } + // Minimum rate restriction due to MMCM used in capture interface to AD9361. + // Xilinx Artix-7 FPGA MMCM minimum input frequency is 10 MHz. + const double min_tick_rate = uhd::usrp::e300::MIN_TICK_RATE / ((chan_count <= 1) ? 1 : 2); + if (tick_rate - min_tick_rate < 0.0) + { + throw uhd::value_error(boost::str( + boost::format("current master clock rate (%.6f MHz) set below minimum possible master clock rate (%.6f MHz)") + % (tick_rate/1e6) + % (min_tick_rate/1e6) + )); + } } } @@ -690,8 +729,8 @@ void e300_impl::_register_loopback_self_test(wb_iface::sptr iface) for (size_t i = 0; i < 100; i++) { boost::hash_combine(hash, i); - iface->poke32(TOREG(SR_TEST), boost::uint32_t(hash)); - test_fail = iface->peek32(RB32_TEST) != boost::uint32_t(hash); + iface->poke32(radio::sr_addr(radio::TEST), boost::uint32_t(hash)); + test_fail = iface->peek32(radio::RB32_TEST) != boost::uint32_t(hash); if (test_fail) break; //exit loop on any failure } UHD_MSG(status) << ((test_fail)? " fail" : "pass") << std::endl; @@ -721,30 +760,6 @@ std::string e300_impl::_get_version_hash(void) % ((git_hash & 0xF000000) ? "-dirty" : "")); } -void e300_impl::_codec_loopback_self_test(wb_iface::sptr iface) -{ - bool test_fail = false; - UHD_ASSERT_THROW(bool(iface)); - UHD_MSG(status) << "Performing CODEC loopback test... " << std::flush; - size_t hash = size_t(time(NULL)); - for (size_t i = 0; i < 100; i++) - { - boost::hash_combine(hash, i); - const boost::uint32_t word32 = boost::uint32_t(hash) & 0xfff0fff0; - iface->poke32(TOREG(SR_CODEC_IDLE), word32); - iface->peek64(RB64_CODEC_READBACK); //enough idleness for loopback to propagate - const boost::uint64_t rb_word64 = iface->peek64(RB64_CODEC_READBACK); - const boost::uint32_t rb_tx = boost::uint32_t(rb_word64 >> 32); - const boost::uint32_t rb_rx = boost::uint32_t(rb_word64 & 0xffffffff); - test_fail = word32 != rb_tx or word32 != rb_rx; - if (test_fail) break; //exit loop on any failure - } - UHD_MSG(status) << ((test_fail)? " fail" : "pass") << std::endl; - - /* Zero out the idle data. */ - iface->poke32(TOREG(SR_CODEC_IDLE), 0); -} - boost::uint32_t e300_impl::_allocate_sid(const sid_config_t &config) { const boost::uint32_t stream = (config.dst_prefix | (config.router_dst_there << 2)) & 0xff; @@ -799,8 +814,10 @@ void e300_impl::_update_time_source(const std::string &source) UHD_MSG(status) << boost::format("Setting time source to %s") % source << std::endl; if (source == "none" or source == "internal") { _misc.pps_sel = global_regs::PPS_INT; +#ifdef E300_GPSD } else if (source == "gpsdo") { _misc.pps_sel = global_regs::PPS_GPS; +#endif } else if (source == "external") { _misc.pps_sel = global_regs::PPS_EXT; } else { @@ -928,6 +945,7 @@ void e300_impl::_setup_radio(const size_t dspno) { radio_perifs_t &perif = _radio_perifs[dspno]; const fs_path mb_path = "/mboards/0"; + std::string slot_name = (dspno == 0) ? "A" : "B"; //////////////////////////////////////////////////////////////////// // crossbar config for ctrl xports @@ -956,137 +974,99 @@ void e300_impl::_setup_radio(const size_t dspno) ctrl_sid, dspno ? "1" : "0"); this->_register_loopback_self_test(perif.ctrl); - perif.atr = gpio_core_200_32wo::make(perif.ctrl, TOREG(SR_GPIO)); + + //////////////////////////////////////////////////////////////////// + // Set up peripherals + //////////////////////////////////////////////////////////////////// + perif.atr = gpio_core_200_32wo::make(perif.ctrl, radio::sr_addr(radio::GPIO)); + perif.rx_fe = rx_frontend_core_200::make(perif.ctrl, radio::sr_addr(radio::RX_FRONT)); + perif.rx_fe->set_dc_offset(rx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE); + perif.rx_fe->set_dc_offset_auto(rx_frontend_core_200::DEFAULT_DC_OFFSET_ENABLE); + perif.rx_fe->set_iq_balance(rx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE); + perif.tx_fe = tx_frontend_core_200::make(perif.ctrl, radio::sr_addr(radio::TX_FRONT)); + perif.tx_fe->set_dc_offset(tx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE); + perif.tx_fe->set_iq_balance(tx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE); + perif.framer = rx_vita_core_3000::make(perif.ctrl, radio::sr_addr(radio::RX_CTRL)); + perif.ddc = rx_dsp_core_3000::make(perif.ctrl, radio::sr_addr(radio::RX_DSP)); + perif.ddc->set_link_rate(10e9/8); //whatever + perif.ddc->set_freq(e300::DEFAULT_DDC_FREQ); + perif.deframer = tx_vita_core_3000::make(perif.ctrl, radio::sr_addr(radio::TX_CTRL)); + perif.duc = tx_dsp_core_3000::make(perif.ctrl, radio::sr_addr(radio::TX_DSP)); + perif.duc->set_link_rate(10e9/8); //whatever + perif.duc->set_freq(e300::DEFAULT_DUC_FREQ); + + //////////////////////////////////////////////////////////////////// + // create time control objects + //////////////////////////////////////////////////////////////////// + time_core_3000::readback_bases_type time64_rb_bases; + time64_rb_bases.rb_now = radio::RB64_TIME_NOW; + time64_rb_bases.rb_pps = radio::RB64_TIME_PPS; + perif.time64 = time_core_3000::make(perif.ctrl, radio::sr_addr(radio::TIME), time64_rb_bases); //////////////////////////////////////////////////////////////////// // front end corrections //////////////////////////////////////////////////////////////////// - std::string slot_name = (dspno == 0) ? "A" : "B"; - perif.rx_fe = rx_frontend_core_200::make(perif.ctrl, TOREG(SR_RX_FRONT)); - const fs_path rx_fe_path = mb_path / "rx_frontends" / slot_name; - _tree->create<std::complex<double> >(rx_fe_path / "dc_offset" / "value") - .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, perif.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, perif.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, perif.rx_fe, _1)) - .set(std::complex<double>(0.0, 0.0)); - - perif.tx_fe = tx_frontend_core_200::make(perif.ctrl, TOREG(SR_TX_FRONT)); - const fs_path tx_fe_path = mb_path / "tx_frontends" / slot_name; - _tree->create<std::complex<double> >(tx_fe_path / "dc_offset" / "value") - .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, perif.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, perif.tx_fe, _1)) - .set(std::complex<double>(0.0, 0.0)); + perif.rx_fe->populate_subtree(_tree->subtree(mb_path / "rx_frontends" / slot_name)); + perif.tx_fe->populate_subtree(_tree->subtree(mb_path / "tx_frontends" / slot_name)); //////////////////////////////////////////////////////////////////// - // create rx dsp control objects + // connect rx dsp control objects //////////////////////////////////////////////////////////////////// - perif.framer = rx_vita_core_3000::make(perif.ctrl, TOREG(SR_RX_CTRL)); - perif.ddc = rx_dsp_core_3000::make(perif.ctrl, TOREG(SR_RX_DSP)); - perif.ddc->set_link_rate(10e9/8); //whatever _tree->access<double>(mb_path / "tick_rate") .subscribe(boost::bind(&rx_vita_core_3000::set_tick_rate, perif.framer, _1)) .subscribe(boost::bind(&rx_dsp_core_3000::set_tick_rate, perif.ddc, _1)); const fs_path rx_dsp_path = mb_path / "rx_dsps" / str(boost::format("%u") % dspno); - _tree->create<meta_range_t>(rx_dsp_path / "rate" / "range") - .publish(boost::bind(&rx_dsp_core_3000::get_host_rates, perif.ddc)); - _tree->create<double>(rx_dsp_path / "rate" / "value") - .coerce(boost::bind(&rx_dsp_core_3000::set_host_rate, perif.ddc, _1)) + perif.ddc->populate_subtree(_tree->subtree(rx_dsp_path)); + _tree->access<double>(rx_dsp_path / "rate" / "value") .subscribe(boost::bind(&e300_impl::_update_rx_samp_rate, this, dspno, _1)) - .set(e300::DEFAULT_RX_SAMP_RATE); - _tree->create<double>(rx_dsp_path / "freq" / "value") - .coerce(boost::bind(&rx_dsp_core_3000::set_freq, perif.ddc, _1)) - .set(e300::DEFAULT_DDC_FREQ); - _tree->create<meta_range_t>(rx_dsp_path / "freq" / "range") - .publish(boost::bind(&rx_dsp_core_3000::get_freq_range, perif.ddc)); + ; _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd") .subscribe(boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1)); //////////////////////////////////////////////////////////////////// // create tx dsp control objects //////////////////////////////////////////////////////////////////// - perif.deframer = tx_vita_core_3000::make(perif.ctrl, TOREG(SR_TX_CTRL)); - perif.duc = tx_dsp_core_3000::make(perif.ctrl, TOREG(SR_TX_DSP)); - perif.duc->set_link_rate(10e9/8); //whatever _tree->access<double>(mb_path / "tick_rate") .subscribe(boost::bind(&tx_vita_core_3000::set_tick_rate, perif.deframer, _1)) .subscribe(boost::bind(&tx_dsp_core_3000::set_tick_rate, perif.duc, _1)); const fs_path tx_dsp_path = mb_path / "tx_dsps" / str(boost::format("%u") % dspno); - _tree->create<meta_range_t>(tx_dsp_path / "rate" / "range") - .publish(boost::bind(&tx_dsp_core_3000::get_host_rates, perif.duc)); - _tree->create<double>(tx_dsp_path / "rate" / "value") - .coerce(boost::bind(&tx_dsp_core_3000::set_host_rate, perif.duc, _1)) + perif.duc->populate_subtree(_tree->subtree(tx_dsp_path)); + _tree->access<double>(tx_dsp_path / "rate" / "value") .subscribe(boost::bind(&e300_impl::_update_tx_samp_rate, this, dspno, _1)) - .set(e300::DEFAULT_TX_SAMP_RATE); - _tree->create<double>(tx_dsp_path / "freq" / "value") - .coerce(boost::bind(&tx_dsp_core_3000::set_freq, perif.duc, _1)) - .set(e300::DEFAULT_DUC_FREQ); - _tree->create<meta_range_t>(tx_dsp_path / "freq" / "range") - .publish(boost::bind(&tx_dsp_core_3000::get_freq_range, perif.duc)); - - //////////////////////////////////////////////////////////////////// - // create time control objects - //////////////////////////////////////////////////////////////////// - time_core_3000::readback_bases_type time64_rb_bases; - time64_rb_bases.rb_now = RB64_TIME_NOW; - time64_rb_bases.rb_pps = RB64_TIME_PPS; - perif.time64 = time_core_3000::make(perif.ctrl, TOREG(SR_TIME), time64_rb_bases); + ; //////////////////////////////////////////////////////////////////// // create RF frontend interfacing //////////////////////////////////////////////////////////////////// - static const std::vector<std::string> data_directions = boost::assign::list_of("rx")("tx"); - BOOST_FOREACH(const std::string& direction, data_directions) - { - const std::string key = boost::to_upper_copy(direction) + std::string(((dspno == FE0)? "1" : "2")); + static const std::vector<direction_t> dirs = boost::assign::list_of(RX_DIRECTION)(TX_DIRECTION); + BOOST_FOREACH(direction_t dir, dirs) { + const std::string x = (dir == RX_DIRECTION) ? "rx" : "tx"; + const std::string key = boost::to_upper_copy(x) + std::string(((dspno == FE0)? "1" : "2")); const fs_path rf_fe_path - = mb_path / "dboards" / "A" / (direction + "_frontends") / ((dspno == 0) ? "A" : "B"); + = mb_path / "dboards" / "A" / (x + "_frontends") / ((dspno == 0) ? "A" : "B"); - _tree->create<std::string>(rf_fe_path / "name").set("FE-"+key); - _tree->create<int>(rf_fe_path / "sensors"); //empty TODO - _tree->create<sensor_value_t>(rf_fe_path / "sensors" / "lo_locked") - .publish(boost::bind(&e300_impl::_get_fe_pll_lock, this, direction == "tx")); - BOOST_FOREACH(const std::string &name, ad9361_ctrl::get_gain_names(key)) - { - _tree->create<meta_range_t>(rf_fe_path / "gains" / name / "range") - .set(ad9361_ctrl::get_gain_range(key)); + // This will connect all the AD936x-specific items + _codec_mgr->populate_frontend_subtree( + _tree->subtree(rf_fe_path), key, dir + ); - _tree->create<double>(rf_fe_path / "gains" / name / "value") - .coerce(boost::bind(&ad9361_ctrl::set_gain, _codec_ctrl, key, _1)) - .set(e300::DEFAULT_FE_GAIN); - } - _tree->create<std::string>(rf_fe_path / "connection").set("IQ"); - _tree->create<bool>(rf_fe_path / "enabled").set(true); - _tree->create<bool>(rf_fe_path / "use_lo_offset").set(false); - _tree->create<double>(rf_fe_path / "bandwidth" / "value") - .coerce(boost::bind(&ad9361_ctrl::set_bw_filter, _codec_ctrl, key, _1)) - .set(e300::DEFAULT_FE_BW); - _tree->create<meta_range_t>(rf_fe_path / "bandwidth" / "range") - .publish(boost::bind(&ad9361_ctrl::get_bw_filter_range, key)); - _tree->create<double>(rf_fe_path / "freq" / "value") - .publish(boost::bind(&ad9361_ctrl::get_freq, _codec_ctrl, key)) - .coerce(boost::bind(&ad9361_ctrl::tune, _codec_ctrl, key, _1)) + // This will connect all the e300_impl-specific items + _tree->create<sensor_value_t>(rf_fe_path / "sensors" / "lo_locked") + .publish(boost::bind(&e300_impl::_get_fe_pll_lock, this, dir == TX_DIRECTION)) + ; + _tree->access<double>(rf_fe_path / "freq" / "value") .subscribe(boost::bind(&e300_impl::_update_fe_lo_freq, this, key, _1)) - .set(e300::DEFAULT_FE_FREQ); - _tree->create<meta_range_t>(rf_fe_path / "freq" / "range") - .publish(boost::bind(&ad9361_ctrl::get_rf_freq_range)); + ; - //setup RX related stuff - if (key[0] == 'R') { + // Antenna Setup + if (dir == RX_DIRECTION) { static const std::vector<std::string> ants = boost::assign::list_of("TX/RX")("RX2"); _tree->create<std::vector<std::string> >(rf_fe_path / "antenna" / "options").set(ants); _tree->create<std::string>(rf_fe_path / "antenna" / "value") .subscribe(boost::bind(&e300_impl::_update_antenna_sel, this, dspno, _1)) .set("RX2"); - _tree->create<sensor_value_t>(rf_fe_path / "sensors" / "rssi") - .publish(boost::bind(&ad9361_ctrl::get_rssi, _codec_ctrl, key)); } - if (key[0] == 'T') { + else if (dir == TX_DIRECTION) { static const std::vector<std::string> ants(1, "TX/RX"); _tree->create<std::vector<std::string> >(rf_fe_path / "antenna" / "options").set(ants); _tree->create<std::string>(rf_fe_path / "antenna" / "value").set("TX/RX"); diff --git a/host/lib/usrp/e300/e300_impl.hpp b/host/lib/usrp/e300/e300_impl.hpp index 7f83c16ed..8aff51466 100644 --- a/host/lib/usrp/e300/e300_impl.hpp +++ b/host/lib/usrp/e300/e300_impl.hpp @@ -1,5 +1,5 @@ // -// Copyright 2013-2014 Ettus Research LLC +// Copyright 2013-2015 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 @@ -20,6 +20,7 @@ #include <uhd/device.hpp> #include <uhd/property_tree.hpp> +#include <uhd/types/device_addr.hpp> #include <uhd/usrp/subdev_spec.hpp> #include <uhd/usrp/mboard_eeprom.hpp> #include <uhd/usrp/dboard_eeprom.hpp> @@ -28,6 +29,7 @@ #include <uhd/types/sensors.hpp> #include <boost/weak_ptr.hpp> #include <boost/thread/mutex.hpp> +#include <string> #include "e300_fifo_config.hpp" #include "radio_ctrl_core_3000.hpp" #include "rx_frontend_core_200.hpp" @@ -38,13 +40,18 @@ #include "rx_dsp_core_3000.hpp" #include "tx_dsp_core_3000.hpp" #include "ad9361_ctrl.hpp" +#include "ad936x_manager.hpp" #include "gpio_core_200.hpp" #include "e300_global_regs.hpp" #include "e300_i2c.hpp" #include "e300_eeprom_manager.hpp" #include "e300_sensor_manager.hpp" -#include "e300_ublox_control.hpp" + +/* if we don't compile with gpsd support, don't bother */ +#ifdef E300_GPSD +#include "gpsd_iface.hpp" +#endif namespace uhd { namespace usrp { namespace e300 { @@ -98,6 +105,10 @@ static const size_t E300_R1_CTRL_STREAM = (1 << 2) | E300_RADIO_DEST_PREFIX_C static const size_t E300_R1_TX_DATA_STREAM = (1 << 2) | E300_RADIO_DEST_PREFIX_TX; static const size_t E300_R1_RX_DATA_STREAM = (1 << 2) | E300_RADIO_DEST_PREFIX_RX; +uhd::device_addrs_t e300_find(const uhd::device_addr_t &multi_dev_hint); +void get_e3x0_fpga_images(const uhd::device_addr_t &device_args, + std::string &fpga_image, + std::string &idle_image); /*! * USRP-E300 implementation guts: @@ -267,13 +278,11 @@ private: // methods uhd::sensor_value_t _get_fe_pll_lock(const bool is_tx); // internal gpios - boost::uint8_t _get_internal_gpio( - gpio_core_200::sptr, - const std::string &); + boost::uint8_t _get_internal_gpio(gpio_core_200::sptr); void _set_internal_gpio( gpio_core_200::sptr gpio, - const std::string &attr, + const gpio_attr_t attr, const boost::uint32_t value); private: // members @@ -284,6 +293,7 @@ private: // members radio_perifs_t _radio_perifs[2]; double _tick_rate; ad9361_ctrl::sptr _codec_ctrl; + ad936x_manager::sptr _codec_mgr; fe_control_settings_t _settings; global_regs::sptr _global_regs; e300_sensor_manager::sptr _sensor_manager; @@ -293,7 +303,10 @@ private: // members std::string _idle_image; bool _do_not_reload; gpio_t _misc; - gps::ublox::ubx::control::sptr _gps; +#ifdef E300_GPSD + gpsd_iface::sptr _gps; + static const size_t _GPS_TIMEOUT = 5; +#endif }; }}} // namespace diff --git a/host/lib/usrp/e300/e300_io_impl.cpp b/host/lib/usrp/e300/e300_io_impl.cpp index dadfb71e9..29d250c8f 100644 --- a/host/lib/usrp/e300/e300_io_impl.cpp +++ b/host/lib/usrp/e300/e300_io_impl.cpp @@ -91,21 +91,13 @@ void e300_impl::_update_tick_rate(const double rate) } } -#define CHECK_BANDWIDTH(dir) \ - if (rate > _codec_ctrl->get_bw_filter_range(dir).stop()) { \ - UHD_MSG(warning) \ - << "Selected " << dir << " bandwidth (" << (rate/1e6) << " MHz) exceeds\n" \ - << "analog frontend filter bandwidth (" << (_codec_ctrl->get_bw_filter_range(dir).stop()/1e6) << " MHz)." \ - << std::endl; \ - } - void e300_impl::_update_rx_samp_rate(const size_t dspno, const double rate) { boost::shared_ptr<sph::recv_packet_streamer> my_streamer = boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_radio_perifs[dspno].rx_streamer.lock()); if (my_streamer) my_streamer->set_samp_rate(rate); - CHECK_BANDWIDTH("Rx"); + _codec_mgr->check_bandwidth(rate, "Rx"); } void e300_impl::_update_tx_samp_rate(const size_t dspno, const double rate) @@ -114,7 +106,7 @@ void e300_impl::_update_tx_samp_rate(const size_t dspno, const double rate) boost::dynamic_pointer_cast<sph::send_packet_streamer>(_radio_perifs[dspno].tx_streamer.lock()); if (my_streamer) my_streamer->set_samp_rate(rate); - CHECK_BANDWIDTH("Tx"); + _codec_mgr->check_bandwidth(rate, "Tx"); } /*********************************************************************** diff --git a/host/lib/usrp/e300/e300_network.cpp b/host/lib/usrp/e300/e300_network.cpp index 408f9e62d..96387d6f7 100644 --- a/host/lib/usrp/e300/e300_network.cpp +++ b/host/lib/usrp/e300/e300_network.cpp @@ -230,6 +230,27 @@ static void e300_codec_ctrl_tunnel( case codec_xact_t::ACTION_GET_RSSI: out->rssi = _codec_ctrl->get_rssi(which_str).to_real(); break; + case codec_xact_t::ACTION_GET_TEMPERATURE: + out->temp = _codec_ctrl->get_temperature().to_real(); + break; + case codec_xact_t::ACTION_SET_DC_OFFSET_AUTO: + _codec_ctrl->set_dc_offset_auto(which_str, in->use_dc_correction == 1); + break; + case codec_xact_t::ACTION_SET_IQ_BALANCE_AUTO: + _codec_ctrl->set_iq_balance_auto(which_str, in->use_iq_correction == 1); + case codec_xact_t::ACTION_SET_AGC: + _codec_ctrl->set_agc(which_str, in->use_agc == 1); + break; + case codec_xact_t::ACTION_SET_AGC_MODE: + if(in->agc_mode == 0) { + _codec_ctrl->set_agc_mode(which_str, "slow"); + } else if (in->agc_mode == 1) { + _codec_ctrl->set_agc_mode(which_str, "fast"); + } + break; + case codec_xact_t::ACTION_SET_BW: + out->bw = _codec_ctrl->set_bw_filter(which_str, in->bw); + break; default: UHD_MSG(status) << "Got unknown request?!" << std::endl; //Zero out actions to fail this request on client @@ -328,19 +349,9 @@ static void e300_sensor_tunnel( // TODO: This is ugly ... use proper serialization in->value = uhd::htonx<boost::uint32_t>( e300_sensor_manager::pack_float_in_uint32_t(temp.to_real())); - } else if (uhd::ntohx(in->which) == GPS_FOUND) { - in->value = uhd::htonx<boost::uint32_t>( - sensor_manager->get_gps_found() ? 1 : 0); - - } else if (uhd::ntohx(in->which) == GPS_LOCK) { - in->value = uhd::htonx<boost::uint32_t>( - sensor_manager->get_gps_lock().to_bool() ? 1 : 0); } else if (uhd::ntohx(in->which) == REF_LOCK) { in->value = uhd::htonx<boost::uint32_t>( sensor_manager->get_ref_lock().to_bool() ? 1 : 0); - } else if (uhd::ntohx(in->which) == GPS_TIME) { - in->value = uhd::htonx<boost::uint32_t>( - sensor_manager->get_gps_time().to_int()); } else UHD_MSG(status) << "Got unknown request?!" << std::endl; @@ -627,8 +638,7 @@ network_server_impl::network_server_impl(const uhd::device_addr_t &device_addr) _codec_ctrl = ad9361_ctrl::make_spi(client_settings, spi::make(E300_SPIDEV_DEVICE), 1); // This is horrible ... why do I have to sleep here? boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - _sensor_manager = e300_sensor_manager::make_local( - gps::ublox::ubx::control::make("/dev/ttyPS1", 9600), _global_regs); + _sensor_manager = e300_sensor_manager::make_local(_global_regs); } }}} // namespace diff --git a/host/lib/usrp/e300/e300_regs.hpp b/host/lib/usrp/e300/e300_regs.hpp index 5736ebfd4..846c759a4 100644 --- a/host/lib/usrp/e300/e300_regs.hpp +++ b/host/lib/usrp/e300/e300_regs.hpp @@ -18,36 +18,48 @@ #ifndef INCLUDED_E300_REGS_HPP #define INCLUDED_E300_REGS_HPP -#include <boost/cstdint.hpp> +#include <stdint.h> +#include <uhd/config.hpp> -#define TOREG(x) ((x)*4) +namespace uhd { namespace usrp { namespace e300 { namespace radio { -#define localparam static const int +static UHD_INLINE uint32_t sr_addr(const uint32_t offset) +{ + return offset * 4; +} + +static const uint32_t DACSYNC = 5; +static const uint32_t LOOPBACK = 6; +static const uint32_t TEST = 7; +static const uint32_t SPI = 8; +static const uint32_t GPIO = 16; +static const uint32_t MISC_OUTS = 24; +static const uint32_t READBACK = 32; +static const uint32_t TX_CTRL = 64; +static const uint32_t RX_CTRL = 96; +static const uint32_t TIME = 128; +static const uint32_t RX_DSP = 144; +static const uint32_t TX_DSP = 184; +static const uint32_t LEDS = 195; +static const uint32_t FP_GPIO = 200; +static const uint32_t RX_FRONT = 208; +static const uint32_t TX_FRONT = 216; +static const uint32_t CODEC_IDLE = 250; -localparam SR_TEST = 7; -localparam SR_SPI = 8; -localparam SR_GPIO = 16; -localparam SR_MISC_OUTS = 24; -localparam SR_READBACK = 32; -localparam SR_TX_CTRL = 64; -localparam SR_RX_CTRL = 96; -localparam SR_TIME = 128; -localparam SR_RX_DSP = 144; -localparam SR_TX_DSP = 184; -localparam SR_LEDS = 195; -localparam SR_FP_GPIO = 200; -localparam SR_RX_FRONT = 208; -localparam SR_TX_FRONT = 216; -localparam SR_CODEC_IDLE = 250; +static const uint32_t RB32_GPIO = 0; +static const uint32_t RB32_SPI = 4; +static const uint32_t RB64_TIME_NOW = 8; +static const uint32_t RB64_TIME_PPS = 16; +static const uint32_t RB32_TEST = 24; +static const uint32_t RB32_RX = 28; +static const uint32_t RB32_FP_GPIO = 32; +static const uint32_t RB32_MISC_INS = 36; +static const uint32_t RB64_CODEC_READBACK = 40; +static const uint32_t RB32_RADIO_NUM = 48; +}}}} // namespace -localparam RB32_SPI = 4; -localparam RB64_TIME_NOW = 8; -localparam RB64_TIME_PPS = 16; -localparam RB32_TEST = 24; -localparam RB32_FP_GPIO = 32; -localparam RB64_CODEC_READBACK = 40; -localparam RB32_RADIO_NUM = 48; +#define localparam static const int localparam ST_RX_ENABLE = 20; localparam ST_TX_ENABLE = 19; diff --git a/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp b/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp index 6742f5f86..be98f4027 100644 --- a/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp +++ b/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp @@ -130,10 +130,123 @@ public: _args.bits = uhd::htonx<boost::uint32_t>(0); _transact(); - return sensor_value_t("RSSI", _retval.rssi, "dB"); } + sensor_value_t get_temperature() + { + _clear(); + _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_GET_TEMPERATURE); + _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_NONE); /*Unused*/ + _args.bits = uhd::htonx<boost::uint32_t>(0); + + _transact(); + return sensor_value_t("temp", _retval.temp, "C"); + } + + void set_dc_offset_auto(const std::string &which, const bool on) + { + _clear(); + _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_SET_DC_OFFSET_AUTO); + if (which == "TX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX1); + else if (which == "TX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX2); + else if (which == "RX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX1); + else if (which == "RX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX2); + else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); + _args.use_dc_correction = on ? 1 : 0; + + _transact(); + } + + void set_iq_balance_auto(const std::string &which, const bool on) + { + _clear(); + _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_SET_IQ_BALANCE_AUTO); + if (which == "TX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX1); + else if (which == "TX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX2); + else if (which == "RX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX1); + else if (which == "RX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX2); + else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); + _args.use_iq_correction = on ? 1 : 0; + + _transact(); + } + + void set_agc(const std::string &which, bool enable) + { + _clear(); + _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_SET_AGC); + if (which == "TX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX1); + else if (which == "TX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX2); + else if (which == "RX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX1); + else if (which == "RX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX2); + else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); + _args.use_agc = enable ? 1 : 0; + + _transact(); + } + + void set_agc_mode(const std::string &which, const std::string &mode) + { + _clear(); + _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_SET_AGC_MODE); + + if (which == "TX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX1); + else if (which == "TX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX2); + else if (which == "RX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX1); + else if (which == "RX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX2); + else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); + + if(mode == "slow") { + _args.agc_mode = 0; + } else if (mode == "fast") { + _args.agc_mode = 1; + } else { + throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect agc mode."); + } + + _transact(); + } + + //! set the filter bandwidth for the frontend's analog low pass + double set_bw_filter(const std::string &which, const double bw) + { + _clear(); + _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_SET_BW); + if (which == "TX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX1); + else if (which == "TX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX2); + else if (which == "RX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX1); + else if (which == "RX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX2); + else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); + _args.bw = bw; + + _transact(); + return _retval.bw; + } + + //! List all available filters by name + std::vector<std::string> get_filter_names(const std::string &) + { + return std::vector<std::string>(); + } + + //! Return a list of all filters + filter_info_base::sptr get_filter(const std::string &, const std::string &) + { + UHD_THROW_INVALID_CODE_PATH(); + } + + //! Write back a filter + void set_filter(const std::string &, const std::string &, const filter_info_base::sptr) + { + UHD_MSG(warning) << "Attempting to set filter on E300 in network mode." << std::endl; + } + + void output_digital_test_tone(bool enb) + { + UHD_THROW_INVALID_CODE_PATH(); + } + private: void _transact() { { diff --git a/host/lib/usrp/e300/e300_remote_codec_ctrl.hpp b/host/lib/usrp/e300/e300_remote_codec_ctrl.hpp index e21f2ef95..43723e0d5 100644 --- a/host/lib/usrp/e300/e300_remote_codec_ctrl.hpp +++ b/host/lib/usrp/e300/e300_remote_codec_ctrl.hpp @@ -34,6 +34,12 @@ public: double gain; double freq; double rssi; + double temp; + double bw; + boost::uint32_t use_dc_correction; + boost::uint32_t use_iq_correction; + boost::uint32_t use_agc; + boost::uint32_t agc_mode; boost::uint64_t bits; }; @@ -44,7 +50,13 @@ public: static const boost::uint32_t ACTION_TUNE = 13; static const boost::uint32_t ACTION_SET_LOOPBACK = 14; static const boost::uint32_t ACTION_GET_RSSI = 15; - static const boost::uint32_t ACTION_GET_FREQ = 16; + static const boost::uint32_t ACTION_GET_TEMPERATURE = 16; + static const boost::uint32_t ACTION_SET_DC_OFFSET_AUTO = 17; + static const boost::uint32_t ACTION_SET_IQ_BALANCE_AUTO = 18; + static const boost::uint32_t ACTION_SET_AGC = 19; + static const boost::uint32_t ACTION_SET_AGC_MODE = 20; + static const boost::uint32_t ACTION_SET_BW = 21; + static const boost::uint32_t ACTION_GET_FREQ = 22; //Values for "which" static const boost::uint32_t CHAIN_NONE = 0; diff --git a/host/lib/usrp/e300/e300_sensor_manager.cpp b/host/lib/usrp/e300/e300_sensor_manager.cpp index 527cfb91a..a4319fa4b 100644 --- a/host/lib/usrp/e300/e300_sensor_manager.cpp +++ b/host/lib/usrp/e300/e300_sensor_manager.cpp @@ -24,7 +24,6 @@ #include <cstring> #include <uhd/exception.hpp> #include <uhd/utils/byteswap.hpp> -#include <uhd/usrp/gps_ctrl.hpp> namespace uhd { namespace usrp { namespace e300 { @@ -38,17 +37,13 @@ public: std::vector<std::string> get_sensors() { - return boost::assign::list_of("temp")("gps_locked")("gps_time")("ref_locked"); + return boost::assign::list_of("temp")("ref_locked"); } uhd::sensor_value_t get_sensor(const std::string &key) { if (key == "temp") return get_mb_temp(); - else if (key == "gps_locked") - return get_gps_lock(); - else if (key == "gps_time") - return get_gps_time(); else if (key == "ref_locked") return get_ref_lock(); else @@ -94,108 +89,6 @@ public: "C"); } - uhd::sensor_value_t get_gps_time(void) - { - boost::mutex::scoped_lock(_mutex); - sensor_transaction_t transaction; - transaction.which = uhd::htonx<boost::uint32_t>(GPS_TIME); - { - uhd::transport::managed_send_buffer::sptr buff - = _xport->get_send_buff(1.0); - if (not buff or buff->size() < sizeof(transaction)) { - throw uhd::runtime_error("sensor proxy send timeout"); - } - std::memcpy( - buff->cast<void *>(), - &transaction, - sizeof(transaction)); - buff->commit(sizeof(transaction)); - } - { - uhd::transport::managed_recv_buffer::sptr buff - = _xport->get_recv_buff(1.0); - - if (not buff or buff->size() < sizeof(transaction)) - throw uhd::runtime_error("sensor proxy recv timeout"); - - std::memcpy( - &transaction, - buff->cast<const void *>(), - sizeof(transaction)); - } - UHD_ASSERT_THROW(uhd::ntohx<boost::uint32_t>(transaction.which) == GPS_TIME); - // TODO: Use proper serialization here ... - return sensor_value_t("GPS epoch time", int(uhd::ntohx<boost::uint32_t>(transaction.value)), "seconds"); - } - - bool get_gps_found(void) - { - boost::mutex::scoped_lock(_mutex); - sensor_transaction_t transaction; - transaction.which = uhd::htonx<boost::uint32_t>(GPS_FOUND); - { - uhd::transport::managed_send_buffer::sptr buff - = _xport->get_send_buff(1.0); - if (not buff or buff->size() < sizeof(transaction)) { - throw uhd::runtime_error("sensor proxy send timeout"); - } - std::memcpy( - buff->cast<void *>(), - &transaction, - sizeof(transaction)); - buff->commit(sizeof(transaction)); - } - { - uhd::transport::managed_recv_buffer::sptr buff - = _xport->get_recv_buff(1.0); - - if (not buff or buff->size() < sizeof(transaction)) - throw uhd::runtime_error("sensor proxy recv timeout"); - - std::memcpy( - &transaction, - buff->cast<const void *>(), - sizeof(transaction)); - } - UHD_ASSERT_THROW(uhd::ntohx<boost::uint32_t>(transaction.which) == GPS_FOUND); - // TODO: Use proper serialization here ... - return (uhd::ntohx(transaction.value) > 0); - } - - uhd::sensor_value_t get_gps_lock(void) - { - boost::mutex::scoped_lock(_mutex); - sensor_transaction_t transaction; - transaction.which = uhd::htonx<boost::uint32_t>(GPS_LOCK); - { - uhd::transport::managed_send_buffer::sptr buff - = _xport->get_send_buff(1.0); - if (not buff or buff->size() < sizeof(transaction)) { - throw uhd::runtime_error("sensor proxy send timeout"); - } - std::memcpy( - buff->cast<void *>(), - &transaction, - sizeof(transaction)); - buff->commit(sizeof(transaction)); - } - { - uhd::transport::managed_recv_buffer::sptr buff - = _xport->get_recv_buff(1.0); - - if (not buff or buff->size() < sizeof(transaction)) - throw uhd::runtime_error("sensor proxy recv timeout"); - - std::memcpy( - &transaction, - buff->cast<const void *>(), - sizeof(transaction)); - } - UHD_ASSERT_THROW(uhd::ntohx<boost::uint32_t>(transaction.which) == GPS_LOCK); - // TODO: Use proper serialization here ... - return sensor_value_t("GPS lock status", (uhd::ntohx(transaction.value) > 0), "locked", "unlocked"); - } - uhd::sensor_value_t get_ref_lock(void) { boost::mutex::scoped_lock(_mutex); @@ -255,24 +148,20 @@ static const std::string E300_TEMP_SYSFS = "iio:device0"; class e300_sensor_local : public e300_sensor_manager { public: - e300_sensor_local(uhd::gps_ctrl::sptr gps_ctrl, global_regs::sptr global_regs) : - _gps_ctrl(gps_ctrl), _global_regs(global_regs) + e300_sensor_local(global_regs::sptr global_regs) : + _global_regs(global_regs) { } std::vector<std::string> get_sensors() { - return boost::assign::list_of("temp")("gps_locked")("gps_time")("ref_locked"); + return boost::assign::list_of("temp")("ref_locked"); } uhd::sensor_value_t get_sensor(const std::string &key) { if (key == "temp") return get_mb_temp(); - else if (key == "gps_locked") - return get_gps_lock(); - else if (key == "gps_time") - return get_gps_time(); else if (key == "ref_locked") return get_ref_lock(); else @@ -291,21 +180,6 @@ public: return sensor_value_t("temp", (raw + offset) * scale / 1000, "C"); } - bool get_gps_found(void) - { - return _gps_ctrl->gps_detected(); - } - - uhd::sensor_value_t get_gps_lock(void) - { - return _gps_ctrl->get_sensor("gps_locked"); - } - - uhd::sensor_value_t get_gps_time(void) - { - return _gps_ctrl->get_sensor("gps_time"); - } - uhd::sensor_value_t get_ref_lock(void) { //PPSLOOP_LOCKED_MASK is asserted in the following cases: @@ -322,22 +196,21 @@ public: } private: - gps_ctrl::sptr _gps_ctrl; global_regs::sptr _global_regs; }; }}} using namespace uhd::usrp::e300; e300_sensor_manager::sptr e300_sensor_manager::make_local( - uhd::gps_ctrl::sptr gps_ctrl, global_regs::sptr global_regs) + global_regs::sptr global_regs) { - return sptr(new e300_sensor_local(gps_ctrl, global_regs)); + return sptr(new e300_sensor_local(global_regs)); } #else using namespace uhd::usrp::e300; e300_sensor_manager::sptr e300_sensor_manager::make_local( - uhd::gps_ctrl::sptr, global_regs::sptr) + global_regs::sptr) { throw uhd::assertion_error("e300_sensor_manager::make_local() !E300_NATIVE"); } diff --git a/host/lib/usrp/e300/e300_sensor_manager.hpp b/host/lib/usrp/e300/e300_sensor_manager.hpp index 09f889251..bfaf8e90c 100644 --- a/host/lib/usrp/e300/e300_sensor_manager.hpp +++ b/host/lib/usrp/e300/e300_sensor_manager.hpp @@ -39,25 +39,22 @@ struct sensor_transaction_t { -enum sensor {ZYNQ_TEMP=0, GPS_FOUND=1, GPS_TIME=2, - GPS_LOCK=3, REF_LOCK=4}; +enum sensor {ZYNQ_TEMP=0, REF_LOCK=4}; class e300_sensor_manager : boost::noncopyable { public: typedef boost::shared_ptr<e300_sensor_manager> sptr; - virtual bool get_gps_found(void) = 0; virtual uhd::sensor_value_t get_sensor(const std::string &key) = 0; virtual std::vector<std::string> get_sensors(void) = 0; virtual uhd::sensor_value_t get_mb_temp(void) = 0; - virtual uhd::sensor_value_t get_gps_lock(void) = 0; - virtual uhd::sensor_value_t get_gps_time(void) = 0; virtual uhd::sensor_value_t get_ref_lock(void) = 0; + static sptr make_proxy(uhd::transport::zero_copy_if::sptr xport); - static sptr make_local(uhd::gps_ctrl::sptr gps_ctrl, global_regs::sptr global_regs); + static sptr make_local(global_regs::sptr global_regs); // Note: This is a hack static boost::uint32_t pack_float_in_uint32_t(const float &v) diff --git a/host/lib/usrp/e300/e300_ublox_control.hpp b/host/lib/usrp/e300/e300_ublox_control.hpp deleted file mode 100644 index 8705d6c52..000000000 --- a/host/lib/usrp/e300/e300_ublox_control.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef INCLUDED_UHD_USRP_UBLOX_CONTROL_HPP -#define INCLUDED_UHD_USRP_UBLOX_CONTROL_HPP - -#include <boost/cstdint.hpp> -#include <boost/shared_ptr.hpp> -#include <boost/asio.hpp> -#include <uhd/config.hpp> -#include <uhd/usrp/gps_ctrl.hpp> -#include <uhd/types/sensors.hpp> - -#include "e300_async_serial.hpp" - -namespace uhd { namespace usrp { namespace gps { - -namespace ublox { namespace ubx { - -class control : public virtual uhd::gps_ctrl -{ -public: - typedef boost::shared_ptr<control> sptr; - - static sptr make(const std::string &node, const size_t baud_rate); - - virtual void configure_message_rate( - const boost::uint16_t msg, - const boost::uint8_t rate) = 0; - - virtual void configure_antenna( - const boost::uint16_t flags, - const boost::uint16_t pins) = 0; - - virtual void configure_pps( - const boost::uint32_t interval, - const boost::uint32_t length, - const boost::int8_t status, - const boost::uint8_t time_ref, - const boost::uint8_t flags, - const boost::int16_t antenna_delay, - const boost::int16_t rf_group_delay, - const boost::int32_t user_delay) = 0; - - virtual void configure_rates( - boost::uint16_t meas_rate, - boost::uint16_t nav_rate, - boost::uint16_t time_ref) = 0; -}; -}} // namespace ublox::ubx - -}}} // namespace -#endif // INCLUDED_UHD_USRP_UBLOX_CONTROL_HPP diff --git a/host/lib/usrp/e300/e300_ublox_control_impl.cpp b/host/lib/usrp/e300/e300_ublox_control_impl.cpp deleted file mode 100644 index 389bf79fa..000000000 --- a/host/lib/usrp/e300/e300_ublox_control_impl.cpp +++ /dev/null @@ -1,505 +0,0 @@ -#include <boost/format.hpp> -#include <boost/foreach.hpp> -#include <boost/bind.hpp> -#include <boost/make_shared.hpp> -#include <boost/lexical_cast.hpp> -#include <boost/assign/list_of.hpp> -#include "boost/date_time/posix_time/posix_time.hpp" - -#include <iostream> - - -#include <uhd/utils/byteswap.hpp> -#include <uhd/utils/msg.hpp> -#include <uhd/exception.hpp> - -#include "e300_ublox_control.hpp" - -#ifdef E300_NATIVE -#include "e300_ublox_control_impl.hpp" - - -namespace uhd { namespace usrp { namespace gps { - -namespace ublox { namespace ubx { - -control_impl::control_impl(const std::string &node, const size_t baud_rate) -{ - _decode_init(); - _serial = boost::make_shared<async_serial>(node, baud_rate); - _serial->set_read_callback(boost::bind(&control_impl::_rx_callback, this, _1, _2)); - - _detect(); - - configure_message_rate(MSG_GLL, 0); - configure_message_rate(MSG_GSV, 0); - configure_message_rate(MSG_GGA, 0); - configure_message_rate(MSG_GSA, 0); - configure_message_rate(MSG_RMC, 0); - configure_message_rate(MSG_VTG, 0); - configure_message_rate(MSG_NAV_TIMEUTC, 1); - configure_message_rate(MSG_NAV_SOL, 1); - - configure_antenna(0x001b, 0x8251); - - configure_pps(0xf4240, 0x3d090, 1, 0 /* utc */, 1, 0, 0, 0); - - _sensors = boost::assign::list_of("gps_locked")("gps_time"); -} - -bool control_impl::gps_detected(void) -{ - return _detected; -} - -void control_impl::_detect(void) -{ - _send_message(MSG_MON_VER, NULL, 0); -} - -std::vector<std::string> control_impl::get_sensors(void) -{ - return _sensors; -} - -uhd::sensor_value_t control_impl::get_sensor(std::string key) -{ - if (key == "gps_time") { - bool lock; - _locked.wait_and_see(lock); - return sensor_value_t("GPS epoch time", - lock ? int(_get_epoch_time()) : 0, "seconds"); - } else if (key == "gps_locked") { - bool lock; - _locked.wait_and_see(lock); - return sensor_value_t("GPS lock status", lock, "locked", "unlocked"); - } else - throw uhd::key_error(str(boost::format("sensor %s unknown.") % key)); -} - -std::time_t control_impl::_get_epoch_time(void) -{ - boost::posix_time::ptime ptime; - _ptime.wait_and_see(ptime); - return (ptime - boost::posix_time::from_time_t(0)).total_seconds(); -} - -control_impl::~control_impl(void) -{ -} - -void control_impl::_decode_init(void) -{ - _decode_state = DECODE_SYNC1; - _rx_ck_a = 0; - _rx_ck_b = 0; - _rx_payload_length = 0; - _rx_payload_index = 0; -} - -void control_impl::_add_byte_to_checksum(const boost::uint8_t b) -{ - _rx_ck_a = _rx_ck_a + b; - _rx_ck_b = _rx_ck_b + _rx_ck_a; -} - -void control_impl::_calc_checksum( - const boost::uint8_t *buffer, - const boost::uint16_t length, - checksum_t &checksum) -{ - for (size_t i = 0; i < length; i++) - { - checksum.ck_a = checksum.ck_a + buffer[i]; - checksum.ck_b = checksum.ck_b + checksum.ck_a; - } -} - -void control_impl::configure_rates( - boost::uint16_t meas_rate, - boost::uint16_t nav_rate, - boost::uint16_t time_ref) -{ - payload_tx_cfg_rate_t cfg_rate; - cfg_rate.meas_rate = uhd::htowx<boost::uint16_t>(meas_rate); - cfg_rate.nav_rate = uhd::htowx<boost::uint16_t>(nav_rate); - cfg_rate.time_ref = uhd::htowx<boost::uint16_t>(time_ref); - - _send_message( - MSG_CFG_RATE, - reinterpret_cast<const uint8_t*>(&cfg_rate), - sizeof(cfg_rate)); - - _wait_for_ack(MSG_CFG_RATE, 1.0); -} - -void control_impl::configure_message_rate( - const boost::uint16_t msg, - const uint8_t rate) -{ - payload_tx_cfg_msg_t cfg_msg; - cfg_msg.msg = uhd::htowx<boost::uint16_t>(msg); - cfg_msg.rate[0] = 0;//rate; - cfg_msg.rate[1] = rate; - cfg_msg.rate[2] = 0;//rate; - cfg_msg.rate[3] = 0;//rate; - cfg_msg.rate[4] = 0;//rate; - cfg_msg.rate[5] = 0;//rate; - _send_message( - MSG_CFG_MSG, - reinterpret_cast<const uint8_t*>(&cfg_msg), - sizeof(cfg_msg)); - - _wait_for_ack(MSG_CFG_MSG, 1.0); -} - -void control_impl::configure_antenna( - const boost::uint16_t flags, - const boost::uint16_t pins) -{ - payload_tx_cfg_ant_t cfg_ant; - cfg_ant.pins = uhd::htowx<boost::uint16_t>(pins); - cfg_ant.flags = uhd::htowx<boost::uint16_t>(flags); - _send_message( - MSG_CFG_ANT, - reinterpret_cast<const uint8_t*>(&cfg_ant), - sizeof(cfg_ant)); - if (_wait_for_ack(MSG_CFG_ANT, 1.0) < 0) { - throw uhd::runtime_error("Didn't get an ACK for antenna configuration."); - } - -} - -void control_impl::configure_pps( - const boost::uint32_t interval, - const boost::uint32_t length, - const boost::int8_t status, - const boost::uint8_t time_ref, - const boost::uint8_t flags, - const boost::int16_t antenna_delay, - const boost::int16_t rf_group_delay, - const boost::int32_t user_delay) -{ - payload_tx_cfg_tp_t cfg_tp; - cfg_tp.interval = uhd::htowx<boost::uint32_t>(interval); - cfg_tp.length = uhd::htowx<boost::uint32_t>(length); - cfg_tp.status = status; - cfg_tp.time_ref = time_ref; - cfg_tp.flags = flags; - cfg_tp.antenna_delay = uhd::htowx<boost::int16_t>(antenna_delay); - cfg_tp.rf_group_delay = uhd::htowx<boost::int16_t>(rf_group_delay); - cfg_tp.user_delay = uhd::htowx<boost::int32_t>(user_delay); - _send_message( - MSG_CFG_TP, - reinterpret_cast<const uint8_t*>(&cfg_tp), - sizeof(cfg_tp)); - if (_wait_for_ack(MSG_CFG_TP, 1.0) < 0) { - throw uhd::runtime_error("Didn't get an ACK for PPS configuration."); - } -} - - -void control_impl::_rx_callback(const char *data, unsigned int len) -{ - //std::cout << "IN RX CALLBACK" << std::flush << std::endl; - std::vector<char> v(data, data+len); - BOOST_FOREACH(const char &c, v) - { - _parse_char(c); - } -} - -void control_impl::_parse_char(const boost::uint8_t b) -{ - int ret = 0; - - switch (_decode_state) { - - // we're expecting the first sync byte - case DECODE_SYNC1: - if (b == SYNC1) { // sync1 found goto next step - _decode_state = DECODE_SYNC2; - } // else stay around - break; - - // we're expecting the second sync byte - case DECODE_SYNC2: - if (b == SYNC2) { // sync2 found goto next step - _decode_state = DECODE_CLASS; - } else { - // failed, reset - _decode_init(); - } - break; - - // we're expecting the class byte - case DECODE_CLASS: - _add_byte_to_checksum(b); - _rx_msg = b; - _decode_state = DECODE_ID; - break; - - // we're expecting the id byte - case DECODE_ID: - _add_byte_to_checksum(b); - _rx_msg |= (b << 8); - _decode_state = DECODE_LENGTH1; - break; - - // we're expecting the first length byte - case DECODE_LENGTH1: - _add_byte_to_checksum(b); - _rx_payload_length = b; - _decode_state = DECODE_LENGTH2; - break; - - // we're expecting the second length byte - case DECODE_LENGTH2: - _add_byte_to_checksum(b); - _rx_payload_length |= (b << 8); - if(_payload_rx_init()) { - _decode_init(); // we failed, give up for this one - } else { - _decode_state = _rx_payload_length ? - DECODE_PAYLOAD : DECODE_CHKSUM1; - } - break; - - // we're expecting payload - case DECODE_PAYLOAD: - _add_byte_to_checksum(b); - switch(_rx_msg) { - default: - ret = _payload_rx_add(b); - break; - }; - if (ret < 0) { - // we couldn't deal with the payload, discard the whole thing - _decode_init(); - } else if (ret > 0) { - // payload was complete, let's check the checksum; - _decode_state = DECODE_CHKSUM1; - } else { - // more payload expected, don't move - } - ret = 0; - break; - - case DECODE_CHKSUM1: - if (_rx_ck_a != b) { - // checksum didn't match, barf - std::cout << boost::format("Failed checksum byte1 %lx != %lx") - % int(_rx_ck_a) % int(b) << std::endl; - _decode_init(); - } else { - _decode_state = DECODE_CHKSUM2; - } - break; - - case DECODE_CHKSUM2: - if (_rx_ck_b != b) { - // checksum didn't match, barf - std::cout << boost::format("Failed checksum byte2 %lx != %lx") - % int(_rx_ck_b) % int(b) << std::endl; - - } else { - ret = _payload_rx_done(); // payload done - } - _decode_init(); - break; - - default: - break; - }; -} - -int control_impl::_payload_rx_init(void) -{ - int ret = 0; - - _rx_state = RXMSG_HANDLE; // by default handle - switch(_rx_msg) { - - case MSG_NAV_SOL: - if (not (_rx_payload_length == sizeof(payload_rx_nav_sol_t))) - _rx_state = RXMSG_ERROR_LENGTH; - break; - - case MSG_NAV_TIMEUTC: - if (not (_rx_payload_length == sizeof(payload_rx_nav_timeutc_t))) - _rx_state = RXMSG_ERROR_LENGTH; - break; - - case MSG_MON_VER: - break; // always take this one - - case MSG_ACK_ACK: - if (not (_rx_payload_length == sizeof(payload_rx_ack_ack_t))) - _rx_state = RXMSG_ERROR_LENGTH; - break; - - case MSG_ACK_NAK: - if (not (_rx_payload_length == sizeof(payload_rx_ack_nak_t))) - _rx_state = RXMSG_ERROR_LENGTH; - break; - - default: - _rx_state = RXMSG_DISABLE; - break; - }; - - switch (_rx_state) { - case RXMSG_HANDLE: // handle message - case RXMSG_IGNORE: // ignore message but don't report error - ret = 0; - break; - case RXMSG_DISABLE: // ignore message but don't report error - case RXMSG_ERROR_LENGTH: // the length doesn't match - ret = -1; - break; - default: // invalid, error - ret = -1; - break; - }; - - return ret; -} - -int control_impl::_payload_rx_add(const boost::uint8_t b) -{ - int ret = 0; - _buf.raw[_rx_payload_index] = b; - if (++_rx_payload_index >= _rx_payload_length) - ret = 1; - return ret; -} - -int control_impl::_payload_rx_done(void) -{ - int ret = 0; - if (_rx_state != RXMSG_HANDLE) { - return 0; - } - - switch (_rx_msg) { - case MSG_MON_VER: - _detected = true; - break; - - case MSG_MON_HW: - std::cout << "MON-HW" << std::endl; - break; - - case MSG_ACK_ACK: - if ((_ack_state == ACK_WAITING) and (_buf.payload_rx_ack_ack.msg == _ack_waiting_msg)) - _ack_state = ACK_GOT_ACK; - break; - - case MSG_ACK_NAK: - if ((_ack_state == ACK_WAITING) and (_buf.payload_rx_ack_nak.msg == _ack_waiting_msg)) - _ack_state = ACK_GOT_NAK; - - break; - - case MSG_CFG_ANT: - break; - - case MSG_NAV_TIMEUTC: - _ptime.update(boost::posix_time::ptime( - boost::gregorian::date( - boost::gregorian::greg_year(uhd::wtohx<boost::uint16_t>( - _buf.payload_rx_nav_timeutc.year)), - boost::gregorian::greg_month(_buf.payload_rx_nav_timeutc.month), - boost::gregorian::greg_day(_buf.payload_rx_nav_timeutc.day)), - (boost::posix_time::hours(_buf.payload_rx_nav_timeutc.hour) - + boost::posix_time::minutes(_buf.payload_rx_nav_timeutc.min) - + boost::posix_time::seconds(_buf.payload_rx_nav_timeutc.sec)))); - break; - - case MSG_NAV_SOL: - _locked.update(_buf.payload_rx_nav_sol.gps_fix > 0); - break; - - default: - std::cout << boost::format("Got unknown message %lx , with good checksum [") % int(_rx_msg); - for(size_t i = 0; i < _rx_payload_length; i++) - std::cout << boost::format("%lx, ") % int(_buf.raw[i]); - std::cout << "]"<< std::endl; - break; - }; - return ret; -} - -void control_impl::_send_message( - const boost::uint16_t msg, - const boost::uint8_t *payload, - const boost::uint16_t len) -{ - header_t header = {SYNC1, SYNC2, msg, len}; - checksum_t checksum = {0, 0}; - - // calculate checksums, first header without sync - // then payload - _calc_checksum( - reinterpret_cast<boost::uint8_t*>(&header) + 2, - sizeof(header) - 2, checksum); - if (payload) - _calc_checksum(payload, len, checksum); - - _serial->write( - reinterpret_cast<const char*>(&header), - sizeof(header)); - - if (payload) - _serial->write((const char *) payload, len); - - _serial->write( - reinterpret_cast<const char*>(&checksum), - sizeof(checksum)); -} - -int control_impl::_wait_for_ack( - const boost::uint16_t msg, - const double timeout) -{ - int ret = -1; - - _ack_state = ACK_WAITING; - _ack_waiting_msg = msg; - - boost::system_time timeout_time = - boost::get_system_time() + - boost::posix_time::milliseconds(timeout * 1000.0); - - do { - if(_ack_state == ACK_GOT_ACK) - return 0; - else if (_ack_state == ACK_GOT_NAK) { - return -1; - } - boost::this_thread::sleep(boost::posix_time::milliseconds(20)); - } while (boost::get_system_time() < timeout_time); - - // we get here ... it's a timeout - _ack_state = ACK_IDLE; - return ret; -} - - -}} // namespace ublox::ubx -}}} // namespace - -using namespace uhd::usrp::gps::ublox::ubx; - -control::sptr control::make(const std::string &node, const size_t baud_rate) -{ - return control::sptr(new control_impl(node, baud_rate)); -} -#else -using namespace uhd::usrp::gps::ublox::ubx; - -control::sptr control::make(const std::string& /* node */, const size_t /* baud_rate */) -{ - throw uhd::assertion_error("control::sptr::make: !E300_NATIVE"); -} -#endif // E300_NATIVE diff --git a/host/lib/usrp/e300/e300_ublox_control_impl.hpp b/host/lib/usrp/e300/e300_ublox_control_impl.hpp deleted file mode 100644 index a1dcbfe6c..000000000 --- a/host/lib/usrp/e300/e300_ublox_control_impl.hpp +++ /dev/null @@ -1,457 +0,0 @@ -#ifndef INCLUDED_UHD_USRP_UBLOX_CONTROL_IMPL_HPP -#define INCLUDED_UHD_USRP_UBLOX_CONTROL_IMPL_HPP - -#include <boost/cstdint.hpp> -#include <boost/noncopyable.hpp> -#include <boost/asio.hpp> -#include <uhd/config.hpp> -#include <uhd/usrp/gps_ctrl.hpp> -#include <uhd/types/sensors.hpp> - -#include "e300_async_serial.hpp" - -namespace uhd { namespace usrp { namespace gps { - -namespace ublox { namespace ubx { -// ublox binary sync words -static const boost::uint8_t SYNC1 = 0xB5; -static const boost::uint8_t SYNC2 = 0x62; - -// message classes -static const boost::uint8_t CLASS_NAV = 0x01; -static const boost::uint8_t CLASS_ACK = 0x05; -static const boost::uint8_t CLASS_CFG = 0x06; -static const boost::uint8_t CLASS_MON = 0x0a; -static const boost::uint8_t CLASS_NMEA = 0xf0; - -// Message IDs -static const boost::uint8_t ID_NAV_POSLLH = 0x02; -static const boost::uint8_t ID_NAV_SOL = 0x06; -static const boost::uint8_t ID_NAV_PVT = 0x07; -static const boost::uint8_t ID_NAV_VELNED = 0x12; -static const boost::uint8_t ID_NAV_TIMEUTC = 0x21; -static const boost::uint8_t ID_NAV_SVINFO = 0x30; -static const boost::uint8_t ID_ACK_NAK = 0x00; -static const boost::uint8_t ID_ACK_ACK = 0x01; -static const boost::uint8_t ID_CFG_PRT = 0x00; -static const boost::uint8_t ID_CFG_ANT = 0x13; -static const boost::uint8_t ID_CFG_TP = 0x07; -static const boost::uint8_t ID_CFG_MSG = 0x01; -static const boost::uint8_t ID_CFG_RATE = 0x08; -static const boost::uint8_t ID_CFG_NAV5 = 0x24; -static const boost::uint8_t ID_MON_VER = 0x04; -static const boost::uint8_t ID_MON_HW = 0x09; -static const boost::uint8_t ID_GGA = 0x00; -static const boost::uint8_t ID_GLL = 0x01; -static const boost::uint8_t ID_GSA = 0x02; -static const boost::uint8_t ID_GSV = 0x03; -static const boost::uint8_t ID_RMC = 0x04; -static const boost::uint8_t ID_VTG = 0x05; -static const boost::uint8_t ID_GST = 0x07; - -// Message Classes & IDs // -static const boost::uint16_t MSG_NAV_POSLLH - = CLASS_NAV | (ID_NAV_POSLLH << 8); -static const boost::uint16_t MSG_NAV_SOL - = CLASS_NAV | (ID_NAV_SOL << 8); -static const boost::uint16_t MSG_NAV_PVT - = CLASS_NAV | (ID_NAV_PVT << 8); -static const boost::uint16_t MSG_NAV_VELNED - = CLASS_NAV | (ID_NAV_VELNED << 8); -static const boost::uint16_t MSG_NAV_TIMEUTC - = CLASS_NAV | (ID_NAV_TIMEUTC << 8); -static const boost::uint16_t MSG_NAV_SVINFO - = CLASS_NAV | (ID_NAV_SVINFO << 8); -static const boost::uint16_t MSG_ACK_NAK - = CLASS_ACK | (ID_ACK_NAK << 8); -static const boost::uint16_t MSG_ACK_ACK - = CLASS_ACK | (ID_ACK_ACK << 8); -static const boost::uint16_t MSG_CFG_PRT - = CLASS_CFG | (ID_CFG_PRT << 8); -static const boost::uint16_t MSG_CFG_ANT - = CLASS_CFG | (ID_CFG_ANT << 8); -static const boost::uint16_t MSG_CFG_TP - = CLASS_CFG | (ID_CFG_TP << 8); -static const boost::uint16_t MSG_CFG_MSG - = CLASS_CFG | (ID_CFG_MSG << 8); -static const boost::uint16_t MSG_CFG_RATE - = CLASS_CFG | (ID_CFG_RATE << 8); -static const boost::uint16_t MSG_CFG_NAV5 - = CLASS_CFG | (ID_CFG_NAV5 << 8); -static const boost::uint16_t MSG_MON_HW - = CLASS_MON | (ID_MON_HW << 8); -static const boost::uint16_t MSG_MON_VER - = CLASS_MON | (ID_MON_VER << 8); - -// NMEA ones -static const boost::uint16_t MSG_GGA - = CLASS_NMEA | (ID_GGA << 8); -static const boost::uint16_t MSG_GLL - = CLASS_NMEA | (ID_GLL << 8); -static const boost::uint16_t MSG_GSA - = CLASS_NMEA | (ID_GSA << 8); -static const boost::uint16_t MSG_GSV - = CLASS_NMEA | (ID_GSV << 8); -static const boost::uint16_t MSG_RMC - = CLASS_NMEA | (ID_RMC << 8); -static const boost::uint16_t MSG_VTG - = CLASS_NMEA | (ID_VTG << 8); - -// header -struct header_t -{ - boost::uint8_t sync1; - boost::uint8_t sync2; - boost::uint16_t msg; - boost::uint16_t length; -}; - -// checksum -struct checksum_t -{ - boost::uint8_t ck_a; - boost::uint8_t ck_b; -}; - -// rx rx mon-hw (ubx6) -struct payload_rx_mon_hw_t -{ - boost::uint32_t pin_sel; - boost::uint32_t pin_bank; - boost::uint32_t pin_dir; - boost::uint32_t pin_val; - boost::uint16_t noise_per_ms; - boost::uint16_t agc_cnt; - boost::uint8_t a_status; - boost::uint8_t a_power; - boost::uint8_t flags; - boost::uint8_t reserved1; - boost::uint32_t used_mask; - boost::uint8_t vp[25]; - boost::uint8_t jam_ind; - boost::uint16_t reserved3; - boost::uint32_t pin_irq; - boost::uint32_t pullh; - boost::uint32_t pulll; -}; - -// rx mon-ver -struct payload_rx_mon_ver_part1_t -{ - char sw_version[30]; - char hw_version[10]; -}; - -struct payload_rx_mon_ver_part2_t -{ - boost::uint8_t extension[30]; -}; - -// rx ack-ack -typedef union { - boost::uint16_t msg; - struct { - boost::uint8_t cls_id; - boost::uint8_t msg_id; - }; -} payload_rx_ack_ack_t; - -// rx ack-nak -typedef union { - boost::uint16_t msg; - struct { - boost::uint8_t cls_id; - boost::uint8_t msg_id; - }; -} payload_rx_ack_nak_t; - -// tx cfg-prt (uart) -struct payload_tx_cfg_prt_t -{ - boost::uint8_t port_id; - boost::uint8_t reserved0; - boost::uint16_t tx_ready; - boost::uint32_t mode; - boost::uint32_t baud_rate; - boost::uint16_t in_proto_mask; - boost::uint16_t out_proto_mask; - boost::uint16_t flags; - boost::uint16_t reserved5; -}; - -// tx cfg-rate -struct payload_tx_cfg_rate_t -{ - boost::uint16_t meas_rate; - boost::uint16_t nav_rate; - boost::uint16_t time_ref; -}; - -// tx cfg-msg -struct payload_tx_cfg_msg_t -{ - boost::uint16_t msg; - boost::uint8_t rate[6]; -}; - - -// tx cfg-ant -struct payload_tx_cfg_ant_t -{ - boost::uint16_t flags; - boost::uint16_t pins; -}; - -// tx cfg-tp -struct payload_tx_cfg_tp_t -{ - boost::uint32_t interval; - boost::uint32_t length; - boost::int8_t status; - boost::uint8_t time_ref; - boost::uint8_t flags; - boost::uint8_t reserved1; - boost::int16_t antenna_delay; - boost::int16_t rf_group_delay; - boost::int32_t user_delay; -}; - -struct payload_rx_nav_sol_t -{ - boost::uint32_t i_tow; - boost::int32_t f_tow; - boost::int16_t week; - boost::uint8_t gps_fix; - boost::uint8_t flags; - boost::int32_t ecef_x; - boost::int32_t ecef_y; - boost::int32_t ecef_z; - boost::uint32_t p_acc; - boost::int32_t ecef_vx; - boost::int32_t ecef_vy; - boost::int32_t ecef_vz; - boost::uint32_t s_acc; - boost::uint16_t p_dop; - boost::uint8_t reserved1; - boost::uint8_t num_sv; - boost::uint32_t reserved2; -}; - -struct payload_rx_nav_timeutc_t -{ - boost::uint32_t i_tow; - boost::uint32_t t_acc; - boost::int32_t nano; - boost::uint16_t year; - boost::uint8_t month; - boost::uint8_t day; - boost::uint8_t hour; - boost::uint8_t min; - boost::uint8_t sec; - boost::uint8_t valid; -}; - -typedef union { - payload_rx_mon_hw_t payload_rx_mon_hw; - - payload_rx_mon_ver_part1_t payload_rx_mon_ver_part1; - payload_rx_mon_ver_part2_t payload_rx_mon_ver_part2; - - payload_rx_ack_ack_t payload_rx_ack_ack; - payload_rx_ack_nak_t payload_rx_ack_nak; - - payload_tx_cfg_prt_t payload_tx_cfg_prt; - payload_tx_cfg_ant_t payload_tx_cfg_ant; - payload_tx_cfg_rate_t payload_tx_cfg_rate; - - payload_tx_cfg_msg_t payload_tx_cfg_msg; - - payload_rx_nav_timeutc_t payload_rx_nav_timeutc; - payload_rx_nav_sol_t payload_rx_nav_sol; - boost::uint8_t raw[]; -} buf_t; - - -template <typename T> -class sensor_entry -{ -public: - sensor_entry() : _seen(false) - { - } - - void update(const T &val) - { - boost::mutex::scoped_lock l(_mutex); - _value = val; - _seen = false; - l.unlock(); - _cond.notify_one(); - } - - bool seen() const - { - boost::mutex::scoped_lock l(_mutex); - return _seen; - } - - bool try_and_see(T &val) - { - boost::mutex::scoped_lock l(_mutex); - if (_seen) - return false; - - val = _value; - _seen = true; - return true; - } - - void wait_and_see(T &val) - { - boost::mutex::scoped_lock l(_mutex); - while(_seen) - { - _cond.wait(l); - //std::cout << "Already seen ... " << std::endl; - } - val = _value; - _seen = true; - } - -private: // members - T _value; - boost::mutex _mutex; - boost::condition_variable _cond; - bool _seen; -}; - -class control_impl : public control -{ -public: - control_impl(const std::string &node, const size_t baud_rate); - - virtual ~control_impl(void); - - void configure_message_rate( - const boost::uint16_t msg, - const boost::uint8_t rate); - - void configure_antenna( - const boost::uint16_t flags, - const boost::uint16_t pins); - - void configure_pps( - const boost::uint32_t interval, - const boost::uint32_t length, - const boost::int8_t status, - const boost::uint8_t time_ref, - const boost::uint8_t flags, - const boost::int16_t antenna_delay, - const boost::int16_t rf_group_delay, - const boost::int32_t user_delay); - - void configure_rates( - boost::uint16_t meas_rate, - boost::uint16_t nav_rate, - boost::uint16_t time_ref); - - // gps_ctrl interface - bool gps_detected(void); - std::vector<std::string> get_sensors(void); - uhd::sensor_value_t get_sensor(std::string key); - -private: // types - enum decoder_state_t { - DECODE_SYNC1 = 0, - DECODE_SYNC2, - DECODE_CLASS, - DECODE_ID, - DECODE_LENGTH1, - DECODE_LENGTH2, - DECODE_PAYLOAD, - DECODE_CHKSUM1, - DECODE_CHKSUM2, - }; - - enum rxmsg_state_t { - RXMSG_IGNORE = 0, - RXMSG_HANDLE, - RXMSG_DISABLE, - RXMSG_ERROR_LENGTH - }; - - enum ack_state_t { - ACK_IDLE = 0, - ACK_WAITING, - ACK_GOT_ACK, - ACK_GOT_NAK - }; - -private: // methods - std::time_t _get_epoch_time(void); - - void _decode_init(void); - - void _add_byte_to_checksum(const boost::uint8_t b); - - void _detect(void); - - void _send_message( - const boost::uint16_t msg, - const boost::uint8_t *payload, - const boost::uint16_t len); - - int _wait_for_ack( - const boost::uint16_t msg, - const double timeout); - - void _calc_checksum( - const boost::uint8_t *buffer, - const boost::uint16_t length, - checksum_t &checksum); - - void _rx_callback(const char *data, unsigned len); - - void _parse_char(const boost::uint8_t b); - - int _payload_rx_init(void); - - int _payload_rx_add(const boost::uint8_t b); - - int _payload_rx_done(void); - -private: // members - // gps_ctrl stuff - bool _detected; - std::vector<std::string> _sensors; - - sensor_entry<bool> _locked; - sensor_entry<boost::posix_time::ptime> _ptime; - - // decoder state - decoder_state_t _decode_state; - rxmsg_state_t _rxmsg_state; - - ack_state_t _ack_state; - boost::uint16_t _ack_waiting_msg; - - boost::uint8_t _rx_ck_a; - boost::uint8_t _rx_ck_b; - - boost::uint16_t _rx_payload_length; - size_t _rx_payload_index; - boost::uint16_t _rx_msg; - - rxmsg_state_t _rx_state; - - boost::shared_ptr<async_serial> _serial; - - // this has to be at the end of the - // class to be valid C++ - buf_t _buf; -}; - -}} // namespace ublox::ubx - -}}} // namespace -#endif // INCLUDED_UHD_USRP_UBLOX_CONTROL_IMPL_HPP diff --git a/host/lib/usrp/gps_ctrl.cpp b/host/lib/usrp/gps_ctrl.cpp index b9d5128bb..207ef10ab 100644 --- a/host/lib/usrp/gps_ctrl.cpp +++ b/host/lib/usrp/gps_ctrl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011,2014 Ettus Research LLC +// Copyright 2010-2011,2014-2015 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 @@ -157,7 +157,7 @@ public: _send("HAAAY GUYYYYS\n"); //to elicit a response from the GPSDO //wait for _send(...) to return - sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); + sleep(milliseconds(GPSDO_COMMAND_DELAY_MS)); //then we loop until we either timeout, or until we get a response that indicates we're a JL device const boost::system_time comm_timeout = boost::get_system_time() + milliseconds(GPS_COMM_TIMEOUT_MS); @@ -237,19 +237,19 @@ private: //issue some setup stuff so it spits out the appropriate data //none of these should issue replies so we don't bother looking for them //we have to sleep between commands because the JL device, despite not acking, takes considerable time to process each command. - sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); + sleep(milliseconds(GPSDO_COMMAND_DELAY_MS)); _send("SYST:COMM:SER:ECHO OFF\n"); - sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); + sleep(milliseconds(GPSDO_COMMAND_DELAY_MS)); _send("SYST:COMM:SER:PRO OFF\n"); - sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); + sleep(milliseconds(GPSDO_COMMAND_DELAY_MS)); _send("GPS:GPGGA 1\n"); - sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); + sleep(milliseconds(GPSDO_COMMAND_DELAY_MS)); _send("GPS:GGAST 0\n"); - sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); + sleep(milliseconds(GPSDO_COMMAND_DELAY_MS)); _send("GPS:GPRMC 1\n"); - sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); + sleep(milliseconds(GPSDO_COMMAND_DELAY_MS)); _send("SERV:TRAC 0\n"); - sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); + sleep(milliseconds(GPSDO_COMMAND_DELAY_MS)); } //retrieve a raw NMEA sentence @@ -352,7 +352,7 @@ private: //enable servo reporting _send("SERV:TRAC 1\n"); - sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); + sleep(milliseconds(GPSDO_COMMAND_DELAY_MS)); std::string reply; @@ -399,7 +399,7 @@ private: static const int GPS_SERVO_FRESHNESS = 2500; static const int GPS_LOCK_FRESHNESS = 2500; static const int GPS_TIMEOUT_DELAY_MS = 200; - static const int GPSDO_STUPID_DELAY_MS = 200; + static const int GPSDO_COMMAND_DELAY_MS = 200; }; /*********************************************************************** diff --git a/host/lib/usrp/gpsd_iface.cpp b/host/lib/usrp/gpsd_iface.cpp new file mode 100644 index 000000000..e0a1dea05 --- /dev/null +++ b/host/lib/usrp/gpsd_iface.cpp @@ -0,0 +1,309 @@ +// +// Copyright 2015 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 <cmath> + +#include <gps.h> + +#include <boost/assign/list_of.hpp> +#include <boost/bind.hpp> +#include <boost/cstdint.hpp> +#include "boost/date_time/gregorian/gregorian.hpp" +#include <boost/format.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/thread/shared_mutex.hpp> +#include <boost/thread.hpp> +#include <boost/math/special_functions/fpclassify.hpp> + +#include <uhd/exception.hpp> +#include <uhd/usrp/gps_ctrl.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/types/dict.hpp> + +#include "gpsd_iface.hpp" + +namespace uhd { namespace usrp { + +static const size_t TIMEOUT = 240; +static const size_t CLICK_RATE = 250000; + +class gpsd_iface_impl : public virtual gpsd_iface { +public: + gpsd_iface_impl(const std::string &addr, boost::uint16_t port) + : _detected(false), _bthread(), _timeout_cnt(0) + { + boost::unique_lock<boost::shared_mutex> l(_d_mutex); + + if (gps_open(addr.c_str(), + str(boost::format("%u") % port).c_str(), + &_gps_data) < 0) { + throw uhd::runtime_error( + str((boost::format("Failed to connect to gpsd: %s") + % gps_errstr(errno)))); + } + + // register for updates, we don't specify a specific device, + // therefore no WATCH_DEVICE + gps_stream(&_gps_data, WATCH_ENABLE, NULL); + + // create background thread talking to gpsd + boost::thread t(boost::bind(&gpsd_iface_impl::_thread_fcn ,this)); + _bthread.swap(t); + + + _sensors = boost::assign::list_of("gps_locked")("gps_time") \ + ("gps_position")("gps_gpgga")("gps_gprmc"); + } + + virtual ~gpsd_iface_impl(void) + { + // interrupt the background thread and wait for it to finish + _bthread.interrupt(); + _bthread.join(); + + // clean up ... + { + boost::unique_lock<boost::shared_mutex> l(_d_mutex); + + gps_stream(&_gps_data, WATCH_DISABLE, NULL); + gps_close(&_gps_data); + } + } + + uhd::sensor_value_t get_sensor(std::string key) + { + if (key == "gps_locked") { + return sensor_value_t( + "GPS lock status", _gps_locked(), "locked", "unlocked"); + } else if (key == "gps_time") { + return sensor_value_t( + "GPS epoch time", int(_epoch_time()), "seconds"); + } else if (key == "gps_gpgga") { + return sensor_value_t( + "GPGGA", _gps_gpgga(), ""); + } else if (key == "gps_gprmc") { + return sensor_value_t( + "GPRMC", _gps_gprmc(), ""); + } else if (key == "gps_position") { + return sensor_value_t( + "GPS Position", str( + boost::format("%s %s %s") + % _gps_position()["lat"] + % _gps_position()["lon"] + % _gps_position()["alt"]), "lat/lon/alt"); + } else + throw uhd::key_error( + str(boost::format("sensor %s unknown.") % key)); + } + + bool gps_detected(void) { return _detected; }; + + std::vector<std::string> get_sensors(void) { return _sensors; }; + +private: // member functions + void _thread_fcn() + { + while (not boost::this_thread::interruption_requested()) { + if (!gps_waiting(&_gps_data, CLICK_RATE)) { + if (TIMEOUT < _timeout_cnt++) + _detected = false; + } else { + boost::unique_lock<boost::shared_mutex> l(_d_mutex); + + _timeout_cnt = 0; + _detected = true; + + if (gps_read(&_gps_data) < 0) + throw std::runtime_error("error while reading"); + } + } + } + + bool _gps_locked(void) + { + boost::shared_lock<boost::shared_mutex> l(_d_mutex); + return _gps_data.fix.mode >= MODE_2D; + } + + std::time_t _epoch_time(void) + { + boost::shared_lock<boost::shared_mutex> l(_d_mutex); + return (boost::posix_time::from_time_t(_gps_data.fix.time) + - boost::posix_time::from_time_t(0)).total_seconds(); + } + + boost::gregorian::date _gregorian_date(void) + { + boost::shared_lock<boost::shared_mutex> l(_d_mutex); + return boost::posix_time::from_time_t(_gps_data.fix.time).date(); + } + + uhd::dict<std::string, std::string> _gps_position(void) + { + boost::shared_lock<boost::shared_mutex> l(_d_mutex); + + uhd::dict<std::string, std::string> tmp; + if (_gps_data.fix.mode >= MODE_2D) { + tmp["lon"] = str(boost::format("%f deg") + % _gps_data.fix.longitude); + tmp["lat"] = str(boost::format("%f deg") + % _gps_data.fix.latitude); + tmp["alt"] = str(boost::format("%fm") + % _gps_data.fix.altitude); + } else { + tmp["lon"] = "n/a"; + tmp["lat"] = "n/a"; + tmp["alt"] = "n/a"; + } + return tmp; + } + + float _zeroize(float x) + { + return boost::math::isnan(x) ? 0.0 : x; + } + + int _nmea_checksum(const std::string &s) + { + if ((s.at(0) != '$')) + return 0; + + boost::uint8_t sum = '\0'; + for (size_t i = 1; i < s.size(); i++) + sum ^= static_cast<boost::uint8_t>(s.at(i)); + + return sum; + } + + double _deg_to_dm(double angle) + { + double fraction, integer; + fraction = std::modf(angle, &integer); + return std::floor(angle) * 100 + fraction * 60; + } + + std::string _gps_gprmc(void) + { + struct tm tm; + time_t intfixtime; + + boost::shared_lock<boost::shared_mutex> l(_d_mutex); + + tm.tm_mday = tm.tm_mon = tm.tm_year = 0; + tm.tm_hour = tm.tm_min = tm.tm_sec = 0; + + if (boost::math::isnan(_gps_data.fix.time) == 0) { + intfixtime = (time_t) _gps_data.fix.time; + (void)gmtime_r(&intfixtime, &tm); + tm.tm_mon++; + tm.tm_year %= 100; + } + std::string string = str(boost::format( + "$GPRMC,%02d%02d%02d,%c,%09.4f,%c,%010.4f,%c,%.4f,%.3f,%02d%02d%02d,,") + % tm.tm_hour + % tm.tm_min + % tm.tm_sec + % (_gps_data.status ? 'A' : 'V') + % _zeroize(_deg_to_dm(std::fabs(_gps_data.fix.latitude))) + % ((_gps_data.fix.latitude > 0) ? 'N' : 'S') + % _zeroize(_deg_to_dm(std::fabs(_gps_data.fix.longitude))) + % ((_gps_data.fix.longitude > 0) ? 'E' : 'W') + % _zeroize(_gps_data.fix.speed * MPS_TO_KNOTS) + % _zeroize(_gps_data.fix.track) + % tm.tm_mday % tm.tm_mon % tm.tm_year); + + string.append(str( + boost::format("*%02X") % _nmea_checksum(string))); + + return string; + } + + std::string _gps_gpgga(void) + { + struct tm tm; + time_t intfixtime; + + // currently not supported, make it blank + float mag_var = NAN; + + boost::shared_lock<boost::shared_mutex> l(_d_mutex); + + intfixtime = (time_t) _gps_data.fix.time; + (void) gmtime_r(&intfixtime, &tm); + + std::string string = str(boost::format( + "$GPGGA,%02d%02d%02d,%09.4f,%c,%010.4f,%c,%d,%02d,") + % tm.tm_hour + % tm.tm_min + % tm.tm_sec + % _deg_to_dm(std::fabs(_gps_data.fix.latitude)) + % ((_gps_data.fix.latitude > 0) ? 'N' : 'S') + % _deg_to_dm(std::fabs(_gps_data.fix.longitude)) + % ((_gps_data.fix.longitude > 0) ? 'E' : 'W') + % _gps_data.status + % _gps_data.satellites_used); + + if (boost::math::isnan(_gps_data.dop.hdop)) + string.append(","); + else + string.append( + str(boost::format("%.2f,") % _gps_data.dop.hdop)); + + if (boost::math::isnan(_gps_data.fix.altitude)) + string.append(","); + else + string.append( + str(boost::format("%.2f,M,") % _gps_data.fix.altitude)); + + if (boost::math::isnan(_gps_data.separation)) + string.append(","); + else + string.append( + str(boost::format("%.3f,M,") % _gps_data.separation)); + + if (boost::math::isnan(mag_var)) + string.append(","); + else { + string.append( + str(boost::format("%3.2f,%s") % std::fabs(mag_var) + % (mag_var > 0 ? "E" : "W"))); + } + + string.append(str( + boost::format("*%02X") % _nmea_checksum(string))); + + return string; + } + +private: // members + std::vector<std::string> _sensors; + bool _detected; + + gps_data_t _gps_data; + boost::shared_mutex _d_mutex; + boost::thread _bthread; + size_t _timeout_cnt; +}; + +}} //namespace + +using namespace uhd::usrp; + +gpsd_iface::sptr gpsd_iface::make(const std::string &addr, const boost::uint16_t port) +{ + return gpsd_iface::sptr(new gpsd_iface_impl(addr, port)); +} diff --git a/host/lib/usrp/gpsd_iface.hpp b/host/lib/usrp/gpsd_iface.hpp new file mode 100644 index 000000000..7d934ae5c --- /dev/null +++ b/host/lib/usrp/gpsd_iface.hpp @@ -0,0 +1,36 @@ +// +// Copyright 2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_GPSD_IFACE_HPP +#define INCLUDED_GPSD_IFACE_HPP + +#include <boost/cstdint.hpp> +#include <boost/shared_ptr.hpp> + +#include <uhd/usrp/gps_ctrl.hpp> + +namespace uhd { namespace usrp { + +class gpsd_iface : public virtual uhd::gps_ctrl { +public: + typedef boost::shared_ptr<gpsd_iface> sptr; + static sptr make(const std::string &addr, boost::uint16_t port); +}; + +}}; + +#endif /* INCLUDED_GPSD_IFACE_HPP */ diff --git a/host/lib/usrp/mboard_eeprom.cpp b/host/lib/usrp/mboard_eeprom.cpp index 68c084589..f60182c76 100644 --- a/host/lib/usrp/mboard_eeprom.cpp +++ b/host/lib/usrp/mboard_eeprom.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2013 Ettus Research LLC +// Copyright 2010-2013,2015 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 @@ -16,6 +16,7 @@ // #include <uhd/usrp/mboard_eeprom.hpp> +#include <uhd/types/byte_vector.hpp> #include <uhd/types/mac_addr.hpp> #include <uhd/utils/byteswap.hpp> #include <boost/asio/ip/address_v4.hpp> @@ -39,32 +40,6 @@ static const size_t NAME_MAX_LEN = 32 - SERIAL_LEN; * Utility functions **********************************************************************/ -//! A wrapper around std::copy that takes ranges instead of iterators. -template<typename RangeSrc, typename RangeDst> inline -void byte_copy(const RangeSrc &src, RangeDst &dst){ - std::copy(boost::begin(src), boost::end(src), boost::begin(dst)); -} - -//! create a string from a byte vector, return empty if invalid ascii -static const std::string bytes_to_string(const byte_vector_t &bytes){ - std::string out; - BOOST_FOREACH(boost::uint8_t byte, bytes){ - if (byte < 32 or byte > 127) return out; - out += byte; - } - return out; -} - -//! create a byte vector from a string, null terminate unless max length -static const byte_vector_t string_to_bytes(const std::string &string, size_t max_length){ - byte_vector_t bytes; - for (size_t i = 0; i < std::min(string.size(), max_length); i++){ - bytes.push_back(string[i]); - } - if (bytes.size() < max_length - 1) bytes.push_back('\0'); - return bytes; -} - //! convert a string to a byte vector to write to eeprom static byte_vector_t string_to_uint16_bytes(const std::string &num_str){ const boost::uint16_t num = boost::lexical_cast<boost::uint16_t>(num_str); @@ -238,7 +213,8 @@ struct x300_eeprom_map //indentifying numbers unsigned char revision[2]; unsigned char product[2]; - boost::uint8_t _pad0[4]; + unsigned char revision_compat[2]; + boost::uint8_t _pad0[2]; //all the mac addrs boost::uint8_t mac_addr0[6]; @@ -264,6 +240,11 @@ static void load_x300(mboard_eeprom_t &mb_eeprom, i2c_iface &iface) iface.read_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, revision), 2) ); + //extract the revision compat number + mb_eeprom["revision_compat"] = uint16_bytes_to_string( + iface.read_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, revision_compat), 2) + ); + //extract the product code mb_eeprom["product"] = uint16_bytes_to_string( iface.read_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, product), 2) @@ -310,6 +291,12 @@ static void store_x300(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface) string_to_uint16_bytes(mb_eeprom["revision"]) ); + //parse the revision compat number + if (mb_eeprom.has_key("revision_compat")) iface.write_eeprom( + X300_EEPROM_ADDR, offsetof(x300_eeprom_map, revision_compat), + string_to_uint16_bytes(mb_eeprom["revision_compat"]) + ); + //parse the product code if (mb_eeprom.has_key("product")) iface.write_eeprom( X300_EEPROM_ADDR, offsetof(x300_eeprom_map, product), diff --git a/host/lib/usrp/mboard_eeprom_c.cpp b/host/lib/usrp/mboard_eeprom_c.cpp new file mode 100644 index 000000000..8d5c069b9 --- /dev/null +++ b/host/lib/usrp/mboard_eeprom_c.cpp @@ -0,0 +1,72 @@ +// +// Copyright 2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/usrp/mboard_eeprom.h> + +#include <uhd/exception.hpp> + +#include <string.h> + +uhd_error uhd_mboard_eeprom_make( + uhd_mboard_eeprom_handle* h +){ + UHD_SAFE_C( + *h = new uhd_mboard_eeprom_t; + ) +} + +uhd_error uhd_mboard_eeprom_free( + uhd_mboard_eeprom_handle* h +){ + UHD_SAFE_C( + delete *h; + *h = NULL; + ) +} + +uhd_error uhd_mboard_eeprom_get_value( + uhd_mboard_eeprom_handle h, + const char* key, + char* value_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string value_cpp = h->mboard_eeprom_cpp.get(key); + strncpy(value_out, value_cpp.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_mboard_eeprom_set_value( + uhd_mboard_eeprom_handle h, + const char* key, + const char* value +){ + UHD_SAFE_C_SAVE_ERROR(h, + h->mboard_eeprom_cpp[key] = value; + ) +} + +uhd_error uhd_mboard_eeprom_last_error( + uhd_mboard_eeprom_handle h, + char* error_out, + size_t strbuffer_len +){ + UHD_SAFE_C( + memset(error_out, '\0', strbuffer_len); + strncpy(error_out, h->last_error.c_str(), strbuffer_len); + ) +} diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp index 794438b90..396237e24 100644 --- a/host/lib/usrp/multi_usrp.cpp +++ b/host/lib/usrp/multi_usrp.cpp @@ -26,10 +26,13 @@ #include <uhd/usrp/mboard_eeprom.hpp> #include <uhd/usrp/dboard_eeprom.hpp> #include <uhd/convert.hpp> +#include <uhd/utils/soft_register.hpp> #include <boost/assign/list_of.hpp> #include <boost/thread.hpp> #include <boost/foreach.hpp> #include <boost/format.hpp> +#include <boost/algorithm/string.hpp> +#include <algorithm> #include <cmath> using namespace uhd; @@ -69,7 +72,7 @@ static void do_samp_rate_warning_message( } } -static void do_tune_freq_results_message( +/*static void do_tune_freq_results_message( const tune_request_t &tune_req, const tune_result_t &tune_result, double actual_freq, @@ -171,7 +174,7 @@ static void do_tune_freq_results_message( UHD_MSG(warning) << results_string << std::endl; } -} +}*/ /*! The CORDIC can be used to shift the baseband below / past the tunable * limits of the actual RF front-end. The baseband filter, located on the @@ -309,7 +312,9 @@ static tune_result_t tune_xx_subdev_and_dsp( //------------------------------------------------------------------ //-- Tune the RF frontend //------------------------------------------------------------------ - rf_fe_subtree->access<double>("freq/value").set(target_rf_freq); + if (tune_request.rf_freq_policy != tune_request_t::POLICY_NONE) { + rf_fe_subtree->access<double>("freq/value").set(target_rf_freq); + } const double actual_rf_freq = rf_fe_subtree->access<double>("freq/value").get(); //------------------------------------------------------------------ @@ -346,7 +351,9 @@ static tune_result_t tune_xx_subdev_and_dsp( //------------------------------------------------------------------ //-- Tune the DSP //------------------------------------------------------------------ - dsp_subtree->access<double>("freq/value").set(target_dsp_freq); + if (tune_request.dsp_freq_policy != tune_request_t::POLICY_NONE) { + dsp_subtree->access<double>("freq/value").set(target_dsp_freq); + } const double actual_dsp_freq = dsp_subtree->access<double>("freq/value").get(); //------------------------------------------------------------------ @@ -431,6 +438,9 @@ public: ******************************************************************/ void set_master_clock_rate(double rate, size_t mboard){ if (mboard != ALL_MBOARDS){ + if (_tree->exists(mb_root(mboard) / "auto_tick_rate")) { + _tree->access<bool>(mb_root(mboard) / "auto_tick_rate").set(false); + } _tree->access<double>(mb_root(mboard) / "tick_rate").set(rate); return; } @@ -800,7 +810,7 @@ public: _tree->subtree(rx_dsp_root(chan)), _tree->subtree(rx_rf_fe_root(chan)), tune_request); - do_tune_freq_results_message(tune_request, result, get_rx_freq(chan), "RX"); + //do_tune_freq_results_message(tune_request, result, get_rx_freq(chan), "RX"); return result; } @@ -821,6 +831,26 @@ public: } void set_rx_gain(double gain, const std::string &name, size_t chan){ + /* Check if any AGC mode is enable and if so warn the user */ + if (chan != ALL_CHANS) { + if (_tree->exists(rx_rf_fe_root(chan) / "gain" / "agc")) { + bool agc = _tree->access<bool>(rx_rf_fe_root(chan) / "gain" / "agc" / "enable").get(); + if(agc) { + UHD_MSG(warning) << "AGC enabled for this channel. Setting will be ignored." << std::endl; + } + } + } else { + for (size_t c = 0; c < get_rx_num_channels(); c++){ + if (_tree->exists(rx_rf_fe_root(c) / "gain" / "agc")) { + bool agc = _tree->access<bool>(rx_rf_fe_root(chan) / "gain" / "agc" / "enable").get(); + if(agc) { + UHD_MSG(warning) << "AGC enabled for this channel. Setting will be ignored." << std::endl; + } + } + } + } + /* Apply gain setting. + * If device is in AGC mode it will ignore the setting. */ try { return rx_gain_group(chan)->set_value(gain, name); } catch (uhd::key_error &) { @@ -828,6 +858,32 @@ public: } } + void set_normalized_rx_gain(double gain, size_t chan = 0) + { + if (gain > 1.0 || gain < 0.0) { + throw uhd::runtime_error("Normalized gain out of range, must be in [0, 1]."); + } + gain_range_t gain_range = get_rx_gain_range(ALL_GAINS, chan); + double abs_gain = (gain * (gain_range.stop() - gain_range.start())) + gain_range.start(); + set_rx_gain(abs_gain, ALL_GAINS, chan); + } + + void set_rx_agc(bool enable, size_t chan = 0) + { + if (chan != ALL_CHANS){ + if (_tree->exists(rx_rf_fe_root(chan) / "gain" / "agc" / "enable")) { + _tree->access<bool>(rx_rf_fe_root(chan) / "gain" / "agc" / "enable").set(enable); + } else { + UHD_MSG(warning) << "AGC is not available on this device." << std::endl; + } + return; + } + for (size_t c = 0; c < get_rx_num_channels(); c++){ + this->set_rx_agc(enable, c); + } + + } + double get_rx_gain(const std::string &name, size_t chan){ try { return rx_gain_group(chan)->get_value(name); @@ -836,6 +892,21 @@ public: } } + double get_normalized_rx_gain(size_t chan) + { + gain_range_t gain_range = get_rx_gain_range(ALL_GAINS, chan); + double gain_range_width = gain_range.stop() - gain_range.start(); + // In case we have a device without a range of gains: + if (gain_range_width == 0.0) { + return 0; + } + double norm_gain = (get_rx_gain(ALL_GAINS, chan) - gain_range.start()) / gain_range_width; + // Avoid rounding errors: + if (norm_gain > 1.0) return 1.0; + if (norm_gain < 0.0) return 0.0; + return norm_gain; + } + gain_range_t get_rx_gain_range(const std::string &name, size_t chan){ try { return rx_gain_group(chan)->get_range(name); @@ -888,6 +959,9 @@ public: if (chan != ALL_CHANS){ if (_tree->exists(rx_fe_root(chan) / "dc_offset" / "enable")) { _tree->access<bool>(rx_fe_root(chan) / "dc_offset" / "enable").set(enb); + } else if (_tree->exists(rx_rf_fe_root(chan) / "dc_offset" / "enable")) { + /*For B2xx devices the dc-offset correction is implemented in the rf front-end*/ + _tree->access<bool>(rx_rf_fe_root(chan) / "dc_offset" / "enable").set(enb); } else { UHD_MSG(warning) << "Setting DC offset compensation is not possible on this device." << std::endl; } @@ -912,6 +986,20 @@ public: } } + void set_rx_iq_balance(const bool enb, size_t chan){ + if (chan != ALL_CHANS){ + if (_tree->exists(rx_rf_fe_root(chan) / "iq_balance" / "enable")) { + _tree->access<bool>(rx_rf_fe_root(chan) / "iq_balance" / "enable").set(enb); + } else { + UHD_MSG(warning) << "Setting IQ imbalance compensation is not possible on this device." << std::endl; + } + return; + } + for (size_t c = 0; c < get_rx_num_channels(); c++){ + this->set_rx_iq_balance(enb, c); + } + } + void set_rx_iq_balance(const std::complex<double> &offset, size_t chan){ if (chan != ALL_CHANS){ if (_tree->exists(rx_fe_root(chan) / "iq_balance" / "value")) { @@ -926,6 +1014,87 @@ public: } } + std::vector<std::string> get_filter_names(const std::string &search_mask) + { + std::vector<std::string> ret; + + for (size_t chan = 0; chan < get_rx_num_channels(); chan++){ + + if (_tree->exists(rx_rf_fe_root(chan) / "filters")) { + std::vector<std::string> names = _tree->list(rx_rf_fe_root(chan) / "filters"); + for(size_t i = 0; i < names.size(); i++) + { + std::string name = rx_rf_fe_root(chan) / "filters" / names[i]; + if((search_mask.empty()) or boost::contains(name, search_mask)) { + ret.push_back(name); + } + } + } + if (_tree->exists(rx_dsp_root(chan) / "filters")) { + std::vector<std::string> names = _tree->list(rx_dsp_root(chan) / "filters"); + for(size_t i = 0; i < names.size(); i++) + { + std::string name = rx_dsp_root(chan) / "filters" / names[i]; + if((search_mask.empty()) or (boost::contains(name, search_mask))) { + ret.push_back(name); + } + } + } + + } + + for (size_t chan = 0; chan < get_tx_num_channels(); chan++){ + + if (_tree->exists(tx_rf_fe_root(chan) / "filters")) { + std::vector<std::string> names = _tree->list(tx_rf_fe_root(chan) / "filters"); + for(size_t i = 0; i < names.size(); i++) + { + std::string name = tx_rf_fe_root(chan) / "filters" / names[i]; + if((search_mask.empty()) or (boost::contains(name, search_mask))) { + ret.push_back(name); + } + } + } + if (_tree->exists(rx_dsp_root(chan) / "filters")) { + std::vector<std::string> names = _tree->list(tx_dsp_root(chan) / "filters"); + for(size_t i = 0; i < names.size(); i++) + { + std::string name = tx_dsp_root(chan) / "filters" / names[i]; + if((search_mask.empty()) or (boost::contains(name, search_mask))) { + ret.push_back(name); + } + } + } + + } + + return ret; + } + + filter_info_base::sptr get_filter(const std::string &path) + { + std::vector<std::string> possible_names = get_filter_names(""); + std::vector<std::string>::iterator it; + it = find(possible_names.begin(), possible_names.end(), path); + if (it == possible_names.end()) { + throw uhd::runtime_error("Attempting to get non-existing filter: "+path); + } + + return _tree->access<filter_info_base::sptr>(path / "value").get(); + } + + void set_filter(const std::string &path, filter_info_base::sptr filter) + { + std::vector<std::string> possible_names = get_filter_names(""); + std::vector<std::string>::iterator it; + it = find(possible_names.begin(), possible_names.end(), path); + if (it == possible_names.end()) { + throw uhd::runtime_error("Attempting to set non-existing filter: "+path); + } + + _tree->access<filter_info_base::sptr>(path / "value").set(filter); + } + /******************************************************************* * TX methods ******************************************************************/ @@ -1001,7 +1170,7 @@ public: _tree->subtree(tx_dsp_root(chan)), _tree->subtree(tx_rf_fe_root(chan)), tune_request); - do_tune_freq_results_message(tune_request, result, get_tx_freq(chan), "TX"); + //do_tune_freq_results_message(tune_request, result, get_tx_freq(chan), "TX"); return result; } @@ -1029,6 +1198,17 @@ public: } } + void set_normalized_tx_gain(double gain, size_t chan = 0) + { + if (gain > 1.0 || gain < 0.0) { + throw uhd::runtime_error("Normalized gain out of range, must be in [0, 1]."); + } + gain_range_t gain_range = get_tx_gain_range(ALL_GAINS, chan); + double abs_gain = (gain * (gain_range.stop() - gain_range.start())) + gain_range.start(); + set_tx_gain(abs_gain, ALL_GAINS, chan); + } + + double get_tx_gain(const std::string &name, size_t chan){ try { return tx_gain_group(chan)->get_value(name); @@ -1037,6 +1217,21 @@ public: } } + double get_normalized_tx_gain(size_t chan) + { + gain_range_t gain_range = get_tx_gain_range(ALL_GAINS, chan); + double gain_range_width = gain_range.stop() - gain_range.start(); + // In case we have a device without a range of gains: + if (gain_range_width == 0.0) { + return 0.0; + } + double norm_gain = (get_rx_gain(ALL_GAINS, chan) - gain_range.start()) / gain_range_width; + // Avoid rounding errors: + if (norm_gain > 1.0) return 1.0; + if (norm_gain < 0.0) return 0.0; + return norm_gain; + } + gain_range_t get_tx_gain_range(const std::string &name, size_t chan){ try { return tx_gain_group(chan)->get_range(name); @@ -1181,6 +1376,121 @@ public: return 0; } + void write_register(const std::string &path, const boost::uint32_t field, const boost::uint64_t value, const size_t mboard) + { + if (_tree->exists(mb_root(mboard) / "registers")) + { + uhd::soft_regmap_accessor_t::sptr accessor = + _tree->access<uhd::soft_regmap_accessor_t::sptr>(mb_root(mboard) / "registers").get(); + uhd::soft_register_base& reg = accessor->lookup(path); + + if (not reg.is_writable()) { + throw uhd::runtime_error("multi_usrp::write_register - register not writable: " + path); + } + + switch (reg.get_bitwidth()) { + case 16: + if (reg.is_readable()) + uhd::soft_register_base::cast<uhd::soft_reg16_rw_t>(reg).write(field, static_cast<boost::uint16_t>(value)); + else + uhd::soft_register_base::cast<uhd::soft_reg16_wo_t>(reg).write(field, static_cast<boost::uint16_t>(value)); + break; + + case 32: + if (reg.is_readable()) + uhd::soft_register_base::cast<uhd::soft_reg32_rw_t>(reg).write(field, static_cast<boost::uint32_t>(value)); + else + uhd::soft_register_base::cast<uhd::soft_reg32_wo_t>(reg).write(field, static_cast<boost::uint32_t>(value)); + break; + + case 64: + if (reg.is_readable()) + uhd::soft_register_base::cast<uhd::soft_reg64_rw_t>(reg).write(field, value); + else + uhd::soft_register_base::cast<uhd::soft_reg64_wo_t>(reg).write(field, value); + break; + + default: + throw uhd::assertion_error("multi_usrp::write_register - register has invalid bitwidth"); + } + + } else { + throw uhd::not_implemented_error("multi_usrp::write_register - register IO not supported for this device"); + } + } + + boost::uint64_t read_register(const std::string &path, const boost::uint32_t field, const size_t mboard) + { + if (_tree->exists(mb_root(mboard) / "registers")) + { + uhd::soft_regmap_accessor_t::sptr accessor = + _tree->access<uhd::soft_regmap_accessor_t::sptr>(mb_root(mboard) / "registers").get(); + uhd::soft_register_base& reg = accessor->lookup(path); + + if (not reg.is_readable()) { + throw uhd::runtime_error("multi_usrp::read_register - register not readable: " + path); + } + + switch (reg.get_bitwidth()) { + case 16: + if (reg.is_writable()) + return static_cast<boost::uint64_t>(uhd::soft_register_base::cast<uhd::soft_reg16_rw_t>(reg).read(field)); + else + return static_cast<boost::uint64_t>(uhd::soft_register_base::cast<uhd::soft_reg16_ro_t>(reg).read(field)); + break; + + case 32: + if (reg.is_writable()) + return static_cast<boost::uint64_t>(uhd::soft_register_base::cast<uhd::soft_reg32_rw_t>(reg).read(field)); + else + return static_cast<boost::uint64_t>(uhd::soft_register_base::cast<uhd::soft_reg32_ro_t>(reg).read(field)); + break; + + case 64: + if (reg.is_writable()) + return uhd::soft_register_base::cast<uhd::soft_reg64_rw_t>(reg).read(field); + else + return uhd::soft_register_base::cast<uhd::soft_reg64_ro_t>(reg).read(field); + break; + + default: + throw uhd::assertion_error("multi_usrp::read_register - register has invalid bitwidth: " + path); + } + } else { + throw uhd::not_implemented_error("multi_usrp::read_register - register IO not supported for this device"); + } + } + + std::vector<std::string> enumerate_registers(const size_t mboard) + { + if (_tree->exists(mb_root(mboard) / "registers")) + { + uhd::soft_regmap_accessor_t::sptr accessor = + _tree->access<uhd::soft_regmap_accessor_t::sptr>(mb_root(mboard) / "registers").get(); + return accessor->enumerate(); + } else { + return std::vector<std::string>(); + } + } + + register_info_t get_register_info(const std::string &path, const size_t mboard = 0) + { + if (_tree->exists(mb_root(mboard) / "registers")) + { + uhd::soft_regmap_accessor_t::sptr accessor = + _tree->access<uhd::soft_regmap_accessor_t::sptr>(mb_root(mboard) / "registers").get(); + uhd::soft_register_base& reg = accessor->lookup(path); + + register_info_t info; + info.bitwidth = reg.get_bitwidth(); + info.readable = reg.is_readable(); + info.writable = reg.is_writable(); + return info; + } else { + throw uhd::not_implemented_error("multi_usrp::read_register - register IO not supported for this device"); + } + } + private: device::sptr _dev; property_tree::sptr _tree; diff --git a/host/lib/usrp/subdev_spec_c.cpp b/host/lib/usrp/subdev_spec_c.cpp new file mode 100644 index 000000000..2c9c20506 --- /dev/null +++ b/host/lib/usrp/subdev_spec_c.cpp @@ -0,0 +1,149 @@ +// +// Copyright 2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/usrp/subdev_spec.h> + +#include <string.h> + +uhd_error uhd_subdev_spec_pair_free( + uhd_subdev_spec_pair_t *subdev_spec_pair +){ + UHD_SAFE_C( + if(subdev_spec_pair->db_name){ + free(subdev_spec_pair->db_name); + subdev_spec_pair->db_name = NULL; + } + if(subdev_spec_pair->sd_name){ + free(subdev_spec_pair->sd_name); + subdev_spec_pair->sd_name = NULL; + } + ) +} + +uhd_error uhd_subdev_spec_pairs_equal( + const uhd_subdev_spec_pair_t* first, + const uhd_subdev_spec_pair_t* second, + bool *result_out +){ + UHD_SAFE_C( + *result_out = (uhd_subdev_spec_pair_c_to_cpp(first) == + uhd_subdev_spec_pair_c_to_cpp(second)); + ) +} + +uhd_error uhd_subdev_spec_make( + uhd_subdev_spec_handle* h, + const char* markup +){ + UHD_SAFE_C( + (*h) = new uhd_subdev_spec_t; + std::string markup_cpp(markup); + if(!markup_cpp.empty()){ + (*h)->subdev_spec_cpp = uhd::usrp::subdev_spec_t(markup_cpp); + } + ) +} + +uhd_error uhd_subdev_spec_free( + uhd_subdev_spec_handle* h +){ + UHD_SAFE_C( + delete (*h); + (*h) = NULL; + ) +} + +uhd_error uhd_subdev_spec_size( + uhd_subdev_spec_handle h, + size_t *size_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *size_out = h->subdev_spec_cpp.size(); + ) +} + +uhd_error uhd_subdev_spec_push_back( + uhd_subdev_spec_handle h, + const char* markup +){ + UHD_SAFE_C_SAVE_ERROR(h, + h->subdev_spec_cpp.push_back(uhd::usrp::subdev_spec_pair_t(markup)); + ) +} + +uhd_error uhd_subdev_spec_at( + uhd_subdev_spec_handle h, + size_t num, + uhd_subdev_spec_pair_t *subdev_spec_pair_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd_subdev_spec_pair_cpp_to_c( + h->subdev_spec_cpp.at(num), + subdev_spec_pair_out + ); + ) +} + +uhd_error uhd_subdev_spec_to_pp_string( + uhd_subdev_spec_handle h, + char* pp_string_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string pp_string_cpp = h->subdev_spec_cpp.to_pp_string(); + memset(pp_string_out, '\0', strbuffer_len); + strncpy(pp_string_out, pp_string_cpp.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_subdev_spec_to_string( + uhd_subdev_spec_handle h, + char* string_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string string_cpp = h->subdev_spec_cpp.to_string(); + memset(string_out, '\0', strbuffer_len); + strncpy(string_out, string_cpp.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_subdev_spec_last_error( + uhd_subdev_spec_handle h, + char* error_out, + size_t strbuffer_len +){ + UHD_SAFE_C( + memset(error_out, '\0', strbuffer_len); + strncpy(error_out, h->last_error.c_str(), strbuffer_len); + ) +} + +uhd::usrp::subdev_spec_pair_t uhd_subdev_spec_pair_c_to_cpp( + const uhd_subdev_spec_pair_t *subdev_spec_pair_c +){ + return uhd::usrp::subdev_spec_pair_t(subdev_spec_pair_c->db_name, + subdev_spec_pair_c->sd_name); +} + +void uhd_subdev_spec_pair_cpp_to_c( + const uhd::usrp::subdev_spec_pair_t &subdev_spec_pair_cpp, + uhd_subdev_spec_pair_t *subdev_spec_pair_c +){ + subdev_spec_pair_c->db_name = strdup(subdev_spec_pair_cpp.db_name.c_str()); + subdev_spec_pair_c->sd_name = strdup(subdev_spec_pair_cpp.sd_name.c_str()); +} diff --git a/host/lib/usrp/usrp1/CMakeLists.txt b/host/lib/usrp/usrp1/CMakeLists.txt index 70bebe502..47344e841 100644 --- a/host/lib/usrp/usrp1/CMakeLists.txt +++ b/host/lib/usrp/usrp1/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-2011,2015 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 @@ -22,7 +22,7 @@ ######################################################################## # Conditionally configure the USRP1 support ######################################################################## -LIBUHD_REGISTER_COMPONENT("USRP1" ENABLE_USRP1 ON "ENABLE_LIBUHD;ENABLE_USB" OFF) +LIBUHD_REGISTER_COMPONENT("USRP1" ENABLE_USRP1 ON "ENABLE_LIBUHD;ENABLE_USB" OFF OFF) IF(ENABLE_USRP1) LIBUHD_APPEND_SOURCES( diff --git a/host/lib/usrp/usrp2/CMakeLists.txt b/host/lib/usrp/usrp2/CMakeLists.txt index c6257c7fe..d9894adaf 100644 --- a/host/lib/usrp/usrp2/CMakeLists.txt +++ b/host/lib/usrp/usrp2/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2011-2012,2014 Ettus Research LLC +# Copyright 2011-2012,2014-2015 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 @@ -22,21 +22,9 @@ ######################################################################## # Conditionally configure the USRP2 support ######################################################################## -LIBUHD_REGISTER_COMPONENT("USRP2" ENABLE_USRP2 ON "ENABLE_LIBUHD" OFF) +LIBUHD_REGISTER_COMPONENT("USRP2" ENABLE_USRP2 ON "ENABLE_LIBUHD" OFF OFF) IF(ENABLE_USRP2) - ######################################################################## - # Define UHD_PKG_DATA_PATH for usrp2_iface.cpp - ######################################################################## - FILE(TO_NATIVE_PATH ${CMAKE_INSTALL_PREFIX} UHD_PKG_PATH) - STRING(REPLACE "\\" "\\\\" UHD_PKG_PATH ${UHD_PKG_PATH}) - - SET_SOURCE_FILES_PROPERTIES( - ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_iface.cpp - PROPERTIES COMPILE_DEFINITIONS - "UHD_LIB_DIR=\"lib${LIB_SUFFIX}\"" - ) - LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/clock_ctrl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/codec_ctrl.cpp @@ -45,5 +33,6 @@ IF(ENABLE_USRP2) ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_iface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_impl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_fifo_ctrl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/n200_image_loader.cpp ) ENDIF(ENABLE_USRP2) diff --git a/host/lib/usrp/usrp2/n200_image_loader.cpp b/host/lib/usrp/usrp2/n200_image_loader.cpp new file mode 100644 index 000000000..29bec8b4a --- /dev/null +++ b/host/lib/usrp/usrp2/n200_image_loader.cpp @@ -0,0 +1,646 @@ +// +// Copyright 2015 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 <cstring> +#include <iostream> +#include <fstream> + +#include <boost/asio/ip/address_v4.hpp> +#include <boost/assign.hpp> +#include <boost/filesystem.hpp> +#include <boost/format.hpp> +#include <boost/thread.hpp> +#include <boost/algorithm/string/erase.hpp> + +#include <uhd/config.hpp> +#include <uhd/image_loader.hpp> +#include <uhd/exception.hpp> +#include <uhd/transport/if_addrs.hpp> +#include <uhd/transport/udp_simple.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/paths.hpp> +#include <uhd/utils/static.hpp> +#include <uhd/types/dict.hpp> + +#include "fw_common.h" +#include "usrp2_iface.hpp" +#include "usrp2_impl.hpp" + +typedef boost::asio::ip::address_v4 ip_v4; + +namespace fs = boost::filesystem; +using namespace boost::algorithm; + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; + +/* + * Constants + */ + +#define N200_FLASH_DATA_PACKET_SIZE 256 +#define N200_UDP_FW_UPDATE_PORT 49154 +#define UDP_TIMEOUT 0.5 + +#define N200_FW_MAX_SIZE_BYTES 31744 +#define N200_PROD_FW_IMAGE_ADDR 0x00300000 +#define N200_SAFE_FW_IMAGE_ADDR 0x003F0000 + +#define N200_FPGA_MAX_SIZE_BYTES 1572864 +#define N200_PROD_FPGA_IMAGE_ADDR 0x00180000 +#define N200_SAFE_FPGA_IMAGE_ADDR 0x00000000 + +/* + * Packet codes + */ +typedef enum { + UNKNOWN = ' ', + + N200_QUERY = 'a', + N200_ACK = 'A', + + GET_FLASH_INFO_CMD = 'f', + GET_FLASH_INFO_ACK = 'F', + + ERASE_FLASH_CMD = 'e', + ERASE_FLASH_ACK = 'E', + + CHECK_ERASING_DONE_CMD = 'd', + DONE_ERASING_ACK = 'D', + NOT_DONE_ERASING_ACK = 'B', + + WRITE_FLASH_CMD = 'w', + WRITE_FLASH_ACK = 'W', + + READ_FLASH_CMD = 'r', + READ_FLASH_ACK = 'R', + + RESET_CMD = 's', + RESET_ACK = 'S', + + GET_HW_REV_CMD = 'v', + GET_HW_REV_ACK = 'V', +} n200_fw_update_id_t; + +/* + * Mapping revision numbers to names + */ +static const uhd::dict<boost::uint32_t, std::string> n200_filename_map = boost::assign::map_list_of + (0, "n2xx") // Is an N-Series, but the EEPROM value is invalid + (0xa, "n200_r3") + (0x100a, "n200_r4") + (0x10a, "n210_r3") + (0x110a, "n210_r4") +; + +/* + * Packet structure + */ +typedef struct { + boost::uint32_t proto_ver; + boost::uint32_t id; + boost::uint32_t seq; + union { + boost::uint32_t ip_addr; + boost::uint32_t hw_rev; + struct { + boost::uint32_t flash_addr; + boost::uint32_t length; + boost::uint8_t data[256]; + } flash_args; + struct { + boost::uint32_t sector_size_bytes; + boost::uint32_t memory_size_bytes; + } flash_info_args; + } data; +} n200_fw_update_data_t; + +/* + * N-Series burn session + */ +typedef struct { + bool fw; + bool overwrite_safe; + bool reset; + uhd::device_addr_t dev_addr; + std::string burn_type; + std::string filepath; + boost::uint8_t data_in[udp_simple::mtu]; + boost::uint32_t size; + boost::uint32_t max_size; + boost::uint32_t flash_addr; + udp_simple::sptr xport; +} n200_session_t; + +/*********************************************************************** + * uhd::image_loader functionality + **********************************************************************/ + +static void print_usrp2_error(const image_loader::image_loader_args_t &image_loader_args){ + #ifdef UHD_PLATFORM_WIN32 + std::string usrp2_card_burner_gui = "\""; + const std::string nl = " ^\n "; + #else + std::string usrp2_card_burner_gui = "sudo \""; + const std::string nl = " \\\n "; + #endif + + usrp2_card_burner_gui += find_utility("usrp2_card_burner_gui.py"); + usrp2_card_burner_gui += "\""; + + if(image_loader_args.load_firmware){ + usrp2_card_burner_gui += str(boost::format("%s--fw=\"%s\"") + % nl + % ((image_loader_args.firmware_path == "") + ? find_image_path("usrp2_fw.bin") + : image_loader_args.firmware_path)); + } + if(image_loader_args.load_fpga){ + usrp2_card_burner_gui += str(boost::format("%s--fpga=\"%s\"") + % nl + % ((image_loader_args.fpga_path == "") + ? find_image_path("usrp2_fpga.bin") + : image_loader_args.fpga_path)); + } + + throw uhd::runtime_error(str(boost::format("The specified device is a USRP2, which is not supported by this utility.\n" + "Instead, plug the device's SD card into your machine and run this command:\n\n" + "%s" + ) % usrp2_card_burner_gui)); +} + +/* + * Ethernet communication functions + */ +static UHD_INLINE size_t n200_send_and_recv(udp_simple::sptr xport, + n200_fw_update_id_t pkt_code, + n200_fw_update_data_t *pkt_out, + boost::uint8_t* data){ + pkt_out->proto_ver = htonx<boost::uint32_t>(USRP2_FW_COMPAT_NUM); + pkt_out->id = htonx<boost::uint32_t>(pkt_code); + xport->send(boost::asio::buffer(pkt_out, sizeof(*pkt_out))); + return xport->recv(boost::asio::buffer(data, udp_simple::mtu), UDP_TIMEOUT); +} + +static UHD_INLINE bool n200_response_matches(const n200_fw_update_data_t *pkt_in, + n200_fw_update_id_t pkt_code, + size_t len){ + return (len > offsetof(n200_fw_update_data_t, data) and + ntohl(pkt_in->id) == pkt_code); +} + +static uhd::device_addr_t n200_find(const image_loader::image_loader_args_t &image_loader_args){ + bool user_specified = image_loader_args.args.has_key("addr") or + image_loader_args.args.has_key("serial") or + image_loader_args.args.has_key("name"); + + uhd::device_addrs_t found = usrp2_find(image_loader_args.args); + + if(found.size() > 0){ + uhd::device_addrs_t n200_found; + udp_simple::sptr rev_xport; + n200_fw_update_data_t pkt_out; + boost::uint8_t data_in[udp_simple::mtu]; + const n200_fw_update_data_t *pkt_in = reinterpret_cast<const n200_fw_update_data_t*>(data_in); + size_t len = 0; + + /* + * Filter out any USRP2 devices by sending a query over the + * UDP update port. Only N-Series devices will respond to + * this query. If the user supplied specific arguments that + * led to a USRP2, throw an error. + */ + BOOST_FOREACH(const uhd::device_addr_t &dev, found){ + rev_xport = udp_simple::make_connected( + dev.get("addr"), + BOOST_STRINGIZE(N200_UDP_FW_UPDATE_PORT) + ); + + len = n200_send_and_recv(rev_xport, GET_HW_REV_CMD, &pkt_out, data_in); + if(n200_response_matches(pkt_in, GET_HW_REV_ACK, len)){ + boost::uint32_t rev = ntohl(pkt_in->data.hw_rev); + std::string hw_rev = n200_filename_map.get(rev, "n2xx"); + + n200_found.push_back(dev); + n200_found[n200_found.size()-1]["hw_rev"] = hw_rev; + } + else if(len > offsetof(n200_fw_update_data_t, data) and ntohl(pkt_in->id) != GET_HW_REV_ACK){ + throw uhd::runtime_error(str(boost::format("Received invalid reply %d from device.") + % ntohl(pkt_in->id))); + } + else if(user_specified){ + // At this point, we haven't received any response, so assume it's a USRP2 + print_usrp2_error(image_loader_args); + } + } + + // At this point, we should have a single N-Series device + if(n200_found.size() == 1){ + return n200_found[0]; + } + else if(n200_found.size() > 1){ + std::string err_msg = "Could not resolve given args to a single N-Series device.\n" + "Applicable devices:\n"; + + BOOST_FOREACH(const uhd::device_addr_t &dev, n200_found){ + err_msg += str(boost::format("* %s (addr=%s)\n") + % dev.get("hw_rev") + % dev.get("addr")); + } + + err_msg += "\nSpecify one of these devices with the given args to load an image onto it."; + + throw uhd::runtime_error(err_msg); + } + } + + return uhd::device_addr_t(); +} + +/* + * Validate and read firmware image + */ +static void n200_validate_firmware_image(n200_session_t &session){ + if(not fs::exists(session.filepath)){ + throw uhd::runtime_error(str(boost::format("Could not find image at path \"%s\".") + % session.filepath)); + } + + session.size = fs::file_size(session.filepath); + session.max_size = N200_FW_MAX_SIZE_BYTES; + + if(session.size > session.max_size){ + throw uhd::runtime_error(str(boost::format("The specified firmware image is too large: %d vs. %d") + % session.size % session.max_size)); + } + + // File must have proper header + std::ifstream image_file(session.filepath.c_str(), std::ios::binary); + boost::uint8_t test_bytes[4]; + image_file.seekg(0, std::ios::beg); + image_file.read((char*)test_bytes,4); + image_file.close(); + for(int i = 0; i < 4; i++) if(test_bytes[i] != 11){ + throw uhd::runtime_error(str(boost::format("The file at path \"%s\" is not a valid firmware image.") + % session.filepath)); + } +} + +/* + * Validate and validate FPGA image + */ +static void n200_validate_fpga_image(n200_session_t &session){ + if(not fs::exists(session.filepath)){ + throw uhd::runtime_error(str(boost::format("Could not find image at path \"%s\".") + % session.filepath)); + } + + session.size = fs::file_size(session.filepath); + session.max_size = N200_FPGA_MAX_SIZE_BYTES; + + if(session.size > session.max_size){ + throw uhd::runtime_error(str(boost::format("The specified FPGA image is too large: %d vs. %d") + % session.size % session.max_size)); + } + + // File must have proper header + std::ifstream image_file(session.filepath.c_str(), std::ios::binary); + boost::uint8_t test_bytes[63]; + image_file.seekg(0, std::ios::beg); + image_file.read((char*)test_bytes, 63); + bool is_good = false; + for(int i = 0; i < 63; i++){ + if(test_bytes[i] == 255) continue; + else if(test_bytes[i] == 170 and + test_bytes[i+1] == 153){ + is_good = true; + break; + } + } + image_file.close(); + if(not is_good){ + throw uhd::runtime_error(str(boost::format("The file at path \"%s\" is not a valid FPGA image.") + % session.filepath)); + } +} + +/* + * Set up a session for burning an N-Series image. This session info + * will be passed into the erase, burn, and verify functions. + */ +static void n200_setup_session(n200_session_t &session, + const image_loader::image_loader_args_t &image_loader_args, + bool fw){ + + + session.fw = fw; + session.reset = image_loader_args.args.has_key("reset"); + + /* + * If no filepath is given, attempt to determine the default image by + * querying the device for its revision. If the device has a corrupt + * EEPROM or is otherwise unable to provide its revision, this is + * impossible, and the user must manually provide a firmware file. + */ + if((session.fw and image_loader_args.firmware_path == "") or + image_loader_args.fpga_path == ""){ + if(session.dev_addr["hw_rev"] == "n2xx"){ + throw uhd::runtime_error("This device's revision cannot be determined. " + "You must manually specify a filepath."); + } + else{ + session.filepath = session.fw ? find_image_path(str(boost::format("usrp_%s_fw.bin") + % erase_tail_copy(session.dev_addr["hw_rev"],3))) + : find_image_path(str(boost::format("usrp_%s_fpga.bin") + % session.dev_addr["hw_rev"])); + } + } + else{ + session.filepath = session.fw ? image_loader_args.firmware_path + : image_loader_args.fpga_path; + } + if(session.fw) n200_validate_firmware_image(session); + else n200_validate_fpga_image(session); + + session.overwrite_safe = image_loader_args.args.has_key("overwrite-safe"); + if(session.overwrite_safe){ + session.flash_addr = session.fw ? N200_SAFE_FW_IMAGE_ADDR + : N200_SAFE_FPGA_IMAGE_ADDR; + session.burn_type = session.fw ? "firmware safe" + : "FPGA safe"; + } + else{ + session.flash_addr = session.fw ? N200_PROD_FW_IMAGE_ADDR + : N200_PROD_FPGA_IMAGE_ADDR; + session.burn_type = session.fw ? "firmware" + : "FPGA"; + } + + session.xport = udp_simple::make_connected(session.dev_addr["addr"], + BOOST_STRINGIZE(N200_UDP_FW_UPDATE_PORT)); +} + +static void n200_erase_image(n200_session_t &session){ + + // UDP receive buffer + n200_fw_update_data_t pkt_out; + const n200_fw_update_data_t *pkt_in = reinterpret_cast<const n200_fw_update_data_t*>(session.data_in); + + // Setting up UDP packet + pkt_out.data.flash_args.flash_addr = htonx<boost::uint32_t>(session.flash_addr); + pkt_out.data.flash_args.length = htonx<boost::uint32_t>(session.size); + + // Begin erasing + size_t len = n200_send_and_recv(session.xport, ERASE_FLASH_CMD, &pkt_out, session.data_in); + if(n200_response_matches(pkt_in, ERASE_FLASH_ACK, len)){ + std::cout << boost::format("-- Erasing %s image...") % session.burn_type << std::flush; + } + else if(len < offsetof(n200_fw_update_data_t, data)){ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Timed out waiting for reply from device."); + } + else if(ntohl(pkt_in->id) != ERASE_FLASH_ACK){ + std::cout << "failed." << std::endl; + throw uhd::runtime_error(str(boost::format("Received invalid reply %d from device.\n") + % ntohl(pkt_in->id))); + } + else{ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Did not receive response from device."); + } + + // Check for erase completion + while(true){ + len = n200_send_and_recv(session.xport, CHECK_ERASING_DONE_CMD, &pkt_out, session.data_in); + if(n200_response_matches(pkt_in, DONE_ERASING_ACK, len)){ + std::cout << "successful." << std::endl; + break; + } + else if(len < offsetof(n200_fw_update_data_t, data)){ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Timed out waiting for reply from device."); + } + else if(ntohl(pkt_in->id) != NOT_DONE_ERASING_ACK){ + std::cout << "failed." << std::endl; + throw uhd::runtime_error(str(boost::format("Received invalid reply %d from device.\n") + % ntohl(pkt_in->id))); + } + } +} + +static void n200_write_image(n200_session_t &session){ + + // UDP receive buffer + n200_fw_update_data_t pkt_out; + const n200_fw_update_data_t *pkt_in = reinterpret_cast<const n200_fw_update_data_t*>(session.data_in); + size_t len = 0; + + // Write image + std::ifstream image(session.filepath.c_str(), std::ios::binary); + boost::uint32_t current_addr = session.flash_addr; + pkt_out.data.flash_args.length = htonx<boost::uint32_t>(N200_FLASH_DATA_PACKET_SIZE); + for(size_t i = 0; i < ((session.size/N200_FLASH_DATA_PACKET_SIZE)+1); i++){ + pkt_out.data.flash_args.flash_addr = htonx<boost::uint32_t>(current_addr); + memset(pkt_out.data.flash_args.data, 0x0, N200_FLASH_DATA_PACKET_SIZE); + image.read((char*)pkt_out.data.flash_args.data, N200_FLASH_DATA_PACKET_SIZE); + + len = n200_send_and_recv(session.xport, WRITE_FLASH_CMD, &pkt_out, session.data_in); + if(n200_response_matches(pkt_in, WRITE_FLASH_ACK, len)){ + std::cout << boost::format("\r-- Writing %s image (%d%%)") + % session.burn_type + % int((double(current_addr-session.flash_addr)/double(session.size))*100) + << std::flush; + } + else if(len < offsetof(n200_fw_update_data_t, data)){ + image.close(); + std::cout << boost::format("\r--Writing %s image..failed at %d%%.") + % session.burn_type + % int((double(current_addr-session.flash_addr)/double(session.size))*100) + << std::endl; + throw uhd::runtime_error("Timed out waiting for reply from device."); + } + else if(ntohl(pkt_in->id) != WRITE_FLASH_ACK){ + image.close(); + std::cout << boost::format("\r--Writing %s image..failed at %d%%.") + % session.burn_type + % int((double(current_addr-session.flash_addr)/double(session.size))*100) + << std::endl; + throw uhd::runtime_error(str(boost::format("Received invalid reply %d from device.\n") + % ntohl(pkt_in->id))); + } + + current_addr += N200_FLASH_DATA_PACKET_SIZE; + } + std::cout << boost::format("\r-- Writing %s image...successful.") + % session.burn_type + << std::endl; + + image.close(); +} + +static void n200_verify_image(n200_session_t &session){ + + // UDP receive buffer + n200_fw_update_data_t pkt_out; + const n200_fw_update_data_t *pkt_in = reinterpret_cast<const n200_fw_update_data_t*>(session.data_in); + size_t len = 0; + + // Read and verify image + std::ifstream image(session.filepath.c_str(), std::ios::binary); + boost::uint8_t image_part[N200_FLASH_DATA_PACKET_SIZE]; + boost::uint32_t current_addr = session.flash_addr; + pkt_out.data.flash_args.length = htonx<boost::uint32_t>(N200_FLASH_DATA_PACKET_SIZE); + boost::uint16_t cmp_len = 0; + for(size_t i = 0; i < ((session.size/N200_FLASH_DATA_PACKET_SIZE)+1); i++){ + memset(image_part, 0x0, N200_FLASH_DATA_PACKET_SIZE); + memset((void*)pkt_in->data.flash_args.data, 0x0, N200_FLASH_DATA_PACKET_SIZE); + + pkt_out.data.flash_args.flash_addr = htonx<boost::uint32_t>(current_addr); + image.read((char*)image_part, N200_FLASH_DATA_PACKET_SIZE); + cmp_len = image.gcount(); + + len = n200_send_and_recv(session.xport, READ_FLASH_CMD, &pkt_out, session.data_in); + if(n200_response_matches(pkt_in, READ_FLASH_ACK, len)){ + std::cout << boost::format("\r-- Verifying %s image (%d%%)") + % session.burn_type + % int((double(current_addr-session.flash_addr)/double(session.size))*100) + << std::flush; + + if(memcmp(image_part, pkt_in->data.flash_args.data, cmp_len)){ + std::cout << boost::format("\r-- Verifying %s image...failed at %d%%.") + % session.burn_type + % int((double(current_addr-session.flash_addr)/double(session.size))*100) + << std::endl; + throw uhd::runtime_error(str(boost::format("Failed to verify %s image.") + % session.burn_type)); + } + } + else if(len < offsetof(n200_fw_update_data_t, data)){ + image.close(); + std::cout << boost::format("\r-- Verifying %s image...failed at %d%%.") + % session.burn_type + % int((double(current_addr-session.flash_addr)/double(session.size))*100) + << std::endl; + throw uhd::runtime_error("Timed out waiting for reply from device."); + } + else if(ntohl(pkt_in->id) != READ_FLASH_ACK){ + image.close(); + std::cout << boost::format("\r-- Verifying %s image...failed at %d%%.") + % session.burn_type + % int((double(current_addr-session.flash_addr)/double(session.size))*100) + << std::endl; + throw uhd::runtime_error(str(boost::format("Received invalid reply %d from device.\n") + % ntohl(pkt_in->id))); + } + + current_addr += N200_FLASH_DATA_PACKET_SIZE; + } + std::cout << boost::format("\r-- Verifying %s image...successful.") % session.burn_type + << std::endl; + + image.close(); +} + +static void n200_reset(n200_session_t &session){ + + // UDP receive buffer + n200_fw_update_data_t pkt_out; + + // There should be no response + std::cout << "-- Resetting device..." << std::flush; + size_t len = n200_send_and_recv(session.xport, RESET_CMD, &pkt_out, session.data_in); + if(len > 0){ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Failed to reset N200."); + } + std::cout << "successful." << std::endl; +} + +// n210_r4 -> N210 r4 +static std::string nice_name(const std::string &fw_rev){ + std::string ret = fw_rev; + ret[0] = ::toupper(ret[0]); + + size_t pos = 0; + if((pos = fw_rev.find("_")) != std::string::npos){ + ret[pos] = ' '; + } + + return ret; +} + +static bool n200_image_loader(const image_loader::image_loader_args_t &image_loader_args){ + if(!image_loader_args.load_firmware and !image_loader_args.load_fpga){ + return false; + } + + // See if any N2x0 with the given args is found + // This will throw if specific args lead to a USRP2 + n200_session_t session; + session.dev_addr = n200_find(image_loader_args); + if(session.dev_addr.size() == 0){ + return false; + } + + std::cout << boost::format("Unit: USRP %s (%s, %s)") + % nice_name(session.dev_addr.get("hw_rev")) + % session.dev_addr.get("serial") + % session.dev_addr.get("addr") + << std::endl; + + if(image_loader_args.load_firmware){ + n200_setup_session(session, + image_loader_args, + true + ); + + std::cout << "Firmware image: " << session.filepath << std::endl; + + n200_erase_image(session); + n200_write_image(session); + n200_verify_image(session); + if(session.reset and !image_loader_args.load_fpga){ + n200_reset(session); + } + } + if(image_loader_args.load_fpga){ + n200_setup_session(session, + image_loader_args, + false + ); + + std::cout << "FPGA image: " << session.filepath << std::endl; + + n200_erase_image(session); + n200_write_image(session); + n200_verify_image(session); + if(session.reset){ + n200_reset(session); + } + } + + return true; +} + +UHD_STATIC_BLOCK(register_n200_image_loader){ + std::string recovery_instructions = "Aborting. Your USRP-N Series unit will likely be unusable.\n" + "Refer to http://files.ettus.com/manual/page_usrp2.html#usrp2_loadflash_brick\n" + "for details on restoring your device."; + + image_loader::register_image_loader("usrp2", n200_image_loader, recovery_instructions); +} diff --git a/host/lib/usrp/usrp2/usrp2_iface.cpp b/host/lib/usrp/usrp2/usrp2_iface.cpp index 1d41173f8..2b382ae38 100644 --- a/host/lib/usrp/usrp2/usrp2_iface.cpp +++ b/host/lib/usrp/usrp2/usrp2_iface.cpp @@ -387,15 +387,15 @@ public: //create the burner commands if (this->get_rev() == USRP2_REV3 or this->get_rev() == USRP2_REV4){ - const std::string card_burner = (fs::path(uhd::get_pkg_path()) / UHD_LIB_DIR / "uhd" / "utils" / "usrp2_card_burner_gui.py").string(); - const std::string card_burner_cmd = str(boost::format("\"%s%s\" %s--fpga=\"%s\" %s--fw=\"%s\"") % sudo % card_burner % ml % fpga_image_path % ml % fw_image_path); + const std::string card_burner = uhd::find_utility("usrp2_card_burner_gui.py"); + const std::string card_burner_cmd = str(boost::format(" %s\"%s\" %s--fpga=\"%s\" %s--fw=\"%s\"") % sudo % card_burner % ml % fpga_image_path % ml % fw_image_path); return str(boost::format("%s\n%s") % print_utility_error("uhd_images_downloader.py") % card_burner_cmd); } else{ const std::string addr = _ctrl_transport->get_recv_addr(); - const std::string net_burner_path = (fs::path(uhd::get_pkg_path()) / UHD_LIB_DIR / "uhd" / "utils" / "usrp_n2xx_simple_net_burner").string(); - const std::string net_burner_cmd = str(boost::format("\"%s\" %s--addr=\"%s\"") % net_burner_path % ml % addr); - return str(boost::format("%s\n%s") % print_utility_error("uhd_images_downloader.py") % net_burner_cmd); + const std::string image_loader_path = (fs::path(uhd::get_pkg_path()) / "bin" / "uhd_image_loader").string(); + const std::string image_loader_cmd = str(boost::format(" \"%s\" %s--args=\"type=usrp2,addr=%s\"") % image_loader_path % ml % addr); + return str(boost::format("%s\n%s") % print_utility_error("uhd_images_downloader.py") % image_loader_cmd); } } diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index 1acc1dad3..7b59dfaf1 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -48,7 +48,7 @@ static const size_t DEFAULT_NUM_FRAMES = 32; /*********************************************************************** * Discovery over the udp transport **********************************************************************/ -static device_addrs_t usrp2_find(const device_addr_t &hint_){ +device_addrs_t usrp2_find(const device_addr_t &hint_){ //handle the multi-device discovery device_addrs_t hints = separate_device_addr(hint_); if (hints.size() > 1){ @@ -782,9 +782,6 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr) : UHD_MSG(status) << "Setting references to the internal GPSDO" << std::endl; _tree->access<std::string>(root / "time_source/value").set("gpsdo"); _tree->access<std::string>(root / "clock_source/value").set("gpsdo"); - UHD_MSG(status) << "Initializing time to the internal GPSDO" << std::endl; - const time_t tp = time_t(_mbc[mb].gps->get_sensor("gps_time").to_int()+1); - _tree->access<time_spec_t>(root / "time" / "pps").set(time_spec_t(tp)); } } diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index 701403029..07cd98b4c 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -42,6 +42,7 @@ #include <uhd/transport/vrt_if_packet.hpp> #include <uhd/transport/udp_simple.hpp> #include <uhd/transport/udp_zero_copy.hpp> +#include <uhd/types/device_addr.hpp> #include <uhd/usrp/dboard_manager.hpp> #include <uhd/usrp/subdev_spec.hpp> #include <boost/weak_ptr.hpp> @@ -55,6 +56,8 @@ static const boost::uint32_t USRP2_TX_ASYNC_SID = 2; static const boost::uint32_t USRP2_RX_SID_BASE = 3; static const std::string USRP2_EEPROM_MAP_KEY = "N100"; +uhd::device_addrs_t usrp2_find(const uhd::device_addr_t &hint_); + //! Make a usrp2 dboard interface. uhd::usrp::dboard_iface::sptr make_usrp2_dboard_iface( uhd::timed_wb_iface::sptr wb_iface, diff --git a/host/lib/usrp/usrp_c.cpp b/host/lib/usrp/usrp_c.cpp new file mode 100644 index 000000000..69f2bd5e5 --- /dev/null +++ b/host/lib/usrp/usrp_c.cpp @@ -0,0 +1,1413 @@ +/* + * Copyright 2015 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/>. + */ + +/* C-Interface for multi_usrp */ + +#include <uhd/utils/static.hpp> +#include <uhd/usrp/multi_usrp.hpp> + +#include <uhd/error.h> +#include <uhd/usrp/usrp.h> + +#include <boost/foreach.hpp> +#include <boost/thread/mutex.hpp> + +#include <string.h> +#include <map> + +/**************************************************************************** + * Helpers + ***************************************************************************/ +uhd::stream_args_t stream_args_c_to_cpp(const uhd_stream_args_t *stream_args_c) +{ + std::string otw_format(stream_args_c->otw_format); + std::string cpu_format(stream_args_c->cpu_format); + std::string args(stream_args_c->args); + std::vector<size_t> channels(stream_args_c->channel_list, stream_args_c->channel_list + stream_args_c->n_channels); + + uhd::stream_args_t stream_args_cpp(cpu_format, otw_format); + stream_args_cpp.args = args; + stream_args_cpp.channels = channels; + + return stream_args_cpp; +} + +uhd::stream_cmd_t stream_cmd_c_to_cpp(const uhd_stream_cmd_t *stream_cmd_c) +{ + uhd::stream_cmd_t stream_cmd_cpp(uhd::stream_cmd_t::stream_mode_t(stream_cmd_c->stream_mode)); + stream_cmd_cpp.num_samps = stream_cmd_c->num_samps; + stream_cmd_cpp.stream_now = stream_cmd_c->stream_now; + stream_cmd_cpp.time_spec = uhd::time_spec_t(stream_cmd_c->time_spec_full_secs, stream_cmd_c->time_spec_frac_secs); + return stream_cmd_cpp; +} + +/**************************************************************************** + * Registry / Pointer Management + ***************************************************************************/ +/* Public structs */ +struct uhd_usrp { + size_t usrp_index; + std::string last_error; +}; + +struct uhd_tx_streamer { + size_t usrp_index; + size_t streamer_index; + std::string last_error; +}; + +struct uhd_rx_streamer { + size_t usrp_index; + size_t streamer_index; + std::string last_error; +}; + +/* Not public: We use this for our internal registry */ +struct usrp_ptr { + uhd::usrp::multi_usrp::sptr ptr; + std::vector< uhd::rx_streamer::sptr > rx_streamers; + std::vector< uhd::tx_streamer::sptr > tx_streamers; + static size_t usrp_counter; +}; +size_t usrp_ptr::usrp_counter = 0; +typedef struct usrp_ptr usrp_ptr; +/* Prefer map, because the list can be discontiguous */ +typedef std::map<size_t, usrp_ptr> usrp_ptrs; + +UHD_SINGLETON_FCN(usrp_ptrs, get_usrp_ptrs); +/* Shortcut for accessing the underlying USRP sptr from a uhd_usrp_handle* */ +#define USRP(h_ptr) (get_usrp_ptrs()[h_ptr->usrp_index].ptr) +#define RX_STREAMER(h_ptr) (get_usrp_ptrs()[h_ptr->usrp_index].rx_streamers[h_ptr->streamer_index]) +#define TX_STREAMER(h_ptr) (get_usrp_ptrs()[h_ptr->usrp_index].tx_streamers[h_ptr->streamer_index]) + +/**************************************************************************** + * RX Streamer + ***************************************************************************/ +static boost::mutex _rx_streamer_make_mutex; +uhd_error uhd_rx_streamer_make(uhd_rx_streamer_handle* h){ + UHD_SAFE_C( + boost::mutex::scoped_lock(_rx_streamer_make_mutex); + (*h) = new uhd_rx_streamer; + ) +} + +static boost::mutex _rx_streamer_free_mutex; +uhd_error uhd_rx_streamer_free(uhd_rx_streamer_handle* h){ + UHD_SAFE_C( + boost::mutex::scoped_lock lock(_rx_streamer_free_mutex); + delete (*h); + (*h) = NULL; + ) +} + +uhd_error uhd_rx_streamer_num_channels(uhd_rx_streamer_handle h, + size_t *num_channels_out){ + UHD_SAFE_C_SAVE_ERROR(h, + *num_channels_out = RX_STREAMER(h)->get_num_channels(); + ) +} + +uhd_error uhd_rx_streamer_max_num_samps(uhd_rx_streamer_handle h, + size_t *max_num_samps_out){ + UHD_SAFE_C_SAVE_ERROR(h, + *max_num_samps_out = RX_STREAMER(h)->get_max_num_samps(); + ) +} + +uhd_error uhd_rx_streamer_recv( + uhd_rx_streamer_handle h, + void **buffs, + size_t samps_per_buff, + uhd_rx_metadata_handle *md, + double timeout, + bool one_packet, + size_t *items_recvd +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::rx_streamer::buffs_type buffs_cpp(buffs, RX_STREAMER(h)->get_num_channels()); + *items_recvd = RX_STREAMER(h)->recv(buffs_cpp, samps_per_buff, (*md)->rx_metadata_cpp, timeout, one_packet); + ) +} + +uhd_error uhd_rx_streamer_issue_stream_cmd( + uhd_rx_streamer_handle h, + const uhd_stream_cmd_t *stream_cmd +){ + UHD_SAFE_C_SAVE_ERROR(h, + RX_STREAMER(h)->issue_stream_cmd(stream_cmd_c_to_cpp(stream_cmd)); + ) +} + +uhd_error uhd_rx_streamer_last_error( + uhd_rx_streamer_handle h, + char* error_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + memset(error_out, '\0', strbuffer_len); + strncpy(error_out, h->last_error.c_str(), strbuffer_len); + ) +} + +/**************************************************************************** + * TX Streamer + ***************************************************************************/ +static boost::mutex _tx_streamer_make_mutex; +uhd_error uhd_tx_streamer_make( + uhd_tx_streamer_handle* h +){ + UHD_SAFE_C( + boost::mutex::scoped_lock lock(_tx_streamer_make_mutex); + (*h) = new uhd_tx_streamer; + ) +} + +static boost::mutex _tx_streamer_free_mutex; +uhd_error uhd_tx_streamer_free( + uhd_tx_streamer_handle* h +){ + UHD_SAFE_C( + boost::mutex::scoped_lock lock(_tx_streamer_free_mutex); + delete *h; + *h = NULL; + ) +} + +uhd_error uhd_tx_streamer_num_channels( + uhd_tx_streamer_handle h, + size_t *num_channels_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *num_channels_out = TX_STREAMER(h)->get_num_channels(); + ) +} + +uhd_error uhd_tx_streamer_max_num_samps( + uhd_tx_streamer_handle h, + size_t *max_num_samps_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *max_num_samps_out = TX_STREAMER(h)->get_max_num_samps(); + ) +} + +uhd_error uhd_tx_streamer_send( + uhd_tx_streamer_handle h, + const void **buffs, + size_t samps_per_buff, + uhd_tx_metadata_handle *md, + double timeout, + size_t *items_sent +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::tx_streamer::buffs_type buffs_cpp(buffs, TX_STREAMER(h)->get_num_channels()); + *items_sent = TX_STREAMER(h)->send( + buffs_cpp, + samps_per_buff, + (*md)->tx_metadata_cpp, + timeout + ); + ) +} + +uhd_error uhd_tx_streamer_recv_async_msg( + uhd_tx_streamer_handle h, + uhd_async_metadata_handle *md, + const double timeout, + bool *valid +){ + UHD_SAFE_C_SAVE_ERROR(h, + *valid = TX_STREAMER(h)->recv_async_msg((*md)->async_metadata_cpp, timeout); + ) +} + +uhd_error uhd_tx_streamer_last_error( + uhd_tx_streamer_handle h, + char* error_out, + size_t strbuffer_len +){ + UHD_SAFE_C( + memset(error_out, '\0', strbuffer_len); + strncpy(error_out, h->last_error.c_str(), strbuffer_len); + ) +} + +/**************************************************************************** + * Generate / Destroy API calls + ***************************************************************************/ +static boost::mutex _usrp_find_mutex; +uhd_error uhd_usrp_find( + const char* args, + uhd_string_vector_handle *strings_out +){ + UHD_SAFE_C( + boost::mutex::scoped_lock _lock(_usrp_find_mutex); + + uhd::device_addrs_t devs = uhd::device::find(std::string(args), uhd::device::USRP); + (*strings_out)->string_vector_cpp.clear(); + BOOST_FOREACH(const uhd::device_addr_t &dev, devs){ + (*strings_out)->string_vector_cpp.push_back(dev.to_string()); + } + ) +} + +static boost::mutex _usrp_make_mutex; +uhd_error uhd_usrp_make( + uhd_usrp_handle *h, + const char *args +){ + UHD_SAFE_C( + boost::mutex::scoped_lock lock(_usrp_make_mutex); + + size_t usrp_count = usrp_ptr::usrp_counter; + usrp_ptr::usrp_counter++; + + // Initialize USRP + uhd::device_addr_t device_addr(args); + usrp_ptr P; + P.ptr = uhd::usrp::multi_usrp::make(device_addr); + + // Dump into registry + get_usrp_ptrs()[usrp_count] = P; + + // Update handle + (*h) = new uhd_usrp; + (*h)->usrp_index = usrp_count; + ) +} + +static boost::mutex _usrp_free_mutex; +uhd_error uhd_usrp_free( + uhd_usrp_handle *h +){ + UHD_SAFE_C( + boost::mutex::scoped_lock lock(_usrp_free_mutex); + + if(!get_usrp_ptrs().count((*h)->usrp_index)){ + return UHD_ERROR_INVALID_DEVICE; + } + + get_usrp_ptrs().erase((*h)->usrp_index); + delete *h; + *h = NULL; + ) +} + +uhd_error uhd_usrp_last_error( + uhd_usrp_handle h, + char* error_out, + size_t strbuffer_len +){ + UHD_SAFE_C( + memset(error_out, '\0', strbuffer_len); + strncpy(error_out, h->last_error.c_str(), strbuffer_len); + ) +} + +static boost::mutex _usrp_get_rx_stream_mutex; +uhd_error uhd_usrp_get_rx_stream( + uhd_usrp_handle h_u, + uhd_stream_args_t *stream_args, + uhd_rx_streamer_handle h_s +){ + UHD_SAFE_C( + boost::mutex::scoped_lock lock(_usrp_get_rx_stream_mutex); + + if(!get_usrp_ptrs().count(h_u->usrp_index)){ + return UHD_ERROR_INVALID_DEVICE; + } + + usrp_ptr &usrp = get_usrp_ptrs()[h_u->usrp_index]; + usrp.rx_streamers.push_back( + usrp.ptr->get_rx_stream(stream_args_c_to_cpp(stream_args)) + ); + h_s->usrp_index = h_u->usrp_index; + h_s->streamer_index = usrp.rx_streamers.size() - 1; + ) +} + +static boost::mutex _usrp_get_tx_stream_mutex; +uhd_error uhd_usrp_get_tx_stream( + uhd_usrp_handle h_u, + uhd_stream_args_t *stream_args, + uhd_tx_streamer_handle h_s +){ + UHD_SAFE_C( + boost::mutex::scoped_lock lock(_usrp_get_tx_stream_mutex); + + if(!get_usrp_ptrs().count(h_u->usrp_index)){ + return UHD_ERROR_INVALID_DEVICE; + } + + usrp_ptr &usrp = get_usrp_ptrs()[h_u->usrp_index]; + usrp.tx_streamers.push_back( + usrp.ptr->get_tx_stream(stream_args_c_to_cpp(stream_args)) + ); + h_s->usrp_index = h_u->usrp_index; + h_s->streamer_index = usrp.tx_streamers.size() - 1; + ) +} + +/**************************************************************************** + * multi_usrp API calls + ***************************************************************************/ + +#define COPY_INFO_FIELD(out, dict, field) \ + out->field = strdup(dict.get(BOOST_STRINGIZE(field)).c_str()) + +uhd_error uhd_usrp_get_rx_info( + uhd_usrp_handle h, + size_t chan, + uhd_usrp_rx_info_t *info_out +) { + UHD_SAFE_C_SAVE_ERROR(h, + uhd::dict<std::string, std::string> rx_info = USRP(h)->get_usrp_rx_info(chan); + + COPY_INFO_FIELD(info_out, rx_info, mboard_id); + COPY_INFO_FIELD(info_out, rx_info, mboard_serial); + COPY_INFO_FIELD(info_out, rx_info, rx_id); + COPY_INFO_FIELD(info_out, rx_info, rx_subdev_name); + COPY_INFO_FIELD(info_out, rx_info, rx_subdev_spec); + COPY_INFO_FIELD(info_out, rx_info, rx_serial); + COPY_INFO_FIELD(info_out, rx_info, rx_antenna); + ) +} + +uhd_error uhd_usrp_get_tx_info( + uhd_usrp_handle h, + size_t chan, + uhd_usrp_tx_info_t *info_out +) { + UHD_SAFE_C_SAVE_ERROR(h, + uhd::dict<std::string, std::string> tx_info = USRP(h)->get_usrp_tx_info(chan); + + COPY_INFO_FIELD(info_out, tx_info, mboard_id); + COPY_INFO_FIELD(info_out, tx_info, mboard_serial); + COPY_INFO_FIELD(info_out, tx_info, tx_id); + COPY_INFO_FIELD(info_out, tx_info, tx_subdev_name); + COPY_INFO_FIELD(info_out, tx_info, tx_subdev_spec); + COPY_INFO_FIELD(info_out, tx_info, tx_serial); + COPY_INFO_FIELD(info_out, tx_info, tx_antenna); + ) +} + +/**************************************************************************** + * Motherboard methods + ***************************************************************************/ +uhd_error uhd_usrp_set_master_clock_rate( + uhd_usrp_handle h, + double rate, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_master_clock_rate(rate, mboard); + ) +} + +uhd_error uhd_usrp_get_master_clock_rate( + uhd_usrp_handle h, + size_t mboard, + double *clock_rate_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *clock_rate_out = USRP(h)->get_master_clock_rate(mboard); + ) +} + +uhd_error uhd_usrp_get_pp_string( + uhd_usrp_handle h, + char* pp_string_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + strncpy(pp_string_out, USRP(h)->get_pp_string().c_str(), strbuffer_len); + ) +} + +uhd_error uhd_usrp_get_mboard_name( + uhd_usrp_handle h, + size_t mboard, + char* mboard_name_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + strncpy(mboard_name_out, USRP(h)->get_mboard_name(mboard).c_str(), strbuffer_len); + ) +} + +uhd_error uhd_usrp_get_time_now( + uhd_usrp_handle h, + size_t mboard, + time_t *full_secs_out, + double *frac_secs_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::time_spec_t time_spec_cpp = USRP(h)->get_time_now(mboard); + *full_secs_out = time_spec_cpp.get_full_secs(); + *frac_secs_out = time_spec_cpp.get_frac_secs(); + ) +} + +uhd_error uhd_usrp_get_time_last_pps( + uhd_usrp_handle h, + size_t mboard, + time_t *full_secs_out, + double *frac_secs_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::time_spec_t time_spec_cpp = USRP(h)->get_time_last_pps(mboard); + *full_secs_out = time_spec_cpp.get_full_secs(); + *frac_secs_out = time_spec_cpp.get_frac_secs(); + ) +} + +uhd_error uhd_usrp_set_time_now( + uhd_usrp_handle h, + time_t full_secs, + double frac_secs, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::time_spec_t time_spec_cpp(full_secs, frac_secs); + USRP(h)->set_time_now(time_spec_cpp, mboard); + ) +} + +uhd_error uhd_usrp_set_time_next_pps( + uhd_usrp_handle h, + time_t full_secs, + double frac_secs, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::time_spec_t time_spec_cpp(full_secs, frac_secs); + USRP(h)->set_time_next_pps(time_spec_cpp, mboard); + ) +} + +uhd_error uhd_usrp_set_time_unknown_pps( + uhd_usrp_handle h, + time_t full_secs, + double frac_secs +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::time_spec_t time_spec_cpp(full_secs, frac_secs); + USRP(h)->set_time_unknown_pps(time_spec_cpp); + ) +} + +uhd_error uhd_usrp_get_time_synchronized( + uhd_usrp_handle h, + bool *result_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *result_out = USRP(h)->get_time_synchronized(); + return UHD_ERROR_NONE; + ) +} + +uhd_error uhd_usrp_set_command_time( + uhd_usrp_handle h, + time_t full_secs, + double frac_secs, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::time_spec_t time_spec_cpp(full_secs, frac_secs); + USRP(h)->set_command_time(time_spec_cpp, mboard); + ) +} + +uhd_error uhd_usrp_clear_command_time( + uhd_usrp_handle h, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->clear_command_time(mboard); + ) +} + +uhd_error uhd_usrp_set_time_source( + uhd_usrp_handle h, + const char* time_source, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_time_source(std::string(time_source), mboard); + ) +} + +uhd_error uhd_usrp_get_time_source( + uhd_usrp_handle h, + size_t mboard, + char* time_source_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + strncpy(time_source_out, USRP(h)->get_time_source(mboard).c_str(), strbuffer_len); + ) +} + +uhd_error uhd_usrp_get_time_sources( + uhd_usrp_handle h, + size_t mboard, + uhd_string_vector_handle *time_sources_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + (*time_sources_out)->string_vector_cpp = USRP(h)->get_time_sources(mboard); + ) +} + +uhd_error uhd_usrp_set_clock_source( + uhd_usrp_handle h, + const char* clock_source, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_clock_source(std::string(clock_source), mboard); + ) +} + +uhd_error uhd_usrp_get_clock_source( + uhd_usrp_handle h, + size_t mboard, + char* clock_source_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + strncpy(clock_source_out, USRP(h)->get_clock_source(mboard).c_str(), strbuffer_len); + ) +} + +uhd_error uhd_usrp_get_clock_sources( + uhd_usrp_handle h, + size_t mboard, + uhd_string_vector_handle *clock_sources_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + (*clock_sources_out)->string_vector_cpp = USRP(h)->get_clock_sources(mboard); + ) +} + +uhd_error uhd_usrp_set_clock_source_out( + uhd_usrp_handle h, + bool enb, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_clock_source_out(enb, mboard); + ) +} + +uhd_error uhd_usrp_get_num_mboards( + uhd_usrp_handle h, + size_t *num_mboards_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *num_mboards_out = USRP(h)->get_num_mboards(); + ) +} + +uhd_error uhd_usrp_get_mboard_sensor( + uhd_usrp_handle h, + const char* name, + size_t mboard, + uhd_sensor_value_handle *sensor_value_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + delete (*sensor_value_out)->sensor_value_cpp; + (*sensor_value_out)->sensor_value_cpp = new uhd::sensor_value_t(USRP(h)->get_mboard_sensor(name, mboard)); + ) +} + +uhd_error uhd_usrp_get_mboard_sensor_names( + uhd_usrp_handle h, + size_t mboard, + uhd_string_vector_handle *mboard_sensor_names_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + (*mboard_sensor_names_out)->string_vector_cpp = USRP(h)->get_mboard_sensor_names(mboard); + ) +} + +uhd_error uhd_usrp_set_user_register( + uhd_usrp_handle h, + uint8_t addr, + uint32_t data, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_user_register(addr, data, mboard); + ) +} + +/**************************************************************************** + * EEPROM access methods + ***************************************************************************/ + +uhd_error uhd_usrp_get_mboard_eeprom( + uhd_usrp_handle h, + uhd_mboard_eeprom_handle mb_eeprom, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::fs_path eeprom_path = str(boost::format("/mboards/%d/eeprom") + % mboard); + + uhd::property_tree::sptr ptree = USRP(h)->get_device()->get_tree(); + mb_eeprom->mboard_eeprom_cpp = ptree->access<uhd::usrp::mboard_eeprom_t>(eeprom_path).get(); + ) +} + +uhd_error uhd_usrp_set_mboard_eeprom( + uhd_usrp_handle h, + uhd_mboard_eeprom_handle mb_eeprom, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::fs_path eeprom_path = str(boost::format("/mboards/%d/eeprom") + % mboard); + + uhd::property_tree::sptr ptree = USRP(h)->get_device()->get_tree(); + ptree->access<uhd::usrp::mboard_eeprom_t>(eeprom_path).set(mb_eeprom->mboard_eeprom_cpp); + ) +} + +uhd_error uhd_usrp_get_dboard_eeprom( + uhd_usrp_handle h, + uhd_dboard_eeprom_handle db_eeprom, + const char* unit, + const char* slot, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::fs_path eeprom_path = str(boost::format("/mboards/%d/dboards/%s/%s_eeprom") + % mboard % slot % unit); + + uhd::property_tree::sptr ptree = USRP(h)->get_device()->get_tree(); + db_eeprom->dboard_eeprom_cpp = ptree->access<uhd::usrp::dboard_eeprom_t>(eeprom_path).get(); + ) +} + +uhd_error uhd_usrp_set_dboard_eeprom( + uhd_usrp_handle h, + uhd_dboard_eeprom_handle db_eeprom, + const char* unit, + const char* slot, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::fs_path eeprom_path = str(boost::format("/mboards/%d/dboards/%s/%s_eeprom") + % mboard % slot % unit); + + uhd::property_tree::sptr ptree = USRP(h)->get_device()->get_tree(); + ptree->access<uhd::usrp::dboard_eeprom_t>(eeprom_path).set(db_eeprom->dboard_eeprom_cpp); + ) +} + +/**************************************************************************** + * RX methods + ***************************************************************************/ + +uhd_error uhd_usrp_set_rx_subdev_spec( + uhd_usrp_handle h, + uhd_subdev_spec_handle subdev_spec, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_rx_subdev_spec(subdev_spec->subdev_spec_cpp, mboard); + ) +} + +uhd_error uhd_usrp_get_rx_subdev_spec( + uhd_usrp_handle h, + size_t mboard, + uhd_subdev_spec_handle subdev_spec_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + subdev_spec_out->subdev_spec_cpp = USRP(h)->get_rx_subdev_spec(mboard); + ) +} + +uhd_error uhd_usrp_get_rx_num_channels( + uhd_usrp_handle h, + size_t *num_channels_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *num_channels_out = USRP(h)->get_rx_num_channels(); + ) +} + +uhd_error uhd_usrp_get_rx_subdev_name( + uhd_usrp_handle h, + size_t chan, + char* rx_subdev_name_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string rx_subdev_name = USRP(h)->get_rx_subdev_name(chan); + strncpy(rx_subdev_name_out, rx_subdev_name.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_usrp_set_rx_rate( + uhd_usrp_handle h, + double rate, + size_t chan +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_rx_rate(rate, chan); + ) +} + +uhd_error uhd_usrp_get_rx_rate( + uhd_usrp_handle h, + size_t chan, + double *rate_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *rate_out = USRP(h)->get_rx_rate(chan); + ) +} + +uhd_error uhd_usrp_get_rx_rates( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle rates_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + rates_out->meta_range_cpp = USRP(h)->get_rx_rates(chan); + ) +} + +uhd_error uhd_usrp_set_rx_freq( + uhd_usrp_handle h, + uhd_tune_request_t *tune_request, + size_t chan, + uhd_tune_result_t *tune_result +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::tune_request_t tune_request_cpp = uhd_tune_request_c_to_cpp(tune_request); + uhd::tune_result_t tune_result_cpp = USRP(h)->set_rx_freq(tune_request_cpp, chan); + uhd_tune_result_cpp_to_c(tune_result_cpp, tune_result); + ) +} + +uhd_error uhd_usrp_get_rx_freq( + uhd_usrp_handle h, + size_t chan, + double *freq_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *freq_out = USRP(h)->get_rx_freq(chan); + ) +} + +uhd_error uhd_usrp_get_rx_freq_range( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle freq_range_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + freq_range_out->meta_range_cpp = USRP(h)->get_rx_freq_range(chan); + ) +} + +uhd_error uhd_usrp_get_fe_rx_freq_range( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle freq_range_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + freq_range_out->meta_range_cpp = USRP(h)->get_fe_rx_freq_range(chan); + ) +} + +uhd_error uhd_usrp_set_rx_gain( + uhd_usrp_handle h, + double gain, + size_t chan, + const char *gain_name +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string name(gain_name); + if(name.empty()){ + USRP(h)->set_rx_gain(gain, chan); + } + else{ + USRP(h)->set_rx_gain(gain, name, chan); + } + ) +} + +uhd_error uhd_usrp_set_normalized_rx_gain( + uhd_usrp_handle h, + double gain, + size_t chan +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_normalized_rx_gain(gain, chan); + ) +} + +uhd_error uhd_usrp_set_rx_agc( + uhd_usrp_handle h, + bool enable, + size_t chan +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_rx_agc(enable, chan); + ) +} + +uhd_error uhd_usrp_get_rx_gain( + uhd_usrp_handle h, + size_t chan, + const char *gain_name, + double *gain_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string name(gain_name); + if(name.empty()){ + *gain_out = USRP(h)->get_rx_gain(chan); + } + else{ + *gain_out = USRP(h)->get_rx_gain(name, chan); + } + ) +} + +uhd_error uhd_usrp_get_normalized_rx_gain( + uhd_usrp_handle h, + size_t chan, + double *gain_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *gain_out = USRP(h)->get_normalized_rx_gain(chan); + ) +} + +uhd_error uhd_usrp_get_rx_gain_range( + uhd_usrp_handle h, + const char* name, + size_t chan, + uhd_meta_range_handle gain_range_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + gain_range_out->meta_range_cpp = USRP(h)->get_rx_gain_range(name, chan); + ) +} + +uhd_error uhd_usrp_get_rx_gain_names( + uhd_usrp_handle h, + size_t chan, + uhd_string_vector_handle *gain_names_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + (*gain_names_out)->string_vector_cpp = USRP(h)->get_rx_gain_names(chan); + ) +} + +uhd_error uhd_usrp_set_rx_antenna( + uhd_usrp_handle h, + const char* ant, + size_t chan +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_rx_antenna(std::string(ant), chan); + ) +} + +uhd_error uhd_usrp_get_rx_antenna( + uhd_usrp_handle h, + size_t chan, + char* ant_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string rx_antenna = USRP(h)->get_rx_antenna(chan); + strncpy(ant_out, rx_antenna.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_usrp_get_rx_antennas( + uhd_usrp_handle h, + size_t chan, + uhd_string_vector_handle *antennas_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + (*antennas_out)->string_vector_cpp = USRP(h)->get_rx_antennas(chan); + ) +} + +uhd_error uhd_usrp_set_rx_bandwidth( + uhd_usrp_handle h, + double bandwidth, + size_t chan +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_rx_bandwidth(bandwidth, chan); + ) +} + +uhd_error uhd_usrp_get_rx_bandwidth( + uhd_usrp_handle h, + size_t chan, + double *bandwidth_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *bandwidth_out = USRP(h)->get_rx_bandwidth(chan); + ) +} + +uhd_error uhd_usrp_get_rx_bandwidth_range( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle bandwidth_range_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + bandwidth_range_out->meta_range_cpp = USRP(h)->get_rx_bandwidth_range(chan); + ) +} + +uhd_error uhd_usrp_get_rx_sensor( + uhd_usrp_handle h, + const char* name, + size_t chan, + uhd_sensor_value_handle *sensor_value_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + delete (*sensor_value_out)->sensor_value_cpp; + (*sensor_value_out)->sensor_value_cpp = new uhd::sensor_value_t(USRP(h)->get_rx_sensor(name, chan)); + ) +} + +uhd_error uhd_usrp_get_rx_sensor_names( + uhd_usrp_handle h, + size_t chan, + uhd_string_vector_handle *sensor_names_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + (*sensor_names_out)->string_vector_cpp = USRP(h)->get_rx_sensor_names(chan); + ) +} + +uhd_error uhd_usrp_set_rx_dc_offset_enabled( + uhd_usrp_handle h, + bool enb, + size_t chan +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_rx_dc_offset(enb, chan); + ) +} + +uhd_error uhd_usrp_set_rx_iq_balance_enabled( + uhd_usrp_handle h, + bool enb, + size_t chan +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_rx_iq_balance(enb, chan); + ) +} + +/**************************************************************************** + * TX methods + ***************************************************************************/ + +uhd_error uhd_usrp_set_tx_subdev_spec( + uhd_usrp_handle h, + uhd_subdev_spec_handle subdev_spec, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_tx_subdev_spec(subdev_spec->subdev_spec_cpp, mboard); + ) +} + +uhd_error uhd_usrp_get_tx_subdev_spec( + uhd_usrp_handle h, + size_t mboard, + uhd_subdev_spec_handle subdev_spec_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + subdev_spec_out->subdev_spec_cpp = USRP(h)->get_tx_subdev_spec(mboard); + ) +} + + +uhd_error uhd_usrp_get_tx_num_channels( + uhd_usrp_handle h, + size_t *num_channels_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *num_channels_out = USRP(h)->get_tx_num_channels(); + ) +} + +uhd_error uhd_usrp_get_tx_subdev_name( + uhd_usrp_handle h, + size_t chan, + char* tx_subdev_name_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string tx_subdev_name = USRP(h)->get_tx_subdev_name(chan); + strncpy(tx_subdev_name_out, tx_subdev_name.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_usrp_set_tx_rate( + uhd_usrp_handle h, + double rate, + size_t chan +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_tx_rate(rate, chan); + ) +} + +uhd_error uhd_usrp_get_tx_rate( + uhd_usrp_handle h, + size_t chan, + double *rate_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *rate_out = USRP(h)->get_tx_rate(chan); + ) +} + +uhd_error uhd_usrp_get_tx_rates( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle rates_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + rates_out->meta_range_cpp = USRP(h)->get_tx_rates(chan); + ) +} + +uhd_error uhd_usrp_set_tx_freq( + uhd_usrp_handle h, + uhd_tune_request_t *tune_request, + size_t chan, + uhd_tune_result_t *tune_result +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::tune_request_t tune_request_cpp = uhd_tune_request_c_to_cpp(tune_request); + uhd::tune_result_t tune_result_cpp = USRP(h)->set_tx_freq(tune_request_cpp, chan); + uhd_tune_result_cpp_to_c(tune_result_cpp, tune_result); + ) +} + +uhd_error uhd_usrp_get_tx_freq( + uhd_usrp_handle h, + size_t chan, + double *freq_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *freq_out = USRP(h)->get_tx_freq(chan); + ) +} + +uhd_error uhd_usrp_get_tx_freq_range( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle freq_range_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + freq_range_out->meta_range_cpp = USRP(h)->get_tx_freq_range(chan); + ) +} + +uhd_error uhd_usrp_get_fe_tx_freq_range( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle freq_range_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + freq_range_out->meta_range_cpp = USRP(h)->get_fe_tx_freq_range(chan); + ) +} + +uhd_error uhd_usrp_set_tx_gain( + uhd_usrp_handle h, + double gain, + size_t chan, + const char *gain_name +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string name(gain_name); + if(name.empty()){ + USRP(h)->set_tx_gain(gain, chan); + } + else{ + USRP(h)->set_tx_gain(gain, name, chan); + } + ) +} + +uhd_error uhd_usrp_set_normalized_tx_gain( + uhd_usrp_handle h, + double gain, + size_t chan +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_normalized_tx_gain(gain, chan); + ) +} + +uhd_error uhd_usrp_get_tx_gain( + uhd_usrp_handle h, + size_t chan, + const char *gain_name, + double *gain_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string name(gain_name); + if(name.empty()){ + *gain_out = USRP(h)->get_tx_gain(chan); + } + else{ + *gain_out = USRP(h)->get_tx_gain(name, chan); + } + ) +} + +uhd_error uhd_usrp_get_normalized_tx_gain( + uhd_usrp_handle h, + size_t chan, + double *gain_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *gain_out = USRP(h)->get_normalized_tx_gain(chan); + ) +} + +uhd_error uhd_usrp_get_tx_gain_range( + uhd_usrp_handle h, + const char* name, + size_t chan, + uhd_meta_range_handle gain_range_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + gain_range_out->meta_range_cpp = USRP(h)->get_tx_gain_range(name, chan); + ) +} + +uhd_error uhd_usrp_get_tx_gain_names( + uhd_usrp_handle h, + size_t chan, + uhd_string_vector_handle *gain_names_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + (*gain_names_out)->string_vector_cpp = USRP(h)->get_tx_gain_names(chan); + ) +} + +uhd_error uhd_usrp_set_tx_antenna( + uhd_usrp_handle h, + const char* ant, + size_t chan +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_tx_antenna(std::string(ant), chan); + ) +} + +uhd_error uhd_usrp_get_tx_antenna( + uhd_usrp_handle h, + size_t chan, + char* ant_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string tx_antenna = USRP(h)->get_tx_antenna(chan); + strncpy(ant_out, tx_antenna.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_usrp_get_tx_antennas( + uhd_usrp_handle h, + size_t chan, + uhd_string_vector_handle *antennas_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + (*antennas_out)->string_vector_cpp = USRP(h)->get_tx_antennas(chan); + ) +} + +uhd_error uhd_usrp_set_tx_bandwidth( + uhd_usrp_handle h, + double bandwidth, + size_t chan +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_tx_bandwidth(bandwidth, chan); + ) +} + +uhd_error uhd_usrp_get_tx_bandwidth( + uhd_usrp_handle h, + size_t chan, + double *bandwidth_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *bandwidth_out = USRP(h)->get_tx_bandwidth(chan); + ) +} + +uhd_error uhd_usrp_get_tx_bandwidth_range( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle bandwidth_range_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + bandwidth_range_out->meta_range_cpp = USRP(h)->get_tx_bandwidth_range(chan); + ) +} + +uhd_error uhd_usrp_get_tx_sensor( + uhd_usrp_handle h, + const char* name, + size_t chan, + uhd_sensor_value_handle *sensor_value_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + delete (*sensor_value_out)->sensor_value_cpp; + (*sensor_value_out)->sensor_value_cpp = new uhd::sensor_value_t(USRP(h)->get_tx_sensor(name, chan)); + ) +} + +uhd_error uhd_usrp_get_tx_sensor_names( + uhd_usrp_handle h, + size_t chan, + uhd_string_vector_handle *sensor_names_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + (*sensor_names_out)->string_vector_cpp = USRP(h)->get_tx_sensor_names(chan); + ) +} + +uhd_error uhd_usrp_set_tx_dc_offset_enabled( + uhd_usrp_handle h, + bool enb, + size_t chan +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_tx_dc_offset(enb, chan); + ) +} + +uhd_error uhd_usrp_set_tx_iq_balance_enabled( + uhd_usrp_handle h, + bool enb, + size_t chan +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_tx_iq_balance(enb, chan); + ) +} + +/**************************************************************************** + * GPIO methods + ***************************************************************************/ + +uhd_error uhd_usrp_get_gpio_banks( + uhd_usrp_handle h, + size_t chan, + uhd_string_vector_handle *gpio_banks_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + (*gpio_banks_out)->string_vector_cpp = USRP(h)->get_gpio_banks(chan); + ) +} + +uhd_error uhd_usrp_set_gpio_attr( + uhd_usrp_handle h, + const char* bank, + const char* attr, + uint32_t value, + uint32_t mask, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_gpio_attr(std::string(bank), std::string(attr), + value, mask, mboard); + ) +} + +uhd_error uhd_usrp_get_gpio_attr( + uhd_usrp_handle h, + const char* bank, + const char* attr, + size_t mboard, + uint32_t *attr_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *attr_out = USRP(h)->get_gpio_attr(std::string(bank), std::string(attr), mboard); + ) +} + +uhd_error uhd_usrp_enumerate_registers( + uhd_usrp_handle h, + size_t mboard, + uhd_string_vector_handle *registers_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + (*registers_out)->string_vector_cpp = USRP(h)->enumerate_registers(mboard); + ) +} + +uhd_error uhd_usrp_get_register_info( + uhd_usrp_handle h, + const char* path, + size_t mboard, + uhd_usrp_register_info_t *register_info_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::usrp::multi_usrp::register_info_t register_info_cpp = USRP(h)->get_register_info(path, mboard); + register_info_out->bitwidth = register_info_cpp.bitwidth; + register_info_out->readable = register_info_cpp.readable; + register_info_out->writable = register_info_cpp.writable; + ) +} + +uhd_error uhd_usrp_write_register( + uhd_usrp_handle h, + const char* path, + uint32_t field, + uint64_t value, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->write_register(path, field, value, mboard); + ) +} + +uhd_error uhd_usrp_write_register( + uhd_usrp_handle h, + const char* path, + uint32_t field, + size_t mboard, + uint64_t *value_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *value_out = USRP(h)->read_register(path, field, mboard); + ) +} diff --git a/host/lib/usrp/x300/CMakeLists.txt b/host/lib/usrp/x300/CMakeLists.txt index a588f901b..3d6348eec 100644 --- a/host/lib/usrp/x300/CMakeLists.txt +++ b/host/lib/usrp/x300/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013 Ettus Research LLC +# Copyright 2013,2015 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 @@ -22,7 +22,7 @@ ######################################################################## # Conditionally configure the X300 support ######################################################################## -LIBUHD_REGISTER_COMPONENT("X300" ENABLE_X300 ON "ENABLE_LIBUHD" OFF) +LIBUHD_REGISTER_COMPONENT("X300" ENABLE_X300 ON "ENABLE_LIBUHD" OFF OFF) IF(ENABLE_X300) LIBUHD_APPEND_SOURCES( @@ -34,5 +34,8 @@ IF(ENABLE_X300) ${CMAKE_CURRENT_SOURCE_DIR}/x300_io_impl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/x300_dboard_iface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/x300_clock_ctrl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/x300_image_loader.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/x300_adc_dac_utils.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/cdecode.c ) ENDIF(ENABLE_X300) diff --git a/host/lib/usrp/x300/cdecode.c b/host/lib/usrp/x300/cdecode.c new file mode 100644 index 000000000..1d09cbe22 --- /dev/null +++ b/host/lib/usrp/x300/cdecode.c @@ -0,0 +1,80 @@ +/* +cdecoder.c - c source to a base64 decoding algorithm implementation + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#include "cdecode.h" + +int base64_decode_value(char value_in){ + static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51}; + static const char decoding_size = sizeof(decoding); + value_in -= 43; + if ((signed char)value_in < 0 || value_in > decoding_size) return -1; + return decoding[(int)value_in]; +} + +void base64_init_decodestate(base64_decodestate* state_in){ + state_in->step = step_a; + state_in->plainchar = 0; +} + +size_t base64_decode_block(const char* code_in, const size_t length_in, char* plaintext_out, base64_decodestate* state_in){ + const char* codechar = code_in; + char* plainchar = plaintext_out; + char fragment; + + *plainchar = state_in->plainchar; + + switch (state_in->step){ + while (1){ + case step_a: + do{ + if (codechar == code_in+length_in){ + state_in->step = step_a; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while ((signed char)fragment < 0); + *plainchar = (fragment & 0x03f) << 2; + + case step_b: + do{ + if (codechar == code_in+length_in){ + state_in->step = step_b; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while ((signed char)fragment < 0); + *plainchar++ |= (fragment & 0x030) >> 4; + *plainchar = (fragment & 0x00f) << 4; + case step_c: + do{ + if (codechar == code_in+length_in) + { + state_in->step = step_c; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while ((signed char)fragment < 0); + *plainchar++ |= (fragment & 0x03c) >> 2; + *plainchar = (fragment & 0x003) << 6; + case step_d: + do{ + if (codechar == code_in+length_in){ + state_in->step = step_d; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while ((signed char)fragment < 0); + *plainchar++ |= (fragment & 0x03f); + } + } + /* control should not reach here */ + return plainchar - plaintext_out; +} diff --git a/host/lib/usrp/x300/cdecode.h b/host/lib/usrp/x300/cdecode.h new file mode 100644 index 000000000..b8da55aa1 --- /dev/null +++ b/host/lib/usrp/x300/cdecode.h @@ -0,0 +1,36 @@ +/* +cdecode.h - c header for a base64 decoding algorithm + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#ifndef BASE64_CDECODE_H +#define BASE64_CDECODE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stddef.h> + +typedef enum{ + step_a, step_b, step_c, step_d +} base64_decodestep; + +typedef struct{ + base64_decodestep step; + char plainchar; +} base64_decodestate; + +void base64_init_decodestate(base64_decodestate* state_in); + +int base64_decode_value(char value_in); + +size_t base64_decode_block(const char* code_in, const size_t length_in, char* plaintext_out, base64_decodestate* state_in); + +#ifdef __cplusplus +} +#endif + +#endif /* BASE64_CDECODE_H */ diff --git a/host/lib/usrp/x300/x300_adc_ctrl.cpp b/host/lib/usrp/x300/x300_adc_ctrl.cpp index b0e4e4b95..ce6102b35 100644 --- a/host/lib/usrp/x300/x300_adc_ctrl.cpp +++ b/host/lib/usrp/x300/x300_adc_ctrl.cpp @@ -55,8 +55,8 @@ public: _ads62p48_regs.lvds_cmos = ads62p48_regs_t::LVDS_CMOS_DDR_LVDS; _ads62p48_regs.channel_control = ads62p48_regs_t::CHANNEL_CONTROL_INDEPENDENT; _ads62p48_regs.data_format = ads62p48_regs_t::DATA_FORMAT_2S_COMPLIMENT; - _ads62p48_regs.clk_out_pos_edge = ads62p48_regs_t::CLK_OUT_POS_EDGE_MINUS7_26; - _ads62p48_regs.clk_out_neg_edge = ads62p48_regs_t::CLK_OUT_NEG_EDGE_MINUS7_26; + _ads62p48_regs.clk_out_pos_edge = ads62p48_regs_t::CLK_OUT_POS_EDGE_MINUS4_26; + _ads62p48_regs.clk_out_neg_edge = ads62p48_regs_t::CLK_OUT_NEG_EDGE_MINUS4_26; this->send_ads62p48_reg(0); diff --git a/host/lib/usrp/x300/x300_adc_dac_utils.cpp b/host/lib/usrp/x300/x300_adc_dac_utils.cpp new file mode 100644 index 000000000..e08825749 --- /dev/null +++ b/host/lib/usrp/x300/x300_adc_dac_utils.cpp @@ -0,0 +1,414 @@ +// +// Copyright 2015 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 "x300_impl.hpp" +#include <boost/date_time/posix_time/posix_time_io.hpp> + +using namespace uhd::usrp::x300; + +/*********************************************************************** + * DAC: Reset and synchronization operations + **********************************************************************/ + +void x300_impl::synchronize_dacs(const std::vector<radio_perifs_t*>& radios) +{ + if (radios.size() < 2) return; //Nothing to synchronize + + //**PRECONDITION** + //This function assumes that all the VITA times in "radios" are synchronized + //to a common reference. Currently, this function is called in get_tx_stream + //which also has the same precondition. + + //Reinitialize and resync all DACs + for (size_t i = 0; i < radios.size(); i++) { + radios[i]->dac->reset_and_resync(); + } + + //Get a rough estimate of the cumulative command latency + boost::posix_time::ptime t_start = boost::posix_time::microsec_clock::local_time(); + for (size_t i = 0; i < radios.size(); i++) { + radios[i]->ctrl->peek64(uhd::usrp::radio::RB64_TIME_NOW); //Discard value. We are just timing the call + } + boost::posix_time::time_duration t_elapsed = + boost::posix_time::microsec_clock::local_time() - t_start; + + //Add 100% of headroom + uncertaintly to the command time + boost::uint64_t t_sync_us = (t_elapsed.total_microseconds() * 2) + 13000 /*Scheduler latency*/; + + //Pick radios[0] as the time reference. + uhd::time_spec_t sync_time = + radios[0]->time64->get_time_now() + uhd::time_spec_t(((double)t_sync_us)/1e6); + + //Send the sync command + for (size_t i = 0; i < radios.size(); i++) { + radios[i]->ctrl->set_time(sync_time); + radios[i]->ctrl->poke32(uhd::usrp::radio::sr_addr(uhd::usrp::radio::DACSYNC), 0x1); //Arm FRAMEP/N sync pulse + radios[i]->ctrl->set_time(uhd::time_spec_t(0.0)); //Clear command time + } + + //Wait and check status + boost::this_thread::sleep(boost::posix_time::microseconds(t_sync_us)); + for (size_t i = 0; i < radios.size(); i++) { + radios[i]->dac->verify_sync(); + } +} + +/*********************************************************************** + * ADC: Self-test operations + **********************************************************************/ + +static void check_adc(uhd::wb_iface::sptr iface, const boost::uint32_t val, const boost::uint32_t i) +{ + boost::uint32_t adc_rb = iface->peek32(uhd::usrp::radio::RB32_RX); + adc_rb ^= 0xfffc0000; //adapt for I inversion in FPGA + if (val != adc_rb) { + throw uhd::runtime_error( + (boost::format("ADC self-test failed for Radio%d. (Exp=0x%x, Got=0x%x)")%i%val%adc_rb).str()); + } +} + +void x300_impl::self_test_adcs(mboard_members_t& mb, boost::uint32_t ramp_time_ms) { + for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) { + radio_perifs_t &perif = mb.radio_perifs[r]; + + //First test basic patterns + perif.adc->set_test_word("ones", "ones"); check_adc(perif.ctrl, 0xfffcfffc,r); + perif.adc->set_test_word("zeros", "zeros"); check_adc(perif.ctrl, 0x00000000,r); + perif.adc->set_test_word("ones", "zeros"); check_adc(perif.ctrl, 0xfffc0000,r); + perif.adc->set_test_word("zeros", "ones"); check_adc(perif.ctrl, 0x0000fffc,r); + for (size_t k = 0; k < 14; k++) + { + perif.adc->set_test_word("zeros", "custom", 1 << k); + check_adc(perif.ctrl, 1 << (k+2),r); + } + for (size_t k = 0; k < 14; k++) + { + perif.adc->set_test_word("custom", "zeros", 1 << k); + check_adc(perif.ctrl, 1 << (k+18),r); + } + + //Turn on ramp pattern test + perif.adc->set_test_word("ramp", "ramp"); + perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); + perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1); + } + boost::this_thread::sleep(boost::posix_time::milliseconds(ramp_time_ms)); + + bool passed = true; + std::string status_str; + for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) { + radio_perifs_t &perif = mb.radio_perifs[r]; + perif.regmap->misc_ins_reg.refresh(); + + std::string i_status, q_status; + if (perif.regmap->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_I_LOCKED)) + if (perif.regmap->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_I_ERROR)) + i_status = "Bit Errors!"; + else + i_status = "Good"; + else + i_status = "Not Locked!"; + + if (perif.regmap->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_Q_LOCKED)) + if (perif.regmap->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_Q_ERROR)) + q_status = "Bit Errors!"; + else + q_status = "Good"; + else + q_status = "Not Locked!"; + + passed = passed && (i_status == "Good") && (q_status == "Good"); + status_str += (boost::format(", ADC%d_I=%s, ADC%d_Q=%s")%r%i_status%r%q_status).str(); + + //Return to normal mode + perif.adc->set_test_word("normal", "normal"); + } + + if (not passed) { + throw uhd::runtime_error( + (boost::format("ADC self-test failed! Ramp checker status: {%s}")%status_str.substr(2)).str()); + } +} + +void x300_impl::extended_adc_test(mboard_members_t& mb, double duration_s) +{ + static const size_t SECS_PER_ITER = 5; + UHD_MSG(status) << boost::format("Running Extended ADC Self-Test (Duration=%.0fs, %ds/iteration)...\n") + % duration_s % SECS_PER_ITER; + + size_t num_iters = static_cast<size_t>(ceil(duration_s/SECS_PER_ITER)); + size_t num_failures = 0; + for (size_t iter = 0; iter < num_iters; iter++) { + //Print date and time + boost::posix_time::time_facet *facet = new boost::posix_time::time_facet("%d-%b-%Y %H:%M:%S"); + std::ostringstream time_strm; + time_strm.imbue(std::locale(std::locale::classic(), facet)); + time_strm << boost::posix_time::second_clock::local_time(); + //Run self-test + UHD_MSG(status) << boost::format("-- [%s] Iteration %06d... ") % time_strm.str() % (iter+1); + try { + self_test_adcs(mb, SECS_PER_ITER*1000); + UHD_MSG(status) << "passed" << std::endl; + } catch(std::exception &e) { + num_failures++; + UHD_MSG(status) << e.what() << std::endl; + } + + } + if (num_failures == 0) { + UHD_MSG(status) << "Extended ADC Self-Test PASSED\n"; + } else { + throw uhd::runtime_error( + (boost::format("Extended ADC Self-Test FAILED!!! (%d/%d failures)\n") % num_failures % num_iters).str()); + } +} + +/*********************************************************************** + * ADC: Self-calibration operations + **********************************************************************/ + +void x300_impl::self_cal_adc_capture_delay(mboard_members_t& mb, const size_t radio_i, bool print_status) +{ + radio_perifs_t& perif = mb.radio_perifs[radio_i]; + if (print_status) UHD_MSG(status) << "Running ADC capture delay self-cal..." << std::flush; + + static const boost::uint32_t NUM_DELAY_STEPS = 32; //The IDELAYE2 element has 32 steps + static const boost::uint32_t NUM_RETRIES = 2; //Retry self-cal if it fails in warmup situations + static const boost::int32_t MIN_WINDOW_LEN = 4; + + boost::int32_t win_start = -1, win_stop = -1; + boost::uint32_t iter = 0; + while (iter++ < NUM_RETRIES) { + for (boost::uint32_t dly_tap = 0; dly_tap < NUM_DELAY_STEPS; dly_tap++) { + //Apply delay + perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_VAL, dly_tap); + perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_STB, 1); + perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_STB, 0); + + boost::uint32_t err_code = 0; + + // -- Test I Channel -- + //Put ADC in ramp test mode. Tie the other channel to all ones. + perif.adc->set_test_word("ramp", "ones"); + //Turn on the pattern checker in the FPGA. It will lock when it sees a zero + //and count deviations from the expected value + perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); + perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1); + //10ms @ 200MHz = 2 million samples + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + if (perif.regmap->misc_ins_reg.read(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER0_I_LOCKED)) { + err_code += perif.regmap->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER0_I_ERROR); + } else { + err_code += 100; //Increment error code by 100 to indicate no lock + } + + // -- Test Q Channel -- + //Put ADC in ramp test mode. Tie the other channel to all ones. + perif.adc->set_test_word("ones", "ramp"); + //Turn on the pattern checker in the FPGA. It will lock when it sees a zero + //and count deviations from the expected value + perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); + perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1); + //10ms @ 200MHz = 2 million samples + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + if (perif.regmap->misc_ins_reg.read(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER0_Q_LOCKED)) { + err_code += perif.regmap->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER0_Q_ERROR); + } else { + err_code += 100; //Increment error code by 100 to indicate no lock + } + + if (err_code == 0) { + if (win_start == -1) { //This is the first window + win_start = dly_tap; + win_stop = dly_tap; + } else { //We are extending the window + win_stop = dly_tap; + } + } else { + if (win_start != -1) { //A valid window turned invalid + if (win_stop - win_start >= MIN_WINDOW_LEN) { + break; //Valid window found + } else { + win_start = -1; //Reset window + } + } + } + //UHD_MSG(status) << (boost::format("CapTap=%d, Error=%d\n") % dly_tap % err_code); + } + + //Retry the self-cal if it fails + if ((win_start == -1 || (win_stop - win_start) < MIN_WINDOW_LEN) && iter < NUM_RETRIES /*not last iteration*/) { + win_start = -1; + win_stop = -1; + boost::this_thread::sleep(boost::posix_time::milliseconds(2000)); + } else { + break; + } + } + perif.adc->set_test_word("normal", "normal"); + perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); + + if (win_start == -1) { + throw uhd::runtime_error("self_cal_adc_capture_delay: Self calibration failed. Convergence error."); + } + + if (win_stop-win_start < MIN_WINDOW_LEN) { + throw uhd::runtime_error("self_cal_adc_capture_delay: Self calibration failed. Valid window too narrow."); + } + + boost::uint32_t ideal_tap = (win_stop + win_start) / 2; + perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_VAL, ideal_tap); + perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_STB, 1); + perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_STB, 0); + + if (print_status) { + double tap_delay = (1.0e12 / mb.clock->get_master_clock_rate()) / (2*32); //in ps + UHD_MSG(status) << boost::format(" done (Tap=%d, Window=%d, TapDelay=%.3fps, Iter=%d)\n") % ideal_tap % (win_stop-win_start) % tap_delay % iter; + } +} + +double x300_impl::self_cal_adc_xfer_delay(mboard_members_t& mb, bool apply_delay) +{ + UHD_MSG(status) << "Running ADC transfer delay self-cal: " << std::flush; + + //Effective resolution of the self-cal. + static const size_t NUM_DELAY_STEPS = 100; + + double master_clk_period = (1.0e9 / mb.clock->get_master_clock_rate()); //in ns + double delay_start = 0.0; + double delay_range = 2 * master_clk_period; + double delay_incr = delay_range / NUM_DELAY_STEPS; + + UHD_MSG(status) << "Measuring..." << std::flush; + double cached_clk_delay = mb.clock->get_clock_delay(X300_CLOCK_WHICH_ADC0); + double fpga_clk_delay = mb.clock->get_clock_delay(X300_CLOCK_WHICH_FPGA); + + //Iterate through several values of delays and measure ADC data integrity + std::vector< std::pair<double,bool> > results; + for (size_t i = 0; i < NUM_DELAY_STEPS; i++) { + //Delay the ADC clock (will set both Ch0 and Ch1 delays) + double delay = mb.clock->set_clock_delay(X300_CLOCK_WHICH_ADC0, delay_incr*i + delay_start); + wait_for_clk_locked(mb, fw_regmap_t::clk_status_reg_t::LMK_LOCK, 0.1); + + boost::uint32_t err_code = 0; + for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) { + //Test each channel (I and Q) individually so as to not accidentally trigger + //on the data from the other channel if there is a swap + + // -- Test I Channel -- + //Put ADC in ramp test mode. Tie the other channel to all ones. + mb.radio_perifs[r].adc->set_test_word("ramp", "ones"); + //Turn on the pattern checker in the FPGA. It will lock when it sees a zero + //and count deviations from the expected value + mb.radio_perifs[r].regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); + mb.radio_perifs[r].regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1); + //50ms @ 200MHz = 10 million samples + boost::this_thread::sleep(boost::posix_time::milliseconds(50)); + if (mb.radio_perifs[r].regmap->misc_ins_reg.read(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_I_LOCKED)) { + err_code += mb.radio_perifs[r].regmap->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_I_ERROR); + } else { + err_code += 100; //Increment error code by 100 to indicate no lock + } + + // -- Test Q Channel -- + //Put ADC in ramp test mode. Tie the other channel to all ones. + mb.radio_perifs[r].adc->set_test_word("ones", "ramp"); + //Turn on the pattern checker in the FPGA. It will lock when it sees a zero + //and count deviations from the expected value + mb.radio_perifs[r].regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); + mb.radio_perifs[r].regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1); + //50ms @ 200MHz = 10 million samples + boost::this_thread::sleep(boost::posix_time::milliseconds(50)); + if (mb.radio_perifs[r].regmap->misc_ins_reg.read(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_Q_LOCKED)) { + err_code += mb.radio_perifs[r].regmap->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_Q_ERROR); + } else { + err_code += 100; //Increment error code by 100 to indicate no lock + } + } + //UHD_MSG(status) << (boost::format("XferDelay=%fns, Error=%d\n") % delay % err_code); + results.push_back(std::pair<double,bool>(delay, err_code==0)); + } + + //Calculate the valid window + int win_start_idx = -1, win_stop_idx = -1, cur_start_idx = -1, cur_stop_idx = -1; + for (size_t i = 0; i < results.size(); i++) { + std::pair<double,bool>& item = results[i]; + if (item.second) { //If data is stable + if (cur_start_idx == -1) { //This is the first window + cur_start_idx = i; + cur_stop_idx = i; + } else { //We are extending the window + cur_stop_idx = i; + } + } else { + if (cur_start_idx == -1) { //We haven't yet seen valid data + //Do nothing + } else if (win_start_idx == -1) { //We passed the first valid window + win_start_idx = cur_start_idx; + win_stop_idx = cur_stop_idx; + } else { //Update cached window if current window is larger + double cur_win_len = results[cur_stop_idx].first - results[cur_start_idx].first; + double cached_win_len = results[win_stop_idx].first - results[win_start_idx].first; + if (cur_win_len > cached_win_len) { + win_start_idx = cur_start_idx; + win_stop_idx = cur_stop_idx; + } + } + //Reset current window + cur_start_idx = -1; + cur_stop_idx = -1; + } + } + if (win_start_idx == -1) { + throw uhd::runtime_error("self_cal_adc_xfer_delay: Self calibration failed. Convergence error."); + } + + double win_center = (results[win_stop_idx].first + results[win_start_idx].first) / 2.0; + double win_length = results[win_stop_idx].first - results[win_start_idx].first; + if (win_length < master_clk_period/4) { + throw uhd::runtime_error("self_cal_adc_xfer_delay: Self calibration failed. Valid window too narrow."); + } + + //Cycle slip the relative delay by a clock cycle to prevent sample misalignment + //fpga_clk_delay > 0 and 0 < win_center < 2*(1/MCR) so one cycle slip is all we need + bool cycle_slip = (win_center-fpga_clk_delay >= master_clk_period); + if (cycle_slip) { + win_center -= master_clk_period; + } + + if (apply_delay) { + UHD_MSG(status) << "Validating..." << std::flush; + //Apply delay + win_center = mb.clock->set_clock_delay(X300_CLOCK_WHICH_ADC0, win_center); //Sets ADC0 and ADC1 + wait_for_clk_locked(mb, fw_regmap_t::clk_status_reg_t::LMK_LOCK, 0.1); + //Validate + self_test_adcs(mb, 2000); + } else { + //Restore delay + mb.clock->set_clock_delay(X300_CLOCK_WHICH_ADC0, cached_clk_delay); //Sets ADC0 and ADC1 + } + + //Teardown + for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) { + mb.radio_perifs[r].adc->set_test_word("normal", "normal"); + mb.radio_perifs[r].regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); + } + UHD_MSG(status) << (boost::format(" done (FPGA->ADC=%.3fns%s, Window=%.3fns)\n") % + (win_center-fpga_clk_delay) % (cycle_slip?" +cyc":"") % win_length); + + return win_center; +} diff --git a/host/lib/usrp/x300/x300_clock_ctrl.cpp b/host/lib/usrp/x300/x300_clock_ctrl.cpp index 6450686dd..d5687f5cc 100644 --- a/host/lib/usrp/x300/x300_clock_ctrl.cpp +++ b/host/lib/usrp/x300/x300_clock_ctrl.cpp @@ -21,6 +21,7 @@ #include <uhd/utils/math.hpp> #include <boost/cstdint.hpp> #include <boost/format.hpp> +#include <boost/math/special_functions/round.hpp> #include <stdexcept> #include <cmath> #include <cstdlib> @@ -29,6 +30,30 @@ static const double X300_REF_CLK_OUT_RATE = 10e6; static const boost::uint16_t X300_MAX_CLKOUT_DIV = 1045; static const double X300_DEFAULT_DBOARD_CLK_RATE = 50e6; +struct x300_clk_delays { + x300_clk_delays() : + fpga_dly_ns(0.0),adc_dly_ns(0.0),dac_dly_ns(0.0),db_rx_dly_ns(0.0),db_tx_dly_ns(0.0) + {} + x300_clk_delays(double fpga, double adc, double dac, double db_rx, double db_tx) : + fpga_dly_ns(fpga),adc_dly_ns(adc),dac_dly_ns(dac),db_rx_dly_ns(db_rx),db_tx_dly_ns(db_tx) + {} + + double fpga_dly_ns; + double adc_dly_ns; + double dac_dly_ns; + double db_rx_dly_ns; + double db_tx_dly_ns; +}; + +// Tune the FPGA->ADC clock delay to ensure a safe ADC_SSCLK -> RADIO_CLK crossing. +// If the FPGA_CLK is delayed, we also need to delay the reference clocks going to the DAC +// because the data interface clock is generated from FPGA_CLK. +static const x300_clk_delays X300_REV0_6_CLK_DELAYS = x300_clk_delays( + /*fpga=*/0.000, /*adc=*/2.200, /*dac=*/0.000, /*db_rx=*/0.000, /*db_tx=*/0.000); + +static const x300_clk_delays X300_REV7_CLK_DELAYS = x300_clk_delays( + /*fpga=*/0.000, /*adc=*/0.000, /*dac=*/0.000, /*db_rx=*/0.000, /*db_tx=*/0.000); + using namespace uhd; x300_clock_ctrl::~x300_clock_ctrl(void){ @@ -213,6 +238,187 @@ public: _spiface->write_spi(_slaveno, spi_config_t::EDGE_RISE, data,32); } + double set_clock_delay(const x300_clock_which_t which, const double delay_ns, const bool resync = true) { + //All dividers have are delayed by 5 taps by default. The delay + //set by this function is relative to the 5 tap delay + static const boost::uint16_t DDLY_MIN_TAPS = 5; + static const boost::uint16_t DDLY_MAX_TAPS = 522; //Extended mode + + //The resolution and range of the analog delay is fixed + static const double ADLY_RES_NS = 0.025; + static const double ADLY_MIN_NS = 0.500; + static const double ADLY_MAX_NS = 0.975; + + //Each digital tap delays the clock by one VCO period + double vco_period_ns = 1.0e9/_vco_freq; + double half_vco_period_ns = vco_period_ns/2.0; + + //Implement as much of the requested delay using digital taps. Whatever is leftover + //will be made up using the analog delay element and the half-cycle digital tap. + //A caveat here is that the analog delay starts at ADLY_MIN_NS, so we need to back off + //by that much when coming up with the digital taps so that the difference can be made + //up using the analog delay. + boost::uint16_t ddly_taps = 0; + if (delay_ns < ADLY_MIN_NS) { + ddly_taps = static_cast<boost::uint16_t>(std::floor((delay_ns)/vco_period_ns)); + } else { + ddly_taps = static_cast<boost::uint16_t>(std::floor((delay_ns-ADLY_MIN_NS)/vco_period_ns)); + } + double leftover_delay = delay_ns - (vco_period_ns * ddly_taps); + + //Compute settings + boost::uint16_t ddly_value = ddly_taps + DDLY_MIN_TAPS; + bool adly_en = false; + boost::uint8_t adly_value = 0; + boost::uint8_t half_shift_en = 0; + + if (ddly_value > DDLY_MAX_TAPS) { + throw uhd::value_error("set_clock_delay: Requested delay is out of range."); + } + + double coerced_delay = (vco_period_ns * ddly_taps); + if (leftover_delay > ADLY_MAX_NS) { + //The VCO is running too slowly for us to compensate the digital delay difference using + //analog delay. Do the best we can. + adly_en = true; + adly_value = static_cast<boost::uint8_t>(boost::math::round((ADLY_MAX_NS-ADLY_MIN_NS)/ADLY_RES_NS)); + coerced_delay += ADLY_MAX_NS; + } else if (leftover_delay >= ADLY_MIN_NS && leftover_delay <= ADLY_MAX_NS) { + //The leftover delay can be compensated by the analog delay up to the analog delay resolution + adly_en = true; + adly_value = static_cast<boost::uint8_t>(boost::math::round((leftover_delay-ADLY_MIN_NS)/ADLY_RES_NS)); + coerced_delay += ADLY_MIN_NS+(ADLY_RES_NS*adly_value); + } else if (leftover_delay >= (ADLY_MIN_NS - half_vco_period_ns) && leftover_delay < ADLY_MIN_NS) { + //The leftover delay if less than the minimum supported analog delay but if we move the digital + //delay back by half a VCO cycle then it will be in the range of the analog delay. So do that! + adly_en = true; + adly_value = static_cast<boost::uint8_t>(boost::math::round((leftover_delay+half_vco_period_ns-ADLY_MIN_NS)/ADLY_RES_NS)); + half_shift_en = 1; + coerced_delay += ADLY_MIN_NS+(ADLY_RES_NS*adly_value)-half_vco_period_ns; + } else { + //Even after moving the digital delay back by half a cycle, we cannot make up the difference + //so give up on compensating for the difference from the digital delay tap. + //If control reaches here then the value of leftover_delay is possible very small and will still + //be close to what the client requested. + } + + UHD_LOGV(often) + << boost::format("x300_clock_ctrl::set_clock_delay: Which=%d, Requested=%f, Digital Taps=%d, Half Shift=%d, Analog Delay=%d (%s), Coerced Delay=%fns" + ) % which % delay_ns % ddly_value % (half_shift_en?"ON":"OFF") % ((int)adly_value) % (adly_en?"ON":"OFF") % coerced_delay << std::endl; + + //Apply settings + switch (which) + { + case X300_CLOCK_WHICH_FPGA: + _lmk04816_regs.CLKout0_1_DDLY = ddly_value; + _lmk04816_regs.CLKout0_1_HS = half_shift_en; + if (adly_en) { + _lmk04816_regs.CLKout0_ADLY_SEL = lmk04816_regs_t::CLKOUT0_ADLY_SEL_D_BOTH; + _lmk04816_regs.CLKout1_ADLY_SEL = lmk04816_regs_t::CLKOUT1_ADLY_SEL_D_BOTH; + _lmk04816_regs.CLKout0_1_ADLY = adly_value; + } else { + _lmk04816_regs.CLKout0_ADLY_SEL = lmk04816_regs_t::CLKOUT0_ADLY_SEL_D_PD; + _lmk04816_regs.CLKout1_ADLY_SEL = lmk04816_regs_t::CLKOUT1_ADLY_SEL_D_PD; + } + write_regs(0); + write_regs(6); + _delays.fpga_dly_ns = coerced_delay; + break; + case X300_CLOCK_WHICH_DB0_RX: + case X300_CLOCK_WHICH_DB1_RX: + _lmk04816_regs.CLKout2_3_DDLY = ddly_value; + _lmk04816_regs.CLKout2_3_HS = half_shift_en; + if (adly_en) { + _lmk04816_regs.CLKout2_ADLY_SEL = lmk04816_regs_t::CLKOUT2_ADLY_SEL_D_BOTH; + _lmk04816_regs.CLKout3_ADLY_SEL = lmk04816_regs_t::CLKOUT3_ADLY_SEL_D_BOTH; + _lmk04816_regs.CLKout2_3_ADLY = adly_value; + } else { + _lmk04816_regs.CLKout2_ADLY_SEL = lmk04816_regs_t::CLKOUT2_ADLY_SEL_D_PD; + _lmk04816_regs.CLKout3_ADLY_SEL = lmk04816_regs_t::CLKOUT3_ADLY_SEL_D_PD; + } + write_regs(1); + write_regs(6); + _delays.db_rx_dly_ns = coerced_delay; + break; + case X300_CLOCK_WHICH_DB0_TX: + case X300_CLOCK_WHICH_DB1_TX: + _lmk04816_regs.CLKout4_5_DDLY = ddly_value; + _lmk04816_regs.CLKout4_5_HS = half_shift_en; + if (adly_en) { + _lmk04816_regs.CLKout4_ADLY_SEL = lmk04816_regs_t::CLKOUT4_ADLY_SEL_D_BOTH; + _lmk04816_regs.CLKout5_ADLY_SEL = lmk04816_regs_t::CLKOUT5_ADLY_SEL_D_BOTH; + _lmk04816_regs.CLKout4_5_ADLY = adly_value; + } else { + _lmk04816_regs.CLKout4_ADLY_SEL = lmk04816_regs_t::CLKOUT4_ADLY_SEL_D_PD; + _lmk04816_regs.CLKout5_ADLY_SEL = lmk04816_regs_t::CLKOUT5_ADLY_SEL_D_PD; + } + write_regs(2); + write_regs(7); + _delays.db_tx_dly_ns = coerced_delay; + break; + case X300_CLOCK_WHICH_DAC0: + case X300_CLOCK_WHICH_DAC1: + _lmk04816_regs.CLKout6_7_DDLY = ddly_value; + _lmk04816_regs.CLKout6_7_HS = half_shift_en; + if (adly_en) { + _lmk04816_regs.CLKout6_ADLY_SEL = lmk04816_regs_t::CLKOUT6_ADLY_SEL_D_BOTH; + _lmk04816_regs.CLKout7_ADLY_SEL = lmk04816_regs_t::CLKOUT7_ADLY_SEL_D_BOTH; + _lmk04816_regs.CLKout6_7_ADLY = adly_value; + } else { + _lmk04816_regs.CLKout6_ADLY_SEL = lmk04816_regs_t::CLKOUT6_ADLY_SEL_D_PD; + _lmk04816_regs.CLKout7_ADLY_SEL = lmk04816_regs_t::CLKOUT7_ADLY_SEL_D_PD; + } + write_regs(3); + write_regs(7); + _delays.dac_dly_ns = coerced_delay; + break; + case X300_CLOCK_WHICH_ADC0: + case X300_CLOCK_WHICH_ADC1: + _lmk04816_regs.CLKout8_9_DDLY = ddly_value; + _lmk04816_regs.CLKout8_9_HS = half_shift_en; + if (adly_en) { + _lmk04816_regs.CLKout8_ADLY_SEL = lmk04816_regs_t::CLKOUT8_ADLY_SEL_D_BOTH; + _lmk04816_regs.CLKout9_ADLY_SEL = lmk04816_regs_t::CLKOUT9_ADLY_SEL_D_BOTH; + _lmk04816_regs.CLKout8_9_ADLY = adly_value; + } else { + _lmk04816_regs.CLKout8_ADLY_SEL = lmk04816_regs_t::CLKOUT8_ADLY_SEL_D_PD; + _lmk04816_regs.CLKout9_ADLY_SEL = lmk04816_regs_t::CLKOUT9_ADLY_SEL_D_PD; + } + write_regs(4); + write_regs(8); + _delays.adc_dly_ns = coerced_delay; + break; + default: + throw uhd::value_error("set_clock_delay: Requested source is invalid."); + } + + //Delays are applied only on a sync event + if (resync) sync_clocks(); + + return coerced_delay; + } + + double get_clock_delay(const x300_clock_which_t which) { + switch (which) + { + case X300_CLOCK_WHICH_FPGA: + return _delays.fpga_dly_ns; + case X300_CLOCK_WHICH_DB0_RX: + case X300_CLOCK_WHICH_DB1_RX: + return _delays.db_rx_dly_ns; + case X300_CLOCK_WHICH_DB0_TX: + case X300_CLOCK_WHICH_DB1_TX: + return _delays.db_tx_dly_ns; + case X300_CLOCK_WHICH_DAC0: + case X300_CLOCK_WHICH_DAC1: + return _delays.dac_dly_ns; + case X300_CLOCK_WHICH_ADC0: + case X300_CLOCK_WHICH_ADC1: + return _delays.adc_dly_ns; + default: + throw uhd::value_error("get_clock_delay: Requested source is invalid."); + } + } private: @@ -409,7 +615,6 @@ private: _lmk04816_regs.CLKout0_1_PD = lmk04816_regs_t::CLKOUT0_1_PD_POWER_UP; this->write_regs(0); _lmk04816_regs.CLKout0_1_DIV = master_clock_div; - _lmk04816_regs.CLKout0_ADLY_SEL = lmk04816_regs_t::CLKOUT0_ADLY_SEL_D_EV_X; this->write_regs(0); // Register 1 @@ -433,9 +638,6 @@ private: _lmk04816_regs.CLKout1_TYPE = lmk04816_regs_t::CLKOUT1_TYPE_P_DOWN; //CPRI feedback clock, use LVDS _lmk04816_regs.CLKout2_TYPE = lmk04816_regs_t::CLKOUT2_TYPE_LVPECL_700MVPP; //DB_0_RX _lmk04816_regs.CLKout3_TYPE = lmk04816_regs_t::CLKOUT3_TYPE_LVPECL_700MVPP; //DB_1_RX - // Analog delay of 900ps to synchronize the radio clock with the source synchronous ADC clocks. - // This delay may need to vary due to temperature. Tested and verified at room temperature only. - _lmk04816_regs.CLKout0_1_ADLY = 0x10; // Register 7 _lmk04816_regs.CLKout4_TYPE = lmk04816_regs_t::CLKOUT4_TYPE_LVPECL_700MVPP; //DB_1_TX @@ -501,6 +703,19 @@ private: // PLL2_P_30 set in individual cases above // PLL2_N_30 set in individual cases above + if (_hw_rev >= 7) { + _delays = X300_REV7_CLK_DELAYS; + } else { + _delays = X300_REV0_6_CLK_DELAYS; + } + + //Apply delay values + set_clock_delay(X300_CLOCK_WHICH_FPGA, _delays.fpga_dly_ns, false); + set_clock_delay(X300_CLOCK_WHICH_DB0_RX, _delays.db_rx_dly_ns, false); //Sets both Ch0 and Ch1 + set_clock_delay(X300_CLOCK_WHICH_DB0_TX, _delays.db_tx_dly_ns, false); //Sets both Ch0 and Ch1 + set_clock_delay(X300_CLOCK_WHICH_ADC0, _delays.adc_dly_ns, false); //Sets both Ch0 and Ch1 + set_clock_delay(X300_CLOCK_WHICH_DAC0, _delays.dac_dly_ns, false); //Sets both Ch0 and Ch1 + /* Write the configuration values into the LMK */ for (size_t i = 1; i <= 16; ++i) { this->write_regs(i); @@ -512,13 +727,14 @@ private: this->sync_clocks(); } - const spi_iface::sptr _spiface; - const size_t _slaveno; - const size_t _hw_rev; - const double _master_clock_rate; - const double _system_ref_rate; - lmk04816_regs_t _lmk04816_regs; - double _vco_freq; + const spi_iface::sptr _spiface; + const size_t _slaveno; + const size_t _hw_rev; + const double _master_clock_rate; + const double _system_ref_rate; + lmk04816_regs_t _lmk04816_regs; + double _vco_freq; + x300_clk_delays _delays; }; x300_clock_ctrl::sptr x300_clock_ctrl::make(uhd::spi_iface::sptr spiface, diff --git a/host/lib/usrp/x300/x300_clock_ctrl.hpp b/host/lib/usrp/x300/x300_clock_ctrl.hpp index 9c08aa356..160a14e6d 100644 --- a/host/lib/usrp/x300/x300_clock_ctrl.hpp +++ b/host/lib/usrp/x300/x300_clock_ctrl.hpp @@ -33,7 +33,7 @@ enum x300_clock_which_t X300_CLOCK_WHICH_DB0_TX, X300_CLOCK_WHICH_DB1_RX, X300_CLOCK_WHICH_DB1_TX, - X300_CLOCK_WHICH_TEST, + X300_CLOCK_WHICH_FPGA, }; class x300_clock_ctrl : boost::noncopyable @@ -94,6 +94,22 @@ public: */ virtual void set_ref_out(const bool) = 0; + /*! Set the clock delay for the given clock divider. + * \param which which clock + * \param rate the delay in nanoseconds + * \param resync resync clocks to apply delays + * \return the actual delay value set + * \throw exception when which invalid or delay_ns out of range + */ + virtual double set_clock_delay(const x300_clock_which_t which, const double delay_ns, const bool resync = true) = 0; + + /*! Get the clock delay for the given clock divider. + * \param which which clock + * \return the actual delay value set + * \throw exception when which invalid + */ + virtual double get_clock_delay(const x300_clock_which_t which) = 0; + /*! Reset the clocks. * Should be called if the reference clock changes * to reduce the time required to achieve a lock. diff --git a/host/lib/usrp/x300/x300_dac_ctrl.cpp b/host/lib/usrp/x300/x300_dac_ctrl.cpp index d3bcb8644..bb41146b6 100644 --- a/host/lib/usrp/x300/x300_dac_ctrl.cpp +++ b/host/lib/usrp/x300/x300_dac_ctrl.cpp @@ -129,12 +129,16 @@ public: _check_pll(); // Configure digital interface settings - write_ad9146_reg(0x16, 0x02); // Skew DCI signal by 615ps to find stable data eye - write_ad9146_reg(0x03, 0x00); // 2's comp, I first, byte wide interface - //fpga wants I,Q in the sample word: - //first transaction goes into low bits - //second transaction goes into high bits - //therefore, we want Q to go first (bit 6 == 1) + // Bypass DCI delay. We center the clock edge in the data + // valid window in the FPGA by phase shifting the DCI going + // to the DAC. + write_ad9146_reg(0x16, 0x04); + // 2's comp, I first, byte wide interface + write_ad9146_reg(0x03, 0x00); + // FPGA wants I,Q in the sample word: + // - First transaction goes into low bits + // - Second transaction goes into high bits + // therefore, we want Q to go first (bit 6 == 1) write_ad9146_reg(0x03, (1 << 6)); //2s comp, i first, byte mode // Configure interpolation filters diff --git a/host/lib/usrp/x300/x300_fw_common.h b/host/lib/usrp/x300/x300_fw_common.h index 76531f921..b0b7cfd02 100644 --- a/host/lib/usrp/x300/x300_fw_common.h +++ b/host/lib/usrp/x300/x300_fw_common.h @@ -29,10 +29,11 @@ extern "C" { #endif -#define X300_MAX_HW_REV 6 -#define X300_FW_COMPAT_MAJOR 3 +#define X300_REVISION_COMPAT 7 +#define X300_REVISION_MIN 2 +#define X300_FW_COMPAT_MAJOR 4 #define X300_FW_COMPAT_MINOR 0 -#define X300_FPGA_COMPAT_MAJOR 9 +#define X300_FPGA_COMPAT_MAJOR 14 //shared memory sections - in between the stack and the program space #define X300_FW_SHMEM_BASE 0x6000 diff --git a/host/lib/usrp/x300/x300_image_loader.cpp b/host/lib/usrp/x300/x300_image_loader.cpp new file mode 100644 index 000000000..9ec8a2e13 --- /dev/null +++ b/host/lib/usrp/x300/x300_image_loader.cpp @@ -0,0 +1,416 @@ +// +// Copyright 2015 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 <fstream> +#include <vector> + +#include <boost/algorithm/string.hpp> +#include <boost/filesystem.hpp> +#include <boost/property_tree/ptree.hpp> +#include <boost/property_tree/xml_parser.hpp> + +#include <uhd/config.hpp> +#include <uhd/device.hpp> +#include <uhd/image_loader.hpp> +#include <uhd/exception.hpp> +#include <uhd/transport/udp_simple.hpp> +#include <uhd/transport/nirio/niusrprio_session.h> +#include <uhd/transport/nirio/status.h> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/paths.hpp> +#include <uhd/utils/static.hpp> + +#include "x300_impl.hpp" +#include "x300_fw_common.h" +#include "cdecode.h" + +namespace fs = boost::filesystem; + +using namespace boost::algorithm; +using namespace uhd; +using namespace uhd::transport; + +/* + * Constants + */ +#define X300_FPGA_BIN_SIZE_BYTES 15877916 +#define X300_FPGA_BIT_SIZE_BYTES 15878032 +#define X300_FPGA_PROG_UDP_PORT 49157 +#define X300_FLASH_SECTOR_SIZE 131072 +#define X300_PACKET_SIZE_BYTES 256 +#define X300_FPGA_SECTOR_START 32 +#define X300_MAX_RESPONSE_BYTES 128 +#define UDP_TIMEOUT 3 +#define FPGA_LOAD_TIMEOUT 15 + +/* + * Packet structure + */ +typedef struct { + boost::uint32_t flags; + boost::uint32_t sector; + boost::uint32_t index; + boost::uint32_t size; + union { + boost::uint8_t data8[X300_PACKET_SIZE_BYTES]; + boost::uint16_t data16[X300_PACKET_SIZE_BYTES/2]; + }; +} x300_fpga_update_data_t; + +/* + * X-Series burn session + */ +typedef struct { + bool found; + bool ethernet; + bool configure; // Reload FPGA after burning to flash (Ethernet only) + bool verify; // Device will verify the download along the way (Ethernet only) + bool lvbitx; + uhd::device_addr_t dev_addr; + std::string ip_addr; + std::string fpga_type; + std::string resource; + std::string filepath; + std::string rpc_port; + boost::uint32_t size; + udp_simple::sptr xport; + std::vector<char> bitstream; // .bin image extracted from .lvbitx file + boost::uint8_t data_in[udp_simple::mtu]; +} x300_session_t; + +/* + * Extract the .bin image from the given LVBITX file. + */ +static void extract_from_lvbitx(x300_session_t &session){ + boost::property_tree::ptree pt; + boost::property_tree::xml_parser::read_xml(session.filepath.c_str(), pt, + boost::property_tree::xml_parser::no_comments | + boost::property_tree::xml_parser::trim_whitespace); + const std::string encoded_bitstream(pt.get<std::string>("Bitfile.Bitstream")); + std::vector<char> decoded_bitstream(encoded_bitstream.size()); + + base64_decodestate decode_state; + base64_init_decodestate(&decode_state); + const size_t decoded_size = base64_decode_block(encoded_bitstream.c_str(), + encoded_bitstream.size(), &decoded_bitstream.front(), &decode_state); + decoded_bitstream.resize(decoded_size); + session.bitstream.swap(decoded_bitstream); + + session.size = session.bitstream.size(); +} + +/* + * Validate X300 image and extract if LVBITX. + */ +static void x300_validate_image(x300_session_t &session){ + if(not fs::exists(session.filepath)){ + throw uhd::runtime_error(str(boost::format("Could not find image at path \"%s\".") + % session.filepath)); + } + + std::string extension = fs::extension(session.filepath); + session.lvbitx = (extension == ".lvbitx"); + + if(session.lvbitx){ + extract_from_lvbitx(session); + if(session.size > X300_FPGA_BIN_SIZE_BYTES){ + throw uhd::runtime_error(str(boost::format("The specified FPGA image is too large: %d vs. %d") + % session.size % X300_FPGA_BIN_SIZE_BYTES)); + } + + /* + * PCIe burning just takes a filepath, even for a .lvbitx file, + * so just extract it to validate the size. + */ + if(!session.ethernet) session.bitstream.clear(); + } + else if(extension == ".bin" or extension == ".bit"){ + boost::uint32_t max_size = (extension == ".bin") ? X300_FPGA_BIN_SIZE_BYTES + : X300_FPGA_BIT_SIZE_BYTES; + + session.size = fs::file_size(session.filepath); + if(session.size > max_size){ + throw uhd::runtime_error(str(boost::format("The specified FPGA image is too large: %d vs. %d") + % session.size % max_size)); + return; + } + } + else{ + throw uhd::runtime_error(str(boost::format("Invalid extension \"%s\". Extension must be .bin, .bit, or .lvbitx.") + % extension)); + } +} + +static void x300_setup_session(x300_session_t &session, + const device_addr_t &args, + const std::string &filepath){ + device_addrs_t devs = x300_find(args); + if(devs.size() == 0){ + session.found = false; + return; + } + else if(devs.size() > 1){ + std::string err_msg = "Could not resolve given args to a single X-Series device.\n" + "Applicable devices:\n"; + + BOOST_FOREACH(const uhd::device_addr_t &dev, devs){ + std::string identifier = dev.has_key("addr") ? "addr" + : "resource"; + + err_msg += str(boost::format(" * %s (%s=%s)\n") + % dev.get("product", "X3XX") + % identifier + % dev.get(identifier)); + } + + err_msg += "\nSpecify one of these devices with the given args to load an image onto it."; + + throw uhd::runtime_error(err_msg); + } + + session.found = true; + session.dev_addr = devs[0]; + session.ethernet = session.dev_addr.has_key("addr"); + if(session.ethernet){ + session.ip_addr = session.dev_addr["addr"]; + session.configure = args.has_key("configure"); + session.xport = udp_simple::make_connected(session.ip_addr, + BOOST_STRINGIZE(X300_FPGA_PROG_UDP_PORT)); + session.verify = args.has_key("verify"); + } + else{ + session.resource = session.dev_addr["resource"]; + session.rpc_port = args.get("rpc-port", "5444"); + } + + /* + * The user can specify an FPGA type (1G, HGS, XGS), rather than a filename. If the user + * does not specify one, this will default to the type currently on the device. If this + * cannot be determined, then the user is forced to specify a filename. + */ + session.fpga_type = args.get("fpga", session.dev_addr.get("fpga", "")); + if(filepath == ""){ + if(!session.dev_addr.has_key("product") or session.fpga_type == ""){ + throw uhd::runtime_error("Found a device but could not auto-generate an image filename."); + } + else session.filepath = find_image_path(str(boost::format("usrp_%s_fpga_%s.bit") + % (to_lower_copy(session.dev_addr["product"])) + % session.fpga_type)); + } + else session.filepath = filepath; + + // Validate image + x300_validate_image(session); +} + +/* + * Ethernet communication functions + */ +static UHD_INLINE size_t x300_send_and_recv(udp_simple::sptr xport, + boost::uint32_t pkt_code, + x300_fpga_update_data_t *pkt_out, + boost::uint8_t* data){ + pkt_out->flags = uhd::htonx<boost::uint32_t>(pkt_code); + xport->send(boost::asio::buffer(pkt_out, sizeof(*pkt_out))); + return xport->recv(boost::asio::buffer(data, udp_simple::mtu), UDP_TIMEOUT); +} + +static UHD_INLINE bool x300_recv_ok(const x300_fpga_update_data_t *pkt_in, + size_t len){ + return (len > 0 and + ((ntohl(pkt_in->flags) & X300_FPGA_PROG_FLAGS_ERROR) != X300_FPGA_PROG_FLAGS_ERROR)); +} + +// Image data needs to be bitswapped +static UHD_INLINE void x300_bitswap(boost::uint8_t *num){ + *num = ((*num & 0xF0) >> 4) | ((*num & 0x0F) << 4); + *num = ((*num & 0xCC) >> 2) | ((*num & 0x33) << 2); + *num = ((*num & 0xAA) >> 1) | ((*num & 0x55) << 1); +} + +static void x300_ethernet_load(x300_session_t &session){ + + // UDP receive buffer + x300_fpga_update_data_t pkt_out; + const x300_fpga_update_data_t *pkt_in = reinterpret_cast<const x300_fpga_update_data_t*>(session.data_in); + + // Initialize write session + boost::uint32_t flags = X300_FPGA_PROG_FLAGS_ACK | X300_FPGA_PROG_FLAGS_INIT; + size_t len = x300_send_and_recv(session.xport, flags, &pkt_out, session.data_in); + if(x300_recv_ok(pkt_in, len)){ + std::cout << "-- Initializing FPGA loading..." << std::flush; + } + else if(len == 0){ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Timed out waiting for reply from device."); + } + else{ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Device reported an error during initialization."); + } + + std::cout << "successful." << std::endl; + if(session.verify){ + std::cout << "-- NOTE: Device is verifying the image it is receiving, increasing the loading time." << std::endl; + } + + size_t current_pos = 0; + size_t sectors = (session.size / X300_FLASH_SECTOR_SIZE); + std::ifstream image(session.filepath.c_str(), std::ios::binary); + + // Each sector + for(size_t i = 0; i < session.size; i += X300_FLASH_SECTOR_SIZE){ + + // Print progress percentage at beginning of each sector + std::cout << boost::format("\r-- Loading %s FPGA image: %d%% (%d/%d sectors)") + % session.fpga_type + % (int(double(i) / double(session.size) * 100.0)) + % (i / X300_FLASH_SECTOR_SIZE) + % sectors + << std::flush; + + // Each packet + for(size_t j = i; (j < session.size and j < (i+X300_FLASH_SECTOR_SIZE)); j += X300_PACKET_SIZE_BYTES){ + flags = X300_FPGA_PROG_FLAGS_ACK; + if(j == i) flags |= X300_FPGA_PROG_FLAGS_ERASE; // Erase at beginning of sector + if(session.verify) flags |= X300_FPGA_PROG_FLAGS_VERIFY; + + // Set burn location + pkt_out.sector = htonx<boost::uint32_t>(X300_FPGA_SECTOR_START + (i/X300_FLASH_SECTOR_SIZE)); + pkt_out.index = htonx<boost::uint32_t>((j % X300_FLASH_SECTOR_SIZE) / 2); + pkt_out.size = htonx<boost::uint32_t>(X300_PACKET_SIZE_BYTES / 2); + + // Read next piece of image + memset(pkt_out.data8, 0, X300_PACKET_SIZE_BYTES); + if(session.lvbitx){ + memcpy(pkt_out.data8, &session.bitstream[current_pos], X300_PACKET_SIZE_BYTES); + current_pos += X300_PACKET_SIZE_BYTES; + } + else{ + image.read((char*)pkt_out.data8, X300_PACKET_SIZE_BYTES); + } + + // Data must be bitswapped and byteswapped + for(size_t k = 0; k < X300_PACKET_SIZE_BYTES; k++){ + x300_bitswap(&pkt_out.data8[k]); + } + for(size_t k = 0; k < (X300_PACKET_SIZE_BYTES/2); k++){ + pkt_out.data16[k] = htonx<boost::uint16_t>(pkt_out.data16[k]); + } + + len = x300_send_and_recv(session.xport, flags, &pkt_out, session.data_in); + if(len == 0){ + if(!session.lvbitx) image.close(); + throw uhd::runtime_error("Timed out waiting for reply from device."); + } + else if((ntohl(pkt_in->flags) & X300_FPGA_PROG_FLAGS_ERROR)){ + if(!session.lvbitx) image.close(); + throw uhd::runtime_error("Device reported an error."); + } + } + } + if(!session.lvbitx){ + image.close(); + } + + std::cout << boost::format("\r-- Loading %s FPGA image: 100%% (%d/%d sectors)") + % session.fpga_type + % sectors + % sectors + << std::endl; + + // Cleanup + if(!session.lvbitx) image.close(); + flags = (X300_FPGA_PROG_FLAGS_CLEANUP | X300_FPGA_PROG_FLAGS_ACK); + pkt_out.sector = pkt_out.index = pkt_out.size = 0; + memset(pkt_out.data8, 0, X300_PACKET_SIZE_BYTES); + std::cout << "-- Finalizing image load..." << std::flush; + len = x300_send_and_recv(session.xport, flags, &pkt_out, session.data_in); + if(len == 0){ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Timed out waiting for reply from device."); + } + else if((ntohl(pkt_in->flags) & X300_FPGA_PROG_FLAGS_ERROR)){ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Device reported an error during cleanup."); + } + else std::cout << "successful." << std::endl; + + // Save new FPGA image (if option set) + if(session.configure){ + flags = (X300_FPGA_PROG_CONFIGURE | X300_FPGA_PROG_FLAGS_ACK); + x300_send_and_recv(session.xport, flags, &pkt_out, session.data_in); + std::cout << "-- Saving image onto device..." << std::flush; + if(len == 0){ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Timed out waiting for reply from device."); + } + else if((ntohl(pkt_in->flags) & X300_FPGA_PROG_FLAGS_ERROR)){ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Device reported an error while saving the image."); + } + else std::cout << "successful." << std::endl; + } +} + +static void x300_pcie_load(x300_session_t &session){ + + std::cout << boost::format("\r-- Loading %s FPGA image (this will take 5-10 minutes)...") + % session.fpga_type + << std::flush; + + nirio_status status = NiRio_Status_Success; + niusrprio::niusrprio_session fpga_session(session.resource, session.rpc_port); + nirio_status_chain(fpga_session.download_bitstream_to_flash(session.filepath), status); + + if(nirio_status_fatal(status)){ + std::cout << "failed." << std::endl; + niusrprio::nirio_status_to_exception(status, "NI-RIO reported the following error:"); + } + else std::cout << "successful." << std::endl; +} + +static bool x300_image_loader(const image_loader::image_loader_args_t &image_loader_args){ + // See if any X3x0 with the given args is found + device_addrs_t devs = x300_find(image_loader_args.args); + if(devs.size() == 0 or !image_loader_args.load_fpga) return false; + + x300_session_t session; + x300_setup_session(session, + image_loader_args.args, + image_loader_args.fpga_path + ); + if(!session.found) return false; + + std::cout << boost::format("Unit: USRP %s (%s, %s)\nFPGA Image: %s\n") + % session.dev_addr["product"] + % session.dev_addr["serial"] + % session.dev_addr[session.ethernet ? "addr" : "resource"] + % session.filepath; + + if(session.ethernet) x300_ethernet_load(session); + else x300_pcie_load(session); + return true; +} + +UHD_STATIC_BLOCK(register_x300_image_loader){ + std::string recovery_instructions = "Aborting. Your USRP X-Series device will likely be unusable. Visit\n" + "http://files.ettus.com/manual/page_usrp_x3x0.html#x3x0_load_fpga_imgs_jtag\n" + "for details on restoring your device."; + + image_loader::register_image_loader("x300", x300_image_loader, recovery_instructions); +} diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp index aff150acb..2b6768079 100644 --- a/host/lib/usrp/x300/x300_impl.cpp +++ b/host/lib/usrp/x300/x300_impl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2013-2014 Ettus Research LLC +// Copyright 2013-2015 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 @@ -16,7 +16,6 @@ // #include "x300_impl.hpp" -#include "x300_regs.hpp" #include "x300_lvbitx.hpp" #include "x310_lvbitx.hpp" #include <boost/algorithm/string.hpp> @@ -30,6 +29,7 @@ #include <uhd/transport/if_addrs.hpp> #include <boost/foreach.hpp> #include <boost/bind.hpp> +#include <boost/make_shared.hpp> #include <boost/functional/hash.hpp> #include <boost/assign/list_of.hpp> #include <fstream> @@ -41,12 +41,13 @@ #define NIUSRPRIO_DEFAULT_RPC_PORT "5444" -#define X300_REV(x) (x - "A" + 1) +#define X300_REV(x) ((x) - "A" + 1) using namespace uhd; using namespace uhd::usrp; using namespace uhd::transport; using namespace uhd::niusrprio; +using namespace uhd::usrp::x300; namespace asio = boost::asio; /*********************************************************************** @@ -236,7 +237,7 @@ static device_addrs_t x300_find_pcie(const device_addr_t &hint, bool explicit_qu return addrs; } -static device_addrs_t x300_find(const device_addr_t &hint_) +device_addrs_t x300_find(const device_addr_t &hint_) { //handle the multi-device discovery device_addrs_t hints = separate_device_addr(hint_); @@ -400,7 +401,7 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) default: nirio_status_to_exception(status, "Motherboard detection error. Please ensure that you \ have a valid USRP X3x0, NI USRP-294xR or NI USRP-295xR device and that all the device \ - driver have been loaded."); + drivers have loaded successfully."); } //Load the lvbitx onto the device UHD_MSG(status) << boost::format("Using LVBITX bitfile %s...\n") % lvbitx->get_bitfile_path(); @@ -410,7 +411,7 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) //Tell the quirks object which FIFOs carry TX stream data const boost::uint32_t tx_data_fifos[2] = {X300_RADIO_DEST_PREFIX_TX, X300_RADIO_DEST_PREFIX_TX + 3}; - mb.rio_fpga_interface->get_kernel_proxy()->get_rio_quirks().register_tx_streams(tx_data_fifos); + mb.rio_fpga_interface->get_kernel_proxy()->get_rio_quirks().register_tx_streams(tx_data_fifos, 2); _tree->create<double>(mb_path / "link_max_rate").set(X300_MAX_RATE_PCIE); } @@ -508,9 +509,13 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) x300_load_fw(mb.zpu_ctrl, x300_fw_image); } - //check compat -- good place to do after conditional loading + //check compat numbers + //check fpga compat before fw compat because the fw is a subset of the fpga image + this->check_fpga_compat(mb_path, mb); this->check_fw_compat(mb_path, mb.zpu_ctrl); - this->check_fpga_compat(mb_path, mb.zpu_ctrl); + + mb.fw_regmap = boost::make_shared<fw_regmap_t>(); + mb.fw_regmap->initialize(*mb.zpu_ctrl.get(), true); //store which FPGA image is loaded mb.loaded_fpga_image = get_fpga_option(mb.zpu_ctrl); @@ -558,6 +563,13 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) .set(mb_eeprom) .subscribe(boost::bind(&x300_impl::set_mb_eeprom, this, mb.zpu_i2c, _1)); + bool recover_mb_eeprom = dev_addr.has_key("recover_mb_eeprom"); + if (recover_mb_eeprom) { + UHD_MSG(warning) << "UHD is operating in EEPROM Recovery Mode which disables hardware version " + "checks.\nOperating in this mode may cause hardware damage and unstable " + "radio performance!"<< std::endl; + } + //////////////////////////////////////////////////////////////////// // parse the product number //////////////////////////////////////////////////////////////////// @@ -570,7 +582,10 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) product_name = "X310"; break; default: - break; + if (not recover_mb_eeprom) + throw uhd::runtime_error("Unrecognized product type.\n" + "Either the software does not support this device in which case please update your driver software to the latest version and retry OR\n" + "The product code in the EEPROM is corrupt and may require reprogramming."); } _tree->create<std::string>(mb_path / "name").set(product_name); _tree->create<std::string>(mb_path / "codename").set("Yetti"); @@ -602,37 +617,57 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) } //////////////////////////////////////////////////////////////////// - // create clock control objects + // read hardware revision and compatibility number //////////////////////////////////////////////////////////////////// - UHD_MSG(status) << "Setup RF frontend clocking..." << std::endl; - mb.hw_rev = 0; if(mb_eeprom.has_key("revision") and not mb_eeprom["revision"].empty()) { try { mb.hw_rev = boost::lexical_cast<size_t>(mb_eeprom["revision"]); } catch(...) { - UHD_MSG(warning) << "Revision in EEPROM is invalid! Please reprogram your EEPROM." << std::endl; + if (not recover_mb_eeprom) + throw uhd::runtime_error("Revision in EEPROM is invalid! Please reprogram your EEPROM."); } } else { - UHD_MSG(warning) << "No revision detected MB EEPROM must be reprogrammed!" << std::endl; + if (not recover_mb_eeprom) + throw uhd::runtime_error("No revision detected. MB EEPROM must be reprogrammed!"); } - if(mb.hw_rev == 0) { - UHD_MSG(warning) << "Defaulting to X300 RevD Clock Settings. This will result in non-optimal lock times." << std::endl; - mb.hw_rev = X300_REV("D"); + size_t hw_rev_compat = 0; + if (mb.hw_rev >= 7) { //Revision compat was added with revision 7 + if (mb_eeprom.has_key("revision_compat") and not mb_eeprom["revision_compat"].empty()) { + try { + hw_rev_compat = boost::lexical_cast<size_t>(mb_eeprom["revision_compat"]); + } catch(...) { + if (not recover_mb_eeprom) + throw uhd::runtime_error("Revision compat in EEPROM is invalid! Please reprogram your EEPROM."); + } + } else { + if (not recover_mb_eeprom) + throw uhd::runtime_error("No revision compat detected. MB EEPROM must be reprogrammed!"); + } + } else { + //For older HW just assume that revision_compat = revision + hw_rev_compat = mb.hw_rev; } - if (mb.hw_rev > X300_MAX_HW_REV) { - throw uhd::runtime_error(str( - boost::format("Unsupported board revision number: %d.\n" - "The maximum board revision number supported in this version is %d.\n" - "Please update your UHD version.") - % mb.hw_rev % X300_MAX_HW_REV - )); + if (hw_rev_compat > X300_REVISION_COMPAT) { + if (not recover_mb_eeprom) + throw uhd::runtime_error(str(boost::format( + "Hardware is too new for this software. Please upgrade to a driver that supports hardware revision %d.") + % mb.hw_rev)); + } else if (mb.hw_rev < X300_REVISION_MIN) { //Compare min against the revision (and not compat) to give us more leeway for partial support for a compat + if (not recover_mb_eeprom) + throw uhd::runtime_error(str(boost::format( + "Software is too new for this hardware. Please downgrade to a driver that supports hardware revision %d.") + % mb.hw_rev)); } - //Create clock control. NOTE: This does not configure the LMK yet. - initialize_clock_control(mb); + //////////////////////////////////////////////////////////////////// + // create clock control objects + //////////////////////////////////////////////////////////////////// + UHD_MSG(status) << "Setup RF frontend clocking..." << std::endl; + + //Initialize clock control registers. NOTE: This does not configure the LMK yet. mb.clock = x300_clock_ctrl::make(mb.zpu_spi, 1 /*slaveno*/, mb.hw_rev, @@ -696,23 +731,33 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) //////////////////////////////////////////////////////////////////// // setup radios //////////////////////////////////////////////////////////////////// - UHD_MSG(status) << "Initialize Radio control..." << std::endl; - this->setup_radio(mb_i, "A"); - this->setup_radio(mb_i, "B"); + this->setup_radio(mb_i, "A", dev_addr); + this->setup_radio(mb_i, "B", dev_addr); + + //////////////////////////////////////////////////////////////////// + // ADC test and cal + //////////////////////////////////////////////////////////////////// + if (dev_addr.has_key("self_cal_adc_delay")) { + self_cal_adc_xfer_delay(mb, true /* Apply ADC delay */); + } + if (dev_addr.has_key("ext_adc_self_test")) { + extended_adc_test(mb, dev_addr.cast<double>("ext_adc_self_test", 30)); + } else { + self_test_adcs(mb); + } //////////////////////////////////////////////////////////////////// // front panel gpio //////////////////////////////////////////////////////////////////// - mb.fp_gpio = gpio_core_200::make(mb.radio_perifs[0].ctrl, TOREG(SR_FP_GPIO), RB32_FP_GPIO); - const std::vector<std::string> GPIO_ATTRS = boost::assign::list_of("CTRL")("DDR")("OUT")("ATR_0X")("ATR_RX")("ATR_TX")("ATR_XX"); - BOOST_FOREACH(const std::string &attr, GPIO_ATTRS) + mb.fp_gpio = gpio_core_200::make(mb.radio_perifs[0].ctrl, radio::sr_addr(radio::GPIO), radio::RB32_FP_GPIO); + BOOST_FOREACH(const gpio_attr_map_t::value_type attr, gpio_attr_map) { - _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / attr) + _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / attr.second) .set(0) - .subscribe(boost::bind(&x300_impl::set_fp_gpio, this, mb.fp_gpio, attr, _1)); + .subscribe(boost::bind(&x300_impl::set_fp_gpio, this, mb.fp_gpio, attr.first, _1)); } _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / "READBACK") - .publish(boost::bind(&x300_impl::get_fp_gpio, this, mb.fp_gpio, "READBACK")); + .publish(boost::bind(&x300_impl::get_fp_gpio, this, mb.fp_gpio)); //////////////////////////////////////////////////////////////////// // register the time keepers - only one can be the highlander @@ -745,8 +790,7 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) //////////////////////////////////////////////////////////////////// _tree->create<std::string>(mb_path / "clock_source" / "value") .set("internal") - .subscribe(boost::bind(&x300_impl::update_clock_source, this, boost::ref(mb), _1)) - .subscribe(boost::bind(&x300_impl::reset_radios, this, boost::ref(mb))); + .subscribe(boost::bind(&x300_impl::update_clock_source, this, boost::ref(mb), _1)); static const std::vector<std::string> clock_source_options = boost::assign::list_of("internal")("external")("gpsdo"); _tree->create<std::vector<std::string> >(mb_path / "clock_source" / "options").set(clock_source_options); @@ -771,23 +815,6 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) .set(mb.clock->get_master_clock_rate()); //////////////////////////////////////////////////////////////////// - // initialize clock and time sources - //////////////////////////////////////////////////////////////////// - if (mb.gps and mb.gps->gps_detected()) - { - UHD_MSG(status) << "Initializing clock and time using GPSDO... " << std::flush; - _tree->access<std::string>(mb_path / "clock_source" / "value").set("gpsdo"); - _tree->access<std::string>(mb_path / "time_source" / "value").set("gpsdo"); - const time_t tp = time_t(mb.gps->get_sensor("gps_time").to_int() + 1); - _tree->access<time_spec_t>(mb_path / "time" / "pps").set(time_spec_t(tp)); - } else { - UHD_MSG(status) << "Initializing clock and time using internal sources... " << std::flush; - _tree->access<std::string>(mb_path / "clock_source" / "value").set("internal"); - _tree->access<std::string>(mb_path / "time_source" / "value").set("internal"); - } - UHD_MSG(status) << "done" << std::endl; - - //////////////////////////////////////////////////////////////////// // create frontend mapping //////////////////////////////////////////////////////////////////// std::vector<size_t> default_map(2, 0); default_map[1] = 1; @@ -802,7 +829,7 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) // and do the misc mboard sensors //////////////////////////////////////////////////////////////////// _tree->create<sensor_value_t>(mb_path / "sensors" / "ref_locked") - .publish(boost::bind(&x300_impl::get_ref_locked, this, mb.zpu_ctrl)); + .publish(boost::bind(&x300_impl::get_ref_locked, this, mb)); //////////////////////////////////////////////////////////////////// // do some post-init tasks @@ -820,6 +847,14 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(rx_fe_spec); _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(tx_fe_spec); + mb.regmap_db = boost::make_shared<uhd::soft_regmap_db_t>(); + mb.regmap_db->add(*mb.fw_regmap); + mb.regmap_db->add(*mb.radio_perifs[0].regmap); + mb.regmap_db->add(*mb.radio_perifs[1].regmap); + + _tree->create<uhd::soft_regmap_accessor_t::sptr>(mb_path / "registers") + .set(mb.regmap_db); + mb.initialization_done = true; } @@ -829,8 +864,13 @@ x300_impl::~x300_impl(void) { BOOST_FOREACH(mboard_members_t &mb, _mb) { - mb.radio_perifs[0].ctrl->poke32(TOREG(SR_MISC_OUTS), (1 << 2)); //disable/reset ADC/DAC - mb.radio_perifs[1].ctrl->poke32(TOREG(SR_MISC_OUTS), (1 << 2)); //disable/reset ADC/DAC + //Disable/reset ADC/DAC + mb.radio_perifs[0].regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 1); + mb.radio_perifs[0].regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 0); + mb.radio_perifs[0].regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_ENABLED, 0); + mb.radio_perifs[0].regmap->misc_outs_reg.flush(); + mb.radio_perifs[1].regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_ENABLED, 0); + mb.radio_perifs[1].regmap->misc_outs_reg.flush(); //kill the claimer task and unclaim the device mb.claimer_task.reset(); @@ -850,15 +890,7 @@ x300_impl::~x300_impl(void) } } -static void check_adc(wb_iface::sptr iface, const boost::uint32_t val) -{ - boost::uint32_t adc_rb = iface->peek32(RB32_RX); - adc_rb ^= 0xfffc0000; //adapt for I inversion in FPGA - //UHD_MSG(status) << "adc_rb " << std::hex << adc_rb << " val " << std::hex << val << std::endl; - UHD_ASSERT_THROW(adc_rb == val); -} - -void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name) +void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name, const uhd::device_addr_t &dev_addr) { const fs_path mb_path = "/mboards/"+boost::lexical_cast<std::string>(mb_i); UHD_ASSERT_THROW(mb_i < _mb.size()); @@ -866,6 +898,8 @@ void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name) const size_t radio_index = mb.get_radio_index(slot_name); radio_perifs_t &perif = mb.radio_perifs[radio_index]; + UHD_MSG(status) << boost::format("Initialize Radio%d control...") % radio_index << std::endl; + //////////////////////////////////////////////////////////////////// // radio control //////////////////////////////////////////////////////////////////// @@ -873,39 +907,57 @@ void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name) boost::uint32_t ctrl_sid; both_xports_t xport = this->make_transport(mb_i, dest, X300_RADIO_DEST_PREFIX_CTRL, device_addr_t(), ctrl_sid); perif.ctrl = radio_ctrl_core_3000::make(mb.if_pkt_is_big_endian, xport.recv, xport.send, ctrl_sid, slot_name); - perif.ctrl->poke32(TOREG(SR_MISC_OUTS), (1 << 2)); //reset adc + dac - perif.ctrl->poke32(TOREG(SR_MISC_OUTS), (1 << 1) | (1 << 0)); //out of reset + dac enable + + perif.regmap = boost::make_shared<radio_regmap_t>(radio_index); + perif.regmap->initialize(*perif.ctrl, true); + + //Only Radio0 has the ADC/DAC reset bits. Those bits are reserved for Radio1 + if (radio_index == 0) { + perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 1); + perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 0); + perif.regmap->misc_outs_reg.flush(); + perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 0); + perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 1); + perif.regmap->misc_outs_reg.flush(); + } + perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::DAC_ENABLED, 1); this->register_loopback_self_test(perif.ctrl); - perif.spi = spi_core_3000::make(perif.ctrl, TOREG(SR_SPI), RB32_SPI); + //////////////////////////////////////////////////////////////// + // Setup peripherals + //////////////////////////////////////////////////////////////// + perif.spi = spi_core_3000::make(perif.ctrl, radio::sr_addr(radio::SPI), radio::RB32_SPI); perif.adc = x300_adc_ctrl::make(perif.spi, DB_ADC_SEN); perif.dac = x300_dac_ctrl::make(perif.spi, DB_DAC_SEN, mb.clock->get_master_clock_rate()); - perif.leds = gpio_core_200_32wo::make(perif.ctrl, TOREG(SR_LEDS)); + perif.leds = gpio_core_200_32wo::make(perif.ctrl, radio::sr_addr(radio::LEDS)); + perif.rx_fe = rx_frontend_core_200::make(perif.ctrl, radio::sr_addr(radio::RX_FRONT)); + perif.rx_fe->set_dc_offset(rx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE); + perif.rx_fe->set_dc_offset_auto(rx_frontend_core_200::DEFAULT_DC_OFFSET_ENABLE); + perif.tx_fe = tx_frontend_core_200::make(perif.ctrl, radio::sr_addr(radio::TX_FRONT)); + perif.tx_fe->set_dc_offset(tx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE); + perif.tx_fe->set_iq_balance(tx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE); + perif.framer = rx_vita_core_3000::make(perif.ctrl, radio::sr_addr(radio::RX_CTRL)); + perif.ddc = rx_dsp_core_3000::make(perif.ctrl, radio::sr_addr(radio::RX_DSP)); + perif.ddc->set_link_rate(10e9/8); //whatever + perif.deframer = tx_vita_core_3000::make(perif.ctrl, radio::sr_addr(radio::TX_CTRL)); + perif.duc = tx_dsp_core_3000::make(perif.ctrl, radio::sr_addr(radio::TX_DSP)); + perif.duc->set_link_rate(10e9/8); //whatever + + //////////////////////////////////////////////////////////////////// + // create time control objects + //////////////////////////////////////////////////////////////////// + time_core_3000::readback_bases_type time64_rb_bases; + time64_rb_bases.rb_now = radio::RB64_TIME_NOW; + time64_rb_bases.rb_pps = radio::RB64_TIME_PPS; + perif.time64 = time_core_3000::make(perif.ctrl, radio::sr_addr(radio::TIME), time64_rb_bases); + + //Capture delays are calibrated every time. The status is only printed is the user + //asks to run the xfer self cal using "self_cal_adc_delay" + self_cal_adc_capture_delay(mb, radio_index, dev_addr.has_key("self_cal_adc_delay")); _tree->access<time_spec_t>(mb_path / "time" / "cmd") .subscribe(boost::bind(&radio_ctrl_core_3000::set_time, perif.ctrl, _1)); - _tree->access<double>(mb_path / "tick_rate") - .subscribe(boost::bind(&radio_ctrl_core_3000::set_tick_rate, perif.ctrl, _1)); - - //////////////////////////////////////////////////////////////// - // ADC self test - //////////////////////////////////////////////////////////////// - perif.adc->set_test_word("ones", "ones"); check_adc(perif.ctrl, 0xfffcfffc); - perif.adc->set_test_word("zeros", "zeros"); check_adc(perif.ctrl, 0x00000000); - perif.adc->set_test_word("ones", "zeros"); check_adc(perif.ctrl, 0xfffc0000); - perif.adc->set_test_word("zeros", "ones"); check_adc(perif.ctrl, 0x0000fffc); - for (size_t k = 0; k < 14; k++) - { - perif.adc->set_test_word("zeros", "custom", 1 << k); - check_adc(perif.ctrl, 1 << (k+2)); - } - for (size_t k = 0; k < 14; k++) - { - perif.adc->set_test_word("custom", "zeros", 1 << k); - check_adc(perif.ctrl, 1 << (k+18)); - } - perif.adc->set_test_word("normal", "normal"); //////////////////////////////////////////////////////////////// // create codec control objects @@ -922,80 +974,28 @@ void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name) //////////////////////////////////////////////////////////////////// // front end corrections //////////////////////////////////////////////////////////////////// - perif.rx_fe = rx_frontend_core_200::make(perif.ctrl, TOREG(SR_RX_FRONT)); - const fs_path rx_fe_path = mb_path / "rx_frontends" / slot_name; - _tree->create<std::complex<double> >(rx_fe_path / "dc_offset" / "value") - .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, perif.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, perif.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, perif.rx_fe, _1)) - .set(std::complex<double>(0.0, 0.0)); - - perif.tx_fe = tx_frontend_core_200::make(perif.ctrl, TOREG(SR_TX_FRONT)); - const fs_path tx_fe_path = mb_path / "tx_frontends" / slot_name; - _tree->create<std::complex<double> >(tx_fe_path / "dc_offset" / "value") - .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, perif.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, perif.tx_fe, _1)) - .set(std::complex<double>(0.0, 0.0)); + perif.rx_fe->populate_subtree(_tree->subtree(mb_path / "rx_frontends" / slot_name)); + perif.tx_fe->populate_subtree(_tree->subtree(mb_path / "tx_frontends" / slot_name)); //////////////////////////////////////////////////////////////////// - // create rx dsp control objects + // connect rx dsp control objects //////////////////////////////////////////////////////////////////// - perif.framer = rx_vita_core_3000::make(perif.ctrl, TOREG(SR_RX_CTRL)); - perif.ddc = rx_dsp_core_3000::make(perif.ctrl, TOREG(SR_RX_DSP)); - perif.ddc->set_link_rate(10e9/8); //whatever - _tree->access<double>(mb_path / "tick_rate") - .subscribe(boost::bind(&rx_vita_core_3000::set_tick_rate, perif.framer, _1)) - .subscribe(boost::bind(&rx_dsp_core_3000::set_tick_rate, perif.ddc, _1)); const fs_path rx_dsp_path = mb_path / "rx_dsps" / str(boost::format("%u") % radio_index); - _tree->create<meta_range_t>(rx_dsp_path / "rate" / "range") - .publish(boost::bind(&rx_dsp_core_3000::get_host_rates, perif.ddc)); - _tree->create<double>(rx_dsp_path / "rate" / "value") - .coerce(boost::bind(&rx_dsp_core_3000::set_host_rate, perif.ddc, _1)) + perif.ddc->populate_subtree(_tree->subtree(rx_dsp_path)); + _tree->access<double>(rx_dsp_path / "rate" / "value") .subscribe(boost::bind(&x300_impl::update_rx_samp_rate, this, boost::ref(mb), radio_index, _1)) - .set(1e6); - _tree->create<double>(rx_dsp_path / "freq" / "value") - .coerce(boost::bind(&rx_dsp_core_3000::set_freq, perif.ddc, _1)) - .set(0.0); - _tree->create<meta_range_t>(rx_dsp_path / "freq" / "range") - .publish(boost::bind(&rx_dsp_core_3000::get_freq_range, perif.ddc)); + ; _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd") .subscribe(boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1)); //////////////////////////////////////////////////////////////////// - // create tx dsp control objects + // connect tx dsp control objects //////////////////////////////////////////////////////////////////// - perif.deframer = tx_vita_core_3000::make(perif.ctrl, TOREG(SR_TX_CTRL)); - perif.duc = tx_dsp_core_3000::make(perif.ctrl, TOREG(SR_TX_DSP)); - perif.duc->set_link_rate(10e9/8); //whatever - _tree->access<double>(mb_path / "tick_rate") - .subscribe(boost::bind(&tx_vita_core_3000::set_tick_rate, perif.deframer, _1)) - .subscribe(boost::bind(&tx_dsp_core_3000::set_tick_rate, perif.duc, _1)); const fs_path tx_dsp_path = mb_path / "tx_dsps" / str(boost::format("%u") % radio_index); - _tree->create<meta_range_t>(tx_dsp_path / "rate" / "range") - .publish(boost::bind(&tx_dsp_core_3000::get_host_rates, perif.duc)); - _tree->create<double>(tx_dsp_path / "rate" / "value") - .coerce(boost::bind(&tx_dsp_core_3000::set_host_rate, perif.duc, _1)) + perif.duc->populate_subtree(_tree->subtree(tx_dsp_path)); + _tree->access<double>(tx_dsp_path / "rate" / "value") .subscribe(boost::bind(&x300_impl::update_tx_samp_rate, this, boost::ref(mb), radio_index, _1)) - .set(1e6); - _tree->create<double>(tx_dsp_path / "freq" / "value") - .coerce(boost::bind(&tx_dsp_core_3000::set_freq, perif.duc, _1)) - .set(0.0); - _tree->create<meta_range_t>(tx_dsp_path / "freq" / "range") - .publish(boost::bind(&tx_dsp_core_3000::get_freq_range, perif.duc)); - - //////////////////////////////////////////////////////////////////// - // create time control objects - //////////////////////////////////////////////////////////////////// - time_core_3000::readback_bases_type time64_rb_bases; - time64_rb_bases.rb_now = RB64_TIME_NOW; - time64_rb_bases.rb_pps = RB64_TIME_PPS; - perif.time64 = time_core_3000::make(perif.ctrl, TOREG(SR_TIME), time64_rb_bases); + ; //////////////////////////////////////////////////////////////////// // create RF frontend interfacing @@ -1014,7 +1014,7 @@ void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name) //create a new dboard interface x300_dboard_iface_config_t db_config; - db_config.gpio = gpio_core_200::make(perif.ctrl, TOREG(SR_GPIO), RB32_GPIO); + db_config.gpio = gpio_core_200::make(perif.ctrl, radio::sr_addr(radio::GPIO), radio::RB32_GPIO); db_config.spi = perif.spi; db_config.rx_spi_slaveno = DB_RX_SEN; db_config.tx_spi_slaveno = DB_TX_SEN; @@ -1309,8 +1309,14 @@ void x300_impl::update_atr_leds(gpio_core_200_32wo::sptr leds, const std::string void x300_impl::set_tick_rate(mboard_members_t &mb, const double rate) { - BOOST_FOREACH(radio_perifs_t &perif, mb.radio_perifs) + BOOST_FOREACH(radio_perifs_t &perif, mb.radio_perifs) { + perif.ctrl->set_tick_rate(rate); perif.time64->set_tick_rate(rate); + perif.framer->set_tick_rate(rate); + perif.ddc->set_tick_rate(rate); + perif.deframer->set_tick_rate(rate); + perif.duc->set_tick_rate(rate); + } } void x300_impl::register_loopback_self_test(wb_iface::sptr iface) @@ -1321,8 +1327,8 @@ void x300_impl::register_loopback_self_test(wb_iface::sptr iface) for (size_t i = 0; i < 100; i++) { boost::hash_combine(hash, i); - iface->poke32(TOREG(SR_TEST), boost::uint32_t(hash)); - test_fail = iface->peek32(RB32_TEST) != boost::uint32_t(hash); + iface->poke32(radio::sr_addr(radio::TEST), boost::uint32_t(hash)); + test_fail = iface->peek32(radio::RB32_TEST) != boost::uint32_t(hash); if (test_fail) break; //exit loop on any failure } UHD_MSG(status) << ((test_fail)? " fail" : "pass") << std::endl; @@ -1332,32 +1338,9 @@ void x300_impl::register_loopback_self_test(wb_iface::sptr iface) * clock and time control logic **********************************************************************/ -void x300_impl::update_clock_control(mboard_members_t &mb) -{ - const size_t reg = mb.clock_control_regs_clock_source - | (mb.clock_control_regs_pps_select << 2) - | (mb.clock_control_regs_pps_out_enb << 4) - | (mb.clock_control_regs_tcxo_enb << 5) - | (mb.clock_control_regs_gpsdo_pwr << 6) - ; - mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_CLOCK_CTRL), reg); -} - -void x300_impl::initialize_clock_control(mboard_members_t &mb) -{ - //Initialize clock control register soft copies - mb.clock_control_regs_clock_source = ZPU_SR_CLOCK_CTRL_CLK_SRC_INTERNAL; - mb.clock_control_regs_pps_select = ZPU_SR_CLOCK_CTRL_PPS_SRC_INTERNAL; - mb.clock_control_regs_pps_out_enb = 0; - mb.clock_control_regs_tcxo_enb = 1; - mb.clock_control_regs_gpsdo_pwr = 1; //GPSDO power always ON - this->update_clock_control(mb); -} - void x300_impl::set_time_source_out(mboard_members_t &mb, const bool enb) { - mb.clock_control_regs_pps_out_enb = enb? 1 : 0; - this->update_clock_control(mb); + mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::PPS_OUT_EN, enb?1:0); } void x300_impl::update_clock_source(mboard_members_t &mb, const std::string &source) @@ -1365,21 +1348,22 @@ void x300_impl::update_clock_source(mboard_members_t &mb, const std::string &sou //Optimize for the case when the current source is internal and we are trying //to set it to internal. This is the only case where we are guaranteed that //the clock has not gone away so we can skip setting the MUX and reseting the LMK. - if (not (mb.current_refclk_src == "internal" and source == "internal")) { + const bool reconfigure_clks = (mb.current_refclk_src != "internal") or (source != "internal"); + if (reconfigure_clks) { //Update the clock MUX on the motherboard to select the requested source - mb.clock_control_regs_clock_source = 0; - mb.clock_control_regs_tcxo_enb = 0; if (source == "internal") { - mb.clock_control_regs_clock_source = ZPU_SR_CLOCK_CTRL_CLK_SRC_INTERNAL; - mb.clock_control_regs_tcxo_enb = 1; + mb.fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::CLK_SOURCE, fw_regmap_t::clk_ctrl_reg_t::SRC_INTERNAL); + mb.fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::TCXO_EN, 1); } else if (source == "external") { - mb.clock_control_regs_clock_source = ZPU_SR_CLOCK_CTRL_CLK_SRC_EXTERNAL; + mb.fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::CLK_SOURCE, fw_regmap_t::clk_ctrl_reg_t::SRC_EXTERNAL); + mb.fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::TCXO_EN, 0); } else if (source == "gpsdo") { - mb.clock_control_regs_clock_source = ZPU_SR_CLOCK_CTRL_CLK_SRC_GPSDO; + mb.fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::CLK_SOURCE, fw_regmap_t::clk_ctrl_reg_t::SRC_GPSDO); + mb.fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::TCXO_EN, 0); } else { throw uhd::key_error("update_clock_source: unknown source: " + source); } - this->update_clock_control(mb); + mb.fw_regmap->clock_ctrl_reg.flush(); //Reset the LMK to make sure it re-locks to the new reference mb.clock->reset_clocks(); @@ -1394,10 +1378,10 @@ void x300_impl::update_clock_source(mboard_members_t &mb, const std::string &sou //The programming code in x300_clock_ctrl is not compatible with revs <= 4 and may //lead to locking issues. So, disable the ref-locked check for older (unsupported) boards. if (mb.hw_rev > 4) { - if (not wait_for_ref_locked(mb.zpu_ctrl, timeout)) { + if (not wait_for_clk_locked(mb, fw_regmap_t::clk_status_reg_t::LMK_LOCK, timeout)) { //failed to lock on reference if (mb.initialization_done) { - throw uhd::runtime_error((boost::format("Reference Clock failed to lock to %s source.") % source).str()); + throw uhd::runtime_error((boost::format("Reference Clock PLL failed to lock to %s source.") % source).str()); } else { //TODO: Re-enable this warning when we figure out a reliable lock time //UHD_MSG(warning) << "Reference clock failed to lock to " + source + " during device initialization. " << @@ -1406,6 +1390,41 @@ void x300_impl::update_clock_source(mboard_members_t &mb, const std::string &sou } } + if (reconfigure_clks) { + //Reset the radio clock PLL in the FPGA + mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), ZPU_SR_SW_RST_RADIO_CLK_PLL); + mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), 0); + + //Wait for radio clock PLL to lock + if (not wait_for_clk_locked(mb, fw_regmap_t::clk_status_reg_t::RADIO_CLK_LOCK, 0.01)) { + throw uhd::runtime_error((boost::format("Reference Clock PLL in FPGA failed to lock to %s source.") % source).str()); + } + + //Reset the IDELAYCTRL used to calibrate the data interface delays + mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), ZPU_SR_SW_RST_ADC_IDELAYCTRL); + mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), 0); + + //Wait for the ADC IDELAYCTRL to be ready + if (not wait_for_clk_locked(mb, fw_regmap_t::clk_status_reg_t::IDELAYCTRL_LOCK, 0.01)) { + throw uhd::runtime_error((boost::format("ADC Calibration Clock in FPGA failed to lock to %s source.") % source).str()); + } + + // Reset ADCs and DACs + for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) { + radio_perifs_t &perif = mb.radio_perifs[r]; + if (perif.regmap && r==0) { //ADC/DAC reset lines only exist in Radio0 + perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 1); + perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 0); + perif.regmap->misc_outs_reg.flush(); + perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 0); + perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 1); + perif.regmap->misc_outs_reg.flush(); + } + if (perif.adc) perif.adc->reset(); + if (perif.dac) perif.dac->reset(); + } + } + //Update cache value mb.current_refclk_src = source; } @@ -1413,119 +1432,60 @@ void x300_impl::update_clock_source(mboard_members_t &mb, const std::string &sou void x300_impl::update_time_source(mboard_members_t &mb, const std::string &source) { if (source == "internal") { - mb.clock_control_regs_pps_select = ZPU_SR_CLOCK_CTRL_PPS_SRC_INTERNAL; + mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::PPS_SELECT, fw_regmap_t::clk_ctrl_reg_t::SRC_INTERNAL); } else if (source == "external") { - mb.clock_control_regs_pps_select = ZPU_SR_CLOCK_CTRL_PPS_SRC_EXTERNAL; + mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::PPS_SELECT, fw_regmap_t::clk_ctrl_reg_t::SRC_EXTERNAL); } else if (source == "gpsdo") { - mb.clock_control_regs_pps_select = ZPU_SR_CLOCK_CTRL_PPS_SRC_GPSDO; + mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::PPS_SELECT, fw_regmap_t::clk_ctrl_reg_t::SRC_GPSDO); } else { throw uhd::key_error("update_time_source: unknown source: " + source); } - this->update_clock_control(mb); - + /* TODO - Implement intelligent PPS detection //check for valid pps - if (!is_pps_present(mb.zpu_ctrl)) - { - // TODO - Implement intelligent PPS detection - /* throw uhd::runtime_error((boost::format("The %d PPS was not detected. Please check the PPS source and try again.") % source).str()); */ + if (!is_pps_present(mb)) { + throw uhd::runtime_error((boost::format("The %d PPS was not detected. Please check the PPS source and try again.") % source).str()); } + */ } -bool x300_impl::wait_for_ref_locked(wb_iface::sptr ctrl, double timeout) +bool x300_impl::wait_for_clk_locked(mboard_members_t& mb, boost::uint32_t which, double timeout) { boost::system_time timeout_time = boost::get_system_time() + boost::posix_time::milliseconds(timeout * 1000.0); - do - { - if (get_ref_locked(ctrl).to_bool()) + do { + if (mb.fw_regmap->clock_status_reg.read(which)==1) return true; boost::this_thread::sleep(boost::posix_time::milliseconds(1)); } while (boost::get_system_time() < timeout_time); - //failed to lock on reference - return false; + //Check one last time + return (mb.fw_regmap->clock_status_reg.read(which)==1); } -sensor_value_t x300_impl::get_ref_locked(wb_iface::sptr ctrl) +sensor_value_t x300_impl::get_ref_locked(mboard_members_t& mb) { - boost::uint32_t clk_status = ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_CLK_STATUS)); - const bool lock = ((clk_status & ZPU_RB_CLK_STATUS_LMK_LOCK) != 0); + mb.fw_regmap->clock_status_reg.refresh(); + const bool lock = (mb.fw_regmap->clock_status_reg.get(fw_regmap_t::clk_status_reg_t::LMK_LOCK)==1) && + (mb.fw_regmap->clock_status_reg.get(fw_regmap_t::clk_status_reg_t::RADIO_CLK_LOCK)==1) && + (mb.fw_regmap->clock_status_reg.get(fw_regmap_t::clk_status_reg_t::IDELAYCTRL_LOCK)==1); return sensor_value_t("Ref", lock, "locked", "unlocked"); } -bool x300_impl::is_pps_present(wb_iface::sptr ctrl) +bool x300_impl::is_pps_present(mboard_members_t& mb) { // The ZPU_RB_CLK_STATUS_PPS_DETECT bit toggles with each rising edge of the PPS. // We monitor it for up to 1.5 seconds looking for it to toggle. - boost::uint32_t pps_detect = ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_CLK_STATUS)) & ZPU_RB_CLK_STATUS_PPS_DETECT; + boost::uint32_t pps_detect = mb.fw_regmap->clock_status_reg.read(fw_regmap_t::clk_status_reg_t::PPS_DETECT); for (int i = 0; i < 15; i++) { boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - boost::uint32_t clk_status = ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_CLK_STATUS)); - if (pps_detect != (clk_status & ZPU_RB_CLK_STATUS_PPS_DETECT)) + if (pps_detect != mb.fw_regmap->clock_status_reg.read(fw_regmap_t::clk_status_reg_t::PPS_DETECT)) return true; } return false; } /*********************************************************************** - * reset and synchronization logic - **********************************************************************/ - -void x300_impl::reset_radios(mboard_members_t &mb) -{ - // Reset ADCs and DACs - BOOST_FOREACH (radio_perifs_t& perif, mb.radio_perifs) - { - perif.adc->reset(); - perif.dac->reset(); - } -} - -void x300_impl::synchronize_dacs(const std::vector<radio_perifs_t*>& radios) -{ - if (radios.size() < 2) return; //Nothing to synchronize - - //**PRECONDITION** - //This function assumes that all the VITA times in "radios" are synchronized - //to a common reference. Currently, this function is called in get_tx_stream - //which also has the same precondition. - - //Reinitialize and resync all DACs - for (size_t i = 0; i < radios.size(); i++) { - radios[i]->dac->reset_and_resync(); - } - - //Get a rough estimate of the cumulative command latency - boost::posix_time::ptime t_start = boost::posix_time::microsec_clock::local_time(); - for (size_t i = 0; i < radios.size(); i++) { - radios[i]->ctrl->peek64(RB64_TIME_NOW); //Discard value. We are just timing the call - } - boost::posix_time::time_duration t_elapsed = - boost::posix_time::microsec_clock::local_time() - t_start; - - //Add 100% of headroom + uncertaintly to the command time - boost::uint64_t t_sync_us = (t_elapsed.total_microseconds() * 2) + 13000 /*Scheduler latency*/; - - //Pick radios[0] as the time reference. - uhd::time_spec_t sync_time = - radios[0]->time64->get_time_now() + uhd::time_spec_t(((double)t_sync_us)/1e6); - - //Send the sync command - for (size_t i = 0; i < radios.size(); i++) { - radios[i]->ctrl->set_time(sync_time); - radios[i]->ctrl->poke32(TOREG(SR_DACSYNC), 0x1); //Arm FRAMEP/N sync pulse - radios[i]->ctrl->set_time(uhd::time_spec_t(0.0)); //Clear command time - } - - //Wait and check status - boost::this_thread::sleep(boost::posix_time::microseconds(t_sync_us)); - for (size_t i = 0; i < radios.size(); i++) { - radios[i]->dac->verify_sync(); - } -} - -/*********************************************************************** * eeprom **********************************************************************/ @@ -1544,20 +1504,24 @@ void x300_impl::set_mb_eeprom(i2c_iface::sptr i2c, const mboard_eeprom_t &mb_eep * front-panel GPIO **********************************************************************/ -boost::uint32_t x300_impl::get_fp_gpio(gpio_core_200::sptr gpio, const std::string &) +boost::uint32_t x300_impl::get_fp_gpio(gpio_core_200::sptr gpio) { return boost::uint32_t(gpio->read_gpio(dboard_iface::UNIT_RX)); } -void x300_impl::set_fp_gpio(gpio_core_200::sptr gpio, const std::string &attr, const boost::uint32_t value) +void x300_impl::set_fp_gpio(gpio_core_200::sptr gpio, const gpio_attr_t attr, const boost::uint32_t value) { - if (attr == "CTRL") return gpio->set_pin_ctrl(dboard_iface::UNIT_RX, value); - if (attr == "DDR") return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value); - if (attr == "OUT") return gpio->set_gpio_out(dboard_iface::UNIT_RX, value); - if (attr == "ATR_0X") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value); - if (attr == "ATR_RX") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value); - if (attr == "ATR_TX") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value); - if (attr == "ATR_XX") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, value); + switch (attr) + { + case GPIO_CTRL: return gpio->set_pin_ctrl(dboard_iface::UNIT_RX, value); + case GPIO_DDR: return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value); + case GPIO_OUT: return gpio->set_gpio_out(dboard_iface::UNIT_RX, value); + case GPIO_ATR_0X: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value); + case GPIO_ATR_RX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value); + case GPIO_ATR_TX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value); + case GPIO_ATR_XX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, value); + default: UHD_THROW_INVALID_CODE_PATH(); + } } /*********************************************************************** @@ -1688,25 +1652,33 @@ void x300_impl::check_fw_compat(const fs_path &mb_path, wb_iface::sptr iface) % compat_major % compat_minor)); } -void x300_impl::check_fpga_compat(const fs_path &mb_path, wb_iface::sptr iface) +void x300_impl::check_fpga_compat(const fs_path &mb_path, const mboard_members_t &members) { - boost::uint32_t compat_num = iface->peek32(SR_ADDR(SET0_BASE, ZPU_RB_COMPAT_NUM)); + boost::uint32_t compat_num = members.zpu_ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_COMPAT_NUM)); boost::uint32_t compat_major = (compat_num >> 16); boost::uint32_t compat_minor = (compat_num & 0xffff); if (compat_major != X300_FPGA_COMPAT_MAJOR) { + std::string image_loader_path = (fs::path(uhd::get_pkg_path()) / "bin" / "uhd_image_loader").string(); + std::string image_loader_cmd = str(boost::format("\"%s\" --args=\"type=x300,%s=%s\"") + % image_loader_path + % (members.xport_path == "eth" ? "addr" + : "resource") + % members.addr); + throw uhd::runtime_error(str(boost::format( "Expected FPGA compatibility number %d, but got %d:\n" "The FPGA image on your device is not compatible with this host code build.\n" "Download the appropriate FPGA images for this version of UHD.\n" "%s\n\n" "Then burn a new image to the on-board flash storage of your\n" - "USRP X3xx device using the burner utility. %s\n\n" + "USRP X3xx device using the image loader utility. Use this command:\n\n%s\n\n" "For more information, refer to the UHD manual:\n\n" " http://files.ettus.com/manual/page_usrp_x3x0.html#x3x0_flash" ) % int(X300_FPGA_COMPAT_MAJOR) % compat_major - % print_utility_error("uhd_images_downloader.py") % print_utility_error("usrp_x3xx_fpga_burner"))); + % print_utility_error("uhd_images_downloader.py") + % image_loader_cmd)); } _tree->create<std::string>(mb_path / "fpga_version").set(str(boost::format("%u.%u") % compat_major % compat_minor)); @@ -1727,17 +1699,39 @@ x300_impl::x300_mboard_t x300_impl::get_mb_type_from_pcie(const std::string& res if (nirio_status_not_fatal(status)) { //The PCIe ID -> MB mapping may be different from the EEPROM -> MB mapping switch (pid) { - case X300_USRP_PCIE_SSID: + case X300_USRP_PCIE_SSID_ADC_33: + case X300_USRP_PCIE_SSID_ADC_18: mb_type = USRP_X300_MB; break; - case X310_USRP_PCIE_SSID: - case X310_2940R_PCIE_SSID: - case X310_2942R_PCIE_SSID: - case X310_2943R_PCIE_SSID: - case X310_2944R_PCIE_SSID: - case X310_2950R_PCIE_SSID: - case X310_2952R_PCIE_SSID: - case X310_2953R_PCIE_SSID: - case X310_2954R_PCIE_SSID: + case X310_USRP_PCIE_SSID_ADC_33: + case X310_2940R_40MHz_PCIE_SSID_ADC_33: + case X310_2940R_120MHz_PCIE_SSID_ADC_33: + case X310_2942R_40MHz_PCIE_SSID_ADC_33: + case X310_2942R_120MHz_PCIE_SSID_ADC_33: + case X310_2943R_40MHz_PCIE_SSID_ADC_33: + case X310_2943R_120MHz_PCIE_SSID_ADC_33: + case X310_2944R_40MHz_PCIE_SSID_ADC_33: + case X310_2950R_40MHz_PCIE_SSID_ADC_33: + case X310_2950R_120MHz_PCIE_SSID_ADC_33: + case X310_2952R_40MHz_PCIE_SSID_ADC_33: + case X310_2952R_120MHz_PCIE_SSID_ADC_33: + case X310_2953R_40MHz_PCIE_SSID_ADC_33: + case X310_2953R_120MHz_PCIE_SSID_ADC_33: + case X310_2954R_40MHz_PCIE_SSID_ADC_33: + case X310_USRP_PCIE_SSID_ADC_18: + case X310_2940R_40MHz_PCIE_SSID_ADC_18: + case X310_2940R_120MHz_PCIE_SSID_ADC_18: + case X310_2942R_40MHz_PCIE_SSID_ADC_18: + case X310_2942R_120MHz_PCIE_SSID_ADC_18: + case X310_2943R_40MHz_PCIE_SSID_ADC_18: + case X310_2943R_120MHz_PCIE_SSID_ADC_18: + case X310_2944R_40MHz_PCIE_SSID_ADC_18: + case X310_2950R_40MHz_PCIE_SSID_ADC_18: + case X310_2950R_120MHz_PCIE_SSID_ADC_18: + case X310_2952R_40MHz_PCIE_SSID_ADC_18: + case X310_2952R_120MHz_PCIE_SSID_ADC_18: + case X310_2953R_40MHz_PCIE_SSID_ADC_18: + case X310_2953R_120MHz_PCIE_SSID_ADC_18: + case X310_2954R_40MHz_PCIE_SSID_ADC_18: mb_type = USRP_X310_MB; break; default: mb_type = UNKNOWN; break; @@ -1762,17 +1756,39 @@ x300_impl::x300_mboard_t x300_impl::get_mb_type_from_eeprom(const uhd::usrp::mbo switch (product_num) { //The PCIe ID -> MB mapping may be different from the EEPROM -> MB mapping - case X300_USRP_PCIE_SSID: + case X300_USRP_PCIE_SSID_ADC_33: + case X300_USRP_PCIE_SSID_ADC_18: mb_type = USRP_X300_MB; break; - case X310_USRP_PCIE_SSID: - case X310_2940R_PCIE_SSID: - case X310_2942R_PCIE_SSID: - case X310_2943R_PCIE_SSID: - case X310_2944R_PCIE_SSID: - case X310_2950R_PCIE_SSID: - case X310_2952R_PCIE_SSID: - case X310_2953R_PCIE_SSID: - case X310_2954R_PCIE_SSID: + case X310_USRP_PCIE_SSID_ADC_33: + case X310_2940R_40MHz_PCIE_SSID_ADC_33: + case X310_2940R_120MHz_PCIE_SSID_ADC_33: + case X310_2942R_40MHz_PCIE_SSID_ADC_33: + case X310_2942R_120MHz_PCIE_SSID_ADC_33: + case X310_2943R_40MHz_PCIE_SSID_ADC_33: + case X310_2943R_120MHz_PCIE_SSID_ADC_33: + case X310_2944R_40MHz_PCIE_SSID_ADC_33: + case X310_2950R_40MHz_PCIE_SSID_ADC_33: + case X310_2950R_120MHz_PCIE_SSID_ADC_33: + case X310_2952R_40MHz_PCIE_SSID_ADC_33: + case X310_2952R_120MHz_PCIE_SSID_ADC_33: + case X310_2953R_40MHz_PCIE_SSID_ADC_33: + case X310_2953R_120MHz_PCIE_SSID_ADC_33: + case X310_2954R_40MHz_PCIE_SSID_ADC_33: + case X310_USRP_PCIE_SSID_ADC_18: + case X310_2940R_40MHz_PCIE_SSID_ADC_18: + case X310_2940R_120MHz_PCIE_SSID_ADC_18: + case X310_2942R_40MHz_PCIE_SSID_ADC_18: + case X310_2942R_120MHz_PCIE_SSID_ADC_18: + case X310_2943R_40MHz_PCIE_SSID_ADC_18: + case X310_2943R_120MHz_PCIE_SSID_ADC_18: + case X310_2944R_40MHz_PCIE_SSID_ADC_18: + case X310_2950R_40MHz_PCIE_SSID_ADC_18: + case X310_2950R_120MHz_PCIE_SSID_ADC_18: + case X310_2952R_40MHz_PCIE_SSID_ADC_18: + case X310_2952R_120MHz_PCIE_SSID_ADC_18: + case X310_2953R_40MHz_PCIE_SSID_ADC_18: + case X310_2953R_120MHz_PCIE_SSID_ADC_18: + case X310_2954R_40MHz_PCIE_SSID_ADC_18: mb_type = USRP_X310_MB; break; default: UHD_MSG(warning) << "X300 unknown product code in EEPROM: " << product_num << std::endl; @@ -1781,4 +1797,3 @@ x300_impl::x300_mboard_t x300_impl::get_mb_type_from_eeprom(const uhd::usrp::mbo } return mb_type; } - diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp index 890ef7bcb..78c497ad9 100644 --- a/host/lib/usrp/x300/x300_impl.hpp +++ b/host/lib/usrp/x300/x300_impl.hpp @@ -1,5 +1,5 @@ // -// Copyright 2013-2014 Ettus Research LLC +// Copyright 2013-2015 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 @@ -49,6 +49,7 @@ #include <uhd/transport/nirio/niusrprio_session.h> #include <uhd/transport/vrt_if_packet.hpp> #include "recv_packet_demuxer_3000.hpp" +#include "x300_regs.hpp" static const std::string X300_FW_FILE_NAME = "usrp_x300_fw.bin"; @@ -140,6 +141,8 @@ uhd::uart_iface::sptr x300_make_uart_iface(uhd::wb_iface::sptr iface); uhd::wb_iface::sptr x300_make_ctrl_iface_enet(uhd::transport::udp_simple::sptr udp); uhd::wb_iface::sptr x300_make_ctrl_iface_pcie(uhd::niusrprio::niriok_proxy::sptr drv_proxy); +uhd::device_addrs_t x300_find(const uhd::device_addr_t &hint_); + class x300_impl : public uhd::device { public: @@ -172,6 +175,7 @@ private: //perifs in the radio core struct radio_perifs_t { + //Interfaces radio_ctrl_core_3000::sptr ctrl; spi_core_3000::sptr spi; x300_adc_ctrl::sptr adc; @@ -184,6 +188,8 @@ private: gpio_core_200_32wo::sptr leds; rx_frontend_core_200::sptr rx_fe; tx_frontend_core_200::sptr tx_fe; + //Registers + uhd::usrp::x300::radio_regmap_t::sptr regmap; }; //overflow recovery impl @@ -211,7 +217,8 @@ private: i2c_core_100_wb32::sptr zpu_i2c; //perifs in each radio - radio_perifs_t radio_perifs[2]; //!< This is hardcoded s.t. radio_perifs[0] points to slot A and [1] to B + static const size_t NUM_RADIOS = 2; + radio_perifs_t radio_perifs[NUM_RADIOS]; //!< This is hardcoded s.t. radio_perifs[0] points to slot A and [1] to B uhd::usrp::dboard_eeprom_t db_eeproms[8]; //! Return the index of a radio component, given a slot name. This means DSPs, radio_perifs size_t get_radio_index(const std::string &slot_name) { @@ -224,18 +231,15 @@ private: uhd::gps_ctrl::sptr gps; gpio_core_200::sptr fp_gpio; - //clock control register bits - int clock_control_regs_clock_source; - int clock_control_regs_pps_select; - int clock_control_regs_pps_out_enb; - int clock_control_regs_tcxo_enb; - int clock_control_regs_gpsdo_pwr; + uhd::usrp::x300::fw_regmap_t::sptr fw_regmap; //which FPGA image is loaded std::string loaded_fpga_image; size_t hw_rev; std::string current_refclk_src; + + uhd::soft_regmap_db_t::sptr regmap_db; }; std::vector<mboard_members_t> _mb; @@ -259,7 +263,7 @@ private: * \param mb_i Motherboard index * \param slot_name Slot name (A or B). */ - void setup_radio(const size_t, const std::string &slot_name); + void setup_radio(const size_t, const std::string &slot_name, const uhd::device_addr_t &dev_addr); size_t _sid_framer; struct sid_config_t @@ -348,21 +352,26 @@ private: void set_time_source_out(mboard_members_t&, const bool); void update_clock_source(mboard_members_t&, const std::string &); void update_time_source(mboard_members_t&, const std::string &); - void reset_radios(mboard_members_t&); - uhd::sensor_value_t get_ref_locked(uhd::wb_iface::sptr); - bool wait_for_ref_locked(uhd::wb_iface::sptr, double timeout = 0.0); - bool is_pps_present(uhd::wb_iface::sptr); + uhd::sensor_value_t get_ref_locked(mboard_members_t& mb); + bool wait_for_clk_locked(mboard_members_t& mb, boost::uint32_t which, double timeout); + bool is_pps_present(mboard_members_t& mb); void set_db_eeprom(uhd::i2c_iface::sptr i2c, const size_t, const uhd::usrp::dboard_eeprom_t &); void set_mb_eeprom(uhd::i2c_iface::sptr i2c, const uhd::usrp::mboard_eeprom_t &); void check_fw_compat(const uhd::fs_path &mb_path, uhd::wb_iface::sptr iface); - void check_fpga_compat(const uhd::fs_path &mb_path, uhd::wb_iface::sptr iface); + void check_fpga_compat(const uhd::fs_path &mb_path, const mboard_members_t &members); void update_atr_leds(gpio_core_200_32wo::sptr, const std::string &ant); - boost::uint32_t get_fp_gpio(gpio_core_200::sptr, const std::string &); - void set_fp_gpio(gpio_core_200::sptr, const std::string &, const boost::uint32_t); + boost::uint32_t get_fp_gpio(gpio_core_200::sptr); + void set_fp_gpio(gpio_core_200::sptr, const gpio_attr_t, const boost::uint32_t); + + void self_cal_adc_capture_delay(mboard_members_t& mb, const size_t radio_i, bool print_status = false); + double self_cal_adc_xfer_delay(mboard_members_t& mb, bool apply_delay = false); + void self_test_adcs(mboard_members_t& mb, boost::uint32_t ramp_time_ms = 100); + + void extended_adc_test(mboard_members_t& mb, double duration_s); //**PRECONDITION** //This function assumes that all the VITA times in "radios" are synchronized diff --git a/host/lib/usrp/x300/x300_io_impl.cpp b/host/lib/usrp/x300/x300_io_impl.cpp index 334ae8168..e3515af0c 100644 --- a/host/lib/usrp/x300/x300_io_impl.cpp +++ b/host/lib/usrp/x300/x300_io_impl.cpp @@ -23,6 +23,7 @@ #include <uhd/transport/nirio_zero_copy.hpp> #include "async_packet_handler.hpp" #include <uhd/transport/bounded_buffer.hpp> +#include <uhd/transport/chdr.hpp> #include <boost/bind.hpp> #include <uhd/utils/tasks.hpp> #include <uhd/utils/log.hpp> @@ -124,41 +125,6 @@ void x300_impl::update_subdev_spec(const std::string &tx_rx, const size_t mb_i, /*********************************************************************** - * VITA stuff - **********************************************************************/ -static void x300_if_hdr_unpack_be( - const boost::uint32_t *packet_buff, - vrt::if_packet_info_t &if_packet_info -){ - if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; - return vrt::if_hdr_unpack_be(packet_buff, if_packet_info); -} - -static void x300_if_hdr_pack_be( - boost::uint32_t *packet_buff, - vrt::if_packet_info_t &if_packet_info -){ - if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; - return vrt::if_hdr_pack_be(packet_buff, if_packet_info); -} - -static void x300_if_hdr_unpack_le( - const boost::uint32_t *packet_buff, - vrt::if_packet_info_t &if_packet_info -){ - if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; - return vrt::if_hdr_unpack_le(packet_buff, if_packet_info); -} - -static void x300_if_hdr_pack_le( - boost::uint32_t *packet_buff, - vrt::if_packet_info_t &if_packet_info -){ - if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; - return vrt::if_hdr_pack_le(packet_buff, if_packet_info); -} - -/*********************************************************************** * RX flow control handler **********************************************************************/ static size_t get_rx_flow_control_window(size_t frame_size, size_t sw_buff_size, const device_addr_t& rx_args) @@ -209,9 +175,9 @@ static void handle_rx_flowctrl(const boost::uint32_t sid, zero_copy_if::sptr xpo //load header if (big_endian) - x300_if_hdr_pack_be(pkt, packet_info); + vrt::chdr::if_hdr_pack_be(pkt, packet_info); else - x300_if_hdr_pack_le(pkt, packet_info); + vrt::chdr::if_hdr_pack_le(pkt, packet_info); //load payload pkt[packet_info.num_header_words32+0] = uhd::htonx<boost::uint32_t>(0); @@ -276,12 +242,12 @@ static void handle_tx_async_msgs(boost::shared_ptr<x300_tx_fc_guts_t> guts, zero { if (big_endian) { - x300_if_hdr_unpack_be(packet_buff, if_packet_info); + vrt::chdr::if_hdr_unpack_be(packet_buff, if_packet_info); endian_conv = uhd::ntohx; } else { - x300_if_hdr_unpack_le(packet_buff, if_packet_info); + vrt::chdr::if_hdr_unpack_le(packet_buff, if_packet_info); endian_conv = uhd::wtohx; } } @@ -430,10 +396,10 @@ rx_streamer::sptr x300_impl::get_rx_stream(const uhd::stream_args_t &args_) //init some streamer stuff std::string conv_endianness; if (mb.if_pkt_is_big_endian) { - my_streamer->set_vrt_unpacker(&x300_if_hdr_unpack_be); + my_streamer->set_vrt_unpacker(&vrt::chdr::if_hdr_unpack_be); conv_endianness = "be"; } else { - my_streamer->set_vrt_unpacker(&x300_if_hdr_unpack_le); + my_streamer->set_vrt_unpacker(&vrt::chdr::if_hdr_unpack_le); conv_endianness = "le"; } @@ -594,10 +560,10 @@ tx_streamer::sptr x300_impl::get_tx_stream(const uhd::stream_args_t &args_) std::string conv_endianness; if (mb.if_pkt_is_big_endian) { - my_streamer->set_vrt_packer(&x300_if_hdr_pack_be); + my_streamer->set_vrt_packer(&vrt::chdr::if_hdr_pack_be); conv_endianness = "be"; } else { - my_streamer->set_vrt_packer(&x300_if_hdr_pack_le); + my_streamer->set_vrt_packer(&vrt::chdr::if_hdr_pack_le); conv_endianness = "le"; } diff --git a/host/lib/usrp/x300/x300_regs.hpp b/host/lib/usrp/x300/x300_regs.hpp index f920b5ae2..eba30abb5 100644 --- a/host/lib/usrp/x300/x300_regs.hpp +++ b/host/lib/usrp/x300/x300_regs.hpp @@ -18,108 +18,133 @@ #ifndef INCLUDED_X300_REGS_HPP #define INCLUDED_X300_REGS_HPP +#include <uhd/config.hpp> #include <stdint.h> -#include <boost/cstdint.hpp> +#include <uhd/utils/soft_register.hpp> -#define TOREG(x) ((x)*4) +namespace uhd { namespace usrp { namespace radio { + +static UHD_INLINE uint32_t sr_addr(const uint32_t offset) +{ + return offset * 4; +} + +static const uint32_t DACSYNC = 5; +static const uint32_t LOOPBACK = 6; +static const uint32_t TEST = 7; +static const uint32_t SPI = 8; +static const uint32_t GPIO = 16; +static const uint32_t MISC_OUTS = 24; +static const uint32_t READBACK = 32; +static const uint32_t TX_CTRL = 64; +static const uint32_t RX_CTRL = 96; +static const uint32_t TIME = 128; +static const uint32_t RX_DSP = 144; +static const uint32_t TX_DSP = 184; +static const uint32_t LEDS = 195; +static const uint32_t FP_GPIO = 200; +static const uint32_t RX_FRONT = 208; +static const uint32_t TX_FRONT = 216; + +static const uint32_t RB32_GPIO = 0; +static const uint32_t RB32_SPI = 4; +static const uint32_t RB64_TIME_NOW = 8; +static const uint32_t RB64_TIME_PPS = 16; +static const uint32_t RB32_TEST = 24; +static const uint32_t RB32_RX = 28; +static const uint32_t RB32_FP_GPIO = 32; +static const uint32_t RB32_MISC_INS = 36; + +}}} // namespace #define localparam static const int -localparam SR_DACSYNC = 5; -localparam SR_LOOPBACK = 6; -localparam SR_TEST = 7; -localparam SR_SPI = 8; -localparam SR_GPIO = 16; -localparam SR_MISC_OUTS = 24; -localparam SR_READBACK = 32; -localparam SR_TX_CTRL = 64; -localparam SR_RX_CTRL = 96; -localparam SR_TIME = 128; -localparam SR_RX_DSP = 144; -localparam SR_TX_DSP = 184; -localparam SR_LEDS = 195; -localparam SR_FP_GPIO = 200; -localparam SR_RX_FRONT = 208; -localparam SR_TX_FRONT = 216; - -localparam RB32_GPIO = 0; -localparam RB32_SPI = 4; -localparam RB64_TIME_NOW = 8; -localparam RB64_TIME_PPS = 16; -localparam RB32_TEST = 24; -localparam RB32_RX = 28; -localparam RB32_FP_GPIO = 32; - -localparam BL_ADDRESS = 0; -localparam BL_DATA = 1; +localparam BL_ADDRESS = 0; +localparam BL_DATA = 1; //wishbone settings map - relevant to host code -#define SET0_BASE 0xa000 -#define SETXB_BASE 0xb000 -#define BOOT_LDR_BASE 0xFA00 -#define I2C0_BASE 0xfe00 -#define I2C1_BASE 0xff00 +#define SET0_BASE 0xa000 +#define SETXB_BASE 0xb000 +#define BOOT_LDR_BASE 0xfa00 +#define I2C0_BASE 0xfe00 +#define I2C1_BASE 0xff00 #define SR_ADDR(base, offset) ((base) + (offset)*4) localparam ZPU_SR_LEDS = 00; -localparam ZPU_SR_PHY_RST = 01; +localparam ZPU_SR_SW_RST = 01; localparam ZPU_SR_CLOCK_CTRL = 02; localparam ZPU_SR_XB_LOCAL = 03; localparam ZPU_SR_SPI = 32; localparam ZPU_SR_ETHINT0 = 40; localparam ZPU_SR_ETHINT1 = 56; -//clock controls -#define ZPU_SR_CLOCK_CTRL_CLK_SRC_EXTERNAL 0x00 -#define ZPU_SR_CLOCK_CTRL_CLK_SRC_INTERNAL 0x02 -#define ZPU_SR_CLOCK_CTRL_CLK_SRC_GPSDO 0x03 -#define ZPU_SR_CLOCK_CTRL_PPS_SRC_EXTERNAL 0x00 -#define ZPU_SR_CLOCK_CTRL_PPS_SRC_INTERNAL 0x02 -#define ZPU_SR_CLOCK_CTRL_PPS_SRC_GPSDO 0x03 +//reset bits +#define ZPU_SR_SW_RST_ETH_PHY (1<<0) +#define ZPU_SR_SW_RST_RADIO_RST (1<<1) +#define ZPU_SR_SW_RST_RADIO_CLK_PLL (1<<2) +#define ZPU_SR_SW_RST_ADC_IDELAYCTRL (1<<3) -localparam ZPU_RB_SPI = 2; +localparam ZPU_RB_SPI = 2; localparam ZPU_RB_CLK_STATUS = 3; localparam ZPU_RB_COMPAT_NUM = 6; localparam ZPU_RB_ETH_TYPE0 = 4; localparam ZPU_RB_ETH_TYPE1 = 5; -//clock status -#define ZPU_RB_CLK_STATUS_LMK_STATUS (0x3 << 0) -#define ZPU_RB_CLK_STATUS_LMK_LOCK (0x1 << 2) -#define ZPU_RB_CLK_STATUS_LMK_HOLDOVER (0x1 << 3) -#define ZPU_RB_CLK_STATUS_PPS_DETECT (0x1 << 4) - //spi slaves on radio -#define DB_DAC_SEN (1 << 7) -#define DB_ADC_SEN (1 << 6) +#define DB_DAC_SEN (1 << 7) +#define DB_ADC_SEN (1 << 6) #define DB_RX_LSADC_SEN (1 << 5) #define DB_RX_LSDAC_SEN (1 << 4) #define DB_TX_LSADC_SEN (1 << 3) #define DB_TX_LSDAC_SEN (1 << 2) -#define DB_RX_SEN (1 << 1) -#define DB_TX_SEN (1 << 0) +#define DB_RX_SEN (1 << 1) +#define DB_TX_SEN (1 << 0) //------------------------------------------------------------------- // PCIe Registers //------------------------------------------------------------------- -static const uint32_t X300_PCIE_VID = 0x1093; -static const uint32_t X300_PCIE_PID = 0xC4C4; -static const uint32_t X300_USRP_PCIE_SSID = 0x7736; -static const uint32_t X310_USRP_PCIE_SSID = 0x76CA; -static const uint32_t X310_2940R_PCIE_SSID = 0x772B; -static const uint32_t X310_2942R_PCIE_SSID = 0x772C; -static const uint32_t X310_2943R_PCIE_SSID = 0x772D; -static const uint32_t X310_2944R_PCIE_SSID = 0x772E; -static const uint32_t X310_2950R_PCIE_SSID = 0x772F; -static const uint32_t X310_2952R_PCIE_SSID = 0x7730; -static const uint32_t X310_2953R_PCIE_SSID = 0x7731; -static const uint32_t X310_2954R_PCIE_SSID = 0x7732; +static const uint32_t X300_PCIE_VID = 0x1093; +static const uint32_t X300_PCIE_PID = 0xC4C4; +//Rev 0-6 motherboard/PCIe IDs (ADC driven at 3.3V) +static const uint32_t X300_USRP_PCIE_SSID_ADC_33 = 0x7736; +static const uint32_t X310_USRP_PCIE_SSID_ADC_33 = 0x76CA; +static const uint32_t X310_2940R_40MHz_PCIE_SSID_ADC_33 = 0x772B; +static const uint32_t X310_2940R_120MHz_PCIE_SSID_ADC_33 = 0x77FB; +static const uint32_t X310_2942R_40MHz_PCIE_SSID_ADC_33 = 0x772C; +static const uint32_t X310_2942R_120MHz_PCIE_SSID_ADC_33 = 0x77FC; +static const uint32_t X310_2943R_40MHz_PCIE_SSID_ADC_33 = 0x772D; +static const uint32_t X310_2943R_120MHz_PCIE_SSID_ADC_33 = 0x77FD; +static const uint32_t X310_2944R_40MHz_PCIE_SSID_ADC_33 = 0x772E; +static const uint32_t X310_2950R_40MHz_PCIE_SSID_ADC_33 = 0x772F; +static const uint32_t X310_2950R_120MHz_PCIE_SSID_ADC_33 = 0x77FE; +static const uint32_t X310_2952R_40MHz_PCIE_SSID_ADC_33 = 0x7730; +static const uint32_t X310_2952R_120MHz_PCIE_SSID_ADC_33 = 0x77FF; +static const uint32_t X310_2953R_40MHz_PCIE_SSID_ADC_33 = 0x7731; +static const uint32_t X310_2953R_120MHz_PCIE_SSID_ADC_33 = 0x7800; +static const uint32_t X310_2954R_40MHz_PCIE_SSID_ADC_33 = 0x7732; +//Rev 7+ motherboard/PCIe IDs (ADCs driven at 1.8V) +static const uint32_t X300_USRP_PCIE_SSID_ADC_18 = 0x7861; +static const uint32_t X310_USRP_PCIE_SSID_ADC_18 = 0x7862; +static const uint32_t X310_2940R_40MHz_PCIE_SSID_ADC_18 = 0x7853; +static const uint32_t X310_2940R_120MHz_PCIE_SSID_ADC_18 = 0x785B; +static const uint32_t X310_2942R_40MHz_PCIE_SSID_ADC_18 = 0x7854; +static const uint32_t X310_2942R_120MHz_PCIE_SSID_ADC_18 = 0x785C; +static const uint32_t X310_2943R_40MHz_PCIE_SSID_ADC_18 = 0x7855; +static const uint32_t X310_2943R_120MHz_PCIE_SSID_ADC_18 = 0x785D; +static const uint32_t X310_2944R_40MHz_PCIE_SSID_ADC_18 = 0x7856; +static const uint32_t X310_2950R_40MHz_PCIE_SSID_ADC_18 = 0x7857; +static const uint32_t X310_2950R_120MHz_PCIE_SSID_ADC_18 = 0x785E; +static const uint32_t X310_2952R_40MHz_PCIE_SSID_ADC_18 = 0x7858; +static const uint32_t X310_2952R_120MHz_PCIE_SSID_ADC_18 = 0x785F; +static const uint32_t X310_2953R_40MHz_PCIE_SSID_ADC_18 = 0x7859; +static const uint32_t X310_2953R_120MHz_PCIE_SSID_ADC_18 = 0x7860; +static const uint32_t X310_2954R_40MHz_PCIE_SSID_ADC_18 = 0x785A; static const uint32_t FPGA_X3xx_SIG_VALUE = 0x58333030; static const uint32_t PCIE_FPGA_ADDR_BASE = 0xC0000; -#define PCIE_FPGA_REG(X) (PCIE_FPGA_ADDR_BASE + X) +#define PCIE_FPGA_REG(X) (PCIE_FPGA_ADDR_BASE + (X)) static const uint32_t FPGA_PCIE_SIG_REG = PCIE_FPGA_REG(0x0000); static const uint32_t FPGA_CNTR_LO_REG = PCIE_FPGA_REG(0x0004); @@ -140,8 +165,8 @@ static const uint32_t DMA_FRAME_SIZE_REG = 0x4; static const uint32_t DMA_SAMPLE_COUNT_REG = 0x8; static const uint32_t DMA_PKT_COUNT_REG = 0xC; -#define PCIE_TX_DMA_REG(REG, CHAN) (PCIE_TX_DMA_REG_BASE + (CHAN*DMA_REG_GRP_SIZE) + REG) -#define PCIE_RX_DMA_REG(REG, CHAN) (PCIE_RX_DMA_REG_BASE + (CHAN*DMA_REG_GRP_SIZE) + REG) +#define PCIE_TX_DMA_REG(REG, CHAN) (PCIE_TX_DMA_REG_BASE + ((CHAN)*DMA_REG_GRP_SIZE) + (REG)) +#define PCIE_RX_DMA_REG(REG, CHAN) (PCIE_RX_DMA_REG_BASE + ((CHAN)*DMA_REG_GRP_SIZE) + (REG)) static const uint32_t DMA_CTRL_DISABLED = 0x00000000; static const uint32_t DMA_CTRL_ENABLED = 0x00000002; @@ -154,20 +179,112 @@ static const uint32_t DMA_STATUS_ERROR = 0x00000001; static const uint32_t DMA_STATUS_BUSY = 0x00000002; static const uint32_t PCIE_ROUTER_REG_BASE = PCIE_FPGA_REG(0x0500); -#define PCIE_ROUTER_REG(X) (PCIE_ROUTER_REG_BASE + X) +#define PCIE_ROUTER_REG(X) (PCIE_ROUTER_REG_BASE + (X)) static const uint32_t PCIE_ZPU_DATA_BASE = 0x30000; static const uint32_t PCIE_ZPU_READ_BASE = 0x20000; //Trig and Status share the same base static const uint32_t PCIE_ZPU_STATUS_BASE = 0x20000; -#define PCIE_ZPU_DATA_REG(X) (PCIE_FPGA_REG(PCIE_ZPU_DATA_BASE) + X) -#define PCIE_ZPU_READ_REG(X) (PCIE_FPGA_REG(PCIE_ZPU_READ_BASE) + X) -#define PCIE_ZPU_STATUS_REG(X) (PCIE_FPGA_REG(PCIE_ZPU_STATUS_BASE) + X) +#define PCIE_ZPU_DATA_REG(X) (PCIE_FPGA_REG(PCIE_ZPU_DATA_BASE) + (X)) +#define PCIE_ZPU_READ_REG(X) (PCIE_FPGA_REG(PCIE_ZPU_READ_BASE) + (X)) +#define PCIE_ZPU_STATUS_REG(X) (PCIE_FPGA_REG(PCIE_ZPU_STATUS_BASE) + (X)) static const uint32_t PCIE_ZPU_READ_START = 0x0; static const uint32_t PCIE_ZPU_READ_CLOBBER = 0x80000000; static const uint32_t PCIE_ZPU_STATUS_BUSY = 0x1; static const uint32_t PCIE_ZPU_STATUS_SUSPENDED = 0x80000000; +//------------------------------------------------------------------- +// Register Maps +//------------------------------------------------------------------- +namespace uhd { namespace usrp { namespace x300 { + class fw_regmap_t : public uhd::soft_regmap_t { + public: + typedef boost::shared_ptr<fw_regmap_t> sptr; + + class clk_ctrl_reg_t : public uhd::soft_reg32_wo_t { + public: + UHD_DEFINE_SOFT_REG_FIELD(CLK_SOURCE, /*width*/ 2, /*shift*/ 0); //[1:0] + UHD_DEFINE_SOFT_REG_FIELD(PPS_SELECT, /*width*/ 2, /*shift*/ 2); //[3:2] + UHD_DEFINE_SOFT_REG_FIELD(PPS_OUT_EN, /*width*/ 1, /*shift*/ 4); //[4] + UHD_DEFINE_SOFT_REG_FIELD(TCXO_EN, /*width*/ 1, /*shift*/ 5); //[5] + UHD_DEFINE_SOFT_REG_FIELD(GPSDO_PWR_EN, /*width*/ 1, /*shift*/ 6); //[6] + + static const boost::uint32_t SRC_EXTERNAL = 0x0; + static const boost::uint32_t SRC_INTERNAL = 0x2; + static const boost::uint32_t SRC_GPSDO = 0x3; + + clk_ctrl_reg_t(): uhd::soft_reg32_wo_t(SR_ADDR(SET0_BASE, ZPU_SR_CLOCK_CTRL)) { + //Initial values + set(CLK_SOURCE, SRC_INTERNAL); + set(PPS_SELECT, SRC_INTERNAL); + set(PPS_OUT_EN, 0); + set(TCXO_EN, 1); + set(GPSDO_PWR_EN, 1); //GPSDO power always ON + } + } clock_ctrl_reg; + + class clk_status_reg_t : public uhd::soft_reg32_ro_t { + public: + UHD_DEFINE_SOFT_REG_FIELD(LMK_STATUS, /*width*/ 2, /*shift*/ 0); //[1:0] + UHD_DEFINE_SOFT_REG_FIELD(LMK_LOCK, /*width*/ 1, /*shift*/ 2); //[2] + UHD_DEFINE_SOFT_REG_FIELD(LMK_HOLDOVER, /*width*/ 1, /*shift*/ 3); //[3] + UHD_DEFINE_SOFT_REG_FIELD(PPS_DETECT, /*width*/ 1, /*shift*/ 4); //[4] + UHD_DEFINE_SOFT_REG_FIELD(RADIO_CLK_LOCK, /*width*/ 1, /*shift*/ 5); //[5] + UHD_DEFINE_SOFT_REG_FIELD(IDELAYCTRL_LOCK, /*width*/ 1, /*shift*/ 6); //[6] + + clk_status_reg_t(): uhd::soft_reg32_ro_t(SR_ADDR(SET0_BASE, ZPU_RB_CLK_STATUS)) {} + } clock_status_reg; + + fw_regmap_t() : soft_regmap_t("fw_regmap") { + add_to_map(clock_ctrl_reg, "clock_ctrl_reg", PUBLIC); + add_to_map(clock_status_reg, "clock_status_reg", PUBLIC); + } + }; + + class radio_regmap_t : public uhd::soft_regmap_t { + public: + typedef boost::shared_ptr<radio_regmap_t> sptr; + class misc_outs_reg_t : public uhd::soft_reg32_wo_t { + public: + UHD_DEFINE_SOFT_REG_FIELD(DAC_ENABLED, /*width*/ 1, /*shift*/ 0); //[0] + UHD_DEFINE_SOFT_REG_FIELD(DAC_RESET_N, /*width*/ 1, /*shift*/ 1); //[1] + UHD_DEFINE_SOFT_REG_FIELD(ADC_RESET, /*width*/ 1, /*shift*/ 2); //[2] + UHD_DEFINE_SOFT_REG_FIELD(ADC_DATA_DLY_STB, /*width*/ 1, /*shift*/ 3); //[3] + UHD_DEFINE_SOFT_REG_FIELD(ADC_DATA_DLY_VAL, /*width*/ 5, /*shift*/ 4); //[8:4] + UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER_ENABLED, /*width*/ 1, /*shift*/ 9); //[9] + + misc_outs_reg_t(): uhd::soft_reg32_wo_t(uhd::usrp::radio::sr_addr(uhd::usrp::radio::MISC_OUTS)) { + //Initial values + set(DAC_ENABLED, 0); + set(DAC_RESET_N, 0); + set(ADC_RESET, 0); + set(ADC_DATA_DLY_STB, 0); + set(ADC_DATA_DLY_VAL, 16); + set(ADC_CHECKER_ENABLED, 0); + } + } misc_outs_reg; + + class misc_ins_reg_t : public uhd::soft_reg32_ro_t { + public: + UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_Q_LOCKED, /*width*/ 1, /*shift*/ 0); //[0] + UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_I_LOCKED, /*width*/ 1, /*shift*/ 1); //[1] + UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_Q_LOCKED, /*width*/ 1, /*shift*/ 2); //[2] + UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_I_LOCKED, /*width*/ 1, /*shift*/ 3); //[3] + UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_Q_ERROR, /*width*/ 1, /*shift*/ 4); //[4] + UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_I_ERROR, /*width*/ 1, /*shift*/ 5); //[5] + UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_Q_ERROR, /*width*/ 1, /*shift*/ 6); //[6] + UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_I_ERROR, /*width*/ 1, /*shift*/ 7); //[7] + + misc_ins_reg_t(): uhd::soft_reg32_ro_t(uhd::usrp::radio::RB32_MISC_INS) { } + } misc_ins_reg; + + radio_regmap_t(int radio_num) : soft_regmap_t("radio" + boost::lexical_cast<std::string>(radio_num) + "_regmap") { + add_to_map(misc_outs_reg, "misc_outs_reg", PUBLIC); + add_to_map(misc_ins_reg, "misc_ins_reg", PUBLIC); + } + }; + +}}} #endif /* INCLUDED_X300_REGS_HPP */ diff --git a/host/lib/usrp_clock/CMakeLists.txt b/host/lib/usrp_clock/CMakeLists.txt index 8a58aa9ac..ba0f5fe0a 100644 --- a/host/lib/usrp_clock/CMakeLists.txt +++ b/host/lib/usrp_clock/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2011-2014 Ettus Research LLC +# Copyright 2011-2015 Ettus Research LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -23,4 +23,10 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/multi_usrp_clock.cpp ) +IF(ENABLE_C_API) + LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/usrp_clock_c.cpp + ) +ENDIF(ENABLE_C_API) + INCLUDE_SUBDIRECTORY(octoclock) diff --git a/host/lib/usrp_clock/octoclock/CMakeLists.txt b/host/lib/usrp_clock/octoclock/CMakeLists.txt index e363bb9da..a74cb034f 100644 --- a/host/lib/usrp_clock/octoclock/CMakeLists.txt +++ b/host/lib/usrp_clock/octoclock/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2011-2014 Ettus Research LLC +# Copyright 2013-2015 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 @@ -16,17 +16,17 @@ # ######################################################################## -# This file included, use CMake directory variables -######################################################################## - -######################################################################## # Conditionally configure the OctoClock support ######################################################################## -LIBUHD_REGISTER_COMPONENT("OctoClock" ENABLE_OCTOCLOCK ON "ENABLE_LIBUHD" OFF) +LIBUHD_REGISTER_COMPONENT("OctoClock" ENABLE_OCTOCLOCK ON "ENABLE_LIBUHD" OFF OFF) IF(ENABLE_OCTOCLOCK) + ADD_DEFINITIONS(-DIHEX_USE_STDBOOL) + LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/kk_ihex_read.c ${CMAKE_CURRENT_SOURCE_DIR}/octoclock_eeprom.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/octoclock_image_loader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/octoclock_impl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/octoclock_uart.cpp ) diff --git a/host/lib/usrp_clock/octoclock/common.h b/host/lib/usrp_clock/octoclock/common.h index 96acbb30f..5861bc4b1 100644 --- a/host/lib/usrp_clock/octoclock/common.h +++ b/host/lib/usrp_clock/octoclock/common.h @@ -1,5 +1,5 @@ /* - * Copyright 2014 Ettus Research LLC + * Copyright 2014-2015 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 @@ -42,11 +42,11 @@ extern "C" { * only valid C code should go in this section. */ -//These values are placed in the octoclock_packet_t.proto_ver field +// These values are placed in the octoclock_packet_t.proto_ver field #define OCTOCLOCK_BOOTLOADER_PROTO_VER 1234 -#define OCTOCLOCK_FW_COMPAT_NUM 2 +#define OCTOCLOCK_FW_COMPAT_NUM 3 -//UDP ports assigned for different tasks +// UDP ports assigned for different tasks #define OCTOCLOCK_UDP_CTRL_PORT 50000 #define OCTOCLOCK_UDP_GPSDO_PORT 50001 #define OCTOCLOCK_UDP_FW_PORT 50002 @@ -98,11 +98,21 @@ typedef enum { } ref_t; typedef enum { - UP, - DOWN + PREFER_INTERNAL, + PREFER_EXTERNAL } switch_pos_t; +/* + * Some versions of AVR-GCC ignore #pragma pack, so + * if AVR-GCC is being used, use __attribute__ + * instead. + */ +#ifdef AVR +#define __AVR_ALIGNED__ __attribute__((aligned(1))) +#else +#define __AVR_ALIGNED__ #pragma pack(push,1) +#endif // Structure of values in EEPROM, starting in location 0 typedef struct { @@ -113,34 +123,37 @@ typedef struct { uint8_t serial[10]; uint8_t name[10]; uint8_t revision; -} octoclock_fw_eeprom_t; +} octoclock_fw_eeprom_t __AVR_ALIGNED__; typedef struct { uint8_t external_detected; uint8_t gps_detected; uint8_t which_ref; uint8_t switch_pos; -} octoclock_state_t; +} octoclock_state_t __AVR_ALIGNED__; typedef struct { uint8_t num_wraps; uint8_t pos; -} gpsdo_cache_state_t; +} gpsdo_cache_state_t __AVR_ALIGNED__; typedef struct { uint32_t proto_ver; uint32_t sequence; uint8_t code; union { - uint16_t len; + uint16_t crc; gpsdo_cache_state_t state; uint16_t poolsize; uint16_t addr; }; uint8_t data[256]; -} octoclock_packet_t; + uint16_t len; +} octoclock_packet_t __AVR_ALIGNED__; +#ifndef AVR #pragma pack(pop) +#endif #ifdef __cplusplus } diff --git a/host/lib/usrp_clock/octoclock/kk_ihex.h b/host/lib/usrp_clock/octoclock/kk_ihex.h new file mode 100644 index 000000000..20eba43cc --- /dev/null +++ b/host/lib/usrp_clock/octoclock/kk_ihex.h @@ -0,0 +1,191 @@ +/* + * kk_ihex.h: A simple library for reading and writing the Intel HEX + * or IHEX format. Intended mainly for embedded systems, and thus + * somewhat optimised for size at the expense of error handling and + * generality. + * + * USAGE + * ----- + * + * The library has been split into read and write parts, which use a + * common data structure (`struct ihex_state`), but each can be used + * independently. Include the header `kk_ihex_read.h` for reading, and/or + * the header `kk_ihex_write.h` for writing (and link with their respective + * object files). Both can be used simultaneously - this header defines + * the shared data structures and definitions. + * + * + * READING INTEL HEX DATA + * ---------------------- + * + * To read data in the Intel HEX format, you must perform the actual reading + * of bytes using other means (e.g., stdio). The bytes read must then be + * passed to `ihex_read_byte` and/or `ihex_read_bytes`. The reading functions + * will then call `ihex_data_read`, at which stage the `struct ihex_state` + * structure will contain the data along with its address. See the header + * `kk_ihex_read.h` for details and example implementation of `ihex_data_read`. + * + * The sequence to read data in IHEX format is: + * struct ihex_state ihex; + * ihex_begin_read(&ihex); + * ihex_read_bytes(&ihex, my_input_bytes, length_of_my_input_bytes); + * ihex_end_read(&ihex); + * + * + * WRITING BINARY DATA AS INTEL HEX + * -------------------------------- + * + * In order to write out data, the `ihex_write_at_address` or + * `ihex_write_at_segment` functions are used to set the data location, + * and then the binary bytes are written with `ihex_write_byte` and/or + * `ihex_write_bytes`. The writing functions will then call the function + * `ihex_flush_buffer` whenever the internal write buffer needs to be + * cleared - it is up to the caller to provide an implementation of + * `ihex_flush_buffer` to do the actual writing. See the header + * `kk_ihex_write.h` for details and an example implementation. + * + * See the declaration further down for an example implementation. + * + * The sequence to write data in IHEX format is: + * struct ihex_state ihex; + * ihex_init(&ihex); + * ihex_write_at_address(&ihex, 0); + * ihex_write_bytes(&ihex, my_data, length_of_my_data); + * ihex_end_write(&ihex); + * + * For outputs larger than 64KiB, 32-bit linear addresses are output. Normally + * the initial linear extended address record of zero is NOT written - it can + * be forced by setting `ihex->flags |= IHEX_FLAG_ADDRESS_OVERFLOW` before + * writing the first byte. + * + * Gaps in the data may be created by calling `ihex_write_at_address` with the + * new starting address without calling `ihex_end_write` in between. + * + * + * The same `struct ihex_state` may be used either for reading or writing, + * but NOT both at the same time. Furthermore, a global output buffer is + * used for writing, i.e., multiple threads must not write simultaneously + * (but multiple writes may be interleaved). + * + * + * CONSERVING MEMORY + * ----------------- + * + * For memory-critical use, you can save additional memory by defining + * `IHEX_LINE_MAX_LENGTH` as something less than 255. Note, however, that + * this limit affects both reading and writing, so the resulting library + * will be unable to read lines with more than this number of data bytes. + * That said, I haven't encountered any IHEX files with more than 32 + * data bytes per line. For write only there is no reason to define the + * maximum as greater than the line length you'll actually be writing, + * e.g., 32 or 16. + * + * If the write functionality is only occasionally used, you can provide + * your own buffer for the duration by defining `IHEX_EXTERNAL_WRITE_BUFFER` + * and providing a `char *ihex_write_buffer` which points to valid storage + * for at least `IHEX_WRITE_BUFFER_LENGTH` characters from before the first + * call to any IHEX write function to until after the last. + * + * If you are doing both reading and writing, you can define the maximum + * output length separately as `IHEX_MAX_OUTPUT_LINE_LENGTH` - this will + * decrease the write buffer size, but `struct ihex_state` will still + * use the larger `IHEX_LINE_MAX_LENGTH` for its data storage. + * + * You can also save a few additional bytes by disabling support for + * segmented addresses, by defining `IHEX_DISABLE_SEGMENTS`. Both the + * read and write modules need to be build with the same option, as the + * resulting data structures will not be compatible otherwise. To be honest, + * this is a fairly pointless optimisation. + * + * + * Copyright (c) 2013-2015 Kimmo Kulovesi, http://arkku.com/ + * Provided with absolutely no warranty, use at your own risk only. + * Use and distribute freely, mark modified copies as such. + */ + +#ifndef KK_IHEX_H +#define KK_IHEX_H + +#define KK_IHEX_VERSION "2015-08-10" + +#include <stdint.h> + +#ifdef IHEX_USE_STDBOOL +#include <stdbool.h> +typedef bool ihex_bool_t; +#else +typedef uint_fast8_t ihex_bool_t; +#endif + +typedef uint_least32_t ihex_address_t; +typedef uint_least16_t ihex_segment_t; +typedef int ihex_count_t; + +// Maximum number of data bytes per line (applies to both reading and +// writing!); specify 255 to support reading all possible lengths. Less +// can be used to limit memory footprint on embedded systems, e.g., +// most programs with IHEX output use 32. +#ifndef IHEX_LINE_MAX_LENGTH +#define IHEX_LINE_MAX_LENGTH 255 +#endif + +enum ihex_flags { + IHEX_FLAG_ADDRESS_OVERFLOW = 0x80 // 16-bit address overflow +}; +typedef uint8_t ihex_flags_t; + +typedef struct ihex_state { + ihex_address_t address; +#ifndef IHEX_DISABLE_SEGMENTS + ihex_segment_t segment; +#endif + ihex_flags_t flags; + uint8_t line_length; + uint8_t length; + uint8_t data[IHEX_LINE_MAX_LENGTH + 1]; +} kk_ihex_t; + +enum ihex_record_type { + IHEX_DATA_RECORD, + IHEX_END_OF_FILE_RECORD, + IHEX_EXTENDED_SEGMENT_ADDRESS_RECORD, + IHEX_START_SEGMENT_ADDRESS_RECORD, + IHEX_EXTENDED_LINEAR_ADDRESS_RECORD, + IHEX_START_LINEAR_ADDRESS_RECORD +}; +typedef uint8_t ihex_record_type_t; + +#ifndef IHEX_DISABLE_SEGMENTS + +// Resolve segmented address (if any). It is the author's recommendation that +// segmented addressing not be used (and indeed the write function of this +// library uses linear 32-bit addressing unless manually overridden). +// +#define IHEX_LINEAR_ADDRESS(ihex) ((ihex)->address + (((ihex_address_t)((ihex)->segment)) << 4)) +// +// Note that segmented addressing with the above macro is not strictly adherent +// to the IHEX specification, which mandates that the lowest 16 bits of the +// address and the index of the data byte must be added modulo 64K (i.e., +// at 16 bits precision with wraparound) and the segment address only added +// afterwards. +// +// To implement fully correct segmented addressing, compute the address +// of _each byte_ with its index in `data` as follows: +// +#define IHEX_BYTE_ADDRESS(ihex, byte_index) ((((ihex)->address + (byte_index)) & 0xFFFFU) + (((ihex_address_t)((ihex)->segment)) << 4)) + +#else // IHEX_DISABLE_SEGMENTS: + +#define IHEX_LINEAR_ADDRESS(ihex) ((ihex)->address) +#define IHEX_BYTE_ADDRESS(ihex, byte_index) ((ihex)->address + (byte_index)) + +#endif + +// The newline string (appended to every output line, e.g., "\r\n") +#ifndef IHEX_NEWLINE_STRING +#define IHEX_NEWLINE_STRING "\n" +#endif + +// See kk_ihex_read.h and kk_ihex_write.h for function declarations! + +#endif // !KK_IHEX_H diff --git a/host/lib/usrp_clock/octoclock/kk_ihex_license.txt b/host/lib/usrp_clock/octoclock/kk_ihex_license.txt new file mode 100644 index 000000000..530f413e3 --- /dev/null +++ b/host/lib/usrp_clock/octoclock/kk_ihex_license.txt @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013-2015 Kimmo Kulovesi + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/host/lib/usrp_clock/octoclock/kk_ihex_read.c b/host/lib/usrp_clock/octoclock/kk_ihex_read.c new file mode 100644 index 000000000..964cdd165 --- /dev/null +++ b/host/lib/usrp_clock/octoclock/kk_ihex_read.c @@ -0,0 +1,261 @@ +/* + * kk_ihex_read.c: A simple library for reading the Intel HEX (IHEX) format. + * + * See the header `kk_ihex.h` for instructions. + * + * Copyright (c) 2013-2015 Kimmo Kulovesi, http://arkku.com/ + * Provided with absolutely no warranty, use at your own risk only. + * Use and distribute freely, mark modified copies as such. + * + * Modifications Copyright (c) 2015 National Instruments Corp. + */ + +#include "kk_ihex_read.h" + +#include <stdio.h> +#include <stdlib.h> + +#define IHEX_START ':' + +#define AUTODETECT_ADDRESS (~0UL) + +#define ADDRESS_HIGH_MASK ((ihex_address_t) 0xFFFF0000U) + +enum ihex_read_state { + READ_WAIT_FOR_START = 0, + READ_COUNT_HIGH = 1, + READ_COUNT_LOW, + READ_ADDRESS_MSB_HIGH, + READ_ADDRESS_MSB_LOW, + READ_ADDRESS_LSB_HIGH, + READ_ADDRESS_LSB_LOW, + READ_RECORD_TYPE_HIGH, + READ_RECORD_TYPE_LOW, + READ_DATA_HIGH, + READ_DATA_LOW +}; + +#define IHEX_READ_RECORD_TYPE_MASK 0x07 +#define IHEX_READ_STATE_MASK 0x78 +#define IHEX_READ_STATE_OFFSET 3 + +void +ihex_begin_read (struct ihex_state * const ihex) { + ihex->address = 0; +#ifndef IHEX_DISABLE_SEGMENTS + ihex->segment = 0; +#endif + ihex->flags = 0; + ihex->line_length = 0; + ihex->length = 0; +} + +void +ihex_read_at_address (struct ihex_state * const ihex, ihex_address_t address) { + ihex_begin_read(ihex); + ihex->address = address; +} + +#ifndef IHEX_DISABLE_SEGMENTS +void +ihex_read_at_segment (struct ihex_state * const ihex, ihex_segment_t segment) { + ihex_begin_read(ihex); + ihex->segment = segment; +} +#endif + +void +ihex_end_read (struct ihex_state * const ihex, FILE* outfile) { + uint_fast8_t type = ihex->flags & IHEX_READ_RECORD_TYPE_MASK; + uint_fast8_t sum; + if ((sum = ihex->length) == 0 && type == IHEX_DATA_RECORD) { + return; + } + { + // compute and validate checksum + const uint8_t * const eptr = ihex->data + sum; + const uint8_t *r = ihex->data; + sum += type + (ihex->address & 0xFFU) + ((ihex->address >> 8) & 0xFFU); + while (r != eptr) { + sum += *r++; + } + sum = (~sum + 1U) ^ *eptr; // *eptr is the received checksum + } + if (ihex_data_read(ihex, type, sum, outfile)) { + if (type == IHEX_EXTENDED_LINEAR_ADDRESS_RECORD) { + ihex->address &= 0xFFFFU; + ihex->address |= (((ihex_address_t) ihex->data[0]) << 24) | + (((ihex_address_t) ihex->data[1]) << 16); +#ifndef IHEX_DISABLE_SEGMENTS + } else if (type == IHEX_EXTENDED_SEGMENT_ADDRESS_RECORD) { + ihex->segment = (ihex_segment_t) ((ihex->data[0] << 8) | ihex->data[1]); +#endif + } + } + ihex->length = 0; + ihex->flags = 0; +} + +void +ihex_read_byte (struct ihex_state * const ihex, const char byte, FILE* outfile) { + uint_fast8_t b = (uint_fast8_t) byte; + uint_fast8_t len = ihex->length; + uint_fast8_t state = (ihex->flags & IHEX_READ_STATE_MASK); + ihex->flags ^= state; // turn off the old state + state >>= IHEX_READ_STATE_OFFSET; + + if (b >= '0' && b <= '9') { + b -= '0'; + } else if (b >= 'A' && b <= 'F') { + b -= 'A' - 10; + } else if (b >= 'a' && b <= 'f') { + b -= 'a' - 10; + } else if (b == IHEX_START) { + // sync to a new record at any state + state = READ_COUNT_HIGH; + goto end_read; + } else { + // ignore unknown characters (e.g., extra whitespace) + goto save_read_state; + } + + if (!(++state & 1)) { + // high nybble, store temporarily at end of data: + b <<= 4; + ihex->data[len] = b; + } else { + // low nybble, combine with stored high nybble: + b = (ihex->data[len] |= b); + switch (state >> 1) { + default: + // remain in initial state while waiting for : + return; + case (READ_COUNT_LOW >> 1): + // data length + ihex->line_length = b; +#if IHEX_LINE_MAX_LENGTH < 255 + if (b > IHEX_LINE_MAX_LENGTH) { + ihex_end_read(ihex); + return; + } +#endif + break; + case (READ_ADDRESS_MSB_LOW >> 1): + // high byte of 16-bit address + ihex->address &= ADDRESS_HIGH_MASK; // clear the 16-bit address + ihex->address |= ((ihex_address_t) b) << 8U; + break; + case (READ_ADDRESS_LSB_LOW >> 1): + // low byte of 16-bit address + ihex->address |= (ihex_address_t) b; + break; + case (READ_RECORD_TYPE_LOW >> 1): + // record type + if (b & ~IHEX_READ_RECORD_TYPE_MASK) { + // skip unknown record types silently + return; + } + ihex->flags = (ihex->flags & ~IHEX_READ_RECORD_TYPE_MASK) | b; + break; + case (READ_DATA_LOW >> 1): + if (len < ihex->line_length) { + // data byte + ihex->length = len + 1; + state = READ_DATA_HIGH; + goto save_read_state; + } + // end of line (last "data" byte is checksum) + state = READ_WAIT_FOR_START; + end_read: + ihex_end_read(ihex, outfile); + } + } +save_read_state: + ihex->flags |= state << IHEX_READ_STATE_OFFSET; +} + +void +ihex_read_bytes (struct ihex_state * ihex, + const char * data, + ihex_count_t count, + FILE* outfile) { + while (count > 0) { + ihex_read_byte(ihex, *data++, outfile); + --count; + } +} + +ihex_bool_t +ihex_data_read (struct ihex_state *ihex, + ihex_record_type_t type, + ihex_bool_t error, + FILE* outfile) { + unsigned long line_number = 1L; + unsigned long file_position = 0L; + unsigned long address_offset = 0L; + bool debug_enabled = false; + + if (error) { + (void) fprintf(stderr, "Checksum error on line %lu\n", line_number); + return false; + } + if ((error = (ihex->length < ihex->line_length))) { + (void) fprintf(stderr, "Line length error on line %lu\n", line_number); + return false; + } + if (!outfile) { + (void) fprintf(stderr, "Excess data after end of file record\n"); + return false; + } + if (type == IHEX_DATA_RECORD) { + unsigned long address = (unsigned long) IHEX_LINEAR_ADDRESS(ihex); + if (address < address_offset) { + if (address_offset == AUTODETECT_ADDRESS) { + // autodetect initial address + address_offset = address; + if (debug_enabled) { + (void) fprintf(stderr, "Address offset: 0x%lx\n", + address_offset); + } + } else { + (void) fprintf(stderr, "Address underflow on line %lu\n", + line_number); + return false; + } + } + address -= address_offset; + if (address != file_position) { + if (debug_enabled) { + (void) fprintf(stderr, + "Seeking from 0x%lx to 0x%lx on line %lu\n", + file_position, address, line_number); + } + if (outfile == stdout || fseek(outfile, (long) address, SEEK_SET)) { + if (file_position < address) { + // "seek" forward in stdout by writing NUL bytes + do { + (void) fputc('\0', outfile); + } while (++file_position < address); + } else { + perror("fseek"); + return false; + } + } + file_position = address; + } + if (!fwrite(ihex->data, ihex->length, 1, outfile)) { + perror("fwrite"); + return false; + } + file_position += ihex->length; + } else if (type == IHEX_END_OF_FILE_RECORD) { + if (debug_enabled) { + (void) fprintf(stderr, "%lu bytes written\n", file_position); + } + if (outfile != stdout) { + (void) fclose(outfile); + } + outfile = NULL; + } + return true; +} diff --git a/host/lib/usrp_clock/octoclock/kk_ihex_read.h b/host/lib/usrp_clock/octoclock/kk_ihex_read.h new file mode 100644 index 000000000..5e210fddb --- /dev/null +++ b/host/lib/usrp_clock/octoclock/kk_ihex_read.h @@ -0,0 +1,119 @@ +/* + * kk_ihex_read.h: A simple library for reading Intel HEX data. See + * the accompanying kk_ihex_write.h for IHEX write support. + * + * + * READING INTEL HEX DATA + * ---------------------- + * + * To read data in the Intel HEX format, you must perform the actual reading + * of bytes using other means (e.g., stdio). The bytes read must then be + * passed to `ihex_read_byte` and/or `ihex_read_bytes`. The reading functions + * will then call `ihex_data_read`, at which stage the `struct ihex_state` + * structure will contain the data along with its address. See below for + * details and example implementation of `ihex_data_read`. + * + * The sequence to read data in IHEX format is: + * struct ihex_state ihex; + * ihex_begin_read(&ihex); + * ihex_read_bytes(&ihex, my_input_bytes, length_of_my_input_bytes); + * ihex_end_read(&ihex); + * + * + * CONSERVING MEMORY + * ----------------- + * + * For memory-critical use, you can save additional memory by defining + * `IHEX_LINE_MAX_LENGTH` as something less than 255. Note, however, that + * this limit affects both reading and writing, so the resulting library + * will be unable to read lines with more than this number of data bytes. + * That said, I haven't encountered any IHEX files with more than 32 + * data bytes per line. + * + * + * Copyright (c) 2013-2015 Kimmo Kulovesi, http://arkku.com/ + * Provided with absolutely no warranty, use at your own risk only. + * Use and distribute freely, mark modified copies as such. + * + * Modifications Copyright (c) 2015 National Instruments Corp. + */ + +#ifndef KK_IHEX_READ_H +#define KK_IHEX_READ_H +#ifdef __cplusplus +extern "C" { +#endif + +#include "kk_ihex.h" + +#include <stdio.h> + +// Begin reading at address 0 +void ihex_begin_read(struct ihex_state * const ihex); + +// Begin reading at `address` (the lowest 16 bits of which will be ignored); +// this is required only if the high bytes of the 32-bit starting address +// are not specified in the input data and they are non-zero +void ihex_read_at_address(struct ihex_state *ihex, + ihex_address_t address); + +// Read a single character +void ihex_read_byte(struct ihex_state *ihex, char chr, FILE* outfile); + +// Read `count` bytes from `data` +void ihex_read_bytes(struct ihex_state * ihex, + const char * data, + ihex_count_t count, + FILE* outfile); + +// End reading (may call `ihex_data_read` if there is data waiting) +void ihex_end_read(struct ihex_state *ihex, FILE* outfile); + +// Called when a complete line has been read, the record type of which is +// passed as `type`. The `ihex` structure will have its fields `data`, +// `line_length`, `address`, and `segment` set appropriately. In case +// of reading an `IHEX_EXTENDED_LINEAR_ADDRESS_RECORD` or an +// `IHEX_EXTENDED_SEGMENT_ADDRESS_RECORD` the record's data is not +// yet parsed - it will be parsed into the `address` or `segment` field +// only if `ihex_data_read` returns `true`. This allows manual handling +// of extended addresses by parsing the `ihex->data` bytes. +// +// Possible error cases include checksum mismatch (which is indicated +// as an argument), and excessive line length (in case this has been +// compiled with `IHEX_LINE_MAX_LENGTH` less than 255) which is indicated +// by `line_length` greater than `length`. Unknown record types and +// other erroneous data is usually silently ignored by this minimalistic +// parser. (It is recommended to compute a hash over the complete data +// once received and verify that against the source.) +// +// Example implementation: +// +// ihex_bool_t ihex_data_read(struct ihex_state *ihex, +// ihex_record_type_t type, +// ihex_bool_t error) { +// error = error || (ihex->length < ihex->line_length); +// if (type == IHEX_DATA_RECORD && !error) { +// (void) fseek(outfile, IHEX_LINEAR_ADDRESS(ihex), SEEK_SET); +// (void) fwrite(ihex->data, 1, ihex->length, outfile); +// } else if (type == IHEX_END_OF_FILE_RECORD) { +// (void) fclose(outfile); +// } +// return !error; +// } +// +ihex_bool_t ihex_data_read(struct ihex_state *ihex, + ihex_record_type_t type, + ihex_bool_t checksum_mismatch, + FILE* outfile); + +// Begin reading at `segment`; this is required only if the initial segment +// is not specified in the input data and it is non-zero. +// +#ifndef IHEX_DISABLE_SEGMENTS +void ihex_read_at_segment(struct ihex_state *ihex, ihex_segment_t segment); +#endif + +#ifdef __cplusplus +} +#endif +#endif // !KK_IHEX_READ_H diff --git a/host/lib/usrp_clock/octoclock/octoclock_eeprom.cpp b/host/lib/usrp_clock/octoclock/octoclock_eeprom.cpp index 93c317191..49d1a0442 100644 --- a/host/lib/usrp_clock/octoclock/octoclock_eeprom.cpp +++ b/host/lib/usrp_clock/octoclock/octoclock_eeprom.cpp @@ -1,5 +1,5 @@ // -// Copyright 2014 Ettus Research LLC +// Copyright 2014-2015 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -19,6 +19,7 @@ #include <uhd/usrp_clock/octoclock_eeprom.hpp> #include <uhd/transport/udp_simple.hpp> #include <uhd/usrp/mboard_eeprom.hpp> +#include <uhd/types/byte_vector.hpp> #include <uhd/types/mac_addr.hpp> #include <uhd/utils/byteswap.hpp> #include <boost/assign/list_of.hpp> @@ -37,26 +38,6 @@ using namespace uhd::usrp_clock; using namespace uhd::transport; /*********************************************************************** - * Utility functions - **********************************************************************/ - -//! A wrapper around std::copy that takes ranges instead of iterators. -template<typename RangeSrc, typename RangeDst> inline -void byte_copy(const RangeSrc &src, RangeDst &dst){ - std::copy(boost::begin(src), boost::end(src), boost::begin(dst)); -} - -//! create a string from a byte vector, return empty if invalid ascii -static const std::string bytes_to_string(const byte_vector_t &bytes){ - std::string out; - BOOST_FOREACH(boost::uint8_t byte, bytes){ - if (byte < 32 or byte > 127) return out; - out += byte; - } - return out; -} - -/*********************************************************************** * Implementation **********************************************************************/ void octoclock_eeprom_t::_load(){ diff --git a/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp b/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp new file mode 100644 index 000000000..8b47da7e5 --- /dev/null +++ b/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp @@ -0,0 +1,382 @@ +// +// Copyright 2015 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 "octoclock_impl.hpp" +#include "common.h" +#include "kk_ihex_read.h" + +#include <uhd/device.hpp> +#include <uhd/image_loader.hpp> +#include <uhd/transport/udp_simple.hpp> +#include <uhd/types/device_addr.hpp> +#include <uhd/types/time_spec.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/paths.hpp> +#include <uhd/utils/static.hpp> + +#include <boost/cstdint.hpp> +#include <boost/filesystem.hpp> +#include <boost/format.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/thread.hpp> + +#include <cstdio> +#include <cstring> +#include <fstream> +#include <iostream> +#include <string> + +namespace fs = boost::filesystem; +using namespace uhd; +using namespace uhd::usrp_clock; +using namespace uhd::transport; + +#define OCTOCLOCK_FIRMWARE_MAX_SIZE_BYTES (1024*120) // Last 8 MB are for bootloader +#define OCTOCLOCK_BLOCK_SIZE 256 + +/* + * OctoClock burn session + */ +typedef struct { + bool found; + uhd::device_addr_t dev_addr; + std::string given_filepath; + std::string actual_filepath; // If using a .hex, this is the converted .bin + bool from_hex; + boost::uint32_t size; + boost::uint16_t crc; + boost::uint16_t num_blocks; + udp_simple::sptr ctrl_xport; + udp_simple::sptr fw_xport; + boost::uint8_t data_in[udp_simple::mtu]; +} octoclock_session_t; + +static void octoclock_calculate_crc(octoclock_session_t &session){ + std::ifstream ifile(session.actual_filepath.c_str()); + boost::uint8_t temp_image[OCTOCLOCK_FIRMWARE_MAX_SIZE_BYTES]; + ifile.read((char*)temp_image, session.size); + + session.crc = 0xFFFF; + for(size_t i = 0; i < session.size; i++){ + session.crc ^= temp_image[i]; + for(boost::uint8_t j = 0; j < 8; ++j){ + if(session.crc & 1) session.crc = (session.crc >> 1) ^ 0xA001; + else session.crc = (session.crc >> 1); + } + } + + ifile.close(); +} + +static void octoclock_convert_ihex(octoclock_session_t &session){ + struct ihex_state ihex; + ihex_count_t count; + char buf[256]; + FILE* infile = fopen(session.given_filepath.c_str(), "r"); + FILE* outfile = fopen(session.actual_filepath.c_str(), "w"); + uint64_t line_number = 1; + + ihex_begin_read(&ihex); + while(fgets(buf, 256, infile)){ + count = ihex_count_t(strlen(buf)); + ihex_read_bytes(&ihex, buf, count, outfile); + line_number += (count && buf[count - 1] == '\n'); + } + ihex_end_read(&ihex, outfile); // Closes outfile + + (void)fclose(infile); +} + +static void octoclock_validate_firmware_image(octoclock_session_t &session){ + if(not fs::exists(session.given_filepath)){ + throw uhd::runtime_error(str(boost::format("Could not find image at path \"%s\"") + % session.given_filepath)); + } + + std::string extension = fs::extension(session.given_filepath); + if(extension == ".bin"){ + session.actual_filepath = session.given_filepath; + session.from_hex = false; + } + else if(extension == ".hex"){ + session.actual_filepath = fs::path(fs::path(uhd::get_tmp_path()) / + str(boost::format("octoclock_fw_%d.bin") + % time_spec_t::get_system_time().get_full_secs()) + ).string(); + + octoclock_convert_ihex(session); + session.from_hex = true; + } + else throw uhd::runtime_error(str(boost::format("Invalid extension \"%s\". Extension must be .hex or .bin."))); + + session.size = fs::file_size(session.actual_filepath); + if(session.size > OCTOCLOCK_FIRMWARE_MAX_SIZE_BYTES){ + throw uhd::runtime_error(str(boost::format("The specified firmware image is too large: %d vs. %d") + % session.size % OCTOCLOCK_FIRMWARE_MAX_SIZE_BYTES)); + } + + session.num_blocks = (session.size % OCTOCLOCK_BLOCK_SIZE) ? ((session.size / OCTOCLOCK_BLOCK_SIZE) + 1) + : (session.size / OCTOCLOCK_BLOCK_SIZE); + + octoclock_calculate_crc(session); +} + +static void octoclock_setup_session(octoclock_session_t &session, + const uhd::device_addr_t &args, + const std::string &filepath){ + + // See if we can find an OctoClock with the given args + device_addrs_t devs = octoclock_find(args); + if(devs.size() == 0){ + session.found = false; + return; + } + else if(devs.size() > 1){ + std::string err_msg = "Could not resolve given args to a single OctoClock device.\n" + "Applicable devices:\n"; + + BOOST_FOREACH(const uhd::device_addr_t &dev, devs){ + std::string name = (dev["type"] == "octoclock") ? str(boost::format("OctoClock r%d") + % dev.get("revision","4")) + : "OctoClock Bootloader"; + err_msg += str(boost::format(" * %s (addr=%s)\n") + % name + % dev.get("addr")); + } + + err_msg += "\nSpecify one of these devices with the given args to load an image onto it."; + + throw uhd::runtime_error(err_msg); + } + + session.dev_addr = devs[0]; + + // If no filepath is given, use the default + if(filepath == ""){ + session.given_filepath = find_image_path(str(boost::format("octoclock_r%s_fw.hex") + % session.dev_addr.get("revision","4") + )); + } + else session.given_filepath = filepath; + + octoclock_validate_firmware_image(session); + + session.ctrl_xport = udp_simple::make_connected(session.dev_addr["addr"], + BOOST_STRINGIZE(OCTOCLOCK_UDP_CTRL_PORT)); + session.fw_xport = udp_simple::make_connected(session.dev_addr["addr"], + BOOST_STRINGIZE(OCTOCLOCK_UDP_FW_PORT)); +} + +static void octoclock_reset_into_bootloader(octoclock_session_t &session){ + + // Already in bootloader + if(session.dev_addr["type"] == "octoclock-bootloader") + return; + + octoclock_packet_t pkt_out; + pkt_out.sequence = uhd::htonx<boost::uint32_t>(std::rand()); + const octoclock_packet_t* pkt_in = reinterpret_cast<const octoclock_packet_t*>(session.data_in); + size_t len; + + std::cout << " -- Resetting into bootloader..." << std::flush; + UHD_OCTOCLOCK_SEND_AND_RECV(session.ctrl_xport, RESET_CMD, pkt_out, len, session.data_in); + if(UHD_OCTOCLOCK_PACKET_MATCHES(RESET_ACK, pkt_out, pkt_in, len)){ + + // Make sure this device is now in its bootloader + boost::this_thread::sleep(boost::posix_time::milliseconds(500)); + uhd::device_addrs_t octoclocks = uhd::device::find( + uhd::device_addr_t(str(boost::format("addr=%s") + % session.dev_addr["addr"] + ))); + if(octoclocks.size() == 0){ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Failed to reset OctoClock."); + } + else if(octoclocks[0]["type"] != "octoclock-bootloader"){ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Failed to reset OctoClock."); + } + else{ + std::cout << "successful." << std::endl; + session.dev_addr = octoclocks[0]; + } + } + else{ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Failed to reset OctoClock."); + } +} + +static void octoclock_burn(octoclock_session_t &session){ + + // Make sure we're in the bootloader for this + octoclock_reset_into_bootloader(session); + + octoclock_packet_t pkt_out; + pkt_out.sequence = htonx<boost::uint32_t>(std::rand()); + const octoclock_packet_t* pkt_in = reinterpret_cast<const octoclock_packet_t*>(session.data_in); + + // Tell OctoClock to prepare for burn + pkt_out.len = htonx<boost::uint16_t>(session.size); + size_t len = 0; + std::cout << " -- Preparing OctoClock for firmware load..." << std::flush; + pkt_out.len = session.size; + pkt_out.crc = session.crc; + UHD_OCTOCLOCK_SEND_AND_RECV(session.fw_xport, PREPARE_FW_BURN_CMD, pkt_out, len, session.data_in); + if(UHD_OCTOCLOCK_PACKET_MATCHES(FW_BURN_READY_ACK, pkt_out, pkt_in, len)){ + std::cout << "successful." << std::endl; + } + else{ + std::cout << "failed." << std::endl; + if(session.from_hex){ + fs::remove(session.actual_filepath); + } + throw uhd::runtime_error("Failed to prepare OctoClock for firmware load."); + } + + // Start burning + std::ifstream image(session.actual_filepath.c_str(), std::ios::binary); + for(size_t i = 0; i < session.num_blocks; i++){ + pkt_out.sequence++; + pkt_out.addr = i * OCTOCLOCK_BLOCK_SIZE; + + std::cout << str(boost::format("\r -- Loading firmware: %d%% (%d/%d blocks)") + % int((double(i)/double(session.num_blocks))*100) + % i % session.num_blocks) + << std::flush; + + memset(pkt_out.data, 0, OCTOCLOCK_BLOCK_SIZE); + image.read((char*)pkt_out.data, OCTOCLOCK_BLOCK_SIZE); + UHD_OCTOCLOCK_SEND_AND_RECV(session.fw_xport, FILE_TRANSFER_CMD, pkt_out, len, session.data_in); + if(not UHD_OCTOCLOCK_PACKET_MATCHES(FILE_TRANSFER_ACK, pkt_out, pkt_in, len)){ + image.close(); + std::cout << std::endl; + if(session.from_hex){ + fs::remove(session.actual_filepath); + } + throw uhd::runtime_error("Failed to load firmware."); + } + } + + std::cout << str(boost::format("\r -- Loading firmware: 100%% (%d/%d blocks)") + % session.num_blocks % session.num_blocks) + << std::endl; + image.close(); +} + +static void octoclock_verify(octoclock_session_t &session){ + + octoclock_packet_t pkt_out; + pkt_out.sequence = htonx<boost::uint32_t>(std::rand()); + const octoclock_packet_t* pkt_in = reinterpret_cast<const octoclock_packet_t*>(session.data_in); + size_t len = 0; + + std::ifstream image(session.actual_filepath.c_str(), std::ios::binary); + boost::uint8_t image_part[OCTOCLOCK_BLOCK_SIZE]; + boost::uint16_t cmp_len = 0; + for(size_t i = 0; i < session.num_blocks; i++){ + pkt_out.sequence++; + pkt_out.addr = i * OCTOCLOCK_BLOCK_SIZE; + + std::cout << str(boost::format("\r -- Verifying firmware load: %d%% (%d/%d blocks)") + % int((double(i)/double(session.num_blocks))*100) + % i % session.num_blocks) + << std::flush; + + memset(image_part, 0, OCTOCLOCK_BLOCK_SIZE); + image.read((char*)image_part, OCTOCLOCK_BLOCK_SIZE); + cmp_len = image.gcount(); + + UHD_OCTOCLOCK_SEND_AND_RECV(session.fw_xport, READ_FW_CMD, pkt_out, len, session.data_in); + if(UHD_OCTOCLOCK_PACKET_MATCHES(READ_FW_ACK, pkt_out, pkt_in, len)){ + if(memcmp(pkt_in->data, image_part, cmp_len)){ + std::cout << std::endl; + image.close(); + if(session.from_hex){ + fs::remove(session.actual_filepath); + } + throw uhd::runtime_error("Failed to verify OctoClock firmware."); + } + } + else{ + std::cout << std::endl; + image.close(); + if(session.from_hex){ + fs::remove(session.actual_filepath); + } + throw uhd::runtime_error("Failed to verify OctoClock firmware."); + } + } + + image.close(); + if(session.from_hex){ + fs::remove(session.actual_filepath); + } + std::cout << str(boost::format("\r -- Verifying firmware load: 100%% (%d/%d blocks)") + % session.num_blocks % session.num_blocks) + << std::endl; +} + +static void octoclock_finalize(octoclock_session_t &session){ + + octoclock_packet_t pkt_out; + pkt_out.sequence = htonx<boost::uint32_t>(std::rand()); + const octoclock_packet_t* pkt_in = reinterpret_cast<const octoclock_packet_t*>(session.data_in); + size_t len = 0; + + std::cout << " -- Finalizing firmware load..." << std::flush; + UHD_OCTOCLOCK_SEND_AND_RECV(session.fw_xport, FINALIZE_BURNING_CMD, pkt_out, len, session.data_in); + if(UHD_OCTOCLOCK_PACKET_MATCHES(FINALIZE_BURNING_ACK, pkt_out, pkt_in, len)){ + std::cout << "successful." << std::endl; + } + else{ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Failed to finalize OctoClock firmware load."); + } +} + +bool octoclock_image_loader(const image_loader::image_loader_args_t &image_loader_args){ + octoclock_session_t session; + octoclock_setup_session(session, + image_loader_args.args, + image_loader_args.firmware_path + ); + if(!session.found or !image_loader_args.load_firmware) return false; + + std::cout << boost::format("Unit: OctoClock (%s)") + % session.dev_addr["addr"] + << std::endl; + std::cout << "Firmware: " << session.given_filepath << std::endl; + + octoclock_burn(session); + octoclock_verify(session); + octoclock_finalize(session); + + return true; +} + +UHD_STATIC_BLOCK(register_octoclock_image_loader){ + std::string recovery_instructions = "Aborting. Your OctoClock firmware is now corrupt. The bootloader\n" + "is functional, but the device will not have functional clock distribution.\n" + "Run this utility again to restore functionality or refer to:\n\n" + "http://files.ettus.com/manual/page_octoclock.html\n\n" + "for alternative setups."; + + image_loader::register_image_loader("octoclock", + octoclock_image_loader, + recovery_instructions); +} diff --git a/host/lib/usrp_clock/octoclock/octoclock_impl.cpp b/host/lib/usrp_clock/octoclock/octoclock_impl.cpp index b98d95725..ef1bc8ca0 100644 --- a/host/lib/usrp_clock/octoclock/octoclock_impl.cpp +++ b/host/lib/usrp_clock/octoclock/octoclock_impl.cpp @@ -357,14 +357,14 @@ void octoclock_impl::_get_state(const std::string &oc){ } uhd::dict<ref_t, std::string> _ref_strings = boost::assign::map_list_of - (NO_REF, "none") + (NO_REF, "none") (INTERNAL, "internal") (EXTERNAL, "external") ; uhd::dict<switch_pos_t, std::string> _switch_pos_strings = boost::assign::map_list_of - (UP, "Prefer internal") - (DOWN, "Prefer external") + (PREFER_INTERNAL, "Prefer internal") + (PREFER_EXTERNAL, "Prefer external") ; sensor_value_t octoclock_impl::_ext_ref_detected(const std::string &oc){ @@ -410,7 +410,7 @@ boost::uint32_t octoclock_impl::_get_time(const std::string &oc){ } std::string octoclock_impl::_get_images_help_message(const std::string &addr){ - const std::string image_name = "octoclock_r4_fw.bin"; + const std::string image_name = "octoclock_r4_fw.hex"; //Check to see if image is in default location std::string image_location; diff --git a/host/lib/usrp_clock/octoclock/octoclock_impl.hpp b/host/lib/usrp_clock/octoclock/octoclock_impl.hpp index ab45cd5f0..453e75ec5 100644 --- a/host/lib/usrp_clock/octoclock/octoclock_impl.hpp +++ b/host/lib/usrp_clock/octoclock/octoclock_impl.hpp @@ -31,6 +31,8 @@ #include "common.h" +uhd::device_addrs_t octoclock_find(const uhd::device_addr_t &hint); + /*! * OctoClock implementation guts */ diff --git a/host/lib/usrp_clock/usrp_clock_c.cpp b/host/lib/usrp_clock/usrp_clock_c.cpp new file mode 100644 index 000000000..220112f37 --- /dev/null +++ b/host/lib/usrp_clock/usrp_clock_c.cpp @@ -0,0 +1,176 @@ +/* + * Copyright 2015 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/>. + */ + +/* C-Interface for multi_usrp_clock */ + +#include <uhd/utils/static.hpp> +#include <uhd/usrp_clock/multi_usrp_clock.hpp> + +#include <uhd/usrp_clock/usrp_clock.h> + +#include <boost/foreach.hpp> +#include <boost/thread/mutex.hpp> + +#include <string.h> +#include <map> + +/**************************************************************************** + * Registry / Pointer Management + ***************************************************************************/ +/* Public structs */ +struct uhd_usrp_clock { + size_t usrp_clock_index; + std::string last_error; +}; + +/* Not public: We use this for our internal registry */ +struct usrp_clock_ptr { + uhd::usrp_clock::multi_usrp_clock::sptr ptr; + static size_t usrp_clock_counter; +}; +size_t usrp_clock_ptr::usrp_clock_counter = 0; +typedef struct usrp_clock_ptr usrp_clock_ptr; +/* Prefer map, because the list can be discontiguous */ +typedef std::map<size_t, usrp_clock_ptr> usrp_clock_ptrs; + +UHD_SINGLETON_FCN(usrp_clock_ptrs, get_usrp_clock_ptrs); +/* Shortcut for accessing the underlying USRP clock sptr from a uhd_usrp_clock_handle* */ +#define USRP_CLOCK(h_ptr) (get_usrp_clock_ptrs()[h_ptr->usrp_clock_index].ptr) + +/**************************************************************************** + * Generate / Destroy API calls + ***************************************************************************/ +static boost::mutex _usrp_clock_find_mutex; +uhd_error uhd_usrp_clock_find( + const char* args, + uhd_string_vector_t *devices_out +){ + UHD_SAFE_C( + boost::mutex::scoped_lock lock(_usrp_clock_find_mutex); + + uhd::device_addrs_t devs = uhd::device::find(std::string(args), uhd::device::CLOCK); + devices_out->string_vector_cpp.clear(); + BOOST_FOREACH(const uhd::device_addr_t &dev, devs){ + devices_out->string_vector_cpp.push_back(dev.to_string()); + } + ) +} + +static boost::mutex _usrp_clock_make_mutex; +uhd_error uhd_usrp_clock_make( + uhd_usrp_clock_handle *h, + const char *args +){ + UHD_SAFE_C( + boost::mutex::scoped_lock lock(_usrp_clock_make_mutex); + + size_t usrp_clock_count = usrp_clock_ptr::usrp_clock_counter; + usrp_clock_ptr::usrp_clock_counter++; + + // Initialize USRP Clock + uhd::device_addr_t device_addr(args); + usrp_clock_ptr P; + P.ptr = uhd::usrp_clock::multi_usrp_clock::make(device_addr); + + // Dump into registry + get_usrp_clock_ptrs()[usrp_clock_count] = P; + + // Update handle + (*h) = new uhd_usrp_clock; + (*h)->usrp_clock_index = usrp_clock_count; + ) +} + +static boost::mutex _usrp_clock_free_mutex; +uhd_error uhd_usrp_clock_free( + uhd_usrp_clock_handle *h +){ + UHD_SAFE_C( + boost::mutex::scoped_lock lock(_usrp_clock_free_mutex); + + if(!get_usrp_clock_ptrs().count((*h)->usrp_clock_index)){ + return UHD_ERROR_INVALID_DEVICE; + } + + get_usrp_clock_ptrs().erase((*h)->usrp_clock_index); + delete *h; + *h = NULL; + ) +} + +uhd_error uhd_usrp_clock_last_error( + uhd_usrp_clock_handle h, + char* error_out, + size_t strbuffer_len +){ + UHD_SAFE_C( + memset(error_out, '\0', strbuffer_len); + strncpy(error_out, h->last_error.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_usrp_clock_get_pp_string( + uhd_usrp_clock_handle h, + char* pp_string_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + memset(pp_string_out, '\0', strbuffer_len); + strncpy(pp_string_out, USRP_CLOCK(h)->get_pp_string().c_str(), strbuffer_len); + ) +} + +uhd_error uhd_usrp_clock_get_num_boards( + uhd_usrp_clock_handle h, + size_t *num_boards_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *num_boards_out = USRP_CLOCK(h)->get_num_boards(); + ) +} + +uhd_error uhd_usrp_clock_get_time( + uhd_usrp_clock_handle h, + size_t board, + uint32_t *clock_time_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *clock_time_out = USRP_CLOCK(h)->get_time(board); + ) +} + +uhd_error uhd_usrp_clock_get_sensor( + uhd_usrp_clock_handle h, + const char* name, + size_t board, + uhd_sensor_value_handle *sensor_value_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + delete (*sensor_value_out)->sensor_value_cpp; + (*sensor_value_out)->sensor_value_cpp = new uhd::sensor_value_t(USRP_CLOCK(h)->get_sensor(name, board)); + ) +} + +uhd_error uhd_usrp_clock_get_sensor_names( + uhd_usrp_clock_handle h, + size_t board, + uhd_string_vector_handle *sensor_names_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + (*sensor_names_out)->string_vector_cpp = USRP_CLOCK(h)->get_sensor_names(board); + ) +} diff --git a/host/lib/utils/CMakeLists.txt b/host/lib/utils/CMakeLists.txt index 369920ac1..c5c975dfa 100644 --- a/host/lib/utils/CMakeLists.txt +++ b/host/lib/utils/CMakeLists.txt @@ -141,3 +141,9 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/tasks.cpp ${CMAKE_CURRENT_SOURCE_DIR}/thread_priority.cpp ) + +IF(ENABLE_C_API) + LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/thread_priority_c.cpp + ) +ENDIF(ENABLE_C_API) diff --git a/host/lib/utils/paths.cpp b/host/lib/utils/paths.cpp index f29318ddd..eb9e69a49 100644 --- a/host/lib/utils/paths.cpp +++ b/host/lib/utils/paths.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2012 Ettus Research LLC +// Copyright 2010-2012,2015 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 @@ -111,6 +111,7 @@ static std::vector<std::string> get_env_paths(const std::string &var_name){ return paths; } +#ifndef UHD_PLATFORM_WIN32 /*! Expand a tilde character to the $HOME path. * * The path passed to this function must start with the tilde character in order @@ -132,6 +133,7 @@ static std::string expand_home_directory(std::string path) { return path; } +#endif /*********************************************************************** * Implement the functions in paths.hpp @@ -239,7 +241,7 @@ std::string _get_images_path_from_registry(const std::string& registry_key_path) //Get a handle to the key location HKEY hkey_location; - if (RegOpenKeyExA(hkey_parent, reg_path.c_str(), NULL, KEY_QUERY_VALUE, &hkey_location) != ERROR_SUCCESS) + if (RegOpenKeyExA(hkey_parent, reg_path.c_str(), 0, KEY_QUERY_VALUE, &hkey_location) != ERROR_SUCCESS) return std::string(); //Query key value @@ -259,7 +261,7 @@ std::string _get_images_path_from_registry(const std::string& registry_key_path) } #endif /*UHD_PLATFORM_WIN32*/ -std::string uhd::get_images_dir(const std::string search_paths) { +std::string uhd::get_images_dir(const std::string &search_paths) { /* This function will check for the existence of directories in this * order: @@ -330,7 +332,7 @@ std::string uhd::get_images_dir(const std::string search_paths) { } } -std::string uhd::find_image_path(const std::string &image_name, const std::string search_paths){ +std::string uhd::find_image_path(const std::string &image_name, const std::string &search_paths){ /* If a path was provided on the command-line or as a hint from the caller, * we default to that. */ if (fs::exists(image_name)){ @@ -362,15 +364,15 @@ std::string uhd::find_image_path(const std::string &image_name, const std::strin + uhd::print_utility_error("uhd_images_downloader.py")); } -std::string uhd::find_utility(std::string name) { +std::string uhd::find_utility(const std::string &name) { return fs::path(fs::path(uhd::get_pkg_path()) / UHD_LIB_DIR / "uhd" / "utils" / name) .string(); } -std::string uhd::print_utility_error(std::string name){ +std::string uhd::print_utility_error(const std::string &name, const std::string &args){ #ifdef UHD_PLATFORM_WIN32 - return "As an Administrator, please run:\n\n\"" + find_utility(name) + "\""; + return "As an Administrator, please run:\n\n\"" + find_utility(name) + args + "\""; #else - return "Please run:\n\n \"" + find_utility(name) + "\""; + return "Please run:\n\n \"" + find_utility(name) + (args.empty() ? "" : (" " + args)) + "\""; #endif } diff --git a/host/lib/utils/platform.cpp b/host/lib/utils/platform.cpp index e2f92039e..a9cef663b 100644 --- a/host/lib/utils/platform.cpp +++ b/host/lib/utils/platform.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2012 Ettus Research LLC +// Copyright 2010-2012,2014 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -19,7 +19,7 @@ #include <uhd/config.hpp> #include <boost/functional/hash.hpp> #ifdef UHD_PLATFORM_WIN32 -#include <Windows.h> +#include <windows.h> #else #include <unistd.h> #endif diff --git a/host/lib/utils/thread_priority.cpp b/host/lib/utils/thread_priority.cpp index 7c3faa37a..98023c5aa 100644 --- a/host/lib/utils/thread_priority.cpp +++ b/host/lib/utils/thread_priority.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2011,2015 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 @@ -74,7 +74,7 @@ static void check_priority_range(float priority){ #ifdef HAVE_WIN_SETTHREADPRIORITY #include <windows.h> - void uhd::set_thread_priority(float priority, bool realtime){ + void uhd::set_thread_priority(float priority, UHD_UNUSED(bool realtime)){ check_priority_range(priority); /* diff --git a/host/lib/utils/thread_priority_c.cpp b/host/lib/utils/thread_priority_c.cpp new file mode 100644 index 000000000..fe019e51d --- /dev/null +++ b/host/lib/utils/thread_priority_c.cpp @@ -0,0 +1,33 @@ +// +// Copyright 2015 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/error.h> +#include <uhd/utils/thread_priority.h> +#include <uhd/utils/thread_priority.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/exception.hpp> +#include <boost/format.hpp> +#include <iostream> + +uhd_error uhd_set_thread_priority( + float priority, + bool realtime +){ + UHD_SAFE_C( + uhd::set_thread_priority(priority, realtime); + ) +} |