aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib')
-rw-r--r--host/lib/CMakeLists.txt25
-rw-r--r--host/lib/convert/CMakeLists.txt34
-rw-r--r--host/lib/convert/convert_common.hpp6
-rw-r--r--host/lib/convert/convert_impl.cpp33
-rw-r--r--host/lib/convert/convert_orc.orc79
-rw-r--r--host/lib/convert/convert_with_orc.cpp65
-rw-r--r--host/lib/convert/gen_convert_general.py156
-rw-r--r--host/lib/error_c.cpp76
-rw-r--r--host/lib/exception.cpp5
-rwxr-xr-x[-rw-r--r--]host/lib/ic_reg_maps/common.py117
-rwxr-xr-xhost/lib/ic_reg_maps/gen_ad5623_regs.py6
-rwxr-xr-xhost/lib/ic_reg_maps/gen_ad7922_regs.py12
-rwxr-xr-xhost/lib/ic_reg_maps/gen_ad9510_regs.py62
-rwxr-xr-xhost/lib/ic_reg_maps/gen_ad9522_regs.py85
-rwxr-xr-xhost/lib/ic_reg_maps/gen_ad9777_regs.py12
-rwxr-xr-xhost/lib/ic_reg_maps/gen_ad9862_regs.py70
-rwxr-xr-xhost/lib/ic_reg_maps/gen_adf4350_regs.py16
-rwxr-xr-xhost/lib/ic_reg_maps/gen_adf4351_regs.py16
-rwxr-xr-xhost/lib/ic_reg_maps/gen_adf4360_regs.py18
-rwxr-xr-xhost/lib/ic_reg_maps/gen_ads62p44_regs.py12
-rwxr-xr-x[-rw-r--r--]host/lib/ic_reg_maps/gen_ads62p48_regs.py12
-rwxr-xr-x[-rw-r--r--]host/lib/ic_reg_maps/gen_lmk04816_regs.py51
-rwxr-xr-xhost/lib/ic_reg_maps/gen_max2112_regs.py51
-rwxr-xr-xhost/lib/ic_reg_maps/gen_max2118_regs.py47
-rwxr-xr-xhost/lib/ic_reg_maps/gen_max2829_regs.py12
-rwxr-xr-x[-rw-r--r--]host/lib/ic_reg_maps/gen_max2870_regs.py43
-rwxr-xr-x[-rw-r--r--]host/lib/ic_reg_maps/gen_max2871_regs.py43
-rwxr-xr-xhost/lib/ic_reg_maps/gen_tda18272hnm_regs.py137
-rwxr-xr-x[-rw-r--r--]host/lib/ic_reg_maps/gen_tuner_4937di5_regs.py12
-rw-r--r--host/lib/image_loader.cpp87
-rw-r--r--host/lib/transport/CMakeLists.txt5
-rw-r--r--host/lib/transport/chdr.cpp182
-rw-r--r--host/lib/transport/gen_vrt_if_packet.py269
-rw-r--r--host/lib/transport/libusb1_base.cpp48
-rw-r--r--host/lib/transport/libusb1_base.hpp27
-rw-r--r--host/lib/transport/libusb1_zero_copy.cpp73
-rwxr-xr-xhost/lib/transport/nirio/lvbitx/process-lvbitx.py16
-rw-r--r--host/lib/transport/nirio/nirio_driver_iface_linux.cpp10
-rw-r--r--host/lib/transport/nirio/niriok_proxy_impl_v1.cpp421
-rw-r--r--host/lib/transport/nirio/niriok_proxy_impl_v2.cpp270
-rw-r--r--host/lib/transport/nirio/rpc/rpc_client.cpp2
-rw-r--r--host/lib/transport/super_recv_packet_handler.hpp7
-rw-r--r--host/lib/transport/udp_zero_copy.cpp9
-rw-r--r--host/lib/types/CMakeLists.txt16
-rw-r--r--host/lib/types/byte_vector.cpp42
-rw-r--r--host/lib/types/filters.cpp74
-rw-r--r--host/lib/types/metadata_c.cpp315
-rw-r--r--host/lib/types/ranges_c.cpp162
-rw-r--r--host/lib/types/sensors_c.cpp228
-rw-r--r--host/lib/types/sid.cpp153
-rw-r--r--host/lib/types/string_vector_c.cpp80
-rw-r--r--host/lib/types/tune_c.cpp76
-rw-r--r--host/lib/types/usrp_info_c.cpp44
-rw-r--r--host/lib/usrp/CMakeLists.txt24
-rw-r--r--host/lib/usrp/b100/CMakeLists.txt4
-rw-r--r--host/lib/usrp/b200/CMakeLists.txt5
-rw-r--r--host/lib/usrp/b200/b200_iface.cpp4
-rw-r--r--host/lib/usrp/b200/b200_iface.hpp31
-rw-r--r--host/lib/usrp/b200/b200_image_loader.cpp150
-rw-r--r--host/lib/usrp/b200/b200_impl.cpp630
-rw-r--r--host/lib/usrp/b200/b200_impl.hpp112
-rw-r--r--host/lib/usrp/b200/b200_io_impl.cpp163
-rw-r--r--host/lib/usrp/b200/b200_regs.hpp2
-rw-r--r--host/lib/usrp/common/CMakeLists.txt1
-rw-r--r--host/lib/usrp/common/ad9361_ctrl.cpp83
-rw-r--r--host/lib/usrp/common/ad9361_ctrl.hpp71
-rw-r--r--host/lib/usrp/common/ad9361_driver/ad9361_client.h15
-rw-r--r--host/lib/usrp/common/ad9361_driver/ad9361_device.cpp1104
-rw-r--r--host/lib/usrp/common/ad9361_driver/ad9361_device.h148
-rw-r--r--host/lib/usrp/common/ad9361_driver/ad9361_filter_taps.h15
-rw-r--r--host/lib/usrp/common/ad9361_driver/ad9361_gain_tables.h177
-rw-r--r--host/lib/usrp/common/ad9361_driver/ad9361_synth_lut.h43
-rw-r--r--host/lib/usrp/common/ad936x_manager.cpp280
-rw-r--r--host/lib/usrp/common/ad936x_manager.hpp132
-rw-r--r--host/lib/usrp/cores/gpio_core_200.hpp26
-rw-r--r--host/lib/usrp/cores/rx_dsp_core_200.cpp1
-rw-r--r--host/lib/usrp/cores/rx_dsp_core_3000.cpp21
-rw-r--r--host/lib/usrp/cores/rx_dsp_core_3000.hpp10
-rw-r--r--host/lib/usrp/cores/rx_frontend_core_200.cpp21
-rw-r--r--host/lib/usrp/cores/rx_frontend_core_200.hpp9
-rw-r--r--host/lib/usrp/cores/tx_dsp_core_3000.cpp21
-rw-r--r--host/lib/usrp/cores/tx_dsp_core_3000.hpp8
-rw-r--r--host/lib/usrp/cores/tx_frontend_core_200.cpp16
-rw-r--r--host/lib/usrp/cores/tx_frontend_core_200.hpp8
-rw-r--r--host/lib/usrp/dboard/db_cbx.cpp2
-rw-r--r--host/lib/usrp/dboard/db_sbx_common.hpp4
-rw-r--r--host/lib/usrp/dboard_eeprom.cpp27
-rw-r--r--host/lib/usrp/dboard_eeprom_c.cpp109
-rw-r--r--host/lib/usrp/e100/CMakeLists.txt4
-rw-r--r--host/lib/usrp/e100/e100_impl.cpp34
-rw-r--r--host/lib/usrp/e100/e100_impl.hpp4
-rw-r--r--host/lib/usrp/e100/fpga_downloader.cpp41
-rw-r--r--host/lib/usrp/e300/CMakeLists.txt18
-rw-r--r--host/lib/usrp/e300/e300_async_serial.cpp245
-rw-r--r--host/lib/usrp/e300/e300_async_serial.hpp113
-rw-r--r--host/lib/usrp/e300/e300_common.cpp51
-rw-r--r--host/lib/usrp/e300/e300_defaults.hpp9
-rw-r--r--host/lib/usrp/e300/e300_fpga_defs.hpp2
-rw-r--r--host/lib/usrp/e300/e300_impl.cpp352
-rw-r--r--host/lib/usrp/e300/e300_impl.hpp27
-rw-r--r--host/lib/usrp/e300/e300_io_impl.cpp12
-rw-r--r--host/lib/usrp/e300/e300_network.cpp34
-rw-r--r--host/lib/usrp/e300/e300_regs.hpp62
-rw-r--r--host/lib/usrp/e300/e300_remote_codec_ctrl.cpp115
-rw-r--r--host/lib/usrp/e300/e300_remote_codec_ctrl.hpp14
-rw-r--r--host/lib/usrp/e300/e300_sensor_manager.cpp141
-rw-r--r--host/lib/usrp/e300/e300_sensor_manager.hpp9
-rw-r--r--host/lib/usrp/e300/e300_ublox_control.hpp50
-rw-r--r--host/lib/usrp/e300/e300_ublox_control_impl.cpp505
-rw-r--r--host/lib/usrp/e300/e300_ublox_control_impl.hpp457
-rw-r--r--host/lib/usrp/gps_ctrl.cpp22
-rw-r--r--host/lib/usrp/gpsd_iface.cpp309
-rw-r--r--host/lib/usrp/gpsd_iface.hpp36
-rw-r--r--host/lib/usrp/mboard_eeprom.cpp43
-rw-r--r--host/lib/usrp/mboard_eeprom_c.cpp72
-rw-r--r--host/lib/usrp/multi_usrp.cpp322
-rw-r--r--host/lib/usrp/subdev_spec_c.cpp149
-rw-r--r--host/lib/usrp/usrp1/CMakeLists.txt4
-rw-r--r--host/lib/usrp/usrp2/CMakeLists.txt17
-rw-r--r--host/lib/usrp/usrp2/n200_image_loader.cpp646
-rw-r--r--host/lib/usrp/usrp2/usrp2_iface.cpp10
-rw-r--r--host/lib/usrp/usrp2/usrp2_impl.cpp5
-rw-r--r--host/lib/usrp/usrp2/usrp2_impl.hpp3
-rw-r--r--host/lib/usrp/usrp_c.cpp1413
-rw-r--r--host/lib/usrp/x300/CMakeLists.txt7
-rw-r--r--host/lib/usrp/x300/cdecode.c80
-rw-r--r--host/lib/usrp/x300/cdecode.h36
-rw-r--r--host/lib/usrp/x300/x300_adc_ctrl.cpp4
-rw-r--r--host/lib/usrp/x300/x300_adc_dac_utils.cpp414
-rw-r--r--host/lib/usrp/x300/x300_clock_ctrl.cpp238
-rw-r--r--host/lib/usrp/x300/x300_clock_ctrl.hpp18
-rw-r--r--host/lib/usrp/x300/x300_dac_ctrl.cpp16
-rw-r--r--host/lib/usrp/x300/x300_fw_common.h7
-rw-r--r--host/lib/usrp/x300/x300_image_loader.cpp416
-rw-r--r--host/lib/usrp/x300/x300_impl.cpp623
-rw-r--r--host/lib/usrp/x300/x300_impl.hpp41
-rw-r--r--host/lib/usrp/x300/x300_io_impl.cpp52
-rw-r--r--host/lib/usrp/x300/x300_regs.hpp261
-rw-r--r--host/lib/usrp_clock/CMakeLists.txt8
-rw-r--r--host/lib/usrp_clock/octoclock/CMakeLists.txt12
-rw-r--r--host/lib/usrp_clock/octoclock/common.h35
-rw-r--r--host/lib/usrp_clock/octoclock/kk_ihex.h191
-rw-r--r--host/lib/usrp_clock/octoclock/kk_ihex_license.txt20
-rw-r--r--host/lib/usrp_clock/octoclock/kk_ihex_read.c261
-rw-r--r--host/lib/usrp_clock/octoclock/kk_ihex_read.h119
-rw-r--r--host/lib/usrp_clock/octoclock/octoclock_eeprom.cpp23
-rw-r--r--host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp382
-rw-r--r--host/lib/usrp_clock/octoclock/octoclock_impl.cpp8
-rw-r--r--host/lib/usrp_clock/octoclock/octoclock_impl.hpp2
-rw-r--r--host/lib/usrp_clock/usrp_clock_c.cpp176
-rw-r--r--host/lib/utils/CMakeLists.txt6
-rw-r--r--host/lib/utils/paths.cpp18
-rw-r--r--host/lib/utils/platform.cpp4
-rw-r--r--host/lib/utils/thread_priority.cpp4
-rw-r--r--host/lib/utils/thread_priority_c.cpp33
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> &regs)
+void sbx_xcvr::cbx::write_lo_regs(dboard_iface::unit_t unit, const std::vector<boost::uint32_t> &regs)
{
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> &regs);
+ void write_lo_regs(dboard_iface::unit_t unit, const std::vector<boost::uint32_t> &regs);
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);
+ )
+}