aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib')
-rw-r--r--host/lib/CMakeLists.txt112
-rw-r--r--host/lib/convert/CMakeLists.txt120
-rw-r--r--host/lib/convert/convert_common.hpp241
-rw-r--r--host/lib/convert/convert_fc32_to_sc8_with_sse2.cpp150
-rw-r--r--host/lib/convert/convert_fc32_with_sse2.cpp196
-rw-r--r--host/lib/convert/convert_fc64_to_sc8_with_sse2.cpp156
-rw-r--r--host/lib/convert/convert_fc64_with_sse2.cpp212
-rw-r--r--host/lib/convert/convert_impl.cpp143
-rw-r--r--host/lib/convert/convert_orc.orc80
-rw-r--r--host/lib/convert/convert_with_neon.cpp61
-rw-r--r--host/lib/convert/convert_with_orc.cpp65
-rw-r--r--host/lib/convert/convert_with_tables.cpp282
-rw-r--r--host/lib/convert/gen_convert_general.py206
-rw-r--r--host/lib/deprecated.cpp81
-rw-r--r--host/lib/device.cpp155
-rw-r--r--host/lib/exception.cpp45
-rw-r--r--host/lib/ic_reg_maps/CMakeLists.txt105
-rw-r--r--host/lib/ic_reg_maps/common.py196
-rwxr-xr-xhost/lib/ic_reg_maps/gen_ad5623_regs.py48
-rwxr-xr-xhost/lib/ic_reg_maps/gen_ad7922_regs.py54
-rwxr-xr-xhost/lib/ic_reg_maps/gen_ad9510_regs.py139
-rwxr-xr-xhost/lib/ic_reg_maps/gen_ad9522_regs.py193
-rwxr-xr-xhost/lib/ic_reg_maps/gen_ad9777_regs.py120
-rwxr-xr-xhost/lib/ic_reg_maps/gen_ad9862_regs.py246
-rwxr-xr-xhost/lib/ic_reg_maps/gen_adf4350_regs.py122
-rwxr-xr-xhost/lib/ic_reg_maps/gen_adf4351_regs.py138
-rwxr-xr-xhost/lib/ic_reg_maps/gen_adf4360_regs.py89
-rwxr-xr-xhost/lib/ic_reg_maps/gen_ads62p44_regs.py124
-rwxr-xr-xhost/lib/ic_reg_maps/gen_max2112_regs.py181
-rwxr-xr-xhost/lib/ic_reg_maps/gen_max2118_regs.py126
-rwxr-xr-xhost/lib/ic_reg_maps/gen_max2829_regs.py133
-rwxr-xr-xhost/lib/ic_reg_maps/gen_tda18272hnm_regs.py521
-rw-r--r--host/lib/ic_reg_maps/gen_tuner_4937di5_regs.py75
-rw-r--r--host/lib/property_tree.cpp181
-rw-r--r--host/lib/transport/CMakeLists.txt106
-rw-r--r--host/lib/transport/buffer_pool.cpp80
-rw-r--r--host/lib/transport/gen_vrt_if_packet.py286
-rw-r--r--host/lib/transport/if_addrs.cpp121
-rw-r--r--host/lib/transport/libusb1_base.cpp279
-rw-r--r--host/lib/transport/libusb1_base.hpp149
-rw-r--r--host/lib/transport/libusb1_control.cpp67
-rw-r--r--host/lib/transport/libusb1_zero_copy.cpp300
-rw-r--r--host/lib/transport/super_recv_packet_handler.hpp582
-rw-r--r--host/lib/transport/super_send_packet_handler.hpp282
-rw-r--r--host/lib/transport/udp_common.hpp60
-rw-r--r--host/lib/transport/udp_simple.cpp134
-rw-r--r--host/lib/transport/udp_zero_copy.cpp312
-rw-r--r--host/lib/transport/usb_dummy_impl.cpp38
-rw-r--r--host/lib/transport/usb_zero_copy_wrapper.cpp198
-rw-r--r--host/lib/types/CMakeLists.txt91
-rw-r--r--host/lib/types/device_addr.cpp133
-rw-r--r--host/lib/types/mac_addr.cpp75
-rw-r--r--host/lib/types/ranges.cpp163
-rw-r--r--host/lib/types/sensors.cpp94
-rw-r--r--host/lib/types/serial.cpp78
-rw-r--r--host/lib/types/time_spec.cpp156
-rw-r--r--host/lib/types/tune.cpp52
-rw-r--r--host/lib/types/types.cpp44
-rw-r--r--host/lib/usrp/CMakeLists.txt39
-rw-r--r--host/lib/usrp/README15
-rw-r--r--host/lib/usrp/b100/CMakeLists.txt36
-rw-r--r--host/lib/usrp/b100/b100_ctrl.cpp257
-rw-r--r--host/lib/usrp/b100/b100_ctrl.hpp70
-rw-r--r--host/lib/usrp/b100/b100_impl.cpp565
-rw-r--r--host/lib/usrp/b100/b100_impl.hpp134
-rw-r--r--host/lib/usrp/b100/b100_regs.hpp123
-rw-r--r--host/lib/usrp/b100/clock_ctrl.cpp536
-rw-r--r--host/lib/usrp/b100/clock_ctrl.hpp133
-rw-r--r--host/lib/usrp/b100/codec_ctrl.cpp283
-rw-r--r--host/lib/usrp/b100/codec_ctrl.hpp90
-rw-r--r--host/lib/usrp/b100/ctrl_packet.hpp75
-rw-r--r--host/lib/usrp/b100/dboard_iface.cpp258
-rw-r--r--host/lib/usrp/b100/io_impl.cpp295
-rw-r--r--host/lib/usrp/common/CMakeLists.txt35
-rw-r--r--host/lib/usrp/common/apply_corrections.cpp217
-rw-r--r--host/lib/usrp/common/apply_corrections.hpp41
-rw-r--r--host/lib/usrp/common/async_packet_handler.hpp71
-rw-r--r--host/lib/usrp/common/fx2_ctrl.cpp472
-rw-r--r--host/lib/usrp/common/fx2_ctrl.hpp129
-rw-r--r--host/lib/usrp/common/recv_packet_demuxer.cpp87
-rw-r--r--host/lib/usrp/common/recv_packet_demuxer.hpp41
-rw-r--r--host/lib/usrp/common/validate_subdev_spec.cpp73
-rw-r--r--host/lib/usrp/common/validate_subdev_spec.hpp38
-rw-r--r--host/lib/usrp/cores/CMakeLists.txt34
-rw-r--r--host/lib/usrp/cores/gpio_core_200.cpp100
-rw-r--r--host/lib/usrp/cores/gpio_core_200.hpp52
-rw-r--r--host/lib/usrp/cores/i2c_core_100.cpp140
-rw-r--r--host/lib/usrp/cores/i2c_core_100.hpp35
-rw-r--r--host/lib/usrp/cores/rx_dsp_core_200.cpp260
-rw-r--r--host/lib/usrp/cores/rx_dsp_core_200.hpp67
-rw-r--r--host/lib/usrp/cores/rx_frontend_core_200.cpp79
-rw-r--r--host/lib/usrp/cores/rx_frontend_core_200.hpp44
-rw-r--r--host/lib/usrp/cores/spi_core_100.cpp88
-rw-r--r--host/lib/usrp/cores/spi_core_100.hpp35
-rw-r--r--host/lib/usrp/cores/time64_core_200.cpp134
-rw-r--r--host/lib/usrp/cores/time64_core_200.hpp61
-rw-r--r--host/lib/usrp/cores/tx_dsp_core_200.cpp214
-rw-r--r--host/lib/usrp/cores/tx_dsp_core_200.hpp59
-rw-r--r--host/lib/usrp/cores/tx_frontend_core_200.cpp76
-rw-r--r--host/lib/usrp/cores/tx_frontend_core_200.hpp42
-rw-r--r--host/lib/usrp/cores/user_settings_core_200.cpp43
-rw-r--r--host/lib/usrp/cores/user_settings_core_200.hpp36
-rw-r--r--host/lib/usrp/cores/wb_iface.hpp60
-rw-r--r--host/lib/usrp/dboard/CMakeLists.txt40
-rw-r--r--host/lib/usrp/dboard/db_basic_and_lf.cpp197
-rw-r--r--host/lib/usrp/dboard/db_dbsrx.cpp536
-rw-r--r--host/lib/usrp/dboard/db_dbsrx2.cpp375
-rw-r--r--host/lib/usrp/dboard/db_rfx.cpp449
-rw-r--r--host/lib/usrp/dboard/db_sbx_common.cpp338
-rw-r--r--host/lib/usrp/dboard/db_sbx_common.hpp225
-rw-r--r--host/lib/usrp/dboard/db_sbx_version3.cpp186
-rw-r--r--host/lib/usrp/dboard/db_sbx_version4.cpp189
-rw-r--r--host/lib/usrp/dboard/db_tvrx.cpp411
-rw-r--r--host/lib/usrp/dboard/db_tvrx2.cpp1844
-rw-r--r--host/lib/usrp/dboard/db_unknown.cpp160
-rw-r--r--host/lib/usrp/dboard/db_wbx_common.cpp152
-rw-r--r--host/lib/usrp/dboard/db_wbx_common.hpp204
-rw-r--r--host/lib/usrp/dboard/db_wbx_simple.cpp159
-rw-r--r--host/lib/usrp/dboard/db_wbx_version2.cpp329
-rw-r--r--host/lib/usrp/dboard/db_wbx_version3.cpp361
-rw-r--r--host/lib/usrp/dboard/db_wbx_version4.cpp344
-rw-r--r--host/lib/usrp/dboard/db_xcvr2450.cpp673
-rw-r--r--host/lib/usrp/dboard_base.cpp100
-rw-r--r--host/lib/usrp/dboard_ctor_args.hpp38
-rw-r--r--host/lib/usrp/dboard_eeprom.cpp169
-rw-r--r--host/lib/usrp/dboard_id.cpp68
-rw-r--r--host/lib/usrp/dboard_iface.cpp78
-rw-r--r--host/lib/usrp/dboard_manager.cpp332
-rw-r--r--host/lib/usrp/e100/CMakeLists.txt40
-rw-r--r--host/lib/usrp/e100/clock_ctrl.cpp538
-rw-r--r--host/lib/usrp/e100/clock_ctrl.hpp127
-rw-r--r--host/lib/usrp/e100/codec_ctrl.cpp288
-rw-r--r--host/lib/usrp/e100/codec_ctrl.hpp90
-rw-r--r--host/lib/usrp/e100/dboard_iface.cpp258
-rw-r--r--host/lib/usrp/e100/e100_ctrl.cpp355
-rw-r--r--host/lib/usrp/e100/e100_ctrl.hpp48
-rw-r--r--host/lib/usrp/e100/e100_impl.cpp494
-rw-r--r--host/lib/usrp/e100/e100_impl.hpp140
-rw-r--r--host/lib/usrp/e100/e100_mmap_zero_copy.cpp269
-rw-r--r--host/lib/usrp/e100/e100_regs.hpp137
-rw-r--r--host/lib/usrp/e100/fpga_downloader.cpp272
-rw-r--r--host/lib/usrp/e100/include/linux/usrp_e.h60
-rw-r--r--host/lib/usrp/e100/io_impl.cpp366
-rw-r--r--host/lib/usrp/gps_ctrl.cpp275
-rw-r--r--host/lib/usrp/mboard_eeprom.cpp445
-rw-r--r--host/lib/usrp/multi_usrp.cpp849
-rw-r--r--host/lib/usrp/subdev_spec.cpp80
-rw-r--r--host/lib/usrp/usrp1/CMakeLists.txt36
-rw-r--r--host/lib/usrp/usrp1/codec_ctrl.cpp419
-rw-r--r--host/lib/usrp/usrp1/codec_ctrl.hpp99
-rw-r--r--host/lib/usrp/usrp1/dboard_iface.cpp397
-rw-r--r--host/lib/usrp/usrp1/io_impl.cpp693
-rw-r--r--host/lib/usrp/usrp1/soft_time_ctrl.cpp227
-rw-r--r--host/lib/usrp/usrp1/soft_time_ctrl.hpp74
-rw-r--r--host/lib/usrp/usrp1/usrp1_calc_mux.hpp156
-rw-r--r--host/lib/usrp/usrp1/usrp1_iface.cpp205
-rw-r--r--host/lib/usrp/usrp1/usrp1_iface.hpp44
-rw-r--r--host/lib/usrp/usrp1/usrp1_impl.cpp502
-rw-r--r--host/lib/usrp/usrp1/usrp1_impl.hpp175
-rw-r--r--host/lib/usrp/usrp2/CMakeLists.txt36
-rw-r--r--host/lib/usrp/usrp2/clock_ctrl.cpp390
-rw-r--r--host/lib/usrp/usrp2/clock_ctrl.hpp109
-rw-r--r--host/lib/usrp/usrp2/codec_ctrl.cpp216
-rw-r--r--host/lib/usrp/usrp2/codec_ctrl.hpp68
-rw-r--r--host/lib/usrp/usrp2/dboard_iface.cpp295
-rw-r--r--host/lib/usrp/usrp2/fw_common.h151
-rw-r--r--host/lib/usrp/usrp2/io_impl.cpp492
-rw-r--r--host/lib/usrp/usrp2/usrp2_clk_regs.hpp87
-rw-r--r--host/lib/usrp/usrp2/usrp2_iface.cpp378
-rw-r--r--host/lib/usrp/usrp2/usrp2_iface.hpp76
-rw-r--r--host/lib/usrp/usrp2/usrp2_impl.cpp763
-rw-r--r--host/lib/usrp/usrp2/usrp2_impl.hpp134
-rw-r--r--host/lib/usrp/usrp2/usrp2_regs.hpp107
-rw-r--r--host/lib/utils/CMakeLists.txt142
-rw-r--r--host/lib/utils/csv.cpp52
-rw-r--r--host/lib/utils/gain_group.cpp186
-rw-r--r--host/lib/utils/images.cpp40
-rw-r--r--host/lib/utils/load_modules.cpp109
-rw-r--r--host/lib/utils/log.cpp221
-rw-r--r--host/lib/utils/msg.cpp128
-rw-r--r--host/lib/utils/paths.cpp108
-rw-r--r--host/lib/utils/static.cpp32
-rw-r--r--host/lib/utils/tasks.cpp82
-rw-r--r--host/lib/utils/thread_priority.cpp110
-rw-r--r--host/lib/version.cpp37
185 files changed, 35412 insertions, 0 deletions
diff --git a/host/lib/CMakeLists.txt b/host/lib/CMakeLists.txt
new file mode 100644
index 000000000..7a76ebd53
--- /dev/null
+++ b/host/lib/CMakeLists.txt
@@ -0,0 +1,112 @@
+#
+# Copyright 2010-2011 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# Helpful Macros
+########################################################################
+MACRO(LIBUHD_APPEND_SOURCES)
+ LIST(APPEND libuhd_sources ${ARGV})
+ENDMACRO(LIBUHD_APPEND_SOURCES)
+
+MACRO(LIBUHD_APPEND_LIBS)
+ LIST(APPEND libuhd_libs ${ARGV})
+ENDMACRO(LIBUHD_APPEND_LIBS)
+
+MACRO(LIBUHD_PYTHON_GEN_SOURCE pyfile outfile)
+ #ensure that the directory exists for outfile
+ GET_FILENAME_COMPONENT(outfile_dir ${outfile} PATH)
+ FILE(MAKE_DIRECTORY ${outfile_dir})
+
+ #make the outfile depend on the python script
+ ADD_CUSTOM_COMMAND(
+ OUTPUT ${outfile} DEPENDS ${pyfile} ${LIBUHD_PYTHON_GEN_SOURCE_DEPS}
+ COMMAND ${PYTHON_EXECUTABLE} -B ${pyfile} ${outfile}
+ COMMENT "Generating ${outfile}"
+ )
+
+ #make libuhd depend on the outfile
+ LIBUHD_APPEND_SOURCES(${outfile})
+ENDMACRO(LIBUHD_PYTHON_GEN_SOURCE)
+
+MACRO(INCLUDE_SUBDIRECTORY subdir)
+ #insert the current directories on the front of the list
+ LIST(INSERT _cmake_source_dirs 0 ${CMAKE_CURRENT_SOURCE_DIR})
+ LIST(INSERT _cmake_binary_dirs 0 ${CMAKE_CURRENT_BINARY_DIR})
+
+ #set the current directories to the names of the subdirs
+ SET(CMAKE_CURRENT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${subdir})
+ SET(CMAKE_CURRENT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${subdir})
+
+ #include the subdirectory CMakeLists to run it
+ FILE(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+ INCLUDE(${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt)
+
+ #reset the value of the current directories
+ LIST(GET _cmake_source_dirs 0 CMAKE_CURRENT_SOURCE_DIR)
+ LIST(GET _cmake_binary_dirs 0 CMAKE_CURRENT_BINARY_DIR)
+
+ #pop the subdir names of the front of the list
+ LIST(REMOVE_AT _cmake_source_dirs 0)
+ LIST(REMOVE_AT _cmake_binary_dirs 0)
+ENDMACRO(INCLUDE_SUBDIRECTORY)
+
+########################################################################
+# Include subdirectories (different than add)
+########################################################################
+INCLUDE_SUBDIRECTORY(ic_reg_maps)
+INCLUDE_SUBDIRECTORY(types)
+INCLUDE_SUBDIRECTORY(convert)
+INCLUDE_SUBDIRECTORY(transport)
+INCLUDE_SUBDIRECTORY(usrp)
+INCLUDE_SUBDIRECTORY(utils)
+
+########################################################################
+# Setup UHD_VERSION_STRING for version.cpp
+########################################################################
+CONFIGURE_FILE(
+ ${CMAKE_CURRENT_SOURCE_DIR}/version.cpp
+ ${CMAKE_CURRENT_BINARY_DIR}/version.cpp
+@ONLY)
+
+########################################################################
+# Append to the list of sources for lib uhd
+########################################################################
+LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/deprecated.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/device.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/exception.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/property_tree.cpp
+ ${CMAKE_CURRENT_BINARY_DIR}/version.cpp
+)
+
+########################################################################
+# Setup libuhd library
+########################################################################
+ADD_LIBRARY(uhd SHARED ${libuhd_sources})
+TARGET_LINK_LIBRARIES(uhd ${Boost_LIBRARIES} ${libuhd_libs})
+SET_TARGET_PROPERTIES(uhd PROPERTIES DEFINE_SYMBOL "UHD_DLL_EXPORTS")
+SET_TARGET_PROPERTIES(uhd PROPERTIES SOVERSION "${UHD_VERSION_MAJOR}")
+SET_TARGET_PROPERTIES(uhd PROPERTIES VERSION "${UHD_VERSION_MAJOR}.${UHD_VERSION_MINOR}")
+IF(DEFINED LIBUHD_OUTPUT_NAME)
+ SET_TARGET_PROPERTIES(uhd PROPERTIES OUTPUT_NAME ${LIBUHD_OUTPUT_NAME})
+ENDIF(DEFINED LIBUHD_OUTPUT_NAME)
+
+INSTALL(TARGETS uhd
+ LIBRARY DESTINATION ${LIBRARY_DIR} COMPONENT libraries # .so file
+ ARCHIVE DESTINATION ${LIBRARY_DIR} COMPONENT libraries # .lib file
+ RUNTIME DESTINATION ${RUNTIME_DIR} COMPONENT libraries # .dll file
+)
diff --git a/host/lib/convert/CMakeLists.txt b/host/lib/convert/CMakeLists.txt
new file mode 100644
index 000000000..c42a0a434
--- /dev/null
+++ b/host/lib/convert/CMakeLists.txt
@@ -0,0 +1,120 @@
+#
+# Copyright 2011-2012 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# This file included, use CMake directory variables
+########################################################################
+INCLUDE(CheckIncludeFileCXX)
+MESSAGE(STATUS "")
+
+########################################################################
+# Look for Orc support
+########################################################################
+FIND_PACKAGE(PkgConfig)
+IF(PKG_CONFIG_FOUND)
+PKG_CHECK_MODULES(ORC "orc-0.4 > 0.4.11")
+ENDIF(PKG_CONFIG_FOUND)
+
+FIND_PROGRAM(ORCC_EXECUTABLE orcc)
+
+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)
+ SET(EMMINTRIN_FLAGS -msse2)
+ELSEIF(MSVC)
+ SET(EMMINTRIN_FLAGS /arch:SSE2)
+ENDIF()
+
+SET(CMAKE_REQUIRED_FLAGS ${EMMINTRIN_FLAGS})
+CHECK_INCLUDE_FILE_CXX(emmintrin.h HAVE_EMMINTRIN_H)
+UNSET(CMAKE_REQUIRED_FLAGS)
+
+IF(HAVE_EMMINTRIN_H)
+ SET(convert_with_sse2_sources
+ ${CMAKE_CURRENT_SOURCE_DIR}/convert_fc32_with_sse2.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/convert_fc64_with_sse2.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/convert_fc32_to_sc8_with_sse2.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/convert_fc64_to_sc8_with_sse2.cpp
+ )
+ SET_SOURCE_FILES_PROPERTIES(
+ ${convert_with_sse2_sources}
+ PROPERTIES COMPILE_FLAGS "${EMMINTRIN_FLAGS}"
+ )
+ LIBUHD_APPEND_SOURCES(${convert_with_sse2_sources})
+ENDIF(HAVE_EMMINTRIN_H)
+
+########################################################################
+# Check for NEON SIMD headers
+########################################################################
+IF(CMAKE_COMPILER_IS_GNUCXX)
+ SET(NEON_FLAGS "-mfloat-abi=softfp -mfpu=neon")
+ SET(CMAKE_REQUIRED_FLAGS ${NEON_FLAGS})
+ CHECK_INCLUDE_FILE_CXX(arm_neon.h HAVE_ARM_NEON_H)
+ UNSET(CMAKE_REQUIRED_FLAGS)
+ENDIF(CMAKE_COMPILER_IS_GNUCXX)
+
+IF(HAVE_ARM_NEON_H)
+ SET_SOURCE_FILES_PROPERTIES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/convert_with_neon.cpp
+ PROPERTIES COMPILE_FLAGS "${NEON_FLAGS}"
+ )
+ LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/convert_with_neon.cpp
+ )
+ENDIF()
+
+########################################################################
+# Convert types generation
+########################################################################
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
+
+LIBUHD_PYTHON_GEN_SOURCE(
+ ${CMAKE_CURRENT_SOURCE_DIR}/gen_convert_general.py
+ ${CMAKE_CURRENT_BINARY_DIR}/convert_general.cpp
+)
+
+LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/convert_with_tables.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/convert_impl.cpp
+)
diff --git a/host/lib/convert/convert_common.hpp b/host/lib/convert/convert_common.hpp
new file mode 100644
index 000000000..55bc2e99d
--- /dev/null
+++ b/host/lib/convert/convert_common.hpp
@@ -0,0 +1,241 @@
+//
+// Copyright 2011-2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_CONVERT_COMMON_HPP
+#define INCLUDED_LIBUHD_CONVERT_COMMON_HPP
+
+#include <uhd/convert.hpp>
+#include <uhd/utils/static.hpp>
+#include <boost/cstdint.hpp>
+#include <complex>
+
+#define _DECLARE_CONVERTER(name, in_form, num_in, out_form, num_out, prio) \
+ struct name : public uhd::convert::converter{ \
+ static sptr make(void){return sptr(new name());} \
+ double scale_factor; \
+ void set_scalar(const double s){scale_factor = s;} \
+ void operator()(const input_type&, const output_type&, const size_t); \
+ }; \
+ UHD_STATIC_BLOCK(__register_##name##_##prio){ \
+ uhd::convert::id_type id; \
+ id.input_format = #in_form; \
+ id.num_inputs = num_in; \
+ id.output_format = #out_form; \
+ id.num_outputs = num_out; \
+ uhd::convert::register_converter(id, &name::make, prio); \
+ } \
+ void name::operator()( \
+ const input_type &inputs, const output_type &outputs, const size_t nsamps \
+ )
+
+#define DECLARE_CONVERTER(in_form, num_in, out_form, num_out, prio) \
+ _DECLARE_CONVERTER(__convert_##in_form##_##num_in##_##out_form##_##num_out##_##prio, in_form, num_in, out_form, num_out, prio)
+
+/***********************************************************************
+ * Setup priorities
+ **********************************************************************/
+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 = 1; //neon conversions could be implemented better, orc wins
+static const int PRIORITY_TABLE = 2; //tables require large cache, so they are slower on arm
+#else
+static const int PRIORITY_LIBORC = 1;
+static const int PRIORITY_SIMD = 2;
+static const int PRIORITY_TABLE = 3;
+#endif
+
+/***********************************************************************
+ * Typedefs
+ **********************************************************************/
+typedef std::complex<double> fc64_t;
+typedef std::complex<float> fc32_t;
+typedef std::complex<boost::int32_t> sc32_t;
+typedef std::complex<boost::int16_t> sc16_t;
+typedef std::complex<boost::int8_t> sc8_t;
+typedef double f64_t;
+typedef float f32_t;
+typedef boost::int32_t s32_t;
+typedef boost::int16_t s16_t;
+typedef boost::int8_t s8_t;
+
+typedef boost::uint32_t item32_t;
+
+/***********************************************************************
+ * Convert complex short buffer to items32 sc16
+ **********************************************************************/
+static UHD_INLINE item32_t sc16_to_item32_sc16(sc16_t num, double){
+ boost::uint16_t real = num.real();
+ boost::uint16_t imag = num.imag();
+ return (item32_t(real) << 16) | (item32_t(imag) << 0);
+}
+
+/***********************************************************************
+ * Convert items32 sc16 buffer to complex short
+ **********************************************************************/
+static UHD_INLINE sc16_t item32_sc16_to_sc16(item32_t item, double){
+ return sc16_t(
+ boost::int16_t(item >> 16),
+ boost::int16_t(item >> 0)
+ );
+}
+
+/***********************************************************************
+ * Convert complex float buffer to items32 sc16
+ **********************************************************************/
+static UHD_INLINE item32_t fc32_to_item32_sc16(fc32_t num, double scale_factor){
+ boost::uint16_t real = boost::int16_t(num.real()*float(scale_factor));
+ boost::uint16_t imag = boost::int16_t(num.imag()*float(scale_factor));
+ return (item32_t(real) << 16) | (item32_t(imag) << 0);
+}
+
+/***********************************************************************
+ * Convert items32 sc16 buffer to complex float
+ **********************************************************************/
+static UHD_INLINE fc32_t item32_sc16_to_fc32(item32_t item, double scale_factor){
+ return fc32_t(
+ float(boost::int16_t(item >> 16)*float(scale_factor)),
+ float(boost::int16_t(item >> 0)*float(scale_factor))
+ );
+}
+
+/***********************************************************************
+ * Convert complex double buffer to items32 sc16
+ **********************************************************************/
+static UHD_INLINE item32_t fc64_to_item32_sc16(fc64_t num, double scale_factor){
+ boost::uint16_t real = boost::int16_t(num.real()*scale_factor);
+ boost::uint16_t imag = boost::int16_t(num.imag()*scale_factor);
+ return (item32_t(real) << 16) | (item32_t(imag) << 0);
+}
+
+/***********************************************************************
+ * Convert items32 sc16 buffer to complex double
+ **********************************************************************/
+static UHD_INLINE fc64_t item32_sc16_to_fc64(item32_t item, double scale_factor){
+ return fc64_t(
+ float(boost::int16_t(item >> 16)*scale_factor),
+ float(boost::int16_t(item >> 0)*scale_factor)
+ );
+}
+
+/***********************************************************************
+ * Convert items32 sc8 buffer to complex char
+ **********************************************************************/
+static UHD_INLINE void item32_sc8_to_sc8(item32_t item, sc8_t &out0, sc8_t &out1, double){
+ out0 = sc8_t(
+ boost::int8_t(item >> 8),
+ boost::int8_t(item >> 0)
+ );
+ out1 = sc8_t(
+ boost::int8_t(item >> 24),
+ boost::int8_t(item >> 16)
+ );
+}
+
+/***********************************************************************
+ * Convert items32 sc8 buffer to complex short
+ **********************************************************************/
+static UHD_INLINE void item32_sc8_to_sc16(item32_t item, sc16_t &out0, sc16_t &out1, double){
+ out0 = sc16_t(
+ boost::int8_t(item >> 8),
+ boost::int8_t(item >> 0)
+ );
+ out1 = sc16_t(
+ boost::int8_t(item >> 24),
+ boost::int8_t(item >> 16)
+ );
+}
+
+/***********************************************************************
+ * Convert items32 sc8 buffer to complex float
+ **********************************************************************/
+static UHD_INLINE void item32_sc8_to_fc32(item32_t item, fc32_t &out0, fc32_t &out1, double scale_factor){
+ out0 = fc32_t(
+ float(boost::int8_t(item >> 8)*float(scale_factor)),
+ float(boost::int8_t(item >> 0)*float(scale_factor))
+ );
+ out1 = fc32_t(
+ float(boost::int8_t(item >> 24)*float(scale_factor)),
+ float(boost::int8_t(item >> 16)*float(scale_factor))
+ );
+}
+
+/***********************************************************************
+ * Convert items32 sc8 buffer to complex double
+ **********************************************************************/
+static UHD_INLINE void item32_sc8_to_fc64(item32_t item, fc64_t &out0, fc64_t &out1, double scale_factor){
+ out0 = fc64_t(
+ float(boost::int8_t(item >> 8)*scale_factor),
+ float(boost::int8_t(item >> 0)*scale_factor)
+ );
+ out1 = fc64_t(
+ float(boost::int8_t(item >> 24)*scale_factor),
+ float(boost::int8_t(item >> 16)*scale_factor)
+ );
+}
+
+/***********************************************************************
+ * Convert complex char to items32 sc8 buffer
+ **********************************************************************/
+static UHD_INLINE item32_t sc8_to_item32_sc8(sc8_t in0, sc8_t in1, double){
+ return
+ (item32_t(boost::uint8_t(in0.real())) << 8) |
+ (item32_t(boost::uint8_t(in0.imag())) << 0) |
+ (item32_t(boost::uint8_t(in1.real())) << 24) |
+ (item32_t(boost::uint8_t(in1.imag())) << 16)
+ ;
+}
+
+/***********************************************************************
+ * Convert complex short to items32 sc8 buffer
+ **********************************************************************/
+static UHD_INLINE item32_t sc16_to_item32_sc8(sc16_t in0, sc16_t in1, double){
+ return
+ (item32_t(boost::uint8_t(in0.real())) << 8) |
+ (item32_t(boost::uint8_t(in0.imag())) << 0) |
+ (item32_t(boost::uint8_t(in1.real())) << 24) |
+ (item32_t(boost::uint8_t(in1.imag())) << 16)
+ ;
+}
+
+/***********************************************************************
+ * Convert complex float to items32 sc8 buffer
+ **********************************************************************/
+static UHD_INLINE item32_t fc32_to_item32_sc8(fc32_t in0, fc32_t in1, double scale_factor){
+ return
+ (item32_t(boost::uint8_t(in0.real()*float(scale_factor))) << 8) |
+ (item32_t(boost::uint8_t(in0.imag()*float(scale_factor))) << 0) |
+ (item32_t(boost::uint8_t(in1.real()*float(scale_factor))) << 24) |
+ (item32_t(boost::uint8_t(in1.imag()*float(scale_factor))) << 16)
+ ;
+}
+
+/***********************************************************************
+ * Convert complex double to items32 sc8 buffer
+ **********************************************************************/
+static UHD_INLINE item32_t fc64_to_item32_sc8(fc64_t in0, fc64_t in1, double scale_factor){
+ return
+ (item32_t(boost::uint8_t(in0.real()*(scale_factor))) << 8) |
+ (item32_t(boost::uint8_t(in0.imag()*(scale_factor))) << 0) |
+ (item32_t(boost::uint8_t(in1.real()*(scale_factor))) << 24) |
+ (item32_t(boost::uint8_t(in1.imag()*(scale_factor))) << 16)
+ ;
+}
+
+#endif /* INCLUDED_LIBUHD_CONVERT_COMMON_HPP */
diff --git a/host/lib/convert/convert_fc32_to_sc8_with_sse2.cpp b/host/lib/convert/convert_fc32_to_sc8_with_sse2.cpp
new file mode 100644
index 000000000..b633f487c
--- /dev/null
+++ b/host/lib/convert/convert_fc32_to_sc8_with_sse2.cpp
@@ -0,0 +1,150 @@
+//
+// Copyright 2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "convert_common.hpp"
+#include <uhd/utils/byteswap.hpp>
+#include <emmintrin.h>
+
+using namespace uhd::convert;
+
+UHD_INLINE __m128i pack_sc32_4x_be(
+ const __m128 &in0, const __m128 &in1,
+ const __m128 &in2, const __m128 &in3,
+ const __m128 &scalar
+){
+ __m128i tmpi0 = _mm_cvtps_epi32(_mm_mul_ps(in0, scalar));
+ tmpi0 = _mm_shuffle_epi32(tmpi0, _MM_SHUFFLE(1, 0, 3, 2));
+ __m128i tmpi1 = _mm_cvtps_epi32(_mm_mul_ps(in1, scalar));
+ tmpi1 = _mm_shuffle_epi32(tmpi1, _MM_SHUFFLE(1, 0, 3, 2));
+ const __m128i lo = _mm_packs_epi32(tmpi0, tmpi1);
+
+ __m128i tmpi2 = _mm_cvtps_epi32(_mm_mul_ps(in2, scalar));
+ tmpi2 = _mm_shuffle_epi32(tmpi2, _MM_SHUFFLE(1, 0, 3, 2));
+ __m128i tmpi3 = _mm_cvtps_epi32(_mm_mul_ps(in3, scalar));
+ tmpi3 = _mm_shuffle_epi32(tmpi3, _MM_SHUFFLE(1, 0, 3, 2));
+ const __m128i hi = _mm_packs_epi32(tmpi2, tmpi3);
+
+ return _mm_packs_epi16(lo, hi);
+}
+
+UHD_INLINE __m128i pack_sc32_4x_le(
+ const __m128 &in0, const __m128 &in1,
+ const __m128 &in2, const __m128 &in3,
+ const __m128 &scalar
+){
+ __m128i tmpi0 = _mm_cvtps_epi32(_mm_mul_ps(in0, scalar));
+ tmpi0 = _mm_shuffle_epi32(tmpi0, _MM_SHUFFLE(2, 3, 0, 1));
+ __m128i tmpi1 = _mm_cvtps_epi32(_mm_mul_ps(in1, scalar));
+ tmpi1 = _mm_shuffle_epi32(tmpi1, _MM_SHUFFLE(2, 3, 0, 1));
+ const __m128i lo = _mm_packs_epi32(tmpi0, tmpi1);
+
+ __m128i tmpi2 = _mm_cvtps_epi32(_mm_mul_ps(in2, scalar));
+ tmpi2 = _mm_shuffle_epi32(tmpi2, _MM_SHUFFLE(2, 3, 0, 1));
+ __m128i tmpi3 = _mm_cvtps_epi32(_mm_mul_ps(in3, scalar));
+ tmpi3 = _mm_shuffle_epi32(tmpi3, _MM_SHUFFLE(2, 3, 0, 1));
+ const __m128i hi = _mm_packs_epi32(tmpi2, tmpi3);
+
+ return _mm_packs_epi16(lo, hi);
+}
+
+DECLARE_CONVERTER(fc32, 1, sc8_item32_be, 1, PRIORITY_SIMD){
+ const fc32_t *input = reinterpret_cast<const fc32_t *>(inputs[0]);
+ item32_t *output = reinterpret_cast<item32_t *>(outputs[0]);
+
+ const __m128 scalar = _mm_set_ps1(float(scale_factor));
+
+ #define convert_fc32_1_to_sc8_item32_1_bswap_guts(_al_) \
+ for (size_t j = 0; i+7 < nsamps; i+=8, j+=4){ \
+ /* load from input */ \
+ __m128 tmp0 = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+0)); \
+ __m128 tmp1 = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+2)); \
+ __m128 tmp2 = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+4)); \
+ __m128 tmp3 = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+6)); \
+ \
+ /* convert */ \
+ const __m128i tmpi = pack_sc32_4x_be(tmp0, tmp1, tmp2, tmp3, scalar); \
+ \
+ /* store to output */ \
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(output+j), tmpi); \
+ } \
+
+ size_t i = 0;
+
+ //dispatch according to alignment
+ if ((size_t(input) & 0xf) == 0){
+ convert_fc32_1_to_sc8_item32_1_bswap_guts(_)
+ }
+ else{
+ convert_fc32_1_to_sc8_item32_1_bswap_guts(u_)
+ }
+
+ //convert remainder
+ const size_t num_pairs = nsamps/2;
+ for (size_t j = i/2; j < num_pairs; j++, i+=2){
+ const item32_t item = fc32_to_item32_sc8(input[i], input[i+1], scale_factor);
+ output[j] = uhd::byteswap(item);
+ }
+
+ if (nsamps != num_pairs*2){
+ const item32_t item = fc32_to_item32_sc8(input[nsamps-1], 0, scale_factor);
+ output[num_pairs] = uhd::byteswap(item);
+ }
+}
+
+DECLARE_CONVERTER(fc32, 1, sc8_item32_le, 1, PRIORITY_SIMD){
+ const fc32_t *input = reinterpret_cast<const fc32_t *>(inputs[0]);
+ item32_t *output = reinterpret_cast<item32_t *>(outputs[0]);
+
+ const __m128 scalar = _mm_set_ps1(float(scale_factor));
+
+ #define convert_fc32_1_to_sc8_item32_1_nswap_guts(_al_) \
+ for (size_t j = 0; i+7 < nsamps; i+=8, j+=4){ \
+ /* load from input */ \
+ __m128 tmp0 = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+0)); \
+ __m128 tmp1 = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+2)); \
+ __m128 tmp2 = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+4)); \
+ __m128 tmp3 = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+6)); \
+ \
+ /* convert */ \
+ const __m128i tmpi = pack_sc32_4x_le(tmp0, tmp1, tmp2, tmp3, scalar); \
+ \
+ /* store to output */ \
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(output+j), tmpi); \
+ } \
+
+ size_t i = 0;
+
+ //dispatch according to alignment
+ if ((size_t(input) & 0xf) == 0){
+ convert_fc32_1_to_sc8_item32_1_nswap_guts(_)
+ }
+ else{
+ convert_fc32_1_to_sc8_item32_1_nswap_guts(u_)
+ }
+
+ //convert remainder
+ const size_t num_pairs = nsamps/2;
+ for (size_t j = i/2; j < num_pairs; j++, i+=2){
+ const item32_t item = fc32_to_item32_sc8(input[i], input[i+1], scale_factor);
+ output[j] = (item);
+ }
+
+ if (nsamps != num_pairs*2){
+ const item32_t item = fc32_to_item32_sc8(input[nsamps-1], 0, scale_factor);
+ output[num_pairs] = (item);
+ }
+}
diff --git a/host/lib/convert/convert_fc32_with_sse2.cpp b/host/lib/convert/convert_fc32_with_sse2.cpp
new file mode 100644
index 000000000..97a3e8cdc
--- /dev/null
+++ b/host/lib/convert/convert_fc32_with_sse2.cpp
@@ -0,0 +1,196 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "convert_common.hpp"
+#include <uhd/utils/byteswap.hpp>
+#include <emmintrin.h>
+
+using namespace uhd::convert;
+
+DECLARE_CONVERTER(fc32, 1, sc16_item32_le, 1, PRIORITY_SIMD){
+ const fc32_t *input = reinterpret_cast<const fc32_t *>(inputs[0]);
+ item32_t *output = reinterpret_cast<item32_t *>(outputs[0]);
+
+ const __m128 scalar = _mm_set_ps1(float(scale_factor));
+
+ #define convert_fc32_1_to_item32_1_nswap_guts(_al_) \
+ for (; i+3 < nsamps; i+=4){ \
+ /* load from input */ \
+ __m128 tmplo = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+0)); \
+ __m128 tmphi = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+2)); \
+ \
+ /* convert and scale */ \
+ __m128i tmpilo = _mm_cvtps_epi32(_mm_mul_ps(tmplo, scalar)); \
+ __m128i tmpihi = _mm_cvtps_epi32(_mm_mul_ps(tmphi, scalar)); \
+ \
+ /* pack + swap 16-bit pairs */ \
+ __m128i tmpi = _mm_packs_epi32(tmpilo, tmpihi); \
+ tmpi = _mm_shufflelo_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); \
+ tmpi = _mm_shufflehi_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); \
+ \
+ /* store to output */ \
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(output+i), tmpi); \
+ } \
+
+ size_t i = 0;
+
+ //dispatch according to alignment
+ switch (size_t(input) & 0xf){
+ case 0x8:
+ output[i] = fc32_to_item32_sc16(input[i], float(scale_factor)); i++;
+ case 0x0:
+ convert_fc32_1_to_item32_1_nswap_guts(_)
+ break;
+ default: convert_fc32_1_to_item32_1_nswap_guts(u_)
+ }
+
+ //convert remainder
+ for (; i < nsamps; i++){
+ output[i] = fc32_to_item32_sc16(input[i], float(scale_factor));
+ }
+}
+
+DECLARE_CONVERTER(fc32, 1, sc16_item32_be, 1, PRIORITY_SIMD){
+ const fc32_t *input = reinterpret_cast<const fc32_t *>(inputs[0]);
+ item32_t *output = reinterpret_cast<item32_t *>(outputs[0]);
+
+ const __m128 scalar = _mm_set_ps1(float(scale_factor));
+
+ #define convert_fc32_1_to_item32_1_bswap_guts(_al_) \
+ for (; i+3 < nsamps; i+=4){ \
+ /* load from input */ \
+ __m128 tmplo = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+0)); \
+ __m128 tmphi = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+2)); \
+ \
+ /* convert and scale */ \
+ __m128i tmpilo = _mm_cvtps_epi32(_mm_mul_ps(tmplo, scalar)); \
+ __m128i tmpihi = _mm_cvtps_epi32(_mm_mul_ps(tmphi, scalar)); \
+ \
+ /* pack + byteswap -> byteswap 16 bit words */ \
+ __m128i tmpi = _mm_packs_epi32(tmpilo, tmpihi); \
+ tmpi = _mm_or_si128(_mm_srli_epi16(tmpi, 8), _mm_slli_epi16(tmpi, 8)); \
+ \
+ /* store to output */ \
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(output+i), tmpi); \
+ } \
+
+ size_t i = 0;
+
+ //dispatch according to alignment
+ switch (size_t(input) & 0xf){
+ case 0x8:
+ output[i] = uhd::byteswap(fc32_to_item32_sc16(input[i], float(scale_factor))); i++;
+ case 0x0:
+ convert_fc32_1_to_item32_1_bswap_guts(_)
+ break;
+ default: convert_fc32_1_to_item32_1_bswap_guts(u_)
+ }
+
+ //convert remainder
+ for (; i < nsamps; i++){
+ output[i] = uhd::byteswap(fc32_to_item32_sc16(input[i], float(scale_factor)));
+ }
+}
+
+DECLARE_CONVERTER(sc16_item32_le, 1, fc32, 1, PRIORITY_SIMD){
+ const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]);
+ fc32_t *output = reinterpret_cast<fc32_t *>(outputs[0]);
+
+ const __m128 scalar = _mm_set_ps1(float(scale_factor)/(1 << 16));
+ const __m128i zeroi = _mm_setzero_si128();
+
+ #define convert_item32_1_to_fc32_1_nswap_guts(_al_) \
+ for (; i+3 < nsamps; i+=4){ \
+ /* load from input */ \
+ __m128i tmpi = _mm_loadu_si128(reinterpret_cast<const __m128i *>(input+i)); \
+ \
+ /* unpack + swap 16-bit pairs */ \
+ tmpi = _mm_shufflelo_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); \
+ tmpi = _mm_shufflehi_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); \
+ __m128i tmpilo = _mm_unpacklo_epi16(zeroi, tmpi); /* value in upper 16 bits */ \
+ __m128i tmpihi = _mm_unpackhi_epi16(zeroi, tmpi); \
+ \
+ /* convert and scale */ \
+ __m128 tmplo = _mm_mul_ps(_mm_cvtepi32_ps(tmpilo), scalar); \
+ __m128 tmphi = _mm_mul_ps(_mm_cvtepi32_ps(tmpihi), scalar); \
+ \
+ /* store to output */ \
+ _mm_store ## _al_ ## ps(reinterpret_cast<float *>(output+i+0), tmplo); \
+ _mm_store ## _al_ ## ps(reinterpret_cast<float *>(output+i+2), tmphi); \
+ } \
+
+ size_t i = 0;
+
+ //dispatch according to alignment
+ switch (size_t(output) & 0xf){
+ case 0x8:
+ output[i] = item32_sc16_to_fc32(input[i], float(scale_factor)); i++;
+ case 0x0:
+ convert_item32_1_to_fc32_1_nswap_guts(_)
+ break;
+ default: convert_item32_1_to_fc32_1_nswap_guts(u_)
+ }
+
+ //convert remainder
+ for (; i < nsamps; i++){
+ output[i] = item32_sc16_to_fc32(input[i], float(scale_factor));
+ }
+}
+
+DECLARE_CONVERTER(sc16_item32_be, 1, fc32, 1, PRIORITY_SIMD){
+ const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]);
+ fc32_t *output = reinterpret_cast<fc32_t *>(outputs[0]);
+
+ const __m128 scalar = _mm_set_ps1(float(scale_factor)/(1 << 16));
+ const __m128i zeroi = _mm_setzero_si128();
+
+ #define convert_item32_1_to_fc32_1_bswap_guts(_al_) \
+ for (; i+3 < nsamps; i+=4){ \
+ /* load from input */ \
+ __m128i tmpi = _mm_loadu_si128(reinterpret_cast<const __m128i *>(input+i)); \
+ \
+ /* byteswap + unpack -> byteswap 16 bit words */ \
+ tmpi = _mm_or_si128(_mm_srli_epi16(tmpi, 8), _mm_slli_epi16(tmpi, 8)); \
+ __m128i tmpilo = _mm_unpacklo_epi16(zeroi, tmpi); /* value in upper 16 bits */ \
+ __m128i tmpihi = _mm_unpackhi_epi16(zeroi, tmpi); \
+ \
+ /* convert and scale */ \
+ __m128 tmplo = _mm_mul_ps(_mm_cvtepi32_ps(tmpilo), scalar); \
+ __m128 tmphi = _mm_mul_ps(_mm_cvtepi32_ps(tmpihi), scalar); \
+ \
+ /* store to output */ \
+ _mm_store ## _al_ ## ps(reinterpret_cast<float *>(output+i+0), tmplo); \
+ _mm_store ## _al_ ## ps(reinterpret_cast<float *>(output+i+2), tmphi); \
+ } \
+
+ size_t i = 0;
+
+ //dispatch according to alignment
+ switch (size_t(output) & 0xf){
+ case 0x8:
+ output[i] = item32_sc16_to_fc32(uhd::byteswap(input[i]), float(scale_factor)); i++;
+ case 0x0:
+ convert_item32_1_to_fc32_1_bswap_guts(_)
+ break;
+ default: convert_item32_1_to_fc32_1_bswap_guts(u_)
+ }
+
+ //convert remainder
+ for (; i < nsamps; i++){
+ output[i] = item32_sc16_to_fc32(uhd::byteswap(input[i]), float(scale_factor));
+ }
+}
diff --git a/host/lib/convert/convert_fc64_to_sc8_with_sse2.cpp b/host/lib/convert/convert_fc64_to_sc8_with_sse2.cpp
new file mode 100644
index 000000000..405850601
--- /dev/null
+++ b/host/lib/convert/convert_fc64_to_sc8_with_sse2.cpp
@@ -0,0 +1,156 @@
+//
+// Copyright 2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "convert_common.hpp"
+#include <uhd/utils/byteswap.hpp>
+#include <emmintrin.h>
+
+using namespace uhd::convert;
+
+UHD_INLINE __m128i pack_sc8_item32_4x(
+ const __m128i &in0, const __m128i &in1,
+ const __m128i &in2, const __m128i &in3
+){
+ const __m128i lo = _mm_packs_epi32(in0, in1);
+ const __m128i hi = _mm_packs_epi32(in2, in3);
+ return _mm_packs_epi16(lo, hi);
+}
+
+UHD_INLINE __m128i pack_sc32_4x_be(
+ const __m128d &lo, const __m128d &hi,
+ const __m128d &scalar
+){
+ const __m128i tmpi_lo = _mm_cvttpd_epi32(_mm_mul_pd(hi, scalar));
+ const __m128i tmpi_hi = _mm_cvttpd_epi32(_mm_mul_pd(lo, scalar));
+ return _mm_unpacklo_epi64(tmpi_lo, tmpi_hi);
+}
+
+UHD_INLINE __m128i pack_sc32_4x_le(
+ const __m128d &lo, const __m128d &hi,
+ const __m128d &scalar
+){
+ const __m128i tmpi_lo = _mm_cvttpd_epi32(_mm_mul_pd(lo, scalar));
+ const __m128i tmpi_hi = _mm_cvttpd_epi32(_mm_mul_pd(hi, scalar));
+ const __m128i tmpi = _mm_unpacklo_epi64(tmpi_lo, tmpi_hi);
+ return _mm_shuffle_epi32(tmpi, _MM_SHUFFLE(2, 3, 0, 1));
+}
+
+DECLARE_CONVERTER(fc64, 1, sc8_item32_be, 1, PRIORITY_SIMD){
+ const fc64_t *input = reinterpret_cast<const fc64_t *>(inputs[0]);
+ item32_t *output = reinterpret_cast<item32_t *>(outputs[0]);
+
+ const __m128d scalar = _mm_set1_pd(scale_factor);
+
+ #define convert_fc64_1_to_sc8_item32_1_bswap_guts(_al_) \
+ for (size_t j = 0; i+7 < nsamps; i+=8, j+=4){ \
+ /* load from input */ \
+ __m128d tmp0 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+0)); \
+ __m128d tmp1 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+1)); \
+ __m128d tmp2 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+2)); \
+ __m128d tmp3 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+3)); \
+ __m128d tmp4 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+4)); \
+ __m128d tmp5 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+5)); \
+ __m128d tmp6 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+6)); \
+ __m128d tmp7 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+7)); \
+ \
+ /* interleave */ \
+ const __m128i tmpi = pack_sc8_item32_4x( \
+ pack_sc32_4x_be(tmp0, tmp1, scalar), \
+ pack_sc32_4x_be(tmp2, tmp3, scalar), \
+ pack_sc32_4x_be(tmp4, tmp5, scalar), \
+ pack_sc32_4x_be(tmp6, tmp7, scalar) \
+ ); \
+ \
+ /* store to output */ \
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(output+j), tmpi); \
+ } \
+
+ size_t i = 0;
+
+ //dispatch according to alignment
+ if ((size_t(input) & 0xf) == 0){
+ convert_fc64_1_to_sc8_item32_1_bswap_guts(_)
+ }
+ else{
+ convert_fc64_1_to_sc8_item32_1_bswap_guts(u_)
+ }
+
+ //convert remainder
+ const size_t num_pairs = nsamps/2;
+ for (size_t j = i/2; j < num_pairs; j++, i+=2){
+ const item32_t item = fc64_to_item32_sc8(input[i], input[i+1], scale_factor);
+ output[j] = uhd::byteswap(item);
+ }
+
+ if (nsamps != num_pairs*2){
+ const item32_t item = fc64_to_item32_sc8(input[nsamps-1], 0, scale_factor);
+ output[num_pairs] = uhd::byteswap(item);
+ }
+}
+
+DECLARE_CONVERTER(fc64, 1, sc8_item32_le, 1, PRIORITY_SIMD){
+ const fc64_t *input = reinterpret_cast<const fc64_t *>(inputs[0]);
+ item32_t *output = reinterpret_cast<item32_t *>(outputs[0]);
+
+ const __m128d scalar = _mm_set1_pd(scale_factor);
+
+ #define convert_fc64_1_to_sc8_item32_1_nswap_guts(_al_) \
+ for (size_t j = 0; i+7 < nsamps; i+=8, j+=4){ \
+ /* load from input */ \
+ __m128d tmp0 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+0)); \
+ __m128d tmp1 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+1)); \
+ __m128d tmp2 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+2)); \
+ __m128d tmp3 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+3)); \
+ __m128d tmp4 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+4)); \
+ __m128d tmp5 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+5)); \
+ __m128d tmp6 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+6)); \
+ __m128d tmp7 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+7)); \
+ \
+ /* interleave */ \
+ const __m128i tmpi = pack_sc8_item32_4x( \
+ pack_sc32_4x_le(tmp0, tmp1, scalar), \
+ pack_sc32_4x_le(tmp2, tmp3, scalar), \
+ pack_sc32_4x_le(tmp4, tmp5, scalar), \
+ pack_sc32_4x_le(tmp6, tmp7, scalar) \
+ ); \
+ \
+ /* store to output */ \
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(output+j), tmpi); \
+ } \
+
+ size_t i = 0;
+
+ //dispatch according to alignment
+ if ((size_t(input) & 0xf) == 0){
+ convert_fc64_1_to_sc8_item32_1_nswap_guts(_)
+ }
+ else{
+ convert_fc64_1_to_sc8_item32_1_nswap_guts(u_)
+ }
+
+ //convert remainder
+ const size_t num_pairs = nsamps/2;
+ for (size_t j = i/2; j < num_pairs; j++, i+=2){
+ const item32_t item = fc64_to_item32_sc8(input[i], input[i+1], scale_factor);
+ output[j] = (item);
+ }
+
+ if (nsamps != num_pairs*2){
+ const item32_t item = fc64_to_item32_sc8(input[nsamps-1], 0, scale_factor);
+ output[num_pairs] = (item);
+ }
+}
diff --git a/host/lib/convert/convert_fc64_with_sse2.cpp b/host/lib/convert/convert_fc64_with_sse2.cpp
new file mode 100644
index 000000000..6e097e380
--- /dev/null
+++ b/host/lib/convert/convert_fc64_with_sse2.cpp
@@ -0,0 +1,212 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "convert_common.hpp"
+#include <uhd/utils/byteswap.hpp>
+#include <emmintrin.h>
+
+using namespace uhd::convert;
+
+DECLARE_CONVERTER(fc64, 1, sc16_item32_le, 1, PRIORITY_SIMD){
+ const fc64_t *input = reinterpret_cast<const fc64_t *>(inputs[0]);
+ item32_t *output = reinterpret_cast<item32_t *>(outputs[0]);
+
+ const __m128d scalar = _mm_set1_pd(scale_factor);
+
+ #define convert_fc64_1_to_item32_1_nswap_guts(_al_) \
+ for (; i+3 < nsamps; i+=4){ \
+ /* load from input */ \
+ __m128d tmp0 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+0)); \
+ __m128d tmp1 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+1)); \
+ __m128d tmp2 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+2)); \
+ __m128d tmp3 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+3)); \
+ \
+ /* convert and scale */ \
+ __m128i tmpi0 = _mm_cvttpd_epi32(_mm_mul_pd(tmp0, scalar)); \
+ __m128i tmpi1 = _mm_cvttpd_epi32(_mm_mul_pd(tmp1, scalar)); \
+ __m128i tmpilo = _mm_unpacklo_epi64(tmpi0, tmpi1); \
+ __m128i tmpi2 = _mm_cvttpd_epi32(_mm_mul_pd(tmp2, scalar)); \
+ __m128i tmpi3 = _mm_cvttpd_epi32(_mm_mul_pd(tmp3, scalar)); \
+ __m128i tmpihi = _mm_unpacklo_epi64(tmpi2, tmpi3); \
+ \
+ /* pack + swap 16-bit pairs */ \
+ __m128i tmpi = _mm_packs_epi32(tmpilo, tmpihi); \
+ tmpi = _mm_shufflelo_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); \
+ tmpi = _mm_shufflehi_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); \
+ \
+ /* store to output */ \
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(output+i), tmpi); \
+ } \
+
+ size_t i = 0;
+
+ //dispatch according to alignment
+ if ((size_t(input) & 0xf) == 0){
+ convert_fc64_1_to_item32_1_nswap_guts(_)
+ }
+ else{
+ convert_fc64_1_to_item32_1_nswap_guts(u_)
+ }
+
+ //convert remainder
+ for (; i < nsamps; i++){
+ output[i] = fc64_to_item32_sc16(input[i], scale_factor);
+ }
+}
+
+DECLARE_CONVERTER(fc64, 1, sc16_item32_be, 1, PRIORITY_SIMD){
+ const fc64_t *input = reinterpret_cast<const fc64_t *>(inputs[0]);
+ item32_t *output = reinterpret_cast<item32_t *>(outputs[0]);
+
+ const __m128d scalar = _mm_set1_pd(scale_factor);
+
+ #define convert_fc64_1_to_item32_1_bswap_guts(_al_) \
+ for (; i+3 < nsamps; i+=4){ \
+ /* load from input */ \
+ __m128d tmp0 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+0)); \
+ __m128d tmp1 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+1)); \
+ __m128d tmp2 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+2)); \
+ __m128d tmp3 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+3)); \
+ \
+ /* convert and scale */ \
+ __m128i tmpi0 = _mm_cvttpd_epi32(_mm_mul_pd(tmp0, scalar)); \
+ __m128i tmpi1 = _mm_cvttpd_epi32(_mm_mul_pd(tmp1, scalar)); \
+ __m128i tmpilo = _mm_unpacklo_epi64(tmpi0, tmpi1); \
+ __m128i tmpi2 = _mm_cvttpd_epi32(_mm_mul_pd(tmp2, scalar)); \
+ __m128i tmpi3 = _mm_cvttpd_epi32(_mm_mul_pd(tmp3, scalar)); \
+ __m128i tmpihi = _mm_unpacklo_epi64(tmpi2, tmpi3); \
+ \
+ /* pack + byteswap -> byteswap 16 bit words */ \
+ __m128i tmpi = _mm_packs_epi32(tmpilo, tmpihi); \
+ tmpi = _mm_or_si128(_mm_srli_epi16(tmpi, 8), _mm_slli_epi16(tmpi, 8)); \
+ \
+ /* store to output */ \
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(output+i), tmpi); \
+ } \
+
+ size_t i = 0;
+
+ //dispatch according to alignment
+ if ((size_t(input) & 0xf) == 0){
+ convert_fc64_1_to_item32_1_bswap_guts(_)
+ }
+ else{
+ convert_fc64_1_to_item32_1_bswap_guts(u_)
+ }
+
+ //convert remainder
+ for (; i < nsamps; i++){
+ output[i] = uhd::byteswap(fc64_to_item32_sc16(input[i], scale_factor));
+ }
+}
+
+DECLARE_CONVERTER(sc16_item32_le, 1, fc64, 1, PRIORITY_SIMD){
+ const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]);
+ fc64_t *output = reinterpret_cast<fc64_t *>(outputs[0]);
+
+ const __m128d scalar = _mm_set1_pd(scale_factor/(1 << 16));
+ const __m128i zeroi = _mm_setzero_si128();
+
+ #define convert_item32_1_to_fc64_1_nswap_guts(_al_) \
+ for (; i+3 < nsamps; i+=4){ \
+ /* load from input */ \
+ __m128i tmpi = _mm_loadu_si128(reinterpret_cast<const __m128i *>(input+i)); \
+ \
+ /* unpack + swap 16-bit pairs */ \
+ tmpi = _mm_shufflelo_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); \
+ tmpi = _mm_shufflehi_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); \
+ __m128i tmpilo = _mm_unpacklo_epi16(zeroi, tmpi); /* value in upper 16 bits */ \
+ __m128i tmpihi = _mm_unpackhi_epi16(zeroi, tmpi); \
+ \
+ /* convert and scale */ \
+ __m128d tmp0 = _mm_mul_pd(_mm_cvtepi32_pd(tmpilo), scalar); \
+ tmpilo = _mm_unpackhi_epi64(tmpilo, zeroi); \
+ __m128d tmp1 = _mm_mul_pd(_mm_cvtepi32_pd(tmpilo), scalar); \
+ __m128d tmp2 = _mm_mul_pd(_mm_cvtepi32_pd(tmpihi), scalar); \
+ tmpihi = _mm_unpackhi_epi64(tmpihi, zeroi); \
+ __m128d tmp3 = _mm_mul_pd(_mm_cvtepi32_pd(tmpihi), scalar); \
+ \
+ /* store to output */ \
+ _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+i+0), tmp0); \
+ _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+i+1), tmp1); \
+ _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+i+2), tmp2); \
+ _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+i+3), tmp3); \
+ } \
+
+ size_t i = 0;
+
+ //dispatch according to alignment
+ if ((size_t(output) & 0xf) == 0){
+ convert_item32_1_to_fc64_1_nswap_guts(_)
+ }
+ else{
+ convert_item32_1_to_fc64_1_nswap_guts(u_)
+ }
+
+ //convert remainder
+ for (; i < nsamps; i++){
+ output[i] = item32_sc16_to_fc64(input[i], scale_factor);
+ }
+}
+
+DECLARE_CONVERTER(sc16_item32_be, 1, fc64, 1, PRIORITY_SIMD){
+ const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]);
+ fc64_t *output = reinterpret_cast<fc64_t *>(outputs[0]);
+
+ const __m128d scalar = _mm_set1_pd(scale_factor/(1 << 16));
+ const __m128i zeroi = _mm_setzero_si128();
+
+ #define convert_item32_1_to_fc64_1_bswap_guts(_al_) \
+ for (; i+3 < nsamps; i+=4){ \
+ /* load from input */ \
+ __m128i tmpi = _mm_loadu_si128(reinterpret_cast<const __m128i *>(input+i)); \
+ \
+ /* byteswap + unpack -> byteswap 16 bit words */ \
+ tmpi = _mm_or_si128(_mm_srli_epi16(tmpi, 8), _mm_slli_epi16(tmpi, 8)); \
+ __m128i tmpilo = _mm_unpacklo_epi16(zeroi, tmpi); /* value in upper 16 bits */ \
+ __m128i tmpihi = _mm_unpackhi_epi16(zeroi, tmpi); \
+ \
+ /* convert and scale */ \
+ __m128d tmp0 = _mm_mul_pd(_mm_cvtepi32_pd(tmpilo), scalar); \
+ tmpilo = _mm_unpackhi_epi64(tmpilo, zeroi); \
+ __m128d tmp1 = _mm_mul_pd(_mm_cvtepi32_pd(tmpilo), scalar); \
+ __m128d tmp2 = _mm_mul_pd(_mm_cvtepi32_pd(tmpihi), scalar); \
+ tmpihi = _mm_unpackhi_epi64(tmpihi, zeroi); \
+ __m128d tmp3 = _mm_mul_pd(_mm_cvtepi32_pd(tmpihi), scalar); \
+ \
+ /* store to output */ \
+ _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+i+0), tmp0); \
+ _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+i+1), tmp1); \
+ _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+i+2), tmp2); \
+ _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+i+3), tmp3); \
+ } \
+
+ size_t i = 0;
+
+ //dispatch according to alignment
+ if ((size_t(output) & 0xf) == 0){
+ convert_item32_1_to_fc64_1_bswap_guts(_)
+ }
+ else{
+ convert_item32_1_to_fc64_1_bswap_guts(u_)
+ }
+
+ //convert remainder
+ for (; i < nsamps; i++){
+ output[i] = item32_sc16_to_fc64(uhd::byteswap(input[i]), scale_factor);
+ }
+}
diff --git a/host/lib/convert/convert_impl.cpp b/host/lib/convert/convert_impl.cpp
new file mode 100644
index 000000000..12ad54486
--- /dev/null
+++ b/host/lib/convert/convert_impl.cpp
@@ -0,0 +1,143 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/convert.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/static.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/exception.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/format.hpp>
+#include <complex>
+
+using namespace uhd;
+
+bool convert::operator==(const convert::id_type &lhs, const convert::id_type &rhs){
+ return true
+ and (lhs.input_format == rhs.input_format)
+ and (lhs.num_inputs == rhs.num_inputs)
+ and (lhs.output_format == rhs.output_format)
+ and (lhs.num_outputs == rhs.num_outputs)
+ ;
+}
+
+std::string convert::id_type::to_pp_string(void) const{
+ return str(boost::format(
+ "conversion ID\n"
+ " Input format: %s\n"
+ " Num inputs: %d\n"
+ " Output format: %s\n"
+ " Num outputs: %d\n"
+ )
+ % this->input_format
+ % this->num_inputs
+ % this->output_format
+ % this->num_outputs
+ );
+}
+
+/***********************************************************************
+ * Define types for the function tables
+ **********************************************************************/
+struct fcn_table_entry_type{
+ convert::priority_type prio;
+ convert::function_type fcn;
+};
+
+/***********************************************************************
+ * Setup the table registry
+ **********************************************************************/
+typedef uhd::dict<convert::id_type, fcn_table_entry_type> fcn_table_type;
+UHD_SINGLETON_FCN(fcn_table_type, get_table);
+
+/***********************************************************************
+ * The registry functions
+ **********************************************************************/
+void uhd::convert::register_converter(
+ const id_type &id,
+ const function_type &fcn,
+ const priority_type prio
+){
+ //get a reference to the function table
+ fcn_table_type &table = get_table();
+
+ //register the function if higher priority
+ if (not table.has_key(id) or table[id].prio < prio){
+ table[id].fcn = fcn;
+ table[id].prio = prio;
+ }
+
+ //----------------------------------------------------------------//
+ UHD_LOGV(always) << "register_converter: " << id.to_pp_string() << std::endl
+ << " prio: " << prio << std::endl
+ << std::endl
+ ;
+ //----------------------------------------------------------------//
+}
+
+/***********************************************************************
+ * The converter functions
+ **********************************************************************/
+convert::function_type convert::get_converter(const id_type &id){
+ if (get_table().has_key(id)) return get_table()[id].fcn;
+ throw uhd::key_error("Cannot find a conversion routine for " + id.to_pp_string());
+}
+
+/***********************************************************************
+ * Mappings for item format to byte size for all items we can
+ **********************************************************************/
+typedef uhd::dict<std::string, size_t> item_size_type;
+UHD_SINGLETON_FCN(item_size_type, get_item_size_table);
+
+void convert::register_bytes_per_item(
+ const std::string &format, const size_t size
+){
+ get_item_size_table()[format] = size;
+}
+
+size_t convert::get_bytes_per_item(const std::string &format){
+ if (get_item_size_table().has_key(format)) return get_item_size_table()[format];
+
+ //OK. I am sorry about this.
+ //We didnt find a match, so lets find a match for the first term.
+ //This is partially a hack because of the way I append strings.
+ //But as long as life is kind, we can keep this.
+ const size_t pos = format.find("_");
+ if (pos != std::string::npos){
+ return get_bytes_per_item(format.substr(0, pos));
+ }
+
+ throw uhd::key_error("Cannot find an item size:\n" + format);
+}
+
+UHD_STATIC_BLOCK(convert_register_item_sizes){
+ //register standard complex types
+ convert::register_bytes_per_item("fc64", sizeof(std::complex<double>));
+ convert::register_bytes_per_item("fc32", sizeof(std::complex<float>));
+ convert::register_bytes_per_item("sc64", sizeof(std::complex<boost::int64_t>));
+ convert::register_bytes_per_item("sc32", sizeof(std::complex<boost::int32_t>));
+ convert::register_bytes_per_item("sc16", sizeof(std::complex<boost::int16_t>));
+ convert::register_bytes_per_item("sc8", sizeof(std::complex<boost::int8_t>));
+
+ //register standard real types
+ convert::register_bytes_per_item("f64", sizeof(double));
+ convert::register_bytes_per_item("f32", sizeof(float));
+ convert::register_bytes_per_item("s64", sizeof(boost::int64_t));
+ 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));
+}
diff --git a/host/lib/convert/convert_orc.orc b/host/lib/convert/convert_orc.orc
new file mode 100644
index 000000000..f7075606e
--- /dev/null
+++ b/host/lib/convert/convert_orc.orc
@@ -0,0 +1,80 @@
+.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
+swaplq tmp, tmp
+x2 convlw tmp2, tmp
+x2 convwb dst, tmp2
diff --git a/host/lib/convert/convert_with_neon.cpp b/host/lib/convert/convert_with_neon.cpp
new file mode 100644
index 000000000..c7ad62104
--- /dev/null
+++ b/host/lib/convert/convert_with_neon.cpp
@@ -0,0 +1,61 @@
+//
+// Copyright 2011-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "convert_common.hpp"
+#include <arm_neon.h>
+
+using namespace uhd::convert;
+
+DECLARE_CONVERTER(fc32, 1, sc16_item32_le, 1, PRIORITY_SIMD){
+ const fc32_t *input = reinterpret_cast<const fc32_t *>(inputs[0]);
+ item32_t *output = reinterpret_cast<item32_t *>(outputs[0]);
+
+ size_t i;
+
+ float32x4_t Q0 = vdupq_n_f32(float(scale_factor));
+ for (i=0; i < (nsamps & ~0x03); i+=2) {
+ float32x4_t Q1 = vld1q_f32(reinterpret_cast<const float *>(&input[i]));
+ float32x4_t Q2 = vmulq_f32(Q1, Q0);
+ int32x4_t Q3 = vcvtq_s32_f32(Q2);
+ int16x4_t D8 = vmovn_s32(Q3);
+ int16x4_t D9 = vrev32_s16(D8);
+ vst1_s16((reinterpret_cast<int16_t *>(&output[i])), D9);
+ }
+
+ for (; i < nsamps; i++)
+ output[i] = fc32_to_item32_sc16(input[i], scale_factor);
+}
+
+DECLARE_CONVERTER(sc16_item32_le, 1, fc32, 1, PRIORITY_SIMD){
+ const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]);
+ fc32_t *output = reinterpret_cast<fc32_t *>(outputs[0]);
+
+ size_t i;
+
+ float32x4_t Q1 = vdupq_n_f32(float(scale_factor));
+ for (i=0; i < (nsamps & ~0x03); i+=2) {
+ int16x4_t D0 = vld1_s16(reinterpret_cast<const int16_t *>(&input[i]));
+ int16x4_t D1 = vrev32_s16(D0);
+ int32x4_t Q2 = vmovl_s16(D1);
+ float32x4_t Q3 = vcvtq_f32_s32(Q2);
+ float32x4_t Q4 = vmulq_f32(Q3, Q1);
+ vst1q_f32((reinterpret_cast<float *>(&output[i])), Q4);
+ }
+
+ for (; i < nsamps; i++)
+ output[i] = item32_sc16_to_fc32(input[i], scale_factor);
+}
diff --git a/host/lib/convert/convert_with_orc.cpp b/host/lib/convert/convert_with_orc.cpp
new file mode 100644
index 000000000..e44c8ca73
--- /dev/null
+++ b/host/lib/convert/convert_with_orc.cpp
@@ -0,0 +1,65 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "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);
+ _convert_swap_byte_pairs_orc(outputs[0], outputs[0], (nsamps + 1)/2);
+}
+
+DECLARE_CONVERTER(fc32, 1, sc8_item32_le, 1, PRIORITY_LIBORC){
+ _convert_fc32_1_to_sc8_1_nswap_orc(outputs[0], inputs[0], scale_factor, nsamps);
+}
diff --git a/host/lib/convert/convert_with_tables.cpp b/host/lib/convert/convert_with_tables.cpp
new file mode 100644
index 000000000..cd7773d4b
--- /dev/null
+++ b/host/lib/convert/convert_with_tables.cpp
@@ -0,0 +1,282 @@
+//
+// Copyright 2011-2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "convert_common.hpp"
+#include <uhd/utils/byteswap.hpp>
+#include <boost/math/special_functions/round.hpp>
+#include <vector>
+
+using namespace uhd::convert;
+
+static const size_t sc16_table_len = size_t(1 << 16);
+
+typedef boost::uint16_t (*tohost16_type)(boost::uint16_t);
+
+/***********************************************************************
+ * Implementation for sc16 to sc8 lookup table
+ * - Lookup the real and imaginary parts individually
+ **********************************************************************/
+template <bool swap>
+class convert_sc16_1_to_sc8_item32_1 : public converter{
+public:
+ convert_sc16_1_to_sc8_item32_1(void): _table(sc16_table_len){}
+
+ void set_scalar(const double scalar){
+ for (size_t i = 0; i < sc16_table_len; i++){
+ const boost::int16_t val = boost::uint16_t(i);
+ _table[i] = boost::int8_t(boost::math::iround(val * scalar / 32767.));
+ }
+ }
+
+ void operator()(const input_type &inputs, const output_type &outputs, const size_t nsamps){
+ const sc16_t *input = reinterpret_cast<const sc16_t *>(inputs[0]);
+ item32_t *output = reinterpret_cast<item32_t *>(outputs[0]);
+
+ const size_t num_pairs = nsamps/2;
+ for (size_t i = 0, j = 0; i < num_pairs; i++, j+=2){
+ output[i] = this->lookup(input[j], input[j+1]);
+ }
+
+ if (nsamps != num_pairs*2){
+ output[num_pairs] = this->lookup(input[nsamps-1], 0);;
+ }
+ }
+
+ item32_t lookup(const sc16_t &in0, const sc16_t &in1){
+ if (swap){ //hope this compiles out, its a template constant
+ return
+ (item32_t(_table[boost::uint16_t(in0.real())]) << 16) |
+ (item32_t(_table[boost::uint16_t(in0.imag())]) << 24) |
+ (item32_t(_table[boost::uint16_t(in1.real())]) << 0) |
+ (item32_t(_table[boost::uint16_t(in1.imag())]) << 8) ;
+ }
+ return
+ (item32_t(_table[boost::uint16_t(in0.real())]) << 8) |
+ (item32_t(_table[boost::uint16_t(in0.imag())]) << 0) |
+ (item32_t(_table[boost::uint16_t(in1.real())]) << 24) |
+ (item32_t(_table[boost::uint16_t(in1.imag())]) << 16) ;
+ }
+
+private:
+ std::vector<boost::uint8_t> _table;
+};
+
+/***********************************************************************
+ * Implementation for sc16 lookup table
+ * - Lookup the real and imaginary parts individually
+ **********************************************************************/
+template <typename type, tohost16_type tohost, size_t re_shift, size_t im_shift>
+class convert_sc16_item32_1_to_fcxx_1 : public converter{
+public:
+ convert_sc16_item32_1_to_fcxx_1(void): _table(sc16_table_len){}
+
+ void set_scalar(const double scalar){
+ for (size_t i = 0; i < sc16_table_len; i++){
+ const boost::uint16_t val = tohost(boost::uint16_t(i & 0xffff));
+ _table[i] = type(boost::int16_t(val)*scalar);
+ }
+ }
+
+ void operator()(const input_type &inputs, const output_type &outputs, const size_t nsamps){
+ const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]);
+ std::complex<type> *output = reinterpret_cast<std::complex<type> *>(outputs[0]);
+
+ for (size_t i = 0; i < nsamps; i++){
+ const item32_t item = input[i];
+ output[i] = std::complex<type>(
+ _table[boost::uint16_t(item >> re_shift)],
+ _table[boost::uint16_t(item >> im_shift)]
+ );
+ }
+ }
+
+private:
+ std::vector<type> _table;
+};
+
+/***********************************************************************
+ * Implementation for sc8 lookup table
+ * - Lookup the real and imaginary parts together
+ **********************************************************************/
+template <typename type, tohost16_type tohost, size_t lo_shift, size_t hi_shift>
+class convert_sc8_item32_1_to_fcxx_1 : public converter{
+public:
+ convert_sc8_item32_1_to_fcxx_1(void): _table(sc16_table_len){}
+
+ //special case for sc16 type, 32767 undoes float normalization
+ static type conv(const boost::int8_t &num, const double scalar){
+ if (sizeof(type) == sizeof(s16_t)){
+ return type(boost::math::iround(num*scalar*32767));
+ }
+ return type(num*scalar);
+ }
+
+ void set_scalar(const double scalar){
+ for (size_t i = 0; i < sc16_table_len; i++){
+ const boost::uint16_t val = tohost(boost::uint16_t(i & 0xffff));
+ const type real = conv(boost::int8_t(val >> 8), scalar);
+ const type imag = conv(boost::int8_t(val >> 0), scalar);
+ _table[i] = std::complex<type>(real, imag);
+ }
+ }
+
+ void operator()(const input_type &inputs, const output_type &outputs, const size_t nsamps){
+ const item32_t *input = reinterpret_cast<const item32_t *>(size_t(inputs[0]) & ~0x3);
+ std::complex<type> *output = reinterpret_cast<std::complex<type> *>(outputs[0]);
+
+ size_t num_samps = nsamps;
+
+ if ((size_t(inputs[0]) & 0x3) != 0){
+ const item32_t item0 = *input++;
+ *output++ = _table[boost::uint16_t(item0 >> hi_shift)];
+ num_samps--;
+ }
+
+ const size_t num_pairs = num_samps/2;
+ for (size_t i = 0, j = 0; i < num_pairs; i++, j+=2){
+ const item32_t item_i = (input[i]);
+ output[j] = _table[boost::uint16_t(item_i >> lo_shift)];
+ output[j + 1] = _table[boost::uint16_t(item_i >> hi_shift)];
+ }
+
+ if (num_samps != num_pairs*2){
+ const item32_t item_n = input[num_pairs];
+ output[num_samps-1] = _table[boost::uint16_t(item_n >> lo_shift)];
+ }
+ }
+
+private:
+ std::vector<std::complex<type> > _table;
+};
+
+/***********************************************************************
+ * Factory functions and registration
+ **********************************************************************/
+
+#ifdef BOOST_BIG_ENDIAN
+# define SHIFT_PAIR0 16, 0
+# define SHIFT_PAIR1 0, 16
+# define BE_SWAP false
+# define LE_SWAP true
+#else
+# define SHIFT_PAIR0 0, 16
+# define SHIFT_PAIR1 16, 0
+# define BE_SWAP true
+# define LE_SWAP false
+#endif
+
+static converter::sptr make_convert_sc16_item32_be_1_to_fc32_1(void){
+ return converter::sptr(new convert_sc16_item32_1_to_fcxx_1<float, uhd::ntohx, SHIFT_PAIR0>());
+}
+
+static converter::sptr make_convert_sc16_item32_be_1_to_fc64_1(void){
+ return converter::sptr(new convert_sc16_item32_1_to_fcxx_1<double, uhd::ntohx, SHIFT_PAIR0>());
+}
+
+static converter::sptr make_convert_sc16_item32_le_1_to_fc32_1(void){
+ return converter::sptr(new convert_sc16_item32_1_to_fcxx_1<float, uhd::wtohx, SHIFT_PAIR1>());
+}
+
+static converter::sptr make_convert_sc16_item32_le_1_to_fc64_1(void){
+ return converter::sptr(new convert_sc16_item32_1_to_fcxx_1<double, uhd::wtohx, SHIFT_PAIR1>());
+}
+
+static converter::sptr make_convert_sc8_item32_be_1_to_fc32_1(void){
+ return converter::sptr(new convert_sc8_item32_1_to_fcxx_1<float, uhd::ntohx, SHIFT_PAIR1>());
+}
+
+static converter::sptr make_convert_sc8_item32_be_1_to_fc64_1(void){
+ return converter::sptr(new convert_sc8_item32_1_to_fcxx_1<double, uhd::ntohx, SHIFT_PAIR1>());
+}
+
+static converter::sptr make_convert_sc8_item32_le_1_to_fc32_1(void){
+ return converter::sptr(new convert_sc8_item32_1_to_fcxx_1<float, uhd::wtohx, SHIFT_PAIR0>());
+}
+
+static converter::sptr make_convert_sc8_item32_le_1_to_fc64_1(void){
+ return converter::sptr(new convert_sc8_item32_1_to_fcxx_1<double, uhd::wtohx, SHIFT_PAIR0>());
+}
+
+static converter::sptr make_convert_sc8_item32_be_1_to_sc16_1(void){
+ return converter::sptr(new convert_sc8_item32_1_to_fcxx_1<s16_t, uhd::ntohx, SHIFT_PAIR1>());
+}
+
+static converter::sptr make_convert_sc8_item32_le_1_to_sc16_1(void){
+ return converter::sptr(new convert_sc8_item32_1_to_fcxx_1<s16_t, uhd::wtohx, SHIFT_PAIR0>());
+}
+
+static converter::sptr make_convert_sc16_1_to_sc8_item32_be_1(void){
+ return converter::sptr(new convert_sc16_1_to_sc8_item32_1<BE_SWAP>());
+}
+
+static converter::sptr make_convert_sc16_1_to_sc8_item32_le_1(void){
+ return converter::sptr(new convert_sc16_1_to_sc8_item32_1<LE_SWAP>());
+}
+
+UHD_STATIC_BLOCK(register_convert_sc16_item32_1_to_fcxx_1){
+ uhd::convert::id_type id;
+ id.num_inputs = 1;
+ id.num_outputs = 1;
+
+ id.output_format = "fc32";
+ id.input_format = "sc16_item32_be";
+ uhd::convert::register_converter(id, &make_convert_sc16_item32_be_1_to_fc32_1, PRIORITY_TABLE);
+
+ id.output_format = "fc64";
+ id.input_format = "sc16_item32_be";
+ uhd::convert::register_converter(id, &make_convert_sc16_item32_be_1_to_fc64_1, PRIORITY_TABLE);
+
+ id.output_format = "fc32";
+ id.input_format = "sc16_item32_le";
+ uhd::convert::register_converter(id, &make_convert_sc16_item32_le_1_to_fc32_1, PRIORITY_TABLE);
+
+ id.output_format = "fc64";
+ id.input_format = "sc16_item32_le";
+ uhd::convert::register_converter(id, &make_convert_sc16_item32_le_1_to_fc64_1, PRIORITY_TABLE);
+
+ id.output_format = "fc32";
+ id.input_format = "sc8_item32_be";
+ uhd::convert::register_converter(id, &make_convert_sc8_item32_be_1_to_fc32_1, PRIORITY_TABLE);
+
+ id.output_format = "fc64";
+ id.input_format = "sc8_item32_be";
+ uhd::convert::register_converter(id, &make_convert_sc8_item32_be_1_to_fc64_1, PRIORITY_TABLE);
+
+ id.output_format = "fc32";
+ id.input_format = "sc8_item32_le";
+ uhd::convert::register_converter(id, &make_convert_sc8_item32_le_1_to_fc32_1, PRIORITY_TABLE);
+
+ id.output_format = "fc64";
+ id.input_format = "sc8_item32_le";
+ uhd::convert::register_converter(id, &make_convert_sc8_item32_le_1_to_fc64_1, PRIORITY_TABLE);
+
+ id.output_format = "sc16";
+ id.input_format = "sc8_item32_be";
+ uhd::convert::register_converter(id, &make_convert_sc8_item32_be_1_to_sc16_1, PRIORITY_TABLE);
+
+ id.output_format = "sc16";
+ id.input_format = "sc8_item32_le";
+ uhd::convert::register_converter(id, &make_convert_sc8_item32_le_1_to_sc16_1, PRIORITY_TABLE);
+
+ id.input_format = "sc16";
+ id.output_format = "sc8_item32_be";
+ uhd::convert::register_converter(id, &make_convert_sc16_1_to_sc8_item32_be_1, PRIORITY_TABLE);
+
+ id.input_format = "sc16";
+ id.output_format = "sc8_item32_le";
+ uhd::convert::register_converter(id, &make_convert_sc16_1_to_sc8_item32_le_1, PRIORITY_TABLE);
+}
diff --git a/host/lib/convert/gen_convert_general.py b/host/lib/convert/gen_convert_general.py
new file mode 100644
index 000000000..364c4bd1a
--- /dev/null
+++ b/host/lib/convert/gen_convert_general.py
@@ -0,0 +1,206 @@
+#!/usr/bin/env python
+#
+# Copyright 2011-2012 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+TMPL_HEADER = """
+#import time
+/***********************************************************************
+ * This file was generated by $file on $time.strftime("%c")
+ **********************************************************************/
+
+\#include "convert_common.hpp"
+\#include <uhd/utils/byteswap.hpp>
+
+using namespace uhd::convert;
+"""
+
+TMPL_CONV_GEN2_ITEM32 = """
+DECLARE_CONVERTER(item32, 1, sc16_item32_$(end), 1, PRIORITY_GENERAL){
+ const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]);
+ item32_t *output = reinterpret_cast<item32_t *>(outputs[0]);
+
+ for (size_t i = 0; i < nsamps; i++){
+ output[i] = $(to_wire)(input[i]);
+ }
+}
+
+DECLARE_CONVERTER(sc16_item32_$(end), 1, item32, 1, PRIORITY_GENERAL){
+ const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]);
+ item32_t *output = reinterpret_cast<item32_t *>(outputs[0]);
+
+ for (size_t i = 0; i < nsamps; i++){
+ output[i] = $(to_host)(input[i]);
+ }
+}
+"""
+
+TMPL_CONV_GEN2_SC16 = """
+DECLARE_CONVERTER($(cpu_type), 1, sc16_item32_$(end), 1, PRIORITY_GENERAL){
+ const $(cpu_type)_t *input = reinterpret_cast<const $(cpu_type)_t *>(inputs[0]);
+ item32_t *output = reinterpret_cast<item32_t *>(outputs[0]);
+
+ for (size_t i = 0; i < nsamps; i++){
+ output[i] = $(to_wire)($(cpu_type)_to_item32_sc16(input[i], scale_factor));
+ }
+}
+
+DECLARE_CONVERTER(sc16_item32_$(end), 1, $(cpu_type), 1, PRIORITY_GENERAL){
+ const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]);
+ $(cpu_type)_t *output = reinterpret_cast<$(cpu_type)_t *>(outputs[0]);
+
+ for (size_t i = 0; i < nsamps; i++){
+ output[i] = item32_sc16_to_$(cpu_type)($(to_host)(input[i]), scale_factor);
+ }
+}
+"""
+
+TMPL_CONV_GEN2_SC8 = """
+DECLARE_CONVERTER(sc8_item32_$(end), 1, $(cpu_type), 1, PRIORITY_GENERAL){
+ const item32_t *input = reinterpret_cast<const item32_t *>(size_t(inputs[0]) & ~0x3);
+ $(cpu_type)_t *output = reinterpret_cast<$(cpu_type)_t *>(outputs[0]);
+ $(cpu_type)_t dummy;
+ size_t num_samps = nsamps;
+
+ if ((size_t(inputs[0]) & 0x3) != 0){
+ const item32_t item0 = $(to_host)(*input++);
+ item32_sc8_to_$(cpu_type)(item0, dummy, *output++, scale_factor);
+ num_samps--;
+ }
+
+ const size_t num_pairs = num_samps/2;
+ for (size_t i = 0, j = 0; i < num_pairs; i++, j+=2){
+ const item32_t item_i = $(to_host)(input[i]);
+ item32_sc8_to_$(cpu_type)(item_i, output[j], output[j+1], scale_factor);
+ }
+
+ if (num_samps != num_pairs*2){
+ const item32_t item_n = $(to_host)(input[num_pairs]);
+ item32_sc8_to_$(cpu_type)(item_n, output[num_samps-1], dummy, scale_factor);
+ }
+}
+
+DECLARE_CONVERTER($(cpu_type), 1, sc8_item32_$(end), 1, PRIORITY_GENERAL){
+ const $(cpu_type)_t *input = reinterpret_cast<const $(cpu_type)_t *>(inputs[0]);
+ item32_t *output = reinterpret_cast<item32_t *>(outputs[0]);
+
+ const size_t num_pairs = nsamps/2;
+ for (size_t i = 0, j = 0; i < num_pairs; i++, j+=2){
+ const item32_t item = $(cpu_type)_to_item32_sc8(input[j], input[j+1], scale_factor);
+ output[i] = $(to_wire)(item);
+ }
+
+ if (nsamps != num_pairs*2){
+ const item32_t item = $(cpu_type)_to_item32_sc8(input[nsamps-1], 0, scale_factor);
+ output[num_pairs] = $(to_wire)(item);
+ }
+}
+"""
+
+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
+ 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
+ }
+}
+
+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 (size_t i = 0, j = 0; i < nsamps; i++){
+ #for $w in range($width)
+ output$(w)[i] = $(cpu_type)_t(
+ boost::int16_t($(to_host)(input[j+0]))$(do_scale),
+ boost::int16_t($(to_host)(input[j+1]))$(do_scale)
+ );
+ j += 2;
+ #end for
+ }
+}
+
+DECLARE_CONVERTER(sc8_item16_usrp1, 1, $(cpu_type), $(width), PRIORITY_GENERAL){
+ const boost::uint16_t *input = reinterpret_cast<const boost::uint16_t *>(inputs[0]);
+ #for $w in range($width)
+ $(cpu_type)_t *output$(w) = reinterpret_cast<$(cpu_type)_t *>(outputs[$(w)]);
+ #end for
+
+ for (size_t i = 0, j = 0; i < nsamps; i++){
+ #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)
+ );
+ }
+ #end for
+ }
+}
+"""
+
+def parse_tmpl(_tmpl_text, **kwargs):
+ from Cheetah.Template import Template
+ return str(Template(_tmpl_text, kwargs))
+
+if __name__ == '__main__':
+ import sys, os
+ file = os.path.basename(__file__)
+ output = parse_tmpl(TMPL_HEADER, file=file)
+
+ #generate complex converters for all gen2 platforms
+ for end, to_host, to_wire in (
+ ('be', 'uhd::ntohx', 'uhd::htonx'),
+ ('le', 'uhd::wtohx', 'uhd::htowx'),
+ ):
+ for cpu_type in 'fc64', 'fc32', 'sc16':
+ output += parse_tmpl(
+ TMPL_CONV_GEN2_SC16,
+ end=end, to_host=to_host, to_wire=to_wire, cpu_type=cpu_type
+ )
+ for cpu_type in 'fc64', 'fc32', 'sc16', 'sc8':
+ output += parse_tmpl(
+ TMPL_CONV_GEN2_SC8,
+ end=end, to_host=to_host, to_wire=to_wire, cpu_type=cpu_type
+ )
+ output += parse_tmpl(
+ TMPL_CONV_GEN2_ITEM32,
+ end=end, to_host=to_host, to_wire=to_wire
+ )
+
+ #generate complex converters for usrp1 format
+ for width in 1, 2, 4:
+ for cpu_type, do_scale in (
+ ('fc64', '*scale_factor'),
+ ('fc32', '*float(scale_factor)'),
+ ('sc16', ''),
+ ):
+ output += parse_tmpl(
+ TMPL_CONV_USRP1_COMPLEX,
+ width=width, to_host='uhd::wtohx', to_wire='uhd::htowx',
+ cpu_type=cpu_type, do_scale=do_scale
+ )
+ open(sys.argv[1], 'w').write(output)
diff --git a/host/lib/deprecated.cpp b/host/lib/deprecated.cpp
new file mode 100644
index 000000000..0fc751eeb
--- /dev/null
+++ b/host/lib/deprecated.cpp
@@ -0,0 +1,81 @@
+//----------------------------------------------------------------------
+//-- deprecated interfaces below, to be removed when the API is changed
+//----------------------------------------------------------------------
+
+#include <uhd/types/otw_type.hpp>
+#include <uhd/types/io_type.hpp>
+#include <boost/cstdint.hpp>
+#include <stdexcept>
+#include <complex>
+#include <vector>
+
+using namespace uhd;
+
+/***********************************************************************
+ * otw type
+ **********************************************************************/
+size_t otw_type_t::get_sample_size(void) const{
+ return (this->width * 2) / 8;
+}
+
+otw_type_t::otw_type_t(void):
+ width(0),
+ shift(0),
+ byteorder(BO_NATIVE)
+{
+ /* NOP */
+}
+
+/***********************************************************************
+ * io type
+ **********************************************************************/
+static std::vector<size_t> get_tid_size_table(void){
+ std::vector<size_t> table(128, 0);
+ table[size_t(io_type_t::COMPLEX_FLOAT64)] = sizeof(std::complex<double>);
+ table[size_t(io_type_t::COMPLEX_FLOAT32)] = sizeof(std::complex<float>);
+ table[size_t(io_type_t::COMPLEX_INT16)] = sizeof(std::complex<boost::int16_t>);
+ table[size_t(io_type_t::COMPLEX_INT8)] = sizeof(std::complex<boost::int8_t>);
+ return table;
+}
+
+static const std::vector<size_t> tid_size_table(get_tid_size_table());
+
+io_type_t::io_type_t(tid_t tid):
+ size(tid_size_table[size_t(tid) & 0x7f]), tid(tid)
+{
+ /* NOP */
+}
+
+io_type_t::io_type_t(size_t size):
+ size(size), tid(CUSTOM_TYPE)
+{
+ /* NOP */
+}
+
+#include <uhd/types/clock_config.hpp>
+
+using namespace uhd;
+
+clock_config_t clock_config_t::external(void){
+ clock_config_t clock_config;
+ clock_config.ref_source = clock_config_t::REF_SMA;
+ clock_config.pps_source = clock_config_t::PPS_SMA;
+ clock_config.pps_polarity = clock_config_t::PPS_POS;
+ return clock_config;
+}
+
+clock_config_t clock_config_t::internal(void){
+ clock_config_t clock_config;
+ clock_config.ref_source = clock_config_t::REF_INT;
+ clock_config.pps_source = clock_config_t::PPS_SMA;
+ clock_config.pps_polarity = clock_config_t::PPS_POS;
+ return clock_config;
+}
+
+clock_config_t::clock_config_t(void):
+ ref_source(REF_INT),
+ pps_source(PPS_SMA),
+ pps_polarity(PPS_POS)
+{
+ /* NOP */
+}
diff --git a/host/lib/device.cpp b/host/lib/device.cpp
new file mode 100644
index 000000000..c5bc047da
--- /dev/null
+++ b/host/lib/device.cpp
@@ -0,0 +1,155 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/device.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/static.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <boost/foreach.hpp>
+#include <boost/format.hpp>
+#include <boost/weak_ptr.hpp>
+#include <boost/functional/hash.hpp>
+#include <boost/tuple/tuple.hpp>
+#include <boost/thread/mutex.hpp>
+
+using namespace uhd;
+
+static boost::mutex _device_mutex;
+
+/***********************************************************************
+ * Helper Functions
+ **********************************************************************/
+/*!
+ * Make a device hash that maps 1 to 1 with a device address.
+ * The hash will be used to identify created devices.
+ * \param dev_addr the device address
+ * \return the hash number
+ */
+static size_t hash_device_addr(
+ const device_addr_t &dev_addr
+){
+ //combine the hashes of sorted keys/value pairs
+ size_t hash = 0;
+ BOOST_FOREACH(const std::string &key, uhd::sorted(dev_addr.keys())){
+ boost::hash_combine(hash, key);
+ boost::hash_combine(hash, dev_addr[key]);
+ }
+ return hash;
+}
+
+/***********************************************************************
+ * Registration
+ **********************************************************************/
+typedef boost::tuple<device::find_t, device::make_t> dev_fcn_reg_t;
+
+// instantiate the device function registry container
+UHD_SINGLETON_FCN(std::vector<dev_fcn_reg_t>, get_dev_fcn_regs)
+
+void device::register_device(
+ const find_t &find,
+ const make_t &make
+){
+ UHD_LOGV(always) << "registering device" << std::endl;
+ get_dev_fcn_regs().push_back(dev_fcn_reg_t(find, make));
+}
+
+/***********************************************************************
+ * Discover
+ **********************************************************************/
+device_addrs_t device::find(const device_addr_t &hint){
+ boost::mutex::scoped_lock lock(_device_mutex);
+
+ device_addrs_t device_addrs;
+
+ BOOST_FOREACH(const dev_fcn_reg_t &fcn, get_dev_fcn_regs()){
+ try{
+ device_addrs_t discovered_addrs = fcn.get<0>()(hint);
+ device_addrs.insert(
+ device_addrs.begin(),
+ discovered_addrs.begin(),
+ discovered_addrs.end()
+ );
+ }
+ catch(const std::exception &e){
+ UHD_MSG(error) << "Device discovery error: " << e.what() << std::endl;
+ }
+ }
+
+ return device_addrs;
+}
+
+/***********************************************************************
+ * Make
+ **********************************************************************/
+device::sptr device::make(const device_addr_t &hint, size_t which){
+ boost::mutex::scoped_lock lock(_device_mutex);
+
+ typedef boost::tuple<device_addr_t, make_t> dev_addr_make_t;
+ std::vector<dev_addr_make_t> dev_addr_makers;
+
+ BOOST_FOREACH(const dev_fcn_reg_t &fcn, get_dev_fcn_regs()){
+ BOOST_FOREACH(device_addr_t dev_addr, fcn.get<0>()(hint)){
+ //append the discovered address and its factory function
+ dev_addr_makers.push_back(dev_addr_make_t(dev_addr, fcn.get<1>()));
+ }
+ }
+
+ //check that we found any devices
+ if (dev_addr_makers.size() == 0){
+ throw uhd::key_error(str(
+ boost::format("No devices found for ----->\n%s") % hint.to_pp_string()
+ ));
+ }
+
+ //check that the which index is valid
+ if (dev_addr_makers.size() <= which){
+ throw uhd::index_error(str(
+ boost::format("No device at index %d for ----->\n%s") % which % hint.to_pp_string()
+ ));
+ }
+
+ //create a unique hash for the device address
+ device_addr_t dev_addr; make_t maker;
+ boost::tie(dev_addr, maker) = dev_addr_makers.at(which);
+ size_t dev_hash = hash_device_addr(dev_addr);
+ UHD_LOG << boost::format("Device hash: %u") % dev_hash << std::endl;
+
+ //copy keys that were in hint but not in dev_addr
+ //this way, we can pass additional transport arguments
+ BOOST_FOREACH(const std::string &key, hint.keys()){
+ if (not dev_addr.has_key(key)) dev_addr[key] = hint[key];
+ }
+
+ //map device address hash to created devices
+ static uhd::dict<size_t, boost::weak_ptr<device> > hash_to_device;
+
+ //try to find an existing device
+ try{
+ UHD_ASSERT_THROW(hash_to_device.has_key(dev_hash));
+ UHD_ASSERT_THROW(not hash_to_device[dev_hash].expired());
+ return hash_to_device[dev_hash].lock();
+ }
+ //create and register a new device
+ catch(const uhd::assertion_error &){
+ device::sptr dev = maker(dev_addr);
+ hash_to_device[dev_hash] = dev;
+ return dev;
+ }
+}
diff --git a/host/lib/exception.cpp b/host/lib/exception.cpp
new file mode 100644
index 000000000..ea056bd3b
--- /dev/null
+++ b/host/lib/exception.cpp
@@ -0,0 +1,45 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/exception.hpp>
+#include <boost/functional/hash.hpp>
+#include <boost/format.hpp>
+
+using namespace uhd;
+
+exception::exception(const std::string &what):
+ std::runtime_error(what){/* NOP */}
+
+#define make_exception_impl(name, class, base) \
+ class::class(const std::string &what): \
+ base(str(boost::format("%s: %s") % name % what)){} \
+ unsigned class::code(void) const{return boost::hash<std::string>()(#class) & 0xfff;} \
+ class *class::dynamic_clone(void) const{return new class(*this);} \
+ void class::dynamic_throw(void) const{throw *this;}
+
+make_exception_impl("AssertionError", assertion_error, exception)
+make_exception_impl("LookupError", lookup_error, exception)
+make_exception_impl("IndexError", index_error, lookup_error)
+make_exception_impl("KeyError", key_error, lookup_error)
+make_exception_impl("TypeError", type_error, exception)
+make_exception_impl("ValueError", value_error, exception)
+make_exception_impl("RuntimeError", runtime_error, exception)
+make_exception_impl("NotImplementedError", not_implemented_error, runtime_error)
+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)
diff --git a/host/lib/ic_reg_maps/CMakeLists.txt b/host/lib/ic_reg_maps/CMakeLists.txt
new file mode 100644
index 000000000..dc2ab7847
--- /dev/null
+++ b/host/lib/ic_reg_maps/CMakeLists.txt
@@ -0,0 +1,105 @@
+#
+# Copyright 2010-2011 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# This file included, use CMake directory variables
+########################################################################
+
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
+SET(LIBUHD_PYTHON_GEN_SOURCE_DEPS ${CMAKE_CURRENT_SOURCE_DIR}/common.py)
+
+LIBUHD_PYTHON_GEN_SOURCE(
+ ${CMAKE_CURRENT_SOURCE_DIR}/gen_adf4350_regs.py
+ ${CMAKE_CURRENT_BINARY_DIR}/adf4350_regs.hpp
+)
+
+LIBUHD_PYTHON_GEN_SOURCE(
+ ${CMAKE_CURRENT_SOURCE_DIR}/gen_adf4351_regs.py
+ ${CMAKE_CURRENT_BINARY_DIR}/adf4351_regs.hpp
+)
+
+LIBUHD_PYTHON_GEN_SOURCE(
+ ${CMAKE_CURRENT_SOURCE_DIR}/gen_adf4360_regs.py
+ ${CMAKE_CURRENT_BINARY_DIR}/adf4360_regs.hpp
+)
+
+LIBUHD_PYTHON_GEN_SOURCE(
+ ${CMAKE_CURRENT_SOURCE_DIR}/gen_ad9510_regs.py
+ ${CMAKE_CURRENT_BINARY_DIR}/ad9510_regs.hpp
+)
+
+LIBUHD_PYTHON_GEN_SOURCE(
+ ${CMAKE_CURRENT_SOURCE_DIR}/gen_ad9777_regs.py
+ ${CMAKE_CURRENT_BINARY_DIR}/ad9777_regs.hpp
+)
+
+LIBUHD_PYTHON_GEN_SOURCE(
+ ${CMAKE_CURRENT_SOURCE_DIR}/gen_ad5623_regs.py
+ ${CMAKE_CURRENT_BINARY_DIR}/ad5623_regs.hpp
+)
+
+LIBUHD_PYTHON_GEN_SOURCE(
+ ${CMAKE_CURRENT_SOURCE_DIR}/gen_ad7922_regs.py
+ ${CMAKE_CURRENT_BINARY_DIR}/ad7922_regs.hpp
+)
+
+LIBUHD_PYTHON_GEN_SOURCE(
+ ${CMAKE_CURRENT_SOURCE_DIR}/gen_max2829_regs.py
+ ${CMAKE_CURRENT_BINARY_DIR}/max2829_regs.hpp
+)
+
+LIBUHD_PYTHON_GEN_SOURCE(
+ ${CMAKE_CURRENT_SOURCE_DIR}/gen_max2118_regs.py
+ ${CMAKE_CURRENT_BINARY_DIR}/max2118_regs.hpp
+)
+
+LIBUHD_PYTHON_GEN_SOURCE(
+ ${CMAKE_CURRENT_SOURCE_DIR}/gen_max2112_regs.py
+ ${CMAKE_CURRENT_BINARY_DIR}/max2112_regs.hpp
+)
+
+LIBUHD_PYTHON_GEN_SOURCE(
+ ${CMAKE_CURRENT_SOURCE_DIR}/gen_max2112_regs.py
+ ${CMAKE_CURRENT_BINARY_DIR}/max2112_regs.hpp
+)
+
+LIBUHD_PYTHON_GEN_SOURCE(
+ ${CMAKE_CURRENT_SOURCE_DIR}/gen_ad9862_regs.py
+ ${CMAKE_CURRENT_BINARY_DIR}/ad9862_regs.hpp
+)
+
+LIBUHD_PYTHON_GEN_SOURCE(
+ ${CMAKE_CURRENT_SOURCE_DIR}/gen_ad9522_regs.py
+ ${CMAKE_CURRENT_BINARY_DIR}/ad9522_regs.hpp
+)
+
+LIBUHD_PYTHON_GEN_SOURCE(
+ ${CMAKE_CURRENT_SOURCE_DIR}/gen_ads62p44_regs.py
+ ${CMAKE_CURRENT_BINARY_DIR}/ads62p44_regs.hpp
+)
+
+LIBUHD_PYTHON_GEN_SOURCE(
+ ${CMAKE_CURRENT_SOURCE_DIR}/gen_tuner_4937di5_regs.py
+ ${CMAKE_CURRENT_BINARY_DIR}/tuner_4937di5_regs.hpp
+)
+
+LIBUHD_PYTHON_GEN_SOURCE(
+ ${CMAKE_CURRENT_SOURCE_DIR}/gen_tda18272hnm_regs.py
+ ${CMAKE_CURRENT_BINARY_DIR}/tda18272hnm_regs.hpp
+)
+
+UNSET(LIBUHD_PYTHON_GEN_SOURCE_DEPS)
diff --git a/host/lib/ic_reg_maps/common.py b/host/lib/ic_reg_maps/common.py
new file mode 100644
index 000000000..24f5bf8be
--- /dev/null
+++ b/host/lib/ic_reg_maps/common.py
@@ -0,0 +1,196 @@
+#
+# Copyright 2010-2011 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import re
+import sys
+import math
+from Cheetah.Template import Template
+
+COMMON_TMPL = """\
+#import time
+/***********************************************************************
+ * This file was generated by $file on $time.strftime("%c")
+ **********************************************************************/
+
+\#ifndef INCLUDED_$(name.upper())_HPP
+\#define INCLUDED_$(name.upper())_HPP
+
+\#include <uhd/config.hpp>
+\#include <uhd/exception.hpp>
+\#include <boost/cstdint.hpp>
+\#include <set>
+
+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
+ };
+ #end if
+ $reg.get_type() $reg.get_name();
+ #end for
+
+ $(name)_t(void){
+ _state = NULL;
+ #for $reg in $regs
+ $reg.get_name() = $reg.get_default();
+ #end for
+ }
+
+ ~$(name)_t(void){
+ delete _state;
+ }
+
+ $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
+ }
+
+ 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());
+ }
+ #end for
+ 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
+ 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
+ }
+
+ #end for
+private:
+ $(name)_t *_state;
+};
+
+\#endif /* INCLUDED_$(name.upper())_HPP */
+"""
+
+def parse_tmpl(_tmpl_text, **kwargs):
+ return str(Template(_tmpl_text, 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)
+
+ def parse(self, reg_des):
+ x = re.match('^(\w*)\s*(\w*)\[(.*)\]\s*(\w*)\s*(.*)$', reg_des)
+ name, addr, bit_range, default, enums = x.groups()
+
+ #store variables
+ self._name = name
+ self._addr = to_num(addr)
+ if ':' in bit_range: self._addr_spec = sorted(map(int, bit_range.split(':')))
+ else: self._addr_spec = int(bit_range), int(bit_range)
+ self._default = to_num(default)
+
+ #extract enum
+ self._enums = list()
+ if enums:
+ enum_val = 0
+ for enum_str in map(str.strip, enums.split(',')):
+ if '=' in enum_str:
+ enum_name, enum_val = enum_str.split('=')
+ enum_val = to_num(enum_val)
+ else: enum_name = enum_str
+ self._enums.append((enum_name, enum_val))
+ enum_val += 1
+
+ def get_addr(self): return self._addr
+ def get_enums(self): return self._enums
+ 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))
+ return self._default
+ def get_type(self):
+ if self.get_enums(): return '%s_t'%self.get_name()
+ return 'boost::uint%d_t'%max(2**math.ceil(math.log(self.get_bit_width(), 2)), 8)
+ def get_shift(self): return self._addr_spec[0]
+ def get_mask(self): return hex(int('1'*self.get_bit_width(), 2))
+ def get_bit_width(self): return self._addr_spec[1] - self._addr_spec[0] + 1
+
+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)
+
+ 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(','))]
+
+ def get_name(self): return self._name
+ def get_regs(self): return self._regs
+ def get_bit_width(self): return sum(map(reg.get_bit_width, self._regs))
+ def get_type(self):
+ return 'boost::uint%d_t'%max(2**math.ceil(math.log(self.get_bit_width(), 2)), 8)
+
+def generate(name, regs_tmpl, body_tmpl='', file=__file__, append=False):
+ #evaluate the regs template and parse each line into a register
+ regs = list(); mregs = list()
+ for entry in parse_tmpl(regs_tmpl).splitlines():
+ if entry.startswith('~'): mregs.append(mreg(entry, regs))
+ else: regs.append(reg(entry))
+
+ #evaluate the body template with the list of registers
+ body = '\n '.join(parse_tmpl(body_tmpl, regs=regs).splitlines())
+
+ #evaluate the code template with the parsed registers and arguments
+ code = parse_tmpl(COMMON_TMPL,
+ name=name,
+ regs=regs,
+ mregs=mregs,
+ body=body,
+ file=file,
+ )
+
+ #write the generated code to file specified by argv1
+ open(sys.argv[1], 'a' if append else 'w').write(code)
diff --git a/host/lib/ic_reg_maps/gen_ad5623_regs.py b/host/lib/ic_reg_maps/gen_ad5623_regs.py
new file mode 100755
index 000000000..e653921ba
--- /dev/null
+++ b/host/lib/ic_reg_maps/gen_ad5623_regs.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+#
+# Copyright 2010 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# Template for raw text data describing registers
+# name addr[bit range inclusive] default optional enums
+########################################################################
+REGS_TMPL="""\
+data 0[4:15] 0
+addr 0[16:18] 0 DAC_A=0, DAC_B=1, ALL=7
+cmd 0[19:21] 0 wr_input_n, up_dac_n, wr_input_n_up_all, wr_up_dac_chan_n, power_down, reset, load_ldac
+"""
+
+########################################################################
+# Template for methods in the body of the struct
+########################################################################
+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
+ return reg;
+}
+"""
+
+if __name__ == '__main__':
+ import common; common.generate(
+ name='ad5623_regs',
+ regs_tmpl=REGS_TMPL,
+ body_tmpl=BODY_TMPL,
+ file=__file__,
+ )
diff --git a/host/lib/ic_reg_maps/gen_ad7922_regs.py b/host/lib/ic_reg_maps/gen_ad7922_regs.py
new file mode 100755
index 000000000..5cec1924a
--- /dev/null
+++ b/host/lib/ic_reg_maps/gen_ad7922_regs.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+#
+# Copyright 2010 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# Template for raw text data describing registers
+# name addr[bit range inclusive] default optional enums
+########################################################################
+REGS_TMPL="""\
+result 0[0:11] 0
+mod 0[12] 0
+chn 0[13] 0
+"""
+
+########################################################################
+# Template for methods in the body of the struct
+########################################################################
+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
+ 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
+}
+"""
+
+if __name__ == '__main__':
+ import common; common.generate(
+ name='ad7922_regs',
+ regs_tmpl=REGS_TMPL,
+ body_tmpl=BODY_TMPL,
+ file=__file__,
+ )
diff --git a/host/lib/ic_reg_maps/gen_ad9510_regs.py b/host/lib/ic_reg_maps/gen_ad9510_regs.py
new file mode 100755
index 000000000..83236c921
--- /dev/null
+++ b/host/lib/ic_reg_maps/gen_ad9510_regs.py
@@ -0,0 +1,139 @@
+#!/usr/bin/env python
+#
+# Copyright 2010 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# Template for raw text data describing registers
+# name addr[bit range inclusive] default optional enums
+########################################################################
+REGS_TMPL="""\
+########################################################################
+## serial control port config
+########################################################################
+long_instruction 0[4] 1 8bits, 16bits
+soft_reset 0[5] 0
+lsb_first 0[6] 0 msb, lsb
+sdo_inactive 0[7] 0 active, inactive
+########################################################################
+## pll settings
+########################################################################
+acounter 4[0:5] 0
+bcounter_msb 5[0:4] 0
+bcounter_lsb 6[0:7] 0
+lor_enable 7[2] 0 enb, dis
+lor_ildd 7[5:6] 0 3cyc, 6cyc, 12cyc, 24cyc
+charge_pump_mode 8[0:1] 0 3state, pump_up, pump_down, normal
+pll_mux_control 8[2:5] 0 off, dld_high, ndiv, dld_low, rdiv, ald_nchan, acounter, prescaler, pfd_up, pfd_down, lor_high, 3state, ald_pchan, lor_lol_high, lor_lol_low, lor_low
+pfd_polarity 8[6] 0 neg, pos
+reset_all_counters 9[0] 0
+ncounter_reset 9[1] 0
+rcounter_reset 9[2] 0
+cp_current_setting 9[4:6] 0 0_60ma, 1_2ma, 1_8ma, 2_4ma, 3_0ma, 3_6ma, 4_2ma, 4_8ma
+pll_power_down 0xA[0:1] 0 normal=0, async_pd=1, sync_pd=3
+prescaler_value 0xA[2:4] 0 div1, div2, 2_3, 4_5, 8_9, 16_17, 32_33, div3
+b_counter_bypass 0xA[6] 0
+ref_counter_msb 0xB[0:5] 0
+ref_counter_lsb 0xC[0:7] 0
+antibacklash_pw 0xD[0:1] 0 1_3ns, 2_9ns, 6_0ns
+dld_window 0xD[5] 0 9_5ns, 3_5ns
+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
+########################################################################
+## 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
+clock_select 45[0] 1 clk2_drives, clk1_drives
+clk1_power_down 45[1] 0
+clk2_power_down 45[2] 0
+prescaler_clock_pd 45[3] 0
+refin_power_down 45[4] 0
+all_clock_inputs_pd 45[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
+########################################################################
+## function
+########################################################################
+sync_detect_enable 58[0] 0 dis, enb
+sync_select 58[1] 0 1_to_0_5, 0_5_to_1
+soft_sync 58[2] 0
+dist_power_down 58[3] 0
+sync_power_down 58[4] 0
+function_pin_select 58[5:6] 0 resetb, syncb, test, pdb
+update_registers 0x5A[0] 0
+"""
+
+########################################################################
+# Template for methods in the body of the struct
+########################################################################
+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
+ break;
+ #end for
+ }
+ return reg;
+}
+
+boost::uint32_t get_write_reg(boost::uint16_t addr){
+ return (boost::uint32_t(addr) << 8) | get_reg(addr);
+}
+
+boost::uint32_t get_read_reg(boost::uint16_t addr){
+ return (boost::uint32_t(addr) << 8) | (1 << 23);
+}
+"""
+
+if __name__ == '__main__':
+ import common; common.generate(
+ name='ad9510_regs',
+ regs_tmpl=REGS_TMPL,
+ body_tmpl=BODY_TMPL,
+ file=__file__,
+ )
diff --git a/host/lib/ic_reg_maps/gen_ad9522_regs.py b/host/lib/ic_reg_maps/gen_ad9522_regs.py
new file mode 100755
index 000000000..1512da811
--- /dev/null
+++ b/host/lib/ic_reg_maps/gen_ad9522_regs.py
@@ -0,0 +1,193 @@
+#!/usr/bin/env python
+#
+# Copyright 2010-2011 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# Template for raw text data describing registers
+# name addr[bit range inclusive] default optional enums
+########################################################################
+REGS_TMPL="""\
+sdo_active 0x000[7] 0 sdio, sdo_sdio
+lsb_first_addr_incr 0x000[6] 0 msb, lsb
+soft_reset 0x000[5] 0
+mirror 0x000[3:0] 0
+readback_active_registers 0x004[0] 0 buffer, active
+pfd_polarity 0x010[7] 0 pos, neg
+cp_current 0x010[6:4] 7 0_6ma, 1_2ma, 1_8ma, 2_4ma, 3_0ma, 3_6ma, 4_2ma, 4_8ma
+cp_mode 0x010[3:2] 3 high_imp, force_source, force_sink, normal
+pll_power_down 0x010[1:0] 1 normal=0, async=1, sync=3
+r_counter_lsb 0x011[7:0] 1
+r_counter_msb 0x012[5:0] 0
+~r_counter r_counter_lsb, r_counter_msb
+a_counter 0x013[5:0] 0
+b_counter_lsb 0x014[7:0] 3
+b_counter_msb 0x015[4:0] 0
+~b_counter b_counter_lsb, b_counter_msb
+set_cp_pin_to_vcp_2 0x016[7] 0 normal, vcp_2
+reset_r_counter 0x016[6] 0
+reset_a_and_b_counters 0x016[5] 0
+reset_all_counters 0x016[4] 0
+b_counter_bypass 0x016[3] 0 normal, div1
+prescaler_p 0x016[2:0] 6 div1, div2, div2_3, div4_5, div8_9, div16_17, div32_33, div3
+status_pin_control 0x017[7:2] 0
+antibacklash_pulse_width 0x017[1:0] 0 2_9ns, 1_3ns, 6_0ns
+enb_cmos_ref_input_dc_off 0x018[7] 0
+lock_detect_counter 0x018[6:5] 0 5cyc, 16cyc, 64cyc, 255cyc
+digital_lock_detect_window 0x018[4] 0 high_range, low_range
+disable_digital_lock_detect 0x018[3] 0 normal, disabled
+vco_calibration_divider 0x018[2:1] 3 div2, div4, div8, div16
+vco_calibration_now 0x018[0] 0
+r_a_b_counters_sync_pin_rst 0x019[7:6] 0 nothing, async, sync
+r_path_delay 0x019[5:3] 0
+n_path_delay 0x019[2:0] 0
+enable_status_pin_divider 0x01A[7] 0
+ref_freq_monitor_threshold 0x01A[6] 0 1_02mhz, 6khz
+ld_pin_control 0x01A[5:0] 0
+enable_vco_freq_monitor 0x01B[7] 0
+enable_ref2_freq_monitor 0x01B[6] 0
+enable_ref1_freq_monitor 0x01B[5] 0
+refmon_pin_control 0x01B[4:0] 0
+disable_switchover_deglitch 0x01C[7] 0
+select_ref 0x01C[6] 0 ref1, ref2
+use_ref_sel_pin 0x01C[5] 0 register, ref_sel
+enb_auto_ref_switchover 0x01C[4] 0 manual, auto
+stay_on_ref2 0x01C[3] 0 return_ref1, stay_ref2
+enable_ref2 0x01C[2] 0
+enable_ref1 0x01C[1] 0
+enable_differential_ref 0x01C[0] 0
+enb_stat_eeprom_at_stat_pin 0x01D[7] 1
+enable_xtal_osc 0x01D[6] 0
+enable_clock_doubler 0x01D[5] 0
+disable_pll_status_reg 0x01D[4] 0
+enable_ld_pin_comparator 0x01D[3] 0
+enable_external_holdover 0x01D[1] 0
+enable_holdover 0x01D[0] 0
+external_zero_delay_fcds 0x01E[4:3] 0
+enable_external_zero_delay 0x01E[2] 0
+enable_zero_delay 0x01E[1] 0
+########################################################################
+vco_calibration_finished 0x01F[6] 0
+holdover_active 0x01F[5] 0
+ref2_selected 0x01F[4] 0
+vco_freq_gt_thresh 0x01F[3] 0
+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 reversed(range(8))
+csdld_en_out_$i 0x0FC[$i] 0 ignore, async
+#end for
+########################################################################
+#for $i in reversed(range(4))
+csdld_en_out_$(8 + $i) 0x0FD[$i] 0 ignore, async
+#end for
+########################################################################
+#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
+########################################################################
+vco_divider 0x1E0[2:0] 2 div2, div3, div4, div5, div6, static, div1
+power_down_clock_input_sel 0x1E1[4] 0
+power_down_vco_clock_ifc 0x1E1[3] 0
+power_down_vco_and_clock 0x1E1[2] 0
+select_vco_or_clock 0x1E1[1] 0 external, vco
+bypass_vco_divider 0x1E1[0] 0
+disable_power_on_sync 0x230[3] 0
+power_down_sync 0x230[2] 0
+power_down_dist_ref 0x230[1] 0
+soft_sync 0x230[0] 0
+io_update 0x232[0] 0
+soft_eeprom 0xB02[1] 0
+enable_eeprom_write 0xB02[0] 0
+reg2eeprom 0xB03[0] 0
+"""
+
+########################################################################
+# Template for methods in the body of the struct
+########################################################################
+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
+ break;
+ #end for
+ }
+ if (addr == 0){ //mirror 4 bits in register 0
+ reg |= ((reg >> 7) & 0x1) << 0;
+ reg |= ((reg >> 6) & 0x1) << 1;
+ reg |= ((reg >> 5) & 0x1) << 2;
+ reg |= ((reg >> 4) & 0x1) << 3;
+ }
+ return reg;
+}
+
+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
+ break;
+ #end for
+ }
+}
+
+boost::uint32_t get_write_reg(boost::uint16_t addr){
+ return (boost::uint32_t(addr) << 8) | get_reg(addr);
+}
+
+boost::uint32_t get_read_reg(boost::uint16_t addr){
+ return (boost::uint32_t(addr) << 8) | (1 << 23);
+}
+
+"""
+
+if __name__ == '__main__':
+ import common; common.generate(
+ name='ad9522_regs',
+ regs_tmpl=REGS_TMPL,
+ body_tmpl=BODY_TMPL,
+ file=__file__,
+ )
diff --git a/host/lib/ic_reg_maps/gen_ad9777_regs.py b/host/lib/ic_reg_maps/gen_ad9777_regs.py
new file mode 100755
index 000000000..47b61cf44
--- /dev/null
+++ b/host/lib/ic_reg_maps/gen_ad9777_regs.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python
+#
+# Copyright 2010 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# Template for raw text data describing registers
+# name addr[bit range inclusive] default optional enums
+########################################################################
+REGS_TMPL="""\
+########################################################################
+## address 0
+########################################################################
+sdio_bidirectional 0[7] 0 input, io
+lsb_msb_first 0[6] 0 msb, lsb
+soft_reset 0[5] 0
+sleep_mode 0[4] 0
+power_down_mode 0[3] 0
+x_1r_2r_mode 0[2] 0 2r, 1r
+pll_lock_indicator 0[1] 0
+########################################################################
+## address 1
+########################################################################
+filter_interp_rate 1[6:7] 0 1x, 2x, 4x, 8x
+modulation_mode 1[4:5] 0 none, fs_2, fs_4, fs_8
+zero_stuff_mode 1[3] 0
+mix_mode 1[2] 1 complex, real
+modulation_form 1[1] 0 e_minus_jwt, e_plus_jwt
+data_clk_pll_lock_sel 1[0] 0 pll_lock, data_clk
+########################################################################
+## address 2
+########################################################################
+signed_input_data 2[7] 0 signed, unsigned
+two_port_mode 2[6] 0 two_port, one_port
+dataclk_driver_strength 2[5] 0 weak, strong
+dataclk_invert 2[4] 0
+oneportclk_invert 2[2] 0
+iqsel_invert 2[1] 0
+iq_first 2[0] 0 i_first, q_first
+########################################################################
+## address 3
+########################################################################
+data_rate_clock_output 3[7] 0 pll_lock, spi_sdo
+pll_divide_ratio 3[0:1] 0 div1, div2, div4, div8
+########################################################################
+## address 4
+########################################################################
+pll_state 4[7] 0 off, on
+auto_cp_control 4[6] 0 auto, manual
+pll_cp_control 4[0:2] 0 50ua=0, 100ua=1, 200ua=2, 400ua=3, 800ua=7
+########################################################################
+## address 5 and 9
+########################################################################
+idac_fine_gain_adjust 5[0:7] 0
+qdac_fine_gain_adjust 9[0:7] 0
+########################################################################
+## address 6 and A
+########################################################################
+idac_coarse_gain_adjust 6[0:3] 0
+qdac_coarse_gain_adjust 0xA[0:3] 0
+########################################################################
+## address 7, 8 and B, C
+########################################################################
+idac_offset_adjust_msb 7[0:7] 0
+idac_offset_adjust_lsb 8[0:1] 0
+~idac_offset_adjust idac_offset_adjust_lsb, idac_offset_adjust_msb
+idac_ioffset_direction 8[7] 0 out_a, out_b
+qdac_offset_adjust_msb 0xB[0:7] 0
+qdac_offset_adjust_lsb 0xC[0:1] 0
+~qdac_offset_adjust qdac_offset_adjust_lsb, qdac_offset_adjust_msb
+qdac_ioffset_direction 0xC[7] 0 out_a, out_b
+"""
+
+########################################################################
+# Template for methods in the body of the struct
+########################################################################
+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
+ break;
+ #end for
+ }
+ return reg;
+}
+
+boost::uint16_t get_write_reg(boost::uint8_t addr){
+ return (boost::uint16_t(addr) << 8) | get_reg(addr);
+}
+
+boost::uint16_t get_read_reg(boost::uint8_t addr){
+ return (boost::uint16_t(addr) << 8) | (1 << 7);
+}
+"""
+
+if __name__ == '__main__':
+ import common; common.generate(
+ name='ad9777_regs',
+ regs_tmpl=REGS_TMPL,
+ body_tmpl=BODY_TMPL,
+ file=__file__,
+ )
diff --git a/host/lib/ic_reg_maps/gen_ad9862_regs.py b/host/lib/ic_reg_maps/gen_ad9862_regs.py
new file mode 100755
index 000000000..00340224c
--- /dev/null
+++ b/host/lib/ic_reg_maps/gen_ad9862_regs.py
@@ -0,0 +1,246 @@
+#!/usr/bin/env python
+#
+# Copyright 2010 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# Template for raw text data describing registers
+# name addr[bit range inclusive] default optional enums
+########################################################################
+REGS_TMPL="""\
+########################################################################
+## General
+########################################################################
+sdio_bidir 0[7] 0 sdio_sdo, sdio
+lsb_first 0[6] 0 msb, lsb
+soft_reset 0[5] 0
+########################################################################
+## Rx Power Down
+########################################################################
+vref_diff_pd 1[7] 0
+vref_pd 1[6] 0
+rx_digital_pd 1[5] 0
+rx_channel_b_pd 1[4] 0
+rx_channel_a_pd 1[3] 0
+buffer_b_pd 1[2] 0
+buffer_a_pd 1[1] 0
+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
+########################################################################
+## Rx Misc
+########################################################################
+hs_duty_cycle 4[2] 0
+shared_ref 4[1] 0
+clk_duty 4[0] 0
+########################################################################
+## RX I/F (INTERFACE)
+########################################################################
+three_state 5[4] 0
+rx_retime 5[3] 0 clkout1, clkout2
+rx_twos_comp 5[2] 0
+inv_rxsync 5[1] 0
+mux_out 5[0] 0 rx_mux_mode=1, dual_port_mode=0
+########################################################################
+## RX Digital
+########################################################################
+two_channel 6[3] 1 rx_b_dis, both_enb
+rx_keep_ve 6[2] 0 pass_pos, pass_neg
+rx_hilbert 6[1] 0 dis, enb
+decimate 6[0] 0 dis, enb
+########################################################################
+## TX Power Down
+########################################################################
+alt_timing_mode 8[5] 0
+txoff_enable 8[4] 0
+tx_digital_pd 8[3] 0
+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
+tx_pga_gain 16[0:7] 0
+########################################################################
+## Tx Misc
+########################################################################
+tx_slave_enable 17[1] 0
+tx_pga_mode 17[0] 0 normal, fast
+########################################################################
+## Tx IF (INTERFACE)
+########################################################################
+tx_retime 18[6] 1 clkout1=1, clkout2=0
+qi_order 18[5] 0 iq, qi
+inv_txsync 18[4] 0
+tx_twos_comp 18[3] 0
+inverse_samp 18[2] 0 rise, fall
+edges 18[1] 0 normal, both
+interleaved 18[0] 0 single, interleaved
+########################################################################
+## TX Digital
+########################################################################
+two_data_paths 19[4] 0 single, both
+tx_keep_ve 19[3] 0 pass_pos, pass_neg
+tx_hilbert 19[2] 0 dis, enb
+interp 19[0:1] 0 1, 2, 4
+########################################################################
+## TX Modulator
+########################################################################
+neg_fine_tune 20[5] 0 pos_shift, neg_shift
+fine_mode 20[4] 0 bypass, nco
+real_mix_mode 20[3] 0 complex, real
+neg_coarse_tune 20[2] 0 pos_shift, neg_shift
+coarse_mod 20[0:1] 0 bypass, fdac_4, fdac_8
+########################################################################
+## NCO Tuning Word
+########################################################################
+ftw_7_0 21[0:7] 0
+ftw_15_8 22[0:7] 0
+ftw_23_16 23[0:7] 0
+########################################################################
+## DLL
+########################################################################
+input_clk_ctrl 24[6] 0 internal, external
+adc_div2 24[5] 0 normal, div2
+dll_mult 24[3:4] 0 1, 2, 4
+dll_pd 24[2] 0
+dll_mode 24[0] 0 slow, fast
+########################################################################
+## Clock Out
+########################################################################
+clkout2_div_factor 25[6:7] 0 1, 2, 4, 8
+inv2 25[5] 0 normal, inverted
+inv1 25[1] 0 normal, inverted
+dis2 25[4] 0 enb, dis
+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
+########################################################################
+## 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
+########################################################################
+## Aux ADC Clock
+########################################################################
+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
+########################################################################
+## Aux DAC Update
+########################################################################
+aux_dac_slave_enable 39[7] 0
+aux_dacupdate_c 39[2] 0
+aux_dacupdate_b 39[1] 0
+aux_dacupdate_a 39[0] 0
+########################################################################
+## AUX DAC Power Down
+########################################################################
+aux_dac_pd_a 40[2] 0
+aux_dac_pd_b 40[1] 0
+aux_dac_pd_c 40[0] 0
+########################################################################
+## AUX DAC Control
+########################################################################
+aux_dac_invert_a 41[2] 0
+aux_dac_invert_b 41[1] 0
+aux_dac_invert_c 41[0] 0
+########################################################################
+## Sig Delt
+########################################################################
+sig_delt_3_0 42[4:7] 0
+sig_delt_11_4 43[0:7] 0
+########################################################################
+## ADC Low Power
+########################################################################
+rx_low_power_mode_r49 49[0:7] 0
+rx_low_power_mode_r50 50[0:7] 0
+########################################################################
+## Chip ID
+########################################################################
+chip_id 63[0:7] 0
+"""
+
+########################################################################
+# Header and Source templates below
+########################################################################
+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
+ break;
+ #end for
+ }
+ 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
+ break;
+ #end for
+ }
+}
+
+boost::uint16_t get_write_reg(boost::uint8_t addr){
+ return (boost::uint16_t(addr) << 8) | get_reg(addr);
+}
+
+boost::uint16_t get_read_reg(boost::uint8_t addr){
+ return (boost::uint16_t(addr) << 8) | (1 << 15);
+}
+"""
+
+if __name__ == '__main__':
+ import common; common.generate(
+ name='ad9862_regs',
+ regs_tmpl=REGS_TMPL,
+ body_tmpl=BODY_TMPL,
+ file=__file__,
+ )
diff --git a/host/lib/ic_reg_maps/gen_adf4350_regs.py b/host/lib/ic_reg_maps/gen_adf4350_regs.py
new file mode 100755
index 000000000..fce2f569b
--- /dev/null
+++ b/host/lib/ic_reg_maps/gen_adf4350_regs.py
@@ -0,0 +1,122 @@
+#!/usr/bin/env python
+#
+# Copyright 2010 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# Template for raw text data describing registers
+# name addr[bit range inclusive] default optional enums
+########################################################################
+REGS_TMPL="""\
+########################################################################
+## address 0
+########################################################################
+frac_12_bit 0[3:14] 0
+int_16_bit 0[15:30] 0x23
+##reserved 0[31] 0
+########################################################################
+## address 1
+########################################################################
+mod_12_bit 1[3:14] 0xfff
+phase_12_bit 1[15:26] 0
+prescaler 1[27] 0 4_5, 8_9
+##reserved 1[28:31] 0
+########################################################################
+## address 2
+########################################################################
+counter_reset 2[3] 0 disabled, enabled
+cp_three_state 2[4] 0 disabled, enabled
+power_down 2[5] 0 disabled, enabled
+pd_polarity 2[6] 1 negative, positive
+ldp 2[7] 0 10ns, 6ns
+ldf 2[8] 0 frac_n, int_n
+#set $current_setting_enums = ', '.join(map(lambda x: '_'.join(("%0.2fma"%(round(x*31.27 + 31.27)/100)).split('.')), range(0,16)))
+charge_pump_current 2[9:12] 5 $current_setting_enums
+double_buffer 2[13] 0 disabled, enabled
+r_counter_10_bit 2[14:23] 0
+reference_divide_by_2 2[24] 1 disabled, enabled
+reference_doubler 2[25] 0 disabled, enabled
+muxout 2[26:28] 1 3state, dvdd, dgnd, rdiv, ndiv, analog_ld, dld, reserved
+low_noise_and_spur 2[29:30] 3 low_noise, reserved0, reserved1, low_spur
+##reserved 2[31] 0
+########################################################################
+## address 3
+########################################################################
+clock_divider_12_bit 3[3:14] 0
+clock_div_mode 3[15:16] 1 clock_divider_off, fast_lock, resync_enable, reserved
+##reserved 3[17] 0
+cycle_slip_reduction 3[18] 0 disabled, enabled
+##reserved 3[19:20] 0
+##reserved 3[21:31] 0
+########################################################################
+## address 4
+########################################################################
+output_power 4[3:4] 3 m4dbm, m1dbm, 2dbm, 5dbm
+rf_output_enable 4[5] 1 disabled, enabled
+aux_output_power 4[6:7] 0 m4dbm, m1dbm, 2dbm, 5dbm
+aux_output_enable 4[8] 0 disabled, enabled
+aux_output_select 4[9] 1 divided, fundamental
+mute_till_lock_detect 4[10] 0 mute_disabled, mute_enabled
+vco_power_down 4[11] 0 vco_powered_up, vco_powered_down
+band_select_clock_div 4[12:19] 0
+rf_divider_select 4[20:22] 0 div1, div2, div4, div8, div16
+feedback_select 4[23] 1 divided, fundamental
+##reserved 4[24:31] 0
+########################################################################
+## address 5
+########################################################################
+##reserved 5[3:18] 0
+##reserved 5[19:20] 0
+##reserved 5[21] 0
+ld_pin_mode 5[22:23] 1 low0, dld, low, high
+##reserved 5[24:31] 0
+"""
+
+########################################################################
+# Template for methods in the body of the struct
+########################################################################
+BODY_TMPL="""\
+enum addr_t{
+ ADDR_R0 = 0,
+ ADDR_R1 = 1,
+ ADDR_R2 = 2,
+ ADDR_R3 = 3,
+ ADDR_R4 = 4,
+ ADDR_R5 = 5
+};
+
+boost::uint32_t get_reg(boost::uint8_t addr){
+ boost::uint32_t reg = addr & 0x7;
+ switch(addr){
+ #for $addr in range(5+1)
+ case $addr:
+ #for $reg in filter(lambda r: r.get_addr() == addr, $regs)
+ reg |= (boost::uint32_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift();
+ #end for
+ break;
+ #end for
+ }
+ return reg;
+}
+"""
+
+if __name__ == '__main__':
+ import common; common.generate(
+ name='adf4350_regs',
+ regs_tmpl=REGS_TMPL,
+ body_tmpl=BODY_TMPL,
+ file=__file__,
+ )
diff --git a/host/lib/ic_reg_maps/gen_adf4351_regs.py b/host/lib/ic_reg_maps/gen_adf4351_regs.py
new file mode 100755
index 000000000..607b2979d
--- /dev/null
+++ b/host/lib/ic_reg_maps/gen_adf4351_regs.py
@@ -0,0 +1,138 @@
+#!/usr/bin/env python
+#
+# Copyright 2010 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# Template for raw text data describing registers
+# name addr[bit range inclusive] default optional enums
+########################################################################
+REGS_TMPL="""\
+########################################################################
+## address 0
+########################################################################
+frac_12_bit 0[3:14] 0
+int_16_bit 0[15:30] 0x23
+##reserved 0[31] 0
+########################################################################
+## address 1
+########################################################################
+mod_12_bit 1[3:14] 0xfff
+phase_12_bit 1[15:26] 0
+prescaler 1[27] 0 4_5, 8_9
+phase_adjust 1[28] 0
+##reserved 1[29:31] 0
+########################################################################
+## address 2
+########################################################################
+counter_reset 2[3] 0 disabled, enabled
+cp_three_state 2[4] 0 disabled, enabled
+power_down 2[5] 0 disabled, enabled
+pd_polarity 2[6] 1 negative, positive
+ldp 2[7] 0 10ns, 6ns
+ldf 2[8] 0 frac_n, int_n
+#set $current_setting_enums = ', '.join(map(lambda x: '_'.join(("%0.2fma"%(round(x*31.27 + 31.27)/100)).split('.')), range(0,16)))
+charge_pump_current 2[9:12] 5 $current_setting_enums
+double_buffer 2[13] 0 disabled, enabled
+r_counter_10_bit 2[14:23] 0
+reference_divide_by_2 2[24] 1 disabled, enabled
+reference_doubler 2[25] 0 disabled, enabled
+muxout 2[26:28] 1 3state, dvdd, dgnd, rdiv, ndiv, analog_ld, dld, reserved
+low_noise_and_spur 2[29:30] 3 low_noise, reserved0, reserved1, low_spur
+##reserved 2[31] 0
+########################################################################
+## address 3
+########################################################################
+clock_divider_12_bit 3[3:14] 0
+clock_div_mode 3[15:16] 0 clock_divider_off, fast_lock, resync_enable, reserved
+##reserved 3[17] 0
+cycle_slip_reduction 3[18] 0 disabled, enabled
+##reserved 3[19:20] 0
+charge_cancel 3[21] 0
+anti_backlash_pulse 3[22] 0 6ns, 3ns
+band_select_mode 3[23] 0 low, high
+##reserved 3[24:31] 0
+########################################################################
+## address 4
+########################################################################
+output_power 4[3:4] 3 m4dbm, m1dbm, 2dbm, 5dbm
+rf_output_enable 4[5] 1 disabled, enabled
+aux_output_power 4[6:7] 0 m4dbm, m1dbm, 2dbm, 5dbm
+aux_output_enable 4[8] 0 disabled, enabled
+aux_output_select 4[9] 1 divided, fundamental
+mute_till_lock_detect 4[10] 0 mute_disabled, mute_enabled
+vco_power_down 4[11] 0 vco_powered_up, vco_powered_down
+band_select_clock_div 4[12:19] 0
+rf_divider_select 4[20:22] 5 div1, div2, div4, div8, div16, div32, div64
+feedback_select 4[23] 1 divided, fundamental
+##reserved 4[24:31] 0
+########################################################################
+## address 5
+########################################################################
+##reserved 5[3:18] 0
+##reserved 5[19:20] 0
+##reserved 5[21] 0
+ld_pin_mode 5[22:23] 1 low0, dld, low, high
+##reserved 5[24:31] 0
+"""
+
+########################################################################
+# Template for methods in the body of the struct
+########################################################################
+BODY_TMPL="""\
+enum addr_t{
+ ADDR_R0 = 0,
+ ADDR_R1 = 1,
+ ADDR_R2 = 2,
+ ADDR_R3 = 3,
+ ADDR_R4 = 4,
+ ADDR_R5 = 5
+};
+
+boost::uint32_t get_reg(boost::uint8_t addr){
+ boost::uint32_t reg = 0;
+ switch(addr){
+ #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs)))
+ case $addr:
+ #for $reg in filter(lambda r: r.get_addr() == addr, $regs)
+ reg |= (boost::uint32_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift();
+ #end for
+ break;
+ #end for
+ }
+ return reg;
+}
+
+void set_reg(boost::uint8_t addr, boost::uint32_t reg){
+ switch(addr){
+ #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs)))
+ case $addr:
+ #for $reg in filter(lambda r: r.get_addr() == addr, $regs)
+ $reg.get_name() = $(reg.get_type())((reg >> $reg.get_shift()) & $reg.get_mask());
+ #end for
+ break;
+ #end for
+ }
+}
+"""
+
+if __name__ == '__main__':
+ import common; common.generate(
+ name='adf4351_regs',
+ regs_tmpl=REGS_TMPL,
+ body_tmpl=BODY_TMPL,
+ file=__file__,
+ )
diff --git a/host/lib/ic_reg_maps/gen_adf4360_regs.py b/host/lib/ic_reg_maps/gen_adf4360_regs.py
new file mode 100755
index 000000000..3fd8707a7
--- /dev/null
+++ b/host/lib/ic_reg_maps/gen_adf4360_regs.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python
+#
+# Copyright 2010 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# Template for raw text data describing registers
+# name addr[bit range inclusive] default optional enums
+########################################################################
+REGS_TMPL="""\
+########################################################################
+## address 0
+########################################################################
+core_power_level 0[2:3] 0 5ma, 10ma, 15ma, 20ma
+counter_operation 0[4] 0 normal, reset
+muxout_control 0[5:7] 0 3state, dld, ndiv, dvdd, rdiv, nchan_od_ld, sdo, dgnd
+phase_detector_polarity 0[8] 0 neg, pos
+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
+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
+########################################################################
+## address 2
+########################################################################
+a_counter 2[2:6] 0
+b_counter 2[8:20] 0
+cp_gain_1 2[21] 0 set1, set2
+divide_by_2_output 2[22] 0 fund, div2
+divide_by_2_prescaler 2[23] 0 fund, div2
+########################################################################
+## address 1
+########################################################################
+r_counter 1[2:15] 0
+ablpw 1[16:17] 0 3_0ns, 1_3ns, 6_0ns
+lock_detect_precision 1[18] 0 3cycles, 5cycles
+test_mode_bit 1[19] 0
+band_select_clock_div 1[20:21] 0 1, 2, 4, 8
+"""
+
+########################################################################
+# Template for methods in the body of the struct
+########################################################################
+BODY_TMPL="""\
+enum addr_t{
+ ADDR_CONTROL = 0,
+ ADDR_NCOUNTER = 2,
+ ADDR_RCOUNTER = 1
+};
+
+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
+ break;
+ #end for
+ }
+ return reg;
+}
+"""
+
+if __name__ == '__main__':
+ import common; common.generate(
+ name='adf4360_regs',
+ regs_tmpl=REGS_TMPL,
+ body_tmpl=BODY_TMPL,
+ file=__file__,
+ )
diff --git a/host/lib/ic_reg_maps/gen_ads62p44_regs.py b/host/lib/ic_reg_maps/gen_ads62p44_regs.py
new file mode 100755
index 000000000..f0a84d940
--- /dev/null
+++ b/host/lib/ic_reg_maps/gen_ads62p44_regs.py
@@ -0,0 +1,124 @@
+#!/usr/bin/env python
+#
+# Copyright 2010 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# Template for raw text data describing registers
+# name addr[bit range inclusive] default optional enums
+########################################################################
+REGS_TMPL="""\
+########################################################################
+## address 0
+########################################################################
+reset 0[1] 0
+serial_readout 0[0] 0
+########################################################################
+## address 16
+########################################################################
+clkout_strength 16[6:7] 0 weaker=1, default=0, stronger=3
+########################################################################
+## address 17
+########################################################################
+dataout_strength 17[0:1] 0 weaker=1, default=0, stronger=3, maximum=2
+lvds_current 17[2:3] 0 3_5ma, 2_5ma, 4_5ma, 1_75ma
+lvds_current_double 17[4:5] 0 default, dblclk, dbldataclock
+########################################################################
+## address 18
+########################################################################
+lvds_clk_term 18[0:2] 0 none, 300, 180, 110, 150, 100, 81, 60
+lvds_data_term 18[3:5] 0 none, 300, 180, 110, 150, 100, 81, 60
+########################################################################
+## address 19
+########################################################################
+offset_freeze 19[4] 0
+########################################################################
+## address 20
+########################################################################
+power_down 20[0:2] 0 normal, a_dis, b_dis, ab_dis, global_pd, a_sby, b_sby, mux
+ref_select 20[3] 0 internal, external
+coarse_gain 20[4] 0 0db, 3_5db
+output_interface 20[5] 0 cmos, lvds
+override 20[7] 0
+########################################################################
+## address 22
+########################################################################
+test_patterns 22[0:2] 0 normal, zeros, ones, toggle, ramp, custom
+lvds_bytewise 22[3] 0
+data_format 22[4] 0 twos_complement, binary
+########################################################################
+## address 23
+########################################################################
+fine_gain 23[0:3] 0
+########################################################################
+## address 24 and 25
+########################################################################
+custom_low 24[0:7] 0
+custom_high 25[0:5] 0
+########################################################################
+## address 26
+########################################################################
+gain_correction 26[0:3] 0
+offset_tc 26[4:6] 0 1_1s, 0_55s, 0_27s, 0_13s, 2_15s, 4_3s
+low_latency 26[7] 0
+########################################################################
+## address 27
+########################################################################
+decimation 27[0:2] 0 decimate_2, decimate_4, decimate_1, decimate_8
+odd_tap_enable 27[3] 0
+filter_enable 27[4] 0
+filter_coeff_sel 27[5] 0 predefined, userdefined
+offset_enable 27[7] 0
+########################################################################
+## address 29
+########################################################################
+decimation_filter_bands 29[0:1] 0
+"""
+
+########################################################################
+# Template for methods in the body of the struct
+########################################################################
+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
+ break;
+ #end for
+ }
+ return reg;
+}
+
+boost::uint16_t get_write_reg(boost::uint8_t addr){
+ return (boost::uint16_t(addr) << 8) | get_reg(addr);
+}
+
+boost::uint16_t get_read_reg(boost::uint8_t addr){
+ return (boost::uint16_t(addr) << 8) | (1 << 7);
+}
+"""
+
+if __name__ == '__main__':
+ import common; common.generate(
+ name='ads62p44_regs',
+ regs_tmpl=REGS_TMPL,
+ body_tmpl=BODY_TMPL,
+ file=__file__,
+ )
diff --git a/host/lib/ic_reg_maps/gen_max2112_regs.py b/host/lib/ic_reg_maps/gen_max2112_regs.py
new file mode 100755
index 000000000..c2fc4e3e2
--- /dev/null
+++ b/host/lib/ic_reg_maps/gen_max2112_regs.py
@@ -0,0 +1,181 @@
+#!/usr/bin/env python
+#
+# Copyright 2010 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# Template for raw text data describing write registers
+# name addr[bit range inclusive] default optional enums
+########################################################################
+WRITE_REGS_TMPL="""\
+########################################################################
+## Note: offsets given from perspective of data bits (excludes address)
+########################################################################
+##
+########################################################################
+## N-Divider MSB (0) Write
+########################################################################
+frac 0[7] 1 invalid, frac
+n_divider_msb 0[0:6] 0
+########################################################################
+## N-Divider LSB (1) Write
+########################################################################
+n_divider_lsb 1[0:7] 0x23
+~n_divider n_divider_lsb, n_divider_msb
+########################################################################
+## Charge Pump (2) Write
+########################################################################
+cpmp 2[6:7] 0
+cplin 2[4:5] 1
+f_divider_mmsb 2[0:3] 0x2
+########################################################################
+## F-Divider MSB (3) Write
+########################################################################
+f_divider_msb 3[0:7] 0xF6
+########################################################################
+## F-Divider LSB (4) Write
+########################################################################
+f_divider_lsb 4[0:7] 0x84
+~f_divider f_divider_lsb, f_divider_msb, f_divider_mmsb
+########################################################################
+## 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
+r_divider 5[0:4] 1
+########################################################################
+## PLL (6) Write
+########################################################################
+d24 6[7] 1 div2, div4 ## div2 for LO <= 1125M, div4 > 1125M
+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
+########################################################################
+## VCO (7) Write
+########################################################################
+vco 7[3:7] 0x19
+vas 7[2] 1 disabled, enabled
+adl 7[1] 1 disabled, enabled
+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
+########################################################################
+## Control (9) Write
+########################################################################
+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
+########################################################################
+## Shutdown (0xA) Write
+########################################################################
+##reserved 0xA[7] 0
+pll_shutdown 0xA[6] 0 normal, shutdown
+div_shutdown 0xA[5] 0 normal, shutdown
+vco_shutdown 0xA[4] 0 normal, shutdown
+bb_shutdown 0xA[3] 0 normal, shutdown
+rfmix_shutdown 0xA[2] 0 normal, shutdown
+rfvga_shutdown 0xA[1] 0 normal, shutdown
+fe_shutdown 0xA[0] 0 normal, shutdown
+########################################################################
+## Test (0xB) Write
+########################################################################
+cptst 0xB[5:7] 0
+##reserved 0xB[4] 0
+turbo 0xB[3] 1
+ld_mux 0xB[0:2] 0 refout=0, invalid
+"""
+
+########################################################################
+# Template for raw text data describing read registers
+# name addr[bit range inclusive] default optional enums
+########################################################################
+READ_REGS_TMPL="""\
+########################################################################
+## Status Byte-1 (0xC) Read
+########################################################################
+por 0xC[7] 0 read, reset
+vasa 0xC[6] 0 vas_fail, vas_win
+vase 0xC[5] 0 active, inactive
+ld 0xC[4] 0 unlocked, locked
+##reserved 0xC[0:3] 0
+########################################################################
+## Status Byte-2 (0xD) Read
+########################################################################
+vcosbr 0xD[3:7] 0 ## vco band readback
+adc 0xD[0:2] 0 ool0, lock0, vaslock0, vaslock1, vaslock2, vaslock3, lock1, ool1
+"""
+
+########################################################################
+# Template for methods in the body of the struct
+########################################################################
+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
+ break;
+ #end for
+ }
+ 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
+ break;
+ #end for
+ }
+}
+"""
+
+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',
+ regs_tmpl=WRITE_REGS_TMPL,
+ body_tmpl=BODY_TMPL,
+ file=__file__,
+ )
+
+ import common; common.generate(
+ name='max2112_read_regs',
+ regs_tmpl=READ_REGS_TMPL,
+ body_tmpl=BODY_TMPL,
+ file=__file__,
+ append=True,
+ )
diff --git a/host/lib/ic_reg_maps/gen_max2118_regs.py b/host/lib/ic_reg_maps/gen_max2118_regs.py
new file mode 100755
index 000000000..506fbaec8
--- /dev/null
+++ b/host/lib/ic_reg_maps/gen_max2118_regs.py
@@ -0,0 +1,126 @@
+#!/usr/bin/env python
+#
+# Copyright 2010 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# Template for raw text data describing write registers
+# name addr[bit range inclusive] default optional enums
+########################################################################
+WRITE_REGS_TMPL="""\
+########################################################################
+## Note: offsets given from perspective of data bits (excludes address)
+########################################################################
+##
+########################################################################
+## N-Divider MSB (0) Write
+########################################################################
+div2 0[7] 0 div4, div2
+n_divider_msb 0[0:6] 3
+########################################################################
+## N-Divider LSB (1) Write
+########################################################################
+n_divider_lsb 1[0:7] 0xB6
+~n_divider n_divider_lsb, n_divider_msb
+########################################################################
+## 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
+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
+########################################################################
+## 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
+########################################################################
+## 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
+"""
+
+########################################################################
+# Template for raw text data describing read registers
+# name addr[bit range inclusive] default optional enums
+########################################################################
+READ_REGS_TMPL="""\
+########################################################################
+## Status (0) Read
+########################################################################
+pwr 0[6] 0 not_reset, reset
+adc 0[2:4] 0 ## VCO tuning voltage, Lock Status
+########################################################################
+## I/Q Filter DAC (1) Read
+########################################################################
+filter_dac 1[0:6] 0 ## I/Q Filter tuning DAC, current
+"""
+
+########################################################################
+# Template for methods in the body of the struct
+########################################################################
+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
+ break;
+ #end for
+ }
+ 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
+ break;
+ #end for
+ }
+}
+"""
+
+if __name__ == '__main__':
+ import common; common.generate(
+ name='max2118_write_regs',
+ regs_tmpl=WRITE_REGS_TMPL,
+ body_tmpl=BODY_TMPL,
+ file=__file__,
+ )
+
+ import common; common.generate(
+ name='max2118_read_regs',
+ regs_tmpl=READ_REGS_TMPL,
+ body_tmpl=BODY_TMPL,
+ file=__file__,
+ append=True,
+ )
diff --git a/host/lib/ic_reg_maps/gen_max2829_regs.py b/host/lib/ic_reg_maps/gen_max2829_regs.py
new file mode 100755
index 000000000..383131c18
--- /dev/null
+++ b/host/lib/ic_reg_maps/gen_max2829_regs.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python
+#
+# Copyright 2010 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# Template for raw text data describing registers
+# name addr[bit range inclusive] default optional enums
+########################################################################
+REGS_TMPL="""\
+########################################################################
+## Note: offsets given from perspective of data bits (excludes address)
+########################################################################
+##
+########################################################################
+## Standby (2)
+########################################################################
+_set_to_1_2_0 2[0] 1
+_set_to_1_2_1 2[1] 1
+_set_to_1_2_2 2[2] 1
+pa_bias_dac 2[10] 0
+voltage_ref 2[11] 0
+_set_to_1_2_12 2[12] 1
+mimo_select 2[13] 0 normal, mimo
+########################################################################
+## Integer Divider Ratio (3)
+########################################################################
+int_div_ratio_word 3[0:7] 0xa2
+frac_div_ratio_lsb 3[12:13] 0
+########################################################################
+## Fractional Divider Ratio (4)
+########################################################################
+frac_div_ratio_msb 4[0:13] 0
+########################################################################
+## Band Select and PLL (5)
+########################################################################
+band_select 5[0] 0 2_4ghz, 5ghz
+ref_divider 5[1:3] 1
+pll_cp_select 5[5] 1 2ma, 4ma
+band_select_802_11a 5[6] 0 4_9ghz_to_5_35ghz, 5_47ghz_to_5_875ghz
+vco_bandswitch 5[7] 0 disable, automatic
+vco_spi_bandswitch 5[8] 0 fsm, spi
+vco_sub_band 5[9:10] 0
+_set_to_1_5_11 5[11] 1
+_set_to_1_5_12 5[12] 1
+band_sel_mimo 5[13] 0 normal, mimo
+########################################################################
+## Calibration (6)
+########################################################################
+rx_cal_mode 6[0] 0 dis, enb
+tx_cal_mode 6[1] 0 dis, enb
+_set_to_1_6_10 6[10] 1
+iq_cal_gain 6[11:12] 3 8db, 18db, 24db, 34db
+########################################################################
+## Lowpass Filter (7)
+########################################################################
+rx_lpf_fine_adj 7[0:2] 2 90, 95, 100, 105, 110
+rx_lpf_coarse_adj 7[3:4] 1 7_5mhz, 9_5mhz, 14mhz, 18mhz
+tx_lpf_coarse_adj 7[5:6] 1 12mhz=1, 18mhz=2, 24mhz=3
+rssi_high_bw 7[11] 0 2mhz, 6mhz
+########################################################################
+## Rx Control/RSSI (8)
+########################################################################
+_set_to_1_8_0 8[0] 1
+rx_highpass 8[2] 1 100hz, 30khz
+_set_to_1_8_5 8[5] 1
+rssi_pin_fcn 8[8] 0 rssi, temp
+rssi_op_mode 8[10] 0 rssi_rxhp, enabled
+rssi_output_range 8[11] 0 low, high
+rx_vga_gain_spi 8[12] 0 io, spi
+########################################################################
+## Tx Linearity/Baseband Gain (9)
+########################################################################
+tx_baseband_gain 9[0:1] 0 0db, 2db, 3_5db, 5db
+tx_upconv_linearity 9[2:3] 0 50, 63, 78, 100
+tx_vga_linearity 9[6:7] 0 50, 63, 78, 100
+pa_driver_linearity 9[8:9] 2 50, 63, 78, 100
+tx_vga_gain_spi 9[10] 0 io, spi
+########################################################################
+## PA Bias DAC (10)
+########################################################################
+pa_bias_dac_out_curr 10[0:5] 0
+pa_bias_dac_delay 10[6:9] 0xf
+########################################################################
+## Rx Gain (11)
+########################################################################
+rx_vga_gain 11[0:4] 0x1f
+rx_lna_gain 11[5:6] 3
+########################################################################
+## Tx VGA Gain (12)
+########################################################################
+tx_vga_gain 12[0:5] 0
+"""
+
+########################################################################
+# Template for methods in the body of the struct
+########################################################################
+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
+ break;
+ #end for
+ }
+ return (boost::uint32_t(reg) << 4) | (addr & 0xf);
+}
+"""
+
+if __name__ == '__main__':
+ import common; common.generate(
+ name='max2829_regs',
+ regs_tmpl=REGS_TMPL,
+ body_tmpl=BODY_TMPL,
+ file=__file__,
+ )
diff --git a/host/lib/ic_reg_maps/gen_tda18272hnm_regs.py b/host/lib/ic_reg_maps/gen_tda18272hnm_regs.py
new file mode 100755
index 000000000..677a201de
--- /dev/null
+++ b/host/lib/ic_reg_maps/gen_tda18272hnm_regs.py
@@ -0,0 +1,521 @@
+#!/usr/bin/env python
+#
+# Copyright 2010 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# Template for raw text data describing write registers
+# name addr[bit range inclusive] default optional enums
+########################################################################
+REGS_TMPL="""\
+########################################################################
+## Note: offsets given from perspective of data bits (excludes address)
+########################################################################
+##
+########################################################################
+## ID_byte_1 (0x00) Read
+########################################################################
+##reserved as 1 0x00[7] 1
+ident_14_8 0x00[0:6] 0
+########################################################################
+## ID_byte_2 (0x01) Read
+########################################################################
+ident_7_0 0x01[0:7] 0
+##~ident ident_7_0, ident_14_8
+########################################################################
+## ID_byte_3 (0x02) Read
+########################################################################
+major_rev 0x02[4:7] 0
+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
+########################################################################
+## Thermo_byte_2 (0x04) Write
+########################################################################
+##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
+por 0x05[1] 0 read, reset
+lo_lock 0x05[0] 0 unlocked, locked
+########################################################################
+## Power_state_byte_2 (0x06) Read/Write ## Standby modes
+########################################################################
+##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
+##resevered as 0 0x06[0] 0
+## (sm, sm_pll, sm_lna) only valid values are 000, 100, 110, and 111
+########################################################################
+## Input_Power_Level_byte (0x07) Read
+########################################################################
+##reserved 0x07[7] 0
+power_level 0x07[0:6] 0 ## 40dB_Vrms to 110dB_Vrms
+## 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
+irq_xtalcal_end 0x08[5] 0 false, true
+irq_rssi_end 0x08[4] 0 false, true
+irq_localc_end 0x08[3] 0 false, true
+irq_rfcal_end 0x08[2] 0 false, true
+irq_ircal_end 0x08[1] 0 false, true
+irq_rccal_end 0x08[0] 0 false, true
+########################################################################
+## IRQ_enable (0x09) Read/Write
+########################################################################
+irq_enable 0x09[7] 1 false, true
+##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
+irq_rfcal_enable 0x09[2] 0 false, true
+irq_ircal_enable 0x09[1] 0 false, true
+irq_rccal_enable 0x09[0] 0 false, true
+########################################################################
+## IRQ_clear (0x0a) Read/Write
+########################################################################
+irq_clear 0x0a[7] 0 false, true
+##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
+irq_rfcal_clear 0x0a[2] 0 false, true
+irq_ircal_clear 0x0a[1] 0 false, true
+irq_rccal_clear 0x0a[0] 0 false, true
+########################################################################
+## IRQ_set (0x0b) Read
+########################################################################
+irq_set 0x0b[7] 0 false, true
+##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
+irq_rfcal_set 0x0b[2] 0 false, true
+irq_ircal_set 0x0b[1] 0 false, true
+irq_rccal_set 0x0b[0] 0 false, true
+########################################################################
+## AGC1_byte_1 (0x0c) Read/Write
+########################################################################
+lt_enable 0x0c[7] 0
+agc1_6_15db 0x0c[6] 1
+##reserved 0x0c[4:5] 0
+agc1_top 0x0c[0:3] 0
+########################################################################
+## AGC2_byte_1 (0x0d) Read
+########################################################################
+##reserved 0x0d[5:7] 0
+agc2_top 0x0d[0:4] 0xf
+########################################################################
+## AGCK_byte_1 (0x0e) Read/Write
+########################################################################
+agcs_up_step_assym 0x0e[6:7] 3
+agcs_up_step 0x0e[5] 1
+pulse_shaper_disable 0x0e[4] 0 vsync_pulse, 500us_pulse
+agck_step 0x0e[2:3] 0
+agck_mode 0x0e[0:1] 1 analog_tv=1, digital_tv=2
+########################################################################
+## RF_AGC_byte_1 (0x0f) Read/Write
+########################################################################
+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
+agc3_top 0x0f[0:2] 1
+########################################################################
+## IR_MIXER_byte_1 (0x10) Read/Write
+########################################################################
+##reserved 0x10[4:7] 0
+agc4_top 0x10[0:3] 1
+########################################################################
+## AGC5_byte_1 (0x11) Read/Write
+########################################################################
+##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
+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
+########################################################################
+if_hp_fc 0x13[6:7] 0 0_4mhz, 0_85mhz, 1_0mhz, 1_5mhz
+if_atsc_notch 0x13[5] 0 off, on
+lp_fc_offset 0x13[3:4] 0 0_percent, m4_percent, m8_percent, forbidden
+lp_fc 0x13[0:2] 3 1_7mhz=4, 6_0mhz=0, 7_0mhz=1, 8_0mhz=2, 10_0mhz=3
+########################################################################
+## Reference_byte (0x14) Read/Write
+########################################################################
+i2c_clock_mode 0x14[7] 0
+digital_clock 0x14[6] 1 spread_off, spread_on
+##reserved 0x14[5] 0
+xtalosc_anareg_en 0x14[4] 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)
+########################################################################
+## RF_Frequency_byte_1 (0x16) Read/Write
+########################################################################
+##reserved 0x16[4:7] 0
+rf_freq_19_16 0x16[0:3] 0
+########################################################################
+## RF_Frequency_byte_2 (0x17) Read/Write
+########################################################################
+rf_freq_15_8 0x17[0:7] 0
+########################################################################
+## RF_Frequency_byte_3 (0x18) Read/Write
+########################################################################
+rf_freq_7_0 0x18[0:7] 0
+~rf_freq rf_freq_7_0, rf_freq_15_8, rf_freq_19_16
+## RF Frequency = rf_freq (kHz)
+########################################################################
+## MSM_byte_1 (0x19) Read/Write
+########################################################################
+rssi_meas 0x19[7] 0
+rf_cal_av 0x19[6] 0
+rf_cal 0x19[5] 0
+ir_cal_loop 0x19[4] 0
+ir_cal_image 0x19[3] 0
+ir_cal_wanted 0x19[2] 0
+rc_cal 0x19[1] 0
+calc_pll 0x19[0] 0
+########################################################################
+## MSM_byte_2 (0x1a) Read
+########################################################################
+##reserved 0x1a[2:7] 0
+xtalcal_launch 0x1a[1] 0
+msm_launch 0x1a[0] 0
+########################################################################
+## PSM_byte_1 (0x1b) Read
+########################################################################
+psm_agc1 0x1b[6:7] 0
+psm_stob 0x1b[5] 0
+psmrfpoly 0x1b[4] 0
+psm_mixer 0x1b[3] 0
+psm_ifpoly 0x1b[2] 0
+psm_lodriver 0x1b[0:1] 0
+########################################################################
+## DCC_byte_1 (0x1c) Read
+########################################################################
+dcc_bypass 0x1c[7] 0
+dcc_slow 0x1c[6] 0
+dcc_psm 0x1c[5] 0
+##reserved 0x1c[0:4] 0
+########################################################################
+## FLO_Max_byte (0x1d) Read
+########################################################################
+##reserved 0x1d[6:7] 0
+fmax_lo 0x1d[0:5] 0xA
+########################################################################
+## IR_Cal_byte_1 (0x1e) Read
+########################################################################
+ir_loop 0x1e[6:7] 0
+ir_target 0x1e[3:5] 0
+ir_gstep 0x1e[0:2] 0
+########################################################################
+## IR_Cal_byte_2 (0x1f) Read
+########################################################################
+ir_corr_boost 0x1f[7] 0
+ir_freqlow_sel 0x1f[6] 0
+ir_mode_ram_store 0x1f[5] 0
+ir_freqlow 0x1f[0:4] 0
+########################################################################
+## IR_Cal_byte_3 (0x20) Read
+########################################################################
+##reserved 0x20[5:7] 0
+ir_freqmid 0x20[0:4] 0
+########################################################################
+## IR_Cal_byte_4 (0x21) Read
+########################################################################
+##reserved 0x21[5:7] 0
+coarse_ir_freqhigh 0x21[4] 0
+ir_freqhigh 0x21[0:3] 0
+########################################################################
+## Vsync_Mgt_byte (0x22) Read
+########################################################################
+pd_vsync_mgt 0x22[7] 0
+pd_ovld 0x22[6] 0
+pd_udld 0x22[5] 0
+agc_ovld_top 0x22[2:4] 0
+agc_ovld_timer 0x22[0:1] 0
+########################################################################
+## IR_MIXER_byte_2 (0x23) Read/Write
+########################################################################
+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
+if_notch 0x23[0] 1 on, off
+########################################################################
+## AGC1_byte_2 (0x24) Read
+########################################################################
+agc1_loop_off 0x24[7] 0
+agc1_do_step 0x24[5:6] 2
+force_agc1_gain 0x24[4] 0
+agc1_gain 0x24[0:3] 8
+########################################################################
+## AGC5_byte_2 (0x25) Read
+########################################################################
+agc5_loop_off 0x25[7] 0
+agc5_do_step 0x25[5:6] 0
+##reserved 0x25[4] 0
+force_agc5_gain 0x25[3] 0
+##reserved 0x25[2] 0
+agc5_gain 0x25[0:1] 2
+########################################################################
+## RF_Cal_byte_1 (0x26) Read
+########################################################################
+rfcal_offset_cprog0 0x26[6:7] 0
+rfcal_freq0 0x26[4:5] 0
+rfcal_offset_cprog1 0x26[2:3] 0
+rfcal_freq1 0x26[0:1] 0
+########################################################################
+## RF_Cal_byte_2 (0x27) Read
+########################################################################
+rfcal_offset_cprog2 0x27[6:7] 0
+rfcal_freq2 0x27[4:5] 0
+rfcal_offset_cprog3 0x27[2:3] 0
+rfcal_freq3 0x27[0:1] 0
+########################################################################
+## RF_Cal_byte_3 (0x28) Read
+########################################################################
+rfcal_offset_cprog4 0x28[6:7] 0
+rfcal_freq4 0x28[4:5] 0
+rfcal_offset_cprog5 0x28[2:3] 0
+rfcal_freq5 0x28[0:1] 0
+########################################################################
+## RF_Cal_byte_4 (0x29) Read
+########################################################################
+rfcal_offset_cprog6 0x29[6:7] 0
+rfcal_freq6 0x29[4:5] 0
+rfcal_offset_cprog7 0x29[2:3] 0
+rfcal_freq7 0x29[0:1] 0
+########################################################################
+## RF_Cal_byte_5 (0x2a) Read
+########################################################################
+rfcal_offset_cprog8 0x2a[6:7] 0
+rfcal_freq8 0x2a[4:5] 0
+rfcal_offset_cprog9 0x2a[2:3] 0
+rfcal_freq9 0x2a[0:1] 0
+########################################################################
+## RF_Cal_byte_6 (0x2b) Read
+########################################################################
+rfcal_offset_cprog10 0x2b[6:7] 0
+rfcal_freq10 0x2b[4:5] 0
+rfcal_offset_cprog11 0x2b[2:3] 0
+rfcal_freq11 0x2b[0:1] 0
+########################################################################
+## RF_Filter_byte_1 (0x2c) Read
+########################################################################
+rf_filter_bypass 0x2c[7] 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
+rf_filter_band 0x2c[0:1] 0
+########################################################################
+## RF_Filter_byte_2 (0x2d) Read
+########################################################################
+rf_filter_cap 0x2d[0:7] 0
+########################################################################
+## RF_Filter_byte_3 (0x2e) Read
+########################################################################
+agc2_do_step 0x2e[6:7] 2
+gain_taper 0x2e[0:5] 0
+########################################################################
+## RF_Band_Pass_Filter_byte (0x2f) Read
+########################################################################
+rf_bpf_bypass 0x2f[7] 0
+##reserved 0x2f[3:6] 0
+rf_bpf 0x2f[0:2] 0
+########################################################################
+## CP_Current_byte (0x30) Read
+########################################################################
+##reserved 0x30[7] 0
+n_cp_current 0x30[0:6] 0x68
+########################################################################
+## AGC_Det_Out_byte (0x31) Read
+########################################################################
+up_agc5 0x31[7] 0
+do_agc5 0x31[6] 0
+up_agc4 0x31[5] 0
+do_agc4 0x31[4] 0
+up_agc2 0x31[3] 0
+do_agc2 0x31[2] 0
+up_agc1 0x31[1] 0
+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
+agc2_gain_read 0x32[4:5] 3 m11db, m8db, m5db, m2db
+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
+########################################################################
+## 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
+########################################################################
+## Power_byte_1 (0x35) Read
+########################################################################
+rssi 0x35[0:7] 0
+########################################################################
+## Power_byte_2 (0x36) Read
+########################################################################
+##reserved 0x36[6:7] 0
+rssi_av 0x36[5] 0
+##reserved 0x36[4] 0
+rssi_cap_reset_en 0x36[3] 1
+rssi_cap_val 0x36[2] 1
+rssi_ck_speed 0x36[1] 0
+rssi_dicho_not 0x36[0] 1
+########################################################################
+## Misc_byte_1 (0x37) Read/Write
+########################################################################
+rfcal_phi2 0x37[6:7] 1
+dds_polarity 0x37[5] 0
+rfcal_deltagain 0x37[1:4] 1
+irq_polarity 0x37[0] 0 raised_vcc, raised_low
+########################################################################
+## rfcal_log_1 (0x38) Read
+########################################################################
+rfcal_log_1 0x38[0:7] 0
+########################################################################
+## rfcal_log_2 (0x39) Read
+########################################################################
+rfcal_log_2 0x39[0:7] 0
+########################################################################
+## rfcal_log_3 (0x3a) Read
+########################################################################
+rfcal_log_3 0x3a[0:7] 0
+########################################################################
+## rfcal_log_4 (0x3b) Read
+########################################################################
+rfcal_log_4 0x3b[0:7] 0
+########################################################################
+## rfcal_log_5 (0x3c) Read
+########################################################################
+rfcal_log_5 0x3c[0:7] 0
+########################################################################
+## rfcal_log_6 (0x3d) Read
+########################################################################
+rfcal_log_6 0x3d[0:7] 0
+########################################################################
+## rfcal_log_7 (0x3e) Read
+########################################################################
+rfcal_log_7 0x3e[0:7] 0
+########################################################################
+## rfcal_log_8 (0x3f) Read
+########################################################################
+rfcal_log_8 0x3f[0:7] 0
+########################################################################
+## rfcal_log_9 (0x40) Read
+########################################################################
+rfcal_log_9 0x40[0:7] 0
+########################################################################
+## rfcal_log_10 (0x41) Read
+########################################################################
+rfcal_log_10 0x41[0:7] 0
+########################################################################
+## rfcal_log_11 (0x42) Read
+########################################################################
+rfcal_log_11 0x42[0:7] 0
+########################################################################
+## rfcal_log_12 (0x43) Read
+########################################################################
+rfcal_log_12 0x43[0:7] 0
+##
+##
+########################################################################
+## FORBIDDEN ACCESS to 0x50-0x67 and 0xFE-0xFF
+########################################################################
+########################################################################
+## xtal_cal_dac (0x65) Write
+########################################################################
+magic 0x43[7] 1 untouched, xtal_cal_dac
+"""
+
+########################################################################
+# Template for methods in the body of the struct
+########################################################################
+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
+ break;
+ #end for
+ }
+ 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
+ break;
+ #end for
+ }
+}
+"""
+
+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',
+ regs_tmpl=REGS_TMPL,
+ body_tmpl=BODY_TMPL,
+ file=__file__,
+ )
diff --git a/host/lib/ic_reg_maps/gen_tuner_4937di5_regs.py b/host/lib/ic_reg_maps/gen_tuner_4937di5_regs.py
new file mode 100644
index 000000000..73f7aa3db
--- /dev/null
+++ b/host/lib/ic_reg_maps/gen_tuner_4937di5_regs.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python
+#
+# Copyright 2010 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# Template for raw text data describing registers
+# name addr[bit range inclusive] default optional enums
+########################################################################
+REGS_TMPL="""\
+########################################################################
+## Note: offsets given from perspective of data bits (excludes address)
+########################################################################
+## Divider byte 1
+########################################################################
+db1 0[0:6] 0x00
+########################################################################
+## Divider byte 2
+########################################################################
+db2 1[0:7] 0x00
+########################################################################
+## Control byte 1
+########################################################################
+cb7 2[7] 0x01
+cp 2[6] 0x00 low,high
+os 2[0] 0x00 on,off
+rs 2[1:2] 0x00 d512=3,d640=0,d1024=1
+test 2[3:5] 0x01 normal=0x01,cpoff=0x02,cpsink=0x06,cpsrc=0x07,cptest1=0x04,cptest2=0x05
+########################################################################
+## Control byte 2
+########################################################################
+bandsel 3[4:7] 0x03 uhf=0x03,vhfhi=0x09,vhflo=0x0a
+power 3[3] 0x00 on,off
+"""
+
+########################################################################
+# Template for methods in the body of the struct
+########################################################################
+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
+ break;
+ #end for
+ }
+ return boost::uint8_t(reg);
+}
+
+"""
+
+if __name__ == '__main__':
+ import common; common.generate(
+ name='tuner_4937di5_regs',
+ regs_tmpl=REGS_TMPL,
+ body_tmpl=BODY_TMPL,
+ file=__file__,
+ )
diff --git a/host/lib/property_tree.cpp b/host/lib/property_tree.cpp
new file mode 100644
index 000000000..50e82a6ba
--- /dev/null
+++ b/host/lib/property_tree.cpp
@@ -0,0 +1,181 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/property_tree.hpp>
+#include <uhd/types/dict.hpp>
+#include <boost/foreach.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/make_shared.hpp>
+#include <iostream>
+
+using namespace uhd;
+
+/***********************************************************************
+ * Helper function to iterate through paths
+ **********************************************************************/
+#include <boost/tokenizer.hpp>
+#define path_tokenizer(path) \
+ boost::tokenizer<boost::char_separator<char> > \
+ (path, boost::char_separator<char>("/"))
+
+/***********************************************************************
+ * Property path implementation wrapper
+ **********************************************************************/
+fs_path::fs_path(void): std::string(){}
+fs_path::fs_path(const char *p): std::string(p){}
+fs_path::fs_path(const std::string &p): std::string(p){}
+
+std::string fs_path::leaf(void) const{
+ const size_t pos = this->rfind("/");
+ if (pos == std::string::npos) return *this;
+ return this->substr(pos+1);
+}
+
+fs_path fs_path::branch_path(void) const{
+ const size_t pos = this->rfind("/");
+ if (pos == std::string::npos) return *this;
+ return fs_path(this->substr(0, pos));
+}
+
+fs_path uhd::operator/(const fs_path &lhs, const fs_path &rhs){
+ //strip trailing slash on left-hand-side
+ if (not lhs.empty() and *lhs.rbegin() == '/'){
+ return fs_path(lhs.substr(0, lhs.size()-1)) / rhs;
+ }
+
+ //strip leading slash on right-hand-side
+ if (not rhs.empty() and *rhs.begin() == '/'){
+ return lhs / fs_path(rhs.substr(1));
+ }
+
+ return fs_path(lhs + "/" + rhs);
+}
+
+/***********************************************************************
+ * Property tree implementation
+ **********************************************************************/
+class property_tree_impl : public uhd::property_tree{
+public:
+
+ property_tree_impl(const fs_path &root = fs_path()):
+ _root(root)
+ {
+ _guts = boost::make_shared<tree_guts_type>();
+ }
+
+ sptr subtree(const fs_path &path_) const{
+ const fs_path path = _root / path_;
+ boost::mutex::scoped_lock lock(_guts->mutex);
+
+ property_tree_impl *subtree = new property_tree_impl(path);
+ subtree->_guts = this->_guts; //copy the guts sptr
+ return sptr(subtree);
+ }
+
+ void remove(const fs_path &path_){
+ const fs_path path = _root / path_;
+ boost::mutex::scoped_lock lock(_guts->mutex);
+
+ node_type *parent = NULL;
+ node_type *node = &_guts->root;
+ BOOST_FOREACH(const std::string &name, path_tokenizer(path)){
+ if (not node->has_key(name)) throw_path_not_found(path);
+ parent = node;
+ node = &(*node)[name];
+ }
+ if (parent == NULL) throw uhd::runtime_error("Cannot uproot");
+ parent->pop(fs_path(path.leaf()));
+ }
+
+ bool exists(const fs_path &path_) const{
+ const fs_path path = _root / path_;
+ boost::mutex::scoped_lock lock(_guts->mutex);
+
+ node_type *node = &_guts->root;
+ BOOST_FOREACH(const std::string &name, path_tokenizer(path)){
+ if (not node->has_key(name)) return false;
+ node = &(*node)[name];
+ }
+ return true;
+ }
+
+ std::vector<std::string> list(const fs_path &path_) const{
+ const fs_path path = _root / path_;
+ boost::mutex::scoped_lock lock(_guts->mutex);
+
+ node_type *node = &_guts->root;
+ BOOST_FOREACH(const std::string &name, path_tokenizer(path)){
+ if (not node->has_key(name)) throw_path_not_found(path);
+ node = &(*node)[name];
+ }
+
+ return node->keys();
+ }
+
+ void _create(const fs_path &path_, const boost::shared_ptr<void> &prop){
+ const fs_path path = _root / path_;
+ boost::mutex::scoped_lock lock(_guts->mutex);
+
+ node_type *node = &_guts->root;
+ BOOST_FOREACH(const std::string &name, path_tokenizer(path)){
+ if (not node->has_key(name)) (*node)[name] = node_type();
+ node = &(*node)[name];
+ }
+ if (node->prop.get() != NULL) throw uhd::runtime_error("Cannot create! Property already exists at: " + path);
+ node->prop = prop;
+ }
+
+ boost::shared_ptr<void> &_access(const fs_path &path_) const{
+ const fs_path path = _root / path_;
+ boost::mutex::scoped_lock lock(_guts->mutex);
+
+ node_type *node = &_guts->root;
+ BOOST_FOREACH(const std::string &name, path_tokenizer(path)){
+ if (not node->has_key(name)) throw_path_not_found(path);
+ node = &(*node)[name];
+ }
+ if (node->prop.get() == NULL) throw uhd::runtime_error("Cannot access! Property uninitialized at: " + path);
+ return node->prop;
+ }
+
+private:
+ void throw_path_not_found(const fs_path &path) const{
+ throw uhd::lookup_error("Path not found in tree: " + path);
+ }
+
+ //basic structural node element
+ struct node_type : uhd::dict<std::string, node_type>{
+ boost::shared_ptr<void> prop;
+ };
+
+ //tree guts which may be referenced in a subtree
+ struct tree_guts_type{
+ node_type root;
+ boost::mutex mutex;
+ };
+
+ //members, the tree and root prefix
+ boost::shared_ptr<tree_guts_type> _guts;
+ const fs_path _root;
+};
+
+/***********************************************************************
+ * Property tree factory
+ **********************************************************************/
+uhd::property_tree::sptr uhd::property_tree::make(void){
+ return sptr(new property_tree_impl());
+}
diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt
new file mode 100644
index 000000000..3cae6180a
--- /dev/null
+++ b/host/lib/transport/CMakeLists.txt
@@ -0,0 +1,106 @@
+#
+# Copyright 2010-2011 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# This file included, use CMake directory variables
+########################################################################
+
+########################################################################
+# Setup libusb
+########################################################################
+MESSAGE(STATUS "")
+FIND_PACKAGE(USB1)
+
+LIBUHD_REGISTER_COMPONENT("USB" ENABLE_USB ON "ENABLE_LIBUHD;LIBUSB_FOUND" OFF)
+
+IF(ENABLE_USB)
+ MESSAGE(STATUS "USB support enabled via libusb.")
+ INCLUDE_DIRECTORIES(${LIBUSB_INCLUDE_DIRS})
+ LIBUHD_APPEND_LIBS(${LIBUSB_LIBRARIES})
+ LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/libusb1_control.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/libusb1_zero_copy.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/libusb1_base.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/libusb1_base.hpp
+ )
+ELSE(ENABLE_USB)
+ LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/usb_dummy_impl.cpp
+ )
+ENDIF(ENABLE_USB)
+
+########################################################################
+# Setup defines for interface address discovery
+########################################################################
+MESSAGE(STATUS "")
+MESSAGE(STATUS "Configuring interface address discovery...")
+INCLUDE(CheckCXXSourceCompiles)
+INCLUDE(CheckIncludeFileCXX)
+
+CHECK_CXX_SOURCE_COMPILES("
+ #include <ifaddrs.h>
+ int main(){
+ struct ifaddrs *ifap;
+ getifaddrs(&ifap);
+ return 0;
+ }
+ " HAVE_GETIFADDRS
+)
+
+CHECK_INCLUDE_FILE_CXX(winsock2.h HAVE_WINSOCK2_H)
+
+IF(HAVE_GETIFADDRS)
+ MESSAGE(STATUS " Interface address discovery supported through getifaddrs.")
+ SET(IF_ADDRS_DEFS HAVE_GETIFADDRS)
+ELSEIF(HAVE_WINSOCK2_H)
+ MESSAGE(STATUS " Interface address discovery supported through SIO_GET_INTERFACE_LIST.")
+ SET(IF_ADDRS_DEFS HAVE_SIO_GET_INTERFACE_LIST)
+ELSE()
+ MESSAGE(STATUS " Interface address discovery not supported.")
+ SET(IF_ADDRS_DEFS HAVE_IF_ADDRS_DUMMY)
+ENDIF()
+
+SET_SOURCE_FILES_PROPERTIES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/if_addrs.cpp
+ PROPERTIES COMPILE_DEFINITIONS "${IF_ADDRS_DEFS}"
+)
+
+########################################################################
+# Setup UDP
+########################################################################
+LIBUHD_APPEND_SOURCES(${CMAKE_CURRENT_SOURCE_DIR}/udp_zero_copy.cpp)
+
+#On windows, the boost asio implementation uses the winsock2 library.
+#Note: we exclude the .lib extension for cygwin and mingw platforms.
+IF(WIN32)
+ LIBUHD_APPEND_LIBS(ws2_32)
+ENDIF()
+
+########################################################################
+# Append to the list of sources for lib uhd
+########################################################################
+LIBUHD_PYTHON_GEN_SOURCE(
+ ${CMAKE_CURRENT_SOURCE_DIR}/gen_vrt_if_packet.py
+ ${CMAKE_CURRENT_BINARY_DIR}/vrt_if_packet.cpp
+)
+
+LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/buffer_pool.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/if_addrs.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/udp_simple.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/usb_zero_copy_wrapper.cpp
+)
diff --git a/host/lib/transport/buffer_pool.cpp b/host/lib/transport/buffer_pool.cpp
new file mode 100644
index 000000000..971bbb75a
--- /dev/null
+++ b/host/lib/transport/buffer_pool.cpp
@@ -0,0 +1,80 @@
+//
+// Copyright 2011-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/transport/buffer_pool.hpp>
+#include <boost/shared_array.hpp>
+#include <vector>
+
+using namespace uhd::transport;
+
+//! pad the byte count to a multiple of alignment
+static size_t pad_to_boundary(const size_t bytes, const size_t alignment){
+ return bytes + (alignment - bytes)%alignment;
+}
+
+/***********************************************************************
+ * Buffer pool implementation
+ **********************************************************************/
+class buffer_pool_impl : public buffer_pool{
+public:
+ buffer_pool_impl(
+ const std::vector<ptr_type> &ptrs,
+ boost::shared_array<char> mem
+ ): _ptrs(ptrs), _mem(mem){
+ /* NOP */
+ }
+
+ ptr_type at(const size_t index) const{
+ return _ptrs.at(index);
+ }
+
+ size_t size(void) const{
+ return _ptrs.size();
+ }
+
+private:
+ std::vector<ptr_type> _ptrs;
+ boost::shared_array<char> _mem;
+};
+
+/***********************************************************************
+ * Buffer pool factor function
+ **********************************************************************/
+buffer_pool::sptr buffer_pool::make(
+ const size_t num_buffs,
+ const size_t buff_size,
+ const size_t alignment
+){
+ //1) pad the buffer size to be a multiple of alignment
+ //2) pad the overall memory size for room after alignment
+ //3) allocate the memory in one block of sufficient size
+ const size_t padded_buff_size = pad_to_boundary(buff_size, alignment);
+ boost::shared_array<char> mem(new char[padded_buff_size*num_buffs + alignment-1]);
+
+ //Fill a vector with boundary-aligned points in the memory
+ const size_t mem_start = pad_to_boundary(size_t(mem.get()), alignment);
+ std::vector<ptr_type> ptrs(num_buffs);
+ for (size_t i = 0; i < num_buffs; i++){
+ ptrs[i] = ptr_type(mem_start + padded_buff_size*i);
+ }
+
+ //Create a new buffer pool implementation with:
+ // - the pre-computed pointers, and
+ // - the reference to allocated memory.
+ return sptr(new buffer_pool_impl(ptrs, mem));
+}
+
diff --git a/host/lib/transport/gen_vrt_if_packet.py b/host/lib/transport/gen_vrt_if_packet.py
new file mode 100644
index 000000000..e28ce3aae
--- /dev/null
+++ b/host/lib/transport/gen_vrt_if_packet.py
@@ -0,0 +1,286 @@
+#!/usr/bin/env python
+#
+# Copyright 2010-2011 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+"""
+The vrt packer/unpacker code generator:
+
+This script will generate the pack and unpack routines that convert
+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
+/***********************************************************************
+ * 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>
+
+//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;
+using namespace uhd::transport;
+
+typedef size_t pred_type;
+typedef std::vector<pred_type> pred_table_type;
+#define pred_table_index(hdr) ((hdr >> 20) & 0x1ff)
+
+static pred_table_type get_pred_unpack_table(void){
+ 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);
+ }
+ return table;
+}
+
+static const pred_table_type pred_unpack_table(get_pred_unpack_table());
+
+//maps trailer bits to num empty bytes
+//maps num empty bytes to trailer bits
+static const size_t occ_table[] = {0, 2, 1, 3};
+
+########################################################################
+#def gen_code($XE_MACRO, $suffix)
+########################################################################
+
+void vrt::if_hdr_pack_$(suffix)(
+ boost::uint32_t *packet_buff,
+ if_packet_info_t &if_packet_info
+){
+ 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);
+
+ switch(pred){
+ #for $pred in range(2**7)
+ case $pred:
+ #set $num_header_words = 1
+ #set $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
+ ########## 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
+ ########## 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
+ ########## 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
+ ########## Burst Flags ##########
+ #if $pred & $eob_p
+ #set $flags |= (0x1 << 24);
+ #end if
+ #if $pred & $sob_p
+ #set $flags |= (0x1 << 25);
+ #end if
+ ########## Trailer ##########
+ #if $pred & $tlr_p
+ {
+ const size_t empty_bytes = if_packet_info.num_payload_words32*sizeof(boost::uint32_t) - if_packet_info.num_payload_bytes;
+ if_packet_info.tlr = (0x3 << 22) | (occ_table[empty_bytes & 0x3] << 10);
+ }
+ packet_buff[$num_header_words+if_packet_info.num_payload_words32] = $(XE_MACRO)(if_packet_info.tlr);
+ #set $flags |= (0x1 << 26);
+ #set $num_trailer_words = 1;
+ #else
+ #set $num_trailer_words = 0;
+ #end if
+ ########## Variables ##########
+ if_packet_info.num_header_words32 = $num_header_words;
+ if_packet_info.num_packet_words32 = $($num_header_words + $num_trailer_words) + if_packet_info.num_payload_words32;
+ vrt_hdr_flags = $hex($flags);
+ break;
+ #end for
+ }
+
+ //fill in complete header word
+ packet_buff[0] = $(XE_MACRO)(boost::uint32_t(0
+ | (if_packet_info.packet_type << 29)
+ | vrt_hdr_flags
+ | ((if_packet_info.packet_count & 0xf) << 16)
+ | (if_packet_info.num_packet_words32 & 0xffff)
+ ));
+}
+
+void vrt::if_hdr_unpack_$(suffix)(
+ const boost::uint32_t *packet_buff,
+ if_packet_info_t &if_packet_info
+){
+ //extract vrt header
+ boost::uint32_t vrt_hdr_word = $(XE_MACRO)(packet_buff[0]);
+ size_t packet_words32 = vrt_hdr_word & 0xffff;
+
+ //failure case
+ if (if_packet_info.num_packet_words32 < packet_words32)
+ throw uhd::value_error("bad vrt header or packet fragment");
+
+ //extract fields from the header
+ if_packet_info.packet_type = if_packet_info_t::packet_type_t(vrt_hdr_word >> 29);
+ if_packet_info.packet_count = (vrt_hdr_word >> 16) & 0xf;
+
+ const pred_type pred = pred_unpack_table[pred_table_index(vrt_hdr_word)];
+
+ size_t empty_bytes = 0;
+
+ switch(pred){
+ #for $pred in range(2**7)
+ case $pred:
+ #set $has_time_spec = False
+ #set $num_header_words = 1
+ ########## Stream ID ##########
+ #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.has_sid = false;
+ #end if
+ ########## Class ID ##########
+ #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.has_cid = false;
+ #end if
+ ########## Integer Time ##########
+ #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.has_tsi = false;
+ #end if
+ ########## Fractional Time ##########
+ #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.has_tsf = false;
+ #end if
+ ########## Burst Flags ##########
+ #if $pred & $eob_p
+ if_packet_info.eob = true;
+ #else
+ if_packet_info.eob = false;
+ #end if
+ #if $pred & $sob_p
+ if_packet_info.sob = true;
+ #else
+ if_packet_info.sob = false;
+ #end if
+ ########## Trailer ##########
+ #if $pred & $tlr_p
+ if_packet_info.has_tlr = true;
+ if_packet_info.tlr = $(XE_MACRO)(packet_buff[packet_words32-1]);
+ #set $num_trailer_words = 1;
+ {
+ const int indicators = (if_packet_info.tlr >> 20) & (if_packet_info.tlr >> 8);
+ if ((indicators & (1 << 0)) != 0) if_packet_info.eob = true;
+ if ((indicators & (1 << 1)) != 0) if_packet_info.sob = true;
+ empty_bytes = occ_table[(indicators >> 2) & 0x3];
+ }
+ #else
+ if_packet_info.has_tlr = false;
+ #set $num_trailer_words = 0;
+ #end if
+ ########## Variables ##########
+ //another failure case
+ if (packet_words32 < $($num_header_words + $num_trailer_words))
+ throw uhd::value_error("bad vrt header or invalid packet length");
+ if_packet_info.num_header_words32 = $num_header_words;
+ if_packet_info.num_payload_words32 = packet_words32 - $($num_header_words + $num_trailer_words);
+ if_packet_info.num_payload_bytes = if_packet_info.num_payload_words32*sizeof(boost::uint32_t) - empty_bytes;
+ break;
+ #end for
+ }
+}
+
+########################################################################
+#end def
+########################################################################
+
+$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))
+
+if __name__ == '__main__':
+ import sys
+ open(sys.argv[1], 'w').write(parse_tmpl(
+ TMPL_TEXT,
+ file=__file__,
+ sid_p = 0b0000001,
+ cid_p = 0b0000010,
+ tsi_p = 0b0000100,
+ tsf_p = 0b0001000,
+ tlr_p = 0b0010000,
+ sob_p = 0b0100000,
+ eob_p = 0b1000000,
+ ))
diff --git a/host/lib/transport/if_addrs.cpp b/host/lib/transport/if_addrs.cpp
new file mode 100644
index 000000000..2ad0c8c53
--- /dev/null
+++ b/host/lib/transport/if_addrs.cpp
@@ -0,0 +1,121 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/transport/if_addrs.hpp>
+#include <boost/asio/ip/address_v4.hpp>
+#include <boost/cstdint.hpp>
+#include <iostream>
+
+/***********************************************************************
+ * Interface address discovery through ifaddrs api
+ **********************************************************************/
+#ifdef HAVE_GETIFADDRS
+#include <ifaddrs.h>
+
+static boost::asio::ip::address_v4 sockaddr_to_ip_addr(sockaddr *addr){
+ return boost::asio::ip::address_v4(ntohl(
+ reinterpret_cast<sockaddr_in*>(addr)->sin_addr.s_addr
+ ));
+}
+
+std::vector<uhd::transport::if_addrs_t> uhd::transport::get_if_addrs(void){
+ std::vector<if_addrs_t> if_addrs;
+ struct ifaddrs *ifap;
+ if (getifaddrs(&ifap) == 0){
+ for (struct ifaddrs *iter = ifap; iter != NULL; iter = iter->ifa_next){
+ //ensure that the entries are valid
+ if (iter->ifa_addr == NULL) continue;
+ if (iter->ifa_addr->sa_family != AF_INET) continue;
+ if (iter->ifa_netmask->sa_family != AF_INET) continue;
+ if (iter->ifa_broadaddr->sa_family != AF_INET) continue;
+
+ //append a new set of interface addresses
+ if_addrs_t if_addr;
+ if_addr.inet = sockaddr_to_ip_addr(iter->ifa_addr).to_string();
+ if_addr.mask = sockaddr_to_ip_addr(iter->ifa_netmask).to_string();
+ if_addr.bcast = sockaddr_to_ip_addr(iter->ifa_broadaddr).to_string();
+
+ //correct the bcast address when its same as the gateway
+ if (if_addr.inet == if_addr.bcast or sockaddr_to_ip_addr(iter->ifa_broadaddr) == boost::asio::ip::address_v4(0)){
+ //manually calculate broadcast address
+ //https://svn.boost.org/trac/boost/ticket/5198
+ const boost::uint32_t addr = sockaddr_to_ip_addr(iter->ifa_addr).to_ulong();
+ const boost::uint32_t mask = sockaddr_to_ip_addr(iter->ifa_netmask).to_ulong();
+ const boost::uint32_t bcast = (addr & mask) | ~mask;
+ if_addr.bcast = boost::asio::ip::address_v4(bcast).to_string();
+ }
+
+ if_addrs.push_back(if_addr);
+ }
+ freeifaddrs(ifap);
+ }
+ return if_addrs;
+}
+
+#endif /* HAVE_GETIFADDRS */
+
+/***********************************************************************
+ * Interface address discovery through windows api
+ **********************************************************************/
+#ifdef HAVE_SIO_GET_INTERFACE_LIST
+#include <winsock2.h>
+
+std::vector<uhd::transport::if_addrs_t> uhd::transport::get_if_addrs(void){
+ std::vector<if_addrs_t> if_addrs;
+ SOCKET sd = WSASocket(AF_INET, SOCK_DGRAM, 0, 0, 0, 0);
+ if (sd == SOCKET_ERROR) {
+ std::cerr << "Failed to get a socket. Error " << WSAGetLastError() <<
+ std::endl; return if_addrs;
+ }
+
+ INTERFACE_INFO InterfaceList[20];
+ unsigned long nBytesReturned;
+ if (WSAIoctl(sd, SIO_GET_INTERFACE_LIST, 0, 0, &InterfaceList,
+ sizeof(InterfaceList), &nBytesReturned, 0, 0) == SOCKET_ERROR) {
+ std::cerr << "Failed calling WSAIoctl: error " << WSAGetLastError() <<
+ std::endl;
+ return if_addrs;
+ }
+
+ int nNumInterfaces = nBytesReturned / sizeof(INTERFACE_INFO);
+ for (int i = 0; i < nNumInterfaces; ++i) {
+ boost::uint32_t iiAddress = ntohl(reinterpret_cast<sockaddr_in&>(InterfaceList[i].iiAddress).sin_addr.s_addr);
+ boost::uint32_t iiNetmask = ntohl(reinterpret_cast<sockaddr_in&>(InterfaceList[i].iiNetmask).sin_addr.s_addr);
+ boost::uint32_t iiBroadcastAddress = (iiAddress & iiNetmask) | ~iiNetmask;
+
+ if_addrs_t if_addr;
+ if_addr.inet = boost::asio::ip::address_v4(iiAddress).to_string();
+ if_addr.mask = boost::asio::ip::address_v4(iiNetmask).to_string();
+ if_addr.bcast = boost::asio::ip::address_v4(iiBroadcastAddress).to_string();
+ if_addrs.push_back(if_addr);
+ }
+
+ return if_addrs;
+}
+
+#endif /* HAVE_SIO_GET_INTERFACE_LIST */
+
+/***********************************************************************
+ * Interface address discovery not included
+ **********************************************************************/
+#ifdef HAVE_IF_ADDRS_DUMMY
+
+std::vector<uhd::transport::if_addrs_t> uhd::transport::get_if_addrs(void){
+ return std::vector<if_addrs_t>();
+}
+
+#endif /* HAVE_IF_ADDRS_DUMMY */
diff --git a/host/lib/transport/libusb1_base.cpp b/host/lib/transport/libusb1_base.cpp
new file mode 100644
index 000000000..d4ec874f1
--- /dev/null
+++ b/host/lib/transport/libusb1_base.cpp
@@ -0,0 +1,279 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "libusb1_base.hpp"
+#include <uhd/exception.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/types/dict.hpp>
+#include <boost/weak_ptr.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/foreach.hpp>
+#include <iostream>
+
+using namespace uhd;
+using namespace uhd::transport;
+
+/***********************************************************************
+ * libusb session
+ **********************************************************************/
+class libusb_session_impl : public libusb::session{
+public:
+ libusb_session_impl(void){
+ UHD_ASSERT_THROW(libusb_init(&_context) == 0);
+ libusb_set_debug(_context, debug_level);
+ }
+
+ ~libusb_session_impl(void){
+ libusb_exit(_context);
+ }
+
+ libusb_context *get_context(void) const{
+ return _context;
+ }
+
+private:
+ libusb_context *_context;
+};
+
+libusb::session::sptr libusb::session::get_global_session(void){
+ static boost::weak_ptr<session> global_session;
+
+ //not expired -> get existing session
+ if (not global_session.expired()) return global_session.lock();
+
+ //create a new global session
+ sptr new_global_session(new libusb_session_impl());
+ global_session = new_global_session;
+ return new_global_session;
+}
+
+/***********************************************************************
+ * libusb device
+ **********************************************************************/
+class libusb_device_impl : public libusb::device{
+public:
+ libusb_device_impl(libusb_device *dev){
+ _session = libusb::session::get_global_session();
+ _dev = dev;
+ }
+
+ ~libusb_device_impl(void){
+ libusb_unref_device(this->get());
+ }
+
+ libusb_device *get(void) const{
+ return _dev;
+ }
+
+private:
+ libusb::session::sptr _session; //always keep a reference to session
+ libusb_device *_dev;
+};
+
+/***********************************************************************
+ * libusb device list
+ **********************************************************************/
+class libusb_device_list_impl : public libusb::device_list{
+public:
+ libusb_device_list_impl(void){
+ libusb::session::sptr sess = libusb::session::get_global_session();
+
+ //allocate a new list of devices
+ libusb_device** dev_list;
+ ssize_t ret = libusb_get_device_list(sess->get_context(), &dev_list);
+ if (ret < 0) throw uhd::os_error("cannot enumerate usb devices");
+
+ //fill the vector of device references
+ for (size_t i = 0; i < size_t(ret); i++) _devs.push_back(
+ libusb::device::sptr(new libusb_device_impl(dev_list[i]))
+ );
+
+ //free the device list but dont unref (done in ~device)
+ libusb_free_device_list(dev_list, false/*dont unref*/);
+ }
+
+ size_t size(void) const{
+ return _devs.size();
+ }
+
+ libusb::device::sptr at(size_t i) const{
+ return _devs.at(i);
+ }
+
+private:
+ std::vector<libusb::device::sptr> _devs;
+};
+
+libusb::device_list::sptr libusb::device_list::make(void){
+ return sptr(new libusb_device_list_impl());
+}
+
+/***********************************************************************
+ * libusb device descriptor
+ **********************************************************************/
+class libusb_device_descriptor_impl : public libusb::device_descriptor{
+public:
+ libusb_device_descriptor_impl(libusb::device::sptr dev){
+ _dev = dev;
+ UHD_ASSERT_THROW(libusb_get_device_descriptor(_dev->get(), &_desc) == 0);
+ }
+
+ const libusb_device_descriptor &get(void) const{
+ return _desc;
+ }
+
+ std::string get_ascii_serial(void) const{
+ if (this->get().iSerialNumber == 0) return "";
+
+ libusb::device_handle::sptr handle(
+ libusb::device_handle::get_cached_handle(_dev)
+ );
+
+ unsigned char buff[512];
+ ssize_t ret = libusb_get_string_descriptor_ascii(
+ handle->get(), this->get().iSerialNumber, buff, sizeof(buff)
+ );
+ if (ret < 0) return ""; //on error, just return empty string
+
+ return std::string((char *)buff, ret);
+ }
+
+private:
+ libusb::device::sptr _dev; //always keep a reference to device
+ libusb_device_descriptor _desc;
+};
+
+libusb::device_descriptor::sptr libusb::device_descriptor::make(device::sptr dev){
+ return sptr(new libusb_device_descriptor_impl(dev));
+}
+
+/***********************************************************************
+ * libusb device handle
+ **********************************************************************/
+class libusb_device_handle_impl : public libusb::device_handle{
+public:
+ libusb_device_handle_impl(libusb::device::sptr dev){
+ _dev = dev;
+ UHD_ASSERT_THROW(libusb_open(_dev->get(), &_handle) == 0);
+ }
+
+ ~libusb_device_handle_impl(void){
+ //release all claimed interfaces
+ for (size_t i = 0; i < _claimed.size(); i++){
+ libusb_release_interface(this->get(), _claimed[i]);
+ }
+ libusb_close(_handle);
+ }
+
+ libusb_device_handle *get(void) const{
+ return _handle;
+ }
+
+ void claim_interface(int interface){
+ UHD_ASSERT_THROW(libusb_claim_interface(this->get(), interface) == 0);
+ _claimed.push_back(interface);
+ }
+
+private:
+ libusb::device::sptr _dev; //always keep a reference to device
+ libusb_device_handle *_handle;
+ std::vector<int> _claimed;
+};
+
+libusb::device_handle::sptr libusb::device_handle::get_cached_handle(device::sptr dev){
+ static uhd::dict<libusb_device *, boost::weak_ptr<device_handle> > handles;
+
+ //lock for atomic access to static table above
+ static boost::mutex mutex;
+ boost::mutex::scoped_lock lock(mutex);
+
+ //not expired -> get existing handle
+ if (handles.has_key(dev->get()) and not handles[dev->get()].expired()){
+ return handles[dev->get()].lock();
+ }
+
+ //create a new cached handle
+ try{
+ sptr new_handle(new libusb_device_handle_impl(dev));
+ handles[dev->get()] = new_handle;
+ return new_handle;
+ }
+ catch(const uhd::exception &){
+ #ifdef UHD_PLATFORM_LINUX
+ UHD_MSG(error) <<
+ "USB open failed: insufficient permissions.\n"
+ "See the application notes for your device.\n"
+ << std::endl;
+ #else
+ UHD_LOG << "USB open failed: device already claimed." << std::endl;
+ #endif
+ throw;
+ }
+}
+
+/***********************************************************************
+ * libusb special handle
+ **********************************************************************/
+class libusb_special_handle_impl : public libusb::special_handle{
+public:
+ libusb_special_handle_impl(libusb::device::sptr dev){
+ _dev = dev;
+ }
+
+ libusb::device::sptr get_device(void) const{
+ return _dev;
+ }
+
+ std::string get_serial(void) const{
+ return libusb::device_descriptor::make(this->get_device())->get_ascii_serial();
+ }
+
+ boost::uint16_t get_vendor_id(void) const{
+ return libusb::device_descriptor::make(this->get_device())->get().idVendor;
+ }
+
+ boost::uint16_t get_product_id(void) const{
+ return libusb::device_descriptor::make(this->get_device())->get().idProduct;
+ }
+
+private:
+ libusb::device::sptr _dev; //always keep a reference to device
+};
+
+libusb::special_handle::sptr libusb::special_handle::make(device::sptr dev){
+ return sptr(new libusb_special_handle_impl(dev));
+}
+
+/***********************************************************************
+ * list device handles implementations
+ **********************************************************************/
+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;
+
+ 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);
+ }
+ }
+
+ return handles;
+}
diff --git a/host/lib/transport/libusb1_base.hpp b/host/lib/transport/libusb1_base.hpp
new file mode 100644
index 000000000..04c1d6574
--- /dev/null
+++ b/host/lib/transport/libusb1_base.hpp
@@ -0,0 +1,149 @@
+//
+// Copyright 2010 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_TRANSPORT_LIBUSB_HPP
+#define INCLUDED_LIBUHD_TRANSPORT_LIBUSB_HPP
+
+#include <uhd/config.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include <uhd/transport/usb_device_handle.hpp>
+#include <libusb.h>
+
+/***********************************************************************
+ * Libusb object oriented smart pointer wrappers:
+ * The following wrappers provide allocation and automatic deallocation
+ * for various libusb data types and handles. The construction routines
+ * also store tables of already allocated structures to avoid multiple
+ * occurrences of opened handles (for example).
+ **********************************************************************/
+namespace uhd { namespace transport {
+
+namespace libusb {
+
+ /*!
+ * This session class holds a global libusb context for this process.
+ * The get global session call will create a new context if none exists.
+ * When all references to session are destroyed, the context will be freed.
+ */
+ class session : boost::noncopyable {
+ public:
+ typedef boost::shared_ptr<session> sptr;
+
+ /*!
+ * Level 0: no messages ever printed by the library (default)
+ * Level 1: error messages are printed to stderr
+ * Level 2: warning and error messages are printed to stderr
+ * Level 3: informational messages are printed to stdout, warning
+ * and error messages are printed to stderr
+ */
+ static const int debug_level = 0;
+
+ //! get a shared pointer to the global session
+ static sptr get_global_session(void);
+
+ //! get the underlying libusb context pointer
+ virtual libusb_context *get_context(void) const = 0;
+ };
+
+ /*!
+ * Holds a device pointer with a reference to the session.
+ */
+ class device : boost::noncopyable {
+ public:
+ typedef boost::shared_ptr<device> sptr;
+
+ //! get the underlying device pointer
+ virtual libusb_device *get(void) const = 0;
+ };
+
+ /*!
+ * This device list class holds a device list that will be
+ * automatically freed when the last reference is destroyed.
+ */
+ class device_list : boost::noncopyable {
+ public:
+ typedef boost::shared_ptr<device_list> sptr;
+
+ //! make a new device list
+ static sptr make(void);
+
+ //! the number of devices in this list
+ virtual size_t size() const = 0;
+
+ //! get the device pointer at a particular index
+ virtual device::sptr at(size_t index) const = 0;
+ };
+
+ /*!
+ * Holds a device descriptor and a reference to the device.
+ */
+ class device_descriptor : boost::noncopyable {
+ public:
+ typedef boost::shared_ptr<device_descriptor> sptr;
+
+ //! make a new descriptor from a device reference
+ static sptr make(device::sptr);
+
+ //! get the underlying device descriptor
+ virtual const libusb_device_descriptor &get(void) const = 0;
+
+ virtual std::string get_ascii_serial(void) const = 0;
+ };
+
+ /*!
+ * Holds a device handle and a reference to the device.
+ */
+ class device_handle : boost::noncopyable {
+ public:
+ typedef boost::shared_ptr<device_handle> sptr;
+
+ //! get a cached handle or make a new one given the device
+ static sptr get_cached_handle(device::sptr);
+
+ //! get the underlying device handle
+ virtual libusb_device_handle *get(void) const = 0;
+
+ /*!
+ * Open USB interfaces for control using magic value
+ * IN interface: 2
+ * OUT interface: 1
+ * Control interface: 0
+ */
+ virtual void claim_interface(int) = 0;
+ };
+
+ /*!
+ * The special handle is our internal implementation of the
+ * usb device handle which is used publicly to identify a device.
+ */
+ class special_handle : public usb_device_handle {
+ public:
+ typedef boost::shared_ptr<special_handle> sptr;
+
+ //! make a new special handle from device
+ static sptr make(device::sptr);
+
+ //! get the underlying device reference
+ virtual device::sptr get_device(void) const = 0;
+ };
+
+}
+
+}} //namespace
+
+#endif /* INCLUDED_LIBUHD_TRANSPORT_LIBUSB_HPP */
diff --git a/host/lib/transport/libusb1_control.cpp b/host/lib/transport/libusb1_control.cpp
new file mode 100644
index 000000000..3d9b38785
--- /dev/null
+++ b/host/lib/transport/libusb1_control.cpp
@@ -0,0 +1,67 @@
+//
+// Copyright 2010 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "libusb1_base.hpp"
+#include <uhd/transport/usb_control.hpp>
+#include <boost/thread/mutex.hpp>
+
+using namespace uhd::transport;
+
+const int libusb_timeout = 0;
+
+/***********************************************************************
+ * libusb-1.0 implementation of USB control transport
+ **********************************************************************/
+class libusb_control_impl : public usb_control {
+public:
+ libusb_control_impl(libusb::device_handle::sptr handle, const size_t interface):
+ _handle(handle)
+ {
+ _handle->claim_interface(interface);
+ }
+
+ ssize_t submit(boost::uint8_t request_type,
+ boost::uint8_t request,
+ boost::uint16_t value,
+ boost::uint16_t index,
+ unsigned char *buff,
+ boost::uint16_t length
+ ){
+ boost::mutex::scoped_lock lock(_mutex);
+ return libusb_control_transfer(_handle->get(),
+ request_type,
+ request,
+ value,
+ index,
+ buff,
+ length,
+ libusb_timeout);
+ }
+
+private:
+ libusb::device_handle::sptr _handle;
+ boost::mutex _mutex;
+};
+
+/***********************************************************************
+ * USB control public make functions
+ **********************************************************************/
+usb_control::sptr usb_control::make(usb_device_handle::sptr handle, const size_t interface){
+ return sptr(new libusb_control_impl(libusb::device_handle::get_cached_handle(
+ boost::static_pointer_cast<libusb::special_handle>(handle)->get_device()
+ ), interface));
+}
diff --git a/host/lib/transport/libusb1_zero_copy.cpp b/host/lib/transport/libusb1_zero_copy.cpp
new file mode 100644
index 000000000..3e67264cd
--- /dev/null
+++ b/host/lib/transport/libusb1_zero_copy.cpp
@@ -0,0 +1,300 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "libusb1_base.hpp"
+#include <uhd/transport/usb_zero_copy.hpp>
+#include <uhd/transport/buffer_pool.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/exception.hpp>
+#include <boost/foreach.hpp>
+#include <boost/thread/thread.hpp>
+#include <list>
+
+using namespace uhd;
+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*/
+
+/*!
+ * All libusb callback functions should be marked with the LIBUSB_CALL macro
+ * to ensure that they are compiled with the same calling convention as libusb.
+ */
+
+//! helper function: handles all async callbacks
+static void LIBUSB_CALL libusb_async_cb(libusb_transfer *lut){
+ *(static_cast<bool *>(lut->user_data)) = true;
+}
+
+/*!
+ * Wait for a managed buffer to become complete.
+ *
+ * This routine processes async events until the transaction completes.
+ * We must call the libusb handle events in a loop because the handler
+ * may complete managed buffers other than the one we are waiting on.
+ *
+ * We cannot determine if handle events timed out or processed an event.
+ * Therefore, the timeout condition is handled by using boost system time.
+ *
+ * \param ctx the libusb context structure
+ * \param timeout the wait timeout in seconds
+ * \param completed a reference to the completed flag
+ * \return true for completion, false for timeout
+ */
+UHD_INLINE bool wait_for_completion(libusb_context *ctx, const double timeout, bool &completed){
+ const boost::system_time timeout_time = boost::get_system_time() + boost::posix_time::microseconds(long(timeout*1000000));
+
+ while (not completed and (boost::get_system_time() < timeout_time)){
+ timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = 10000; /*10ms*/
+ libusb_handle_events_timeout(ctx, &tv);
+ }
+
+ return completed;
+}
+
+/***********************************************************************
+ * Reusable managed receiver buffer:
+ * - Associated with a particular libusb transfer struct.
+ * - Submits the transfer to libusb in the release method.
+ **********************************************************************/
+class libusb_zero_copy_mrb : public managed_recv_buffer{
+public:
+ libusb_zero_copy_mrb(libusb_transfer *lut, const size_t frame_size):
+ _ctx(libusb::session::get_global_session()->get_context()),
+ _lut(lut), _expired(false), _frame_size(frame_size) { /* NOP */ }
+
+ void release(void){
+ if (_expired) return;
+ completed = false;
+ _lut->length = _frame_size; //always reset length
+ UHD_ASSERT_THROW(libusb_submit_transfer(_lut) == 0);
+ _expired = true;
+ }
+
+ sptr get_new(const double timeout, size_t &index){
+ if (wait_for_completion(_ctx, timeout, completed)){
+ index++;
+ _expired = false;
+ return make_managed_buffer(this);
+ }
+ return managed_recv_buffer::sptr();
+ }
+
+ bool completed;
+
+private:
+ const void *get_buff(void) const{return _lut->buffer;}
+ size_t get_size(void) const{return _lut->actual_length;}
+
+ libusb_context *_ctx;
+ libusb_transfer *_lut;
+ bool _expired;
+ const size_t _frame_size;
+};
+
+/***********************************************************************
+ * Reusable managed send buffer:
+ * - Associated with a particular libusb transfer struct.
+ * - Submits the transfer to libusb in the commit method.
+ **********************************************************************/
+class libusb_zero_copy_msb : public managed_send_buffer{
+public:
+ libusb_zero_copy_msb(libusb_transfer *lut, const size_t frame_size):
+ _ctx(libusb::session::get_global_session()->get_context()),
+ _lut(lut), _expired(false), _frame_size(frame_size) { /* NOP */ }
+
+ void commit(size_t len){
+ if (_expired) return;
+ completed = false;
+ _lut->length = len;
+ if (len == 0) libusb_async_cb(_lut);
+ else UHD_ASSERT_THROW(libusb_submit_transfer(_lut) == 0);
+ _expired = true;
+ }
+
+ sptr get_new(const double timeout, size_t &index){
+ if (wait_for_completion(_ctx, timeout, completed)){
+ index++;
+ _expired = false;
+ return make_managed_buffer(this);
+ }
+ return managed_send_buffer::sptr();
+ }
+
+ bool completed;
+
+private:
+ void *get_buff(void) const{return _lut->buffer;}
+ size_t get_size(void) const{return _frame_size;}
+
+ libusb_context *_ctx;
+ libusb_transfer *_lut;
+ bool _expired;
+ const size_t _frame_size;
+};
+
+/***********************************************************************
+ * USB zero_copy device class
+ **********************************************************************/
+class libusb_zero_copy_impl : public usb_zero_copy{
+public:
+
+ libusb_zero_copy_impl(
+ libusb::device_handle::sptr handle,
+ const size_t recv_interface,
+ const size_t recv_endpoint,
+ const size_t send_interface,
+ const size_t send_endpoint,
+ const device_addr_t &hints
+ ):
+ _handle(handle),
+ _recv_frame_size(size_t(hints.cast<double>("recv_frame_size", DEFAULT_XFER_SIZE))),
+ _num_recv_frames(size_t(hints.cast<double>("num_recv_frames", DEFAULT_NUM_XFERS))),
+ _send_frame_size(size_t(hints.cast<double>("send_frame_size", DEFAULT_XFER_SIZE))),
+ _num_send_frames(size_t(hints.cast<double>("num_send_frames", DEFAULT_NUM_XFERS))),
+ _recv_buffer_pool(buffer_pool::make(_num_recv_frames, _recv_frame_size)),
+ _send_buffer_pool(buffer_pool::make(_num_send_frames, _send_frame_size)),
+ _next_recv_buff_index(0),
+ _next_send_buff_index(0)
+ {
+ _handle->claim_interface(recv_interface);
+ _handle->claim_interface(send_interface);
+
+ //allocate libusb transfer structs and managed receive buffers
+ for (size_t i = 0; i < get_num_recv_frames(); i++){
+
+ libusb_transfer *lut = libusb_alloc_transfer(0);
+ UHD_ASSERT_THROW(lut != NULL);
+
+ _mrb_pool.push_back(boost::shared_ptr<libusb_zero_copy_mrb>(new libusb_zero_copy_mrb(lut, this->get_recv_frame_size())));
+
+ libusb_fill_bulk_transfer(
+ lut, // transfer
+ _handle->get(), // dev_handle
+ (recv_endpoint & 0x7f) | 0x80, // endpoint
+ static_cast<unsigned char *>(_recv_buffer_pool->at(i)), // buffer
+ this->get_recv_frame_size(), // length
+ libusb_transfer_cb_fn(&libusb_async_cb), // callback
+ static_cast<void *>(&_mrb_pool.back()->completed), // user_data
+ 0 // timeout (ms)
+ );
+
+ _all_luts.push_back(lut);
+ _mrb_pool.back()->release();
+ }
+
+ //allocate libusb transfer structs and managed send buffers
+ for (size_t i = 0; i < get_num_send_frames(); i++){
+
+ libusb_transfer *lut = libusb_alloc_transfer(0);
+ UHD_ASSERT_THROW(lut != NULL);
+
+ _msb_pool.push_back(boost::shared_ptr<libusb_zero_copy_msb>(new libusb_zero_copy_msb(lut, this->get_send_frame_size())));
+
+ libusb_fill_bulk_transfer(
+ lut, // transfer
+ _handle->get(), // dev_handle
+ (send_endpoint & 0x7f) | 0x00, // endpoint
+ static_cast<unsigned char *>(_send_buffer_pool->at(i)), // buffer
+ this->get_send_frame_size(), // length
+ libusb_transfer_cb_fn(&libusb_async_cb), // callback
+ static_cast<void *>(&_msb_pool.back()->completed), // user_data
+ 0 // timeout
+ );
+
+ _all_luts.push_back(lut);
+ _msb_pool.back()->commit(0);
+ }
+ }
+
+ ~libusb_zero_copy_impl(void){
+ libusb_context *ctx = libusb::session::get_global_session()->get_context();
+
+ //cancel all transfers
+ BOOST_FOREACH(libusb_transfer *lut, _all_luts){
+ libusb_cancel_transfer(lut);
+ }
+
+ //process all transfers until timeout occurs
+ bool completed = false;
+ wait_for_completion(ctx, 0.01, completed);
+
+ //free all transfers
+ BOOST_FOREACH(libusb_transfer *lut, _all_luts){
+ libusb_free_transfer(lut);
+ }
+
+ }
+
+ managed_recv_buffer::sptr get_recv_buff(double timeout){
+ if (_next_recv_buff_index == _num_recv_frames) _next_recv_buff_index = 0;
+ return _mrb_pool[_next_recv_buff_index]->get_new(timeout, _next_recv_buff_index);
+ }
+
+ managed_send_buffer::sptr get_send_buff(double timeout){
+ if (_next_send_buff_index == _num_send_frames) _next_send_buff_index = 0;
+ return _msb_pool[_next_send_buff_index]->get_new(timeout, _next_send_buff_index);
+ }
+
+ size_t get_num_recv_frames(void) const { return _num_recv_frames; }
+ size_t get_num_send_frames(void) const { return _num_send_frames; }
+
+ size_t get_recv_frame_size(void) const { return _recv_frame_size; }
+ size_t get_send_frame_size(void) const { return _send_frame_size; }
+
+private:
+ libusb::device_handle::sptr _handle;
+ const size_t _recv_frame_size, _num_recv_frames;
+ const size_t _send_frame_size, _num_send_frames;
+
+ //! Storage for transfer related objects
+ buffer_pool::sptr _recv_buffer_pool, _send_buffer_pool;
+ std::vector<boost::shared_ptr<libusb_zero_copy_mrb> > _mrb_pool;
+ std::vector<boost::shared_ptr<libusb_zero_copy_msb> > _msb_pool;
+ size_t _next_recv_buff_index, _next_send_buff_index;
+
+ //! a list of all transfer structs we allocated
+ std::list<libusb_transfer *> _all_luts;
+
+
+};
+
+/***********************************************************************
+ * USB zero_copy make functions
+ **********************************************************************/
+usb_zero_copy::sptr usb_zero_copy::make(
+ usb_device_handle::sptr handle,
+ const size_t recv_interface,
+ const size_t recv_endpoint,
+ const size_t send_interface,
+ const size_t send_endpoint,
+ const device_addr_t &hints
+){
+ libusb::device_handle::sptr dev_handle(libusb::device_handle::get_cached_handle(
+ boost::static_pointer_cast<libusb::special_handle>(handle)->get_device()
+ ));
+ return sptr(new libusb_zero_copy_impl(
+ dev_handle, recv_interface, recv_endpoint, send_interface, send_endpoint, hints
+ ));
+}
diff --git a/host/lib/transport/super_recv_packet_handler.hpp b/host/lib/transport/super_recv_packet_handler.hpp
new file mode 100644
index 000000000..74fbe82fb
--- /dev/null
+++ b/host/lib/transport/super_recv_packet_handler.hpp
@@ -0,0 +1,582 @@
+//
+// Copyright 2011-2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_TRANSPORT_SUPER_RECV_PACKET_HANDLER_HPP
+#define INCLUDED_LIBUHD_TRANSPORT_SUPER_RECV_PACKET_HANDLER_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/convert.hpp>
+#include <uhd/stream.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/types/metadata.hpp>
+#include <uhd/transport/vrt_if_packet.hpp>
+#include <uhd/transport/zero_copy.hpp>
+#include <boost/dynamic_bitset.hpp>
+#include <boost/foreach.hpp>
+#include <boost/function.hpp>
+#include <boost/format.hpp>
+#include <iostream>
+#include <vector>
+
+namespace uhd{ namespace transport{ namespace sph{
+
+UHD_INLINE boost::uint32_t get_context_code(
+ const boost::uint32_t *vrt_hdr, const vrt::if_packet_info_t &if_packet_info
+){
+ //extract the context word (we dont know the endianness so mirror the bytes)
+ boost::uint32_t word0 = vrt_hdr[if_packet_info.num_header_words32] |
+ uhd::byteswap(vrt_hdr[if_packet_info.num_header_words32]);
+ return word0 & 0xff;
+}
+
+typedef boost::function<void(void)> handle_overflow_type;
+static inline void handle_overflow_nop(void){}
+
+/***********************************************************************
+ * Super receive packet handler
+ *
+ * A receive packet handler represents a group of channels.
+ * The channel group shares a common sample rate.
+ * All channels are received in unison in recv().
+ **********************************************************************/
+class recv_packet_handler{
+public:
+ typedef boost::function<managed_recv_buffer::sptr(double)> get_buff_type;
+ typedef void(*vrt_unpacker_type)(const boost::uint32_t *, vrt::if_packet_info_t &);
+ //typedef boost::function<void(const boost::uint32_t *, vrt::if_packet_info_t &)> vrt_unpacker_type;
+
+ /*!
+ * Make a new packet handler for receive
+ * \param size the number of transport channels
+ */
+ recv_packet_handler(const size_t size = 1):
+ _queue_error_for_next_call(false),
+ _buffers_infos_index(0)
+ {
+ this->resize(size);
+ set_alignment_failure_threshold(1000);
+ }
+
+ //! Resize the number of transport channels
+ void resize(const size_t size){
+ if (this->size() == size) return;
+ _props.resize(size);
+ //re-initialize all buffers infos by re-creating the vector
+ _buffers_infos = std::vector<buffers_info_type>(4, buffers_info_type(size));
+ }
+
+ //! Get the channel width of this handler
+ size_t size(void) const{
+ return _props.size();
+ }
+
+ //! Setup the vrt unpacker function and offset
+ void set_vrt_unpacker(const vrt_unpacker_type &vrt_unpacker, const size_t header_offset_words32 = 0){
+ _vrt_unpacker = vrt_unpacker;
+ _header_offset_words32 = header_offset_words32;
+ }
+
+ /*!
+ * Set the threshold for alignment failure.
+ * How many packets throw out before giving up?
+ * \param threshold number of packets per channel
+ */
+ void set_alignment_failure_threshold(const size_t threshold){
+ _alignment_faulure_threshold = threshold*this->size();
+ }
+
+ //! Set the rate of ticks per second
+ void set_tick_rate(const double rate){
+ _tick_rate = rate;
+ }
+
+ //! Set the rate of samples per second
+ void set_samp_rate(const double rate){
+ _samp_rate = rate;
+ }
+
+ /*!
+ * Set the function to get a managed buffer.
+ * \param xport_chan which transport channel
+ * \param get_buff the getter function
+ */
+ 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));
+ }
+ _props.at(xport_chan).get_buff = get_buff;
+ }
+
+ //! Set the conversion routine for all channels
+ void set_converter(const uhd::convert::id_type &id){
+ _io_buffs.resize(id.num_outputs);
+ _converter = uhd::convert::get_converter(id)();
+ this->set_scale_factor(1/32767.); //update after setting converter
+ _bytes_per_otw_item = uhd::convert::get_bytes_per_item(id.input_format);
+ _bytes_per_cpu_item = uhd::convert::get_bytes_per_item(id.output_format);
+ }
+
+ //! Set the transport channel's overflow handler
+ void set_overflow_handler(const size_t xport_chan, const handle_overflow_type &handle_overflow){
+ _props.at(xport_chan).handle_overflow = handle_overflow;
+ }
+
+ //! Set the scale factor used in float conversion
+ void set_scale_factor(const double scale_factor){
+ _converter->set_scalar(scale_factor);
+ }
+
+ /*******************************************************************
+ * Receive:
+ * The entry point for the fast-path receive calls.
+ * Dispatch into combinations of single packet receive calls.
+ ******************************************************************/
+ UHD_INLINE size_t recv(
+ const uhd::rx_streamer::buffs_type &buffs,
+ const size_t nsamps_per_buff,
+ uhd::rx_metadata_t &metadata,
+ const double timeout,
+ const bool one_packet
+ ){
+ //handle metadata queued from a previous receive
+ if (_queue_error_for_next_call){
+ _queue_error_for_next_call = false;
+ metadata = _queue_metadata;
+ //We want to allow a full buffer recv to be cut short by a timeout,
+ //but do not want to generate an inline timeout message packet.
+ if (_queue_metadata.error_code != rx_metadata_t::ERROR_CODE_TIMEOUT) return 0;
+ }
+
+ size_t accum_num_samps = recv_one_packet(
+ buffs, nsamps_per_buff, metadata, timeout
+ );
+
+ if (one_packet) return accum_num_samps;
+
+ //first recv had an error code set, return immediately
+ if (metadata.error_code != rx_metadata_t::ERROR_CODE_NONE) return accum_num_samps;
+
+ //loop until buffer is filled or error code
+ while(accum_num_samps < nsamps_per_buff){
+ size_t num_samps = recv_one_packet(
+ buffs, nsamps_per_buff - accum_num_samps, _queue_metadata,
+ timeout, accum_num_samps*_bytes_per_cpu_item
+ );
+
+ //metadata had an error code set, store for next call and return
+ if (_queue_metadata.error_code != rx_metadata_t::ERROR_CODE_NONE){
+ _queue_error_for_next_call = true;
+ break;
+ }
+ accum_num_samps += num_samps;
+ }
+ return accum_num_samps;
+ }
+
+private:
+
+ vrt_unpacker_type _vrt_unpacker;
+ size_t _header_offset_words32;
+ double _tick_rate, _samp_rate;
+ bool _queue_error_for_next_call;
+ size_t _alignment_faulure_threshold;
+ rx_metadata_t _queue_metadata;
+ struct xport_chan_props_type{
+ xport_chan_props_type(void):
+ packet_count(0),
+ handle_overflow(&handle_overflow_nop)
+ {}
+ get_buff_type get_buff;
+ size_t packet_count;
+ handle_overflow_type handle_overflow;
+ };
+ std::vector<xport_chan_props_type> _props;
+ std::vector<void *> _io_buffs; //used in conversion
+ size_t _bytes_per_otw_item; //used in conversion
+ size_t _bytes_per_cpu_item; //used in conversion
+ uhd::convert::converter::sptr _converter; //used in conversion
+
+ //! information stored for a received buffer
+ struct per_buffer_info_type{
+ managed_recv_buffer::sptr buff;
+ const boost::uint32_t *vrt_hdr;
+ vrt::if_packet_info_t ifpi;
+ time_spec_t time;
+ const char *copy_buff;
+ };
+
+ //!information stored for a set of aligned buffers
+ struct buffers_info_type : std::vector<per_buffer_info_type> {
+ buffers_info_type(const size_t size):
+ std::vector<per_buffer_info_type>(size),
+ indexes_todo(size, true),
+ alignment_time_valid(false),
+ data_bytes_to_copy(0),
+ fragment_offset_in_samps(0)
+ {/* NOP */}
+ boost::dynamic_bitset<> indexes_todo; //used in alignment logic
+ time_spec_t alignment_time; //used in alignment logic
+ bool alignment_time_valid; //used in alignment logic
+ size_t data_bytes_to_copy; //keeps track of state
+ size_t fragment_offset_in_samps; //keeps track of state
+ rx_metadata_t metadata; //packet description
+ };
+
+ //! a circular queue of buffer infos
+ std::vector<buffers_info_type> _buffers_infos;
+ size_t _buffers_infos_index;
+ buffers_info_type &get_curr_buffer_info(void){return _buffers_infos[_buffers_infos_index];}
+ buffers_info_type &get_prev_buffer_info(void){return _buffers_infos[(_buffers_infos_index + 3)%4];}
+ buffers_info_type &get_next_buffer_info(void){return _buffers_infos[(_buffers_infos_index + 1)%4];}
+ void increment_buffer_info(void){_buffers_infos_index = (_buffers_infos_index + 1)%4;}
+
+ //! possible return options for the packet receiver
+ enum packet_type{
+ PACKET_IF_DATA,
+ PACKET_TIMESTAMP_ERROR,
+ PACKET_INLINE_MESSAGE,
+ PACKET_TIMEOUT_ERROR,
+ PACKET_SEQUENCE_ERROR
+ };
+
+ /*******************************************************************
+ * Get and process a single packet from the transport:
+ * Receive a single packet at the given index.
+ * Extract all the relevant info and store.
+ * Check the info to determine the return code.
+ ******************************************************************/
+ UHD_INLINE packet_type get_and_process_single_packet(
+ const size_t index,
+ buffers_info_type &prev_buffer_info,
+ buffers_info_type &curr_buffer_info,
+ double timeout
+ ){
+ //get a single packet from the transport layer
+ managed_recv_buffer::sptr &buff = curr_buffer_info[index].buff;
+ buff = _props[index].get_buff(timeout);
+ if (buff.get() == NULL) return PACKET_TIMEOUT_ERROR;
+
+ //bounds check before extract
+ size_t num_packet_words32 = buff->size()/sizeof(boost::uint32_t);
+ if (num_packet_words32 <= _header_offset_words32){
+ throw std::runtime_error("recv buffer smaller than vrt packet offset");
+ }
+
+ //extract packet info
+ per_buffer_info_type &info = curr_buffer_info[index];
+ info.ifpi.num_packet_words32 = num_packet_words32 - _header_offset_words32;
+ info.vrt_hdr = buff->cast<const boost::uint32_t *>() + _header_offset_words32;
+ _vrt_unpacker(info.vrt_hdr, info.ifpi);
+ info.time = time_spec_t::from_ticks(info.ifpi.tsf, _tick_rate); //assumes has_tsf is true
+ info.copy_buff = reinterpret_cast<const char *>(info.vrt_hdr + info.ifpi.num_header_words32);
+
+ //--------------------------------------------------------------
+ //-- Determine return conditions:
+ //-- The order of these checks is HOLY.
+ //--------------------------------------------------------------
+
+ //1) check for inline IF message packets
+ if (info.ifpi.packet_type != vrt::if_packet_info_t::PACKET_TYPE_DATA){
+ return PACKET_INLINE_MESSAGE;
+ }
+
+ //2) check for sequence errors
+ #ifndef SRPH_DONT_CHECK_SEQUENCE
+ const size_t expected_packet_count = _props[index].packet_count;
+ _props[index].packet_count = (info.ifpi.packet_count + 1)%16;
+ if (expected_packet_count != info.ifpi.packet_count){
+ return PACKET_SEQUENCE_ERROR;
+ }
+ #endif
+
+ //3) check for out of order timestamps
+ if (info.ifpi.has_tsi and info.ifpi.has_tsf and prev_buffer_info[index].time > info.time){
+ return PACKET_TIMESTAMP_ERROR;
+ }
+
+ //4) otherwise the packet is normal!
+ return PACKET_IF_DATA;
+ }
+
+ /*******************************************************************
+ * Alignment check:
+ * Check the received packet for alignment and mark accordingly.
+ ******************************************************************/
+ UHD_INLINE void alignment_check(
+ const size_t index, buffers_info_type &info
+ ){
+ //if alignment time was not valid or if the sequence id is newer:
+ // use this index's time as the alignment time
+ // reset the indexes list and remove this index
+ if (not info.alignment_time_valid or info[index].time > info.alignment_time){
+ info.alignment_time_valid = true;
+ info.alignment_time = info[index].time;
+ info.indexes_todo.set();
+ info.indexes_todo.reset(index);
+ info.data_bytes_to_copy = info[index].ifpi.num_payload_bytes;
+ }
+
+ //if the sequence id matches:
+ // remove this index from the list and continue
+ else if (info[index].time == info.alignment_time){
+ info.indexes_todo.reset(index);
+ }
+
+ //if the sequence id is older:
+ // continue with the same index to try again
+ //else if (info[index].time < info.alignment_time)...
+ }
+
+ /*******************************************************************
+ * Get aligned buffers:
+ * Iterate through each index and try to accumulate aligned buffers.
+ * Handle all of the edge cases like inline messages and errors.
+ * The logic will throw out older packets until it finds a match.
+ ******************************************************************/
+ UHD_INLINE void get_aligned_buffs(double timeout){
+
+ increment_buffer_info(); //increment to next buffer
+ buffers_info_type &prev_info = get_prev_buffer_info();
+ buffers_info_type &curr_info = get_curr_buffer_info();
+ buffers_info_type &next_info = get_next_buffer_info();
+
+ //Loop until we get a message of an aligned set of buffers:
+ // - Receive a single packet and extract its info.
+ // - Handle the packet type yielded by the receive.
+ // - Check the timestamps for alignment conditions.
+ size_t iterations = 0;
+ while (curr_info.indexes_todo.any()){
+
+ //get the index to process for this iteration
+ const size_t index = curr_info.indexes_todo.find_first();
+ packet_type packet;
+
+ //receive a single packet from the transport
+ try{
+ packet = get_and_process_single_packet(
+ index, prev_info, curr_info, timeout
+ );
+ }
+
+ //handle the case when the get packet throws
+ catch(const std::exception &e){
+ UHD_MSG(error) << boost::format(
+ "The receive packet handler caught an exception.\n%s"
+ ) % e.what() << std::endl;
+ std::swap(curr_info, next_info); //save progress from curr -> next
+ curr_info.metadata.has_time_spec = false;
+ curr_info.metadata.time_spec = time_spec_t(0.0);
+ curr_info.metadata.more_fragments = false;
+ curr_info.metadata.fragment_offset = 0;
+ curr_info.metadata.start_of_burst = false;
+ curr_info.metadata.end_of_burst = false;
+ curr_info.metadata.error_code = rx_metadata_t::ERROR_CODE_BAD_PACKET;
+ return;
+ }
+
+ switch(packet){
+ case PACKET_IF_DATA:
+ alignment_check(index, curr_info);
+ break;
+
+ case PACKET_TIMESTAMP_ERROR:
+ //If the user changes the device time while streaming or without flushing,
+ //we can receive a packet that comes before the previous packet in time.
+ //This could cause the alignment logic to discard future received packets.
+ //Therefore, when this occurs, we reset the info to restart from scratch.
+ if (curr_info.alignment_time_valid and curr_info.alignment_time != curr_info[index].time){
+ curr_info.alignment_time_valid = false;
+ }
+ alignment_check(index, curr_info);
+ break;
+
+ case PACKET_INLINE_MESSAGE:
+ std::swap(curr_info, next_info); //save progress from curr -> next
+ curr_info.metadata.has_time_spec = next_info[index].ifpi.has_tsf;
+ curr_info.metadata.time_spec = next_info[index].time;
+ curr_info.metadata.more_fragments = false;
+ curr_info.metadata.fragment_offset = 0;
+ curr_info.metadata.start_of_burst = false;
+ curr_info.metadata.end_of_burst = false;
+ curr_info.metadata.error_code = rx_metadata_t::error_code_t(get_context_code(next_info[index].vrt_hdr, next_info[index].ifpi));
+ if (curr_info.metadata.error_code == rx_metadata_t::ERROR_CODE_OVERFLOW){
+ _props[index].handle_overflow();
+ UHD_MSG(fastpath) << "O";
+ }
+ return;
+
+ case PACKET_TIMEOUT_ERROR:
+ std::swap(curr_info, next_info); //save progress from curr -> next
+ curr_info.metadata.has_time_spec = false;
+ curr_info.metadata.time_spec = time_spec_t(0.0);
+ curr_info.metadata.more_fragments = false;
+ curr_info.metadata.fragment_offset = 0;
+ curr_info.metadata.start_of_burst = false;
+ curr_info.metadata.end_of_burst = false;
+ curr_info.metadata.error_code = rx_metadata_t::ERROR_CODE_TIMEOUT;
+ return;
+
+ case PACKET_SEQUENCE_ERROR:
+ alignment_check(index, curr_info);
+ std::swap(curr_info, next_info); //save progress from curr -> next
+ curr_info.metadata.has_time_spec = prev_info.metadata.has_time_spec;
+ curr_info.metadata.time_spec = prev_info.metadata.time_spec + time_spec_t::from_ticks(
+ prev_info[index].ifpi.num_payload_words32*sizeof(boost::uint32_t)/_bytes_per_otw_item, _samp_rate);
+ curr_info.metadata.more_fragments = false;
+ curr_info.metadata.fragment_offset = 0;
+ curr_info.metadata.start_of_burst = false;
+ curr_info.metadata.end_of_burst = false;
+ curr_info.metadata.error_code = rx_metadata_t::ERROR_CODE_OVERFLOW;
+ UHD_MSG(fastpath) << "O";
+ return;
+
+ }
+
+ //too many iterations: detect alignment failure
+ if (iterations++ > _alignment_faulure_threshold){
+ UHD_MSG(error) << boost::format(
+ "The receive packet handler failed to time-align packets.\n"
+ "%u received packets were processed by the handler.\n"
+ "However, a timestamp match could not be determined.\n"
+ ) % iterations << std::endl;
+ std::swap(curr_info, next_info); //save progress from curr -> next
+ curr_info.metadata.has_time_spec = false;
+ curr_info.metadata.time_spec = time_spec_t(0.0);
+ curr_info.metadata.more_fragments = false;
+ curr_info.metadata.fragment_offset = 0;
+ curr_info.metadata.start_of_burst = false;
+ curr_info.metadata.end_of_burst = false;
+ curr_info.metadata.error_code = rx_metadata_t::ERROR_CODE_ALIGNMENT;
+ return;
+ }
+
+ }
+
+ //set the metadata from the buffer information at index zero
+ curr_info.metadata.has_time_spec = curr_info[0].ifpi.has_tsf;
+ curr_info.metadata.time_spec = curr_info[0].time;
+ curr_info.metadata.more_fragments = false;
+ curr_info.metadata.fragment_offset = 0;
+ curr_info.metadata.start_of_burst = curr_info[0].ifpi.sob;
+ curr_info.metadata.end_of_burst = curr_info[0].ifpi.eob;
+ curr_info.metadata.error_code = rx_metadata_t::ERROR_CODE_NONE;
+
+ }
+
+ /*******************************************************************
+ * Receive a single packet:
+ * Handles fragmentation, messages, errors, and copy-conversion.
+ * When no fragments are available, call the get aligned buffers.
+ * Then copy-convert available data into the user's IO buffers.
+ ******************************************************************/
+ UHD_INLINE size_t recv_one_packet(
+ const uhd::rx_streamer::buffs_type &buffs,
+ const size_t nsamps_per_buff,
+ uhd::rx_metadata_t &metadata,
+ const double timeout,
+ const size_t buffer_offset_bytes = 0
+ ){
+ //get the next buffer if the current one has expired
+ if (get_curr_buffer_info().data_bytes_to_copy == 0){
+
+ //reset current buffer info members for reuse
+ get_curr_buffer_info().fragment_offset_in_samps = 0;
+ get_curr_buffer_info().alignment_time_valid = false;
+ get_curr_buffer_info().indexes_todo.set();
+
+ //perform receive with alignment logic
+ get_aligned_buffs(timeout);
+ }
+
+ buffers_info_type &info = get_curr_buffer_info();
+ metadata = info.metadata;
+
+ //interpolate the time spec (useful when this is a fragment)
+ metadata.time_spec += time_spec_t::from_ticks(info.fragment_offset_in_samps, _samp_rate);
+
+ //extract the number of samples available to copy
+ const size_t nsamps_available = info.data_bytes_to_copy/_bytes_per_otw_item;
+ const size_t nsamps_to_copy = std::min(nsamps_per_buff*_io_buffs.size(), nsamps_available);
+ const size_t bytes_to_copy = nsamps_to_copy*_bytes_per_otw_item;
+ const size_t nsamps_to_copy_per_io_buff = nsamps_to_copy/_io_buffs.size();
+
+ size_t buff_index = 0;
+ BOOST_FOREACH(per_buffer_info_type &buff_info, info){
+
+ //fill a vector with pointers to the io buffers
+ BOOST_FOREACH(void *&io_buff, _io_buffs){
+ io_buff = reinterpret_cast<char *>(buffs[buff_index++]) + buffer_offset_bytes;
+ }
+
+ //copy-convert the samples from the recv buffer
+ _converter->conv(buff_info.copy_buff, _io_buffs, nsamps_to_copy_per_io_buff);
+
+ //update the rx copy buffer to reflect the bytes copied
+ buff_info.copy_buff += bytes_to_copy;
+ }
+ //update the copy buffer's availability
+ info.data_bytes_to_copy -= bytes_to_copy;
+
+ //setup the fragment flags and offset
+ metadata.more_fragments = info.data_bytes_to_copy != 0;
+ metadata.fragment_offset = info.fragment_offset_in_samps;
+ info.fragment_offset_in_samps += nsamps_to_copy; //set for next call
+
+ //done with buffers? this action releases buffers in-order
+ if (not metadata.more_fragments){
+ BOOST_FOREACH(per_buffer_info_type &buff_info, info){
+ buff_info.buff.reset(); //effectively a release
+ }
+ }
+
+ return nsamps_to_copy_per_io_buff;
+ }
+};
+
+class recv_packet_streamer : public recv_packet_handler, public rx_streamer{
+public:
+ recv_packet_streamer(const size_t max_num_samps){
+ _max_num_samps = max_num_samps;
+ }
+
+ size_t get_num_channels(void) const{
+ return this->size();
+ }
+
+ size_t get_max_num_samps(void) const{
+ return _max_num_samps;
+ }
+
+ size_t recv(
+ const rx_streamer::buffs_type &buffs,
+ const size_t nsamps_per_buff,
+ uhd::rx_metadata_t &metadata,
+ const double timeout,
+ const bool one_packet
+ ){
+ return recv_packet_handler::recv(buffs, nsamps_per_buff, metadata, timeout, one_packet);
+ }
+
+private:
+ size_t _max_num_samps;
+};
+
+}}} //namespace
+
+#endif /* INCLUDED_LIBUHD_TRANSPORT_SUPER_RECV_PACKET_HANDLER_HPP */
diff --git a/host/lib/transport/super_send_packet_handler.hpp b/host/lib/transport/super_send_packet_handler.hpp
new file mode 100644
index 000000000..46c98afea
--- /dev/null
+++ b/host/lib/transport/super_send_packet_handler.hpp
@@ -0,0 +1,282 @@
+//
+// Copyright 2011-2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_TRANSPORT_SUPER_SEND_PACKET_HANDLER_HPP
+#define INCLUDED_LIBUHD_TRANSPORT_SUPER_SEND_PACKET_HANDLER_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/convert.hpp>
+#include <uhd/stream.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/types/metadata.hpp>
+#include <uhd/transport/vrt_if_packet.hpp>
+#include <uhd/transport/zero_copy.hpp>
+#include <boost/thread/thread_time.hpp>
+#include <boost/foreach.hpp>
+#include <boost/function.hpp>
+#include <iostream>
+#include <vector>
+
+namespace uhd{ namespace transport{ namespace sph{
+
+/***********************************************************************
+ * Super send packet handler
+ *
+ * A send packet handler represents a group of channels.
+ * The channel group shares a common sample rate.
+ * All channels are sent in unison in send().
+ **********************************************************************/
+class send_packet_handler{
+public:
+ typedef boost::function<managed_send_buffer::sptr(double)> get_buff_type;
+ typedef void(*vrt_packer_type)(boost::uint32_t *, vrt::if_packet_info_t &);
+ //typedef boost::function<void(boost::uint32_t *, vrt::if_packet_info_t &)> vrt_packer_type;
+
+ /*!
+ * Make a new packet handler for send
+ * \param size the number of transport channels
+ */
+ send_packet_handler(const size_t size = 1):
+ _next_packet_seq(0)
+ {
+ this->resize(size);
+ }
+
+ //! Resize the number of transport channels
+ void resize(const size_t size){
+ if (this->size() == size) return;
+ _props.resize(size);
+ static const boost::uint64_t zero = 0;
+ _zero_buffs.resize(size, &zero);
+ }
+
+ //! Get the channel width of this handler
+ size_t size(void) const{
+ return _props.size();
+ }
+
+ //! Setup the vrt packer function and offset
+ void set_vrt_packer(const vrt_packer_type &vrt_packer, const size_t header_offset_words32 = 0){
+ _vrt_packer = vrt_packer;
+ _header_offset_words32 = header_offset_words32;
+ }
+
+ //! Set the rate of ticks per second
+ void set_tick_rate(const double rate){
+ _tick_rate = rate;
+ }
+
+ //! Set the rate of samples per second
+ void set_samp_rate(const double rate){
+ _samp_rate = rate;
+ }
+
+ /*!
+ * Set the function to get a managed buffer.
+ * \param xport_chan which transport channel
+ * \param get_buff the getter function
+ */
+ void set_xport_chan_get_buff(const size_t xport_chan, const get_buff_type &get_buff){
+ _props.at(xport_chan).get_buff = get_buff;
+ }
+
+ //! Set the conversion routine for all channels
+ void set_converter(const uhd::convert::id_type &id){
+ _io_buffs.resize(id.num_inputs);
+ _converter = uhd::convert::get_converter(id)();
+ this->set_scale_factor(32767.); //update after setting converter
+ _bytes_per_otw_item = uhd::convert::get_bytes_per_item(id.output_format);
+ _bytes_per_cpu_item = uhd::convert::get_bytes_per_item(id.input_format);
+ }
+
+ /*!
+ * Set the maximum number of samples per host packet.
+ * Ex: A USRP1 in dual channel mode would be half.
+ * \param num_samps the maximum samples in a packet
+ */
+ void set_max_samples_per_packet(const size_t num_samps){
+ _max_samples_per_packet = num_samps;
+ }
+
+ //! Set the scale factor used in float conversion
+ void set_scale_factor(const double scale_factor){
+ _converter->set_scalar(scale_factor);
+ }
+
+ /*******************************************************************
+ * Send:
+ * The entry point for the fast-path send calls.
+ * Dispatch into combinations of single packet send calls.
+ ******************************************************************/
+ UHD_INLINE size_t send(
+ const uhd::tx_streamer::buffs_type &buffs,
+ const size_t nsamps_per_buff,
+ const uhd::tx_metadata_t &metadata,
+ const double timeout
+ ){
+ //translate the metadata to vrt if packet info
+ vrt::if_packet_info_t if_packet_info;
+ if_packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_DATA;
+ if_packet_info.has_sid = false;
+ if_packet_info.has_cid = false;
+ if_packet_info.has_tlr = true;
+ if_packet_info.has_tsi = false;
+ if_packet_info.has_tsf = metadata.has_time_spec;
+ if_packet_info.tsf = metadata.time_spec.to_ticks(_tick_rate);
+ if_packet_info.sob = metadata.start_of_burst;
+ if_packet_info.eob = metadata.end_of_burst;
+
+ if (nsamps_per_buff <= _max_samples_per_packet){
+
+ //TODO remove this code when sample counts of zero are supported by hardware
+ #ifndef SSPH_DONT_PAD_TO_ONE
+ if (nsamps_per_buff == 0) return send_one_packet(
+ _zero_buffs, 1, if_packet_info, timeout
+ ) & 0x0;
+ #endif
+
+ return send_one_packet(buffs, nsamps_per_buff, if_packet_info, timeout);
+ }
+ size_t total_num_samps_sent = 0;
+
+ //false until final fragment
+ if_packet_info.eob = false;
+
+ const size_t num_fragments = (nsamps_per_buff-1)/_max_samples_per_packet;
+ const size_t final_length = ((nsamps_per_buff-1)%_max_samples_per_packet)+1;
+
+ //loop through the following fragment indexes
+ for (size_t i = 0; i < num_fragments; i++){
+
+ //send a fragment with the helper function
+ const size_t num_samps_sent = send_one_packet(
+ buffs, _max_samples_per_packet,
+ if_packet_info, timeout,
+ total_num_samps_sent*_bytes_per_cpu_item
+ );
+ total_num_samps_sent += num_samps_sent;
+ if (num_samps_sent == 0) return total_num_samps_sent;
+
+ //setup metadata for the next fragment
+ const time_spec_t time_spec = metadata.time_spec + time_spec_t::from_ticks(total_num_samps_sent, _samp_rate);
+ if_packet_info.tsf = time_spec.to_ticks(_tick_rate);
+ if_packet_info.sob = false;
+
+ }
+
+ //send the final fragment with the helper function
+ if_packet_info.eob = metadata.end_of_burst;
+ return total_num_samps_sent + send_one_packet(
+ buffs, final_length,
+ if_packet_info, timeout,
+ total_num_samps_sent*_bytes_per_cpu_item
+ );
+ }
+
+private:
+
+ vrt_packer_type _vrt_packer;
+ size_t _header_offset_words32;
+ double _tick_rate, _samp_rate;
+ struct xport_chan_props_type{
+ get_buff_type get_buff;
+ };
+ std::vector<xport_chan_props_type> _props;
+ std::vector<const void *> _io_buffs; //used in conversion
+ size_t _bytes_per_otw_item; //used in conversion
+ size_t _bytes_per_cpu_item; //used in conversion
+ uhd::convert::converter::sptr _converter; //used in conversion
+ size_t _max_samples_per_packet;
+ std::vector<const void *> _zero_buffs;
+ size_t _next_packet_seq;
+
+ /*******************************************************************
+ * Send a single packet:
+ ******************************************************************/
+ UHD_INLINE size_t send_one_packet(
+ const uhd::tx_streamer::buffs_type &buffs,
+ const size_t nsamps_per_buff,
+ vrt::if_packet_info_t &if_packet_info,
+ const double timeout,
+ const size_t buffer_offset_bytes = 0
+ ){
+ //load the rest of the if_packet_info in here
+ if_packet_info.num_payload_bytes = nsamps_per_buff*_io_buffs.size()*_bytes_per_otw_item;
+ if_packet_info.num_payload_words32 = (if_packet_info.num_payload_bytes + 3/*round up*/)/sizeof(boost::uint32_t);
+ if_packet_info.packet_count = _next_packet_seq;
+
+ size_t buff_index = 0;
+ BOOST_FOREACH(xport_chan_props_type &props, _props){
+ managed_send_buffer::sptr buff = props.get_buff(timeout);
+ if (buff.get() == NULL) return 0; //timeout
+
+ //fill a vector with pointers to the io buffers
+ BOOST_FOREACH(const void *&io_buff, _io_buffs){
+ io_buff = reinterpret_cast<const char *>(buffs[buff_index++]) + buffer_offset_bytes;
+ }
+ boost::uint32_t *otw_mem = buff->cast<boost::uint32_t *>() + _header_offset_words32;
+
+ //pack metadata into a vrt header
+ _vrt_packer(otw_mem, if_packet_info);
+ otw_mem += if_packet_info.num_header_words32;
+
+ //copy-convert the samples into the send buffer
+ _converter->conv(_io_buffs, otw_mem, nsamps_per_buff);
+
+ //commit the samples to the zero-copy interface
+ size_t num_bytes_total = (_header_offset_words32+if_packet_info.num_packet_words32)*sizeof(boost::uint32_t);
+ buff->commit(num_bytes_total);
+
+ }
+ _next_packet_seq++; //increment sequence after commits
+ return nsamps_per_buff;
+ }
+};
+
+class send_packet_streamer : public send_packet_handler, public tx_streamer{
+public:
+ send_packet_streamer(const size_t max_num_samps){
+ _max_num_samps = max_num_samps;
+ this->set_max_samples_per_packet(_max_num_samps);
+ }
+
+ size_t get_num_channels(void) const{
+ return this->size();
+ }
+
+ size_t get_max_num_samps(void) const{
+ return _max_num_samps;
+ }
+
+ size_t send(
+ const tx_streamer::buffs_type &buffs,
+ const size_t nsamps_per_buff,
+ const uhd::tx_metadata_t &metadata,
+ const double timeout
+ ){
+ return send_packet_handler::send(buffs, nsamps_per_buff, metadata, timeout);
+ }
+
+private:
+ size_t _max_num_samps;
+};
+
+}}} //namespace
+
+#endif /* INCLUDED_LIBUHD_TRANSPORT_SUPER_SEND_PACKET_HANDLER_HPP */
diff --git a/host/lib/transport/udp_common.hpp b/host/lib/transport/udp_common.hpp
new file mode 100644
index 000000000..bf4712613
--- /dev/null
+++ b/host/lib/transport/udp_common.hpp
@@ -0,0 +1,60 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_TRANSPORT_VRT_PACKET_HANDLER_HPP
+#define INCLUDED_LIBUHD_TRANSPORT_VRT_PACKET_HANDLER_HPP
+
+#include <uhd/config.hpp>
+#include <boost/asio.hpp>
+
+namespace uhd{ namespace transport{
+
+ typedef boost::shared_ptr<boost::asio::ip::udp::socket> socket_sptr;
+
+ /*!
+ * Wait for the socket to become ready for a receive operation.
+ * \param sock_fd the open socket file descriptor
+ * \param timeout the timeout duration in seconds
+ * \return true when the socket is ready for receive
+ */
+ UHD_INLINE bool wait_for_recv_ready(int sock_fd, double timeout){
+ //setup timeval for timeout
+ timeval tv;
+ //If the tv_usec > 1 second on some platforms, select will
+ //error EINVAL: An invalid timeout interval was specified.
+ tv.tv_sec = int(timeout);
+ tv.tv_usec = int(timeout*1000000)%1000000;
+
+ //setup rset for timeout
+ fd_set rset;
+ FD_ZERO(&rset);
+ FD_SET(sock_fd, &rset);
+
+ //http://www.gnu.org/s/hello/manual/libc/Interrupted-Primitives.html
+ //This macro is provided with gcc to properly deal with EINTR.
+ //If not provided, define an empty macro, assume that is OK
+ #ifndef TEMP_FAILURE_RETRY
+ #define TEMP_FAILURE_RETRY(x) (x)
+ #endif
+
+ //call select with timeout on receive socket
+ return TEMP_FAILURE_RETRY(::select(sock_fd+1, &rset, NULL, NULL, &tv)) > 0;
+ }
+
+}} //namespace uhd::transport
+
+#endif /* INCLUDED_LIBUHD_TRANSPORT_VRT_PACKET_HANDLER_HPP */
diff --git a/host/lib/transport/udp_simple.cpp b/host/lib/transport/udp_simple.cpp
new file mode 100644
index 000000000..d6c55eae7
--- /dev/null
+++ b/host/lib/transport/udp_simple.cpp
@@ -0,0 +1,134 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "udp_common.hpp"
+#include <uhd/transport/udp_simple.hpp>
+#include <uhd/utils/log.hpp>
+#include <boost/format.hpp>
+
+using namespace uhd::transport;
+namespace asio = boost::asio;
+
+/***********************************************************************
+ * UDP simple implementation: connected and broadcast
+ **********************************************************************/
+class udp_simple_impl : public udp_simple{
+public:
+ udp_simple_impl(
+ const std::string &addr, const std::string &port, bool bcast, bool connect
+ ):_connected(connect){
+ UHD_LOG << boost::format("Creating udp transport for %s %s") % addr % port << std::endl;
+
+ //resolve the address
+ asio::ip::udp::resolver resolver(_io_service);
+ asio::ip::udp::resolver::query query(asio::ip::udp::v4(), addr, port);
+ _send_endpoint = *resolver.resolve(query);
+
+ //create and open the socket
+ _socket = socket_sptr(new asio::ip::udp::socket(_io_service));
+ _socket->open(asio::ip::udp::v4());
+
+ //allow broadcasting
+ _socket->set_option(asio::socket_base::broadcast(bcast));
+
+ //connect the socket
+ if (connect) _socket->connect(_send_endpoint);
+
+ }
+
+ size_t send(const asio::const_buffer &buff){
+ if (_connected) return _socket->send(asio::buffer(buff));
+ return _socket->send_to(asio::buffer(buff), _send_endpoint);
+ }
+
+ size_t recv(const asio::mutable_buffer &buff, double timeout){
+ if (not wait_for_recv_ready(_socket->native(), timeout)) return 0;
+ return _socket->receive_from(asio::buffer(buff), _recv_endpoint);
+ }
+
+ std::string get_recv_addr(void){
+ return _recv_endpoint.address().to_string();
+ }
+
+private:
+ bool _connected;
+ asio::io_service _io_service;
+ socket_sptr _socket;
+ asio::ip::udp::endpoint _send_endpoint;
+ asio::ip::udp::endpoint _recv_endpoint;
+};
+
+/***********************************************************************
+ * UDP public make functions
+ **********************************************************************/
+udp_simple::sptr udp_simple::make_connected(
+ const std::string &addr, const std::string &port
+){
+ return sptr(new udp_simple_impl(addr, port, false, true /* no bcast, connect */));
+}
+
+udp_simple::sptr udp_simple::make_broadcast(
+ const std::string &addr, const std::string &port
+){
+ return sptr(new udp_simple_impl(addr, port, true, false /* bcast, no connect */));
+}
+
+/***********************************************************************
+ * Simple UART over UDP
+ **********************************************************************/
+#include <boost/thread/thread.hpp>
+class udp_simple_uart_impl : public uhd::uart_iface{
+public:
+ udp_simple_uart_impl(udp_simple::sptr udp){
+ _udp = udp;
+ _len = 0;
+ _off = 0;
+ this->write_uart(""); //send an empty packet to init
+ }
+
+ void write_uart(const std::string &buf){
+ _udp->send(asio::buffer(buf));
+ }
+
+ std::string read_uart(double timeout){
+ std::string line;
+ const boost::system_time exit_time = boost::get_system_time() + boost::posix_time::milliseconds(long(timeout*1000));
+ do{
+ //drain anything in current buffer
+ while (_off < _len){
+ const char ch = _buf[_off]; _off++;
+ line += std::string(1, ch);
+ if (ch == '\n' or ch == '\r') return line;
+ }
+
+ //recv a new packet into the buffer
+ _len = _udp->recv(asio::buffer(_buf), std::max((exit_time - boost::get_system_time()).total_milliseconds()/1000., 0.0));
+ _off = 0;
+
+ } while (_len != 0);
+ return line;
+ }
+
+private:
+ udp_simple::sptr _udp;
+ size_t _len, _off;
+ boost::uint8_t _buf[udp_simple::mtu];
+};
+
+uhd::uart_iface::sptr udp_simple::make_uart(sptr udp){
+ return uart_iface::sptr(new udp_simple_uart_impl(udp));
+}
diff --git a/host/lib/transport/udp_zero_copy.cpp b/host/lib/transport/udp_zero_copy.cpp
new file mode 100644
index 000000000..e43a96dd0
--- /dev/null
+++ b/host/lib/transport/udp_zero_copy.cpp
@@ -0,0 +1,312 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "udp_common.hpp"
+#include <uhd/transport/udp_zero_copy.hpp>
+#include <uhd/transport/udp_simple.hpp> //mtu
+#include <uhd/transport/bounded_buffer.hpp>
+#include <uhd/transport/buffer_pool.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/log.hpp>
+#include <boost/format.hpp>
+#include <list>
+
+using namespace uhd;
+using namespace uhd::transport;
+namespace asio = boost::asio;
+
+//A reasonable number of frames for send/recv and async/sync
+static const size_t DEFAULT_NUM_FRAMES = 32;
+
+/***********************************************************************
+ * Check registry for correct fast-path setting (windows only)
+ **********************************************************************/
+#ifdef UHD_PLATFORM_WIN32
+#include <atlbase.h> //CRegKey
+static void check_registry_for_fast_send_threshold(const size_t mtu){
+ static bool warned = false;
+ if (warned) return; //only allow one printed warning per process
+
+ CRegKey reg_key;
+ DWORD threshold = 1024; //system default when threshold is not specified
+ if (
+ reg_key.Open(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\AFD\\Parameters", KEY_READ) != ERROR_SUCCESS or
+ reg_key.QueryDWORDValue("FastSendDatagramThreshold", threshold) != ERROR_SUCCESS or threshold < mtu
+ ){
+ UHD_MSG(warning) << boost::format(
+ "The MTU (%d) is larger than the FastSendDatagramThreshold (%d)!\n"
+ "This will negatively affect the transmit performance.\n"
+ "See the transport application notes for more detail.\n"
+ ) % mtu % threshold << std::endl;
+ warned = true;
+ }
+ reg_key.Close();
+}
+#endif /*UHD_PLATFORM_WIN32*/
+
+/***********************************************************************
+ * Reusable managed receiver buffer:
+ * - Initialize with memory and a release callback.
+ * - Call get new with a length in bytes to re-use.
+ **********************************************************************/
+class udp_zero_copy_asio_mrb : public managed_recv_buffer{
+public:
+ udp_zero_copy_asio_mrb(void *mem, bounded_buffer<udp_zero_copy_asio_mrb *> &pending):
+ _mem(mem), _len(0), _pending(pending){/* NOP */}
+
+ void release(void){
+ if (_len == 0) return;
+ _pending.push_with_haste(this);
+ _len = 0;
+ }
+
+ sptr get_new(size_t len){
+ _len = len;
+ return make_managed_buffer(this);
+ }
+
+ template <class T> T cast(void) const{return static_cast<T>(_mem);}
+
+private:
+ const void *get_buff(void) const{return _mem;}
+ size_t get_size(void) const{return _len;}
+
+ void *_mem;
+ size_t _len;
+ bounded_buffer<udp_zero_copy_asio_mrb *> &_pending;
+};
+
+/***********************************************************************
+ * Reusable managed send buffer:
+ * - Initialize with memory and a commit callback.
+ * - Call get new with a length in bytes to re-use.
+ **********************************************************************/
+class udp_zero_copy_asio_msb : public managed_send_buffer{
+public:
+ udp_zero_copy_asio_msb(void *mem, bounded_buffer<udp_zero_copy_asio_msb *> &pending, int sock_fd):
+ _mem(mem), _len(0), _pending(pending), _sock_fd(sock_fd){/* NOP */}
+
+ void commit(size_t len){
+ if (_len == 0) return;
+ ::send(_sock_fd, this->cast<const char *>(), len, 0);
+ _pending.push_with_haste(this);
+ _len = 0;
+ }
+
+ sptr get_new(size_t len){
+ _len = len;
+ return make_managed_buffer(this);
+ }
+
+private:
+ void *get_buff(void) const{return _mem;}
+ size_t get_size(void) const{return _len;}
+
+ void *_mem;
+ size_t _len;
+ bounded_buffer<udp_zero_copy_asio_msb *> &_pending;
+ int _sock_fd;
+};
+
+/***********************************************************************
+ * Zero Copy UDP implementation with ASIO:
+ * This is the portable zero copy implementation for systems
+ * where a faster, platform specific solution is not available.
+ * However, it is not a true zero copy implementation as each
+ * send and recv requires a copy operation to/from userspace.
+ **********************************************************************/
+class udp_zero_copy_asio_impl : public udp_zero_copy{
+public:
+ typedef boost::shared_ptr<udp_zero_copy_asio_impl> sptr;
+
+ udp_zero_copy_asio_impl(
+ const std::string &addr,
+ const std::string &port,
+ const device_addr_t &hints
+ ):
+ _recv_frame_size(size_t(hints.cast<double>("recv_frame_size", udp_simple::mtu))),
+ _num_recv_frames(size_t(hints.cast<double>("num_recv_frames", DEFAULT_NUM_FRAMES))),
+ _send_frame_size(size_t(hints.cast<double>("send_frame_size", udp_simple::mtu))),
+ _num_send_frames(size_t(hints.cast<double>("num_send_frames", DEFAULT_NUM_FRAMES))),
+ _recv_buffer_pool(buffer_pool::make(_num_recv_frames, _recv_frame_size)),
+ _send_buffer_pool(buffer_pool::make(_num_send_frames, _send_frame_size)),
+ _pending_recv_buffs(_num_recv_frames),
+ _pending_send_buffs(_num_send_frames)
+ {
+ UHD_LOG << boost::format("Creating udp transport for %s %s") % addr % port << std::endl;
+
+ #ifdef UHD_PLATFORM_WIN32
+ check_registry_for_fast_send_threshold(this->get_send_frame_size());
+ #endif /*UHD_PLATFORM_WIN32*/
+
+ //resolve the address
+ asio::ip::udp::resolver resolver(_io_service);
+ asio::ip::udp::resolver::query query(asio::ip::udp::v4(), addr, port);
+ asio::ip::udp::endpoint receiver_endpoint = *resolver.resolve(query);
+
+ //create, open, and connect the socket
+ _socket = socket_sptr(new asio::ip::udp::socket(_io_service));
+ _socket->open(asio::ip::udp::v4());
+ _socket->connect(receiver_endpoint);
+ _sock_fd = _socket->native();
+
+ //allocate re-usable managed receive buffers
+ for (size_t i = 0; i < get_num_recv_frames(); i++){
+ _mrb_pool.push_back(udp_zero_copy_asio_mrb(
+ _recv_buffer_pool->at(i), _pending_recv_buffs
+ ));
+ _pending_recv_buffs.push_with_haste(&_mrb_pool.back());
+ }
+
+ //allocate re-usable managed send buffers
+ for (size_t i = 0; i < get_num_send_frames(); i++){
+ _msb_pool.push_back(udp_zero_copy_asio_msb(
+ _send_buffer_pool->at(i), _pending_send_buffs, _sock_fd
+ ));
+ _pending_send_buffs.push_with_haste(&_msb_pool.back());
+ }
+ }
+
+ //get size for internal socket buffer
+ template <typename Opt> size_t get_buff_size(void) const{
+ Opt option;
+ _socket->get_option(option);
+ return option.value();
+ }
+
+ //set size for internal socket buffer
+ template <typename Opt> size_t resize_buff(size_t num_bytes){
+ Opt option(num_bytes);
+ _socket->set_option(option);
+ return get_buff_size<Opt>();
+ }
+
+ /*******************************************************************
+ * Receive implementation:
+ *
+ * Perform a non-blocking receive for performance,
+ * and then fall back to a blocking receive with timeout.
+ * Return the managed receive buffer with the new length.
+ * When the caller is finished with the managed buffer,
+ * the managed receive buffer is released back into the queue.
+ ******************************************************************/
+ managed_recv_buffer::sptr get_recv_buff(double timeout){
+ udp_zero_copy_asio_mrb *mrb = NULL;
+ if (_pending_recv_buffs.pop_with_timed_wait(mrb, timeout)){
+
+ #ifdef MSG_DONTWAIT //try a non-blocking recv() if supported
+ ssize_t ret = ::recv(_sock_fd, mrb->cast<char *>(), _recv_frame_size, MSG_DONTWAIT);
+ if (ret > 0) return mrb->get_new(ret);
+ #endif
+
+ if (wait_for_recv_ready(_sock_fd, timeout)) return mrb->get_new(
+ ::recv(_sock_fd, mrb->cast<char *>(), _recv_frame_size, 0)
+ );
+
+ _pending_recv_buffs.push_with_haste(mrb); //timeout: return the managed buffer to the queue
+ }
+ return managed_recv_buffer::sptr();
+ }
+
+ size_t get_num_recv_frames(void) const {return _num_recv_frames;}
+ size_t get_recv_frame_size(void) const {return _recv_frame_size;}
+
+ /*******************************************************************
+ * Send implementation:
+ *
+ * Get a managed receive buffer immediately with max length set.
+ * The caller will fill the buffer and commit it when finished.
+ * The commit routine will perform a blocking send operation,
+ * and push the managed send buffer back into the queue.
+ ******************************************************************/
+ managed_send_buffer::sptr get_send_buff(double timeout){
+ udp_zero_copy_asio_msb *msb = NULL;
+ if (_pending_send_buffs.pop_with_timed_wait(msb, timeout)){
+ return msb->get_new(_send_frame_size);
+ }
+ return managed_send_buffer::sptr();
+ }
+
+ size_t get_num_send_frames(void) const {return _num_send_frames;}
+ size_t get_send_frame_size(void) const {return _send_frame_size;}
+
+private:
+ //memory management -> buffers and fifos
+ const size_t _recv_frame_size, _num_recv_frames;
+ const size_t _send_frame_size, _num_send_frames;
+ buffer_pool::sptr _recv_buffer_pool, _send_buffer_pool;
+ bounded_buffer<udp_zero_copy_asio_mrb *> _pending_recv_buffs;
+ bounded_buffer<udp_zero_copy_asio_msb *> _pending_send_buffs;
+ std::list<udp_zero_copy_asio_msb> _msb_pool;
+ std::list<udp_zero_copy_asio_mrb> _mrb_pool;
+
+ //asio guts -> socket and service
+ asio::io_service _io_service;
+ socket_sptr _socket;
+ int _sock_fd;
+};
+
+/***********************************************************************
+ * UDP zero copy make function
+ **********************************************************************/
+template<typename Opt> static void resize_buff_helper(
+ udp_zero_copy_asio_impl::sptr udp_trans,
+ const size_t target_size,
+ const std::string &name
+){
+ std::string help_message;
+ #if defined(UHD_PLATFORM_LINUX)
+ help_message = str(boost::format(
+ "Please run: sudo sysctl -w net.core.%smem_max=%d\n"
+ ) % ((name == "recv")?"r":"w") % target_size);
+ #endif /*defined(UHD_PLATFORM_LINUX)*/
+
+ //resize the buffer if size was provided
+ if (target_size > 0){
+ size_t actual_size = udp_trans->resize_buff<Opt>(target_size);
+ UHD_LOG << boost::format(
+ "Target %s sock buff size: %d bytes\n"
+ "Actual %s sock buff size: %d bytes"
+ ) % name % target_size % name % actual_size << std::endl;
+ if (actual_size < target_size) UHD_MSG(warning) << boost::format(
+ "The %s buffer could not be resized sufficiently.\n"
+ "Target sock buff size: %d bytes.\n"
+ "Actual sock buff size: %d bytes.\n"
+ "See the transport application notes on buffer resizing.\n%s"
+ ) % name % target_size % actual_size % help_message;
+ }
+}
+
+udp_zero_copy::sptr udp_zero_copy::make(
+ const std::string &addr,
+ const std::string &port,
+ const device_addr_t &hints
+){
+ udp_zero_copy_asio_impl::sptr udp_trans(
+ new udp_zero_copy_asio_impl(addr, port, hints)
+ );
+
+ //extract buffer size hints from the device addr
+ size_t recv_buff_size = size_t(hints.cast<double>("recv_buff_size", 0.0));
+ size_t send_buff_size = size_t(hints.cast<double>("send_buff_size", 0.0));
+
+ //call the helper to resize send and recv buffers
+ resize_buff_helper<asio::socket_base::receive_buffer_size>(udp_trans, recv_buff_size, "recv");
+ resize_buff_helper<asio::socket_base::send_buffer_size> (udp_trans, send_buff_size, "send");
+
+ return udp_trans;
+}
diff --git a/host/lib/transport/usb_dummy_impl.cpp b/host/lib/transport/usb_dummy_impl.cpp
new file mode 100644
index 000000000..7be753f76
--- /dev/null
+++ b/host/lib/transport/usb_dummy_impl.cpp
@@ -0,0 +1,38 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/transport/usb_device_handle.hpp>
+#include <uhd/transport/usb_control.hpp>
+#include <uhd/transport/usb_zero_copy.hpp>
+#include <uhd/exception.hpp>
+
+using namespace uhd;
+using namespace uhd::transport;
+
+std::vector<usb_device_handle::sptr> usb_device_handle::get_device_list(boost::uint16_t, boost::uint16_t){
+ return std::vector<usb_device_handle::sptr>(); //empty list
+}
+
+usb_control::sptr usb_control::make(usb_device_handle::sptr, const size_t){
+ throw uhd::not_implemented_error("no usb support -> usb_control::make not implemented");
+}
+
+usb_zero_copy::sptr usb_zero_copy::make(
+ usb_device_handle::sptr, const size_t, const size_t, const size_t, const size_t, const device_addr_t &
+){
+ throw uhd::not_implemented_error("no usb support -> usb_zero_copy::make not implemented");
+}
diff --git a/host/lib/transport/usb_zero_copy_wrapper.cpp b/host/lib/transport/usb_zero_copy_wrapper.cpp
new file mode 100644
index 000000000..690e5aaa2
--- /dev/null
+++ b/host/lib/transport/usb_zero_copy_wrapper.cpp
@@ -0,0 +1,198 @@
+//
+// Copyright 2011-2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/transport/usb_zero_copy.hpp>
+#include <uhd/transport/bounded_buffer.hpp>
+#include <uhd/transport/buffer_pool.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/msg.hpp>
+#include <boost/foreach.hpp>
+#include <vector>
+#include <iostream>
+
+using namespace uhd::transport;
+
+/***********************************************************************
+ * USB zero copy wrapper - managed receive buffer
+ **********************************************************************/
+class usb_zero_copy_wrapper_mrb : public managed_recv_buffer{
+public:
+ usb_zero_copy_wrapper_mrb(bounded_buffer<usb_zero_copy_wrapper_mrb *> &queue):
+ _queue(queue){/*NOP*/}
+
+ void release(void){
+ if (_mrb.get() == NULL) return;
+ _mrb->release();
+ _queue.push_with_haste(this);
+ _mrb.reset();
+ }
+
+ UHD_INLINE sptr get_new(managed_recv_buffer::sptr mrb, const void *mem, size_t len){
+ _mrb = mrb;
+ _mem = mem;
+ _len = len;
+ return make_managed_buffer(this);
+ }
+
+private:
+ const void *get_buff(void) const{return _mem;}
+ size_t get_size(void) const{return _len;}
+
+ bounded_buffer<usb_zero_copy_wrapper_mrb *> &_queue;
+ const void *_mem;
+ size_t _len;
+ managed_recv_buffer::sptr _mrb;
+};
+
+/***********************************************************************
+ * USB zero copy wrapper - managed send buffer
+ **********************************************************************/
+class usb_zero_copy_wrapper_msb : public managed_send_buffer{
+public:
+ usb_zero_copy_wrapper_msb(const usb_zero_copy::sptr internal, const size_t fragmentation_size):
+ _internal(internal), _fragmentation_size(fragmentation_size){/*NOP*/}
+
+ void commit(size_t len){
+ if (len == 0) return;
+
+ //get a reference to the VITA header before incrementing
+ const boost::uint32_t vita_header = reinterpret_cast<const boost::uint32_t *>(_mem_buffer_tip)[0];
+
+ _bytes_in_buffer += len;
+ _mem_buffer_tip += len;
+
+ //extract VITA end of packet flag, we must force flush under eof conditions
+ const bool eop = (uhd::wtohx(vita_header) & (0x1 << 24)) != 0;
+ const bool full = _bytes_in_buffer >= (_last_send_buff->size() - _fragmentation_size);
+ if (eop or full){
+ _last_send_buff->commit(_bytes_in_buffer);
+ _last_send_buff.reset();
+ }
+ }
+
+ UHD_INLINE sptr get_new(const double timeout){
+ if (not _last_send_buff){
+ _last_send_buff = _internal->get_send_buff(timeout);
+ if (not _last_send_buff) return sptr();
+ _mem_buffer_tip = _last_send_buff->cast<char *>();
+ _bytes_in_buffer = 0;
+ }
+ return make_managed_buffer(this);
+ }
+
+private:
+ void *get_buff(void) const{return reinterpret_cast<void *>(_mem_buffer_tip);}
+ size_t get_size(void) const{return _fragmentation_size;}
+
+ usb_zero_copy::sptr _internal;
+ const size_t _fragmentation_size;
+ managed_send_buffer::sptr _last_send_buff;
+ size_t _bytes_in_buffer;
+ char *_mem_buffer_tip;
+};
+
+/***********************************************************************
+ * USB zero copy wrapper implementation
+ **********************************************************************/
+class usb_zero_copy_wrapper : public usb_zero_copy{
+public:
+ usb_zero_copy_wrapper(sptr usb_zc, const size_t frame_boundary):
+ _internal_zc(usb_zc),
+ _frame_boundary(frame_boundary),
+ _available_recv_buffs(this->get_num_recv_frames()),
+ _mrb_pool(this->get_num_recv_frames(), usb_zero_copy_wrapper_mrb(_available_recv_buffs)),
+ _the_only_msb(usb_zero_copy_wrapper_msb(usb_zc, frame_boundary))
+ {
+ BOOST_FOREACH(usb_zero_copy_wrapper_mrb &mrb, _mrb_pool){
+ _available_recv_buffs.push_with_haste(&mrb);
+ }
+ }
+
+ managed_recv_buffer::sptr get_recv_buff(double timeout){
+ //attempt to get a managed recv buffer
+ if (not _last_recv_buff.get()){
+ _last_recv_buff = _internal_zc->get_recv_buff(timeout);
+ _last_recv_offset = 0;
+ }
+
+ //attempt to get a wrapper for a managed recv buffer
+ usb_zero_copy_wrapper_mrb *wmrb = NULL;
+ if (_last_recv_buff.get() and _available_recv_buffs.pop_with_timed_wait(wmrb, timeout)){
+ //extract this packet's memory address and length in bytes
+ const char *mem = _last_recv_buff->cast<const char *>() + _last_recv_offset;
+ const boost::uint32_t *mem32 = reinterpret_cast<const boost::uint32_t *>(mem);
+ const size_t len = (uhd::wtohx(mem32[0]) & 0xffff)*sizeof(boost::uint32_t); //length in bytes (from VRT header)
+
+ managed_recv_buffer::sptr recv_buff; //the buffer to be returned to the user
+ recv_buff = wmrb->get_new(_last_recv_buff, mem, len);
+ _last_recv_offset += len;
+
+ //check if this receive buffer has been exhausted
+ if (_last_recv_offset >= _last_recv_buff->size()) {
+ _last_recv_buff.reset();
+ }
+
+ return recv_buff;
+ }
+
+ //otherwise return a null sptr for failure
+ return managed_recv_buffer::sptr();
+ }
+
+ size_t get_num_recv_frames(void) const{
+ return _internal_zc->get_num_recv_frames();
+ }
+
+ size_t get_recv_frame_size(void) const{
+ return std::min(_frame_boundary, _internal_zc->get_recv_frame_size());
+ }
+
+ managed_send_buffer::sptr get_send_buff(double timeout){
+ return _the_only_msb.get_new(timeout);
+ }
+
+ size_t get_num_send_frames(void) const{
+ return _internal_zc->get_num_send_frames();
+ }
+
+ size_t get_send_frame_size(void) const{
+ return std::min(_frame_boundary, _internal_zc->get_send_frame_size());
+ }
+
+private:
+ sptr _internal_zc;
+ size_t _frame_boundary;
+ bounded_buffer<usb_zero_copy_wrapper_mrb *> _available_recv_buffs;
+ std::vector<usb_zero_copy_wrapper_mrb> _mrb_pool;
+ usb_zero_copy_wrapper_msb _the_only_msb;
+
+ //buffer to store partially-received VRT packets in
+ buffer_pool::sptr _fragment_mem;
+
+ //state for last recv buffer to create multiple managed buffers
+ managed_recv_buffer::sptr _last_recv_buff;
+ size_t _last_recv_offset;
+};
+
+/***********************************************************************
+ * USB zero copy wrapper factory function
+ **********************************************************************/
+usb_zero_copy::sptr usb_zero_copy::make_wrapper(
+ sptr usb_zc, size_t usb_frame_boundary
+){
+ return sptr(new usb_zero_copy_wrapper(usb_zc, usb_frame_boundary));
+}
diff --git a/host/lib/types/CMakeLists.txt b/host/lib/types/CMakeLists.txt
new file mode 100644
index 000000000..2ca0faef7
--- /dev/null
+++ b/host/lib/types/CMakeLists.txt
@@ -0,0 +1,91 @@
+#
+# Copyright 2011 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# Setup defines for high resolution timing
+########################################################################
+MESSAGE(STATUS "")
+MESSAGE(STATUS "Configuring high resolution timing...")
+INCLUDE(CheckCXXSourceCompiles)
+
+SET(CMAKE_REQUIRED_LIBRARIES -lrt)
+CHECK_CXX_SOURCE_COMPILES("
+ #include <ctime>
+ int main(){
+ timespec ts;
+ return clock_gettime(CLOCK_MONOTONIC, &ts);
+ }
+ " HAVE_CLOCK_GETTIME
+)
+UNSET(CMAKE_REQUIRED_LIBRARIES)
+
+INCLUDE(CheckCXXSourceCompiles)
+CHECK_CXX_SOURCE_COMPILES("
+ #include <mach/mach_time.h>
+ int main(){
+ mach_timebase_info_data_t info;
+ mach_timebase_info(&info);
+ mach_absolute_time();
+ return 0;
+ }
+ " HAVE_MACH_ABSOLUTE_TIME
+)
+
+CHECK_CXX_SOURCE_COMPILES("
+ #include <Windows.h>
+ int main(){
+ LARGE_INTEGER value;
+ QueryPerformanceCounter(&value);
+ QueryPerformanceFrequency(&value);
+ return 0;
+ }
+ " HAVE_QUERY_PERFORMANCE_COUNTER
+)
+
+IF(HAVE_CLOCK_GETTIME)
+ MESSAGE(STATUS " High resolution timing supported through clock_gettime.")
+ SET(TIME_SPEC_DEFS HAVE_CLOCK_GETTIME)
+ LIBUHD_APPEND_LIBS("-lrt")
+ELSEIF(HAVE_MACH_ABSOLUTE_TIME)
+ MESSAGE(STATUS " High resolution timing supported through mach_absolute_time.")
+ SET(TIME_SPEC_DEFS HAVE_MACH_ABSOLUTE_TIME)
+ELSEIF(HAVE_QUERY_PERFORMANCE_COUNTER)
+ MESSAGE(STATUS " High resolution timing supported through QueryPerformanceCounter.")
+ SET(TIME_SPEC_DEFS HAVE_QUERY_PERFORMANCE_COUNTER)
+ELSE()
+ MESSAGE(STATUS " High resolution timing supported though microsec_clock.")
+ SET(TIME_SPEC_DEFS HAVE_MICROSEC_CLOCK)
+ENDIF()
+
+SET_SOURCE_FILES_PROPERTIES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/time_spec.cpp
+ PROPERTIES COMPILE_DEFINITIONS "${TIME_SPEC_DEFS}"
+)
+
+########################################################################
+# This file included, use CMake directory variables
+########################################################################
+LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/device_addr.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/mac_addr.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/ranges.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/sensors.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/serial.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/time_spec.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/tune.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/types.cpp
+)
diff --git a/host/lib/types/device_addr.cpp b/host/lib/types/device_addr.cpp
new file mode 100644
index 000000000..1554c3e4e
--- /dev/null
+++ b/host/lib/types/device_addr.cpp
@@ -0,0 +1,133 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/types/device_addr.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/tokenizer.hpp>
+#include <boost/foreach.hpp>
+#include <boost/format.hpp>
+#include <boost/regex.hpp>
+#include <stdexcept>
+#include <sstream>
+
+using namespace uhd;
+
+static const std::string arg_delim = ",";
+static const std::string pair_delim = "=";
+
+static std::string trim(const std::string &in){
+ return boost::algorithm::trim_copy(in);
+}
+
+#define tokenizer(inp, sep) \
+ boost::tokenizer<boost::char_separator<char> > \
+ (inp, boost::char_separator<char>(sep.c_str()))
+
+device_addr_t::device_addr_t(const std::string &args){
+ BOOST_FOREACH(const std::string &pair, tokenizer(args, arg_delim)){
+ if (trim(pair) == "") continue;
+ std::vector<std::string> toks;
+ BOOST_FOREACH(const std::string &tok, tokenizer(pair, pair_delim)){
+ toks.push_back(tok);
+ }
+ if (toks.size() == 1) toks.push_back(""); //pad empty value
+ if (toks.size() == 2 and not trim(toks[0]).empty()){ //only valid combination
+ this->set(trim(toks[0]), trim(toks[1]));
+ }
+ else throw uhd::value_error("invalid args string: "+args); //otherwise error
+ }
+}
+
+std::string device_addr_t::to_pp_string(void) const{
+ if (this->size() == 0) return "Empty Device Address";
+
+ std::stringstream ss;
+ ss << "Device Address:" << std::endl;
+ BOOST_FOREACH(std::string key, this->keys()){
+ ss << boost::format(" %s: %s") % key % this->get(key) << std::endl;
+ }
+ return ss.str();
+}
+
+std::string device_addr_t::to_string(void) const{
+ std::string args_str;
+ size_t count = 0;
+ BOOST_FOREACH(const std::string &key, this->keys()){
+ args_str += ((count++)? arg_delim : "") + key + pair_delim + this->get(key);
+ }
+ return args_str;
+}
+
+#include <uhd/utils/msg.hpp>
+
+device_addrs_t uhd::separate_device_addr(const device_addr_t &dev_addr){
+ //------------ support old deprecated way and print warning --------
+ if (dev_addr.has_key("addr") and not dev_addr["addr"].empty()){
+ std::vector<std::string> addrs; boost::split(addrs, dev_addr["addr"], boost::is_any_of(" "));
+ if (addrs.size() > 1){
+ device_addr_t fixed_dev_addr = dev_addr;
+ fixed_dev_addr.pop("addr");
+ for (size_t i = 0; i < addrs.size(); i++){
+ fixed_dev_addr[str(boost::format("addr%d") % i)] = addrs[i];
+ }
+ UHD_MSG(warning) <<
+ "addr = <space separated list of ip addresses> is deprecated.\n"
+ "To address a multi-device, use multiple <key><index> = <val>.\n"
+ "See the USRP-NXXX application notes. Two device example:\n"
+ " addr0 = 192.168.10.2\n"
+ " addr1 = 192.168.10.3\n"
+ ;
+ return separate_device_addr(fixed_dev_addr);
+ }
+ }
+ //------------------------------------------------------------------
+ device_addrs_t dev_addrs(1); //must be at least one (obviously)
+ std::vector<std::string> global_keys; //keys that apply to all (no numerical suffix)
+ BOOST_FOREACH(const std::string &key, dev_addr.keys()){
+ boost::cmatch matches;
+ if (not boost::regex_match(key.c_str(), matches, boost::regex("^(\\D+)(\\d*)$"))){
+ throw std::runtime_error("unknown key format: " + key);
+ }
+ std::string key_part(matches[1].first, matches[1].second);
+ std::string num_part(matches[2].first, matches[2].second);
+ if (num_part.empty()){ //no number? save it for later
+ global_keys.push_back(key);
+ continue;
+ }
+ const size_t num = boost::lexical_cast<size_t>(num_part);
+ dev_addrs.resize(std::max(num+1, dev_addrs.size()));
+ dev_addrs[num][key_part] = dev_addr[key];
+ }
+
+ //copy the global settings across all device addresses
+ BOOST_FOREACH(device_addr_t &my_dev_addr, dev_addrs){
+ BOOST_FOREACH(const std::string &global_key, global_keys){
+ my_dev_addr[global_key] = dev_addr[global_key];
+ }
+ }
+ return dev_addrs;
+}
+
+device_addr_t uhd::combine_device_addrs(const device_addrs_t &dev_addrs){
+ device_addr_t dev_addr;
+ for (size_t i = 0; i < dev_addrs.size(); i++){
+ BOOST_FOREACH(const std::string &key, dev_addrs[i].keys()){
+ dev_addr[str(boost::format("%s%d") % key % i)] = dev_addrs[i][key];
+ }
+ }
+ return dev_addr;
+}
diff --git a/host/lib/types/mac_addr.cpp b/host/lib/types/mac_addr.cpp
new file mode 100644
index 000000000..a5cb90f97
--- /dev/null
+++ b/host/lib/types/mac_addr.cpp
@@ -0,0 +1,75 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/types/mac_addr.hpp>
+#include <uhd/exception.hpp>
+#include <boost/tokenizer.hpp>
+#include <boost/foreach.hpp>
+#include <boost/format.hpp>
+#include <boost/cstdint.hpp>
+#include <sstream>
+
+using namespace uhd;
+
+mac_addr_t::mac_addr_t(const byte_vector_t &bytes) : _bytes(bytes){
+ UHD_ASSERT_THROW(_bytes.size() == 6);
+}
+
+mac_addr_t mac_addr_t::from_bytes(const byte_vector_t &bytes){
+ return mac_addr_t(bytes);
+}
+
+mac_addr_t mac_addr_t::from_string(const std::string &mac_addr_str){
+
+ byte_vector_t bytes;
+
+ try{
+ if (mac_addr_str.size() != 17){
+ throw uhd::value_error("expected exactly 17 characters");
+ }
+
+ //split the mac addr hex string at the colons
+ boost::tokenizer<boost::char_separator<char> > hex_num_toks(
+ mac_addr_str, boost::char_separator<char>(":"));
+ BOOST_FOREACH(const std::string &hex_str, hex_num_toks){
+ int hex_num;
+ std::istringstream iss(hex_str);
+ iss >> std::hex >> hex_num;
+ bytes.push_back(boost::uint8_t(hex_num));
+ }
+
+ }
+ catch(std::exception const& e){
+ throw uhd::value_error(str(
+ boost::format("Invalid mac address: %s\n\t%s") % mac_addr_str % e.what()
+ ));
+ }
+
+ return mac_addr_t::from_bytes(bytes);
+}
+
+byte_vector_t mac_addr_t::to_bytes(void) const{
+ return _bytes;
+}
+
+std::string mac_addr_t::to_string(void) const{
+ std::string addr = "";
+ BOOST_FOREACH(boost::uint8_t byte, this->to_bytes()){
+ addr += str(boost::format("%s%02x") % ((addr == "")?"":":") % int(byte));
+ }
+ return addr;
+}
diff --git a/host/lib/types/ranges.cpp b/host/lib/types/ranges.cpp
new file mode 100644
index 000000000..6e39bc688
--- /dev/null
+++ b/host/lib/types/ranges.cpp
@@ -0,0 +1,163 @@
+//
+// Copyright 2011-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/types/ranges.hpp>
+#include <uhd/exception.hpp>
+#include <boost/math/special_functions/round.hpp>
+#include <boost/foreach.hpp>
+#include <algorithm>
+#include <sstream>
+
+using namespace uhd;
+
+/***********************************************************************
+ * range_t implementation code
+ **********************************************************************/
+struct range_t::impl{
+ impl(double start, double stop, double step):
+ start(start), stop(stop), step(step)
+ {
+ /* NOP */
+ }
+ double start, stop, step;
+};
+
+range_t::range_t(double value):
+ _impl(UHD_PIMPL_MAKE(impl, (value, value, 0)))
+{
+ /* NOP */
+}
+
+range_t::range_t(
+ double start, double stop, double step
+):
+ _impl(UHD_PIMPL_MAKE(impl, (start, stop, step)))
+{
+ if (stop < start){
+ throw uhd::value_error("cannot make range where stop < start");
+ }
+}
+
+double range_t::start(void) const{
+ return _impl->start;
+}
+
+double range_t::stop(void) const{
+ return _impl->stop;
+}
+
+double range_t::step(void) const{
+ return _impl->step;
+}
+
+const std::string range_t::to_pp_string(void) const{
+ std::stringstream ss;
+ ss << "(" << this->start();
+ if (this->start() != this->stop()) ss << ", " << this->stop();
+ if (this->step() != 0) ss << ", " << this->step();
+ ss << ")";
+ return ss.str();
+}
+
+/***********************************************************************
+ * meta_range_t implementation code
+ **********************************************************************/
+void check_meta_range_monotonic(const meta_range_t &mr){
+ if (mr.empty()){
+ throw uhd::value_error("meta-range cannot be empty");
+ }
+ for (size_t i = 1; i < mr.size(); i++){
+ if (mr.at(i).start() < mr.at(i-1).stop()){
+ throw uhd::value_error("meta-range is not monotonic");
+ }
+ }
+}
+
+meta_range_t::meta_range_t(void){
+ /* NOP */
+}
+
+meta_range_t::meta_range_t(
+ double start, double stop, double step
+):
+ std::vector<range_t > (1, range_t(start, stop, step))
+{
+ /* NOP */
+}
+
+double meta_range_t::start(void) const{
+ check_meta_range_monotonic(*this);
+ double min_start = this->front().start();
+ BOOST_FOREACH(const range_t &r, (*this)){
+ min_start = std::min(min_start, r.start());
+ }
+ return min_start;
+}
+
+double meta_range_t::stop(void) const{
+ check_meta_range_monotonic(*this);
+ double max_stop = this->front().stop();
+ BOOST_FOREACH(const range_t &r, (*this)){
+ max_stop = std::max(max_stop, r.stop());
+ }
+ return max_stop;
+}
+
+double meta_range_t::step(void) const{
+ check_meta_range_monotonic(*this);
+ std::vector<double> non_zero_steps;
+ range_t last = this->front();
+ BOOST_FOREACH(const range_t &r, (*this)){
+ //steps at each range
+ if (r.step() > 0) non_zero_steps.push_back(r.step());
+ //and steps in-between ranges
+ double ibtw_step = r.start() - last.stop();
+ if (ibtw_step > 0) non_zero_steps.push_back(ibtw_step);
+ //store ref to last
+ last = r;
+ }
+ if (non_zero_steps.empty()) return 0; //all zero steps, its zero...
+ return *std::min_element(non_zero_steps.begin(), non_zero_steps.end());
+}
+
+double meta_range_t::clip(double value, bool clip_step) const{
+ check_meta_range_monotonic(*this);
+ double last_stop = this->front().stop();
+ BOOST_FOREACH(const range_t &r, (*this)){
+ //in-between ranges, clip to nearest
+ if (value < r.start()){
+ return (std::abs(value - r.start()) < std::abs(value - last_stop))?
+ r.start() : last_stop;
+ }
+ //in this range, clip here
+ if (value <= r.stop()){
+ if (not clip_step or r.step() == 0) return value;
+ return boost::math::round((value - r.start())/r.step())*r.step() + r.start();
+ }
+ //continue on to the next range
+ last_stop = r.stop();
+ }
+ return last_stop;
+}
+
+const std::string meta_range_t::to_pp_string(void) const{
+ std::stringstream ss;
+ BOOST_FOREACH(const range_t &r, (*this)){
+ ss << r.to_pp_string() << std::endl;
+ }
+ return ss.str();
+}
diff --git a/host/lib/types/sensors.cpp b/host/lib/types/sensors.cpp
new file mode 100644
index 000000000..52a63d14c
--- /dev/null
+++ b/host/lib/types/sensors.cpp
@@ -0,0 +1,94 @@
+//
+// Copyright 2011-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/types/sensors.hpp>
+#include <uhd/exception.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/format.hpp>
+
+using namespace uhd;
+
+sensor_value_t::sensor_value_t(
+ const std::string &name,
+ bool value,
+ const std::string &utrue,
+ const std::string &ufalse
+):
+ name(name), value(value?"true":"false"),
+ unit(value?utrue:ufalse), type(BOOLEAN)
+{
+ /* NOP */
+}
+
+sensor_value_t::sensor_value_t(
+ const std::string &name,
+ signed value,
+ const std::string &unit,
+ const std::string &formatter
+):
+ name(name), value(str(boost::format(formatter) % value)),
+ unit(unit), type(INTEGER)
+{
+ /* NOP */
+}
+
+sensor_value_t::sensor_value_t(
+ const std::string &name,
+ double value,
+ const std::string &unit,
+ const std::string &formatter
+):
+ name(name), value(str(boost::format(formatter) % value)),
+ unit(unit), type(REALNUM)
+{
+ /* NOP */
+}
+
+sensor_value_t::sensor_value_t(
+ const std::string &name,
+ const std::string &value,
+ const std::string &unit
+):
+ name(name), value(value),
+ unit(unit), type(STRING)
+{
+ /* NOP */
+}
+
+std::string sensor_value_t::to_pp_string(void) const{
+ switch(type){
+ case BOOLEAN:
+ return str(boost::format("%s: %s") % name % unit);
+ case INTEGER:
+ case REALNUM:
+ case STRING:
+ return str(boost::format("%s: %s %s") % name % value % unit);
+ }
+ UHD_THROW_INVALID_CODE_PATH();
+}
+
+bool sensor_value_t::to_bool(void) const{
+ return value == "true";
+}
+
+signed sensor_value_t::to_int(void) const{
+ return boost::lexical_cast<signed>(value);
+}
+
+double sensor_value_t::to_real(void) const{
+ return boost::lexical_cast<double>(value);
+}
diff --git a/host/lib/types/serial.cpp b/host/lib/types/serial.cpp
new file mode 100644
index 000000000..9e9d32954
--- /dev/null
+++ b/host/lib/types/serial.cpp
@@ -0,0 +1,78 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/types/serial.hpp>
+#include <boost/thread.hpp> //for sleeping
+#include <boost/assign/list_of.hpp>
+
+using namespace uhd;
+
+spi_config_t::spi_config_t(edge_t edge):
+ mosi_edge(edge),
+ miso_edge(edge)
+{
+ /* NOP */
+}
+
+void i2c_iface::write_eeprom(
+ boost::uint8_t addr,
+ boost::uint8_t offset,
+ const byte_vector_t &bytes
+){
+ for (size_t i = 0; i < bytes.size(); i++){
+ //write a byte at a time, its easy that way
+ byte_vector_t cmd = boost::assign::list_of(offset+i)(bytes[i]);
+ this->write_i2c(addr, cmd);
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10)); //worst case write
+ }
+}
+
+byte_vector_t i2c_iface::read_eeprom(
+ boost::uint8_t addr,
+ boost::uint8_t offset,
+ size_t num_bytes
+){
+ byte_vector_t bytes;
+ for (size_t i = 0; i < num_bytes; i++){
+ //do a zero byte write to start read cycle
+ this->write_i2c(addr, byte_vector_t(1, offset+i));
+ bytes.push_back(this->read_i2c(addr, 1).at(0));
+ }
+ return bytes;
+}
+
+boost::uint32_t spi_iface::read_spi(
+ int which_slave,
+ const spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits
+){
+ return transact_spi(
+ which_slave, config, data, num_bits, true
+ );
+}
+
+void spi_iface::write_spi(
+ int which_slave,
+ const spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits
+){
+ transact_spi(
+ which_slave, config, data, num_bits, false
+ );
+}
diff --git a/host/lib/types/time_spec.cpp b/host/lib/types/time_spec.cpp
new file mode 100644
index 000000000..ec939e52e
--- /dev/null
+++ b/host/lib/types/time_spec.cpp
@@ -0,0 +1,156 @@
+//
+// Copyright 2011-2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/types/time_spec.hpp>
+#include <inttypes.h> //imaxdiv, intmax_t
+
+using namespace uhd;
+
+/***********************************************************************
+ * Time spec system time
+ **********************************************************************/
+
+#ifdef HAVE_CLOCK_GETTIME
+#include <time.h>
+time_spec_t time_spec_t::get_system_time(void){
+ timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts);
+ return time_spec_t(ts.tv_sec, ts.tv_nsec, 1e9);
+}
+#endif /* HAVE_CLOCK_GETTIME */
+
+
+#ifdef HAVE_MACH_ABSOLUTE_TIME
+#include <mach/mach_time.h>
+time_spec_t time_spec_t::get_system_time(void){
+ mach_timebase_info_data_t info; mach_timebase_info(&info);
+ intmax_t nanosecs = mach_absolute_time()*info.numer/info.denom;
+ return time_spec_t::from_ticks(nanosecs, 1e9);
+}
+#endif /* HAVE_MACH_ABSOLUTE_TIME */
+
+
+#ifdef HAVE_QUERY_PERFORMANCE_COUNTER
+#include <Windows.h>
+time_spec_t time_spec_t::get_system_time(void){
+ LARGE_INTEGER counts, freq;
+ QueryPerformanceCounter(&counts);
+ QueryPerformanceFrequency(&freq);
+ return time_spec_t::from_ticks(counts.QuadPart, double(freq.QuadPart));
+}
+#endif /* HAVE_QUERY_PERFORMANCE_COUNTER */
+
+
+#ifdef HAVE_MICROSEC_CLOCK
+#include <boost/date_time/posix_time/posix_time.hpp>
+namespace pt = boost::posix_time;
+time_spec_t time_spec_t::get_system_time(void){
+ pt::ptime time_now = pt::microsec_clock::universal_time();
+ pt::time_duration time_dur = time_now - pt::from_time_t(0);
+ return time_spec_t(
+ time_t(time_dur.total_seconds()),
+ long(time_dur.fractional_seconds()),
+ double(pt::time_duration::ticks_per_second())
+ );
+}
+#endif /* HAVE_MICROSEC_CLOCK */
+
+/***********************************************************************
+ * Time spec constructors
+ **********************************************************************/
+#define time_spec_init(full, frac) { \
+ const time_t _full = time_t(full); \
+ const double _frac = double(frac); \
+ const int _frac_int = int(_frac); \
+ _full_secs = _full + _frac_int; \
+ _frac_secs = _frac - _frac_int; \
+ if (_frac_secs < 0) {\
+ _full_secs -= 1; \
+ _frac_secs += 1; \
+ } \
+}
+
+UHD_INLINE long long fast_llround(const double x){
+ return (long long)(x + 0.5); // assumption of non-negativity
+}
+
+time_spec_t::time_spec_t(double secs){
+ time_spec_init(0, secs);
+}
+
+time_spec_t::time_spec_t(time_t full_secs, double frac_secs){
+ time_spec_init(full_secs, frac_secs);
+}
+
+time_spec_t::time_spec_t(time_t full_secs, long tick_count, double tick_rate){
+ const double frac_secs = tick_count/tick_rate;
+ time_spec_init(full_secs, frac_secs);
+}
+
+time_spec_t time_spec_t::from_ticks(long long ticks, double tick_rate){
+ const imaxdiv_t divres = imaxdiv(ticks, fast_llround(tick_rate));
+ return time_spec_t(time_t(divres.quot), double(divres.rem)/tick_rate);
+}
+
+/***********************************************************************
+ * Time spec accessors
+ **********************************************************************/
+long time_spec_t::get_tick_count(double tick_rate) const{
+ return long(fast_llround(this->get_frac_secs()*tick_rate));
+}
+
+long long time_spec_t::to_ticks(double tick_rate) const{
+ return fast_llround(this->get_frac_secs()*tick_rate) + \
+ (this->get_full_secs() * fast_llround(tick_rate));
+}
+
+double time_spec_t::get_real_secs(void) const{
+ return this->get_full_secs() + this->get_frac_secs();
+}
+
+/***********************************************************************
+ * Time spec math overloads
+ **********************************************************************/
+time_spec_t &time_spec_t::operator+=(const time_spec_t &rhs){
+ time_spec_init(
+ this->get_full_secs() + rhs.get_full_secs(),
+ this->get_frac_secs() + rhs.get_frac_secs()
+ );
+ return *this;
+}
+
+time_spec_t &time_spec_t::operator-=(const time_spec_t &rhs){
+ time_spec_init(
+ this->get_full_secs() - rhs.get_full_secs(),
+ this->get_frac_secs() - rhs.get_frac_secs()
+ );
+ return *this;
+}
+
+bool uhd::operator==(const time_spec_t &lhs, const time_spec_t &rhs){
+ return
+ lhs.get_full_secs() == rhs.get_full_secs() and
+ lhs.get_frac_secs() == rhs.get_frac_secs()
+ ;
+}
+
+bool uhd::operator<(const time_spec_t &lhs, const time_spec_t &rhs){
+ return (
+ (lhs.get_full_secs() < rhs.get_full_secs()) or (
+ (lhs.get_full_secs() == rhs.get_full_secs()) and
+ (lhs.get_frac_secs() < rhs.get_frac_secs())
+ ));
+}
diff --git a/host/lib/types/tune.cpp b/host/lib/types/tune.cpp
new file mode 100644
index 000000000..154f0990f
--- /dev/null
+++ b/host/lib/types/tune.cpp
@@ -0,0 +1,52 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/types/tune_request.hpp>
+#include <uhd/types/tune_result.hpp>
+#include <boost/format.hpp>
+
+using namespace uhd;
+
+tune_request_t::tune_request_t(double target_freq):
+ target_freq(target_freq),
+ rf_freq_policy(POLICY_AUTO),
+ dsp_freq_policy(POLICY_AUTO)
+{
+ /* NOP */
+}
+
+tune_request_t::tune_request_t(double target_freq, double lo_off):
+ target_freq(target_freq),
+ rf_freq_policy(POLICY_MANUAL),
+ rf_freq(target_freq + lo_off),
+ dsp_freq_policy(POLICY_AUTO)
+{
+ /* NOP */
+}
+
+std::string tune_result_t::to_pp_string(void) const{
+ return str(boost::format(
+ "Tune Result:\n"
+ " Target RF Freq: %f (MHz)\n"
+ " Actual RF Freq: %f (MHz)\n"
+ " Target DSP Freq: %f (MHz)\n"
+ " Actual DSP Freq: %f (MHz)\n"
+ )
+ % (target_rf_freq/1e6) % (actual_rf_freq/1e6)
+ % (target_dsp_freq/1e6) % (actual_dsp_freq/1e6)
+ );
+}
diff --git a/host/lib/types/types.cpp b/host/lib/types/types.cpp
new file mode 100644
index 000000000..1a3e7e860
--- /dev/null
+++ b/host/lib/types/types.cpp
@@ -0,0 +1,44 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/types/stream_cmd.hpp>
+#include <uhd/types/metadata.hpp>
+
+using namespace uhd;
+
+/***********************************************************************
+ * stream command
+ **********************************************************************/
+stream_cmd_t::stream_cmd_t(const stream_mode_t &stream_mode):
+ stream_mode(stream_mode),
+ num_samps(0),
+ stream_now(true)
+{
+ /* NOP */
+}
+
+/***********************************************************************
+ * metadata
+ **********************************************************************/
+tx_metadata_t::tx_metadata_t(void):
+ has_time_spec(false),
+ time_spec(time_spec_t()),
+ start_of_burst(false),
+ end_of_burst(false)
+{
+ /* NOP */
+}
diff --git a/host/lib/usrp/CMakeLists.txt b/host/lib/usrp/CMakeLists.txt
new file mode 100644
index 000000000..8ae379f73
--- /dev/null
+++ b/host/lib/usrp/CMakeLists.txt
@@ -0,0 +1,39 @@
+#
+# Copyright 2010-2011 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# This file included, use CMake directory variables
+########################################################################
+LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/dboard_base.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/dboard_eeprom.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/dboard_id.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/dboard_iface.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/dboard_manager.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/gps_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/mboard_eeprom.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/multi_usrp.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/subdev_spec.cpp
+)
+
+INCLUDE_SUBDIRECTORY(cores)
+INCLUDE_SUBDIRECTORY(dboard)
+INCLUDE_SUBDIRECTORY(common)
+INCLUDE_SUBDIRECTORY(usrp1)
+INCLUDE_SUBDIRECTORY(usrp2)
+INCLUDE_SUBDIRECTORY(b100)
+INCLUDE_SUBDIRECTORY(e100)
diff --git a/host/lib/usrp/README b/host/lib/usrp/README
new file mode 100644
index 000000000..344209179
--- /dev/null
+++ b/host/lib/usrp/README
@@ -0,0 +1,15 @@
+########################################################################
+# lib USRP directories:
+########################################################################
+
+dboard:
+ Daughterboard implementation code for all USRP daughterboards
+
+usrp1:
+ Implementation code for the USB-based USRP Classic motherboard.
+
+usrp2:
+ Implementation code for USRP2, USRP-N200, and USRP-N210.
+
+usrp_e100:
+ Implementation code for USRP-E100.
diff --git a/host/lib/usrp/b100/CMakeLists.txt b/host/lib/usrp/b100/CMakeLists.txt
new file mode 100644
index 000000000..1237f52d1
--- /dev/null
+++ b/host/lib/usrp/b100/CMakeLists.txt
@@ -0,0 +1,36 @@
+#
+# Copyright 2011 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# This file included, use CMake directory variables
+########################################################################
+
+########################################################################
+# Conditionally configure the B100 support
+########################################################################
+LIBUHD_REGISTER_COMPONENT("B100" ENABLE_B100 ON "ENABLE_LIBUHD;ENABLE_USB" OFF)
+
+IF(ENABLE_B100)
+ LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/b100_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/b100_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/clock_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/codec_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/dboard_iface.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/io_impl.cpp
+ )
+ENDIF(ENABLE_B100)
diff --git a/host/lib/usrp/b100/b100_ctrl.cpp b/host/lib/usrp/b100/b100_ctrl.cpp
new file mode 100644
index 000000000..e6136c00e
--- /dev/null
+++ b/host/lib/usrp/b100/b100_ctrl.cpp
@@ -0,0 +1,257 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "b100_ctrl.hpp"
+#include <uhd/transport/bounded_buffer.hpp>
+#include <uhd/transport/usb_zero_copy.hpp>
+#include <uhd/transport/zero_copy.hpp>
+#include <uhd/transport/vrt_if_packet.hpp>
+#include <uhd/utils/thread_priority.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/tasks.hpp>
+#include <uhd/types/metadata.hpp>
+#include <uhd/types/serial.hpp>
+#include "ctrl_packet.hpp"
+#include <boost/thread/thread.hpp>
+#include <boost/bind.hpp>
+#include <uhd/exception.hpp>
+
+using namespace uhd::transport;
+using namespace uhd;
+
+bool b100_ctrl_debug = false;
+
+class b100_ctrl_impl : public b100_ctrl {
+public:
+ b100_ctrl_impl(uhd::transport::zero_copy_if::sptr ctrl_transport):
+ sync_ctrl_fifo(2),
+ _ctrl_transport(ctrl_transport),
+ _seq(0)
+ {
+ viking_marauder = task::make(boost::bind(&b100_ctrl_impl::viking_marauder_loop, this));
+ }
+
+ ~b100_ctrl_impl(void){
+ //stop the marauder first so it cant access deconstructed objects
+ viking_marauder.reset();
+ }
+
+ int write(boost::uint32_t addr, const ctrl_data_t &data);
+ ctrl_data_t read(boost::uint32_t addr, size_t len);
+
+ bool get_ctrl_data(ctrl_data_t &pkt_data, double timeout);
+
+ void poke32(wb_addr_type addr, boost::uint32_t data){
+ boost::mutex::scoped_lock lock(_ctrl_mutex);
+
+ ctrl_data_t words(2);
+ words[0] = data & 0x0000FFFF;
+ words[1] = data >> 16;
+ this->write(addr, words);
+ }
+
+ boost::uint32_t peek32(wb_addr_type addr){
+ boost::mutex::scoped_lock lock(_ctrl_mutex);
+
+ ctrl_data_t words = this->read(addr, 2);
+ return boost::uint32_t((boost::uint32_t(words[1]) << 16) | words[0]);
+ }
+
+ void poke16(wb_addr_type addr, boost::uint16_t data){
+ boost::mutex::scoped_lock lock(_ctrl_mutex);
+
+ ctrl_data_t words(1);
+ words[0] = data;
+ this->write(addr, words);
+ }
+
+ boost::uint16_t peek16(wb_addr_type addr){
+ boost::mutex::scoped_lock lock(_ctrl_mutex);
+
+ ctrl_data_t words = this->read(addr, 1);
+ return boost::uint16_t(words[0]);
+ }
+
+ void set_async_cb(const async_cb_type &async_cb){
+ boost::mutex::scoped_lock lock(_async_mutex);
+ _async_cb = async_cb;
+ }
+
+private:
+ int send_pkt(boost::uint16_t *cmd);
+
+ //änd hërë wë gö ä-Vïkïng för äsynchronous control packets
+ void viking_marauder_loop(void);
+ bounded_buffer<ctrl_data_t> sync_ctrl_fifo;
+ async_cb_type _async_cb;
+ task::sptr viking_marauder;
+
+ uhd::transport::zero_copy_if::sptr _ctrl_transport;
+ boost::uint8_t _seq;
+ boost::mutex _ctrl_mutex, _async_mutex;
+};
+
+/***********************************************************************
+ * helper functions for packing/unpacking control packets
+ **********************************************************************/
+void pack_ctrl_pkt(boost::uint16_t *pkt_buff,
+ const ctrl_pkt_t &pkt){
+ //first two bits are OP
+ //next six bits are CALLBACKS
+ //next 8 bits are SEQUENCE
+ //next 16 bits are LENGTH (16-bit word)
+ //next 32 bits are ADDRESS (16-bit word LSW)
+ //then DATA (28 16-bit words)
+ pkt_buff[0] = (boost::uint16_t(pkt.pkt_meta.op) << 14) | (boost::uint16_t(pkt.pkt_meta.callbacks) << 8) | pkt.pkt_meta.seq;
+ pkt_buff[1] = pkt.pkt_meta.len;
+ pkt_buff[2] = (pkt.pkt_meta.addr & 0x00000FFF);
+ pkt_buff[3] = 0x0000; //address high bits always 0 on this device
+
+ for(size_t i = 0; i < pkt.data.size(); i++) {
+ pkt_buff[4+i] = pkt.data[i];
+ }
+}
+
+void unpack_ctrl_pkt(const boost::uint16_t *pkt_buff,
+ ctrl_pkt_t &pkt){
+ pkt.pkt_meta.seq = pkt_buff[0] & 0xFF;
+ pkt.pkt_meta.op = CTRL_PKT_OP_READ; //really this is useless
+ pkt.pkt_meta.len = pkt_buff[1];
+ pkt.pkt_meta.callbacks = 0; //callbacks aren't implemented yet
+ pkt.pkt_meta.addr = pkt_buff[2] | boost::uint32_t(pkt_buff[3] << 16);
+
+ //let's check this so we don't go pushing 64K of crap onto the pkt
+ if(pkt.pkt_meta.len > CTRL_PACKET_DATA_LENGTH) {
+ throw uhd::runtime_error("Received control packet too long");
+ }
+
+ for(int i = 4; i < 4+pkt.pkt_meta.len; i++) pkt.data.push_back(pkt_buff[i]);
+}
+
+int b100_ctrl_impl::send_pkt(boost::uint16_t *cmd) {
+ managed_send_buffer::sptr sbuf = _ctrl_transport->get_send_buff();
+ if(!sbuf.get()) {
+ throw uhd::runtime_error("Control channel send error");
+ }
+
+ //FIXME there's a better way to do this
+ for(size_t i = 0; i < (CTRL_PACKET_LENGTH / sizeof(boost::uint16_t)); i++) {
+ sbuf->cast<boost::uint16_t *>()[i] = cmd[i];
+ }
+ sbuf->commit(CTRL_PACKET_LENGTH); //fixed size transaction
+ return 0;
+}
+
+int b100_ctrl_impl::write(boost::uint32_t addr, const ctrl_data_t &data) {
+ UHD_ASSERT_THROW(data.size() <= (CTRL_PACKET_DATA_LENGTH / sizeof(boost::uint16_t)));
+ ctrl_pkt_t pkt;
+ pkt.data = data;
+ pkt.pkt_meta.op = CTRL_PKT_OP_WRITE;
+ pkt.pkt_meta.callbacks = 0;
+ pkt.pkt_meta.seq = _seq++;
+ pkt.pkt_meta.len = pkt.data.size();
+ pkt.pkt_meta.addr = addr;
+ boost::uint16_t pkt_buff[CTRL_PACKET_LENGTH / sizeof(boost::uint16_t)] = {};
+
+ pack_ctrl_pkt(pkt_buff, pkt);
+ size_t result = send_pkt(pkt_buff);
+ return result;
+}
+
+ctrl_data_t b100_ctrl_impl::read(boost::uint32_t addr, size_t len) {
+ UHD_ASSERT_THROW(len <= (CTRL_PACKET_DATA_LENGTH / sizeof(boost::uint16_t)));
+
+ ctrl_pkt_t pkt;
+ pkt.pkt_meta.op = CTRL_PKT_OP_READ;
+ pkt.pkt_meta.callbacks = 0;
+ pkt.pkt_meta.seq = _seq++;
+ pkt.pkt_meta.len = len;
+ pkt.pkt_meta.addr = addr;
+ boost::uint16_t pkt_buff[CTRL_PACKET_LENGTH / sizeof(boost::uint16_t)] = {};
+
+ //flush anything that might be in the queue
+ while (get_ctrl_data(pkt.data, 0.0)){
+ UHD_MSG(error) << "B100: control read found unexpected packet." << std::endl;
+ }
+
+ pack_ctrl_pkt(pkt_buff, pkt);
+ send_pkt(pkt_buff);
+
+ //block with timeout waiting for the response to appear
+ if (not get_ctrl_data(pkt.data, 0.1)) throw uhd::runtime_error(
+ "B100: timeout waiting for control response packet."
+ );
+
+ return pkt.data;
+}
+
+/***********************************************************************
+ * Viking marauders go pillaging for asynchronous control packets in the
+ * control response endpoint. Sync packets go in sync_ctrl_fifo,
+ * async TX error messages go in async_msg_fifo. sync_ctrl_fifo should
+ * never have more than 1 message in it, since it's expected that we'll
+ * wait for a control operation to finish before starting another one.
+ **********************************************************************/
+void b100_ctrl_impl::viking_marauder_loop(void){
+ set_thread_priority_safe();
+
+ while (not boost::this_thread::interruption_requested()){
+ managed_recv_buffer::sptr rbuf = _ctrl_transport->get_recv_buff(1.0);
+ if(rbuf.get() == NULL) continue; //that's ok, there are plenty of villages to pillage!
+ const boost::uint16_t *pkt_buf = rbuf->cast<const boost::uint16_t *>();
+
+ if(pkt_buf[0] >> 8 == CTRL_PACKET_HEADER_MAGIC) {
+ //so it's got a control packet header, let's parse it.
+ ctrl_pkt_t pkt;
+ unpack_ctrl_pkt(pkt_buf, pkt);
+
+ if(pkt.pkt_meta.seq != boost::uint8_t(_seq - 1)) {
+ UHD_MSG(error)
+ << "Sequence error on control channel." << std::endl
+ << "Exiting control loop." << std::endl
+ ;
+ return;
+ }
+ if(pkt.pkt_meta.len > (CTRL_PACKET_LENGTH - CTRL_PACKET_HEADER_LENGTH)) {
+ UHD_MSG(error)
+ << "Control channel packet length too long" << std::endl
+ << "Exiting control loop." << std::endl
+ ;
+ return;
+ }
+
+ //push it onto the queue
+ sync_ctrl_fifo.push_with_pop_on_full(pkt.data);
+ }
+ else{ //otherwise let the async callback handle it
+ boost::mutex::scoped_lock lock(_async_mutex);
+ if (not _async_cb.empty()) _async_cb(rbuf);
+ }
+ }
+}
+
+bool b100_ctrl_impl::get_ctrl_data(ctrl_data_t &pkt_data, double timeout){
+ boost::this_thread::disable_interruption di; //disable because the wait can throw
+ return sync_ctrl_fifo.pop_with_timed_wait(pkt_data, timeout);
+}
+
+/***********************************************************************
+ * Public make function for b100_ctrl interface
+ **********************************************************************/
+b100_ctrl::sptr b100_ctrl::make(uhd::transport::zero_copy_if::sptr ctrl_transport){
+ return sptr(new b100_ctrl_impl(ctrl_transport));
+}
diff --git a/host/lib/usrp/b100/b100_ctrl.hpp b/host/lib/usrp/b100/b100_ctrl.hpp
new file mode 100644
index 000000000..74884d525
--- /dev/null
+++ b/host/lib/usrp/b100/b100_ctrl.hpp
@@ -0,0 +1,70 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_B100_CTRL_HPP
+#define INCLUDED_B100_CTRL_HPP
+
+#include "wb_iface.hpp"
+#include <uhd/transport/usb_zero_copy.hpp>
+#include <uhd/types/serial.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+#include "ctrl_packet.hpp"
+#include <boost/function.hpp>
+
+class b100_ctrl : boost::noncopyable, public wb_iface{
+public:
+ typedef boost::shared_ptr<b100_ctrl> sptr;
+ typedef boost::function<void(uhd::transport::managed_recv_buffer::sptr)> async_cb_type;
+
+ /*!
+ * Make a USRP control object from a data transport
+ * \param ctrl_transport a USB data transport
+ * \return a new b100 control object
+ */
+ static sptr make(uhd::transport::zero_copy_if::sptr ctrl_transport);
+
+ //! set an async callback for messages
+ virtual void set_async_cb(const async_cb_type &async_cb) = 0;
+
+ /*!
+ * Write a byte vector to an FPGA register
+ * \param addr the FPGA register address
+ * \param bytes the data to write
+ * \return 0 on success, error code on failure
+ */
+ virtual int write(boost::uint32_t addr, const ctrl_data_t &data) = 0;
+
+ /*!
+ * Read a byte vector from an FPGA register (blocking read)
+ * \param addr the FPGA register address
+ * \param len the length of the read
+ * \return a vector of bytes from the register(s) in question
+ */
+ virtual ctrl_data_t read(boost::uint32_t addr, size_t len) = 0;
+
+ /*!
+ * Get a sync ctrl packet (blocking)
+ * \param the packet data buffer
+ * \param the timeout value
+ * \return true if it got something
+ */
+ virtual bool get_ctrl_data(ctrl_data_t &pkt_data, double timeout) = 0;
+
+};
+
+#endif /* INCLUDED_B100_CTRL_HPP */
diff --git a/host/lib/usrp/b100/b100_impl.cpp b/host/lib/usrp/b100/b100_impl.cpp
new file mode 100644
index 000000000..6b888ef67
--- /dev/null
+++ b/host/lib/usrp/b100/b100_impl.cpp
@@ -0,0 +1,565 @@
+//
+// Copyright 2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "apply_corrections.hpp"
+#include "b100_impl.hpp"
+#include "b100_ctrl.hpp"
+#include "fpga_regs_standard.h"
+#include "usrp_i2c_addr.h"
+#include "usrp_commands.h"
+#include <uhd/transport/usb_control.hpp>
+#include "ctrl_packet.hpp"
+#include <uhd/utils/msg.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/utils/static.hpp>
+#include <uhd/utils/images.hpp>
+#include <uhd/utils/safe_call.hpp>
+#include <boost/format.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/lexical_cast.hpp>
+#include "b100_regs.hpp"
+#include <cstdio>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+
+const boost::uint16_t B100_VENDOR_ID = 0x2500;
+const boost::uint16_t B100_PRODUCT_ID = 0x0002;
+const boost::uint16_t FX2_VENDOR_ID = 0x04b4;
+const boost::uint16_t FX2_PRODUCT_ID = 0x8613;
+static const boost::posix_time::milliseconds REENUMERATION_TIMEOUT_MS(3000);
+
+/***********************************************************************
+ * Discovery
+ **********************************************************************/
+static device_addrs_t b100_find(const device_addr_t &hint)
+{
+ device_addrs_t b100_addrs;
+
+ //return an empty list of addresses when type is set to non-b100
+ if (hint.has_key("type") and hint["type"] != "b100") return b100_addrs;
+
+ //Return an empty list of addresses when an address is specified,
+ //since an address is intended for a different, non-USB, device.
+ if (hint.has_key("addr")) return b100_addrs;
+
+ unsigned int vid, pid;
+
+ if(hint.has_key("vid") && hint.has_key("pid") && hint.has_key("type") && hint["type"] == "b100") {
+ sscanf(hint.get("vid").c_str(), "%x", &vid);
+ sscanf(hint.get("pid").c_str(), "%x", &pid);
+ } else {
+ vid = B100_VENDOR_ID;
+ pid = B100_PRODUCT_ID;
+ }
+
+ // Important note:
+ // The get device list calls are nested inside the for loop.
+ // This allows the usb guts to decontruct when not in use,
+ // so that re-enumeration after fw load can occur successfully.
+ // This requirement is a courtesy of libusb1.0 on windows.
+
+ //find the usrps and load firmware
+ size_t found = 0;
+ BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) {
+ //extract the firmware path for the b100
+ std::string b100_fw_image;
+ try{
+ b100_fw_image = find_image_path(hint.get("fw", B100_FW_FILE_NAME));
+ }
+ catch(...){
+ UHD_MSG(warning) << boost::format(
+ "Could not locate B100 firmware.\n"
+ "Please install the images package.\n"
+ );
+ return b100_addrs;
+ }
+ UHD_LOG << "the firmware image: " << b100_fw_image << std::endl;
+
+ usb_control::sptr control;
+ try{control = usb_control::make(handle, 0);}
+ catch(const uhd::exception &){continue;} //ignore claimed
+
+ fx2_ctrl::make(control)->usrp_load_firmware(b100_fw_image);
+ found++;
+ }
+
+ //get descriptors again with serial number, but using the initialized VID/PID now since we have firmware
+ vid = B100_VENDOR_ID;
+ pid = B100_PRODUCT_ID;
+
+ const boost::system_time timeout_time = boost::get_system_time() + REENUMERATION_TIMEOUT_MS;
+
+ //search for the device until found or timeout
+ while (boost::get_system_time() < timeout_time and b100_addrs.empty() and found != 0)
+ {
+ BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid))
+ {
+ usb_control::sptr control;
+ try{control = usb_control::make(handle, 0);}
+ catch(const uhd::exception &){continue;} //ignore claimed
+
+ fx2_ctrl::sptr fx2_ctrl = fx2_ctrl::make(control);
+ const mboard_eeprom_t mb_eeprom = mboard_eeprom_t(*fx2_ctrl, mboard_eeprom_t::MAP_B100);
+ device_addr_t new_addr;
+ new_addr["type"] = "b100";
+ new_addr["name"] = mb_eeprom["name"];
+ new_addr["serial"] = handle->get_serial();
+ //this is a found b100 when the hint serial and name match or blank
+ if (
+ (not hint.has_key("name") or hint["name"] == new_addr["name"]) and
+ (not hint.has_key("serial") or hint["serial"] == new_addr["serial"])
+ ){
+ b100_addrs.push_back(new_addr);
+ }
+ }
+ }
+
+ return b100_addrs;
+}
+
+/***********************************************************************
+ * Make
+ **********************************************************************/
+static device::sptr b100_make(const device_addr_t &device_addr){
+ return device::sptr(new b100_impl(device_addr));
+}
+
+UHD_STATIC_BLOCK(register_b100_device){
+ device::register_device(&b100_find, &b100_make);
+}
+
+/***********************************************************************
+ * Structors
+ **********************************************************************/
+b100_impl::b100_impl(const device_addr_t &device_addr){
+ size_t initialization_count = 0;
+ b100_impl_constructor_begin:
+ initialization_count++;
+
+ _tree = property_tree::make();
+
+ //extract the FPGA path for the B100
+ std::string b100_fpga_image = find_image_path(
+ device_addr.has_key("fpga")? device_addr["fpga"] : B100_FPGA_FILE_NAME
+ );
+
+ //try to match the given device address with something on the USB bus
+ std::vector<usb_device_handle::sptr> device_list =
+ usb_device_handle::get_device_list(B100_VENDOR_ID, B100_PRODUCT_ID);
+
+ //locate the matching handle in the device list
+ usb_device_handle::sptr handle;
+ BOOST_FOREACH(usb_device_handle::sptr dev_handle, device_list) {
+ if (dev_handle->get_serial() == device_addr["serial"]){
+ handle = dev_handle;
+ break;
+ }
+ }
+ UHD_ASSERT_THROW(handle.get() != NULL); //better be found
+
+ //create control objects
+ usb_control::sptr fx2_transport = usb_control::make(handle, 0);
+ _fx2_ctrl = fx2_ctrl::make(fx2_transport);
+ this->check_fw_compat(); //check after making fx2
+ //-- setup clock after making fx2 and before loading fpga --//
+ _clock_ctrl = b100_clock_ctrl::make(_fx2_ctrl, device_addr.cast<double>("master_clock_rate", B100_DEFAULT_TICK_RATE));
+
+ //load FPGA image, gpif is disabled while loading
+ this->enable_gpif(false);
+ _fx2_ctrl->usrp_load_fpga(b100_fpga_image);
+ _fx2_ctrl->usrp_fpga_reset(false); //active low reset
+ _fx2_ctrl->usrp_fpga_reset(true);
+ this->enable_gpif(true);
+
+ //create the control transport
+ device_addr_t ctrl_xport_args;
+ ctrl_xport_args["recv_frame_size"] = boost::lexical_cast<std::string>(CTRL_PACKET_LENGTH);
+ ctrl_xport_args["num_recv_frames"] = "16";
+ ctrl_xport_args["send_frame_size"] = boost::lexical_cast<std::string>(CTRL_PACKET_LENGTH);
+ ctrl_xport_args["num_send_frames"] = "4";
+
+ _ctrl_transport = usb_zero_copy::make(
+ handle,
+ 4, 8, //interface, endpoint
+ 3, 4, //interface, endpoint
+ ctrl_xport_args
+ );
+ while (_ctrl_transport->get_recv_buff(0.0)){} //flush ctrl xport
+
+ ////////////////////////////////////////////////////////////////////
+ // Initialize FPGA wishbone communication
+ ////////////////////////////////////////////////////////////////////
+ _fpga_ctrl = b100_ctrl::make(_ctrl_transport);
+ _fpga_ctrl->poke32(B100_REG_GLOBAL_RESET, 0); //global fpga reset
+ //perform a test peek operation
+ try{
+ _fpga_ctrl->peek32(0);
+ }
+ //try reset once in the case of failure
+ catch(const uhd::exception &){
+ if (initialization_count > 1) throw;
+ UHD_MSG(warning) <<
+ "The control endpoint was left in a bad state.\n"
+ "Attempting endpoint re-enumeration...\n" << std::endl;
+ _fpga_ctrl.reset();
+ _ctrl_transport.reset();
+ _fx2_ctrl->usrp_fx2_reset();
+ goto b100_impl_constructor_begin;
+ }
+ this->check_fpga_compat(); //check after reset and making control
+
+ ////////////////////////////////////////////////////////////////////
+ // Initialize peripherals after reset
+ ////////////////////////////////////////////////////////////////////
+ _fpga_i2c_ctrl = i2c_core_100::make(_fpga_ctrl, B100_REG_SLAVE(3));
+ _fpga_spi_ctrl = spi_core_100::make(_fpga_ctrl, B100_REG_SLAVE(2));
+
+ ////////////////////////////////////////////////////////////////////
+ // Create data transport
+ // This happens after FPGA ctrl instantiated so any junk that might
+ // be in the FPGAs buffers doesn't get pulled into the transport
+ // before being cleared.
+ ////////////////////////////////////////////////////////////////////
+ device_addr_t data_xport_args;
+ data_xport_args["recv_frame_size"] = device_addr.get("recv_frame_size", "16384");
+ data_xport_args["num_recv_frames"] = device_addr.get("num_recv_frames", "16");
+ data_xport_args["send_frame_size"] = device_addr.get("send_frame_size", "16384");
+ data_xport_args["num_send_frames"] = device_addr.get("num_send_frames", "16");
+
+ _data_transport = usb_zero_copy::make_wrapper(
+ usb_zero_copy::make(
+ handle, // identifier
+ 2, 6, // IN interface, endpoint
+ 1, 2, // OUT interface, endpoint
+ data_xport_args // param hints
+ ),
+ B100_MAX_PKT_BYTE_LIMIT
+ );
+ while (_data_transport->get_recv_buff(0.0)){} //flush data xport
+
+ ////////////////////////////////////////////////////////////////////
+ // Initialize the properties tree
+ ////////////////////////////////////////////////////////////////////
+ _tree->create<std::string>("/name").set("B-Series Device");
+ const fs_path mb_path = "/mboards/0";
+ _tree->create<std::string>(mb_path / "name").set("B100 (B-Hundo)");
+ _tree->create<std::string>(mb_path / "load_eeprom")
+ .subscribe(boost::bind(&fx2_ctrl::usrp_load_eeprom, _fx2_ctrl, _1));
+
+ ////////////////////////////////////////////////////////////////////
+ // setup the mboard eeprom
+ ////////////////////////////////////////////////////////////////////
+ const mboard_eeprom_t mb_eeprom(*_fx2_ctrl, mboard_eeprom_t::MAP_B100);
+ _tree->create<mboard_eeprom_t>(mb_path / "eeprom")
+ .set(mb_eeprom)
+ .subscribe(boost::bind(&b100_impl::set_mb_eeprom, this, _1));
+
+ ////////////////////////////////////////////////////////////////////
+ // create clock control objects
+ ////////////////////////////////////////////////////////////////////
+ //^^^ clock created up top, just reg props here... ^^^
+ _tree->create<double>(mb_path / "tick_rate")
+ .publish(boost::bind(&b100_clock_ctrl::get_fpga_clock_rate, _clock_ctrl))
+ .subscribe(boost::bind(&b100_impl::update_tick_rate, this, _1));
+
+ ////////////////////////////////////////////////////////////////////
+ // create codec control objects
+ ////////////////////////////////////////////////////////////////////
+ _codec_ctrl = b100_codec_ctrl::make(_fpga_spi_ctrl);
+ const fs_path rx_codec_path = mb_path / "rx_codecs/A";
+ const fs_path tx_codec_path = mb_path / "tx_codecs/A";
+ _tree->create<std::string>(rx_codec_path / "name").set("ad9522");
+ _tree->create<meta_range_t>(rx_codec_path / "gains/pga/range").set(b100_codec_ctrl::rx_pga_gain_range);
+ _tree->create<double>(rx_codec_path / "gains/pga/value")
+ .coerce(boost::bind(&b100_impl::update_rx_codec_gain, this, _1));
+ _tree->create<std::string>(tx_codec_path / "name").set("ad9522");
+ _tree->create<meta_range_t>(tx_codec_path / "gains/pga/range").set(b100_codec_ctrl::tx_pga_gain_range);
+ _tree->create<double>(tx_codec_path / "gains/pga/value")
+ .subscribe(boost::bind(&b100_codec_ctrl::set_tx_pga_gain, _codec_ctrl, _1))
+ .publish(boost::bind(&b100_codec_ctrl::get_tx_pga_gain, _codec_ctrl));
+
+ ////////////////////////////////////////////////////////////////////
+ // and do the misc mboard sensors
+ ////////////////////////////////////////////////////////////////////
+ _tree->create<sensor_value_t>(mb_path / "sensors/ref_locked")
+ .publish(boost::bind(&b100_impl::get_ref_locked, this));
+
+ ////////////////////////////////////////////////////////////////////
+ // create frontend control objects
+ ////////////////////////////////////////////////////////////////////
+ _rx_fe = rx_frontend_core_200::make(_fpga_ctrl, B100_REG_SR_ADDR(B100_SR_RX_FRONT));
+ _tx_fe = tx_frontend_core_200::make(_fpga_ctrl, B100_REG_SR_ADDR(B100_SR_TX_FRONT));
+
+ _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec")
+ .subscribe(boost::bind(&b100_impl::update_rx_subdev_spec, this, _1));
+ _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec")
+ .subscribe(boost::bind(&b100_impl::update_tx_subdev_spec, this, _1));
+
+ const fs_path rx_fe_path = mb_path / "rx_frontends" / "A";
+ const fs_path tx_fe_path = mb_path / "tx_frontends" / "A";
+
+ _tree->create<std::complex<double> >(rx_fe_path / "dc_offset" / "value")
+ .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, _rx_fe, _1))
+ .set(std::complex<double>(0.0, 0.0));
+ _tree->create<bool>(rx_fe_path / "dc_offset" / "enable")
+ .subscribe(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, _rx_fe, _1))
+ .set(true);
+ _tree->create<std::complex<double> >(rx_fe_path / "iq_balance" / "value")
+ .subscribe(boost::bind(&rx_frontend_core_200::set_iq_balance, _rx_fe, _1))
+ .set(std::polar<double>(1.0, 0.0));
+ _tree->create<std::complex<double> >(tx_fe_path / "dc_offset" / "value")
+ .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, _tx_fe, _1))
+ .set(std::complex<double>(0.0, 0.0));
+ _tree->create<std::complex<double> >(tx_fe_path / "iq_balance" / "value")
+ .subscribe(boost::bind(&tx_frontend_core_200::set_iq_balance, _tx_fe, _1))
+ .set(std::polar<double>(1.0, 0.0));
+
+ ////////////////////////////////////////////////////////////////////
+ // create rx dsp control objects
+ ////////////////////////////////////////////////////////////////////
+ _rx_dsps.push_back(rx_dsp_core_200::make(
+ _fpga_ctrl, B100_REG_SR_ADDR(B100_SR_RX_DSP0), B100_REG_SR_ADDR(B100_SR_RX_CTRL0), B100_RX_SID_BASE + 0
+ ));
+ _rx_dsps.push_back(rx_dsp_core_200::make(
+ _fpga_ctrl, B100_REG_SR_ADDR(B100_SR_RX_DSP1), B100_REG_SR_ADDR(B100_SR_RX_CTRL1), B100_RX_SID_BASE + 1
+ ));
+ for (size_t dspno = 0; dspno < _rx_dsps.size(); dspno++){
+ _rx_dsps[dspno]->set_link_rate(B100_LINK_RATE_BPS);
+ _tree->access<double>(mb_path / "tick_rate")
+ .subscribe(boost::bind(&rx_dsp_core_200::set_tick_rate, _rx_dsps[dspno], _1));
+ fs_path rx_dsp_path = mb_path / str(boost::format("rx_dsps/%u") % dspno);
+ _tree->create<meta_range_t>(rx_dsp_path / "rate/range")
+ .publish(boost::bind(&rx_dsp_core_200::get_host_rates, _rx_dsps[dspno]));
+ _tree->create<double>(rx_dsp_path / "rate/value")
+ .set(1e6) //some default
+ .coerce(boost::bind(&rx_dsp_core_200::set_host_rate, _rx_dsps[dspno], _1))
+ .subscribe(boost::bind(&b100_impl::update_rx_samp_rate, this, dspno, _1));
+ _tree->create<double>(rx_dsp_path / "freq/value")
+ .coerce(boost::bind(&rx_dsp_core_200::set_freq, _rx_dsps[dspno], _1));
+ _tree->create<meta_range_t>(rx_dsp_path / "freq/range")
+ .publish(boost::bind(&rx_dsp_core_200::get_freq_range, _rx_dsps[dspno]));
+ _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd")
+ .subscribe(boost::bind(&rx_dsp_core_200::issue_stream_command, _rx_dsps[dspno], _1));
+ }
+
+ ////////////////////////////////////////////////////////////////////
+ // create tx dsp control objects
+ ////////////////////////////////////////////////////////////////////
+ _tx_dsp = tx_dsp_core_200::make(
+ _fpga_ctrl, B100_REG_SR_ADDR(B100_SR_TX_DSP), B100_REG_SR_ADDR(B100_SR_TX_CTRL), B100_TX_ASYNC_SID
+ );
+ _tx_dsp->set_link_rate(B100_LINK_RATE_BPS);
+ _tree->access<double>(mb_path / "tick_rate")
+ .subscribe(boost::bind(&tx_dsp_core_200::set_tick_rate, _tx_dsp, _1));
+ _tree->create<meta_range_t>(mb_path / "tx_dsps/0/rate/range")
+ .publish(boost::bind(&tx_dsp_core_200::get_host_rates, _tx_dsp));
+ _tree->create<double>(mb_path / "tx_dsps/0/rate/value")
+ .set(1e6) //some default
+ .coerce(boost::bind(&tx_dsp_core_200::set_host_rate, _tx_dsp, _1))
+ .subscribe(boost::bind(&b100_impl::update_tx_samp_rate, this, 0, _1));
+ _tree->create<double>(mb_path / "tx_dsps/0/freq/value")
+ .coerce(boost::bind(&tx_dsp_core_200::set_freq, _tx_dsp, _1));
+ _tree->create<meta_range_t>(mb_path / "tx_dsps/0/freq/range")
+ .publish(boost::bind(&tx_dsp_core_200::get_freq_range, _tx_dsp));
+
+ ////////////////////////////////////////////////////////////////////
+ // create time control objects
+ ////////////////////////////////////////////////////////////////////
+ time64_core_200::readback_bases_type time64_rb_bases;
+ time64_rb_bases.rb_hi_now = B100_REG_RB_TIME_NOW_HI;
+ time64_rb_bases.rb_lo_now = B100_REG_RB_TIME_NOW_LO;
+ time64_rb_bases.rb_hi_pps = B100_REG_RB_TIME_PPS_HI;
+ time64_rb_bases.rb_lo_pps = B100_REG_RB_TIME_PPS_LO;
+ _time64 = time64_core_200::make(
+ _fpga_ctrl, B100_REG_SR_ADDR(B100_SR_TIME64), time64_rb_bases
+ );
+ _tree->access<double>(mb_path / "tick_rate")
+ .subscribe(boost::bind(&time64_core_200::set_tick_rate, _time64, _1));
+ _tree->create<time_spec_t>(mb_path / "time/now")
+ .publish(boost::bind(&time64_core_200::get_time_now, _time64))
+ .subscribe(boost::bind(&time64_core_200::set_time_now, _time64, _1));
+ _tree->create<time_spec_t>(mb_path / "time/pps")
+ .publish(boost::bind(&time64_core_200::get_time_last_pps, _time64))
+ .subscribe(boost::bind(&time64_core_200::set_time_next_pps, _time64, _1));
+ //setup time source props
+ _tree->create<std::string>(mb_path / "time_source/value")
+ .subscribe(boost::bind(&time64_core_200::set_time_source, _time64, _1));
+ _tree->create<std::vector<std::string> >(mb_path / "time_source/options")
+ .publish(boost::bind(&time64_core_200::get_time_sources, _time64));
+ //setup reference source props
+ _tree->create<std::string>(mb_path / "clock_source/value")
+ .subscribe(boost::bind(&b100_impl::update_clock_source, this, _1));
+ static const std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("auto");
+ _tree->create<std::vector<std::string> >(mb_path / "clock_source/options").set(clock_sources);
+
+ ////////////////////////////////////////////////////////////////////
+ // create user-defined control objects
+ ////////////////////////////////////////////////////////////////////
+ _user = user_settings_core_200::make(_fpga_ctrl, B100_REG_SR_ADDR(B100_SR_USER_REGS));
+ _tree->create<user_settings_core_200::user_reg_t>(mb_path / "user/regs")
+ .subscribe(boost::bind(&user_settings_core_200::set_reg, _user, _1));
+
+ ////////////////////////////////////////////////////////////////////
+ // create dboard control objects
+ ////////////////////////////////////////////////////////////////////
+
+ //read the dboard eeprom to extract the dboard ids
+ dboard_eeprom_t rx_db_eeprom, tx_db_eeprom, gdb_eeprom;
+ rx_db_eeprom.load(*_fpga_i2c_ctrl, I2C_ADDR_RX_A);
+ tx_db_eeprom.load(*_fpga_i2c_ctrl, I2C_ADDR_TX_A);
+ gdb_eeprom.load(*_fpga_i2c_ctrl, I2C_ADDR_TX_A ^ 5);
+
+ //create the properties and register subscribers
+ _tree->create<dboard_eeprom_t>(mb_path / "dboards/A/rx_eeprom")
+ .set(rx_db_eeprom)
+ .subscribe(boost::bind(&b100_impl::set_db_eeprom, this, "rx", _1));
+ _tree->create<dboard_eeprom_t>(mb_path / "dboards/A/tx_eeprom")
+ .set(tx_db_eeprom)
+ .subscribe(boost::bind(&b100_impl::set_db_eeprom, this, "tx", _1));
+ _tree->create<dboard_eeprom_t>(mb_path / "dboards/A/gdb_eeprom")
+ .set(gdb_eeprom)
+ .subscribe(boost::bind(&b100_impl::set_db_eeprom, this, "gdb", _1));
+
+ //create a new dboard interface and manager
+ _dboard_iface = make_b100_dboard_iface(_fpga_ctrl, _fpga_i2c_ctrl, _fpga_spi_ctrl, _clock_ctrl, _codec_ctrl);
+ _tree->create<dboard_iface::sptr>(mb_path / "dboards/A/iface").set(_dboard_iface);
+ _dboard_manager = dboard_manager::make(
+ rx_db_eeprom.id, tx_db_eeprom.id, gdb_eeprom.id,
+ _dboard_iface, _tree->subtree(mb_path / "dboards/A")
+ );
+
+ //bind frontend corrections to the dboard freq props
+ const fs_path db_tx_fe_path = mb_path / "dboards" / "A" / "tx_frontends";
+ BOOST_FOREACH(const std::string &name, _tree->list(db_tx_fe_path)){
+ _tree->access<double>(db_tx_fe_path / name / "freq" / "value")
+ .subscribe(boost::bind(&b100_impl::set_tx_fe_corrections, this, _1));
+ }
+ const fs_path db_rx_fe_path = mb_path / "dboards" / "A" / "rx_frontends";
+ BOOST_FOREACH(const std::string &name, _tree->list(db_rx_fe_path)){
+ _tree->access<double>(db_rx_fe_path / name / "freq" / "value")
+ .subscribe(boost::bind(&b100_impl::set_rx_fe_corrections, this, _1));
+ }
+
+ //initialize io handling
+ this->io_init();
+
+ ////////////////////////////////////////////////////////////////////
+ // do some post-init tasks
+ ////////////////////////////////////////////////////////////////////
+ this->update_rates();
+
+ _tree->access<double>(mb_path / "tick_rate") //now subscribe the clock rate setter
+ .subscribe(boost::bind(&b100_clock_ctrl::set_fpga_clock_rate, _clock_ctrl, _1));
+
+ //reset cordic rates and their properties to zero
+ BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "rx_dsps")){
+ _tree->access<double>(mb_path / "rx_dsps" / name / "freq" / "value").set(0.0);
+ }
+ BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "tx_dsps")){
+ _tree->access<double>(mb_path / "tx_dsps" / name / "freq" / "value").set(0.0);
+ }
+
+ _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(subdev_spec_t("A:" + _tree->list(mb_path / "dboards/A/rx_frontends").at(0)));
+ _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(subdev_spec_t("A:" + _tree->list(mb_path / "dboards/A/tx_frontends").at(0)));
+ _tree->access<std::string>(mb_path / "clock_source/value").set("internal");
+ _tree->access<std::string>(mb_path / "time_source/value").set("none");
+}
+
+b100_impl::~b100_impl(void){
+ //set an empty async callback now that we deconstruct
+ _fpga_ctrl->set_async_cb(b100_ctrl::async_cb_type());
+}
+
+void b100_impl::check_fw_compat(void){
+ unsigned char data[4]; //useless data buffer
+ const boost::uint16_t fw_compat_num = _fx2_ctrl->usrp_control_read(
+ VRQ_FW_COMPAT, 0, 0, data, sizeof(data)
+ );
+ if (fw_compat_num != B100_FW_COMPAT_NUM){
+ throw uhd::runtime_error(str(boost::format(
+ "Expected firmware compatibility number 0x%x, but got 0x%x:\n"
+ "The firmware build is not compatible with the host code build."
+ ) % B100_FW_COMPAT_NUM % fw_compat_num));
+ }
+}
+
+void b100_impl::check_fpga_compat(void){
+ const boost::uint32_t fpga_compat_num = _fpga_ctrl->peek32(B100_REG_RB_COMPAT);
+ boost::uint16_t fpga_major = fpga_compat_num >> 16, fpga_minor = fpga_compat_num & 0xffff;
+ if (fpga_major == 0){ //old version scheme
+ fpga_major = fpga_minor;
+ fpga_minor = 0;
+ }
+ if (fpga_major != B100_FPGA_COMPAT_NUM){
+ throw uhd::runtime_error(str(boost::format(
+ "Expected FPGA compatibility number %d, but got %d:\n"
+ "The FPGA build is not compatible with the host code build."
+ ) % int(B100_FPGA_COMPAT_NUM) % fpga_major));
+ }
+ _tree->create<std::string>("/mboards/0/fpga_version").set(str(boost::format("%u.%u") % fpga_major % fpga_minor));
+}
+
+double b100_impl::update_rx_codec_gain(const double gain){
+ //set gain on both I and Q, readback on one
+ //TODO in the future, gains should have individual control
+ _codec_ctrl->set_rx_pga_gain(gain, 'A');
+ _codec_ctrl->set_rx_pga_gain(gain, 'B');
+ return _codec_ctrl->get_rx_pga_gain('A');
+}
+
+void b100_impl::set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &mb_eeprom){
+ mb_eeprom.commit(*_fx2_ctrl, mboard_eeprom_t::MAP_B100);
+}
+
+void b100_impl::set_db_eeprom(const std::string &type, const uhd::usrp::dboard_eeprom_t &db_eeprom){
+ if (type == "rx") db_eeprom.store(*_fpga_i2c_ctrl, I2C_ADDR_RX_A);
+ if (type == "tx") db_eeprom.store(*_fpga_i2c_ctrl, I2C_ADDR_TX_A);
+ if (type == "gdb") db_eeprom.store(*_fpga_i2c_ctrl, I2C_ADDR_TX_A ^ 5);
+}
+
+void b100_impl::update_clock_source(const std::string &source){
+ if (source == "auto") _clock_ctrl->use_auto_ref();
+ else if (source == "internal") _clock_ctrl->use_internal_ref();
+ else if (source == "external") _clock_ctrl->use_external_ref();
+ else throw uhd::runtime_error("unhandled clock configuration reference source: " + source);
+}
+
+////////////////// some GPIF preparation related stuff /////////////////
+void b100_impl::enable_gpif(const bool en) {
+ _fx2_ctrl->usrp_control_write(VRQ_ENABLE_GPIF, en ? 1 : 0, 0, 0, 0);
+}
+
+void b100_impl::clear_fpga_fifo(void) {
+ _fx2_ctrl->usrp_control_write(VRQ_CLEAR_FPGA_FIFO, 0, 0, 0, 0);
+}
+
+sensor_value_t b100_impl::get_ref_locked(void){
+ const bool lock = _clock_ctrl->get_locked();
+ return sensor_value_t("Ref", lock, "locked", "unlocked");
+}
+
+void b100_impl::set_rx_fe_corrections(const double lo_freq){
+ apply_rx_fe_corrections(this->get_tree()->subtree("/mboards/0"), "A", lo_freq);
+}
+
+void b100_impl::set_tx_fe_corrections(const double lo_freq){
+ apply_tx_fe_corrections(this->get_tree()->subtree("/mboards/0"), "A", lo_freq);
+}
diff --git a/host/lib/usrp/b100/b100_impl.hpp b/host/lib/usrp/b100/b100_impl.hpp
new file mode 100644
index 000000000..eab9c750b
--- /dev/null
+++ b/host/lib/usrp/b100/b100_impl.hpp
@@ -0,0 +1,134 @@
+//
+// Copyright 2011-2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_B100_IMPL_HPP
+#define INCLUDED_B100_IMPL_HPP
+
+#include "fx2_ctrl.hpp"
+#include "b100_ctrl.hpp"
+#include "clock_ctrl.hpp"
+#include "codec_ctrl.hpp"
+#include "spi_core_100.hpp"
+#include "i2c_core_100.hpp"
+#include "rx_frontend_core_200.hpp"
+#include "tx_frontend_core_200.hpp"
+#include "rx_dsp_core_200.hpp"
+#include "tx_dsp_core_200.hpp"
+#include "time64_core_200.hpp"
+#include "user_settings_core_200.hpp"
+#include <uhd/device.hpp>
+#include <uhd/property_tree.hpp>
+#include <uhd/utils/pimpl.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/types/sensors.hpp>
+#include <uhd/types/clock_config.hpp>
+#include <uhd/types/stream_cmd.hpp>
+#include <uhd/usrp/mboard_eeprom.hpp>
+#include <uhd/usrp/subdev_spec.hpp>
+#include <uhd/usrp/dboard_eeprom.hpp>
+#include <uhd/usrp/dboard_manager.hpp>
+#include <uhd/transport/usb_zero_copy.hpp>
+#include <boost/weak_ptr.hpp>
+
+static const double B100_LINK_RATE_BPS = 256e6/5; //pratical link rate (< 480 Mbps)
+static const std::string B100_FW_FILE_NAME = "usrp_b100_fw.ihx";
+static const std::string B100_FPGA_FILE_NAME = "usrp_b100_fpga.bin";
+static const boost::uint16_t B100_FW_COMPAT_NUM = 0x03;
+static const boost::uint16_t B100_FPGA_COMPAT_NUM = 0x09;
+static const boost::uint32_t B100_RX_SID_BASE = 2;
+static const boost::uint32_t B100_TX_ASYNC_SID = 1;
+static const double B100_DEFAULT_TICK_RATE = 64e6;
+static const size_t B100_MAX_PKT_BYTE_LIMIT = 2048;
+
+//! Make a b100 dboard interface
+uhd::usrp::dboard_iface::sptr make_b100_dboard_iface(
+ wb_iface::sptr wb_iface,
+ uhd::i2c_iface::sptr i2c_iface,
+ uhd::spi_iface::sptr spi_iface,
+ b100_clock_ctrl::sptr clock,
+ b100_codec_ctrl::sptr codec
+);
+
+//! Implementation guts
+class b100_impl : public uhd::device {
+public:
+ //structors
+ b100_impl(const uhd::device_addr_t &);
+ ~b100_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);
+
+private:
+ uhd::property_tree::sptr _tree;
+
+ //controllers
+ spi_core_100::sptr _fpga_spi_ctrl;
+ i2c_core_100::sptr _fpga_i2c_ctrl;
+ rx_frontend_core_200::sptr _rx_fe;
+ tx_frontend_core_200::sptr _tx_fe;
+ std::vector<rx_dsp_core_200::sptr> _rx_dsps;
+ tx_dsp_core_200::sptr _tx_dsp;
+ time64_core_200::sptr _time64;
+ user_settings_core_200::sptr _user;
+ b100_clock_ctrl::sptr _clock_ctrl;
+ b100_codec_ctrl::sptr _codec_ctrl;
+ b100_ctrl::sptr _fpga_ctrl;
+ uhd::usrp::fx2_ctrl::sptr _fx2_ctrl;
+
+ //transports
+ uhd::transport::zero_copy_if::sptr _data_transport, _ctrl_transport;
+
+ //dboard stuff
+ uhd::usrp::dboard_manager::sptr _dboard_manager;
+ uhd::usrp::dboard_iface::sptr _dboard_iface;
+
+ //handle io stuff
+ UHD_PIMPL_DECL(io_impl) _io_impl;
+ void io_init(void);
+
+ //device properties interface
+ uhd::property_tree::sptr get_tree(void) const{
+ return _tree;
+ }
+
+ std::vector<boost::weak_ptr<uhd::rx_streamer> > _rx_streamers;
+ std::vector<boost::weak_ptr<uhd::tx_streamer> > _tx_streamers;
+
+ void check_fw_compat(void);
+ void check_fpga_compat(void);
+ double update_rx_codec_gain(const double); //sets A and B at once
+ void set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &);
+ void set_db_eeprom(const std::string &, const uhd::usrp::dboard_eeprom_t &);
+ void update_tick_rate(const double rate);
+ void update_rx_samp_rate(const size_t, const double rate);
+ void update_tx_samp_rate(const size_t, const double rate);
+ void update_rates(void);
+ void update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &);
+ void update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &);
+ void update_clock_source(const std::string &);
+ void enable_gpif(const bool);
+ void clear_fpga_fifo(void);
+ void handle_async_message(uhd::transport::managed_recv_buffer::sptr);
+ uhd::sensor_value_t get_ref_locked(void);
+ void set_rx_fe_corrections(const double);
+ void set_tx_fe_corrections(const double);
+};
+
+#endif /* INCLUDED_b100_IMPL_HPP */
diff --git a/host/lib/usrp/b100/b100_regs.hpp b/host/lib/usrp/b100/b100_regs.hpp
new file mode 100644
index 000000000..987a09f03
--- /dev/null
+++ b/host/lib/usrp/b100/b100_regs.hpp
@@ -0,0 +1,123 @@
+//
+// Copyright 2010-2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+////////////////////////////////////////////////////////////////
+//
+// Memory map for wishbone bus
+//
+////////////////////////////////////////////////////////////////
+
+// All addresses are byte addresses. All accesses are word (16-bit) accesses.
+// This means that address bit 0 is usually 0.
+// There are 11 bits of address for the control.
+
+#ifndef INCLUDED_B100_REGS_HPP
+#define INCLUDED_B100_REGS_HPP
+
+/////////////////////////////////////////////////////
+// Slave pointers
+
+#define B100_REG_SLAVE(n) ((n)<<7)
+
+/////////////////////////////////////////////////////
+// Slave 0 -- Misc Regs
+
+#define B100_REG_MISC_BASE B100_REG_SLAVE(0)
+
+#define B100_REG_MISC_LED B100_REG_MISC_BASE + 0
+#define B100_REG_MISC_SW B100_REG_MISC_BASE + 2
+#define B100_REG_MISC_CGEN_CTRL B100_REG_MISC_BASE + 4
+#define B100_REG_MISC_CGEN_ST B100_REG_MISC_BASE + 6
+
+/////////////////////////////////////////////////////
+// Slave 1 -- UART
+// CLKDIV is 16 bits, others are only 8
+
+#define B100_REG_UART_BASE B100_REG_SLAVE(1)
+
+#define B100_REG_UART_CLKDIV B100_REG_UART_BASE + 0
+#define B100_REG_UART_TXLEVEL B100_REG_UART_BASE + 2
+#define B100_REG_UART_RXLEVEL B100_REG_UART_BASE + 4
+#define B100_REG_UART_TXCHAR B100_REG_UART_BASE + 6
+#define B100_REG_UART_RXCHAR B100_REG_UART_BASE + 8
+
+/////////////////////////////////////////////////////
+// Slave 2 -- SPI Core
+//these are 32-bit registers mapped onto the 16-bit Wishbone bus.
+//Using peek32/poke32 should allow transparent use of these registers.
+#define B100_REG_SPI_BASE B100_REG_SLAVE(2)
+
+//spi slave constants
+#define B100_SPI_SS_AD9862 (1 << 2)
+#define B100_SPI_SS_TX_DB (1 << 1)
+#define B100_SPI_SS_RX_DB (1 << 0)
+
+////////////////////////////////////////////////
+// Slave 3 -- I2C Core
+
+#define B100_REG_I2C_BASE B100_REG_SLAVE(3)
+
+///////////////////////////////////////////////////
+// Slave 7 -- Readback Mux 32
+
+#define B100_REG_RB_MUX_32_BASE B100_REG_SLAVE(7)
+
+#define B100_REG_RB_TIME_NOW_HI B100_REG_RB_MUX_32_BASE + 0
+#define B100_REG_RB_TIME_NOW_LO B100_REG_RB_MUX_32_BASE + 4
+#define B100_REG_RB_TIME_PPS_HI B100_REG_RB_MUX_32_BASE + 8
+#define B100_REG_RB_TIME_PPS_LO B100_REG_RB_MUX_32_BASE + 12
+#define B100_REG_RB_MISC_TEST32 B100_REG_RB_MUX_32_BASE + 16
+#define B100_REG_RB_COMPAT B100_REG_RB_MUX_32_BASE + 24
+#define B100_REG_RB_GPIO B100_REG_RB_MUX_32_BASE + 28
+
+////////////////////////////////////////////////////
+// Slaves 8 & 9 -- Settings Bus
+//
+// Output-only, no readback, 64 registers total
+// Each register must be written 32 bits at a time
+// First the address xxx_xx00 and then xxx_xx10
+// 64 total regs in address space
+#define B100_SR_RX_CTRL0 0 // 9 regs (+0 to +8)
+#define B100_SR_RX_DSP0 10 // 4 regs (+0 to +3)
+#define B100_SR_RX_CTRL1 16 // 9 regs (+0 to +8)
+#define B100_SR_RX_DSP1 26 // 4 regs (+0 to +3)
+#define B100_SR_TX_CTRL 32 // 4 regs (+0 to +3)
+#define B100_SR_TX_DSP 38 // 3 regs (+0 to +2)
+
+#define B100_SR_TIME64 42 // 6 regs (+0 to +5)
+#define B100_SR_RX_FRONT 48 // 5 regs (+0 to +4)
+#define B100_SR_TX_FRONT 54 // 5 regs (+0 to +4)
+
+#define B100_SR_REG_TEST32 60 // 1 reg
+#define B100_SR_CLEAR_FIFO 61 // 1 reg
+#define B100_SR_GLOBAL_RESET 63 // 1 reg
+#define B100_SR_USER_REGS 64 // 2 regs
+
+#define B100_SR_GPIO 128
+
+#define B100_REG_SR_ADDR(n) (B100_REG_SLAVE(8) + (4*(n)))
+
+#define B100_REG_SR_MISC_TEST32 B100_REG_SR_ADDR(B100_SR_REG_TEST32)
+
+/////////////////////////////////////////////////
+// Magic reset regs
+////////////////////////////////////////////////
+#define B100_REG_CLEAR_FIFO B100_REG_SR_ADDR(B100_SR_CLEAR_FIFO)
+#define B100_REG_GLOBAL_RESET B100_REG_SR_ADDR(B100_SR_GLOBAL_RESET)
+
+#endif
+
diff --git a/host/lib/usrp/b100/clock_ctrl.cpp b/host/lib/usrp/b100/clock_ctrl.cpp
new file mode 100644
index 000000000..cbe6c40a0
--- /dev/null
+++ b/host/lib/usrp/b100/clock_ctrl.cpp
@@ -0,0 +1,536 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "clock_ctrl.hpp"
+#include "ad9522_regs.hpp"
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/utils/assert_has.hpp>
+#include <uhd/utils/safe_call.hpp>
+#include <boost/cstdint.hpp>
+#include "b100_regs.hpp" //spi slave constants
+#include <boost/assign/list_of.hpp>
+#include <boost/foreach.hpp>
+#include <boost/format.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/math/common_factor_rt.hpp> //gcd
+#include <algorithm>
+#include <utility>
+
+using namespace uhd;
+
+/***********************************************************************
+ * Constants
+ **********************************************************************/
+static const bool ENABLE_THE_TEST_OUT = true;
+static const double REFERENCE_INPUT_RATE = 10e6;
+
+/***********************************************************************
+ * Helpers
+ **********************************************************************/
+template <typename div_type, typename bypass_type> static void set_clock_divider(
+ size_t divider, div_type &low, div_type &high, bypass_type &bypass
+){
+ high = divider/2 - 1;
+ low = divider - high - 2;
+ bypass = (divider == 1)? 1 : 0;
+}
+
+/***********************************************************************
+ * Clock rate calculation stuff:
+ * Using the internal VCO between 1400 and 1800 MHz
+ **********************************************************************/
+struct clock_settings_type{
+ size_t ref_clock_doubler, r_counter, a_counter, b_counter, prescaler, vco_divider, chan_divider;
+ size_t get_n_counter(void) const{return prescaler * b_counter + a_counter;}
+ double get_ref_rate(void) const{return REFERENCE_INPUT_RATE * ref_clock_doubler;}
+ double get_vco_rate(void) const{return get_ref_rate()/r_counter * get_n_counter();}
+ double get_chan_rate(void) const{return get_vco_rate()/vco_divider;}
+ double get_out_rate(void) const{return get_chan_rate()/chan_divider;}
+ std::string to_pp_string(void) const{
+ return str(boost::format(
+ " r_counter: %d\n"
+ " a_counter: %d\n"
+ " b_counter: %d\n"
+ " prescaler: %d\n"
+ " vco_divider: %d\n"
+ " chan_divider: %d\n"
+ " vco_rate: %fMHz\n"
+ " chan_rate: %fMHz\n"
+ " out_rate: %fMHz\n"
+ )
+ % r_counter
+ % a_counter
+ % b_counter
+ % prescaler
+ % vco_divider
+ % chan_divider
+ % (get_vco_rate()/1e6)
+ % (get_chan_rate()/1e6)
+ % (get_out_rate()/1e6)
+ );
+ }
+};
+
+//! gives the greatest divisor of num between 1 and max inclusive
+template<typename T> static inline T greatest_divisor(T num, T max){
+ for (T i = max; i > 1; i--) if (num%i == 0) return i; return 1;
+}
+
+//! gives the least divisor of num between min and num exclusive
+template<typename T> static inline T least_divisor(T num, T min){
+ for (T i = min; i < num; i++) if (num%i == 0) return i; return 1;
+}
+
+static clock_settings_type get_clock_settings(double rate){
+ clock_settings_type cs;
+ cs.ref_clock_doubler = 2; //always doubling
+ cs.prescaler = 8; //set to 8 when input is under 2400 MHz
+
+ //basic formulas used below:
+ //out_rate*X = ref_rate*Y
+ //X = i*ref_rate/gcd
+ //Y = i*out_rate/gcd
+ //X = chan_div * vco_div * R
+ //Y = P*B + A
+
+ const boost::uint64_t out_rate = boost::uint64_t(rate);
+ const boost::uint64_t ref_rate = boost::uint64_t(cs.get_ref_rate());
+ const size_t gcd = size_t(boost::math::gcd(ref_rate, out_rate));
+
+ for (size_t i = 1; i <= 100; i++){
+ const size_t X = i*ref_rate/gcd;
+ const size_t Y = i*out_rate/gcd;
+
+ //determine A and B (P is fixed)
+ cs.b_counter = Y/cs.prescaler;
+ cs.a_counter = Y - cs.b_counter*cs.prescaler;
+
+ static const double vco_bound_pad = 100e6;
+ for ( //calculate an r divider that fits into the bounds of the vco
+ cs.r_counter = size_t(cs.get_n_counter()*cs.get_ref_rate()/(1800e6 - vco_bound_pad));
+ cs.r_counter <= size_t(cs.get_n_counter()*cs.get_ref_rate()/(1400e6 + vco_bound_pad))
+ and cs.r_counter > 0; cs.r_counter++
+ ){
+
+ //determine chan_div and vco_div
+ //and fill in that order of preference
+ cs.chan_divider = greatest_divisor<size_t>(X/cs.r_counter, 32);
+ cs.vco_divider = greatest_divisor<size_t>(X/cs.chan_divider/cs.r_counter, 6);
+
+ //avoid a vco divider of 1 (if possible)
+ if (cs.vco_divider == 1){
+ cs.vco_divider = least_divisor<size_t>(cs.chan_divider, 2);
+ cs.chan_divider /= cs.vco_divider;
+ }
+
+ UHD_LOGV(always)
+ << "gcd " << gcd << std::endl
+ << "X " << X << std::endl
+ << "Y " << Y << std::endl
+ << cs.to_pp_string() << std::endl
+ ;
+
+ //filter limits on the counters
+ if (cs.vco_divider == 1) continue;
+ if (cs.r_counter >= (1<<14)) continue;
+ if (cs.b_counter == 2) continue;
+ if (cs.b_counter == 1 and cs.a_counter != 0) continue;
+ if (cs.b_counter >= (1<<13)) continue;
+ if (cs.a_counter >= (1<<6)) continue;
+ if (cs.get_vco_rate() > 1800e6 - vco_bound_pad) continue;
+ if (cs.get_vco_rate() < 1400e6 + vco_bound_pad) continue;
+ if (cs.get_out_rate() != rate) continue;
+
+ UHD_MSG(status) << "USRP-B100 clock control: " << i << std::endl << cs.to_pp_string() << std::endl;
+ return cs;
+ }
+ }
+
+ throw uhd::value_error(str(boost::format(
+ "USRP-B100 clock control: could not calculate settings for clock rate %fMHz"
+ ) % (rate/1e6)));
+}
+
+/***********************************************************************
+ * Clock Control Implementation
+ **********************************************************************/
+class b100_clock_ctrl_impl : public b100_clock_ctrl{
+public:
+ b100_clock_ctrl_impl(i2c_iface::sptr iface, double master_clock_rate){
+ _iface = iface;
+ _chan_rate = 0.0;
+ _out_rate = 0.0;
+
+ //perform soft-reset
+ _ad9522_regs.soft_reset = 1;
+ this->send_reg(0x000);
+ this->latch_regs();
+ _ad9522_regs.soft_reset = 0;
+
+ //init the clock gen registers
+ _ad9522_regs.sdo_active = ad9522_regs_t::SDO_ACTIVE_SDO_SDIO;
+ _ad9522_regs.enb_stat_eeprom_at_stat_pin = 0; //use status pin
+ _ad9522_regs.status_pin_control = 0x1; //n divider
+ _ad9522_regs.ld_pin_control = 0x00; //dld
+ _ad9522_regs.refmon_pin_control = 0x12; //show ref2
+ _ad9522_regs.lock_detect_counter = ad9522_regs_t::LOCK_DETECT_COUNTER_16CYC;
+
+ this->use_internal_ref();
+
+ this->set_fpga_clock_rate(master_clock_rate); //initialize to something
+
+ this->enable_fpga_clock(true);
+ this->enable_test_clock(ENABLE_THE_TEST_OUT);
+ this->enable_rx_dboard_clock(false);
+ this->enable_tx_dboard_clock(false);
+ }
+
+ ~b100_clock_ctrl_impl(void){
+ UHD_SAFE_CALL(
+ this->enable_test_clock(ENABLE_THE_TEST_OUT);
+ this->enable_rx_dboard_clock(false);
+ this->enable_tx_dboard_clock(false);
+ //this->enable_fpga_clock(false); //FIXME
+ )
+ }
+
+ /***********************************************************************
+ * Clock rate control:
+ * - set clock rate w/ internal VCO
+ * - set clock rate w/ external VCXO
+ **********************************************************************/
+ void set_clock_settings_with_internal_vco(double rate){
+ const clock_settings_type cs = get_clock_settings(rate);
+
+ //set the rates to private variables so the implementation knows!
+ _chan_rate = cs.get_chan_rate();
+ _out_rate = cs.get_out_rate();
+
+ _ad9522_regs.enable_clock_doubler = (cs.ref_clock_doubler == 2)? 1 : 0;
+
+ _ad9522_regs.set_r_counter(cs.r_counter);
+ _ad9522_regs.a_counter = cs.a_counter;
+ _ad9522_regs.set_b_counter(cs.b_counter);
+ UHD_ASSERT_THROW(cs.prescaler == 8); //assumes this below:
+ _ad9522_regs.prescaler_p = ad9522_regs_t::PRESCALER_P_DIV8_9;
+
+ _ad9522_regs.pll_power_down = ad9522_regs_t::PLL_POWER_DOWN_NORMAL;
+ _ad9522_regs.cp_current = ad9522_regs_t::CP_CURRENT_1_2MA;
+
+ _ad9522_regs.bypass_vco_divider = 0;
+ switch(cs.vco_divider){
+ case 1: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV1; break;
+ case 2: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV2; break;
+ case 3: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV3; break;
+ case 4: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV4; break;
+ case 5: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV5; break;
+ case 6: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV6; break;
+ }
+ _ad9522_regs.select_vco_or_clock = ad9522_regs_t::SELECT_VCO_OR_CLOCK_VCO;
+
+ //setup fpga master clock
+ _ad9522_regs.out0_format = ad9522_regs_t::OUT0_FORMAT_LVDS;
+ set_clock_divider(cs.chan_divider,
+ _ad9522_regs.divider0_low_cycles,
+ _ad9522_regs.divider0_high_cycles,
+ _ad9522_regs.divider0_bypass
+ );
+
+ //setup codec clock
+ _ad9522_regs.out3_format = ad9522_regs_t::OUT3_FORMAT_LVDS;
+ set_clock_divider(cs.chan_divider,
+ _ad9522_regs.divider1_low_cycles,
+ _ad9522_regs.divider1_high_cycles,
+ _ad9522_regs.divider1_bypass
+ );
+
+ this->send_all_regs();
+ calibrate_now();
+ }
+
+ void set_clock_settings_with_external_vcxo(double rate){
+ //set the rates to private variables so the implementation knows!
+ _chan_rate = rate;
+ _out_rate = rate;
+
+ _ad9522_regs.enable_clock_doubler = 1; //doubler always on
+ const double ref_rate = REFERENCE_INPUT_RATE*2;
+
+ //bypass prescaler such that N = B
+ long gcd = boost::math::gcd(long(ref_rate), long(rate));
+ _ad9522_regs.set_r_counter(int(ref_rate/gcd));
+ _ad9522_regs.a_counter = 0;
+ _ad9522_regs.set_b_counter(int(rate/gcd));
+ _ad9522_regs.prescaler_p = ad9522_regs_t::PRESCALER_P_DIV1;
+
+ //setup external vcxo
+ _ad9522_regs.pll_power_down = ad9522_regs_t::PLL_POWER_DOWN_NORMAL;
+ _ad9522_regs.cp_current = ad9522_regs_t::CP_CURRENT_1_2MA;
+ _ad9522_regs.bypass_vco_divider = 1;
+ _ad9522_regs.select_vco_or_clock = ad9522_regs_t::SELECT_VCO_OR_CLOCK_EXTERNAL;
+
+ //setup fpga master clock
+ _ad9522_regs.out0_format = ad9522_regs_t::OUT0_FORMAT_LVDS;
+ _ad9522_regs.divider0_bypass = 1;
+
+ //setup codec clock
+ _ad9522_regs.out3_format = ad9522_regs_t::OUT3_FORMAT_LVDS;
+ _ad9522_regs.divider1_bypass = 1;
+
+ this->send_all_regs();
+ }
+
+ void set_fpga_clock_rate(double rate){
+ if (_out_rate == rate) return;
+ if (rate == 61.44e6) set_clock_settings_with_external_vcxo(rate);
+ else set_clock_settings_with_internal_vco(rate);
+ //clock rate changed! update dboard clocks and FPGA ticks per second
+ set_rx_dboard_clock_rate(rate);
+ set_tx_dboard_clock_rate(rate);
+ }
+
+ double get_fpga_clock_rate(void){
+ return this->_out_rate;
+ }
+
+ /***********************************************************************
+ * FPGA clock enable
+ **********************************************************************/
+ void enable_fpga_clock(bool enb){
+ _ad9522_regs.out0_format = ad9522_regs_t::OUT0_FORMAT_LVDS;
+ _ad9522_regs.out0_lvds_power_down = !enb;
+ this->send_reg(0x0F0);
+ this->latch_regs();
+ }
+
+ /***********************************************************************
+ * Special test clock output
+ **********************************************************************/
+ void enable_test_clock(bool enb){
+ //setup test clock (same divider as codec clock)
+ _ad9522_regs.out4_format = ad9522_regs_t::OUT4_FORMAT_CMOS;
+ _ad9522_regs.out4_cmos_configuration = (enb)?
+ ad9522_regs_t::OUT4_CMOS_CONFIGURATION_A_ON :
+ ad9522_regs_t::OUT4_CMOS_CONFIGURATION_OFF;
+ this->send_reg(0x0F4);
+ this->latch_regs();
+ }
+
+ /***********************************************************************
+ * RX Dboard Clock Control (output 9, divider 3)
+ **********************************************************************/
+ void enable_rx_dboard_clock(bool enb){
+ _ad9522_regs.out9_format = ad9522_regs_t::OUT9_FORMAT_LVDS;
+ _ad9522_regs.out9_lvds_power_down = !enb;
+ this->send_reg(0x0F9);
+ this->latch_regs();
+ }
+
+ std::vector<double> get_rx_dboard_clock_rates(void){
+ std::vector<double> rates;
+ for(size_t div = 1; div <= 16+16; div++)
+ rates.push_back(this->_chan_rate/div);
+ return rates;
+ }
+
+ void set_rx_dboard_clock_rate(double rate){
+ assert_has(get_rx_dboard_clock_rates(), rate, "rx dboard clock rate");
+ _rx_clock_rate = rate;
+ size_t divider = size_t(this->_chan_rate/rate);
+ //set the divider registers
+ set_clock_divider(divider,
+ _ad9522_regs.divider3_low_cycles,
+ _ad9522_regs.divider3_high_cycles,
+ _ad9522_regs.divider3_bypass
+ );
+ this->send_reg(0x199);
+ this->send_reg(0x19a);
+ this->soft_sync();
+ }
+
+ double get_rx_clock_rate(void){
+ return _rx_clock_rate;
+ }
+
+ /***********************************************************************
+ * TX Dboard Clock Control (output 6, divider 2)
+ **********************************************************************/
+ void enable_tx_dboard_clock(bool enb){
+ _ad9522_regs.out6_format = ad9522_regs_t::OUT6_FORMAT_LVDS;
+ _ad9522_regs.out6_lvds_power_down = !enb;
+ this->send_reg(0x0F6);
+ this->latch_regs();
+ }
+
+ std::vector<double> get_tx_dboard_clock_rates(void){
+ return get_rx_dboard_clock_rates(); //same master clock, same dividers...
+ }
+
+ void set_tx_dboard_clock_rate(double rate){
+ assert_has(get_tx_dboard_clock_rates(), rate, "tx dboard clock rate");
+ _tx_clock_rate = rate;
+ size_t divider = size_t(this->_chan_rate/rate);
+ //set the divider registers
+ set_clock_divider(divider,
+ _ad9522_regs.divider2_low_cycles,
+ _ad9522_regs.divider2_high_cycles,
+ _ad9522_regs.divider2_bypass
+ );
+ this->send_reg(0x196);
+ this->send_reg(0x197);
+ this->soft_sync();
+ }
+
+ double get_tx_clock_rate(void){
+ return _tx_clock_rate;
+ }
+
+ /***********************************************************************
+ * Clock reference control
+ **********************************************************************/
+ void use_internal_ref(void) {
+ _ad9522_regs.enable_ref2 = 1;
+ _ad9522_regs.enable_ref1 = 0;
+ _ad9522_regs.select_ref = ad9522_regs_t::SELECT_REF_REF2;
+ _ad9522_regs.enb_auto_ref_switchover = ad9522_regs_t::ENB_AUTO_REF_SWITCHOVER_MANUAL;
+ this->send_reg(0x01C);
+ this->latch_regs();
+ }
+
+ void use_external_ref(void) {
+ _ad9522_regs.enable_ref2 = 0;
+ _ad9522_regs.enable_ref1 = 1;
+ _ad9522_regs.select_ref = ad9522_regs_t::SELECT_REF_REF1;
+ _ad9522_regs.enb_auto_ref_switchover = ad9522_regs_t::ENB_AUTO_REF_SWITCHOVER_MANUAL;
+ this->send_reg(0x01C);
+ this->latch_regs();
+ }
+
+ void use_auto_ref(void) {
+ _ad9522_regs.enable_ref2 = 1;
+ _ad9522_regs.enable_ref1 = 1;
+ _ad9522_regs.select_ref = ad9522_regs_t::SELECT_REF_REF1;
+ _ad9522_regs.enb_auto_ref_switchover = ad9522_regs_t::ENB_AUTO_REF_SWITCHOVER_AUTO;
+ this->send_reg(0x01C);
+ this->latch_regs();
+ }
+
+ bool get_locked(void){
+ static const boost::uint8_t addr = 0x01F;
+ boost::uint32_t reg = this->read_reg(addr);
+ _ad9522_regs.set_reg(addr, reg);
+ return _ad9522_regs.digital_lock_detect != 0;
+ }
+
+private:
+ i2c_iface::sptr _iface;
+ ad9522_regs_t _ad9522_regs;
+ double _out_rate; //rate at the fpga and codec
+ double _chan_rate; //rate before final dividers
+ double _rx_clock_rate, _tx_clock_rate;
+
+ void latch_regs(void){
+ _ad9522_regs.io_update = 1;
+ this->send_reg(0x232);
+ }
+
+ void send_reg(boost::uint16_t addr){
+ boost::uint32_t reg = _ad9522_regs.get_write_reg(addr);
+ UHD_LOGV(often) << "clock control write reg: " << std::hex << reg << std::endl;
+ byte_vector_t buf;
+ buf.push_back(boost::uint8_t(reg >> 16));
+ buf.push_back(boost::uint8_t(reg >> 8));
+ buf.push_back(boost::uint8_t(reg & 0xff));
+
+ _iface->write_i2c(0x5C, buf);
+ }
+
+ boost::uint8_t read_reg(boost::uint16_t addr){
+ byte_vector_t buf;
+ buf.push_back(boost::uint8_t(addr >> 8));
+ buf.push_back(boost::uint8_t(addr & 0xff));
+ _iface->write_i2c(0x5C, buf);
+
+ buf = _iface->read_i2c(0x5C, 1);
+
+ return boost::uint32_t(buf[0] & 0xFF);
+ }
+
+ void calibrate_now(void){
+ //vco calibration routine:
+ _ad9522_regs.vco_calibration_now = 0;
+ this->send_reg(0x18);
+ this->latch_regs();
+ _ad9522_regs.vco_calibration_now = 1;
+ this->send_reg(0x18);
+ this->latch_regs();
+ //wait for calibration done:
+ static const boost::uint8_t addr = 0x01F;
+ for (size_t ms10 = 0; ms10 < 100; ms10++){
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+ boost::uint32_t reg = read_reg(addr);
+ _ad9522_regs.set_reg(addr, reg);
+ if (_ad9522_regs.vco_calibration_finished) goto wait_for_ld;
+ }
+ UHD_MSG(error) << "USRP-B100 clock control: VCO calibration timeout" << std::endl;
+ wait_for_ld:
+ //wait for digital lock detect:
+ for (size_t ms10 = 0; ms10 < 100; ms10++){
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+ boost::uint32_t reg = read_reg(addr);
+ _ad9522_regs.set_reg(addr, reg);
+ if (_ad9522_regs.digital_lock_detect) return;
+ }
+ UHD_MSG(error) << "USRP-B100 clock control: lock detection timeout" << std::endl;
+ }
+
+ void soft_sync(void){
+ _ad9522_regs.soft_sync = 1;
+ this->send_reg(0x230);
+ this->latch_regs();
+ _ad9522_regs.soft_sync = 0;
+ this->send_reg(0x230);
+ this->latch_regs();
+ }
+
+ void send_all_regs(void){
+ //setup a list of register ranges to write
+ typedef std::pair<boost::uint16_t, boost::uint16_t> range_t;
+ static const std::vector<range_t> ranges = boost::assign::list_of
+ (range_t(0x000, 0x000)) (range_t(0x010, 0x01F))
+ (range_t(0x0F0, 0x0FD)) (range_t(0x190, 0x19B))
+ (range_t(0x1E0, 0x1E1)) (range_t(0x230, 0x230))
+ ;
+
+ //write initial register values and latch/update
+ BOOST_FOREACH(const range_t &range, ranges){
+ for(boost::uint16_t addr = range.first; addr <= range.second; addr++){
+ this->send_reg(addr);
+ }
+ }
+ this->latch_regs();
+ }
+};
+
+/***********************************************************************
+ * Clock Control Make
+ **********************************************************************/
+b100_clock_ctrl::sptr b100_clock_ctrl::make(i2c_iface::sptr iface, double master_clock_rate){
+ return sptr(new b100_clock_ctrl_impl(iface, master_clock_rate));
+}
diff --git a/host/lib/usrp/b100/clock_ctrl.hpp b/host/lib/usrp/b100/clock_ctrl.hpp
new file mode 100644
index 000000000..387892bf7
--- /dev/null
+++ b/host/lib/usrp/b100/clock_ctrl.hpp
@@ -0,0 +1,133 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_B100_CLOCK_CTRL_HPP
+#define INCLUDED_B100_CLOCK_CTRL_HPP
+
+#include <uhd/types/serial.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+#include <vector>
+
+/*!
+ * The usrp-e clock control:
+ * - Setup system clocks.
+ * - Disable/enable clock lines.
+ */
+class b100_clock_ctrl : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<b100_clock_ctrl> sptr;
+
+ /*!
+ * Make a new clock control object.
+ * \param iface the controller iface object
+ * \param master_clock_rate the master FPGA/sample clock rate
+ * \return the clock control object
+ */
+ static sptr make(uhd::i2c_iface::sptr iface, double master_clock_rate);
+
+ /*!
+ * Set the rate of the fpga clock line.
+ * Throws if rate is not valid.
+ * \param rate the new rate in Hz
+ */
+ virtual void set_fpga_clock_rate(double rate) = 0;
+
+ /*!
+ * Get the rate of the fpga clock line.
+ * \return the fpga clock rate in Hz
+ */
+ virtual double get_fpga_clock_rate(void) = 0;
+
+ /*!
+ * Get the possible rates of the rx dboard clock.
+ * \return a vector of clock rates in Hz
+ */
+ virtual std::vector<double> get_rx_dboard_clock_rates(void) = 0;
+
+ /*!
+ * Get the possible rates of the tx dboard clock.
+ * \return a vector of clock rates in Hz
+ */
+ virtual std::vector<double> get_tx_dboard_clock_rates(void) = 0;
+
+ /*!
+ * Set the rx dboard clock rate to a possible rate.
+ * \param rate the new clock rate in Hz
+ * \throw exception when rate cannot be achieved
+ */
+ virtual void set_rx_dboard_clock_rate(double rate) = 0;
+
+ /*!
+ * Set the tx dboard clock rate to a possible rate.
+ * \param rate the new clock rate in Hz
+ * \throw exception when rate cannot be achieved
+ */
+ virtual void set_tx_dboard_clock_rate(double rate) = 0;
+
+ /*!
+ * Get the current rx dboard clock rate.
+ * \return the clock rate in Hz
+ */
+ virtual double get_rx_clock_rate(void) = 0;
+
+ /*!
+ * Get the current tx dboard clock rate.
+ * \return the clock rate in Hz
+ */
+ virtual double get_tx_clock_rate(void) = 0;
+
+ /*!
+ * Enable/disable the FPGA clock.
+ * \param enb true to enable
+ */
+
+ virtual void enable_fpga_clock(bool enb) = 0;
+
+ /*!
+ * Enable/disable the rx dboard clock.
+ * \param enb true to enable
+ */
+ virtual void enable_rx_dboard_clock(bool enb) = 0;
+
+ /*!
+ * Enable/disable the tx dboard clock.
+ * \param enb true to enable
+ */
+ virtual void enable_tx_dboard_clock(bool enb) = 0;
+
+ /*!
+ * Use the internal TCXO reference
+ */
+ virtual void use_internal_ref(void) = 0;
+
+ /*!
+ * Use the external SMA reference
+ */
+ virtual void use_external_ref(void) = 0;
+
+ /*!
+ * Use external if available, internal otherwise
+ */
+ virtual void use_auto_ref(void) = 0;
+
+ //! Is the reference locked?
+ virtual bool get_locked(void) = 0;
+
+};
+
+#endif /* INCLUDED_B100_CLOCK_CTRL_HPP */
diff --git a/host/lib/usrp/b100/codec_ctrl.cpp b/host/lib/usrp/b100/codec_ctrl.cpp
new file mode 100644
index 000000000..4f9036039
--- /dev/null
+++ b/host/lib/usrp/b100/codec_ctrl.cpp
@@ -0,0 +1,283 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "codec_ctrl.hpp"
+#include "ad9862_regs.hpp"
+#include <uhd/types/dict.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/safe_call.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/tuple/tuple.hpp>
+#include <boost/math/special_functions/round.hpp>
+#include "b100_regs.hpp" //spi slave constants
+#include <boost/assign/list_of.hpp>
+
+using namespace uhd;
+
+const gain_range_t b100_codec_ctrl::tx_pga_gain_range(-20, 0, double(0.1));
+const gain_range_t b100_codec_ctrl::rx_pga_gain_range(0, 20, 1);
+
+/***********************************************************************
+ * Codec Control Implementation
+ **********************************************************************/
+class b100_codec_ctrl_impl : public b100_codec_ctrl{
+public:
+ //structors
+ b100_codec_ctrl_impl(spi_iface::sptr iface);
+ ~b100_codec_ctrl_impl(void);
+
+ //aux adc and dac control
+ double read_aux_adc(aux_adc_t which);
+ void write_aux_dac(aux_dac_t which, double volts);
+
+ //pga gain control
+ void set_tx_pga_gain(double);
+ double get_tx_pga_gain(void);
+ void set_rx_pga_gain(double, char);
+ double get_rx_pga_gain(char);
+
+private:
+ spi_iface::sptr _iface;
+ ad9862_regs_t _ad9862_regs;
+ void send_reg(boost::uint8_t addr);
+ void recv_reg(boost::uint8_t addr);
+};
+
+/***********************************************************************
+ * Codec Control Structors
+ **********************************************************************/
+b100_codec_ctrl_impl::b100_codec_ctrl_impl(spi_iface::sptr iface){
+ _iface = iface;
+
+ //soft reset
+ _ad9862_regs.soft_reset = 1;
+ this->send_reg(0);
+
+ //initialize the codec register settings
+ _ad9862_regs.sdio_bidir = ad9862_regs_t::SDIO_BIDIR_SDIO_SDO;
+ _ad9862_regs.lsb_first = ad9862_regs_t::LSB_FIRST_MSB;
+ _ad9862_regs.soft_reset = 0;
+
+ //setup rx side of codec
+ _ad9862_regs.byp_buffer_a = 1;
+ _ad9862_regs.byp_buffer_b = 1;
+ _ad9862_regs.buffer_a_pd = 1;
+ _ad9862_regs.buffer_b_pd = 1;
+ _ad9862_regs.mux_out = ad9862_regs_t::MUX_OUT_RX_MUX_MODE; //B100 uses interleaved RX->FPGA
+ _ad9862_regs.rx_pga_a = 0;//0x1f; //TODO bring under api control
+ _ad9862_regs.rx_pga_b = 0;//0x1f; //TODO bring under api control
+ _ad9862_regs.rx_twos_comp = 1;
+ _ad9862_regs.rx_hilbert = ad9862_regs_t::RX_HILBERT_DIS;
+
+ //setup tx side of codec
+ _ad9862_regs.two_data_paths = ad9862_regs_t::TWO_DATA_PATHS_BOTH;
+ _ad9862_regs.interleaved = ad9862_regs_t::INTERLEAVED_INTERLEAVED;
+ _ad9862_regs.tx_retime = ad9862_regs_t::TX_RETIME_CLKOUT2;
+ _ad9862_regs.tx_pga_gain = 199; //TODO bring under api control
+ _ad9862_regs.tx_hilbert = ad9862_regs_t::TX_HILBERT_DIS;
+ _ad9862_regs.interp = ad9862_regs_t::INTERP_2;
+ _ad9862_regs.tx_twos_comp = 1;
+ _ad9862_regs.fine_mode = ad9862_regs_t::FINE_MODE_BYPASS;
+ _ad9862_regs.coarse_mod = ad9862_regs_t::COARSE_MOD_BYPASS;
+ _ad9862_regs.dac_a_coarse_gain = 0x3;
+ _ad9862_regs.dac_b_coarse_gain = 0x3;
+ _ad9862_regs.edges = ad9862_regs_t::EDGES_NORMAL;
+
+ //setup the dll
+ _ad9862_regs.input_clk_ctrl = ad9862_regs_t::INPUT_CLK_CTRL_EXTERNAL;
+ _ad9862_regs.dll_mult = ad9862_regs_t::DLL_MULT_2;
+ _ad9862_regs.dll_mode = ad9862_regs_t::DLL_MODE_FAST;
+
+ //write the register settings to the codec
+ for (uint8_t addr = 0; addr <= 25; addr++){
+ this->send_reg(addr);
+ }
+
+ //always start conversions for aux ADC
+ _ad9862_regs.start_a = 1;
+ _ad9862_regs.start_b = 1;
+
+ //aux adc clock
+ _ad9862_regs.clk_4 = ad9862_regs_t::CLK_4_1_4;
+ this->send_reg(34);
+}
+
+b100_codec_ctrl_impl::~b100_codec_ctrl_impl(void){
+ UHD_SAFE_CALL(
+ //set aux dacs to zero
+ this->write_aux_dac(AUX_DAC_A, 0);
+ this->write_aux_dac(AUX_DAC_B, 0);
+ this->write_aux_dac(AUX_DAC_C, 0);
+ this->write_aux_dac(AUX_DAC_D, 0);
+
+ //power down
+ _ad9862_regs.all_rx_pd = 1;
+ this->send_reg(1);
+ _ad9862_regs.tx_digital_pd = 1;
+ _ad9862_regs.tx_analog_pd = ad9862_regs_t::TX_ANALOG_PD_BOTH;
+ this->send_reg(8);
+ )
+}
+
+/***********************************************************************
+ * Codec Control Gain Control Methods
+ **********************************************************************/
+static const int mtpgw = 255; //maximum tx pga gain word
+
+void b100_codec_ctrl_impl::set_tx_pga_gain(double gain){
+ int gain_word = int(mtpgw*(gain - tx_pga_gain_range.start())/(tx_pga_gain_range.stop() - tx_pga_gain_range.start()));
+ _ad9862_regs.tx_pga_gain = uhd::clip(gain_word, 0, mtpgw);
+ this->send_reg(16);
+}
+
+double b100_codec_ctrl_impl::get_tx_pga_gain(void){
+ return (_ad9862_regs.tx_pga_gain*(tx_pga_gain_range.stop() - tx_pga_gain_range.start())/mtpgw) + tx_pga_gain_range.start();
+}
+
+static const int mrpgw = 0x14; //maximum rx pga gain word
+
+void b100_codec_ctrl_impl::set_rx_pga_gain(double gain, char which){
+ int gain_word = int(mrpgw*(gain - rx_pga_gain_range.start())/(rx_pga_gain_range.stop() - rx_pga_gain_range.start()));
+ gain_word = uhd::clip(gain_word, 0, mrpgw);
+ switch(which){
+ case 'A':
+ _ad9862_regs.rx_pga_a = gain_word;
+ this->send_reg(2);
+ return;
+ case 'B':
+ _ad9862_regs.rx_pga_b = gain_word;
+ this->send_reg(3);
+ return;
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+double b100_codec_ctrl_impl::get_rx_pga_gain(char which){
+ int gain_word;
+ switch(which){
+ case 'A': gain_word = _ad9862_regs.rx_pga_a; break;
+ case 'B': gain_word = _ad9862_regs.rx_pga_b; break;
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+ return (gain_word*(rx_pga_gain_range.stop() - rx_pga_gain_range.start())/mrpgw) + rx_pga_gain_range.start();
+}
+
+/***********************************************************************
+ * Codec Control AUX ADC Methods
+ **********************************************************************/
+static double aux_adc_to_volts(boost::uint8_t high, boost::uint8_t low){
+ return double((boost::uint16_t(high) << 2) | low)*3.3/0x3ff;
+}
+
+double b100_codec_ctrl_impl::read_aux_adc(aux_adc_t which){
+ switch(which){
+
+ case AUX_ADC_A1:
+ _ad9862_regs.select_a = ad9862_regs_t::SELECT_A_AUX_ADC1;
+ this->send_reg(34); //start conversion and select mux
+ this->recv_reg(28); //read the value (2 bytes, 2 reads)
+ this->recv_reg(29);
+ return aux_adc_to_volts(_ad9862_regs.aux_adc_a1_9_2, _ad9862_regs.aux_adc_a1_1_0);
+ case AUX_ADC_A2:
+ _ad9862_regs.select_a = ad9862_regs_t::SELECT_A_AUX_ADC2;
+ this->send_reg(34); //start conversion and select mux
+ this->recv_reg(26); //read the value (2 bytes, 2 reads)
+ this->recv_reg(27);
+ return aux_adc_to_volts(_ad9862_regs.aux_adc_a2_9_2, _ad9862_regs.aux_adc_a2_1_0);
+
+ case AUX_ADC_B1:
+ _ad9862_regs.select_b = ad9862_regs_t::SELECT_B_AUX_ADC1;
+ this->send_reg(34); //start conversion and select mux
+ this->recv_reg(32); //read the value (2 bytes, 2 reads)
+ this->recv_reg(33);
+ return aux_adc_to_volts(_ad9862_regs.aux_adc_b1_9_2, _ad9862_regs.aux_adc_b1_1_0);
+ case AUX_ADC_B2:
+ _ad9862_regs.select_b = ad9862_regs_t::SELECT_B_AUX_ADC2;
+ this->send_reg(34); //start conversion and select mux
+ this->recv_reg(30); //read the value (2 bytes, 2 reads)
+ this->recv_reg(31);
+ return aux_adc_to_volts(_ad9862_regs.aux_adc_b2_9_2, _ad9862_regs.aux_adc_b2_1_0);
+ }
+ UHD_THROW_INVALID_CODE_PATH();
+}
+
+/***********************************************************************
+ * Codec Control AUX DAC Methods
+ **********************************************************************/
+void b100_codec_ctrl_impl::write_aux_dac(aux_dac_t which, double volts){
+ //special case for aux dac d (aka sigma delta word)
+ if (which == AUX_DAC_D){
+ boost::uint16_t dac_word = uhd::clip(boost::math::iround(volts*0xfff/3.3), 0, 0xfff);
+ _ad9862_regs.sig_delt_11_4 = boost::uint8_t(dac_word >> 4);
+ _ad9862_regs.sig_delt_3_0 = boost::uint8_t(dac_word & 0xf);
+ this->send_reg(42);
+ this->send_reg(43);
+ return;
+ }
+
+ //calculate the dac word for aux dac a, b, c
+ boost::uint8_t dac_word = uhd::clip(boost::math::iround(volts*0xff/3.3), 0, 0xff);
+
+ //setup a lookup table for the aux dac params (reg ref, reg addr)
+ typedef boost::tuple<boost::uint8_t*, boost::uint8_t> dac_params_t;
+ uhd::dict<aux_dac_t, dac_params_t> aux_dac_to_params = boost::assign::map_list_of
+ (AUX_DAC_A, dac_params_t(&_ad9862_regs.aux_dac_a, 36))
+ (AUX_DAC_B, dac_params_t(&_ad9862_regs.aux_dac_b, 37))
+ (AUX_DAC_C, dac_params_t(&_ad9862_regs.aux_dac_c, 38))
+ ;
+
+ //set the aux dac register
+ UHD_ASSERT_THROW(aux_dac_to_params.has_key(which));
+ boost::uint8_t *reg_ref, reg_addr;
+ boost::tie(reg_ref, reg_addr) = aux_dac_to_params[which];
+ *reg_ref = dac_word;
+ this->send_reg(reg_addr);
+}
+
+/***********************************************************************
+ * Codec Control SPI Methods
+ **********************************************************************/
+void b100_codec_ctrl_impl::send_reg(boost::uint8_t addr){
+ boost::uint32_t reg = _ad9862_regs.get_write_reg(addr);
+ UHD_LOGV(rarely) << "codec control write reg: " << std::hex << reg << std::endl;
+ _iface->transact_spi(
+ B100_SPI_SS_AD9862,
+ spi_config_t::EDGE_RISE,
+ reg, 16, false /*no rb*/
+ );
+}
+
+void b100_codec_ctrl_impl::recv_reg(boost::uint8_t addr){
+ boost::uint32_t reg = _ad9862_regs.get_read_reg(addr);
+ UHD_LOGV(rarely) << "codec control read reg: " << std::hex << reg << std::endl;
+ boost::uint32_t ret = _iface->transact_spi(
+ B100_SPI_SS_AD9862,
+ spi_config_t::EDGE_RISE,
+ reg, 16, true /*rb*/
+ );
+ UHD_LOGV(rarely) << "codec control read ret: " << std::hex << boost::uint16_t(ret & 0xFF) << std::endl;
+ _ad9862_regs.set_reg(addr, boost::uint8_t(ret&0xff));
+}
+
+/***********************************************************************
+ * Codec Control Make
+ **********************************************************************/
+b100_codec_ctrl::sptr b100_codec_ctrl::make(spi_iface::sptr iface){
+ return sptr(new b100_codec_ctrl_impl(iface));
+}
diff --git a/host/lib/usrp/b100/codec_ctrl.hpp b/host/lib/usrp/b100/codec_ctrl.hpp
new file mode 100644
index 000000000..1f7bdef09
--- /dev/null
+++ b/host/lib/usrp/b100/codec_ctrl.hpp
@@ -0,0 +1,90 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_B100_CODEC_CTRL_HPP
+#define INCLUDED_B100_CODEC_CTRL_HPP
+
+#include <uhd/types/serial.hpp>
+#include <uhd/types/ranges.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+
+/*!
+ * The usrp-e codec control:
+ * - Init/power down codec.
+ * - Read aux adc, write aux dac.
+ */
+class b100_codec_ctrl : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<b100_codec_ctrl> sptr;
+
+ static const uhd::gain_range_t tx_pga_gain_range;
+ static const uhd::gain_range_t rx_pga_gain_range;
+
+ /*!
+ * Make a new codec control object.
+ * \param iface the usrp_e iface object
+ * \return the codec control object
+ */
+ static sptr make(uhd::spi_iface::sptr iface);
+
+ //! aux adc identifier constants
+ enum aux_adc_t{
+ AUX_ADC_A2 = 0xA2,
+ AUX_ADC_A1 = 0xA1,
+ AUX_ADC_B2 = 0xB2,
+ AUX_ADC_B1 = 0xB1
+ };
+
+ /*!
+ * Read an auxiliary adc:
+ * The internals remember which aux adc was read last.
+ * Therefore, the aux adc switch is only changed as needed.
+ * \param which which of the 4 adcs
+ * \return a value in volts
+ */
+ virtual double read_aux_adc(aux_adc_t which) = 0;
+
+ //! aux dac identifier constants
+ enum aux_dac_t{
+ AUX_DAC_A = 0xA,
+ AUX_DAC_B = 0xB,
+ AUX_DAC_C = 0xC,
+ AUX_DAC_D = 0xD //really the sigma delta output
+ };
+
+ /*!
+ * Write an auxiliary dac.
+ * \param which which of the 4 dacs
+ * \param volts the level in in volts
+ */
+ virtual void write_aux_dac(aux_dac_t which, double volts) = 0;
+
+ //! Set the TX PGA gain
+ virtual void set_tx_pga_gain(double gain) = 0;
+
+ //! Get the TX PGA gain
+ virtual double get_tx_pga_gain(void) = 0;
+
+ //! Set the RX PGA gain ('A' or 'B')
+ virtual void set_rx_pga_gain(double gain, char which) = 0;
+
+ //! Get the RX PGA gain ('A' or 'B')
+ virtual double get_rx_pga_gain(char which) = 0;
+};
+
+#endif /* INCLUDED_B100_CODEC_CTRL_HPP */
diff --git a/host/lib/usrp/b100/ctrl_packet.hpp b/host/lib/usrp/b100/ctrl_packet.hpp
new file mode 100644
index 000000000..bab1f0de1
--- /dev/null
+++ b/host/lib/usrp/b100/ctrl_packet.hpp
@@ -0,0 +1,75 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_CTRL_PACKET_HPP
+#define INCLUDED_CTRL_PACKET_HPP
+
+#include <uhd/config.hpp>
+#include <boost/cstdint.hpp>
+#include <uhd/types/serial.hpp>
+
+typedef std::vector<boost::uint16_t> ctrl_data_t;
+
+/*!
+ * Control packet operation type
+ */
+enum ctrl_pkt_op_t {
+ CTRL_PKT_OP_WRITE = 1,
+ CTRL_PKT_OP_READ = 2,
+ CTRL_PKT_OP_READBACK = 3
+};
+
+/*!
+ * Control packet transaction length
+ */
+const size_t CTRL_PACKET_LENGTH = 32;
+const size_t CTRL_PACKET_HEADER_LENGTH = 8;
+const size_t CTRL_PACKET_DATA_LENGTH = 24; //=length-header
+
+/*!
+ * Control packet header magic value
+ */
+const boost::uint8_t CTRL_PACKET_HEADER_MAGIC = 0xAA;
+
+/*!
+ * Callback triggers for readback operation
+ */
+//FIXME: these are not real numbers, callbacks aren't implemented yet
+const boost::uint16_t CTRL_PACKET_CALLBACK_SPI = 0x0001;
+const boost::uint16_t CTRL_PACKET_CALLBACK_I2C = 0x0002;
+//and so on
+
+/*!
+ * Metadata structure to describe a control packet
+ */
+struct UHD_API ctrl_pkt_meta_t {
+ ctrl_pkt_op_t op;
+ boost::uint8_t callbacks;
+ boost::uint8_t seq;
+ boost::uint16_t len;
+ boost::uint32_t addr;
+};
+
+/*!
+ * Full control packet structure
+ */
+struct UHD_API ctrl_pkt_t {
+ ctrl_pkt_meta_t pkt_meta;
+ ctrl_data_t data;
+};
+
+#endif /* INCLUDED_CTRL_PACKET_HPP */
diff --git a/host/lib/usrp/b100/dboard_iface.cpp b/host/lib/usrp/b100/dboard_iface.cpp
new file mode 100644
index 000000000..39ad5c5ac
--- /dev/null
+++ b/host/lib/usrp/b100/dboard_iface.cpp
@@ -0,0 +1,258 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "gpio_core_200.hpp"
+#include <uhd/types/serial.hpp>
+#include "b100_regs.hpp"
+#include "clock_ctrl.hpp"
+#include "codec_ctrl.hpp"
+#include <uhd/usrp/dboard_iface.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/exception.hpp>
+#include <boost/assign/list_of.hpp>
+
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace boost::assign;
+
+class b100_dboard_iface : public dboard_iface{
+public:
+
+ b100_dboard_iface(
+ wb_iface::sptr wb_iface,
+ i2c_iface::sptr i2c_iface,
+ spi_iface::sptr spi_iface,
+ b100_clock_ctrl::sptr clock,
+ b100_codec_ctrl::sptr codec
+ ){
+ _wb_iface = wb_iface;
+ _i2c_iface = i2c_iface;
+ _spi_iface = spi_iface;
+ _clock = clock;
+ _codec = codec;
+ _gpio = gpio_core_200::make(_wb_iface, B100_REG_SR_ADDR(B100_SR_GPIO), B100_REG_RB_GPIO);
+
+ //init the clock rate shadows
+ this->set_clock_rate(UNIT_RX, _clock->get_fpga_clock_rate());
+ this->set_clock_rate(UNIT_TX, _clock->get_fpga_clock_rate());
+ }
+
+ ~b100_dboard_iface(void){
+ /* NOP */
+ }
+
+ special_props_t get_special_props(void){
+ special_props_t props;
+ props.soft_clock_divider = false;
+ props.mangle_i2c_addrs = false;
+ return props;
+ }
+
+ void write_aux_dac(unit_t, aux_dac_t, double);
+ double read_aux_adc(unit_t, aux_adc_t);
+
+ void _set_pin_ctrl(unit_t, boost::uint16_t);
+ void _set_atr_reg(unit_t, atr_reg_t, boost::uint16_t);
+ void _set_gpio_ddr(unit_t, boost::uint16_t);
+ void _set_gpio_out(unit_t, boost::uint16_t);
+ void set_gpio_debug(unit_t, int);
+ boost::uint16_t read_gpio(unit_t);
+
+ void write_i2c(boost::uint8_t, const byte_vector_t &);
+ byte_vector_t read_i2c(boost::uint8_t, size_t);
+
+ void write_spi(
+ unit_t unit,
+ const spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits
+ );
+
+ boost::uint32_t read_write_spi(
+ unit_t unit,
+ const spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits
+ );
+
+ void set_clock_rate(unit_t, double);
+ std::vector<double> get_clock_rates(unit_t);
+ double get_clock_rate(unit_t);
+ void set_clock_enabled(unit_t, bool);
+ double get_codec_rate(unit_t);
+
+private:
+ wb_iface::sptr _wb_iface;
+ i2c_iface::sptr _i2c_iface;
+ spi_iface::sptr _spi_iface;
+ b100_clock_ctrl::sptr _clock;
+ b100_codec_ctrl::sptr _codec;
+ gpio_core_200::sptr _gpio;
+};
+
+/***********************************************************************
+ * Make Function
+ **********************************************************************/
+dboard_iface::sptr make_b100_dboard_iface(
+ wb_iface::sptr wb_iface,
+ i2c_iface::sptr i2c_iface,
+ spi_iface::sptr spi_iface,
+ b100_clock_ctrl::sptr clock,
+ b100_codec_ctrl::sptr codec
+){
+ return dboard_iface::sptr(new b100_dboard_iface(wb_iface, i2c_iface, spi_iface, clock, codec));
+}
+
+/***********************************************************************
+ * Clock Rates
+ **********************************************************************/
+void b100_dboard_iface::set_clock_rate(unit_t unit, double rate){
+ switch(unit){
+ case UNIT_RX: return _clock->set_rx_dboard_clock_rate(rate);
+ case UNIT_TX: return _clock->set_tx_dboard_clock_rate(rate);
+ }
+}
+
+std::vector<double> b100_dboard_iface::get_clock_rates(unit_t unit){
+ switch(unit){
+ case UNIT_RX: return _clock->get_rx_dboard_clock_rates();
+ case UNIT_TX: return _clock->get_tx_dboard_clock_rates();
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+double b100_dboard_iface::get_clock_rate(unit_t unit){
+ switch(unit){
+ case UNIT_RX: return _clock->get_rx_clock_rate();
+ case UNIT_TX: return _clock->get_tx_clock_rate();
+ }
+ UHD_THROW_INVALID_CODE_PATH();
+}
+
+void b100_dboard_iface::set_clock_enabled(unit_t unit, bool enb){
+ switch(unit){
+ case UNIT_RX: return _clock->enable_rx_dboard_clock(enb);
+ case UNIT_TX: return _clock->enable_tx_dboard_clock(enb);
+ }
+}
+
+double b100_dboard_iface::get_codec_rate(unit_t){
+ return _clock->get_fpga_clock_rate();
+}
+
+/***********************************************************************
+ * GPIO
+ **********************************************************************/
+void b100_dboard_iface::_set_pin_ctrl(unit_t unit, boost::uint16_t value){
+ return _gpio->set_pin_ctrl(unit, value);
+}
+
+void b100_dboard_iface::_set_gpio_ddr(unit_t unit, boost::uint16_t value){
+ return _gpio->set_gpio_ddr(unit, value);
+}
+
+void b100_dboard_iface::_set_gpio_out(unit_t unit, boost::uint16_t value){
+ return _gpio->set_gpio_out(unit, value);
+}
+
+boost::uint16_t b100_dboard_iface::read_gpio(unit_t unit){
+ return _gpio->read_gpio(unit);
+}
+
+void b100_dboard_iface::_set_atr_reg(unit_t unit, atr_reg_t atr, boost::uint16_t value){
+ return _gpio->set_atr_reg(unit, atr, value);
+}
+
+void b100_dboard_iface::set_gpio_debug(unit_t, int){
+ throw uhd::not_implemented_error("no set_gpio_debug implemented");
+}
+
+/***********************************************************************
+ * SPI
+ **********************************************************************/
+/*!
+ * Static function to convert a unit type to a spi slave device number.
+ * \param unit the dboard interface unit type enum
+ * \return the slave device number
+ */
+static boost::uint32_t unit_to_otw_spi_dev(dboard_iface::unit_t unit){
+ switch(unit){
+ case dboard_iface::UNIT_TX: return B100_SPI_SS_TX_DB;
+ case dboard_iface::UNIT_RX: return B100_SPI_SS_RX_DB;
+ }
+ UHD_THROW_INVALID_CODE_PATH();
+}
+
+void b100_dboard_iface::write_spi(
+ unit_t unit,
+ const spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits
+){
+ _spi_iface->write_spi(unit_to_otw_spi_dev(unit), config, data, num_bits);
+}
+
+boost::uint32_t b100_dboard_iface::read_write_spi(
+ unit_t unit,
+ const spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits
+){
+ return _spi_iface->read_spi(unit_to_otw_spi_dev(unit), config, data, num_bits);
+}
+
+/***********************************************************************
+ * I2C
+ **********************************************************************/
+void b100_dboard_iface::write_i2c(boost::uint8_t addr, const byte_vector_t &bytes){
+ return _i2c_iface->write_i2c(addr, bytes);
+}
+
+byte_vector_t b100_dboard_iface::read_i2c(boost::uint8_t addr, size_t num_bytes){
+ return _i2c_iface->read_i2c(addr, num_bytes);
+}
+
+/***********************************************************************
+ * Aux DAX/ADC
+ **********************************************************************/
+void b100_dboard_iface::write_aux_dac(dboard_iface::unit_t, aux_dac_t which, double value){
+ //same aux dacs for each unit
+ static const uhd::dict<aux_dac_t, b100_codec_ctrl::aux_dac_t> which_to_aux_dac = map_list_of
+ (AUX_DAC_A, b100_codec_ctrl::AUX_DAC_A)
+ (AUX_DAC_B, b100_codec_ctrl::AUX_DAC_B)
+ (AUX_DAC_C, b100_codec_ctrl::AUX_DAC_C)
+ (AUX_DAC_D, b100_codec_ctrl::AUX_DAC_D)
+ ;
+ _codec->write_aux_dac(which_to_aux_dac[which], value);
+}
+
+double b100_dboard_iface::read_aux_adc(dboard_iface::unit_t unit, aux_adc_t which){
+ static const uhd::dict<
+ unit_t, uhd::dict<aux_adc_t, b100_codec_ctrl::aux_adc_t>
+ > unit_to_which_to_aux_adc = map_list_of
+ (UNIT_RX, map_list_of
+ (AUX_ADC_A, b100_codec_ctrl::AUX_ADC_A1)
+ (AUX_ADC_B, b100_codec_ctrl::AUX_ADC_B1)
+ )
+ (UNIT_TX, map_list_of
+ (AUX_ADC_A, b100_codec_ctrl::AUX_ADC_A2)
+ (AUX_ADC_B, b100_codec_ctrl::AUX_ADC_B2)
+ )
+ ;
+ return _codec->read_aux_adc(unit_to_which_to_aux_adc[unit][which]);
+}
diff --git a/host/lib/usrp/b100/io_impl.cpp b/host/lib/usrp/b100/io_impl.cpp
new file mode 100644
index 000000000..674380cca
--- /dev/null
+++ b/host/lib/usrp/b100/io_impl.cpp
@@ -0,0 +1,295 @@
+//
+// Copyright 2011-2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "recv_packet_demuxer.hpp"
+#include "validate_subdev_spec.hpp"
+#include "async_packet_handler.hpp"
+#include "../../transport/super_recv_packet_handler.hpp"
+#include "../../transport/super_send_packet_handler.hpp"
+#include "usrp_commands.h"
+#include "b100_impl.hpp"
+#include "b100_regs.hpp"
+#include <uhd/utils/thread_priority.hpp>
+#include <uhd/transport/bounded_buffer.hpp>
+#include <boost/bind.hpp>
+#include <boost/format.hpp>
+#include <boost/bind.hpp>
+#include <boost/thread.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/log.hpp>
+#include <boost/make_shared.hpp>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+
+/***********************************************************************
+ * IO Implementation Details
+ **********************************************************************/
+struct b100_impl::io_impl{
+ io_impl(void):
+ async_msg_fifo(1000/*messages deep*/)
+ { /* NOP */ }
+
+ zero_copy_if::sptr data_transport;
+ bounded_buffer<async_metadata_t> async_msg_fifo;
+ recv_packet_demuxer::sptr demuxer;
+ double tick_rate;
+};
+
+/***********************************************************************
+ * Initialize internals within this file
+ **********************************************************************/
+void b100_impl::io_init(void){
+
+ //clear fifo state machines
+ _fpga_ctrl->poke32(B100_REG_CLEAR_FIFO, 0);
+
+ //allocate streamer weak ptrs containers
+ _rx_streamers.resize(_rx_dsps.size());
+ _tx_streamers.resize(1/*known to be 1 dsp*/);
+
+ //create new io impl
+ _io_impl = UHD_PIMPL_MAKE(io_impl, ());
+ _io_impl->demuxer = recv_packet_demuxer::make(_data_transport, _rx_dsps.size(), B100_RX_SID_BASE);
+
+ //now its safe to register the async callback
+ _fpga_ctrl->set_async_cb(boost::bind(&b100_impl::handle_async_message, this, _1));
+}
+
+void b100_impl::handle_async_message(managed_recv_buffer::sptr rbuf){
+ vrt::if_packet_info_t if_packet_info;
+ if_packet_info.num_packet_words32 = rbuf->size()/sizeof(boost::uint32_t);
+ const boost::uint32_t *vrt_hdr = rbuf->cast<const boost::uint32_t *>();
+ try{
+ vrt::if_hdr_unpack_le(vrt_hdr, if_packet_info);
+ }
+ catch(const std::exception &e){
+ UHD_MSG(error) << "Error (handle_async_message): " << e.what() << std::endl;
+ }
+
+ if (if_packet_info.sid == B100_TX_ASYNC_SID and if_packet_info.packet_type != vrt::if_packet_info_t::PACKET_TYPE_DATA){
+
+ //fill in the async metadata
+ async_metadata_t metadata;
+ load_metadata_from_buff(uhd::wtohx<boost::uint32_t>, metadata, if_packet_info, vrt_hdr, _io_impl->tick_rate);
+
+ //push the message onto the queue
+ _io_impl->async_msg_fifo.push_with_pop_on_full(metadata);
+
+ //print some fastpath messages
+ standard_async_msg_prints(metadata);
+ }
+ else UHD_MSG(error) << "Unknown async packet" << std::endl;
+}
+
+void b100_impl::update_rates(void){
+ const fs_path mb_path = "/mboards/0";
+ _tree->access<double>(mb_path / "tick_rate").update();
+
+ //and now that the tick rate is set, init the host rates to something
+ BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "rx_dsps")){
+ _tree->access<double>(mb_path / "rx_dsps" / name / "rate" / "value").update();
+ }
+ BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "tx_dsps")){
+ _tree->access<double>(mb_path / "tx_dsps" / name / "rate" / "value").update();
+ }
+}
+
+void b100_impl::update_tick_rate(const double rate){
+ _io_impl->tick_rate = rate;
+
+ //update the tick rate on all existing streamers -> thread safe
+ for (size_t i = 0; i < _rx_streamers.size(); i++){
+ boost::shared_ptr<sph::recv_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_rx_streamers[i].lock());
+ if (my_streamer.get() == NULL) continue;
+ my_streamer->set_tick_rate(rate);
+ }
+ for (size_t i = 0; i < _tx_streamers.size(); i++){
+ boost::shared_ptr<sph::send_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<sph::send_packet_streamer>(_tx_streamers[i].lock());
+ if (my_streamer.get() == NULL) continue;
+ my_streamer->set_tick_rate(rate);
+ }
+}
+
+void b100_impl::update_rx_samp_rate(const size_t dspno, const double rate){
+ boost::shared_ptr<sph::recv_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_rx_streamers[dspno].lock());
+ if (my_streamer.get() == NULL) return;
+
+ my_streamer->set_samp_rate(rate);
+ const double adj = _rx_dsps[dspno]->get_scaling_adjustment();
+ my_streamer->set_scale_factor(adj);
+}
+
+void b100_impl::update_tx_samp_rate(const size_t dspno, const double rate){
+ boost::shared_ptr<sph::send_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<sph::send_packet_streamer>(_tx_streamers[dspno].lock());
+ if (my_streamer.get() == NULL) return;
+
+ my_streamer->set_samp_rate(rate);
+ const double adj = _tx_dsp->get_scaling_adjustment();
+ my_streamer->set_scale_factor(adj);
+}
+
+void b100_impl::update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){
+ fs_path root = "/mboards/0/dboards";
+
+ //sanity checking
+ validate_subdev_spec(_tree, spec, "rx");
+
+ //setup mux for this spec
+ bool fe_swapped = false;
+ for (size_t i = 0; i < spec.size(); i++){
+ const std::string conn = _tree->access<std::string>(root / spec[i].db_name / "rx_frontends" / spec[i].sd_name / "connection").get();
+ if (i == 0 and (conn == "QI" or conn == "Q")) fe_swapped = true;
+ _rx_dsps[i]->set_mux(conn, fe_swapped);
+ }
+ _rx_fe->set_mux(fe_swapped);
+}
+
+void b100_impl::update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){
+ fs_path root = "/mboards/0/dboards";
+
+ //sanity checking
+ validate_subdev_spec(_tree, spec, "tx");
+
+ //set the mux for this spec
+ const std::string conn = _tree->access<std::string>(root / spec[0].db_name / "tx_frontends" / spec[0].sd_name / "connection").get();
+ _tx_fe->set_mux(conn);
+}
+
+/***********************************************************************
+ * Async Data
+ **********************************************************************/
+bool b100_impl::recv_async_msg(
+ async_metadata_t &async_metadata, double timeout
+){
+ boost::this_thread::disable_interruption di; //disable because the wait can throw
+ return _io_impl->async_msg_fifo.pop_with_timed_wait(async_metadata, timeout);
+}
+
+/***********************************************************************
+ * Receive streamer
+ **********************************************************************/
+rx_streamer::sptr b100_impl::get_rx_stream(const uhd::stream_args_t &args_){
+ stream_args_t args = args_;
+
+ //setup defaults for unspecified values
+ args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format;
+ args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels;
+
+ //calculate packet size
+ static const size_t hdr_size = 0
+ + vrt::max_if_hdr_words32*sizeof(boost::uint32_t)
+ + sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer
+ - sizeof(vrt::if_packet_info_t().cid) //no class id ever used
+ - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used
+ ;
+ const size_t bpp = _data_transport->get_recv_frame_size() - hdr_size;
+ const size_t bpi = convert::get_bytes_per_item(args.otw_format);
+ const size_t spp = unsigned(args.args.cast<double>("spp", bpp/bpi));
+
+ //make the new streamer given the samples per packet
+ boost::shared_ptr<sph::recv_packet_streamer> my_streamer = boost::make_shared<sph::recv_packet_streamer>(spp);
+
+ //init some streamer stuff
+ my_streamer->resize(args.channels.size());
+ my_streamer->set_vrt_unpacker(&vrt::if_hdr_unpack_le);
+
+ //set the converter
+ uhd::convert::id_type id;
+ id.input_format = args.otw_format + "_item32_le";
+ id.num_inputs = 1;
+ id.output_format = args.cpu_format;
+ id.num_outputs = 1;
+ my_streamer->set_converter(id);
+
+ //bind callbacks for the handler
+ for (size_t chan_i = 0; chan_i < args.channels.size(); chan_i++){
+ const size_t dsp = args.channels[chan_i];
+ _rx_dsps[dsp]->set_nsamps_per_packet(spp); //seems to be a good place to set this
+ _rx_dsps[dsp]->setup(args);
+ my_streamer->set_xport_chan_get_buff(chan_i, boost::bind(
+ &recv_packet_demuxer::get_recv_buff, _io_impl->demuxer, dsp, _1
+ ), true /*flush*/);
+ my_streamer->set_overflow_handler(chan_i, boost::bind(
+ &rx_dsp_core_200::handle_overflow, _rx_dsps[dsp]
+ ));
+ _rx_streamers[dsp] = my_streamer; //store weak pointer
+ }
+
+ //sets all tick and samp rates on this streamer
+ this->update_rates();
+
+ return my_streamer;
+}
+
+/***********************************************************************
+ * Transmit streamer
+ **********************************************************************/
+tx_streamer::sptr b100_impl::get_tx_stream(const uhd::stream_args_t &args_){
+ stream_args_t args = args_;
+
+ //setup defaults for unspecified values
+ args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format;
+ args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels;
+
+ //calculate packet size
+ static const size_t hdr_size = 0
+ + vrt::max_if_hdr_words32*sizeof(boost::uint32_t)
+ + sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer
+ - sizeof(vrt::if_packet_info_t().sid) //no stream id ever used
+ - sizeof(vrt::if_packet_info_t().cid) //no class id ever used
+ - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used
+ ;
+ static const size_t bpp = _data_transport->get_send_frame_size() - hdr_size;
+ const size_t spp = bpp/convert::get_bytes_per_item(args.otw_format);
+
+ //make the new streamer given the samples per packet
+ boost::shared_ptr<sph::send_packet_streamer> my_streamer = boost::make_shared<sph::send_packet_streamer>(spp);
+
+ //init some streamer stuff
+ my_streamer->resize(args.channels.size());
+ my_streamer->set_vrt_packer(&vrt::if_hdr_pack_le);
+
+ //set the converter
+ uhd::convert::id_type id;
+ id.input_format = args.cpu_format;
+ id.num_inputs = 1;
+ id.output_format = args.otw_format + "_item32_le";
+ id.num_outputs = 1;
+ my_streamer->set_converter(id);
+
+ //bind callbacks for the handler
+ for (size_t chan_i = 0; chan_i < args.channels.size(); chan_i++){
+ const size_t dsp = args.channels[chan_i];
+ UHD_ASSERT_THROW(dsp == 0); //always 0
+ _tx_dsp->setup(args);
+ my_streamer->set_xport_chan_get_buff(chan_i, boost::bind(
+ &zero_copy_if::get_send_buff, _data_transport, _1
+ ));
+ _tx_streamers[dsp] = my_streamer; //store weak pointer
+ }
+
+ //sets all tick and samp rates on this streamer
+ this->update_rates();
+
+ return my_streamer;
+}
diff --git a/host/lib/usrp/common/CMakeLists.txt b/host/lib/usrp/common/CMakeLists.txt
new file mode 100644
index 000000000..9abd34afa
--- /dev/null
+++ b/host/lib/usrp/common/CMakeLists.txt
@@ -0,0 +1,35 @@
+#
+# Copyright 2011 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# This file included, use CMake directory variables
+########################################################################
+IF(ENABLE_USB)
+ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/../firmware/fx2/common)
+
+ LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/fx2_ctrl.cpp
+ )
+ENDIF(ENABLE_USB)
+
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
+
+LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/apply_corrections.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/validate_subdev_spec.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/recv_packet_demuxer.cpp
+)
diff --git a/host/lib/usrp/common/apply_corrections.cpp b/host/lib/usrp/common/apply_corrections.cpp
new file mode 100644
index 000000000..6aee00753
--- /dev/null
+++ b/host/lib/usrp/common/apply_corrections.cpp
@@ -0,0 +1,217 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "apply_corrections.hpp"
+#include <uhd/usrp/dboard_eeprom.hpp>
+#include <uhd/utils/paths.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/csv.hpp>
+#include <uhd/types/dict.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/foreach.hpp>
+#include <boost/thread/mutex.hpp>
+#include <cstdio>
+#include <complex>
+#include <fstream>
+
+namespace fs = boost::filesystem;
+
+boost::mutex corrections_mutex;
+
+/***********************************************************************
+ * Helper routines
+ **********************************************************************/
+static double linear_interp(double x, double x0, double y0, double x1, double y1){
+ return y0 + (x - x0)*(y1 - y0)/(x1 - x0);
+}
+
+/***********************************************************************
+ * FE apply corrections implementation
+ **********************************************************************/
+struct fe_cal_t{
+ double lo_freq;
+ double iq_corr_real;
+ double iq_corr_imag;
+};
+
+static bool fe_cal_comp(fe_cal_t a, fe_cal_t b){
+ return (a.lo_freq < b.lo_freq);
+}
+
+static uhd::dict<std::string, std::vector<fe_cal_t> > fe_cal_cache;
+
+static std::complex<double> get_fe_dc_correction(
+ const std::string &key, const double lo_freq
+){
+ const std::vector<fe_cal_t> &datas = fe_cal_cache[key];
+ if (datas.empty()) throw uhd::runtime_error("empty calibration table " + key);
+
+ //search for lo freq
+ size_t lo_index = 0;
+ size_t hi_index = datas.size()-1;
+ for (size_t i = 0; i < datas.size(); i++){
+ if (datas[i].lo_freq > lo_freq){
+ hi_index = i;
+ break;
+ }
+ lo_index = i;
+ }
+
+ if (lo_index == 0) return std::complex<double>(datas[lo_index].iq_corr_real, datas[lo_index].iq_corr_imag);
+ if (hi_index == lo_index) return std::complex<double>(datas[hi_index].iq_corr_real, datas[hi_index].iq_corr_imag);
+
+ //interpolation time
+ return std::complex<double>(
+ linear_interp(lo_freq, datas[lo_index].lo_freq, datas[lo_index].iq_corr_real, datas[hi_index].lo_freq, datas[hi_index].iq_corr_real),
+ linear_interp(lo_freq, datas[lo_index].lo_freq, datas[lo_index].iq_corr_imag, datas[hi_index].lo_freq, datas[hi_index].iq_corr_imag)
+ );
+}
+
+static std::complex<double> get_fe_iq_correction(
+ const std::string &key, const double lo_freq
+){
+ const std::vector<fe_cal_t> &datas = fe_cal_cache[key];
+ if (datas.empty()) throw uhd::runtime_error("empty calibration table " + key);
+
+ //search for lo freq
+ size_t lo_index = 0;
+ size_t hi_index = datas.size()-1;
+ for (size_t i = 0; i < datas.size(); i++){
+ if (datas[i].lo_freq > lo_freq){
+ hi_index = i;
+ break;
+ }
+ lo_index = i;
+ }
+
+ if (lo_index == 0) return std::complex<double>(datas[lo_index].iq_corr_real, datas[lo_index].iq_corr_imag);
+ if (hi_index == lo_index) return std::complex<double>(datas[hi_index].iq_corr_real, datas[hi_index].iq_corr_imag);
+
+ const std::complex<double> lo_val(datas[lo_index].iq_corr_real, datas[lo_index].iq_corr_imag);
+ const std::complex<double> hi_val(datas[hi_index].iq_corr_real, datas[hi_index].iq_corr_imag);
+
+ //interpolation time
+ return std::polar<double>(
+ linear_interp(lo_freq, datas[lo_index].lo_freq, std::abs(lo_val), datas[hi_index].lo_freq, std::abs(hi_val)),
+ linear_interp(lo_freq, datas[lo_index].lo_freq, std::arg(lo_val), datas[hi_index].lo_freq, std::arg(hi_val))
+ );
+}
+
+static void apply_fe_corrections(
+ uhd::property_tree::sptr sub_tree,
+ const uhd::fs_path &db_path,
+ const uhd::fs_path &fe_path,
+ const std::string &file_prefix,
+ const double lo_freq
+){
+ //extract eeprom serial
+ const uhd::usrp::dboard_eeprom_t db_eeprom = sub_tree->access<uhd::usrp::dboard_eeprom_t>(db_path).get();
+
+ //make the calibration file path
+ const fs::path cal_data_path = fs::path(uhd::get_app_path()) / ".uhd" / "cal" / (file_prefix + db_eeprom.serial + ".csv");
+ if (not fs::exists(cal_data_path)) return;
+
+ //parse csv file or get from cache
+ if (not fe_cal_cache.has_key(cal_data_path.string())){
+ std::ifstream cal_data(cal_data_path.string().c_str());
+ const uhd::csv::rows_type rows = uhd::csv::to_rows(cal_data);
+
+ bool read_data = false, skip_next = false;;
+ std::vector<fe_cal_t> datas;
+ BOOST_FOREACH(const uhd::csv::row_type &row, rows){
+ if (not read_data and not row.empty() and row[0] == "DATA STARTS HERE"){
+ read_data = true;
+ skip_next = true;
+ continue;
+ }
+ if (not read_data) continue;
+ if (skip_next){
+ skip_next = false;
+ continue;
+ }
+ fe_cal_t data;
+ std::sscanf(row[0].c_str(), "%lf" , &data.lo_freq);
+ std::sscanf(row[1].c_str(), "%lf" , &data.iq_corr_real);
+ std::sscanf(row[2].c_str(), "%lf" , &data.iq_corr_imag);
+ datas.push_back(data);
+ }
+ std::sort(datas.begin(), datas.end(), fe_cal_comp);
+ fe_cal_cache[cal_data_path.string()] = datas;
+ UHD_MSG(status) << "Loaded " << cal_data_path.string() << std::endl;
+
+ }
+
+ if (file_prefix.find("dc_cal") != std::string::npos){
+ sub_tree->access<std::complex<double> >(fe_path)
+ .set(get_fe_dc_correction(cal_data_path.string(), lo_freq));
+ }
+ else if (file_prefix.find("iq_cal") != std::string::npos){
+ sub_tree->access<std::complex<double> >(fe_path)
+ .set(get_fe_iq_correction(cal_data_path.string(), lo_freq));
+ }
+ else throw uhd::runtime_error("could not determine interpolation function");
+}
+
+/***********************************************************************
+ * Wrapper routines with nice try/catch + print
+ **********************************************************************/
+void uhd::usrp::apply_tx_fe_corrections(
+ property_tree::sptr sub_tree, //starts at mboards/x
+ const std::string &slot, //name of dboard slot
+ const double lo_freq //actual lo freq
+){
+ boost::mutex::scoped_lock l(corrections_mutex);
+ try{
+ apply_fe_corrections(
+ sub_tree,
+ "dboards/" + slot + "/tx_eeprom",
+ "tx_frontends/" + slot + "/iq_balance/value",
+ "tx_iq_cal_v0.1_",
+ lo_freq
+ );
+ apply_fe_corrections(
+ sub_tree,
+ "dboards/" + slot + "/tx_eeprom",
+ "tx_frontends/" + slot + "/dc_offset/value",
+ "tx_dc_cal_v0.1_",
+ lo_freq
+ );
+ }
+ catch(const std::exception &e){
+ UHD_MSG(error) << "Failure in apply_tx_fe_corrections: " << e.what() << std::endl;
+ }
+}
+
+void uhd::usrp::apply_rx_fe_corrections(
+ property_tree::sptr sub_tree, //starts at mboards/x
+ const std::string &slot, //name of dboard slot
+ const double lo_freq //actual lo freq
+){
+ boost::mutex::scoped_lock l(corrections_mutex);
+ try{
+ apply_fe_corrections(
+ sub_tree,
+ "dboards/" + slot + "/rx_eeprom",
+ "rx_frontends/" + slot + "/iq_balance/value",
+ "rx_iq_cal_v0.1_",
+ lo_freq
+ );
+ }
+ catch(const std::exception &e){
+ UHD_MSG(error) << "Failure in apply_rx_fe_corrections: " << e.what() << std::endl;
+ }
+}
diff --git a/host/lib/usrp/common/apply_corrections.hpp b/host/lib/usrp/common/apply_corrections.hpp
new file mode 100644
index 000000000..c516862d1
--- /dev/null
+++ b/host/lib/usrp/common/apply_corrections.hpp
@@ -0,0 +1,41 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_COMMON_APPLY_CORRECTIONS_HPP
+#define INCLUDED_LIBUHD_USRP_COMMON_APPLY_CORRECTIONS_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/property_tree.hpp>
+#include <string>
+
+namespace uhd{ namespace usrp{
+
+ void apply_tx_fe_corrections(
+ property_tree::sptr sub_tree, //starts at mboards/x
+ const std::string &slot, //name of dboard slot
+ const double tx_lo_freq //actual lo freq
+ );
+
+ void apply_rx_fe_corrections(
+ property_tree::sptr sub_tree, //starts at mboards/x
+ const std::string &slot, //name of dboard slot
+ const double rx_lo_freq //actual lo freq
+ );
+
+}} //namespace uhd::usrp
+
+#endif /* INCLUDED_LIBUHD_USRP_COMMON_APPLY_CORRECTIONS_HPP */
diff --git a/host/lib/usrp/common/async_packet_handler.hpp b/host/lib/usrp/common/async_packet_handler.hpp
new file mode 100644
index 000000000..fef03483f
--- /dev/null
+++ b/host/lib/usrp/common/async_packet_handler.hpp
@@ -0,0 +1,71 @@
+//
+// Copyright 2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_COMMON_ASYNC_PACKET_HANDLER_HPP
+#define INCLUDED_LIBUHD_USRP_COMMON_ASYNC_PACKET_HANDLER_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/transport/vrt_if_packet.hpp>
+#include <uhd/types/metadata.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/msg.hpp>
+
+namespace uhd{ namespace usrp{
+
+ template <typename to_host_type>
+ void load_metadata_from_buff(
+ const to_host_type &to_host,
+ async_metadata_t &metadata,
+ const transport::vrt::if_packet_info_t &if_packet_info,
+ const boost::uint32_t *vrt_hdr,
+ const double tick_rate,
+ const size_t channel = 0
+ ){
+ const boost::uint32_t *payload = vrt_hdr + if_packet_info.num_header_words32;
+
+ //load into metadata
+ metadata.channel = channel;
+ metadata.has_time_spec = if_packet_info.has_tsf;
+ metadata.time_spec = time_spec_t::from_ticks(if_packet_info.tsf, tick_rate);
+ metadata.event_code = async_metadata_t::event_code_t(to_host(payload[0]) & 0xff);
+
+ //load user payload
+ for (size_t i = 1; i < if_packet_info.num_payload_words32; i++){
+ if (i-1 == 4) break; //limit of 4 words32
+ metadata.user_payload[i-1] = to_host(payload[i]);
+ }
+ }
+
+ UHD_INLINE void standard_async_msg_prints(const async_metadata_t &metadata)
+ {
+ if (metadata.event_code &
+ ( async_metadata_t::EVENT_CODE_UNDERFLOW
+ | async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET)
+ ) UHD_MSG(fastpath) << "U";
+ else if (metadata.event_code &
+ ( async_metadata_t::EVENT_CODE_SEQ_ERROR
+ | async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST)
+ ) UHD_MSG(fastpath) << "S";
+ else if (metadata.event_code &
+ async_metadata_t::EVENT_CODE_TIME_ERROR
+ ) UHD_MSG(fastpath) << "L";
+ }
+
+
+}} //namespace uhd::usrp
+
+#endif /* INCLUDED_LIBUHD_USRP_COMMON_ASYNC_PACKET_HANDLER_HPP */
diff --git a/host/lib/usrp/common/fx2_ctrl.cpp b/host/lib/usrp/common/fx2_ctrl.cpp
new file mode 100644
index 000000000..7b8920eb1
--- /dev/null
+++ b/host/lib/usrp/common/fx2_ctrl.cpp
@@ -0,0 +1,472 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "fx2_ctrl.hpp"
+#include "usrp_commands.h"
+#include <uhd/utils/msg.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/transport/usb_control.hpp>
+#include <boost/functional/hash.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/cstdint.hpp>
+#include <fstream>
+#include <sstream>
+#include <string>
+#include <vector>
+#include <cstring>
+
+using namespace uhd;
+using namespace uhd::usrp;
+
+#define FX2_FIRMWARE_LOAD 0xa0
+
+static const bool load_img_msg = true;
+
+typedef boost::uint32_t hash_type;
+
+/***********************************************************************
+ * Helper Functions
+ **********************************************************************/
+/*!
+ * Create a file hash
+ * The hash will be used to identify the loaded firmware and fpga image
+ * \param filename file used to generate hash value
+ * \return hash value in a size_t type
+ */
+static hash_type generate_hash(const char *filename)
+{
+ std::ifstream file(filename);
+ if (not file){
+ throw uhd::io_error(std::string("cannot open input file ") + filename);
+ }
+
+ size_t hash = 0;
+
+ char ch;
+ while (file.get(ch)) {
+ boost::hash_combine(hash, ch);
+ }
+
+ if (not file.eof()){
+ throw uhd::io_error(std::string("file error ") + filename);
+ }
+
+ file.close();
+ return hash_type(hash);
+}
+
+
+/*!
+ * Verify checksum of a Intel HEX record
+ * \param record a line from an Intel HEX file
+ * \return true if record is valid, false otherwise
+ */
+static bool checksum(std::string *record)
+{
+
+ size_t len = record->length();
+ unsigned int i;
+ unsigned char sum = 0;
+ unsigned int val;
+
+ for (i = 1; i < len; i += 2) {
+ std::istringstream(record->substr(i, 2)) >> std::hex >> val;
+ sum += val;
+ }
+
+ if (sum == 0)
+ return true;
+ else
+ return false;
+}
+
+
+/*!
+ * Parse Intel HEX record
+ *
+ * \param record a line from an Intel HEX file
+ * \param len output length of record
+ * \param addr output address
+ * \param type output type
+ * \param data output data
+ * \return true if record is sucessfully read, false on error
+ */
+bool parse_record(std::string *record, unsigned int &len,
+ unsigned int &addr, unsigned int &type,
+ unsigned char* data)
+{
+ unsigned int i;
+ std::string _data;
+ unsigned int val;
+
+ if (record->substr(0, 1) != ":")
+ return false;
+
+ std::istringstream(record->substr(1, 2)) >> std::hex >> len;
+ std::istringstream(record->substr(3, 4)) >> std::hex >> addr;
+ std::istringstream(record->substr(7, 2)) >> std::hex >> type;
+
+ for (i = 0; i < len; i++) {
+ std::istringstream(record->substr(9 + 2 * i, 2)) >> std::hex >> val;
+ data[i] = (unsigned char) val;
+ }
+
+ return true;
+}
+
+
+/*!
+ * USRP control implementation for device discovery and configuration
+ */
+class fx2_ctrl_impl : public fx2_ctrl {
+public:
+ fx2_ctrl_impl(uhd::transport::usb_control::sptr ctrl_transport)
+ {
+ _ctrl_transport = ctrl_transport;
+ }
+
+ void usrp_fx2_reset(void){
+ unsigned char reset_y = 1;
+ unsigned char reset_n = 0;
+ usrp_control_write(FX2_FIRMWARE_LOAD, 0xe600, 0, &reset_y, 1);
+ usrp_control_write(FX2_FIRMWARE_LOAD, 0xe600, 0, &reset_n, 1);
+ //wait for things to settle
+ boost::this_thread::sleep(boost::posix_time::milliseconds(2000));
+ }
+
+ void usrp_load_firmware(std::string filestring, bool force)
+ {
+ const char *filename = filestring.c_str();
+
+ hash_type hash = generate_hash(filename);
+
+ hash_type loaded_hash; usrp_get_firmware_hash(loaded_hash);
+
+ if (not force and (hash == loaded_hash)) return;
+
+ //FIXME: verify types
+ unsigned int len;
+ unsigned int addr;
+ unsigned int type;
+ unsigned char data[512];
+
+ std::ifstream file;
+ file.open(filename, std::ifstream::in);
+
+ if (!file.good()) {
+ throw uhd::io_error("usrp_load_firmware: cannot open firmware input file");
+ }
+
+ unsigned char reset_y = 1;
+ unsigned char reset_n = 0;
+
+ //hit the reset line
+ if (load_img_msg) UHD_MSG(status) << "Loading firmware image: " << filestring << "..." << std::flush;
+ usrp_control_write(FX2_FIRMWARE_LOAD, 0xe600, 0, &reset_y, 1);
+
+ while (!file.eof()) {
+ std::string record;
+ file >> record;
+
+ //check for valid record
+ if (not checksum(&record) or not parse_record(&record, len, addr, type, data)) {
+ throw uhd::io_error("usrp_load_firmware: bad record checksum");
+ }
+
+ //type 0x00 is data
+ if (type == 0x00) {
+ int ret = usrp_control_write(FX2_FIRMWARE_LOAD, addr, 0, data, len);
+ if (ret < 0) throw uhd::io_error("usrp_load_firmware: usrp_control_write failed");
+ }
+ //type 0x01 is end
+ else if (type == 0x01) {
+ usrp_set_firmware_hash(hash); //set hash before reset
+ usrp_control_write(FX2_FIRMWARE_LOAD, 0xe600, 0, &reset_n, 1);
+ file.close();
+
+ //wait for things to settle
+ boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
+ if (load_img_msg) UHD_MSG(status) << " done" << std::endl;
+ return;
+ }
+ //type anything else is unhandled
+ else {
+ throw uhd::io_error("usrp_load_firmware: unsupported record");
+ }
+ }
+
+ //file did not end
+ throw uhd::io_error("usrp_load_firmware: bad record");
+ }
+
+ void usrp_init(void){
+ //disable
+ usrp_rx_enable(false);
+ usrp_tx_enable(false);
+
+ //toggle resets
+ usrp_rx_reset(true);
+ usrp_tx_reset(true);
+ usrp_rx_reset(false);
+ usrp_tx_reset(false);
+ }
+
+ void usrp_load_fpga(std::string filestring)
+ {
+ const char *filename = filestring.c_str();
+
+ hash_type hash = generate_hash(filename);
+
+ hash_type loaded_hash; usrp_get_fpga_hash(loaded_hash);
+
+ if (hash == loaded_hash) return;
+ const int ep0_size = 64;
+ unsigned char buf[ep0_size];
+
+ if (load_img_msg) UHD_MSG(status) << "Loading FPGA image: " << filestring << "..." << std::flush;
+ std::ifstream file;
+ file.open(filename, std::ios::in | std::ios::binary);
+ if (not file.good()) {
+ throw uhd::io_error("usrp_load_fpga: cannot open fpga input file");
+ }
+
+ usrp_fpga_reset(true); //holding the fpga in reset while loading
+
+ if (usrp_control_write_cmd(VRQ_FPGA_LOAD, 0, FL_BEGIN) < 0) {
+ throw uhd::io_error("usrp_load_fpga: fpga load error");
+ }
+
+ while (not file.eof()) {
+ file.read((char *)buf, sizeof(buf));
+ const std::streamsize n = file.gcount();
+ if(n == 0) continue;
+ int ret = usrp_control_write(VRQ_FPGA_LOAD, 0, FL_XFER, buf, boost::uint16_t(n));
+ if (ret < 0 or std::streamsize(ret) != n) {
+ throw uhd::io_error("usrp_load_fpga: fpga load error");
+ }
+ }
+
+ if (usrp_control_write_cmd(VRQ_FPGA_LOAD, 0, FL_END) < 0) {
+ throw uhd::io_error("usrp_load_fpga: fpga load error");
+ }
+
+ usrp_set_fpga_hash(hash);
+
+ usrp_fpga_reset(false); //done loading, take fpga out of reset
+
+ file.close();
+ if (load_img_msg) UHD_MSG(status) << " done" << std::endl;
+ }
+
+ void usrp_load_eeprom(std::string filestring)
+ {
+ if (load_img_msg) UHD_MSG(status) << "Loading EEPROM image: " << filestring << "..." << std::flush;
+ const char *filename = filestring.c_str();
+ const boost::uint16_t i2c_addr = 0x50;
+
+ unsigned int addr;
+ unsigned char data[256];
+ unsigned char sendbuf[17];
+
+ std::ifstream file;
+ file.open(filename, std::ifstream::in);
+
+ if (not file.good()) {
+ throw uhd::io_error("usrp_load_eeprom: cannot open EEPROM input file");
+ }
+
+ file.read((char *)data, 256);
+ std::streamsize len = file.gcount();
+
+ if(len == 256) {
+ throw uhd::io_error("usrp_load_eeprom: image size too large");
+ }
+
+ const int pagesize = 16;
+ addr = 0;
+ while(len > 0) {
+ sendbuf[0] = addr;
+ memcpy(sendbuf+1, &data[addr], len > pagesize ? pagesize : size_t(len));
+ int ret = usrp_i2c_write(i2c_addr, sendbuf, (len > pagesize ? pagesize : size_t(len))+1);
+ if (ret < 0) {
+ throw uhd::io_error("usrp_load_eeprom: usrp_i2c_write failed");
+ }
+ addr += pagesize;
+ len -= pagesize;
+ boost::this_thread::sleep(boost::posix_time::milliseconds(100));
+ }
+ file.close();
+ if (load_img_msg) UHD_MSG(status) << " done" << std::endl;
+ }
+
+
+ void usrp_set_led(int led_num, bool on)
+ {
+ UHD_ASSERT_THROW(usrp_control_write_cmd(VRQ_SET_LED, on, led_num) >= 0);
+ }
+
+
+ void usrp_get_firmware_hash(hash_type &hash)
+ {
+ UHD_ASSERT_THROW(usrp_control_read(0xa0, USRP_HASH_SLOT_0_ADDR, 0,
+ (unsigned char*) &hash, sizeof(hash)) >= 0);
+ }
+
+
+ void usrp_set_firmware_hash(hash_type hash)
+ {
+ UHD_ASSERT_THROW(usrp_control_write(0xa0, USRP_HASH_SLOT_0_ADDR, 0,
+ (unsigned char*) &hash, sizeof(hash)) >= 0);
+
+ }
+
+
+ void usrp_get_fpga_hash(hash_type &hash)
+ {
+ UHD_ASSERT_THROW(usrp_control_read(0xa0, USRP_HASH_SLOT_1_ADDR, 0,
+ (unsigned char*) &hash, sizeof(hash)) >= 0);
+ }
+
+
+ void usrp_set_fpga_hash(hash_type hash)
+ {
+ UHD_ASSERT_THROW(usrp_control_write(0xa0, USRP_HASH_SLOT_1_ADDR, 0,
+ (unsigned char*) &hash, sizeof(hash)) >= 0);
+ }
+
+ void usrp_tx_enable(bool on)
+ {
+ UHD_ASSERT_THROW(usrp_control_write_cmd(VRQ_FPGA_SET_TX_ENABLE, on, 0) >= 0);
+ }
+
+
+ void usrp_rx_enable(bool on)
+ {
+ UHD_ASSERT_THROW(usrp_control_write_cmd(VRQ_FPGA_SET_RX_ENABLE, on, 0) >= 0);
+ }
+
+
+ void usrp_tx_reset(bool on)
+ {
+ UHD_ASSERT_THROW(usrp_control_write_cmd(VRQ_FPGA_SET_TX_RESET, on, 0) >= 0);
+ }
+
+
+ void usrp_rx_reset(bool on)
+ {
+ UHD_ASSERT_THROW(usrp_control_write_cmd(VRQ_FPGA_SET_RX_RESET, on, 0) >= 0);
+ }
+
+ void usrp_fpga_reset(bool on)
+ {
+ UHD_ASSERT_THROW(usrp_control_write_cmd(VRQ_FPGA_SET_RESET, on, 0) >= 0);
+ }
+
+ int usrp_control_write(boost::uint8_t request,
+ boost::uint16_t value,
+ boost::uint16_t index,
+ unsigned char *buff,
+ boost::uint16_t length)
+ {
+ return _ctrl_transport->submit(VRT_VENDOR_OUT, // bmReqeustType
+ request, // bRequest
+ value, // wValue
+ index, // wIndex
+ buff, // data
+ length); // wLength
+ }
+
+
+ int usrp_control_read(boost::uint8_t request,
+ boost::uint16_t value,
+ boost::uint16_t index,
+ unsigned char *buff,
+ boost::uint16_t length)
+ {
+ return _ctrl_transport->submit(VRT_VENDOR_IN, // bmReqeustType
+ request, // bRequest
+ value, // wValue
+ index, // wIndex
+ buff, // data
+ length); // wLength
+ }
+
+
+ int usrp_control_write_cmd(boost::uint8_t request, boost::uint16_t value, boost::uint16_t index)
+ {
+ return usrp_control_write(request, value, index, 0, 0);
+ }
+
+ int usrp_i2c_write(boost::uint16_t i2c_addr, unsigned char *buf, boost::uint16_t len)
+ {
+ return usrp_control_write(VRQ_I2C_WRITE, i2c_addr, 0, buf, len);
+ }
+
+ int usrp_i2c_read(boost::uint16_t i2c_addr, unsigned char *buf, boost::uint16_t len)
+ {
+ return usrp_control_read(VRQ_I2C_READ, i2c_addr, 0, buf, len);
+ }
+
+ static const bool iface_debug = false;
+ static const size_t max_i2c_data_bytes = 64;
+
+ void write_i2c(boost::uint8_t addr, const byte_vector_t &bytes)
+ {
+ UHD_ASSERT_THROW(bytes.size() < max_i2c_data_bytes);
+
+ unsigned char buff[max_i2c_data_bytes] = {};
+ std::copy(bytes.begin(), bytes.end(), buff);
+
+ int ret = this->usrp_i2c_write(addr & 0xff,
+ buff,
+ bytes.size());
+
+ if (iface_debug && (ret < 0))
+ uhd::runtime_error("USRP: failed i2c write");
+ }
+
+ byte_vector_t read_i2c(boost::uint8_t addr, size_t num_bytes)
+ {
+ UHD_ASSERT_THROW(num_bytes < max_i2c_data_bytes);
+
+ unsigned char buff[max_i2c_data_bytes] = {};
+ int ret = this->usrp_i2c_read(addr & 0xff,
+ buff,
+ num_bytes);
+
+ if (iface_debug && ((ret < 0) || (unsigned)ret < (num_bytes)))
+ uhd::runtime_error("USRP: failed i2c read");
+
+ byte_vector_t out_bytes;
+ for (size_t i = 0; i < num_bytes; i++)
+ out_bytes.push_back(buff[i]);
+
+ return out_bytes;
+ }
+
+
+private:
+ uhd::transport::usb_control::sptr _ctrl_transport;
+};
+
+/***********************************************************************
+ * Public make function for fx2_ctrl interface
+ **********************************************************************/
+fx2_ctrl::sptr fx2_ctrl::make(uhd::transport::usb_control::sptr ctrl_transport){
+ return sptr(new fx2_ctrl_impl(ctrl_transport));
+}
+
diff --git a/host/lib/usrp/common/fx2_ctrl.hpp b/host/lib/usrp/common/fx2_ctrl.hpp
new file mode 100644
index 000000000..f2e060862
--- /dev/null
+++ b/host/lib/usrp/common/fx2_ctrl.hpp
@@ -0,0 +1,129 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_COMMON_FX2_CTRL_HPP
+#define INCLUDED_LIBUHD_USRP_COMMON_FX2_CTRL_HPP
+
+#include <uhd/transport/usb_control.hpp>
+#include <uhd/types/serial.hpp> //i2c iface
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+
+namespace uhd{ namespace usrp{
+
+class fx2_ctrl : boost::noncopyable, public uhd::i2c_iface{
+public:
+ typedef boost::shared_ptr<fx2_ctrl> sptr;
+
+ /*!
+ * Make a usrp control object from a control transport
+ * \param ctrl_transport a USB control transport
+ * \return a new usrp control object
+ */
+ static sptr make(uhd::transport::usb_control::sptr ctrl_transport);
+
+ //! Call init after the fpga is loaded
+ virtual void usrp_init(void) = 0;
+
+ //! For emergency situations
+ virtual void usrp_fx2_reset(void) = 0;
+
+ /*!
+ * Load firmware in Intel HEX Format onto device
+ * \param filename name of firmware file
+ * \param force reload firmware if already loaded
+ */
+ virtual void usrp_load_firmware(std::string filename,
+ bool force = false) = 0;
+
+ /*!
+ * Load fpga file onto usrp
+ * \param filename name of fpga image
+ */
+ virtual void usrp_load_fpga(std::string filename) = 0;
+
+ /*!
+ * Load USB descriptor file in Intel HEX format into EEPROM
+ * \param filename name of EEPROM image
+ */
+ virtual void usrp_load_eeprom(std::string filestring) = 0;
+
+ /*!
+ * Submit an IN transfer
+ * \param request device specific request
+ * \param value device specific field
+ * \param index device specific field
+ * \param buff buffer to place data
+ * \return number of bytes read or error
+ */
+ virtual int usrp_control_read(boost::uint8_t request,
+ boost::uint16_t value,
+ boost::uint16_t index,
+ unsigned char *buff,
+ boost::uint16_t length) = 0;
+
+ /*!
+ * Submit an OUT transfer
+ * \param request device specific request
+ * \param value device specific field
+ * \param index device specific field
+ * \param buff buffer of data to be sent
+ * \return number of bytes written or error
+ */
+ virtual int usrp_control_write(boost::uint8_t request,
+ boost::uint16_t value,
+ boost::uint16_t index,
+ unsigned char *buff,
+ boost::uint16_t length) = 0;
+
+ /*!
+ * Perform an I2C write
+ * \param i2c_addr I2C device address
+ * \param buf data to be written
+ * \param len length of data in bytes
+ * \return number of bytes written or error
+ */
+
+ virtual int usrp_i2c_write(boost::uint16_t i2c_addr,
+ unsigned char *buf,
+ boost::uint16_t len) = 0;
+
+ /*!
+ * Perform an I2C read
+ * \param i2c_addr I2C device address
+ * \param buf data to be read
+ * \param len length of data in bytes
+ * \return number of bytes read or error
+ */
+
+ virtual int usrp_i2c_read(boost::uint16_t i2c_addr,
+ unsigned char *buf,
+ boost::uint16_t len) = 0;
+
+ //! enable/disable the rx path
+ virtual void usrp_rx_enable(bool on) = 0;
+
+ //! enable/disable the tx path
+ virtual void usrp_tx_enable(bool on) = 0;
+
+ //! reset the fpga
+ virtual void usrp_fpga_reset(bool on) = 0;
+};
+
+}} //namespace uhd::usrp
+
+#endif /* INCLUDED_LIBUHD_USRP_COMMON_FX2_CTRL_HPP */
diff --git a/host/lib/usrp/common/recv_packet_demuxer.cpp b/host/lib/usrp/common/recv_packet_demuxer.cpp
new file mode 100644
index 000000000..f2cfe3bb0
--- /dev/null
+++ b/host/lib/usrp/common/recv_packet_demuxer.cpp
@@ -0,0 +1,87 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "recv_packet_demuxer.hpp"
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <boost/thread/mutex.hpp>
+#include <queue>
+#include <deque>
+#include <vector>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+
+static UHD_INLINE boost::uint32_t extract_sid(managed_recv_buffer::sptr &buff){
+ //ASSUME that the data is in little endian format
+ return uhd::wtohx(buff->cast<const boost::uint32_t *>()[1]);
+}
+
+class recv_packet_demuxer_impl : public uhd::usrp::recv_packet_demuxer{
+public:
+ recv_packet_demuxer_impl(
+ transport::zero_copy_if::sptr transport,
+ const size_t size,
+ const boost::uint32_t sid_base
+ ):
+ _transport(transport), _sid_base(sid_base), _queues(size)
+ {
+ /* NOP */
+ }
+
+ managed_recv_buffer::sptr get_recv_buff(const size_t index, const double timeout){
+ boost::mutex::scoped_lock lock(_mutex);
+ managed_recv_buffer::sptr buff;
+
+ //there is already an entry in the queue, so pop that
+ if (not _queues[index].wrapper.empty()){
+ std::swap(buff, _queues[index].wrapper.front());
+ _queues[index].wrapper.pop();
+ return buff;
+ }
+
+ while (true){
+ //otherwise call into the transport
+ buff = _transport->get_recv_buff(timeout);
+ if (buff.get() == NULL) return buff; //timeout
+
+ //check the stream id to know which channel
+ const size_t rx_index = extract_sid(buff) - _sid_base;
+ if (rx_index == index) return buff; //got expected message
+
+ //otherwise queue and try again
+ if (rx_index < _queues.size()) _queues[rx_index].wrapper.push(buff);
+ else UHD_MSG(error) << "Got a data packet with unknown SID " << extract_sid(buff) << std::endl;
+ }
+ }
+
+private:
+ transport::zero_copy_if::sptr _transport;
+ const boost::uint32_t _sid_base;
+ boost::mutex _mutex;
+ struct channel_guts_type{
+ channel_guts_type(void): wrapper(container){}
+ std::deque<managed_recv_buffer::sptr> container;
+ std::queue<managed_recv_buffer::sptr> wrapper;
+ };
+ std::vector<channel_guts_type> _queues;
+};
+
+recv_packet_demuxer::sptr recv_packet_demuxer::make(transport::zero_copy_if::sptr transport, const size_t size, const boost::uint32_t sid_base){
+ return sptr(new recv_packet_demuxer_impl(transport, size, sid_base));
+}
diff --git a/host/lib/usrp/common/recv_packet_demuxer.hpp b/host/lib/usrp/common/recv_packet_demuxer.hpp
new file mode 100644
index 000000000..fde756d27
--- /dev/null
+++ b/host/lib/usrp/common/recv_packet_demuxer.hpp
@@ -0,0 +1,41 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_HPP
+#define INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/transport/zero_copy.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/cstdint.hpp>
+
+namespace uhd{ namespace usrp{
+
+ class recv_packet_demuxer{
+ public:
+ typedef boost::shared_ptr<recv_packet_demuxer> sptr;
+
+ //! Make a new demuxer from a transport and parameters
+ static sptr make(transport::zero_copy_if::sptr transport, const size_t size, const boost::uint32_t sid_base);
+
+ //! Get a buffer at the given index from the transport
+ virtual transport::managed_recv_buffer::sptr get_recv_buff(const size_t index, const double timeout) = 0;
+ };
+
+}} //namespace uhd::usrp
+
+#endif /* INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_HPP */
diff --git a/host/lib/usrp/common/validate_subdev_spec.cpp b/host/lib/usrp/common/validate_subdev_spec.cpp
new file mode 100644
index 000000000..fab40b204
--- /dev/null
+++ b/host/lib/usrp/common/validate_subdev_spec.cpp
@@ -0,0 +1,73 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "validate_subdev_spec.hpp"
+#include <uhd/exception.hpp>
+#include <uhd/utils/assert_has.hpp>
+#include <boost/foreach.hpp>
+#include <boost/format.hpp>
+
+using namespace uhd;
+using namespace uhd::usrp;
+
+namespace uhd{ namespace usrp{
+
+ static std::ostream& operator<< (std::ostream &out, const subdev_spec_pair_t &pair){
+ out << pair.db_name << ":" << pair.sd_name;
+ return out;
+ }
+
+}}
+
+void uhd::usrp::validate_subdev_spec(
+ property_tree::sptr tree,
+ const subdev_spec_t &spec,
+ const std::string &type,
+ const std::string &mb
+){
+ const size_t num_dsps = tree->list(str(boost::format("/mboards/%s/%s_dsps") % mb % type)).size();
+
+ //sanity checking on the length
+ if (spec.size() == 0) throw uhd::value_error(str(boost::format(
+ "Empty %s subdevice specification is not supported.\n"
+ ) % type));
+ if (spec.size() > num_dsps) throw uhd::value_error(str(boost::format(
+ "The subdevice specification \"%s\" is too long.\n"
+ "The user specified %u channels, but there are only %u %s dsps on mboard %s.\n"
+ ) % spec.to_string() % spec.size() % num_dsps % type % mb));
+
+ //make a list of all possible specs
+ subdev_spec_t all_specs;
+ BOOST_FOREACH(const std::string &db, tree->list(str(boost::format("/mboards/%s/dboards") % mb))){
+ BOOST_FOREACH(const std::string &sd, tree->list(str(boost::format("/mboards/%s/dboards/%s/%s_frontends") % mb % db % type))){
+ all_specs.push_back(subdev_spec_pair_t(db, sd));
+ }
+ }
+
+ //validate that the spec is possible
+ BOOST_FOREACH(const subdev_spec_pair_t &pair, spec){
+ uhd::assert_has(all_specs, pair, str(boost::format("%s subdevice specification on mboard %s") % type % mb));
+ }
+
+ //enable selected frontends, disable others
+ BOOST_FOREACH(const subdev_spec_pair_t &pair, all_specs){
+ const bool enb = uhd::has(spec, pair);
+ tree->access<bool>(str(boost::format(
+ "/mboards/%s/dboards/%s/%s_frontends/%s/enabled"
+ ) % mb % pair.db_name % type % pair.sd_name)).set(enb);
+ }
+}
diff --git a/host/lib/usrp/common/validate_subdev_spec.hpp b/host/lib/usrp/common/validate_subdev_spec.hpp
new file mode 100644
index 000000000..7d9e2c309
--- /dev/null
+++ b/host/lib/usrp/common/validate_subdev_spec.hpp
@@ -0,0 +1,38 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_COMMON_VALIDATE_SUBDEV_SPEC_HPP
+#define INCLUDED_LIBUHD_USRP_COMMON_VALIDATE_SUBDEV_SPEC_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/usrp/subdev_spec.hpp>
+#include <uhd/property_tree.hpp>
+#include <string>
+
+namespace uhd{ namespace usrp{
+
+ //! Validate a subdev spec against a property tree
+ void validate_subdev_spec(
+ property_tree::sptr tree,
+ const subdev_spec_t &spec,
+ const std::string &type, //rx or tx
+ const std::string &mb = "0"
+ );
+
+}} //namespace uhd::usrp
+
+#endif /* INCLUDED_LIBUHD_USRP_COMMON_VALIDATE_SUBDEV_SPEC_HPP */
diff --git a/host/lib/usrp/cores/CMakeLists.txt b/host/lib/usrp/cores/CMakeLists.txt
new file mode 100644
index 000000000..aa5f0bcbb
--- /dev/null
+++ b/host/lib/usrp/cores/CMakeLists.txt
@@ -0,0 +1,34 @@
+#
+# Copyright 2011-2012 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# This file included, use CMake directory variables
+########################################################################
+
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
+
+LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/gpio_core_200.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/i2c_core_100.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/spi_core_100.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/time64_core_200.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/rx_dsp_core_200.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/tx_dsp_core_200.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/rx_frontend_core_200.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/tx_frontend_core_200.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/user_settings_core_200.cpp
+)
diff --git a/host/lib/usrp/cores/gpio_core_200.cpp b/host/lib/usrp/cores/gpio_core_200.cpp
new file mode 100644
index 000000000..d756097ff
--- /dev/null
+++ b/host/lib/usrp/cores/gpio_core_200.cpp
@@ -0,0 +1,100 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "gpio_core_200.hpp"
+#include <uhd/types/dict.hpp>
+
+#define REG_GPIO_IDLE _base + 0
+#define REG_GPIO_RX_ONLY _base + 4
+#define REG_GPIO_TX_ONLY _base + 8
+#define REG_GPIO_BOTH _base + 12
+#define REG_GPIO_DDR _base + 16
+
+using namespace uhd;
+using namespace usrp;
+
+class gpio_core_200_impl : public gpio_core_200{
+public:
+ gpio_core_200_impl(wb_iface::sptr iface, const size_t base, const size_t rb_addr):
+ _iface(iface), _base(base), _rb_addr(rb_addr) { /* NOP */ }
+
+ void set_pin_ctrl(const unit_t unit, const boost::uint16_t value){
+ _pin_ctrl[unit] = value; //shadow
+ this->update(); //full update
+ }
+
+ void set_atr_reg(const unit_t unit, const atr_reg_t atr, const boost::uint16_t value){
+ _atr_regs[unit][atr] = value; //shadow
+ this->update(); //full update
+ }
+
+ void set_gpio_ddr(const unit_t unit, const boost::uint16_t value){
+ _gpio_ddr[unit] = value; //shadow
+ _iface->poke32(REG_GPIO_DDR, //update the 32 bit register
+ (boost::uint32_t(_gpio_ddr[dboard_iface::UNIT_RX]) << unit2shit(dboard_iface::UNIT_RX)) |
+ (boost::uint32_t(_gpio_ddr[dboard_iface::UNIT_TX]) << unit2shit(dboard_iface::UNIT_TX))
+ );
+ }
+
+ void set_gpio_out(const unit_t unit, const boost::uint16_t value){
+ _gpio_out[unit] = value; //shadow
+ this->update(); //full update
+ }
+
+ boost::uint16_t read_gpio(const unit_t unit){
+ return boost::uint16_t(_iface->peek32(_rb_addr) >> unit2shit(unit));
+ }
+
+private:
+ wb_iface::sptr _iface;
+ const size_t _base;
+ const size_t _rb_addr;
+
+ uhd::dict<unit_t, boost::uint16_t> _pin_ctrl, _gpio_out, _gpio_ddr;
+ uhd::dict<unit_t, uhd::dict<atr_reg_t, boost::uint16_t> > _atr_regs;
+
+ unsigned unit2shit(const unit_t unit){
+ return (unit == dboard_iface::UNIT_RX)? 0 : 16;
+ }
+
+ void update(void){
+ this->update(dboard_iface::ATR_REG_IDLE, REG_GPIO_IDLE);
+ this->update(dboard_iface::ATR_REG_TX_ONLY, REG_GPIO_TX_ONLY);
+ this->update(dboard_iface::ATR_REG_RX_ONLY, REG_GPIO_RX_ONLY);
+ this->update(dboard_iface::ATR_REG_FULL_DUPLEX, REG_GPIO_BOTH);
+ }
+
+ void update(const atr_reg_t atr, const size_t addr){
+ const boost::uint32_t atr_val =
+ (boost::uint32_t(_atr_regs[dboard_iface::UNIT_RX][atr]) << unit2shit(dboard_iface::UNIT_RX)) |
+ (boost::uint32_t(_atr_regs[dboard_iface::UNIT_TX][atr]) << unit2shit(dboard_iface::UNIT_TX));
+
+ const boost::uint32_t gpio_val =
+ (boost::uint32_t(_gpio_out[dboard_iface::UNIT_RX]) << unit2shit(dboard_iface::UNIT_RX)) |
+ (boost::uint32_t(_gpio_out[dboard_iface::UNIT_TX]) << unit2shit(dboard_iface::UNIT_TX));
+
+ const boost::uint32_t ctrl =
+ (boost::uint32_t(_pin_ctrl[dboard_iface::UNIT_RX]) << unit2shit(dboard_iface::UNIT_RX)) |
+ (boost::uint32_t(_pin_ctrl[dboard_iface::UNIT_TX]) << unit2shit(dboard_iface::UNIT_TX));
+ _iface->poke32(addr, (ctrl & atr_val) | ((~ctrl) & gpio_val));
+ }
+
+};
+
+gpio_core_200::sptr gpio_core_200::make(wb_iface::sptr iface, const size_t base, const size_t rb_addr){
+ return sptr(new gpio_core_200_impl(iface, base, rb_addr));
+}
diff --git a/host/lib/usrp/cores/gpio_core_200.hpp b/host/lib/usrp/cores/gpio_core_200.hpp
new file mode 100644
index 000000000..278575874
--- /dev/null
+++ b/host/lib/usrp/cores/gpio_core_200.hpp
@@ -0,0 +1,52 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_GPIO_CORE_200_HPP
+#define INCLUDED_LIBUHD_USRP_GPIO_CORE_200_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/usrp/dboard_iface.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include "wb_iface.hpp"
+
+class gpio_core_200 : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<gpio_core_200> sptr;
+
+ typedef uhd::usrp::dboard_iface::unit_t unit_t;
+ typedef uhd::usrp::dboard_iface::atr_reg_t atr_reg_t;
+
+ //! makes a new GPIO core from iface and slave base
+ static sptr make(wb_iface::sptr iface, const size_t base, const size_t rb_addr);
+
+ //! 1 = ATR
+ virtual void set_pin_ctrl(const unit_t unit, const boost::uint16_t value) = 0;
+
+ virtual void set_atr_reg(const unit_t unit, const atr_reg_t atr, const boost::uint16_t value) = 0;
+
+ //! 1 = OUTPUT
+ virtual void set_gpio_ddr(const unit_t unit, const boost::uint16_t value) = 0;
+
+ virtual void set_gpio_out(const unit_t unit, const boost::uint16_t value) = 0;
+
+ virtual boost::uint16_t read_gpio(const unit_t unit) = 0;
+
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_GPIO_CORE_200_HPP */
diff --git a/host/lib/usrp/cores/i2c_core_100.cpp b/host/lib/usrp/cores/i2c_core_100.cpp
new file mode 100644
index 000000000..ceeb3f518
--- /dev/null
+++ b/host/lib/usrp/cores/i2c_core_100.cpp
@@ -0,0 +1,140 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "i2c_core_100.hpp"
+#include <uhd/exception.hpp>
+#include <uhd/utils/msg.hpp>
+#include <boost/thread/thread.hpp> //sleep
+
+#define REG_I2C_PRESCALER_LO _base + 0
+#define REG_I2C_PRESCALER_HI _base + 2
+#define REG_I2C_CTRL _base + 4
+#define REG_I2C_DATA _base + 6
+#define REG_I2C_CMD_STATUS _base + 8
+
+//
+// STA, STO, RD, WR, and IACK bits are cleared automatically
+//
+
+#define I2C_CTRL_EN (1 << 7) // core enable
+#define I2C_CTRL_IE (1 << 6) // interrupt enable
+
+#define I2C_CMD_START (1 << 7) // generate (repeated) start condition
+#define I2C_CMD_STOP (1 << 6) // generate stop condition
+#define I2C_CMD_RD (1 << 5) // read from slave
+#define I2C_CMD_WR (1 << 4) // write to slave
+#define I2C_CMD_NACK (1 << 3) // when a rcvr, send ACK (ACK=0) or NACK (ACK=1)
+#define I2C_CMD_RSVD_2 (1 << 2) // reserved
+#define I2C_CMD_RSVD_1 (1 << 1) // reserved
+#define I2C_CMD_IACK (1 << 0) // set to clear pending interrupt
+
+#define I2C_ST_RXACK (1 << 7) // Received acknowledgement from slave (1 = NAK, 0 = ACK)
+#define I2C_ST_BUSY (1 << 6) // 1 after START signal detected; 0 after STOP signal detected
+#define I2C_ST_AL (1 << 5) // Arbitration lost. 1 when core lost arbitration
+#define I2C_ST_RSVD_4 (1 << 4) // reserved
+#define I2C_ST_RSVD_3 (1 << 3) // reserved
+#define I2C_ST_RSVD_2 (1 << 2) // reserved
+#define I2C_ST_TIP (1 << 1) // Transfer-in-progress
+#define I2C_ST_IP (1 << 0) // Interrupt pending
+
+using namespace uhd;
+
+class i2c_core_100_impl : public i2c_core_100{
+public:
+ i2c_core_100_impl(wb_iface::sptr iface, const size_t base):
+ _iface(iface), _base(base)
+ {
+ //init I2C FPGA interface.
+ _iface->poke16(REG_I2C_CTRL, 0x0000);
+ //set prescalers to operate at 400kHz: WB_CLK is 64MHz...
+ static const boost::uint32_t i2c_datarate = 400000;
+ static const boost::uint32_t wishbone_clk = 64000000; //FIXME should go somewhere else
+ boost::uint16_t prescaler = wishbone_clk / (i2c_datarate*5) - 1;
+ _iface->poke16(REG_I2C_PRESCALER_LO, prescaler & 0xFF);
+ _iface->poke16(REG_I2C_PRESCALER_HI, (prescaler >> 8) & 0xFF);
+ _iface->poke16(REG_I2C_CTRL, I2C_CTRL_EN); //enable I2C core
+ }
+
+ void write_i2c(
+ boost::uint8_t addr,
+ const byte_vector_t &bytes
+ ){
+ _iface->poke16(REG_I2C_DATA, (addr << 1) | 0); //addr and read bit (0)
+ _iface->poke16(REG_I2C_CMD_STATUS, I2C_CMD_WR | I2C_CMD_START | (bytes.size() == 0 ? I2C_CMD_STOP : 0));
+
+ //wait for previous transfer to complete
+ if (not wait_chk_ack()) {
+ _iface->poke16(REG_I2C_CMD_STATUS, I2C_CMD_STOP);
+ return;
+ }
+
+ for (size_t i = 0; i < bytes.size(); i++) {
+ _iface->poke16(REG_I2C_DATA, bytes[i]);
+ _iface->poke16(REG_I2C_CMD_STATUS, I2C_CMD_WR | ((i == (bytes.size() - 1)) ? I2C_CMD_STOP : 0));
+ if(!wait_chk_ack()) {
+ _iface->poke16(REG_I2C_CMD_STATUS, I2C_CMD_STOP);
+ return;
+ }
+ }
+ }
+
+ byte_vector_t read_i2c(
+ boost::uint8_t addr,
+ size_t num_bytes
+ ){
+ byte_vector_t bytes;
+ if (num_bytes == 0) return bytes;
+
+ while (_iface->peek16(REG_I2C_CMD_STATUS) & I2C_ST_BUSY){
+ /* NOP */
+ }
+
+ _iface->poke16(REG_I2C_DATA, (addr << 1) | 1); //addr and read bit (1)
+ _iface->poke16(REG_I2C_CMD_STATUS, I2C_CMD_WR | I2C_CMD_START);
+ //wait for previous transfer to complete
+ if (not wait_chk_ack()) {
+ _iface->poke16(REG_I2C_CMD_STATUS, I2C_CMD_STOP);
+ }
+ for (size_t i = 0; i < num_bytes; i++) {
+ _iface->poke16(REG_I2C_CMD_STATUS, I2C_CMD_RD | ((num_bytes == i+1) ? (I2C_CMD_STOP | I2C_CMD_NACK) : 0));
+ i2c_wait();
+ bytes.push_back(boost::uint8_t(_iface->peek16(REG_I2C_DATA)));
+ }
+ return bytes;
+ }
+
+private:
+ void i2c_wait(void) {
+ for (size_t i = 0; i < 100; i++){
+ if ((_iface->peek16(REG_I2C_CMD_STATUS) & I2C_ST_TIP) == 0) return;
+ boost::this_thread::sleep(boost::posix_time::milliseconds(1));
+ }
+ UHD_MSG(error) << "i2c_core_100: i2c_wait timeout" << std::endl;
+ }
+
+ bool wait_chk_ack(void){
+ i2c_wait();
+ return (_iface->peek16(REG_I2C_CMD_STATUS) & I2C_ST_RXACK) == 0;
+ }
+
+ wb_iface::sptr _iface;
+ const size_t _base;
+};
+
+i2c_core_100::sptr i2c_core_100::make(wb_iface::sptr iface, const size_t base){
+ return sptr(new i2c_core_100_impl(iface, base));
+}
diff --git a/host/lib/usrp/cores/i2c_core_100.hpp b/host/lib/usrp/cores/i2c_core_100.hpp
new file mode 100644
index 000000000..f7a5ae4f7
--- /dev/null
+++ b/host/lib/usrp/cores/i2c_core_100.hpp
@@ -0,0 +1,35 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_I2C_CORE_100_HPP
+#define INCLUDED_LIBUHD_USRP_I2C_CORE_100_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/types/serial.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include "wb_iface.hpp"
+
+class i2c_core_100 : boost::noncopyable, public uhd::i2c_iface{
+public:
+ typedef boost::shared_ptr<i2c_core_100> sptr;
+
+ //! makes a new i2c core from iface and slave base
+ static sptr make(wb_iface::sptr iface, const size_t base);
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_I2C_CORE_100_HPP */
diff --git a/host/lib/usrp/cores/rx_dsp_core_200.cpp b/host/lib/usrp/cores/rx_dsp_core_200.cpp
new file mode 100644
index 000000000..0996952ff
--- /dev/null
+++ b/host/lib/usrp/cores/rx_dsp_core_200.cpp
@@ -0,0 +1,260 @@
+//
+// Copyright 2011-2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "rx_dsp_core_200.hpp"
+#include <uhd/types/dict.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/thread/thread.hpp> //thread sleep
+#include <boost/math/special_functions/round.hpp>
+#include <boost/math/special_functions/sign.hpp>
+#include <algorithm>
+#include <cmath>
+
+#define REG_DSP_RX_FREQ _dsp_base + 0
+#define REG_DSP_RX_SCALE_IQ _dsp_base + 4
+#define REG_DSP_RX_DECIM _dsp_base + 8
+#define REG_DSP_RX_MUX _dsp_base + 12
+
+#define FLAG_DSP_RX_MUX_SWAP_IQ (1 << 0)
+#define FLAG_DSP_RX_MUX_REAL_MODE (1 << 1)
+
+#define REG_RX_CTRL_STREAM_CMD _ctrl_base + 0
+#define REG_RX_CTRL_TIME_HI _ctrl_base + 4
+#define REG_RX_CTRL_TIME_LO _ctrl_base + 8
+#define REG_RX_CTRL_CLEAR _ctrl_base + 12
+#define REG_RX_CTRL_VRT_HDR _ctrl_base + 16
+#define REG_RX_CTRL_VRT_SID _ctrl_base + 20
+#define REG_RX_CTRL_VRT_TLR _ctrl_base + 24
+#define REG_RX_CTRL_NSAMPS_PP _ctrl_base + 28
+#define REG_RX_CTRL_NCHANNELS _ctrl_base + 32
+#define REG_RX_CTRL_FORMAT REG_RX_CTRL_CLEAR //re-use clear address
+
+template <class T> T ceil_log2(T num){
+ return std::ceil(std::log(num)/std::log(T(2)));
+}
+
+using namespace uhd;
+
+class rx_dsp_core_200_impl : public rx_dsp_core_200{
+public:
+ rx_dsp_core_200_impl(
+ wb_iface::sptr iface,
+ const size_t dsp_base, const size_t ctrl_base,
+ const boost::uint32_t sid, const bool lingering_packet
+ ):
+ _iface(iface), _dsp_base(dsp_base), _ctrl_base(ctrl_base), _sid(sid)
+ {
+ //init to something so update method has reasonable defaults
+ _scaling_adjustment = 1.0;
+ _dsp_extra_scaling = 1.0;
+
+ //This is a hack/fix for the lingering packet problem.
+ //The caller should also flush the recv transports
+ if (lingering_packet){
+ stream_cmd_t stream_cmd(stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE);
+ stream_cmd.num_samps = 1;
+ issue_stream_command(stream_cmd);
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10)); //lets lingering pkt propagate
+ }
+
+ this->clear();
+ }
+
+ void clear(void){
+ _iface->poke32(REG_RX_CTRL_CLEAR, 1); //reset
+ _iface->poke32(REG_RX_CTRL_NCHANNELS, 1);
+ _iface->poke32(REG_RX_CTRL_VRT_HDR, 0
+ | (0x1 << 28) //if data with stream id
+ | (0x1 << 26) //has trailer
+ | (0x1 << 20) //fractional time sample count
+ );
+ _iface->poke32(REG_RX_CTRL_VRT_SID, _sid);
+ _iface->poke32(REG_RX_CTRL_VRT_TLR, 0);
+ }
+
+ void set_nsamps_per_packet(const size_t nsamps){
+ _iface->poke32(REG_RX_CTRL_NSAMPS_PP, nsamps);
+ }
+
+ void issue_stream_command(const stream_cmd_t &stream_cmd){
+ UHD_ASSERT_THROW(stream_cmd.num_samps <= 0x0fffffff);
+ _continuous_streaming = stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
+
+ //setup the mode to instruction flags
+ typedef boost::tuple<bool, bool, bool, bool> inst_t;
+ static const uhd::dict<stream_cmd_t::stream_mode_t, inst_t> mode_to_inst = boost::assign::map_list_of
+ //reload, chain, samps, stop
+ (stream_cmd_t::STREAM_MODE_START_CONTINUOUS, inst_t(true, true, false, false))
+ (stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS, inst_t(false, false, false, true))
+ (stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE, inst_t(false, false, true, false))
+ (stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE, inst_t(false, true, true, false))
+ ;
+
+ //setup the instruction flag values
+ bool inst_reload, inst_chain, inst_samps, inst_stop;
+ boost::tie(inst_reload, inst_chain, inst_samps, inst_stop) = mode_to_inst[stream_cmd.stream_mode];
+
+ //calculate the word from flags and length
+ boost::uint32_t cmd_word = 0;
+ cmd_word |= boost::uint32_t((stream_cmd.stream_now)? 1 : 0) << 31;
+ cmd_word |= boost::uint32_t((inst_chain)? 1 : 0) << 30;
+ cmd_word |= boost::uint32_t((inst_reload)? 1 : 0) << 29;
+ cmd_word |= boost::uint32_t((inst_stop)? 1 : 0) << 28;
+ cmd_word |= (inst_samps)? stream_cmd.num_samps : ((inst_stop)? 0 : 1);
+
+ //issue the stream command
+ _iface->poke32(REG_RX_CTRL_STREAM_CMD, cmd_word);
+ const boost::uint64_t ticks = (stream_cmd.stream_now)? 0 : stream_cmd.time_spec.to_ticks(_tick_rate);
+ _iface->poke32(REG_RX_CTRL_TIME_HI, boost::uint32_t(ticks >> 32));
+ _iface->poke32(REG_RX_CTRL_TIME_LO, boost::uint32_t(ticks >> 0)); //latches the command
+ }
+
+ void set_mux(const std::string &mode, const bool fe_swapped){
+ static const uhd::dict<std::string, boost::uint32_t> mode_to_mux = boost::assign::map_list_of
+ ("IQ", 0)
+ ("QI", FLAG_DSP_RX_MUX_SWAP_IQ)
+ ("I", FLAG_DSP_RX_MUX_REAL_MODE)
+ ("Q", FLAG_DSP_RX_MUX_SWAP_IQ | FLAG_DSP_RX_MUX_REAL_MODE)
+ ;
+ _iface->poke32(REG_DSP_RX_MUX, mode_to_mux[mode] ^ (fe_swapped? FLAG_DSP_RX_MUX_SWAP_IQ : 0));
+ }
+
+ void set_tick_rate(const double rate){
+ _tick_rate = rate;
+ }
+
+ void set_link_rate(const double rate){
+ //_link_rate = rate/sizeof(boost::uint32_t); //in samps/s
+ _link_rate = rate/sizeof(boost::uint16_t); //in samps/s (allows for 8sc)
+ }
+
+ uhd::meta_range_t get_host_rates(void){
+ meta_range_t range;
+ for (int rate = 512; rate > 256; rate -= 4){
+ range.push_back(range_t(_tick_rate/rate));
+ }
+ for (int rate = 256; rate > 128; rate -= 2){
+ range.push_back(range_t(_tick_rate/rate));
+ }
+ for (int rate = 128; rate >= int(std::ceil(_tick_rate/_link_rate)); rate -= 1){
+ range.push_back(range_t(_tick_rate/rate));
+ }
+ return range;
+ }
+
+ double set_host_rate(const double rate){
+ const size_t decim_rate = boost::math::iround(_tick_rate/this->get_host_rates().clip(rate, true));
+ size_t decim = decim_rate;
+
+ //determine which half-band filters are activated
+ int hb0 = 0, hb1 = 0;
+ if (decim % 2 == 0){
+ hb0 = 1;
+ decim /= 2;
+ }
+ if (decim % 2 == 0){
+ hb1 = 1;
+ decim /= 2;
+ }
+
+ _iface->poke32(REG_DSP_RX_DECIM, (hb1 << 9) | (hb0 << 8) | (decim & 0xff));
+
+ // Calculate CIC decimation (i.e., without halfband decimators)
+ // Calculate closest multiplier constant to reverse gain absent scale multipliers
+ const double rate_pow = std::pow(double(decim & 0xff), 4);
+ _scaling_adjustment = std::pow(2, ceil_log2(rate_pow))/(1.65*rate_pow);
+ this->update_scalar();
+
+ return _tick_rate/decim_rate;
+ }
+
+ void update_scalar(void){
+ const double target_scalar = (1 << 16)*_scaling_adjustment/_dsp_extra_scaling;
+ const boost::int32_t actual_scalar = boost::math::iround(target_scalar);
+ _fxpt_scalar_correction = target_scalar/actual_scalar; //should be small
+ _iface->poke32(REG_DSP_RX_SCALE_IQ, actual_scalar);
+ }
+
+ double get_scaling_adjustment(void){
+ return _fxpt_scalar_correction*_host_extra_scaling/32767.;
+ }
+
+ double set_freq(const double freq_){
+ //correct for outside of rate (wrap around)
+ double freq = std::fmod(freq_, _tick_rate);
+ if (std::abs(freq) > _tick_rate/2.0)
+ freq -= boost::math::sign(freq)*_tick_rate;
+
+ //calculate the freq register word (signed)
+ UHD_ASSERT_THROW(std::abs(freq) <= _tick_rate/2.0);
+ static const double scale_factor = std::pow(2.0, 32);
+ const boost::int32_t freq_word = boost::int32_t(boost::math::round((freq / _tick_rate) * scale_factor));
+
+ //update the actual frequency
+ const double actual_freq = (double(freq_word) / scale_factor) * _tick_rate;
+
+ _iface->poke32(REG_DSP_RX_FREQ, boost::uint32_t(freq_word));
+
+ return actual_freq;
+ }
+
+ uhd::meta_range_t get_freq_range(void){
+ return uhd::meta_range_t(-_tick_rate/2, +_tick_rate/2, _tick_rate/std::pow(2.0, 32));
+ }
+
+ void handle_overflow(void){
+ if (_continuous_streaming) issue_stream_command(stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
+ }
+
+ void setup(const uhd::stream_args_t &stream_args){
+ if (not stream_args.args.has_key("noclear")) this->clear();
+
+ unsigned format_word = 0;
+ if (stream_args.otw_format == "sc16"){
+ format_word = 0;
+ _dsp_extra_scaling = 1.0;
+ _host_extra_scaling = 1.0;
+ }
+ else if (stream_args.otw_format == "sc8"){
+ format_word = (1 << 0);
+ double peak = stream_args.args.cast<double>("peak", 1.0);
+ peak = std::max(peak, 1.0/256);
+ _host_extra_scaling = peak*256;
+ _dsp_extra_scaling = peak*256;
+ }
+ else throw uhd::value_error("USRP RX cannot handle requested wire format: " + stream_args.otw_format);
+
+ this->update_scalar();
+
+ _iface->poke32(REG_RX_CTRL_FORMAT, format_word);
+ }
+
+private:
+ wb_iface::sptr _iface;
+ const size_t _dsp_base, _ctrl_base;
+ double _tick_rate, _link_rate;
+ bool _continuous_streaming;
+ double _scaling_adjustment, _dsp_extra_scaling, _host_extra_scaling, _fxpt_scalar_correction;
+ const boost::uint32_t _sid;
+};
+
+rx_dsp_core_200::sptr rx_dsp_core_200::make(wb_iface::sptr iface, const size_t dsp_base, const size_t ctrl_base, const boost::uint32_t sid, const bool lingering_packet){
+ return sptr(new rx_dsp_core_200_impl(iface, dsp_base, ctrl_base, sid, lingering_packet));
+}
diff --git a/host/lib/usrp/cores/rx_dsp_core_200.hpp b/host/lib/usrp/cores/rx_dsp_core_200.hpp
new file mode 100644
index 000000000..b01f751e9
--- /dev/null
+++ b/host/lib/usrp/cores/rx_dsp_core_200.hpp
@@ -0,0 +1,67 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_RX_DSP_CORE_200_HPP
+#define INCLUDED_LIBUHD_USRP_RX_DSP_CORE_200_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/stream.hpp>
+#include <uhd/types/ranges.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include <uhd/types/stream_cmd.hpp>
+#include "wb_iface.hpp"
+#include <string>
+
+class rx_dsp_core_200 : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<rx_dsp_core_200> sptr;
+
+ static sptr make(
+ wb_iface::sptr iface,
+ const size_t dsp_base, const size_t ctrl_base,
+ const boost::uint32_t sid, const bool lingering_packet = false
+ );
+
+ virtual void clear(void) = 0;
+
+ virtual void set_nsamps_per_packet(const size_t nsamps) = 0;
+
+ virtual void issue_stream_command(const uhd::stream_cmd_t &stream_cmd) = 0;
+
+ virtual void set_mux(const std::string &mode, const bool fe_swapped = false) = 0;
+
+ virtual void set_tick_rate(const double rate) = 0;
+
+ virtual void set_link_rate(const double rate) = 0;
+
+ virtual double set_host_rate(const double rate) = 0;
+
+ virtual uhd::meta_range_t get_host_rates(void) = 0;
+
+ virtual double get_scaling_adjustment(void) = 0;
+
+ virtual uhd::meta_range_t get_freq_range(void) = 0;
+
+ virtual double set_freq(const double freq) = 0;
+
+ virtual void handle_overflow(void) = 0;
+
+ virtual void setup(const uhd::stream_args_t &stream_args) = 0;
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_RX_DSP_CORE_200_HPP */
diff --git a/host/lib/usrp/cores/rx_frontend_core_200.cpp b/host/lib/usrp/cores/rx_frontend_core_200.cpp
new file mode 100644
index 000000000..d6396ef45
--- /dev/null
+++ b/host/lib/usrp/cores/rx_frontend_core_200.cpp
@@ -0,0 +1,79 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "rx_frontend_core_200.hpp"
+#include <boost/math/special_functions/round.hpp>
+
+#define REG_RX_FE_SWAP_IQ _base + 0 //lower bit
+#define REG_RX_FE_MAG_CORRECTION _base + 4 //18 bits
+#define REG_RX_FE_PHASE_CORRECTION _base + 8 //18 bits
+#define REG_RX_FE_OFFSET_I _base + 12 //18 bits
+#define REG_RX_FE_OFFSET_Q _base + 16 //18 bits
+
+#define OFFSET_FIXED (1ul << 31)
+#define OFFSET_SET (1ul << 30)
+
+static boost::uint32_t fs_to_bits(const double num, const size_t bits){
+ return boost::int32_t(boost::math::round(num * (1 << (bits-1))));
+}
+
+
+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):
+ _iface(iface), _base(base)
+ {
+ //NOP
+ }
+
+ void set_mux(const bool swap){
+ _iface->poke32(REG_RX_FE_SWAP_IQ, swap? 1 : 0);
+ }
+
+ void set_dc_offset_auto(const bool enb){
+ this->set_dc_offset(enb? 0 : OFFSET_FIXED);
+ }
+
+ std::complex<double> set_dc_offset(const std::complex<double> &off){
+ static const double scaler = double(1ul << 29);
+ _i_dc_off = boost::math::iround(off.real()*scaler);
+ _q_dc_off = boost::math::iround(off.imag()*scaler);
+
+ this->set_dc_offset(OFFSET_SET | OFFSET_FIXED);
+
+ return std::complex<double>(_i_dc_off/scaler, _q_dc_off/scaler);
+ }
+
+ void set_dc_offset(const boost::uint32_t flags){
+ _iface->poke32(REG_RX_FE_OFFSET_I, flags | _i_dc_off);
+ _iface->poke32(REG_RX_FE_OFFSET_Q, flags | _q_dc_off);
+ }
+
+ void set_iq_balance(const std::complex<double> &cor){
+ _iface->poke32(REG_RX_FE_MAG_CORRECTION, fs_to_bits(std::abs(cor) - 1, 18));
+ _iface->poke32(REG_RX_FE_PHASE_CORRECTION, fs_to_bits(std::arg(cor)/6.28318531, 18));
+ }
+
+private:
+ boost::int32_t _i_dc_off, _q_dc_off;
+ wb_iface::sptr _iface;
+ const size_t _base;
+};
+
+rx_frontend_core_200::sptr rx_frontend_core_200::make(wb_iface::sptr iface, const size_t base){
+ return sptr(new rx_frontend_core_200_impl(iface, base));
+}
diff --git a/host/lib/usrp/cores/rx_frontend_core_200.hpp b/host/lib/usrp/cores/rx_frontend_core_200.hpp
new file mode 100644
index 000000000..5755424c8
--- /dev/null
+++ b/host/lib/usrp/cores/rx_frontend_core_200.hpp
@@ -0,0 +1,44 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_TX_FRONTEND_CORE_200_HPP
+#define INCLUDED_LIBUHD_USRP_TX_FRONTEND_CORE_200_HPP
+
+#include <uhd/config.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include "wb_iface.hpp"
+#include <complex>
+#include <string>
+
+class rx_frontend_core_200 : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<rx_frontend_core_200> sptr;
+
+ static sptr make(wb_iface::sptr iface, const size_t base);
+
+ virtual void set_mux(const bool swap) = 0;
+
+ virtual void set_dc_offset_auto(const bool enb) = 0;
+
+ virtual std::complex<double> set_dc_offset(const std::complex<double> &off) = 0;
+
+ virtual void set_iq_balance(const std::complex<double> &cor) = 0;
+
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_TX_FRONTEND_CORE_200_HPP */
diff --git a/host/lib/usrp/cores/spi_core_100.cpp b/host/lib/usrp/cores/spi_core_100.cpp
new file mode 100644
index 000000000..d11a499a9
--- /dev/null
+++ b/host/lib/usrp/cores/spi_core_100.cpp
@@ -0,0 +1,88 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "spi_core_100.hpp"
+#include <uhd/exception.hpp>
+#include <uhd/utils/msg.hpp>
+#include <boost/thread/thread.hpp> //sleep
+
+#define REG_SPI_TXRX0 _base + 0
+#define REG_SPI_TXRX1 _base + 4
+#define REG_SPI_TXRX2 _base + 8
+#define REG_SPI_TXRX3 _base + 12
+#define REG_SPI_CTRL _base + 16
+#define REG_SPI_DIV _base + 20
+#define REG_SPI_SS _base + 24
+
+//spi ctrl register bit definitions
+#define SPI_CTRL_ASS (1<<13)
+#define SPI_CTRL_IE (1<<12)
+#define SPI_CTRL_LSB (1<<11)
+#define SPI_CTRL_TXNEG (1<<10) //mosi edge, push on falling edge when 1
+#define SPI_CTRL_RXNEG (1<< 9) //miso edge, latch on falling edge when 1
+#define SPI_CTRL_GO_BSY (1<< 8)
+#define SPI_CTRL_CHAR_LEN_MASK 0x7F
+
+using namespace uhd;
+
+class spi_core_100_impl : public spi_core_100{
+public:
+ spi_core_100_impl(wb_iface::sptr iface, const size_t base):
+ _iface(iface), _base(base) { /* NOP */}
+
+ boost::uint32_t transact_spi(
+ int which_slave,
+ const spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits,
+ bool readback
+ ){
+ UHD_ASSERT_THROW(num_bits <= 32 and (num_bits % 8) == 0);
+
+ int edge_flags = ((config.miso_edge==spi_config_t::EDGE_FALL) ? SPI_CTRL_RXNEG : 0) |
+ ((config.mosi_edge==spi_config_t::EDGE_FALL) ? 0 : SPI_CTRL_TXNEG)
+ ;
+ boost::uint16_t ctrl = SPI_CTRL_ASS | (SPI_CTRL_CHAR_LEN_MASK & num_bits) | edge_flags;
+
+ spi_wait();
+ _iface->poke16(REG_SPI_DIV, 0x0001); // = fpga_clk / 4
+ _iface->poke32(REG_SPI_SS, which_slave & 0xFFFF);
+ _iface->poke32(REG_SPI_TXRX0, data);
+ _iface->poke16(REG_SPI_CTRL, ctrl);
+ _iface->poke16(REG_SPI_CTRL, ctrl | SPI_CTRL_GO_BSY);
+
+ if (not readback) return 0;
+ spi_wait();
+ return _iface->peek32(REG_SPI_TXRX0);
+ }
+
+private:
+ void spi_wait(void) {
+ for (size_t i = 0; i < 100; i++){
+ if ((_iface->peek16(REG_SPI_CTRL) & SPI_CTRL_GO_BSY) == 0) return;
+ boost::this_thread::sleep(boost::posix_time::milliseconds(1));
+ }
+ UHD_MSG(error) << "spi_core_100: spi_wait timeout" << std::endl;
+ }
+
+ wb_iface::sptr _iface;
+ const size_t _base;
+};
+
+spi_core_100::sptr spi_core_100::make(wb_iface::sptr iface, const size_t base){
+ return sptr(new spi_core_100_impl(iface, base));
+}
diff --git a/host/lib/usrp/cores/spi_core_100.hpp b/host/lib/usrp/cores/spi_core_100.hpp
new file mode 100644
index 000000000..87d328aaa
--- /dev/null
+++ b/host/lib/usrp/cores/spi_core_100.hpp
@@ -0,0 +1,35 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_SPI_CORE_100_HPP
+#define INCLUDED_LIBUHD_USRP_SPI_CORE_100_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/types/serial.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include "wb_iface.hpp"
+
+class spi_core_100 : boost::noncopyable, public uhd::spi_iface{
+public:
+ typedef boost::shared_ptr<spi_core_100> sptr;
+
+ //! makes a new spi core from iface and slave base
+ static sptr make(wb_iface::sptr iface, const size_t base);
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_SPI_CORE_100_HPP */
diff --git a/host/lib/usrp/cores/time64_core_200.cpp b/host/lib/usrp/cores/time64_core_200.cpp
new file mode 100644
index 000000000..e460d1106
--- /dev/null
+++ b/host/lib/usrp/cores/time64_core_200.cpp
@@ -0,0 +1,134 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "time64_core_200.hpp"
+#include <uhd/exception.hpp>
+#include <uhd/utils/assert_has.hpp>
+#include <boost/math/special_functions/round.hpp>
+
+#define REG_TIME64_TICKS_HI _base + 0
+#define REG_TIME64_TICKS_LO _base + 4
+#define REG_TIME64_FLAGS _base + 8
+#define REG_TIME64_IMM _base + 12
+#define REG_TIME64_MIMO_SYNC _base + 20 //lower byte is delay cycles
+
+//pps flags (see above)
+#define FLAG_TIME64_PPS_NEGEDGE (0 << 0)
+#define FLAG_TIME64_PPS_POSEDGE (1 << 0)
+#define FLAG_TIME64_PPS_SMA (0 << 1)
+#define FLAG_TIME64_PPS_MIMO (1 << 1) //apparently not used
+
+#define FLAG_TIME64_LATCH_NOW 1
+#define FLAG_TIME64_LATCH_NEXT_PPS 0
+
+#define FLAG_TIME64_MIMO_SYNC (1 << 8)
+
+using namespace uhd;
+
+class time64_core_200_impl : public time64_core_200{
+public:
+ time64_core_200_impl(
+ wb_iface::sptr iface, const size_t base,
+ const readback_bases_type &readback_bases,
+ const size_t mimo_delay_cycles
+ ):
+ _iface(iface), _base(base),
+ _readback_bases(readback_bases),
+ _mimo_delay_cycles(mimo_delay_cycles)
+ {
+ _sources.push_back("none");
+ _sources.push_back("external");
+ _sources.push_back("_external_");
+ if (_mimo_delay_cycles != 0) _sources.push_back("mimo");
+ }
+
+ void set_tick_rate(const double rate){
+ _tick_rate = rate;
+ }
+
+ uhd::time_spec_t get_time_now(void){
+ for (size_t i = 0; i < 3; i++){ //special algorithm because we cant read 64 bits synchronously
+ const boost::uint32_t ticks_hi = _iface->peek32(_readback_bases.rb_hi_now);
+ const boost::uint32_t ticks_lo = _iface->peek32(_readback_bases.rb_lo_now);
+ if (ticks_hi != _iface->peek32(_readback_bases.rb_hi_now)) continue;
+ const boost::uint64_t ticks = (boost::uint64_t(ticks_hi) << 32) | ticks_lo;
+ return time_spec_t::from_ticks(ticks, _tick_rate);
+ }
+ throw uhd::runtime_error("time64_core_200: get time now timeout");
+ }
+
+ uhd::time_spec_t get_time_last_pps(void){
+ for (size_t i = 0; i < 3; i++){ //special algorithm because we cant read 64 bits synchronously
+ const boost::uint32_t ticks_hi = _iface->peek32(_readback_bases.rb_hi_pps);
+ const boost::uint32_t ticks_lo = _iface->peek32(_readback_bases.rb_lo_pps);
+ if (ticks_hi != _iface->peek32(_readback_bases.rb_hi_pps)) continue;
+ const boost::uint64_t ticks = (boost::uint64_t(ticks_hi) << 32) | ticks_lo;
+ return time_spec_t::from_ticks(ticks, _tick_rate);
+ }
+ throw uhd::runtime_error("time64_core_200: get time last pps timeout");
+ }
+
+ void set_time_now(const uhd::time_spec_t &time){
+ const boost::uint64_t ticks = time.to_ticks(_tick_rate);
+ _iface->poke32(REG_TIME64_TICKS_LO, boost::uint32_t(ticks >> 0));
+ _iface->poke32(REG_TIME64_IMM, FLAG_TIME64_LATCH_NOW);
+ _iface->poke32(REG_TIME64_TICKS_HI, boost::uint32_t(ticks >> 32)); //latches all 3
+ }
+
+ void set_time_next_pps(const uhd::time_spec_t &time){
+ const boost::uint64_t ticks = time.to_ticks(_tick_rate);
+ _iface->poke32(REG_TIME64_TICKS_LO, boost::uint32_t(ticks >> 0));
+ _iface->poke32(REG_TIME64_IMM, FLAG_TIME64_LATCH_NEXT_PPS);
+ _iface->poke32(REG_TIME64_TICKS_HI, boost::uint32_t(ticks >> 32)); //latches all 3
+ }
+
+ void set_time_source(const std::string &source){
+ assert_has(_sources, source, "time source");
+
+ //setup pps flags
+ if (source == "external"){
+ _iface->poke32(REG_TIME64_FLAGS, FLAG_TIME64_PPS_SMA | FLAG_TIME64_PPS_POSEDGE);
+ }
+ else if (source == "_external_"){
+ _iface->poke32(REG_TIME64_FLAGS, FLAG_TIME64_PPS_SMA | FLAG_TIME64_PPS_NEGEDGE);
+ }
+
+ //setup mimo flags
+ if (source == "mimo"){
+ _iface->poke32(REG_TIME64_MIMO_SYNC, FLAG_TIME64_MIMO_SYNC | (_mimo_delay_cycles & 0xff));
+ }
+ else{
+ _iface->poke32(REG_TIME64_MIMO_SYNC, 0);
+ }
+ }
+
+ std::vector<std::string> get_time_sources(void){
+ return _sources;
+ }
+
+private:
+ wb_iface::sptr _iface;
+ const size_t _base;
+ const readback_bases_type _readback_bases;
+ double _tick_rate;
+ const size_t _mimo_delay_cycles;
+ std::vector<std::string> _sources;
+};
+
+time64_core_200::sptr time64_core_200::make(wb_iface::sptr iface, const size_t base, const readback_bases_type &readback_bases, const size_t mimo_delay_cycles){
+ return sptr(new time64_core_200_impl(iface, base, readback_bases, mimo_delay_cycles));
+}
diff --git a/host/lib/usrp/cores/time64_core_200.hpp b/host/lib/usrp/cores/time64_core_200.hpp
new file mode 100644
index 000000000..7571573a5
--- /dev/null
+++ b/host/lib/usrp/cores/time64_core_200.hpp
@@ -0,0 +1,61 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_TIME64_CORE_200_HPP
+#define INCLUDED_LIBUHD_USRP_TIME64_CORE_200_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/types/time_spec.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include "wb_iface.hpp"
+#include <string>
+#include <vector>
+
+class time64_core_200 : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<time64_core_200> sptr;
+
+ struct readback_bases_type{
+ size_t rb_hi_now, rb_lo_now;
+ size_t rb_hi_pps, rb_lo_pps;
+ };
+
+ //! makes a new time64 core from iface and slave base
+ static sptr make(
+ wb_iface::sptr iface, const size_t base,
+ const readback_bases_type &readback_bases,
+ const size_t mimo_delay_cycles = 0 // 0 means no-mimo
+ );
+
+ virtual void set_tick_rate(const double rate) = 0;
+
+ virtual uhd::time_spec_t get_time_now(void) = 0;
+
+ virtual uhd::time_spec_t get_time_last_pps(void) = 0;
+
+ virtual void set_time_now(const uhd::time_spec_t &time) = 0;
+
+ virtual void set_time_next_pps(const uhd::time_spec_t &time) = 0;
+
+ virtual void set_time_source(const std::string &source) = 0;
+
+ virtual std::vector<std::string> get_time_sources(void) = 0;
+
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_TIME64_CORE_200_HPP */
diff --git a/host/lib/usrp/cores/tx_dsp_core_200.cpp b/host/lib/usrp/cores/tx_dsp_core_200.cpp
new file mode 100644
index 000000000..7f02d59ca
--- /dev/null
+++ b/host/lib/usrp/cores/tx_dsp_core_200.cpp
@@ -0,0 +1,214 @@
+//
+// Copyright 2011-2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "tx_dsp_core_200.hpp"
+#include <uhd/types/dict.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/math/special_functions/round.hpp>
+#include <boost/math/special_functions/sign.hpp>
+#include <boost/thread/thread.hpp> //sleep
+#include <algorithm>
+#include <cmath>
+
+#define REG_DSP_TX_FREQ _dsp_base + 0
+#define REG_DSP_TX_SCALE_IQ _dsp_base + 4
+#define REG_DSP_TX_INTERP _dsp_base + 8
+
+#define REG_TX_CTRL_CLEAR _ctrl_base + 0
+#define REG_TX_CTRL_FORMAT _ctrl_base + 4
+#define REG_TX_CTRL_REPORT_SID _ctrl_base + 8
+#define REG_TX_CTRL_POLICY _ctrl_base + 12
+#define REG_TX_CTRL_CYCLES_PER_UP _ctrl_base + 16
+#define REG_TX_CTRL_PACKETS_PER_UP _ctrl_base + 20
+
+#define FLAG_TX_CTRL_POLICY_WAIT (0x1 << 0)
+#define FLAG_TX_CTRL_POLICY_NEXT_PACKET (0x1 << 1)
+#define FLAG_TX_CTRL_POLICY_NEXT_BURST (0x1 << 2)
+
+//enable flag for registers: cycles and packets per update packet
+#define FLAG_TX_CTRL_UP_ENB (1ul << 31)
+
+template <class T> T ceil_log2(T num){
+ return std::ceil(std::log(num)/std::log(T(2)));
+}
+
+using namespace uhd;
+
+class tx_dsp_core_200_impl : public tx_dsp_core_200{
+public:
+ tx_dsp_core_200_impl(
+ wb_iface::sptr iface,
+ const size_t dsp_base, const size_t ctrl_base,
+ const boost::uint32_t sid
+ ):
+ _iface(iface), _dsp_base(dsp_base), _ctrl_base(ctrl_base), _sid(sid)
+ {
+ //init to something so update method has reasonable defaults
+ _scaling_adjustment = 1.0;
+ _dsp_extra_scaling = 1.0;
+
+ //init the tx control registers
+ this->clear();
+ this->set_underflow_policy("next_packet");
+ }
+
+ void clear(void){
+ _iface->poke32(REG_TX_CTRL_CLEAR, 1); //reset and flush technique
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+ _iface->poke32(REG_TX_CTRL_CLEAR, 0);
+ _iface->poke32(REG_TX_CTRL_REPORT_SID, _sid);
+ }
+
+ void set_underflow_policy(const std::string &policy){
+ if (policy == "next_packet"){
+ _iface->poke32(REG_TX_CTRL_POLICY, FLAG_TX_CTRL_POLICY_NEXT_PACKET);
+ }
+ else if (policy == "next_burst"){
+ _iface->poke32(REG_TX_CTRL_POLICY, FLAG_TX_CTRL_POLICY_NEXT_BURST);
+ }
+ else throw uhd::value_error("USRP TX cannot handle requested underflow policy: " + policy);
+ }
+
+ void set_tick_rate(const double rate){
+ _tick_rate = rate;
+ }
+
+ void set_link_rate(const double rate){
+ //_link_rate = rate/sizeof(boost::uint32_t); //in samps/s
+ _link_rate = rate/sizeof(boost::uint16_t); //in samps/s (allows for 8sc)
+ }
+
+ uhd::meta_range_t get_host_rates(void){
+ meta_range_t range;
+ for (int rate = 512; rate > 256; rate -= 4){
+ range.push_back(range_t(_tick_rate/rate));
+ }
+ for (int rate = 256; rate > 128; rate -= 2){
+ range.push_back(range_t(_tick_rate/rate));
+ }
+ for (int rate = 128; rate >= int(std::ceil(_tick_rate/_link_rate)); rate -= 1){
+ range.push_back(range_t(_tick_rate/rate));
+ }
+ return range;
+ }
+
+ double set_host_rate(const double rate){
+ const size_t interp_rate = boost::math::iround(_tick_rate/this->get_host_rates().clip(rate, true));
+ size_t interp = interp_rate;
+
+ //determine which half-band filters are activated
+ int hb0 = 0, hb1 = 0;
+ if (interp % 2 == 0){
+ hb0 = 1;
+ interp /= 2;
+ }
+ if (interp % 2 == 0){
+ hb1 = 1;
+ interp /= 2;
+ }
+
+ _iface->poke32(REG_DSP_TX_INTERP, (hb1 << 9) | (hb0 << 8) | (interp & 0xff));
+
+ // Calculate CIC interpolation (i.e., without halfband interpolators)
+ // Calculate closest multiplier constant to reverse gain absent scale multipliers
+ const double rate_pow = std::pow(double(interp & 0xff), 3);
+ _scaling_adjustment = std::pow(2, ceil_log2(rate_pow))/(1.65*rate_pow);
+ this->update_scalar();
+
+ return _tick_rate/interp_rate;
+ }
+
+ void update_scalar(void){
+ const double target_scalar = (1 << 17)*_scaling_adjustment/_dsp_extra_scaling;
+ const boost::int32_t actual_scalar = boost::math::iround(target_scalar);
+ _fxpt_scalar_correction = target_scalar/actual_scalar; //should be small
+ _iface->poke32(REG_DSP_TX_SCALE_IQ, actual_scalar);
+ }
+
+ double get_scaling_adjustment(void){
+ return _fxpt_scalar_correction*_host_extra_scaling*32767.;
+ }
+
+ double set_freq(const double freq_){
+ //correct for outside of rate (wrap around)
+ double freq = std::fmod(freq_, _tick_rate);
+ if (std::abs(freq) > _tick_rate/2.0)
+ freq -= boost::math::sign(freq)*_tick_rate;
+
+ //calculate the freq register word (signed)
+ UHD_ASSERT_THROW(std::abs(freq) <= _tick_rate/2.0);
+ static const double scale_factor = std::pow(2.0, 32);
+ const boost::int32_t freq_word = boost::int32_t(boost::math::round((freq / _tick_rate) * scale_factor));
+
+ //update the actual frequency
+ const double actual_freq = (double(freq_word) / scale_factor) * _tick_rate;
+
+ _iface->poke32(REG_DSP_TX_FREQ, boost::uint32_t(freq_word));
+
+ return actual_freq;
+ }
+
+ uhd::meta_range_t get_freq_range(void){
+ return uhd::meta_range_t(-_tick_rate/2, +_tick_rate/2, _tick_rate/std::pow(2.0, 32));
+ }
+
+ void set_updates(const size_t cycles_per_up, const size_t packets_per_up){
+ _iface->poke32(REG_TX_CTRL_CYCLES_PER_UP, (cycles_per_up == 0)? 0 : (FLAG_TX_CTRL_UP_ENB | cycles_per_up));
+ _iface->poke32(REG_TX_CTRL_PACKETS_PER_UP, (packets_per_up == 0)? 0 : (FLAG_TX_CTRL_UP_ENB | packets_per_up));
+ }
+
+ void setup(const uhd::stream_args_t &stream_args){
+ if (not stream_args.args.has_key("noclear")) this->clear();
+
+ unsigned format_word = 0;
+ if (stream_args.otw_format == "sc16"){
+ format_word = 0;
+ _dsp_extra_scaling = 1.0;
+ _host_extra_scaling = 1.0;
+ }
+ else if (stream_args.otw_format == "sc8"){
+ format_word = (1 << 0);
+ double peak = stream_args.args.cast<double>("peak", 1.0);
+ peak = std::max(peak, 1.0/256);
+ _host_extra_scaling = 1.0/peak/256;
+ _dsp_extra_scaling = 1.0/peak;
+ }
+ else throw uhd::value_error("USRP TX cannot handle requested wire format: " + stream_args.otw_format);
+
+ this->update_scalar();
+
+ _iface->poke32(REG_TX_CTRL_FORMAT, format_word);
+
+ if (stream_args.args.has_key("underflow_policy")){
+ this->set_underflow_policy(stream_args.args["underflow_policy"]);
+ }
+ }
+
+private:
+ wb_iface::sptr _iface;
+ const size_t _dsp_base, _ctrl_base;
+ double _tick_rate, _link_rate;
+ double _scaling_adjustment, _dsp_extra_scaling, _host_extra_scaling, _fxpt_scalar_correction;
+ const boost::uint32_t _sid;
+};
+
+tx_dsp_core_200::sptr tx_dsp_core_200::make(wb_iface::sptr iface, const size_t dsp_base, const size_t ctrl_base, const boost::uint32_t sid){
+ return sptr(new tx_dsp_core_200_impl(iface, dsp_base, ctrl_base, sid));
+}
diff --git a/host/lib/usrp/cores/tx_dsp_core_200.hpp b/host/lib/usrp/cores/tx_dsp_core_200.hpp
new file mode 100644
index 000000000..0e1cfb6bc
--- /dev/null
+++ b/host/lib/usrp/cores/tx_dsp_core_200.hpp
@@ -0,0 +1,59 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_TX_DSP_CORE_200_HPP
+#define INCLUDED_LIBUHD_USRP_TX_DSP_CORE_200_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/stream.hpp>
+#include <uhd/types/ranges.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include "wb_iface.hpp"
+
+class tx_dsp_core_200 : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<tx_dsp_core_200> sptr;
+
+ static sptr make(
+ wb_iface::sptr iface,
+ const size_t dsp_base, const size_t ctrl_base,
+ const boost::uint32_t sid
+ );
+
+ virtual void clear(void) = 0;
+
+ virtual void set_tick_rate(const double rate) = 0;
+
+ virtual void set_link_rate(const double rate) = 0;
+
+ virtual double set_host_rate(const double rate) = 0;
+
+ virtual uhd::meta_range_t get_host_rates(void) = 0;
+
+ virtual double get_scaling_adjustment(void) = 0;
+
+ virtual uhd::meta_range_t get_freq_range(void) = 0;
+
+ virtual double set_freq(const double freq) = 0;
+
+ virtual void set_updates(const size_t cycles_per_up, const size_t packets_per_up) = 0;
+
+ virtual void setup(const uhd::stream_args_t &stream_args) = 0;
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_TX_DSP_CORE_200_HPP */
diff --git a/host/lib/usrp/cores/tx_frontend_core_200.cpp b/host/lib/usrp/cores/tx_frontend_core_200.cpp
new file mode 100644
index 000000000..b90281d9f
--- /dev/null
+++ b/host/lib/usrp/cores/tx_frontend_core_200.cpp
@@ -0,0 +1,76 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "tx_frontend_core_200.hpp"
+#include <uhd/types/dict.hpp>
+#include <uhd/exception.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/math/special_functions/round.hpp>
+
+#define REG_TX_FE_DC_OFFSET_I _base + 0 //24 bits
+#define REG_TX_FE_DC_OFFSET_Q _base + 4 //24 bits
+#define REG_TX_FE_MAG_CORRECTION _base + 8 //18 bits
+#define REG_TX_FE_PHASE_CORRECTION _base + 12 //18 bits
+#define REG_TX_FE_MUX _base + 16 //8 bits (std output = 0x10, reversed = 0x01)
+
+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))));
+}
+
+
+class tx_frontend_core_200_impl : public tx_frontend_core_200{
+public:
+ tx_frontend_core_200_impl(wb_iface::sptr iface, const size_t base):
+ _iface(iface), _base(base)
+ {
+ //NOP
+ }
+
+ void set_mux(const std::string &mode){
+ static const uhd::dict<std::string, boost::uint32_t> mode_to_mux = boost::assign::map_list_of
+ ("IQ", (0x1 << 4) | (0x0 << 0)) //DAC0Q=DUC0Q, DAC0I=DUC0I
+ ("QI", (0x0 << 4) | (0x1 << 0)) //DAC0Q=DUC0I, DAC0I=DUC0Q
+ ("I", (0xf << 4) | (0x0 << 0)) //DAC0Q=ZERO, DAC0I=DUC0I
+ ("Q", (0x0 << 4) | (0xf << 0)) //DAC0Q=DUC0I, DAC0I=ZERO
+ ;
+ _iface->poke32(REG_TX_FE_MUX, mode_to_mux[mode]);
+ }
+
+ std::complex<double> set_dc_offset(const std::complex<double> &off){
+ static const double scaler = double(1ul << 23);
+ const boost::int32_t i_dc_off = boost::math::iround(off.real()*scaler);
+ const boost::int32_t q_dc_off = boost::math::iround(off.imag()*scaler);
+
+ _iface->poke32(REG_TX_FE_DC_OFFSET_I, i_dc_off);
+ _iface->poke32(REG_TX_FE_DC_OFFSET_Q, q_dc_off);
+
+ return std::complex<double>(i_dc_off/scaler, q_dc_off/scaler);
+ }
+
+ void set_iq_balance(const std::complex<double> &cor){
+ _iface->poke32(REG_TX_FE_MAG_CORRECTION, fs_to_bits(std::abs(cor) - 1, 18));
+ _iface->poke32(REG_TX_FE_PHASE_CORRECTION, fs_to_bits(std::arg(cor)/6.28318531, 18));
+ }
+
+private:
+ wb_iface::sptr _iface;
+ const size_t _base;
+};
+
+tx_frontend_core_200::sptr tx_frontend_core_200::make(wb_iface::sptr iface, const size_t base){
+ return sptr(new tx_frontend_core_200_impl(iface, base));
+}
diff --git a/host/lib/usrp/cores/tx_frontend_core_200.hpp b/host/lib/usrp/cores/tx_frontend_core_200.hpp
new file mode 100644
index 000000000..8ee0f3e6d
--- /dev/null
+++ b/host/lib/usrp/cores/tx_frontend_core_200.hpp
@@ -0,0 +1,42 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_RX_FRONTEND_CORE_200_HPP
+#define INCLUDED_LIBUHD_USRP_RX_FRONTEND_CORE_200_HPP
+
+#include <uhd/config.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include "wb_iface.hpp"
+#include <complex>
+#include <string>
+
+class tx_frontend_core_200 : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<tx_frontend_core_200> sptr;
+
+ static sptr make(wb_iface::sptr iface, const size_t base);
+
+ virtual void set_mux(const std::string &mode) = 0;
+
+ virtual std::complex<double> set_dc_offset(const std::complex<double> &off) = 0;
+
+ virtual void set_iq_balance(const std::complex<double> &cor) = 0;
+
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_RX_FRONTEND_CORE_200_HPP */
diff --git a/host/lib/usrp/cores/user_settings_core_200.cpp b/host/lib/usrp/cores/user_settings_core_200.cpp
new file mode 100644
index 000000000..d262631b1
--- /dev/null
+++ b/host/lib/usrp/cores/user_settings_core_200.cpp
@@ -0,0 +1,43 @@
+//
+// Copyright 2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "user_settings_core_200.hpp"
+
+#define REG_USER_ADDR _base + 0
+#define REG_USER_DATA _base + 4
+
+class user_settings_core_200_impl : public user_settings_core_200{
+public:
+ user_settings_core_200_impl(wb_iface::sptr iface, const size_t base):
+ _iface(iface), _base(base)
+ {
+ //NOP
+ }
+
+ void set_reg(const user_reg_t &reg){
+ _iface->poke32(REG_USER_ADDR, reg.first);
+ _iface->poke32(REG_USER_DATA, reg.second);
+ }
+
+private:
+ wb_iface::sptr _iface;
+ const size_t _base;
+};
+
+user_settings_core_200::sptr user_settings_core_200::make(wb_iface::sptr iface, const size_t base){
+ return sptr(new user_settings_core_200_impl(iface, base));
+}
diff --git a/host/lib/usrp/cores/user_settings_core_200.hpp b/host/lib/usrp/cores/user_settings_core_200.hpp
new file mode 100644
index 000000000..1f5d13de7
--- /dev/null
+++ b/host/lib/usrp/cores/user_settings_core_200.hpp
@@ -0,0 +1,36 @@
+//
+// Copyright 2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_200_HPP
+#define INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_200_HPP
+
+#include <uhd/config.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include "wb_iface.hpp"
+
+class user_settings_core_200 : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<user_settings_core_200> sptr;
+ typedef std::pair<boost::uint8_t, boost::uint32_t> user_reg_t;
+
+ static sptr make(wb_iface::sptr iface, const size_t base);
+
+ virtual void set_reg(const user_reg_t &reg) = 0;
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_200_HPP */
diff --git a/host/lib/usrp/cores/wb_iface.hpp b/host/lib/usrp/cores/wb_iface.hpp
new file mode 100644
index 000000000..982594b21
--- /dev/null
+++ b/host/lib/usrp/cores/wb_iface.hpp
@@ -0,0 +1,60 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_WB_IFACE_HPP
+#define INCLUDED_LIBUHD_USRP_WB_IFACE_HPP
+
+#include <uhd/config.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/shared_ptr.hpp>
+
+class wb_iface{
+public:
+ typedef boost::shared_ptr<wb_iface> sptr;
+ typedef boost::uint32_t wb_addr_type;
+
+ /*!
+ * Write a register (32 bits)
+ * \param addr the address
+ * \param data the 32bit data
+ */
+ virtual void poke32(wb_addr_type addr, boost::uint32_t data) = 0;
+
+ /*!
+ * Read a register (32 bits)
+ * \param addr the address
+ * \return the 32bit data
+ */
+ virtual boost::uint32_t peek32(wb_addr_type addr) = 0;
+
+ /*!
+ * Write a register (16 bits)
+ * \param addr the address
+ * \param data the 16bit data
+ */
+ virtual void poke16(wb_addr_type addr, boost::uint16_t data) = 0;
+
+ /*!
+ * Read a register (16 bits)
+ * \param addr the address
+ * \return the 16bit data
+ */
+ virtual boost::uint16_t peek16(wb_addr_type addr) = 0;
+
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_WB_IFACE_HPP */
diff --git a/host/lib/usrp/dboard/CMakeLists.txt b/host/lib/usrp/dboard/CMakeLists.txt
new file mode 100644
index 000000000..b000c7f33
--- /dev/null
+++ b/host/lib/usrp/dboard/CMakeLists.txt
@@ -0,0 +1,40 @@
+#
+# Copyright 2010-2011 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# This file included, use CMake directory variables
+########################################################################
+
+LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/db_basic_and_lf.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/db_rfx.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/db_xcvr2450.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/db_sbx_common.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/db_sbx_version3.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/db_sbx_version4.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/db_wbx_common.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/db_wbx_version2.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/db_wbx_version3.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/db_wbx_version4.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/db_wbx_simple.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/db_dbsrx.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/db_unknown.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/db_tvrx.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/db_dbsrx2.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/db_tvrx2.cpp
+)
+
diff --git a/host/lib/usrp/dboard/db_basic_and_lf.cpp b/host/lib/usrp/dboard/db_basic_and_lf.cpp
new file mode 100644
index 000000000..fc42a73d5
--- /dev/null
+++ b/host/lib/usrp/dboard/db_basic_and_lf.cpp
@@ -0,0 +1,197 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/types/dict.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhd/utils/assert_has.hpp>
+#include <uhd/utils/static.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/usrp/dboard_base.hpp>
+#include <uhd/usrp/dboard_manager.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/bind.hpp>
+#include <boost/format.hpp>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace boost::assign;
+
+//! provider function for the always zero freq
+static double always_zero_freq(void){return 0.0;}
+
+/***********************************************************************
+ * Constants
+ **********************************************************************/
+static const uhd::dict<std::string, double> subdev_bandwidth_scalar = map_list_of
+ ("A", 1.0)
+ ("B", 1.0)
+ ("AB", 2.0)
+ ("BA", 2.0)
+;
+
+/***********************************************************************
+ * The basic and lf boards:
+ * They share a common class because only the frequency bounds differ.
+ **********************************************************************/
+class basic_rx : public rx_dboard_base{
+public:
+ basic_rx(ctor_args_t args, double max_freq);
+ ~basic_rx(void);
+
+private:
+ double _max_freq;
+};
+
+class basic_tx : public tx_dboard_base{
+public:
+ basic_tx(ctor_args_t args, double max_freq);
+ ~basic_tx(void);
+
+private:
+ double _max_freq;
+};
+
+static const uhd::dict<std::string, std::string> sd_name_to_conn = map_list_of
+ ("AB", "IQ")
+ ("BA", "QI")
+ ("A", "I")
+ ("B", "Q")
+;
+
+/***********************************************************************
+ * Register the basic and LF dboards
+ **********************************************************************/
+static dboard_base::sptr make_basic_rx(dboard_base::ctor_args_t args){
+ return dboard_base::sptr(new basic_rx(args, 250e6));
+}
+
+static dboard_base::sptr make_basic_tx(dboard_base::ctor_args_t args){
+ return dboard_base::sptr(new basic_tx(args, 250e6));
+}
+
+static dboard_base::sptr make_lf_rx(dboard_base::ctor_args_t args){
+ return dboard_base::sptr(new basic_rx(args, 32e6));
+}
+
+static dboard_base::sptr make_lf_tx(dboard_base::ctor_args_t args){
+ return dboard_base::sptr(new basic_tx(args, 32e6));
+}
+
+UHD_STATIC_BLOCK(reg_basic_and_lf_dboards){
+ dboard_manager::register_dboard(0x0000, &make_basic_tx, "Basic TX", sd_name_to_conn.keys());
+ dboard_manager::register_dboard(0x0001, &make_basic_rx, "Basic RX", sd_name_to_conn.keys());
+ dboard_manager::register_dboard(0x000e, &make_lf_tx, "LF TX", sd_name_to_conn.keys());
+ dboard_manager::register_dboard(0x000f, &make_lf_rx, "LF RX", sd_name_to_conn.keys());
+}
+
+/***********************************************************************
+ * Basic and LF RX dboard
+ **********************************************************************/
+basic_rx::basic_rx(ctor_args_t args, double max_freq) : rx_dboard_base(args){
+ _max_freq = max_freq;
+ //this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true);
+
+ ////////////////////////////////////////////////////////////////////
+ // Register properties
+ ////////////////////////////////////////////////////////////////////
+ this->get_rx_subtree()->create<std::string>("name").set(
+ std::string(str(boost::format("%s - %s")
+ % get_rx_id().to_pp_string()
+ % get_subdev_name()
+ )));
+ this->get_rx_subtree()->create<int>("gains"); //phony property so this dir exists
+ this->get_rx_subtree()->create<double>("freq/value")
+ .publish(&always_zero_freq);
+ this->get_rx_subtree()->create<meta_range_t>("freq/range")
+ .set(freq_range_t(-_max_freq, +_max_freq));
+ this->get_rx_subtree()->create<std::string>("antenna/value")
+ .set("");
+ this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options")
+ .set(list_of(""));
+ this->get_rx_subtree()->create<int>("sensors"); //phony property so this dir exists
+ this->get_rx_subtree()->create<std::string>("connection")
+ .set(sd_name_to_conn[get_subdev_name()]);
+ this->get_rx_subtree()->create<bool>("enabled")
+ .set(true); //always enabled
+ this->get_rx_subtree()->create<bool>("use_lo_offset")
+ .set(false);
+ this->get_rx_subtree()->create<double>("bandwidth/value")
+ .set(subdev_bandwidth_scalar[get_subdev_name()]*_max_freq);
+ this->get_rx_subtree()->create<meta_range_t>("bandwidth/range")
+ .set(freq_range_t(subdev_bandwidth_scalar[get_subdev_name()]*_max_freq, subdev_bandwidth_scalar[get_subdev_name()]*_max_freq));
+
+ //disable RX dboard clock by default
+ this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, false);
+
+ //set GPIOs to output 0x0000 to decrease noise pickup
+ this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, 0x0000);
+ this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0xFFFF);
+ this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, 0x0000);
+}
+
+basic_rx::~basic_rx(void){
+ /* NOP */
+}
+
+/***********************************************************************
+ * Basic and LF TX dboard
+ **********************************************************************/
+basic_tx::basic_tx(ctor_args_t args, double max_freq) : tx_dboard_base(args){
+ _max_freq = max_freq;
+ //this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true);
+
+ ////////////////////////////////////////////////////////////////////
+ // Register properties
+ ////////////////////////////////////////////////////////////////////
+ this->get_tx_subtree()->create<std::string>("name").set(
+ std::string(str(boost::format("%s - %s")
+ % get_tx_id().to_pp_string()
+ % get_subdev_name()
+ )));
+ this->get_tx_subtree()->create<int>("gains"); //phony property so this dir exists
+ this->get_tx_subtree()->create<double>("freq/value")
+ .publish(&always_zero_freq);
+ this->get_tx_subtree()->create<meta_range_t>("freq/range")
+ .set(freq_range_t(-_max_freq, +_max_freq));
+ this->get_tx_subtree()->create<std::string>("antenna/value")
+ .set("");
+ this->get_tx_subtree()->create<std::vector<std::string> >("antenna/options")
+ .set(list_of(""));
+ this->get_tx_subtree()->create<int>("sensors"); //phony property so this dir exists
+ this->get_tx_subtree()->create<std::string>("connection")
+ .set(sd_name_to_conn[get_subdev_name()]);
+ this->get_tx_subtree()->create<bool>("enabled")
+ .set(true); //always enabled
+ this->get_tx_subtree()->create<bool>("use_lo_offset")
+ .set(false);
+ this->get_tx_subtree()->create<double>("bandwidth/value")
+ .set(subdev_bandwidth_scalar[get_subdev_name()]*_max_freq);
+ this->get_tx_subtree()->create<meta_range_t>("bandwidth/range")
+ .set(freq_range_t(subdev_bandwidth_scalar[get_subdev_name()]*_max_freq, subdev_bandwidth_scalar[get_subdev_name()]*_max_freq));
+
+ //disable TX dboard clock by default
+ this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, false);
+
+ //set GPIOs to output 0x0000 to decrease noise pickup
+ this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, 0x0000);
+ this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, 0xFFFF);
+ this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, 0x0000);
+}
+
+basic_tx::~basic_tx(void){
+ /* NOP */
+}
diff --git a/host/lib/usrp/dboard/db_dbsrx.cpp b/host/lib/usrp/dboard/db_dbsrx.cpp
new file mode 100644
index 000000000..ff4bce20d
--- /dev/null
+++ b/host/lib/usrp/dboard/db_dbsrx.cpp
@@ -0,0 +1,536 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+// No RX IO Pins Used
+
+// RX IO Functions
+
+#include "max2118_regs.hpp"
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/static.hpp>
+#include <uhd/utils/assert_has.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhd/types/sensors.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/usrp/dboard_base.hpp>
+#include <uhd/usrp/dboard_manager.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/format.hpp>
+#include <boost/thread.hpp>
+#include <boost/math/special_functions/round.hpp>
+#include <utility>
+#include <cmath>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace boost::assign;
+
+/***********************************************************************
+ * The DBSRX constants
+ **********************************************************************/
+static const freq_range_t dbsrx_freq_range(0.8e9, 2.4e9);
+
+//Multiplied by 2.0 for conversion to complex bandpass from lowpass
+static const freq_range_t dbsrx_bandwidth_range(2.0*4.0e6, 2.0*33.0e6);
+
+static const freq_range_t dbsrx_pfd_freq_range(0.15e6, 2.01e6);
+
+static const std::vector<std::string> dbsrx_antennas = list_of("J3");
+
+static const uhd::dict<std::string, gain_range_t> dbsrx_gain_ranges = map_list_of
+ ("GC1", gain_range_t(0, 56, 0.5))
+ ("GC2", gain_range_t(0, 24, 1))
+;
+
+/***********************************************************************
+ * The DBSRX dboard class
+ **********************************************************************/
+class dbsrx : public rx_dboard_base{
+public:
+ dbsrx(ctor_args_t args);
+ ~dbsrx(void);
+
+private:
+ double _lo_freq;
+ double _bandwidth;
+ uhd::dict<std::string, double> _gains;
+ max2118_write_regs_t _max2118_write_regs;
+ max2118_read_regs_t _max2118_read_regs;
+ boost::uint8_t _max2118_addr(void){
+ return (this->get_iface()->get_special_props().mangle_i2c_addrs)? 0x65 : 0x67;
+ };
+
+ double set_lo_freq(double target_freq);
+ double set_gain(double gain, const std::string &name);
+ double set_bandwidth(double bandwidth);
+
+ void send_reg(boost::uint8_t start_reg, boost::uint8_t stop_reg){
+ start_reg = boost::uint8_t(uhd::clip(int(start_reg), 0x0, 0x5));
+ stop_reg = boost::uint8_t(uhd::clip(int(stop_reg), 0x0, 0x5));
+
+ for(boost::uint8_t start_addr=start_reg; start_addr <= stop_reg; start_addr += sizeof(boost::uint32_t) - 1){
+ int num_bytes = int(stop_reg - start_addr + 1) > int(sizeof(boost::uint32_t)) - 1 ? sizeof(boost::uint32_t) - 1 : stop_reg - start_addr + 1;
+
+ //create buffer for register data (+1 for start address)
+ byte_vector_t regs_vector(num_bytes + 1);
+
+ //first byte is the address of first register
+ regs_vector[0] = start_addr;
+
+ //get the register data
+ for(int i=0; i<num_bytes; i++){
+ regs_vector[1+i] = _max2118_write_regs.get_reg(start_addr+i);
+ UHD_LOGV(often) << boost::format(
+ "DBSRX: send reg 0x%02x, value 0x%04x, start_addr = 0x%04x, num_bytes %d"
+ ) % int(start_addr+i) % int(regs_vector[1+i]) % int(start_addr) % num_bytes << std::endl;
+ }
+
+ //send the data
+ this->get_iface()->write_i2c(
+ _max2118_addr(), regs_vector
+ );
+ }
+ }
+
+ void read_reg(boost::uint8_t start_reg, boost::uint8_t stop_reg){
+ static const boost::uint8_t status_addr = 0x0;
+ start_reg = boost::uint8_t(uhd::clip(int(start_reg), 0x0, 0x1));
+ stop_reg = boost::uint8_t(uhd::clip(int(stop_reg), 0x0, 0x1));
+
+ for(boost::uint8_t start_addr=start_reg; start_addr <= stop_reg; start_addr += sizeof(boost::uint32_t)){
+ int num_bytes = int(stop_reg - start_addr + 1) > int(sizeof(boost::uint32_t)) ? sizeof(boost::uint32_t) : stop_reg - start_addr + 1;
+
+ //create buffer for register data
+ byte_vector_t regs_vector(num_bytes);
+
+ //read from i2c
+ regs_vector = this->get_iface()->read_i2c(
+ _max2118_addr(), num_bytes
+ );
+
+ for(boost::uint8_t i=0; i < num_bytes; i++){
+ if (i + start_addr >= status_addr){
+ _max2118_read_regs.set_reg(i + start_addr, regs_vector[i]);
+ }
+ UHD_LOGV(often) << boost::format(
+ "DBSRX: read reg 0x%02x, value 0x%04x, start_addr = 0x%04x, num_bytes %d"
+ ) % int(start_addr+i) % int(regs_vector[i]) % int(start_addr) % num_bytes << std::endl;
+ }
+ }
+ }
+
+ /*!
+ * Get the lock detect status of the LO.
+ * \return sensor for locked
+ */
+ sensor_value_t get_locked(void){
+ read_reg(0x0, 0x0);
+
+ //mask and return lock detect
+ bool locked = 5 >= _max2118_read_regs.adc and _max2118_read_regs.adc >= 2;
+
+ UHD_LOGV(often) << boost::format(
+ "DBSRX: locked %d"
+ ) % locked << std::endl;
+
+ return sensor_value_t("LO", locked, "locked", "unlocked");
+ }
+};
+
+/***********************************************************************
+ * Register the DBSRX dboard
+ **********************************************************************/
+static dboard_base::sptr make_dbsrx(dboard_base::ctor_args_t args){
+ return dboard_base::sptr(new dbsrx(args));
+}
+
+UHD_STATIC_BLOCK(reg_dbsrx_dboard){
+ //register the factory function for the rx dbid (others version)
+ dboard_manager::register_dboard(0x000D, &make_dbsrx, "DBSRX");
+ //register the factory function for the rx dbid (USRP1 version)
+ dboard_manager::register_dboard(0x0002, &make_dbsrx, "DBSRX");
+}
+
+/***********************************************************************
+ * Structors
+ **********************************************************************/
+dbsrx::dbsrx(ctor_args_t args) : rx_dboard_base(args){
+ //warn user about incorrect DBID on USRP1, requires R193 populated
+ if (this->get_iface()->get_special_props().soft_clock_divider and this->get_rx_id() == 0x000D)
+ UHD_MSG(warning) << boost::format(
+ "DBSRX: incorrect dbid\n"
+ "Expected dbid 0x0002 and R193\n"
+ "found dbid == %d\n"
+ "Please see the daughterboard app notes"
+ ) % this->get_rx_id().to_pp_string();
+
+ //warn user about incorrect DBID on non-USRP1, requires R194 populated
+ if (not this->get_iface()->get_special_props().soft_clock_divider and this->get_rx_id() == 0x0002)
+ UHD_MSG(warning) << boost::format(
+ "DBSRX: incorrect dbid\n"
+ "Expected dbid 0x000D and R194\n"
+ "found dbid == %d\n"
+ "Please see the daughterboard app notes"
+ ) % this->get_rx_id().to_pp_string();
+
+ //send initial register settings
+ this->send_reg(0x0, 0x5);
+
+ //set defaults for LO, gains, and filter bandwidth
+ _bandwidth = 33e6;
+
+ ////////////////////////////////////////////////////////////////////
+ // Register properties
+ ////////////////////////////////////////////////////////////////////
+ this->get_rx_subtree()->create<std::string>("name")
+ .set(get_rx_id().to_pp_string());
+ this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked")
+ .publish(boost::bind(&dbsrx::get_locked, this));
+ BOOST_FOREACH(const std::string &name, dbsrx_gain_ranges.keys()){
+ this->get_rx_subtree()->create<double>("gains/"+name+"/value")
+ .coerce(boost::bind(&dbsrx::set_gain, this, _1, name))
+ .set(dbsrx_gain_ranges[name].start());
+ this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range")
+ .set(dbsrx_gain_ranges[name]);
+ }
+ this->get_rx_subtree()->create<double>("freq/value")
+ .coerce(boost::bind(&dbsrx::set_lo_freq, this, _1));
+ this->get_rx_subtree()->create<meta_range_t>("freq/range")
+ .set(dbsrx_freq_range);
+ this->get_rx_subtree()->create<std::string>("antenna/value")
+ .set(dbsrx_antennas.at(0));
+ this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options")
+ .set(dbsrx_antennas);
+ this->get_rx_subtree()->create<std::string>("connection")
+ .set("IQ");
+ this->get_rx_subtree()->create<bool>("enabled")
+ .set(true); //always enabled
+ this->get_rx_subtree()->create<bool>("use_lo_offset")
+ .set(false);
+ this->get_rx_subtree()->create<double>("bandwidth/value")
+ .coerce(boost::bind(&dbsrx::set_bandwidth, this, _1));
+ this->get_rx_subtree()->create<meta_range_t>("bandwidth/range")
+ .set(dbsrx_bandwidth_range);
+
+ //enable only the clocks we need
+ this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true);
+
+ //set the gpio directions and atr controls (identically)
+ this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, 0x0); // All unused in atr
+ if (this->get_iface()->get_special_props().soft_clock_divider){
+ this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x1); // GPIO0 is clock when on USRP1
+ }
+ else{
+ this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x0); // All Inputs
+ }
+
+ //now its safe to set inital freq and bw
+ this->get_rx_subtree()->access<double>("freq/value")
+ .set(dbsrx_freq_range.start());
+ this->get_rx_subtree()->access<double>("bandwidth/value")
+ .set(2.0*_bandwidth); //_bandwidth in lowpass, convert to complex bandpass
+}
+
+dbsrx::~dbsrx(void){
+}
+
+
+/***********************************************************************
+ * Tuning
+ **********************************************************************/
+double dbsrx::set_lo_freq(double target_freq){
+ target_freq = dbsrx_freq_range.clip(target_freq);
+
+ double actual_freq=0.0, pfd_freq=0.0, ref_clock=0.0;
+ int R=0, N=0, r=0, m=0;
+ bool update_filter_settings = false;
+ //choose refclock
+ std::vector<double> clock_rates = this->get_iface()->get_clock_rates(dboard_iface::UNIT_RX);
+ const double max_clock_rate = uhd::sorted(clock_rates).back();
+ BOOST_FOREACH(ref_clock, uhd::reversed(uhd::sorted(clock_rates))){
+ if (ref_clock > 27.0e6) continue;
+ if (size_t(max_clock_rate/ref_clock)%2 == 1) continue; //reject asymmetric clocks (odd divisors)
+
+ //choose m_divider such that filter tuning constraint is met
+ m = 31;
+ while ((ref_clock/m < 1e6 or ref_clock/m > 2.5e6) and m > 0){ m--; }
+
+ UHD_LOGV(often) << boost::format(
+ "DBSRX: trying ref_clock %f and m_divider %d"
+ ) % (ref_clock) % m << std::endl;
+
+ if (m >= 32) continue;
+
+ //choose R
+ for(r = 0; r <= 6; r += 1) {
+ //compute divider from setting
+ R = 1 << (r+1);
+ UHD_LOGV(often) << boost::format("DBSRX R:%d\n") % R << std::endl;
+
+ //compute PFD compare frequency = ref_clock/R
+ pfd_freq = ref_clock / R;
+
+ //constrain the PFD frequency to specified range
+ if ((pfd_freq < dbsrx_pfd_freq_range.start()) or (pfd_freq > dbsrx_pfd_freq_range.stop())) continue;
+
+ //compute N
+ N = int(std::floor(target_freq/pfd_freq));
+
+ //constrain N to specified range
+ if ((N < 256) or (N > 32768)) continue;
+
+ goto done_loop;
+ }
+ }
+
+ done_loop:
+
+ //Assert because we failed to find a suitable combination of ref_clock, R and N
+ UHD_ASSERT_THROW(ref_clock <= 27.0e6 and ref_clock >= 0.0);
+ UHD_ASSERT_THROW(ref_clock/m >= 1e6 and ref_clock/m <= 2.5e6);
+ UHD_ASSERT_THROW((pfd_freq >= dbsrx_pfd_freq_range.start()) and (pfd_freq <= dbsrx_pfd_freq_range.stop()));
+ UHD_ASSERT_THROW((N >= 256) and (N <= 32768));
+
+ UHD_LOGV(often) << boost::format(
+ "DBSRX: choose ref_clock (current: %f, new: %f) and m_divider %d"
+ ) % (this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX)) % ref_clock % m << std::endl;
+
+ //if ref_clock or m divider changed, we need to update the filter settings
+ if (ref_clock != this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX) or m != _max2118_write_regs.m_divider) update_filter_settings = true;
+
+ //compute resulting output frequency
+ actual_freq = pfd_freq * N;
+
+ //apply ref_clock, R, and N settings
+ this->get_iface()->set_clock_rate(dboard_iface::UNIT_RX, ref_clock);
+ ref_clock = this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX);
+ _max2118_write_regs.m_divider = m;
+ _max2118_write_regs.r_divider = (max2118_write_regs_t::r_divider_t) r;
+ _max2118_write_regs.set_n_divider(N);
+ _max2118_write_regs.ade_vco_ade_read = max2118_write_regs_t::ADE_VCO_ADE_READ_ENABLED;
+
+ //compute prescaler variables
+ int scaler = actual_freq > 1125e6 ? 2 : 4;
+ _max2118_write_regs.div2 = scaler == 4 ? max2118_write_regs_t::DIV2_DIV4 : max2118_write_regs_t::DIV2_DIV2;
+
+ UHD_LOGV(often) << boost::format(
+ "DBSRX: scaler %d, actual_freq %f MHz, register bit: %d"
+ ) % scaler % (actual_freq/1e6) % int(_max2118_write_regs.div2) << std::endl;
+
+ //compute vco frequency and select vco
+ double vco_freq = actual_freq * scaler;
+ if (vco_freq < 2433e6)
+ _max2118_write_regs.osc_band = 0;
+ else if (vco_freq < 2711e6)
+ _max2118_write_regs.osc_band = 1;
+ else if (vco_freq < 3025e6)
+ _max2118_write_regs.osc_band = 2;
+ else if (vco_freq < 3341e6)
+ _max2118_write_regs.osc_band = 3;
+ else if (vco_freq < 3727e6)
+ _max2118_write_regs.osc_band = 4;
+ else if (vco_freq < 4143e6)
+ _max2118_write_regs.osc_band = 5;
+ else if (vco_freq < 4493e6)
+ _max2118_write_regs.osc_band = 6;
+ else
+ _max2118_write_regs.osc_band = 7;
+
+ //send settings over i2c
+ send_reg(0x0, 0x4);
+
+ //check vtune for lock condition
+ read_reg(0x0, 0x0);
+
+ UHD_LOGV(often) << boost::format(
+ "DBSRX: initial guess for vco %d, vtune adc %d"
+ ) % int(_max2118_write_regs.osc_band) % int(_max2118_read_regs.adc) << std::endl;
+
+ //if we are out of lock for chosen vco, change vco
+ while ((_max2118_read_regs.adc == 0) or (_max2118_read_regs.adc == 7)){
+
+ //vtune is too low, try lower frequency vco
+ if (_max2118_read_regs.adc == 0){
+ if (_max2118_write_regs.osc_band == 0){
+ UHD_MSG(warning) << boost::format(
+ "DBSRX: Tuning exceeded vco range, _max2118_write_regs.osc_band == %d\n"
+ ) % int(_max2118_write_regs.osc_band);
+ UHD_ASSERT_THROW(_max2118_read_regs.adc != 0); //just to cause a throw
+ }
+ if (_max2118_write_regs.osc_band <= 0) break;
+ _max2118_write_regs.osc_band -= 1;
+ }
+
+ //vtune is too high, try higher frequency vco
+ if (_max2118_read_regs.adc == 7){
+ if (_max2118_write_regs.osc_band == 7){
+ UHD_MSG(warning) << boost::format(
+ "DBSRX: Tuning exceeded vco range, _max2118_write_regs.osc_band == %d\n"
+ ) % int(_max2118_write_regs.osc_band);
+ UHD_ASSERT_THROW(_max2118_read_regs.adc != 7); //just to cause a throw
+ }
+ if (_max2118_write_regs.osc_band >= 7) break;
+ _max2118_write_regs.osc_band += 1;
+ }
+
+ UHD_LOGV(often) << boost::format(
+ "DBSRX: trying vco %d, vtune adc %d"
+ ) % int(_max2118_write_regs.osc_band) % int(_max2118_read_regs.adc) << std::endl;
+
+ //update vco selection and check vtune
+ send_reg(0x2, 0x2);
+ read_reg(0x0, 0x0);
+
+ //allow for setup time before checking condition again
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+ }
+
+ UHD_LOGV(often) << boost::format(
+ "DBSRX: final vco %d, vtune adc %d"
+ ) % int(_max2118_write_regs.osc_band) % int(_max2118_read_regs.adc) << std::endl;
+
+ //select charge pump bias current
+ if (_max2118_read_regs.adc <= 2) _max2118_write_regs.cp_current = max2118_write_regs_t::CP_CURRENT_I_CP_100UA;
+ else if (_max2118_read_regs.adc >= 5) _max2118_write_regs.cp_current = max2118_write_regs_t::CP_CURRENT_I_CP_400UA;
+ else _max2118_write_regs.cp_current = max2118_write_regs_t::CP_CURRENT_I_CP_200UA;
+
+ //update charge pump bias current setting
+ send_reg(0x2, 0x2);
+
+ //compute actual tuned frequency
+ _lo_freq = this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX) / std::pow(2.0,(1 + _max2118_write_regs.r_divider)) * _max2118_write_regs.get_n_divider();
+
+ //debug output of calculated variables
+ UHD_LOGV(often)
+ << boost::format("DBSRX tune:\n")
+ << boost::format(" VCO=%d, CP=%d, PFD Freq=%fMHz\n") % int(_max2118_write_regs.osc_band) % _max2118_write_regs.cp_current % (pfd_freq/1e6)
+ << boost::format(" R=%d, N=%f, scaler=%d, div2=%d\n") % R % N % scaler % int(_max2118_write_regs.div2)
+ << boost::format(" Ref Freq=%fMHz\n") % (ref_clock/1e6)
+ << boost::format(" Target Freq=%fMHz\n") % (target_freq/1e6)
+ << boost::format(" Actual Freq=%fMHz\n") % (_lo_freq/1e6)
+ << boost::format(" VCO Freq=%fMHz\n") % (vco_freq/1e6)
+ << std::endl;
+
+ if (update_filter_settings) set_bandwidth(_bandwidth);
+ get_locked();
+
+ return _lo_freq;
+}
+
+/***********************************************************************
+ * Gain Handling
+ **********************************************************************/
+/*!
+ * Convert a requested gain for the GC2 vga into the integer register value.
+ * The gain passed into the function will be set to the actual value.
+ * \param gain the requested gain in dB
+ * \return 5 bit the register value
+ */
+static int gain_to_gc2_vga_reg(double &gain){
+ int reg = 0;
+ gain = dbsrx_gain_ranges["GC2"].clip(gain);
+
+ // Half dB steps from 0-5dB, 1dB steps from 5-24dB
+ if (gain < 5) {
+ reg = boost::math::iround(31.0 - gain/0.5);
+ gain = double(boost::math::iround(gain) * 0.5);
+ } else {
+ reg = boost::math::iround(22.0 - (gain - 4.0));
+ gain = double(boost::math::iround(gain));
+ }
+
+ UHD_LOGV(often) << boost::format(
+ "DBSRX GC2 Gain: %f dB, reg: %d"
+ ) % gain % reg << std::endl;
+
+ return reg;
+}
+
+/*!
+ * Convert a requested gain for the GC1 rf vga into the dac_volts value.
+ * The gain passed into the function will be set to the actual value.
+ * \param gain the requested gain in dB
+ * \return dac voltage value
+ */
+static double gain_to_gc1_rfvga_dac(double &gain){
+ //clip the input
+ gain = dbsrx_gain_ranges["GC1"].clip(gain);
+
+ //voltage level constants
+ static const double max_volts = 1.2, min_volts = 2.7;
+ static const double slope = (max_volts-min_volts)/dbsrx_gain_ranges["GC1"].stop();
+
+ //calculate the voltage for the aux dac
+ double dac_volts = gain*slope + min_volts;
+
+ UHD_LOGV(often) << boost::format(
+ "DBSRX GC1 Gain: %f dB, dac_volts: %f V"
+ ) % gain % dac_volts << std::endl;
+
+ //the actual gain setting
+ gain = (dac_volts - min_volts)/slope;
+
+ return dac_volts;
+}
+
+double dbsrx::set_gain(double gain, const std::string &name){
+ assert_has(dbsrx_gain_ranges.keys(), name, "dbsrx gain name");
+ if (name == "GC2"){
+ _max2118_write_regs.gc2 = gain_to_gc2_vga_reg(gain);
+ send_reg(0x5, 0x5);
+ }
+ else if(name == "GC1"){
+ //write the new voltage to the aux dac
+ this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, dboard_iface::AUX_DAC_A, gain_to_gc1_rfvga_dac(gain));
+ }
+ else UHD_THROW_INVALID_CODE_PATH();
+ _gains[name] = gain;
+
+ return gain;
+}
+
+/***********************************************************************
+ * Bandwidth Handling
+ **********************************************************************/
+double dbsrx::set_bandwidth(double bandwidth){
+ //convert complex bandpass to lowpass bandwidth
+ bandwidth = bandwidth/2.0;
+
+ //clip the input
+ bandwidth = dbsrx_bandwidth_range.clip(bandwidth);
+
+ double ref_clock = this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX);
+
+ //NOTE: _max2118_write_regs.m_divider set in set_lo_freq
+
+ //compute f_dac setting
+ _max2118_write_regs.f_dac = uhd::clip<int>(int((((bandwidth*_max2118_write_regs.m_divider)/ref_clock) - 4)/0.145),0,127);
+
+ //determine actual bandwidth
+ _bandwidth = double((ref_clock/(_max2118_write_regs.m_divider))*(4+0.145*_max2118_write_regs.f_dac));
+
+ UHD_LOGV(often) << boost::format(
+ "DBSRX Filter Bandwidth: %f MHz, m: %d, f_dac: %d\n"
+ ) % (_bandwidth/1e6) % int(_max2118_write_regs.m_divider) % int(_max2118_write_regs.f_dac) << std::endl;
+
+ this->send_reg(0x3, 0x4);
+
+ //convert lowpass back to complex bandpass bandwidth
+ return 2.0*_bandwidth;
+}
diff --git a/host/lib/usrp/dboard/db_dbsrx2.cpp b/host/lib/usrp/dboard/db_dbsrx2.cpp
new file mode 100644
index 000000000..01eec133f
--- /dev/null
+++ b/host/lib/usrp/dboard/db_dbsrx2.cpp
@@ -0,0 +1,375 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+// No RX IO Pins Used
+
+#include "max2112_regs.hpp"
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/static.hpp>
+#include <uhd/utils/assert_has.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhd/types/sensors.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/usrp/dboard_base.hpp>
+#include <uhd/usrp/dboard_manager.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/format.hpp>
+#include <boost/thread.hpp>
+#include <boost/math/special_functions/round.hpp>
+#include <utility>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace boost::assign;
+
+/***********************************************************************
+ * The DBSRX2 constants
+ **********************************************************************/
+static const freq_range_t dbsrx2_freq_range(0.8e9, 2.4e9);
+
+//Multiplied by 2.0 for conversion to complex bandpass from lowpass
+static const freq_range_t dbsrx2_bandwidth_range(2.0*4.0e6, 2.0*40.0e6);
+
+static const int dbsrx2_ref_divider = 4; // Hitachi HMC426 divider (U7)
+
+static const std::vector<std::string> dbsrx2_antennas = list_of("J3");
+
+static const uhd::dict<std::string, gain_range_t> dbsrx2_gain_ranges = map_list_of
+ ("GC1", gain_range_t(0, 73, 0.05))
+ ("BBG", gain_range_t(0, 15, 1))
+;
+
+/***********************************************************************
+ * The DBSRX2 dboard class
+ **********************************************************************/
+class dbsrx2 : public rx_dboard_base{
+public:
+ dbsrx2(ctor_args_t args);
+ ~dbsrx2(void);
+
+private:
+ double _lo_freq;
+ double _bandwidth;
+ uhd::dict<std::string, double> _gains;
+ max2112_write_regs_t _max2112_write_regs;
+ max2112_read_regs_t _max2112_read_regs;
+ boost::uint8_t _max2112_addr(){ //0x60 or 0x61 depending on which side
+ return (this->get_iface()->get_special_props().mangle_i2c_addrs)? 0x60 : 0x61;
+ }
+
+ double set_lo_freq(double target_freq);
+ double set_gain(double gain, const std::string &name);
+ double set_bandwidth(double bandwidth);
+
+ void send_reg(boost::uint8_t start_reg, boost::uint8_t stop_reg){
+ start_reg = boost::uint8_t(uhd::clip(int(start_reg), 0x0, 0xB));
+ stop_reg = boost::uint8_t(uhd::clip(int(stop_reg), 0x0, 0xB));
+
+ for(boost::uint8_t start_addr=start_reg; start_addr <= stop_reg; start_addr += sizeof(boost::uint32_t) - 1){
+ int num_bytes = int(stop_reg - start_addr + 1) > int(sizeof(boost::uint32_t)) - 1 ? sizeof(boost::uint32_t) - 1 : stop_reg - start_addr + 1;
+
+ //create buffer for register data (+1 for start address)
+ byte_vector_t regs_vector(num_bytes + 1);
+
+ //first byte is the address of first register
+ regs_vector[0] = start_addr;
+
+ //get the register data
+ for(int i=0; i<num_bytes; i++){
+ regs_vector[1+i] = _max2112_write_regs.get_reg(start_addr+i);
+ UHD_LOGV(often) << boost::format(
+ "DBSRX2: send reg 0x%02x, value 0x%04x, start_addr = 0x%04x, num_bytes %d"
+ ) % int(start_addr+i) % int(regs_vector[1+i]) % int(start_addr) % num_bytes << std::endl;
+ }
+
+ //send the data
+ this->get_iface()->write_i2c(
+ _max2112_addr(), regs_vector
+ );
+ }
+ }
+
+ void read_reg(boost::uint8_t start_reg, boost::uint8_t stop_reg){
+ static const boost::uint8_t status_addr = 0xC;
+ start_reg = boost::uint8_t(uhd::clip(int(start_reg), 0x0, 0xD));
+ stop_reg = boost::uint8_t(uhd::clip(int(stop_reg), 0x0, 0xD));
+
+ for(boost::uint8_t start_addr=start_reg; start_addr <= stop_reg; start_addr += sizeof(boost::uint32_t)){
+ int num_bytes = int(stop_reg - start_addr + 1) > int(sizeof(boost::uint32_t)) ? sizeof(boost::uint32_t) : stop_reg - start_addr + 1;
+
+ //create address to start reading register data
+ byte_vector_t address_vector(1);
+ address_vector[0] = start_addr;
+
+ //send the address
+ this->get_iface()->write_i2c(
+ _max2112_addr(), address_vector
+ );
+
+ //create buffer for register data
+ byte_vector_t regs_vector(num_bytes);
+
+ //read from i2c
+ regs_vector = this->get_iface()->read_i2c(
+ _max2112_addr(), num_bytes
+ );
+
+ for(boost::uint8_t i=0; i < num_bytes; i++){
+ if (i + start_addr >= status_addr){
+ _max2112_read_regs.set_reg(i + start_addr, regs_vector[i]);
+ /*
+ UHD_LOGV(always) << boost::format(
+ "DBSRX2: set reg 0x%02x, value 0x%04x"
+ ) % int(i + start_addr) % int(_max2112_read_regs.get_reg(i + start_addr)) << std::endl;
+ */
+ }
+ UHD_LOGV(often) << boost::format(
+ "DBSRX2: read reg 0x%02x, value 0x%04x, start_addr = 0x%04x, num_bytes %d"
+ ) % int(start_addr+i) % int(regs_vector[i]) % int(start_addr) % num_bytes << std::endl;
+ }
+ }
+ }
+
+ /*!
+ * Get the lock detect status of the LO.
+ * \return sensor for locked
+ */
+ sensor_value_t get_locked(void){
+ read_reg(0xC, 0xD);
+
+ //mask and return lock detect
+ bool locked = (_max2112_read_regs.ld & _max2112_read_regs.vasa & _max2112_read_regs.vase) != 0;
+
+ UHD_LOGV(often) << boost::format(
+ "DBSRX2 locked: %d"
+ ) % locked << std::endl;
+
+ return sensor_value_t("LO", locked, "locked", "unlocked");
+ }
+};
+
+/***********************************************************************
+ * Register the DBSRX2 dboard
+ **********************************************************************/
+// FIXME 0x67 is the default i2c address on USRP2
+// need to handle which side for USRP1 with different address
+static dboard_base::sptr make_dbsrx2(dboard_base::ctor_args_t args){
+ return dboard_base::sptr(new dbsrx2(args));
+}
+
+UHD_STATIC_BLOCK(reg_dbsrx2_dboard){
+ //register the factory function for the rx dbid
+ dboard_manager::register_dboard(0x0012, &make_dbsrx2, "DBSRX2");
+}
+
+/***********************************************************************
+ * Structors
+ **********************************************************************/
+dbsrx2::dbsrx2(ctor_args_t args) : rx_dboard_base(args){
+ //send initial register settings
+ send_reg(0x0, 0xB);
+ //for (boost::uint8_t addr=0; addr<=12; addr++) this->send_reg(addr, addr);
+
+ ////////////////////////////////////////////////////////////////////
+ // Register properties
+ ////////////////////////////////////////////////////////////////////
+ this->get_rx_subtree()->create<std::string>("name")
+ .set(get_rx_id().to_pp_string());
+ this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked")
+ .publish(boost::bind(&dbsrx2::get_locked, this));
+ BOOST_FOREACH(const std::string &name, dbsrx2_gain_ranges.keys()){
+ this->get_rx_subtree()->create<double>("gains/"+name+"/value")
+ .coerce(boost::bind(&dbsrx2::set_gain, this, _1, name))
+ .set(dbsrx2_gain_ranges[name].start());
+ this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range")
+ .set(dbsrx2_gain_ranges[name]);
+ }
+ this->get_rx_subtree()->create<double>("freq/value")
+ .coerce(boost::bind(&dbsrx2::set_lo_freq, this, _1))
+ .set(dbsrx2_freq_range.start());
+ this->get_rx_subtree()->create<meta_range_t>("freq/range")
+ .set(dbsrx2_freq_range);
+ this->get_rx_subtree()->create<std::string>("antenna/value")
+ .set(dbsrx2_antennas.at(0));
+ this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options")
+ .set(dbsrx2_antennas);
+ this->get_rx_subtree()->create<std::string>("connection")
+ .set("QI");
+ this->get_rx_subtree()->create<bool>("enabled")
+ .set(true); //always enabled
+ this->get_rx_subtree()->create<bool>("use_lo_offset")
+ .set(false);
+ this->get_rx_subtree()->create<double>("bandwidth/value")
+ .coerce(boost::bind(&dbsrx2::set_bandwidth, this, _1))
+ .set(2.0*40.0e6); //bandwidth in lowpass, convert to complex bandpass
+ this->get_rx_subtree()->create<meta_range_t>("bandwidth/range")
+ .set(dbsrx2_bandwidth_range);
+
+ //enable only the clocks we need
+ this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true);
+
+ //set the gpio directions and atr controls (identically)
+ this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, 0x0); // All unused in atr
+ this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x0); // All Inputs
+
+ get_locked();
+}
+
+dbsrx2::~dbsrx2(void){
+}
+
+
+/***********************************************************************
+ * Tuning
+ **********************************************************************/
+double dbsrx2::set_lo_freq(double target_freq){
+ //target_freq = dbsrx2_freq_range.clip(target_freq);
+
+ //variables used in the calculation below
+ int scaler = target_freq > 1125e6 ? 2 : 4;
+ double ref_freq = this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX);
+ int R, intdiv, fracdiv, ext_div;
+ double N;
+
+ //compute tuning variables
+ ext_div = dbsrx2_ref_divider; // 12MHz < ref_freq/ext_divider < 30MHz
+
+ R = 1; //Divide by 1 is the only tested value
+
+ N = (target_freq*R*ext_div)/(ref_freq); //actual spec range is (19, 251)
+ intdiv = int(std::floor(N)); // if (intdiv < 19 or intdiv > 251) continue;
+ fracdiv = boost::math::iround((N - intdiv)*double(1 << 20));
+
+ //calculate the actual freq from the values above
+ N = double(intdiv) + double(fracdiv)/double(1 << 20);
+ _lo_freq = (N*ref_freq)/(R*ext_div);
+
+ //load new counters into registers
+ _max2112_write_regs.set_n_divider(intdiv);
+ _max2112_write_regs.set_f_divider(fracdiv);
+ _max2112_write_regs.r_divider = R;
+ _max2112_write_regs.d24 = scaler == 4 ? max2112_write_regs_t::D24_DIV4 : max2112_write_regs_t::D24_DIV2;
+
+ //debug output of calculated variables
+ UHD_LOGV(often)
+ << boost::format("DBSRX2 tune:\n")
+ << boost::format(" R=%d, N=%f, scaler=%d, ext_div=%d\n") % R % N % scaler % ext_div
+ << boost::format(" int=%d, frac=%d, d24=%d\n") % intdiv % fracdiv % int(_max2112_write_regs.d24)
+ << boost::format(" Ref Freq=%fMHz\n") % (ref_freq/1e6)
+ << boost::format(" Target Freq=%fMHz\n") % (target_freq/1e6)
+ << boost::format(" Actual Freq=%fMHz\n") % (_lo_freq/1e6)
+ << std::endl;
+
+ //send the registers
+ send_reg(0x0, 0x7);
+
+ //FIXME: probably unnecessary to call get_locked here
+ //get_locked();
+
+ return _lo_freq;
+}
+
+/***********************************************************************
+ * Gain Handling
+ **********************************************************************/
+/*!
+ * Convert a requested gain for the BBG vga into the integer register value.
+ * The gain passed into the function will be set to the actual value.
+ * \param gain the requested gain in dB
+ * \return 4 bit the register value
+ */
+static int gain_to_bbg_vga_reg(double &gain){
+ int reg = boost::math::iround(dbsrx2_gain_ranges["BBG"].clip(gain));
+
+ gain = double(reg);
+
+ UHD_LOGV(often)
+ << boost::format("DBSRX2 BBG Gain:\n")
+ << boost::format(" %f dB, bbg: %d") % gain % reg
+ << std::endl;
+
+ return reg;
+}
+
+/*!
+ * Convert a requested gain for the GC1 rf vga into the dac_volts value.
+ * The gain passed into the function will be set to the actual value.
+ * \param gain the requested gain in dB
+ * \return dac voltage value
+ */
+static double gain_to_gc1_rfvga_dac(double &gain){
+ //clip the input
+ gain = dbsrx2_gain_ranges["GC1"].clip(gain);
+
+ //voltage level constants
+ static const double max_volts = 0.5, min_volts = 2.7;
+ static const double slope = (max_volts-min_volts)/dbsrx2_gain_ranges["GC1"].stop();
+
+ //calculate the voltage for the aux dac
+ double dac_volts = gain*slope + min_volts;
+
+ UHD_LOGV(often)
+ << boost::format("DBSRX2 GC1 Gain:\n")
+ << boost::format(" %f dB, dac_volts: %f V") % gain % dac_volts
+ << std::endl;
+
+ //the actual gain setting
+ gain = (dac_volts - min_volts)/slope;
+
+ return dac_volts;
+}
+
+double dbsrx2::set_gain(double gain, const std::string &name){
+ assert_has(dbsrx2_gain_ranges.keys(), name, "dbsrx2 gain name");
+ if (name == "BBG"){
+ _max2112_write_regs.bbg = gain_to_bbg_vga_reg(gain);
+ send_reg(0x9, 0x9);
+ }
+ else if(name == "GC1"){
+ //write the new voltage to the aux dac
+ this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, dboard_iface::AUX_DAC_A, gain_to_gc1_rfvga_dac(gain));
+ }
+ else UHD_THROW_INVALID_CODE_PATH();
+ _gains[name] = gain;
+
+ return gain;
+}
+
+/***********************************************************************
+ * Bandwidth Handling
+ **********************************************************************/
+double dbsrx2::set_bandwidth(double bandwidth){
+ //convert complex bandpass to lowpass bandwidth
+ bandwidth = bandwidth/2.0;
+
+ //clip the input
+ bandwidth = dbsrx2_bandwidth_range.clip(bandwidth);
+
+ _max2112_write_regs.lp = int((bandwidth/1e6 - 4)/0.29 + 12);
+ _bandwidth = double(4 + (_max2112_write_regs.lp - 12) * 0.29)*1e6;
+
+ UHD_LOGV(often)
+ << boost::format("DBSRX2 Bandwidth:\n")
+ << boost::format(" %f MHz, lp: %f V") % (_bandwidth/1e6) % int(_max2112_write_regs.lp)
+ << std::endl;
+
+ this->send_reg(0x8, 0x8);
+
+ //convert lowpass back to complex bandpass bandwidth
+ return 2.0*_bandwidth;
+}
diff --git a/host/lib/usrp/dboard/db_rfx.cpp b/host/lib/usrp/dboard/db_rfx.cpp
new file mode 100644
index 000000000..58382f180
--- /dev/null
+++ b/host/lib/usrp/dboard/db_rfx.cpp
@@ -0,0 +1,449 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+// IO Pin functions
+#define POWER_IO (1 << 7) // Low enables power supply
+#define ANTSW_IO (1 << 6) // On TX DB, 0 = TX, 1 = RX, on RX DB 0 = main ant, 1 = RX2
+#define MIXER_IO (1 << 5) // Enable appropriate mixer
+#define LOCKDET_MASK (1 << 2) // Input pin
+
+// Mixer constants
+#define MIXER_ENB MIXER_IO
+#define MIXER_DIS 0
+
+// Antenna constants
+#define ANT_TX 0 //the tx line is transmitting
+#define ANT_RX ANTSW_IO //the tx line is receiving
+#define ANT_TXRX 0 //the rx line is on txrx
+#define ANT_RX2 ANTSW_IO //the rx line in on rx2
+#define ANT_XX 0 //dont care how the antenna is set
+
+#include "adf4360_regs.hpp"
+#include <uhd/types/dict.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhd/types/sensors.hpp>
+#include <uhd/utils/assert_has.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/static.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/usrp/dboard_id.hpp>
+#include <uhd/usrp/dboard_base.hpp>
+#include <uhd/usrp/dboard_manager.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/bind.hpp>
+#include <boost/format.hpp>
+#include <boost/math/special_functions/round.hpp>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace boost::assign;
+
+/***********************************************************************
+ * The RFX Series constants
+ **********************************************************************/
+static const std::vector<std::string> rfx_tx_antennas = list_of("TX/RX")("CAL");
+
+static const std::vector<std::string> rfx_rx_antennas = list_of("TX/RX")("RX2")("CAL");
+
+static const uhd::dict<std::string, gain_range_t> rfx_rx_gain_ranges = map_list_of
+ ("PGA0", gain_range_t(0, 70, 0.022))
+;
+
+static const uhd::dict<std::string, gain_range_t> rfx400_rx_gain_ranges = map_list_of
+ ("PGA0", gain_range_t(0, 45, 0.022))
+;
+
+/***********************************************************************
+ * The RFX series of dboards
+ **********************************************************************/
+class rfx_xcvr : public xcvr_dboard_base{
+public:
+ rfx_xcvr(
+ ctor_args_t args,
+ const freq_range_t &freq_range,
+ bool rx_div2, bool tx_div2
+ );
+ ~rfx_xcvr(void);
+
+private:
+ const freq_range_t _freq_range;
+ const uhd::dict<std::string, gain_range_t> _rx_gain_ranges;
+ const uhd::dict<dboard_iface::unit_t, bool> _div2;
+ std::string _rx_ant;
+ uhd::dict<std::string, double> _rx_gains;
+ boost::uint16_t _power_up;
+
+ void set_rx_ant(const std::string &ant);
+ void set_tx_ant(const std::string &ant);
+ double set_rx_gain(double gain, const std::string &name);
+
+ /*!
+ * Set the LO frequency for the particular dboard unit.
+ * \param unit which unit rx or tx
+ * \param target_freq the desired frequency in Hz
+ * \return the actual frequency in Hz
+ */
+ double set_lo_freq(dboard_iface::unit_t unit, double target_freq);
+
+ /*!
+ * Get the lock detect status of the LO.
+ * \param unit which unit rx or tx
+ * \return sensor for locked
+ */
+ sensor_value_t get_locked(dboard_iface::unit_t unit){
+ const bool locked = (this->get_iface()->read_gpio(unit) & LOCKDET_MASK) != 0;
+ return sensor_value_t("LO", locked, "locked", "unlocked");
+ }
+
+ /*!
+ * Read the RSSI from the aux adc
+ * \return the rssi sensor in dBm
+ */
+ sensor_value_t get_rssi(void){
+ //RSSI from VAGC vs RF Power, Fig 34, pg 13
+ double max_power = -3.0;
+
+ //constants for the rssi calculation
+ static const double min_v = 0.35, max_v = 1.0;
+ static const double rssi_dyn_range = 60;
+ //calculate the rssi from the voltage
+ double voltage = this->get_iface()->read_aux_adc(dboard_iface::UNIT_RX, dboard_iface::AUX_ADC_B);
+ const double rssi = max_power - rssi_dyn_range*(voltage - min_v)/(max_v - min_v);
+ return sensor_value_t("RSSI", rssi, "dBm");
+ }
+};
+
+/***********************************************************************
+ * Register the RFX dboards (min freq, max freq, rx div2, tx div2)
+ **********************************************************************/
+static dboard_base::sptr make_rfx_flex400(dboard_base::ctor_args_t args){
+ return dboard_base::sptr(new rfx_xcvr(args, freq_range_t(400e6, 500e6), true, true));
+}
+
+static dboard_base::sptr make_rfx_flex900(dboard_base::ctor_args_t args){
+ return dboard_base::sptr(new rfx_xcvr(args, freq_range_t(750e6, 1050e6), true, true));
+}
+
+static dboard_base::sptr make_rfx_flex1800(dboard_base::ctor_args_t args){
+ return dboard_base::sptr(new rfx_xcvr(args, freq_range_t(1500e6, 2100e6), false, false));
+}
+
+static dboard_base::sptr make_rfx_flex1200(dboard_base::ctor_args_t args){
+ return dboard_base::sptr(new rfx_xcvr(args, freq_range_t(1150e6, 1450e6), true, true));
+}
+
+static dboard_base::sptr make_rfx_flex2200(dboard_base::ctor_args_t args){
+ return dboard_base::sptr(new rfx_xcvr(args, freq_range_t(2000e6, 2400e6), false, false));
+}
+
+static dboard_base::sptr make_rfx_flex2400(dboard_base::ctor_args_t args){
+ return dboard_base::sptr(new rfx_xcvr(args, freq_range_t(2300e6, 2900e6), false, false));
+}
+
+UHD_STATIC_BLOCK(reg_rfx_dboards){
+ dboard_manager::register_dboard(0x0024, 0x0028, &make_rfx_flex400, "RFX400");
+ dboard_manager::register_dboard(0x0025, 0x0029, &make_rfx_flex900, "RFX900");
+ dboard_manager::register_dboard(0x0034, 0x0035, &make_rfx_flex1800, "RFX1800");
+ dboard_manager::register_dboard(0x0026, 0x002a, &make_rfx_flex1200, "RFX1200");
+ dboard_manager::register_dboard(0x002c, 0x002d, &make_rfx_flex2200, "RFX2200");
+ dboard_manager::register_dboard(0x0027, 0x002b, &make_rfx_flex2400, "RFX2400");
+}
+
+/***********************************************************************
+ * Structors
+ **********************************************************************/
+rfx_xcvr::rfx_xcvr(
+ ctor_args_t args,
+ const freq_range_t &freq_range,
+ bool rx_div2, bool tx_div2
+):
+ xcvr_dboard_base(args),
+ _freq_range(freq_range),
+ _rx_gain_ranges((get_rx_id() == 0x0024)?
+ rfx400_rx_gain_ranges : rfx_rx_gain_ranges
+ ),
+ _div2(map_list_of
+ (dboard_iface::UNIT_RX, rx_div2)
+ (dboard_iface::UNIT_TX, tx_div2)
+ ),
+ _power_up((get_rx_id() == 0x0024 && get_tx_id() == 0x0028) ? POWER_IO : 0)
+{
+ ////////////////////////////////////////////////////////////////////
+ // Register RX properties
+ ////////////////////////////////////////////////////////////////////
+ this->get_rx_subtree()->create<std::string>("name").set("RFX RX");
+ this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked")
+ .publish(boost::bind(&rfx_xcvr::get_locked, this, dboard_iface::UNIT_RX));
+ this->get_rx_subtree()->create<sensor_value_t>("sensors/rssi")
+ .publish(boost::bind(&rfx_xcvr::get_rssi, this));
+ BOOST_FOREACH(const std::string &name, _rx_gain_ranges.keys()){
+ this->get_rx_subtree()->create<double>("gains/"+name+"/value")
+ .coerce(boost::bind(&rfx_xcvr::set_rx_gain, this, _1, name))
+ .set(_rx_gain_ranges[name].start());
+ this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range")
+ .set(_rx_gain_ranges[name]);
+ }
+ this->get_rx_subtree()->create<double>("freq/value")
+ .coerce(boost::bind(&rfx_xcvr::set_lo_freq, this, dboard_iface::UNIT_RX, _1))
+ .set((_freq_range.start() + _freq_range.stop())/2.0);
+ this->get_rx_subtree()->create<meta_range_t>("freq/range").set(_freq_range);
+ this->get_rx_subtree()->create<std::string>("antenna/value")
+ .subscribe(boost::bind(&rfx_xcvr::set_rx_ant, this, _1))
+ .set("RX2");
+ this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options")
+ .set(rfx_rx_antennas);
+ this->get_rx_subtree()->create<std::string>("connection").set("QI");
+ this->get_rx_subtree()->create<bool>("enabled").set(true); //always enabled
+ this->get_rx_subtree()->create<bool>("use_lo_offset").set(false);
+ this->get_rx_subtree()->create<double>("bandwidth/value").set(2*20.0e6); //20MHz low-pass, we want complex double-sided
+ this->get_rx_subtree()->create<meta_range_t>("bandwidth/range")
+ .set(freq_range_t(2*20.0e6, 2*20.0e6));
+
+ ////////////////////////////////////////////////////////////////////
+ // Register TX properties
+ ////////////////////////////////////////////////////////////////////
+ this->get_tx_subtree()->create<std::string>("name").set("RFX TX");
+ this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked")
+ .publish(boost::bind(&rfx_xcvr::get_locked, this, dboard_iface::UNIT_TX));
+ this->get_tx_subtree()->create<int>("gains"); //phony property so this dir exists
+ this->get_tx_subtree()->create<double>("freq/value")
+ .coerce(boost::bind(&rfx_xcvr::set_lo_freq, this, dboard_iface::UNIT_TX, _1))
+ .set((_freq_range.start() + _freq_range.stop())/2.0);
+ this->get_tx_subtree()->create<meta_range_t>("freq/range").set(_freq_range);
+ this->get_tx_subtree()->create<std::string>("antenna/value")
+ .subscribe(boost::bind(&rfx_xcvr::set_tx_ant, this, _1)).set(rfx_tx_antennas.at(0));
+ this->get_tx_subtree()->create<std::vector<std::string> >("antenna/options")
+ .set(rfx_tx_antennas);
+ this->get_tx_subtree()->create<std::string>("connection").set("IQ");
+ this->get_tx_subtree()->create<bool>("enabled").set(true); //always enabled
+ this->get_tx_subtree()->create<bool>("use_lo_offset").set(true);
+ this->get_tx_subtree()->create<double>("bandwidth/value").set(2*20.0e6); //20MHz low-pass, we want complex double-sided
+ this->get_tx_subtree()->create<meta_range_t>("bandwidth/range")
+ .set(freq_range_t(2*20.0e6, 2*20.0e6));
+
+ //enable the clocks that we need
+ this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true);
+ this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true);
+
+ //set the gpio directions and atr controls (identically)
+ boost::uint16_t output_enables = POWER_IO | ANTSW_IO | MIXER_IO;
+ this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, output_enables);
+ this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, output_enables);
+ this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, output_enables);
+ this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, output_enables);
+
+ //setup the tx atr (this does not change with antenna)
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE, _power_up | ANT_XX | MIXER_DIS);
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY, _power_up | ANT_RX | MIXER_DIS);
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY, _power_up | ANT_TX | MIXER_ENB);
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, _power_up | ANT_TX | MIXER_ENB);
+
+ //setup the rx atr (this does not change with antenna)
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, _power_up | ANT_XX | MIXER_DIS);
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, _power_up | ANT_XX | MIXER_DIS);
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, _power_up | ANT_RX2| MIXER_ENB);
+}
+
+rfx_xcvr::~rfx_xcvr(void){
+ /* NOP */
+}
+
+/***********************************************************************
+ * Antenna Handling
+ **********************************************************************/
+void rfx_xcvr::set_rx_ant(const std::string &ant){
+ //validate input
+ assert_has(rfx_rx_antennas, ant, "rfx rx antenna name");
+
+ //set the rx atr regs that change with antenna setting
+ if (ant == "CAL") {
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, _power_up | ANT_TXRX | MIXER_DIS);
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, _power_up | ANT_TXRX | MIXER_ENB);
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, _power_up | MIXER_ENB | ANT_TXRX );
+ }
+ else {
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, _power_up | ANT_XX | MIXER_DIS);
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, _power_up | ANT_RX2| MIXER_ENB);
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, _power_up | MIXER_ENB |
+ ((ant == "TX/RX")? ANT_TXRX : ANT_RX2));
+ }
+
+ //shadow the setting
+ _rx_ant = ant;
+}
+
+void rfx_xcvr::set_tx_ant(const std::string &ant){
+ assert_has(rfx_tx_antennas, ant, "rfx tx antenna name");
+
+ //set the tx atr regs that change with antenna setting
+ if (ant == "CAL") {
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY, _power_up | ANT_RX | MIXER_ENB);
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, _power_up | ANT_RX | MIXER_ENB);
+ }
+ else {
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY, _power_up | ANT_TX | MIXER_ENB);
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, _power_up | ANT_TX | MIXER_ENB);
+ }
+}
+
+/***********************************************************************
+ * Gain Handling
+ **********************************************************************/
+static double rx_pga0_gain_to_dac_volts(double &gain, double range){
+ //voltage level constants (negative slope)
+ static const double max_volts = .2, min_volts = 1.2;
+ static const double slope = (max_volts-min_volts)/(range);
+
+ //calculate the voltage for the aux dac
+ double dac_volts = uhd::clip<double>(gain*slope + min_volts, max_volts, min_volts);
+
+ //the actual gain setting
+ gain = (dac_volts - min_volts)/slope;
+
+ return dac_volts;
+}
+
+double rfx_xcvr::set_rx_gain(double gain, const std::string &name){
+ assert_has(_rx_gain_ranges.keys(), name, "rfx rx gain name");
+ if(name == "PGA0"){
+ double dac_volts = rx_pga0_gain_to_dac_volts(gain,
+ (_rx_gain_ranges["PGA0"].stop() - _rx_gain_ranges["PGA0"].start()));
+
+ //write the new voltage to the aux dac
+ this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, dboard_iface::AUX_DAC_A, dac_volts);
+
+ return gain;
+ }
+ else UHD_THROW_INVALID_CODE_PATH();
+}
+
+/***********************************************************************
+ * Tuning
+ **********************************************************************/
+double rfx_xcvr::set_lo_freq(
+ dboard_iface::unit_t unit,
+ double target_freq
+){
+ UHD_LOGV(often) << boost::format(
+ "RFX tune: target frequency %f Mhz"
+ ) % (target_freq/1e6) << std::endl;
+
+ //clip the input
+ target_freq = _freq_range.clip(target_freq);
+ if (_div2[unit]) target_freq *= 2;
+
+ //rfx400 rx is a special case with div2 in mixer, so adf4360 must output fundamental
+ bool is_rx_rfx400 = ((get_rx_id() == 0x0024) && unit != dboard_iface::UNIT_TX);
+
+ //map prescalers to the register enums
+ static const uhd::dict<int, adf4360_regs_t::prescaler_value_t> prescaler_to_enum = map_list_of
+ (8, adf4360_regs_t::PRESCALER_VALUE_8_9)
+ (16, adf4360_regs_t::PRESCALER_VALUE_16_17)
+ (32, adf4360_regs_t::PRESCALER_VALUE_32_33)
+ ;
+
+ //map band select clock dividers to enums
+ static const uhd::dict<int, adf4360_regs_t::band_select_clock_div_t> bandsel_to_enum = map_list_of
+ (1, adf4360_regs_t::BAND_SELECT_CLOCK_DIV_1)
+ (2, adf4360_regs_t::BAND_SELECT_CLOCK_DIV_2)
+ (4, adf4360_regs_t::BAND_SELECT_CLOCK_DIV_4)
+ (8, adf4360_regs_t::BAND_SELECT_CLOCK_DIV_8)
+ ;
+
+ double actual_freq=0, ref_freq = this->get_iface()->get_clock_rate(unit);
+ int R=0, BS=0, P=0, B=0, A=0;
+
+ /*
+ * The goal here to to loop though possible R dividers,
+ * band select clock dividers, and prescaler values.
+ * Calculate the A and B counters for each set of values.
+ * The loop exists when it meets all of the constraints.
+ * The resulting loop values are loaded into the registers.
+ *
+ * fvco = [P*B + A] * fref/R
+ * fvco*R/fref = P*B + A = N
+ */
+ for(R = 2; R <= 32; R+=2){
+ BOOST_FOREACH(BS, bandsel_to_enum.keys()){
+ if (ref_freq/R/BS > 1e6) continue; //constraint on band select clock
+ BOOST_FOREACH(P, prescaler_to_enum.keys()){
+ //calculate B and A from N
+ double N = target_freq*R/ref_freq;
+ B = int(std::floor(N/P));
+ A = boost::math::iround(N - P*B);
+ if (B < A or B > 8191 or B < 3 or A > 31) continue; //constraints on A, B
+ //calculate the actual frequency
+ actual_freq = double(P*B + A)*ref_freq/R;
+ if (actual_freq/P > 300e6) continue; //constraint on prescaler output
+ //constraints met: exit loop
+ goto done_loop;
+ }
+ }
+ } done_loop:
+
+ UHD_LOGV(often) << boost::format(
+ "RFX tune: R=%d, BS=%d, P=%d, B=%d, A=%d, DIV2=%d"
+ ) % R % BS % P % B % A % int(_div2[unit] && (!is_rx_rfx400)) << std::endl;
+
+ //load the register values
+ adf4360_regs_t regs;
+ regs.core_power_level = adf4360_regs_t::CORE_POWER_LEVEL_10MA;
+ regs.counter_operation = adf4360_regs_t::COUNTER_OPERATION_NORMAL;
+ regs.muxout_control = adf4360_regs_t::MUXOUT_CONTROL_DLD;
+ regs.phase_detector_polarity = adf4360_regs_t::PHASE_DETECTOR_POLARITY_POS;
+ regs.charge_pump_output = adf4360_regs_t::CHARGE_PUMP_OUTPUT_NORMAL;
+ regs.cp_gain_0 = adf4360_regs_t::CP_GAIN_0_SET1;
+ regs.mute_till_ld = adf4360_regs_t::MUTE_TILL_LD_ENB;
+ regs.output_power_level = adf4360_regs_t::OUTPUT_POWER_LEVEL_3_5MA;
+ regs.current_setting1 = adf4360_regs_t::CURRENT_SETTING1_0_31MA;
+ regs.current_setting2 = adf4360_regs_t::CURRENT_SETTING2_0_31MA;
+ regs.power_down = adf4360_regs_t::POWER_DOWN_NORMAL_OP;
+ regs.prescaler_value = prescaler_to_enum[P];
+ regs.a_counter = A;
+ regs.b_counter = B;
+ regs.cp_gain_1 = adf4360_regs_t::CP_GAIN_1_SET1;
+ regs.divide_by_2_output = (_div2[unit] && (!is_rx_rfx400)) ? // Special case RFX400 RX Mixer divides by two
+ adf4360_regs_t::DIVIDE_BY_2_OUTPUT_DIV2 :
+ adf4360_regs_t::DIVIDE_BY_2_OUTPUT_FUND ;
+ regs.divide_by_2_prescaler = adf4360_regs_t::DIVIDE_BY_2_PRESCALER_FUND;
+ regs.r_counter = R;
+ regs.ablpw = adf4360_regs_t::ABLPW_3_0NS;
+ regs.lock_detect_precision = adf4360_regs_t::LOCK_DETECT_PRECISION_5CYCLES;
+ regs.test_mode_bit = 0;
+ regs.band_select_clock_div = bandsel_to_enum[BS];
+
+ //write the registers
+ std::vector<adf4360_regs_t::addr_t> addrs = list_of //correct power-up sequence to write registers (R, C, N)
+ (adf4360_regs_t::ADDR_RCOUNTER)
+ (adf4360_regs_t::ADDR_CONTROL)
+ (adf4360_regs_t::ADDR_NCOUNTER)
+ ;
+ BOOST_FOREACH(adf4360_regs_t::addr_t addr, addrs){
+ this->get_iface()->write_spi(
+ unit, spi_config_t::EDGE_RISE,
+ regs.get_reg(addr), 24
+ );
+ }
+
+ //return the actual frequency
+ if (_div2[unit]) actual_freq /= 2;
+ UHD_LOGV(often) << boost::format(
+ "RFX tune: actual frequency %f Mhz"
+ ) % (actual_freq/1e6) << std::endl;
+ return actual_freq;
+}
diff --git a/host/lib/usrp/dboard/db_sbx_common.cpp b/host/lib/usrp/dboard/db_sbx_common.cpp
new file mode 100644
index 000000000..d1cd5b373
--- /dev/null
+++ b/host/lib/usrp/dboard/db_sbx_common.cpp
@@ -0,0 +1,338 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "db_sbx_common.hpp"
+#include "adf4350_regs.hpp"
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace boost::assign;
+
+
+/***********************************************************************
+ * Register the SBX dboard (min freq, max freq, rx div2, tx div2)
+ **********************************************************************/
+static dboard_base::sptr make_sbx(dboard_base::ctor_args_t args){
+ return dboard_base::sptr(new sbx_xcvr(args));
+}
+
+UHD_STATIC_BLOCK(reg_sbx_dboards){
+ dboard_manager::register_dboard(0x0054, 0x0055, &make_sbx, "SBX");
+ dboard_manager::register_dboard(0x0065, 0x0064, &make_sbx, "SBX v4");
+}
+
+
+/***********************************************************************
+ * Gain Handling
+ **********************************************************************/
+static int rx_pga0_gain_to_iobits(double &gain){
+ //clip the input
+ gain = sbx_rx_gain_ranges["PGA0"].clip(gain);
+
+ //convert to attenuation and update iobits for atr
+ double attn = sbx_rx_gain_ranges["PGA0"].stop() - gain;
+
+ //calculate the RX attenuation
+ int attn_code = int(floor(attn*2));
+ int iobits = ((~attn_code) << RX_ATTN_SHIFT) & RX_ATTN_MASK;
+
+ UHD_LOGV(often) << boost::format(
+ "SBX TX Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x"
+ ) % attn % attn_code % (iobits & RX_ATTN_MASK) % RX_ATTN_MASK << std::endl;
+
+ //the actual gain setting
+ gain = sbx_rx_gain_ranges["PGA0"].stop() - double(attn_code)/2;
+
+ return iobits;
+}
+
+static int tx_pga0_gain_to_iobits(double &gain){
+ //clip the input
+ gain = sbx_tx_gain_ranges["PGA0"].clip(gain);
+
+ //convert to attenuation and update iobits for atr
+ double attn = sbx_tx_gain_ranges["PGA0"].stop() - gain;
+
+ //calculate the TX attenuation
+ int attn_code = int(floor(attn*2));
+ int iobits = ((~attn_code) << TX_ATTN_SHIFT) & TX_ATTN_MASK;
+
+ UHD_LOGV(often) << boost::format(
+ "SBX TX Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x"
+ ) % attn % attn_code % (iobits & TX_ATTN_MASK) % TX_ATTN_MASK << std::endl;
+
+ //the actual gain setting
+ gain = sbx_tx_gain_ranges["PGA0"].stop() - double(attn_code)/2;
+
+ return iobits;
+}
+
+double sbx_xcvr::set_tx_gain(double gain, const std::string &name){
+ assert_has(sbx_tx_gain_ranges.keys(), name, "sbx tx gain name");
+ if(name == "PGA0"){
+ tx_pga0_gain_to_iobits(gain);
+ _tx_gains[name] = gain;
+
+ //write the new gain to atr regs
+ update_atr();
+ }
+ else UHD_THROW_INVALID_CODE_PATH();
+ return _tx_gains[name];
+}
+
+double sbx_xcvr::set_rx_gain(double gain, const std::string &name){
+ assert_has(sbx_rx_gain_ranges.keys(), name, "sbx rx gain name");
+ if(name == "PGA0"){
+ rx_pga0_gain_to_iobits(gain);
+ _rx_gains[name] = gain;
+
+ //write the new gain to atr regs
+ update_atr();
+ }
+ else UHD_THROW_INVALID_CODE_PATH();
+ return _rx_gains[name];
+}
+
+
+/***********************************************************************
+ * Structors
+ **********************************************************************/
+sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){
+ switch(get_rx_id().to_uint16()) {
+ case 0x054:
+ db_actual = sbx_versionx_sptr(new sbx_version3(this));
+ break;
+ case 0x065:
+ db_actual = sbx_versionx_sptr(new sbx_version4(this));
+ break;
+ default:
+ /* We didn't recognize the version of the board... */
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+
+ ////////////////////////////////////////////////////////////////////
+ // Register RX properties
+ ////////////////////////////////////////////////////////////////////
+ this->get_rx_subtree()->create<std::string>("name").set("SBX RX");
+ this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked")
+ .publish(boost::bind(&sbx_xcvr::get_locked, this, dboard_iface::UNIT_RX));
+ BOOST_FOREACH(const std::string &name, sbx_rx_gain_ranges.keys()){
+ this->get_rx_subtree()->create<double>("gains/"+name+"/value")
+ .coerce(boost::bind(&sbx_xcvr::set_rx_gain, this, _1, name))
+ .set(sbx_rx_gain_ranges[name].start());
+ this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range")
+ .set(sbx_rx_gain_ranges[name]);
+ }
+ this->get_rx_subtree()->create<double>("freq/value")
+ .coerce(boost::bind(&sbx_xcvr::set_lo_freq, this, dboard_iface::UNIT_RX, _1))
+ .set((sbx_freq_range.start() + sbx_freq_range.stop())/2.0);
+ this->get_rx_subtree()->create<meta_range_t>("freq/range").set(sbx_freq_range);
+ this->get_rx_subtree()->create<std::string>("antenna/value")
+ .subscribe(boost::bind(&sbx_xcvr::set_rx_ant, this, _1))
+ .set("RX2");
+ this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options")
+ .set(sbx_rx_antennas);
+ this->get_rx_subtree()->create<std::string>("connection").set("IQ");
+ this->get_rx_subtree()->create<bool>("enabled").set(true); //always enabled
+ this->get_rx_subtree()->create<bool>("use_lo_offset").set(false);
+ this->get_rx_subtree()->create<double>("bandwidth/value").set(2*20.0e6); //20MHz low-pass, we want complex double-sided
+ this->get_rx_subtree()->create<meta_range_t>("bandwidth/range")
+ .set(freq_range_t(2*20.0e6, 2*20.0e6));
+
+ ////////////////////////////////////////////////////////////////////
+ // Register TX properties
+ ////////////////////////////////////////////////////////////////////
+ this->get_tx_subtree()->create<std::string>("name").set("SBX TX");
+ this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked")
+ .publish(boost::bind(&sbx_xcvr::get_locked, this, dboard_iface::UNIT_TX));
+ BOOST_FOREACH(const std::string &name, sbx_tx_gain_ranges.keys()){
+ this->get_tx_subtree()->create<double>("gains/"+name+"/value")
+ .coerce(boost::bind(&sbx_xcvr::set_tx_gain, this, _1, name))
+ .set(sbx_tx_gain_ranges[name].start());
+ this->get_tx_subtree()->create<meta_range_t>("gains/"+name+"/range")
+ .set(sbx_tx_gain_ranges[name]);
+ }
+ this->get_tx_subtree()->create<double>("freq/value")
+ .coerce(boost::bind(&sbx_xcvr::set_lo_freq, this, dboard_iface::UNIT_TX, _1))
+ .set((sbx_freq_range.start() + sbx_freq_range.stop())/2.0);
+ this->get_tx_subtree()->create<meta_range_t>("freq/range").set(sbx_freq_range);
+ this->get_tx_subtree()->create<std::string>("antenna/value")
+ .subscribe(boost::bind(&sbx_xcvr::set_tx_ant, this, _1))
+ .set(sbx_tx_antennas.at(0));
+ this->get_tx_subtree()->create<std::vector<std::string> >("antenna/options")
+ .set(sbx_tx_antennas);
+ this->get_tx_subtree()->create<std::string>("connection").set("QI");
+ this->get_tx_subtree()->create<bool>("enabled").set(true); //always enabled
+ this->get_tx_subtree()->create<bool>("use_lo_offset").set(false);
+ this->get_tx_subtree()->create<double>("bandwidth/value").set(2*20.0e6); //20MHz low-pass, we want complex double-sided
+ this->get_tx_subtree()->create<meta_range_t>("bandwidth/range")
+ .set(freq_range_t(2*20.0e6, 2*20.0e6));
+
+ //enable the clocks that we need
+ this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true);
+ this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true);
+
+ //set the gpio directions and atr controls (identically)
+ this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO));
+ this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO));
+ this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO));
+ this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO));
+
+ //flash LEDs
+ flash_leds();
+
+ UHD_LOGV(often) << boost::format(
+ "SBX GPIO Direction: RX: 0x%08x, TX: 0x%08x"
+ ) % RXIO_MASK % TXIO_MASK << std::endl;
+}
+
+sbx_xcvr::~sbx_xcvr(void){
+ /* NOP */
+}
+
+/***********************************************************************
+ * Antenna Handling
+ **********************************************************************/
+void sbx_xcvr::update_atr(void){
+ //calculate atr pins
+ int rx_pga0_iobits = rx_pga0_gain_to_iobits(_rx_gains["PGA0"]);
+ int tx_pga0_iobits = tx_pga0_gain_to_iobits(_tx_gains["PGA0"]);
+ int rx_lo_lpf_en = (_rx_lo_freq == sbx_enable_rx_lo_filter.clip(_rx_lo_freq)) ? LO_LPF_EN : 0;
+ int tx_lo_lpf_en = (_tx_lo_freq == sbx_enable_tx_lo_filter.clip(_tx_lo_freq)) ? LO_LPF_EN : 0;
+ int rx_ld_led = get_locked(dboard_iface::UNIT_RX).to_bool() ? 0 : RX_LED_LD;
+ int tx_ld_led = get_locked(dboard_iface::UNIT_TX).to_bool() ? 0 : TX_LED_LD;
+ int rx_ant_led = _rx_ant == "TX/RX" ? RX_LED_RX1RX2 : 0;
+ int tx_ant_led = _tx_ant == "TX/RX" ? 0 : TX_LED_TXRX;
+
+ //setup the tx atr (this does not change with antenna)
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \
+ dboard_iface::ATR_REG_IDLE, 0 | tx_lo_lpf_en \
+ | tx_ld_led | tx_ant_led | TX_POWER_UP | ANT_XX | TX_MIXER_DIS);
+
+ //setup the rx atr (this does not change with antenna)
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \
+ dboard_iface::ATR_REG_IDLE, rx_pga0_iobits | rx_lo_lpf_en \
+ | rx_ld_led | rx_ant_led | RX_POWER_UP | ANT_XX | RX_MIXER_DIS);
+
+ //set the RX atr regs that change with antenna setting
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \
+ dboard_iface::ATR_REG_RX_ONLY, rx_pga0_iobits | rx_lo_lpf_en \
+ | rx_ld_led | rx_ant_led | RX_POWER_UP | RX_MIXER_ENB \
+ | ((_rx_ant != "RX2")? ANT_TXRX : ANT_RX2));
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \
+ dboard_iface::ATR_REG_TX_ONLY, rx_pga0_iobits | rx_lo_lpf_en \
+ | rx_ld_led | rx_ant_led | RX_POWER_UP | RX_MIXER_DIS \
+ | ((_rx_ant == "CAL")? ANT_TXRX : ANT_RX2));
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \
+ dboard_iface::ATR_REG_FULL_DUPLEX, rx_pga0_iobits | rx_lo_lpf_en \
+ | rx_ld_led | rx_ant_led | RX_POWER_UP | RX_MIXER_ENB \
+ | ((_rx_ant == "CAL")? ANT_TXRX : ANT_RX2));
+
+ //set the TX atr regs that change with antenna setting
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \
+ dboard_iface::ATR_REG_RX_ONLY, 0 | tx_lo_lpf_en \
+ | tx_ld_led | tx_ant_led | TX_POWER_UP | TX_MIXER_DIS \
+ | ((_rx_ant != "RX2")? ANT_RX : ANT_TX));
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \
+ dboard_iface::ATR_REG_TX_ONLY, tx_pga0_iobits | tx_lo_lpf_en \
+ | tx_ld_led | tx_ant_led | TX_POWER_UP | TX_MIXER_ENB \
+ | ((_tx_ant == "CAL")? ANT_RX : ANT_TX));
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \
+ dboard_iface::ATR_REG_FULL_DUPLEX, tx_pga0_iobits | tx_lo_lpf_en \
+ | tx_ld_led | tx_ant_led | TX_POWER_UP | TX_MIXER_ENB \
+ | ((_tx_ant == "CAL")? ANT_RX : ANT_TX));
+}
+
+void sbx_xcvr::set_rx_ant(const std::string &ant){
+ //validate input
+ assert_has(sbx_rx_antennas, ant, "sbx rx antenna name");
+
+ //shadow the setting
+ _rx_ant = ant;
+
+ //write the new antenna setting to atr regs
+ update_atr();
+}
+
+void sbx_xcvr::set_tx_ant(const std::string &ant){
+ assert_has(sbx_tx_antennas, ant, "sbx tx antenna name");
+
+ //shadow the setting
+ _tx_ant = ant;
+
+ //write the new antenna setting to atr regs
+ update_atr();
+}
+
+/***********************************************************************
+ * Tuning
+ **********************************************************************/
+double sbx_xcvr::set_lo_freq(dboard_iface::unit_t unit, double target_freq) {
+ const double actual = db_actual->set_lo_freq(unit, target_freq);
+ if (unit == dboard_iface::UNIT_RX) _rx_lo_freq = actual;
+ if (unit == dboard_iface::UNIT_TX) _tx_lo_freq = actual;
+ update_atr();
+ return actual;
+}
+
+
+sensor_value_t sbx_xcvr::get_locked(dboard_iface::unit_t unit) {
+ const bool locked = (this->get_iface()->read_gpio(unit) & LOCKDET_MASK) != 0;
+ return sensor_value_t("LO", locked, "locked", "unlocked");
+}
+
+
+void sbx_xcvr::flash_leds(void) {
+ //Remove LED gpios from ATR control temporarily and set to outputs
+ this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, TXIO_MASK);
+ this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, RXIO_MASK);
+ this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|RX_LED_IO));
+ this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO));
+
+ this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_LD, TX_LED_IO);
+ boost::this_thread::sleep(boost::posix_time::milliseconds(100));
+
+ this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, \
+ TX_LED_TXRX|TX_LED_LD, TX_LED_IO);
+ boost::this_thread::sleep(boost::posix_time::milliseconds(100));
+
+ this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_LD, RX_LED_IO);
+ boost::this_thread::sleep(boost::posix_time::milliseconds(100));
+
+ this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, \
+ RX_LED_RX1RX2|RX_LED_LD, RX_LED_IO);
+ boost::this_thread::sleep(boost::posix_time::milliseconds(100));
+
+ this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_LD, RX_LED_IO);
+ boost::this_thread::sleep(boost::posix_time::milliseconds(100));
+
+ this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, 0, RX_LED_IO);
+ boost::this_thread::sleep(boost::posix_time::milliseconds(100));
+
+ this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_LD, TX_LED_IO);
+ boost::this_thread::sleep(boost::posix_time::milliseconds(100));
+
+ this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, 0, TX_LED_IO);
+ boost::this_thread::sleep(boost::posix_time::milliseconds(100));
+
+ //Put LED gpios back in ATR control and update atr
+ this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO));
+ this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO));
+ this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO));
+ this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO));
+}
+
diff --git a/host/lib/usrp/dboard/db_sbx_common.hpp b/host/lib/usrp/dboard/db_sbx_common.hpp
new file mode 100644
index 000000000..6380ddb47
--- /dev/null
+++ b/host/lib/usrp/dboard/db_sbx_common.hpp
@@ -0,0 +1,225 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+
+
+// Common IO Pins
+#define LO_LPF_EN (1 << 15)
+#define SYNTH_CE (1 << 3)
+#define SYNTH_PDBRF (1 << 2)
+#define SYNTH_MUXOUT (1 << 1) // INPUT!!!
+#define LOCKDET_MASK (1 << 0) // INPUT!!!
+
+// TX IO Pins
+#define TRSW (1 << 14) // 0 = TX, 1 = RX
+#define TX_LED_TXRX (1 << 7) // LED for TX Antenna Selection TX/RX
+#define TX_LED_LD (1 << 6) // LED for TX Lock Detect
+#define DIS_POWER_TX (1 << 5) // on UNIT_TX, 0 powers up TX
+#define TX_ENABLE (1 << 4) // on UNIT_TX, 0 disables TX Mixer
+
+// RX IO Pins
+#define LNASW (1 << 14) // 0 = TX/RX, 1 = RX2
+#define RX_LED_RX1RX2 (1 << 7) // LED for RX Antenna Selection RX1/RX2
+#define RX_LED_LD (1 << 6) // LED for RX Lock Detect
+#define DIS_POWER_RX (1 << 5) // on UNIT_RX, 0 powers up RX
+#define RX_DISABLE (1 << 4) // on UNIT_RX, 1 disables RX Mixer and Baseband
+
+// RX Attenuator Pins
+#define RX_ATTN_SHIFT 8 // lsb of RX Attenuator Control
+#define RX_ATTN_MASK (63 << RX_ATTN_SHIFT) // valid bits of RX Attenuator Control
+
+// TX Attenuator Pins
+#define TX_ATTN_SHIFT 8 // lsb of TX Attenuator Control
+#define TX_ATTN_MASK (63 << TX_ATTN_SHIFT) // valid bits of TX Attenuator Control
+
+// Mixer functions
+#define TX_MIXER_ENB (SYNTH_PDBRF|TX_ENABLE)
+#define TX_MIXER_DIS 0
+
+#define RX_MIXER_ENB (SYNTH_PDBRF)
+#define RX_MIXER_DIS RX_DISABLE
+
+// Pin functions
+#define TX_LED_IO (TX_LED_TXRX|TX_LED_LD) // LED gpio lines, pull down for LED
+#define TXIO_MASK (LO_LPF_EN|TRSW|SYNTH_CE|SYNTH_PDBRF|TX_ATTN_MASK|DIS_POWER_TX|TX_ENABLE)
+
+#define RX_LED_IO (RX_LED_RX1RX2|RX_LED_LD) // LED gpio lines, pull down for LED
+#define RXIO_MASK (LO_LPF_EN|LNASW|SYNTH_CE|SYNTH_PDBRF|RX_ATTN_MASK|DIS_POWER_RX|RX_DISABLE)
+
+// Power functions
+#define TX_POWER_UP (SYNTH_CE)
+#define TX_POWER_DOWN (DIS_POWER_TX)
+
+#define RX_POWER_UP (SYNTH_CE)
+#define RX_POWER_DOWN (DIS_POWER_RX)
+
+// Antenna constants
+#define ANT_TX TRSW //the tx line is transmitting
+#define ANT_RX 0 //the tx line is receiving
+#define ANT_TXRX 0 //the rx line is on txrx
+#define ANT_RX2 LNASW //the rx line in on rx2
+#define ANT_XX LNASW //dont care how the antenna is set
+
+
+#include <uhd/types/dict.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhd/types/sensors.hpp>
+#include <uhd/utils/assert_has.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/static.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/usrp/dboard_base.hpp>
+#include <uhd/usrp/dboard_manager.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/format.hpp>
+#include <boost/math/special_functions/round.hpp>
+#include <boost/thread.hpp>
+
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace boost::assign;
+
+
+/***********************************************************************
+ * The SBX dboard constants
+ **********************************************************************/
+static const freq_range_t sbx_freq_range(400e6, 4.4e9);
+
+static const freq_range_t sbx_tx_lo_2dbm = list_of
+ (range_t(0.35e9, 0.37e9))
+;
+
+static const freq_range_t sbx_enable_tx_lo_filter = list_of
+ (range_t(0.4e9, 1.5e9))
+;
+
+static const freq_range_t sbx_enable_rx_lo_filter = list_of
+ (range_t(0.4e9, 1.5e9))
+;
+
+static const std::vector<std::string> sbx_tx_antennas = list_of("TX/RX")("CAL");
+
+static const std::vector<std::string> sbx_rx_antennas = list_of("TX/RX")("RX2")("CAL");
+
+static const uhd::dict<std::string, gain_range_t> sbx_tx_gain_ranges = map_list_of
+ ("PGA0", gain_range_t(0, 31.5, double(0.5)))
+;
+
+static const uhd::dict<std::string, gain_range_t> sbx_rx_gain_ranges = map_list_of
+ ("PGA0", gain_range_t(0, 31.5, double(0.5)))
+;
+
+/***********************************************************************
+ * The SBX dboard
+ **********************************************************************/
+class sbx_xcvr : public xcvr_dboard_base{
+public:
+ sbx_xcvr(ctor_args_t args);
+ ~sbx_xcvr(void);
+
+protected:
+
+ uhd::dict<std::string, double> _tx_gains, _rx_gains;
+ double _rx_lo_freq, _tx_lo_freq;
+ std::string _tx_ant, _rx_ant;
+
+ void set_rx_ant(const std::string &ant);
+ void set_tx_ant(const std::string &ant);
+ double set_rx_gain(double gain, const std::string &name);
+ double set_tx_gain(double gain, const std::string &name);
+
+ void update_atr(void);
+
+ /*!
+ * Set the LO frequency for the particular dboard unit.
+ * \param unit which unit rx or tx
+ * \param target_freq the desired frequency in Hz
+ * \return the actual frequency in Hz
+ */
+ virtual double set_lo_freq(dboard_iface::unit_t unit, double target_freq);
+
+ /*!
+ * Get the lock detect status of the LO.
+ * \param unit which unit rx or tx
+ * \return true for locked
+ */
+ sensor_value_t get_locked(dboard_iface::unit_t unit);
+
+ /*!
+ * Flash the LEDs
+ */
+ void flash_leds(void);
+
+ /*!
+ * Version-agnostic ABC that wraps version-specific implementations of the
+ * WBX base daughterboard.
+ *
+ * This class is an abstract base class, and thus is impossible to
+ * instantiate.
+ */
+ class sbx_versionx {
+ public:
+ sbx_versionx() {}
+ ~sbx_versionx(void) {}
+
+ virtual double set_lo_freq(dboard_iface::unit_t unit, double target_freq) = 0;
+ };
+
+ /*!
+ * Version 3 of the SBX Daughterboard
+ */
+ class sbx_version3 : public sbx_versionx {
+ public:
+ sbx_version3(sbx_xcvr *_self_sbx_xcvr);
+ ~sbx_version3(void);
+
+ double set_lo_freq(dboard_iface::unit_t unit, double target_freq);
+
+ /*! This is the registered instance of the wrapper class, sbx_base. */
+ sbx_xcvr *self_base;
+ };
+
+ /*!
+ * Version 4 of the SBX Daughterboard
+ *
+ * The only difference in the fourth revision is the ADF4351 vs the ADF4350.
+ */
+ class sbx_version4 : public sbx_versionx {
+ public:
+ sbx_version4(sbx_xcvr *_self_sbx_xcvr);
+ ~sbx_version4(void);
+
+ double set_lo_freq(dboard_iface::unit_t unit, double target_freq);
+
+ /*! This is the registered instance of the wrapper class, sbx_base. */
+ sbx_xcvr *self_base;
+ };
+
+ /*!
+ * Handle to the version-specific implementation of the SBX.
+ *
+ * Since many of this class's functions are dependent on the version of the
+ * SBX board, this class will instantiate an object of the appropriate
+ * sbx_version* subclass, and invoke any relevant functions through that
+ * object. This pointer is set to the proper object at construction time.
+ */
+ typedef boost::shared_ptr<sbx_versionx> sbx_versionx_sptr;
+ sbx_versionx_sptr db_actual;
+};
+
diff --git a/host/lib/usrp/dboard/db_sbx_version3.cpp b/host/lib/usrp/dboard/db_sbx_version3.cpp
new file mode 100644
index 000000000..6e20d5882
--- /dev/null
+++ b/host/lib/usrp/dboard/db_sbx_version3.cpp
@@ -0,0 +1,186 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+
+#include "adf4350_regs.hpp"
+#include "db_sbx_common.hpp"
+
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace boost::assign;
+
+/***********************************************************************
+ * Structors
+ **********************************************************************/
+sbx_xcvr::sbx_version3::sbx_version3(sbx_xcvr *_self_sbx_xcvr) {
+ //register the handle to our base SBX class
+ self_base = _self_sbx_xcvr;
+}
+
+sbx_xcvr::sbx_version3::~sbx_version3(void){
+ /* NOP */
+}
+
+
+/***********************************************************************
+ * Tuning
+ **********************************************************************/
+double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double target_freq) {
+ UHD_LOGV(often) << boost::format(
+ "SBX tune: target frequency %f Mhz"
+ ) % (target_freq/1e6) << std::endl;
+
+ //clip the input
+ target_freq = sbx_freq_range.clip(target_freq);
+
+ //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler)
+ static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of
+ (0,23) //adf4350_regs_t::PRESCALER_4_5
+ (1,75) //adf4350_regs_t::PRESCALER_8_9
+ ;
+
+ //map rf divider select output dividers to enums
+ static const uhd::dict<int, adf4350_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of
+ (1, adf4350_regs_t::RF_DIVIDER_SELECT_DIV1)
+ (2, adf4350_regs_t::RF_DIVIDER_SELECT_DIV2)
+ (4, adf4350_regs_t::RF_DIVIDER_SELECT_DIV4)
+ (8, adf4350_regs_t::RF_DIVIDER_SELECT_DIV8)
+ (16, adf4350_regs_t::RF_DIVIDER_SELECT_DIV16)
+ ;
+
+ double actual_freq, pfd_freq;
+ double ref_freq = self_base->get_iface()->get_clock_rate(unit);
+ int R=0, BS=0, N=0, FRAC=0, MOD=0;
+ int RFdiv = 1;
+ adf4350_regs_t::reference_divide_by_2_t T = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED;
+ adf4350_regs_t::reference_doubler_t D = adf4350_regs_t::REFERENCE_DOUBLER_DISABLED;
+
+ //Reference doubler for 50% duty cycle
+ // if ref_freq < 12.5MHz enable regs.reference_divide_by_2
+ if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED;
+
+ //increase RF divider until acceptable VCO frequency
+ //start with target_freq*2 because mixer has divide by 2
+ double vco_freq = target_freq;
+ while (vco_freq < 2.2e9) {
+ vco_freq *= 2;
+ RFdiv *= 2;
+ }
+
+ //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler)
+ adf4350_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5;
+
+ /*
+ * The goal here is to loop though possible R dividers,
+ * band select clock dividers, N (int) dividers, and FRAC
+ * (frac) dividers.
+ *
+ * Calculate the N and F dividers for each set of values.
+ * The loop exists when it meets all of the constraints.
+ * The resulting loop values are loaded into the registers.
+ *
+ * from pg.21
+ *
+ * f_pfd = f_ref*(1+D)/(R*(1+T))
+ * f_vco = (N + (FRAC/MOD))*f_pfd
+ * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD
+ * f_rf = f_vco/RFdiv)
+ * f_actual = f_rf/2
+ */
+ for(R = 1; R <= 1023; R+=1){
+ //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T)
+ pfd_freq = ref_freq*(1+D)/(R*(1+T));
+
+ //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth)
+ if (pfd_freq > 25e6) continue;
+
+ //ignore fractional part of tuning
+ N = int(std::floor(vco_freq/pfd_freq));
+
+ //keep N > minimum int divider requirement
+ if (N < prescaler_to_min_int_div[prescaler]) continue;
+
+ for(BS=1; BS <= 255; BS+=1){
+ //keep the band select frequency at or below 100KHz
+ //constraint on band select clock
+ if (pfd_freq/BS > 100e3) continue;
+ goto done_loop;
+ }
+ } done_loop:
+
+ //Fractional-N calculation
+ MOD = 4095; //max fractional accuracy
+ FRAC = int((vco_freq/pfd_freq - N)*MOD);
+
+ //Reference divide-by-2 for 50% duty cycle
+ // if R even, move one divide by 2 to to regs.reference_divide_by_2
+ if(R % 2 == 0){
+ T = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED;
+ R /= 2;
+ }
+
+ //actual frequency calculation
+ actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv);
+
+ UHD_LOGV(often)
+ << boost::format("SBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl
+ << boost::format("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s"
+ ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl
+ << boost::format("SBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f"
+ ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl;
+
+ //load the register values
+ adf4350_regs_t regs;
+
+ if ((unit == dboard_iface::UNIT_TX) and (actual_freq == sbx_tx_lo_2dbm.clip(actual_freq)))
+ regs.output_power = adf4350_regs_t::OUTPUT_POWER_2DBM;
+ else
+ regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM;
+
+ regs.frac_12_bit = FRAC;
+ regs.int_16_bit = N;
+ regs.mod_12_bit = MOD;
+ regs.prescaler = prescaler;
+ regs.r_counter_10_bit = R;
+ regs.reference_divide_by_2 = T;
+ regs.reference_doubler = D;
+ regs.band_select_clock_div = BS;
+ UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv));
+ regs.rf_divider_select = rfdivsel_to_enum[RFdiv];
+
+ //write the registers
+ //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0)
+ int addr;
+
+ for(addr=5; addr>=0; addr--){
+ UHD_LOGV(often) << boost::format(
+ "SBX SPI Reg (0x%02x): 0x%08x"
+ ) % addr % regs.get_reg(addr) << std::endl;
+ self_base->get_iface()->write_spi(
+ unit, spi_config_t::EDGE_RISE,
+ regs.get_reg(addr), 32
+ );
+ }
+
+ //return the actual frequency
+ UHD_LOGV(often) << boost::format(
+ "SBX tune: actual frequency %f Mhz"
+ ) % (actual_freq/1e6) << std::endl;
+ return actual_freq;
+}
+
diff --git a/host/lib/usrp/dboard/db_sbx_version4.cpp b/host/lib/usrp/dboard/db_sbx_version4.cpp
new file mode 100644
index 000000000..c8128d5f4
--- /dev/null
+++ b/host/lib/usrp/dboard/db_sbx_version4.cpp
@@ -0,0 +1,189 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+
+#include "adf4351_regs.hpp"
+#include "db_sbx_common.hpp"
+
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace boost::assign;
+
+/***********************************************************************
+ * Structors
+ **********************************************************************/
+sbx_xcvr::sbx_version4::sbx_version4(sbx_xcvr *_self_sbx_xcvr) {
+ //register the handle to our base SBX class
+ self_base = _self_sbx_xcvr;
+}
+
+
+sbx_xcvr::sbx_version4::~sbx_version4(void){
+ /* NOP */
+}
+
+
+/***********************************************************************
+ * Tuning
+ **********************************************************************/
+double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double target_freq) {
+ UHD_LOGV(often) << boost::format(
+ "SBX tune: target frequency %f Mhz"
+ ) % (target_freq/1e6) << std::endl;
+
+ //clip the input
+ target_freq = sbx_freq_range.clip(target_freq);
+
+ //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler)
+ static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of
+ (0,23) //adf4351_regs_t::PRESCALER_4_5
+ (1,75) //adf4351_regs_t::PRESCALER_8_9
+ ;
+
+ //map rf divider select output dividers to enums
+ static const uhd::dict<int, adf4351_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of
+ (1, adf4351_regs_t::RF_DIVIDER_SELECT_DIV1)
+ (2, adf4351_regs_t::RF_DIVIDER_SELECT_DIV2)
+ (4, adf4351_regs_t::RF_DIVIDER_SELECT_DIV4)
+ (8, adf4351_regs_t::RF_DIVIDER_SELECT_DIV8)
+ (16, adf4351_regs_t::RF_DIVIDER_SELECT_DIV16)
+ (32, adf4351_regs_t::RF_DIVIDER_SELECT_DIV32)
+ (64, adf4351_regs_t::RF_DIVIDER_SELECT_DIV64)
+ ;
+
+ double actual_freq, pfd_freq;
+ double ref_freq = self_base->get_iface()->get_clock_rate(unit);
+ int R=0, BS=0, N=0, FRAC=0, MOD=0;
+ int RFdiv = 1;
+ adf4351_regs_t::reference_divide_by_2_t T = adf4351_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED;
+ adf4351_regs_t::reference_doubler_t D = adf4351_regs_t::REFERENCE_DOUBLER_DISABLED;
+
+ //Reference doubler for 50% duty cycle
+ // if ref_freq < 12.5MHz enable regs.reference_divide_by_2
+ if(ref_freq <= 12.5e6) D = adf4351_regs_t::REFERENCE_DOUBLER_ENABLED;
+
+ //increase RF divider until acceptable VCO frequency
+ //start with target_freq*2 because mixer has divide by 2
+ double vco_freq = target_freq;
+ while (vco_freq < 2.2e9) {
+ vco_freq *= 2;
+ RFdiv *= 2;
+ }
+
+ //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler)
+ adf4351_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5;
+
+ /*
+ * The goal here is to loop though possible R dividers,
+ * band select clock dividers, N (int) dividers, and FRAC
+ * (frac) dividers.
+ *
+ * Calculate the N and F dividers for each set of values.
+ * The loop exists when it meets all of the constraints.
+ * The resulting loop values are loaded into the registers.
+ *
+ * from pg.21
+ *
+ * f_pfd = f_ref*(1+D)/(R*(1+T))
+ * f_vco = (N + (FRAC/MOD))*f_pfd
+ * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD
+ * f_rf = f_vco/RFdiv)
+ * f_actual = f_rf/2
+ */
+ for(R = 1; R <= 1023; R+=1){
+ //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T)
+ pfd_freq = ref_freq*(1+D)/(R*(1+T));
+
+ //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth)
+ if (pfd_freq > 25e6) continue;
+
+ //ignore fractional part of tuning
+ N = int(std::floor(vco_freq/pfd_freq));
+
+ //keep N > minimum int divider requirement
+ if (N < prescaler_to_min_int_div[prescaler]) continue;
+
+ for(BS=1; BS <= 255; BS+=1){
+ //keep the band select frequency at or below 100KHz
+ //constraint on band select clock
+ if (pfd_freq/BS > 100e3) continue;
+ goto done_loop;
+ }
+ } done_loop:
+
+ //Fractional-N calculation
+ MOD = 4095; //max fractional accuracy
+ FRAC = int((vco_freq/pfd_freq - N)*MOD);
+
+ //Reference divide-by-2 for 50% duty cycle
+ // if R even, move one divide by 2 to to regs.reference_divide_by_2
+ if(R % 2 == 0){
+ T = adf4351_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED;
+ R /= 2;
+ }
+
+ //actual frequency calculation
+ actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv);
+
+ UHD_LOGV(often)
+ << boost::format("SBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl
+ << boost::format("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s"
+ ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl
+ << boost::format("SBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f"
+ ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl;
+
+ //load the register values
+ adf4351_regs_t regs;
+
+ if ((unit == dboard_iface::UNIT_TX) and (actual_freq == sbx_tx_lo_2dbm.clip(actual_freq)))
+ regs.output_power = adf4351_regs_t::OUTPUT_POWER_2DBM;
+ else
+ regs.output_power = adf4351_regs_t::OUTPUT_POWER_5DBM;
+
+ regs.frac_12_bit = FRAC;
+ regs.int_16_bit = N;
+ regs.mod_12_bit = MOD;
+ regs.prescaler = prescaler;
+ regs.r_counter_10_bit = R;
+ regs.reference_divide_by_2 = T;
+ regs.reference_doubler = D;
+ regs.band_select_clock_div = BS;
+ UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv));
+ regs.rf_divider_select = rfdivsel_to_enum[RFdiv];
+
+ //write the registers
+ //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0)
+ int addr;
+
+ for(addr=5; addr>=0; addr--){
+ UHD_LOGV(often) << boost::format(
+ "SBX SPI Reg (0x%02x): 0x%08x"
+ ) % addr % regs.get_reg(addr) << std::endl;
+ self_base->get_iface()->write_spi(
+ unit, spi_config_t::EDGE_RISE,
+ regs.get_reg(addr), 32
+ );
+ }
+
+ //return the actual frequency
+ UHD_LOGV(often) << boost::format(
+ "SBX tune: actual frequency %f Mhz"
+ ) % (actual_freq/1e6) << std::endl;
+ return actual_freq;
+}
+
diff --git a/host/lib/usrp/dboard/db_tvrx.cpp b/host/lib/usrp/dboard/db_tvrx.cpp
new file mode 100644
index 000000000..fd86d5b83
--- /dev/null
+++ b/host/lib/usrp/dboard/db_tvrx.cpp
@@ -0,0 +1,411 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+// No RX IO Pins Used
+
+// RX IO Functions
+
+//ADC/DAC functions:
+//DAC 1: RF AGC
+//DAC 2: IF AGC
+
+//min freq: 50e6
+//max freq: 860e6
+//gain range: [0:1dB:115dB]
+
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/static.hpp>
+#include <uhd/utils/assert_has.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhd/types/sensors.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/usrp/dboard_base.hpp>
+#include <uhd/usrp/dboard_manager.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/format.hpp>
+#include <boost/thread.hpp>
+#include <boost/array.hpp>
+#include <boost/math/special_functions/round.hpp>
+#include <utility>
+#include <cmath>
+#include <cfloat>
+#include <limits>
+#include <tuner_4937di5_regs.hpp>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace boost::assign;
+
+/***********************************************************************
+ * The tvrx constants
+ **********************************************************************/
+static const freq_range_t tvrx_freq_range(50e6, 860e6);
+
+static const std::vector<std::string> tvrx_antennas = list_of("RX");
+
+static const uhd::dict<std::string, freq_range_t> tvrx_freq_ranges = map_list_of
+ ("VHFLO", freq_range_t(50e6, 158e6))
+ ("VHFHI", freq_range_t(158e6, 454e6))
+ ("UHF" , freq_range_t(454e6, 860e6))
+;
+
+static const boost::array<double, 17> vhflo_gains_db =
+ {{-6.00000, -6.00000, -6.00000, -4.00000, 0.00000,
+ 5.00000, 10.00000, 17.40000, 26.30000, 36.00000,
+ 43.00000, 48.00000, 49.50000, 50.10000, 50.30000,
+ 50.30000, 50.30000}};
+
+static const boost::array<double, 17> vhfhi_gains_db =
+ {{-13.3000, -13.3000, -13.3000, -1.0000, 7.7000,
+ 11.0000, 14.7000, 19.3000, 26.1000, 36.0000,
+ 42.7000, 46.0000, 47.0000, 47.8000, 48.2000,
+ 48.2000, 48.2000}};
+
+static const boost::array<double, 17> uhf_gains_db =
+ {{-8.0000, -8.0000, -7.0000, 4.0000, 10.2000,
+ 14.5000, 17.5000, 20.0000, 24.5000, 30.8000,
+ 37.0000, 39.8000, 40.7000, 41.6000, 42.6000,
+ 43.2000, 43.8000}};
+
+static const boost::array<double, 17> tvrx_if_gains_db =
+ {{-1.50000, -1.50000, -1.50000, -1.00000, 0.20000,
+ 2.10000, 4.30000, 6.40000, 9.00000, 12.00000,
+ 14.80000, 18.20000, 26.10000, 32.50000, 32.50000,
+ 32.50000, 32.50000}};
+
+//gain linearization data
+//this is from the datasheet and is dB vs. volts (below)
+//i tried to curve fit this, but it's really just so nonlinear that you'd
+//need dang near as many coefficients as to just map it like this and interp.
+//these numbers are culled from the 4937DI5 datasheet and are probably totally inaccurate
+//but if it's better than the old linear fit i'm happy
+static const uhd::dict<std::string, boost::array<double, 17> > tvrx_rf_gains_db = map_list_of
+ ("VHFLO", vhflo_gains_db)
+ ("VHFHI", vhfhi_gains_db)
+ ("UHF" , uhf_gains_db)
+;
+
+//sample voltages for the above points
+static const boost::array<double, 17> tvrx_gains_volts =
+ {{0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0}};
+
+static uhd::dict<std::string, gain_range_t> get_tvrx_gain_ranges(void) {
+ double rfmax = 0.0, rfmin = FLT_MAX;
+ BOOST_FOREACH(const std::string range, tvrx_rf_gains_db.keys()) {
+ double my_max = tvrx_rf_gains_db[range].back(); //we're assuming it's monotonic
+ double my_min = tvrx_rf_gains_db[range].front(); //if it's not this is wrong wrong wrong
+ if(my_max > rfmax) rfmax = my_max;
+ if(my_min < rfmin) rfmin = my_min;
+ }
+
+ double ifmin = tvrx_if_gains_db.front();
+ double ifmax = tvrx_if_gains_db.back();
+
+ return map_list_of
+ ("RF", gain_range_t(rfmin, rfmax, (rfmax-rfmin)/4096.0))
+ ("IF", gain_range_t(ifmin, ifmax, (ifmax-ifmin)/4096.0))
+ ;
+}
+
+static const double opamp_gain = 1.22; //onboard DAC opamp gain
+static const double tvrx_if_freq = 43.75e6; //IF freq of TVRX module
+static const boost::uint16_t reference_divider = 640; //clock reference divider to use
+static const double reference_freq = 4.0e6;
+
+/***********************************************************************
+ * The tvrx dboard class
+ **********************************************************************/
+class tvrx : public rx_dboard_base{
+public:
+ tvrx(ctor_args_t args);
+ ~tvrx(void);
+
+private:
+ uhd::dict<std::string, double> _gains;
+ double _lo_freq;
+ tuner_4937di5_regs_t _tuner_4937di5_regs;
+ boost::uint8_t _tuner_4937di5_addr(void){
+ return (this->get_iface()->get_special_props().mangle_i2c_addrs)? 0x61 : 0x60; //ok really? we could rename that call
+ };
+
+ double set_gain(double gain, const std::string &name);
+ double set_freq(double freq);
+
+ void update_regs(void){
+ byte_vector_t regs_vector(4);
+
+ //get the register data
+ for(int i=0; i<4; i++){
+ regs_vector[i] = _tuner_4937di5_regs.get_reg(i);
+ UHD_LOGV(often) << boost::format(
+ "tvrx: send reg 0x%02x, value 0x%04x"
+ ) % int(i) % int(regs_vector[i]) << std::endl;
+ }
+
+ //send the data
+ this->get_iface()->write_i2c(
+ _tuner_4937di5_addr(), regs_vector
+ );
+ }
+
+};
+
+/***********************************************************************
+ * Register the tvrx dboard
+ **********************************************************************/
+static dboard_base::sptr make_tvrx(dboard_base::ctor_args_t args){
+ return dboard_base::sptr(new tvrx(args));
+}
+
+UHD_STATIC_BLOCK(reg_tvrx_dboard){
+ //register the factory function for the rx dbid
+ dboard_manager::register_dboard(0x0040, &make_tvrx, "TVRX");
+}
+
+/***********************************************************************
+ * Structors
+ **********************************************************************/
+tvrx::tvrx(ctor_args_t args) : rx_dboard_base(args){
+ ////////////////////////////////////////////////////////////////////
+ // Register properties
+ ////////////////////////////////////////////////////////////////////
+ this->get_rx_subtree()->create<std::string>("name")
+ .set(get_rx_id().to_pp_string());
+ this->get_rx_subtree()->create<int>("sensors"); //phony property so this dir exists
+ BOOST_FOREACH(const std::string &name, get_tvrx_gain_ranges().keys()){
+ this->get_rx_subtree()->create<double>("gains/"+name+"/value")
+ .coerce(boost::bind(&tvrx::set_gain, this, _1, name));
+ this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range")
+ .set(get_tvrx_gain_ranges()[name]);
+ }
+ this->get_rx_subtree()->create<double>("freq/value")
+ .coerce(boost::bind(&tvrx::set_freq, this, _1));
+ this->get_rx_subtree()->create<meta_range_t>("freq/range")
+ .set(tvrx_freq_range);
+ this->get_rx_subtree()->create<std::string>("antenna/value")
+ .set(tvrx_antennas.at(0));
+ this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options")
+ .set(tvrx_antennas);
+ this->get_rx_subtree()->create<std::string>("connection")
+ .set("I");
+ this->get_rx_subtree()->create<bool>("enabled")
+ .set(true); //always enabled
+ this->get_rx_subtree()->create<bool>("use_lo_offset")
+ .set(false);
+ this->get_rx_subtree()->create<double>("bandwidth/value")
+ .set(6.0e6);
+ this->get_rx_subtree()->create<meta_range_t>("bandwidth/range")
+ .set(freq_range_t(6.0e6, 6.0e6));
+
+ //enable only the clocks we need
+ this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true);
+
+ //set the gpio directions and atr controls (identically)
+ this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, 0x0); // All unused in atr
+ if (this->get_iface()->get_special_props().soft_clock_divider){
+ this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x1); // GPIO0 is clock
+ }
+ else{
+ this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x0); // All Inputs
+ }
+
+ //send initial register settings if necessary
+
+ //set default freq
+ _lo_freq = tvrx_freq_range.start() + tvrx_if_freq; //init _lo_freq to a sane default
+ this->get_rx_subtree()->access<double>("freq/value").set(tvrx_freq_range.start());
+
+ //set default gains
+ BOOST_FOREACH(const std::string &name, get_tvrx_gain_ranges().keys()){
+ this->get_rx_subtree()->access<double>("gains/"+name+"/value")
+ .set(get_tvrx_gain_ranges()[name].start());
+ }
+}
+
+tvrx::~tvrx(void){
+}
+
+/*! Return a string corresponding to the relevant band
+ * \param freq the frequency of interest
+ * \return a string corresponding to the band
+ */
+
+static std::string get_band(double freq) {
+ BOOST_FOREACH(const std::string &band, tvrx_freq_ranges.keys()) {
+ if(freq >= tvrx_freq_ranges[band].start() && freq <= tvrx_freq_ranges[band].stop()){
+ UHD_LOGV(often) << "Band: " << band << std::endl;
+ return band;
+ }
+ }
+ UHD_THROW_INVALID_CODE_PATH();
+}
+
+/***********************************************************************
+ * Gain Handling
+ **********************************************************************/
+/*!
+ * Execute a linear interpolation to find the voltage corresponding to a desired gain
+ * \param gain the desired gain in dB
+ * \param db_vector the vector of dB readings
+ * \param volts_vector the corresponding vector of voltages db_vector was sampled at
+ * \return a voltage to feed the TVRX analog gain
+ */
+
+static double gain_interp(double gain, boost::array<double, 17> db_vector, boost::array<double, 17> volts_vector) {
+ double volts;
+ gain = uhd::clip<double>(gain, db_vector.front(), db_vector.back()); //let's not get carried away here
+
+ boost::uint8_t gain_step = 0;
+ //find which bin we're in
+ for(size_t i = 0; i < db_vector.size()-1; i++) {
+ if(gain >= db_vector[i] && gain <= db_vector[i+1]) gain_step = i;
+ }
+
+ //find the current slope for linear interpolation
+ double slope = (volts_vector[gain_step + 1] - volts_vector[gain_step])
+ / (db_vector[gain_step + 1] - db_vector[gain_step]);
+
+ //the problem here is that for gains approaching the maximum, the voltage slope becomes infinite
+ //i.e., a small change in gain requires an infinite change in voltage
+ //to cope, we limit the slope
+
+ if(slope == std::numeric_limits<double>::infinity())
+ return volts_vector[gain_step];
+
+ //use the volts per dB slope to find the final interpolated voltage
+ volts = volts_vector[gain_step] + (slope * (gain - db_vector[gain_step]));
+
+ UHD_LOGV(often) << "Gain interp: gain: " << gain << ", gain_step: " << int(gain_step) << ", slope: " << slope << ", volts: " << volts << std::endl;
+
+ return volts;
+}
+
+/*!
+ * Convert a requested gain for the RF gain into a DAC voltage.
+ * The gain passed into the function will be set to the actual value.
+ * \param gain the requested gain in dB
+ * \return dac voltage value
+ */
+
+static double rf_gain_to_voltage(double gain, double lo_freq){
+ //clip the input
+ gain = get_tvrx_gain_ranges()["RF"].clip(gain);
+
+ //first we need to find out what band we're in, because gains are different across different bands
+ std::string band = get_band(lo_freq - tvrx_if_freq);
+
+ //this is the voltage at the TVRX gain input
+ double gain_volts = gain_interp(gain, tvrx_rf_gains_db[band], tvrx_gains_volts);
+ //this is the voltage at the USRP DAC output
+ double dac_volts = gain_volts / opamp_gain;
+
+ dac_volts = uhd::clip<double>(dac_volts, 0.0, 3.3);
+
+ UHD_LOGV(often) << boost::format(
+ "tvrx RF AGC gain: %f dB, dac_volts: %f V"
+ ) % gain % dac_volts << std::endl;
+
+ return dac_volts;
+}
+
+/*!
+ * Convert a requested gain for the IF gain into a DAC voltage.
+ * The gain passed into the function will be set to the actual value.
+ * \param gain the requested gain in dB
+ * \return dac voltage value
+ */
+
+static double if_gain_to_voltage(double gain){
+ //clip the input
+ gain = get_tvrx_gain_ranges()["IF"].clip(gain);
+
+ double gain_volts = gain_interp(gain, tvrx_if_gains_db, tvrx_gains_volts);
+ double dac_volts = gain_volts / opamp_gain;
+
+ dac_volts = uhd::clip<double>(dac_volts, 0.0, 3.3);
+
+ UHD_LOGV(often) << boost::format(
+ "tvrx IF AGC gain: %f dB, dac_volts: %f V"
+ ) % gain % dac_volts << std::endl;
+
+ return dac_volts;
+}
+
+double tvrx::set_gain(double gain, const std::string &name){
+ assert_has(get_tvrx_gain_ranges().keys(), name, "tvrx gain name");
+ if (name == "RF"){
+ this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, dboard_iface::AUX_DAC_B, rf_gain_to_voltage(gain, _lo_freq));
+ }
+ else if(name == "IF"){
+ this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, dboard_iface::AUX_DAC_A, if_gain_to_voltage(gain));
+ }
+ else UHD_THROW_INVALID_CODE_PATH();
+ _gains[name] = gain;
+
+ return gain;
+}
+
+/*!
+ * Set the tuner to center the desired frequency at 43.75MHz
+ * \param freq the requested frequency
+ */
+
+double tvrx::set_freq(double freq) {
+ freq = tvrx_freq_range.clip(freq);
+ std::string prev_band = get_band(_lo_freq - tvrx_if_freq);
+ std::string new_band = get_band(freq);
+
+ double target_lo_freq = freq + tvrx_if_freq; //the desired LO freq for high-side mixing
+ double f_ref = reference_freq / double(reference_divider); //your tuning step size
+
+ int divisor = int((target_lo_freq + (f_ref * 4.0)) / (f_ref * 8)); //the divisor we'll use
+ double actual_lo_freq = (f_ref * 8 * divisor); //the LO freq we'll actually get
+
+ if((divisor & ~0x7fff)) UHD_THROW_INVALID_CODE_PATH();
+
+ //now we update the registers
+ _tuner_4937di5_regs.db1 = (divisor >> 8) & 0xff;
+ _tuner_4937di5_regs.db2 = divisor & 0xff;
+
+ if(new_band == "VHFLO") _tuner_4937di5_regs.bandsel = tuner_4937di5_regs_t::BANDSEL_VHFLO;
+ else if(new_band == "VHFHI") _tuner_4937di5_regs.bandsel = tuner_4937di5_regs_t::BANDSEL_VHFHI;
+ else if(new_band == "UHF") _tuner_4937di5_regs.bandsel = tuner_4937di5_regs_t::BANDSEL_UHF;
+ else UHD_THROW_INVALID_CODE_PATH();
+
+ _tuner_4937di5_regs.power = tuner_4937di5_regs_t::POWER_OFF;
+ update_regs();
+
+ //ok don't forget to reset RF gain here if the new band != the old band
+ //we do this because the gains are different for different band settings
+ //not FAR off, but we do this to be consistent
+ if(prev_band != new_band) set_gain(_gains["RF"], "RF");
+
+ UHD_LOGV(often) << boost::format("set_freq: target LO: %f f_ref: %f divisor: %i actual LO: %f") % target_lo_freq % f_ref % divisor % actual_lo_freq << std::endl;
+
+ _lo_freq = actual_lo_freq; //for rx props
+
+ //Check the the IF if larger than the dsp rate and apply a corrective adjustment
+ //so that the cordic will be tuned to a possible rate within its range.
+ const double codec_rate = this->get_iface()->get_codec_rate(dboard_iface::UNIT_RX);
+ if (tvrx_if_freq >= codec_rate/2){
+ return _lo_freq - codec_rate;
+ }
+
+ return _lo_freq;
+}
diff --git a/host/lib/usrp/dboard/db_tvrx2.cpp b/host/lib/usrp/dboard/db_tvrx2.cpp
new file mode 100644
index 000000000..628221527
--- /dev/null
+++ b/host/lib/usrp/dboard/db_tvrx2.cpp
@@ -0,0 +1,1844 @@
+//
+// Copyright 2010 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+// Common IO Pins
+#define REFCLOCK_DIV_MASK ((1 << 8)|(1 << 9)|(1 << 10)) // Three GPIO lines to CPLD for Clock Divisor Selection
+#define REFCLOCK_DIV8 ((1 << 8)|(1 << 9)|(1 << 10)) // GPIO to set clock div8 mode
+#define REFCLOCK_DIV7 ((0 << 8)|(1 << 9)|(1 << 10)) // GPIO to set clock div7 mode
+#define REFCLOCK_DIV6 ((1 << 8)|(0 << 9)|(1 << 10)) // GPIO to set clock div6 mode
+#define REFCLOCK_DIV5 ((0 << 8)|(0 << 9)|(1 << 10)) // GPIO to set clock div5 mode
+#define REFCLOCK_DIV4 ((1 << 8)|(1 <<9)) // GPIO to set clock div4 mode
+#define REFCLOCK_DIV3 (1 <<9) // GPIO to set clock div3 mode
+#define REFCLOCK_DIV2 (1 <<8) // GPIO to set clock div2 mode
+#define REFCLOCK_DIV1 ((0 << 8)|(0 << 9)|(0 << 10)) // GPIO to set clock div1 mode
+
+// RX1 IO Pins
+#define RX1_MASTERSYNC (1 << 3) // MASTERSYNC Signal for Slave Tuner Coordination
+#define RX1_FREEZE (1 << 2) // FREEZE Signal for Slave Tuner Coordination
+#define RX1_IRQ (1 << 1) // IRQ Signals TDA18272HNM State Machine Completion
+#define RX1_VSYNC (1 << 0) // VSYNC Pulse for AGC Holdoff
+
+// RX2 IO Pins
+#define RX2_MASTERSYNC (1 << 7) // MASTERSYNC Signal for Slave Tuner Coordination
+#define RX2_FREEZE (1 << 6) // FREEZE Signal for Slave Tuner Coordination
+#define RX2_IRQ (1 << 5) // IRQ Signals TDA18272HNM State Machine Completion
+#define RX2_VSYNC (1 << 4) // VSYNC Pulse for AGC Holdoff
+
+// Pin functions
+#define RX1_OUTPUT_MASK (0)
+#define RX1_INPUT_MASK (RX1_VSYNC|RX1_MASTERSYNC|RX1_FREEZE|RX1_IRQ)
+
+#define RX2_OUTPUT_MASK (0)
+#define RX2_INPUT_MASK (RX2_VSYNC|RX2_MASTERSYNC|RX2_FREEZE|RX2_IRQ)
+
+#define OUTPUT_MASK (RX1_OUTPUT_MASK|RX2_OUTPUT_MASK|REFCLOCK_DIV_MASK)
+#define INPUT_MASK (RX1_INPUT_MASK|RX2_INPUT_MASK)
+
+
+#include "tda18272hnm_regs.hpp"
+#include <uhd/utils/static.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/safe_call.hpp>
+#include <uhd/utils/assert_has.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhd/types/sensors.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/usrp/dboard_base.hpp>
+#include <uhd/usrp/dboard_manager.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/format.hpp>
+#include <boost/thread.hpp>
+#include <boost/array.hpp>
+#include <boost/math/special_functions/round.hpp>
+#include <utility>
+#include <cmath>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace boost::assign;
+
+/***********************************************************************
+ * The TVRX2 types
+ **********************************************************************/
+struct tvrx2_tda18272_rfcal_result_t {
+ boost::int8_t delta_c;
+ boost::int8_t c_offset;
+ tvrx2_tda18272_rfcal_result_t(void): delta_c(0), c_offset(0){}
+};
+
+struct tvrx2_tda18272_rfcal_coeffs_t {
+ boost::uint8_t cal_number;
+ boost::int32_t RF_A1;
+ boost::int32_t RF_B1;
+ tvrx2_tda18272_rfcal_coeffs_t(void): cal_number(0), RF_A1(0), RF_B1(0) {}
+ tvrx2_tda18272_rfcal_coeffs_t(boost::uint32_t num): RF_A1(0), RF_B1(0) { cal_number = num; }
+};
+
+struct tvrx2_tda18272_cal_map_t {
+ boost::array<boost::uint32_t, 4> cal_freq;
+ boost::array<boost::uint8_t, 4> c_offset;
+ tvrx2_tda18272_cal_map_t(boost::array<boost::uint32_t, 4> freqs, boost::array<boost::uint8_t, 4> offsets)
+ { cal_freq = freqs; c_offset = offsets; }
+};
+
+struct tvrx2_tda18272_freq_map_t {
+ boost::uint32_t rf_max;
+ boost::uint8_t c_prog;
+ boost::uint8_t gain_taper;
+ boost::uint8_t rf_band;
+ tvrx2_tda18272_freq_map_t( boost::uint32_t max, boost::uint8_t c, boost::uint8_t taper, boost::uint8_t band)
+ { rf_max = max; c_prog = c; gain_taper = taper; rf_band = band; }
+};
+
+/***********************************************************************
+ * The TVRX2 constants
+ **********************************************************************/
+
+static const boost::array<freq_range_t, 4> tvrx2_tda18272_rf_bands = list_of
+ ( freq_range_t( 44.056e6, 144.408e6) )
+ ( freq_range_t( 145.432e6, 361.496e6) )
+ ( freq_range_t( 365.592e6, 618.520e6) )
+ ( freq_range_t( 619.544e6, 865.304e6) )
+;
+
+#define TVRX2_TDA18272_FREQ_MAP_ENTRIES (565)
+
+static const uhd::dict<boost::uint32_t, tvrx2_tda18272_cal_map_t> tvrx2_tda18272_cal_map = map_list_of
+ ( 0, tvrx2_tda18272_cal_map_t( list_of( 44032000)( 48128000)( 52224000)( 56320000), list_of(15)( 0)(10)(17) ) )
+ ( 1, tvrx2_tda18272_cal_map_t( list_of( 84992000)( 89088000)( 93184000)( 97280000), list_of( 1)( 0)(-2)( 3) ) )
+ ( 2, tvrx2_tda18272_cal_map_t( list_of(106496000)(111616000)(115712000)(123904000), list_of( 0)(-1)( 1)( 2) ) )
+ ( 3, tvrx2_tda18272_cal_map_t( list_of(161792000)(165888000)(169984000)(174080000), list_of( 3)( 0)( 1)( 2) ) )
+ ( 4, tvrx2_tda18272_cal_map_t( list_of(224256000)(228352000)(232448000)(235520000), list_of( 3)( 0)( 1)( 2) ) )
+ ( 5, tvrx2_tda18272_cal_map_t( list_of(301056000)(312320000)(322560000)(335872000), list_of( 0)(-1)( 1)( 2) ) )
+ ( 6, tvrx2_tda18272_cal_map_t( list_of(389120000)(393216000)(397312000)(401408000), list_of(-2)( 0)(-1)( 1) ) )
+ ( 7, tvrx2_tda18272_cal_map_t( list_of(455680000)(460800000)(465920000)(471040000), list_of( 0)(-2)(-3)( 1) ) )
+ ( 8, tvrx2_tda18272_cal_map_t( list_of(555008000)(563200000)(570368000)(577536000), list_of(-1)( 0)(-3)(-2) ) )
+ ( 9, tvrx2_tda18272_cal_map_t( list_of(647168000)(652288000)(658432000)(662528000), list_of(-6)(-3)( 0)(-5) ) )
+ ( 10, tvrx2_tda18272_cal_map_t( list_of(748544000)(755712000)(762880000)(770048000), list_of(-6)(-3)( 0)(-5) ) )
+ ( 11, tvrx2_tda18272_cal_map_t( list_of(792576000)(801792000)(809984000)(818176000), list_of(-5)(-2)( 0)(-4) ) )
+;
+
+static const std::vector<tvrx2_tda18272_freq_map_t> tvrx2_tda18272_freq_map = list_of
+ ( tvrx2_tda18272_freq_map_t( 39936000, 0xFF, 0x17, 0) )
+ ( tvrx2_tda18272_freq_map_t( 40960000, 0xFD, 0x17, 0) )
+ ( tvrx2_tda18272_freq_map_t( 41984000, 0xF1, 0x15, 0) )
+ ( tvrx2_tda18272_freq_map_t( 43008000, 0xE5, 0x13, 0) )
+ ( tvrx2_tda18272_freq_map_t( 44032000, 0xDB, 0x13, 0) )
+ ( tvrx2_tda18272_freq_map_t( 45056000, 0xD1, 0x12, 0) )
+ ( tvrx2_tda18272_freq_map_t( 46080000, 0xC7, 0x10, 0) )
+ ( tvrx2_tda18272_freq_map_t( 47104000, 0xBE, 0x0F, 0) )
+ ( tvrx2_tda18272_freq_map_t( 48128000, 0xB5, 0x0F, 0) )
+ ( tvrx2_tda18272_freq_map_t( 49152000, 0xAD, 0x0F, 0) )
+ ( tvrx2_tda18272_freq_map_t( 50176000, 0xA6, 0x0F, 0) )
+ ( tvrx2_tda18272_freq_map_t( 51200000, 0x9F, 0x0F, 0) )
+ ( tvrx2_tda18272_freq_map_t( 52224000, 0x98, 0x0F, 0) )
+ ( tvrx2_tda18272_freq_map_t( 53248000, 0x91, 0x0F, 0) )
+ ( tvrx2_tda18272_freq_map_t( 54272000, 0x8B, 0x0F, 0) )
+ ( tvrx2_tda18272_freq_map_t( 55296000, 0x86, 0x0F, 0) )
+ ( tvrx2_tda18272_freq_map_t( 56320000, 0x80, 0x0F, 0) )
+ ( tvrx2_tda18272_freq_map_t( 57344000, 0x7B, 0x0E, 0) )
+ ( tvrx2_tda18272_freq_map_t( 58368000, 0x76, 0x0E, 0) )
+ ( tvrx2_tda18272_freq_map_t( 59392000, 0x72, 0x0D, 0) )
+ ( tvrx2_tda18272_freq_map_t( 60416000, 0x6D, 0x0D, 0) )
+ ( tvrx2_tda18272_freq_map_t( 61440000, 0x69, 0x0C, 0) )
+ ( tvrx2_tda18272_freq_map_t( 62464000, 0x65, 0x0C, 0) )
+ ( tvrx2_tda18272_freq_map_t( 63488000, 0x61, 0x0B, 0) )
+ ( tvrx2_tda18272_freq_map_t( 64512000, 0x5E, 0x0B, 0) )
+ ( tvrx2_tda18272_freq_map_t( 64512000, 0x5A, 0x0B, 0) )
+ ( tvrx2_tda18272_freq_map_t( 65536000, 0x57, 0x0A, 0) )
+ ( tvrx2_tda18272_freq_map_t( 66560000, 0x54, 0x0A, 0) )
+ ( tvrx2_tda18272_freq_map_t( 67584000, 0x51, 0x09, 0) )
+ ( tvrx2_tda18272_freq_map_t( 68608000, 0x4E, 0x09, 0) )
+ ( tvrx2_tda18272_freq_map_t( 69632000, 0x4B, 0x09, 0) )
+ ( tvrx2_tda18272_freq_map_t( 70656000, 0x49, 0x08, 0) )
+ ( tvrx2_tda18272_freq_map_t( 71680000, 0x46, 0x08, 0) )
+ ( tvrx2_tda18272_freq_map_t( 72704000, 0x44, 0x08, 0) )
+ ( tvrx2_tda18272_freq_map_t( 73728000, 0x41, 0x07, 0) )
+ ( tvrx2_tda18272_freq_map_t( 74752000, 0x3F, 0x07, 0) )
+ ( tvrx2_tda18272_freq_map_t( 75776000, 0x3D, 0x07, 0) )
+ ( tvrx2_tda18272_freq_map_t( 76800000, 0x3B, 0x07, 0) )
+ ( tvrx2_tda18272_freq_map_t( 77824000, 0x39, 0x07, 0) )
+ ( tvrx2_tda18272_freq_map_t( 78848000, 0x37, 0x07, 0) )
+ ( tvrx2_tda18272_freq_map_t( 79872000, 0x35, 0x07, 0) )
+ ( tvrx2_tda18272_freq_map_t( 80896000, 0x33, 0x07, 0) )
+ ( tvrx2_tda18272_freq_map_t( 81920000, 0x32, 0x07, 0) )
+ ( tvrx2_tda18272_freq_map_t( 82944000, 0x30, 0x07, 0) )
+ ( tvrx2_tda18272_freq_map_t( 83968000, 0x2F, 0x07, 0) )
+ ( tvrx2_tda18272_freq_map_t( 84992000, 0x2D, 0x07, 0) )
+ ( tvrx2_tda18272_freq_map_t( 86016000, 0x2C, 0x07, 0) )
+ ( tvrx2_tda18272_freq_map_t( 87040000, 0x2A, 0x07, 0) )
+ ( tvrx2_tda18272_freq_map_t( 88064000, 0x29, 0x06, 0) )
+ ( tvrx2_tda18272_freq_map_t( 89088000, 0x27, 0x06, 0) )
+ ( tvrx2_tda18272_freq_map_t( 90112000, 0x26, 0x06, 0) )
+ ( tvrx2_tda18272_freq_map_t( 91136000, 0x25, 0x06, 0) )
+ ( tvrx2_tda18272_freq_map_t( 92160000, 0x24, 0x06, 0) )
+ ( tvrx2_tda18272_freq_map_t( 93184000, 0x22, 0x05, 0) )
+ ( tvrx2_tda18272_freq_map_t( 94208000, 0x21, 0x05, 0) )
+ ( tvrx2_tda18272_freq_map_t( 95232000, 0x20, 0x05, 0) )
+ ( tvrx2_tda18272_freq_map_t( 96256000, 0x1F, 0x05, 0) )
+ ( tvrx2_tda18272_freq_map_t( 97280000, 0x1E, 0x05, 0) )
+ ( tvrx2_tda18272_freq_map_t( 98304000, 0x1D, 0x05, 0) )
+ ( tvrx2_tda18272_freq_map_t( 99328000, 0x1C, 0x04, 0) )
+ ( tvrx2_tda18272_freq_map_t(100352000, 0x1B, 0x04, 0) )
+ ( tvrx2_tda18272_freq_map_t(101376000, 0x1A, 0x04, 0) )
+ ( tvrx2_tda18272_freq_map_t(103424000, 0x19, 0x04, 0) )
+ ( tvrx2_tda18272_freq_map_t(104448000, 0x18, 0x04, 0) )
+ ( tvrx2_tda18272_freq_map_t(105472000, 0x17, 0x04, 0) )
+ ( tvrx2_tda18272_freq_map_t(106496000, 0x16, 0x03, 0) )
+ ( tvrx2_tda18272_freq_map_t(106496000, 0x15, 0x03, 0) )
+ ( tvrx2_tda18272_freq_map_t(108544000, 0x14, 0x03, 0) )
+ ( tvrx2_tda18272_freq_map_t(109568000, 0x13, 0x03, 0) )
+ ( tvrx2_tda18272_freq_map_t(111616000, 0x12, 0x03, 0) )
+ ( tvrx2_tda18272_freq_map_t(112640000, 0x11, 0x03, 0) )
+ ( tvrx2_tda18272_freq_map_t(113664000, 0x11, 0x07, 0) )
+ ( tvrx2_tda18272_freq_map_t(114688000, 0x10, 0x07, 0) )
+ ( tvrx2_tda18272_freq_map_t(115712000, 0x0F, 0x07, 0) )
+ ( tvrx2_tda18272_freq_map_t(117760000, 0x0E, 0x07, 0) )
+ ( tvrx2_tda18272_freq_map_t(119808000, 0x0D, 0x06, 0) )
+ ( tvrx2_tda18272_freq_map_t(121856000, 0x0C, 0x06, 0) )
+ ( tvrx2_tda18272_freq_map_t(123904000, 0x0B, 0x06, 0) )
+ ( tvrx2_tda18272_freq_map_t(125952000, 0x0A, 0x05, 0) )
+ ( tvrx2_tda18272_freq_map_t(128000000, 0x09, 0x05, 0) )
+ ( tvrx2_tda18272_freq_map_t(130048000, 0x08, 0x05, 0) )
+ ( tvrx2_tda18272_freq_map_t(133120000, 0x07, 0x04, 0) )
+ ( tvrx2_tda18272_freq_map_t(135168000, 0x06, 0x04, 0) )
+ ( tvrx2_tda18272_freq_map_t(138240000, 0x05, 0x04, 0) )
+ ( tvrx2_tda18272_freq_map_t(141312000, 0x04, 0x04, 0) )
+ ( tvrx2_tda18272_freq_map_t(144384000, 0x03, 0x03, 0) )
+ ( tvrx2_tda18272_freq_map_t(145408000, 0xE0, 0x3F, 1) )
+ ( tvrx2_tda18272_freq_map_t(147456000, 0xDC, 0x37, 1) )
+ ( tvrx2_tda18272_freq_map_t(148480000, 0xD9, 0x32, 1) )
+ ( tvrx2_tda18272_freq_map_t(149504000, 0xD6, 0x2F, 1) )
+ ( tvrx2_tda18272_freq_map_t(149504000, 0xD2, 0x2F, 1) )
+ ( tvrx2_tda18272_freq_map_t(150528000, 0xCF, 0x2F, 1) )
+ ( tvrx2_tda18272_freq_map_t(151552000, 0xCC, 0x2B, 1) )
+ ( tvrx2_tda18272_freq_map_t(152576000, 0xC9, 0x27, 1) )
+ ( tvrx2_tda18272_freq_map_t(153600000, 0xC5, 0x27, 1) )
+ ( tvrx2_tda18272_freq_map_t(154624000, 0xC2, 0x25, 1) )
+ ( tvrx2_tda18272_freq_map_t(155648000, 0xBF, 0x23, 1) )
+ ( tvrx2_tda18272_freq_map_t(156672000, 0xBD, 0x20, 1) )
+ ( tvrx2_tda18272_freq_map_t(157696000, 0xBA, 0x1F, 1) )
+ ( tvrx2_tda18272_freq_map_t(158720000, 0xB7, 0x1F, 1) )
+ ( tvrx2_tda18272_freq_map_t(159744000, 0xB4, 0x1F, 1) )
+ ( tvrx2_tda18272_freq_map_t(160768000, 0xB1, 0x1F, 1) )
+ ( tvrx2_tda18272_freq_map_t(161792000, 0xAF, 0x1F, 1) )
+ ( tvrx2_tda18272_freq_map_t(162816000, 0xAC, 0x1F, 1) )
+ ( tvrx2_tda18272_freq_map_t(163840000, 0xAA, 0x1F, 1) )
+ ( tvrx2_tda18272_freq_map_t(164864000, 0xA7, 0x1F, 1) )
+ ( tvrx2_tda18272_freq_map_t(165888000, 0xA5, 0x1F, 1) )
+ ( tvrx2_tda18272_freq_map_t(166912000, 0xA2, 0x1F, 1) )
+ ( tvrx2_tda18272_freq_map_t(167936000, 0xA0, 0x1F, 1) )
+ ( tvrx2_tda18272_freq_map_t(168960000, 0x9D, 0x1F, 1) )
+ ( tvrx2_tda18272_freq_map_t(169984000, 0x9B, 0x1F, 1) )
+ ( tvrx2_tda18272_freq_map_t(171008000, 0x99, 0x1F, 1) )
+ ( tvrx2_tda18272_freq_map_t(172032000, 0x97, 0x1E, 1) )
+ ( tvrx2_tda18272_freq_map_t(173056000, 0x95, 0x1D, 1) )
+ ( tvrx2_tda18272_freq_map_t(174080000, 0x92, 0x1C, 1) )
+ ( tvrx2_tda18272_freq_map_t(175104000, 0x90, 0x1B, 1) )
+ ( tvrx2_tda18272_freq_map_t(176128000, 0x8E, 0x1A, 1) )
+ ( tvrx2_tda18272_freq_map_t(177152000, 0x8C, 0x19, 1) )
+ ( tvrx2_tda18272_freq_map_t(178176000, 0x8A, 0x18, 1) )
+ ( tvrx2_tda18272_freq_map_t(179200000, 0x88, 0x17, 1) )
+ ( tvrx2_tda18272_freq_map_t(180224000, 0x86, 0x17, 1) )
+ ( tvrx2_tda18272_freq_map_t(181248000, 0x84, 0x17, 1) )
+ ( tvrx2_tda18272_freq_map_t(182272000, 0x82, 0x17, 1) )
+ ( tvrx2_tda18272_freq_map_t(183296000, 0x81, 0x17, 1) )
+ ( tvrx2_tda18272_freq_map_t(184320000, 0x7F, 0x17, 1) )
+ ( tvrx2_tda18272_freq_map_t(185344000, 0x7D, 0x16, 1) )
+ ( tvrx2_tda18272_freq_map_t(186368000, 0x7B, 0x15, 1) )
+ ( tvrx2_tda18272_freq_map_t(187392000, 0x7A, 0x14, 1) )
+ ( tvrx2_tda18272_freq_map_t(188416000, 0x78, 0x14, 1) )
+ ( tvrx2_tda18272_freq_map_t(189440000, 0x76, 0x13, 1) )
+ ( tvrx2_tda18272_freq_map_t(190464000, 0x75, 0x13, 1) )
+ ( tvrx2_tda18272_freq_map_t(191488000, 0x73, 0x13, 1) )
+ ( tvrx2_tda18272_freq_map_t(192512000, 0x71, 0x12, 1) )
+ ( tvrx2_tda18272_freq_map_t(192512000, 0x70, 0x11, 1) )
+ ( tvrx2_tda18272_freq_map_t(193536000, 0x6E, 0x11, 1) )
+ ( tvrx2_tda18272_freq_map_t(194560000, 0x6D, 0x10, 1) )
+ ( tvrx2_tda18272_freq_map_t(195584000, 0x6B, 0x10, 1) )
+ ( tvrx2_tda18272_freq_map_t(196608000, 0x6A, 0x0F, 1) )
+ ( tvrx2_tda18272_freq_map_t(197632000, 0x68, 0x0F, 1) )
+ ( tvrx2_tda18272_freq_map_t(198656000, 0x67, 0x0F, 1) )
+ ( tvrx2_tda18272_freq_map_t(199680000, 0x65, 0x0F, 1) )
+ ( tvrx2_tda18272_freq_map_t(200704000, 0x64, 0x0F, 1) )
+ ( tvrx2_tda18272_freq_map_t(201728000, 0x63, 0x0F, 1) )
+ ( tvrx2_tda18272_freq_map_t(202752000, 0x61, 0x0F, 1) )
+ ( tvrx2_tda18272_freq_map_t(203776000, 0x60, 0x0F, 1) )
+ ( tvrx2_tda18272_freq_map_t(204800000, 0x5F, 0x0F, 1) )
+ ( tvrx2_tda18272_freq_map_t(205824000, 0x5D, 0x0F, 1) )
+ ( tvrx2_tda18272_freq_map_t(206848000, 0x5C, 0x0F, 1) )
+ ( tvrx2_tda18272_freq_map_t(207872000, 0x5B, 0x0F, 1) )
+ ( tvrx2_tda18272_freq_map_t(208896000, 0x5A, 0x0F, 1) )
+ ( tvrx2_tda18272_freq_map_t(209920000, 0x58, 0x0F, 1) )
+ ( tvrx2_tda18272_freq_map_t(210944000, 0x57, 0x0F, 1) )
+ ( tvrx2_tda18272_freq_map_t(211968000, 0x56, 0x0F, 1) )
+ ( tvrx2_tda18272_freq_map_t(212992000, 0x55, 0x0F, 1) )
+ ( tvrx2_tda18272_freq_map_t(214016000, 0x54, 0x0F, 1) )
+ ( tvrx2_tda18272_freq_map_t(215040000, 0x53, 0x0F, 1) )
+ ( tvrx2_tda18272_freq_map_t(216064000, 0x52, 0x0F, 1) )
+ ( tvrx2_tda18272_freq_map_t(217088000, 0x50, 0x0F, 1) )
+ ( tvrx2_tda18272_freq_map_t(218112000, 0x4F, 0x0F, 1) )
+ ( tvrx2_tda18272_freq_map_t(219136000, 0x4E, 0x0F, 1) )
+ ( tvrx2_tda18272_freq_map_t(220160000, 0x4D, 0x0E, 1) )
+ ( tvrx2_tda18272_freq_map_t(221184000, 0x4C, 0x0E, 1) )
+ ( tvrx2_tda18272_freq_map_t(222208000, 0x4B, 0x0E, 1) )
+ ( tvrx2_tda18272_freq_map_t(223232000, 0x4A, 0x0E, 1) )
+ ( tvrx2_tda18272_freq_map_t(224256000, 0x49, 0x0D, 1) )
+ ( tvrx2_tda18272_freq_map_t(225280000, 0x48, 0x0D, 1) )
+ ( tvrx2_tda18272_freq_map_t(226304000, 0x47, 0x0D, 1) )
+ ( tvrx2_tda18272_freq_map_t(227328000, 0x46, 0x0D, 1) )
+ ( tvrx2_tda18272_freq_map_t(228352000, 0x45, 0x0C, 1) )
+ ( tvrx2_tda18272_freq_map_t(229376000, 0x44, 0x0C, 1) )
+ ( tvrx2_tda18272_freq_map_t(230400000, 0x43, 0x0C, 1) )
+ ( tvrx2_tda18272_freq_map_t(231424000, 0x42, 0x0C, 1) )
+ ( tvrx2_tda18272_freq_map_t(232448000, 0x42, 0x0B, 1) )
+ ( tvrx2_tda18272_freq_map_t(233472000, 0x41, 0x0B, 1) )
+ ( tvrx2_tda18272_freq_map_t(234496000, 0x40, 0x0B, 1) )
+ ( tvrx2_tda18272_freq_map_t(234496000, 0x3F, 0x0B, 1) )
+ ( tvrx2_tda18272_freq_map_t(235520000, 0x3E, 0x0B, 1) )
+ ( tvrx2_tda18272_freq_map_t(236544000, 0x3D, 0x0B, 1) )
+ ( tvrx2_tda18272_freq_map_t(237568000, 0x3C, 0x0B, 1) )
+ ( tvrx2_tda18272_freq_map_t(239616000, 0x3B, 0x0A, 1) )
+ ( tvrx2_tda18272_freq_map_t(240640000, 0x3A, 0x0A, 1) )
+ ( tvrx2_tda18272_freq_map_t(241664000, 0x39, 0x0A, 1) )
+ ( tvrx2_tda18272_freq_map_t(242688000, 0x38, 0x0A, 1) )
+ ( tvrx2_tda18272_freq_map_t(244736000, 0x37, 0x09, 1) )
+ ( tvrx2_tda18272_freq_map_t(245760000, 0x36, 0x09, 1) )
+ ( tvrx2_tda18272_freq_map_t(246784000, 0x35, 0x09, 1) )
+ ( tvrx2_tda18272_freq_map_t(248832000, 0x34, 0x09, 1) )
+ ( tvrx2_tda18272_freq_map_t(249856000, 0x33, 0x09, 1) )
+ ( tvrx2_tda18272_freq_map_t(250880000, 0x32, 0x08, 1) )
+ ( tvrx2_tda18272_freq_map_t(252928000, 0x31, 0x08, 1) )
+ ( tvrx2_tda18272_freq_map_t(253952000, 0x30, 0x08, 1) )
+ ( tvrx2_tda18272_freq_map_t(256000000, 0x2F, 0x08, 1) )
+ ( tvrx2_tda18272_freq_map_t(257024000, 0x2E, 0x08, 1) )
+ ( tvrx2_tda18272_freq_map_t(259072000, 0x2D, 0x07, 1) )
+ ( tvrx2_tda18272_freq_map_t(260096000, 0x2C, 0x07, 1) )
+ ( tvrx2_tda18272_freq_map_t(262144000, 0x2B, 0x07, 1) )
+ ( tvrx2_tda18272_freq_map_t(264192000, 0x2A, 0x07, 1) )
+ ( tvrx2_tda18272_freq_map_t(265216000, 0x29, 0x07, 1) )
+ ( tvrx2_tda18272_freq_map_t(267264000, 0x28, 0x07, 1) )
+ ( tvrx2_tda18272_freq_map_t(269312000, 0x27, 0x07, 1) )
+ ( tvrx2_tda18272_freq_map_t(270336000, 0x26, 0x07, 1) )
+ ( tvrx2_tda18272_freq_map_t(272384000, 0x25, 0x07, 1) )
+ ( tvrx2_tda18272_freq_map_t(274432000, 0x24, 0x07, 1) )
+ ( tvrx2_tda18272_freq_map_t(276480000, 0x23, 0x07, 1) )
+ ( tvrx2_tda18272_freq_map_t(277504000, 0x22, 0x07, 1) )
+ ( tvrx2_tda18272_freq_map_t(279552000, 0x21, 0x07, 1) )
+ ( tvrx2_tda18272_freq_map_t(281600000, 0x20, 0x07, 1) )
+ ( tvrx2_tda18272_freq_map_t(283648000, 0x1F, 0x07, 1) )
+ ( tvrx2_tda18272_freq_map_t(285696000, 0x1E, 0x0F, 1) )
+ ( tvrx2_tda18272_freq_map_t(287744000, 0x1D, 0x0F, 1) )
+ ( tvrx2_tda18272_freq_map_t(289792000, 0x1C, 0x0E, 1) )
+ ( tvrx2_tda18272_freq_map_t(291840000, 0x1B, 0x0E, 1) )
+ ( tvrx2_tda18272_freq_map_t(293888000, 0x1A, 0x0D, 1) )
+ ( tvrx2_tda18272_freq_map_t(296960000, 0x19, 0x0D, 1) )
+ ( tvrx2_tda18272_freq_map_t(299008000, 0x18, 0x0C, 1) )
+ ( tvrx2_tda18272_freq_map_t(301056000, 0x17, 0x0C, 1) )
+ ( tvrx2_tda18272_freq_map_t(304128000, 0x16, 0x0C, 1) )
+ ( tvrx2_tda18272_freq_map_t(306176000, 0x15, 0x0B, 1) )
+ ( tvrx2_tda18272_freq_map_t(309248000, 0x14, 0x0B, 1) )
+ ( tvrx2_tda18272_freq_map_t(312320000, 0x13, 0x0B, 1) )
+ ( tvrx2_tda18272_freq_map_t(314368000, 0x12, 0x0B, 1) )
+ ( tvrx2_tda18272_freq_map_t(317440000, 0x11, 0x0A, 1) )
+ ( tvrx2_tda18272_freq_map_t(320512000, 0x10, 0x0A, 1) )
+ ( tvrx2_tda18272_freq_map_t(322560000, 0x0F, 0x0A, 1) )
+ ( tvrx2_tda18272_freq_map_t(325632000, 0x0E, 0x09, 1) )
+ ( tvrx2_tda18272_freq_map_t(328704000, 0x0D, 0x09, 1) )
+ ( tvrx2_tda18272_freq_map_t(331776000, 0x0C, 0x08, 1) )
+ ( tvrx2_tda18272_freq_map_t(335872000, 0x0B, 0x08, 1) )
+ ( tvrx2_tda18272_freq_map_t(338944000, 0x0A, 0x08, 1) )
+ ( tvrx2_tda18272_freq_map_t(343040000, 0x09, 0x07, 1) )
+ ( tvrx2_tda18272_freq_map_t(346112000, 0x08, 0x07, 1) )
+ ( tvrx2_tda18272_freq_map_t(350208000, 0x07, 0x07, 1) )
+ ( tvrx2_tda18272_freq_map_t(354304000, 0x06, 0x07, 1) )
+ ( tvrx2_tda18272_freq_map_t(358400000, 0x05, 0x07, 1) )
+ ( tvrx2_tda18272_freq_map_t(362496000, 0x04, 0x07, 1) )
+ ( tvrx2_tda18272_freq_map_t(365568000, 0x04, 0x07, 1) )
+ ( tvrx2_tda18272_freq_map_t(367616000, 0xDA, 0x2A, 2) )
+ ( tvrx2_tda18272_freq_map_t(367616000, 0xD9, 0x27, 2) )
+ ( tvrx2_tda18272_freq_map_t(368640000, 0xD8, 0x27, 2) )
+ ( tvrx2_tda18272_freq_map_t(369664000, 0xD6, 0x27, 2) )
+ ( tvrx2_tda18272_freq_map_t(370688000, 0xD5, 0x27, 2) )
+ ( tvrx2_tda18272_freq_map_t(371712000, 0xD3, 0x25, 2) )
+ ( tvrx2_tda18272_freq_map_t(372736000, 0xD2, 0x23, 2) )
+ ( tvrx2_tda18272_freq_map_t(373760000, 0xD0, 0x23, 2) )
+ ( tvrx2_tda18272_freq_map_t(374784000, 0xCF, 0x21, 2) )
+ ( tvrx2_tda18272_freq_map_t(375808000, 0xCD, 0x1F, 2) )
+ ( tvrx2_tda18272_freq_map_t(376832000, 0xCC, 0x1F, 2) )
+ ( tvrx2_tda18272_freq_map_t(377856000, 0xCA, 0x1F, 2) )
+ ( tvrx2_tda18272_freq_map_t(378880000, 0xC9, 0x1F, 2) )
+ ( tvrx2_tda18272_freq_map_t(379904000, 0xC7, 0x1F, 2) )
+ ( tvrx2_tda18272_freq_map_t(380928000, 0xC6, 0x1F, 2) )
+ ( tvrx2_tda18272_freq_map_t(381952000, 0xC4, 0x1F, 2) )
+ ( tvrx2_tda18272_freq_map_t(382976000, 0xC3, 0x1F, 2) )
+ ( tvrx2_tda18272_freq_map_t(384000000, 0xC1, 0x1F, 2) )
+ ( tvrx2_tda18272_freq_map_t(385024000, 0xC0, 0x1F, 2) )
+ ( tvrx2_tda18272_freq_map_t(386048000, 0xBF, 0x1F, 2) )
+ ( tvrx2_tda18272_freq_map_t(387072000, 0xBD, 0x1F, 2) )
+ ( tvrx2_tda18272_freq_map_t(388096000, 0xBC, 0x1F, 2) )
+ ( tvrx2_tda18272_freq_map_t(389120000, 0xBB, 0x1F, 2) )
+ ( tvrx2_tda18272_freq_map_t(390144000, 0xB9, 0x1F, 2) )
+ ( tvrx2_tda18272_freq_map_t(391168000, 0xB8, 0x1F, 2) )
+ ( tvrx2_tda18272_freq_map_t(392192000, 0xB7, 0x1F, 2) )
+ ( tvrx2_tda18272_freq_map_t(393216000, 0xB5, 0x1F, 2) )
+ ( tvrx2_tda18272_freq_map_t(394240000, 0xB4, 0x1F, 2) )
+ ( tvrx2_tda18272_freq_map_t(395264000, 0xB3, 0x1F, 2) )
+ ( tvrx2_tda18272_freq_map_t(396288000, 0xB1, 0x1F, 2) )
+ ( tvrx2_tda18272_freq_map_t(397312000, 0xB0, 0x1F, 2) )
+ ( tvrx2_tda18272_freq_map_t(398336000, 0xAF, 0x1F, 2) )
+ ( tvrx2_tda18272_freq_map_t(399360000, 0xAD, 0x1F, 2) )
+ ( tvrx2_tda18272_freq_map_t(400384000, 0xAC, 0x1F, 2) )
+ ( tvrx2_tda18272_freq_map_t(401408000, 0xAB, 0x1F, 2) )
+ ( tvrx2_tda18272_freq_map_t(402432000, 0xAA, 0x1F, 2) )
+ ( tvrx2_tda18272_freq_map_t(403456000, 0xA8, 0x1E, 2) )
+ ( tvrx2_tda18272_freq_map_t(404480000, 0xA7, 0x1D, 2) )
+ ( tvrx2_tda18272_freq_map_t(405504000, 0xA6, 0x1D, 2) )
+ ( tvrx2_tda18272_freq_map_t(405504000, 0xA5, 0x1C, 2) )
+ ( tvrx2_tda18272_freq_map_t(406528000, 0xA3, 0x1C, 2) )
+ ( tvrx2_tda18272_freq_map_t(407552000, 0xA2, 0x1B, 2) )
+ ( tvrx2_tda18272_freq_map_t(408576000, 0xA1, 0x1B, 2) )
+ ( tvrx2_tda18272_freq_map_t(409600000, 0xA0, 0x1B, 2) )
+ ( tvrx2_tda18272_freq_map_t(410624000, 0x9F, 0x1A, 2) )
+ ( tvrx2_tda18272_freq_map_t(411648000, 0x9D, 0x1A, 2) )
+ ( tvrx2_tda18272_freq_map_t(412672000, 0x9C, 0x19, 2) )
+ ( tvrx2_tda18272_freq_map_t(413696000, 0x9B, 0x18, 2) )
+ ( tvrx2_tda18272_freq_map_t(414720000, 0x9A, 0x18, 2) )
+ ( tvrx2_tda18272_freq_map_t(415744000, 0x99, 0x17, 2) )
+ ( tvrx2_tda18272_freq_map_t(416768000, 0x98, 0x17, 2) )
+ ( tvrx2_tda18272_freq_map_t(417792000, 0x97, 0x17, 2) )
+ ( tvrx2_tda18272_freq_map_t(418816000, 0x95, 0x17, 2) )
+ ( tvrx2_tda18272_freq_map_t(419840000, 0x94, 0x17, 2) )
+ ( tvrx2_tda18272_freq_map_t(420864000, 0x93, 0x17, 2) )
+ ( tvrx2_tda18272_freq_map_t(421888000, 0x92, 0x17, 2) )
+ ( tvrx2_tda18272_freq_map_t(422912000, 0x91, 0x17, 2) )
+ ( tvrx2_tda18272_freq_map_t(423936000, 0x90, 0x17, 2) )
+ ( tvrx2_tda18272_freq_map_t(424960000, 0x8F, 0x17, 2) )
+ ( tvrx2_tda18272_freq_map_t(425984000, 0x8E, 0x16, 2) )
+ ( tvrx2_tda18272_freq_map_t(427008000, 0x8D, 0x16, 2) )
+ ( tvrx2_tda18272_freq_map_t(428032000, 0x8C, 0x15, 2) )
+ ( tvrx2_tda18272_freq_map_t(429056000, 0x8B, 0x15, 2) )
+ ( tvrx2_tda18272_freq_map_t(430080000, 0x8A, 0x15, 2) )
+ ( tvrx2_tda18272_freq_map_t(431104000, 0x88, 0x14, 2) )
+ ( tvrx2_tda18272_freq_map_t(432128000, 0x87, 0x14, 2) )
+ ( tvrx2_tda18272_freq_map_t(433152000, 0x86, 0x14, 2) )
+ ( tvrx2_tda18272_freq_map_t(434176000, 0x85, 0x13, 2) )
+ ( tvrx2_tda18272_freq_map_t(435200000, 0x84, 0x13, 2) )
+ ( tvrx2_tda18272_freq_map_t(436224000, 0x83, 0x13, 2) )
+ ( tvrx2_tda18272_freq_map_t(437248000, 0x82, 0x13, 2) )
+ ( tvrx2_tda18272_freq_map_t(438272000, 0x81, 0x13, 2) )
+ ( tvrx2_tda18272_freq_map_t(439296000, 0x80, 0x12, 2) )
+ ( tvrx2_tda18272_freq_map_t(440320000, 0x7F, 0x12, 2) )
+ ( tvrx2_tda18272_freq_map_t(441344000, 0x7E, 0x12, 2) )
+ ( tvrx2_tda18272_freq_map_t(442368000, 0x7D, 0x11, 2) )
+ ( tvrx2_tda18272_freq_map_t(444416000, 0x7C, 0x11, 2) )
+ ( tvrx2_tda18272_freq_map_t(445440000, 0x7B, 0x10, 2) )
+ ( tvrx2_tda18272_freq_map_t(446464000, 0x7A, 0x10, 2) )
+ ( tvrx2_tda18272_freq_map_t(447488000, 0x79, 0x10, 2) )
+ ( tvrx2_tda18272_freq_map_t(448512000, 0x78, 0x10, 2) )
+ ( tvrx2_tda18272_freq_map_t(448512000, 0x77, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(449536000, 0x76, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(450560000, 0x75, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(451584000, 0x74, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(452608000, 0x73, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(453632000, 0x72, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(454656000, 0x71, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(455680000, 0x70, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(457728000, 0x6F, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(458752000, 0x6E, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(459776000, 0x6D, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(460800000, 0x6C, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(461824000, 0x6B, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(462848000, 0x6A, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(464896000, 0x69, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(465920000, 0x68, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(466944000, 0x67, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(467968000, 0x66, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(468992000, 0x65, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(471040000, 0x64, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(472064000, 0x63, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(473088000, 0x62, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(474112000, 0x61, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(476160000, 0x60, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(477184000, 0x5F, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(478208000, 0x5E, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(479232000, 0x5D, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(481280000, 0x5C, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(482304000, 0x5B, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(483328000, 0x5A, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(485376000, 0x59, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(486400000, 0x58, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(487424000, 0x57, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(489472000, 0x56, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(490496000, 0x55, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(490496000, 0x54, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(492544000, 0x53, 0x0E, 2) )
+ ( tvrx2_tda18272_freq_map_t(493568000, 0x52, 0x0E, 2) )
+ ( tvrx2_tda18272_freq_map_t(495616000, 0x51, 0x0E, 2) )
+ ( tvrx2_tda18272_freq_map_t(496640000, 0x50, 0x0E, 2) )
+ ( tvrx2_tda18272_freq_map_t(497664000, 0x4F, 0x0E, 2) )
+ ( tvrx2_tda18272_freq_map_t(499712000, 0x4E, 0x0D, 2) )
+ ( tvrx2_tda18272_freq_map_t(500736000, 0x4D, 0x0D, 2) )
+ ( tvrx2_tda18272_freq_map_t(502784000, 0x4C, 0x0D, 2) )
+ ( tvrx2_tda18272_freq_map_t(503808000, 0x4B, 0x0D, 2) )
+ ( tvrx2_tda18272_freq_map_t(505856000, 0x4A, 0x0C, 2) )
+ ( tvrx2_tda18272_freq_map_t(506880000, 0x49, 0x0C, 2) )
+ ( tvrx2_tda18272_freq_map_t(508928000, 0x48, 0x0C, 2) )
+ ( tvrx2_tda18272_freq_map_t(509952000, 0x47, 0x0C, 2) )
+ ( tvrx2_tda18272_freq_map_t(512000000, 0x46, 0x0C, 2) )
+ ( tvrx2_tda18272_freq_map_t(513024000, 0x45, 0x0B, 2) )
+ ( tvrx2_tda18272_freq_map_t(515072000, 0x44, 0x0B, 2) )
+ ( tvrx2_tda18272_freq_map_t(517120000, 0x43, 0x0B, 2) )
+ ( tvrx2_tda18272_freq_map_t(518144000, 0x42, 0x0B, 2) )
+ ( tvrx2_tda18272_freq_map_t(520192000, 0x41, 0x0B, 2) )
+ ( tvrx2_tda18272_freq_map_t(521216000, 0x40, 0x0B, 2) )
+ ( tvrx2_tda18272_freq_map_t(523264000, 0x3F, 0x0B, 2) )
+ ( tvrx2_tda18272_freq_map_t(525312000, 0x3E, 0x0B, 2) )
+ ( tvrx2_tda18272_freq_map_t(526336000, 0x3D, 0x0B, 2) )
+ ( tvrx2_tda18272_freq_map_t(528384000, 0x3C, 0x0A, 2) )
+ ( tvrx2_tda18272_freq_map_t(530432000, 0x3B, 0x0A, 2) )
+ ( tvrx2_tda18272_freq_map_t(531456000, 0x3A, 0x0A, 2) )
+ ( tvrx2_tda18272_freq_map_t(533504000, 0x39, 0x0A, 2) )
+ ( tvrx2_tda18272_freq_map_t(534528000, 0x38, 0x0A, 2) )
+ ( tvrx2_tda18272_freq_map_t(536576000, 0x37, 0x0A, 2) )
+ ( tvrx2_tda18272_freq_map_t(537600000, 0x36, 0x09, 2) )
+ ( tvrx2_tda18272_freq_map_t(539648000, 0x35, 0x09, 2) )
+ ( tvrx2_tda18272_freq_map_t(541696000, 0x34, 0x09, 2) )
+ ( tvrx2_tda18272_freq_map_t(543744000, 0x33, 0x09, 2) )
+ ( tvrx2_tda18272_freq_map_t(544768000, 0x32, 0x09, 2) )
+ ( tvrx2_tda18272_freq_map_t(546816000, 0x31, 0x09, 2) )
+ ( tvrx2_tda18272_freq_map_t(548864000, 0x30, 0x08, 2) )
+ ( tvrx2_tda18272_freq_map_t(550912000, 0x2F, 0x08, 2) )
+ ( tvrx2_tda18272_freq_map_t(552960000, 0x2E, 0x08, 2) )
+ ( tvrx2_tda18272_freq_map_t(555008000, 0x2D, 0x08, 2) )
+ ( tvrx2_tda18272_freq_map_t(557056000, 0x2C, 0x08, 2) )
+ ( tvrx2_tda18272_freq_map_t(559104000, 0x2B, 0x08, 2) )
+ ( tvrx2_tda18272_freq_map_t(561152000, 0x2A, 0x07, 2) )
+ ( tvrx2_tda18272_freq_map_t(563200000, 0x29, 0x07, 2) )
+ ( tvrx2_tda18272_freq_map_t(565248000, 0x28, 0x07, 2) )
+ ( tvrx2_tda18272_freq_map_t(567296000, 0x27, 0x07, 2) )
+ ( tvrx2_tda18272_freq_map_t(569344000, 0x26, 0x07, 2) )
+ ( tvrx2_tda18272_freq_map_t(570368000, 0x26, 0x07, 2) )
+ ( tvrx2_tda18272_freq_map_t(571392000, 0x25, 0x07, 2) )
+ ( tvrx2_tda18272_freq_map_t(573440000, 0x24, 0x07, 2) )
+ ( tvrx2_tda18272_freq_map_t(575488000, 0x23, 0x07, 2) )
+ ( tvrx2_tda18272_freq_map_t(577536000, 0x22, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(578560000, 0x21, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(580608000, 0x20, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(583680000, 0x1F, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(585728000, 0x1E, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(587776000, 0x1D, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(589824000, 0x1C, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(592896000, 0x1B, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(594944000, 0x1A, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(596992000, 0x19, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(600064000, 0x18, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(602112000, 0x17, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(604160000, 0x16, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(607232000, 0x15, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(609280000, 0x14, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(612352000, 0x13, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(615424000, 0x12, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(617472000, 0x11, 0x0F, 2) )
+ ( tvrx2_tda18272_freq_map_t(619520000, 0x10, 0x0E, 2) )
+ ( tvrx2_tda18272_freq_map_t(621568000, 0x0F, 0x0E, 2) )
+ ( tvrx2_tda18272_freq_map_t(623616000, 0x0F, 0x0E, 2) )
+ ( tvrx2_tda18272_freq_map_t(624640000, 0xA3, 0x1F, 3) )
+ ( tvrx2_tda18272_freq_map_t(625664000, 0xA2, 0x1F, 3) )
+ ( tvrx2_tda18272_freq_map_t(626688000, 0xA1, 0x1F, 3) )
+ ( tvrx2_tda18272_freq_map_t(627712000, 0xA0, 0x1F, 3) )
+ ( tvrx2_tda18272_freq_map_t(628736000, 0x9F, 0x1F, 3) )
+ ( tvrx2_tda18272_freq_map_t(630784000, 0x9E, 0x1F, 3) )
+ ( tvrx2_tda18272_freq_map_t(631808000, 0x9D, 0x1F, 3) )
+ ( tvrx2_tda18272_freq_map_t(632832000, 0x9C, 0x1F, 3) )
+ ( tvrx2_tda18272_freq_map_t(633856000, 0x9B, 0x1F, 3) )
+ ( tvrx2_tda18272_freq_map_t(635904000, 0x9A, 0x1F, 3) )
+ ( tvrx2_tda18272_freq_map_t(636928000, 0x99, 0x1F, 3) )
+ ( tvrx2_tda18272_freq_map_t(637952000, 0x98, 0x1F, 3) )
+ ( tvrx2_tda18272_freq_map_t(638976000, 0x97, 0x1F, 3) )
+ ( tvrx2_tda18272_freq_map_t(641024000, 0x96, 0x1E, 3) )
+ ( tvrx2_tda18272_freq_map_t(642048000, 0x95, 0x1E, 3) )
+ ( tvrx2_tda18272_freq_map_t(643072000, 0x94, 0x1E, 3) )
+ ( tvrx2_tda18272_freq_map_t(644096000, 0x93, 0x1D, 3) )
+ ( tvrx2_tda18272_freq_map_t(646144000, 0x92, 0x1D, 3) )
+ ( tvrx2_tda18272_freq_map_t(647168000, 0x91, 0x1C, 3) )
+ ( tvrx2_tda18272_freq_map_t(648192000, 0x90, 0x1C, 3) )
+ ( tvrx2_tda18272_freq_map_t(650240000, 0x8F, 0x1B, 3) )
+ ( tvrx2_tda18272_freq_map_t(651264000, 0x8E, 0x1B, 3) )
+ ( tvrx2_tda18272_freq_map_t(652288000, 0x8D, 0x1B, 3) )
+ ( tvrx2_tda18272_freq_map_t(654336000, 0x8C, 0x1B, 3) )
+ ( tvrx2_tda18272_freq_map_t(655360000, 0x8B, 0x1B, 3) )
+ ( tvrx2_tda18272_freq_map_t(656384000, 0x8A, 0x1B, 3) )
+ ( tvrx2_tda18272_freq_map_t(658432000, 0x89, 0x1A, 3) )
+ ( tvrx2_tda18272_freq_map_t(659456000, 0x88, 0x1A, 3) )
+ ( tvrx2_tda18272_freq_map_t(660480000, 0x87, 0x1A, 3) )
+ ( tvrx2_tda18272_freq_map_t(661504000, 0x86, 0x19, 3) )
+ ( tvrx2_tda18272_freq_map_t(662528000, 0x85, 0x19, 3) )
+ ( tvrx2_tda18272_freq_map_t(664576000, 0x84, 0x18, 3) )
+ ( tvrx2_tda18272_freq_map_t(665600000, 0x83, 0x18, 3) )
+ ( tvrx2_tda18272_freq_map_t(666624000, 0x82, 0x18, 3) )
+ ( tvrx2_tda18272_freq_map_t(668672000, 0x81, 0x18, 3) )
+ ( tvrx2_tda18272_freq_map_t(669696000, 0x80, 0x17, 3) )
+ ( tvrx2_tda18272_freq_map_t(671744000, 0x7F, 0x17, 3) )
+ ( tvrx2_tda18272_freq_map_t(672768000, 0x7E, 0x17, 3) )
+ ( tvrx2_tda18272_freq_map_t(674816000, 0x7D, 0x17, 3) )
+ ( tvrx2_tda18272_freq_map_t(675840000, 0x7C, 0x17, 3) )
+ ( tvrx2_tda18272_freq_map_t(676864000, 0x7B, 0x17, 3) )
+ ( tvrx2_tda18272_freq_map_t(678912000, 0x7A, 0x17, 3) )
+ ( tvrx2_tda18272_freq_map_t(679936000, 0x79, 0x17, 3) )
+ ( tvrx2_tda18272_freq_map_t(681984000, 0x78, 0x17, 3) )
+ ( tvrx2_tda18272_freq_map_t(683008000, 0x77, 0x17, 3) )
+ ( tvrx2_tda18272_freq_map_t(685056000, 0x76, 0x17, 3) )
+ ( tvrx2_tda18272_freq_map_t(686080000, 0x75, 0x17, 3) )
+ ( tvrx2_tda18272_freq_map_t(688128000, 0x74, 0x17, 3) )
+ ( tvrx2_tda18272_freq_map_t(689152000, 0x73, 0x17, 3) )
+ ( tvrx2_tda18272_freq_map_t(691200000, 0x72, 0x16, 3) )
+ ( tvrx2_tda18272_freq_map_t(693248000, 0x71, 0x16, 3) )
+ ( tvrx2_tda18272_freq_map_t(694272000, 0x70, 0x16, 3) )
+ ( tvrx2_tda18272_freq_map_t(696320000, 0x6F, 0x15, 3) )
+ ( tvrx2_tda18272_freq_map_t(697344000, 0x6E, 0x15, 3) )
+ ( tvrx2_tda18272_freq_map_t(699392000, 0x6D, 0x15, 3) )
+ ( tvrx2_tda18272_freq_map_t(700416000, 0x6C, 0x15, 3) )
+ ( tvrx2_tda18272_freq_map_t(702464000, 0x6B, 0x14, 3) )
+ ( tvrx2_tda18272_freq_map_t(704512000, 0x6A, 0x14, 3) )
+ ( tvrx2_tda18272_freq_map_t(704512000, 0x69, 0x14, 3) )
+ ( tvrx2_tda18272_freq_map_t(706560000, 0x68, 0x14, 3) )
+ ( tvrx2_tda18272_freq_map_t(707584000, 0x67, 0x13, 3) )
+ ( tvrx2_tda18272_freq_map_t(709632000, 0x66, 0x13, 3) )
+ ( tvrx2_tda18272_freq_map_t(711680000, 0x65, 0x13, 3) )
+ ( tvrx2_tda18272_freq_map_t(712704000, 0x64, 0x13, 3) )
+ ( tvrx2_tda18272_freq_map_t(714752000, 0x63, 0x13, 3) )
+ ( tvrx2_tda18272_freq_map_t(716800000, 0x62, 0x13, 3) )
+ ( tvrx2_tda18272_freq_map_t(717824000, 0x61, 0x13, 3) )
+ ( tvrx2_tda18272_freq_map_t(719872000, 0x60, 0x13, 3) )
+ ( tvrx2_tda18272_freq_map_t(721920000, 0x5F, 0x12, 3) )
+ ( tvrx2_tda18272_freq_map_t(723968000, 0x5E, 0x12, 3) )
+ ( tvrx2_tda18272_freq_map_t(724992000, 0x5D, 0x12, 3) )
+ ( tvrx2_tda18272_freq_map_t(727040000, 0x5C, 0x12, 3) )
+ ( tvrx2_tda18272_freq_map_t(729088000, 0x5B, 0x11, 3) )
+ ( tvrx2_tda18272_freq_map_t(731136000, 0x5A, 0x11, 3) )
+ ( tvrx2_tda18272_freq_map_t(732160000, 0x59, 0x11, 3) )
+ ( tvrx2_tda18272_freq_map_t(734208000, 0x58, 0x11, 3) )
+ ( tvrx2_tda18272_freq_map_t(736256000, 0x57, 0x10, 3) )
+ ( tvrx2_tda18272_freq_map_t(738304000, 0x56, 0x10, 3) )
+ ( tvrx2_tda18272_freq_map_t(740352000, 0x55, 0x10, 3) )
+ ( tvrx2_tda18272_freq_map_t(741376000, 0x54, 0x10, 3) )
+ ( tvrx2_tda18272_freq_map_t(743424000, 0x53, 0x10, 3) )
+ ( tvrx2_tda18272_freq_map_t(745472000, 0x52, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(746496000, 0x51, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(748544000, 0x50, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(750592000, 0x4F, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(752640000, 0x4E, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(753664000, 0x4D, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(755712000, 0x4C, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(757760000, 0x4B, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(759808000, 0x4A, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(761856000, 0x49, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(762880000, 0x49, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(763904000, 0x48, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(765952000, 0x47, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(768000000, 0x46, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(770048000, 0x45, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(772096000, 0x44, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(774144000, 0x43, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(776192000, 0x42, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(778240000, 0x41, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(780288000, 0x40, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(783360000, 0x3F, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(785408000, 0x3E, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(787456000, 0x3D, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(789504000, 0x3C, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(790528000, 0x3B, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(792576000, 0x3A, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(794624000, 0x39, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(797696000, 0x38, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(799744000, 0x37, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(801792000, 0x36, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(803840000, 0x35, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(806912000, 0x34, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(808960000, 0x33, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(809984000, 0x33, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(811008000, 0x32, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(813056000, 0x31, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(816128000, 0x30, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(818176000, 0x2F, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(820224000, 0x2E, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(823296000, 0x2D, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(825344000, 0x2C, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(828416000, 0x2B, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(830464000, 0x2A, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(832512000, 0x29, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(834560000, 0x28, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(836608000, 0x27, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(839680000, 0x26, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(841728000, 0x25, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(844800000, 0x24, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(847872000, 0x23, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(849920000, 0x22, 0x0F, 3) )
+ ( tvrx2_tda18272_freq_map_t(852992000, 0x21, 0x0E, 3) )
+ ( tvrx2_tda18272_freq_map_t(855040000, 0x20, 0x0E, 3) )
+ ( tvrx2_tda18272_freq_map_t(858112000, 0x1F, 0x0E, 3) )
+ ( tvrx2_tda18272_freq_map_t(861184000, 0x1E, 0x0E, 3) )
+ ( tvrx2_tda18272_freq_map_t(863232000, 0x1D, 0x0E, 3) )
+ ( tvrx2_tda18272_freq_map_t(866304000, 0x1C, 0x0E, 3) )
+ ( tvrx2_tda18272_freq_map_t(900096000, 0x10, 0x0C, 3) )
+ ( tvrx2_tda18272_freq_map_t(929792000, 0x07, 0x0B, 3) )
+ ( tvrx2_tda18272_freq_map_t(969728000, 0x00, 0x0A, 3) )
+;
+
+static const freq_range_t tvrx2_freq_range(42e6, 870e6);
+
+static const freq_range_t tvrx2_bandwidth_range = list_of
+ (range_t(1.7e6))
+ (range_t(6.0e6))
+ (range_t(7.0e6))
+ (range_t(8.0e6))
+ (range_t(10.0e6))
+;
+
+static const uhd::dict<std::string, std::string> tvrx2_sd_name_to_antennas = map_list_of
+ ("RX1", "J100")
+ ("RX2", "J140")
+;
+
+static const uhd::dict<std::string, std::string> tvrx2_sd_name_to_conn = map_list_of
+ ("RX1", "Q")
+ ("RX2", "I")
+;
+
+static const uhd::dict<std::string, boost::uint8_t> tvrx2_sd_name_to_i2c_addr = map_list_of
+ ("RX1", 0x63)
+ ("RX2", 0x60)
+;
+
+static const uhd::dict<std::string, boost::uint8_t> tvrx2_sd_name_to_irq_io = map_list_of
+ ("RX1", (RX1_IRQ))
+ ("RX2", (RX2_IRQ))
+;
+
+static const uhd::dict<std::string, dboard_iface::aux_dac_t> tvrx2_sd_name_to_dac = map_list_of
+ ("RX1", dboard_iface::AUX_DAC_A)
+ ("RX2", dboard_iface::AUX_DAC_B)
+;
+
+static const uhd::dict<std::string, gain_range_t> tvrx2_gain_ranges = map_list_of
+// ("LNA", gain_range_t(-12, 15, 3))
+// ("RF_FILTER", gain_range_t(-11, -2, 3))
+// ("IR_MIXER", gain_range_t(2, 14, 3))
+// ("LPF", gain_range_t(0, 9, 3))
+ ("IF", gain_range_t(0, 30, 0.5))
+;
+
+/***********************************************************************
+ * The TVRX2 dboard class
+ **********************************************************************/
+class tvrx2 : public rx_dboard_base{
+public:
+ tvrx2(ctor_args_t args);
+ ~tvrx2(void);
+
+private:
+ double _freq_scalar;
+ double _lo_freq;
+ double _if_freq;
+ double _bandwidth;
+ uhd::dict<std::string, double> _gains;
+ tda18272hnm_regs_t _tda18272hnm_regs;
+ uhd::dict<boost::uint32_t, tvrx2_tda18272_rfcal_result_t> _rfcal_results;
+ uhd::dict<boost::uint32_t, tvrx2_tda18272_rfcal_coeffs_t> _rfcal_coeffs;
+
+ bool _enabled;
+
+ bool set_enabled(bool);
+
+ double set_lo_freq(double target_freq);
+ double set_gain(double gain, const std::string &name);
+ double set_bandwidth(double bandwidth);
+
+ void set_scaled_rf_freq(double rf_freq);
+ double get_scaled_rf_freq(void);
+ void set_scaled_if_freq(double if_freq);
+ double get_scaled_if_freq(void);
+ void send_reg(boost::uint8_t start_reg, boost::uint8_t stop_reg);
+ void read_reg(boost::uint8_t start_reg, boost::uint8_t stop_reg);
+
+ freq_range_t get_tda18272_rfcal_result_freq_range(boost::uint32_t result);
+ void tvrx2_tda18272_init_rfcal(void);
+ void tvrx2_tda18272_tune_rf_filter(boost::uint32_t uRF);
+ void soft_calibration(void);
+ void transition_0(void);
+ void transition_1(void);
+ void transition_2(int rf_freq);
+ void transition_3(void);
+ void transition_4(int rf_freq);
+ void wait_irq(void);
+ void test_rf_filter_robustness(void);
+
+/***********************************************************************
+ * The TVRX2 class helper functions
+ **********************************************************************/
+ /*!
+ * Is the IRQ set or cleared?
+ * \return true for set
+ */
+ bool get_irq(void){
+ read_reg(0x8, 0x8);
+
+ //return irq status
+ bool irq = _tda18272hnm_regs.irq_status == tda18272hnm_regs_t::IRQ_STATUS_SET;
+
+ UHD_LOGV(often) << boost::format(
+ "TVRX2 (%s): IRQ %d"
+ ) % (get_subdev_name()) % irq << std::endl;
+
+ return irq;
+ }
+
+ /*!
+ * In Power-On Reset State?
+ * Check POR logic for reset state (causes POR to clear)
+ * \return true for reset
+ */
+ bool get_power_reset(void){
+ read_reg(0x5, 0x5);
+
+ //return POR state
+ bool por = _tda18272hnm_regs.por == tda18272hnm_regs_t::POR_RESET;
+
+ UHD_LOGV(often) << boost::format(
+ "TVRX2 (%s): POR %d"
+ ) % (get_subdev_name()) % int(_tda18272hnm_regs.por) << std::endl;
+
+ return por;
+ }
+
+ /*!
+ * Get the lock detect status of the LO.
+ * \return sensor for locked
+ */
+ sensor_value_t get_locked(void){
+ read_reg(0x5, 0x5);
+
+ //return lock detect
+ bool locked = _tda18272hnm_regs.lo_lock == tda18272hnm_regs_t::LO_LOCK_LOCKED;
+
+ UHD_LOGV(often) << boost::format(
+ "TVRX2 (%s): locked %d"
+ ) % (get_subdev_name()) % locked << std::endl;
+
+ return sensor_value_t("LO", locked, "locked", "unlocked");
+ }
+
+ /*!
+ * Read the RSSI from the registers
+ * Read the RSSI from the aux adc
+ * \return the rssi sensor in dB(m?) FIXME
+ */
+ sensor_value_t get_rssi(void){
+ //Launch RSSI calculation with MSM statemachine
+ _tda18272hnm_regs.set_reg(0x19, 0x80); //set MSM_byte_1 for rssi calculation
+ _tda18272hnm_regs.set_reg(0x1A, 0x01); //set MSM_byte_2 for launching rssi calculation
+
+ send_reg(0x19, 0x1A);
+
+ wait_irq();
+
+ //read rssi in dBuV
+ read_reg(0x7, 0x7);
+
+ //calculate the rssi from the voltage
+ double rssi_dBuV = 40.0 + double(((110.0 - 40.0)/128.0) * _tda18272hnm_regs.get_reg(0x7));
+ double rssi = rssi_dBuV - 107.0; //convert to dBm in 50ohm environment ( -108.8 if 75ohm ) FIXME
+
+ return sensor_value_t("RSSI", rssi, "dBm");
+ }
+
+ /*!
+ * Read the Temperature from the registers
+ * \return the temp in degC
+ */
+ sensor_value_t get_temp(void){
+ //Enable Temperature reading
+ _tda18272hnm_regs.tm_on = tda18272hnm_regs_t::TM_ON_SENSOR_ON;
+ send_reg(0x4, 0x4);
+
+ //read temp in degC
+ read_reg(0x3, 0x3);
+
+ UHD_LOGV(often) << boost::format(
+ "TVRX2 (%s): Temperature %f C"
+ ) % (get_subdev_name()) % (double(_tda18272hnm_regs.tm_d)) << std::endl;
+
+ //Disable Temperature reading
+ _tda18272hnm_regs.tm_on = tda18272hnm_regs_t::TM_ON_SENSOR_OFF;
+ send_reg(0x4, 0x4);
+
+ return sensor_value_t("TEMP", double(_tda18272hnm_regs.tm_d), "degC");
+ }
+};
+
+/***********************************************************************
+ * Register the TVRX2 dboard
+ **********************************************************************/
+static dboard_base::sptr make_tvrx2(dboard_base::ctor_args_t args){
+ return dboard_base::sptr(new tvrx2(args));
+}
+
+UHD_STATIC_BLOCK(reg_tvrx2_dboard){
+ //register the factory function for the rx dbid
+ dboard_manager::register_dboard(0x0046, &make_tvrx2, "TVRX2", tvrx2_sd_name_to_conn.keys());
+}
+
+/***********************************************************************
+ * Structors
+ **********************************************************************/
+tvrx2::tvrx2(ctor_args_t args) : rx_dboard_base(args){
+ //FIXME for USRP1, we can only support one TVRX2 installed
+
+ _rfcal_results = map_list_of
+ ( 0, tvrx2_tda18272_rfcal_result_t() )
+ ( 1, tvrx2_tda18272_rfcal_result_t() )
+ ( 2, tvrx2_tda18272_rfcal_result_t() )
+ ( 3, tvrx2_tda18272_rfcal_result_t() )
+ ( 4, tvrx2_tda18272_rfcal_result_t() )
+ ( 5, tvrx2_tda18272_rfcal_result_t() )
+ ( 6, tvrx2_tda18272_rfcal_result_t() )
+ ( 7, tvrx2_tda18272_rfcal_result_t() )
+ ( 8, tvrx2_tda18272_rfcal_result_t() )
+ ( 9, tvrx2_tda18272_rfcal_result_t() )
+ ( 10, tvrx2_tda18272_rfcal_result_t() )
+ ( 11, tvrx2_tda18272_rfcal_result_t() )
+ ;
+
+ _rfcal_coeffs = map_list_of
+ ( 0, tvrx2_tda18272_rfcal_coeffs_t(0) )
+ ( 1, tvrx2_tda18272_rfcal_coeffs_t(1) )
+ ( 2, tvrx2_tda18272_rfcal_coeffs_t(3) )
+ ( 3, tvrx2_tda18272_rfcal_coeffs_t(4) )
+ ( 4, tvrx2_tda18272_rfcal_coeffs_t(6) )
+ ( 5, tvrx2_tda18272_rfcal_coeffs_t(7) )
+ ( 6, tvrx2_tda18272_rfcal_coeffs_t(9) )
+ ( 7, tvrx2_tda18272_rfcal_coeffs_t(10) )
+ ;
+
+ //set defaults for LO, gains, and filter bandwidth
+ _bandwidth = 10e6;
+
+ _if_freq = 12.5e6;
+
+ _enabled = false;
+
+ //send initial register settings
+ //this->read_reg(0x0, 0x43);
+ //this->send_reg(0x0, 0x43);
+
+ //send magic xtal_cal_dac setting
+ send_reg(0x65, 0x65);
+
+ ////////////////////////////////////////////////////////////////////
+ // Register properties
+ ////////////////////////////////////////////////////////////////////
+ this->get_rx_subtree()->create<std::string>("name")
+ .set(get_rx_id().to_pp_string());
+ this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked")
+ .publish(boost::bind(&tvrx2::get_locked, this));
+ this->get_rx_subtree()->create<sensor_value_t>("sensors/rssi")
+ .publish(boost::bind(&tvrx2::get_rssi, this));
+ this->get_rx_subtree()->create<sensor_value_t>("sensors/temperature")
+ .publish(boost::bind(&tvrx2::get_temp, this));
+ BOOST_FOREACH(const std::string &name, tvrx2_gain_ranges.keys()){
+ this->get_rx_subtree()->create<double>("gains/"+name+"/value")
+ .coerce(boost::bind(&tvrx2::set_gain, this, _1, name));
+ this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range")
+ .set(tvrx2_gain_ranges[name]);
+ }
+ this->get_rx_subtree()->create<double>("freq/value")
+ .coerce(boost::bind(&tvrx2::set_lo_freq, this, _1));
+ this->get_rx_subtree()->create<meta_range_t>("freq/range")
+ .set(tvrx2_freq_range);
+ this->get_rx_subtree()->create<std::string>("antenna/value")
+ .set(tvrx2_sd_name_to_antennas[get_subdev_name()]);
+ this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options")
+ .set(list_of(tvrx2_sd_name_to_antennas[get_subdev_name()]));
+ this->get_rx_subtree()->create<std::string>("connection")
+ .set(tvrx2_sd_name_to_conn[get_subdev_name()]);
+ this->get_rx_subtree()->create<bool>("enabled")
+ .coerce(boost::bind(&tvrx2::set_enabled, this, _1))
+ .set(_enabled);
+ this->get_rx_subtree()->create<bool>("use_lo_offset")
+ .set(false);
+ this->get_rx_subtree()->create<double>("bandwidth/value")
+ .coerce(boost::bind(&tvrx2::set_bandwidth, this, _1))
+ .set(_bandwidth);
+ this->get_rx_subtree()->create<meta_range_t>("bandwidth/range")
+ .set(tvrx2_bandwidth_range);
+
+ //set the gpio directions and atr controls (identically)
+ this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, 0); // All unused in atr
+ this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, OUTPUT_MASK); // Set outputs
+
+ //configure ref_clock
+ double ref_clock = this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX);
+
+ if (ref_clock == 64.0e6) {
+ this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, REFCLOCK_DIV4);
+
+ UHD_LOGV(often) << boost::format(
+ "TVRX2 (%s): Dividing Refclock by 4"
+ ) % (get_subdev_name()) << std::endl;
+
+ _freq_scalar = (4*16.0e6)/(this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX));
+ } else if (ref_clock == 100e6) {
+ this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, REFCLOCK_DIV6);
+
+ UHD_LOGV(often) << boost::format(
+ "TVRX2 (%s): Dividing Refclock by 6"
+ ) % (get_subdev_name()) << std::endl;
+
+ _freq_scalar = (6*16.0e6)/this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX);
+ } else {
+ this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, REFCLOCK_DIV6);
+ UHD_MSG(warning) << boost::format("Unsupported ref_clock %0.2f, valid options 64e6 and 100e6") % ref_clock << std::endl;
+ _freq_scalar = 1.0;
+ }
+
+ //enable only the clocks we need
+ this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true);
+
+ UHD_LOGV(often) << boost::format(
+ "TVRX2 (%s): Refclock %f Hz, scalar = %f"
+ ) % (get_subdev_name()) % (this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX)) % _freq_scalar << std::endl;
+
+ _tda18272hnm_regs.irq_polarity = tda18272hnm_regs_t::IRQ_POLARITY_RAISED_VCC;
+ _tda18272hnm_regs.irq_clear = tda18272hnm_regs_t::IRQ_CLEAR_TRUE;
+ send_reg(0x37, 0x37);
+ send_reg(0xA, 0xA);
+
+ send_reg(0x31, 0x31); //N_CP_Current
+ send_reg(0x36, 0x36); //RSSI_Clock
+ send_reg(0x24, 0x25); //AGC1_Do_step
+ send_reg(0x2C, 0x2C); //AGC1_Do_step
+ send_reg(0x2E, 0x2E); //AGC2_Do_step
+ send_reg(0x0E, 0x0E); //AGCs_Up_step_assym
+ send_reg(0x11, 0x11); //AGCs_Do_step_assym
+
+ //intialize i2c
+ //soft_calibration();
+ //tvrx2_tda18272_init_rfcal();
+ transition_0();
+}
+
+bool tvrx2::set_enabled(bool enable){
+ if (enable == _enabled) return _enabled;
+
+ if (enable and not _enabled){
+ //setup tuner parameters
+ transition_1();
+
+ transition_2(int(tvrx2_freq_range.start()));
+
+ test_rf_filter_robustness();
+
+ BOOST_FOREACH(const std::string &name, tvrx2_gain_ranges.keys()){
+ this->get_rx_subtree()->access<double>("gains/"+name+"/value")
+ .set(tvrx2_gain_ranges[name].start());
+ }
+
+ this->get_rx_subtree()->access<double>("bandwidth/value")
+ .set(_bandwidth); // default bandwidth from datasheet
+
+ //transition_2 equivalent
+ this->get_rx_subtree()->access<double>("freq/value")
+ .set(tvrx2_freq_range.start());
+
+ //enter standby mode
+ transition_3();
+ _enabled = true;
+
+ } else {
+ //enter standby mode
+ transition_3();
+ _enabled = false;
+ }
+
+ return _enabled;
+}
+
+tvrx2::~tvrx2(void){
+ UHD_LOGV(often) << boost::format(
+ "TVRX2 (%s): Called Destructor"
+ ) % (get_subdev_name()) << std::endl;
+ UHD_SAFE_CALL(if (_enabled) set_enabled(false);)
+}
+
+
+/***********************************************************************
+ * TDA18272 Register IO Functions
+ **********************************************************************/
+void tvrx2::set_scaled_rf_freq(double rf_freq){
+ _tda18272hnm_regs.set_rf_freq(_freq_scalar*rf_freq/1e3);
+}
+
+double tvrx2::get_scaled_rf_freq(void){
+ return _tda18272hnm_regs.get_rf_freq()*1e3/_freq_scalar;
+}
+
+void tvrx2::set_scaled_if_freq(double if_freq){
+ _tda18272hnm_regs.if_freq = int(_freq_scalar*if_freq/(50e3)); //max 12.8MHz??
+}
+
+double tvrx2::get_scaled_if_freq(void){
+ return _tda18272hnm_regs.if_freq*50e3/_freq_scalar;
+}
+
+void tvrx2::send_reg(boost::uint8_t start_reg, boost::uint8_t stop_reg){
+ start_reg = boost::uint8_t(uhd::clip(int(start_reg), 0x0, 0x43));
+ stop_reg = boost::uint8_t(uhd::clip(int(stop_reg), 0x0, 0x43));
+
+ for(boost::uint8_t start_addr=start_reg; start_addr <= stop_reg; start_addr += sizeof(boost::uint32_t) - 1){
+ int num_bytes = int(stop_reg - start_addr + 1) > int(sizeof(boost::uint32_t)) - 1 ? sizeof(boost::uint32_t) - 1 : stop_reg - start_addr + 1;
+
+ //create buffer for register data (+1 for start address)
+ byte_vector_t regs_vector(num_bytes + 1);
+
+ //first byte is the address of first register
+ regs_vector[0] = start_addr;
+
+ //get the register data
+ for(int i=0; i<num_bytes; i++){
+ regs_vector[1+i] = _tda18272hnm_regs.get_reg(start_addr+i);
+ UHD_LOGV(often) << boost::format(
+ "TVRX2 (%s, 0x%02x): send reg 0x%02x, value 0x%04x, start_addr = 0x%04x, num_bytes %d"
+ ) % (get_subdev_name()) % int(tvrx2_sd_name_to_i2c_addr[get_subdev_name()]) % int(start_addr+i) % int(regs_vector[1+i]) % int(start_addr) % num_bytes << std::endl;
+ }
+
+ //send the data
+ this->get_iface()->write_i2c(
+ tvrx2_sd_name_to_i2c_addr[get_subdev_name()], regs_vector
+ );
+ }
+}
+
+void tvrx2::read_reg(boost::uint8_t start_reg, boost::uint8_t stop_reg){
+ static const boost::uint8_t status_addr = 0x0;
+ start_reg = boost::uint8_t(uhd::clip(int(start_reg), 0x0, 0x43));
+ stop_reg = boost::uint8_t(uhd::clip(int(stop_reg), 0x0, 0x43));
+
+ for(boost::uint8_t start_addr=start_reg; start_addr <= stop_reg; start_addr += sizeof(boost::uint32_t)){
+ int num_bytes = int(stop_reg - start_addr + 1) > int(sizeof(boost::uint32_t)) ? sizeof(boost::uint32_t) : stop_reg - start_addr + 1;
+
+ //create buffer for starting address
+ byte_vector_t start_address_vector(1);
+
+ //first byte is the address of first register
+ start_address_vector[0] = start_addr;
+
+ //send the start address
+ this->get_iface()->write_i2c(
+ tvrx2_sd_name_to_i2c_addr[get_subdev_name()], start_address_vector
+ );
+
+ //create buffer for register data
+ byte_vector_t regs_vector(num_bytes);
+
+ //read from i2c
+ regs_vector = this->get_iface()->read_i2c(
+ tvrx2_sd_name_to_i2c_addr[get_subdev_name()], num_bytes
+ );
+
+ for(boost::uint8_t i=0; i < num_bytes; i++){
+ if (i + start_addr >= status_addr){
+ _tda18272hnm_regs.set_reg(i + start_addr, regs_vector[i]);
+ }
+ UHD_LOGV(often) << boost::format(
+ "TVRX2 (%s, 0x%02x): read reg 0x%02x, value 0x%04x, start_addr = 0x%04x, num_bytes %d"
+ ) % (get_subdev_name()) % int(tvrx2_sd_name_to_i2c_addr[get_subdev_name()]) % int(start_addr+i) % int(regs_vector[i]) % int(start_addr) % num_bytes << std::endl;
+ }
+ }
+}
+
+
+/***********************************************************************
+ * TDA18272 Calibration Functions
+ **********************************************************************/
+freq_range_t tvrx2::get_tda18272_rfcal_result_freq_range(boost::uint32_t result)
+{
+
+ uhd::dict<boost::uint32_t, freq_range_t> result_to_cal_freq_ranges_map = map_list_of
+ ( 0, freq_range_t(
+ (double) tvrx2_tda18272_cal_map[0].cal_freq[_tda18272hnm_regs.rfcal_freq0] * _freq_scalar,
+ (double) tvrx2_tda18272_cal_map[1].cal_freq[_tda18272hnm_regs.rfcal_freq1] * _freq_scalar
+ ) )
+ ( 1, freq_range_t(
+ (double) tvrx2_tda18272_cal_map[1].cal_freq[_tda18272hnm_regs.rfcal_freq1] * _freq_scalar,
+ (double) tvrx2_tda18272_cal_map[2].cal_freq[_tda18272hnm_regs.rfcal_freq2] * _freq_scalar
+ ) )
+ ( 2, freq_range_t(
+ (double) tvrx2_tda18272_cal_map[2].cal_freq[_tda18272hnm_regs.rfcal_freq2] * _freq_scalar,
+ (double) tvrx2_tda18272_cal_map[3].cal_freq[_tda18272hnm_regs.rfcal_freq3] * _freq_scalar
+ ) )
+ ( 3, freq_range_t(
+ (double) tvrx2_tda18272_cal_map[3].cal_freq[_tda18272hnm_regs.rfcal_freq3] * _freq_scalar,
+ (double) tvrx2_tda18272_cal_map[4].cal_freq[_tda18272hnm_regs.rfcal_freq4] * _freq_scalar
+ ) )
+ ( 4, freq_range_t(
+ (double) tvrx2_tda18272_cal_map[4].cal_freq[_tda18272hnm_regs.rfcal_freq4] * _freq_scalar,
+ (double) tvrx2_tda18272_cal_map[5].cal_freq[_tda18272hnm_regs.rfcal_freq5] * _freq_scalar
+ ) )
+ ( 5, freq_range_t(
+ (double) tvrx2_tda18272_cal_map[5].cal_freq[_tda18272hnm_regs.rfcal_freq5] * _freq_scalar,
+ (double) tvrx2_tda18272_cal_map[6].cal_freq[_tda18272hnm_regs.rfcal_freq6] * _freq_scalar
+ ) )
+ ( 6, freq_range_t(
+ (double) tvrx2_tda18272_cal_map[6].cal_freq[_tda18272hnm_regs.rfcal_freq6] * _freq_scalar,
+ (double) tvrx2_tda18272_cal_map[7].cal_freq[_tda18272hnm_regs.rfcal_freq7] * _freq_scalar
+ ) )
+ ( 7, freq_range_t(
+ (double) tvrx2_tda18272_cal_map[7].cal_freq[_tda18272hnm_regs.rfcal_freq7] * _freq_scalar,
+ (double) tvrx2_tda18272_cal_map[8].cal_freq[_tda18272hnm_regs.rfcal_freq8] * _freq_scalar
+ ) )
+ ( 8, freq_range_t(
+ (double) tvrx2_tda18272_cal_map[8].cal_freq[_tda18272hnm_regs.rfcal_freq8] * _freq_scalar,
+ (double) tvrx2_tda18272_cal_map[9].cal_freq[_tda18272hnm_regs.rfcal_freq9] * _freq_scalar
+ ) )
+ ( 9, freq_range_t(
+ (double) tvrx2_tda18272_cal_map[9].cal_freq[_tda18272hnm_regs.rfcal_freq9] * _freq_scalar,
+ (double) tvrx2_tda18272_cal_map[10].cal_freq[_tda18272hnm_regs.rfcal_freq10] * _freq_scalar
+ ) )
+ (10, freq_range_t(
+ (double) tvrx2_tda18272_cal_map[10].cal_freq[_tda18272hnm_regs.rfcal_freq10] * _freq_scalar,
+ (double) tvrx2_tda18272_cal_map[11].cal_freq[_tda18272hnm_regs.rfcal_freq11] * _freq_scalar
+ ) )
+ ;
+
+ if (result < 11)
+ return result_to_cal_freq_ranges_map[result];
+ else
+ return freq_range_t(0.0, 0.0);
+}
+
+
+/*
+ * Initialize the RF Filter calibration maps after hardware init
+ */
+void tvrx2::tvrx2_tda18272_init_rfcal(void)
+{
+
+ /* read byte 0x38-0x43 */
+ read_reg(0x38, 0x43);
+
+ uhd::dict<boost::uint32_t, boost::uint8_t> result_to_cal_regs = map_list_of
+ ( 0, _tda18272hnm_regs.rfcal_log_1)
+ ( 1, _tda18272hnm_regs.rfcal_log_2)
+ ( 2, _tda18272hnm_regs.rfcal_log_3)
+ ( 3, _tda18272hnm_regs.rfcal_log_4)
+ ( 4, _tda18272hnm_regs.rfcal_log_5)
+ ( 5, _tda18272hnm_regs.rfcal_log_6)
+ ( 6, _tda18272hnm_regs.rfcal_log_7)
+ ( 7, _tda18272hnm_regs.rfcal_log_8)
+ ( 8, _tda18272hnm_regs.rfcal_log_9)
+ ( 9, _tda18272hnm_regs.rfcal_log_10)
+ (10, _tda18272hnm_regs.rfcal_log_11)
+ (11, _tda18272hnm_regs.rfcal_log_12)
+ ;
+
+
+ // Loop through rfcal_log_* registers, initialize _rfcal_results
+ BOOST_FOREACH(const boost::uint32_t &result, result_to_cal_regs.keys())
+ _rfcal_results[result].delta_c = result_to_cal_regs[result] > 63 ? result_to_cal_regs[result] - 128 : result_to_cal_regs[result];
+
+ /* read byte 0x26-0x2B */
+ read_reg(0x26, 0x2B);
+
+ // Loop through rfcal_byte_* registers, initialize _rfcal_coeffs
+ BOOST_FOREACH(const boost::uint32_t &subband, _rfcal_coeffs.keys())
+ {
+ freq_range_t subband_freqs;
+
+ boost::uint32_t result = _rfcal_coeffs[subband].cal_number;
+
+ subband_freqs = get_tda18272_rfcal_result_freq_range(result);
+
+ _rfcal_coeffs[subband].RF_B1 = _rfcal_results[result].delta_c + tvrx2_tda18272_cal_map[result].c_offset[_rfcal_results[result].c_offset];
+
+ boost::uint32_t quotient = (((_rfcal_results[result+1].delta_c + tvrx2_tda18272_cal_map[result+1].c_offset[_rfcal_results[result].c_offset])
+ - (_rfcal_results[result].delta_c + tvrx2_tda18272_cal_map[result].c_offset[_rfcal_results[result].c_offset])) * 1000000);
+
+ boost::uint32_t divisor = ((boost::int32_t)(subband_freqs.stop() - subband_freqs.start())/1000);
+
+ _rfcal_coeffs[subband].RF_A1 = quotient / divisor;
+
+ }
+
+}
+
+/*
+ * Apply calibration coefficients to RF Filter tuning
+ */
+void tvrx2::tvrx2_tda18272_tune_rf_filter(boost::uint32_t uRF)
+{
+ boost::uint32_t uCounter = 0;
+ boost::uint8_t cal_result = 0;
+ boost::uint32_t uRFCal0 = 0;
+ boost::uint32_t uRFCal1 = 0;
+ boost::uint8_t subband = 0;
+ boost::int32_t cProg = 0;
+ boost::uint8_t gain_taper = 0;
+ boost::uint8_t RFBand = 0;
+ boost::int32_t RF_A1 = 0;
+ boost::int32_t RF_B1 = 0;
+ freq_range_t subband_freqs;
+
+ /* read byte 0x26-0x2B */
+ read_reg(0x26, 0x2B);
+
+ subband_freqs = get_tda18272_rfcal_result_freq_range(1);
+ uRFCal0 = subband_freqs.start();
+ subband_freqs = get_tda18272_rfcal_result_freq_range(4);
+ uRFCal1 = subband_freqs.start();
+
+ if(uRF < uRFCal0)
+ subband = 0;
+ else if(uRF < 145700000)
+ subband = 1;
+ else if(uRF < uRFCal1)
+ subband = 2;
+ else if(uRF < 367400000)
+ subband = 3;
+ else
+ {
+ subband_freqs = get_tda18272_rfcal_result_freq_range(7);
+ uRFCal0 = subband_freqs.start();
+ subband_freqs = get_tda18272_rfcal_result_freq_range(10);
+ uRFCal1 = subband_freqs.start();
+
+ if(uRF < uRFCal0)
+ subband = 4;
+ else if(uRF < 625000000)
+ subband = 5;
+ else if(uRF < uRFCal1)
+ subband = 6;
+ else
+ subband = 7;
+ }
+
+ cal_result = _rfcal_coeffs[subband].cal_number;
+ subband_freqs = get_tda18272_rfcal_result_freq_range(cal_result);
+ uRFCal0 = subband_freqs.start();
+
+ RF_A1 = _rfcal_coeffs[subband].RF_A1;
+ RF_B1 = _rfcal_coeffs[subband].RF_B1;
+
+ uCounter = 0;
+ do uCounter ++;
+ while (uRF >= tvrx2_tda18272_freq_map[uCounter].rf_max && uCounter < TVRX2_TDA18272_FREQ_MAP_ENTRIES);
+
+ cProg = tvrx2_tda18272_freq_map[uCounter - 1].c_prog;
+ gain_taper = tvrx2_tda18272_freq_map[uCounter - 1].gain_taper;
+ RFBand = tvrx2_tda18272_freq_map[uCounter - 1].rf_band;
+
+ cProg = (boost::int32_t)(cProg + RF_B1 + (RF_A1*((boost::int32_t)(uRF - uRFCal0)/1000))/1000000);
+
+ if(cProg>255) cProg = 255;
+ if(cProg<0) cProg = 0;
+
+ _tda18272hnm_regs.rf_filter_bypass = 1;
+ _tda18272hnm_regs.rf_filter_cap = (boost::uint8_t) cProg;
+ _tda18272hnm_regs.gain_taper = gain_taper;
+ _tda18272hnm_regs.rf_filter_band = RFBand;
+
+ UHD_LOGV(often) << boost::format(
+ "\nTVRX2 (%s): Software Calibration:\n"
+ "\tRF Filter Bypass = %d\n"
+ "\tRF Filter Cap = %d\n"
+ "\tRF Filter Band = %d\n"
+ "\tGain Taper = %d\n")
+ % (get_subdev_name())
+ % int(_tda18272hnm_regs.rf_filter_bypass)
+ % int(_tda18272hnm_regs.rf_filter_cap)
+ % int(_tda18272hnm_regs.rf_filter_band)
+ % int(_tda18272hnm_regs.gain_taper)
+ << std::endl;
+
+ send_reg(0x2c, 0x2f);
+}
+
+void tvrx2::soft_calibration(void){
+ UHD_LOGV(often) << boost::format(
+ "\nTVRX2 (%s): Software Calibration: Initialize Tuner, Calibrate and Standby\n") % (get_subdev_name()) << std::endl;
+
+ _tda18272hnm_regs.sm = tda18272hnm_regs_t::SM_NORMAL;
+ _tda18272hnm_regs.sm_lna = tda18272hnm_regs_t::SM_LNA_ON;
+ _tda18272hnm_regs.sm_pll = tda18272hnm_regs_t::SM_PLL_ON;
+
+ send_reg(0x6, 0x6);
+ read_reg(0x6, 0x6);
+
+ read_reg(0x19, 0x1A);
+ read_reg(0x26, 0x2B);
+
+ _tda18272hnm_regs.rfcal_freq0 = 0x2;
+ _tda18272hnm_regs.rfcal_freq1 = 0x2;
+ _tda18272hnm_regs.rfcal_freq2 = 0x2;
+ _tda18272hnm_regs.rfcal_freq3 = 0x2;
+ _tda18272hnm_regs.rfcal_freq4 = 0x2;
+ _tda18272hnm_regs.rfcal_freq5 = 0x2;
+ _tda18272hnm_regs.rfcal_freq6 = 0x2;
+ _tda18272hnm_regs.rfcal_freq7 = 0x2;
+ _tda18272hnm_regs.rfcal_freq8 = 0x2;
+ _tda18272hnm_regs.rfcal_freq9 = 0x2;
+ _tda18272hnm_regs.rfcal_freq10 = 0x2;
+ _tda18272hnm_regs.rfcal_freq11 = 0x2;
+
+ send_reg(0x26, 0x2B);
+
+ _tda18272hnm_regs.set_reg(0x19, 0x3B); //set MSM_byte_1 for calibration per datasheet
+ _tda18272hnm_regs.set_reg(0x1A, 0x01); //set MSM_byte_2 for launching calibration
+
+ send_reg(0x19, 0x1A);
+
+ wait_irq();
+
+ send_reg(0x1D, 0x1D); //Fmax_LO
+ send_reg(0x0C, 0x0C); //LT_Enable
+ send_reg(0x1B, 0x1B); //PSM_AGC1
+ send_reg(0x0C, 0x0C); //AGC1_6_15dB
+
+ //set spread spectrum for clock
+ //FIXME: NXP turns clock spread on and off
+ // based on where clock spurs would be relative to RF frequency
+ // we should do this also
+ _tda18272hnm_regs.digital_clock = tda18272hnm_regs_t::DIGITAL_CLOCK_SPREAD_OFF;
+ if (get_subdev_name() == "RX1")
+ //_tda18272hnm_regs.xtout = tda18272hnm_regs_t::XTOUT_NO;
+ _tda18272hnm_regs.xtout = tda18272hnm_regs_t::XTOUT_16MHZ;
+ else
+ //_tda18272hnm_regs.xtout = tda18272hnm_regs_t::XTOUT_NO;
+ _tda18272hnm_regs.xtout = tda18272hnm_regs_t::XTOUT_16MHZ;
+
+ send_reg(0x14, 0x14);
+
+ _tda18272hnm_regs.set_reg(0x36, 0x0E); //sets clock mode
+ send_reg(0x36, 0x36);
+
+ //go to standby mode
+ _tda18272hnm_regs.sm = tda18272hnm_regs_t::SM_STANDBY;
+ send_reg(0x6, 0x6);
+}
+
+void tvrx2::test_rf_filter_robustness(void){
+ typedef uhd::dict<std::string, std::string> tvrx2_filter_ratings_t;
+ typedef uhd::dict<std::string, double> tvrx2_filter_margins_t;
+
+ tvrx2_filter_margins_t _filter_margins;
+ tvrx2_filter_ratings_t _filter_ratings;
+
+ read_reg(0x38, 0x43);
+
+ uhd::dict<std::string, boost::uint8_t> filter_cal_regs = map_list_of
+ ("VHFLow_0", 0x38)
+ ("VHFLow_1", 0x3a)
+ ("VHFHigh_0", 0x3b)
+ ("VHFHigh_1", 0x3d)
+ ("UHFLow_0", 0x3e)
+ ("UHFLow_1", 0x40)
+ ("UHFHigh_0", 0x41)
+ ("UHFHigh_1", 0x43)
+ ;
+
+ BOOST_FOREACH(const std::string &name, filter_cal_regs.keys()){
+ boost::uint8_t cal_result = _tda18272hnm_regs.get_reg(filter_cal_regs[name]);
+ if (cal_result & 0x80) {
+ _filter_ratings.set(name, "E");
+ _filter_margins.set(name, 0.0);
+ }
+ else {
+ double partial;
+
+ if (name == "VHFLow_0")
+ partial = 100 * (45 - 39.8225 * (1 + (0.31 * (cal_result < 64 ? cal_result : cal_result - 128)) / 1.0 / 100.0)) / 45.0;
+
+ else if (name == "VHFLow_1")
+ partial = 100 * (152.1828 * (1 + (1.53 * (cal_result < 64 ? cal_result : cal_result - 128)) / 1.0 / 100.0) - (144.896 - 6)) / (144.896 - 6);
+
+ else if (name == "VHFHigh_0")
+ partial = 100 * ((144.896 + 6) - 135.4063 * (1 + (0.27 * (cal_result < 64 ? cal_result : cal_result - 128)) / 1.0 / 100.0)) / (144.896 + 6);
+
+ else if (name == "VHFHigh_1")
+ partial = 100 * (383.1455 * (1 + (0.91 * (cal_result < 64 ? cal_result : cal_result - 128)) / 1.0 / 100.0) - (367.104 - 8)) / (367.104 - 8);
+
+ else if (name == "UHFLow_0")
+ partial = 100 * ((367.104 + 8) - 342.6224 * (1 + (0.21 * (cal_result < 64 ? cal_result : cal_result - 128)) / 1.0 / 100.0)) / (367.104 + 8);
+
+ else if (name == "UHFLow_1")
+ partial = 100 * (662.5595 * (1 + (0.33 * (cal_result < 64 ? cal_result : cal_result - 128)) / 1.0 / 100.0) - (624.128 - 2)) / (624.128 - 2);
+
+ else if (name == "UHFHigh_0")
+ partial = 100 * ((624.128 + 2) - 508.2747 * (1 + (0.23 * (cal_result < 64 ? cal_result : cal_result - 128)) / 1.0 / 100.0)) / (624.128 + 2);
+
+ else if (name == "UHFHigh_1")
+ partial = 100 * (947.8913 * (1 + (0.3 * (cal_result < 64 ? cal_result : cal_result - 128)) / 1.0 / 100.0) - (866 - 14)) / (866 - 14);
+
+ else
+ UHD_THROW_INVALID_CODE_PATH();
+
+ _filter_margins.set(name, 0.0024 * partial * partial * partial - 0.101 * partial * partial + 1.629 * partial + 1.8266);
+ _filter_ratings.set(name, _filter_margins[name] >= 0.0 ? "H" : "L");
+ }
+ }
+
+ std::stringstream robustness_message;
+ robustness_message << boost::format("TVRX2 (%s): RF Filter Robustness Results:") % (get_subdev_name()) << std::endl;
+ BOOST_FOREACH(const std::string &name, uhd::sorted(_filter_ratings.keys())){
+ robustness_message << boost::format("\t%s:\tMargin = %0.2f,\tRobustness = %c") % name % (_filter_margins[name]) % (_filter_ratings[name]) << std::endl;
+ }
+
+ UHD_LOGV(often) << robustness_message.str();
+}
+
+/***********************************************************************
+ * TDA18272 State Functions
+ **********************************************************************/
+void tvrx2::transition_0(void){
+ //Transition 0: Initialize Tuner and place in standby
+ UHD_LOGV(often) << boost::format(
+ "\nTVRX2 (%s): Transistion 0: Initialize Tuner, Calibrate and Standby\n") % (get_subdev_name()) << std::endl;
+
+ //Check for Power-On Reset, if reset, initialze tuner
+ if (get_power_reset()) {
+ _tda18272hnm_regs.sm = tda18272hnm_regs_t::SM_NORMAL;
+ _tda18272hnm_regs.sm_lna = tda18272hnm_regs_t::SM_LNA_ON;
+ _tda18272hnm_regs.sm_pll = tda18272hnm_regs_t::SM_PLL_ON;
+
+ send_reg(0x6, 0x6);
+ read_reg(0x6, 0x6);
+
+ read_reg(0x19, 0x1A);
+
+ _tda18272hnm_regs.set_reg(0x19, 0x3B); //set MSM_byte_1 for calibration per datasheet
+ _tda18272hnm_regs.set_reg(0x1A, 0x01); //set MSM_byte_2 for launching calibration
+
+ send_reg(0x19, 0x1A);
+
+ wait_irq();
+ }
+
+ //send magic xtal_cal_dac setting
+ send_reg(0x65, 0x65);
+
+ send_reg(0x1D, 0x1D); //Fmax_LO
+ send_reg(0x0C, 0x0C); //LT_Enable
+ send_reg(0x1B, 0x1B); //PSM_AGC1
+ send_reg(0x0C, 0x0C); //AGC1_6_15dB
+
+ //set spread spectrum for clock
+ //FIXME: NXP turns clock spread on and off
+ // based on where clock spurs would be relative to RF frequency
+ // we should do this also
+ _tda18272hnm_regs.digital_clock = tda18272hnm_regs_t::DIGITAL_CLOCK_SPREAD_OFF;
+ if (get_subdev_name() == "RX1")
+ //_tda18272hnm_regs.xtout = tda18272hnm_regs_t::XTOUT_NO;
+ _tda18272hnm_regs.xtout = tda18272hnm_regs_t::XTOUT_16MHZ;
+ else
+ //_tda18272hnm_regs.xtout = tda18272hnm_regs_t::XTOUT_NO;
+ _tda18272hnm_regs.xtout = tda18272hnm_regs_t::XTOUT_16MHZ;
+
+ send_reg(0x14, 0x14);
+
+ _tda18272hnm_regs.set_reg(0x36, 0x0E); //sets clock mode
+ send_reg(0x36, 0x36);
+
+ //go to standby mode
+ _tda18272hnm_regs.sm = tda18272hnm_regs_t::SM_STANDBY;
+ send_reg(0x6, 0x6);
+}
+
+void tvrx2::transition_1(void){
+ //Transition 1: Select TV Standard
+ UHD_LOGV(often) << boost::format(
+ "\nTVRX2 (%s): Transistion 1: Select TV Standard\n") % (get_subdev_name()) << std::endl;
+
+ //send magic xtal_cal_dac setting
+ send_reg(0x65, 0x65);
+
+ //Choose IF Byte 1 Setting
+ //_tda18272hnm_regs.if_hp_fc = tda18272hnm_regs_t::IF_HP_FC_0_4MHZ;
+ //_tda18272hnm_regs.if_notch = tda18272hnm_regs_t::IF_NOTCH_OFF;
+ //_tda18272hnm_regs.lp_fc_offset = tda18272hnm_regs_t::LP_FC_OFFSET_0_PERCENT;
+ //_tda18272hnm_regs.lp_fc = tda18272hnm_regs_t::LP_FC_10_0MHZ;
+ //send_reg(0x13, 0x13);
+
+ //Choose IR Mixer Byte 2 Setting
+ //_tda18272hnm_regs.hi_pass = tda18272hnm_regs_t::HI_PASS_DISABLE;
+ //_tda18272hnm_regs.dc_notch = tda18272hnm_regs_t::DC_NOTCH_OFF;
+ send_reg(0x23, 0x23);
+
+ //Set AGC TOP Bytes
+ send_reg(0x0C, 0x13);
+
+ //Set PSM Byt1
+ send_reg(0x1B, 0x1B);
+
+ //Choose IF Frequency, setting is 50KHz steps
+ set_scaled_if_freq(_if_freq);
+ send_reg(0x15, 0x15);
+}
+
+void tvrx2::transition_2(int rf_freq){
+ //Transition 2: Select RF Frequency after changing TV Standard
+ UHD_LOGV(often) << boost::format(
+ "\nTVRX2 (%s): Transistion 2: Select RF Frequency after changing TV Standard\n") % (get_subdev_name()) << std::endl;
+
+ //send magic xtal_cal_dac setting
+ send_reg(0x65, 0x65);
+
+ //Wake up from Standby
+ _tda18272hnm_regs.sm = tda18272hnm_regs_t::SM_NORMAL;
+ _tda18272hnm_regs.sm_lna = tda18272hnm_regs_t::SM_LNA_ON;
+ _tda18272hnm_regs.sm_pll = tda18272hnm_regs_t::SM_PLL_ON;
+
+ send_reg(0x6, 0x6);
+
+ //Set Clock Mode
+ _tda18272hnm_regs.set_reg(0x36, 0x00);
+ send_reg(0x36, 0x36);
+
+ //Set desired RF Frequency
+ set_scaled_rf_freq(rf_freq);
+ send_reg(0x16, 0x18);
+
+ //Lock PLL and tune RF Filters
+ _tda18272hnm_regs.set_reg(0x19, 0x41); //set MSM_byte_1 for RF Filters Tuning, PLL Locking
+ _tda18272hnm_regs.set_reg(0x1A, 0x01); //set MSM_byte_2 for launching calibration
+
+ send_reg(0x19, 0x1A);
+
+ wait_irq();
+
+ tvrx2_tda18272_tune_rf_filter(rf_freq);
+
+ ////LO Lock state in Reg 0x5 LSB
+ //read_reg(0x6, 0x6);
+
+}
+
+void tvrx2::transition_3(void){
+ //Transition 3: Standby Mode
+ UHD_LOGV(often) << boost::format(
+ "\nTVRX2 (%s): Transistion 3: Standby Mode\n") % (get_subdev_name()) << std::endl;
+
+ //send magic xtal_cal_dac setting
+ send_reg(0x65, 0x65);
+
+ //Set clock mode
+ _tda18272hnm_regs.set_reg(0x36, 0x0E);
+ send_reg(0x36, 0x36);
+
+ //go to standby mode
+ _tda18272hnm_regs.sm = tda18272hnm_regs_t::SM_STANDBY;
+ _tda18272hnm_regs.sm_lna = tda18272hnm_regs_t::SM_LNA_OFF;
+ _tda18272hnm_regs.sm_pll = tda18272hnm_regs_t::SM_PLL_OFF;
+ send_reg(0x6, 0x6);
+}
+
+void tvrx2::transition_4(int rf_freq){
+ //Transition 4: Change RF Frequency without changing TV Standard
+ UHD_LOGV(often) << boost::format(
+ "\nTVRX2 (%s): Transistion 4: Change RF Frequency without changing TV Standard\n") % (get_subdev_name()) << std::endl;
+
+ //send magic xtal_cal_dac setting
+ send_reg(0x65, 0x65);
+
+ //Set desired RF Frequency
+ set_scaled_rf_freq(rf_freq);
+ send_reg(0x16, 0x18);
+
+ //Lock PLL and tune RF Filters
+ _tda18272hnm_regs.set_reg(0x19, 0x41); //set MSM_byte_1 for RF Filters Tuning, PLL Locking
+ _tda18272hnm_regs.set_reg(0x1A, 0x01); //set MSM_byte_2 for launching calibration
+
+ send_reg(0x19, 0x1A);
+
+ wait_irq();
+
+ tvrx2_tda18272_tune_rf_filter(rf_freq);
+
+ ////LO Lock state in Reg 0x5 LSB
+ //read_reg(0x5, 0x6);
+
+}
+
+void tvrx2::wait_irq(void){
+ int timeout = 20; //irq waiting timeout in milliseconds
+ //int irq = (this->get_iface()->read_gpio(dboard_iface::UNIT_RX) & int(tvrx2_sd_name_to_irq_io[get_subdev_name()]));
+ bool irq = get_irq();
+ UHD_LOGV(often) << boost::format(
+ "\nTVRX2 (%s): Waiting on IRQ, subdev = %d, mask = 0x%x, Status: 0x%x\n") % (get_subdev_name()) % get_subdev_name() % (int(tvrx2_sd_name_to_irq_io[get_subdev_name()])) % irq << std::endl;
+
+ while (not irq and timeout > 0) {
+ //irq = (this->get_iface()->read_gpio(dboard_iface::UNIT_RX) & tvrx2_sd_name_to_irq_io[get_subdev_name()]);
+ irq = get_irq();
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+ timeout -= 1;
+ }
+
+ UHD_LOGV(often) << boost::format(
+ "\nTVRX2 (%s): IRQ Raised, subdev = %d, mask = 0x%x, Status: 0x%x, Timeout: %d\n") % (get_subdev_name()) % get_subdev_name() % (int(tvrx2_sd_name_to_irq_io[get_subdev_name()])) % irq % timeout << std::endl;
+
+ read_reg(0xA, 0xB);
+ //UHD_ASSERT_THROW(timeout > 0);
+ if(timeout <= 0) UHD_MSG(warning) << boost::format(
+ "\nTVRX2 (%s): Timeout waiting on IRQ\n") % (get_subdev_name()) << std::endl;
+
+ _tda18272hnm_regs.irq_clear = tda18272hnm_regs_t::IRQ_CLEAR_TRUE;
+ send_reg(0xA, 0xA);
+ read_reg(0xA, 0xB);
+
+ irq = (this->get_iface()->read_gpio(dboard_iface::UNIT_RX) & tvrx2_sd_name_to_irq_io[get_subdev_name()]);
+
+ UHD_LOGV(often) << boost::format(
+ "\nTVRX2 (%s): Cleared IRQ, subdev = %d, mask = 0x%x, Status: 0x%x\n") % (get_subdev_name()) % get_subdev_name() % (int(tvrx2_sd_name_to_irq_io[get_subdev_name()])) % irq << std::endl;
+}
+
+
+
+/***********************************************************************
+ * Tuning
+ **********************************************************************/
+double tvrx2::set_lo_freq(double target_freq){
+ //target_freq = std::clip(target_freq, tvrx2_freq_range.min, tvrx2_freq_range.max);
+
+ read_reg(0x6, 0x6);
+
+ if (_tda18272hnm_regs.sm == tda18272hnm_regs_t::SM_STANDBY) {
+ transition_2(int(target_freq + _bandwidth/2 - get_scaled_if_freq()));
+ } else {
+ transition_4(int(target_freq + _bandwidth/2 - get_scaled_if_freq()));
+ }
+ read_reg(0x16, 0x18);
+
+ //compute actual tuned frequency
+ _lo_freq = get_scaled_rf_freq() + get_scaled_if_freq(); // - _bandwidth/2;
+
+ //debug output of calculated variables
+ UHD_LOGV(often) << boost::format(
+ "\nTVRX2 (%s): LO Frequency\n"
+ "\tRequested: \t%f\n"
+ "\tComputed: \t%f\n"
+ "\tReadback: \t%f\n"
+ "\tIF Frequency: \t%f\n") % (get_subdev_name()) % target_freq % double(int(target_freq/1e3)*1e3) % get_scaled_rf_freq() % get_scaled_if_freq() << std::endl;
+
+ get_locked();
+
+ test_rf_filter_robustness();
+
+ UHD_LOGV(often) << boost::format(
+ "\nTVRX2 (%s): RSSI = %f dBm\n"
+ ) % (get_subdev_name()) % (get_rssi().to_real()) << std::endl;
+
+ return _lo_freq;
+}
+
+/***********************************************************************
+ * Gain Handling
+ **********************************************************************/
+/*
+ * Convert the requested gain into a dac voltage
+ */
+static double gain_to_if_gain_dac(double &gain){
+ //clip the input
+ gain = tvrx2_gain_ranges["IF"].clip(gain);
+
+ //voltage level constants
+ static const double max_volts = double(1.7), min_volts = double(0.5);
+ static const double slope = (max_volts-min_volts)/tvrx2_gain_ranges["IF"].stop();
+
+ //calculate the voltage for the aux dac
+ double dac_volts = gain*slope + min_volts;
+
+ UHD_LOGV(often) << boost::format(
+ "TVRX2 IF Gain: %f dB, dac_volts: %f V"
+ ) % gain % dac_volts << std::endl;
+
+ //the actual gain setting
+ gain = (dac_volts - min_volts)/slope;
+
+ return dac_volts;
+}
+
+double tvrx2::set_gain(double gain, const std::string &name){
+ assert_has(tvrx2_gain_ranges.keys(), name, "tvrx2 gain name");
+
+ if (name == "IF"){
+ //write voltage to aux_dac
+ this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, tvrx2_sd_name_to_dac[get_subdev_name()], gain_to_if_gain_dac(gain));
+ }
+ else UHD_THROW_INVALID_CODE_PATH();
+
+ //shadow gain setting
+ _gains[name] = gain;
+
+ return gain;
+}
+
+/***********************************************************************
+ * Bandwidth Handling
+ **********************************************************************/
+static tda18272hnm_regs_t::lp_fc_t bandwidth_to_lp_fc_reg(double &bandwidth){
+ int reg = uhd::clip(boost::math::iround((bandwidth-5.0e6)/1.0e6), 0, 4);
+
+ switch(reg){
+ case 0:
+ bandwidth = 1.7e6;
+ return tda18272hnm_regs_t::LP_FC_1_7MHZ;
+ case 1:
+ bandwidth = 6e6;
+ return tda18272hnm_regs_t::LP_FC_6_0MHZ;
+ case 2:
+ bandwidth = 7e6;
+ return tda18272hnm_regs_t::LP_FC_7_0MHZ;
+ case 3:
+ bandwidth = 8e6;
+ return tda18272hnm_regs_t::LP_FC_8_0MHZ;
+ case 4:
+ bandwidth = 10e6;
+ return tda18272hnm_regs_t::LP_FC_10_0MHZ;
+ }
+ UHD_THROW_INVALID_CODE_PATH();
+}
+
+double tvrx2::set_bandwidth(double bandwidth){
+ //clip the input
+ bandwidth = tvrx2_bandwidth_range.clip(bandwidth);
+
+ //compute low pass cutoff frequency setting
+ _tda18272hnm_regs.lp_fc = bandwidth_to_lp_fc_reg(bandwidth);
+
+ //shadow bandwidth setting
+ _bandwidth = bandwidth;
+
+ //update register
+ send_reg(0x13, 0x13);
+
+ UHD_LOGV(often) << boost::format(
+ "TVRX2 (%s) Bandwidth (lp_fc): %f Hz, reg: %d"
+ ) % (get_subdev_name()) % _bandwidth % (int(_tda18272hnm_regs.lp_fc)) << std::endl;
+
+ return _bandwidth;
+}
diff --git a/host/lib/usrp/dboard/db_unknown.cpp b/host/lib/usrp/dboard/db_unknown.cpp
new file mode 100644
index 000000000..2ed50cd91
--- /dev/null
+++ b/host/lib/usrp/dboard/db_unknown.cpp
@@ -0,0 +1,160 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/types/ranges.hpp>
+#include <uhd/utils/assert_has.hpp>
+#include <uhd/utils/static.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/usrp/dboard_base.hpp>
+#include <uhd/usrp/dboard_manager.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/format.hpp>
+#include <boost/foreach.hpp>
+#include <boost/tuple/tuple.hpp>
+#include <vector>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace boost::assign;
+
+/***********************************************************************
+ * Utility functions
+ **********************************************************************/
+static void warn_if_old_rfx(const dboard_id_t &dboard_id, const std::string &xx){
+ typedef boost::tuple<std::string, dboard_id_t, dboard_id_t> old_ids_t; //name, rx_id, tx_id
+ static const std::vector<old_ids_t> old_rfx_ids = list_of
+ (old_ids_t("Flex 400 Classic", 0x0004, 0x0008))
+ (old_ids_t("Flex 900 Classic", 0x0005, 0x0009))
+ (old_ids_t("Flex 1200 Classic", 0x0006, 0x000a))
+ (old_ids_t("Flex 1800 Classic", 0x0030, 0x0031))
+ (old_ids_t("Flex 2400 Classic", 0x0007, 0x000b))
+ ;
+ BOOST_FOREACH(const old_ids_t &old_id, old_rfx_ids){
+ std::string name; dboard_id_t rx_id, tx_id;
+ boost::tie(name, rx_id, tx_id) = old_id;
+ if (
+ (xx == "RX" and rx_id == dboard_id) or
+ (xx == "TX" and tx_id == dboard_id)
+ ) UHD_MSG(warning) << boost::format(
+ "Detected %s daughterboard %s\n"
+ "This board requires modification to use.\n"
+ "See the daughterboard application notes.\n"
+ ) % xx % name;
+ }
+}
+
+/***********************************************************************
+ * The unknown boards:
+ * Like a basic board, but with only one subdev.
+ **********************************************************************/
+class unknown_rx : public rx_dboard_base{
+public:
+ unknown_rx(ctor_args_t args);
+};
+
+class unknown_tx : public tx_dboard_base{
+public:
+ unknown_tx(ctor_args_t args);
+};
+
+/***********************************************************************
+ * Register the unknown dboards
+ **********************************************************************/
+static dboard_base::sptr make_unknown_rx(dboard_base::ctor_args_t args){
+ return dboard_base::sptr(new unknown_rx(args));
+}
+
+static dboard_base::sptr make_unknown_tx(dboard_base::ctor_args_t args){
+ return dboard_base::sptr(new unknown_tx(args));
+}
+
+UHD_STATIC_BLOCK(reg_unknown_dboards){
+ dboard_manager::register_dboard(0xfff0, &make_unknown_tx, "Unknown TX");
+ dboard_manager::register_dboard(0xfff1, &make_unknown_rx, "Unknown RX");
+}
+
+/***********************************************************************
+ * Unknown RX dboard
+ **********************************************************************/
+unknown_rx::unknown_rx(ctor_args_t args) : rx_dboard_base(args){
+ warn_if_old_rfx(this->get_rx_id(), "RX");
+
+ ////////////////////////////////////////////////////////////////////
+ // Register properties
+ ////////////////////////////////////////////////////////////////////
+ this->get_rx_subtree()->create<std::string>("name").set(
+ std::string(str(boost::format("%s - %s")
+ % get_rx_id().to_pp_string()
+ % get_subdev_name()
+ )));
+ this->get_rx_subtree()->create<int>("gains"); //phony property so this dir exists
+ this->get_rx_subtree()->create<double>("freq/value")
+ .set(double(0.0));
+ this->get_rx_subtree()->create<meta_range_t>("freq/range")
+ .set(freq_range_t(double(0.0), double(0.0)));
+ this->get_rx_subtree()->create<std::string>("antenna/value")
+ .set("");
+ this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options")
+ .set(list_of(""));
+ this->get_rx_subtree()->create<int>("sensors"); //phony property so this dir exists
+ this->get_rx_subtree()->create<std::string>("connection")
+ .set("IQ");
+ this->get_rx_subtree()->create<bool>("enabled")
+ .set(true); //always enabled
+ this->get_rx_subtree()->create<bool>("use_lo_offset")
+ .set(false);
+ this->get_rx_subtree()->create<double>("bandwidth/value")
+ .set(double(0.0));
+ this->get_rx_subtree()->create<meta_range_t>("bandwidth/range")
+ .set(freq_range_t(0.0, 0.0));
+}
+
+/***********************************************************************
+ * Unknown TX dboard
+ **********************************************************************/
+unknown_tx::unknown_tx(ctor_args_t args) : tx_dboard_base(args){
+ warn_if_old_rfx(this->get_tx_id(), "TX");
+
+ ////////////////////////////////////////////////////////////////////
+ // Register properties
+ ////////////////////////////////////////////////////////////////////
+ this->get_tx_subtree()->create<std::string>("name").set(
+ std::string(str(boost::format("%s - %s")
+ % get_tx_id().to_pp_string()
+ % get_subdev_name()
+ )));
+ this->get_tx_subtree()->create<int>("gains"); //phony property so this dir exists
+ this->get_tx_subtree()->create<double>("freq/value")
+ .set(double(0.0));
+ this->get_tx_subtree()->create<meta_range_t>("freq/range")
+ .set(freq_range_t(double(0.0), double(0.0)));
+ this->get_tx_subtree()->create<std::string>("antenna/value")
+ .set("");
+ this->get_tx_subtree()->create<std::vector<std::string> >("antenna/options")
+ .set(list_of(""));
+ this->get_tx_subtree()->create<int>("sensors"); //phony property so this dir exists
+ this->get_tx_subtree()->create<std::string>("connection")
+ .set("IQ");
+ this->get_tx_subtree()->create<bool>("enabled")
+ .set(true); //always enabled
+ this->get_tx_subtree()->create<bool>("use_lo_offset")
+ .set(false);
+ this->get_tx_subtree()->create<double>("bandwidth/value")
+ .set(double(0.0));
+ this->get_tx_subtree()->create<meta_range_t>("bandwidth/range")
+ .set(freq_range_t(0.0, 0.0));
+}
diff --git a/host/lib/usrp/dboard/db_wbx_common.cpp b/host/lib/usrp/dboard/db_wbx_common.cpp
new file mode 100644
index 000000000..58ce03d79
--- /dev/null
+++ b/host/lib/usrp/dboard/db_wbx_common.cpp
@@ -0,0 +1,152 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "db_wbx_common.hpp"
+#include "adf4350_regs.hpp"
+#include <uhd/types/dict.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhd/types/sensors.hpp>
+#include <uhd/utils/assert_has.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <uhd/utils/msg.hpp>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace boost::assign;
+
+
+/***********************************************************************
+ * Gain-related functions
+ **********************************************************************/
+static int rx_pga0_gain_to_iobits(double &gain){
+ //clip the input
+ gain = wbx_rx_gain_ranges["PGA0"].clip(gain);
+
+ //convert to attenuation
+ double attn = wbx_rx_gain_ranges["PGA0"].stop() - gain;
+
+ //calculate the attenuation
+ int attn_code = boost::math::iround(attn*2);
+ int iobits = ((~attn_code) << RX_ATTN_SHIFT) & RX_ATTN_MASK;
+
+ UHD_LOGV(often) << boost::format(
+ "WBX RX Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x"
+ ) % attn % attn_code % (iobits & RX_ATTN_MASK) % RX_ATTN_MASK << std::endl;
+
+ //the actual gain setting
+ gain = wbx_rx_gain_ranges["PGA0"].stop() - double(attn_code)/2;
+
+ return iobits;
+}
+
+
+/***********************************************************************
+ * WBX Common Implementation
+ **********************************************************************/
+wbx_base::wbx_base(ctor_args_t args) : xcvr_dboard_base(args){
+
+ //enable the clocks that we need
+ this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true);
+ this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true);
+
+ ////////////////////////////////////////////////////////////////////
+ // Register RX properties
+ ////////////////////////////////////////////////////////////////////
+ this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked")
+ .publish(boost::bind(&wbx_base::get_locked, this, dboard_iface::UNIT_RX));
+ BOOST_FOREACH(const std::string &name, wbx_rx_gain_ranges.keys()){
+ this->get_rx_subtree()->create<double>("gains/"+name+"/value")
+ .coerce(boost::bind(&wbx_base::set_rx_gain, this, _1, name))
+ .set(wbx_rx_gain_ranges[name].start());
+ this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range")
+ .set(wbx_rx_gain_ranges[name]);
+ }
+ this->get_rx_subtree()->create<std::string>("connection").set("IQ");
+ this->get_rx_subtree()->create<bool>("enabled")
+ .subscribe(boost::bind(&wbx_base::set_rx_enabled, this, _1))
+ .set(true); //start enabled
+ this->get_rx_subtree()->create<bool>("use_lo_offset").set(false);
+ this->get_rx_subtree()->create<double>("bandwidth/value").set(2*20.0e6); //20MHz low-pass, we want complex double-sided
+ this->get_rx_subtree()->create<meta_range_t>("bandwidth/range")
+ .set(freq_range_t(2*20.0e6, 2*20.0e6));
+
+ ////////////////////////////////////////////////////////////////////
+ // Register TX properties
+ ////////////////////////////////////////////////////////////////////
+ this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked")
+ .publish(boost::bind(&wbx_base::get_locked, this, dboard_iface::UNIT_TX));
+ this->get_tx_subtree()->create<std::string>("connection").set("IQ");
+ this->get_tx_subtree()->create<bool>("use_lo_offset").set(false);
+ this->get_tx_subtree()->create<double>("bandwidth/value").set(2*20.0e6); //20MHz low-pass, we want complex double-sided
+ this->get_tx_subtree()->create<meta_range_t>("bandwidth/range")
+ .set(freq_range_t(2*20.0e6, 2*20.0e6));
+
+ // instantiate subclass foo
+ switch(get_rx_id().to_uint16()) {
+ case 0x053:
+ db_actual = wbx_versionx_sptr(new wbx_version2(this));
+ return;
+ case 0x057:
+ db_actual = wbx_versionx_sptr(new wbx_version3(this));
+ return;
+ case 0x063:
+ db_actual = wbx_versionx_sptr(new wbx_version4(this));
+ return;
+ default:
+ /* We didn't recognize the version of the board... */
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+
+}
+
+
+wbx_base::~wbx_base(void){
+ /* NOP */
+}
+
+/***********************************************************************
+ * Enables
+ **********************************************************************/
+void wbx_base::set_rx_enabled(bool enb){
+ this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX,
+ (enb)? RX_POWER_UP : RX_POWER_DOWN, RX_POWER_UP | RX_POWER_DOWN
+ );
+}
+
+/***********************************************************************
+ * Gain Handling
+ **********************************************************************/
+double wbx_base::set_rx_gain(double gain, const std::string &name){
+ assert_has(wbx_rx_gain_ranges.keys(), name, "wbx rx gain name");
+ if(name == "PGA0"){
+ boost::uint16_t io_bits = rx_pga0_gain_to_iobits(gain);
+ _rx_gains[name] = gain;
+
+ //write the new gain to rx gpio outputs
+ this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, io_bits, RX_ATTN_MASK);
+ }
+ else UHD_THROW_INVALID_CODE_PATH();
+ return _rx_gains[name]; //returned shadowed
+}
+
+/***********************************************************************
+ * Tuning
+ **********************************************************************/
+sensor_value_t wbx_base::get_locked(dboard_iface::unit_t unit){
+ const bool locked = (this->get_iface()->read_gpio(unit) & LOCKDET_MASK) != 0;
+ return sensor_value_t("LO", locked, "locked", "unlocked");
+}
diff --git a/host/lib/usrp/dboard/db_wbx_common.hpp b/host/lib/usrp/dboard/db_wbx_common.hpp
new file mode 100644
index 000000000..9e984dce7
--- /dev/null
+++ b/host/lib/usrp/dboard/db_wbx_common.hpp
@@ -0,0 +1,204 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_DBOARD_DB_WBX_COMMON_HPP
+#define INCLUDED_LIBUHD_USRP_DBOARD_DB_WBX_COMMON_HPP
+
+// Common IO Pins
+#define ADF4350_CE (1 << 3)
+#define ADF4350_PDBRF (1 << 2)
+#define ADF4350_MUXOUT (1 << 1) // INPUT!!!
+#define ADF4351_CE (1 << 3)
+#define ADF4351_PDBRF (1 << 2)
+#define ADF4351_MUXOUT (1 << 1) // INPUT!!!
+
+#define LOCKDET_MASK (1 << 0) // INPUT!!!
+
+// TX IO Pins
+#define TX_PUP_5V (1 << 7) // enables 5.0V power supply
+#define TX_PUP_3V (1 << 6) // enables 3.3V supply
+#define TXMOD_EN (1 << 4) // on UNIT_TX, 1 enables TX Modulator
+
+// RX IO Pins
+#define RX_PUP_5V (1 << 7) // enables 5.0V power supply
+#define RX_PUP_3V (1 << 6) // enables 3.3V supply
+#define RXBB_PDB (1 << 4) // on UNIT_RX, 1 powers up RX baseband
+
+// RX Attenuator Pins
+#define RX_ATTN_SHIFT 8 // lsb of RX Attenuator Control
+#define RX_ATTN_MASK (63 << RX_ATTN_SHIFT) // valid bits of RX Attenuator Control
+
+// TX Attenuator Pins (v3 only)
+#define TX_ATTN_16 (1 << 14)
+#define TX_ATTN_8 (1 << 5)
+#define TX_ATTN_4 (1 << 4)
+#define TX_ATTN_2 (1 << 3)
+#define TX_ATTN_1 (1 << 1)
+#define TX_ATTN_MASK (TX_ATTN_16|TX_ATTN_8|TX_ATTN_4|TX_ATTN_2|TX_ATTN_1) // valid bits of TX Attenuator Control
+
+// Mixer functions
+#define TX_MIXER_ENB (TXMOD_EN|ADF4350_PDBRF) // for v3, TXMOD_EN tied to ADF4350_PDBRF rather than separate
+#define TX_MIXER_DIS 0
+
+#define RX_MIXER_ENB (RXBB_PDB|ADF4350_PDBRF)
+#define RX_MIXER_DIS 0
+
+// Power functions
+#define TX_POWER_UP (TX_PUP_5V|TX_PUP_3V) // high enables power supply
+#define TX_POWER_DOWN 0
+
+#define RX_POWER_UP (RX_PUP_5V|RX_PUP_3V|ADF4350_CE) // high enables power supply
+#define RX_POWER_DOWN 0
+
+
+#include <uhd/types/dict.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhd/types/sensors.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/static.hpp>
+#include <uhd/usrp/dboard_base.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/format.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/math/special_functions/round.hpp>
+#include <boost/bind.hpp>
+
+namespace uhd{ namespace usrp{
+
+
+/***********************************************************************
+ * The WBX Common dboard constants
+ **********************************************************************/
+static const uhd::dict<std::string, gain_range_t> wbx_rx_gain_ranges = boost::assign::map_list_of
+ ("PGA0", gain_range_t(0, 31.5, 0.5));
+
+
+/***********************************************************************
+ * The WBX dboard base class
+ **********************************************************************/
+class wbx_base : public xcvr_dboard_base{
+public:
+ wbx_base(ctor_args_t args);
+ virtual ~wbx_base(void);
+
+protected:
+ virtual double set_rx_gain(double gain, const std::string &name);
+
+ virtual void set_rx_enabled(bool enb);
+
+ /*!
+ * Get the lock detect status of the LO.
+ *
+ * This operation is identical for all versions of the WBX board.
+ * \param unit which unit rx or tx
+ * \return true for locked
+ */
+ virtual sensor_value_t get_locked(dboard_iface::unit_t unit);
+
+ /*!
+ * Version-agnostic ABC that wraps version-specific implementations of the
+ * WBX base daughterboard.
+ *
+ * This class is an abstract base class, and thus is impossible to
+ * instantiate.
+ */
+ class wbx_versionx {
+ public:
+ wbx_versionx() {}
+ ~wbx_versionx(void) {}
+
+ virtual double set_tx_gain(double gain, const std::string &name) = 0;
+ virtual void set_tx_enabled(bool enb) = 0;
+ virtual double set_lo_freq(dboard_iface::unit_t unit, double target_freq) = 0;
+
+ /*! This is the registered instance of the wrapper class, wbx_base. */
+ wbx_base *self_base;
+
+ property_tree::sptr get_rx_subtree(void){
+ return self_base->get_rx_subtree();
+ }
+
+ property_tree::sptr get_tx_subtree(void){
+ return self_base->get_tx_subtree();
+ }
+ };
+
+
+ /*!
+ * Version 2 of the WBX Daughterboard
+ *
+ * Basically the original release of the DB.
+ */
+ class wbx_version2 : public wbx_versionx {
+ public:
+ wbx_version2(wbx_base *_self_wbx_base);
+ ~wbx_version2(void);
+
+ double set_tx_gain(double gain, const std::string &name);
+ void set_tx_enabled(bool enb);
+ double set_lo_freq(dboard_iface::unit_t unit, double target_freq);
+ };
+
+ /*!
+ * Version 3 of the WBX Daughterboard
+ *
+ * Fixed a problem with the AGC from Version 2.
+ */
+ class wbx_version3 : public wbx_versionx {
+ public:
+ wbx_version3(wbx_base *_self_wbx_base);
+ ~wbx_version3(void);
+
+ double set_tx_gain(double gain, const std::string &name);
+ void set_tx_enabled(bool enb);
+ double set_lo_freq(dboard_iface::unit_t unit, double target_freq);
+ };
+
+ /*!
+ * Version 4 of the WBX Daughterboard
+ *
+ * Upgrades the Frequnecy Synthensizer from ADF4350 to ADF4351.
+ */
+ class wbx_version4 : public wbx_versionx {
+ public:
+ wbx_version4(wbx_base *_self_wbx_base);
+ ~wbx_version4(void);
+
+ double set_tx_gain(double gain, const std::string &name);
+ void set_tx_enabled(bool enb);
+ double set_lo_freq(dboard_iface::unit_t unit, double target_freq);
+ };
+
+ /*!
+ * Handle to the version-specific implementation of the WBX.
+ *
+ * Since many of this class's functions are dependent on the version of the
+ * WBX board, this class will instantiate an object of the appropriate
+ * wbx_version_* subclass, and invoke any relevant functions through that
+ * object. This pointer is set to the proper object at construction time.
+ */
+ typedef boost::shared_ptr<wbx_versionx> wbx_versionx_sptr;
+ wbx_versionx_sptr db_actual;
+
+ uhd::dict<std::string, double> _tx_gains, _rx_gains;
+ bool _rx_enabled, _tx_enabled;
+};
+
+
+}} //namespace uhd::usrp
+
+#endif /* INCLUDED_LIBUHD_USRP_DBOARD_DB_WBX_COMMON_HPP */
diff --git a/host/lib/usrp/dboard/db_wbx_simple.cpp b/host/lib/usrp/dboard/db_wbx_simple.cpp
new file mode 100644
index 000000000..3d633a672
--- /dev/null
+++ b/host/lib/usrp/dboard/db_wbx_simple.cpp
@@ -0,0 +1,159 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+// Antenna constants
+#define ANTSW_IO ((1 << 15)) // on UNIT_TX, 0 = TX, 1 = RX, on UNIT_RX 0 = main ant, 1 = RX2
+#define ANT_TX 0 //the tx line is transmitting
+#define ANT_RX ANTSW_IO //the tx line is receiving
+#define ANT_TXRX 0 //the rx line is on txrx
+#define ANT_RX2 ANTSW_IO //the rx line in on rx2
+
+#include "db_wbx_common.hpp"
+#include <uhd/utils/static.hpp>
+#include <uhd/utils/assert_has.hpp>
+#include <uhd/usrp/dboard_manager.hpp>
+#include <boost/assign/list_of.hpp>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace boost::assign;
+
+
+/***********************************************************************
+ * The WBX Simple dboard constants
+ **********************************************************************/
+static const std::vector<std::string> wbx_tx_antennas = list_of("TX/RX")("CAL");
+
+static const std::vector<std::string> wbx_rx_antennas = list_of("TX/RX")("RX2")("CAL");
+
+/***********************************************************************
+ * The WBX simple implementation
+ **********************************************************************/
+class wbx_simple : public wbx_base{
+public:
+ wbx_simple(ctor_args_t args);
+ ~wbx_simple(void);
+
+private:
+ void set_rx_ant(const std::string &ant);
+ void set_tx_ant(const std::string &ant);
+ std::string _rx_ant;
+};
+
+/***********************************************************************
+ * Register the WBX simple implementation
+ **********************************************************************/
+static dboard_base::sptr make_wbx_simple(dboard_base::ctor_args_t args){
+ return dboard_base::sptr(new wbx_simple(args));
+}
+
+/***********************************************************************
+ * ID Numbers for WBX daughterboard combinations.
+ **********************************************************************/
+UHD_STATIC_BLOCK(reg_wbx_simple_dboards){
+ dboard_manager::register_dboard(0x0053, 0x0052, &make_wbx_simple, "WBX");
+ dboard_manager::register_dboard(0x0053, 0x004f, &make_wbx_simple, "WBX + Simple GDB");
+ dboard_manager::register_dboard(0x0057, 0x0056, &make_wbx_simple, "WBX v3");
+ dboard_manager::register_dboard(0x0057, 0x004f, &make_wbx_simple, "WBX v3 + Simple GDB");
+ dboard_manager::register_dboard(0x0063, 0x0062, &make_wbx_simple, "WBX v4");
+ dboard_manager::register_dboard(0x0063, 0x004f, &make_wbx_simple, "WBX v4 + Simple GDB");
+}
+
+/***********************************************************************
+ * Structors
+ **********************************************************************/
+wbx_simple::wbx_simple(ctor_args_t args) : wbx_base(args){
+
+ ////////////////////////////////////////////////////////////////////
+ // Register RX properties
+ ////////////////////////////////////////////////////////////////////
+ this->get_rx_subtree()->access<std::string>("name").set(
+ this->get_rx_subtree()->access<std::string>("name").get() + " + Simple GDB");
+ this->get_rx_subtree()->create<std::string>("antenna/value")
+ .subscribe(boost::bind(&wbx_simple::set_rx_ant, this, _1))
+ .set("RX2");
+ this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options")
+ .set(wbx_rx_antennas);
+
+ ////////////////////////////////////////////////////////////////////
+ // Register TX properties
+ ////////////////////////////////////////////////////////////////////
+ this->get_tx_subtree()->access<std::string>("name").set(
+ this->get_tx_subtree()->access<std::string>("name").get() + " + Simple GDB");
+ this->get_tx_subtree()->create<std::string>("antenna/value")
+ .subscribe(boost::bind(&wbx_simple::set_tx_ant, this, _1))
+ .set(wbx_tx_antennas.at(0));
+ this->get_tx_subtree()->create<std::vector<std::string> >("antenna/options")
+ .set(wbx_tx_antennas);
+
+ //set the gpio directions and atr controls (antenna switches all under ATR)
+ this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, ANTSW_IO, ANTSW_IO);
+ this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, ANTSW_IO, ANTSW_IO);
+ this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, ANTSW_IO, ANTSW_IO);
+ this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, ANTSW_IO, ANTSW_IO);
+
+ //setup ATR for the antenna switches (constant)
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE, ANT_RX, ANTSW_IO);
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY, ANT_RX, ANTSW_IO);
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY, ANT_TX, ANTSW_IO);
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, ANT_TX, ANTSW_IO);
+
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, ANT_TXRX, ANTSW_IO);
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, ANT_RX2, ANTSW_IO);
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, ANT_RX2, ANTSW_IO);
+}
+
+wbx_simple::~wbx_simple(void){
+ /* NOP */
+}
+
+/***********************************************************************
+ * Antennas
+ **********************************************************************/
+void wbx_simple::set_rx_ant(const std::string &ant){
+ //validate input
+ assert_has(wbx_rx_antennas, ant, "wbx rx antenna name");
+
+ //shadow the setting
+ _rx_ant = ant;
+
+ //write the new antenna setting to atr regs
+ if (_rx_ant == "CAL") {
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, ANT_TXRX, ANTSW_IO);
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, ANT_TXRX, ANTSW_IO);
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, ANT_TXRX, ANTSW_IO);
+ }
+ else {
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, ANT_RX2, ANTSW_IO);
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, ANT_RX2, ANTSW_IO);
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, ((_rx_ant == "TX/RX")? ANT_TXRX : ANT_RX2), ANTSW_IO);
+ }
+}
+
+void wbx_simple::set_tx_ant(const std::string &ant){
+ assert_has(wbx_tx_antennas, ant, "wbx tx antenna name");
+
+ //write the new antenna setting to atr regs
+ if (ant == "CAL") {
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY, ANT_RX, ANTSW_IO);
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, ANT_RX, ANTSW_IO);
+ }
+ else {
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY, ANT_TX, ANTSW_IO);
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, ANT_TX, ANTSW_IO);
+ }
+}
diff --git a/host/lib/usrp/dboard/db_wbx_version2.cpp b/host/lib/usrp/dboard/db_wbx_version2.cpp
new file mode 100644
index 000000000..ad31339e7
--- /dev/null
+++ b/host/lib/usrp/dboard/db_wbx_version2.cpp
@@ -0,0 +1,329 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "db_wbx_common.hpp"
+#include "adf4350_regs.hpp"
+#include <uhd/utils/log.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhd/types/sensors.hpp>
+#include <uhd/utils/assert_has.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/usrp/dboard_base.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/format.hpp>
+#include <boost/math/special_functions/round.hpp>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace boost::assign;
+
+
+/***********************************************************************
+ * WBX Version 2 Constants
+ **********************************************************************/
+static const uhd::dict<std::string, gain_range_t> wbx_v2_tx_gain_ranges = map_list_of
+ ("PGA0", gain_range_t(0, 25, 0.05))
+;
+
+static const freq_range_t wbx_v2_freq_range(68.75e6, 2.2e9);
+
+/***********************************************************************
+ * Gain-related functions
+ **********************************************************************/
+static double tx_pga0_gain_to_dac_volts(double &gain){
+ //clip the input
+ gain = wbx_v2_tx_gain_ranges["PGA0"].clip(gain);
+
+ //voltage level constants
+ static const double max_volts = 0.5, min_volts = 1.4;
+ static const double slope = (max_volts-min_volts)/wbx_v2_tx_gain_ranges["PGA0"].stop();
+
+ //calculate the voltage for the aux dac
+ double dac_volts = gain*slope + min_volts;
+
+ UHD_LOGV(often) << boost::format(
+ "WBX TX Gain: %f dB, dac_volts: %f V"
+ ) % gain % dac_volts << std::endl;
+
+ //the actual gain setting
+ gain = (dac_volts - min_volts)/slope;
+
+ return dac_volts;
+}
+
+
+/***********************************************************************
+ * WBX Version 2 Implementation
+ **********************************************************************/
+wbx_base::wbx_version2::wbx_version2(wbx_base *_self_wbx_base) {
+ //register our handle on the primary wbx_base instance
+ self_base = _self_wbx_base;
+
+ ////////////////////////////////////////////////////////////////////
+ // Register RX properties
+ ////////////////////////////////////////////////////////////////////
+ this->get_rx_subtree()->create<std::string>("name").set("WBX RX v2");
+ this->get_rx_subtree()->create<double>("freq/value")
+ .coerce(boost::bind(&wbx_base::wbx_version2::set_lo_freq, this, dboard_iface::UNIT_RX, _1))
+ .set((wbx_v2_freq_range.start() + wbx_v2_freq_range.stop())/2.0);
+ this->get_rx_subtree()->create<meta_range_t>("freq/range").set(wbx_v2_freq_range);
+
+ ////////////////////////////////////////////////////////////////////
+ // Register TX properties
+ ////////////////////////////////////////////////////////////////////
+ this->get_tx_subtree()->create<std::string>("name").set("WBX TX v2");
+ BOOST_FOREACH(const std::string &name, wbx_v2_tx_gain_ranges.keys()){
+ self_base->get_tx_subtree()->create<double>("gains/"+name+"/value")
+ .coerce(boost::bind(&wbx_base::wbx_version2::set_tx_gain, this, _1, name))
+ .set(wbx_v2_tx_gain_ranges[name].start());
+ self_base->get_tx_subtree()->create<meta_range_t>("gains/"+name+"/range")
+ .set(wbx_v2_tx_gain_ranges[name]);
+ }
+ this->get_tx_subtree()->create<double>("freq/value")
+ .coerce(boost::bind(&wbx_base::wbx_version2::set_lo_freq, this, dboard_iface::UNIT_TX, _1))
+ .set((wbx_v2_freq_range.start() + wbx_v2_freq_range.stop())/2.0);
+ this->get_tx_subtree()->create<meta_range_t>("freq/range").set(wbx_v2_freq_range);
+ this->get_tx_subtree()->create<bool>("enabled")
+ .subscribe(boost::bind(&wbx_base::wbx_version2::set_tx_enabled, this, _1))
+ .set(true); //start enabled
+
+ //set attenuator control bits
+ int v2_iobits = ADF4350_CE;
+ int v2_tx_mod = TXMOD_EN|ADF4350_PDBRF;
+
+ //set the gpio directions and atr controls
+ self_base->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, v2_tx_mod);
+ self_base->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, RXBB_PDB|ADF4350_PDBRF);
+ self_base->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, TX_PUP_5V|TX_PUP_3V|v2_tx_mod|v2_iobits);
+ self_base->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, RX_PUP_5V|RX_PUP_3V|ADF4350_CE|RXBB_PDB|ADF4350_PDBRF|RX_ATTN_MASK);
+
+ //setup ATR for the mixer enables (always enabled to prevent phase slip between bursts)
+ self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE, v2_tx_mod, TX_MIXER_DIS | v2_tx_mod);
+ self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY, v2_tx_mod, TX_MIXER_DIS | v2_tx_mod);
+ self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY, v2_tx_mod, TX_MIXER_DIS | v2_tx_mod);
+ self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, v2_tx_mod, TX_MIXER_DIS | v2_tx_mod);
+
+ self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB);
+ self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB);
+ self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB);
+ self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB);
+}
+
+wbx_base::wbx_version2::~wbx_version2(void){
+ /* NOP */
+}
+
+/***********************************************************************
+ * Enables
+ **********************************************************************/
+void wbx_base::wbx_version2::set_tx_enabled(bool enb){
+ self_base->get_iface()->set_gpio_out(dboard_iface::UNIT_TX,
+ (enb)? TX_POWER_UP | ADF4350_CE : TX_POWER_DOWN, TX_POWER_UP | TX_POWER_DOWN | ADF4350_CE);
+}
+
+
+/***********************************************************************
+ * Gain Handling
+ **********************************************************************/
+double wbx_base::wbx_version2::set_tx_gain(double gain, const std::string &name){
+ assert_has(wbx_v2_tx_gain_ranges.keys(), name, "wbx tx gain name");
+ if(name == "PGA0"){
+ double dac_volts = tx_pga0_gain_to_dac_volts(gain);
+ self_base->_tx_gains[name] = gain;
+
+ //write the new voltage to the aux dac
+ self_base->get_iface()->write_aux_dac(dboard_iface::UNIT_TX, dboard_iface::AUX_DAC_A, dac_volts);
+ }
+ else UHD_THROW_INVALID_CODE_PATH();
+ return self_base->_tx_gains[name]; //shadowed
+}
+
+
+/***********************************************************************
+ * Tuning
+ **********************************************************************/
+double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double target_freq) {
+ //clip to tuning range
+ target_freq = wbx_v2_freq_range.clip(target_freq);
+
+ UHD_LOGV(often) << boost::format(
+ "WBX tune: target frequency %f Mhz"
+ ) % (target_freq/1e6) << std::endl;
+
+ //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler)
+ static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of
+ (0,23) //adf4350_regs_t::PRESCALER_4_5
+ (1,75) //adf4350_regs_t::PRESCALER_8_9
+ ;
+
+ //map rf divider select output dividers to enums
+ static const uhd::dict<int, adf4350_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of
+ (1, adf4350_regs_t::RF_DIVIDER_SELECT_DIV1)
+ (2, adf4350_regs_t::RF_DIVIDER_SELECT_DIV2)
+ (4, adf4350_regs_t::RF_DIVIDER_SELECT_DIV4)
+ (8, adf4350_regs_t::RF_DIVIDER_SELECT_DIV8)
+ (16, adf4350_regs_t::RF_DIVIDER_SELECT_DIV16)
+ ;
+
+ double actual_freq, pfd_freq;
+ double ref_freq = self_base->get_iface()->get_clock_rate(unit);
+ int R=0, BS=0, N=0, FRAC=0, MOD=0;
+ int RFdiv = 1;
+ adf4350_regs_t::reference_divide_by_2_t T = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED;
+ adf4350_regs_t::reference_doubler_t D = adf4350_regs_t::REFERENCE_DOUBLER_DISABLED;
+
+ //Reference doubler for 50% duty cycle
+ // if ref_freq < 12.5MHz enable regs.reference_divide_by_2
+ if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED;
+
+ //increase RF divider until acceptable VCO frequency
+ //start with target_freq*2 because mixer has divide by 2
+ double vco_freq = target_freq*2;
+ while (vco_freq < 2.2e9) {
+ vco_freq *= 2;
+ RFdiv *= 2;
+ }
+
+ //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler)
+ adf4350_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5;
+
+ /*
+ * The goal here is to loop though possible R dividers,
+ * band select clock dividers, N (int) dividers, and FRAC
+ * (frac) dividers.
+ *
+ * Calculate the N and F dividers for each set of values.
+ * The loop exists when it meets all of the constraints.
+ * The resulting loop values are loaded into the registers.
+ *
+ * from pg.21
+ *
+ * f_pfd = f_ref*(1+D)/(R*(1+T))
+ * f_vco = (N + (FRAC/MOD))*f_pfd
+ * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD
+ * f_rf = f_vco/RFdiv)
+ * f_actual = f_rf/2
+ */
+ for(R = 1; R <= 1023; R+=1){
+ //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T)
+ pfd_freq = ref_freq*(1+D)/(R*(1+T));
+
+ //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth)
+ if (pfd_freq > 25e6) continue;
+
+ //ignore fractional part of tuning
+ N = int(std::floor(vco_freq/pfd_freq));
+
+ //keep N > minimum int divider requirement
+ if (N < prescaler_to_min_int_div[prescaler]) continue;
+
+ for(BS=1; BS <= 255; BS+=1){
+ //keep the band select frequency at or below 100KHz
+ //constraint on band select clock
+ if (pfd_freq/BS > 100e3) continue;
+ goto done_loop;
+ }
+ } done_loop:
+
+ //Fractional-N calculation
+ MOD = 4095; //max fractional accuracy
+ FRAC = int((vco_freq/pfd_freq - N)*MOD);
+
+ //Reference divide-by-2 for 50% duty cycle
+ // if R even, move one divide by 2 to to regs.reference_divide_by_2
+ if(R % 2 == 0){
+ T = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED;
+ R /= 2;
+ }
+
+ //actual frequency calculation
+ actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv/2);
+
+
+ UHD_LOGV(often)
+ << boost::format("WBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl
+
+ << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s"
+ ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl
+ << boost::format("WBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f"
+ ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl;
+
+ //load the register values
+ adf4350_regs_t regs;
+
+ regs.frac_12_bit = FRAC;
+ regs.int_16_bit = N;
+ regs.mod_12_bit = MOD;
+ regs.prescaler = prescaler;
+ regs.r_counter_10_bit = R;
+ regs.reference_divide_by_2 = T;
+ regs.reference_doubler = D;
+ regs.band_select_clock_div = BS;
+ UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv));
+ regs.rf_divider_select = rfdivsel_to_enum[RFdiv];
+
+ if (unit == dboard_iface::UNIT_RX) {
+ freq_range_t rx_lo_5dbm = list_of
+ (range_t(0.05e9, 1.4e9))
+ ;
+
+ freq_range_t rx_lo_2dbm = list_of
+ (range_t(1.4e9, 2.2e9))
+ ;
+
+ if (actual_freq == rx_lo_5dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM;
+
+ if (actual_freq == rx_lo_2dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_2DBM;
+
+ } else if (unit == dboard_iface::UNIT_TX) {
+ freq_range_t tx_lo_5dbm = list_of
+ (range_t(0.05e9, 1.7e9))
+ (range_t(1.9e9, 2.2e9))
+ ;
+
+ freq_range_t tx_lo_m1dbm = list_of
+ (range_t(1.7e9, 1.9e9))
+ ;
+
+ if (actual_freq == tx_lo_5dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM;
+
+ if (actual_freq == tx_lo_m1dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_M1DBM;
+
+ }
+
+ //write the registers
+ //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0)
+ int addr;
+
+ for(addr=5; addr>=0; addr--){
+ UHD_LOGV(often) << boost::format(
+ "WBX SPI Reg (0x%02x): 0x%08x"
+ ) % addr % regs.get_reg(addr) << std::endl;
+ self_base->get_iface()->write_spi(
+ unit, spi_config_t::EDGE_RISE,
+ regs.get_reg(addr), 32
+ );
+ }
+
+ //return the actual frequency
+ UHD_LOGV(often) << boost::format(
+ "WBX tune: actual frequency %f Mhz"
+ ) % (actual_freq/1e6) << std::endl;
+ return actual_freq;
+}
diff --git a/host/lib/usrp/dboard/db_wbx_version3.cpp b/host/lib/usrp/dboard/db_wbx_version3.cpp
new file mode 100644
index 000000000..7ef47edd4
--- /dev/null
+++ b/host/lib/usrp/dboard/db_wbx_version3.cpp
@@ -0,0 +1,361 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "db_wbx_common.hpp"
+#include "adf4350_regs.hpp"
+#include <uhd/utils/log.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhd/types/sensors.hpp>
+#include <uhd/utils/assert_has.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/usrp/dboard_base.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/format.hpp>
+#include <boost/math/special_functions/round.hpp>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace boost::assign;
+
+
+/***********************************************************************
+ * WBX Version 3 Constants
+ **********************************************************************/
+static const uhd::dict<std::string, gain_range_t> wbx_v3_tx_gain_ranges = map_list_of
+ ("PGA0", gain_range_t(0, 31, 1.0))
+;
+
+static const freq_range_t wbx_v3_freq_range(68.75e6, 2.2e9);
+
+/***********************************************************************
+ * Gain-related functions
+ **********************************************************************/
+static int tx_pga0_gain_to_iobits(double &gain){
+ //clip the input
+ gain = wbx_v3_tx_gain_ranges["PGA0"].clip(gain);
+
+ //convert to attenuation
+ double attn = wbx_v3_tx_gain_ranges["PGA0"].stop() - gain;
+
+ //calculate the attenuation
+ int attn_code = boost::math::iround(attn);
+ int iobits = (
+ (attn_code & 16 ? 0 : TX_ATTN_16) |
+ (attn_code & 8 ? 0 : TX_ATTN_8) |
+ (attn_code & 4 ? 0 : TX_ATTN_4) |
+ (attn_code & 2 ? 0 : TX_ATTN_2) |
+ (attn_code & 1 ? 0 : TX_ATTN_1)
+ ) & TX_ATTN_MASK;
+
+ UHD_LOGV(often) << boost::format(
+ "WBX TX Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x"
+ ) % attn % attn_code % (iobits & TX_ATTN_MASK) % TX_ATTN_MASK << std::endl;
+
+ //the actual gain setting
+ gain = wbx_v3_tx_gain_ranges["PGA0"].stop() - double(attn_code);
+
+ return iobits;
+}
+
+
+/***********************************************************************
+ * WBX Common Implementation
+ **********************************************************************/
+wbx_base::wbx_version3::wbx_version3(wbx_base *_self_wbx_base) {
+ //register our handle on the primary wbx_base instance
+ self_base = _self_wbx_base;
+
+ ////////////////////////////////////////////////////////////////////
+ // Register RX properties
+ ////////////////////////////////////////////////////////////////////
+ this->get_rx_subtree()->create<std::string>("name").set("WBX RX v3");
+ this->get_rx_subtree()->create<double>("freq/value")
+ .coerce(boost::bind(&wbx_base::wbx_version3::set_lo_freq, this, dboard_iface::UNIT_RX, _1))
+ .set((wbx_v3_freq_range.start() + wbx_v3_freq_range.stop())/2.0);
+ this->get_rx_subtree()->create<meta_range_t>("freq/range").set(wbx_v3_freq_range);
+
+ ////////////////////////////////////////////////////////////////////
+ // Register TX properties
+ ////////////////////////////////////////////////////////////////////
+ this->get_tx_subtree()->create<std::string>("name").set("WBX TX v3");
+ BOOST_FOREACH(const std::string &name, wbx_v3_tx_gain_ranges.keys()){
+ self_base->get_tx_subtree()->create<double>("gains/"+name+"/value")
+ .coerce(boost::bind(&wbx_base::wbx_version3::set_tx_gain, this, _1, name))
+ .set(wbx_v3_tx_gain_ranges[name].start());
+ self_base->get_tx_subtree()->create<meta_range_t>("gains/"+name+"/range")
+ .set(wbx_v3_tx_gain_ranges[name]);
+ }
+ this->get_tx_subtree()->create<double>("freq/value")
+ .coerce(boost::bind(&wbx_base::wbx_version3::set_lo_freq, this, dboard_iface::UNIT_TX, _1))
+ .set((wbx_v3_freq_range.start() + wbx_v3_freq_range.stop())/2.0);
+ this->get_tx_subtree()->create<meta_range_t>("freq/range").set(wbx_v3_freq_range);
+ this->get_tx_subtree()->create<bool>("enabled")
+ .subscribe(boost::bind(&wbx_base::wbx_version3::set_tx_enabled, this, _1))
+ .set(true); //start enabled
+
+ //set attenuator control bits
+ int v3_iobits = TX_ATTN_MASK;
+ int v3_tx_mod = ADF4350_PDBRF;
+
+ //set the gpio directions and atr controls
+ self_base->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, \
+ v3_tx_mod|v3_iobits);
+ self_base->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, \
+ RXBB_PDB|ADF4350_PDBRF);
+ self_base->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, \
+ TX_PUP_5V|TX_PUP_3V|v3_tx_mod|v3_iobits);
+ self_base->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, \
+ RX_PUP_5V|RX_PUP_3V|ADF4350_CE|RXBB_PDB|ADF4350_PDBRF|RX_ATTN_MASK);
+
+ //setup ATR for the mixer enables (always enabled to prevent phase
+ //slip between bursts). set TX gain iobits to min gain (max attenuation)
+ //when RX_ONLY or IDLE to suppress LO leakage
+ self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \
+ dboard_iface::ATR_REG_IDLE, v3_tx_mod, \
+ TX_ATTN_MASK | TX_MIXER_DIS | v3_tx_mod);
+ self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \
+ dboard_iface::ATR_REG_RX_ONLY, v3_tx_mod, \
+ TX_ATTN_MASK | TX_MIXER_DIS | v3_tx_mod);
+ self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \
+ dboard_iface::ATR_REG_TX_ONLY, v3_tx_mod, \
+ TX_ATTN_MASK | TX_MIXER_DIS | v3_tx_mod);
+ self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \
+ dboard_iface::ATR_REG_FULL_DUPLEX, v3_tx_mod, \
+ TX_ATTN_MASK | TX_MIXER_DIS | v3_tx_mod);
+
+ self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \
+ dboard_iface::ATR_REG_IDLE, \
+ RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB);
+ self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \
+ dboard_iface::ATR_REG_TX_ONLY, \
+ RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB);
+ self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \
+ dboard_iface::ATR_REG_RX_ONLY, \
+ RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB);
+ self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \
+ dboard_iface::ATR_REG_FULL_DUPLEX, \
+ RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB);
+}
+
+wbx_base::wbx_version3::~wbx_version3(void){
+ /* NOP */
+}
+
+
+/***********************************************************************
+ * Enables
+ **********************************************************************/
+void wbx_base::wbx_version3::set_tx_enabled(bool enb){
+ self_base->get_iface()->set_gpio_out(dboard_iface::UNIT_TX,
+ (enb)? TX_POWER_UP | ADF4350_CE : TX_POWER_DOWN, TX_POWER_UP | TX_POWER_DOWN | 0);
+}
+
+
+/***********************************************************************
+ * Gain Handling
+ **********************************************************************/
+double wbx_base::wbx_version3::set_tx_gain(double gain, const std::string &name){
+ assert_has(wbx_v3_tx_gain_ranges.keys(), name, "wbx tx gain name");
+ if(name == "PGA0"){
+ boost::uint16_t io_bits = tx_pga0_gain_to_iobits(gain);
+
+ self_base->_tx_gains[name] = gain;
+
+ //write the new gain to tx gpio outputs
+ //Update ATR with gain io_bits, only update for TX_ONLY and FULL_DUPLEX ATR states
+ self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY, io_bits, TX_ATTN_MASK);
+ self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, io_bits, TX_ATTN_MASK);
+ }
+ else UHD_THROW_INVALID_CODE_PATH();
+ return self_base->_tx_gains[name]; //shadow
+}
+
+
+/***********************************************************************
+ * Tuning
+ **********************************************************************/
+double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double target_freq) {
+ //clip to tuning range
+ target_freq = wbx_v3_freq_range.clip(target_freq);
+
+ UHD_LOGV(often) << boost::format(
+ "WBX tune: target frequency %f Mhz"
+ ) % (target_freq/1e6) << std::endl;
+
+ //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler)
+ static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of
+ (0,23) //adf4350_regs_t::PRESCALER_4_5
+ (1,75) //adf4350_regs_t::PRESCALER_8_9
+ ;
+
+ //map rf divider select output dividers to enums
+ static const uhd::dict<int, adf4350_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of
+ (1, adf4350_regs_t::RF_DIVIDER_SELECT_DIV1)
+ (2, adf4350_regs_t::RF_DIVIDER_SELECT_DIV2)
+ (4, adf4350_regs_t::RF_DIVIDER_SELECT_DIV4)
+ (8, adf4350_regs_t::RF_DIVIDER_SELECT_DIV8)
+ (16, adf4350_regs_t::RF_DIVIDER_SELECT_DIV16)
+ ;
+
+ double actual_freq, pfd_freq;
+ double ref_freq = self_base->get_iface()->get_clock_rate(unit);
+ int R=0, BS=0, N=0, FRAC=0, MOD=0;
+ int RFdiv = 1;
+ adf4350_regs_t::reference_divide_by_2_t T = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED;
+ adf4350_regs_t::reference_doubler_t D = adf4350_regs_t::REFERENCE_DOUBLER_DISABLED;
+
+ //Reference doubler for 50% duty cycle
+ // if ref_freq < 12.5MHz enable regs.reference_divide_by_2
+ if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED;
+
+ //increase RF divider until acceptable VCO frequency
+ //start with target_freq*2 because mixer has divide by 2
+ double vco_freq = target_freq*2;
+ while (vco_freq < 2.2e9) {
+ vco_freq *= 2;
+ RFdiv *= 2;
+ }
+
+ //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler)
+ adf4350_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5;
+
+ /*
+ * The goal here is to loop though possible R dividers,
+ * band select clock dividers, N (int) dividers, and FRAC
+ * (frac) dividers.
+ *
+ * Calculate the N and F dividers for each set of values.
+ * The loop exists when it meets all of the constraints.
+ * The resulting loop values are loaded into the registers.
+ *
+ * from pg.21
+ *
+ * f_pfd = f_ref*(1+D)/(R*(1+T))
+ * f_vco = (N + (FRAC/MOD))*f_pfd
+ * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD
+ * f_rf = f_vco/RFdiv)
+ * f_actual = f_rf/2
+ */
+ for(R = 1; R <= 1023; R+=1){
+ //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T)
+ pfd_freq = ref_freq*(1+D)/(R*(1+T));
+
+ //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth)
+ if (pfd_freq > 25e6) continue;
+
+ //ignore fractional part of tuning
+ N = int(std::floor(vco_freq/pfd_freq));
+
+ //keep N > minimum int divider requirement
+ if (N < prescaler_to_min_int_div[prescaler]) continue;
+
+ for(BS=1; BS <= 255; BS+=1){
+ //keep the band select frequency at or below 100KHz
+ //constraint on band select clock
+ if (pfd_freq/BS > 100e3) continue;
+ goto done_loop;
+ }
+ } done_loop:
+
+ //Fractional-N calculation
+ MOD = 4095; //max fractional accuracy
+ FRAC = int((vco_freq/pfd_freq - N)*MOD);
+
+ //Reference divide-by-2 for 50% duty cycle
+ // if R even, move one divide by 2 to to regs.reference_divide_by_2
+ if(R % 2 == 0){
+ T = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED;
+ R /= 2;
+ }
+
+ //actual frequency calculation
+ actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv/2);
+
+
+ UHD_LOGV(often)
+ << boost::format("WBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl
+
+ << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s"
+ ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl
+ << boost::format("WBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f"
+ ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl;
+
+ //load the register values
+ adf4350_regs_t regs;
+
+ regs.frac_12_bit = FRAC;
+ regs.int_16_bit = N;
+ regs.mod_12_bit = MOD;
+ regs.prescaler = prescaler;
+ regs.r_counter_10_bit = R;
+ regs.reference_divide_by_2 = T;
+ regs.reference_doubler = D;
+ regs.band_select_clock_div = BS;
+ UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv));
+ regs.rf_divider_select = rfdivsel_to_enum[RFdiv];
+
+ if (unit == dboard_iface::UNIT_RX) {
+ freq_range_t rx_lo_5dbm = list_of
+ (range_t(0.05e9, 1.4e9))
+ ;
+
+ freq_range_t rx_lo_2dbm = list_of
+ (range_t(1.4e9, 2.2e9))
+ ;
+
+ if (actual_freq == rx_lo_5dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM;
+
+ if (actual_freq == rx_lo_2dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_2DBM;
+
+ } else if (unit == dboard_iface::UNIT_TX) {
+ freq_range_t tx_lo_5dbm = list_of
+ (range_t(0.05e9, 1.7e9))
+ (range_t(1.9e9, 2.2e9))
+ ;
+
+ freq_range_t tx_lo_m1dbm = list_of
+ (range_t(1.7e9, 1.9e9))
+ ;
+
+ if (actual_freq == tx_lo_5dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM;
+
+ if (actual_freq == tx_lo_m1dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_M1DBM;
+
+ }
+
+ //write the registers
+ //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0)
+ int addr;
+
+ for(addr=5; addr>=0; addr--){
+ UHD_LOGV(often) << boost::format(
+ "WBX SPI Reg (0x%02x): 0x%08x"
+ ) % addr % regs.get_reg(addr) << std::endl;
+ self_base->get_iface()->write_spi(
+ unit, spi_config_t::EDGE_RISE,
+ regs.get_reg(addr), 32
+ );
+ }
+
+ //return the actual frequency
+ UHD_LOGV(often) << boost::format(
+ "WBX tune: actual frequency %f Mhz"
+ ) % (actual_freq/1e6) << std::endl;
+ return actual_freq;
+}
diff --git a/host/lib/usrp/dboard/db_wbx_version4.cpp b/host/lib/usrp/dboard/db_wbx_version4.cpp
new file mode 100644
index 000000000..3a85826cd
--- /dev/null
+++ b/host/lib/usrp/dboard/db_wbx_version4.cpp
@@ -0,0 +1,344 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "db_wbx_common.hpp"
+#include "adf4351_regs.hpp"
+#include <uhd/utils/log.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhd/types/sensors.hpp>
+#include <uhd/utils/assert_has.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/usrp/dboard_base.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/format.hpp>
+#include <boost/math/special_functions/round.hpp>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace boost::assign;
+
+
+/***********************************************************************
+ * WBX Version 3 Constants
+ **********************************************************************/
+static const uhd::dict<std::string, gain_range_t> wbx_v4_tx_gain_ranges = map_list_of
+ ("PGA0", gain_range_t(0, 31, 1.0))
+;
+
+static const freq_range_t wbx_v4_freq_range(50.0e6, 2.2e9);
+
+
+/***********************************************************************
+ * Gain-related functions
+ **********************************************************************/
+static int tx_pga0_gain_to_iobits(double &gain){
+ //clip the input
+ gain = wbx_v4_tx_gain_ranges["PGA0"].clip(gain);
+
+ //convert to attenuation
+ double attn = wbx_v4_tx_gain_ranges["PGA0"].stop() - gain;
+
+ //calculate the attenuation
+ int attn_code = boost::math::iround(attn);
+ int iobits = (
+ (attn_code & 16 ? 0 : TX_ATTN_16) |
+ (attn_code & 8 ? 0 : TX_ATTN_8) |
+ (attn_code & 4 ? 0 : TX_ATTN_4) |
+ (attn_code & 2 ? 0 : TX_ATTN_2) |
+ (attn_code & 1 ? 0 : TX_ATTN_1)
+ ) & TX_ATTN_MASK;
+
+ UHD_LOGV(often) << boost::format(
+ "WBX TX Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x"
+ ) % attn % attn_code % (iobits & TX_ATTN_MASK) % TX_ATTN_MASK << std::endl;
+
+ //the actual gain setting
+ gain = wbx_v4_tx_gain_ranges["PGA0"].stop() - double(attn_code);
+
+ return iobits;
+}
+
+
+/***********************************************************************
+ * WBX Common Implementation
+ **********************************************************************/
+wbx_base::wbx_version4::wbx_version4(wbx_base *_self_wbx_base) {
+ //register our handle on the primary wbx_base instance
+ self_base = _self_wbx_base;
+
+ ////////////////////////////////////////////////////////////////////
+ // Register RX properties
+ ////////////////////////////////////////////////////////////////////
+ this->get_rx_subtree()->create<std::string>("name").set("WBX RX v4");
+ this->get_rx_subtree()->create<double>("freq/value")
+ .coerce(boost::bind(&wbx_base::wbx_version4::set_lo_freq, this, dboard_iface::UNIT_RX, _1))
+ .set((wbx_v4_freq_range.start() + wbx_v4_freq_range.stop())/2.0);
+ this->get_rx_subtree()->create<meta_range_t>("freq/range").set(wbx_v4_freq_range);
+
+ ////////////////////////////////////////////////////////////////////
+ // Register TX properties
+ ////////////////////////////////////////////////////////////////////
+ this->get_tx_subtree()->create<std::string>("name").set("WBX TX v4");
+ BOOST_FOREACH(const std::string &name, wbx_v4_tx_gain_ranges.keys()){
+ self_base->get_tx_subtree()->create<double>("gains/"+name+"/value")
+ .coerce(boost::bind(&wbx_base::wbx_version4::set_tx_gain, this, _1, name))
+ .set(wbx_v4_tx_gain_ranges[name].start());
+ self_base->get_tx_subtree()->create<meta_range_t>("gains/"+name+"/range")
+ .set(wbx_v4_tx_gain_ranges[name]);
+ }
+ this->get_tx_subtree()->create<double>("freq/value")
+ .coerce(boost::bind(&wbx_base::wbx_version4::set_lo_freq, this, dboard_iface::UNIT_TX, _1))
+ .set((wbx_v4_freq_range.start() + wbx_v4_freq_range.stop())/2.0);
+ this->get_tx_subtree()->create<meta_range_t>("freq/range").set(wbx_v4_freq_range);
+ this->get_tx_subtree()->create<bool>("enabled")
+ .subscribe(boost::bind(&wbx_base::wbx_version4::set_tx_enabled, this, _1))
+ .set(true); //start enabled
+
+ //set attenuator control bits
+ int v4_iobits = TX_ATTN_MASK;
+ int v4_tx_mod = ADF4351_PDBRF;
+
+ //set the gpio directions and atr controls
+ self_base->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, v4_tx_mod|v4_iobits);
+ self_base->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, RXBB_PDB|ADF4351_PDBRF);
+ self_base->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, TX_PUP_5V|TX_PUP_3V|v4_tx_mod|v4_iobits);
+ self_base->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, RX_PUP_5V|RX_PUP_3V|ADF4351_CE|RXBB_PDB|ADF4351_PDBRF|RX_ATTN_MASK);
+
+ //setup ATR for the mixer enables (always enabled to prevent phase slip between bursts)
+ //set TX gain iobits to min gain (max attenuation) when RX_ONLY or IDLE to suppress LO leakage
+ self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE, v4_tx_mod, TX_ATTN_MASK | TX_MIXER_DIS | v4_tx_mod);
+ self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY, v4_tx_mod, TX_ATTN_MASK | TX_MIXER_DIS | v4_tx_mod);
+ self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY, v4_tx_mod, TX_ATTN_MASK | TX_MIXER_DIS | v4_tx_mod);
+ self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, v4_tx_mod, TX_ATTN_MASK | TX_MIXER_DIS | v4_tx_mod);
+
+ self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB);
+ self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB);
+ self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB);
+ self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB);
+}
+
+wbx_base::wbx_version4::~wbx_version4(void){
+ /* NOP */
+}
+
+
+/***********************************************************************
+ * Enables
+ **********************************************************************/
+void wbx_base::wbx_version4::set_tx_enabled(bool enb) {
+ self_base->get_iface()->set_gpio_out(dboard_iface::UNIT_TX,
+ (enb)? TX_POWER_UP | ADF4351_CE : TX_POWER_DOWN, TX_POWER_UP | TX_POWER_DOWN | 0);
+}
+
+
+/***********************************************************************
+ * Gain Handling
+ **********************************************************************/
+double wbx_base::wbx_version4::set_tx_gain(double gain, const std::string &name) {
+ assert_has(wbx_v4_tx_gain_ranges.keys(), name, "wbx tx gain name");
+ if(name == "PGA0"){
+ boost::uint16_t io_bits = tx_pga0_gain_to_iobits(gain);
+
+ self_base->_tx_gains[name] = gain;
+
+ //write the new gain to tx gpio outputs
+ //Update ATR with gain io_bits, only update for TX_ONLY and FULL_DUPLEX ATR states
+ self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY, io_bits, TX_ATTN_MASK);
+ self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, io_bits, TX_ATTN_MASK);
+
+ }
+ else UHD_THROW_INVALID_CODE_PATH();
+ return self_base->_tx_gains[name];
+}
+
+
+/***********************************************************************
+ * Tuning
+ **********************************************************************/
+double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double target_freq) {
+ //clip to tuning range
+ target_freq = wbx_v4_freq_range.clip(target_freq);
+
+ UHD_LOGV(often) << boost::format(
+ "WBX tune: target frequency %f Mhz"
+ ) % (target_freq/1e6) << std::endl;
+
+ //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler)
+ static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of
+ (0,23) //adf4351_regs_t::PRESCALER_4_5
+ (1,75) //adf4351_regs_t::PRESCALER_8_9
+ ;
+
+ //map rf divider select output dividers to enums
+ static const uhd::dict<int, adf4351_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of
+ (1, adf4351_regs_t::RF_DIVIDER_SELECT_DIV1)
+ (2, adf4351_regs_t::RF_DIVIDER_SELECT_DIV2)
+ (4, adf4351_regs_t::RF_DIVIDER_SELECT_DIV4)
+ (8, adf4351_regs_t::RF_DIVIDER_SELECT_DIV8)
+ (16, adf4351_regs_t::RF_DIVIDER_SELECT_DIV16)
+ (32, adf4351_regs_t::RF_DIVIDER_SELECT_DIV32)
+ (64, adf4351_regs_t::RF_DIVIDER_SELECT_DIV64)
+ ;
+
+ double actual_freq, pfd_freq;
+ double ref_freq = self_base->get_iface()->get_clock_rate(unit);
+ int R=0, BS=0, N=0, FRAC=0, MOD=0;
+ int RFdiv = 1;
+ adf4351_regs_t::reference_divide_by_2_t T = adf4351_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED;
+ adf4351_regs_t::reference_doubler_t D = adf4351_regs_t::REFERENCE_DOUBLER_DISABLED;
+
+ //Reference doubler for 50% duty cycle
+ // if ref_freq < 12.5MHz enable regs.reference_divide_by_2
+ if(ref_freq <= 12.5e6) D = adf4351_regs_t::REFERENCE_DOUBLER_ENABLED;
+
+ //increase RF divider until acceptable VCO frequency
+ //start with target_freq*2 because mixer has divide by 2
+ double vco_freq = target_freq*2;
+ while (vco_freq < 2.2e9) {
+ vco_freq *= 2;
+ RFdiv *= 2;
+ }
+
+ //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler)
+ adf4351_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5;
+
+ /*
+ * The goal here is to loop though possible R dividers,
+ * band select clock dividers, N (int) dividers, and FRAC
+ * (frac) dividers.
+ *
+ * Calculate the N and F dividers for each set of values.
+ * The loop exits when it meets all of the constraints.
+ * The resulting loop values are loaded into the registers.
+ *
+ * from pg.21
+ *
+ * f_pfd = f_ref*(1+D)/(R*(1+T))
+ * f_vco = (N + (FRAC/MOD))*f_pfd
+ * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD
+ * f_rf = f_vco/RFdiv)
+ * f_actual = f_rf/2
+ */
+ for(R = 1; R <= 1023; R+=1){
+ //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T)
+ pfd_freq = ref_freq*(1+D)/(R*(1+T));
+
+ //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth)
+ if (pfd_freq > 25e6) continue;
+
+ //ignore fractional part of tuning
+ N = int(std::floor(vco_freq/pfd_freq));
+
+ //keep N > minimum int divider requirement
+ if (N < prescaler_to_min_int_div[prescaler]) continue;
+
+ for(BS=1; BS <= 255; BS+=1){
+ //keep the band select frequency at or below 100KHz
+ //constraint on band select clock
+ if (pfd_freq/BS > 100e3) continue;
+ goto done_loop;
+ }
+ } done_loop:
+
+ //Fractional-N calculation
+ MOD = 4095; //max fractional accuracy
+ FRAC = int((vco_freq/pfd_freq - N)*MOD);
+
+ //Reference divide-by-2 for 50% duty cycle
+ // if R even, move one divide by 2 to to regs.reference_divide_by_2
+ if(R % 2 == 0){
+ T = adf4351_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED;
+ R /= 2;
+ }
+
+ //actual frequency calculation
+ actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv/2);
+
+
+ UHD_LOGV(often)
+ << boost::format("WBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl
+
+ << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s"
+ ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl
+ << boost::format("WBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f"
+ ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl;
+
+ //load the register values
+ adf4351_regs_t regs;
+
+ regs.frac_12_bit = FRAC;
+ regs.int_16_bit = N;
+ regs.mod_12_bit = MOD;
+ regs.prescaler = prescaler;
+ regs.r_counter_10_bit = R;
+ regs.reference_divide_by_2 = T;
+ regs.reference_doubler = D;
+ regs.band_select_clock_div = BS;
+ UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv));
+ regs.rf_divider_select = rfdivsel_to_enum[RFdiv];
+
+ if (unit == dboard_iface::UNIT_RX) {
+ freq_range_t rx_lo_5dbm = list_of
+ (range_t(0.05e9, 1.4e9))
+ ;
+
+ freq_range_t rx_lo_2dbm = list_of
+ (range_t(1.4e9, 2.2e9))
+ ;
+
+ if (actual_freq == rx_lo_5dbm.clip(actual_freq)) regs.output_power = adf4351_regs_t::OUTPUT_POWER_5DBM;
+
+ if (actual_freq == rx_lo_2dbm.clip(actual_freq)) regs.output_power = adf4351_regs_t::OUTPUT_POWER_2DBM;
+
+ } else if (unit == dboard_iface::UNIT_TX) {
+ freq_range_t tx_lo_5dbm = list_of
+ (range_t(0.05e9, 1.7e9))
+ (range_t(1.9e9, 2.2e9))
+ ;
+
+ freq_range_t tx_lo_m1dbm = list_of
+ (range_t(1.7e9, 1.9e9))
+ ;
+
+ if (actual_freq == tx_lo_5dbm.clip(actual_freq)) regs.output_power = adf4351_regs_t::OUTPUT_POWER_5DBM;
+
+ if (actual_freq == tx_lo_m1dbm.clip(actual_freq)) regs.output_power = adf4351_regs_t::OUTPUT_POWER_M1DBM;
+
+ }
+
+ //write the registers
+ //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0)
+ int addr;
+
+ for(addr=5; addr>=0; addr--){
+ UHD_LOGV(often) << boost::format(
+ "WBX SPI Reg (0x%02x): 0x%08x"
+ ) % addr % regs.get_reg(addr) << std::endl;
+ self_base->get_iface()->write_spi(
+ unit, spi_config_t::EDGE_RISE,
+ regs.get_reg(addr), 32
+ );
+ }
+
+ //return the actual frequency
+ UHD_LOGV(often) << boost::format(
+ "WBX tune: actual frequency %f Mhz"
+ ) % (actual_freq/1e6) << std::endl;
+ return actual_freq;
+}
diff --git a/host/lib/usrp/dboard/db_xcvr2450.cpp b/host/lib/usrp/dboard/db_xcvr2450.cpp
new file mode 100644
index 000000000..7c76c45ef
--- /dev/null
+++ b/host/lib/usrp/dboard/db_xcvr2450.cpp
@@ -0,0 +1,673 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+// TX IO Pins
+#define HB_PA_OFF_TXIO (1 << 15) // 5GHz PA, 1 = off, 0 = on
+#define LB_PA_OFF_TXIO (1 << 14) // 2.4GHz PA, 1 = off, 0 = on
+#define ANTSEL_TX1_RX2_TXIO (1 << 13) // 1 = Ant 1 to TX, Ant 2 to RX
+#define ANTSEL_TX2_RX1_TXIO (1 << 12) // 1 = Ant 2 to TX, Ant 1 to RX
+#define TX_EN_TXIO (1 << 11) // 1 = TX on, 0 = TX off
+#define AD9515DIV_TXIO (1 << 4) // 1 = Div by 3, 0 = Div by 2
+
+#define TXIO_MASK (HB_PA_OFF_TXIO | LB_PA_OFF_TXIO | ANTSEL_TX1_RX2_TXIO | ANTSEL_TX2_RX1_TXIO | TX_EN_TXIO | AD9515DIV_TXIO)
+
+// TX IO Functions
+#define HB_PA_TXIO LB_PA_OFF_TXIO
+#define LB_PA_TXIO HB_PA_OFF_TXIO
+#define TX_ENB_TXIO TX_EN_TXIO
+#define TX_DIS_TXIO 0
+#define AD9515DIV_3_TXIO AD9515DIV_TXIO
+#define AD9515DIV_2_TXIO 0
+
+// RX IO Pins
+#define LOCKDET_RXIO (1 << 15) // This is an INPUT!!!
+#define POWER_RXIO (1 << 14) // 1 = power on, 0 = shutdown
+#define RX_EN_RXIO (1 << 13) // 1 = RX on, 0 = RX off
+#define RX_HP_RXIO (1 << 12) // 0 = Fc set by rx_hpf, 1 = 600 KHz
+
+#define RXIO_MASK (POWER_RXIO | RX_EN_RXIO | RX_HP_RXIO)
+
+// RX IO Functions
+#define POWER_UP_RXIO POWER_RXIO
+#define POWER_DOWN_RXIO 0
+#define RX_ENB_RXIO RX_EN_RXIO
+#define RX_DIS_RXIO 0
+
+#include "max2829_regs.hpp"
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/static.hpp>
+#include <uhd/utils/safe_call.hpp>
+#include <uhd/utils/assert_has.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhd/types/sensors.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/usrp/dboard_base.hpp>
+#include <uhd/usrp/dboard_manager.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/format.hpp>
+#include <boost/thread.hpp>
+#include <boost/math/special_functions/round.hpp>
+#include <utility>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace boost::assign;
+
+/***********************************************************************
+ * The XCVR 2450 constants
+ **********************************************************************/
+static const freq_range_t xcvr_freq_range = list_of
+ (range_t(2.4e9, 2.5e9))
+ (range_t(4.9e9, 6.0e9))
+;
+
+//Multiplied by 2.0 for conversion to complex bandpass from lowpass
+static const freq_range_t xcvr_tx_bandwidth_range = list_of
+ (range_t(2.0*12e6))
+ (range_t(2.0*18e6))
+ (range_t(2.0*24e6))
+;
+
+//Multiplied by 2.0 for conversion to complex bandpass from lowpass
+static const freq_range_t xcvr_rx_bandwidth_range = list_of
+ (range_t(2.0*0.9*7.5e6, 2.0*1.1*7.5e6))
+ (range_t(2.0*0.9*9.5e6, 2.0*1.1*9.5e6))
+ (range_t(2.0*0.9*14e6, 2.0*1.1*14e6))
+ (range_t(2.0*0.9*18e6, 2.0*1.1*18e6))
+;
+
+static const std::vector<std::string> xcvr_antennas = list_of("J1")("J2");
+
+static const uhd::dict<std::string, gain_range_t> xcvr_tx_gain_ranges = map_list_of
+ ("VGA", gain_range_t(0, 30, 0.5))
+ ("BB", gain_range_t(0, 5, 1.5))
+;
+static const uhd::dict<std::string, gain_range_t> xcvr_rx_gain_ranges = map_list_of
+ ("LNA", gain_range_t(list_of
+ (range_t(0))
+ (range_t(15))
+ (range_t(30.5))
+ ))
+ ("VGA", gain_range_t(0, 62, 2.0))
+;
+
+/***********************************************************************
+ * The XCVR 2450 dboard class
+ **********************************************************************/
+class xcvr2450 : public xcvr_dboard_base{
+public:
+ xcvr2450(ctor_args_t args);
+ ~xcvr2450(void);
+
+private:
+ double _lo_freq;
+ double _rx_bandwidth, _tx_bandwidth;
+ uhd::dict<std::string, double> _tx_gains, _rx_gains;
+ std::string _tx_ant, _rx_ant;
+ int _ad9515div;
+ max2829_regs_t _max2829_regs;
+
+ double set_lo_freq(double target_freq);
+ double set_lo_freq_core(double target_freq);
+ void set_tx_ant(const std::string &ant);
+ void set_rx_ant(const std::string &ant);
+ double set_tx_gain(double gain, const std::string &name);
+ double set_rx_gain(double gain, const std::string &name);
+ double set_rx_bandwidth(double bandwidth);
+ double set_tx_bandwidth(double bandwidth);
+
+ void update_atr(void);
+ void spi_reset(void);
+ void send_reg(boost::uint8_t addr){
+ boost::uint32_t value = _max2829_regs.get_reg(addr);
+ UHD_LOGV(often) << boost::format(
+ "XCVR2450: send reg 0x%02x, value 0x%05x"
+ ) % int(addr) % value << std::endl;
+ this->get_iface()->write_spi(
+ dboard_iface::UNIT_RX,
+ spi_config_t::EDGE_RISE,
+ value, 24
+ );
+ }
+
+ static bool is_highband(double freq){return freq > 3e9;}
+
+ /*!
+ * Get the lock detect status of the LO.
+ * \return sensor for locked
+ */
+ sensor_value_t get_locked(void){
+ const bool locked = (this->get_iface()->read_gpio(dboard_iface::UNIT_RX) & LOCKDET_RXIO) != 0;
+ return sensor_value_t("LO", locked, "locked", "unlocked");
+ }
+
+ /*!
+ * Read the RSSI from the aux adc
+ * \return the rssi sensor in dBm
+ */
+ sensor_value_t get_rssi(void){
+ //*FIXME* RSSI depends on LNA Gain Setting (datasheet pg 16 top middle chart)
+ double max_power = 0.0;
+ switch(_max2829_regs.rx_lna_gain){
+ case 0:
+ case 1: max_power = 0; break;
+ case 2: max_power = -15; break;
+ case 3: max_power = -30.5; break;
+ }
+
+ //constants for the rssi calculation
+ static const double min_v = 0.5, max_v = 2.5;
+ static const double rssi_dyn_range = 60;
+ //calculate the rssi from the voltage
+ double voltage = this->get_iface()->read_aux_adc(dboard_iface::UNIT_RX, dboard_iface::AUX_ADC_B);
+ double rssi = max_power - rssi_dyn_range*(voltage - min_v)/(max_v - min_v);
+ return sensor_value_t("RSSI", rssi, "dBm");
+ }
+};
+
+/***********************************************************************
+ * Register the XCVR 2450 dboard
+ **********************************************************************/
+static dboard_base::sptr make_xcvr2450(dboard_base::ctor_args_t args){
+ return dboard_base::sptr(new xcvr2450(args));
+}
+
+UHD_STATIC_BLOCK(reg_xcvr2450_dboard){
+ //register the factory function for the rx and tx dbids
+ dboard_manager::register_dboard(0x0061, 0x0060, &make_xcvr2450, "XCVR2450");
+}
+
+/***********************************************************************
+ * Structors
+ **********************************************************************/
+xcvr2450::xcvr2450(ctor_args_t args) : xcvr_dboard_base(args){
+ spi_reset(); //prepare the spi
+
+ _rx_bandwidth = 9.5e6;
+ _tx_bandwidth = 12.0e6;
+
+ //setup the misc max2829 registers
+ _max2829_regs.mimo_select = max2829_regs_t::MIMO_SELECT_MIMO;
+ _max2829_regs.band_sel_mimo = max2829_regs_t::BAND_SEL_MIMO_MIMO;
+ _max2829_regs.pll_cp_select = max2829_regs_t::PLL_CP_SELECT_4MA;
+ _max2829_regs.rssi_high_bw = max2829_regs_t::RSSI_HIGH_BW_6MHZ;
+ _max2829_regs.tx_lpf_coarse_adj = max2829_regs_t::TX_LPF_COARSE_ADJ_12MHZ;
+ _max2829_regs.rx_lpf_coarse_adj = max2829_regs_t::RX_LPF_COARSE_ADJ_9_5MHZ;
+ _max2829_regs.rx_lpf_fine_adj = max2829_regs_t::RX_LPF_FINE_ADJ_100;
+ _max2829_regs.rx_vga_gain_spi = max2829_regs_t::RX_VGA_GAIN_SPI_SPI;
+ _max2829_regs.rssi_output_range = max2829_regs_t::RSSI_OUTPUT_RANGE_HIGH;
+ _max2829_regs.rssi_op_mode = max2829_regs_t::RSSI_OP_MODE_ENABLED;
+ _max2829_regs.rssi_pin_fcn = max2829_regs_t::RSSI_PIN_FCN_RSSI;
+ _max2829_regs.rx_highpass = max2829_regs_t::RX_HIGHPASS_100HZ;
+ _max2829_regs.tx_vga_gain_spi = max2829_regs_t::TX_VGA_GAIN_SPI_SPI;
+ _max2829_regs.pa_driver_linearity = max2829_regs_t::PA_DRIVER_LINEARITY_78;
+ _max2829_regs.tx_vga_linearity = max2829_regs_t::TX_VGA_LINEARITY_78;
+ _max2829_regs.tx_upconv_linearity = max2829_regs_t::TX_UPCONV_LINEARITY_78;
+
+ //send initial register settings
+ for(boost::uint8_t reg = 0x2; reg <= 0xC; reg++){
+ this->send_reg(reg);
+ }
+
+ ////////////////////////////////////////////////////////////////////
+ // Register RX properties
+ ////////////////////////////////////////////////////////////////////
+ this->get_rx_subtree()->create<std::string>("name")
+ .set(get_rx_id().to_pp_string());
+ this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked")
+ .publish(boost::bind(&xcvr2450::get_locked, this));
+ this->get_rx_subtree()->create<sensor_value_t>("sensors/rssi")
+ .publish(boost::bind(&xcvr2450::get_rssi, this));
+ BOOST_FOREACH(const std::string &name, xcvr_rx_gain_ranges.keys()){
+ this->get_rx_subtree()->create<double>("gains/"+name+"/value")
+ .coerce(boost::bind(&xcvr2450::set_rx_gain, this, _1, name))
+ .set(xcvr_rx_gain_ranges[name].start());
+ this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range")
+ .set(xcvr_rx_gain_ranges[name]);
+ }
+ this->get_rx_subtree()->create<double>("freq/value")
+ .coerce(boost::bind(&xcvr2450::set_lo_freq, this, _1))
+ .set(double(2.45e9));
+ this->get_rx_subtree()->create<meta_range_t>("freq/range")
+ .set(xcvr_freq_range);
+ this->get_rx_subtree()->create<std::string>("antenna/value")
+ .subscribe(boost::bind(&xcvr2450::set_rx_ant, this, _1))
+ .set(xcvr_antennas.at(0));
+ this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options")
+ .set(xcvr_antennas);
+ this->get_rx_subtree()->create<std::string>("connection")
+ .set("IQ");
+ this->get_rx_subtree()->create<bool>("enabled")
+ .set(true); //always enabled
+ this->get_rx_subtree()->create<bool>("use_lo_offset")
+ .set(false);
+ this->get_rx_subtree()->create<double>("bandwidth/value")
+ .coerce(boost::bind(&xcvr2450::set_rx_bandwidth, this, _1)) //complex bandpass bandwidth
+ .set(2.0*_rx_bandwidth); //_rx_bandwidth in lowpass, convert to complex bandpass
+ this->get_rx_subtree()->create<meta_range_t>("bandwidth/range")
+ .set(xcvr_rx_bandwidth_range);
+
+ ////////////////////////////////////////////////////////////////////
+ // Register TX properties
+ ////////////////////////////////////////////////////////////////////
+ this->get_tx_subtree()->create<std::string>("name")
+ .set(get_tx_id().to_pp_string());
+ this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked")
+ .publish(boost::bind(&xcvr2450::get_locked, this));
+ BOOST_FOREACH(const std::string &name, xcvr_tx_gain_ranges.keys()){
+ this->get_tx_subtree()->create<double>("gains/"+name+"/value")
+ .coerce(boost::bind(&xcvr2450::set_tx_gain, this, _1, name))
+ .set(xcvr_tx_gain_ranges[name].start());
+ this->get_tx_subtree()->create<meta_range_t>("gains/"+name+"/range")
+ .set(xcvr_tx_gain_ranges[name]);
+ }
+ this->get_tx_subtree()->create<double>("freq/value")
+ .coerce(boost::bind(&xcvr2450::set_lo_freq, this, _1))
+ .set(double(2.45e9));
+ this->get_tx_subtree()->create<meta_range_t>("freq/range")
+ .set(xcvr_freq_range);
+ this->get_tx_subtree()->create<std::string>("antenna/value")
+ .subscribe(boost::bind(&xcvr2450::set_tx_ant, this, _1))
+ .set(xcvr_antennas.at(1));
+ this->get_tx_subtree()->create<std::vector<std::string> >("antenna/options")
+ .set(xcvr_antennas);
+ this->get_tx_subtree()->create<std::string>("connection")
+ .set("QI");
+ this->get_tx_subtree()->create<bool>("enabled")
+ .set(true); //always enabled
+ this->get_tx_subtree()->create<bool>("use_lo_offset")
+ .set(true);
+ this->get_tx_subtree()->create<double>("bandwidth/value")
+ .coerce(boost::bind(&xcvr2450::set_tx_bandwidth, this, _1)) //complex bandpass bandwidth
+ .set(2.0*_tx_bandwidth); //_tx_bandwidth in lowpass, convert to complex bandpass
+ this->get_tx_subtree()->create<meta_range_t>("bandwidth/range")
+ .set(xcvr_tx_bandwidth_range);
+
+ //enable only the clocks we need
+ this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true);
+
+ //set the gpio directions and atr controls (identically)
+ this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, TXIO_MASK);
+ this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, RXIO_MASK);
+ this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, TXIO_MASK);
+ this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, RXIO_MASK);
+}
+
+xcvr2450::~xcvr2450(void){
+ UHD_SAFE_CALL(spi_reset();)
+}
+
+void xcvr2450::spi_reset(void){
+ //spi reset mode: global enable = off, tx and rx enable = on
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE, TX_ENB_TXIO);
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, RX_ENB_RXIO | POWER_DOWN_RXIO);
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+
+ //take it back out of spi reset mode and wait a bit
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, RX_DIS_RXIO | POWER_UP_RXIO);
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+}
+
+/***********************************************************************
+ * Update ATR regs which change with Antenna or Freq
+ **********************************************************************/
+void xcvr2450::update_atr(void){
+ //calculate tx atr pins
+ int band_sel = (xcvr2450::is_highband(_lo_freq))? HB_PA_TXIO : LB_PA_TXIO;
+ int tx_ant_sel = (_tx_ant == "J1")? ANTSEL_TX1_RX2_TXIO : ANTSEL_TX2_RX1_TXIO;
+ int rx_ant_sel = (_rx_ant == "J2")? ANTSEL_TX1_RX2_TXIO : ANTSEL_TX2_RX1_TXIO;
+ int xx_ant_sel = tx_ant_sel; //Prefer the tx antenna selection for full duplex,
+ //due to the issue that USRP1 will take the value of full duplex for its TXATR.
+ int ad9515div = (_ad9515div == 3)? AD9515DIV_3_TXIO : AD9515DIV_2_TXIO;
+
+ //set the tx registers
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE, band_sel | ad9515div | TX_DIS_TXIO);
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY, band_sel | ad9515div | TX_DIS_TXIO | rx_ant_sel);
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY, band_sel | ad9515div | TX_ENB_TXIO | tx_ant_sel);
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, band_sel | ad9515div | TX_ENB_TXIO | xx_ant_sel);
+
+ //set the rx registers
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, POWER_UP_RXIO | RX_DIS_RXIO);
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, POWER_UP_RXIO | RX_ENB_RXIO);
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, POWER_UP_RXIO | RX_DIS_RXIO);
+ this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, POWER_UP_RXIO | RX_DIS_RXIO);
+}
+
+/***********************************************************************
+ * Tuning
+ **********************************************************************/
+double xcvr2450::set_lo_freq(double target_freq){
+ //tune the LO and sleep a bit for lock
+ //if not locked, try some carrier offsets
+ double actual = 0.0;
+ for (double offset = 0.0; offset <= 3e6; offset+=1e6){
+ actual = this->set_lo_freq_core(target_freq + offset);
+ boost::this_thread::sleep(boost::posix_time::milliseconds(50));
+ if (this->get_locked().to_bool()) break;
+ }
+ return actual;
+}
+
+double xcvr2450::set_lo_freq_core(double target_freq){
+
+ //clip the input to the range
+ target_freq = xcvr_freq_range.clip(target_freq);
+
+ //variables used in the calculation below
+ double scaler = xcvr2450::is_highband(target_freq)? (4.0/5.0) : (4.0/3.0);
+ double ref_freq = this->get_iface()->get_clock_rate(dboard_iface::UNIT_TX);
+ int R, intdiv, fracdiv;
+
+ //loop through values until we get a match
+ for(_ad9515div = 2; _ad9515div <= 3; _ad9515div++){
+ for(R = 1; R <= 7; R++){
+ double N = (target_freq*scaler*R*_ad9515div)/ref_freq;
+ intdiv = int(std::floor(N));
+ fracdiv = boost::math::iround((N - intdiv)*double(1 << 16));
+ //actual minimum is 128, but most chips seems to require higher to lock
+ if (intdiv < 131 or intdiv > 255) continue;
+ //constraints met: exit loop
+ goto done_loop;
+ }
+ } done_loop:
+
+ //calculate the actual freq from the values above
+ double N = double(intdiv) + double(fracdiv)/double(1 << 16);
+ _lo_freq = (N*ref_freq)/(scaler*R*_ad9515div);
+
+ UHD_LOGV(often)
+ << boost::format("XCVR2450 tune:\n")
+ << boost::format(" R=%d, N=%f, ad9515=%d, scaler=%f\n") % R % N % _ad9515div % scaler
+ << boost::format(" Ref Freq=%fMHz\n") % (ref_freq/1e6)
+ << boost::format(" Target Freq=%fMHz\n") % (target_freq/1e6)
+ << boost::format(" Actual Freq=%fMHz\n") % (_lo_freq/1e6)
+ << std::endl;
+
+ //high-high band or low-high band?
+ if(_lo_freq > (5.35e9 + 5.47e9)/2.0){
+ UHD_LOGV(often) << "XCVR2450 tune: Using high-high band" << std::endl;
+ _max2829_regs.band_select_802_11a = max2829_regs_t::BAND_SELECT_802_11A_5_47GHZ_TO_5_875GHZ;
+ }else{
+ UHD_LOGV(often) << "XCVR2450 tune: Using low-high band" << std::endl;
+ _max2829_regs.band_select_802_11a = max2829_regs_t::BAND_SELECT_802_11A_4_9GHZ_TO_5_35GHZ;
+ }
+
+ //new band select settings and ad9515 divider
+ this->update_atr();
+
+ //load new counters into registers
+ _max2829_regs.int_div_ratio_word = intdiv;
+ _max2829_regs.frac_div_ratio_lsb = fracdiv & 0x3;
+ _max2829_regs.frac_div_ratio_msb = fracdiv >> 2;
+ this->send_reg(0x3); //integer
+ this->send_reg(0x4); //fractional
+
+ //load the reference divider and band select into registers
+ //toggle the bandswitch from off to automatic (which really means start)
+ _max2829_regs.ref_divider = R;
+ _max2829_regs.band_select = (xcvr2450::is_highband(_lo_freq))?
+ max2829_regs_t::BAND_SELECT_5GHZ :
+ max2829_regs_t::BAND_SELECT_2_4GHZ ;
+ _max2829_regs.vco_bandswitch = max2829_regs_t::VCO_BANDSWITCH_DISABLE;
+ this->send_reg(0x5);
+ _max2829_regs.vco_bandswitch = max2829_regs_t::VCO_BANDSWITCH_AUTOMATIC;;
+ this->send_reg(0x5);
+
+ return _lo_freq;
+}
+
+/***********************************************************************
+ * Antenna Handling
+ **********************************************************************/
+void xcvr2450::set_tx_ant(const std::string &ant){
+ assert_has(xcvr_antennas, ant, "xcvr antenna name");
+ _tx_ant = ant;
+ this->update_atr(); //sets the atr to the new antenna setting
+}
+
+void xcvr2450::set_rx_ant(const std::string &ant){
+ assert_has(xcvr_antennas, ant, "xcvr antenna name");
+ _rx_ant = ant;
+ this->update_atr(); //sets the atr to the new antenna setting
+}
+
+/***********************************************************************
+ * Gain Handling
+ **********************************************************************/
+/*!
+ * Convert a requested gain for the tx vga into the integer register value.
+ * The gain passed into the function will be set to the actual value.
+ * \param gain the requested gain in dB
+ * \return 6 bit the register value
+ */
+static int gain_to_tx_vga_reg(double &gain){
+ //calculate the register value
+ int reg = uhd::clip(boost::math::iround(gain*60/30.0) + 3, 0, 63);
+
+ //calculate the actual gain value
+ if (reg < 4) gain = 0;
+ else if (reg < 48) gain = double(reg/2 - 1);
+ else gain = double(reg/2.0 - 1.5);
+
+ //return register value
+ return reg;
+}
+
+/*!
+ * Convert a requested gain for the tx bb into the integer register value.
+ * The gain passed into the function will be set to the actual value.
+ * \param gain the requested gain in dB
+ * \return gain enum value
+ */
+static max2829_regs_t::tx_baseband_gain_t gain_to_tx_bb_reg(double &gain){
+ int reg = uhd::clip(boost::math::iround(gain*3/5.0), 0, 3);
+ switch(reg){
+ case 0:
+ gain = 0;
+ return max2829_regs_t::TX_BASEBAND_GAIN_0DB;
+ case 1:
+ gain = 2;
+ return max2829_regs_t::TX_BASEBAND_GAIN_2DB;
+ case 2:
+ gain = 3.5;
+ return max2829_regs_t::TX_BASEBAND_GAIN_3_5DB;
+ case 3:
+ gain = 5;
+ return max2829_regs_t::TX_BASEBAND_GAIN_5DB;
+ }
+ UHD_THROW_INVALID_CODE_PATH();
+}
+
+/*!
+ * Convert a requested gain for the rx vga into the integer register value.
+ * The gain passed into the function will be set to the actual value.
+ * \param gain the requested gain in dB
+ * \return 5 bit the register value
+ */
+static int gain_to_rx_vga_reg(double &gain){
+ int reg = uhd::clip(boost::math::iround(gain/2.0), 0, 31);
+ gain = double(reg*2);
+ return reg;
+}
+
+/*!
+ * Convert a requested gain for the rx lna into the integer register value.
+ * The gain passed into the function will be set to the actual value.
+ * \param gain the requested gain in dB
+ * \return 2 bit the register value
+ */
+static int gain_to_rx_lna_reg(double &gain){
+ int reg = uhd::clip(boost::math::iround(gain*2/30.5) + 1, 0, 3);
+ switch(reg){
+ case 0:
+ case 1: gain = 0; break;
+ case 2: gain = 15; break;
+ case 3: gain = 30.5; break;
+ }
+ return reg;
+}
+
+double xcvr2450::set_tx_gain(double gain, const std::string &name){
+ assert_has(xcvr_tx_gain_ranges.keys(), name, "xcvr tx gain name");
+ if (name == "VGA"){
+ _max2829_regs.tx_vga_gain = gain_to_tx_vga_reg(gain);
+ send_reg(0xC);
+ }
+ else if(name == "BB"){
+ _max2829_regs.tx_baseband_gain = gain_to_tx_bb_reg(gain);
+ send_reg(0x9);
+ }
+ else UHD_THROW_INVALID_CODE_PATH();
+ _tx_gains[name] = gain;
+
+ return gain;
+}
+
+double xcvr2450::set_rx_gain(double gain, const std::string &name){
+ assert_has(xcvr_rx_gain_ranges.keys(), name, "xcvr rx gain name");
+ if (name == "VGA"){
+ _max2829_regs.rx_vga_gain = gain_to_rx_vga_reg(gain);
+ send_reg(0xB);
+ }
+ else if(name == "LNA"){
+ _max2829_regs.rx_lna_gain = gain_to_rx_lna_reg(gain);
+ send_reg(0xB);
+ }
+ else UHD_THROW_INVALID_CODE_PATH();
+ _rx_gains[name] = gain;
+
+ return gain;
+}
+
+
+/***********************************************************************
+ * Bandwidth Handling
+ **********************************************************************/
+static max2829_regs_t::tx_lpf_coarse_adj_t bandwidth_to_tx_lpf_coarse_reg(double &bandwidth){
+ int reg = uhd::clip(boost::math::iround((bandwidth-6.0e6)/6.0e6), 1, 3);
+
+ switch(reg){
+ case 1: // bandwidth < 15MHz
+ bandwidth = 12e6;
+ return max2829_regs_t::TX_LPF_COARSE_ADJ_12MHZ;
+ case 2: // 15MHz < bandwidth < 21MHz
+ bandwidth = 18e6;
+ return max2829_regs_t::TX_LPF_COARSE_ADJ_18MHZ;
+ case 3: // bandwidth > 21MHz
+ bandwidth = 24e6;
+ return max2829_regs_t::TX_LPF_COARSE_ADJ_24MHZ;
+ }
+ UHD_THROW_INVALID_CODE_PATH();
+}
+
+static max2829_regs_t::rx_lpf_fine_adj_t bandwidth_to_rx_lpf_fine_reg(double &bandwidth, double requested_bandwidth){
+ int reg = uhd::clip(boost::math::iround((requested_bandwidth/bandwidth)/0.05), 18, 22);
+
+ switch(reg){
+ case 18: // requested_bandwidth < 92.5%
+ bandwidth = 0.9 * bandwidth;
+ return max2829_regs_t::RX_LPF_FINE_ADJ_90;
+ case 19: // 92.5% < requested_bandwidth < 97.5%
+ bandwidth = 0.95 * bandwidth;
+ return max2829_regs_t::RX_LPF_FINE_ADJ_95;
+ case 20: // 97.5% < requested_bandwidth < 102.5%
+ bandwidth = 1.0 * bandwidth;
+ return max2829_regs_t::RX_LPF_FINE_ADJ_100;
+ case 21: // 102.5% < requested_bandwidth < 107.5%
+ bandwidth = 1.05 * bandwidth;
+ return max2829_regs_t::RX_LPF_FINE_ADJ_105;
+ case 22: // 107.5% < requested_bandwidth
+ bandwidth = 1.1 * bandwidth;
+ return max2829_regs_t::RX_LPF_FINE_ADJ_110;
+ }
+ UHD_THROW_INVALID_CODE_PATH();
+}
+
+static max2829_regs_t::rx_lpf_coarse_adj_t bandwidth_to_rx_lpf_coarse_reg(double &bandwidth){
+ int reg = uhd::clip(boost::math::iround((bandwidth-7.0e6)/1.0e6), 0, 11);
+
+ switch(reg){
+ case 0: // bandwidth < 7.5MHz
+ case 1: // 7.5MHz < bandwidth < 8.5MHz
+ bandwidth = 7.5e6;
+ return max2829_regs_t::RX_LPF_COARSE_ADJ_7_5MHZ;
+ case 2: // 8.5MHz < bandwidth < 9.5MHz
+ case 3: // 9.5MHz < bandwidth < 10.5MHz
+ case 4: // 10.5MHz < bandwidth < 11.5MHz
+ bandwidth = 9.5e6;
+ return max2829_regs_t::RX_LPF_COARSE_ADJ_9_5MHZ;
+ case 5: // 11.5MHz < bandwidth < 12.5MHz
+ case 6: // 12.5MHz < bandwidth < 13.5MHz
+ case 7: // 13.5MHz < bandwidth < 14.5MHz
+ case 8: // 14.5MHz < bandwidth < 15.5MHz
+ bandwidth = 14e6;
+ return max2829_regs_t::RX_LPF_COARSE_ADJ_14MHZ;
+ case 9: // 15.5MHz < bandwidth < 16.5MHz
+ case 10: // 16.5MHz < bandwidth < 17.5MHz
+ case 11: // 17.5MHz < bandwidth
+ bandwidth = 18e6;
+ return max2829_regs_t::RX_LPF_COARSE_ADJ_18MHZ;
+ }
+ UHD_THROW_INVALID_CODE_PATH();
+}
+
+double xcvr2450::set_rx_bandwidth(double bandwidth){
+ double requested_bandwidth = bandwidth;
+
+ //convert complex bandpass to lowpass bandwidth
+ bandwidth = bandwidth/2.0;
+
+ //compute coarse low pass cutoff frequency setting
+ _max2829_regs.rx_lpf_coarse_adj = bandwidth_to_rx_lpf_coarse_reg(bandwidth);
+
+ //compute fine low pass cutoff frequency setting
+ _max2829_regs.rx_lpf_fine_adj = bandwidth_to_rx_lpf_fine_reg(bandwidth, requested_bandwidth);
+
+ //shadow bandwidth setting
+ _rx_bandwidth = bandwidth;
+
+ //update register
+ send_reg(0x7);
+
+ UHD_LOGV(often) << boost::format(
+ "XCVR2450 RX Bandwidth (lp_fc): %f Hz, coarse reg: %d, fine reg: %d"
+ ) % _rx_bandwidth % (int(_max2829_regs.rx_lpf_coarse_adj)) % (int(_max2829_regs.rx_lpf_fine_adj)) << std::endl;
+
+ return 2.0*_rx_bandwidth;
+}
+
+double xcvr2450::set_tx_bandwidth(double bandwidth){
+ //convert complex bandpass to lowpass bandwidth
+ bandwidth = bandwidth/2.0;
+
+ //compute coarse low pass cutoff frequency setting
+ _max2829_regs.tx_lpf_coarse_adj = bandwidth_to_tx_lpf_coarse_reg(bandwidth);
+
+ //shadow bandwidth setting
+ _tx_bandwidth = bandwidth;
+
+ //update register
+ send_reg(0x7);
+
+ UHD_LOGV(often) << boost::format(
+ "XCVR2450 TX Bandwidth (lp_fc): %f Hz, coarse reg: %d"
+ ) % _tx_bandwidth % (int(_max2829_regs.tx_lpf_coarse_adj)) << std::endl;
+
+ //convert lowpass back to complex bandpass bandwidth
+ return 2.0*_tx_bandwidth;
+}
diff --git a/host/lib/usrp/dboard_base.cpp b/host/lib/usrp/dboard_base.cpp
new file mode 100644
index 000000000..fe14c02b9
--- /dev/null
+++ b/host/lib/usrp/dboard_base.cpp
@@ -0,0 +1,100 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "dboard_ctor_args.hpp"
+#include <uhd/usrp/dboard_base.hpp>
+#include <boost/format.hpp>
+#include <stdexcept>
+
+using namespace uhd;
+using namespace uhd::usrp;
+
+/***********************************************************************
+ * dboard_base dboard dboard_base class
+ **********************************************************************/
+struct dboard_base::impl{
+ dboard_ctor_args_t args;
+};
+
+dboard_base::dboard_base(ctor_args_t args){
+ _impl = UHD_PIMPL_MAKE(impl, ());
+ _impl->args = *static_cast<dboard_ctor_args_t *>(args);
+}
+
+std::string dboard_base::get_subdev_name(void){
+ return _impl->args.sd_name;
+}
+
+dboard_iface::sptr dboard_base::get_iface(void){
+ return _impl->args.db_iface;
+}
+
+dboard_id_t dboard_base::get_rx_id(void){
+ return _impl->args.rx_id;
+}
+
+dboard_id_t dboard_base::get_tx_id(void){
+ return _impl->args.tx_id;
+}
+
+property_tree::sptr dboard_base::get_rx_subtree(void){
+ return _impl->args.rx_subtree;
+}
+
+property_tree::sptr dboard_base::get_tx_subtree(void){
+ return _impl->args.tx_subtree;
+}
+
+/***********************************************************************
+ * xcvr dboard dboard_base class
+ **********************************************************************/
+xcvr_dboard_base::xcvr_dboard_base(ctor_args_t args) : dboard_base(args){
+ if (get_rx_id() == dboard_id_t::none()){
+ throw uhd::runtime_error(str(boost::format(
+ "cannot create xcvr board when the rx id is \"%s\""
+ ) % dboard_id_t::none().to_pp_string()));
+ }
+ if (get_tx_id() == dboard_id_t::none()){
+ throw uhd::runtime_error(str(boost::format(
+ "cannot create xcvr board when the tx id is \"%s\""
+ ) % dboard_id_t::none().to_pp_string()));
+ }
+}
+
+/***********************************************************************
+ * rx dboard dboard_base class
+ **********************************************************************/
+rx_dboard_base::rx_dboard_base(ctor_args_t args) : dboard_base(args){
+ if (get_tx_id() != dboard_id_t::none()){
+ throw uhd::runtime_error(str(boost::format(
+ "cannot create rx board when the tx id is \"%s\""
+ " -> expected a tx id of \"%s\""
+ ) % get_tx_id().to_pp_string() % dboard_id_t::none().to_pp_string()));
+ }
+}
+
+/***********************************************************************
+ * tx dboard dboard_base class
+ **********************************************************************/
+tx_dboard_base::tx_dboard_base(ctor_args_t args) : dboard_base(args){
+ if (get_rx_id() != dboard_id_t::none()){
+ throw uhd::runtime_error(str(boost::format(
+ "cannot create tx board when the rx id is \"%s\""
+ " -> expected a rx id of \"%s\""
+ ) % get_rx_id().to_pp_string() % dboard_id_t::none().to_pp_string()));
+ }
+}
diff --git a/host/lib/usrp/dboard_ctor_args.hpp b/host/lib/usrp/dboard_ctor_args.hpp
new file mode 100644
index 000000000..99c071ff8
--- /dev/null
+++ b/host/lib/usrp/dboard_ctor_args.hpp
@@ -0,0 +1,38 @@
+//
+// Copyright 2010 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_DBOARD_CTOR_ARGS_HPP
+#define INCLUDED_LIBUHD_USRP_DBOARD_CTOR_ARGS_HPP
+
+#include <uhd/property_tree.hpp>
+#include <uhd/usrp/dboard_id.hpp>
+#include <uhd/usrp/dboard_base.hpp>
+#include <uhd/usrp/dboard_iface.hpp>
+#include <string>
+
+namespace uhd{ namespace usrp{
+
+ struct dboard_ctor_args_t{
+ std::string sd_name;
+ dboard_iface::sptr db_iface;
+ dboard_id_t rx_id, tx_id;
+ property_tree::sptr rx_subtree, tx_subtree;
+ };
+
+}} //namespace
+
+#endif /* INCLUDED_LIBUHD_USRP_DBOARD_CTOR_ARGS_HPP */
diff --git a/host/lib/usrp/dboard_eeprom.cpp b/host/lib/usrp/dboard_eeprom.cpp
new file mode 100644
index 000000000..f2bee47a9
--- /dev/null
+++ b/host/lib/usrp/dboard_eeprom.cpp
@@ -0,0 +1,169 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/usrp/dboard_eeprom.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/utils/log.hpp>
+#include <boost/foreach.hpp>
+#include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
+#include <algorithm>
+#include <sstream>
+
+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''
+// 01: .. Daughterboard ID (LSB)
+// 02: .. Daughterboard ID (MSB)
+// 03: .. io bits 7-0 direction (bit set if it's an output from m'board)
+// 04: .. io bits 15-8 direction (bit set if it's an output from m'board)
+// 05: .. ADC0 DC offset correction (LSB)
+// 06: .. ADC0 DC offset correction (MSB)
+// 07: .. ADC1 DC offset correction (LSB)
+// 08: .. ADC1 DC offset correction (MSB)
+// ...
+// 1f: .. negative of the sum of bytes [0x00, 0x1e]
+
+#define DB_EEPROM_MAGIC 0x00
+#define DB_EEPROM_MAGIC_VALUE 0xDB
+#define DB_EEPROM_ID_LSB 0x01
+#define DB_EEPROM_ID_MSB 0x02
+#define DB_EEPROM_REV_LSB 0x03
+#define DB_EEPROM_REV_MSB 0x04
+#define DB_EEPROM_OFFSET_0_LSB 0x05 // offset correction for ADC or DAC 0
+#define DB_EEPROM_OFFSET_0_MSB 0x06
+#define DB_EEPROM_OFFSET_1_LSB 0x07 // offset correction for ADC or DAC 1
+#define DB_EEPROM_OFFSET_1_MSB 0x08
+#define DB_EEPROM_SERIAL 0x09
+#define DB_EEPROM_SERIAL_LEN 0x09 //9 ASCII characters
+#define DB_EEPROM_CHKSUM 0x1f
+
+#define DB_EEPROM_CLEN 0x20 // length of common portion of eeprom
+
+#define DB_EEPROM_CUSTOM_BASE DB_EEPROM_CLEN // first avail offset for
+ // daughterboard specific use
+////////////////////////////////////////////////////////////////////////
+
+//negative sum of bytes excluding checksum byte
+static boost::uint8_t checksum(const byte_vector_t &bytes){
+ int sum = 0;
+ for (size_t i = 0; i < std::min(bytes.size(), size_t(DB_EEPROM_CHKSUM)); i++){
+ sum -= int(bytes.at(i));
+ }
+ UHD_LOGV(often) << boost::format("sum: 0x%02x") % sum << std::endl;
+ return boost::uint8_t(sum);
+}
+
+dboard_eeprom_t::dboard_eeprom_t(void){
+ id = dboard_id_t::none();
+ serial = "";
+}
+
+void dboard_eeprom_t::load(i2c_iface &iface, boost::uint8_t addr){
+ byte_vector_t bytes = iface.read_eeprom(addr, 0, DB_EEPROM_CLEN);
+
+ std::ostringstream ss;
+ for (size_t i = 0; i < bytes.size(); i++){
+ ss << boost::format(
+ "eeprom byte[0x%02x] = 0x%02x") % i % int(bytes.at(i)
+ ) << std::endl;
+ }
+ UHD_LOGV(often) << ss.str() << std::endl;
+
+ try{
+ UHD_ASSERT_THROW(bytes.size() >= DB_EEPROM_CLEN);
+ UHD_ASSERT_THROW(bytes[DB_EEPROM_MAGIC] == DB_EEPROM_MAGIC_VALUE);
+ UHD_ASSERT_THROW(bytes[DB_EEPROM_CHKSUM] == checksum(bytes));
+
+ //parse the ids
+ id = dboard_id_t::from_uint16(0
+ | (boost::uint16_t(bytes[DB_EEPROM_ID_LSB]) << 0)
+ | (boost::uint16_t(bytes[DB_EEPROM_ID_MSB]) << 8)
+ );
+
+ //parse the serial
+ serial = bytes_to_string(
+ byte_vector_t(&bytes.at(DB_EEPROM_SERIAL),
+ &bytes.at(DB_EEPROM_SERIAL+DB_EEPROM_SERIAL_LEN))
+ );
+
+ //parse the revision
+ const boost::uint16_t rev_num = 0
+ | (boost::uint16_t(bytes[DB_EEPROM_REV_LSB]) << 0)
+ | (boost::uint16_t(bytes[DB_EEPROM_REV_MSB]) << 8)
+ ;
+ if (rev_num != 0 and rev_num != 0xffff){
+ revision = boost::lexical_cast<std::string>(rev_num);
+ }
+
+ }catch(const uhd::assertion_error &){
+ id = dboard_id_t::none();
+ serial = "";
+ }
+}
+
+void dboard_eeprom_t::store(i2c_iface &iface, boost::uint8_t addr) const{
+ byte_vector_t bytes(DB_EEPROM_CLEN, 0); //defaults to all zeros
+ bytes[DB_EEPROM_MAGIC] = DB_EEPROM_MAGIC_VALUE;
+
+ //load the id bytes
+ bytes[DB_EEPROM_ID_LSB] = boost::uint8_t(id.to_uint16() >> 0);
+ bytes[DB_EEPROM_ID_MSB] = boost::uint8_t(id.to_uint16() >> 8);
+
+ //load the serial bytes
+ byte_vector_t ser_bytes = string_to_bytes(serial, DB_EEPROM_SERIAL_LEN);
+ std::copy(ser_bytes.begin(), ser_bytes.end(), &bytes.at(DB_EEPROM_SERIAL));
+
+ //load the revision bytes
+ if (not revision.empty()){
+ const boost::uint16_t rev_num = boost::lexical_cast<boost::uint16_t>(revision);
+ bytes[DB_EEPROM_REV_LSB] = boost::uint8_t(rev_num >> 0);
+ bytes[DB_EEPROM_REV_MSB] = boost::uint8_t(rev_num >> 8);
+ }
+
+ //load the checksum
+ bytes[DB_EEPROM_CHKSUM] = checksum(bytes);
+
+ iface.write_eeprom(addr, 0, bytes);
+}
diff --git a/host/lib/usrp/dboard_id.cpp b/host/lib/usrp/dboard_id.cpp
new file mode 100644
index 000000000..3028d2a3b
--- /dev/null
+++ b/host/lib/usrp/dboard_id.cpp
@@ -0,0 +1,68 @@
+//
+// Copyright 2010 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/usrp/dboard_id.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/format.hpp>
+#include <sstream>
+#include <iostream>
+
+using namespace uhd::usrp;
+
+dboard_id_t::dboard_id_t(boost::uint16_t id){
+ _id = id;
+}
+
+dboard_id_t dboard_id_t::none(void){
+ return dboard_id_t();
+}
+
+dboard_id_t dboard_id_t::from_uint16(boost::uint16_t uint16){
+ return dboard_id_t(uint16);
+}
+
+boost::uint16_t dboard_id_t::to_uint16(void) const{
+ return _id;
+}
+
+//used with lexical cast to parse a hex string
+template <class T> struct to_hex{
+ T value;
+ operator T() const {return value;}
+ friend std::istream& operator>>(std::istream& in, to_hex& out){
+ in >> std::hex >> out.value;
+ return in;
+ }
+};
+
+dboard_id_t dboard_id_t::from_string(const std::string &string){
+ if (string.substr(0, 2) == "0x"){
+ return dboard_id_t::from_uint16(boost::lexical_cast<to_hex<boost::uint16_t> >(string));
+ }
+ return dboard_id_t::from_uint16(boost::lexical_cast<boost::uint16_t>(string));
+}
+
+std::string dboard_id_t::to_string(void) const{
+ return str(boost::format("0x%04x") % this->to_uint16());
+}
+
+//Note: to_pp_string is implemented in the dboard manager
+//because it needs access to the dboard registration table
+
+bool uhd::usrp::operator==(const dboard_id_t &lhs, const dboard_id_t &rhs){
+ return lhs.to_uint16() == rhs.to_uint16();
+}
diff --git a/host/lib/usrp/dboard_iface.cpp b/host/lib/usrp/dboard_iface.cpp
new file mode 100644
index 000000000..5cc5ea470
--- /dev/null
+++ b/host/lib/usrp/dboard_iface.cpp
@@ -0,0 +1,78 @@
+//
+// Copyright 2010 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/usrp/dboard_iface.hpp>
+#include <uhd/types/dict.hpp>
+
+using namespace uhd::usrp;
+
+struct dboard_iface::impl{
+ uhd::dict<unit_t, boost::uint16_t> pin_ctrl_shadow;
+ uhd::dict<unit_t, uhd::dict<atr_reg_t, boost::uint16_t> > atr_reg_shadow;
+ uhd::dict<unit_t, boost::uint16_t> gpio_ddr_shadow;
+ uhd::dict<unit_t, boost::uint16_t> gpio_out_shadow;
+};
+
+dboard_iface::dboard_iface(void){
+ _impl = UHD_PIMPL_MAKE(impl, ());
+}
+
+template <typename T>
+static T shadow_it(T &shadow, const T &value, const T &mask){
+ shadow = (shadow & ~mask) | (value & mask);
+ return shadow;
+}
+
+void dboard_iface::set_pin_ctrl(
+ unit_t unit, boost::uint16_t value, boost::uint16_t mask
+){
+ _set_pin_ctrl(unit, shadow_it(_impl->pin_ctrl_shadow[unit], value, mask));
+}
+
+boost::uint16_t dboard_iface::get_pin_ctrl(unit_t unit){
+ return _impl->pin_ctrl_shadow[unit];
+}
+
+void dboard_iface::set_atr_reg(
+ unit_t unit, atr_reg_t reg, boost::uint16_t value, boost::uint16_t mask
+){
+ _set_atr_reg(unit, reg, shadow_it(_impl->atr_reg_shadow[unit][reg], value, mask));
+}
+
+boost::uint16_t dboard_iface::get_atr_reg(unit_t unit, atr_reg_t reg){
+ return _impl->atr_reg_shadow[unit][reg];
+}
+
+void dboard_iface::set_gpio_ddr(
+ unit_t unit, boost::uint16_t value, boost::uint16_t mask
+){
+ _set_gpio_ddr(unit, shadow_it(_impl->gpio_ddr_shadow[unit], value, mask));
+}
+
+boost::uint16_t dboard_iface::get_gpio_ddr(unit_t unit){
+ return _impl->gpio_ddr_shadow[unit];
+}
+
+void dboard_iface::set_gpio_out(
+ unit_t unit, boost::uint16_t value, boost::uint16_t mask
+){
+ _set_gpio_out(unit, shadow_it(_impl->gpio_out_shadow[unit], value, mask));
+}
+
+boost::uint16_t dboard_iface::get_gpio_out(unit_t unit){
+ return _impl->gpio_out_shadow[unit];
+}
diff --git a/host/lib/usrp/dboard_manager.cpp b/host/lib/usrp/dboard_manager.cpp
new file mode 100644
index 000000000..340c1d3f9
--- /dev/null
+++ b/host/lib/usrp/dboard_manager.cpp
@@ -0,0 +1,332 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "dboard_ctor_args.hpp"
+#include <uhd/usrp/dboard_manager.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/safe_call.hpp>
+#include <uhd/utils/static.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/types/dict.hpp>
+#include <boost/tuple/tuple.hpp>
+#include <boost/format.hpp>
+#include <boost/bind.hpp>
+#include <boost/foreach.hpp>
+#include <boost/assign/list_of.hpp>
+
+using namespace uhd;
+using namespace uhd::usrp;
+
+/***********************************************************************
+ * dboard key class to use for look-up
+ **********************************************************************/
+class dboard_key_t{
+public:
+ dboard_key_t(const dboard_id_t &id = dboard_id_t::none()):
+ _rx_id(id), _tx_id(id), _xcvr(false){}
+
+ dboard_key_t(const dboard_id_t &rx_id, const dboard_id_t &tx_id):
+ _rx_id(rx_id), _tx_id(tx_id), _xcvr(true){}
+
+ dboard_id_t xx_id(void) const{
+ UHD_ASSERT_THROW(not this->is_xcvr());
+ return this->_rx_id;
+ }
+
+ dboard_id_t rx_id(void) const{
+ UHD_ASSERT_THROW(this->is_xcvr());
+ return this->_rx_id;
+ }
+
+ dboard_id_t tx_id(void) const{
+ UHD_ASSERT_THROW(this->is_xcvr());
+ return this->_tx_id;
+ }
+
+ bool is_xcvr(void) const{
+ return this->_xcvr;
+ }
+
+private:
+ dboard_id_t _rx_id, _tx_id;
+ bool _xcvr;
+};
+
+bool operator==(const dboard_key_t &lhs, const dboard_key_t &rhs){
+ if (lhs.is_xcvr() and rhs.is_xcvr())
+ return lhs.rx_id() == rhs.rx_id() and lhs.tx_id() == rhs.tx_id();
+ if (not lhs.is_xcvr() and not rhs.is_xcvr())
+ return lhs.xx_id() == rhs.xx_id();
+ return false;
+}
+
+/***********************************************************************
+ * storage and registering for dboards
+ **********************************************************************/
+//dboard registry tuple: dboard constructor, canonical name, subdev names
+typedef boost::tuple<dboard_manager::dboard_ctor_t, std::string, std::vector<std::string> > args_t;
+
+//map a dboard id to a dboard constructor
+typedef uhd::dict<dboard_key_t, args_t> id_to_args_map_t;
+UHD_SINGLETON_FCN(id_to_args_map_t, get_id_to_args_map)
+
+static void register_dboard_key(
+ const dboard_key_t &dboard_key,
+ dboard_manager::dboard_ctor_t dboard_ctor,
+ const std::string &name,
+ const std::vector<std::string> &subdev_names
+){
+ UHD_LOGV(always) << "registering: " << name << std::endl;
+ if (get_id_to_args_map().has_key(dboard_key)){
+
+ if (dboard_key.is_xcvr()) throw uhd::key_error(str(boost::format(
+ "The dboard id pair [%s, %s] is already registered to %s."
+ ) % dboard_key.rx_id().to_string() % dboard_key.tx_id().to_string() % get_id_to_args_map()[dboard_key].get<1>()));
+
+ else throw uhd::key_error(str(boost::format(
+ "The dboard id %s is already registered to %s."
+ ) % dboard_key.xx_id().to_string() % get_id_to_args_map()[dboard_key].get<1>()));
+
+ }
+ get_id_to_args_map()[dboard_key] = args_t(dboard_ctor, name, subdev_names);
+}
+
+void dboard_manager::register_dboard(
+ const dboard_id_t &dboard_id,
+ dboard_ctor_t dboard_ctor,
+ const std::string &name,
+ const std::vector<std::string> &subdev_names
+){
+ register_dboard_key(dboard_key_t(dboard_id), dboard_ctor, name, subdev_names);
+}
+
+void dboard_manager::register_dboard(
+ const dboard_id_t &rx_dboard_id,
+ const dboard_id_t &tx_dboard_id,
+ dboard_ctor_t dboard_ctor,
+ const std::string &name,
+ const std::vector<std::string> &subdev_names
+){
+ register_dboard_key(dboard_key_t(rx_dboard_id, tx_dboard_id), dboard_ctor, name, subdev_names);
+}
+
+std::string dboard_id_t::to_cname(void) const{
+ std::string cname;
+ BOOST_FOREACH(const dboard_key_t &key, get_id_to_args_map().keys()){
+ if (
+ (not key.is_xcvr() and *this == key.xx_id()) or
+ (key.is_xcvr() and (*this == key.rx_id() or *this == key.tx_id()))
+ ){
+ if (not cname.empty()) cname += ", ";
+ cname += get_id_to_args_map()[key].get<1>();
+ }
+ }
+ return (cname.empty())? "Unknown" : cname;
+}
+
+std::string dboard_id_t::to_pp_string(void) const{
+ return str(boost::format("%s (%s)") % this->to_cname() % this->to_string());
+}
+
+/***********************************************************************
+ * dboard manager implementation class
+ **********************************************************************/
+class dboard_manager_impl : public dboard_manager{
+
+public:
+ dboard_manager_impl(
+ dboard_id_t rx_dboard_id,
+ dboard_id_t tx_dboard_id,
+ dboard_iface::sptr iface,
+ property_tree::sptr subtree
+ );
+ ~dboard_manager_impl(void);
+
+private:
+ void init(dboard_id_t, dboard_id_t, property_tree::sptr);
+ //list of rx and tx dboards in this dboard_manager
+ //each dboard here is actually a subdevice proxy
+ //the subdevice proxy is internal to the cpp file
+ uhd::dict<std::string, dboard_base::sptr> _rx_dboards;
+ uhd::dict<std::string, dboard_base::sptr> _tx_dboards;
+ dboard_iface::sptr _iface;
+ void set_nice_dboard_if(void);
+};
+
+/***********************************************************************
+ * make routine for dboard manager
+ **********************************************************************/
+dboard_manager::sptr dboard_manager::make(
+ dboard_id_t rx_dboard_id,
+ dboard_id_t tx_dboard_id,
+ dboard_id_t gdboard_id,
+ dboard_iface::sptr iface,
+ property_tree::sptr subtree
+){
+ return dboard_manager::sptr(
+ new dboard_manager_impl(
+ rx_dboard_id,
+ (gdboard_id == dboard_id_t::none())? tx_dboard_id : gdboard_id,
+ iface, subtree
+ )
+ );
+}
+
+/***********************************************************************
+ * implementation class methods
+ **********************************************************************/
+dboard_manager_impl::dboard_manager_impl(
+ dboard_id_t rx_dboard_id,
+ dboard_id_t tx_dboard_id,
+ dboard_iface::sptr iface,
+ property_tree::sptr subtree
+):
+ _iface(iface)
+{
+ try{
+ this->init(rx_dboard_id, tx_dboard_id, subtree);
+ }
+ catch(const std::exception &e){
+ UHD_MSG(error) << boost::format(
+ "The daughterboard manager encountered a recoverable error in init.\n"
+ "Loading the \"unknown\" daughterboard implementations to continue.\n"
+ "The daughterboard cannot operate until this error is resolved.\n"
+ ) << e.what() << std::endl;
+ //clean up the stuff added by the call above
+ if (subtree->exists("rx_frontends")) subtree->remove("rx_frontends");
+ if (subtree->exists("tx_frontends")) subtree->remove("tx_frontends");
+ this->init(dboard_id_t::none(), dboard_id_t::none(), subtree);
+ }
+}
+
+void dboard_manager_impl::init(
+ dboard_id_t rx_dboard_id, dboard_id_t tx_dboard_id, property_tree::sptr subtree
+){
+ //find the dboard key matches for the dboard ids
+ dboard_key_t rx_dboard_key, tx_dboard_key, xcvr_dboard_key;
+ BOOST_FOREACH(const dboard_key_t &key, get_id_to_args_map().keys()){
+ if (key.is_xcvr()){
+ if (rx_dboard_id == key.rx_id() and tx_dboard_id == key.tx_id()) xcvr_dboard_key = key;
+ if (rx_dboard_id == key.rx_id()) rx_dboard_key = key; //kept to handle warning
+ if (tx_dboard_id == key.tx_id()) tx_dboard_key = key; //kept to handle warning
+ }
+ else{
+ if (rx_dboard_id == key.xx_id()) rx_dboard_key = key;
+ if (tx_dboard_id == key.xx_id()) tx_dboard_key = key;
+ }
+ }
+
+ //warn for invalid dboard id xcvr combinations
+ if (not xcvr_dboard_key.is_xcvr() and (rx_dboard_key.is_xcvr() or tx_dboard_key.is_xcvr())){
+ UHD_MSG(warning) << boost::format(
+ "Unknown transceiver board ID combination.\n"
+ "Is your daughter-board mounted properly?\n"
+ "RX dboard ID: %s\n"
+ "TX dboard ID: %s\n"
+ ) % rx_dboard_id.to_pp_string() % tx_dboard_id.to_pp_string();
+ }
+
+ //initialize the gpio pins before creating subdevs
+ set_nice_dboard_if();
+
+ //dboard constructor args
+ dboard_ctor_args_t db_ctor_args;
+ db_ctor_args.db_iface = _iface;
+
+ //make xcvr subdevs
+ if (xcvr_dboard_key.is_xcvr()){
+
+ //extract data for the xcvr dboard key
+ dboard_ctor_t dboard_ctor; std::string name; std::vector<std::string> subdevs;
+ boost::tie(dboard_ctor, name, subdevs) = get_id_to_args_map()[xcvr_dboard_key];
+
+ //create the xcvr object for each subdevice
+ BOOST_FOREACH(const std::string &subdev, subdevs){
+ db_ctor_args.sd_name = subdev;
+ db_ctor_args.rx_id = rx_dboard_id;
+ db_ctor_args.tx_id = tx_dboard_id;
+ db_ctor_args.rx_subtree = subtree->subtree("rx_frontends/" + subdev);
+ db_ctor_args.tx_subtree = subtree->subtree("tx_frontends/" + subdev);
+ dboard_base::sptr xcvr_dboard = dboard_ctor(&db_ctor_args);
+ _rx_dboards[subdev] = xcvr_dboard;
+ _tx_dboards[subdev] = xcvr_dboard;
+ }
+ }
+
+ //make tx and rx subdevs (separate subdevs for rx and tx dboards)
+ else{
+
+ //force the rx key to the unknown board for bad combinations
+ if (rx_dboard_key.is_xcvr() or rx_dboard_key.xx_id() == dboard_id_t::none()){
+ rx_dboard_key = dboard_key_t(0xfff1);
+ }
+
+ //extract data for the rx dboard key
+ dboard_ctor_t rx_dboard_ctor; std::string rx_name; std::vector<std::string> rx_subdevs;
+ boost::tie(rx_dboard_ctor, rx_name, rx_subdevs) = get_id_to_args_map()[rx_dboard_key];
+
+ //make the rx subdevs
+ BOOST_FOREACH(const std::string &subdev, rx_subdevs){
+ db_ctor_args.sd_name = subdev;
+ db_ctor_args.rx_id = rx_dboard_id;
+ db_ctor_args.tx_id = dboard_id_t::none();
+ db_ctor_args.rx_subtree = subtree->subtree("rx_frontends/" + subdev);
+ db_ctor_args.tx_subtree = property_tree::sptr(); //null
+ _rx_dboards[subdev] = rx_dboard_ctor(&db_ctor_args);
+ }
+
+ //force the tx key to the unknown board for bad combinations
+ if (tx_dboard_key.is_xcvr() or tx_dboard_key.xx_id() == dboard_id_t::none()){
+ tx_dboard_key = dboard_key_t(0xfff0);
+ }
+
+ //extract data for the tx dboard key
+ dboard_ctor_t tx_dboard_ctor; std::string tx_name; std::vector<std::string> tx_subdevs;
+ boost::tie(tx_dboard_ctor, tx_name, tx_subdevs) = get_id_to_args_map()[tx_dboard_key];
+
+ //make the tx subdevs
+ BOOST_FOREACH(const std::string &subdev, tx_subdevs){
+ db_ctor_args.sd_name = subdev;
+ db_ctor_args.rx_id = dboard_id_t::none();
+ db_ctor_args.tx_id = tx_dboard_id;
+ db_ctor_args.rx_subtree = property_tree::sptr(); //null
+ db_ctor_args.tx_subtree = subtree->subtree("tx_frontends/" + subdev);
+ _tx_dboards[subdev] = tx_dboard_ctor(&db_ctor_args);
+ }
+ }
+}
+
+dboard_manager_impl::~dboard_manager_impl(void){UHD_SAFE_CALL(
+ set_nice_dboard_if();
+)}
+
+void dboard_manager_impl::set_nice_dboard_if(void){
+ //make a list of possible unit types
+ std::vector<dboard_iface::unit_t> units = boost::assign::list_of
+ (dboard_iface::UNIT_RX)
+ (dboard_iface::UNIT_TX)
+ ;
+
+ //set nice settings on each unit
+ BOOST_FOREACH(dboard_iface::unit_t unit, units){
+ _iface->set_gpio_ddr(unit, 0x0000); //all inputs
+ _iface->set_gpio_out(unit, 0x0000); //all low
+ _iface->set_pin_ctrl(unit, 0x0000); //all gpio
+ _iface->set_clock_enabled(unit, false); //clock off
+ }
+}
diff --git a/host/lib/usrp/e100/CMakeLists.txt b/host/lib/usrp/e100/CMakeLists.txt
new file mode 100644
index 000000000..ac9d8c655
--- /dev/null
+++ b/host/lib/usrp/e100/CMakeLists.txt
@@ -0,0 +1,40 @@
+#
+# Copyright 2010-2011 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# This file included, use CMake directory variables
+########################################################################
+
+########################################################################
+# Conditionally configure the USRP-E100 support
+########################################################################
+LIBUHD_REGISTER_COMPONENT("E100" ENABLE_E100 OFF "ENABLE_LIBUHD;LINUX" OFF)
+
+IF(ENABLE_E100)
+ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include)
+
+ LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/clock_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/codec_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/dboard_iface.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/e100_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/e100_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/e100_mmap_zero_copy.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/fpga_downloader.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/io_impl.cpp
+ )
+ENDIF(ENABLE_E100)
diff --git a/host/lib/usrp/e100/clock_ctrl.cpp b/host/lib/usrp/e100/clock_ctrl.cpp
new file mode 100644
index 000000000..9e355ce17
--- /dev/null
+++ b/host/lib/usrp/e100/clock_ctrl.cpp
@@ -0,0 +1,538 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "clock_ctrl.hpp"
+#include "ad9522_regs.hpp"
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/assert_has.hpp>
+#include <boost/cstdint.hpp>
+#include "e100_regs.hpp" //spi slave constants
+#include <boost/assign/list_of.hpp>
+#include <boost/foreach.hpp>
+#include <boost/format.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/math/common_factor_rt.hpp> //gcd
+#include <algorithm>
+#include <utility>
+
+using namespace uhd;
+
+/***********************************************************************
+ * Constants
+ **********************************************************************/
+static const bool ENABLE_THE_TEST_OUT = true;
+static const double REFERENCE_INPUT_RATE = 10e6;
+
+/***********************************************************************
+ * Helpers
+ **********************************************************************/
+template <typename div_type, typename bypass_type> static void set_clock_divider(
+ size_t divider, div_type &low, div_type &high, bypass_type &bypass
+){
+ high = divider/2 - 1;
+ low = divider - high - 2;
+ bypass = (divider == 1)? 1 : 0;
+}
+
+/***********************************************************************
+ * Clock rate calculation stuff:
+ * Using the internal VCO between 1400 and 1800 MHz
+ **********************************************************************/
+struct clock_settings_type{
+ size_t ref_clock_doubler, r_counter, a_counter, b_counter, prescaler, vco_divider, chan_divider;
+ size_t get_n_counter(void) const{return prescaler * b_counter + a_counter;}
+ double get_ref_rate(void) const{return REFERENCE_INPUT_RATE * ref_clock_doubler;}
+ double get_vco_rate(void) const{return get_ref_rate()/r_counter * get_n_counter();}
+ double get_chan_rate(void) const{return get_vco_rate()/vco_divider;}
+ double get_out_rate(void) const{return get_chan_rate()/chan_divider;}
+ std::string to_pp_string(void) const{
+ return str(boost::format(
+ " r_counter: %d\n"
+ " a_counter: %d\n"
+ " b_counter: %d\n"
+ " prescaler: %d\n"
+ " vco_divider: %d\n"
+ " chan_divider: %d\n"
+ " vco_rate: %fMHz\n"
+ " chan_rate: %fMHz\n"
+ " out_rate: %fMHz\n"
+ )
+ % r_counter
+ % a_counter
+ % b_counter
+ % prescaler
+ % vco_divider
+ % chan_divider
+ % (get_vco_rate()/1e6)
+ % (get_chan_rate()/1e6)
+ % (get_out_rate()/1e6)
+ );
+ }
+};
+
+//! gives the greatest divisor of num between 1 and max inclusive
+template<typename T> static inline T greatest_divisor(T num, T max){
+ for (T i = max; i > 1; i--) if (num%i == 0) return i; return 1;
+}
+
+//! gives the least divisor of num between min and num exclusive
+template<typename T> static inline T least_divisor(T num, T min){
+ for (T i = min; i < num; i++) if (num%i == 0) return i; return 1;
+}
+
+static clock_settings_type get_clock_settings(double rate){
+ clock_settings_type cs;
+ cs.ref_clock_doubler = 2; //always doubling
+ cs.prescaler = 8; //set to 8 when input is under 2400 MHz
+
+ //basic formulas used below:
+ //out_rate*X = ref_rate*Y
+ //X = i*ref_rate/gcd
+ //Y = i*out_rate/gcd
+ //X = chan_div * vco_div * R
+ //Y = P*B + A
+
+ const boost::uint64_t out_rate = boost::uint64_t(rate);
+ const boost::uint64_t ref_rate = boost::uint64_t(cs.get_ref_rate());
+ const size_t gcd = size_t(boost::math::gcd(ref_rate, out_rate));
+
+ for (size_t i = 1; i <= 100; i++){
+ const size_t X = i*ref_rate/gcd;
+ const size_t Y = i*out_rate/gcd;
+
+ //determine A and B (P is fixed)
+ cs.b_counter = Y/cs.prescaler;
+ cs.a_counter = Y - cs.b_counter*cs.prescaler;
+
+ static const double vco_bound_pad = 100e6;
+ for ( //calculate an r divider that fits into the bounds of the vco
+ cs.r_counter = size_t(cs.get_n_counter()*cs.get_ref_rate()/(1800e6 - vco_bound_pad));
+ cs.r_counter <= size_t(cs.get_n_counter()*cs.get_ref_rate()/(1400e6 + vco_bound_pad))
+ and cs.r_counter > 0; cs.r_counter++
+ ){
+
+ //determine chan_div and vco_div
+ //and fill in that order of preference
+ cs.chan_divider = greatest_divisor<size_t>(X/cs.r_counter, 32);
+ cs.vco_divider = greatest_divisor<size_t>(X/cs.chan_divider/cs.r_counter, 6);
+
+ //avoid a vco divider of 1 (if possible)
+ if (cs.vco_divider == 1){
+ cs.vco_divider = least_divisor<size_t>(cs.chan_divider, 2);
+ cs.chan_divider /= cs.vco_divider;
+ }
+
+ UHD_LOGV(always)
+ << "gcd " << gcd << std::endl
+ << "X " << X << std::endl
+ << "Y " << Y << std::endl
+ << cs.to_pp_string() << std::endl
+ ;
+
+ //filter limits on the counters
+ if (cs.vco_divider == 1) continue;
+ if (cs.r_counter >= (1<<14)) continue;
+ if (cs.b_counter == 2) continue;
+ if (cs.b_counter == 1 and cs.a_counter != 0) continue;
+ if (cs.b_counter >= (1<<13)) continue;
+ if (cs.a_counter >= (1<<6)) continue;
+ if (cs.get_vco_rate() > 1800e6 - vco_bound_pad) continue;
+ if (cs.get_vco_rate() < 1400e6 + vco_bound_pad) continue;
+ if (cs.get_out_rate() != rate) continue;
+
+ UHD_MSG(status) << "USRP-E100 clock control: " << i << std::endl << cs.to_pp_string() << std::endl;
+ return cs;
+ }
+ }
+
+ throw uhd::value_error(str(boost::format(
+ "USRP-E100 clock control: could not calculate settings for clock rate %fMHz"
+ ) % (rate/1e6)));
+}
+
+/***********************************************************************
+ * Clock Control Implementation
+ **********************************************************************/
+class e100_clock_ctrl_impl : public e100_clock_ctrl{
+public:
+ e100_clock_ctrl_impl(spi_iface::sptr iface, double master_clock_rate, const bool dboard_clocks_diff):
+ _dboard_clocks_diff(dboard_clocks_diff)
+ {
+ _iface = iface;
+ _chan_rate = 0.0;
+ _out_rate = 0.0;
+
+ //perform soft-reset
+ _ad9522_regs.soft_reset = 1;
+ this->send_reg(0x000);
+ this->latch_regs();
+ _ad9522_regs.soft_reset = 0;
+
+ //init the clock gen registers
+ //Note: out0 should already be clocking the FPGA or this isnt going to work
+ _ad9522_regs.sdo_active = ad9522_regs_t::SDO_ACTIVE_SDO_SDIO;
+ _ad9522_regs.enb_stat_eeprom_at_stat_pin = 0; //use status pin
+ _ad9522_regs.status_pin_control = 0x1; //n divider
+ _ad9522_regs.ld_pin_control = 0x00; //dld
+ _ad9522_regs.refmon_pin_control = 0x12; //show ref2
+ _ad9522_regs.lock_detect_counter = ad9522_regs_t::LOCK_DETECT_COUNTER_16CYC;
+
+ this->use_internal_ref();
+
+ //initialize the FPGA clock rate
+ UHD_MSG(status) << boost::format("Initializing FPGA clock to %fMHz...") % (master_clock_rate/1e6) << std::endl;
+ this->set_fpga_clock_rate(master_clock_rate);
+
+ this->enable_test_clock(ENABLE_THE_TEST_OUT);
+ this->enable_rx_dboard_clock(false);
+ this->enable_tx_dboard_clock(false);
+ }
+
+ ~e100_clock_ctrl_impl(void){
+ this->enable_test_clock(ENABLE_THE_TEST_OUT);
+ this->enable_rx_dboard_clock(false);
+ this->enable_tx_dboard_clock(false);
+ }
+
+ /***********************************************************************
+ * Clock rate control:
+ * - set clock rate w/ internal VCO
+ * - set clock rate w/ external VCXO
+ **********************************************************************/
+ void set_clock_settings_with_internal_vco(double rate){
+ const clock_settings_type cs = get_clock_settings(rate);
+
+ //set the rates to private variables so the implementation knows!
+ _chan_rate = cs.get_chan_rate();
+ _out_rate = cs.get_out_rate();
+
+ _ad9522_regs.enable_clock_doubler = (cs.ref_clock_doubler == 2)? 1 : 0;
+
+ _ad9522_regs.set_r_counter(cs.r_counter);
+ _ad9522_regs.a_counter = cs.a_counter;
+ _ad9522_regs.set_b_counter(cs.b_counter);
+ UHD_ASSERT_THROW(cs.prescaler == 8); //assumes this below:
+ _ad9522_regs.prescaler_p = ad9522_regs_t::PRESCALER_P_DIV8_9;
+
+ _ad9522_regs.pll_power_down = ad9522_regs_t::PLL_POWER_DOWN_NORMAL;
+ _ad9522_regs.cp_current = ad9522_regs_t::CP_CURRENT_1_2MA;
+
+ _ad9522_regs.bypass_vco_divider = 0;
+ switch(cs.vco_divider){
+ case 1: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV1; break;
+ case 2: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV2; break;
+ case 3: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV3; break;
+ case 4: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV4; break;
+ case 5: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV5; break;
+ case 6: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV6; break;
+ }
+ _ad9522_regs.select_vco_or_clock = ad9522_regs_t::SELECT_VCO_OR_CLOCK_VCO;
+
+ //setup fpga master clock
+ _ad9522_regs.out0_format = ad9522_regs_t::OUT0_FORMAT_LVDS;
+ set_clock_divider(cs.chan_divider,
+ _ad9522_regs.divider0_low_cycles,
+ _ad9522_regs.divider0_high_cycles,
+ _ad9522_regs.divider0_bypass
+ );
+
+ //setup codec clock
+ _ad9522_regs.out3_format = ad9522_regs_t::OUT3_FORMAT_LVDS;
+ set_clock_divider(cs.chan_divider,
+ _ad9522_regs.divider1_low_cycles,
+ _ad9522_regs.divider1_high_cycles,
+ _ad9522_regs.divider1_bypass
+ );
+
+ this->send_all_regs();
+ calibrate_now();
+ }
+
+ void set_clock_settings_with_external_vcxo(double rate){
+ //set the rates to private variables so the implementation knows!
+ _chan_rate = rate;
+ _out_rate = rate;
+
+ _ad9522_regs.enable_clock_doubler = 1; //doubler always on
+ const double ref_rate = REFERENCE_INPUT_RATE*2;
+
+ //bypass prescaler such that N = B
+ long gcd = boost::math::gcd(long(ref_rate), long(rate));
+ _ad9522_regs.set_r_counter(int(ref_rate/gcd));
+ _ad9522_regs.a_counter = 0;
+ _ad9522_regs.set_b_counter(int(rate/gcd));
+ _ad9522_regs.prescaler_p = ad9522_regs_t::PRESCALER_P_DIV1;
+
+ //setup external vcxo
+ _ad9522_regs.pll_power_down = ad9522_regs_t::PLL_POWER_DOWN_NORMAL;
+ _ad9522_regs.cp_current = ad9522_regs_t::CP_CURRENT_1_2MA;
+ _ad9522_regs.bypass_vco_divider = 1;
+ _ad9522_regs.select_vco_or_clock = ad9522_regs_t::SELECT_VCO_OR_CLOCK_EXTERNAL;
+
+ //setup fpga master clock
+ _ad9522_regs.out0_format = ad9522_regs_t::OUT0_FORMAT_LVDS;
+ _ad9522_regs.divider0_bypass = 1;
+
+ //setup codec clock
+ _ad9522_regs.out3_format = ad9522_regs_t::OUT3_FORMAT_LVDS;
+ _ad9522_regs.divider1_bypass = 1;
+
+ this->send_all_regs();
+ }
+
+ void set_fpga_clock_rate(double rate){
+ if (_out_rate == rate) return;
+ if (rate == 61.44e6) set_clock_settings_with_external_vcxo(rate);
+ else set_clock_settings_with_internal_vco(rate);
+ set_rx_dboard_clock_rate(rate);
+ set_tx_dboard_clock_rate(rate);
+ }
+
+ double get_fpga_clock_rate(void){
+ return this->_out_rate;
+ }
+
+ /***********************************************************************
+ * Special test clock output
+ **********************************************************************/
+ void enable_test_clock(bool enb){
+ //setup test clock (same divider as codec clock)
+ _ad9522_regs.out4_format = ad9522_regs_t::OUT4_FORMAT_CMOS;
+ _ad9522_regs.out4_cmos_configuration = (enb)?
+ ad9522_regs_t::OUT4_CMOS_CONFIGURATION_A_ON :
+ ad9522_regs_t::OUT4_CMOS_CONFIGURATION_OFF;
+ this->send_reg(0x0F4);
+ this->latch_regs();
+ }
+
+ /***********************************************************************
+ * RX Dboard Clock Control (output 9, divider 3)
+ **********************************************************************/
+ void enable_rx_dboard_clock(bool enb){
+ if (_dboard_clocks_diff){
+ _ad9522_regs.out9_format = ad9522_regs_t::OUT9_FORMAT_LVDS;
+ _ad9522_regs.out9_lvds_power_down = enb? 0 : 1;
+ }
+ else{
+ _ad9522_regs.out9_format = ad9522_regs_t::OUT9_FORMAT_CMOS;
+ _ad9522_regs.out9_cmos_configuration = (enb)?
+ ad9522_regs_t::OUT9_CMOS_CONFIGURATION_B_ON :
+ ad9522_regs_t::OUT9_CMOS_CONFIGURATION_OFF;
+ }
+ this->send_reg(0x0F9);
+ this->latch_regs();
+ }
+
+ std::vector<double> get_rx_dboard_clock_rates(void){
+ std::vector<double> rates;
+ for(size_t div = 1; div <= 16+16; div++)
+ rates.push_back(this->_chan_rate/div);
+ return rates;
+ }
+
+ void set_rx_dboard_clock_rate(double rate){
+ assert_has(get_rx_dboard_clock_rates(), rate, "rx dboard clock rate");
+ _rx_clock_rate = rate;
+ size_t divider = size_t(this->_chan_rate/rate);
+ //set the divider registers
+ set_clock_divider(divider,
+ _ad9522_regs.divider3_low_cycles,
+ _ad9522_regs.divider3_high_cycles,
+ _ad9522_regs.divider3_bypass
+ );
+ this->send_reg(0x199);
+ this->send_reg(0x19a);
+ this->soft_sync();
+ }
+
+ double get_rx_clock_rate(void){
+ return _rx_clock_rate;
+ }
+
+ /***********************************************************************
+ * TX Dboard Clock Control (output 6, divider 2)
+ **********************************************************************/
+ void enable_tx_dboard_clock(bool enb){
+ if (_dboard_clocks_diff){
+ _ad9522_regs.out6_format = ad9522_regs_t::OUT6_FORMAT_LVDS;
+ _ad9522_regs.out6_lvds_power_down = enb? 0 : 1;
+ }
+ else{
+ _ad9522_regs.out6_format = ad9522_regs_t::OUT6_FORMAT_CMOS;
+ _ad9522_regs.out6_cmos_configuration = (enb)?
+ ad9522_regs_t::OUT6_CMOS_CONFIGURATION_B_ON :
+ ad9522_regs_t::OUT6_CMOS_CONFIGURATION_OFF;
+ }
+ this->send_reg(0x0F6);
+ this->latch_regs();
+ }
+
+ std::vector<double> get_tx_dboard_clock_rates(void){
+ return get_rx_dboard_clock_rates(); //same master clock, same dividers...
+ }
+
+ void set_tx_dboard_clock_rate(double rate){
+ assert_has(get_tx_dboard_clock_rates(), rate, "tx dboard clock rate");
+ _tx_clock_rate = rate;
+ size_t divider = size_t(this->_chan_rate/rate);
+ //set the divider registers
+ set_clock_divider(divider,
+ _ad9522_regs.divider2_low_cycles,
+ _ad9522_regs.divider2_high_cycles,
+ _ad9522_regs.divider2_bypass
+ );
+ this->send_reg(0x196);
+ this->send_reg(0x197);
+ this->soft_sync();
+ }
+
+ double get_tx_clock_rate(void){
+ return _tx_clock_rate;
+ }
+
+ /***********************************************************************
+ * Clock reference control
+ **********************************************************************/
+ void use_internal_ref(void) {
+ _ad9522_regs.enable_ref2 = 1;
+ _ad9522_regs.enable_ref1 = 0;
+ _ad9522_regs.select_ref = ad9522_regs_t::SELECT_REF_REF2;
+ _ad9522_regs.enb_auto_ref_switchover = ad9522_regs_t::ENB_AUTO_REF_SWITCHOVER_MANUAL;
+ this->send_reg(0x01C);
+ this->latch_regs();
+ }
+
+ void use_external_ref(void) {
+ _ad9522_regs.enable_ref2 = 0;
+ _ad9522_regs.enable_ref1 = 1;
+ _ad9522_regs.select_ref = ad9522_regs_t::SELECT_REF_REF1;
+ _ad9522_regs.enb_auto_ref_switchover = ad9522_regs_t::ENB_AUTO_REF_SWITCHOVER_MANUAL;
+ this->send_reg(0x01C);
+ this->latch_regs();
+ }
+
+ void use_auto_ref(void) {
+ _ad9522_regs.enable_ref2 = 1;
+ _ad9522_regs.enable_ref1 = 1;
+ _ad9522_regs.select_ref = ad9522_regs_t::SELECT_REF_REF1;
+ _ad9522_regs.enb_auto_ref_switchover = ad9522_regs_t::ENB_AUTO_REF_SWITCHOVER_AUTO;
+ this->send_reg(0x01C);
+ this->latch_regs();
+ }
+
+ bool get_locked(void){
+ static const boost::uint8_t addr = 0x01F;
+ boost::uint32_t reg = _iface->read_spi(
+ UE_SPI_SS_AD9522, spi_config_t::EDGE_RISE,
+ _ad9522_regs.get_read_reg(addr), 24
+ );
+ _ad9522_regs.set_reg(addr, reg);
+ return _ad9522_regs.digital_lock_detect != 0;
+ }
+
+private:
+ spi_iface::sptr _iface;
+ const bool _dboard_clocks_diff;
+ ad9522_regs_t _ad9522_regs;
+ double _out_rate; //rate at the fpga and codec
+ double _chan_rate; //rate before final dividers
+ double _rx_clock_rate, _tx_clock_rate;
+
+ void latch_regs(void){
+ _ad9522_regs.io_update = 1;
+ this->send_reg(0x232);
+ }
+
+ void send_reg(boost::uint16_t addr){
+ boost::uint32_t reg = _ad9522_regs.get_write_reg(addr);
+ UHD_LOGV(often) << "clock control write reg: " << std::hex << reg << std::endl;
+ _iface->write_spi(
+ UE_SPI_SS_AD9522,
+ spi_config_t::EDGE_RISE,
+ reg, 24
+ );
+ }
+
+ void calibrate_now(void){
+ //vco calibration routine:
+ _ad9522_regs.vco_calibration_now = 0;
+ this->send_reg(0x18);
+ this->latch_regs();
+ _ad9522_regs.vco_calibration_now = 1;
+ this->send_reg(0x18);
+ this->latch_regs();
+ //wait for calibration done:
+ static const boost::uint8_t addr = 0x01F;
+ for (size_t ms10 = 0; ms10 < 100; ms10++){
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+ boost::uint32_t reg = _iface->read_spi(
+ UE_SPI_SS_AD9522, spi_config_t::EDGE_RISE,
+ _ad9522_regs.get_read_reg(addr), 24
+ );
+ _ad9522_regs.set_reg(addr, reg);
+ if (_ad9522_regs.vco_calibration_finished) goto wait_for_ld;
+ }
+ UHD_MSG(error) << "USRP-E100 clock control: VCO calibration timeout" << std::endl;
+ wait_for_ld:
+ //wait for digital lock detect:
+ for (size_t ms10 = 0; ms10 < 100; ms10++){
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+ boost::uint32_t reg = _iface->read_spi(
+ UE_SPI_SS_AD9522, spi_config_t::EDGE_RISE,
+ _ad9522_regs.get_read_reg(addr), 24
+ );
+ _ad9522_regs.set_reg(addr, reg);
+ if (_ad9522_regs.digital_lock_detect) return;
+ }
+ UHD_MSG(error) << "USRP-E100 clock control: lock detection timeout" << std::endl;
+ }
+
+ void soft_sync(void){
+ _ad9522_regs.soft_sync = 1;
+ this->send_reg(0x230);
+ this->latch_regs();
+ _ad9522_regs.soft_sync = 0;
+ this->send_reg(0x230);
+ this->latch_regs();
+ }
+
+ void send_all_regs(void){
+ //setup a list of register ranges to write
+ typedef std::pair<boost::uint16_t, boost::uint16_t> range_t;
+ static const std::vector<range_t> ranges = boost::assign::list_of
+ (range_t(0x000, 0x000)) (range_t(0x010, 0x01F))
+ (range_t(0x0F0, 0x0FD)) (range_t(0x190, 0x19B))
+ (range_t(0x1E0, 0x1E1)) (range_t(0x230, 0x230))
+ ;
+
+ //write initial register values and latch/update
+ BOOST_FOREACH(const range_t &range, ranges){
+ for(boost::uint16_t addr = range.first; addr <= range.second; addr++){
+ this->send_reg(addr);
+ }
+ }
+ this->latch_regs();
+ }
+};
+
+/***********************************************************************
+ * Clock Control Make
+ **********************************************************************/
+e100_clock_ctrl::sptr e100_clock_ctrl::make(spi_iface::sptr iface, double master_clock_rate, const bool dboard_clocks_diff){
+ return sptr(new e100_clock_ctrl_impl(iface, master_clock_rate, dboard_clocks_diff));
+}
diff --git a/host/lib/usrp/e100/clock_ctrl.hpp b/host/lib/usrp/e100/clock_ctrl.hpp
new file mode 100644
index 000000000..803265556
--- /dev/null
+++ b/host/lib/usrp/e100/clock_ctrl.hpp
@@ -0,0 +1,127 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_USRP_E100_CLOCK_CTRL_HPP
+#define INCLUDED_USRP_E100_CLOCK_CTRL_HPP
+
+#include <uhd/types/serial.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+#include <vector>
+
+/*!
+ * The usrp-e clock control:
+ * - Setup system clocks.
+ * - Disable/enable clock lines.
+ */
+class e100_clock_ctrl : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<e100_clock_ctrl> sptr;
+
+ /*!
+ * Make a new clock control object.
+ * \param iface the spi iface object
+ * \param master clock rate the FPGA rate
+ * param dboard_clocks_diff are they differential?
+ * \return the clock control object
+ */
+ static sptr make(uhd::spi_iface::sptr iface, double master_clock_rate, const bool dboard_clocks_diff);
+
+ /*!
+ * Set the rate of the fpga clock line.
+ * Throws if rate is not valid.
+ * \param rate the new rate in Hz
+ */
+ virtual void set_fpga_clock_rate(double rate) = 0;
+
+ /*!
+ * Get the rate of the fpga clock line.
+ * \return the fpga clock rate in Hz
+ */
+ virtual double get_fpga_clock_rate(void) = 0;
+
+ /*!
+ * Get the possible rates of the rx dboard clock.
+ * \return a vector of clock rates in Hz
+ */
+ virtual std::vector<double> get_rx_dboard_clock_rates(void) = 0;
+
+ /*!
+ * Get the possible rates of the tx dboard clock.
+ * \return a vector of clock rates in Hz
+ */
+ virtual std::vector<double> get_tx_dboard_clock_rates(void) = 0;
+
+ /*!
+ * Set the rx dboard clock rate to a possible rate.
+ * \param rate the new clock rate in Hz
+ * \throw exception when rate cannot be achieved
+ */
+ virtual void set_rx_dboard_clock_rate(double rate) = 0;
+
+ /*!
+ * Set the tx dboard clock rate to a possible rate.
+ * \param rate the new clock rate in Hz
+ * \throw exception when rate cannot be achieved
+ */
+ virtual void set_tx_dboard_clock_rate(double rate) = 0;
+
+ /*!
+ * Get the current rx dboard clock rate.
+ * \return the clock rate in Hz
+ */
+ virtual double get_rx_clock_rate(void) = 0;
+
+ /*!
+ * Get the current tx dboard clock rate.
+ * \return the clock rate in Hz
+ */
+ virtual double get_tx_clock_rate(void) = 0;
+
+ /*!
+ * Enable/disable the rx dboard clock.
+ * \param enb true to enable
+ */
+ virtual void enable_rx_dboard_clock(bool enb) = 0;
+
+ /*!
+ * Enable/disable the tx dboard clock.
+ * \param enb true to enable
+ */
+ virtual void enable_tx_dboard_clock(bool enb) = 0;
+
+ /*!
+ * Use the internal TCXO reference
+ */
+ virtual void use_internal_ref(void) = 0;
+
+ /*!
+ * Use the external SMA reference
+ */
+ virtual void use_external_ref(void) = 0;
+
+ /*!
+ * Use external if available, internal otherwise
+ */
+ virtual void use_auto_ref(void) = 0;
+
+ //! Is the reference locked?
+ virtual bool get_locked(void) = 0;
+
+};
+
+#endif /* INCLUDED_USRP_E100_CLOCK_CTRL_HPP */
diff --git a/host/lib/usrp/e100/codec_ctrl.cpp b/host/lib/usrp/e100/codec_ctrl.cpp
new file mode 100644
index 000000000..13b3bc951
--- /dev/null
+++ b/host/lib/usrp/e100/codec_ctrl.cpp
@@ -0,0 +1,288 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "codec_ctrl.hpp"
+#include "ad9862_regs.hpp"
+#include <uhd/utils/log.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/tuple/tuple.hpp>
+#include <boost/math/special_functions/round.hpp>
+#include "e100_regs.hpp" //spi slave constants
+#include <boost/assign/list_of.hpp>
+
+using namespace uhd;
+
+const gain_range_t e100_codec_ctrl::tx_pga_gain_range(-20, 0, double(0.1));
+const gain_range_t e100_codec_ctrl::rx_pga_gain_range(0, 20, 1);
+
+/***********************************************************************
+ * Codec Control Implementation
+ **********************************************************************/
+class e100_codec_ctrl_impl : public e100_codec_ctrl{
+public:
+ //structors
+ e100_codec_ctrl_impl(spi_iface::sptr iface);
+ ~e100_codec_ctrl_impl(void);
+
+ //aux adc and dac control
+ double read_aux_adc(aux_adc_t which);
+ void write_aux_dac(aux_dac_t which, double volts);
+
+ //pga gain control
+ void set_tx_pga_gain(double);
+ double get_tx_pga_gain(void);
+ void set_rx_pga_gain(double, char);
+ double get_rx_pga_gain(char);
+
+private:
+ spi_iface::sptr _iface;
+ ad9862_regs_t _ad9862_regs;
+ void send_reg(boost::uint8_t addr);
+ void recv_reg(boost::uint8_t addr);
+};
+
+/***********************************************************************
+ * Codec Control Structors
+ **********************************************************************/
+e100_codec_ctrl_impl::e100_codec_ctrl_impl(spi_iface::sptr iface){
+ _iface = iface;
+
+ //soft reset
+ _ad9862_regs.soft_reset = 1;
+ this->send_reg(0);
+
+ //initialize the codec register settings
+ _ad9862_regs.sdio_bidir = ad9862_regs_t::SDIO_BIDIR_SDIO_SDO;
+ _ad9862_regs.lsb_first = ad9862_regs_t::LSB_FIRST_MSB;
+ _ad9862_regs.soft_reset = 0;
+
+ //setup rx side of codec
+ _ad9862_regs.byp_buffer_a = 0;
+ _ad9862_regs.byp_buffer_b = 0;
+ _ad9862_regs.buffer_a_pd = 0;
+ _ad9862_regs.buffer_b_pd = 0;
+ _ad9862_regs.rx_pga_a = 0;//0x1f; //TODO bring under api control
+ _ad9862_regs.rx_pga_b = 0;//0x1f; //TODO bring under api control
+ _ad9862_regs.rx_twos_comp = 1;
+ _ad9862_regs.rx_hilbert = ad9862_regs_t::RX_HILBERT_DIS;
+ _ad9862_regs.shared_ref = 1;
+
+ //setup tx side of codec
+ _ad9862_regs.two_data_paths = ad9862_regs_t::TWO_DATA_PATHS_BOTH;
+ _ad9862_regs.interleaved = ad9862_regs_t::INTERLEAVED_INTERLEAVED;
+ _ad9862_regs.tx_retime = ad9862_regs_t::TX_RETIME_CLKOUT2;
+ _ad9862_regs.tx_pga_gain = 199; //TODO bring under api control
+ _ad9862_regs.tx_hilbert = ad9862_regs_t::TX_HILBERT_DIS;
+ _ad9862_regs.interp = ad9862_regs_t::INTERP_2;
+ _ad9862_regs.tx_twos_comp = 1;
+ _ad9862_regs.fine_mode = ad9862_regs_t::FINE_MODE_BYPASS;
+ _ad9862_regs.coarse_mod = ad9862_regs_t::COARSE_MOD_BYPASS;
+ _ad9862_regs.dac_a_coarse_gain = 0x3;
+ _ad9862_regs.dac_b_coarse_gain = 0x3;
+ _ad9862_regs.edges = ad9862_regs_t::EDGES_NORMAL;
+
+ //setup the dll
+ _ad9862_regs.input_clk_ctrl = ad9862_regs_t::INPUT_CLK_CTRL_EXTERNAL;
+ _ad9862_regs.dll_mult = ad9862_regs_t::DLL_MULT_2;
+ _ad9862_regs.dll_mode = ad9862_regs_t::DLL_MODE_FAST;
+ _ad9862_regs.hs_duty_cycle = 1;
+ _ad9862_regs.clk_duty = 1;
+
+ //disable clkout1 and clkout2
+ _ad9862_regs.dis1 = ad9862_regs_t::DIS1_DIS;
+ //_ad9862_regs.dis2 = ad9862_regs_t::DIS2_DIS; needed for transmit
+
+ //write the register settings to the codec
+ for (uint8_t addr = 0; addr <= 25; addr++){
+ this->send_reg(addr);
+ }
+
+ //always start conversions for aux ADC
+ _ad9862_regs.start_a = 1;
+ _ad9862_regs.start_b = 1;
+
+ //aux adc clock
+ _ad9862_regs.clk_4 = ad9862_regs_t::CLK_4_1_4;
+ this->send_reg(34);
+ this->send_reg(35);
+}
+
+e100_codec_ctrl_impl::~e100_codec_ctrl_impl(void){
+ //set aux dacs to zero
+ this->write_aux_dac(AUX_DAC_A, 0);
+ this->write_aux_dac(AUX_DAC_B, 0);
+ this->write_aux_dac(AUX_DAC_C, 0);
+ this->write_aux_dac(AUX_DAC_D, 0);
+
+ //power down
+ _ad9862_regs.all_rx_pd = 1;
+ this->send_reg(1);
+ _ad9862_regs.tx_digital_pd = 1;
+ _ad9862_regs.tx_analog_pd = ad9862_regs_t::TX_ANALOG_PD_BOTH;
+ this->send_reg(8);
+}
+
+/***********************************************************************
+ * Codec Control Gain Control Methods
+ **********************************************************************/
+static const int mtpgw = 255; //maximum tx pga gain word
+
+void e100_codec_ctrl_impl::set_tx_pga_gain(double gain){
+ int gain_word = int(mtpgw*(gain - tx_pga_gain_range.start())/(tx_pga_gain_range.stop() - tx_pga_gain_range.start()));
+ _ad9862_regs.tx_pga_gain = uhd::clip(gain_word, 0, mtpgw);
+ this->send_reg(16);
+}
+
+double e100_codec_ctrl_impl::get_tx_pga_gain(void){
+ return (_ad9862_regs.tx_pga_gain*(tx_pga_gain_range.stop() - tx_pga_gain_range.start())/mtpgw) + tx_pga_gain_range.start();
+}
+
+static const int mrpgw = 0x14; //maximum rx pga gain word
+
+void e100_codec_ctrl_impl::set_rx_pga_gain(double gain, char which){
+ int gain_word = int(mrpgw*(gain - rx_pga_gain_range.start())/(rx_pga_gain_range.stop() - rx_pga_gain_range.start()));
+ gain_word = uhd::clip(gain_word, 0, mrpgw);
+ switch(which){
+ case 'A':
+ _ad9862_regs.rx_pga_a = gain_word;
+ this->send_reg(2);
+ return;
+ case 'B':
+ _ad9862_regs.rx_pga_b = gain_word;
+ this->send_reg(3);
+ return;
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+double e100_codec_ctrl_impl::get_rx_pga_gain(char which){
+ int gain_word;
+ switch(which){
+ case 'A': gain_word = _ad9862_regs.rx_pga_a; break;
+ case 'B': gain_word = _ad9862_regs.rx_pga_b; break;
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+ return (gain_word*(rx_pga_gain_range.stop() - rx_pga_gain_range.start())/mrpgw) + rx_pga_gain_range.start();
+}
+
+/***********************************************************************
+ * Codec Control AUX ADC Methods
+ **********************************************************************/
+static double aux_adc_to_volts(boost::uint8_t high, boost::uint8_t low){
+ return double((boost::uint16_t(high) << 2) | low)*3.3/0x3ff;
+}
+
+double e100_codec_ctrl_impl::read_aux_adc(aux_adc_t which){
+ switch(which){
+ case AUX_ADC_A1:
+ _ad9862_regs.select_a = ad9862_regs_t::SELECT_A_AUX_ADC1;
+ this->send_reg(34); //start conversion and select mux
+ this->recv_reg(28); //read the value (2 bytes, 2 reads)
+ this->recv_reg(29);
+ return aux_adc_to_volts(_ad9862_regs.aux_adc_a1_9_2, _ad9862_regs.aux_adc_a1_1_0);
+
+ case AUX_ADC_A2:
+ _ad9862_regs.select_a = ad9862_regs_t::SELECT_A_AUX_ADC2;
+ this->send_reg(34); //start conversion and select mux
+ this->recv_reg(26); //read the value (2 bytes, 2 reads)
+ this->recv_reg(27);
+ return aux_adc_to_volts(_ad9862_regs.aux_adc_a2_9_2, _ad9862_regs.aux_adc_a2_1_0);
+
+ case AUX_ADC_B1:
+ _ad9862_regs.select_b = ad9862_regs_t::SELECT_B_AUX_ADC1;
+ this->send_reg(34); //start conversion and select mux
+ this->recv_reg(32); //read the value (2 bytes, 2 reads)
+ this->recv_reg(33);
+ return aux_adc_to_volts(_ad9862_regs.aux_adc_b1_9_2, _ad9862_regs.aux_adc_b1_1_0);
+
+ case AUX_ADC_B2:
+ _ad9862_regs.select_b = ad9862_regs_t::SELECT_B_AUX_ADC2;
+ this->send_reg(34); //start conversion and select mux
+ this->recv_reg(30); //read the value (2 bytes, 2 reads)
+ this->recv_reg(31);
+ return aux_adc_to_volts(_ad9862_regs.aux_adc_b2_9_2, _ad9862_regs.aux_adc_b2_1_0);
+ }
+ UHD_THROW_INVALID_CODE_PATH();
+}
+
+/***********************************************************************
+ * Codec Control AUX DAC Methods
+ **********************************************************************/
+void e100_codec_ctrl_impl::write_aux_dac(aux_dac_t which, double volts){
+ //special case for aux dac d (aka sigma delta word)
+ if (which == AUX_DAC_D){
+ boost::uint16_t dac_word = uhd::clip(boost::math::iround(volts*0xfff/3.3), 0, 0xfff);
+ _ad9862_regs.sig_delt_11_4 = boost::uint8_t(dac_word >> 4);
+ _ad9862_regs.sig_delt_3_0 = boost::uint8_t(dac_word & 0xf);
+ this->send_reg(42);
+ this->send_reg(43);
+ return;
+ }
+
+ //calculate the dac word for aux dac a, b, c
+ boost::uint8_t dac_word = uhd::clip(boost::math::iround(volts*0xff/3.3), 0, 0xff);
+
+ //setup a lookup table for the aux dac params (reg ref, reg addr)
+ typedef boost::tuple<boost::uint8_t*, boost::uint8_t> dac_params_t;
+ uhd::dict<aux_dac_t, dac_params_t> aux_dac_to_params = boost::assign::map_list_of
+ (AUX_DAC_A, dac_params_t(&_ad9862_regs.aux_dac_a, 36))
+ (AUX_DAC_B, dac_params_t(&_ad9862_regs.aux_dac_b, 37))
+ (AUX_DAC_C, dac_params_t(&_ad9862_regs.aux_dac_c, 38))
+ ;
+
+ //set the aux dac register
+ UHD_ASSERT_THROW(aux_dac_to_params.has_key(which));
+ boost::uint8_t *reg_ref, reg_addr;
+ boost::tie(reg_ref, reg_addr) = aux_dac_to_params[which];
+ *reg_ref = dac_word;
+ this->send_reg(reg_addr);
+}
+
+/***********************************************************************
+ * Codec Control SPI Methods
+ **********************************************************************/
+void e100_codec_ctrl_impl::send_reg(boost::uint8_t addr){
+ boost::uint32_t reg = _ad9862_regs.get_write_reg(addr);
+ UHD_LOGV(often) << "codec control write reg: " << std::hex << reg << std::endl;
+ _iface->write_spi(
+ UE_SPI_SS_AD9862,
+ spi_config_t::EDGE_RISE,
+ reg, 16
+ );
+}
+
+void e100_codec_ctrl_impl::recv_reg(boost::uint8_t addr){
+ boost::uint32_t reg = _ad9862_regs.get_read_reg(addr);
+ UHD_LOGV(often) << "codec control read reg: " << std::hex << reg << std::endl;
+ boost::uint32_t ret = _iface->read_spi(
+ UE_SPI_SS_AD9862,
+ spi_config_t::EDGE_RISE,
+ reg, 16
+ );
+ UHD_LOGV(often) << "codec control read ret: " << std::hex << ret << std::endl;
+ _ad9862_regs.set_reg(addr, boost::uint16_t(ret));
+}
+
+/***********************************************************************
+ * Codec Control Make
+ **********************************************************************/
+e100_codec_ctrl::sptr e100_codec_ctrl::make(spi_iface::sptr iface){
+ return sptr(new e100_codec_ctrl_impl(iface));
+}
diff --git a/host/lib/usrp/e100/codec_ctrl.hpp b/host/lib/usrp/e100/codec_ctrl.hpp
new file mode 100644
index 000000000..707f6f521
--- /dev/null
+++ b/host/lib/usrp/e100/codec_ctrl.hpp
@@ -0,0 +1,90 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_USRP_E100_CODEC_CTRL_HPP
+#define INCLUDED_USRP_E100_CODEC_CTRL_HPP
+
+#include <uhd/types/serial.hpp>
+#include <uhd/types/ranges.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+
+/*!
+ * The usrp-e codec control:
+ * - Init/power down codec.
+ * - Read aux adc, write aux dac.
+ */
+class e100_codec_ctrl : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<e100_codec_ctrl> sptr;
+
+ static const uhd::gain_range_t tx_pga_gain_range;
+ static const uhd::gain_range_t rx_pga_gain_range;
+
+ /*!
+ * Make a new codec control object.
+ * \param iface the spi iface object
+ * \return the codec control object
+ */
+ static sptr make(uhd::spi_iface::sptr iface);
+
+ //! aux adc identifier constants
+ enum aux_adc_t{
+ AUX_ADC_A2 = 0xA2,
+ AUX_ADC_A1 = 0xA1,
+ AUX_ADC_B2 = 0xB2,
+ AUX_ADC_B1 = 0xB1
+ };
+
+ /*!
+ * Read an auxiliary adc:
+ * The internals remember which aux adc was read last.
+ * Therefore, the aux adc switch is only changed as needed.
+ * \param which which of the 4 adcs
+ * \return a value in volts
+ */
+ virtual double read_aux_adc(aux_adc_t which) = 0;
+
+ //! aux dac identifier constants
+ enum aux_dac_t{
+ AUX_DAC_A = 0xA,
+ AUX_DAC_B = 0xB,
+ AUX_DAC_C = 0xC,
+ AUX_DAC_D = 0xD //really the sigma delta output
+ };
+
+ /*!
+ * Write an auxiliary dac.
+ * \param which which of the 4 dacs
+ * \param volts the level in in volts
+ */
+ virtual void write_aux_dac(aux_dac_t which, double volts) = 0;
+
+ //! Set the TX PGA gain
+ virtual void set_tx_pga_gain(double gain) = 0;
+
+ //! Get the TX PGA gain
+ virtual double get_tx_pga_gain(void) = 0;
+
+ //! Set the RX PGA gain ('A' or 'B')
+ virtual void set_rx_pga_gain(double gain, char which) = 0;
+
+ //! Get the RX PGA gain ('A' or 'B')
+ virtual double get_rx_pga_gain(char which) = 0;
+};
+
+#endif /* INCLUDED_USRP_E100_CODEC_CTRL_HPP */
diff --git a/host/lib/usrp/e100/dboard_iface.cpp b/host/lib/usrp/e100/dboard_iface.cpp
new file mode 100644
index 000000000..6afc7bc48
--- /dev/null
+++ b/host/lib/usrp/e100/dboard_iface.cpp
@@ -0,0 +1,258 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "gpio_core_200.hpp"
+#include <uhd/types/serial.hpp>
+#include "e100_regs.hpp"
+#include "clock_ctrl.hpp"
+#include "codec_ctrl.hpp"
+#include <uhd/usrp/dboard_iface.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/exception.hpp>
+#include <boost/assign/list_of.hpp>
+#include <linux/usrp_e.h> //i2c and spi constants
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace boost::assign;
+
+class e100_dboard_iface : public dboard_iface{
+public:
+
+ e100_dboard_iface(
+ wb_iface::sptr wb_iface,
+ i2c_iface::sptr i2c_iface,
+ spi_iface::sptr spi_iface,
+ e100_clock_ctrl::sptr clock,
+ e100_codec_ctrl::sptr codec
+ ){
+ _wb_iface = wb_iface;
+ _i2c_iface = i2c_iface;
+ _spi_iface = spi_iface;
+ _clock = clock;
+ _codec = codec;
+ _gpio = gpio_core_200::make(_wb_iface, E100_REG_SR_ADDR(UE_SR_GPIO), E100_REG_RB_GPIO);
+
+ //init the clock rate shadows
+ this->set_clock_rate(UNIT_RX, _clock->get_fpga_clock_rate());
+ this->set_clock_rate(UNIT_TX, _clock->get_fpga_clock_rate());
+ }
+
+ ~e100_dboard_iface(void){
+ /* NOP */
+ }
+
+ special_props_t get_special_props(void){
+ special_props_t props;
+ props.soft_clock_divider = false;
+ props.mangle_i2c_addrs = false;
+ return props;
+ }
+
+ void write_aux_dac(unit_t, aux_dac_t, double);
+ double read_aux_adc(unit_t, aux_adc_t);
+
+ void _set_pin_ctrl(unit_t, boost::uint16_t);
+ void _set_atr_reg(unit_t, atr_reg_t, boost::uint16_t);
+ void _set_gpio_ddr(unit_t, boost::uint16_t);
+ void _set_gpio_out(unit_t, boost::uint16_t);
+ void set_gpio_debug(unit_t, int);
+ boost::uint16_t read_gpio(unit_t);
+
+ void write_i2c(boost::uint8_t, const byte_vector_t &);
+ byte_vector_t read_i2c(boost::uint8_t, size_t);
+
+ void write_spi(
+ unit_t unit,
+ const spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits
+ );
+
+ boost::uint32_t read_write_spi(
+ unit_t unit,
+ const spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits
+ );
+
+ void set_clock_rate(unit_t, double);
+ std::vector<double> get_clock_rates(unit_t);
+ double get_clock_rate(unit_t);
+ void set_clock_enabled(unit_t, bool);
+ double get_codec_rate(unit_t);
+
+private:
+ wb_iface::sptr _wb_iface;
+ i2c_iface::sptr _i2c_iface;
+ spi_iface::sptr _spi_iface;
+ e100_clock_ctrl::sptr _clock;
+ e100_codec_ctrl::sptr _codec;
+ gpio_core_200::sptr _gpio;
+};
+
+/***********************************************************************
+ * Make Function
+ **********************************************************************/
+dboard_iface::sptr make_e100_dboard_iface(
+ wb_iface::sptr wb_iface,
+ i2c_iface::sptr i2c_iface,
+ spi_iface::sptr spi_iface,
+ e100_clock_ctrl::sptr clock,
+ e100_codec_ctrl::sptr codec
+){
+ return dboard_iface::sptr(new e100_dboard_iface(wb_iface, i2c_iface, spi_iface, clock, codec));
+}
+
+/***********************************************************************
+ * Clock Rates
+ **********************************************************************/
+void e100_dboard_iface::set_clock_rate(unit_t unit, double rate){
+ switch(unit){
+ case UNIT_RX: return _clock->set_rx_dboard_clock_rate(rate);
+ case UNIT_TX: return _clock->set_tx_dboard_clock_rate(rate);
+ }
+}
+
+std::vector<double> e100_dboard_iface::get_clock_rates(unit_t unit){
+ switch(unit){
+ case UNIT_RX: return _clock->get_rx_dboard_clock_rates();
+ case UNIT_TX: return _clock->get_tx_dboard_clock_rates();
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+double e100_dboard_iface::get_clock_rate(unit_t unit){
+ switch(unit){
+ case UNIT_RX: return _clock->get_rx_clock_rate();
+ case UNIT_TX: return _clock->get_tx_clock_rate();
+ }
+ UHD_THROW_INVALID_CODE_PATH();
+}
+
+void e100_dboard_iface::set_clock_enabled(unit_t unit, bool enb){
+ switch(unit){
+ case UNIT_RX: return _clock->enable_rx_dboard_clock(enb);
+ case UNIT_TX: return _clock->enable_tx_dboard_clock(enb);
+ }
+}
+
+double e100_dboard_iface::get_codec_rate(unit_t){
+ return _clock->get_fpga_clock_rate();
+}
+
+/***********************************************************************
+ * GPIO
+ **********************************************************************/
+void e100_dboard_iface::_set_pin_ctrl(unit_t unit, boost::uint16_t value){
+ return _gpio->set_pin_ctrl(unit, value);
+}
+
+void e100_dboard_iface::_set_gpio_ddr(unit_t unit, boost::uint16_t value){
+ return _gpio->set_gpio_ddr(unit, value);
+}
+
+void e100_dboard_iface::_set_gpio_out(unit_t unit, boost::uint16_t value){
+ return _gpio->set_gpio_out(unit, value);
+}
+
+boost::uint16_t e100_dboard_iface::read_gpio(unit_t unit){
+ return _gpio->read_gpio(unit);
+}
+
+void e100_dboard_iface::_set_atr_reg(unit_t unit, atr_reg_t atr, boost::uint16_t value){
+ return _gpio->set_atr_reg(unit, atr, value);
+}
+
+void e100_dboard_iface::set_gpio_debug(unit_t, int){
+ throw uhd::not_implemented_error("no set_gpio_debug implemented");
+}
+
+/***********************************************************************
+ * SPI
+ **********************************************************************/
+/*!
+ * Static function to convert a unit type to a spi slave device number.
+ * \param unit the dboard interface unit type enum
+ * \return the slave device number
+ */
+static boost::uint32_t unit_to_otw_spi_dev(dboard_iface::unit_t unit){
+ switch(unit){
+ case dboard_iface::UNIT_TX: return UE_SPI_SS_TX_DB;
+ case dboard_iface::UNIT_RX: return UE_SPI_SS_RX_DB;
+ }
+ UHD_THROW_INVALID_CODE_PATH();
+}
+
+void e100_dboard_iface::write_spi(
+ unit_t unit,
+ const spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits
+){
+ _spi_iface->write_spi(unit_to_otw_spi_dev(unit), config, data, num_bits);
+}
+
+boost::uint32_t e100_dboard_iface::read_write_spi(
+ unit_t unit,
+ const spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits
+){
+ return _spi_iface->read_spi(unit_to_otw_spi_dev(unit), config, data, num_bits);
+}
+
+/***********************************************************************
+ * I2C
+ **********************************************************************/
+void e100_dboard_iface::write_i2c(boost::uint8_t addr, const byte_vector_t &bytes){
+ return _i2c_iface->write_i2c(addr, bytes);
+}
+
+byte_vector_t e100_dboard_iface::read_i2c(boost::uint8_t addr, size_t num_bytes){
+ return _i2c_iface->read_i2c(addr, num_bytes);
+}
+
+/***********************************************************************
+ * Aux DAX/ADC
+ **********************************************************************/
+void e100_dboard_iface::write_aux_dac(dboard_iface::unit_t, aux_dac_t which, double value){
+ //same aux dacs for each unit
+ static const uhd::dict<aux_dac_t, e100_codec_ctrl::aux_dac_t> which_to_aux_dac = map_list_of
+ (AUX_DAC_A, e100_codec_ctrl::AUX_DAC_A)
+ (AUX_DAC_B, e100_codec_ctrl::AUX_DAC_B)
+ (AUX_DAC_C, e100_codec_ctrl::AUX_DAC_C)
+ (AUX_DAC_D, e100_codec_ctrl::AUX_DAC_D)
+ ;
+ _codec->write_aux_dac(which_to_aux_dac[which], value);
+}
+
+double e100_dboard_iface::read_aux_adc(dboard_iface::unit_t unit, aux_adc_t which){
+ static const uhd::dict<
+ unit_t, uhd::dict<aux_adc_t, e100_codec_ctrl::aux_adc_t>
+ > unit_to_which_to_aux_adc = map_list_of
+ (UNIT_RX, map_list_of
+ (AUX_ADC_A, e100_codec_ctrl::AUX_ADC_A1)
+ (AUX_ADC_B, e100_codec_ctrl::AUX_ADC_B1)
+ )
+ (UNIT_TX, map_list_of
+ (AUX_ADC_A, e100_codec_ctrl::AUX_ADC_A2)
+ (AUX_ADC_B, e100_codec_ctrl::AUX_ADC_B2)
+ )
+ ;
+ return _codec->read_aux_adc(unit_to_which_to_aux_adc[unit][which]);
+}
diff --git a/host/lib/usrp/e100/e100_ctrl.cpp b/host/lib/usrp/e100/e100_ctrl.cpp
new file mode 100644
index 000000000..eb529c9c1
--- /dev/null
+++ b/host/lib/usrp/e100/e100_ctrl.cpp
@@ -0,0 +1,355 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "e100_ctrl.hpp"
+#include "e100_regs.hpp"
+#include <uhd/exception.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/msg.hpp>
+#include <sys/ioctl.h> //ioctl
+#include <fcntl.h> //open, close
+#include <linux/usrp_e.h> //ioctl structures and constants
+#include <boost/thread/thread.hpp> //sleep
+#include <boost/thread/mutex.hpp>
+#include <boost/foreach.hpp>
+#include <boost/format.hpp>
+#include <fstream>
+
+using namespace uhd;
+
+/***********************************************************************
+ * Sysfs GPIO wrapper class
+ **********************************************************************/
+class gpio{
+public:
+ gpio(const int num, const std::string &dir) : _num(num){
+ this->set_xport("export");
+ this->set_dir(dir);
+ _value_file.open(str(boost::format("/sys/class/gpio/gpio%d/value") % num).c_str(), std::ios_base::in | std::ios_base::out);
+ }
+ ~gpio(void){
+ _value_file.close();
+ this->set_dir("in");
+ this->set_xport("unexport");
+ }
+ void operator()(const int val){
+ _value_file << val << std::endl << std::flush;
+ }
+ int operator()(void){
+ std::string val;
+ std::getline(_value_file, val);
+ _value_file.seekg(0);
+ return int(val.at(0) - '0') & 0x1;
+ }
+private:
+ void set_xport(const std::string &xport){
+ std::ofstream export_file(("/sys/class/gpio/" + xport).c_str());
+ export_file << _num << std::endl << std::flush;
+ export_file.close();
+ }
+ void set_dir(const std::string &dir){
+ std::ofstream dir_file(str(boost::format("/sys/class/gpio/gpio%d/direction") % _num).c_str());
+ dir_file << dir << std::endl << std::flush;
+ dir_file.close();
+ }
+ const int _num;
+ std::fstream _value_file;
+};
+
+/***********************************************************************
+ * Aux spi implementation
+ **********************************************************************/
+class aux_spi_iface_impl : public spi_iface{
+public:
+ aux_spi_iface_impl(void):
+ spi_sclk_gpio(65, "out"),
+ spi_sen_gpio(186, "out"),
+ spi_mosi_gpio(145, "out"),
+ spi_miso_gpio(147, "in"){}
+
+ boost::uint32_t transact_spi(
+ int, const spi_config_t &, //not used params
+ boost::uint32_t bits,
+ size_t num_bits,
+ bool readback
+ ){
+ boost::uint32_t rb_bits = 0;
+ this->spi_sen_gpio(0);
+
+ for (size_t i = 0; i < num_bits; i++){
+ this->spi_sclk_gpio(0);
+ this->spi_mosi_gpio((bits >> (num_bits-i-1)) & 0x1);
+ boost::this_thread::sleep(boost::posix_time::microseconds(10));
+ if (readback) rb_bits = (rb_bits << 1) | this->spi_miso_gpio();
+ this->spi_sclk_gpio(1);
+ boost::this_thread::sleep(boost::posix_time::microseconds(10));
+ }
+
+ this->spi_sen_gpio(1);
+ boost::this_thread::sleep(boost::posix_time::microseconds(100));
+ return rb_bits;
+ }
+
+private:
+ gpio spi_sclk_gpio, spi_sen_gpio, spi_mosi_gpio, spi_miso_gpio;
+};
+
+uhd::spi_iface::sptr e100_ctrl::make_aux_spi_iface(void){
+ return uhd::spi_iface::sptr(new aux_spi_iface_impl());
+}
+
+/***********************************************************************
+ * I2C device node implementation wrapper
+ **********************************************************************/
+#include <linux/i2c-dev.h>
+#include <linux/i2c.h>
+class i2c_dev_iface : public i2c_iface{
+public:
+ i2c_dev_iface(const std::string &node){
+ if ((_node_fd = ::open(node.c_str(), O_RDWR)) < 0){
+ throw uhd::io_error("Failed to open " + node);
+ }
+ }
+
+ ~i2c_dev_iface(void){
+ ::close(_node_fd);
+ }
+
+ void write_i2c(boost::uint8_t addr, const byte_vector_t &bytes){
+ byte_vector_t rw_bytes(bytes);
+
+ //setup the message
+ i2c_msg msg;
+ msg.addr = addr;
+ msg.flags = 0;
+ msg.len = bytes.size();
+ msg.buf = &rw_bytes.front();
+
+ //setup the data
+ i2c_rdwr_ioctl_data data;
+ data.msgs = &msg;
+ data.nmsgs = 1;
+
+ //call the ioctl
+ UHD_ASSERT_THROW(::ioctl(_node_fd, I2C_RDWR, &data) >= 0);
+ }
+
+ byte_vector_t read_i2c(boost::uint8_t addr, size_t num_bytes){
+ byte_vector_t bytes(num_bytes);
+
+ //setup the message
+ i2c_msg msg;
+ msg.addr = addr;
+ msg.flags = I2C_M_RD;
+ msg.len = bytes.size();
+ msg.buf = &bytes.front();
+
+ //setup the data
+ i2c_rdwr_ioctl_data data;
+ data.msgs = &msg;
+ data.nmsgs = 1;
+
+ //call the ioctl
+ UHD_ASSERT_THROW(::ioctl(_node_fd, I2C_RDWR, &data) >= 0);
+
+ return bytes;
+ }
+
+private: int _node_fd;
+};
+
+uhd::i2c_iface::sptr e100_ctrl::make_dev_i2c_iface(const std::string &node){
+ return uhd::i2c_iface::sptr(new i2c_dev_iface(node));
+}
+
+/***********************************************************************
+ * UART control implementation
+ **********************************************************************/
+#include <termios.h>
+#include <cstring>
+class uart_dev_iface : public uart_iface{
+public:
+ uart_dev_iface(const std::string &node){
+ if ((_node_fd = ::open(node.c_str(), O_RDWR | O_NONBLOCK)) < 0){
+ throw uhd::io_error("Failed to open " + node);
+ }
+
+ //init the tty settings w/ termios
+ termios tio;
+ std::memset(&tio,0,sizeof(tio));
+ tio.c_iflag = IGNCR; //Ignore CR
+ tio.c_oflag = OPOST | ONLCR; //Map NL to CR-NL on output
+ tio.c_cflag = CS8 | CREAD | CLOCAL; // 8n1
+ tio.c_lflag = 0;
+
+ cfsetospeed(&tio, B115200); // 115200 baud
+ cfsetispeed(&tio, B115200); // 115200 baud
+
+ tcsetattr(_node_fd, TCSANOW, &tio);
+ }
+
+ void write_uart(const std::string &buf){
+ const ssize_t ret = ::write(_node_fd, buf.c_str(), buf.size());
+ if (size_t(ret) != buf.size()) UHD_LOG << ret;
+ }
+
+ std::string read_uart(double timeout){
+ const boost::system_time exit_time = boost::get_system_time() + boost::posix_time::milliseconds(long(timeout*1000));
+
+ std::string line;
+ while(true){
+ char ch;
+ const ssize_t ret = ::read(_node_fd, &ch, 1);
+
+ //got a character -> process it
+ if (ret == 1){
+ const bool flush = ch == '\n' or ch == '\r';
+ if (flush and line.empty()) continue; //avoid flushing on empty lines
+ line += std::string(1, ch);
+ if (flush) break;
+ }
+
+ //didnt get a character, check the timeout
+ else if (boost::get_system_time() > exit_time){
+ break;
+ }
+
+ //otherwise sleep for a bit
+ else{
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+ }
+ }
+ return line;
+ }
+
+private: int _node_fd;
+};
+
+uhd::uart_iface::sptr e100_ctrl::make_gps_uart_iface(const std::string &node){
+ return uhd::uart_iface::sptr(new uart_dev_iface(node));
+}
+
+/***********************************************************************
+ * USRP-E100 control implementation
+ **********************************************************************/
+class e100_ctrl_impl : public e100_ctrl{
+public:
+
+ int get_file_descriptor(void){
+ return _node_fd;
+ }
+
+ /*******************************************************************
+ * Structors
+ ******************************************************************/
+ e100_ctrl_impl(const std::string &node){
+ UHD_MSG(status) << "Opening device node " << node << "..." << std::endl;
+
+ //open the device node and check file descriptor
+ if ((_node_fd = ::open(node.c_str(), O_RDWR)) < 0){
+ throw uhd::io_error("Failed to open " + node);
+ }
+
+ //check the module compatibility number
+ int module_compat_num = ::ioctl(_node_fd, USRP_E_GET_COMPAT_NUMBER, NULL);
+ if (module_compat_num != USRP_E_COMPAT_NUMBER){
+ throw uhd::runtime_error(str(boost::format(
+ "Expected module compatibility number 0x%x, but got 0x%x:\n"
+ "The module build is not compatible with the host code build."
+ ) % USRP_E_COMPAT_NUMBER % module_compat_num));
+ }
+
+ //perform a global reset after opening
+ this->poke32(E100_REG_GLOBAL_RESET, 0);
+ }
+
+ ~e100_ctrl_impl(void){
+ ::close(_node_fd);
+ }
+
+ /*******************************************************************
+ * IOCTL: provides the communication base for all other calls
+ ******************************************************************/
+ void ioctl(int request, void *mem){
+ boost::mutex::scoped_lock lock(_ioctl_mutex);
+
+ if (::ioctl(_node_fd, request, mem) < 0){
+ throw uhd::os_error(str(
+ boost::format("ioctl failed with request %d") % request
+ ));
+ }
+ }
+ /*******************************************************************
+ * Peek and Poke
+ ******************************************************************/
+ void poke32(wb_addr_type addr, boost::uint32_t value){
+ //load the data struct
+ usrp_e_ctl32 data;
+ data.offset = addr;
+ data.count = 1;
+ data.buf[0] = value;
+
+ //call the ioctl
+ this->ioctl(USRP_E_WRITE_CTL32, &data);
+ }
+
+ void poke16(wb_addr_type addr, boost::uint16_t value){
+ //load the data struct
+ usrp_e_ctl16 data;
+ data.offset = addr;
+ data.count = 1;
+ data.buf[0] = value;
+
+ //call the ioctl
+ this->ioctl(USRP_E_WRITE_CTL16, &data);
+ }
+
+ boost::uint32_t peek32(wb_addr_type addr){
+ //load the data struct
+ usrp_e_ctl32 data;
+ data.offset = addr;
+ data.count = 1;
+
+ //call the ioctl
+ this->ioctl(USRP_E_READ_CTL32, &data);
+
+ return data.buf[0];
+ }
+
+ boost::uint16_t peek16(wb_addr_type addr){
+ //load the data struct
+ usrp_e_ctl16 data;
+ data.offset = addr;
+ data.count = 1;
+
+ //call the ioctl
+ this->ioctl(USRP_E_READ_CTL16, &data);
+
+ return data.buf[0];
+ }
+
+private:
+ int _node_fd;
+ boost::mutex _ioctl_mutex;
+};
+
+/***********************************************************************
+ * Public Make Function
+ **********************************************************************/
+e100_ctrl::sptr e100_ctrl::make(const std::string &node){
+ return sptr(new e100_ctrl_impl(node));
+}
diff --git a/host/lib/usrp/e100/e100_ctrl.hpp b/host/lib/usrp/e100/e100_ctrl.hpp
new file mode 100644
index 000000000..fd66791d4
--- /dev/null
+++ b/host/lib/usrp/e100/e100_ctrl.hpp
@@ -0,0 +1,48 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_B100_CTRL_HPP
+#define INCLUDED_B100_CTRL_HPP
+
+#include "wb_iface.hpp"
+#include <uhd/types/serial.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+
+class e100_ctrl : boost::noncopyable, public wb_iface{
+public:
+ typedef boost::shared_ptr<e100_ctrl> sptr;
+
+ //! Make a new controller for E100
+ static sptr make(const std::string &node);
+
+ //! Make an i2c iface for the i2c device node
+ static uhd::i2c_iface::sptr make_dev_i2c_iface(const std::string &node);
+
+ //! Make an i2c iface for the i2c device node
+ static uhd::spi_iface::sptr make_aux_spi_iface(void);
+
+ //! Make a uart iface for the uart device node
+ static uhd::uart_iface::sptr make_gps_uart_iface(const std::string &node);
+
+ virtual void ioctl(int request, void *mem) = 0;
+
+ virtual int get_file_descriptor(void) = 0;
+
+};
+
+#endif /* INCLUDED_B100_CTRL_HPP */
diff --git a/host/lib/usrp/e100/e100_impl.cpp b/host/lib/usrp/e100/e100_impl.cpp
new file mode 100644
index 000000000..bb135b646
--- /dev/null
+++ b/host/lib/usrp/e100/e100_impl.cpp
@@ -0,0 +1,494 @@
+//
+// Copyright 2010-2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "apply_corrections.hpp"
+#include "e100_impl.hpp"
+#include "e100_regs.hpp"
+#include <uhd/utils/msg.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/utils/static.hpp>
+#include <uhd/utils/images.hpp>
+#include <boost/bind.hpp>
+#include <boost/format.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/functional/hash.hpp>
+#include <boost/assign/list_of.hpp>
+#include <fstream>
+
+using namespace uhd;
+using namespace uhd::usrp;
+namespace fs = boost::filesystem;
+
+////////////////////////////////////////////////////////////////////////
+// I2C addresses
+////////////////////////////////////////////////////////////////////////
+#define I2C_DEV_EEPROM 0x50 // 24LC02[45]: 7-bits 1010xxx
+#define I2C_ADDR_MBOARD (I2C_DEV_EEPROM | 0x0)
+#define I2C_ADDR_TX_DB (I2C_DEV_EEPROM | 0x4)
+#define I2C_ADDR_RX_DB (I2C_DEV_EEPROM | 0x5)
+
+/***********************************************************************
+ * Discovery
+ **********************************************************************/
+static 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
+ if (hint.has_key("type") and hint["type"] != "e100") return e100_addrs;
+
+ //device node not provided, assume its 0
+ if (not hint.has_key("node")){
+ device_addr_t new_addr = hint;
+ new_addr["node"] = "/dev/usrp_e0";
+ return e100_find(new_addr);
+ }
+
+ //use the given device node name
+ if (fs::exists(hint["node"])){
+ device_addr_t new_addr;
+ new_addr["type"] = "e100";
+ new_addr["node"] = fs::system_complete(fs::path(hint["node"])).string();
+ try{
+ i2c_iface::sptr i2c_iface = e100_ctrl::make_dev_i2c_iface(E100_I2C_DEV_NODE);
+ const mboard_eeprom_t mb_eeprom(*i2c_iface, mboard_eeprom_t::MAP_E100);
+ new_addr["name"] = mb_eeprom["name"];
+ new_addr["serial"] = mb_eeprom["serial"];
+ }
+ catch(const std::exception &e){
+ new_addr["name"] = "";
+ new_addr["serial"] = "";
+ }
+ if (
+ (not hint.has_key("name") or hint["name"] == new_addr["name"]) and
+ (not hint.has_key("serial") or hint["serial"] == new_addr["serial"])
+ ){
+ e100_addrs.push_back(new_addr);
+ }
+ }
+
+ return e100_addrs;
+}
+
+/***********************************************************************
+ * Make
+ **********************************************************************/
+static size_t hash_fpga_file(const std::string &file_path){
+ size_t hash = 0;
+ std::ifstream file(file_path.c_str());
+ if (not file.good()) throw uhd::io_error("cannot open fpga file for read: " + file_path);
+ while (file.good()) boost::hash_combine(hash, file.get());
+ file.close();
+ return hash;
+}
+
+static device::sptr e100_make(const device_addr_t &device_addr){
+ return device::sptr(new e100_impl(device_addr));
+}
+
+UHD_STATIC_BLOCK(register_e100_device){
+ device::register_device(&e100_find, &e100_make);
+}
+
+static const uhd::dict<std::string, std::string> model_to_fpga_file_name = boost::assign::map_list_of
+ ("E100", "usrp_e100_fpga_v2.bin")
+ ("E110", "usrp_e110_fpga.bin")
+;
+
+/***********************************************************************
+ * Structors
+ **********************************************************************/
+e100_impl::e100_impl(const uhd::device_addr_t &device_addr){
+ _tree = property_tree::make();
+
+ //setup the main interface into fpga
+ const std::string node = device_addr["node"];
+ _fpga_ctrl = e100_ctrl::make(node);
+
+ //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, mboard_eeprom_t::MAP_E100);
+
+ //determine the model string for this device
+ const std::string model = device_addr.get("model", mb_eeprom.get("model", ""));
+ if (not model_to_fpga_file_name.has_key(model)) throw uhd::runtime_error(str(boost::format(
+ "\n"
+ " The specified model string \"%s\" is not recognized.\n"
+ " Perhaps the EEPROM is uninitialized, missing, or damaged.\n"
+ " Or, a monitor is pirating the I2C address of the EEPROM.\n"
+ ) % model));
+
+ //extract the fpga path and compute hash
+ const std::string default_fpga_file_name = model_to_fpga_file_name[model];
+ const std::string e100_fpga_image = find_image_path(device_addr.get("fpga", default_fpga_file_name));
+ const boost::uint32_t file_hash = boost::uint32_t(hash_fpga_file(e100_fpga_image));
+
+ //When the hash does not match:
+ // - close the device node
+ // - load the fpga bin file
+ // - re-open the device node
+ if (_fpga_ctrl->peek32(E100_REG_RB_MISC_TEST32) != file_hash){
+ _fpga_ctrl.reset();
+ e100_load_fpga(e100_fpga_image);
+ _fpga_ctrl = e100_ctrl::make(node);
+ }
+
+ //setup clock control here to ensure that the FPGA has a good clock before we continue
+ bool dboard_clocks_diff = true;
+ if (mb_eeprom.get("revision", "0") == "3") dboard_clocks_diff = false;
+ else if (mb_eeprom.get("revision", "0") == "4") dboard_clocks_diff = true;
+ else UHD_MSG(warning)
+ << "Unknown E1XX revision number!\n"
+ << "defaulting to differential dboard clocks to be safe.\n"
+ << std::endl;
+ const double master_clock_rate = device_addr.cast<double>("master_clock_rate", E100_DEFAULT_CLOCK_RATE);
+ _aux_spi_iface = e100_ctrl::make_aux_spi_iface();
+ _clock_ctrl = e100_clock_ctrl::make(_aux_spi_iface, master_clock_rate, dboard_clocks_diff);
+
+ //Perform wishbone readback tests, these tests also write the hash
+ bool test_fail = false;
+ UHD_MSG(status) << "Performing wishbone readback test... " << std::flush;
+ for (size_t i = 0; i < 100; i++){
+ _fpga_ctrl->poke32(E100_REG_SR_MISC_TEST32, file_hash);
+ test_fail = _fpga_ctrl->peek32(E100_REG_RB_MISC_TEST32) != file_hash;
+ if (test_fail) break; //exit loop on any failure
+ }
+ UHD_MSG(status) << ((test_fail)? " fail" : "pass") << std::endl;
+
+ if (test_fail) UHD_MSG(error) << boost::format(
+ "The FPGA is either clocked improperly\n"
+ "or the FPGA build is not compatible.\n"
+ "Subsequent errors may follow...\n"
+ );
+
+ //check that the compatibility is correct
+ this->check_fpga_compat();
+
+ ////////////////////////////////////////////////////////////////////
+ // Create controller objects
+ ////////////////////////////////////////////////////////////////////
+ _fpga_i2c_ctrl = i2c_core_100::make(_fpga_ctrl, E100_REG_SLAVE(3));
+ _fpga_spi_ctrl = spi_core_100::make(_fpga_ctrl, E100_REG_SLAVE(2));
+ _data_transport = e100_make_mmap_zero_copy(_fpga_ctrl);
+
+ ////////////////////////////////////////////////////////////////////
+ // Initialize the properties tree
+ ////////////////////////////////////////////////////////////////////
+ _tree->create<std::string>("/name").set("E-Series Device");
+ const fs_path mb_path = "/mboards/0";
+ _tree->create<std::string>(mb_path / "name").set(str(boost::format("%s (euewanee)") % model));
+
+ ////////////////////////////////////////////////////////////////////
+ // setup the mboard eeprom
+ ////////////////////////////////////////////////////////////////////
+ _tree->create<mboard_eeprom_t>(mb_path / "eeprom")
+ .set(mb_eeprom)
+ .subscribe(boost::bind(&e100_impl::set_mb_eeprom, this, _1));
+
+ ////////////////////////////////////////////////////////////////////
+ // create clock control objects
+ ////////////////////////////////////////////////////////////////////
+ //^^^ clock created up top, just reg props here... ^^^
+ _tree->create<double>(mb_path / "tick_rate")
+ .publish(boost::bind(&e100_clock_ctrl::get_fpga_clock_rate, _clock_ctrl))
+ .subscribe(boost::bind(&e100_impl::update_tick_rate, this, _1));
+
+ ////////////////////////////////////////////////////////////////////
+ // create codec control objects
+ ////////////////////////////////////////////////////////////////////
+ _codec_ctrl = e100_codec_ctrl::make(_fpga_spi_ctrl);
+ const fs_path rx_codec_path = mb_path / "rx_codecs/A";
+ const fs_path tx_codec_path = mb_path / "tx_codecs/A";
+ _tree->create<std::string>(rx_codec_path / "name").set("ad9522");
+ _tree->create<meta_range_t>(rx_codec_path / "gains/pga/range").set(e100_codec_ctrl::rx_pga_gain_range);
+ _tree->create<double>(rx_codec_path / "gains/pga/value")
+ .coerce(boost::bind(&e100_impl::update_rx_codec_gain, this, _1));
+ _tree->create<std::string>(tx_codec_path / "name").set("ad9522");
+ _tree->create<meta_range_t>(tx_codec_path / "gains/pga/range").set(e100_codec_ctrl::tx_pga_gain_range);
+ _tree->create<double>(tx_codec_path / "gains/pga/value")
+ .subscribe(boost::bind(&e100_codec_ctrl::set_tx_pga_gain, _codec_ctrl, _1))
+ .publish(boost::bind(&e100_codec_ctrl::get_tx_pga_gain, _codec_ctrl));
+
+ ////////////////////////////////////////////////////////////////////
+ // and do the misc mboard sensors
+ ////////////////////////////////////////////////////////////////////
+ _tree->create<sensor_value_t>(mb_path / "sensors/ref_locked")
+ .publish(boost::bind(&e100_impl::get_ref_locked, this));
+
+ ////////////////////////////////////////////////////////////////////
+ // Create the GPSDO control
+ ////////////////////////////////////////////////////////////////////
+ try{
+ _gps = gps_ctrl::make(e100_ctrl::make_gps_uart_iface(E100_UART_DEV_NODE));
+ }
+ catch(std::exception &e){
+ UHD_MSG(error) << "An error occurred making GPSDO control: " << e.what() << std::endl;
+ }
+ if (_gps.get() != NULL and _gps->gps_detected()){
+ 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));
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////
+ // create frontend control objects
+ ////////////////////////////////////////////////////////////////////
+ _rx_fe = rx_frontend_core_200::make(_fpga_ctrl, E100_REG_SR_ADDR(UE_SR_RX_FRONT));
+ _tx_fe = tx_frontend_core_200::make(_fpga_ctrl, E100_REG_SR_ADDR(UE_SR_TX_FRONT));
+
+ _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec")
+ .subscribe(boost::bind(&e100_impl::update_rx_subdev_spec, this, _1));
+ _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec")
+ .subscribe(boost::bind(&e100_impl::update_tx_subdev_spec, this, _1));
+
+ const fs_path rx_fe_path = mb_path / "rx_frontends" / "A";
+ const fs_path tx_fe_path = mb_path / "tx_frontends" / "A";
+
+ _tree->create<std::complex<double> >(rx_fe_path / "dc_offset" / "value")
+ .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, _rx_fe, _1))
+ .set(std::complex<double>(0.0, 0.0));
+ _tree->create<bool>(rx_fe_path / "dc_offset" / "enable")
+ .subscribe(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, _rx_fe, _1))
+ .set(true);
+ _tree->create<std::complex<double> >(rx_fe_path / "iq_balance" / "value")
+ .subscribe(boost::bind(&rx_frontend_core_200::set_iq_balance, _rx_fe, _1))
+ .set(std::polar<double>(1.0, 0.0));
+ _tree->create<std::complex<double> >(tx_fe_path / "dc_offset" / "value")
+ .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, _tx_fe, _1))
+ .set(std::complex<double>(0.0, 0.0));
+ _tree->create<std::complex<double> >(tx_fe_path / "iq_balance" / "value")
+ .subscribe(boost::bind(&tx_frontend_core_200::set_iq_balance, _tx_fe, _1))
+ .set(std::polar<double>(1.0, 0.0));
+
+ ////////////////////////////////////////////////////////////////////
+ // create rx dsp control objects
+ ////////////////////////////////////////////////////////////////////
+ _rx_dsps.push_back(rx_dsp_core_200::make(
+ _fpga_ctrl, E100_REG_SR_ADDR(UE_SR_RX_DSP0), E100_REG_SR_ADDR(UE_SR_RX_CTRL0), E100_RX_SID_BASE + 0
+ ));
+ _rx_dsps.push_back(rx_dsp_core_200::make(
+ _fpga_ctrl, E100_REG_SR_ADDR(UE_SR_RX_DSP1), E100_REG_SR_ADDR(UE_SR_RX_CTRL1), E100_RX_SID_BASE + 1
+ ));
+ for (size_t dspno = 0; dspno < _rx_dsps.size(); dspno++){
+ _rx_dsps[dspno]->set_link_rate(E100_RX_LINK_RATE_BPS);
+ _tree->access<double>(mb_path / "tick_rate")
+ .subscribe(boost::bind(&rx_dsp_core_200::set_tick_rate, _rx_dsps[dspno], _1));
+ fs_path rx_dsp_path = mb_path / str(boost::format("rx_dsps/%u") % dspno);
+ _tree->create<meta_range_t>(rx_dsp_path / "rate/range")
+ .publish(boost::bind(&rx_dsp_core_200::get_host_rates, _rx_dsps[dspno]));
+ _tree->create<double>(rx_dsp_path / "rate/value")
+ .set(1e6) //some default
+ .coerce(boost::bind(&rx_dsp_core_200::set_host_rate, _rx_dsps[dspno], _1))
+ .subscribe(boost::bind(&e100_impl::update_rx_samp_rate, this, dspno, _1));
+ _tree->create<double>(rx_dsp_path / "freq/value")
+ .coerce(boost::bind(&rx_dsp_core_200::set_freq, _rx_dsps[dspno], _1));
+ _tree->create<meta_range_t>(rx_dsp_path / "freq/range")
+ .publish(boost::bind(&rx_dsp_core_200::get_freq_range, _rx_dsps[dspno]));
+ _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd")
+ .subscribe(boost::bind(&rx_dsp_core_200::issue_stream_command, _rx_dsps[dspno], _1));
+ }
+
+ ////////////////////////////////////////////////////////////////////
+ // create tx dsp control objects
+ ////////////////////////////////////////////////////////////////////
+ _tx_dsp = tx_dsp_core_200::make(
+ _fpga_ctrl, E100_REG_SR_ADDR(UE_SR_TX_DSP), E100_REG_SR_ADDR(UE_SR_TX_CTRL), E100_TX_ASYNC_SID
+ );
+ _tx_dsp->set_link_rate(E100_TX_LINK_RATE_BPS);
+ _tree->access<double>(mb_path / "tick_rate")
+ .subscribe(boost::bind(&tx_dsp_core_200::set_tick_rate, _tx_dsp, _1));
+ _tree->create<meta_range_t>(mb_path / "tx_dsps/0/rate/range")
+ .publish(boost::bind(&tx_dsp_core_200::get_host_rates, _tx_dsp));
+ _tree->create<double>(mb_path / "tx_dsps/0/rate/value")
+ .set(1e6) //some default
+ .coerce(boost::bind(&tx_dsp_core_200::set_host_rate, _tx_dsp, _1))
+ .subscribe(boost::bind(&e100_impl::update_tx_samp_rate, this, 0, _1));
+ _tree->create<double>(mb_path / "tx_dsps/0/freq/value")
+ .coerce(boost::bind(&tx_dsp_core_200::set_freq, _tx_dsp, _1));
+ _tree->create<meta_range_t>(mb_path / "tx_dsps/0/freq/range")
+ .publish(boost::bind(&tx_dsp_core_200::get_freq_range, _tx_dsp));
+
+ ////////////////////////////////////////////////////////////////////
+ // create time control objects
+ ////////////////////////////////////////////////////////////////////
+ time64_core_200::readback_bases_type time64_rb_bases;
+ time64_rb_bases.rb_hi_now = E100_REG_RB_TIME_NOW_HI;
+ time64_rb_bases.rb_lo_now = E100_REG_RB_TIME_NOW_LO;
+ time64_rb_bases.rb_hi_pps = E100_REG_RB_TIME_PPS_HI;
+ time64_rb_bases.rb_lo_pps = E100_REG_RB_TIME_PPS_LO;
+ _time64 = time64_core_200::make(
+ _fpga_ctrl, E100_REG_SR_ADDR(UE_SR_TIME64), time64_rb_bases
+ );
+ _tree->access<double>(mb_path / "tick_rate")
+ .subscribe(boost::bind(&time64_core_200::set_tick_rate, _time64, _1));
+ _tree->create<time_spec_t>(mb_path / "time/now")
+ .publish(boost::bind(&time64_core_200::get_time_now, _time64))
+ .subscribe(boost::bind(&time64_core_200::set_time_now, _time64, _1));
+ _tree->create<time_spec_t>(mb_path / "time/pps")
+ .publish(boost::bind(&time64_core_200::get_time_last_pps, _time64))
+ .subscribe(boost::bind(&time64_core_200::set_time_next_pps, _time64, _1));
+ //setup time source props
+ _tree->create<std::string>(mb_path / "time_source/value")
+ .subscribe(boost::bind(&time64_core_200::set_time_source, _time64, _1));
+ _tree->create<std::vector<std::string> >(mb_path / "time_source/options")
+ .publish(boost::bind(&time64_core_200::get_time_sources, _time64));
+ //setup reference source props
+ _tree->create<std::string>(mb_path / "clock_source/value")
+ .subscribe(boost::bind(&e100_impl::update_clock_source, this, _1));
+ static const std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("auto");
+ _tree->create<std::vector<std::string> >(mb_path / "clock_source/options").set(clock_sources);
+
+ ////////////////////////////////////////////////////////////////////
+ // create user-defined control objects
+ ////////////////////////////////////////////////////////////////////
+ _user = user_settings_core_200::make(_fpga_ctrl, E100_REG_SR_ADDR(UE_SR_USER_REGS));
+ _tree->create<user_settings_core_200::user_reg_t>(mb_path / "user/regs")
+ .subscribe(boost::bind(&user_settings_core_200::set_reg, _user, _1));
+
+ ////////////////////////////////////////////////////////////////////
+ // create dboard control objects
+ ////////////////////////////////////////////////////////////////////
+
+ //read the dboard eeprom to extract the dboard ids
+ dboard_eeprom_t rx_db_eeprom, tx_db_eeprom, gdb_eeprom;
+ rx_db_eeprom.load(*_fpga_i2c_ctrl, I2C_ADDR_RX_DB);
+ tx_db_eeprom.load(*_fpga_i2c_ctrl, I2C_ADDR_TX_DB);
+ gdb_eeprom.load(*_fpga_i2c_ctrl, I2C_ADDR_TX_DB ^ 5);
+
+ //create the properties and register subscribers
+ _tree->create<dboard_eeprom_t>(mb_path / "dboards/A/rx_eeprom")
+ .set(rx_db_eeprom)
+ .subscribe(boost::bind(&e100_impl::set_db_eeprom, this, "rx", _1));
+ _tree->create<dboard_eeprom_t>(mb_path / "dboards/A/tx_eeprom")
+ .set(tx_db_eeprom)
+ .subscribe(boost::bind(&e100_impl::set_db_eeprom, this, "tx", _1));
+ _tree->create<dboard_eeprom_t>(mb_path / "dboards/A/gdb_eeprom")
+ .set(gdb_eeprom)
+ .subscribe(boost::bind(&e100_impl::set_db_eeprom, this, "gdb", _1));
+
+ //create a new dboard interface and manager
+ _dboard_iface = make_e100_dboard_iface(_fpga_ctrl, _fpga_i2c_ctrl, _fpga_spi_ctrl, _clock_ctrl, _codec_ctrl);
+ _tree->create<dboard_iface::sptr>(mb_path / "dboards/A/iface").set(_dboard_iface);
+ _dboard_manager = dboard_manager::make(
+ rx_db_eeprom.id, tx_db_eeprom.id, gdb_eeprom.id,
+ _dboard_iface, _tree->subtree(mb_path / "dboards/A")
+ );
+
+ //bind frontend corrections to the dboard freq props
+ const fs_path db_tx_fe_path = mb_path / "dboards" / "A" / "tx_frontends";
+ BOOST_FOREACH(const std::string &name, _tree->list(db_tx_fe_path)){
+ _tree->access<double>(db_tx_fe_path / name / "freq" / "value")
+ .subscribe(boost::bind(&e100_impl::set_tx_fe_corrections, this, _1));
+ }
+ const fs_path db_rx_fe_path = mb_path / "dboards" / "A" / "rx_frontends";
+ BOOST_FOREACH(const std::string &name, _tree->list(db_rx_fe_path)){
+ _tree->access<double>(db_rx_fe_path / name / "freq" / "value")
+ .subscribe(boost::bind(&e100_impl::set_rx_fe_corrections, this, _1));
+ }
+
+ //initialize io handling
+ this->io_init();
+
+ ////////////////////////////////////////////////////////////////////
+ // do some post-init tasks
+ ////////////////////////////////////////////////////////////////////
+ this->update_rates();
+
+ _tree->access<double>(mb_path / "tick_rate") //now subscribe the clock rate setter
+ .subscribe(boost::bind(&e100_clock_ctrl::set_fpga_clock_rate, _clock_ctrl, _1));
+
+ //reset cordic rates and their properties to zero
+ BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "rx_dsps")){
+ _tree->access<double>(mb_path / "rx_dsps" / name / "freq" / "value").set(0.0);
+ }
+ BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "tx_dsps")){
+ _tree->access<double>(mb_path / "tx_dsps" / name / "freq" / "value").set(0.0);
+ }
+
+ _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(subdev_spec_t("A:" + _tree->list(mb_path / "dboards/A/rx_frontends").at(0)));
+ _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(subdev_spec_t("A:" + _tree->list(mb_path / "dboards/A/tx_frontends").at(0)));
+ _tree->access<std::string>(mb_path / "clock_source/value").set("internal");
+ _tree->access<std::string>(mb_path / "time_source/value").set("none");
+
+ //GPS installed: use external ref, time, and init time spec
+ if (_gps.get() != NULL 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("external");
+ _tree->access<std::string>(mb_path / "clock_source/value").set("external");
+ UHD_MSG(status) << "Initializing time to the internal GPSDO" << std::endl;
+ _time64->set_time_next_pps(time_spec_t(time_t(_gps->get_sensor("gps_time").to_int()+1)));
+ }
+
+}
+
+e100_impl::~e100_impl(void){
+ /* NOP */
+}
+
+double e100_impl::update_rx_codec_gain(const double gain){
+ //set gain on both I and Q, readback on one
+ //TODO in the future, gains should have individual control
+ _codec_ctrl->set_rx_pga_gain(gain, 'A');
+ _codec_ctrl->set_rx_pga_gain(gain, 'B');
+ return _codec_ctrl->get_rx_pga_gain('A');
+}
+
+void e100_impl::set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &mb_eeprom){
+ mb_eeprom.commit(*_dev_i2c_iface, mboard_eeprom_t::MAP_E100);
+}
+
+void e100_impl::set_db_eeprom(const std::string &type, const uhd::usrp::dboard_eeprom_t &db_eeprom){
+ if (type == "rx") db_eeprom.store(*_fpga_i2c_ctrl, I2C_ADDR_RX_DB);
+ if (type == "tx") db_eeprom.store(*_fpga_i2c_ctrl, I2C_ADDR_TX_DB);
+ if (type == "gdb") db_eeprom.store(*_fpga_i2c_ctrl, I2C_ADDR_TX_DB ^ 5);
+}
+
+void e100_impl::update_clock_source(const std::string &source){
+ if (source == "auto") _clock_ctrl->use_auto_ref();
+ else if (source == "internal") _clock_ctrl->use_internal_ref();
+ else if (source == "external") _clock_ctrl->use_external_ref();
+ else throw uhd::runtime_error("unhandled clock configuration reference source: " + source);
+}
+
+sensor_value_t e100_impl::get_ref_locked(void){
+ const bool lock = _clock_ctrl->get_locked();
+ return sensor_value_t("Ref", lock, "locked", "unlocked");
+}
+
+void e100_impl::check_fpga_compat(void){
+ const boost::uint32_t fpga_compat_num = _fpga_ctrl->peek32(E100_REG_RB_COMPAT);
+ boost::uint16_t fpga_major = fpga_compat_num >> 16, fpga_minor = fpga_compat_num & 0xffff;
+ if (fpga_major == 0){ //old version scheme
+ fpga_major = fpga_minor;
+ fpga_minor = 0;
+ }
+ if (fpga_major != E100_FPGA_COMPAT_NUM){
+ throw uhd::runtime_error(str(boost::format(
+ "Expected FPGA compatibility number %d, but got %d:\n"
+ "The FPGA build is not compatible with the host code build."
+ ) % int(E100_FPGA_COMPAT_NUM) % fpga_major));
+ }
+ _tree->create<std::string>("/mboards/0/fpga_version").set(str(boost::format("%u.%u") % fpga_major % fpga_minor));
+}
+
+void e100_impl::set_rx_fe_corrections(const double lo_freq){
+ apply_rx_fe_corrections(this->get_tree()->subtree("/mboards/0"), "A", lo_freq);
+}
+
+void e100_impl::set_tx_fe_corrections(const double lo_freq){
+ apply_tx_fe_corrections(this->get_tree()->subtree("/mboards/0"), "A", lo_freq);
+}
diff --git a/host/lib/usrp/e100/e100_impl.hpp b/host/lib/usrp/e100/e100_impl.hpp
new file mode 100644
index 000000000..1d36cb2ac
--- /dev/null
+++ b/host/lib/usrp/e100/e100_impl.hpp
@@ -0,0 +1,140 @@
+//
+// Copyright 2010-2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "e100_ctrl.hpp"
+#include "clock_ctrl.hpp"
+#include "codec_ctrl.hpp"
+#include "spi_core_100.hpp"
+#include "i2c_core_100.hpp"
+#include "rx_frontend_core_200.hpp"
+#include "tx_frontend_core_200.hpp"
+#include "rx_dsp_core_200.hpp"
+#include "tx_dsp_core_200.hpp"
+#include "time64_core_200.hpp"
+#include "user_settings_core_200.hpp"
+#include <uhd/device.hpp>
+#include <uhd/property_tree.hpp>
+#include <uhd/utils/pimpl.hpp>
+#include <uhd/usrp/subdev_spec.hpp>
+#include <uhd/usrp/dboard_eeprom.hpp>
+#include <uhd/usrp/mboard_eeprom.hpp>
+#include <uhd/usrp/gps_ctrl.hpp>
+#include <uhd/types/sensors.hpp>
+#include <uhd/types/stream_cmd.hpp>
+#include <uhd/usrp/dboard_manager.hpp>
+#include <uhd/transport/zero_copy.hpp>
+#include <boost/weak_ptr.hpp>
+
+#ifndef INCLUDED_E100_IMPL_HPP
+#define INCLUDED_E100_IMPL_HPP
+
+uhd::transport::zero_copy_if::sptr e100_make_mmap_zero_copy(e100_ctrl::sptr iface);
+
+// = gpmc_clock_rate/clk_div/cycles_per_transaction*bytes_per_transaction
+static const double E100_RX_LINK_RATE_BPS = 166e6/3/2*2;
+static const double E100_TX_LINK_RATE_BPS = 166e6/3/1*2;
+static const std::string E100_I2C_DEV_NODE = "/dev/i2c-3";
+static const std::string E100_UART_DEV_NODE = "/dev/ttyO0";
+static const boost::uint16_t E100_FPGA_COMPAT_NUM = 0x09;
+static const boost::uint32_t E100_RX_SID_BASE = 2;
+static const boost::uint32_t E100_TX_ASYNC_SID = 1;
+static const double E100_DEFAULT_CLOCK_RATE = 64e6;
+
+//! load an fpga image from a bin file into the usrp-e fpga
+extern void e100_load_fpga(const std::string &bin_file);
+
+//! Make an e100 dboard interface
+uhd::usrp::dboard_iface::sptr make_e100_dboard_iface(
+ wb_iface::sptr wb_iface,
+ uhd::i2c_iface::sptr i2c_iface,
+ uhd::spi_iface::sptr spi_iface,
+ e100_clock_ctrl::sptr clock,
+ e100_codec_ctrl::sptr codec
+);
+
+/*!
+ * USRP-E100 implementation guts:
+ * The implementation details are encapsulated here.
+ * Handles properties on the mboard, dboard, dsps...
+ */
+class e100_impl : public uhd::device{
+public:
+ //structors
+ e100_impl(const uhd::device_addr_t &);
+ ~e100_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);
+
+private:
+ uhd::property_tree::sptr _tree;
+
+ //controllers
+ spi_core_100::sptr _fpga_spi_ctrl;
+ i2c_core_100::sptr _fpga_i2c_ctrl;
+ rx_frontend_core_200::sptr _rx_fe;
+ tx_frontend_core_200::sptr _tx_fe;
+ std::vector<rx_dsp_core_200::sptr> _rx_dsps;
+ tx_dsp_core_200::sptr _tx_dsp;
+ time64_core_200::sptr _time64;
+ user_settings_core_200::sptr _user;
+ e100_clock_ctrl::sptr _clock_ctrl;
+ e100_codec_ctrl::sptr _codec_ctrl;
+ e100_ctrl::sptr _fpga_ctrl;
+ uhd::i2c_iface::sptr _dev_i2c_iface;
+ uhd::spi_iface::sptr _aux_spi_iface;
+ uhd::gps_ctrl::sptr _gps;
+
+ //transports
+ uhd::transport::zero_copy_if::sptr _data_transport;
+
+ //dboard stuff
+ uhd::usrp::dboard_manager::sptr _dboard_manager;
+ uhd::usrp::dboard_iface::sptr _dboard_iface;
+
+ //handle io stuff
+ UHD_PIMPL_DECL(io_impl) _io_impl;
+ void io_init(void);
+
+ //device properties interface
+ uhd::property_tree::sptr get_tree(void) const{
+ return _tree;
+ }
+
+ std::vector<boost::weak_ptr<uhd::rx_streamer> > _rx_streamers;
+ std::vector<boost::weak_ptr<uhd::tx_streamer> > _tx_streamers;
+
+ double update_rx_codec_gain(const double); //sets A and B at once
+ void set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &);
+ void set_db_eeprom(const std::string &, const uhd::usrp::dboard_eeprom_t &);
+ void update_tick_rate(const double rate);
+ void update_rx_samp_rate(const size_t, const double rate);
+ void update_tx_samp_rate(const size_t, const double rate);
+ void update_rates(void);
+ void update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &);
+ void update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &);
+ void update_clock_source(const std::string &);
+ uhd::sensor_value_t get_ref_locked(void);
+ void check_fpga_compat(void);
+ void set_rx_fe_corrections(const double);
+ void set_tx_fe_corrections(const double);
+
+};
+
+#endif /* INCLUDED_E100_IMPL_HPP */
diff --git a/host/lib/usrp/e100/e100_mmap_zero_copy.cpp b/host/lib/usrp/e100/e100_mmap_zero_copy.cpp
new file mode 100644
index 000000000..cdb7094f4
--- /dev/null
+++ b/host/lib/usrp/e100/e100_mmap_zero_copy.cpp
@@ -0,0 +1,269 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "e100_ctrl.hpp"
+#include <uhd/transport/zero_copy.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/exception.hpp>
+#include <linux/usrp_e.h>
+#include <sys/mman.h> //mmap
+#include <unistd.h> //getpagesize
+#include <poll.h> //poll
+#include <vector>
+
+using namespace uhd;
+using namespace uhd::transport;
+
+#define fp_verbose false //fast-path verbose
+static const size_t poll_breakout = 10; //how many poll timeouts constitute a full timeout
+
+/***********************************************************************
+ * Reusable managed receiver buffer:
+ * - The buffer knows how to claim and release a frame.
+ **********************************************************************/
+class e100_mmap_zero_copy_mrb : public managed_recv_buffer{
+public:
+ e100_mmap_zero_copy_mrb(void *mem, ring_buffer_info *info):
+ _mem(mem), _info(info) { /* NOP */ }
+
+ void release(void){
+ if (_info->flags != RB_USER_PROCESS) return;
+ if (fp_verbose) UHD_LOGV(always) << "recv buff: release" << std::endl;
+ _info->flags = RB_KERNEL; //release the frame
+ }
+
+ bool ready(void){return _info->flags & RB_USER;}
+
+ sptr get_new(void){
+ if (fp_verbose) UHD_LOGV(always) << " make_recv_buff: " << get_size() << std::endl;
+ _info->flags = RB_USER_PROCESS; //claim the frame
+ return make_managed_buffer(this);
+ }
+
+private:
+ const void *get_buff(void) const{return _mem;}
+ size_t get_size(void) const{return _info->len;}
+
+ void *_mem;
+ ring_buffer_info *_info;
+};
+
+/***********************************************************************
+ * Reusable managed send buffer:
+ * - The buffer knows how to claim and release a frame.
+ **********************************************************************/
+class e100_mmap_zero_copy_msb : public managed_send_buffer{
+public:
+ e100_mmap_zero_copy_msb(void *mem, ring_buffer_info *info, size_t len, int fd):
+ _mem(mem), _info(info), _len(len), _fd(fd) { /* NOP */ }
+
+ void commit(size_t len){
+ if (_info->flags != RB_USER_PROCESS) return;
+ if (fp_verbose) UHD_LOGV(always) << "send buff: commit " << len << std::endl;
+ _info->len = len;
+ _info->flags = RB_USER; //release the frame
+ if (::write(_fd, NULL, 0) < 0){ //notifies the kernel
+ UHD_LOGV(rarely) << UHD_THROW_SITE_INFO("write error") << std::endl;
+ }
+ }
+
+ bool ready(void){return _info->flags & RB_KERNEL;}
+
+ sptr get_new(void){
+ if (fp_verbose) UHD_LOGV(always) << " make_send_buff: " << get_size() << std::endl;
+ _info->flags = RB_USER_PROCESS; //claim the frame
+ return make_managed_buffer(this);
+ }
+
+private:
+ void *get_buff(void) const{return _mem;}
+ size_t get_size(void) const{return _len;}
+
+ void *_mem;
+ ring_buffer_info *_info;
+ size_t _len;
+ int _fd;
+};
+
+/***********************************************************************
+ * The zero copy interface implementation
+ **********************************************************************/
+class e100_mmap_zero_copy_impl : public zero_copy_if{
+public:
+ e100_mmap_zero_copy_impl(e100_ctrl::sptr iface):
+ _fd(iface->get_file_descriptor()), _recv_index(0), _send_index(0)
+ {
+ //get system sizes
+ iface->ioctl(USRP_E_GET_RB_INFO, &_rb_size);
+ size_t page_size = getpagesize();
+ _frame_size = page_size/2;
+
+ //calculate the memory size
+ _map_size =
+ (_rb_size.num_pages_rx_flags + _rb_size.num_pages_tx_flags) * page_size +
+ (_rb_size.num_rx_frames + _rb_size.num_tx_frames) * _frame_size;
+
+ //print sizes summary
+ UHD_LOG
+ << "page_size: " << page_size << std::endl
+ << "frame_size: " << _frame_size << std::endl
+ << "num_pages_rx_flags: " << _rb_size.num_pages_rx_flags << std::endl
+ << "num_rx_frames: " << _rb_size.num_rx_frames << std::endl
+ << "num_pages_tx_flags: " << _rb_size.num_pages_tx_flags << std::endl
+ << "num_tx_frames: " << _rb_size.num_tx_frames << std::endl
+ << "map_size: " << _map_size << std::endl
+ ;
+
+ //call mmap to get the memory
+ _mapped_mem = ::mmap(
+ NULL, _map_size, PROT_READ | PROT_WRITE, MAP_SHARED, _fd, 0
+ );
+ UHD_ASSERT_THROW(_mapped_mem != MAP_FAILED);
+
+ //calculate the memory offsets for info and buffers
+ size_t recv_info_off = 0;
+ size_t recv_buff_off = recv_info_off + (_rb_size.num_pages_rx_flags * page_size);
+ size_t send_info_off = recv_buff_off + (_rb_size.num_rx_frames * _frame_size);
+ size_t send_buff_off = send_info_off + (_rb_size.num_pages_tx_flags * page_size);
+
+ //print offset summary
+ UHD_LOG
+ << "recv_info_off: " << recv_info_off << std::endl
+ << "recv_buff_off: " << recv_buff_off << std::endl
+ << "send_info_off: " << send_info_off << std::endl
+ << "send_buff_off: " << send_buff_off << std::endl
+ ;
+
+ //pointers to sections in the mapped memory
+ ring_buffer_info (*recv_info)[], (*send_info)[];
+ char *recv_buff, *send_buff;
+
+ //set the internal pointers for info and buffers
+ typedef ring_buffer_info (*rbi_pta)[];
+ char *rb_ptr = reinterpret_cast<char *>(_mapped_mem);
+ recv_info = reinterpret_cast<rbi_pta>(rb_ptr + recv_info_off);
+ recv_buff = rb_ptr + recv_buff_off;
+ send_info = reinterpret_cast<rbi_pta>(rb_ptr + send_info_off);
+ send_buff = rb_ptr + send_buff_off;
+
+ //initialize the managed receive buffers
+ for (size_t i = 0; i < get_num_recv_frames(); i++){
+ _mrb_pool.push_back(e100_mmap_zero_copy_mrb(
+ recv_buff + get_recv_frame_size()*i, (*recv_info) + i
+ ));
+ }
+
+ //initialize the managed send buffers
+ for (size_t i = 0; i < get_num_recv_frames(); i++){
+ _msb_pool.push_back(e100_mmap_zero_copy_msb(
+ send_buff + get_send_frame_size()*i, (*send_info) + i,
+ get_send_frame_size(), _fd
+ ));
+ }
+ }
+
+ ~e100_mmap_zero_copy_impl(void){
+ UHD_LOG << "cleanup: munmap" << std::endl;
+ ::munmap(_mapped_mem, _map_size);
+ }
+
+ managed_recv_buffer::sptr get_recv_buff(double timeout){
+ if (fp_verbose) UHD_LOGV(always) << "get_recv_buff: " << _recv_index << std::endl;
+ e100_mmap_zero_copy_mrb &mrb = _mrb_pool[_recv_index];
+
+ //poll/wait for a ready frame
+ if (not mrb.ready()){
+ for (size_t i = 0; i < poll_breakout; i++){
+ pollfd pfd;
+ pfd.fd = _fd;
+ pfd.events = POLLIN;
+ ssize_t poll_ret = ::poll(&pfd, 1, size_t(timeout*1e3/poll_breakout));
+ if (fp_verbose) UHD_LOGV(always) << " POLLIN: " << poll_ret << std::endl;
+ if (poll_ret > 0) goto found_user_frame; //good poll, continue on
+ }
+ return managed_recv_buffer::sptr(); //timed-out for real
+ } found_user_frame:
+
+ //increment the index for the next call
+ if (++_recv_index == get_num_recv_frames()) _recv_index = 0;
+
+ //return the managed buffer for this frame
+ return mrb.get_new();
+ }
+
+ size_t get_num_recv_frames(void) const{
+ return _rb_size.num_rx_frames;
+ }
+
+ size_t get_recv_frame_size(void) const{
+ return _frame_size;
+ }
+
+ managed_send_buffer::sptr get_send_buff(double timeout){
+ if (fp_verbose) UHD_LOGV(always) << "get_send_buff: " << _send_index << std::endl;
+ e100_mmap_zero_copy_msb &msb = _msb_pool[_send_index];
+
+ //poll/wait for a ready frame
+ if (not msb.ready()){
+ pollfd pfd;
+ pfd.fd = _fd;
+ pfd.events = POLLOUT;
+ ssize_t poll_ret = ::poll(&pfd, 1, size_t(timeout*1e3));
+ if (fp_verbose) UHD_LOGV(always) << " POLLOUT: " << poll_ret << std::endl;
+ if (poll_ret <= 0) return managed_send_buffer::sptr();
+ }
+
+ //increment the index for the next call
+ if (++_send_index == get_num_send_frames()) _send_index = 0;
+
+ //return the managed buffer for this frame
+ return msb.get_new();
+ }
+
+ size_t get_num_send_frames(void) const{
+ return _rb_size.num_tx_frames;
+ }
+
+ size_t get_send_frame_size(void) const{
+ return _frame_size;
+ }
+
+private:
+ //file descriptor for mmap
+ int _fd;
+
+ //the mapped memory itself
+ void *_mapped_mem;
+
+ //mapped memory sizes
+ usrp_e_ring_buffer_size_t _rb_size;
+ size_t _frame_size, _map_size;
+
+ //re-usable managed buffers
+ std::vector<e100_mmap_zero_copy_mrb> _mrb_pool;
+ std::vector<e100_mmap_zero_copy_msb> _msb_pool;
+
+ //indexes into sub-sections of mapped memory
+ size_t _recv_index, _send_index;
+};
+
+/***********************************************************************
+ * The zero copy interface make function
+ **********************************************************************/
+zero_copy_if::sptr e100_make_mmap_zero_copy(e100_ctrl::sptr iface){
+ return zero_copy_if::sptr(new e100_mmap_zero_copy_impl(iface));
+}
diff --git a/host/lib/usrp/e100/e100_regs.hpp b/host/lib/usrp/e100/e100_regs.hpp
new file mode 100644
index 000000000..75be2cfbe
--- /dev/null
+++ b/host/lib/usrp/e100/e100_regs.hpp
@@ -0,0 +1,137 @@
+//
+// Copyright 2010-2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+////////////////////////////////////////////////////////////////
+//
+// Memory map for embedded wishbone bus
+//
+////////////////////////////////////////////////////////////////
+
+// All addresses are byte addresses. All accesses are word (16-bit) accesses.
+// This means that address bit 0 is usually 0.
+// There are 11 bits of address for the control.
+
+#ifndef INCLUDED_E100_REGS_HPP
+#define INCLUDED_E100_REGS_HPP
+
+/////////////////////////////////////////////////////
+// Slave pointers
+
+#define E100_REG_SLAVE(n) ((n)<<7)
+
+/////////////////////////////////////////////////////
+// Slave 0 -- Misc Regs
+
+#define E100_REG_MISC_BASE E100_REG_SLAVE(0)
+
+#define E100_REG_MISC_LED E100_REG_MISC_BASE + 0
+#define E100_REG_MISC_SW E100_REG_MISC_BASE + 2
+#define E100_REG_MISC_CGEN_CTRL E100_REG_MISC_BASE + 4
+#define E100_REG_MISC_CGEN_ST E100_REG_MISC_BASE + 6
+#define E100_REG_MISC_TEST E100_REG_MISC_BASE + 8
+#define E100_REG_MISC_RX_LEN E100_REG_MISC_BASE + 10
+#define E100_REG_MISC_TX_LEN E100_REG_MISC_BASE + 12
+#define E100_REG_MISC_XFER_RATE E100_REG_MISC_BASE + 14
+
+/////////////////////////////////////////////////////
+// Slave 1 -- UART
+// CLKDIV is 16 bits, others are only 8
+
+#define E100_REG_UART_BASE E100_REG_SLAVE(1)
+
+#define E100_REG_UART_CLKDIV E100_REG_UART_BASE + 0
+#define E100_REG_UART_TXLEVEL E100_REG_UART_BASE + 2
+#define E100_REG_UART_RXLEVEL E100_REG_UART_BASE + 4
+#define E100_REG_UART_TXCHAR E100_REG_UART_BASE + 6
+#define E100_REG_UART_RXCHAR E100_REG_UART_BASE + 8
+
+/////////////////////////////////////////////////////
+// Slave 2 -- SPI Core
+//these are 32-bit registers mapped onto the 16-bit Wishbone bus.
+//Using peek32/poke32 should allow transparent use of these registers.
+#define E100_REG_SPI_BASE E100_REG_SLAVE(2)
+
+//spi slave constants
+#define UE_SPI_SS_AD9522 (1 << 3)
+#define UE_SPI_SS_AD9862 (1 << 2)
+#define UE_SPI_SS_TX_DB (1 << 1)
+#define UE_SPI_SS_RX_DB (1 << 0)
+
+////////////////////////////////////////////////
+// Slave 3 -- I2C Core
+
+#define E100_REG_I2C_BASE E100_REG_SLAVE(3)
+
+////////////////////////////////////////////////
+// Slave 5 -- Error messages buffer
+
+#define E100_REG_ERR_BUFF E100_REG_SLAVE(5)
+
+///////////////////////////////////////////////////
+// Slave 7 -- Readback Mux 32
+
+#define E100_REG_RB_MUX_32_BASE E100_REG_SLAVE(7)
+
+#define E100_REG_RB_TIME_NOW_HI E100_REG_RB_MUX_32_BASE + 0
+#define E100_REG_RB_TIME_NOW_LO E100_REG_RB_MUX_32_BASE + 4
+#define E100_REG_RB_TIME_PPS_HI E100_REG_RB_MUX_32_BASE + 8
+#define E100_REG_RB_TIME_PPS_LO E100_REG_RB_MUX_32_BASE + 12
+#define E100_REG_RB_MISC_TEST32 E100_REG_RB_MUX_32_BASE + 16
+#define E100_REG_RB_ERR_STATUS E100_REG_RB_MUX_32_BASE + 20
+#define E100_REG_RB_COMPAT E100_REG_RB_MUX_32_BASE + 24
+#define E100_REG_RB_GPIO E100_REG_RB_MUX_32_BASE + 28
+
+////////////////////////////////////////////////////
+// Slave 8 -- Settings Bus
+//
+// Output-only, no readback, 64 registers total
+// Each register must be written 64 bits at a time
+// First the address xxx_xx00 and then xxx_xx10
+
+// 64 total regs in address space
+#define UE_SR_RX_CTRL0 0 // 9 regs (+0 to +8)
+#define UE_SR_RX_DSP0 10 // 4 regs (+0 to +3)
+#define UE_SR_RX_CTRL1 16 // 9 regs (+0 to +8)
+#define UE_SR_RX_DSP1 26 // 4 regs (+0 to +3)
+#define UE_SR_ERR_CTRL 30 // 1 reg
+#define UE_SR_TX_CTRL 32 // 4 regs (+0 to +3)
+#define UE_SR_TX_DSP 38 // 3 regs (+0 to +2)
+
+#define UE_SR_TIME64 42 // 6 regs (+0 to +5)
+#define UE_SR_RX_FRONT 48 // 5 regs (+0 to +4)
+#define UE_SR_TX_FRONT 54 // 5 regs (+0 to +4)
+
+#define UE_SR_REG_TEST32 60 // 1 reg
+#define UE_SR_CLEAR_FIFO 61 // 1 reg
+#define UE_SR_GLOBAL_RESET 63 // 1 reg
+#define UE_SR_USER_REGS 64 // 2 regs
+
+#define UE_SR_GPIO 128
+
+#define E100_REG_SR_ADDR(n) (E100_REG_SLAVE(8) + (4*(n)))
+
+#define E100_REG_SR_MISC_TEST32 E100_REG_SR_ADDR(UE_SR_REG_TEST32)
+#define E100_REG_SR_ERR_CTRL E100_REG_SR_ADDR(UE_SR_ERR_CTRL)
+
+/////////////////////////////////////////////////
+// Magic reset regs
+////////////////////////////////////////////////
+#define E100_REG_CLEAR_FIFO E100_REG_SR_ADDR(UE_SR_CLEAR_FIFO)
+#define E100_REG_GLOBAL_RESET E100_REG_SR_ADDR(UE_SR_GLOBAL_RESET)
+
+#endif
+
diff --git a/host/lib/usrp/e100/fpga_downloader.cpp b/host/lib/usrp/e100/fpga_downloader.cpp
new file mode 100644
index 000000000..7074c8299
--- /dev/null
+++ b/host/lib/usrp/e100/fpga_downloader.cpp
@@ -0,0 +1,272 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/config.hpp>
+#ifdef UHD_DLL_EXPORTS
+#include <uhd/exception.hpp>
+#include <uhd/utils/msg.hpp>
+#else //special case when this file is externally included
+#include <stdexcept>
+#include <iostream>
+#define UHD_MSG(type) std::cout
+namespace uhd{
+ typedef std::runtime_error os_error;
+ typedef std::runtime_error io_error;
+}
+#endif
+
+#include <sstream>
+#include <fstream>
+#include <string>
+#include <cstdlib>
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+#include <linux/spi/spidev.h>
+
+/*
+ * Configuration connections
+ *
+ * CCK - MCSPI1_CLK
+ * DIN - MCSPI1_MOSI
+ * PROG_B - GPIO_175 - output (change mux)
+ * DONE - GPIO_173 - input (change mux)
+ * INIT_B - GPIO_114 - input (change mux)
+ *
+*/
+
+namespace usrp_e_fpga_downloader_utility{
+
+const unsigned int PROG_B = 175;
+const unsigned int DONE = 173;
+const unsigned int INIT_B = 114;
+
+//static std::string bit_file = "safe_u1e.bin";
+
+const int BUF_SIZE = 4096;
+
+enum gpio_direction {IN, OUT};
+
+class gpio {
+ public:
+
+ gpio(unsigned int gpio_num, gpio_direction pin_direction);
+
+ bool get_value();
+ void set_value(bool state);
+
+ private:
+
+ std::stringstream base_path;
+ std::fstream value_file;
+};
+
+class spidev {
+ public:
+
+ spidev(std::string dev_name);
+ ~spidev();
+
+ void send(char *wbuf, char *rbuf, unsigned int nbytes);
+
+ private:
+
+ int fd;
+
+};
+
+gpio::gpio(unsigned int gpio_num, gpio_direction pin_direction)
+{
+ std::fstream export_file;
+
+ export_file.open("/sys/class/gpio/export", std::ios::out);
+ if (not export_file.is_open()) throw uhd::os_error(
+ "Failed to open gpio export file."
+ );
+
+ export_file << gpio_num << std::endl;
+
+ base_path << "/sys/class/gpio/gpio" << gpio_num << std::flush;
+
+ std::fstream direction_file;
+ std::string direction_file_name;
+
+ if (gpio_num != 114) {
+ direction_file_name = base_path.str() + "/direction";
+
+ direction_file.open(direction_file_name.c_str());
+ if (!direction_file.is_open()) throw uhd::os_error(
+ "Failed to open direction file."
+ );
+ if (pin_direction == OUT)
+ direction_file << "out" << std::endl;
+ else
+ direction_file << "in" << std::endl;
+ }
+
+ std::string value_file_name;
+
+ value_file_name = base_path.str() + "/value";
+
+ value_file.open(value_file_name.c_str(), std::ios_base::in | std::ios_base::out);
+ if (!value_file.is_open()) throw uhd::os_error(
+ "Failed to open value file."
+ );
+}
+
+bool gpio::get_value()
+{
+
+ std::string val;
+
+ std::getline(value_file, val);
+ value_file.seekg(0);
+
+ if (val == "0")
+ return false;
+ else if (val == "1")
+ return true;
+ else
+ throw uhd::os_error("Data read from value file|" + val + "|");
+
+ return false;
+}
+
+void gpio::set_value(bool state)
+{
+
+ if (state)
+ value_file << "1" << std::endl;
+ else
+ value_file << "0" << std::endl;
+}
+
+static void prepare_fpga_for_configuration(gpio &prog, gpio &)//init)
+{
+
+ prog.set_value(true);
+ prog.set_value(false);
+ prog.set_value(true);
+
+#if 0
+ bool ready_to_program(false);
+ unsigned int count(0);
+ do {
+ ready_to_program = init.get_value();
+ count++;
+
+ sleep(1);
+ } while (count < 10 && !ready_to_program);
+
+ if (count == 10) {
+ throw uhd::os_error("FPGA not ready for programming.");
+ }
+#endif
+}
+
+spidev::spidev(std::string fname)
+{
+ int ret;
+ int mode = 0;
+ int speed = 12000000;
+ int bits = 8;
+
+ fd = open(fname.c_str(), O_RDWR);
+
+ ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
+ ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
+ ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
+}
+
+
+spidev::~spidev()
+{
+ close(fd);
+}
+
+void spidev::send(char *buf, char *rbuf, unsigned int nbytes)
+{
+ int ret;
+
+ struct spi_ioc_transfer tr;
+ tr.tx_buf = (unsigned long) buf;
+ tr.rx_buf = (unsigned long) rbuf;
+ tr.len = nbytes;
+ tr.delay_usecs = 0;
+ tr.speed_hz = 48000000;
+ tr.bits_per_word = 8;
+
+ ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
+
+}
+
+static void send_file_to_fpga(const std::string &file_name, gpio &error, gpio &done)
+{
+ std::ifstream bitstream;
+
+ bitstream.open(file_name.c_str(), std::ios::binary);
+ if (!bitstream.is_open()) throw uhd::os_error(
+ "Coult not open the file: " + file_name
+ );
+
+ spidev spi("/dev/spidev1.0");
+ char buf[BUF_SIZE];
+ char rbuf[BUF_SIZE];
+
+ do {
+ bitstream.read(buf, BUF_SIZE);
+ spi.send(buf, rbuf, bitstream.gcount());
+
+ if (error.get_value())
+ throw uhd::os_error("INIT_B went high, error occured.");
+
+ if (!done.get_value())
+ UHD_MSG(status) << "Configuration complete." << std::endl;
+
+ } while (bitstream.gcount() == BUF_SIZE);
+}
+
+}//namespace usrp_e_fpga_downloader_utility
+
+void e100_load_fpga(const std::string &bin_file){
+ using namespace usrp_e_fpga_downloader_utility;
+
+ gpio gpio_prog_b(PROG_B, OUT);
+ gpio gpio_init_b(INIT_B, IN);
+ gpio gpio_done (DONE, IN);
+
+ UHD_MSG(status) << "Loading FPGA image: " << bin_file << "... " << std::flush;
+
+// if(std::system("/sbin/rmmod usrp_e") != 0){
+// UHD_MSG(warning) << "USRP-E100 FPGA downloader: could not unload usrp_e module" << std::endl;
+// }
+
+ prepare_fpga_for_configuration(gpio_prog_b, gpio_init_b);
+
+ UHD_MSG(status) << "done = " << gpio_done.get_value() << std::endl;
+
+ send_file_to_fpga(bin_file, gpio_init_b, gpio_done);
+
+// if(std::system("/sbin/modprobe usrp_e") != 0){
+// UHD_MSG(warning) << "USRP-E100 FPGA downloader: could not load usrp_e module" << std::endl;
+// }
+
+}
+
diff --git a/host/lib/usrp/e100/include/linux/usrp_e.h b/host/lib/usrp/e100/include/linux/usrp_e.h
new file mode 100644
index 000000000..37e9ee31a
--- /dev/null
+++ b/host/lib/usrp/e100/include/linux/usrp_e.h
@@ -0,0 +1,60 @@
+
+/*
+ * Copyright (C) 2010 Ettus Research, LLC
+ *
+ * Written by Philip Balister <philip@opensdr.com>
+ *
+ * This 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 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __USRP_E_H
+#define __USRP_E_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+struct usrp_e_ctl16 {
+ __u32 offset;
+ __u32 count;
+ __u16 buf[20];
+};
+
+struct usrp_e_ctl32 {
+ __u32 offset;
+ __u32 count;
+ __u32 buf[10];
+};
+
+#define USRP_E_IOC_MAGIC 'u'
+#define USRP_E_WRITE_CTL16 _IOW(USRP_E_IOC_MAGIC, 0x20, struct usrp_e_ctl16)
+#define USRP_E_READ_CTL16 _IOWR(USRP_E_IOC_MAGIC, 0x21, struct usrp_e_ctl16)
+#define USRP_E_WRITE_CTL32 _IOW(USRP_E_IOC_MAGIC, 0x22, struct usrp_e_ctl32)
+#define USRP_E_READ_CTL32 _IOWR(USRP_E_IOC_MAGIC, 0x23, struct usrp_e_ctl32)
+#define USRP_E_GET_RB_INFO _IOR(USRP_E_IOC_MAGIC, 0x27, struct usrp_e_ring_buffer_size_t)
+#define USRP_E_GET_COMPAT_NUMBER _IO(USRP_E_IOC_MAGIC, 0x28)
+
+#define USRP_E_COMPAT_NUMBER 3
+
+/* Flag defines */
+#define RB_USER (1<<0)
+#define RB_KERNEL (1<<1)
+#define RB_OVERRUN (1<<2)
+#define RB_DMA_ACTIVE (1<<3)
+#define RB_USER_PROCESS (1<<4)
+
+struct ring_buffer_info {
+ int flags;
+ int len;
+};
+
+struct usrp_e_ring_buffer_size_t {
+ int num_pages_rx_flags;
+ int num_rx_frames;
+ int num_pages_tx_flags;
+ int num_tx_frames;
+};
+
+#endif
diff --git a/host/lib/usrp/e100/io_impl.cpp b/host/lib/usrp/e100/io_impl.cpp
new file mode 100644
index 000000000..e9608125f
--- /dev/null
+++ b/host/lib/usrp/e100/io_impl.cpp
@@ -0,0 +1,366 @@
+//
+// Copyright 2010-2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "recv_packet_demuxer.hpp"
+#include "validate_subdev_spec.hpp"
+#include "async_packet_handler.hpp"
+#include "../../transport/super_recv_packet_handler.hpp"
+#include "../../transport/super_send_packet_handler.hpp"
+#include <linux/usrp_e.h> //ioctl structures and constants
+#include "e100_impl.hpp"
+#include "e100_regs.hpp"
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/tasks.hpp>
+#include <uhd/utils/thread_priority.hpp>
+#include <uhd/transport/bounded_buffer.hpp>
+#include <boost/bind.hpp>
+#include <boost/format.hpp>
+#include <boost/bind.hpp>
+#include <boost/thread/thread.hpp>
+#include <poll.h> //poll
+#include <fcntl.h> //open, close
+#include <sstream>
+#include <fstream>
+#include <boost/make_shared.hpp>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+
+/***********************************************************************
+ * io impl details (internal to this file)
+ * - pirate crew of 1
+ * - bounded buffer
+ * - thread loop
+ * - vrt packet handler states
+ **********************************************************************/
+struct e100_impl::io_impl{
+ io_impl(void):
+ false_alarm(0), async_msg_fifo(1000/*messages deep*/)
+ { /* NOP */ }
+
+ double tick_rate; //set by update tick rate method
+ e100_ctrl::sptr iface; //so handle irq can peek and poke
+ void handle_irq(void);
+ size_t false_alarm;
+ //The data transport is listed first so that it is deconstructed last,
+ //which is after the states and booty which may hold managed buffers.
+ recv_packet_demuxer::sptr demuxer;
+
+ //a pirate's life is the life for me!
+ void recv_pirate_loop(
+ spi_iface::sptr //keep a sptr to iface which shares gpio147
+ ){
+ //open the GPIO and set it up for an IRQ
+ std::ofstream edge_file("/sys/class/gpio/gpio147/edge");
+ edge_file << "rising" << std::endl << std::flush;
+ edge_file.close();
+ int fd = ::open("/sys/class/gpio/gpio147/value", O_RDONLY);
+ if (fd < 0) UHD_MSG(error) << "Unable to open GPIO for IRQ\n";
+
+ while (not boost::this_thread::interruption_requested()){
+ pollfd pfd;
+ pfd.fd = fd;
+ pfd.events = POLLPRI | POLLERR;
+ ssize_t ret = ::poll(&pfd, 1, 100/*ms*/);
+ if (ret > 0) this->handle_irq();
+ }
+
+ //cleanup before thread exit
+ ::close(fd);
+ }
+ bounded_buffer<async_metadata_t> async_msg_fifo;
+ task::sptr pirate_task;
+};
+
+void e100_impl::io_impl::handle_irq(void){
+ //check the status of the async msg buffer
+ const boost::uint32_t status = iface->peek32(E100_REG_RB_ERR_STATUS);
+ if ((status & 0x3) == 0){ //not done or error
+ //This could be a false-alarm because spi readback is mixed in.
+ //So we just sleep for a bit rather than interrupt continuously.
+ if (false_alarm++ > 3) boost::this_thread::sleep(boost::posix_time::milliseconds(1));
+ return;
+ }
+ false_alarm = 0; //its a real message, reset the count...
+ //std::cout << boost::format("status: 0x%x") % status << std::endl;
+
+ //load the data struct and call the ioctl
+ usrp_e_ctl32 data;
+ data.offset = E100_REG_ERR_BUFF;
+ data.count = status >> 16;
+ iface->ioctl(USRP_E_READ_CTL32, &data);
+ //for (size_t i = 0; i < data.count; i++){
+ //data.buf[i] = iface->peek32(E100_REG_ERR_BUFF + i*sizeof(boost::uint32_t));
+ //std::cout << boost::format(" buff[%u] = 0x%08x\n") % i % data.buf[i];
+ //}
+
+ //unpack the vrt header and process below...
+ vrt::if_packet_info_t if_packet_info;
+ if_packet_info.num_packet_words32 = data.count;
+ try{vrt::if_hdr_unpack_le(data.buf, if_packet_info);}
+ catch(const std::exception &e){
+ UHD_MSG(error) << "Error unpacking vrt header:\n" << e.what() << std::endl;
+ goto prepare;
+ }
+
+ //handle a tx async report message
+ if (if_packet_info.sid == E100_TX_ASYNC_SID and if_packet_info.packet_type != vrt::if_packet_info_t::PACKET_TYPE_DATA){
+
+ //fill in the async metadata
+ async_metadata_t metadata;
+ load_metadata_from_buff(uhd::wtohx<boost::uint32_t>, metadata, if_packet_info, data.buf, tick_rate);
+
+ //push the message onto the queue
+ async_msg_fifo.push_with_pop_on_full(metadata);
+
+ //print some fastpath messages
+ standard_async_msg_prints(metadata);
+ }
+
+ //prepare for the next round
+ prepare:
+ iface->poke32(E100_REG_SR_ERR_CTRL, 1 << 0); //clear
+ while ((iface->peek32(E100_REG_RB_ERR_STATUS) & (1 << 2)) == 0){} //wait for idle
+ iface->poke32(E100_REG_SR_ERR_CTRL, 1 << 1); //start
+}
+
+/***********************************************************************
+ * Helper Functions
+ **********************************************************************/
+void e100_impl::io_init(void){
+
+ //create new io impl
+ _io_impl = UHD_PIMPL_MAKE(io_impl, ());
+ _io_impl->demuxer = recv_packet_demuxer::make(_data_transport, _rx_dsps.size(), E100_RX_SID_BASE);
+ _io_impl->iface = _fpga_ctrl;
+
+ //clear fifo state machines
+ _fpga_ctrl->poke32(E100_REG_CLEAR_FIFO, 0);
+
+ //allocate streamer weak ptrs containers
+ _rx_streamers.resize(_rx_dsps.size());
+ _tx_streamers.resize(1/*known to be 1 dsp*/);
+
+ //prepare the async msg buffer for incoming messages
+ _fpga_ctrl->poke32(E100_REG_SR_ERR_CTRL, 1 << 0); //clear
+ while ((_fpga_ctrl->peek32(E100_REG_RB_ERR_STATUS) & (1 << 2)) == 0){} //wait for idle
+ _fpga_ctrl->poke32(E100_REG_SR_ERR_CTRL, 1 << 1); //start
+
+ //spawn a pirate, yarrr!
+ _io_impl->pirate_task = task::make(boost::bind(
+ &e100_impl::io_impl::recv_pirate_loop, _io_impl.get(), _aux_spi_iface
+ ));
+}
+
+void e100_impl::update_tick_rate(const double rate){
+ _io_impl->tick_rate = rate;
+
+ //update the tick rate on all existing streamers -> thread safe
+ for (size_t i = 0; i < _rx_streamers.size(); i++){
+ boost::shared_ptr<sph::recv_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_rx_streamers[i].lock());
+ if (my_streamer.get() == NULL) continue;
+ my_streamer->set_tick_rate(rate);
+ }
+ for (size_t i = 0; i < _tx_streamers.size(); i++){
+ boost::shared_ptr<sph::send_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<sph::send_packet_streamer>(_tx_streamers[i].lock());
+ if (my_streamer.get() == NULL) continue;
+ my_streamer->set_tick_rate(rate);
+ }
+}
+
+void e100_impl::update_rx_samp_rate(const size_t dspno, const double rate){
+ boost::shared_ptr<sph::recv_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_rx_streamers[dspno].lock());
+ if (my_streamer.get() == NULL) return;
+
+ my_streamer->set_samp_rate(rate);
+ const double adj = _rx_dsps[dspno]->get_scaling_adjustment();
+ my_streamer->set_scale_factor(adj);
+}
+
+void e100_impl::update_tx_samp_rate(const size_t dspno, const double rate){
+ boost::shared_ptr<sph::send_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<sph::send_packet_streamer>(_tx_streamers[dspno].lock());
+ if (my_streamer.get() == NULL) return;
+
+ my_streamer->set_samp_rate(rate);
+ const double adj = _tx_dsp->get_scaling_adjustment();
+ my_streamer->set_scale_factor(adj);
+}
+
+void e100_impl::update_rates(void){
+ const fs_path mb_path = "/mboards/0";
+ _tree->access<double>(mb_path / "tick_rate").update();
+
+ //and now that the tick rate is set, init the host rates to something
+ BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "rx_dsps")){
+ _tree->access<double>(mb_path / "rx_dsps" / name / "rate" / "value").update();
+ }
+ BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "tx_dsps")){
+ _tree->access<double>(mb_path / "tx_dsps" / name / "rate" / "value").update();
+ }
+}
+
+void e100_impl::update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){
+ fs_path root = "/mboards/0/dboards";
+
+ //sanity checking
+ validate_subdev_spec(_tree, spec, "rx");
+
+ //setup mux for this spec
+ bool fe_swapped = false;
+ for (size_t i = 0; i < spec.size(); i++){
+ const std::string conn = _tree->access<std::string>(root / spec[i].db_name / "rx_frontends" / spec[i].sd_name / "connection").get();
+ if (i == 0 and (conn == "QI" or conn == "Q")) fe_swapped = true;
+ _rx_dsps[i]->set_mux(conn, fe_swapped);
+ }
+ _rx_fe->set_mux(fe_swapped);
+}
+
+void e100_impl::update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){
+ fs_path root = "/mboards/0/dboards";
+
+ //sanity checking
+ validate_subdev_spec(_tree, spec, "tx");
+
+ //set the mux for this spec
+ const std::string conn = _tree->access<std::string>(root / spec[0].db_name / "tx_frontends" / spec[0].sd_name / "connection").get();
+ _tx_fe->set_mux(conn);
+}
+
+/***********************************************************************
+ * Async Recv
+ **********************************************************************/
+bool e100_impl::recv_async_msg(
+ async_metadata_t &async_metadata, double timeout
+){
+ boost::this_thread::disable_interruption di; //disable because the wait can throw
+ return _io_impl->async_msg_fifo.pop_with_timed_wait(async_metadata, timeout);
+}
+
+/***********************************************************************
+ * Receive streamer
+ **********************************************************************/
+rx_streamer::sptr e100_impl::get_rx_stream(const uhd::stream_args_t &args_){
+ stream_args_t args = args_;
+
+ //setup defaults for unspecified values
+ args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format;
+ args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels;
+
+ //calculate packet size
+ static const size_t hdr_size = 0
+ + vrt::max_if_hdr_words32*sizeof(boost::uint32_t)
+ + sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer
+ - sizeof(vrt::if_packet_info_t().cid) //no class id ever used
+ - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used
+ ;
+ const size_t bpp = _data_transport->get_recv_frame_size() - hdr_size;
+ const size_t bpi = convert::get_bytes_per_item(args.otw_format);
+ const size_t spp = unsigned(args.args.cast<double>("spp", bpp/bpi));
+
+ //make the new streamer given the samples per packet
+ boost::shared_ptr<sph::recv_packet_streamer> my_streamer = boost::make_shared<sph::recv_packet_streamer>(spp);
+
+ //init some streamer stuff
+ my_streamer->resize(args.channels.size());
+ my_streamer->set_vrt_unpacker(&vrt::if_hdr_unpack_le);
+
+ //set the converter
+ uhd::convert::id_type id;
+ id.input_format = args.otw_format + "_item32_le";
+ id.num_inputs = 1;
+ id.output_format = args.cpu_format;
+ id.num_outputs = 1;
+ my_streamer->set_converter(id);
+
+ //bind callbacks for the handler
+ for (size_t chan_i = 0; chan_i < args.channels.size(); chan_i++){
+ const size_t dsp = args.channels[chan_i];
+ _rx_dsps[dsp]->set_nsamps_per_packet(spp); //seems to be a good place to set this
+ _rx_dsps[dsp]->setup(args);
+ my_streamer->set_xport_chan_get_buff(chan_i, boost::bind(
+ &recv_packet_demuxer::get_recv_buff, _io_impl->demuxer, dsp, _1
+ ), true /*flush*/);
+ my_streamer->set_overflow_handler(chan_i, boost::bind(
+ &rx_dsp_core_200::handle_overflow, _rx_dsps[dsp]
+ ));
+ _rx_streamers[dsp] = my_streamer; //store weak pointer
+ }
+
+ //sets all tick and samp rates on this streamer
+ this->update_rates();
+
+ return my_streamer;
+}
+
+/***********************************************************************
+ * Transmit streamer
+ **********************************************************************/
+tx_streamer::sptr e100_impl::get_tx_stream(const uhd::stream_args_t &args_){
+ stream_args_t args = args_;
+
+ //setup defaults for unspecified values
+ args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format;
+ args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels;
+
+ //calculate packet size
+ static const size_t hdr_size = 0
+ + vrt::max_if_hdr_words32*sizeof(boost::uint32_t)
+ + sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer
+ - sizeof(vrt::if_packet_info_t().sid) //no stream id ever used
+ - sizeof(vrt::if_packet_info_t().cid) //no class id ever used
+ - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used
+ ;
+ static const size_t bpp = _data_transport->get_send_frame_size() - hdr_size;
+ const size_t spp = bpp/convert::get_bytes_per_item(args.otw_format);
+
+ //make the new streamer given the samples per packet
+ boost::shared_ptr<sph::send_packet_streamer> my_streamer = boost::make_shared<sph::send_packet_streamer>(spp);
+
+ //init some streamer stuff
+ my_streamer->resize(args.channels.size());
+ my_streamer->set_vrt_packer(&vrt::if_hdr_pack_le);
+
+ //set the converter
+ uhd::convert::id_type id;
+ id.input_format = args.cpu_format;
+ id.num_inputs = 1;
+ id.output_format = args.otw_format + "_item32_le";
+ id.num_outputs = 1;
+ my_streamer->set_converter(id);
+
+ //bind callbacks for the handler
+ for (size_t chan_i = 0; chan_i < args.channels.size(); chan_i++){
+ const size_t dsp = args.channels[chan_i];
+ UHD_ASSERT_THROW(dsp == 0); //always 0
+ _tx_dsp->setup(args);
+ my_streamer->set_xport_chan_get_buff(chan_i, boost::bind(
+ &zero_copy_if::get_send_buff, _data_transport, _1
+ ));
+ _tx_streamers[dsp] = my_streamer; //store weak pointer
+ }
+
+ //sets all tick and samp rates on this streamer
+ this->update_rates();
+
+ return my_streamer;
+}
diff --git a/host/lib/usrp/gps_ctrl.cpp b/host/lib/usrp/gps_ctrl.cpp
new file mode 100644
index 000000000..45fa1f39e
--- /dev/null
+++ b/host/lib/usrp/gps_ctrl.cpp
@@ -0,0 +1,275 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/usrp/gps_ctrl.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/types/sensors.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/tokenizer.hpp>
+#include <boost/format.hpp>
+
+using namespace uhd;
+using namespace boost::gregorian;
+using namespace boost::posix_time;
+using namespace boost::algorithm;
+using namespace boost::this_thread;
+
+/*!
+ * A GPS control for Jackson Labs devices (and other NMEA compatible GPS's)
+ */
+
+class gps_ctrl_impl : public gps_ctrl{
+public:
+ gps_ctrl_impl(uart_iface::sptr uart){
+ _uart = uart;
+
+ std::string reply;
+ bool i_heard_some_nmea = false, i_heard_something_weird = false;
+ gps_type = GPS_TYPE_NONE;
+
+ //first we look for a Jackson Labs Firefly (since that's what we provide...)
+ _flush(); //get whatever junk is in the rx buffer right now, and throw it away
+ _send("HAAAY GUYYYYS\n"); //to elicit a response from the Firefly
+
+ //wait for _send(...) to return
+ sleep(milliseconds(FIREFLY_STUPID_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);
+ while(boost::get_system_time() < comm_timeout) {
+ reply = _recv();
+ if(reply.find("Command Error") != std::string::npos) {
+ gps_type = GPS_TYPE_JACKSON_LABS;
+ break;
+ }
+ else if(reply.substr(0, 3) == "$GP") i_heard_some_nmea = true; //but keep looking for that "Command Error" response
+ else if(reply.length() != 0) i_heard_something_weird = true; //probably wrong baud rate
+ sleep(milliseconds(GPS_TIMEOUT_DELAY_MS));
+ }
+
+ if((i_heard_some_nmea) && (gps_type != GPS_TYPE_JACKSON_LABS)) gps_type = GPS_TYPE_GENERIC_NMEA;
+
+ if((gps_type == GPS_TYPE_NONE) && i_heard_something_weird) {
+ UHD_MSG(error) << "GPS invalid reply \"" << reply << "\", assuming none available" << std::endl;
+ }
+
+ switch(gps_type) {
+ case GPS_TYPE_JACKSON_LABS:
+ UHD_MSG(status) << "Found a Jackson Labs GPS" << std::endl;
+ init_firefly();
+ break;
+
+ case GPS_TYPE_GENERIC_NMEA:
+ if(gps_type == GPS_TYPE_GENERIC_NMEA) UHD_MSG(status) << "Found a generic NMEA GPS device" << std::endl;
+ break;
+
+ case GPS_TYPE_NONE:
+ default:
+ break;
+
+ }
+ }
+
+ ~gps_ctrl_impl(void){
+ /* NOP */
+ }
+
+ //return a list of supported sensors
+ std::vector<std::string> get_sensors(void) {
+ std::vector<std::string> ret = boost::assign::list_of
+ ("gps_gpgga")
+ ("gps_gprmc")
+ ("gps_gpgsa")
+ ("gps_time")
+ ("gps_locked");
+ return ret;
+ }
+
+ uhd::sensor_value_t get_sensor(std::string key) {
+ if(key == "gps_gpgga"
+ or key == "gps_gprmc"
+ or key == "gps_gpgsa") {
+ return sensor_value_t(
+ boost::to_upper_copy(key),
+ get_nmea(boost::to_upper_copy(key.substr(4,8))),
+ "");
+ }
+ else if(key == "gps_time") {
+ return sensor_value_t("GPS epoch time", int(get_epoch_time()), "seconds");
+ }
+ else if(key == "gps_locked") {
+ return sensor_value_t("GPS lock status", locked(), "locked", "unlocked");
+ }
+ else {
+ throw uhd::value_error("gps ctrl get_sensor unknown key: " + key);
+ }
+ }
+
+private:
+ void init_firefly(void) {
+ //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(FIREFLY_STUPID_DELAY_MS));
+ _send("SYST:COMM:SER:ECHO OFF\n");
+ sleep(milliseconds(FIREFLY_STUPID_DELAY_MS));
+ _send("SYST:COMM:SER:PRO OFF\n");
+ sleep(milliseconds(FIREFLY_STUPID_DELAY_MS));
+ _send("GPS:GPGGA 1\n");
+ sleep(milliseconds(FIREFLY_STUPID_DELAY_MS));
+ _send("GPS:GGAST 0\n");
+ sleep(milliseconds(FIREFLY_STUPID_DELAY_MS));
+ _send("GPS:GPRMC 1\n");
+ sleep(milliseconds(FIREFLY_STUPID_DELAY_MS));
+ _send("GPS:GPGSA 1\n");
+ sleep(milliseconds(FIREFLY_STUPID_DELAY_MS));
+ }
+
+ //retrieve a raw NMEA sentence
+ std::string get_nmea(std::string msgtype) {
+ msgtype.insert(0, "$");
+ std::string reply;
+ if(not gps_detected()) {
+ UHD_MSG(error) << "get_nmea(): unsupported GPS or no GPS detected";
+ return std::string();
+ }
+
+ _flush(); //flush all input before waiting for a message
+
+ const boost::system_time comm_timeout = boost::get_system_time() + milliseconds(GPS_COMM_TIMEOUT_MS);
+ while(boost::get_system_time() < comm_timeout) {
+ reply = _recv();
+ if(reply.substr(0, 6) == msgtype)
+ return reply;
+ boost::this_thread::sleep(milliseconds(GPS_TIMEOUT_DELAY_MS));
+ }
+ throw uhd::value_error(str(boost::format("get_nmea(): no %s message found") % msgtype));
+ return std::string();
+ }
+
+ //helper function to retrieve a field from an NMEA sentence
+ std::string get_token(std::string sentence, size_t offset) {
+ boost::tokenizer<boost::escaped_list_separator<char> > tok(sentence);
+ std::vector<std::string> toked;
+ tok.assign(sentence); //this can throw
+ toked.assign(tok.begin(), tok.end());
+
+ if(toked.size() <= offset) {
+ throw uhd::value_error(str(boost::format("Invalid response \"%s\"") % sentence));
+ }
+ return toked[offset];
+ }
+
+ ptime get_time(void) {
+ int error_cnt = 0;
+ ptime gps_time;
+ while(error_cnt < 3) {
+ try {
+ std::string reply = get_nmea("GPRMC");
+
+ std::string datestr = get_token(reply, 9);
+ std::string timestr = get_token(reply, 1);
+
+ if(datestr.size() == 0 or timestr.size() == 0) {
+ throw uhd::value_error(str(boost::format("Invalid response \"%s\"") % reply));
+ }
+
+ //just trust me on this one
+ gps_time = ptime( date(
+ greg_year(boost::lexical_cast<int>(datestr.substr(4, 2)) + 2000),
+ greg_month(boost::lexical_cast<int>(datestr.substr(2, 2))),
+ greg_day(boost::lexical_cast<int>(datestr.substr(0, 2)))
+ ),
+ hours( boost::lexical_cast<int>(timestr.substr(0, 2)))
+ + minutes(boost::lexical_cast<int>(timestr.substr(2, 2)))
+ + seconds(boost::lexical_cast<int>(timestr.substr(4, 2)))
+ );
+ return gps_time;
+
+ } catch(std::exception &e) {
+ UHD_MSG(warning) << "get_time: " << e.what();
+ error_cnt++;
+ }
+ }
+ throw uhd::value_error("Timeout after no valid message found");
+
+ return gps_time; //keep gcc from complaining
+ }
+
+ time_t get_epoch_time(void) {
+ return (get_time() - from_time_t(0)).total_seconds();
+ }
+
+ bool gps_detected(void) {
+ return (gps_type != GPS_TYPE_NONE);
+ }
+
+ bool locked(void) {
+ int error_cnt = 0;
+ while(error_cnt < 3) {
+ try {
+ std::string reply = get_nmea("GPGGA");
+ if(reply.size() <= 1) return false;
+
+ return (get_token(reply, 6) != "0");
+ } catch(std::exception &e) {
+ UHD_MSG(warning) << "locked: " << e.what();
+ error_cnt++;
+ }
+ }
+ throw uhd::value_error("Timeout after no valid message found");
+ return false;
+ }
+
+ uart_iface::sptr _uart;
+
+ void _flush(void){
+ while (not _uart->read_uart(0.0).empty()){
+ //NOP
+ }
+ }
+
+ std::string _recv(void){
+ return _uart->read_uart(GPS_TIMEOUT_DELAY_MS/1000.);
+ }
+
+ void _send(const std::string &buf){
+ return _uart->write_uart(buf);
+ }
+
+ enum {
+ GPS_TYPE_JACKSON_LABS,
+ GPS_TYPE_GENERIC_NMEA,
+ GPS_TYPE_NONE
+ } gps_type;
+
+ static const int GPS_COMM_TIMEOUT_MS = 1500;
+ static const int GPS_TIMEOUT_DELAY_MS = 200;
+ static const int FIREFLY_STUPID_DELAY_MS = 200;
+};
+
+/***********************************************************************
+ * Public make function for the GPS control
+ **********************************************************************/
+gps_ctrl::sptr gps_ctrl::make(uart_iface::sptr uart){
+ return sptr(new gps_ctrl_impl(uart));
+}
diff --git a/host/lib/usrp/mboard_eeprom.cpp b/host/lib/usrp/mboard_eeprom.cpp
new file mode 100644
index 000000000..785d30296
--- /dev/null
+++ b/host/lib/usrp/mboard_eeprom.cpp
@@ -0,0 +1,445 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/usrp/mboard_eeprom.hpp>
+#include <uhd/types/mac_addr.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <boost/asio/ip/address_v4.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/foreach.hpp>
+#include <algorithm>
+#include <iostream>
+#include <cstddef>
+
+using namespace uhd;
+using namespace uhd::usrp;
+
+/***********************************************************************
+ * Constants
+ **********************************************************************/
+static const size_t SERIAL_LEN = 9;
+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);
+ const byte_vector_t lsb_msb = boost::assign::list_of
+ (boost::uint8_t(num >> 0))(boost::uint8_t(num >> 8));
+ return lsb_msb;
+}
+
+//! convert a byte vector read from eeprom to a string
+static std::string uint16_bytes_to_string(const byte_vector_t &bytes){
+ const boost::uint16_t num = (boost::uint16_t(bytes.at(0)) << 0) | (boost::uint16_t(bytes.at(1)) << 8);
+ return (num == 0 or num == 0xffff)? "" : boost::lexical_cast<std::string>(num);
+}
+
+/***********************************************************************
+ * Implementation of N100 load/store
+ **********************************************************************/
+static const boost::uint8_t N100_EEPROM_ADDR = 0x50;
+
+static const uhd::dict<std::string, boost::uint8_t> USRP_N100_OFFSETS = boost::assign::map_list_of
+ ("hardware", 0x00)
+ ("mac-addr", 0x02)
+ ("ip-addr", 0x0C)
+ //leave space here for other addresses (perhaps)
+ ("revision", 0x12)
+ ("product", 0x14)
+ ("gpsdo", 0x17)
+ ("serial", 0x18)
+ ("name", 0x18 + SERIAL_LEN)
+;
+
+enum n200_gpsdo_type{
+ N200_GPSDO_NONE = 0,
+ N200_GPSDO_INTERNAL = 1,
+ N200_GPSDO_ONBOARD = 2
+};
+
+static void load_n100(mboard_eeprom_t &mb_eeprom, i2c_iface &iface){
+ //extract the hardware number
+ mb_eeprom["hardware"] = uint16_bytes_to_string(
+ iface.read_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["hardware"], 2)
+ );
+
+ //extract the revision number
+ mb_eeprom["revision"] = uint16_bytes_to_string(
+ iface.read_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["revision"], 2)
+ );
+
+ //extract the product code
+ mb_eeprom["product"] = uint16_bytes_to_string(
+ iface.read_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["product"], 2)
+ );
+
+ //extract the addresses
+ mb_eeprom["mac-addr"] = mac_addr_t::from_bytes(iface.read_eeprom(
+ N100_EEPROM_ADDR, USRP_N100_OFFSETS["mac-addr"], 6
+ )).to_string();
+
+ boost::asio::ip::address_v4::bytes_type ip_addr_bytes;
+ byte_copy(iface.read_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["ip-addr"], 4), ip_addr_bytes);
+ mb_eeprom["ip-addr"] = boost::asio::ip::address_v4(ip_addr_bytes).to_string();
+
+ //gpsdo capabilities
+ boost::uint8_t gpsdo_byte = iface.read_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["gpsdo"], 1).at(0);
+ switch(n200_gpsdo_type(gpsdo_byte)){
+ case N200_GPSDO_INTERNAL: mb_eeprom["gpsdo"] = "internal"; break;
+ case N200_GPSDO_ONBOARD: mb_eeprom["gpsdo"] = "onboard"; break;
+ default: mb_eeprom["gpsdo"] = "none";
+ }
+
+ //extract the serial
+ mb_eeprom["serial"] = bytes_to_string(iface.read_eeprom(
+ N100_EEPROM_ADDR, USRP_N100_OFFSETS["serial"], SERIAL_LEN
+ ));
+
+ //extract the name
+ mb_eeprom["name"] = bytes_to_string(iface.read_eeprom(
+ N100_EEPROM_ADDR, USRP_N100_OFFSETS["name"], NAME_MAX_LEN
+ ));
+
+ //Empty serial correction: use the mac address to determine serial.
+ //Older usrp2 models don't have a serial burned into EEPROM.
+ //The lower mac address bits will function as the serial number.
+ if (mb_eeprom["serial"].empty()){
+ byte_vector_t mac_addr_bytes = mac_addr_t::from_string(mb_eeprom["mac-addr"]).to_bytes();
+ unsigned serial = mac_addr_bytes.at(5) | (unsigned(mac_addr_bytes.at(4) & 0x0f) << 8);
+ mb_eeprom["serial"] = boost::lexical_cast<std::string>(serial);
+ }
+}
+
+static void store_n100(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface){
+ //parse the revision number
+ if (mb_eeprom.has_key("hardware")) iface.write_eeprom(
+ N100_EEPROM_ADDR, USRP_N100_OFFSETS["hardware"],
+ string_to_uint16_bytes(mb_eeprom["hardware"])
+ );
+
+ //parse the revision number
+ if (mb_eeprom.has_key("revision")) iface.write_eeprom(
+ N100_EEPROM_ADDR, USRP_N100_OFFSETS["revision"],
+ string_to_uint16_bytes(mb_eeprom["revision"])
+ );
+
+ //parse the product code
+ if (mb_eeprom.has_key("product")) iface.write_eeprom(
+ N100_EEPROM_ADDR, USRP_N100_OFFSETS["product"],
+ string_to_uint16_bytes(mb_eeprom["product"])
+ );
+
+ //store the addresses
+ if (mb_eeprom.has_key("mac-addr")) iface.write_eeprom(
+ N100_EEPROM_ADDR, USRP_N100_OFFSETS["mac-addr"],
+ mac_addr_t::from_string(mb_eeprom["mac-addr"]).to_bytes()
+ );
+
+ if (mb_eeprom.has_key("ip-addr")){
+ byte_vector_t ip_addr_bytes(4);
+ byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["ip-addr"]).to_bytes(), ip_addr_bytes);
+ iface.write_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["ip-addr"], ip_addr_bytes);
+ }
+
+ //gpsdo capabilities
+ if (mb_eeprom.has_key("gpsdo")){
+ boost::uint8_t gpsdo_byte = N200_GPSDO_NONE;
+ if (mb_eeprom["gpsdo"] == "internal") gpsdo_byte = N200_GPSDO_INTERNAL;
+ if (mb_eeprom["gpsdo"] == "onboard") gpsdo_byte = N200_GPSDO_ONBOARD;
+ iface.write_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["gpsdo"], byte_vector_t(1, gpsdo_byte));
+ }
+
+ //store the serial
+ if (mb_eeprom.has_key("serial")) iface.write_eeprom(
+ N100_EEPROM_ADDR, USRP_N100_OFFSETS["serial"],
+ string_to_bytes(mb_eeprom["serial"], SERIAL_LEN)
+ );
+
+ //store the name
+ if (mb_eeprom.has_key("name")) iface.write_eeprom(
+ N100_EEPROM_ADDR, USRP_N100_OFFSETS["name"],
+ string_to_bytes(mb_eeprom["name"], NAME_MAX_LEN)
+ );
+}
+
+/***********************************************************************
+ * Implementation of B000 load/store
+ **********************************************************************/
+static const boost::uint8_t B000_EEPROM_ADDR = 0x50;
+static const size_t B000_SERIAL_LEN = 8;
+
+//use char array so we dont need to attribute packed
+struct b000_eeprom_map{
+ unsigned char _r[221];
+ unsigned char mcr[4];
+ unsigned char name[NAME_MAX_LEN];
+ unsigned char serial[B000_SERIAL_LEN];
+};
+
+static void load_b000(mboard_eeprom_t &mb_eeprom, i2c_iface &iface){
+ //extract the serial
+ mb_eeprom["serial"] = bytes_to_string(iface.read_eeprom(
+ B000_EEPROM_ADDR, offsetof(b000_eeprom_map, serial), B000_SERIAL_LEN
+ ));
+
+ //extract the name
+ mb_eeprom["name"] = bytes_to_string(iface.read_eeprom(
+ B000_EEPROM_ADDR, offsetof(b000_eeprom_map, name), NAME_MAX_LEN
+ ));
+
+ //extract master clock rate as a 32-bit uint in Hz
+ boost::uint32_t master_clock_rate;
+ const byte_vector_t rate_bytes = iface.read_eeprom(
+ B000_EEPROM_ADDR, offsetof(b000_eeprom_map, mcr), sizeof(master_clock_rate)
+ );
+ std::copy(
+ rate_bytes.begin(), rate_bytes.end(), //input
+ reinterpret_cast<boost::uint8_t *>(&master_clock_rate) //output
+ );
+ master_clock_rate = ntohl(master_clock_rate);
+ if (master_clock_rate > 1e6 and master_clock_rate < 1e9){
+ mb_eeprom["mcr"] = boost::lexical_cast<std::string>(master_clock_rate);
+ }
+ else mb_eeprom["mcr"] = "";
+}
+
+static void store_b000(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface){
+ //store the serial
+ if (mb_eeprom.has_key("serial")) iface.write_eeprom(
+ B000_EEPROM_ADDR, offsetof(b000_eeprom_map, serial),
+ string_to_bytes(mb_eeprom["serial"], B000_SERIAL_LEN)
+ );
+
+ //store the name
+ if (mb_eeprom.has_key("name")) iface.write_eeprom(
+ B000_EEPROM_ADDR, offsetof(b000_eeprom_map, name),
+ string_to_bytes(mb_eeprom["name"], NAME_MAX_LEN)
+ );
+
+ //store the master clock rate as a 32-bit uint in Hz
+ if (mb_eeprom.has_key("mcr")){
+ boost::uint32_t master_clock_rate = boost::uint32_t(boost::lexical_cast<double>(mb_eeprom["mcr"]));
+ master_clock_rate = htonl(master_clock_rate);
+ const byte_vector_t rate_bytes(
+ reinterpret_cast<const boost::uint8_t *>(&master_clock_rate),
+ reinterpret_cast<const boost::uint8_t *>(&master_clock_rate) + sizeof(master_clock_rate)
+ );
+ iface.write_eeprom(
+ B000_EEPROM_ADDR, offsetof(b000_eeprom_map, mcr), rate_bytes
+ );
+ }
+}
+
+/***********************************************************************
+ * Implementation of B100 load/store
+ **********************************************************************/
+static const boost::uint8_t B100_EEPROM_ADDR = 0x50;
+
+//use char array so we dont need to attribute packed
+struct b100_eeprom_map{
+ unsigned char _r[220];
+ unsigned char revision[2];
+ unsigned char product[2];
+ unsigned char name[NAME_MAX_LEN];
+ unsigned char serial[SERIAL_LEN];
+};
+
+static void load_b100(mboard_eeprom_t &mb_eeprom, i2c_iface &iface){
+ //extract the revision number
+ mb_eeprom["revision"] = uint16_bytes_to_string(
+ iface.read_eeprom(B100_EEPROM_ADDR, offsetof(b100_eeprom_map, revision), 2)
+ );
+
+ //extract the product code
+ mb_eeprom["product"] = uint16_bytes_to_string(
+ iface.read_eeprom(B100_EEPROM_ADDR, offsetof(b100_eeprom_map, product), 2)
+ );
+
+ //extract the serial
+ mb_eeprom["serial"] = bytes_to_string(iface.read_eeprom(
+ B100_EEPROM_ADDR, offsetof(b100_eeprom_map, serial), SERIAL_LEN
+ ));
+
+ //extract the name
+ mb_eeprom["name"] = bytes_to_string(iface.read_eeprom(
+ B100_EEPROM_ADDR, offsetof(b100_eeprom_map, name), NAME_MAX_LEN
+ ));
+}
+
+static void store_b100(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface){
+ //parse the revision number
+ if (mb_eeprom.has_key("revision")) iface.write_eeprom(
+ B100_EEPROM_ADDR, offsetof(b100_eeprom_map, revision),
+ string_to_uint16_bytes(mb_eeprom["revision"])
+ );
+
+ //parse the product code
+ if (mb_eeprom.has_key("product")) iface.write_eeprom(
+ B100_EEPROM_ADDR, offsetof(b100_eeprom_map, product),
+ string_to_uint16_bytes(mb_eeprom["product"])
+ );
+
+ //store the serial
+ if (mb_eeprom.has_key("serial")) iface.write_eeprom(
+ B100_EEPROM_ADDR, offsetof(b100_eeprom_map, serial),
+ string_to_bytes(mb_eeprom["serial"], SERIAL_LEN)
+ );
+
+ //store the name
+ if (mb_eeprom.has_key("name")) iface.write_eeprom(
+ B100_EEPROM_ADDR, offsetof(b100_eeprom_map, name),
+ string_to_bytes(mb_eeprom["name"], NAME_MAX_LEN)
+ );
+}
+
+/***********************************************************************
+ * Implementation of E100 load/store
+ **********************************************************************/
+static const boost::uint8_t E100_EEPROM_ADDR = 0x51;
+
+struct e100_eeprom_map{
+ boost::uint16_t vendor;
+ boost::uint16_t device;
+ unsigned char revision;
+ unsigned char content;
+ unsigned char model[8];
+ unsigned char env_var[16];
+ unsigned char env_setting[64];
+ unsigned char serial[10];
+ unsigned char name[NAME_MAX_LEN];
+};
+
+template <typename T> static const byte_vector_t to_bytes(const T &item){
+ return byte_vector_t(
+ reinterpret_cast<const byte_vector_t::value_type *>(&item),
+ reinterpret_cast<const byte_vector_t::value_type *>(&item)+sizeof(item)
+ );
+}
+
+#define sizeof_member(struct_name, member_name) \
+ sizeof(reinterpret_cast<struct_name*>(NULL)->member_name)
+
+static void load_e100(mboard_eeprom_t &mb_eeprom, i2c_iface &iface){
+ const size_t num_bytes = offsetof(e100_eeprom_map, model);
+ byte_vector_t map_bytes = iface.read_eeprom(E100_EEPROM_ADDR, 0, num_bytes);
+ e100_eeprom_map map; std::memcpy(&map, &map_bytes[0], map_bytes.size());
+
+ mb_eeprom["vendor"] = boost::lexical_cast<std::string>(uhd::ntohx(map.vendor));
+ mb_eeprom["device"] = boost::lexical_cast<std::string>(uhd::ntohx(map.device));
+ mb_eeprom["revision"] = boost::lexical_cast<std::string>(unsigned(map.revision));
+ mb_eeprom["content"] = boost::lexical_cast<std::string>(unsigned(map.content));
+
+ #define load_e100_string_xx(key) mb_eeprom[#key] = bytes_to_string(iface.read_eeprom( \
+ E100_EEPROM_ADDR, offsetof(e100_eeprom_map, key), sizeof_member(e100_eeprom_map, key) \
+ ));
+
+ load_e100_string_xx(model);
+ load_e100_string_xx(env_var);
+ load_e100_string_xx(env_setting);
+ load_e100_string_xx(serial);
+ load_e100_string_xx(name);
+}
+
+static void store_e100(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface){
+
+ if (mb_eeprom.has_key("vendor")) iface.write_eeprom(
+ E100_EEPROM_ADDR, offsetof(e100_eeprom_map, vendor),
+ to_bytes(uhd::htonx(boost::lexical_cast<boost::uint16_t>(mb_eeprom["vendor"])))
+ );
+
+ if (mb_eeprom.has_key("device")) iface.write_eeprom(
+ E100_EEPROM_ADDR, offsetof(e100_eeprom_map, device),
+ to_bytes(uhd::htonx(boost::lexical_cast<boost::uint16_t>(mb_eeprom["device"])))
+ );
+
+ if (mb_eeprom.has_key("revision")) iface.write_eeprom(
+ E100_EEPROM_ADDR, offsetof(e100_eeprom_map, revision),
+ byte_vector_t(1, boost::lexical_cast<unsigned>(mb_eeprom["revision"]))
+ );
+
+ if (mb_eeprom.has_key("content")) iface.write_eeprom(
+ E100_EEPROM_ADDR, offsetof(e100_eeprom_map, content),
+ byte_vector_t(1, boost::lexical_cast<unsigned>(mb_eeprom["content"]))
+ );
+
+ #define store_e100_string_xx(key) if (mb_eeprom.has_key(#key)) iface.write_eeprom( \
+ E100_EEPROM_ADDR, offsetof(e100_eeprom_map, key), \
+ string_to_bytes(mb_eeprom[#key], sizeof_member(e100_eeprom_map, key)) \
+ );
+
+ store_e100_string_xx(model);
+ store_e100_string_xx(env_var);
+ store_e100_string_xx(env_setting);
+ store_e100_string_xx(serial);
+ store_e100_string_xx(name);
+}
+
+/***********************************************************************
+ * Implementation of mboard eeprom
+ **********************************************************************/
+mboard_eeprom_t::mboard_eeprom_t(void){
+ /* NOP */
+}
+
+mboard_eeprom_t::mboard_eeprom_t(i2c_iface &iface, map_type map){
+ switch(map){
+ case MAP_N100: load_n100(*this, iface); break;
+ case MAP_B000: load_b000(*this, iface); break;
+ case MAP_B100: load_b100(*this, iface); break;
+ case MAP_E100: load_e100(*this, iface); break;
+ }
+}
+
+void mboard_eeprom_t::commit(i2c_iface &iface, map_type map) const{
+ switch(map){
+ case MAP_N100: store_n100(*this, iface); break;
+ case MAP_B000: store_b000(*this, iface); break;
+ case MAP_B100: store_b100(*this, iface); break;
+ case MAP_E100: store_e100(*this, iface); break;
+ }
+}
diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp
new file mode 100644
index 000000000..d9be19b83
--- /dev/null
+++ b/host/lib/usrp/multi_usrp.cpp
@@ -0,0 +1,849 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/property_tree.hpp>
+#include <uhd/usrp/multi_usrp.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/gain_group.hpp>
+#include <boost/thread.hpp>
+#include <boost/foreach.hpp>
+#include <boost/format.hpp>
+#include <cmath>
+
+using namespace uhd;
+using namespace uhd::usrp;
+
+const std::string multi_usrp::ALL_GAINS = "";
+
+/***********************************************************************
+ * Helper methods
+ **********************************************************************/
+static void do_samp_rate_warning_message(
+ double target_rate,
+ double actual_rate,
+ const std::string &xx
+){
+ static const double max_allowed_error = 1.0; //Sps
+ if (std::abs(target_rate - actual_rate) > max_allowed_error){
+ UHD_MSG(warning) << boost::format(
+ "The hardware does not support the requested %s sample rate:\n"
+ "Target sample rate: %f MSps\n"
+ "Actual sample rate: %f MSps\n"
+ ) % xx % (target_rate/1e6) % (actual_rate/1e6);
+ }
+}
+
+static void do_tune_freq_warning_message(
+ const tune_request_t &tune_req,
+ double actual_freq,
+ const std::string &xx
+){
+ //forget the warning when manual policy
+ if (tune_req.dsp_freq_policy == tune_request_t::POLICY_MANUAL) return;
+ if (tune_req.rf_freq_policy == tune_request_t::POLICY_MANUAL) return;
+
+ const double target_freq = tune_req.target_freq;
+ static const double max_allowed_error = 1.0; //Hz
+ if (std::abs(target_freq - actual_freq) > max_allowed_error){
+ UHD_MSG(warning) << boost::format(
+ "The hardware does not support the requested %s frequency:\n"
+ "Target frequency: %f MHz\n"
+ "Actual frequency: %f MHz\n"
+ ) % xx % (target_freq/1e6) % (actual_freq/1e6);
+ }
+}
+
+static meta_range_t make_overall_tune_range(
+ const meta_range_t &fe_range,
+ const meta_range_t &dsp_range,
+ const double bw
+){
+ meta_range_t range;
+ BOOST_FOREACH(const range_t &sub_range, fe_range){
+ range.push_back(range_t(
+ sub_range.start() + std::max(dsp_range.start(), -bw),
+ sub_range.stop() + std::min(dsp_range.stop(), bw),
+ dsp_range.step()
+ ));
+ }
+ return range;
+}
+
+/***********************************************************************
+ * Gain helper functions
+ **********************************************************************/
+static double get_gain_value(property_tree::sptr subtree){
+ return subtree->access<double>("value").get();
+}
+
+static void set_gain_value(property_tree::sptr subtree, const double gain){
+ subtree->access<double>("value").set(gain);
+}
+
+static meta_range_t get_gain_range(property_tree::sptr subtree){
+ return subtree->access<meta_range_t>("range").get();
+}
+
+static gain_fcns_t make_gain_fcns_from_subtree(property_tree::sptr subtree){
+ gain_fcns_t gain_fcns;
+ gain_fcns.get_range = boost::bind(&get_gain_range, subtree);
+ gain_fcns.get_value = boost::bind(&get_gain_value, subtree);
+ gain_fcns.set_value = boost::bind(&set_gain_value, subtree, _1);
+ return gain_fcns;
+}
+
+/***********************************************************************
+ * Tune Helper Functions
+ **********************************************************************/
+static const double RX_SIGN = +1.0;
+static const double TX_SIGN = -1.0;
+
+static tune_result_t tune_xx_subdev_and_dsp(
+ const double xx_sign,
+ property_tree::sptr dsp_subtree,
+ property_tree::sptr rf_fe_subtree,
+ const tune_request_t &tune_request
+){
+ //------------------------------------------------------------------
+ //-- calculate the LO offset, only used with automatic policy
+ //------------------------------------------------------------------
+ double lo_offset = 0.0;
+ if (rf_fe_subtree->access<bool>("use_lo_offset").get()){
+ //If the local oscillator will be in the passband, use an offset.
+ //But constrain the LO offset by the width of the filter bandwidth.
+ const double rate = dsp_subtree->access<double>("rate/value").get();
+ const double bw = rf_fe_subtree->access<double>("bandwidth/value").get();
+ if (bw > rate) lo_offset = std::min((bw - rate)/2, rate/2);
+ }
+
+ //------------------------------------------------------------------
+ //-- set the RF frequency depending upon the policy
+ //------------------------------------------------------------------
+ double target_rf_freq = 0.0;
+ switch (tune_request.rf_freq_policy){
+ case tune_request_t::POLICY_AUTO:
+ target_rf_freq = tune_request.target_freq + lo_offset;
+ rf_fe_subtree->access<double>("freq/value").set(target_rf_freq);
+ break;
+
+ case tune_request_t::POLICY_MANUAL:
+ target_rf_freq = tune_request.rf_freq;
+ rf_fe_subtree->access<double>("freq/value").set(target_rf_freq);
+ break;
+
+ case tune_request_t::POLICY_NONE: break; //does not set
+ }
+ const double actual_rf_freq = rf_fe_subtree->access<double>("freq/value").get();
+
+ //------------------------------------------------------------------
+ //-- calculate the dsp freq, only used with automatic policy
+ //------------------------------------------------------------------
+ double target_dsp_freq = actual_rf_freq - tune_request.target_freq;
+
+ //invert the sign on the dsp freq for transmit
+ target_dsp_freq *= xx_sign;
+
+ //------------------------------------------------------------------
+ //-- set the dsp frequency depending upon the dsp frequency policy
+ //------------------------------------------------------------------
+ switch (tune_request.dsp_freq_policy){
+ case tune_request_t::POLICY_AUTO:
+ dsp_subtree->access<double>("freq/value").set(target_dsp_freq);
+ break;
+
+ case tune_request_t::POLICY_MANUAL:
+ target_dsp_freq = tune_request.dsp_freq;
+ dsp_subtree->access<double>("freq/value").set(target_dsp_freq);
+ break;
+
+ case tune_request_t::POLICY_NONE: break; //does not set
+ }
+ const double actual_dsp_freq = dsp_subtree->access<double>("freq/value").get();
+
+ //------------------------------------------------------------------
+ //-- load and return the tune result
+ //------------------------------------------------------------------
+ tune_result_t tune_result;
+ tune_result.target_rf_freq = target_rf_freq;
+ tune_result.actual_rf_freq = actual_rf_freq;
+ tune_result.target_dsp_freq = target_dsp_freq;
+ tune_result.actual_dsp_freq = actual_dsp_freq;
+ return tune_result;
+}
+
+static double derive_freq_from_xx_subdev_and_dsp(
+ const double xx_sign,
+ property_tree::sptr dsp_subtree,
+ property_tree::sptr rf_fe_subtree
+){
+ //extract actual dsp and IF frequencies
+ const double actual_rf_freq = rf_fe_subtree->access<double>("freq/value").get();
+ const double actual_dsp_freq = dsp_subtree->access<double>("freq/value").get();
+
+ //invert the sign on the dsp freq for transmit
+ return actual_rf_freq - actual_dsp_freq * xx_sign;
+}
+
+/***********************************************************************
+ * Multi USRP Implementation
+ **********************************************************************/
+class multi_usrp_impl : public multi_usrp{
+public:
+ multi_usrp_impl(const device_addr_t &addr){
+ _dev = device::make(addr);
+ _tree = _dev->get_tree();
+ }
+
+ device::sptr get_device(void){
+ return _dev;
+ }
+
+ /*******************************************************************
+ * Mboard methods
+ ******************************************************************/
+ void set_master_clock_rate(double rate, size_t mboard){
+ if (mboard != ALL_MBOARDS){
+ _tree->access<double>(mb_root(mboard) / "tick_rate").set(rate);
+ return;
+ }
+ for (size_t m = 0; m < get_num_mboards(); m++){
+ set_master_clock_rate(rate, m);
+ }
+ }
+
+ double get_master_clock_rate(size_t mboard){
+ return _tree->access<double>(mb_root(mboard) / "tick_rate").get();
+ }
+
+ std::string get_pp_string(void){
+ std::string buff = str(boost::format(
+ "%s USRP:\n"
+ " Device: %s\n"
+ )
+ % ((get_num_mboards() > 1)? "Multi" : "Single")
+ % (_tree->access<std::string>("/name").get())
+ );
+ for (size_t m = 0; m < get_num_mboards(); m++){
+ buff += str(boost::format(
+ " Mboard %d: %s\n"
+ ) % m
+ % (_tree->access<std::string>(mb_root(m) / "name").get())
+ );
+ }
+
+ //----------- rx side of life ----------------------------------
+ for (size_t m = 0, chan = 0; m < get_num_mboards(); m++){
+ for (; chan < (m + 1)*get_rx_subdev_spec(m).size(); chan++){
+ buff += str(boost::format(
+ " RX Channel: %u\n"
+ " RX DSP: %s\n"
+ " RX Dboard: %s\n"
+ " RX Subdev: %s\n"
+ ) % chan
+ % rx_dsp_root(chan).leaf()
+ % rx_rf_fe_root(chan).branch_path().branch_path().leaf()
+ % (_tree->access<std::string>(rx_rf_fe_root(chan) / "name").get())
+ );
+ }
+ }
+
+ //----------- tx side of life ----------------------------------
+ for (size_t m = 0, chan = 0; m < get_num_mboards(); m++){
+ for (; chan < (m + 1)*get_tx_subdev_spec(m).size(); chan++){
+ buff += str(boost::format(
+ " TX Channel: %u\n"
+ " TX DSP: %s\n"
+ " TX Dboard: %s\n"
+ " TX Subdev: %s\n"
+ ) % chan
+ % tx_dsp_root(chan).leaf()
+ % tx_rf_fe_root(chan).branch_path().branch_path().leaf()
+ % (_tree->access<std::string>(tx_rf_fe_root(chan) / "name").get())
+ );
+ }
+ }
+
+ return buff;
+ }
+
+ std::string get_mboard_name(size_t mboard){
+ return _tree->access<std::string>(mb_root(mboard) / "name").get();
+ }
+
+ time_spec_t get_time_now(size_t mboard = 0){
+ return _tree->access<time_spec_t>(mb_root(mboard) / "time/now").get();
+ }
+
+ time_spec_t get_time_last_pps(size_t mboard = 0){
+ return _tree->access<time_spec_t>(mb_root(mboard) / "time/pps").get();
+ }
+
+ void set_time_now(const time_spec_t &time_spec, size_t mboard){
+ if (mboard != ALL_MBOARDS){
+ _tree->access<time_spec_t>(mb_root(mboard) / "time/now").set(time_spec);
+ return;
+ }
+ for (size_t m = 0; m < get_num_mboards(); m++){
+ set_time_now(time_spec, m);
+ }
+ }
+
+ void set_time_next_pps(const time_spec_t &time_spec, size_t mboard){
+ if (mboard != ALL_MBOARDS){
+ _tree->access<time_spec_t>(mb_root(mboard) / "time/pps").set(time_spec);
+ return;
+ }
+ for (size_t m = 0; m < get_num_mboards(); m++){
+ set_time_next_pps(time_spec, m);
+ }
+ }
+
+ void set_time_unknown_pps(const time_spec_t &time_spec){
+ UHD_MSG(status) << " 1) catch time transition at pps edge" << std::endl;
+ time_spec_t time_start = get_time_now();
+ time_spec_t time_start_last_pps = get_time_last_pps();
+ while(true){
+ if (get_time_last_pps() != time_start_last_pps) break;
+ if ((get_time_now() - time_start) > time_spec_t(1.1)){
+ throw uhd::runtime_error(
+ "Board 0 may not be getting a PPS signal!\n"
+ "No PPS detected within the time interval.\n"
+ "See the application notes for your device.\n"
+ );
+ }
+ }
+
+ UHD_MSG(status) << " 2) set times next pps (synchronously)" << std::endl;
+ set_time_next_pps(time_spec, ALL_MBOARDS);
+ boost::this_thread::sleep(boost::posix_time::seconds(1));
+
+ //verify that the time registers are read to be within a few RTT
+ for (size_t m = 1; m < get_num_mboards(); m++){
+ time_spec_t time_0 = this->get_time_now(0);
+ time_spec_t time_i = this->get_time_now(m);
+ if (time_i < time_0 or (time_i - time_0) > time_spec_t(0.01)){ //10 ms: greater than RTT but not too big
+ UHD_MSG(warning) << boost::format(
+ "Detected time deviation between board %d and board 0.\n"
+ "Board 0 time is %f seconds.\n"
+ "Board %d time is %f seconds.\n"
+ ) % m % time_0.get_real_secs() % m % time_i.get_real_secs();
+ }
+ }
+ }
+
+ bool get_time_synchronized(void){
+ for (size_t m = 1; m < get_num_mboards(); m++){
+ time_spec_t time_0 = this->get_time_now(0);
+ time_spec_t time_i = this->get_time_now(m);
+ if (time_i < time_0 or (time_i - time_0) > time_spec_t(0.01)) return false;
+ }
+ return true;
+ }
+
+ void set_command_time(const time_spec_t &, size_t){
+ throw uhd::not_implemented_error("Not implemented yet, but we have a very good idea of how to do it.");
+ }
+
+ void clear_command_time(size_t){
+ throw uhd::not_implemented_error("Not implemented yet, but we have a very good idea of how to do it.");
+ }
+
+ void issue_stream_cmd(const stream_cmd_t &stream_cmd, size_t chan){
+ if (chan != ALL_CHANS){
+ _tree->access<stream_cmd_t>(rx_dsp_root(chan) / "stream_cmd").set(stream_cmd);
+ return;
+ }
+ for (size_t c = 0; c < get_rx_num_channels(); c++){
+ issue_stream_cmd(stream_cmd, c);
+ }
+ }
+
+ void set_clock_config(const clock_config_t &clock_config, size_t mboard){
+ //set the reference source...
+ std::string clock_source;
+ switch(clock_config.ref_source){
+ case clock_config_t::REF_INT: clock_source = "internal"; break;
+ case clock_config_t::PPS_SMA: clock_source = "external"; break;
+ case clock_config_t::PPS_MIMO: clock_source = "mimo"; break;
+ default: clock_source = "unknown";
+ }
+ this->set_clock_source(clock_source, mboard);
+
+ //set the time source
+ std::string time_source;
+ switch(clock_config.pps_source){
+ case clock_config_t::PPS_INT: time_source = "internal"; break;
+ case clock_config_t::PPS_SMA: time_source = "external"; break;
+ case clock_config_t::PPS_MIMO: time_source = "mimo"; break;
+ default: time_source = "unknown";
+ }
+ if (time_source == "external" and clock_config.pps_polarity == clock_config_t::PPS_NEG) time_source = "_external_";
+ this->set_time_source(time_source, mboard);
+ }
+
+ void set_time_source(const std::string &source, const size_t mboard){
+ if (mboard != ALL_MBOARDS){
+ _tree->access<std::string>(mb_root(mboard) / "time_source" / "value").set(source);
+ return;
+ }
+ for (size_t m = 0; m < get_num_mboards(); m++){
+ return this->set_time_source(source, m);
+ }
+ }
+
+ std::string get_time_source(const size_t mboard){
+ return _tree->access<std::string>(mb_root(mboard) / "time_source" / "value").get();
+ }
+
+ std::vector<std::string> get_time_sources(const size_t mboard){
+ return _tree->access<std::vector<std::string> >(mb_root(mboard) / "time_source" / "options").get();
+ }
+
+ void set_clock_source(const std::string &source, const size_t mboard){
+ if (mboard != ALL_MBOARDS){
+ _tree->access<std::string>(mb_root(mboard) / "clock_source" / "value").set(source);
+ return;
+ }
+ for (size_t m = 0; m < get_num_mboards(); m++){
+ return this->set_clock_source(source, m);
+ }
+ }
+
+ std::string get_clock_source(const size_t mboard){
+ return _tree->access<std::string>(mb_root(mboard) / "clock_source" / "value").get();
+ }
+
+ std::vector<std::string> get_clock_sources(const size_t mboard){
+ return _tree->access<std::vector<std::string> >(mb_root(mboard) / "clock_source" / "options").get();
+ }
+
+ size_t get_num_mboards(void){
+ return _tree->list("/mboards").size();
+ }
+
+ sensor_value_t get_mboard_sensor(const std::string &name, size_t mboard){
+ return _tree->access<sensor_value_t>(mb_root(mboard) / "sensors" / name).get();
+ }
+
+ std::vector<std::string> get_mboard_sensor_names(size_t mboard){
+ return _tree->list(mb_root(mboard) / "sensors");
+ }
+
+ void set_user_register(const boost::uint8_t addr, const boost::uint32_t data, size_t mboard){
+ if (mboard != ALL_MBOARDS){
+ typedef std::pair<boost::uint8_t, boost::uint32_t> user_reg_t;
+ _tree->access<user_reg_t>(mb_root(mboard) / "user/reg").set(user_reg_t(addr, data));
+ return;
+ }
+ for (size_t m = 0; m < get_num_mboards(); m++){
+ set_user_register(addr, data, m);
+ }
+ }
+
+ /*******************************************************************
+ * RX methods
+ ******************************************************************/
+ void set_rx_subdev_spec(const subdev_spec_t &spec, size_t mboard){
+ if (mboard != ALL_MBOARDS){
+ _tree->access<subdev_spec_t>(mb_root(mboard) / "rx_subdev_spec").set(spec);
+ return;
+ }
+ for (size_t m = 0; m < get_num_mboards(); m++){
+ set_rx_subdev_spec(spec, m);
+ }
+ }
+
+ subdev_spec_t get_rx_subdev_spec(size_t mboard){
+ return _tree->access<subdev_spec_t>(mb_root(mboard) / "rx_subdev_spec").get();
+ }
+
+ size_t get_rx_num_channels(void){
+ size_t sum = 0;
+ for (size_t m = 0; m < get_num_mboards(); m++){
+ sum += get_rx_subdev_spec(m).size();
+ }
+ return sum;
+ }
+
+ std::string get_rx_subdev_name(size_t chan){
+ return _tree->access<std::string>(rx_rf_fe_root(chan) / "name").get();
+ }
+
+ void set_rx_rate(double rate, size_t chan){
+ if (chan != ALL_CHANS){
+ _tree->access<double>(rx_dsp_root(chan) / "rate" / "value").set(rate);
+ do_samp_rate_warning_message(rate, get_rx_rate(chan), "RX");
+ return;
+ }
+ for (size_t c = 0; c < get_rx_num_channels(); c++){
+ set_rx_rate(rate, c);
+ }
+ }
+
+ double get_rx_rate(size_t chan){
+ return _tree->access<double>(rx_dsp_root(chan) / "rate" / "value").get();
+ }
+
+ meta_range_t get_rx_rates(size_t chan){
+ return _tree->access<meta_range_t>(rx_dsp_root(chan) / "rate" / "range").get();
+ }
+
+ tune_result_t set_rx_freq(const tune_request_t &tune_request, size_t chan){
+ tune_result_t r = tune_xx_subdev_and_dsp(RX_SIGN, _tree->subtree(rx_dsp_root(chan)), _tree->subtree(rx_rf_fe_root(chan)), tune_request);
+ do_tune_freq_warning_message(tune_request, get_rx_freq(chan), "RX");
+ return r;
+ }
+
+ double get_rx_freq(size_t chan){
+ return derive_freq_from_xx_subdev_and_dsp(RX_SIGN, _tree->subtree(rx_dsp_root(chan)), _tree->subtree(rx_rf_fe_root(chan)));
+ }
+
+ freq_range_t get_rx_freq_range(size_t chan){
+ return make_overall_tune_range(
+ _tree->access<meta_range_t>(rx_rf_fe_root(chan) / "freq" / "range").get(),
+ _tree->access<meta_range_t>(rx_dsp_root(chan) / "freq" / "range").get(),
+ this->get_rx_bandwidth(chan)
+ );
+ }
+
+ void set_rx_gain(double gain, const std::string &name, size_t chan){
+ return rx_gain_group(chan)->set_value(gain, name);
+ }
+
+ double get_rx_gain(const std::string &name, size_t chan){
+ return rx_gain_group(chan)->get_value(name);
+ }
+
+ gain_range_t get_rx_gain_range(const std::string &name, size_t chan){
+ return rx_gain_group(chan)->get_range(name);
+ }
+
+ std::vector<std::string> get_rx_gain_names(size_t chan){
+ return rx_gain_group(chan)->get_names();
+ }
+
+ void set_rx_antenna(const std::string &ant, size_t chan){
+ _tree->access<std::string>(rx_rf_fe_root(chan) / "antenna" / "value").set(ant);
+ }
+
+ std::string get_rx_antenna(size_t chan){
+ return _tree->access<std::string>(rx_rf_fe_root(chan) / "antenna" / "value").get();
+ }
+
+ std::vector<std::string> get_rx_antennas(size_t chan){
+ return _tree->access<std::vector<std::string> >(rx_rf_fe_root(chan) / "antenna" / "options").get();
+ }
+
+ void set_rx_bandwidth(double bandwidth, size_t chan){
+ _tree->access<double>(rx_rf_fe_root(chan) / "bandwidth" / "value").set(bandwidth);
+ }
+
+ double get_rx_bandwidth(size_t chan){
+ return _tree->access<double>(rx_rf_fe_root(chan) / "bandwidth" / "value").get();
+ }
+
+ meta_range_t get_rx_bandwidth_range(size_t chan){
+ return _tree->access<meta_range_t>(rx_rf_fe_root(chan) / "bandwidth" / "range").get();
+ }
+
+ dboard_iface::sptr get_rx_dboard_iface(size_t chan){
+ return _tree->access<dboard_iface::sptr>(rx_rf_fe_root(chan).branch_path().branch_path() / "iface").get();
+ }
+
+ sensor_value_t get_rx_sensor(const std::string &name, size_t chan){
+ return _tree->access<sensor_value_t>(rx_rf_fe_root(chan) / "sensors" / name).get();
+ }
+
+ std::vector<std::string> get_rx_sensor_names(size_t chan){
+ return _tree->list(rx_rf_fe_root(chan) / "sensors");
+ }
+
+ void set_rx_dc_offset(const bool enb, size_t chan){
+ if (chan != ALL_CHANS){
+ _tree->access<bool>(rx_fe_root(chan) / "dc_offset" / "enable").set(enb);
+ return;
+ }
+ for (size_t c = 0; c < get_rx_num_channels(); c++){
+ this->set_rx_dc_offset(enb, c);
+ }
+ }
+
+ void set_rx_dc_offset(const std::complex<double> &offset, size_t chan){
+ if (chan != ALL_CHANS){
+ _tree->access<std::complex<double> >(rx_fe_root(chan) / "dc_offset" / "value").set(offset);
+ return;
+ }
+ for (size_t c = 0; c < get_rx_num_channels(); c++){
+ this->set_rx_dc_offset(offset, c);
+ }
+ }
+
+ void set_rx_iq_balance(const std::complex<double> &offset, size_t chan){
+ if (chan != ALL_CHANS){
+ _tree->access<std::complex<double> >(rx_fe_root(chan) / "iq_balance" / "value").set(offset);
+ return;
+ }
+ for (size_t c = 0; c < get_rx_num_channels(); c++){
+ this->set_rx_iq_balance(offset, c);
+ }
+ }
+
+ /*******************************************************************
+ * TX methods
+ ******************************************************************/
+ void set_tx_subdev_spec(const subdev_spec_t &spec, size_t mboard){
+ if (mboard != ALL_MBOARDS){
+ _tree->access<subdev_spec_t>(mb_root(mboard) / "tx_subdev_spec").set(spec);
+ return;
+ }
+ for (size_t m = 0; m < get_num_mboards(); m++){
+ set_tx_subdev_spec(spec, m);
+ }
+ }
+
+ subdev_spec_t get_tx_subdev_spec(size_t mboard){
+ return _tree->access<subdev_spec_t>(mb_root(mboard) / "tx_subdev_spec").get();
+ }
+
+ std::string get_tx_subdev_name(size_t chan){
+ return _tree->access<std::string>(tx_rf_fe_root(chan) / "name").get();
+ }
+
+ size_t get_tx_num_channels(void){
+ size_t sum = 0;
+ for (size_t m = 0; m < get_num_mboards(); m++){
+ sum += get_tx_subdev_spec(m).size();
+ }
+ return sum;
+ }
+
+ void set_tx_rate(double rate, size_t chan){
+ if (chan != ALL_CHANS){
+ _tree->access<double>(tx_dsp_root(chan) / "rate" / "value").set(rate);
+ do_samp_rate_warning_message(rate, get_tx_rate(chan), "TX");
+ return;
+ }
+ for (size_t c = 0; c < get_tx_num_channels(); c++){
+ set_tx_rate(rate, c);
+ }
+ }
+
+ double get_tx_rate(size_t chan){
+ return _tree->access<double>(tx_dsp_root(chan) / "rate" / "value").get();
+ }
+
+ meta_range_t get_tx_rates(size_t chan){
+ return _tree->access<meta_range_t>(tx_dsp_root(chan) / "rate" / "range").get();
+ }
+
+ tune_result_t set_tx_freq(const tune_request_t &tune_request, size_t chan){
+ tune_result_t r = tune_xx_subdev_and_dsp(TX_SIGN, _tree->subtree(tx_dsp_root(chan)), _tree->subtree(tx_rf_fe_root(chan)), tune_request);
+ do_tune_freq_warning_message(tune_request, get_tx_freq(chan), "TX");
+ return r;
+ }
+
+ double get_tx_freq(size_t chan){
+ return derive_freq_from_xx_subdev_and_dsp(TX_SIGN, _tree->subtree(tx_dsp_root(chan)), _tree->subtree(tx_rf_fe_root(chan)));
+ }
+
+ freq_range_t get_tx_freq_range(size_t chan){
+ return make_overall_tune_range(
+ _tree->access<meta_range_t>(tx_rf_fe_root(chan) / "freq" / "range").get(),
+ _tree->access<meta_range_t>(tx_dsp_root(chan) / "freq" / "range").get(),
+ this->get_tx_bandwidth(chan)
+ );
+ }
+
+ void set_tx_gain(double gain, const std::string &name, size_t chan){
+ return tx_gain_group(chan)->set_value(gain, name);
+ }
+
+ double get_tx_gain(const std::string &name, size_t chan){
+ return tx_gain_group(chan)->get_value(name);
+ }
+
+ gain_range_t get_tx_gain_range(const std::string &name, size_t chan){
+ return tx_gain_group(chan)->get_range(name);
+ }
+
+ std::vector<std::string> get_tx_gain_names(size_t chan){
+ return tx_gain_group(chan)->get_names();
+ }
+
+ void set_tx_antenna(const std::string &ant, size_t chan){
+ _tree->access<std::string>(tx_rf_fe_root(chan) / "antenna" / "value").set(ant);
+ }
+
+ std::string get_tx_antenna(size_t chan){
+ return _tree->access<std::string>(tx_rf_fe_root(chan) / "antenna" / "value").get();
+ }
+
+ std::vector<std::string> get_tx_antennas(size_t chan){
+ return _tree->access<std::vector<std::string> >(tx_rf_fe_root(chan) / "antenna" / "options").get();
+ }
+
+ void set_tx_bandwidth(double bandwidth, size_t chan){
+ _tree->access<double>(tx_rf_fe_root(chan) / "bandwidth" / "value").set(bandwidth);
+ }
+
+ double get_tx_bandwidth(size_t chan){
+ return _tree->access<double>(tx_rf_fe_root(chan) / "bandwidth" / "value").get();
+ }
+
+ meta_range_t get_tx_bandwidth_range(size_t chan){
+ return _tree->access<meta_range_t>(tx_rf_fe_root(chan) / "bandwidth" / "range").get();
+ }
+
+ dboard_iface::sptr get_tx_dboard_iface(size_t chan){
+ return _tree->access<dboard_iface::sptr>(tx_rf_fe_root(chan).branch_path().branch_path() / "iface").get();
+ }
+
+ sensor_value_t get_tx_sensor(const std::string &name, size_t chan){
+ return _tree->access<sensor_value_t>(tx_rf_fe_root(chan) / "sensors" / name).get();
+ }
+
+ std::vector<std::string> get_tx_sensor_names(size_t chan){
+ return _tree->list(tx_rf_fe_root(chan) / "sensors");
+ }
+
+ void set_tx_dc_offset(const std::complex<double> &offset, size_t chan){
+ if (chan != ALL_CHANS){
+ _tree->access<std::complex<double> >(tx_fe_root(chan) / "dc_offset" / "value").set(offset);
+ return;
+ }
+ for (size_t c = 0; c < get_tx_num_channels(); c++){
+ this->set_tx_dc_offset(offset, c);
+ }
+ }
+
+ void set_tx_iq_balance(const std::complex<double> &offset, size_t chan){
+ if (chan != ALL_CHANS){
+ _tree->access<std::complex<double> >(tx_fe_root(chan) / "iq_balance" / "value").set(offset);
+ return;
+ }
+ for (size_t c = 0; c < get_tx_num_channels(); c++){
+ this->set_tx_iq_balance(offset, c);
+ }
+ }
+
+private:
+ device::sptr _dev;
+ property_tree::sptr _tree;
+
+ struct mboard_chan_pair{
+ size_t mboard, chan;
+ mboard_chan_pair(void): mboard(0), chan(0){}
+ };
+
+ mboard_chan_pair rx_chan_to_mcp(size_t chan){
+ mboard_chan_pair mcp;
+ mcp.chan = chan;
+ for (mcp.mboard = 0; mcp.mboard < get_num_mboards(); mcp.mboard++){
+ size_t sss = get_rx_subdev_spec(mcp.mboard).size();
+ if (mcp.chan < sss) break;
+ mcp.chan -= sss;
+ }
+ return mcp;
+ }
+
+ mboard_chan_pair tx_chan_to_mcp(size_t chan){
+ mboard_chan_pair mcp;
+ mcp.chan = chan;
+ for (mcp.mboard = 0; mcp.mboard < get_num_mboards(); mcp.mboard++){
+ size_t sss = get_tx_subdev_spec(mcp.mboard).size();
+ if (mcp.chan < sss) break;
+ mcp.chan -= sss;
+ }
+ return mcp;
+ }
+
+ fs_path mb_root(const size_t mboard){
+ const std::string name = _tree->list("/mboards").at(mboard);
+ return "/mboards/" + name;
+ }
+
+ fs_path rx_dsp_root(const size_t chan){
+ mboard_chan_pair mcp = rx_chan_to_mcp(chan);
+ const std::string name = _tree->list(mb_root(mcp.mboard) / "rx_dsps").at(mcp.chan);
+ return mb_root(mcp.mboard) / "rx_dsps" / name;
+ }
+
+ fs_path tx_dsp_root(const size_t chan){
+ mboard_chan_pair mcp = tx_chan_to_mcp(chan);
+ const std::string name = _tree->list(mb_root(mcp.mboard) / "tx_dsps").at(mcp.chan);
+ return mb_root(mcp.mboard) / "tx_dsps" / name;
+ }
+
+ fs_path rx_fe_root(const size_t chan){
+ mboard_chan_pair mcp = rx_chan_to_mcp(chan);
+ const subdev_spec_pair_t spec = get_rx_subdev_spec(mcp.mboard).at(mcp.chan);
+ return mb_root(mcp.mboard) / "rx_frontends" / spec.db_name;
+ }
+
+ fs_path tx_fe_root(const size_t chan){
+ mboard_chan_pair mcp = tx_chan_to_mcp(chan);
+ const subdev_spec_pair_t spec = get_tx_subdev_spec(mcp.mboard).at(mcp.chan);
+ return mb_root(mcp.mboard) / "tx_frontends" / spec.db_name;
+ }
+
+ fs_path rx_rf_fe_root(const size_t chan){
+ mboard_chan_pair mcp = rx_chan_to_mcp(chan);
+ const subdev_spec_pair_t spec = get_rx_subdev_spec(mcp.mboard).at(mcp.chan);
+ return mb_root(mcp.mboard) / "dboards" / spec.db_name / "rx_frontends" / spec.sd_name;
+ }
+
+ fs_path tx_rf_fe_root(const size_t chan){
+ mboard_chan_pair mcp = tx_chan_to_mcp(chan);
+ const subdev_spec_pair_t spec = get_tx_subdev_spec(mcp.mboard).at(mcp.chan);
+ return mb_root(mcp.mboard) / "dboards" / spec.db_name / "tx_frontends" / spec.sd_name;
+ }
+
+ gain_group::sptr rx_gain_group(size_t chan){
+ mboard_chan_pair mcp = rx_chan_to_mcp(chan);
+ const subdev_spec_pair_t spec = get_rx_subdev_spec(mcp.mboard).at(mcp.chan);
+ gain_group::sptr gg = gain_group::make();
+ BOOST_FOREACH(const std::string &name, _tree->list(mb_root(mcp.mboard) / "rx_codecs" / spec.db_name / "gains")){
+ gg->register_fcns("ADC-"+name, make_gain_fcns_from_subtree(_tree->subtree(mb_root(mcp.mboard) / "rx_codecs" / spec.db_name / "gains" / name)), 0 /* low prio */);
+ }
+ BOOST_FOREACH(const std::string &name, _tree->list(rx_rf_fe_root(chan) / "gains")){
+ gg->register_fcns(name, make_gain_fcns_from_subtree(_tree->subtree(rx_rf_fe_root(chan) / "gains" / name)), 1 /* high prio */);
+ }
+ return gg;
+ }
+
+ gain_group::sptr tx_gain_group(size_t chan){
+ mboard_chan_pair mcp = tx_chan_to_mcp(chan);
+ const subdev_spec_pair_t spec = get_tx_subdev_spec(mcp.mboard).at(mcp.chan);
+ gain_group::sptr gg = gain_group::make();
+ BOOST_FOREACH(const std::string &name, _tree->list(mb_root(mcp.mboard) / "tx_codecs" / spec.db_name / "gains")){
+ gg->register_fcns("ADC-"+name, make_gain_fcns_from_subtree(_tree->subtree(mb_root(mcp.mboard) / "tx_codecs" / spec.db_name / "gains" / name)), 1 /* high prio */);
+ }
+ BOOST_FOREACH(const std::string &name, _tree->list(tx_rf_fe_root(chan) / "gains")){
+ gg->register_fcns(name, make_gain_fcns_from_subtree(_tree->subtree(tx_rf_fe_root(chan) / "gains" / name)), 0 /* low prio */);
+ }
+ return gg;
+ }
+};
+
+/***********************************************************************
+ * The Make Function
+ **********************************************************************/
+multi_usrp::sptr multi_usrp::make(const device_addr_t &dev_addr){
+ return sptr(new multi_usrp_impl(dev_addr));
+}
diff --git a/host/lib/usrp/subdev_spec.cpp b/host/lib/usrp/subdev_spec.cpp
new file mode 100644
index 000000000..6912afec8
--- /dev/null
+++ b/host/lib/usrp/subdev_spec.cpp
@@ -0,0 +1,80 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/usrp/subdev_spec.hpp>
+#include <uhd/exception.hpp>
+#include <boost/algorithm/string.hpp> //for split
+#include <boost/tokenizer.hpp>
+#include <boost/format.hpp>
+#include <boost/foreach.hpp>
+#include <sstream>
+#include <vector>
+
+using namespace uhd;
+using namespace uhd::usrp;
+
+#define pair_tokenizer(inp) \
+ boost::tokenizer<boost::char_separator<char> > \
+ (inp, boost::char_separator<char>(" "))
+
+subdev_spec_pair_t::subdev_spec_pair_t(
+ const std::string &db_name, const std::string &sd_name
+):
+ db_name(db_name),
+ sd_name(sd_name)
+{
+ /* NOP */
+}
+
+bool usrp::operator==(const subdev_spec_pair_t &lhs, const subdev_spec_pair_t &rhs){
+ return (lhs.db_name == rhs.db_name) and (lhs.sd_name == rhs.sd_name);
+}
+
+subdev_spec_t::subdev_spec_t(const std::string &markup){
+ BOOST_FOREACH(const std::string &pair, pair_tokenizer(markup)){
+ if (pair.empty()) continue;
+ std::vector<std::string> db_sd; boost::split(db_sd, pair, boost::is_any_of(":"));
+ switch(db_sd.size()){
+ case 1: this->push_back(subdev_spec_pair_t("", db_sd.front())); break;
+ case 2: this->push_back(subdev_spec_pair_t(db_sd.front(), db_sd.back())); break;
+ default: throw uhd::value_error("invalid subdev-spec markup string: "+markup);
+ }
+ }
+}
+
+std::string subdev_spec_t::to_pp_string(void) const{
+ if (this->size() == 0) return "Empty Subdevice Specification";
+
+ std::stringstream ss;
+ size_t count = 0;
+ ss << "Subdevice Specification:" << std::endl;
+ BOOST_FOREACH(const subdev_spec_pair_t &pair, *this){
+ ss << boost::format(
+ " Channel %d: Daughterboard %s, Subdevice %s"
+ ) % (count++) % pair.db_name % pair.sd_name << std::endl;
+ }
+ return ss.str();
+}
+
+std::string subdev_spec_t::to_string(void) const{
+ std::string markup;
+ size_t count = 0;
+ BOOST_FOREACH(const subdev_spec_pair_t &pair, *this){
+ markup += ((count++)? " " : "") + pair.db_name + ":" + pair.sd_name;
+ }
+ return markup;
+}
diff --git a/host/lib/usrp/usrp1/CMakeLists.txt b/host/lib/usrp/usrp1/CMakeLists.txt
new file mode 100644
index 000000000..70bebe502
--- /dev/null
+++ b/host/lib/usrp/usrp1/CMakeLists.txt
@@ -0,0 +1,36 @@
+#
+# Copyright 2010-2011 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# This file included, use CMake directory variables
+########################################################################
+
+########################################################################
+# Conditionally configure the USRP1 support
+########################################################################
+LIBUHD_REGISTER_COMPONENT("USRP1" ENABLE_USRP1 ON "ENABLE_LIBUHD;ENABLE_USB" OFF)
+
+IF(ENABLE_USRP1)
+ LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/codec_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/dboard_iface.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/io_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/soft_time_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/usrp1_iface.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/usrp1_impl.cpp
+ )
+ENDIF(ENABLE_USRP1)
diff --git a/host/lib/usrp/usrp1/codec_ctrl.cpp b/host/lib/usrp/usrp1/codec_ctrl.cpp
new file mode 100644
index 000000000..c82569ea3
--- /dev/null
+++ b/host/lib/usrp/usrp1/codec_ctrl.cpp
@@ -0,0 +1,419 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "codec_ctrl.hpp"
+#include "ad9862_regs.hpp"
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/safe_call.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/format.hpp>
+#include <boost/tuple/tuple.hpp>
+#include <boost/math/special_functions/round.hpp>
+#include <boost/assign/list_of.hpp>
+#include <iomanip>
+
+using namespace uhd;
+
+const gain_range_t usrp1_codec_ctrl::tx_pga_gain_range(-20, 0, double(0.1));
+const gain_range_t usrp1_codec_ctrl::rx_pga_gain_range(0, 20, 1);
+
+/***********************************************************************
+ * Codec Control Implementation
+ **********************************************************************/
+class usrp1_codec_ctrl_impl : public usrp1_codec_ctrl {
+public:
+ //structors
+ usrp1_codec_ctrl_impl(spi_iface::sptr iface, int spi_slave);
+ ~usrp1_codec_ctrl_impl(void);
+
+ //aux adc and dac control
+ double read_aux_adc(aux_adc_t which);
+ void write_aux_dac(aux_dac_t which, double volts);
+
+ //duc control
+ void set_duc_freq(double freq, double);
+ void enable_tx_digital(bool enb);
+
+ //pga gain control
+ void set_tx_pga_gain(double);
+ double get_tx_pga_gain(void);
+ void set_rx_pga_gain(double, char);
+ double get_rx_pga_gain(char);
+
+ //rx adc buffer control
+ void bypass_adc_buffers(bool bypass);
+
+private:
+ spi_iface::sptr _iface;
+ int _spi_slave;
+ ad9862_regs_t _ad9862_regs;
+ void send_reg(boost::uint8_t addr);
+ void recv_reg(boost::uint8_t addr);
+
+ double coarse_tune(double codec_rate, double freq);
+ double fine_tune(double codec_rate, double freq);
+};
+
+/***********************************************************************
+ * Codec Control Structors
+ **********************************************************************/
+usrp1_codec_ctrl_impl::usrp1_codec_ctrl_impl(spi_iface::sptr iface, int spi_slave){
+ _iface = iface;
+ _spi_slave = spi_slave;
+
+ //soft reset
+ _ad9862_regs.soft_reset = 1;
+ this->send_reg(0);
+
+ //initialize the codec register settings
+ _ad9862_regs.sdio_bidir = ad9862_regs_t::SDIO_BIDIR_SDIO_SDO;
+ _ad9862_regs.lsb_first = ad9862_regs_t::LSB_FIRST_MSB;
+ _ad9862_regs.soft_reset = 0;
+
+ //setup rx side of codec
+ _ad9862_regs.byp_buffer_a = 1;
+ _ad9862_regs.byp_buffer_b = 1;
+ _ad9862_regs.buffer_a_pd = 1;
+ _ad9862_regs.buffer_b_pd = 1;
+ _ad9862_regs.rx_pga_a = 0;
+ _ad9862_regs.rx_pga_b = 0;
+ _ad9862_regs.rx_twos_comp = 1;
+ _ad9862_regs.rx_hilbert = ad9862_regs_t::RX_HILBERT_DIS;
+
+ //setup tx side of codec
+ _ad9862_regs.two_data_paths = ad9862_regs_t::TWO_DATA_PATHS_BOTH;
+ _ad9862_regs.interleaved = ad9862_regs_t::INTERLEAVED_INTERLEAVED;
+ _ad9862_regs.tx_pga_gain = 199;
+ _ad9862_regs.tx_hilbert = ad9862_regs_t::TX_HILBERT_DIS;
+ _ad9862_regs.interp = ad9862_regs_t::INTERP_4;
+ _ad9862_regs.tx_twos_comp = 1;
+ _ad9862_regs.fine_mode = ad9862_regs_t::FINE_MODE_NCO;
+ _ad9862_regs.coarse_mod = ad9862_regs_t::COARSE_MOD_BYPASS;
+ _ad9862_regs.dac_a_coarse_gain = 0x3;
+ _ad9862_regs.dac_b_coarse_gain = 0x3;
+
+ //setup the dll
+ _ad9862_regs.input_clk_ctrl = ad9862_regs_t::INPUT_CLK_CTRL_EXTERNAL;
+ _ad9862_regs.dll_mult = ad9862_regs_t::DLL_MULT_2;
+ _ad9862_regs.dll_mode = ad9862_regs_t::DLL_MODE_FAST;
+
+ //setup clockout
+ _ad9862_regs.clkout2_div_factor = ad9862_regs_t::CLKOUT2_DIV_FACTOR_2;
+
+ //write the register settings to the codec
+ for (boost::uint8_t addr = 0; addr <= 25; addr++) {
+ this->send_reg(addr);
+ }
+
+ //always start conversions for aux ADC
+ _ad9862_regs.start_a = 1;
+ _ad9862_regs.start_b = 1;
+
+ //aux adc clock
+ _ad9862_regs.clk_4 = ad9862_regs_t::CLK_4_1_4;
+ this->send_reg(34);
+}
+
+usrp1_codec_ctrl_impl::~usrp1_codec_ctrl_impl(void){UHD_SAFE_CALL(
+ //set aux dacs to zero
+ this->write_aux_dac(AUX_DAC_A, 0);
+ this->write_aux_dac(AUX_DAC_B, 0);
+ this->write_aux_dac(AUX_DAC_C, 0);
+ this->write_aux_dac(AUX_DAC_D, 0);
+
+ //power down
+ _ad9862_regs.all_rx_pd = 1;
+ this->send_reg(1);
+ _ad9862_regs.tx_digital_pd = 1;
+ _ad9862_regs.tx_analog_pd = ad9862_regs_t::TX_ANALOG_PD_BOTH;
+ this->send_reg(8);
+)}
+
+/***********************************************************************
+ * Codec Control Gain Control Methods
+ **********************************************************************/
+static const int mtpgw = 255; //maximum tx pga gain word
+
+void usrp1_codec_ctrl_impl::set_tx_pga_gain(double gain){
+ int gain_word = int(mtpgw*(gain - tx_pga_gain_range.start())/(tx_pga_gain_range.stop() - tx_pga_gain_range.start()));
+ _ad9862_regs.tx_pga_gain = uhd::clip(gain_word, 0, mtpgw);
+ this->send_reg(16);
+}
+
+double usrp1_codec_ctrl_impl::get_tx_pga_gain(void){
+ return (_ad9862_regs.tx_pga_gain*(tx_pga_gain_range.stop() - tx_pga_gain_range.start())/mtpgw) + tx_pga_gain_range.start();
+}
+
+static const int mrpgw = 0x14; //maximum rx pga gain word
+
+void usrp1_codec_ctrl_impl::set_rx_pga_gain(double gain, char which){
+ int gain_word = int(mrpgw*(gain - rx_pga_gain_range.start())/(rx_pga_gain_range.stop() - rx_pga_gain_range.start()));
+ gain_word = uhd::clip(gain_word, 0, mrpgw);
+ switch(which){
+ case 'A':
+ _ad9862_regs.rx_pga_a = gain_word;
+ this->send_reg(2);
+ return;
+ case 'B':
+ _ad9862_regs.rx_pga_b = gain_word;
+ this->send_reg(3);
+ return;
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+double usrp1_codec_ctrl_impl::get_rx_pga_gain(char which){
+ int gain_word;
+ switch(which){
+ case 'A': gain_word = _ad9862_regs.rx_pga_a; break;
+ case 'B': gain_word = _ad9862_regs.rx_pga_b; break;
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+ return (gain_word*(rx_pga_gain_range.stop() - rx_pga_gain_range.start())/mrpgw) + rx_pga_gain_range.start();
+}
+
+/***********************************************************************
+ * Codec Control AUX ADC Methods
+ **********************************************************************/
+static double aux_adc_to_volts(boost::uint8_t high, boost::uint8_t low)
+{
+ return double(((boost::uint16_t(high) << 2) | low)*3.3)/0x3ff;
+}
+
+double usrp1_codec_ctrl_impl::read_aux_adc(aux_adc_t which){
+ switch(which){
+ case AUX_ADC_A1:
+ _ad9862_regs.select_a = ad9862_regs_t::SELECT_A_AUX_ADC1;
+ this->send_reg(34); //start conversion and select mux
+ this->recv_reg(28); //read the value (2 bytes, 2 reads)
+ this->recv_reg(29);
+ return aux_adc_to_volts(_ad9862_regs.aux_adc_a1_9_2, _ad9862_regs.aux_adc_a1_1_0);
+
+ case AUX_ADC_A2:
+ _ad9862_regs.select_a = ad9862_regs_t::SELECT_A_AUX_ADC2;
+ this->send_reg(34); //start conversion and select mux
+ this->recv_reg(26); //read the value (2 bytes, 2 reads)
+ this->recv_reg(27);
+ return aux_adc_to_volts(_ad9862_regs.aux_adc_a2_9_2, _ad9862_regs.aux_adc_a2_1_0);
+
+ case AUX_ADC_B1:
+ _ad9862_regs.select_b = ad9862_regs_t::SELECT_B_AUX_ADC1;
+ this->send_reg(34); //start conversion and select mux
+ this->recv_reg(32); //read the value (2 bytes, 2 reads)
+ this->recv_reg(33);
+ return aux_adc_to_volts(_ad9862_regs.aux_adc_b1_9_2, _ad9862_regs.aux_adc_b1_1_0);
+
+ case AUX_ADC_B2:
+ _ad9862_regs.select_b = ad9862_regs_t::SELECT_B_AUX_ADC2;
+ this->send_reg(34); //start conversion and select mux
+ this->recv_reg(30); //read the value (2 bytes, 2 reads)
+ this->recv_reg(31);
+ return aux_adc_to_volts(_ad9862_regs.aux_adc_b2_9_2, _ad9862_regs.aux_adc_b2_1_0);
+ }
+ UHD_THROW_INVALID_CODE_PATH();
+}
+
+/***********************************************************************
+ * Codec Control AUX DAC Methods
+ **********************************************************************/
+void usrp1_codec_ctrl_impl::write_aux_dac(aux_dac_t which, double volts)
+{
+ //special case for aux dac d (aka sigma delta word)
+ if (which == AUX_DAC_D) {
+ boost::uint16_t dac_word = uhd::clip(boost::math::iround(volts*0xfff/3.3), 0, 0xfff);
+ _ad9862_regs.sig_delt_11_4 = boost::uint8_t(dac_word >> 4);
+ _ad9862_regs.sig_delt_3_0 = boost::uint8_t(dac_word & 0xf);
+ this->send_reg(42);
+ this->send_reg(43);
+ return;
+ }
+
+ //calculate the dac word for aux dac a, b, c
+ boost::uint8_t dac_word = uhd::clip(boost::math::iround(volts*0xff/3.3), 0, 0xff);
+
+ //setup a lookup table for the aux dac params (reg ref, reg addr)
+ typedef boost::tuple<boost::uint8_t*, boost::uint8_t> dac_params_t;
+ uhd::dict<aux_dac_t, dac_params_t> aux_dac_to_params = boost::assign::map_list_of
+ (AUX_DAC_A, dac_params_t(&_ad9862_regs.aux_dac_a, 36))
+ (AUX_DAC_B, dac_params_t(&_ad9862_regs.aux_dac_b, 37))
+ (AUX_DAC_C, dac_params_t(&_ad9862_regs.aux_dac_c, 38))
+ ;
+
+ //set the aux dac register
+ UHD_ASSERT_THROW(aux_dac_to_params.has_key(which));
+ boost::uint8_t *reg_ref, reg_addr;
+ boost::tie(reg_ref, reg_addr) = aux_dac_to_params[which];
+ *reg_ref = dac_word;
+ this->send_reg(reg_addr);
+}
+
+/***********************************************************************
+ * Codec Control SPI Methods
+ **********************************************************************/
+void usrp1_codec_ctrl_impl::send_reg(boost::uint8_t addr)
+{
+ boost::uint32_t reg = _ad9862_regs.get_write_reg(addr);
+
+ UHD_LOGV(often)
+ << "codec control write reg: 0x"
+ << std::setw(8) << std::hex << reg << std::endl
+ ;
+ _iface->write_spi(_spi_slave,
+ spi_config_t::EDGE_RISE, reg, 16);
+}
+
+void usrp1_codec_ctrl_impl::recv_reg(boost::uint8_t addr)
+{
+ boost::uint32_t reg = _ad9862_regs.get_read_reg(addr);
+
+ UHD_LOGV(often)
+ << "codec control read reg: 0x"
+ << std::setw(8) << std::hex << reg << std::endl
+ ;
+
+ boost::uint32_t ret = _iface->read_spi(_spi_slave,
+ spi_config_t::EDGE_RISE, reg, 16);
+
+ UHD_LOGV(often)
+ << "codec control read ret: 0x"
+ << std::setw(8) << std::hex << ret << std::endl
+ ;
+
+ _ad9862_regs.set_reg(addr, boost::uint16_t(ret));
+}
+
+/***********************************************************************
+ * DUC tuning
+ **********************************************************************/
+double usrp1_codec_ctrl_impl::coarse_tune(double codec_rate, double freq)
+{
+ double coarse_freq;
+
+ double coarse_freq_1 = codec_rate / 8;
+ double coarse_freq_2 = codec_rate / 4;
+ double coarse_limit_1 = coarse_freq_1 / 2;
+ double coarse_limit_2 = (coarse_freq_1 + coarse_freq_2) / 2;
+ double max_freq = coarse_freq_2 + .09375 * codec_rate;
+
+ if (freq < -max_freq) {
+ return false;
+ }
+ else if (freq < -coarse_limit_2) {
+ _ad9862_regs.neg_coarse_tune = ad9862_regs_t::NEG_COARSE_TUNE_NEG_SHIFT;
+ _ad9862_regs.coarse_mod = ad9862_regs_t::COARSE_MOD_FDAC_4;
+ coarse_freq = -coarse_freq_2;
+ }
+ else if (freq < -coarse_limit_1) {
+ _ad9862_regs.neg_coarse_tune = ad9862_regs_t::NEG_COARSE_TUNE_NEG_SHIFT;
+ _ad9862_regs.coarse_mod = ad9862_regs_t::COARSE_MOD_FDAC_8;
+ coarse_freq = -coarse_freq_1;
+ }
+ else if (freq < coarse_limit_1) {
+ _ad9862_regs.coarse_mod = ad9862_regs_t::COARSE_MOD_BYPASS;
+ coarse_freq = 0;
+ }
+ else if (freq < coarse_limit_2) {
+ _ad9862_regs.neg_coarse_tune = ad9862_regs_t::NEG_COARSE_TUNE_POS_SHIFT;
+ _ad9862_regs.coarse_mod = ad9862_regs_t::COARSE_MOD_FDAC_8;
+ coarse_freq = coarse_freq_1;
+ }
+ else if (freq <= max_freq) {
+ _ad9862_regs.neg_coarse_tune = ad9862_regs_t::NEG_COARSE_TUNE_POS_SHIFT;
+ _ad9862_regs.coarse_mod = ad9862_regs_t::COARSE_MOD_FDAC_4;
+ coarse_freq = coarse_freq_2;
+ }
+ else {
+ return 0;
+ }
+
+ return coarse_freq;
+}
+
+double usrp1_codec_ctrl_impl::fine_tune(double codec_rate, double target_freq)
+{
+ static const double scale_factor = std::pow(2.0, 24);
+
+ boost::uint32_t freq_word = boost::uint32_t(
+ boost::math::round(abs((target_freq / codec_rate) * scale_factor)));
+
+ double actual_freq = freq_word * codec_rate / scale_factor;
+
+ if (target_freq < 0) {
+ _ad9862_regs.neg_fine_tune = ad9862_regs_t::NEG_FINE_TUNE_NEG_SHIFT;
+ actual_freq = -actual_freq;
+ }
+ else {
+ _ad9862_regs.neg_fine_tune = ad9862_regs_t::NEG_FINE_TUNE_POS_SHIFT;
+ }
+
+ _ad9862_regs.fine_mode = ad9862_regs_t::FINE_MODE_NCO;
+ _ad9862_regs.ftw_23_16 = (freq_word >> 16) & 0xff;
+ _ad9862_regs.ftw_15_8 = (freq_word >> 8) & 0xff;
+ _ad9862_regs.ftw_7_0 = (freq_word >> 0) & 0xff;
+
+ return actual_freq;
+}
+
+void usrp1_codec_ctrl_impl::set_duc_freq(double freq, double rate)
+{
+ double codec_rate = rate * 2;
+ double coarse_freq = coarse_tune(codec_rate, freq);
+ double fine_freq = fine_tune(codec_rate / 4, freq - coarse_freq);
+
+ UHD_LOG
+ << "ad9862 tuning result:" << std::endl
+ << " requested: " << freq << std::endl
+ << " actual: " << coarse_freq + fine_freq << std::endl
+ << " coarse freq: " << coarse_freq << std::endl
+ << " fine freq: " << fine_freq << std::endl
+ << " codec rate: " << codec_rate << std::endl
+ ;
+
+ this->send_reg(20);
+ this->send_reg(21);
+ this->send_reg(22);
+ this->send_reg(23);
+}
+
+void usrp1_codec_ctrl_impl::enable_tx_digital(bool enb){
+ _ad9862_regs.tx_digital_pd = (enb)? 0 : 1;
+ this->send_reg(8);
+}
+
+/***********************************************************************
+ * Codec Control ADC buffer bypass
+ * Disable this for AC-coupled daughterboards (TVRX)
+ * By default it is initialized TRUE.
+ **********************************************************************/
+void usrp1_codec_ctrl_impl::bypass_adc_buffers(bool bypass) {
+ _ad9862_regs.byp_buffer_a = bypass;
+ _ad9862_regs.byp_buffer_b = bypass;
+ this->send_reg(2);
+}
+
+/***********************************************************************
+ * Codec Control Make
+ **********************************************************************/
+usrp1_codec_ctrl::sptr usrp1_codec_ctrl::make(spi_iface::sptr iface,
+ int spi_slave)
+{
+ return sptr(new usrp1_codec_ctrl_impl(iface, spi_slave));
+}
diff --git a/host/lib/usrp/usrp1/codec_ctrl.hpp b/host/lib/usrp/usrp1/codec_ctrl.hpp
new file mode 100644
index 000000000..70f4e0b61
--- /dev/null
+++ b/host/lib/usrp/usrp1/codec_ctrl.hpp
@@ -0,0 +1,99 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_USRP1_CODEC_CTRL_HPP
+#define INCLUDED_USRP1_CODEC_CTRL_HPP
+
+#include <uhd/types/serial.hpp>
+#include <uhd/types/ranges.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+
+/*!
+ * The usrp1 codec control:
+ * - Init/power down codec.
+ * - Read aux adc, write aux dac.
+ */
+class usrp1_codec_ctrl : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<usrp1_codec_ctrl> sptr;
+
+ static const uhd::gain_range_t tx_pga_gain_range;
+ static const uhd::gain_range_t rx_pga_gain_range;
+
+ /*!
+ * Make a new clock control object.
+ * \param iface the spi iface object
+ * \param spi_slave which spi device
+ */
+ static sptr make(uhd::spi_iface::sptr iface, int spi_slave);
+
+ //! aux adc identifier constants
+ enum aux_adc_t{
+ AUX_ADC_A2 = 0xA2,
+ AUX_ADC_A1 = 0xA1,
+ AUX_ADC_B2 = 0xB2,
+ AUX_ADC_B1 = 0xB1
+ };
+
+ /*!
+ * Read an auxiliary adc:
+ * The internals remember which aux adc was read last.
+ * Therefore, the aux adc switch is only changed as needed.
+ * \param which which of the 4 adcs
+ * \return a value in volts
+ */
+ virtual double read_aux_adc(aux_adc_t which) = 0;
+
+ //! aux dac identifier constants
+ enum aux_dac_t{
+ AUX_DAC_A = 0xA,
+ AUX_DAC_B = 0xB,
+ AUX_DAC_C = 0xC,
+ AUX_DAC_D = 0xD
+ };
+
+ /*!
+ * Write an auxiliary dac.
+ * \param which which of the 4 dacs
+ * \param volts the level in in volts
+ */
+ virtual void write_aux_dac(aux_dac_t which, double volts) = 0;
+
+ //! Set the TX PGA gain
+ virtual void set_tx_pga_gain(double gain) = 0;
+
+ //! Get the TX PGA gain
+ virtual double get_tx_pga_gain(void) = 0;
+
+ //! Set the RX PGA gain ('A' or 'B')
+ virtual void set_rx_pga_gain(double gain, char which) = 0;
+
+ //! Get the RX PGA gain ('A' or 'B')
+ virtual double get_rx_pga_gain(char which) = 0;
+
+ //! Set the TX modulator frequency
+ virtual void set_duc_freq(double freq, double rate) = 0;
+
+ //! Enable or disable the digital part of the DAC
+ virtual void enable_tx_digital(bool enb) = 0;
+
+ //! Enable or disable ADC buffer bypass
+ virtual void bypass_adc_buffers(bool bypass) = 0;
+};
+
+#endif /* INCLUDED_USRP1_CODEC_CTRL_HPP */
diff --git a/host/lib/usrp/usrp1/dboard_iface.cpp b/host/lib/usrp/usrp1/dboard_iface.cpp
new file mode 100644
index 000000000..34bbe1893
--- /dev/null
+++ b/host/lib/usrp/usrp1/dboard_iface.cpp
@@ -0,0 +1,397 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "usrp1_iface.hpp"
+#include "usrp1_impl.hpp"
+#include "fpga_regs_common.h"
+#include "usrp_spi_defs.h"
+#include "fpga_regs_standard.h"
+#include "codec_ctrl.hpp"
+#include <uhd/usrp/dboard_iface.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/utils/assert_has.hpp>
+#include <boost/assign/list_of.hpp>
+#include <iostream>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace boost::assign;
+
+static const dboard_id_t tvrx_id(0x0040);
+
+class usrp1_dboard_iface : public dboard_iface {
+public:
+
+ usrp1_dboard_iface(usrp1_iface::sptr iface,
+ usrp1_codec_ctrl::sptr codec,
+ usrp1_impl::dboard_slot_t dboard_slot,
+ const double &master_clock_rate,
+ const dboard_id_t &rx_dboard_id
+ ):
+ _dboard_slot(dboard_slot),
+ _master_clock_rate(master_clock_rate),
+ _rx_dboard_id(rx_dboard_id)
+ {
+ _iface = iface;
+ _codec = codec;
+
+ _dbsrx_classic_div = 1;
+
+ //yes this is evil but it's necessary for TVRX to work on USRP1
+ if(_rx_dboard_id == tvrx_id) _codec->bypass_adc_buffers(false);
+ //else _codec->bypass_adc_buffers(false); //don't think this is necessary
+ }
+
+ ~usrp1_dboard_iface()
+ {
+ /* NOP */
+ }
+
+ special_props_t get_special_props()
+ {
+ special_props_t props;
+ props.soft_clock_divider = true;
+ props.mangle_i2c_addrs = (_dboard_slot == usrp1_impl::DBOARD_SLOT_B);
+ return props;
+ }
+
+ void write_aux_dac(unit_t, aux_dac_t, double);
+ double read_aux_adc(unit_t, aux_adc_t);
+
+ void _set_pin_ctrl(unit_t, boost::uint16_t);
+ void _set_atr_reg(unit_t, atr_reg_t, boost::uint16_t);
+ void _set_gpio_ddr(unit_t, boost::uint16_t);
+ void _set_gpio_out(unit_t, boost::uint16_t);
+ void set_gpio_debug(unit_t, int);
+ boost::uint16_t read_gpio(unit_t);
+
+ void write_i2c(boost::uint8_t, const byte_vector_t &);
+ byte_vector_t read_i2c(boost::uint8_t, size_t);
+
+ void write_spi(unit_t unit,
+ const spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits);
+
+ boost::uint32_t read_write_spi(unit_t unit,
+ const spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits);
+
+ void set_clock_rate(unit_t, double);
+ std::vector<double> get_clock_rates(unit_t);
+ double get_clock_rate(unit_t);
+ void set_clock_enabled(unit_t, bool);
+ double get_codec_rate(unit_t);
+
+private:
+ usrp1_iface::sptr _iface;
+ usrp1_codec_ctrl::sptr _codec;
+ unsigned _dbsrx_classic_div;
+ const usrp1_impl::dboard_slot_t _dboard_slot;
+ const double &_master_clock_rate;
+ const dboard_id_t _rx_dboard_id;
+};
+
+/***********************************************************************
+ * Make Function
+ **********************************************************************/
+dboard_iface::sptr usrp1_impl::make_dboard_iface(usrp1_iface::sptr iface,
+ usrp1_codec_ctrl::sptr codec,
+ usrp1_impl::dboard_slot_t dboard_slot,
+ const double &master_clock_rate,
+ const dboard_id_t &rx_dboard_id
+){
+ return dboard_iface::sptr(new usrp1_dboard_iface(
+ iface, codec, dboard_slot, master_clock_rate, rx_dboard_id
+ ));
+}
+
+/***********************************************************************
+ * Clock Rates
+ **********************************************************************/
+static const dboard_id_t dbsrx_classic_id(0x0002);
+
+/*
+ * Daughterboard reference clock register
+ *
+ * Bit 7 - 1 turns on refclk, 0 allows IO use
+ * Bits 6:0 - Divider value
+ */
+void usrp1_dboard_iface::set_clock_rate(unit_t unit, double rate)
+{
+ assert_has(this->get_clock_rates(unit), rate, "dboard clock rate");
+
+ if (unit == UNIT_RX && _rx_dboard_id == dbsrx_classic_id){
+ _dbsrx_classic_div = size_t(_master_clock_rate/rate);
+ switch(_dboard_slot){
+ case usrp1_impl::DBOARD_SLOT_A:
+ _iface->poke32(FR_RX_A_REFCLK, (_dbsrx_classic_div & 0x7f) | 0x80);
+ break;
+
+ case usrp1_impl::DBOARD_SLOT_B:
+ _iface->poke32(FR_RX_B_REFCLK, (_dbsrx_classic_div & 0x7f) | 0x80);
+ break;
+ }
+ }
+}
+
+std::vector<double> usrp1_dboard_iface::get_clock_rates(unit_t unit)
+{
+ std::vector<double> rates;
+ if (unit == UNIT_RX && _rx_dboard_id == dbsrx_classic_id){
+ for (size_t div = 1; div <= 127; div++)
+ rates.push_back(_master_clock_rate / div);
+ }
+ else{
+ rates.push_back(_master_clock_rate);
+ }
+ return rates;
+}
+
+double usrp1_dboard_iface::get_clock_rate(unit_t unit)
+{
+ if (unit == UNIT_RX && _rx_dboard_id == dbsrx_classic_id){
+ return _master_clock_rate/_dbsrx_classic_div;
+ }
+ return _master_clock_rate;
+}
+
+void usrp1_dboard_iface::set_clock_enabled(unit_t, bool)
+{
+ //TODO we can only enable for special case anyway...
+}
+
+double usrp1_dboard_iface::get_codec_rate(unit_t){
+ return _master_clock_rate;
+}
+
+/***********************************************************************
+ * GPIO
+ **********************************************************************/
+void usrp1_dboard_iface::_set_pin_ctrl(unit_t unit, boost::uint16_t value)
+{
+ switch(unit) {
+ case UNIT_RX:
+ if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A)
+ _iface->poke32(FR_ATR_MASK_1, value);
+ else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)
+ _iface->poke32(FR_ATR_MASK_3, value);
+ break;
+ case UNIT_TX:
+ if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A)
+ _iface->poke32(FR_ATR_MASK_0, value);
+ else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)
+ _iface->poke32(FR_ATR_MASK_2, value);
+ break;
+ }
+}
+
+void usrp1_dboard_iface::_set_gpio_ddr(unit_t unit, boost::uint16_t value)
+{
+ switch(unit) {
+ case UNIT_RX:
+ if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A)
+ _iface->poke32(FR_OE_1, 0xffff0000 | value);
+ else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)
+ _iface->poke32(FR_OE_3, 0xffff0000 | value);
+ break;
+ case UNIT_TX:
+ if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A)
+ _iface->poke32(FR_OE_0, 0xffff0000 | value);
+ else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)
+ _iface->poke32(FR_OE_2, 0xffff0000 | value);
+ break;
+ }
+}
+
+void usrp1_dboard_iface::_set_gpio_out(unit_t unit, boost::uint16_t value)
+{
+ switch(unit) {
+ case UNIT_RX:
+ if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A)
+ _iface->poke32(FR_IO_1, 0xffff0000 | value);
+ else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)
+ _iface->poke32(FR_IO_3, 0xffff0000 | value);
+ break;
+ case UNIT_TX:
+ if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A)
+ _iface->poke32(FR_IO_0, 0xffff0000 | value);
+ else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)
+ _iface->poke32(FR_IO_2, 0xffff0000 | value);
+ break;
+ }
+}
+
+void usrp1_dboard_iface::set_gpio_debug(unit_t, int)
+{
+ /* NOP */
+}
+
+boost::uint16_t usrp1_dboard_iface::read_gpio(unit_t unit)
+{
+ boost::uint32_t out_value;
+
+ if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A)
+ out_value = _iface->peek32(1);
+ else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)
+ out_value = _iface->peek32(2);
+ else
+ UHD_THROW_INVALID_CODE_PATH();
+
+ switch(unit) {
+ case UNIT_RX:
+ return (boost::uint16_t)((out_value >> 16) & 0x0000ffff);
+ case UNIT_TX:
+ return (boost::uint16_t)((out_value >> 0) & 0x0000ffff);
+ }
+ UHD_ASSERT_THROW(false);
+}
+
+void usrp1_dboard_iface::_set_atr_reg(unit_t unit,
+ atr_reg_t atr, boost::uint16_t value)
+{
+ // Ignore unsupported states
+ if ((atr == ATR_REG_IDLE) || (atr == ATR_REG_TX_ONLY))
+ return;
+ if(atr == ATR_REG_RX_ONLY) {
+ switch(unit) {
+ case UNIT_RX:
+ if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A)
+ _iface->poke32(FR_ATR_RXVAL_1, value);
+ else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)
+ _iface->poke32(FR_ATR_RXVAL_3, value);
+ break;
+ case UNIT_TX:
+ if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A)
+ _iface->poke32(FR_ATR_RXVAL_0, value);
+ else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)
+ _iface->poke32(FR_ATR_RXVAL_2, value);
+ break;
+ }
+ } else if (atr == ATR_REG_FULL_DUPLEX) {
+ switch(unit) {
+ case UNIT_RX:
+ if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A)
+ _iface->poke32(FR_ATR_TXVAL_1, value);
+ else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)
+ _iface->poke32(FR_ATR_TXVAL_3, value);
+ break;
+ case UNIT_TX:
+ if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A)
+ _iface->poke32(FR_ATR_TXVAL_0, value);
+ else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)
+ _iface->poke32(FR_ATR_TXVAL_2, value);
+ break;
+ }
+ }
+}
+/***********************************************************************
+ * SPI
+ **********************************************************************/
+/*!
+ * Static function to convert a unit type to a spi slave device number.
+ * \param unit the dboard interface unit type enum
+ * \param slot the side (A or B) the dboard is attached
+ * \return the slave device number
+ */
+static boost::uint32_t unit_to_otw_spi_dev(dboard_iface::unit_t unit,
+ usrp1_impl::dboard_slot_t slot)
+{
+ switch(unit) {
+ case dboard_iface::UNIT_TX:
+ if (slot == usrp1_impl::DBOARD_SLOT_A)
+ return SPI_ENABLE_TX_A;
+ else if (slot == usrp1_impl::DBOARD_SLOT_B)
+ return SPI_ENABLE_TX_B;
+ else
+ break;
+ case dboard_iface::UNIT_RX:
+ if (slot == usrp1_impl::DBOARD_SLOT_A)
+ return SPI_ENABLE_RX_A;
+ else if (slot == usrp1_impl::DBOARD_SLOT_B)
+ return SPI_ENABLE_RX_B;
+ else
+ break;
+ }
+ UHD_THROW_INVALID_CODE_PATH();
+}
+
+void usrp1_dboard_iface::write_spi(unit_t unit,
+ const spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits)
+{
+ _iface->write_spi(unit_to_otw_spi_dev(unit, _dboard_slot),
+ config, data, num_bits);
+}
+
+boost::uint32_t usrp1_dboard_iface::read_write_spi(unit_t unit,
+ const spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits)
+{
+ return _iface->read_spi(unit_to_otw_spi_dev(unit, _dboard_slot),
+ config, data, num_bits);
+}
+
+/***********************************************************************
+ * I2C
+ **********************************************************************/
+void usrp1_dboard_iface::write_i2c(boost::uint8_t addr,
+ const byte_vector_t &bytes)
+{
+ return _iface->write_i2c(addr, bytes);
+}
+
+byte_vector_t usrp1_dboard_iface::read_i2c(boost::uint8_t addr,
+ size_t num_bytes)
+{
+ return _iface->read_i2c(addr, num_bytes);
+}
+
+/***********************************************************************
+ * Aux DAX/ADC
+ **********************************************************************/
+void usrp1_dboard_iface::write_aux_dac(dboard_iface::unit_t,
+ aux_dac_t which, double value)
+{
+ //same aux dacs for each unit
+ static const uhd::dict<aux_dac_t, usrp1_codec_ctrl::aux_dac_t>
+ which_to_aux_dac = map_list_of
+ (AUX_DAC_A, usrp1_codec_ctrl::AUX_DAC_A)
+ (AUX_DAC_B, usrp1_codec_ctrl::AUX_DAC_B)
+ (AUX_DAC_C, usrp1_codec_ctrl::AUX_DAC_C)
+ (AUX_DAC_D, usrp1_codec_ctrl::AUX_DAC_D);
+
+ _codec->write_aux_dac(which_to_aux_dac[which], value);
+}
+
+double usrp1_dboard_iface::read_aux_adc(dboard_iface::unit_t unit,
+ aux_adc_t which)
+{
+ static const
+ uhd::dict<unit_t, uhd::dict<aux_adc_t, usrp1_codec_ctrl::aux_adc_t> >
+ unit_to_which_to_aux_adc = map_list_of(UNIT_RX, map_list_of
+ (AUX_ADC_A, usrp1_codec_ctrl::AUX_ADC_A1)
+ (AUX_ADC_B, usrp1_codec_ctrl::AUX_ADC_B1))
+ (UNIT_TX, map_list_of
+ (AUX_ADC_A, usrp1_codec_ctrl::AUX_ADC_A2)
+ (AUX_ADC_B, usrp1_codec_ctrl::AUX_ADC_B2));
+
+ return _codec->read_aux_adc(unit_to_which_to_aux_adc[unit][which]);
+}
diff --git a/host/lib/usrp/usrp1/io_impl.cpp b/host/lib/usrp/usrp1/io_impl.cpp
new file mode 100644
index 000000000..d256df660
--- /dev/null
+++ b/host/lib/usrp/usrp1/io_impl.cpp
@@ -0,0 +1,693 @@
+//
+// Copyright 2010-2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "validate_subdev_spec.hpp"
+#define SRPH_DONT_CHECK_SEQUENCE
+#include "../../transport/super_recv_packet_handler.hpp"
+#define SSPH_DONT_PAD_TO_ONE
+#include "../../transport/super_send_packet_handler.hpp"
+#include "usrp1_calc_mux.hpp"
+#include "fpga_regs_standard.h"
+#include "fpga_regs_common.h"
+#include "usrp_commands.h"
+#include "usrp1_impl.hpp"
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/tasks.hpp>
+#include <uhd/utils/safe_call.hpp>
+#include <uhd/transport/bounded_buffer.hpp>
+#include <boost/math/special_functions/sign.hpp>
+#include <boost/math/special_functions/round.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/bind.hpp>
+#include <boost/format.hpp>
+#include <boost/make_shared.hpp>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+
+static const size_t alignment_padding = 512;
+
+/***********************************************************************
+ * Helper struct to associate an offset with a buffer
+ **********************************************************************/
+struct offset_send_buffer{
+ offset_send_buffer(void){
+ /* NOP */
+ }
+
+ offset_send_buffer(managed_send_buffer::sptr buff, size_t offset = 0):
+ buff(buff), offset(offset)
+ {
+ /* NOP */
+ }
+
+ //member variables
+ managed_send_buffer::sptr buff;
+ size_t offset; /* in bytes */
+};
+
+/***********************************************************************
+ * Reusable managed send buffer to handle aligned commits
+ **********************************************************************/
+class offset_managed_send_buffer : public managed_send_buffer{
+public:
+ typedef boost::function<void(offset_send_buffer&, offset_send_buffer&, size_t)> commit_cb_type;
+ offset_managed_send_buffer(const commit_cb_type &commit_cb):
+ _commit_cb(commit_cb)
+ {
+ /* NOP */
+ }
+
+ void commit(size_t size){
+ if (size != 0) this->_commit_cb(_curr_buff, _next_buff, size);
+ }
+
+ sptr get_new(
+ offset_send_buffer &curr_buff,
+ offset_send_buffer &next_buff
+ ){
+ _curr_buff = curr_buff;
+ _next_buff = next_buff;
+ return make_managed_buffer(this);
+ }
+
+private:
+ void *get_buff(void) const{return _curr_buff.buff->cast<char *>() + _curr_buff.offset;}
+ size_t get_size(void) const{return _curr_buff.buff->size() - _curr_buff.offset;}
+
+ offset_send_buffer _curr_buff, _next_buff;
+ commit_cb_type _commit_cb;
+};
+
+/***********************************************************************
+ * BS VRT packer/unpacker functions (since samples don't have headers)
+ **********************************************************************/
+static void usrp1_bs_vrt_packer(
+ boost::uint32_t *,
+ vrt::if_packet_info_t &if_packet_info
+){
+ if_packet_info.num_header_words32 = 0;
+ if_packet_info.num_packet_words32 = if_packet_info.num_payload_words32;
+}
+
+static void usrp1_bs_vrt_unpacker(
+ const boost::uint32_t *,
+ vrt::if_packet_info_t &if_packet_info
+){
+ if_packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_DATA;
+ if_packet_info.num_payload_words32 = if_packet_info.num_packet_words32;
+ if_packet_info.num_payload_bytes = if_packet_info.num_packet_words32*sizeof(boost::uint32_t);
+ if_packet_info.num_header_words32 = 0;
+ if_packet_info.packet_count = 0;
+ if_packet_info.sob = false;
+ if_packet_info.eob = false;
+ if_packet_info.has_sid = false;
+ if_packet_info.has_cid = false;
+ if_packet_info.has_tsi = false;
+ if_packet_info.has_tsf = false;
+ if_packet_info.has_tlr = false;
+}
+
+/***********************************************************************
+ * IO Implementation Details
+ **********************************************************************/
+struct usrp1_impl::io_impl{
+ io_impl(zero_copy_if::sptr data_transport):
+ data_transport(data_transport),
+ curr_buff(offset_send_buffer(data_transport->get_send_buff())),
+ omsb(boost::bind(&usrp1_impl::io_impl::commit_send_buff, this, _1, _2, _3))
+ {
+ /* NOP */
+ }
+
+ ~io_impl(void){
+ UHD_SAFE_CALL(flush_send_buff();)
+ }
+
+ zero_copy_if::sptr data_transport;
+
+ //wrapper around the actual send buffer interface
+ //all of this to ensure only aligned lengths are committed
+ //NOTE: you must commit before getting a new buffer
+ //since the vrt packet handler obeys this, we are ok
+ offset_send_buffer curr_buff;
+ offset_managed_send_buffer omsb;
+ void commit_send_buff(offset_send_buffer&, offset_send_buffer&, size_t);
+ void flush_send_buff(void);
+ managed_send_buffer::sptr get_send_buff(double timeout){
+ //try to get a new managed buffer with timeout
+ offset_send_buffer next_buff(data_transport->get_send_buff(timeout));
+ if (not next_buff.buff.get()) return managed_send_buffer::sptr(); /* propagate timeout here */
+
+ //make a new managed buffer with the offset buffs
+ return omsb.get_new(curr_buff, next_buff);
+ }
+
+ task::sptr vandal_task;
+ boost::system_time last_send_time;
+};
+
+/*!
+ * Perform an actual commit on the send buffer:
+ * Copy the remainder of alignment to the next buffer.
+ * Commit the current buffer at multiples of alignment.
+ */
+void usrp1_impl::io_impl::commit_send_buff(
+ offset_send_buffer &curr,
+ offset_send_buffer &next,
+ size_t num_bytes
+){
+ //total number of bytes now in the current buffer
+ size_t bytes_in_curr_buffer = curr.offset + num_bytes;
+
+ //calculate how many to commit and remainder
+ size_t num_bytes_remaining = bytes_in_curr_buffer % alignment_padding;
+ size_t num_bytes_to_commit = bytes_in_curr_buffer - num_bytes_remaining;
+
+ //copy the remainder into the next buffer
+ std::memcpy(
+ next.buff->cast<char *>() + next.offset,
+ curr.buff->cast<char *>() + num_bytes_to_commit,
+ num_bytes_remaining
+ );
+
+ //update the offset into the next buffer
+ next.offset += num_bytes_remaining;
+
+ //commit the current buffer
+ curr.buff->commit(num_bytes_to_commit);
+
+ //store the next buffer for the next call
+ curr_buff = next;
+}
+
+/*!
+ * Flush the current buffer by padding out to alignment and committing.
+ */
+void usrp1_impl::io_impl::flush_send_buff(void){
+ //calculate the number of bytes to alignment
+ size_t bytes_to_pad = (-1*curr_buff.offset)%alignment_padding;
+
+ //send at least alignment_padding to guarantee zeros are sent
+ if (bytes_to_pad == 0) bytes_to_pad = alignment_padding;
+
+ //get the buffer, clear, and commit (really current buffer)
+ managed_send_buffer::sptr buff = this->get_send_buff(.1);
+ if (buff.get() != NULL){
+ std::memset(buff->cast<void *>(), 0, bytes_to_pad);
+ buff->commit(bytes_to_pad);
+ }
+}
+
+/***********************************************************************
+ * Initialize internals within this file
+ **********************************************************************/
+void usrp1_impl::io_init(void){
+
+ _io_impl = UHD_PIMPL_MAKE(io_impl, (_data_transport));
+
+ //init as disabled, then call the real function (uses restore)
+ this->enable_rx(false);
+ this->enable_tx(false);
+ rx_stream_on_off(false);
+ tx_stream_on_off(false);
+ _io_impl->flush_send_buff();
+
+ //create a new vandal thread to poll xerflow conditions
+ _io_impl->vandal_task = task::make(boost::bind(
+ &usrp1_impl::vandal_conquest_loop, this
+ ));
+}
+
+void usrp1_impl::rx_stream_on_off(bool enb){
+ this->restore_rx(enb);
+ //drain any junk in the receive transport after stop streaming command
+ while(not enb and _data_transport->get_recv_buff().get() != NULL){
+ /* NOP */
+ }
+}
+
+void usrp1_impl::tx_stream_on_off(bool enb){
+ _io_impl->last_send_time = boost::get_system_time();
+ if (_tx_enabled and not enb) _io_impl->flush_send_buff();
+ this->restore_tx(enb);
+}
+
+/*!
+ * Casually poll the overflow and underflow registers.
+ * On an underflow, push an async message into the queue and print.
+ * On an overflow, interleave an inline message into recv and print.
+ * This procedure creates "soft" inline and async user messages.
+ */
+void usrp1_impl::vandal_conquest_loop(void){
+
+ //initialize the async metadata
+ async_metadata_t async_metadata;
+ async_metadata.channel = 0;
+ async_metadata.has_time_spec = true;
+ async_metadata.event_code = async_metadata_t::EVENT_CODE_UNDERFLOW;
+
+ //initialize the inline metadata
+ rx_metadata_t inline_metadata;
+ inline_metadata.has_time_spec = true;
+ inline_metadata.error_code = rx_metadata_t::ERROR_CODE_OVERFLOW;
+
+ //start the polling loop...
+ try{ while (not boost::this_thread::interruption_requested()){
+ boost::uint8_t underflow = 0, overflow = 0;
+
+ //shutoff transmit if it has been too long since send() was called
+ if (_tx_enabled and (boost::get_system_time() - _io_impl->last_send_time) > boost::posix_time::milliseconds(100)){
+ this->tx_stream_on_off(false);
+ }
+
+ //always poll regardless of enabled so we can clear the conditions
+ _fx2_ctrl->usrp_control_read(
+ VRQ_GET_STATUS, 0, GS_TX_UNDERRUN, &underflow, sizeof(underflow)
+ );
+ _fx2_ctrl->usrp_control_read(
+ VRQ_GET_STATUS, 0, GS_RX_OVERRUN, &overflow, sizeof(overflow)
+ );
+
+ //handle message generation for xerflow conditions
+ if (_tx_enabled and underflow){
+ async_metadata.time_spec = _soft_time_ctrl->get_time();
+ _soft_time_ctrl->get_async_queue().push_with_pop_on_full(async_metadata);
+ UHD_MSG(fastpath) << "U";
+ }
+ if (_rx_enabled and overflow){
+ inline_metadata.time_spec = _soft_time_ctrl->get_time();
+ _soft_time_ctrl->get_inline_queue().push_with_pop_on_full(inline_metadata);
+ UHD_MSG(fastpath) << "O";
+ }
+
+ boost::this_thread::sleep(boost::posix_time::milliseconds(50));
+ }}
+ catch(const boost::thread_interrupted &){} //normal exit condition
+ catch(const std::exception &e){
+ UHD_MSG(error) << "The vandal caught an unexpected exception " << e.what() << std::endl;
+ }
+}
+
+/***********************************************************************
+ * RX streamer wrapper that talks to soft time control
+ **********************************************************************/
+class usrp1_recv_packet_streamer : public sph::recv_packet_handler, public rx_streamer{
+public:
+ usrp1_recv_packet_streamer(const size_t max_num_samps, soft_time_ctrl::sptr stc){
+ _max_num_samps = max_num_samps;
+ _stc = stc;
+ }
+
+ size_t get_num_channels(void) const{
+ return this->size();
+ }
+
+ size_t get_max_num_samps(void) const{
+ return _max_num_samps;
+ }
+
+ size_t recv(
+ const rx_streamer::buffs_type &buffs,
+ const size_t nsamps_per_buff,
+ uhd::rx_metadata_t &metadata,
+ const double timeout,
+ const bool one_packet
+ ){
+ //interleave a "soft" inline message into the receive stream:
+ if (_stc->get_inline_queue().pop_with_haste(metadata)) return 0;
+
+ size_t num_samps_recvd = sph::recv_packet_handler::recv(
+ buffs, nsamps_per_buff, metadata, timeout, one_packet
+ );
+
+ return _stc->recv_post(metadata, num_samps_recvd);
+ }
+
+private:
+ size_t _max_num_samps;
+ soft_time_ctrl::sptr _stc;
+};
+
+/***********************************************************************
+ * TX streamer wrapper that talks to soft time control
+ **********************************************************************/
+class usrp1_send_packet_streamer : public sph::send_packet_handler, public tx_streamer{
+public:
+ usrp1_send_packet_streamer(const size_t max_num_samps, soft_time_ctrl::sptr stc, boost::function<void(bool)> tx_enb_fcn){
+ _max_num_samps = max_num_samps;
+ this->set_max_samples_per_packet(_max_num_samps);
+ _stc = stc;
+ _tx_enb_fcn = tx_enb_fcn;
+ }
+
+ size_t get_num_channels(void) const{
+ return this->size();
+ }
+
+ size_t get_max_num_samps(void) const{
+ return _max_num_samps;
+ }
+
+ size_t send(
+ const tx_streamer::buffs_type &buffs,
+ const size_t nsamps_per_buff,
+ const uhd::tx_metadata_t &metadata,
+ const double timeout_
+ ){
+ double timeout = timeout_; //rw copy
+ _stc->send_pre(metadata, timeout);
+
+ _tx_enb_fcn(true); //always enable (it will do the right thing)
+ size_t num_samps_sent = sph::send_packet_handler::send(
+ buffs, nsamps_per_buff, metadata, timeout
+ );
+
+ //handle eob flag (commit the buffer, //disable the DACs)
+ //check num samps sent to avoid flush on incomplete/timeout
+ if (metadata.end_of_burst and num_samps_sent == nsamps_per_buff){
+ async_metadata_t metadata;
+ metadata.channel = 0;
+ metadata.has_time_spec = true;
+ metadata.time_spec = _stc->get_time();
+ metadata.event_code = async_metadata_t::EVENT_CODE_BURST_ACK;
+ _stc->get_async_queue().push_with_pop_on_full(metadata);
+ _tx_enb_fcn(false);
+ }
+
+ return num_samps_sent;
+ }
+
+private:
+ size_t _max_num_samps;
+ soft_time_ctrl::sptr _stc;
+ boost::function<void(bool)> _tx_enb_fcn;
+};
+
+/***********************************************************************
+ * Properties callback methods below
+ **********************************************************************/
+void usrp1_impl::update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){
+
+ //sanity checking
+ validate_subdev_spec(_tree, spec, "rx");
+
+ _rx_subdev_spec = spec; //shadow
+
+ //set the mux and set the number of rx channels
+ std::vector<mapping_pair_t> mapping;
+ BOOST_FOREACH(const subdev_spec_pair_t &pair, spec){
+ const std::string conn = _tree->access<std::string>(str(boost::format(
+ "/mboards/0/dboards/%s/rx_frontends/%s/connection"
+ ) % pair.db_name % pair.sd_name)).get();
+ mapping.push_back(std::make_pair(pair.db_name, conn));
+ }
+ bool s = this->disable_rx();
+ _iface->poke32(FR_RX_MUX, calc_rx_mux(mapping));
+ this->restore_rx(s);
+}
+
+void usrp1_impl::update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){
+
+ //sanity checking
+ validate_subdev_spec(_tree, spec, "tx");
+
+ _tx_subdev_spec = spec; //shadow
+
+ //set the mux and set the number of tx channels
+ std::vector<mapping_pair_t> mapping;
+ BOOST_FOREACH(const subdev_spec_pair_t &pair, spec){
+ const std::string conn = _tree->access<std::string>(str(boost::format(
+ "/mboards/0/dboards/%s/tx_frontends/%s/connection"
+ ) % pair.db_name % pair.sd_name)).get();
+ mapping.push_back(std::make_pair(pair.db_name, conn));
+ }
+ bool s = this->disable_tx();
+ _iface->poke32(FR_TX_MUX, calc_tx_mux(mapping));
+ this->restore_tx(s);
+}
+
+void usrp1_impl::update_tick_rate(const double rate){
+ //updating this variable should:
+ //update dboard iface -> it has a reference
+ //update dsp freq bounds -> publisher
+ _master_clock_rate = rate;
+}
+
+uhd::meta_range_t usrp1_impl::get_rx_dsp_host_rates(void){
+ meta_range_t range;
+ const size_t div = this->has_rx_halfband()? 2 : 1;
+ for (int rate = 256; rate >= 4; rate -= div){
+ range.push_back(range_t(_master_clock_rate/rate));
+ }
+ return range;
+}
+
+uhd::meta_range_t usrp1_impl::get_tx_dsp_host_rates(void){
+ meta_range_t range;
+ const size_t div = this->has_tx_halfband()? 2 : 1;
+ for (int rate = 256; rate >= 8; rate -= div){
+ range.push_back(range_t(_master_clock_rate/rate));
+ }
+ return range;
+}
+
+double usrp1_impl::update_rx_samp_rate(size_t dspno, const double samp_rate){
+
+ const size_t div = this->has_rx_halfband()? 2 : 1;
+ const size_t rate = boost::math::iround(_master_clock_rate/this->get_rx_dsp_host_rates().clip(samp_rate, true));
+
+ if (rate < 8 and this->has_rx_halfband()) UHD_MSG(warning) <<
+ "USRP1 cannot achieve decimations below 8 when the half-band filter is present.\n"
+ "The usrp1_fpga_4rx.rbf file is a special FPGA image without RX half-band filters.\n"
+ "To load this image, set the device address key/value pair: fpga=usrp1_fpga_4rx.rbf\n"
+ << std::endl;
+
+ if (dspno == 0){ //only care if dsp0 is set since its homogeneous
+ bool s = this->disable_rx();
+ _iface->poke32(FR_RX_SAMPLE_RATE_DIV, div - 1);
+ _iface->poke32(FR_DECIM_RATE, rate/div - 1);
+ this->restore_rx(s);
+
+ //update the streamer if created
+ boost::shared_ptr<usrp1_recv_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<usrp1_recv_packet_streamer>(_rx_streamer.lock());
+ if (my_streamer.get() != NULL){
+ my_streamer->set_samp_rate(_master_clock_rate / rate);
+ }
+ }
+
+ return _master_clock_rate / rate;
+}
+
+double usrp1_impl::update_tx_samp_rate(size_t dspno, const double samp_rate){
+
+ const size_t div = this->has_tx_halfband()? 4 : 2; //doubled for codec interp
+ const size_t rate = boost::math::iround(_master_clock_rate/this->get_tx_dsp_host_rates().clip(samp_rate, true));
+
+ if (dspno == 0){ //only care if dsp0 is set since its homogeneous
+ bool s = this->disable_tx();
+ _iface->poke32(FR_TX_SAMPLE_RATE_DIV, div - 1);
+ _iface->poke32(FR_INTERP_RATE, rate/div - 1);
+ this->restore_tx(s);
+
+ //update the streamer if created
+ boost::shared_ptr<usrp1_send_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<usrp1_send_packet_streamer>(_tx_streamer.lock());
+ if (my_streamer.get() != NULL){
+ my_streamer->set_samp_rate(_master_clock_rate / rate);
+ }
+ }
+
+ return _master_clock_rate / rate;
+}
+
+void usrp1_impl::update_rates(void){
+ const fs_path mb_path = "/mboards/0";
+ this->update_tick_rate(_master_clock_rate);
+ BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "rx_dsps")){
+ _tree->access<double>(mb_path / "rx_dsps" / name / "rate" / "value").update();
+ }
+ BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "tx_dsps")){
+ _tree->access<double>(mb_path / "tx_dsps" / name / "rate" / "value").update();
+ }
+}
+
+double usrp1_impl::update_rx_dsp_freq(const size_t dspno, const double freq_){
+
+ //correct for outside of rate (wrap around)
+ double freq = std::fmod(freq_, _master_clock_rate);
+ if (std::abs(freq) > _master_clock_rate/2.0)
+ freq -= boost::math::sign(freq)*_master_clock_rate;
+
+ //calculate the freq register word (signed)
+ UHD_ASSERT_THROW(std::abs(freq) <= _master_clock_rate/2.0);
+ static const double scale_factor = std::pow(2.0, 32);
+ const boost::int32_t freq_word = boost::int32_t(boost::math::round((freq / _master_clock_rate) * scale_factor));
+
+ static const boost::uint32_t dsp_index_to_reg_val[4] = {
+ FR_RX_FREQ_0, FR_RX_FREQ_1, FR_RX_FREQ_2, FR_RX_FREQ_3
+ };
+ _iface->poke32(dsp_index_to_reg_val[dspno], freq_word);
+
+ return (double(freq_word) / scale_factor) * _master_clock_rate;
+}
+
+double usrp1_impl::update_tx_dsp_freq(const size_t dspno, const double freq){
+ const subdev_spec_pair_t pair = _tx_subdev_spec.at(dspno);
+
+ //determine the connection type and hence, the sign
+ const std::string conn = _tree->access<std::string>(str(boost::format(
+ "/mboards/0/dboards/%s/tx_frontends/%s/connection"
+ ) % pair.db_name % pair.sd_name)).get();
+ double sign = (conn == "I" or conn == "IQ")? +1.0 : -1.0;
+
+ //map this DSP's subdev spec to a particular codec chip
+ _dbc[pair.db_name].codec->set_duc_freq(sign*freq, _master_clock_rate);
+ return freq; //assume infinite precision
+}
+
+/***********************************************************************
+ * Async Data
+ **********************************************************************/
+bool usrp1_impl::recv_async_msg(
+ async_metadata_t &async_metadata, double timeout
+){
+ boost::this_thread::disable_interruption di; //disable because the wait can throw
+ return _soft_time_ctrl->get_async_queue().pop_with_timed_wait(async_metadata, timeout);
+}
+
+/***********************************************************************
+ * Receive streamer
+ **********************************************************************/
+rx_streamer::sptr usrp1_impl::get_rx_stream(const uhd::stream_args_t &args_){
+ stream_args_t args = args_;
+
+ //setup defaults for unspecified values
+ args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format;
+ args.channels.clear(); //NOTE: we have no choice about the channel mapping
+ for (size_t ch = 0; ch < _rx_subdev_spec.size(); ch++){
+ args.channels.push_back(ch);
+ }
+
+ if (args.otw_format == "sc16"){
+ _iface->poke32(FR_RX_FORMAT, 0
+ | (0 << bmFR_RX_FORMAT_SHIFT_SHIFT)
+ | (16 << bmFR_RX_FORMAT_WIDTH_SHIFT)
+ | bmFR_RX_FORMAT_WANT_Q
+ );
+ }
+ else if (args.otw_format == "sc8"){
+ _iface->poke32(FR_RX_FORMAT, 0
+ | (8 << bmFR_RX_FORMAT_SHIFT_SHIFT)
+ | (8 << bmFR_RX_FORMAT_WIDTH_SHIFT)
+ | bmFR_RX_FORMAT_WANT_Q
+ );
+ }
+ else{
+ throw uhd::value_error("USRP1 RX cannot handle requested wire format: " + args.otw_format);
+ }
+
+ //calculate packet size
+ const size_t bpp = _data_transport->get_recv_frame_size()/args.channels.size();
+ const size_t spp = bpp/convert::get_bytes_per_item(args.otw_format);
+
+ //make the new streamer given the samples per packet
+ boost::shared_ptr<usrp1_recv_packet_streamer> my_streamer =
+ boost::make_shared<usrp1_recv_packet_streamer>(spp, _soft_time_ctrl);
+
+ //init some streamer stuff
+ my_streamer->set_tick_rate(_master_clock_rate);
+ my_streamer->set_vrt_unpacker(&usrp1_bs_vrt_unpacker);
+ my_streamer->set_xport_chan_get_buff(0, boost::bind(
+ &uhd::transport::zero_copy_if::get_recv_buff, _io_impl->data_transport, _1
+ ));
+
+ //set the converter
+ uhd::convert::id_type id;
+ id.input_format = args.otw_format + "_item16_usrp1";
+ id.num_inputs = 1;
+ id.output_format = args.cpu_format;
+ id.num_outputs = args.channels.size();
+ my_streamer->set_converter(id);
+
+ //special scale factor change for sc8
+ if (args.otw_format == "sc8")
+ my_streamer->set_scale_factor(1.0/127);
+
+ //save as weak ptr for update access
+ _rx_streamer = my_streamer;
+
+ //sets all tick and samp rates on this streamer
+ this->update_rates();
+
+ return my_streamer;
+}
+
+/***********************************************************************
+ * Transmit streamer
+ **********************************************************************/
+tx_streamer::sptr usrp1_impl::get_tx_stream(const uhd::stream_args_t &args_){
+ stream_args_t args = args_;
+
+ //setup defaults for unspecified values
+ args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format;
+ args.channels.clear(); //NOTE: we have no choice about the channel mapping
+ for (size_t ch = 0; ch < _tx_subdev_spec.size(); ch++){
+ args.channels.push_back(ch);
+ }
+
+ if (args.otw_format != "sc16"){
+ throw uhd::value_error("USRP1 TX cannot handle requested wire format: " + args.otw_format);
+ }
+
+ _iface->poke32(FR_TX_FORMAT, bmFR_TX_FORMAT_16_IQ);
+
+ //calculate packet size
+ size_t bpp = _data_transport->get_send_frame_size()/args.channels.size();
+ bpp -= alignment_padding - 1; //minus the max remainder after LUT commit
+ const size_t spp = bpp/convert::get_bytes_per_item(args.otw_format);
+
+ //make the new streamer given the samples per packet
+ boost::function<void(bool)> tx_fcn = boost::bind(&usrp1_impl::tx_stream_on_off, this, _1);
+ boost::shared_ptr<usrp1_send_packet_streamer> my_streamer =
+ boost::make_shared<usrp1_send_packet_streamer>(spp, _soft_time_ctrl, tx_fcn);
+
+ //init some streamer stuff
+ my_streamer->set_tick_rate(_master_clock_rate);
+ my_streamer->set_vrt_packer(&usrp1_bs_vrt_packer);
+ my_streamer->set_xport_chan_get_buff(0, boost::bind(
+ &usrp1_impl::io_impl::get_send_buff, _io_impl.get(), _1
+ ));
+
+ //set the converter
+ uhd::convert::id_type id;
+ id.input_format = args.cpu_format;
+ id.num_inputs = args.channels.size();
+ id.output_format = args.otw_format + "_item16_usrp1";
+ id.num_outputs = 1;
+ my_streamer->set_converter(id);
+
+ //save as weak ptr for update access
+ _tx_streamer = my_streamer;
+
+ //sets all tick and samp rates on this streamer
+ this->update_rates();
+
+ return my_streamer;
+}
diff --git a/host/lib/usrp/usrp1/soft_time_ctrl.cpp b/host/lib/usrp/usrp1/soft_time_ctrl.cpp
new file mode 100644
index 000000000..90b3a92da
--- /dev/null
+++ b/host/lib/usrp/usrp1/soft_time_ctrl.cpp
@@ -0,0 +1,227 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "soft_time_ctrl.hpp"
+#include <uhd/utils/tasks.hpp>
+#include <boost/make_shared.hpp>
+#include <boost/thread/condition_variable.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <iostream>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+namespace pt = boost::posix_time;
+
+static const time_spec_t TWIDDLE(0.0011);
+
+/***********************************************************************
+ * Soft time control implementation
+ **********************************************************************/
+class soft_time_ctrl_impl : public soft_time_ctrl{
+public:
+
+ soft_time_ctrl_impl(const cb_fcn_type &stream_on_off):
+ _nsamps_remaining(0),
+ _stream_mode(stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS),
+ _cmd_queue(2),
+ _async_msg_queue(1000),
+ _inline_msg_queue(1000),
+ _stream_on_off(stream_on_off)
+ {
+ //synchronously spawn a new thread
+ _recv_cmd_task = task::make(boost::bind(&soft_time_ctrl_impl::recv_cmd_task, this));
+
+ //initialize the time to something
+ this->set_time(time_spec_t(0.0));
+ }
+
+ /*******************************************************************
+ * Time control
+ ******************************************************************/
+ void set_time(const time_spec_t &time){
+ boost::mutex::scoped_lock lock(_update_mutex);
+ _time_offset = time_spec_t::get_system_time() - time;
+ }
+
+ time_spec_t get_time(void){
+ boost::mutex::scoped_lock lock(_update_mutex);
+ return time_now();
+ }
+
+ UHD_INLINE time_spec_t time_now(void){
+ //internal get time without scoped lock
+ return time_spec_t::get_system_time() - _time_offset;
+ }
+
+ UHD_INLINE void sleep_until_time(
+ boost::mutex::scoped_lock &lock, const time_spec_t &time
+ ){
+ boost::condition_variable cond;
+ //use a condition variable to unlock, sleep, lock
+ double seconds_to_sleep = (time - time_now()).get_real_secs();
+ cond.timed_wait(lock, pt::microseconds(long(seconds_to_sleep*1e6)));
+ }
+
+ /*******************************************************************
+ * Receive control
+ ******************************************************************/
+ size_t recv_post(rx_metadata_t &md, const size_t nsamps){
+ boost::mutex::scoped_lock lock(_update_mutex);
+
+ //Since it timed out on the receive, check for inline messages...
+ //Must do a post check because recv() will not wake up for a message.
+ if (md.error_code == rx_metadata_t::ERROR_CODE_TIMEOUT){
+ if (_inline_msg_queue.pop_with_haste(md)) return 0;
+ }
+
+ //load the metadata with the expected time
+ md.has_time_spec = true;
+ md.time_spec = time_now();
+
+ //none of the stuff below matters in continuous streaming mode
+ if (_stream_mode == stream_cmd_t::STREAM_MODE_START_CONTINUOUS) return nsamps;
+
+ //When to stop streaming:
+ //The samples have been received and the stream mode is non-continuous.
+ //Rewrite the sample count to clip to the requested number of samples.
+ if (_nsamps_remaining <= nsamps) switch(_stream_mode){
+ case stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE:{
+ rx_metadata_t metadata;
+ metadata.has_time_spec = true;
+ metadata.time_spec = this->time_now();
+ metadata.error_code = rx_metadata_t::ERROR_CODE_BROKEN_CHAIN;
+ _inline_msg_queue.push_with_pop_on_full(metadata);
+ } //continue to next case...
+ case stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE:
+ md.end_of_burst = true;
+ this->issue_stream_cmd(stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS);
+ return _nsamps_remaining;
+ default: break;
+ }
+
+ //update the consumed samples
+ _nsamps_remaining -= nsamps;
+ return nsamps;
+ }
+
+ void issue_stream_cmd(const stream_cmd_t &cmd){
+ _cmd_queue.push_with_wait(boost::make_shared<stream_cmd_t>(cmd));
+ }
+
+ void stream_on_off(bool enb){
+ _stream_on_off(enb);
+ _nsamps_remaining = 0;
+ }
+
+ /*******************************************************************
+ * Transmit control
+ ******************************************************************/
+ void send_pre(const tx_metadata_t &md, double &timeout){
+ if (not md.has_time_spec) return;
+
+ boost::mutex::scoped_lock lock(_update_mutex);
+
+ time_spec_t time_at(md.time_spec - TWIDDLE);
+
+ //handle late packets
+ if (time_at < time_now()){
+ async_metadata_t metadata;
+ metadata.channel = 0;
+ metadata.has_time_spec = true;
+ metadata.time_spec = this->time_now();
+ metadata.event_code = async_metadata_t::EVENT_CODE_TIME_ERROR;
+ _async_msg_queue.push_with_pop_on_full(metadata);
+ return;
+ }
+
+ timeout -= (time_at - time_now()).get_real_secs();
+ sleep_until_time(lock, time_at);
+ }
+
+ /*******************************************************************
+ * Thread control
+ ******************************************************************/
+ void recv_cmd_handle_cmd(const stream_cmd_t &cmd){
+ boost::mutex::scoped_lock lock(_update_mutex);
+
+ //handle the stream at time by sleeping
+ if (not cmd.stream_now){
+ time_spec_t time_at(cmd.time_spec - TWIDDLE);
+ if (time_at < time_now()){
+ rx_metadata_t metadata;
+ metadata.has_time_spec = true;
+ metadata.time_spec = this->time_now();
+ metadata.error_code = rx_metadata_t::ERROR_CODE_LATE_COMMAND;
+ _inline_msg_queue.push_with_pop_on_full(metadata);
+ this->issue_stream_cmd(stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS);
+ return;
+ }
+ else{
+ sleep_until_time(lock, time_at);
+ }
+ }
+
+ //When to stop streaming:
+ //Stop streaming when the command is a stop and streaming.
+ if (cmd.stream_mode == stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS
+ and _stream_mode != stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS
+ ) stream_on_off(false);
+
+ //When to start streaming:
+ //Start streaming when the command is not a stop and not streaming.
+ if (cmd.stream_mode != stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS
+ and _stream_mode == stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS
+ ) stream_on_off(true);
+
+ //update the state
+ _nsamps_remaining += cmd.num_samps;
+ _stream_mode = cmd.stream_mode;
+ }
+
+ void recv_cmd_task(void){ //task is looped
+ boost::shared_ptr<stream_cmd_t> cmd;
+ _cmd_queue.pop_with_wait(cmd);
+ recv_cmd_handle_cmd(*cmd);
+ }
+
+ bounded_buffer<async_metadata_t> &get_async_queue(void){
+ return _async_msg_queue;
+ }
+
+ bounded_buffer<rx_metadata_t> &get_inline_queue(void){
+ return _inline_msg_queue;
+ }
+
+private:
+ boost::mutex _update_mutex;
+ size_t _nsamps_remaining;
+ stream_cmd_t::stream_mode_t _stream_mode;
+ time_spec_t _time_offset;
+ bounded_buffer<boost::shared_ptr<stream_cmd_t> > _cmd_queue;
+ bounded_buffer<async_metadata_t> _async_msg_queue;
+ bounded_buffer<rx_metadata_t> _inline_msg_queue;
+ const cb_fcn_type _stream_on_off;
+ task::sptr _recv_cmd_task;
+};
+
+/***********************************************************************
+ * Soft time control factor
+ **********************************************************************/
+soft_time_ctrl::sptr soft_time_ctrl::make(const cb_fcn_type &stream_on_off){
+ return sptr(new soft_time_ctrl_impl(stream_on_off));
+}
diff --git a/host/lib/usrp/usrp1/soft_time_ctrl.hpp b/host/lib/usrp/usrp1/soft_time_ctrl.hpp
new file mode 100644
index 000000000..b92b51252
--- /dev/null
+++ b/host/lib/usrp/usrp1/soft_time_ctrl.hpp
@@ -0,0 +1,74 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_USRP1_SOFT_TIME_CTRL_HPP
+#define INCLUDED_LIBUHD_USRP_USRP1_SOFT_TIME_CTRL_HPP
+
+#include <uhd/types/stream_cmd.hpp>
+#include <uhd/types/time_spec.hpp>
+#include <uhd/types/metadata.hpp>
+#include <uhd/transport/bounded_buffer.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/function.hpp>
+
+namespace uhd{ namespace usrp{
+
+/*!
+ * The soft time control emulates some of the
+ * advanced streaming capabilities of the later USRP models.
+ * Soft time control uses the system time to emulate
+ * timed transmits, timed receive commands, device time,
+ * and inline and async error messages.
+ */
+class soft_time_ctrl : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<soft_time_ctrl> sptr;
+ typedef boost::function<void(bool)> cb_fcn_type;
+
+ /*!
+ * Make a new soft time control.
+ * \param stream_on_off a function to enable/disable rx
+ * \return a new soft time control object
+ */
+ static sptr make(const cb_fcn_type &stream_on_off);
+
+ //! Set the current time
+ virtual void set_time(const time_spec_t &time) = 0;
+
+ //! Get the current time
+ virtual time_spec_t get_time(void) = 0;
+
+ //! Call after the internal recv function
+ virtual size_t recv_post(rx_metadata_t &md, const size_t nsamps) = 0;
+
+ //! Call before the internal send function
+ virtual void send_pre(const tx_metadata_t &md, double &timeout) = 0;
+
+ //! Issue a stream command to receive
+ virtual void issue_stream_cmd(const stream_cmd_t &cmd) = 0;
+
+ //! Get access to a buffer of async metadata
+ virtual transport::bounded_buffer<async_metadata_t> &get_async_queue(void) = 0;
+
+ //! Get access to a buffer of inline metadata
+ virtual transport::bounded_buffer<rx_metadata_t> &get_inline_queue(void) = 0;
+};
+
+}} //namespace
+
+#endif /* INCLUDED_LIBUHD_USRP_USRP1_SOFT_TIME_CTRL_HPP */
diff --git a/host/lib/usrp/usrp1/usrp1_calc_mux.hpp b/host/lib/usrp/usrp1/usrp1_calc_mux.hpp
new file mode 100644
index 000000000..d86a7a809
--- /dev/null
+++ b/host/lib/usrp/usrp1/usrp1_calc_mux.hpp
@@ -0,0 +1,156 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/config.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/format.hpp>
+#include <utility>
+#include <vector>
+#include <string>
+
+#ifndef INCLUDED_USRP1_CALC_MUX_HPP
+#define INCLUDED_USRP1_CALC_MUX_HPP
+
+//db_name, conn_type for the mux calculations below...
+typedef std::pair<std::string, std::string> mapping_pair_t;
+
+/***********************************************************************
+ * Calculate the RX mux value:
+ * The I and Q mux values are intentionally reversed to flip I and Q
+ * to account for the reversal in the type conversion routines.
+ **********************************************************************/
+static int calc_rx_mux_pair(int adc_for_i, int adc_for_q){
+ return (adc_for_i << 0) | (adc_for_q << 2);
+}
+
+/*!
+ * 3 2 1 0
+ * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+ * +-----------------------+-------+-------+-------+-------+-+-----+
+ * | must be zero | Q3| I3| Q2| I2| Q1| I1| Q0| I0|Z| NCH |
+ * +-----------------------+-------+-------+-------+-------+-+-----+
+ */
+static boost::uint32_t calc_rx_mux(const std::vector<mapping_pair_t> &mapping){
+ //create look-up-table for mapping dboard name and connection type to ADC flags
+ static const int ADC0 = 0, ADC1 = 1, ADC2 = 2, ADC3 = 3;
+ static const uhd::dict<std::string, uhd::dict<std::string, int> > name_to_conn_to_flag = boost::assign::map_list_of
+ ("A", boost::assign::map_list_of
+ ("IQ", calc_rx_mux_pair(ADC0, ADC1)) //I and Q
+ ("QI", calc_rx_mux_pair(ADC1, ADC0)) //I and Q
+ ("I", calc_rx_mux_pair(ADC0, ADC0)) //I and Q (Q identical but ignored Z=1)
+ ("Q", calc_rx_mux_pair(ADC1, ADC1)) //I and Q (Q identical but ignored Z=1)
+ )
+ ("B", boost::assign::map_list_of
+ ("IQ", calc_rx_mux_pair(ADC2, ADC3)) //I and Q
+ ("QI", calc_rx_mux_pair(ADC3, ADC2)) //I and Q
+ ("I", calc_rx_mux_pair(ADC2, ADC2)) //I and Q (Q identical but ignored Z=1)
+ ("Q", calc_rx_mux_pair(ADC3, ADC3)) //I and Q (Q identical but ignored Z=1)
+ )
+ ;
+
+ //extract the number of channels
+ const size_t nchan = mapping.size();
+
+ //calculate the channel flags
+ int channel_flags = 0;
+ size_t num_reals = 0, num_quads = 0;
+ BOOST_FOREACH(const mapping_pair_t &pair, uhd::reversed(mapping)){
+ const std::string name = pair.first, conn = pair.second;
+ if (conn == "IQ" or conn == "QI") num_quads++;
+ if (conn == "I" or conn == "Q") num_reals++;
+ channel_flags = (channel_flags << 4) | name_to_conn_to_flag[name][conn];
+ }
+
+ //calculate Z:
+ // for all real sources: Z = 1
+ // for all quadrature sources: Z = 0
+ // for mixed sources: warning + Z = 0
+ int Z = (num_quads > 0)? 0 : 1;
+ if (num_quads != 0 and num_reals != 0) UHD_MSG(warning) << boost::format(
+ "Mixing real and quadrature rx subdevices is not supported.\n"
+ "The Q input to the real source(s) will be non-zero.\n"
+ );
+
+ //calculate the rx mux value
+ return ((channel_flags & 0xffff) << 4) | ((Z & 0x1) << 3) | ((nchan & 0x7) << 0);
+}
+
+/***********************************************************************
+ * Calculate the TX mux value:
+ * The I and Q mux values are intentionally reversed to flip I and Q
+ * to account for the reversal in the type conversion routines.
+ **********************************************************************/
+static int calc_tx_mux_pair(int chn_for_i, int chn_for_q){
+ return (chn_for_i << 0) | (chn_for_q << 4);
+}
+
+/*!
+ * 3 2 1 0
+ * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+ * +-----------------------+-------+-------+-------+-------+-+-----+
+ * | | DAC1Q | DAC1I | DAC0Q | DAC0I |0| NCH |
+ * +-----------------------------------------------+-------+-+-----+
+ */
+static boost::uint32_t calc_tx_mux(const std::vector<mapping_pair_t> &mapping){
+ //create look-up-table for mapping channel number and connection type to flags
+ static const int ENB = 1 << 3, CHAN_I0 = 0, CHAN_Q0 = 1, CHAN_I1 = 2, CHAN_Q1 = 3;
+ static const uhd::dict<size_t, uhd::dict<std::string, int> > chan_to_conn_to_flag = boost::assign::map_list_of
+ (0, boost::assign::map_list_of
+ ("IQ", calc_tx_mux_pair(CHAN_I0 | ENB, CHAN_Q0 | ENB))
+ ("QI", calc_tx_mux_pair(CHAN_Q0 | ENB, CHAN_I0 | ENB))
+ ("I", calc_tx_mux_pair(CHAN_I0 | ENB, 0 ))
+ ("Q", calc_tx_mux_pair(0, CHAN_I0 | ENB))
+ )
+ (1, boost::assign::map_list_of
+ ("IQ", calc_tx_mux_pair(CHAN_I1 | ENB, CHAN_Q1 | ENB))
+ ("QI", calc_tx_mux_pair(CHAN_Q1 | ENB, CHAN_I1 | ENB))
+ ("I", calc_tx_mux_pair(CHAN_I1 | ENB, 0 ))
+ ("Q", calc_tx_mux_pair(0, CHAN_I1 | ENB))
+ )
+ ;
+
+ //extract the number of channels
+ size_t nchan = mapping.size();
+
+ //calculate the channel flags
+ int channel_flags = 0, chan = 0;
+ uhd::dict<std::string, int> slot_to_chan_count = boost::assign::map_list_of("A", 0)("B", 0);
+ BOOST_FOREACH(const mapping_pair_t &pair, mapping){
+ const std::string name = pair.first, conn = pair.second;
+
+ //combine the channel flags: shift for slot A vs B
+ if (name == "A") channel_flags |= chan_to_conn_to_flag[chan][conn] << 0;
+ if (name == "B") channel_flags |= chan_to_conn_to_flag[chan][conn] << 8;
+
+ //sanity check, only 1 channel per slot
+ slot_to_chan_count[name]++;
+ if (slot_to_chan_count[name] > 1) throw uhd::value_error(
+ "cannot assign dboard slot to multiple channels: " + name
+ );
+
+ //increment for the next channel
+ chan++;
+ }
+
+ //calculate the tx mux value
+ return ((channel_flags & 0xffff) << 4) | ((nchan & 0x7) << 0);
+}
+
+#endif /* INCLUDED_USRP1_CALC_MUX_HPP */
diff --git a/host/lib/usrp/usrp1/usrp1_iface.cpp b/host/lib/usrp/usrp1/usrp1_iface.cpp
new file mode 100644
index 000000000..c790aecb4
--- /dev/null
+++ b/host/lib/usrp/usrp1/usrp1_iface.cpp
@@ -0,0 +1,205 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "usrp1_iface.hpp"
+#include "usrp_commands.h"
+#include <uhd/utils/log.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <boost/format.hpp>
+#include <stdexcept>
+#include <iomanip>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+
+class usrp1_iface_impl : public usrp1_iface{
+public:
+ /*******************************************************************
+ * Structors
+ ******************************************************************/
+ usrp1_iface_impl(uhd::usrp::fx2_ctrl::sptr ctrl_transport)
+ {
+ _ctrl_transport = ctrl_transport;
+ }
+
+ ~usrp1_iface_impl(void)
+ {
+ /* NOP */
+ }
+
+ /*******************************************************************
+ * Peek and Poke
+ ******************************************************************/
+ void poke32(boost::uint32_t addr, boost::uint32_t value)
+ {
+ boost::uint32_t swapped = uhd::htonx(value);
+
+ UHD_LOGV(always)
+ << "poke32("
+ << std::dec << std::setw(2) << addr << ", 0x"
+ << std::hex << std::setw(8) << value << ")" << std::endl
+ ;
+
+ boost::uint8_t w_index_h = SPI_ENABLE_FPGA & 0xff;
+ boost::uint8_t w_index_l = (SPI_FMT_MSB | SPI_FMT_HDR_1) & 0xff;
+
+ int ret =_ctrl_transport->usrp_control_write(
+ VRQ_SPI_WRITE,
+ addr & 0x7f,
+ (w_index_h << 8) | (w_index_l << 0),
+ (unsigned char*) &swapped,
+ sizeof(boost::uint32_t));
+
+ if (ret < 0) throw uhd::io_error("USRP1: failed control write");
+ }
+
+ boost::uint32_t peek32(boost::uint32_t addr)
+ {
+ UHD_LOGV(always)
+ << "peek32("
+ << std::dec << std::setw(2) << addr << ")" << std::endl
+ ;
+
+ boost::uint32_t value_out;
+
+ boost::uint8_t w_index_h = SPI_ENABLE_FPGA & 0xff;
+ boost::uint8_t w_index_l = (SPI_FMT_MSB | SPI_FMT_HDR_1) & 0xff;
+
+ int ret = _ctrl_transport->usrp_control_read(
+ VRQ_SPI_READ,
+ 0x80 | (addr & 0x7f),
+ (w_index_h << 8) | (w_index_l << 0),
+ (unsigned char*) &value_out,
+ sizeof(boost::uint32_t));
+
+ if (ret < 0) throw uhd::io_error("USRP1: failed control read");
+
+ return uhd::ntohx(value_out);
+ }
+
+ void poke16(boost::uint32_t, boost::uint16_t) {
+ throw uhd::not_implemented_error("Unhandled command poke16()");
+ }
+
+ boost::uint16_t peek16(boost::uint32_t) {
+ throw uhd::not_implemented_error("Unhandled command peek16()");
+ return 0;
+ }
+
+ /*******************************************************************
+ * I2C
+ ******************************************************************/
+ void write_i2c(boost::uint8_t addr, const byte_vector_t &bytes){
+ return _ctrl_transport->write_i2c(addr, bytes);
+ }
+
+ byte_vector_t read_i2c(boost::uint8_t addr, size_t num_bytes){
+ return _ctrl_transport->read_i2c(addr, num_bytes);
+ }
+
+ /*******************************************************************
+ * SPI
+ *
+ * For non-readback transactions use the SPI_WRITE command, which is
+ * simpler and uses the USB control buffer for OUT data. No data
+ * needs to be returned.
+ *
+ * For readback transactions use SPI_TRANSACT, which places up to
+ * 4 bytes of OUT data in the device request fields and uses the
+ * control buffer for IN data.
+ ******************************************************************/
+ boost::uint32_t transact_spi(int which_slave,
+ const spi_config_t &,
+ boost::uint32_t bits,
+ size_t num_bits,
+ bool readback)
+ {
+ UHD_LOGV(always)
+ << "transact_spi: " << std::endl
+ << " slave: " << which_slave << std::endl
+ << " bits: " << bits << std::endl
+ << " num_bits: " << num_bits << std::endl
+ << " readback: " << readback << std::endl
+ ;
+ UHD_ASSERT_THROW((num_bits <= 32) && !(num_bits % 8));
+ size_t num_bytes = num_bits / 8;
+
+ if (readback) {
+ unsigned char buff[4] = {
+ (bits >> 0) & 0xff, (bits >> 8) & 0xff,
+ (bits >> 16) & 0xff, (bits >> 24) & 0xff
+ };
+ //conditions where there are two header bytes
+ if (num_bytes >= 3 and buff[num_bytes-1] != 0 and buff[num_bytes-2] != 0 and buff[num_bytes-3] == 0){
+ if (int(num_bytes-2) != _ctrl_transport->usrp_control_read(
+ VRQ_SPI_READ, (buff[num_bytes-1] << 8) | (buff[num_bytes-2] << 0),
+ (which_slave << 8) | SPI_FMT_MSB | SPI_FMT_HDR_2,
+ buff, num_bytes-2
+ )) throw uhd::io_error("USRP1: failed SPI readback transaction");
+ }
+
+ //conditions where there is one header byte
+ else if (num_bytes >= 2 and buff[num_bytes-1] != 0 and buff[num_bytes-2] == 0){
+ if (int(num_bytes-1) != _ctrl_transport->usrp_control_read(
+ VRQ_SPI_READ, buff[num_bytes-1],
+ (which_slave << 8) | SPI_FMT_MSB | SPI_FMT_HDR_1,
+ buff, num_bytes-1
+ )) throw uhd::io_error("USRP1: failed SPI readback transaction");
+ }
+ else{
+ throw uhd::io_error("USRP1: invalid input data for SPI readback");
+ }
+ boost::uint32_t val = (((boost::uint32_t)buff[0]) << 0) |
+ (((boost::uint32_t)buff[1]) << 8) |
+ (((boost::uint32_t)buff[2]) << 16) |
+ (((boost::uint32_t)buff[3]) << 24);
+ return val;
+ }
+ else {
+ // Byteswap on num_bytes
+ unsigned char buff[4] = { 0 };
+ for (size_t i = 1; i <= num_bytes; i++)
+ buff[num_bytes - i] = (bits >> ((i - 1) * 8)) & 0xff;
+
+ boost::uint8_t w_index_h = which_slave & 0xff;
+ boost::uint8_t w_index_l = (SPI_FMT_MSB | SPI_FMT_HDR_0) & 0xff;
+
+ int ret =_ctrl_transport->usrp_control_write(
+ VRQ_SPI_WRITE,
+ 0x00,
+ (w_index_h << 8) | (w_index_l << 0),
+ buff, num_bytes);
+
+ if (ret < 0) throw uhd::io_error("USRP1: failed SPI transaction");
+
+ return 0;
+ }
+ }
+
+private:
+ uhd::usrp::fx2_ctrl::sptr _ctrl_transport;
+};
+
+/***********************************************************************
+ * Public Make Function
+ **********************************************************************/
+usrp1_iface::sptr usrp1_iface::make(uhd::usrp::fx2_ctrl::sptr ctrl_transport)
+{
+ return sptr(new usrp1_iface_impl(ctrl_transport));
+}
diff --git a/host/lib/usrp/usrp1/usrp1_iface.hpp b/host/lib/usrp/usrp1/usrp1_iface.hpp
new file mode 100644
index 000000000..c1ac34f25
--- /dev/null
+++ b/host/lib/usrp/usrp1/usrp1_iface.hpp
@@ -0,0 +1,44 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_USRP1_IFACE_HPP
+#define INCLUDED_USRP1_IFACE_HPP
+
+#include "fx2_ctrl.hpp"
+#include "wb_iface.hpp"
+#include <uhd/types/serial.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+
+/*!
+ * The usrp1 interface class:
+ * Provides a set of functions to implementation layer.
+ * Including spi, peek, poke, control...
+ */
+class usrp1_iface : public wb_iface, public uhd::i2c_iface, public uhd::spi_iface, boost::noncopyable{
+public:
+ typedef boost::shared_ptr<usrp1_iface> sptr;
+
+ /*!
+ * Make a new usrp1 interface with the control transport.
+ * \param ctrl_transport the usrp controller object
+ * \return a new usrp1 interface object
+ */
+ static sptr make(uhd::usrp::fx2_ctrl::sptr ctrl_transport);
+};
+
+#endif /* INCLUDED_USRP1_IFACE_HPP */
diff --git a/host/lib/usrp/usrp1/usrp1_impl.cpp b/host/lib/usrp/usrp1/usrp1_impl.cpp
new file mode 100644
index 000000000..607badda9
--- /dev/null
+++ b/host/lib/usrp/usrp1/usrp1_impl.cpp
@@ -0,0 +1,502 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "usrp1_impl.hpp"
+#include "usrp_spi_defs.h"
+#include "usrp_commands.h"
+#include "fpga_regs_standard.h"
+#include "fpga_regs_common.h"
+#include "usrp_i2c_addr.h"
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/safe_call.hpp>
+#include <uhd/transport/usb_control.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/utils/static.hpp>
+#include <uhd/utils/images.hpp>
+#include <boost/format.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/math/special_functions/round.hpp>
+#include <cstdio>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+
+const boost::uint16_t USRP1_VENDOR_ID = 0xfffe;
+const boost::uint16_t USRP1_PRODUCT_ID = 0x0002;
+const boost::uint16_t FX2_VENDOR_ID = 0x04b4;
+const boost::uint16_t FX2_PRODUCT_ID = 0x8613;
+static const boost::posix_time::milliseconds REENUMERATION_TIMEOUT_MS(3000);
+
+const std::vector<usrp1_impl::dboard_slot_t> usrp1_impl::_dboard_slots = boost::assign::list_of
+ (usrp1_impl::DBOARD_SLOT_A)(usrp1_impl::DBOARD_SLOT_B)
+;
+
+/***********************************************************************
+ * Discovery
+ **********************************************************************/
+static device_addrs_t usrp1_find(const device_addr_t &hint)
+{
+ device_addrs_t usrp1_addrs;
+
+ //return an empty list of addresses when type is set to non-usrp1
+ if (hint.has_key("type") and hint["type"] != "usrp1") return usrp1_addrs;
+
+ //Return an empty list of addresses when an address is specified,
+ //since an address is intended for a different, non-USB, device.
+ if (hint.has_key("addr")) return usrp1_addrs;
+
+ unsigned int vid, pid;
+
+ if(hint.has_key("vid") && hint.has_key("pid") && hint.has_key("type") && hint["type"] == "usrp1") {
+ sscanf(hint.get("vid").c_str(), "%x", &vid);
+ sscanf(hint.get("pid").c_str(), "%x", &pid);
+ } else {
+ vid = USRP1_VENDOR_ID;
+ pid = USRP1_PRODUCT_ID;
+ }
+
+ // Important note:
+ // The get device list calls are nested inside the for loop.
+ // This allows the usb guts to decontruct when not in use,
+ // so that re-enumeration after fw load can occur successfully.
+ // This requirement is a courtesy of libusb1.0 on windows.
+
+ //find the usrps and load firmware
+ size_t found = 0;
+ BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) {
+ //extract the firmware path for the USRP1
+ std::string usrp1_fw_image;
+ try{
+ usrp1_fw_image = find_image_path(hint.get("fw", "usrp1_fw.ihx"));
+ }
+ catch(...){
+ UHD_MSG(warning) << boost::format(
+ "Could not locate USRP1 firmware.\n"
+ "Please install the images package.\n"
+ );
+ return usrp1_addrs;
+ }
+ UHD_LOG << "USRP1 firmware image: " << usrp1_fw_image << std::endl;
+
+ usb_control::sptr control;
+ try{control = usb_control::make(handle, 0);}
+ catch(const uhd::exception &){continue;} //ignore claimed
+
+ fx2_ctrl::make(control)->usrp_load_firmware(usrp1_fw_image);
+ found++;
+ }
+
+ //get descriptors again with serial number, but using the initialized VID/PID now since we have firmware
+ vid = USRP1_VENDOR_ID;
+ pid = USRP1_PRODUCT_ID;
+
+ const boost::system_time timeout_time = boost::get_system_time() + REENUMERATION_TIMEOUT_MS;
+
+ //search for the device until found or timeout
+ while (boost::get_system_time() < timeout_time and usrp1_addrs.empty() and found != 0)
+ {
+ BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid))
+ {
+ usb_control::sptr control;
+ try{control = usb_control::make(handle, 0);}
+ catch(const uhd::exception &){continue;} //ignore claimed
+
+ fx2_ctrl::sptr fx2_ctrl = fx2_ctrl::make(control);
+ const mboard_eeprom_t mb_eeprom(*fx2_ctrl, mboard_eeprom_t::MAP_B000);
+ device_addr_t new_addr;
+ new_addr["type"] = "usrp1";
+ new_addr["name"] = mb_eeprom["name"];
+ new_addr["serial"] = handle->get_serial();
+ //this is a found usrp1 when the hint serial and name match or blank
+ if (
+ (not hint.has_key("name") or hint["name"] == new_addr["name"]) and
+ (not hint.has_key("serial") or hint["serial"] == new_addr["serial"])
+ ){
+ usrp1_addrs.push_back(new_addr);
+ }
+ }
+ }
+
+ return usrp1_addrs;
+}
+
+/***********************************************************************
+ * Make
+ **********************************************************************/
+static device::sptr usrp1_make(const device_addr_t &device_addr){
+ return device::sptr(new usrp1_impl(device_addr));
+}
+
+UHD_STATIC_BLOCK(register_usrp1_device){
+ device::register_device(&usrp1_find, &usrp1_make);
+}
+
+/***********************************************************************
+ * Structors
+ **********************************************************************/
+usrp1_impl::usrp1_impl(const device_addr_t &device_addr){
+ UHD_MSG(status) << "Opening a USRP1 device..." << std::endl;
+
+ //extract the FPGA path for the USRP1
+ std::string usrp1_fpga_image = find_image_path(
+ device_addr.get("fpga", "usrp1_fpga.rbf")
+ );
+ UHD_LOG << "USRP1 FPGA image: " << usrp1_fpga_image << std::endl;
+
+ //try to match the given device address with something on the USB bus
+ std::vector<usb_device_handle::sptr> device_list =
+ usb_device_handle::get_device_list(USRP1_VENDOR_ID, USRP1_PRODUCT_ID);
+
+ //locate the matching handle in the device list
+ usb_device_handle::sptr handle;
+ BOOST_FOREACH(usb_device_handle::sptr dev_handle, device_list) {
+ if (dev_handle->get_serial() == device_addr["serial"]){
+ handle = dev_handle;
+ break;
+ }
+ }
+ UHD_ASSERT_THROW(handle.get() != NULL); //better be found
+
+ ////////////////////////////////////////////////////////////////////
+ // Create controller objects
+ ////////////////////////////////////////////////////////////////////
+ //usb_control::sptr usb_ctrl = usb_control::make(handle);
+ _fx2_ctrl = fx2_ctrl::make(usb_control::make(handle, 0));
+ _fx2_ctrl->usrp_load_fpga(usrp1_fpga_image);
+ _fx2_ctrl->usrp_init();
+ _data_transport = usb_zero_copy::make(
+ handle, // identifier
+ 2, 6, // IN interface, endpoint
+ 1, 2, // OUT interface, endpoint
+ device_addr // param hints
+ );
+ _iface = usrp1_iface::make(_fx2_ctrl);
+ _soft_time_ctrl = soft_time_ctrl::make(
+ boost::bind(&usrp1_impl::rx_stream_on_off, this, _1)
+ );
+ _dbc["A"]; _dbc["B"]; //ensure that keys exist
+
+ // Normal mode with no loopback or Rx counting
+ _iface->poke32(FR_MODE, 0x00000000);
+ _iface->poke32(FR_DEBUG_EN, 0x00000000);
+
+ UHD_LOG
+ << "USRP1 Capabilities" << std::endl
+ << " number of duc's: " << get_num_ddcs() << std::endl
+ << " number of ddc's: " << get_num_ducs() << std::endl
+ << " rx halfband: " << has_rx_halfband() << std::endl
+ << " tx halfband: " << has_tx_halfband() << std::endl
+ ;
+
+ ////////////////////////////////////////////////////////////////////
+ // Initialize the properties tree
+ ////////////////////////////////////////////////////////////////////
+ _rx_dc_offset_shadow = 0;
+ _tree = property_tree::make();
+ _tree->create<std::string>("/name").set("USRP1 Device");
+ const fs_path mb_path = "/mboards/0";
+ _tree->create<std::string>(mb_path / "name").set("USRP1 (Classic)");
+ _tree->create<std::string>(mb_path / "load_eeprom")
+ .subscribe(boost::bind(&fx2_ctrl::usrp_load_eeprom, _fx2_ctrl, _1));
+
+ ////////////////////////////////////////////////////////////////////
+ // setup the mboard eeprom
+ ////////////////////////////////////////////////////////////////////
+ const mboard_eeprom_t mb_eeprom(*_fx2_ctrl, mboard_eeprom_t::MAP_B000);
+ _tree->create<mboard_eeprom_t>(mb_path / "eeprom")
+ .set(mb_eeprom)
+ .subscribe(boost::bind(&usrp1_impl::set_mb_eeprom, this, _1));
+
+ ////////////////////////////////////////////////////////////////////
+ // create clock control objects
+ ////////////////////////////////////////////////////////////////////
+ _master_clock_rate = 64e6;
+ if (device_addr.has_key("mcr")){
+ try{
+ _master_clock_rate = boost::lexical_cast<double>(device_addr["mcr"]);
+ }
+ catch(const std::exception &e){
+ UHD_MSG(error) << "Error parsing FPGA clock rate from device address: " << e.what() << std::endl;
+ }
+ }
+ else if (not mb_eeprom["mcr"].empty()){
+ try{
+ _master_clock_rate = boost::lexical_cast<double>(mb_eeprom["mcr"]);
+ }
+ catch(const std::exception &e){
+ UHD_MSG(error) << "Error parsing FPGA clock rate from EEPROM: " << e.what() << std::endl;
+ }
+ }
+ UHD_MSG(status) << boost::format("Using FPGA clock rate of %fMHz...") % (_master_clock_rate/1e6) << std::endl;
+ _tree->create<double>(mb_path / "tick_rate")
+ .subscribe(boost::bind(&usrp1_impl::update_tick_rate, this, _1))
+ .set(_master_clock_rate);
+
+ ////////////////////////////////////////////////////////////////////
+ // create codec control objects
+ ////////////////////////////////////////////////////////////////////
+ BOOST_FOREACH(const std::string &db, _dbc.keys()){
+ _dbc[db].codec = usrp1_codec_ctrl::make(_iface, (db == "A")? SPI_ENABLE_CODEC_A : SPI_ENABLE_CODEC_B);
+ const fs_path rx_codec_path = mb_path / "rx_codecs" / db;
+ const fs_path tx_codec_path = mb_path / "tx_codecs" / db;
+ _tree->create<std::string>(rx_codec_path / "name").set("ad9522");
+ _tree->create<meta_range_t>(rx_codec_path / "gains/pga/range").set(usrp1_codec_ctrl::rx_pga_gain_range);
+ _tree->create<double>(rx_codec_path / "gains/pga/value")
+ .coerce(boost::bind(&usrp1_impl::update_rx_codec_gain, this, db, _1));
+ _tree->create<std::string>(tx_codec_path / "name").set("ad9522");
+ _tree->create<meta_range_t>(tx_codec_path / "gains/pga/range").set(usrp1_codec_ctrl::tx_pga_gain_range);
+ _tree->create<double>(tx_codec_path / "gains/pga/value")
+ .subscribe(boost::bind(&usrp1_codec_ctrl::set_tx_pga_gain, _dbc[db].codec, _1))
+ .publish(boost::bind(&usrp1_codec_ctrl::get_tx_pga_gain, _dbc[db].codec));
+ }
+
+ ////////////////////////////////////////////////////////////////////
+ // and do the misc mboard sensors
+ ////////////////////////////////////////////////////////////////////
+ //none for now...
+ _tree->create<int>(mb_path / "sensors"); //phony property so this dir exists
+
+ ////////////////////////////////////////////////////////////////////
+ // create frontend control objects
+ ////////////////////////////////////////////////////////////////////
+ _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec")
+ .subscribe(boost::bind(&usrp1_impl::update_rx_subdev_spec, this, _1));
+ _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec")
+ .subscribe(boost::bind(&usrp1_impl::update_tx_subdev_spec, this, _1));
+
+ BOOST_FOREACH(const std::string &db, _dbc.keys()){
+ const fs_path rx_fe_path = mb_path / "rx_frontends" / db;
+ _tree->create<std::complex<double> >(rx_fe_path / "dc_offset" / "value")
+ .coerce(boost::bind(&usrp1_impl::set_rx_dc_offset, this, db, _1))
+ .set(std::complex<double>(0.0, 0.0));
+ _tree->create<bool>(rx_fe_path / "dc_offset" / "enable")
+ .subscribe(boost::bind(&usrp1_impl::set_enb_rx_dc_offset, this, db, _1))
+ .set(true);
+ }
+
+ ////////////////////////////////////////////////////////////////////
+ // create rx dsp control objects
+ ////////////////////////////////////////////////////////////////////
+ _tree->create<int>(mb_path / "rx_dsps"); //dummy in case we have none
+ for (size_t dspno = 0; dspno < get_num_ddcs(); dspno++){
+ fs_path rx_dsp_path = mb_path / str(boost::format("rx_dsps/%u") % dspno);
+ _tree->create<meta_range_t>(rx_dsp_path / "rate/range")
+ .publish(boost::bind(&usrp1_impl::get_rx_dsp_host_rates, this));
+ _tree->create<double>(rx_dsp_path / "rate/value")
+ .set(1e6) //some default rate
+ .coerce(boost::bind(&usrp1_impl::update_rx_samp_rate, this, dspno, _1));
+ _tree->create<double>(rx_dsp_path / "freq/value")
+ .coerce(boost::bind(&usrp1_impl::update_rx_dsp_freq, this, dspno, _1));
+ _tree->create<meta_range_t>(rx_dsp_path / "freq/range")
+ .publish(boost::bind(&usrp1_impl::get_rx_dsp_freq_range, this));
+ _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd");
+ if (dspno == 0){
+ //only subscribe the callback for dspno 0 since it will stream all dsps
+ _tree->access<stream_cmd_t>(rx_dsp_path / "stream_cmd")
+ .subscribe(boost::bind(&soft_time_ctrl::issue_stream_cmd, _soft_time_ctrl, _1));
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////
+ // create tx dsp control objects
+ ////////////////////////////////////////////////////////////////////
+ _tree->create<int>(mb_path / "tx_dsps"); //dummy in case we have none
+ for (size_t dspno = 0; dspno < get_num_ducs(); dspno++){
+ fs_path tx_dsp_path = mb_path / str(boost::format("tx_dsps/%u") % dspno);
+ _tree->create<meta_range_t>(tx_dsp_path / "rate/range")
+ .publish(boost::bind(&usrp1_impl::get_tx_dsp_host_rates, this));
+ _tree->create<double>(tx_dsp_path / "rate/value")
+ .set(1e6) //some default rate
+ .coerce(boost::bind(&usrp1_impl::update_tx_samp_rate, this, dspno, _1));
+ _tree->create<double>(tx_dsp_path / "freq/value")
+ .coerce(boost::bind(&usrp1_impl::update_tx_dsp_freq, this, dspno, _1));
+ _tree->create<meta_range_t>(tx_dsp_path / "freq/range")
+ .publish(boost::bind(&usrp1_impl::get_tx_dsp_freq_range, this));
+ }
+
+ ////////////////////////////////////////////////////////////////////
+ // create time control objects
+ ////////////////////////////////////////////////////////////////////
+ _tree->create<time_spec_t>(mb_path / "time/now")
+ .publish(boost::bind(&soft_time_ctrl::get_time, _soft_time_ctrl))
+ .subscribe(boost::bind(&soft_time_ctrl::set_time, _soft_time_ctrl, _1));
+
+ _tree->create<std::vector<std::string> >(mb_path / "clock_source/options").set(std::vector<std::string>(1, "internal"));
+ _tree->create<std::vector<std::string> >(mb_path / "time_source/options").set(std::vector<std::string>(1, "none"));
+ _tree->create<std::string>(mb_path / "clock_source/value").set("internal");
+ _tree->create<std::string>(mb_path / "time_source/value").set("none");
+
+ ////////////////////////////////////////////////////////////////////
+ // create dboard control objects
+ ////////////////////////////////////////////////////////////////////
+ BOOST_FOREACH(const std::string &db, _dbc.keys()){
+
+ //read the dboard eeprom to extract the dboard ids
+ dboard_eeprom_t rx_db_eeprom, tx_db_eeprom, gdb_eeprom;
+ rx_db_eeprom.load(*_fx2_ctrl, (db == "A")? (I2C_ADDR_RX_A) : (I2C_ADDR_RX_B));
+ tx_db_eeprom.load(*_fx2_ctrl, (db == "A")? (I2C_ADDR_TX_A) : (I2C_ADDR_TX_B));
+ gdb_eeprom.load(*_fx2_ctrl, (db == "A")? (I2C_ADDR_TX_A ^ 5) : (I2C_ADDR_TX_B ^ 5));
+
+ //create the properties and register subscribers
+ _tree->create<dboard_eeprom_t>(mb_path / "dboards" / db/ "rx_eeprom")
+ .set(rx_db_eeprom)
+ .subscribe(boost::bind(&usrp1_impl::set_db_eeprom, this, db, "rx", _1));
+ _tree->create<dboard_eeprom_t>(mb_path / "dboards" / db/ "tx_eeprom")
+ .set(tx_db_eeprom)
+ .subscribe(boost::bind(&usrp1_impl::set_db_eeprom, this, db, "tx", _1));
+ _tree->create<dboard_eeprom_t>(mb_path / "dboards" / db/ "gdb_eeprom")
+ .set(gdb_eeprom)
+ .subscribe(boost::bind(&usrp1_impl::set_db_eeprom, this, db, "gdb", _1));
+
+ //create a new dboard interface and manager
+ _dbc[db].dboard_iface = make_dboard_iface(
+ _iface, _dbc[db].codec,
+ (db == "A")? DBOARD_SLOT_A : DBOARD_SLOT_B,
+ _master_clock_rate, rx_db_eeprom.id
+ );
+ _tree->create<dboard_iface::sptr>(mb_path / "dboards" / db/ "iface").set(_dbc[db].dboard_iface);
+ _dbc[db].dboard_manager = dboard_manager::make(
+ rx_db_eeprom.id, tx_db_eeprom.id, gdb_eeprom.id,
+ _dbc[db].dboard_iface, _tree->subtree(mb_path / "dboards" / db)
+ );
+
+ //init the subdev specs if we have a dboard (wont leave this loop empty)
+ if (rx_db_eeprom.id != dboard_id_t::none() or _rx_subdev_spec.empty()){
+ _rx_subdev_spec = subdev_spec_t(db + ":" + _tree->list(mb_path / "dboards" / db / "rx_frontends").at(0));
+ }
+ if (tx_db_eeprom.id != dboard_id_t::none() or _tx_subdev_spec.empty()){
+ _tx_subdev_spec = subdev_spec_t(db + ":" + _tree->list(mb_path / "dboards" / db / "tx_frontends").at(0));
+ }
+ }
+
+ //initialize io handling
+ this->io_init();
+
+ ////////////////////////////////////////////////////////////////////
+ // do some post-init tasks
+ ////////////////////////////////////////////////////////////////////
+ this->update_rates();
+
+ //reset cordic rates and their properties to zero
+ BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "rx_dsps")){
+ _tree->access<double>(mb_path / "rx_dsps" / name / "freq" / "value").set(0.0);
+ }
+
+ if (_tree->list(mb_path / "rx_dsps").size() > 0)
+ _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(_rx_subdev_spec);
+ if (_tree->list(mb_path / "tx_dsps").size() > 0)
+ _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(_tx_subdev_spec);
+
+}
+
+usrp1_impl::~usrp1_impl(void){
+ UHD_SAFE_CALL(
+ this->enable_rx(false);
+ this->enable_tx(false);
+ )
+ _tree.reset(); //resets counts on sptrs held in tree
+ _soft_time_ctrl.reset(); //stops cmd task before proceeding
+ _io_impl.reset(); //stops vandal before other stuff gets deconstructed
+}
+
+/*!
+ * Capabilities Register
+ *
+ * 3 2 1 0
+ * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+ * +-----------------------------------------------+-+-----+-+-----+
+ * | Reserved |T|DUCs |R|DDCs |
+ * +-----------------------------------------------+-+-----+-+-----+
+ */
+size_t usrp1_impl::get_num_ddcs(void){
+ boost::uint32_t regval = _iface->peek32(FR_RB_CAPS);
+ return (regval >> 0) & 0x0007;
+}
+
+size_t usrp1_impl::get_num_ducs(void){
+ boost::uint32_t regval = _iface->peek32(FR_RB_CAPS);
+ return (regval >> 4) & 0x0007;
+}
+
+bool usrp1_impl::has_rx_halfband(void){
+ boost::uint32_t regval = _iface->peek32(FR_RB_CAPS);
+ return (regval >> 3) & 0x0001;
+}
+
+bool usrp1_impl::has_tx_halfband(void){
+ boost::uint32_t regval = _iface->peek32(FR_RB_CAPS);
+ return (regval >> 7) & 0x0001;
+}
+
+/***********************************************************************
+ * Properties callback methods below
+ **********************************************************************/
+void usrp1_impl::set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &mb_eeprom){
+ mb_eeprom.commit(*_fx2_ctrl, mboard_eeprom_t::MAP_B000);
+}
+
+void usrp1_impl::set_db_eeprom(const std::string &db, const std::string &type, const uhd::usrp::dboard_eeprom_t &db_eeprom){
+ if (type == "rx") db_eeprom.store(*_fx2_ctrl, (db == "A")? (I2C_ADDR_RX_A) : (I2C_ADDR_RX_B));
+ if (type == "tx") db_eeprom.store(*_fx2_ctrl, (db == "A")? (I2C_ADDR_TX_A) : (I2C_ADDR_TX_B));
+ if (type == "gdb") db_eeprom.store(*_fx2_ctrl, (db == "A")? (I2C_ADDR_TX_A ^ 5) : (I2C_ADDR_TX_B ^ 5));
+}
+
+double usrp1_impl::update_rx_codec_gain(const std::string &db, const double gain){
+ //set gain on both I and Q, readback on one
+ //TODO in the future, gains should have individual control
+ _dbc[db].codec->set_rx_pga_gain(gain, 'A');
+ _dbc[db].codec->set_rx_pga_gain(gain, 'B');
+ return _dbc[db].codec->get_rx_pga_gain('A');
+}
+
+uhd::meta_range_t usrp1_impl::get_rx_dsp_freq_range(void){
+ return meta_range_t(-_master_clock_rate/2, +_master_clock_rate/2);
+}
+
+uhd::meta_range_t usrp1_impl::get_tx_dsp_freq_range(void){
+ //magic scalar comes from codec control:
+ return meta_range_t(-_master_clock_rate*0.6875, +_master_clock_rate*0.6875);
+}
+
+void usrp1_impl::set_enb_rx_dc_offset(const std::string &db, const bool enb){
+ const size_t shift = (db == "A")? 0 : 2;
+ _rx_dc_offset_shadow &= ~(0x3 << shift); //clear bits
+ _rx_dc_offset_shadow |= ((enb)? 0x3 : 0x0) << shift;
+ _iface->poke32(FR_DC_OFFSET_CL_EN, _rx_dc_offset_shadow & 0xf);
+}
+
+std::complex<double> usrp1_impl::set_rx_dc_offset(const std::string &db, const std::complex<double> &offset){
+ const boost::int32_t i_off = boost::math::iround(offset.real() * (1ul << 31));
+ const boost::int32_t q_off = boost::math::iround(offset.imag() * (1ul << 31));
+
+ if (db == "A"){
+ _iface->poke32(FR_ADC_OFFSET_0, i_off);
+ _iface->poke32(FR_ADC_OFFSET_1, q_off);
+ }
+
+ if (db == "B"){
+ _iface->poke32(FR_ADC_OFFSET_2, i_off);
+ _iface->poke32(FR_ADC_OFFSET_3, q_off);
+ }
+
+ return std::complex<double>(double(i_off) * (1ul << 31), double(q_off) * (1ul << 31));
+}
diff --git a/host/lib/usrp/usrp1/usrp1_impl.hpp b/host/lib/usrp/usrp1/usrp1_impl.hpp
new file mode 100644
index 000000000..fd1b8c193
--- /dev/null
+++ b/host/lib/usrp/usrp1/usrp1_impl.hpp
@@ -0,0 +1,175 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "usrp1_iface.hpp"
+#include "codec_ctrl.hpp"
+#include "soft_time_ctrl.hpp"
+#include <uhd/device.hpp>
+#include <uhd/property_tree.hpp>
+#include <uhd/utils/pimpl.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/types/otw_type.hpp>
+#include <uhd/types/clock_config.hpp>
+#include <uhd/types/stream_cmd.hpp>
+#include <uhd/usrp/dboard_id.hpp>
+#include <uhd/usrp/mboard_eeprom.hpp>
+#include <uhd/usrp/subdev_spec.hpp>
+#include <uhd/usrp/dboard_eeprom.hpp>
+#include <uhd/usrp/dboard_manager.hpp>
+#include <uhd/transport/usb_zero_copy.hpp>
+#include <boost/weak_ptr.hpp>
+#include <complex>
+
+#ifndef INCLUDED_USRP1_IMPL_HPP
+#define INCLUDED_USRP1_IMPL_HPP
+
+/*!
+ * USRP1 implementation guts:
+ * The implementation details are encapsulated here.
+ * Handles properties on the mboard, dboard, dsps...
+ */
+class usrp1_impl : public uhd::device {
+public:
+ //! used everywhere to differentiate slots/sides...
+ enum dboard_slot_t{
+ DBOARD_SLOT_A = 'A',
+ DBOARD_SLOT_B = 'B'
+ };
+ //and a way to enumerate through a list of the above...
+ static const std::vector<dboard_slot_t> _dboard_slots;
+
+ //structors
+ usrp1_impl(const uhd::device_addr_t &);
+ ~usrp1_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);
+
+private:
+ uhd::property_tree::sptr _tree;
+
+ //device properties interface
+ uhd::property_tree::sptr get_tree(void) const{
+ return _tree;
+ }
+
+ //controllers
+ uhd::usrp::fx2_ctrl::sptr _fx2_ctrl;
+ usrp1_iface::sptr _iface;
+ uhd::usrp::soft_time_ctrl::sptr _soft_time_ctrl;
+ uhd::transport::usb_zero_copy::sptr _data_transport;
+ struct db_container_type{
+ usrp1_codec_ctrl::sptr codec;
+ uhd::usrp::dboard_iface::sptr dboard_iface;
+ uhd::usrp::dboard_manager::sptr dboard_manager;
+ };
+ uhd::dict<std::string, db_container_type> _dbc;
+
+ double _master_clock_rate; //clock rate shadow
+
+ //weak pointers to streamers for update purposes
+ boost::weak_ptr<uhd::rx_streamer> _rx_streamer;
+ boost::weak_ptr<uhd::tx_streamer> _tx_streamer;
+
+ void set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &);
+ void set_db_eeprom(const std::string &, const std::string &, const uhd::usrp::dboard_eeprom_t &);
+ double update_rx_codec_gain(const std::string &, const double); //sets A and B at once
+ void update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &);
+ void update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &);
+ double update_rx_samp_rate(size_t dspno, const double);
+ double update_tx_samp_rate(size_t dspno, const double);
+ void update_rates(void);
+ double update_rx_dsp_freq(const size_t, const double);
+ double update_tx_dsp_freq(const size_t, const double);
+ void update_tick_rate(const double rate);
+ uhd::meta_range_t get_rx_dsp_freq_range(void);
+ uhd::meta_range_t get_tx_dsp_freq_range(void);
+ uhd::meta_range_t get_rx_dsp_host_rates(void);
+ uhd::meta_range_t get_tx_dsp_host_rates(void);
+ size_t _rx_dc_offset_shadow;
+ void set_enb_rx_dc_offset(const std::string &db, const bool);
+ std::complex<double> set_rx_dc_offset(const std::string &db, const std::complex<double> &);
+
+ static uhd::usrp::dboard_iface::sptr make_dboard_iface(
+ usrp1_iface::sptr,
+ usrp1_codec_ctrl::sptr,
+ dboard_slot_t,
+ const double &,
+ const uhd::usrp::dboard_id_t &
+ );
+
+ //handle io stuff
+ UHD_PIMPL_DECL(io_impl) _io_impl;
+ void io_init(void);
+ void rx_stream_on_off(bool);
+ void tx_stream_on_off(bool);
+ void handle_overrun(size_t);
+
+ //channel mapping shadows
+ uhd::usrp::subdev_spec_t _rx_subdev_spec, _tx_subdev_spec;
+
+ //capabilities
+ size_t get_num_ducs(void);
+ size_t get_num_ddcs(void);
+ bool has_rx_halfband(void);
+ bool has_tx_halfband(void);
+
+ void vandal_conquest_loop(void);
+
+ //handle the enables
+ bool _rx_enabled, _tx_enabled;
+ void enable_rx(bool enb){
+ _rx_enabled = enb;
+ _fx2_ctrl->usrp_rx_enable(enb);
+ }
+ void enable_tx(bool enb){
+ _tx_enabled = enb;
+ _fx2_ctrl->usrp_tx_enable(enb);
+ }
+
+ //conditionally disable and enable rx
+ bool disable_rx(void){
+ if (_rx_enabled){
+ enable_rx(false);
+ return true;
+ }
+ return false;
+ }
+ void restore_rx(bool last){
+ if (last != _rx_enabled){
+ enable_rx(last);
+ }
+ }
+
+ //conditionally disable and enable tx
+ bool disable_tx(void){
+ if (_tx_enabled){
+ enable_tx(false);
+ return true;
+ }
+ return false;
+ }
+ void restore_tx(bool last){
+ if (last != _tx_enabled){
+ enable_tx(last);
+ }
+ }
+};
+
+#endif /* INCLUDED_USRP1_IMPL_HPP */
diff --git a/host/lib/usrp/usrp2/CMakeLists.txt b/host/lib/usrp/usrp2/CMakeLists.txt
new file mode 100644
index 000000000..10f7407b0
--- /dev/null
+++ b/host/lib/usrp/usrp2/CMakeLists.txt
@@ -0,0 +1,36 @@
+#
+# Copyright 2011 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# This file included, use CMake directory variables
+########################################################################
+
+########################################################################
+# Conditionally configure the USRP2 support
+########################################################################
+LIBUHD_REGISTER_COMPONENT("USRP2" ENABLE_USRP2 ON "ENABLE_LIBUHD" OFF)
+
+IF(ENABLE_USRP2)
+ LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/clock_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/codec_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/dboard_iface.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/io_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_iface.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_impl.cpp
+ )
+ENDIF(ENABLE_USRP2)
diff --git a/host/lib/usrp/usrp2/clock_ctrl.cpp b/host/lib/usrp/usrp2/clock_ctrl.cpp
new file mode 100644
index 000000000..7d3ffefa2
--- /dev/null
+++ b/host/lib/usrp/usrp2/clock_ctrl.cpp
@@ -0,0 +1,390 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "clock_ctrl.hpp"
+#include "ad9510_regs.hpp"
+#include "usrp2_regs.hpp" //spi slave constants
+#include "usrp2_clk_regs.hpp"
+#include <uhd/utils/safe_call.hpp>
+#include <uhd/utils/assert_has.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/math/special_functions/round.hpp>
+#include <iostream>
+
+using namespace uhd;
+
+static const bool enb_test_clk = false;
+
+/*!
+ * A usrp2 clock control specific to the ad9510 ic.
+ */
+class usrp2_clock_ctrl_impl : public usrp2_clock_ctrl{
+public:
+ usrp2_clock_ctrl_impl(usrp2_iface::sptr iface){
+ _iface = iface;
+ clk_regs = usrp2_clk_regs_t(_iface->get_rev());
+
+ _ad9510_regs.cp_current_setting = ad9510_regs_t::CP_CURRENT_SETTING_3_0MA;
+ this->write_reg(clk_regs.pll_3);
+
+ // Setup the clock registers to 100MHz:
+ // This was already done by the firmware (or the host couldnt communicate).
+ // We could remove this part, and just leave it to the firmware.
+ // But why not leave it in for those who want to mess with clock settings?
+ // 100mhz = 10mhz/R * (P*B + A)
+
+ _ad9510_regs.pll_power_down = ad9510_regs_t::PLL_POWER_DOWN_NORMAL;
+ _ad9510_regs.prescaler_value = ad9510_regs_t::PRESCALER_VALUE_DIV2;
+ this->write_reg(clk_regs.pll_4);
+
+ _ad9510_regs.acounter = 0;
+ this->write_reg(clk_regs.acounter);
+
+ _ad9510_regs.bcounter_msb = 0;
+ _ad9510_regs.bcounter_lsb = 5;
+ this->write_reg(clk_regs.bcounter_msb);
+ this->write_reg(clk_regs.bcounter_lsb);
+
+ _ad9510_regs.ref_counter_msb = 0;
+ _ad9510_regs.ref_counter_lsb = 1; // r divider = 1
+ this->write_reg(clk_regs.ref_counter_msb);
+ this->write_reg(clk_regs.ref_counter_lsb);
+
+ /* regs will be updated in commands below */
+
+ this->enable_external_ref(false);
+ this->enable_rx_dboard_clock(false);
+ this->enable_tx_dboard_clock(false);
+ this->enable_mimo_clock_out(false);
+
+ /* private clock enables, must be set here */
+ this->enable_dac_clock(true);
+ this->enable_adc_clock(true);
+ this->enable_test_clock(enb_test_clk);
+ }
+
+ ~usrp2_clock_ctrl_impl(void){UHD_SAFE_CALL(
+ //power down clock outputs
+ this->enable_external_ref(false);
+ this->enable_rx_dboard_clock(false);
+ this->enable_tx_dboard_clock(false);
+ this->enable_dac_clock(false);
+ this->enable_adc_clock(false);
+ this->enable_mimo_clock_out(false);
+ this->enable_test_clock(false);
+ )}
+
+ void enable_mimo_clock_out(bool enb){
+ //calculate the low and high dividers
+ size_t divider = size_t(this->get_master_clock_rate()/10e6);
+ size_t high = divider/2;
+ size_t low = divider - high;
+
+ switch(clk_regs.exp){
+ case 2: //U2 rev 3
+ _ad9510_regs.power_down_lvpecl_out2 = enb?
+ ad9510_regs_t::POWER_DOWN_LVPECL_OUT2_NORMAL :
+ ad9510_regs_t::POWER_DOWN_LVPECL_OUT2_SAFE_PD;
+ _ad9510_regs.output_level_lvpecl_out2 = ad9510_regs_t::OUTPUT_LEVEL_LVPECL_OUT2_810MV;
+ //set the registers (divider - 1)
+ _ad9510_regs.divider_low_cycles_out2 = low - 1;
+ _ad9510_regs.divider_high_cycles_out2 = high - 1;
+ _ad9510_regs.bypass_divider_out2 = 0;
+ break;
+
+ case 5: //U2 rev 4
+ _ad9510_regs.power_down_lvds_cmos_out5 = enb? 0 : 1;
+ _ad9510_regs.lvds_cmos_select_out5 = ad9510_regs_t::LVDS_CMOS_SELECT_OUT5_LVDS;
+ _ad9510_regs.output_level_lvds_out5 = ad9510_regs_t::OUTPUT_LEVEL_LVDS_OUT5_1_75MA;
+ //set the registers (divider - 1)
+ _ad9510_regs.divider_low_cycles_out5 = low - 1;
+ _ad9510_regs.divider_high_cycles_out5 = high - 1;
+ _ad9510_regs.bypass_divider_out5 = 0;
+ break;
+
+ case 6: //U2+
+ _ad9510_regs.power_down_lvds_cmos_out6 = enb? 0 : 1;
+ _ad9510_regs.lvds_cmos_select_out6 = ad9510_regs_t::LVDS_CMOS_SELECT_OUT6_LVDS;
+ _ad9510_regs.output_level_lvds_out6 = ad9510_regs_t::OUTPUT_LEVEL_LVDS_OUT6_1_75MA;
+ //set the registers (divider - 1)
+ _ad9510_regs.divider_low_cycles_out6 = low - 1;
+ _ad9510_regs.divider_high_cycles_out6 = high - 1;
+ _ad9510_regs.bypass_divider_out5 = 0;
+ break;
+
+ default:
+ break;
+ }
+ this->write_reg(clk_regs.output(clk_regs.exp));
+ this->write_reg(clk_regs.div_lo(clk_regs.exp));
+ this->update_regs();
+ }
+
+ //uses output clock 7 (cmos)
+ void enable_rx_dboard_clock(bool enb){
+ switch(_iface->get_rev()) {
+ case usrp2_iface::USRP_N200_R4:
+ case usrp2_iface::USRP_N210_R4:
+ _ad9510_regs.power_down_lvds_cmos_out7 = enb? 0 : 1;
+ _ad9510_regs.lvds_cmos_select_out7 = ad9510_regs_t::LVDS_CMOS_SELECT_OUT7_LVDS;
+ _ad9510_regs.output_level_lvds_out7 = ad9510_regs_t::OUTPUT_LEVEL_LVDS_OUT7_1_75MA;
+ this->write_reg(clk_regs.output(clk_regs.rx_db));
+ this->update_regs();
+ break;
+ default:
+ _ad9510_regs.power_down_lvds_cmos_out7 = enb? 0 : 1;
+ _ad9510_regs.lvds_cmos_select_out7 = ad9510_regs_t::LVDS_CMOS_SELECT_OUT7_CMOS;
+ _ad9510_regs.output_level_lvds_out7 = ad9510_regs_t::OUTPUT_LEVEL_LVDS_OUT7_1_75MA;
+ this->write_reg(clk_regs.output(clk_regs.rx_db));
+ this->update_regs();
+ break;
+ }
+ }
+
+ void set_rate_rx_dboard_clock(double rate){
+ assert_has(get_rates_rx_dboard_clock(), rate, "rx dboard clock rate");
+ size_t divider = size_t(get_master_clock_rate()/rate);
+ //bypass when the divider ratio is one
+ _ad9510_regs.bypass_divider_out7 = (divider == 1)? 1 : 0;
+ //calculate the low and high dividers
+ size_t high = divider/2;
+ size_t low = divider - high;
+ //set the registers (divider - 1)
+ _ad9510_regs.divider_low_cycles_out7 = low - 1;
+ _ad9510_regs.divider_high_cycles_out7 = high - 1;
+ //write the registers
+ this->write_reg(clk_regs.div_lo(clk_regs.rx_db));
+ this->write_reg(clk_regs.div_hi(clk_regs.rx_db));
+ this->update_regs();
+ }
+
+ std::vector<double> get_rates_rx_dboard_clock(void){
+ std::vector<double> rates;
+ for (size_t i = 1; i <= 16+16; i++) rates.push_back(get_master_clock_rate()/i);
+ return rates;
+ }
+
+ //uses output clock 6 (cmos) on USRP2, output clock 5 (cmos) on N200/N210 r3,
+ //and output clock 5 (lvds) on N200/N210 r4
+ void enable_tx_dboard_clock(bool enb){
+ switch(_iface->get_rev()) {
+ case usrp2_iface::USRP_N200_R4:
+ case usrp2_iface::USRP_N210_R4:
+ _ad9510_regs.power_down_lvds_cmos_out5 = enb? 0 : 1;
+ _ad9510_regs.lvds_cmos_select_out5 = ad9510_regs_t::LVDS_CMOS_SELECT_OUT5_LVDS;
+ _ad9510_regs.output_level_lvds_out5 = ad9510_regs_t::OUTPUT_LEVEL_LVDS_OUT5_1_75MA;
+ break;
+ case usrp2_iface::USRP_N200:
+ case usrp2_iface::USRP_N210:
+ _ad9510_regs.power_down_lvds_cmos_out5 = enb? 0 : 1;
+ _ad9510_regs.lvds_cmos_select_out5 = ad9510_regs_t::LVDS_CMOS_SELECT_OUT5_CMOS;
+ _ad9510_regs.output_level_lvds_out5 = ad9510_regs_t::OUTPUT_LEVEL_LVDS_OUT5_1_75MA;
+ break;
+ case usrp2_iface::USRP2_REV3:
+ case usrp2_iface::USRP2_REV4:
+ _ad9510_regs.power_down_lvds_cmos_out6 = enb? 0 : 1;
+ _ad9510_regs.lvds_cmos_select_out6 = ad9510_regs_t::LVDS_CMOS_SELECT_OUT6_CMOS;
+ _ad9510_regs.output_level_lvds_out6 = ad9510_regs_t::OUTPUT_LEVEL_LVDS_OUT6_1_75MA;
+ break;
+
+ default:
+ throw uhd::not_implemented_error("enable_tx_dboard_clock: unknown hardware version");
+ break;
+ }
+
+ this->write_reg(clk_regs.output(clk_regs.tx_db));
+ this->update_regs();
+ }
+
+ void set_rate_tx_dboard_clock(double rate){
+ assert_has(get_rates_tx_dboard_clock(), rate, "tx dboard clock rate");
+ size_t divider = size_t(get_master_clock_rate()/rate);
+ //bypass when the divider ratio is one
+ _ad9510_regs.bypass_divider_out6 = (divider == 1)? 1 : 0;
+ //calculate the low and high dividers
+ size_t high = divider/2;
+ size_t low = divider - high;
+
+ switch(clk_regs.tx_db) {
+ case 5: //USRP2+
+ _ad9510_regs.bypass_divider_out5 = (divider == 1)? 1 : 0;
+ _ad9510_regs.divider_low_cycles_out5 = low - 1;
+ _ad9510_regs.divider_high_cycles_out5 = high - 1;
+ break;
+ case 6: //USRP2
+ //bypass when the divider ratio is one
+ _ad9510_regs.bypass_divider_out6 = (divider == 1)? 1 : 0;
+ //set the registers (divider - 1)
+ _ad9510_regs.divider_low_cycles_out6 = low - 1;
+ _ad9510_regs.divider_high_cycles_out6 = high - 1;
+ break;
+ }
+
+ //write the registers
+ this->write_reg(clk_regs.div_hi(clk_regs.tx_db));
+ this->write_reg(clk_regs.div_lo(clk_regs.tx_db));
+ this->update_regs();
+ }
+
+ std::vector<double> get_rates_tx_dboard_clock(void){
+ return get_rates_rx_dboard_clock(); //same master clock, same dividers...
+ }
+
+ void enable_test_clock(bool enb) {
+ _ad9510_regs.power_down_lvpecl_out0 = enb?
+ ad9510_regs_t::POWER_DOWN_LVPECL_OUT0_NORMAL :
+ ad9510_regs_t::POWER_DOWN_LVPECL_OUT0_SAFE_PD;
+ _ad9510_regs.output_level_lvpecl_out0 = ad9510_regs_t::OUTPUT_LEVEL_LVPECL_OUT0_810MV;
+ _ad9510_regs.divider_low_cycles_out0 = 0;
+ _ad9510_regs.divider_high_cycles_out0 = 0;
+ _ad9510_regs.bypass_divider_out0 = 1;
+ this->write_reg(0x3c);
+ this->write_reg(0x48);
+ this->write_reg(0x49);
+ }
+
+ /*!
+ * If we are to use an external reference, enable the charge pump.
+ * \param enb true to enable the CP
+ */
+ void enable_external_ref(bool enb){
+ _ad9510_regs.charge_pump_mode = (enb)?
+ ad9510_regs_t::CHARGE_PUMP_MODE_NORMAL :
+ ad9510_regs_t::CHARGE_PUMP_MODE_3STATE ;
+ _ad9510_regs.pll_mux_control = ad9510_regs_t::PLL_MUX_CONTROL_DLD_HIGH;
+ _ad9510_regs.pfd_polarity = ad9510_regs_t::PFD_POLARITY_POS;
+ this->write_reg(clk_regs.pll_2);
+ this->update_regs();
+ }
+
+ double get_master_clock_rate(void){
+ return 100e6;
+ }
+
+ void set_mimo_clock_delay(double delay) {
+ //delay_val is a 5-bit value (0-31) for fine control
+ //the equations below determine delay for a given ramp current, # of caps and fine delay register
+ //delay range:
+ //range_ns = 200*((caps+3)/i_ramp_ua)*1.3286
+ //offset (zero delay):
+ //offset_ns = 0.34 + (1600 - i_ramp_ua)*1e-4 + ((caps-1)/ramp)*6
+ //delay_ns = offset_ns + range_ns * delay / 31
+
+ int delay_val = boost::math::iround(delay/9.744e-9*31);
+
+ if(delay_val == 0) {
+ switch(clk_regs.exp) {
+ case 5:
+ _ad9510_regs.delay_control_out5 = 1;
+ break;
+ case 6:
+ _ad9510_regs.delay_control_out6 = 1;
+ break;
+ default:
+ break; //delay not supported on U2 rev 3
+ }
+ } else {
+ switch(clk_regs.exp) {
+ case 5:
+ _ad9510_regs.delay_control_out5 = 0;
+ _ad9510_regs.ramp_current_out5 = ad9510_regs_t::RAMP_CURRENT_OUT5_200UA;
+ _ad9510_regs.ramp_capacitor_out5 = ad9510_regs_t::RAMP_CAPACITOR_OUT5_4CAPS;
+ _ad9510_regs.delay_fine_adjust_out5 = delay_val;
+ this->write_reg(0x34);
+ this->write_reg(0x35);
+ this->write_reg(0x36);
+ break;
+ case 6:
+ _ad9510_regs.delay_control_out6 = 0;
+ _ad9510_regs.ramp_current_out6 = ad9510_regs_t::RAMP_CURRENT_OUT6_200UA;
+ _ad9510_regs.ramp_capacitor_out6 = ad9510_regs_t::RAMP_CAPACITOR_OUT6_4CAPS;
+ _ad9510_regs.delay_fine_adjust_out6 = delay_val;
+ this->write_reg(0x38);
+ this->write_reg(0x39);
+ this->write_reg(0x3A);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+private:
+ /*!
+ * Write a single register to the spi regs.
+ * \param addr the address to write
+ */
+ void write_reg(boost::uint8_t addr){
+ boost::uint32_t data = _ad9510_regs.get_write_reg(addr);
+ _iface->write_spi(SPI_SS_AD9510, spi_config_t::EDGE_RISE, data, 24);
+ }
+
+ /*!
+ * Tells the ad9510 to latch the settings into the operational registers.
+ */
+ void update_regs(void){
+ _ad9510_regs.update_registers = 1;
+ this->write_reg(clk_regs.update);
+ }
+
+ //uses output clock 3 (pecl)
+ //this is the same between USRP2 and USRP2+ and doesn't get a switch statement
+ void enable_dac_clock(bool enb){
+ _ad9510_regs.power_down_lvpecl_out3 = (enb)?
+ ad9510_regs_t::POWER_DOWN_LVPECL_OUT3_NORMAL :
+ ad9510_regs_t::POWER_DOWN_LVPECL_OUT3_SAFE_PD;
+ _ad9510_regs.output_level_lvpecl_out3 = ad9510_regs_t::OUTPUT_LEVEL_LVPECL_OUT3_810MV;
+ _ad9510_regs.bypass_divider_out3 = 1;
+ this->write_reg(clk_regs.output(clk_regs.dac));
+ this->write_reg(clk_regs.div_hi(clk_regs.dac));
+ this->update_regs();
+ }
+
+ //uses output clock 4 (lvds) on USRP2 and output clock 2 (lvpecl) on USRP2+
+ void enable_adc_clock(bool enb){
+ switch(clk_regs.adc) {
+ case 2:
+ _ad9510_regs.power_down_lvpecl_out2 = enb? ad9510_regs_t::POWER_DOWN_LVPECL_OUT2_NORMAL : ad9510_regs_t::POWER_DOWN_LVPECL_OUT2_SAFE_PD;
+ _ad9510_regs.output_level_lvpecl_out2 = ad9510_regs_t::OUTPUT_LEVEL_LVPECL_OUT2_500MV;
+ _ad9510_regs.bypass_divider_out2 = 1;
+ break;
+ case 4:
+ _ad9510_regs.power_down_lvds_cmos_out4 = enb? 0 : 1;
+ _ad9510_regs.lvds_cmos_select_out4 = ad9510_regs_t::LVDS_CMOS_SELECT_OUT4_LVDS;
+ _ad9510_regs.output_level_lvds_out4 = ad9510_regs_t::OUTPUT_LEVEL_LVDS_OUT4_1_75MA;
+ _ad9510_regs.bypass_divider_out4 = 1;
+ break;
+ }
+
+ this->write_reg(clk_regs.output(clk_regs.adc));
+ this->write_reg(clk_regs.div_hi(clk_regs.adc));
+ this->update_regs();
+ }
+
+ usrp2_iface::sptr _iface;
+
+ usrp2_clk_regs_t clk_regs;
+ ad9510_regs_t _ad9510_regs;
+};
+
+/***********************************************************************
+ * Public make function for the ad9510 clock control
+ **********************************************************************/
+usrp2_clock_ctrl::sptr usrp2_clock_ctrl::make(usrp2_iface::sptr iface){
+ return sptr(new usrp2_clock_ctrl_impl(iface));
+}
diff --git a/host/lib/usrp/usrp2/clock_ctrl.hpp b/host/lib/usrp/usrp2/clock_ctrl.hpp
new file mode 100644
index 000000000..9ccbc959e
--- /dev/null
+++ b/host/lib/usrp/usrp2/clock_ctrl.hpp
@@ -0,0 +1,109 @@
+//
+// Copyright 2010 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_CLOCK_CTRL_HPP
+#define INCLUDED_CLOCK_CTRL_HPP
+
+#include "usrp2_iface.hpp"
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+#include <vector>
+
+class usrp2_clock_ctrl : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<usrp2_clock_ctrl> sptr;
+
+ /*!
+ * Make a clock config for the ad9510 ic.
+ * \param _iface a pointer to the usrp2 interface object
+ * \return a new clock control object
+ */
+ static sptr make(usrp2_iface::sptr iface);
+
+ /*!
+ * Get the master clock frequency for the fpga.
+ * \return the clock frequency in Hz
+ */
+ virtual double get_master_clock_rate(void) = 0;
+
+ /*!
+ * Enable/disable the rx dboard clock.
+ * \param enb true to enable
+ */
+ virtual void enable_rx_dboard_clock(bool enb) = 0;
+
+ /*!
+ * Set the clock rate on the rx dboard clock.
+ * \param rate the new clock rate
+ * \throw exception when rate invalid
+ */
+ virtual void set_rate_rx_dboard_clock(double rate) = 0;
+
+ /*!
+ * Get a list of possible rx dboard clock rates.
+ * \return a list of clock rates in Hz
+ */
+ virtual std::vector<double> get_rates_rx_dboard_clock(void) = 0;
+
+ /*!
+ * Enable/disable the tx dboard clock.
+ * \param enb true to enable
+ */
+ virtual void enable_tx_dboard_clock(bool enb) = 0;
+
+ /*!
+ * Set the clock rate on the tx dboard clock.
+ * \param rate the new clock rate
+ * \throw exception when rate invalid
+ */
+ virtual void set_rate_tx_dboard_clock(double rate) = 0;
+
+ /*!
+ * Get a list of possible tx dboard clock rates.
+ * \return a list of clock rates in Hz
+ */
+ virtual std::vector<double> get_rates_tx_dboard_clock(void) = 0;
+
+ /*!
+ * Enable/disable external reference.
+ * \param enb true to enable
+ */
+ virtual void enable_external_ref(bool enb) = 0;
+
+ /*!
+ * Enable/disable test clock output.
+ * \param enb true to enable
+ */
+ virtual void enable_test_clock(bool enb) = 0;
+
+ /*!
+ * Enable/disable the ref clock output over the serdes cable.
+ * \param enb true to enable
+ */
+ virtual void enable_mimo_clock_out(bool enb) = 0;
+
+ /*!
+ * Set the output delay of the mimo clock
+ * Used to synchronise daisy-chained USRPs over the MIMO cable
+ * Can also be used to adjust delay for uneven reference cable lengths
+ * \param delay the clock delay in seconds
+ */
+ virtual void set_mimo_clock_delay(double delay) = 0;
+
+};
+
+#endif /* INCLUDED_CLOCK_CTRL_HPP */
diff --git a/host/lib/usrp/usrp2/codec_ctrl.cpp b/host/lib/usrp/usrp2/codec_ctrl.cpp
new file mode 100644
index 000000000..06bf83b15
--- /dev/null
+++ b/host/lib/usrp/usrp2/codec_ctrl.cpp
@@ -0,0 +1,216 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "codec_ctrl.hpp"
+#include "ad9777_regs.hpp"
+#include "ads62p44_regs.hpp"
+#include "usrp2_regs.hpp"
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/safe_call.hpp>
+#include <uhd/exception.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/foreach.hpp>
+
+using namespace uhd;
+
+/*!
+ * A usrp2 codec control specific to the ad9777 ic.
+ */
+class usrp2_codec_ctrl_impl : public usrp2_codec_ctrl{
+public:
+ usrp2_codec_ctrl_impl(usrp2_iface::sptr iface){
+ _iface = iface;
+
+ //setup the ad9777 dac
+ _ad9777_regs.x_1r_2r_mode = ad9777_regs_t::X_1R_2R_MODE_1R;
+ _ad9777_regs.filter_interp_rate = ad9777_regs_t::FILTER_INTERP_RATE_4X;
+ _ad9777_regs.mix_mode = ad9777_regs_t::MIX_MODE_COMPLEX;
+ _ad9777_regs.pll_divide_ratio = ad9777_regs_t::PLL_DIVIDE_RATIO_DIV1;
+ _ad9777_regs.pll_state = ad9777_regs_t::PLL_STATE_ON;
+ _ad9777_regs.auto_cp_control = ad9777_regs_t::AUTO_CP_CONTROL_AUTO;
+ //I dac values
+ _ad9777_regs.idac_fine_gain_adjust = 0;
+ _ad9777_regs.idac_coarse_gain_adjust = 0xf;
+ _ad9777_regs.idac_offset_adjust_lsb = 0;
+ _ad9777_regs.idac_offset_adjust_msb = 0;
+ //Q dac values
+ _ad9777_regs.qdac_fine_gain_adjust = 0;
+ _ad9777_regs.qdac_coarse_gain_adjust = 0xf;
+ _ad9777_regs.qdac_offset_adjust_lsb = 0;
+ _ad9777_regs.qdac_offset_adjust_msb = 0;
+ //write all regs
+ for(boost::uint8_t addr = 0; addr <= 0xC; addr++){
+ this->send_ad9777_reg(addr);
+ }
+ set_tx_mod_mode(0);
+
+ //power-up adc
+ switch(_iface->get_rev()){
+ case usrp2_iface::USRP2_REV3:
+ case usrp2_iface::USRP2_REV4:
+ _iface->poke32(U2_REG_MISC_CTRL_ADC, U2_FLAG_MISC_CTRL_ADC_ON);
+ break;
+
+ case usrp2_iface::USRP_N200:
+ case usrp2_iface::USRP_N210:
+ _ads62p44_regs.reset = 1;
+ this->send_ads62p44_reg(0x00); //issue a reset to the ADC
+ //everything else should be pretty much default, i think
+ //_ads62p44_regs.decimation = DECIMATION_DECIMATE_1;
+ _ads62p44_regs.power_down = ads62p44_regs_t::POWER_DOWN_NORMAL;
+ this->send_ads62p44_reg(0x14);
+ this->set_rx_analog_gain(1);
+ break;
+
+ case usrp2_iface::USRP_N200_R4:
+ case usrp2_iface::USRP_N210_R4:
+ _ads62p44_regs.reset = 1;
+ this->send_ads62p44_reg(0x00); //issue a reset to the ADC
+ //everything else should be pretty much default, i think
+ //_ads62p44_regs.decimation = DECIMATION_DECIMATE_1;
+ _ads62p44_regs.override = 1;
+ this->send_ads62p44_reg(0x14);
+ _ads62p44_regs.power_down = ads62p44_regs_t::POWER_DOWN_NORMAL;
+ _ads62p44_regs.output_interface = ads62p44_regs_t::OUTPUT_INTERFACE_LVDS;
+ _ads62p44_regs.lvds_current = ads62p44_regs_t::LVDS_CURRENT_2_5MA;
+ _ads62p44_regs.lvds_data_term = ads62p44_regs_t::LVDS_DATA_TERM_100;
+ this->send_ads62p44_reg(0x11);
+ this->send_ads62p44_reg(0x12);
+ this->send_ads62p44_reg(0x14);
+ this->set_rx_analog_gain(1);
+ break;
+
+ case usrp2_iface::USRP_NXXX: break;
+ }
+ }
+
+ ~usrp2_codec_ctrl_impl(void){UHD_SAFE_CALL(
+ //power-down dac
+ _ad9777_regs.power_down_mode = 1;
+ this->send_ad9777_reg(0);
+
+ //power-down adc
+ switch(_iface->get_rev()){
+ case usrp2_iface::USRP2_REV3:
+ case usrp2_iface::USRP2_REV4:
+ _iface->poke32(U2_REG_MISC_CTRL_ADC, U2_FLAG_MISC_CTRL_ADC_OFF);
+ break;
+
+ case usrp2_iface::USRP_N200:
+ case usrp2_iface::USRP_N210:
+ case usrp2_iface::USRP_N200_R4:
+ case usrp2_iface::USRP_N210_R4:
+ //send a global power-down to the ADC here... it will get lifted on reset
+ _ads62p44_regs.power_down = ads62p44_regs_t::POWER_DOWN_GLOBAL_PD;
+ this->send_ads62p44_reg(0x14);
+ break;
+
+ case usrp2_iface::USRP_NXXX: break;
+ }
+ )}
+
+ void set_tx_mod_mode(int mod_mode){
+ //set the sign of the frequency shift
+ _ad9777_regs.modulation_form = (mod_mode > 0)?
+ ad9777_regs_t::MODULATION_FORM_E_PLUS_JWT:
+ ad9777_regs_t::MODULATION_FORM_E_MINUS_JWT
+ ;
+
+ //set the frequency shift
+ switch(std::abs(mod_mode)){
+ case 0:
+ case 1: _ad9777_regs.modulation_mode = ad9777_regs_t::MODULATION_MODE_NONE; break;
+ case 2: _ad9777_regs.modulation_mode = ad9777_regs_t::MODULATION_MODE_FS_2; break;
+ case 4: _ad9777_regs.modulation_mode = ad9777_regs_t::MODULATION_MODE_FS_4; break;
+ case 8: _ad9777_regs.modulation_mode = ad9777_regs_t::MODULATION_MODE_FS_8; break;
+ default: throw uhd::value_error("unknown modulation mode for ad9777");
+ }
+
+ this->send_ad9777_reg(0x01); //set the register
+ }
+
+ void set_rx_digital_gain(double gain) { //fine digital gain
+ switch(_iface->get_rev()){
+ case usrp2_iface::USRP_N200:
+ case usrp2_iface::USRP_N210:
+ case usrp2_iface::USRP_N200_R4:
+ case usrp2_iface::USRP_N210_R4:
+ _ads62p44_regs.fine_gain = int(gain/0.5);
+ this->send_ads62p44_reg(0x17);
+ break;
+
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+ }
+
+ void set_rx_digital_fine_gain(double gain) { //gain correction
+ switch(_iface->get_rev()){
+ case usrp2_iface::USRP_N200:
+ case usrp2_iface::USRP_N210:
+ case usrp2_iface::USRP_N200_R4:
+ case usrp2_iface::USRP_N210_R4:
+ _ads62p44_regs.gain_correction = int(gain / 0.05);
+ this->send_ads62p44_reg(0x1A);
+ break;
+
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+ }
+
+ void set_rx_analog_gain(bool /*gain*/) { //turns on/off analog 3.5dB preamp
+ switch(_iface->get_rev()){
+ case usrp2_iface::USRP_N200:
+ case usrp2_iface::USRP_N210:
+ case usrp2_iface::USRP_N200_R4:
+ case usrp2_iface::USRP_N210_R4:
+ _ads62p44_regs.coarse_gain = ads62p44_regs_t::COARSE_GAIN_3_5DB;//gain ? ads62p44_regs_t::COARSE_GAIN_3_5DB : ads62p44_regs_t::COARSE_GAIN_0DB;
+ this->send_ads62p44_reg(0x14);
+ break;
+
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+ }
+
+private:
+ ad9777_regs_t _ad9777_regs;
+ ads62p44_regs_t _ads62p44_regs;
+ usrp2_iface::sptr _iface;
+
+ void send_ad9777_reg(boost::uint8_t addr){
+ boost::uint16_t reg = _ad9777_regs.get_write_reg(addr);
+ UHD_LOGV(always) << "send_ad9777_reg: " << std::hex << reg << std::endl;
+ _iface->write_spi(
+ SPI_SS_AD9777, spi_config_t::EDGE_RISE,
+ reg, 16
+ );
+ }
+
+ void send_ads62p44_reg(boost::uint8_t addr) {
+ boost::uint16_t reg = _ads62p44_regs.get_write_reg(addr);
+ _iface->write_spi(
+ SPI_SS_ADS62P44, spi_config_t::EDGE_FALL,
+ reg, 16
+ );
+ }
+};
+
+/***********************************************************************
+ * Public make function for the usrp2 codec control
+ **********************************************************************/
+usrp2_codec_ctrl::sptr usrp2_codec_ctrl::make(usrp2_iface::sptr iface){
+ return sptr(new usrp2_codec_ctrl_impl(iface));
+}
diff --git a/host/lib/usrp/usrp2/codec_ctrl.hpp b/host/lib/usrp/usrp2/codec_ctrl.hpp
new file mode 100644
index 000000000..ca300e2b1
--- /dev/null
+++ b/host/lib/usrp/usrp2/codec_ctrl.hpp
@@ -0,0 +1,68 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_CODEC_CTRL_HPP
+#define INCLUDED_CODEC_CTRL_HPP
+
+#include "usrp2_iface.hpp"
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+
+class usrp2_codec_ctrl : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<usrp2_codec_ctrl> sptr;
+
+ /*!
+ * Make a codec control for the DAC and ADC.
+ * \param _iface a pointer to the usrp2 interface object
+ * \return a new codec control object
+ */
+ static sptr make(usrp2_iface::sptr iface);
+
+ /*!
+ * Set the modulation mode for the DAC.
+ * Possible modes are 0, +/-1, +/-2, +/-4, +/-8
+ * which correspond to shifts of fs/mod_mode.
+ * A mode of 0 or +/-1 means no modulation.
+ * \param mod_mode the modulation mode
+ */
+ virtual void set_tx_mod_mode(int mod_mode) = 0;
+
+ /*!
+ * Set the analog preamplifier on the USRP2+ ADC (ADS62P44).
+ * \param gain enable or disable the 3.5dB preamp
+ */
+
+ virtual void set_rx_analog_gain(bool gain) = 0;
+
+ /*!
+ * Set the digital gain on the USRP2+ ADC (ADS62P44).
+ * \param gain from 0-6dB
+ */
+
+ virtual void set_rx_digital_gain(double gain) = 0;
+
+ /*!
+ * Set the digital gain correction on the USRP2+ ADC (ADS62P44).
+ * \param gain from 0-0.5dB
+ */
+
+ virtual void set_rx_digital_fine_gain(double gain) = 0;
+
+};
+
+#endif /* INCLUDED_CODEC_CTRL_HPP */
diff --git a/host/lib/usrp/usrp2/dboard_iface.cpp b/host/lib/usrp/usrp2/dboard_iface.cpp
new file mode 100644
index 000000000..bc510c8a1
--- /dev/null
+++ b/host/lib/usrp/usrp2/dboard_iface.cpp
@@ -0,0 +1,295 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "gpio_core_200.hpp"
+#include "usrp2_iface.hpp"
+#include "clock_ctrl.hpp"
+#include "usrp2_regs.hpp" //wishbone address constants
+#include <uhd/usrp/dboard_iface.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/asio.hpp> //htonl and ntohl
+#include <boost/math/special_functions/round.hpp>
+#include "ad7922_regs.hpp" //aux adc
+#include "ad5623_regs.hpp" //aux dac
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace boost::assign;
+
+class usrp2_dboard_iface : public dboard_iface{
+public:
+ usrp2_dboard_iface(usrp2_iface::sptr iface, usrp2_clock_ctrl::sptr clock_ctrl);
+ ~usrp2_dboard_iface(void);
+
+ special_props_t get_special_props(void){
+ special_props_t props;
+ props.soft_clock_divider = false;
+ props.mangle_i2c_addrs = false;
+ return props;
+ }
+
+ void write_aux_dac(unit_t, aux_dac_t, double);
+ double read_aux_adc(unit_t, aux_adc_t);
+
+ void _set_pin_ctrl(unit_t, boost::uint16_t);
+ void _set_atr_reg(unit_t, atr_reg_t, boost::uint16_t);
+ void _set_gpio_ddr(unit_t, boost::uint16_t);
+ void _set_gpio_out(unit_t, boost::uint16_t);
+ void set_gpio_debug(unit_t, int);
+ boost::uint16_t read_gpio(unit_t);
+
+ void write_i2c(boost::uint8_t, const byte_vector_t &);
+ byte_vector_t read_i2c(boost::uint8_t, size_t);
+
+ void set_clock_rate(unit_t, double);
+ double get_clock_rate(unit_t);
+ std::vector<double> get_clock_rates(unit_t);
+ void set_clock_enabled(unit_t, bool);
+ double get_codec_rate(unit_t);
+
+ void write_spi(
+ unit_t unit,
+ const spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits
+ );
+
+ boost::uint32_t read_write_spi(
+ unit_t unit,
+ const spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits
+ );
+
+private:
+ usrp2_iface::sptr _iface;
+ usrp2_clock_ctrl::sptr _clock_ctrl;
+ gpio_core_200::sptr _gpio;
+
+ uhd::dict<unit_t, ad5623_regs_t> _dac_regs;
+ uhd::dict<unit_t, double> _clock_rates;
+ void _write_aux_dac(unit_t);
+};
+
+/***********************************************************************
+ * Make Function
+ **********************************************************************/
+dboard_iface::sptr make_usrp2_dboard_iface(
+ usrp2_iface::sptr iface,
+ usrp2_clock_ctrl::sptr clock_ctrl
+){
+ return dboard_iface::sptr(new usrp2_dboard_iface(iface, clock_ctrl));
+}
+
+/***********************************************************************
+ * Structors
+ **********************************************************************/
+usrp2_dboard_iface::usrp2_dboard_iface(
+ usrp2_iface::sptr iface,
+ usrp2_clock_ctrl::sptr clock_ctrl
+){
+ _iface = iface;
+ _clock_ctrl = clock_ctrl;
+ _gpio = gpio_core_200::make(_iface, U2_REG_SR_ADDR(SR_GPIO), U2_REG_GPIO_RB);
+
+ //reset the aux dacs
+ _dac_regs[UNIT_RX] = ad5623_regs_t();
+ _dac_regs[UNIT_TX] = ad5623_regs_t();
+ BOOST_FOREACH(unit_t unit, _dac_regs.keys()){
+ _dac_regs[unit].data = 1;
+ _dac_regs[unit].addr = ad5623_regs_t::ADDR_ALL;
+ _dac_regs[unit].cmd = ad5623_regs_t::CMD_RESET;
+ this->_write_aux_dac(unit);
+ }
+
+ //init the clock rate shadows with max rate clock
+ this->set_clock_rate(UNIT_RX, sorted(this->get_clock_rates(UNIT_RX)).back());
+ this->set_clock_rate(UNIT_TX, sorted(this->get_clock_rates(UNIT_TX)).back());
+}
+
+usrp2_dboard_iface::~usrp2_dboard_iface(void){
+ /* NOP */
+}
+
+/***********************************************************************
+ * Clocks
+ **********************************************************************/
+void usrp2_dboard_iface::set_clock_rate(unit_t unit, double rate){
+ _clock_rates[unit] = rate; //set to shadow
+ switch(unit){
+ case UNIT_RX: _clock_ctrl->set_rate_rx_dboard_clock(rate); return;
+ case UNIT_TX: _clock_ctrl->set_rate_tx_dboard_clock(rate); return;
+ }
+}
+
+double usrp2_dboard_iface::get_clock_rate(unit_t unit){
+ return _clock_rates[unit]; //get from shadow
+}
+
+std::vector<double> usrp2_dboard_iface::get_clock_rates(unit_t unit){
+ switch(unit){
+ case UNIT_RX: return _clock_ctrl->get_rates_rx_dboard_clock();
+ case UNIT_TX: return _clock_ctrl->get_rates_tx_dboard_clock();
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+void usrp2_dboard_iface::set_clock_enabled(unit_t unit, bool enb){
+ switch(unit){
+ case UNIT_RX: _clock_ctrl->enable_rx_dboard_clock(enb); return;
+ case UNIT_TX: _clock_ctrl->enable_tx_dboard_clock(enb); return;
+ }
+}
+
+double usrp2_dboard_iface::get_codec_rate(unit_t){
+ return _clock_ctrl->get_master_clock_rate();
+}
+/***********************************************************************
+ * GPIO
+ **********************************************************************/
+void usrp2_dboard_iface::_set_pin_ctrl(unit_t unit, boost::uint16_t value){
+ return _gpio->set_pin_ctrl(unit, value);
+}
+
+void usrp2_dboard_iface::_set_gpio_ddr(unit_t unit, boost::uint16_t value){
+ return _gpio->set_gpio_ddr(unit, value);
+}
+
+void usrp2_dboard_iface::_set_gpio_out(unit_t unit, boost::uint16_t value){
+ return _gpio->set_gpio_out(unit, value);
+}
+
+boost::uint16_t usrp2_dboard_iface::read_gpio(unit_t unit){
+ return _gpio->read_gpio(unit);
+}
+
+void usrp2_dboard_iface::_set_atr_reg(unit_t unit, atr_reg_t atr, boost::uint16_t value){
+ return _gpio->set_atr_reg(unit, atr, value);
+}
+
+void usrp2_dboard_iface::set_gpio_debug(unit_t, int){
+ throw uhd::not_implemented_error("no set_gpio_debug implemented");
+}
+
+/***********************************************************************
+ * SPI
+ **********************************************************************/
+static const uhd::dict<dboard_iface::unit_t, int> unit_to_spi_dev = map_list_of
+ (dboard_iface::UNIT_TX, SPI_SS_TX_DB)
+ (dboard_iface::UNIT_RX, SPI_SS_RX_DB)
+;
+
+void usrp2_dboard_iface::write_spi(
+ unit_t unit,
+ const spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits
+){
+ _iface->write_spi(unit_to_spi_dev[unit], config, data, num_bits);
+}
+
+boost::uint32_t usrp2_dboard_iface::read_write_spi(
+ unit_t unit,
+ const spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits
+){
+ return _iface->read_spi(unit_to_spi_dev[unit], config, data, num_bits);
+}
+
+/***********************************************************************
+ * I2C
+ **********************************************************************/
+void usrp2_dboard_iface::write_i2c(boost::uint8_t addr, const byte_vector_t &bytes){
+ return _iface->write_i2c(addr, bytes);
+}
+
+byte_vector_t usrp2_dboard_iface::read_i2c(boost::uint8_t addr, size_t num_bytes){
+ return _iface->read_i2c(addr, num_bytes);
+}
+
+/***********************************************************************
+ * Aux DAX/ADC
+ **********************************************************************/
+void usrp2_dboard_iface::_write_aux_dac(unit_t unit){
+ static const uhd::dict<unit_t, int> unit_to_spi_dac = map_list_of
+ (UNIT_RX, SPI_SS_RX_DAC)
+ (UNIT_TX, SPI_SS_TX_DAC)
+ ;
+ _iface->write_spi(
+ unit_to_spi_dac[unit], spi_config_t::EDGE_FALL,
+ _dac_regs[unit].get_reg(), 24
+ );
+}
+
+void usrp2_dboard_iface::write_aux_dac(unit_t unit, aux_dac_t which, double value){
+ _dac_regs[unit].data = boost::math::iround(4095*value/3.3);
+ _dac_regs[unit].cmd = ad5623_regs_t::CMD_WR_UP_DAC_CHAN_N;
+
+ typedef uhd::dict<aux_dac_t, ad5623_regs_t::addr_t> aux_dac_to_addr;
+ static const uhd::dict<unit_t, aux_dac_to_addr> unit_to_which_to_addr = map_list_of
+ (UNIT_RX, map_list_of
+ (AUX_DAC_A, ad5623_regs_t::ADDR_DAC_B)
+ (AUX_DAC_B, ad5623_regs_t::ADDR_DAC_A)
+ (AUX_DAC_C, ad5623_regs_t::ADDR_DAC_A)
+ (AUX_DAC_D, ad5623_regs_t::ADDR_DAC_B)
+ )
+ (UNIT_TX, map_list_of
+ (AUX_DAC_A, ad5623_regs_t::ADDR_DAC_A)
+ (AUX_DAC_B, ad5623_regs_t::ADDR_DAC_B)
+ (AUX_DAC_C, ad5623_regs_t::ADDR_DAC_B)
+ (AUX_DAC_D, ad5623_regs_t::ADDR_DAC_A)
+ )
+ ;
+ _dac_regs[unit].addr = unit_to_which_to_addr[unit][which];
+ this->_write_aux_dac(unit);
+}
+
+double usrp2_dboard_iface::read_aux_adc(unit_t unit, aux_adc_t which){
+ static const uhd::dict<unit_t, int> unit_to_spi_adc = map_list_of
+ (UNIT_RX, SPI_SS_RX_ADC)
+ (UNIT_TX, SPI_SS_TX_ADC)
+ ;
+
+ //setup spi config args
+ spi_config_t config;
+ config.mosi_edge = spi_config_t::EDGE_FALL;
+ config.miso_edge = spi_config_t::EDGE_RISE;
+
+ //setup the spi registers
+ ad7922_regs_t ad7922_regs;
+ switch(which){
+ case AUX_ADC_A: ad7922_regs.mod = 0; break;
+ case AUX_ADC_B: ad7922_regs.mod = 1; break;
+ } ad7922_regs.chn = ad7922_regs.mod; //normal mode: mod == chn
+
+ //write and read spi
+ _iface->write_spi(
+ unit_to_spi_adc[unit], config,
+ ad7922_regs.get_reg(), 16
+ );
+ ad7922_regs.set_reg(boost::uint16_t(_iface->read_spi(
+ unit_to_spi_adc[unit], config,
+ ad7922_regs.get_reg(), 16
+ )));
+
+ //convert to voltage and return
+ return 3.3*ad7922_regs.result/4095;
+}
diff --git a/host/lib/usrp/usrp2/fw_common.h b/host/lib/usrp/usrp2/fw_common.h
new file mode 100644
index 000000000..0babf7445
--- /dev/null
+++ b/host/lib/usrp/usrp2/fw_common.h
@@ -0,0 +1,151 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_USRP2_FW_COMMON_H
+#define INCLUDED_USRP2_FW_COMMON_H
+
+#include <stdint.h>
+
+/*!
+ * Structs and constants for usrp2 communication.
+ * This header is shared by the firmware and host code.
+ * Therefore, this header may only contain valid C code.
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//fpga and firmware compatibility numbers
+#define USRP2_FPGA_COMPAT_NUM 9
+#define USRP2_FW_COMPAT_NUM 11
+#define USRP2_FW_VER_MINOR 2
+
+//used to differentiate control packets over data port
+#define USRP2_INVALID_VRT_HEADER 0
+
+// udp ports for the usrp2 communication
+// Dynamic and/or private ports: 49152-65535
+#define USRP2_UDP_CTRL_PORT 49152
+//#define USRP2_UDP_UPDATE_PORT 49154
+#define USRP2_UDP_RX_DSP0_PORT 49156
+#define USRP2_UDP_TX_DSP0_PORT 49157
+#define USRP2_UDP_RX_DSP1_PORT 49158
+#define USRP2_UDP_UART_BASE_PORT 49170
+#define USRP2_UDP_UART_GPS_PORT 49172
+
+// Map for virtual firmware regs (not very big so we can keep it here for now)
+#define U2_FW_REG_LOCK_TIME 0
+#define U2_FW_REG_LOCK_GPID 1
+#define U2_FW_REG_VER_MINOR 7
+
+////////////////////////////////////////////////////////////////////////
+// I2C addresses
+////////////////////////////////////////////////////////////////////////
+#define USRP2_I2C_DEV_EEPROM 0x50 // 24LC02[45]: 7-bits 1010xxx
+#define USRP2_I2C_ADDR_MBOARD (USRP2_I2C_DEV_EEPROM | 0x0)
+#define USRP2_I2C_ADDR_TX_DB (USRP2_I2C_DEV_EEPROM | 0x4)
+#define USRP2_I2C_ADDR_RX_DB (USRP2_I2C_DEV_EEPROM | 0x5)
+
+////////////////////////////////////////////////////////////////////////
+// EEPROM Layout
+////////////////////////////////////////////////////////////////////////
+#define USRP2_EE_MBOARD_REV 0x00 //2 bytes, little-endian (historic, don't blame me)
+#define USRP2_EE_MBOARD_MAC_ADDR 0x02 //6 bytes
+#define USRP2_EE_MBOARD_IP_ADDR 0x0C //uint32, big-endian
+#define USRP2_EE_MBOARD_BOOTLOADER_FLAGS 0xF7
+
+typedef enum{
+ USRP2_CTRL_ID_HUH_WHAT = ' ',
+ //USRP2_CTRL_ID_FOR_SURE, //TODO error condition enums
+ //USRP2_CTRL_ID_SUX_MAN,
+
+ USRP2_CTRL_ID_WAZZUP_BRO = 'a',
+ USRP2_CTRL_ID_WAZZUP_DUDE = 'A',
+
+ USRP2_CTRL_ID_TRANSACT_ME_SOME_SPI_BRO = 's',
+ USRP2_CTRL_ID_OMG_TRANSACTED_SPI_DUDE = 'S',
+
+ USRP2_CTRL_ID_DO_AN_I2C_READ_FOR_ME_BRO = 'i',
+ USRP2_CTRL_ID_HERES_THE_I2C_DATA_DUDE = 'I',
+
+ USRP2_CTRL_ID_WRITE_THESE_I2C_VALUES_BRO = 'h',
+ USRP2_CTRL_ID_COOL_IM_DONE_I2C_WRITE_DUDE = 'H',
+
+ USRP2_CTRL_ID_GET_THIS_REGISTER_FOR_ME_BRO = 'r',
+ USRP2_CTRL_ID_OMG_GOT_REGISTER_SO_BAD_DUDE = 'R',
+
+ USRP2_CTRL_ID_HOLLER_AT_ME_BRO = 'l',
+ USRP2_CTRL_ID_HOLLER_BACK_DUDE = 'L',
+
+ USRP2_CTRL_ID_PEACE_OUT = '~'
+
+} usrp2_ctrl_id_t;
+
+typedef enum{
+ USRP2_DIR_RX = 'r',
+ USRP2_DIR_TX = 't'
+} usrp2_dir_which_t;
+
+typedef enum{
+ USRP2_CLK_EDGE_RISE = 'r',
+ USRP2_CLK_EDGE_FALL = 'f'
+} usrp2_clk_edge_t;
+
+typedef enum{
+ USRP2_REG_ACTION_FPGA_PEEK32 = 1,
+ USRP2_REG_ACTION_FPGA_PEEK16 = 2,
+ USRP2_REG_ACTION_FPGA_POKE32 = 3,
+ USRP2_REG_ACTION_FPGA_POKE16 = 4,
+ USRP2_REG_ACTION_FW_PEEK32 = 5,
+ USRP2_REG_ACTION_FW_POKE32 = 6
+} usrp2_reg_action_t;
+
+typedef struct{
+ uint32_t proto_ver;
+ uint32_t id;
+ uint32_t seq;
+ union{
+ uint32_t ip_addr;
+ struct {
+ uint32_t dev;
+ uint32_t data;
+ uint8_t miso_edge;
+ uint8_t mosi_edge;
+ uint8_t num_bits;
+ uint8_t readback;
+ } spi_args;
+ struct {
+ uint8_t addr;
+ uint8_t bytes;
+ uint8_t data[20];
+ } i2c_args;
+ struct {
+ uint32_t addr;
+ uint32_t data;
+ uint8_t action;
+ } reg_args;
+ struct {
+ uint32_t len;
+ } echo_args;
+ } data;
+} usrp2_ctrl_data_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* INCLUDED_USRP2_FW_COMMON_H */
diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp
new file mode 100644
index 000000000..d32ffb62c
--- /dev/null
+++ b/host/lib/usrp/usrp2/io_impl.cpp
@@ -0,0 +1,492 @@
+//
+// Copyright 2010-2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "validate_subdev_spec.hpp"
+#include "async_packet_handler.hpp"
+#include "../../transport/super_recv_packet_handler.hpp"
+#include "../../transport/super_send_packet_handler.hpp"
+#include "usrp2_impl.hpp"
+#include "usrp2_regs.hpp"
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/tasks.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/thread_priority.hpp>
+#include <uhd/transport/bounded_buffer.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/format.hpp>
+#include <boost/bind.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/make_shared.hpp>
+#include <iostream>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+namespace asio = boost::asio;
+namespace pt = boost::posix_time;
+
+/***********************************************************************
+ * helpers
+ **********************************************************************/
+static UHD_INLINE pt::time_duration to_time_dur(double timeout){
+ return pt::microseconds(long(timeout*1e6));
+}
+
+static UHD_INLINE double from_time_dur(const pt::time_duration &time_dur){
+ return 1e-6*time_dur.total_microseconds();
+}
+
+/***********************************************************************
+ * constants
+ **********************************************************************/
+static const size_t vrt_send_header_offset_words32 = 1;
+
+/***********************************************************************
+ * flow control monitor for a single tx channel
+ * - the pirate thread calls update
+ * - the get send buffer calls check
+ **********************************************************************/
+class flow_control_monitor{
+public:
+ typedef boost::uint32_t seq_type;
+ typedef boost::shared_ptr<flow_control_monitor> sptr;
+
+ /*!
+ * Make a new flow control monitor.
+ * \param max_seqs_out num seqs before throttling
+ */
+ flow_control_monitor(seq_type max_seqs_out):_max_seqs_out(max_seqs_out){
+ this->clear();
+ _ready_fcn = boost::bind(&flow_control_monitor::ready, this);
+ }
+
+ //! Clear the monitor, Ex: when a streamer is created
+ void clear(void){
+ _last_seq_out = 0;
+ _last_seq_ack = 0;
+ }
+
+ /*!
+ * Gets the current sequence number to go out.
+ * Increments the sequence for the next call
+ * \return the sequence to be sent to the dsp
+ */
+ UHD_INLINE seq_type get_curr_seq_out(void){
+ return _last_seq_out++;
+ }
+
+ /*!
+ * Check the flow control condition.
+ * \param timeout the timeout in seconds
+ * \return false on timeout
+ */
+ UHD_INLINE bool check_fc_condition(double timeout){
+ boost::mutex::scoped_lock lock(_fc_mutex);
+ if (this->ready()) return true;
+ boost::this_thread::disable_interruption di; //disable because the wait can throw
+ return _fc_cond.timed_wait(lock, to_time_dur(timeout), _ready_fcn);
+ }
+
+ /*!
+ * Update the flow control condition.
+ * \param seq the last sequence number to be ACK'd
+ */
+ UHD_INLINE void update_fc_condition(seq_type seq){
+ boost::mutex::scoped_lock lock(_fc_mutex);
+ _last_seq_ack = seq;
+ lock.unlock();
+ _fc_cond.notify_one();
+ }
+
+private:
+ bool ready(void){
+ return seq_type(_last_seq_out -_last_seq_ack) < _max_seqs_out;
+ }
+
+ boost::mutex _fc_mutex;
+ boost::condition _fc_cond;
+ seq_type _last_seq_out, _last_seq_ack;
+ const seq_type _max_seqs_out;
+ boost::function<bool(void)> _ready_fcn;
+};
+
+/***********************************************************************
+ * io impl details (internal to this file)
+ * - pirate crew
+ * - alignment buffer
+ * - thread loop
+ * - vrt packet handler states
+ **********************************************************************/
+struct usrp2_impl::io_impl{
+
+ io_impl(void):
+ async_msg_fifo(1000/*messages deep*/),
+ tick_rate(1 /*non-zero default*/)
+ {
+ /* NOP */
+ }
+
+ ~io_impl(void){
+ //Manually deconstuct the tasks, since this was not happening automatically.
+ pirate_tasks.clear();
+ }
+
+ managed_send_buffer::sptr get_send_buff(size_t chan, double timeout){
+ flow_control_monitor &fc_mon = *fc_mons[chan];
+
+ //wait on flow control w/ timeout
+ if (not fc_mon.check_fc_condition(timeout)) return managed_send_buffer::sptr();
+
+ //get a buffer from the transport w/ timeout
+ managed_send_buffer::sptr buff = tx_xports[chan]->get_send_buff(timeout);
+
+ //write the flow control word into the buffer
+ if (buff.get()) buff->cast<boost::uint32_t *>()[0] = uhd::htonx(fc_mon.get_curr_seq_out());
+
+ return buff;
+ }
+
+ //tx dsp: xports and flow control monitors
+ std::vector<zero_copy_if::sptr> tx_xports;
+ std::vector<flow_control_monitor::sptr> fc_mons;
+
+ //methods and variables for the pirate crew
+ void recv_pirate_loop(zero_copy_if::sptr, size_t);
+ std::list<task::sptr> pirate_tasks;
+ bounded_buffer<async_metadata_t> async_msg_fifo;
+ double tick_rate;
+};
+
+/***********************************************************************
+ * Receive Pirate Loop
+ * - while raiding, loot for message packet
+ * - update flow control condition count
+ * - put async message packets into queue
+ **********************************************************************/
+void usrp2_impl::io_impl::recv_pirate_loop(
+ zero_copy_if::sptr err_xport, size_t index
+){
+ set_thread_priority_safe();
+
+ //store a reference to the flow control monitor (offset by max dsps)
+ flow_control_monitor &fc_mon = *(this->fc_mons[index]);
+
+ while (not boost::this_thread::interruption_requested()){
+ managed_recv_buffer::sptr buff = err_xport->get_recv_buff();
+ if (not buff.get()) continue; //ignore timeout/error buffers
+
+ try{
+ //extract the vrt header packet info
+ vrt::if_packet_info_t if_packet_info;
+ if_packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t);
+ const boost::uint32_t *vrt_hdr = buff->cast<const boost::uint32_t *>();
+ vrt::if_hdr_unpack_be(vrt_hdr, if_packet_info);
+
+ //handle a tx async report message
+ if (if_packet_info.sid == USRP2_TX_ASYNC_SID and if_packet_info.packet_type != vrt::if_packet_info_t::PACKET_TYPE_DATA){
+
+ //fill in the async metadata
+ async_metadata_t metadata;
+ load_metadata_from_buff(uhd::ntohx<boost::uint32_t>, metadata, if_packet_info, vrt_hdr, tick_rate, index);
+
+ //catch the flow control packets and react
+ if (metadata.event_code == 0){
+ boost::uint32_t fc_word32 = (vrt_hdr + if_packet_info.num_header_words32)[1];
+ fc_mon.update_fc_condition(uhd::ntohx(fc_word32));
+ continue;
+ }
+ //else UHD_MSG(often) << "metadata.event_code " << metadata.event_code << std::endl;
+ async_msg_fifo.push_with_pop_on_full(metadata);
+
+ standard_async_msg_prints(metadata);
+ }
+ else{
+ //TODO unknown received packet, may want to print error...
+ }
+ }catch(const std::exception &e){
+ UHD_MSG(error) << "Error in recv pirate loop: " << e.what() << std::endl;
+ }
+ }
+}
+
+/***********************************************************************
+ * Helper Functions
+ **********************************************************************/
+void usrp2_impl::io_init(void){
+ //create new io impl
+ _io_impl = UHD_PIMPL_MAKE(io_impl, ());
+
+ //init first so we dont have an access race
+ BOOST_FOREACH(const std::string &mb, _mbc.keys()){
+ //init the tx xport and flow control monitor
+ _io_impl->tx_xports.push_back(_mbc[mb].tx_dsp_xport);
+ _io_impl->fc_mons.push_back(flow_control_monitor::sptr(new flow_control_monitor(
+ USRP2_SRAM_BYTES/_mbc[mb].tx_dsp_xport->get_send_frame_size()
+ )));
+ }
+
+ //allocate streamer weak ptrs containers
+ BOOST_FOREACH(const std::string &mb, _mbc.keys()){
+ _mbc[mb].rx_streamers.resize(_mbc[mb].rx_dsps.size());
+ _mbc[mb].tx_streamers.resize(1/*known to be 1 dsp*/);
+ }
+
+ //create a new pirate thread for each zc if (yarr!!)
+ size_t index = 0;
+ BOOST_FOREACH(const std::string &mb, _mbc.keys()){
+ //spawn a new pirate to plunder the recv booty
+ _io_impl->pirate_tasks.push_back(task::make(boost::bind(
+ &usrp2_impl::io_impl::recv_pirate_loop, _io_impl.get(),
+ _mbc[mb].tx_dsp_xport, index++
+ )));
+ }
+}
+
+void usrp2_impl::update_tick_rate(const double rate){
+ _io_impl->tick_rate = rate; //shadow for async msg
+
+ //update the tick rate on all existing streamers -> thread safe
+ BOOST_FOREACH(const std::string &mb, _mbc.keys()){
+ for (size_t i = 0; i < _mbc[mb].rx_streamers.size(); i++){
+ boost::shared_ptr<sph::recv_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_mbc[mb].rx_streamers[i].lock());
+ if (my_streamer.get() == NULL) continue;
+ my_streamer->set_tick_rate(rate);
+ }
+ for (size_t i = 0; i < _mbc[mb].tx_streamers.size(); i++){
+ boost::shared_ptr<sph::send_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<sph::send_packet_streamer>(_mbc[mb].tx_streamers[i].lock());
+ if (my_streamer.get() == NULL) continue;
+ my_streamer->set_tick_rate(rate);
+ }
+ }
+}
+
+void usrp2_impl::update_rx_samp_rate(const std::string &mb, const size_t dsp, const double rate){
+ boost::shared_ptr<sph::recv_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_mbc[mb].rx_streamers[dsp].lock());
+ if (my_streamer.get() == NULL) return;
+
+ my_streamer->set_samp_rate(rate);
+ const double adj = _mbc[mb].rx_dsps[dsp]->get_scaling_adjustment();
+ my_streamer->set_scale_factor(adj);
+}
+
+void usrp2_impl::update_tx_samp_rate(const std::string &mb, const size_t dsp, const double rate){
+ boost::shared_ptr<sph::send_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<sph::send_packet_streamer>(_mbc[mb].tx_streamers[dsp].lock());
+ if (my_streamer.get() == NULL) return;
+
+ my_streamer->set_samp_rate(rate);
+ const double adj = _mbc[mb].tx_dsp->get_scaling_adjustment();
+ my_streamer->set_scale_factor(adj);
+}
+
+void usrp2_impl::update_rates(void){
+ BOOST_FOREACH(const std::string &mb, _mbc.keys()){
+ fs_path root = "/mboards/" + mb;
+ _tree->access<double>(root / "tick_rate").update();
+
+ //and now that the tick rate is set, init the host rates to something
+ BOOST_FOREACH(const std::string &name, _tree->list(root / "rx_dsps")){
+ _tree->access<double>(root / "rx_dsps" / name / "rate" / "value").update();
+ }
+ BOOST_FOREACH(const std::string &name, _tree->list(root / "tx_dsps")){
+ _tree->access<double>(root / "tx_dsps" / name / "rate" / "value").update();
+ }
+ }
+}
+
+void usrp2_impl::update_rx_subdev_spec(const std::string &which_mb, const subdev_spec_t &spec){
+ fs_path root = "/mboards/" + which_mb + "/dboards";
+
+ //sanity checking
+ validate_subdev_spec(_tree, spec, "rx", which_mb);
+
+ //setup mux for this spec
+ bool fe_swapped = false;
+ for (size_t i = 0; i < spec.size(); i++){
+ const std::string conn = _tree->access<std::string>(root / spec[i].db_name / "rx_frontends" / spec[i].sd_name / "connection").get();
+ if (i == 0 and (conn == "QI" or conn == "Q")) fe_swapped = true;
+ _mbc[which_mb].rx_dsps[i]->set_mux(conn, fe_swapped);
+ }
+ _mbc[which_mb].rx_fe->set_mux(fe_swapped);
+
+ //compute the new occupancy and resize
+ _mbc[which_mb].rx_chan_occ = spec.size();
+ size_t nchan = 0;
+ BOOST_FOREACH(const std::string &mb, _mbc.keys()) nchan += _mbc[mb].rx_chan_occ;
+}
+
+void usrp2_impl::update_tx_subdev_spec(const std::string &which_mb, const subdev_spec_t &spec){
+ fs_path root = "/mboards/" + which_mb + "/dboards";
+
+ //sanity checking
+ validate_subdev_spec(_tree, spec, "tx", which_mb);
+
+ //set the mux for this spec
+ const std::string conn = _tree->access<std::string>(root / spec[0].db_name / "tx_frontends" / spec[0].sd_name / "connection").get();
+ _mbc[which_mb].tx_fe->set_mux(conn);
+
+ //compute the new occupancy and resize
+ _mbc[which_mb].tx_chan_occ = spec.size();
+ size_t nchan = 0;
+ BOOST_FOREACH(const std::string &mb, _mbc.keys()) nchan += _mbc[mb].tx_chan_occ;
+}
+
+/***********************************************************************
+ * Async Data
+ **********************************************************************/
+bool usrp2_impl::recv_async_msg(
+ async_metadata_t &async_metadata, double timeout
+){
+ boost::this_thread::disable_interruption di; //disable because the wait can throw
+ return _io_impl->async_msg_fifo.pop_with_timed_wait(async_metadata, timeout);
+}
+
+/***********************************************************************
+ * Receive streamer
+ **********************************************************************/
+rx_streamer::sptr usrp2_impl::get_rx_stream(const uhd::stream_args_t &args_){
+ stream_args_t args = args_;
+
+ //setup defaults for unspecified values
+ args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format;
+ args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels;
+
+ //calculate packet size
+ static const size_t hdr_size = 0
+ + vrt::max_if_hdr_words32*sizeof(boost::uint32_t)
+ + sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer
+ - sizeof(vrt::if_packet_info_t().cid) //no class id ever used
+ - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used
+ ;
+ const size_t bpp = _mbc[_mbc.keys().front()].rx_dsp_xports[0]->get_recv_frame_size() - hdr_size;
+ const size_t bpi = convert::get_bytes_per_item(args.otw_format);
+ const size_t spp = unsigned(args.args.cast<double>("spp", bpp/bpi));
+
+ //make the new streamer given the samples per packet
+ boost::shared_ptr<sph::recv_packet_streamer> my_streamer = boost::make_shared<sph::recv_packet_streamer>(spp);
+
+ //init some streamer stuff
+ my_streamer->resize(args.channels.size());
+ my_streamer->set_vrt_unpacker(&vrt::if_hdr_unpack_be);
+
+ //set the converter
+ uhd::convert::id_type id;
+ id.input_format = args.otw_format + "_item32_be";
+ id.num_inputs = 1;
+ id.output_format = args.cpu_format;
+ id.num_outputs = 1;
+ my_streamer->set_converter(id);
+
+ //bind callbacks for the handler
+ for (size_t chan_i = 0; chan_i < args.channels.size(); chan_i++){
+ const size_t chan = args.channels[chan_i];
+ size_t num_chan_so_far = 0;
+ BOOST_FOREACH(const std::string &mb, _mbc.keys()){
+ num_chan_so_far += _mbc[mb].rx_chan_occ;
+ if (chan < num_chan_so_far){
+ const size_t dsp = chan + _mbc[mb].rx_chan_occ - num_chan_so_far;
+ _mbc[mb].rx_dsps[dsp]->set_nsamps_per_packet(spp); //seems to be a good place to set this
+ _mbc[mb].rx_dsps[dsp]->setup(args);
+ my_streamer->set_xport_chan_get_buff(chan_i, boost::bind(
+ &zero_copy_if::get_recv_buff, _mbc[mb].rx_dsp_xports[dsp], _1
+ ), true /*flush*/);
+ _mbc[mb].rx_streamers[dsp] = my_streamer; //store weak pointer
+ break;
+ }
+ }
+ }
+
+ //set the packet threshold to be an entire socket buffer's worth
+ const size_t packets_per_sock_buff = size_t(50e6/_mbc[_mbc.keys().front()].rx_dsp_xports[0]->get_recv_frame_size());
+ my_streamer->set_alignment_failure_threshold(packets_per_sock_buff);
+
+ //sets all tick and samp rates on this streamer
+ this->update_rates();
+
+ return my_streamer;
+}
+
+/***********************************************************************
+ * Transmit streamer
+ **********************************************************************/
+tx_streamer::sptr usrp2_impl::get_tx_stream(const uhd::stream_args_t &args_){
+ stream_args_t args = args_;
+
+ //setup defaults for unspecified values
+ args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format;
+ args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels;
+
+ //calculate packet size
+ static const size_t hdr_size = 0
+ + vrt_send_header_offset_words32*sizeof(boost::uint32_t)
+ + vrt::max_if_hdr_words32*sizeof(boost::uint32_t)
+ + sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer
+ - sizeof(vrt::if_packet_info_t().cid) //no class id ever used
+ - sizeof(vrt::if_packet_info_t().sid) //no stream id ever used
+ - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used
+ ;
+ const size_t bpp = _mbc[_mbc.keys().front()].tx_dsp_xport->get_send_frame_size() - hdr_size;
+ const size_t spp = bpp/convert::get_bytes_per_item(args.otw_format);
+
+ //make the new streamer given the samples per packet
+ boost::shared_ptr<sph::send_packet_streamer> my_streamer = boost::make_shared<sph::send_packet_streamer>(spp);
+
+ //init some streamer stuff
+ my_streamer->resize(args.channels.size());
+ my_streamer->set_vrt_packer(&vrt::if_hdr_pack_be, vrt_send_header_offset_words32);
+
+ //set the converter
+ uhd::convert::id_type id;
+ id.input_format = args.cpu_format;
+ id.num_inputs = 1;
+ id.output_format = args.otw_format + "_item32_be";
+ id.num_outputs = 1;
+ my_streamer->set_converter(id);
+
+ //bind callbacks for the handler
+ for (size_t chan_i = 0; chan_i < args.channels.size(); chan_i++){
+ const size_t chan = args.channels[chan_i];
+ size_t num_chan_so_far = 0;
+ size_t abs = 0;
+ BOOST_FOREACH(const std::string &mb, _mbc.keys()){
+ num_chan_so_far += _mbc[mb].tx_chan_occ;
+ if (chan < num_chan_so_far){
+ const size_t dsp = chan + _mbc[mb].tx_chan_occ - num_chan_so_far;
+ if (not args.args.has_key("noclear")){
+ _io_impl->fc_mons[abs]->clear();
+ }
+ _mbc[mb].tx_dsp->setup(args);
+ my_streamer->set_xport_chan_get_buff(chan_i, boost::bind(
+ &usrp2_impl::io_impl::get_send_buff, _io_impl.get(), abs, _1
+ ));
+ _mbc[mb].tx_streamers[dsp] = my_streamer; //store weak pointer
+ break;
+ }
+ abs += 1; //assume 1 tx dsp
+ }
+ }
+
+ //sets all tick and samp rates on this streamer
+ this->update_rates();
+
+ return my_streamer;
+}
diff --git a/host/lib/usrp/usrp2/usrp2_clk_regs.hpp b/host/lib/usrp/usrp2/usrp2_clk_regs.hpp
new file mode 100644
index 000000000..8b185eac0
--- /dev/null
+++ b/host/lib/usrp/usrp2/usrp2_clk_regs.hpp
@@ -0,0 +1,87 @@
+//
+// Copyright 2010 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_USRP2_CLK_REGS_HPP
+#define INCLUDED_USRP2_CLK_REGS_HPP
+
+#include "usrp2_iface.hpp"
+
+class usrp2_clk_regs_t {
+public:
+ usrp2_clk_regs_t(void) { ; }
+ usrp2_clk_regs_t(usrp2_iface::rev_type rev) {
+ test = 0;
+ fpga = 1;
+ dac = 3;
+
+ switch(rev) {
+ case usrp2_iface::USRP2_REV3:
+ exp = 2;
+ adc = 4;
+ serdes = 2;
+ tx_db = 6;
+ break;
+ case usrp2_iface::USRP2_REV4:
+ exp = 5;
+ adc = 4;
+ serdes = 2;
+ tx_db = 6;
+ break;
+ case usrp2_iface::USRP_N200:
+ case usrp2_iface::USRP_N210:
+ case usrp2_iface::USRP_N200_R4:
+ case usrp2_iface::USRP_N210_R4:
+ exp = 6;
+ adc = 2;
+ serdes = 4;
+ tx_db = 5;
+ break;
+ case usrp2_iface::USRP_NXXX:
+ //dont throw, it may be unitialized
+ break;
+ }
+
+ rx_db = 7;
+ }
+
+ static int output(int clknum) { return 0x3C + clknum; }
+ static int div_lo(int clknum) { return 0x48 + 2 * clknum; }
+ static int div_hi(int clknum) { return 0x49 + 2 * clknum; }
+
+ const static int acounter = 0x04;
+ const static int bcounter_msb = 0x05;
+ const static int bcounter_lsb = 0x06;
+ const static int pll_1 = 0x07;
+ const static int pll_2 = 0x08;
+ const static int pll_3 = 0x09;
+ const static int pll_4 = 0x0A;
+ const static int ref_counter_msb = 0x0B;
+ const static int ref_counter_lsb = 0x0C;
+ const static int pll_5 = 0x0D;
+ const static int update = 0x5A;
+
+ int test;
+ int fpga;
+ int adc;
+ int dac;
+ int serdes;
+ int exp;
+ int tx_db;
+ int rx_db;
+};
+
+#endif //INCLUDED_USRP2_CLK_REGS_HPP
diff --git a/host/lib/usrp/usrp2/usrp2_iface.cpp b/host/lib/usrp/usrp2/usrp2_iface.cpp
new file mode 100644
index 000000000..123910166
--- /dev/null
+++ b/host/lib/usrp/usrp2/usrp2_iface.cpp
@@ -0,0 +1,378 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "usrp2_regs.hpp"
+#include "fw_common.h"
+#include "usrp2_iface.hpp"
+#include <uhd/exception.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/tasks.hpp>
+#include <uhd/utils/safe_call.hpp>
+#include <uhd/types/dict.hpp>
+#include <boost/thread.hpp>
+#include <boost/foreach.hpp>
+#include <boost/asio.hpp> //used for htonl and ntohl
+#include <boost/assign/list_of.hpp>
+#include <boost/format.hpp>
+#include <boost/bind.hpp>
+#include <boost/tokenizer.hpp>
+#include <boost/functional/hash.hpp>
+#include <algorithm>
+#include <iostream>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+
+static const double CTRL_RECV_TIMEOUT = 1.0;
+static const size_t CTRL_RECV_RETRIES = 3;
+
+//custom timeout error for retry logic to catch/retry
+struct timeout_error : uhd::runtime_error
+{
+ timeout_error(const std::string &what):
+ uhd::runtime_error(what)
+ {
+ //NOP
+ }
+};
+
+static const boost::uint32_t MIN_PROTO_COMPAT_SPI = 7;
+static const boost::uint32_t MIN_PROTO_COMPAT_I2C = 7;
+// The register compat number must reflect the protocol compatibility
+// and the compatibility of the register mapping (more likely to change).
+static const boost::uint32_t MIN_PROTO_COMPAT_REG = 10;
+static const boost::uint32_t MIN_PROTO_COMPAT_UART = 7;
+
+//Define get_gpid() to get a globally unique identifier for this process.
+//The gpid is implemented as a hash of the pid and a unique machine identifier.
+#ifdef UHD_PLATFORM_WIN32
+#include <Windows.h>
+static inline size_t get_gpid(void){
+ //extract volume serial number
+ char szVolName[MAX_PATH+1], szFileSysName[MAX_PATH+1];
+ DWORD dwSerialNumber, dwMaxComponentLen, dwFileSysFlags;
+ GetVolumeInformation("C:\\", szVolName, MAX_PATH,
+ &dwSerialNumber, &dwMaxComponentLen,
+ &dwFileSysFlags, szFileSysName, sizeof(szFileSysName));
+
+ size_t hash = 0;
+ boost::hash_combine(hash, GetCurrentProcessId());
+ boost::hash_combine(hash, dwSerialNumber);
+ return hash;
+}
+#else
+#include <unistd.h>
+static inline size_t get_gpid(void){
+ size_t hash = 0;
+ boost::hash_combine(hash, getpid());
+ boost::hash_combine(hash, gethostid());
+ return hash;
+}
+#endif
+
+class usrp2_iface_impl : public usrp2_iface{
+public:
+/***********************************************************************
+ * Structors
+ **********************************************************************/
+ usrp2_iface_impl(udp_simple::sptr ctrl_transport):
+ _ctrl_transport(ctrl_transport),
+ _ctrl_seq_num(0),
+ _protocol_compat(0) //initialized below...
+ {
+ //Obtain the firmware's compat number.
+ //Save the response compat number for communication.
+ //TODO can choose to reject certain older compat numbers
+ usrp2_ctrl_data_t ctrl_data = usrp2_ctrl_data_t();
+ ctrl_data.id = htonl(USRP2_CTRL_ID_WAZZUP_BRO);
+ ctrl_data = ctrl_send_and_recv(ctrl_data, 0, ~0);
+ if (ntohl(ctrl_data.id) != USRP2_CTRL_ID_WAZZUP_DUDE)
+ throw uhd::runtime_error("firmware not responding");
+ _protocol_compat = ntohl(ctrl_data.proto_ver);
+
+ mb_eeprom = mboard_eeprom_t(*this, mboard_eeprom_t::MAP_N100);
+ }
+
+ ~usrp2_iface_impl(void){UHD_SAFE_CALL(
+ this->lock_device(false);
+ )}
+
+/***********************************************************************
+ * Device locking
+ **********************************************************************/
+
+ void lock_device(bool lock){
+ if (lock){
+ this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_POKE32>(U2_FW_REG_LOCK_GPID, boost::uint32_t(get_gpid()));
+ _lock_task = task::make(boost::bind(&usrp2_iface_impl::lock_task, this));
+ }
+ else{
+ _lock_task.reset(); //shutdown the task
+ this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_POKE32>(U2_FW_REG_LOCK_TIME, 0); //unlock
+ }
+ }
+
+ bool is_device_locked(void){
+ //never assume lock with fpga image mismatch
+ if ((this->peek32(U2_REG_COMPAT_NUM_RB) >> 16) != USRP2_FPGA_COMPAT_NUM) return false;
+
+ boost::uint32_t lock_time = this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_PEEK32>(U2_FW_REG_LOCK_TIME);
+ boost::uint32_t lock_gpid = this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_PEEK32>(U2_FW_REG_LOCK_GPID);
+
+ //may not be the right tick rate, but this is ok for locking purposes
+ const boost::uint32_t lock_timeout_time = boost::uint32_t(3*100e6);
+
+ //if the difference is larger, assume not locked anymore
+ if ((lock_time & 1) == 0) return false; //bit0 says unlocked
+ const boost::uint32_t time_diff = this->get_curr_time() - lock_time;
+ if (time_diff >= lock_timeout_time) return false;
+
+ //otherwise only lock if the device hash is different that ours
+ return lock_gpid != boost::uint32_t(get_gpid());
+ }
+
+ void lock_task(void){
+ //re-lock in task
+ this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_POKE32>(U2_FW_REG_LOCK_TIME, this->get_curr_time());
+ //sleep for a bit
+ boost::this_thread::sleep(boost::posix_time::milliseconds(1500));
+ }
+
+ boost::uint32_t get_curr_time(void){
+ return this->peek32(U2_REG_TIME64_LO_RB_IMM) | 1; //bit 1 says locked
+ }
+
+/***********************************************************************
+ * Peek and Poke
+ **********************************************************************/
+ void poke32(wb_addr_type addr, boost::uint32_t data){
+ this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FPGA_POKE32>(addr, data);
+ }
+
+ boost::uint32_t peek32(wb_addr_type addr){
+ return this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FPGA_PEEK32>(addr);
+ }
+
+ void poke16(wb_addr_type addr, boost::uint16_t data){
+ this->get_reg<boost::uint16_t, USRP2_REG_ACTION_FPGA_POKE16>(addr, data);
+ }
+
+ boost::uint16_t peek16(wb_addr_type addr){
+ return this->get_reg<boost::uint16_t, USRP2_REG_ACTION_FPGA_PEEK16>(addr);
+ }
+
+ template <class T, usrp2_reg_action_t action>
+ T get_reg(wb_addr_type addr, T data = 0){
+ //setup the out data
+ usrp2_ctrl_data_t out_data = usrp2_ctrl_data_t();
+ out_data.id = htonl(USRP2_CTRL_ID_GET_THIS_REGISTER_FOR_ME_BRO);
+ out_data.data.reg_args.addr = htonl(addr);
+ out_data.data.reg_args.data = htonl(boost::uint32_t(data));
+ out_data.data.reg_args.action = action;
+
+ //send and recv
+ usrp2_ctrl_data_t in_data = this->ctrl_send_and_recv(out_data, MIN_PROTO_COMPAT_REG);
+ UHD_ASSERT_THROW(ntohl(in_data.id) == USRP2_CTRL_ID_OMG_GOT_REGISTER_SO_BAD_DUDE);
+ return T(ntohl(in_data.data.reg_args.data));
+ }
+
+/***********************************************************************
+ * SPI
+ **********************************************************************/
+ boost::uint32_t transact_spi(
+ int which_slave,
+ const spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits,
+ bool readback
+ ){
+ static const uhd::dict<spi_config_t::edge_t, int> spi_edge_to_otw = boost::assign::map_list_of
+ (spi_config_t::EDGE_RISE, USRP2_CLK_EDGE_RISE)
+ (spi_config_t::EDGE_FALL, USRP2_CLK_EDGE_FALL)
+ ;
+
+ //setup the out data
+ usrp2_ctrl_data_t out_data = usrp2_ctrl_data_t();
+ out_data.id = htonl(USRP2_CTRL_ID_TRANSACT_ME_SOME_SPI_BRO);
+ out_data.data.spi_args.dev = htonl(which_slave);
+ out_data.data.spi_args.miso_edge = spi_edge_to_otw[config.miso_edge];
+ out_data.data.spi_args.mosi_edge = spi_edge_to_otw[config.mosi_edge];
+ out_data.data.spi_args.readback = (readback)? 1 : 0;
+ out_data.data.spi_args.num_bits = num_bits;
+ out_data.data.spi_args.data = htonl(data);
+
+ //send and recv
+ usrp2_ctrl_data_t in_data = this->ctrl_send_and_recv(out_data, MIN_PROTO_COMPAT_SPI);
+ UHD_ASSERT_THROW(ntohl(in_data.id) == USRP2_CTRL_ID_OMG_TRANSACTED_SPI_DUDE);
+
+ return ntohl(in_data.data.spi_args.data);
+ }
+
+/***********************************************************************
+ * I2C
+ **********************************************************************/
+ void write_i2c(boost::uint8_t addr, const byte_vector_t &buf){
+ //setup the out data
+ usrp2_ctrl_data_t out_data = usrp2_ctrl_data_t();
+ out_data.id = htonl(USRP2_CTRL_ID_WRITE_THESE_I2C_VALUES_BRO);
+ out_data.data.i2c_args.addr = addr;
+ out_data.data.i2c_args.bytes = buf.size();
+
+ //limitation of i2c transaction size
+ UHD_ASSERT_THROW(buf.size() <= sizeof(out_data.data.i2c_args.data));
+
+ //copy in the data
+ std::copy(buf.begin(), buf.end(), out_data.data.i2c_args.data);
+
+ //send and recv
+ usrp2_ctrl_data_t in_data = this->ctrl_send_and_recv(out_data, MIN_PROTO_COMPAT_I2C);
+ UHD_ASSERT_THROW(ntohl(in_data.id) == USRP2_CTRL_ID_COOL_IM_DONE_I2C_WRITE_DUDE);
+ }
+
+ byte_vector_t read_i2c(boost::uint8_t addr, size_t num_bytes){
+ //setup the out data
+ usrp2_ctrl_data_t out_data = usrp2_ctrl_data_t();
+ out_data.id = htonl(USRP2_CTRL_ID_DO_AN_I2C_READ_FOR_ME_BRO);
+ out_data.data.i2c_args.addr = addr;
+ out_data.data.i2c_args.bytes = num_bytes;
+
+ //limitation of i2c transaction size
+ UHD_ASSERT_THROW(num_bytes <= sizeof(out_data.data.i2c_args.data));
+
+ //send and recv
+ usrp2_ctrl_data_t in_data = this->ctrl_send_and_recv(out_data, MIN_PROTO_COMPAT_I2C);
+ UHD_ASSERT_THROW(ntohl(in_data.id) == USRP2_CTRL_ID_HERES_THE_I2C_DATA_DUDE);
+ UHD_ASSERT_THROW(in_data.data.i2c_args.addr = num_bytes);
+
+ //copy out the data
+ byte_vector_t result(num_bytes);
+ std::copy(in_data.data.i2c_args.data, in_data.data.i2c_args.data + num_bytes, result.begin());
+ return result;
+ }
+
+/***********************************************************************
+ * Send/Recv over control
+ **********************************************************************/
+ usrp2_ctrl_data_t ctrl_send_and_recv(
+ const usrp2_ctrl_data_t &out_data,
+ boost::uint32_t lo = USRP2_FW_COMPAT_NUM,
+ boost::uint32_t hi = USRP2_FW_COMPAT_NUM
+ ){
+ boost::mutex::scoped_lock lock(_ctrl_mutex);
+
+ for (size_t i = 0; i < CTRL_RECV_RETRIES; i++){
+ try{
+ return ctrl_send_and_recv_internal(out_data, lo, hi, CTRL_RECV_TIMEOUT/CTRL_RECV_RETRIES);
+ }
+ catch(const timeout_error &e){
+ UHD_MSG(error)
+ << "Control packet attempt " << i
+ << ", sequence number " << _ctrl_seq_num
+ << ":\n" << e.what() << std::endl;
+ }
+ }
+ throw uhd::runtime_error("link dead: timeout waiting for control packet ACK");
+ }
+
+ usrp2_ctrl_data_t ctrl_send_and_recv_internal(
+ const usrp2_ctrl_data_t &out_data,
+ boost::uint32_t lo, boost::uint32_t hi,
+ const double timeout
+ ){
+ //fill in the seq number and send
+ usrp2_ctrl_data_t out_copy = out_data;
+ out_copy.proto_ver = htonl(_protocol_compat);
+ out_copy.seq = htonl(++_ctrl_seq_num);
+ _ctrl_transport->send(boost::asio::buffer(&out_copy, sizeof(usrp2_ctrl_data_t)));
+
+ //loop until we get the packet or timeout
+ boost::uint8_t usrp2_ctrl_data_in_mem[udp_simple::mtu]; //allocate max bytes for recv
+ const usrp2_ctrl_data_t *ctrl_data_in = reinterpret_cast<const usrp2_ctrl_data_t *>(usrp2_ctrl_data_in_mem);
+ while(true){
+ size_t len = _ctrl_transport->recv(boost::asio::buffer(usrp2_ctrl_data_in_mem), timeout);
+ boost::uint32_t compat = ntohl(ctrl_data_in->proto_ver);
+ if(len >= sizeof(boost::uint32_t) and (hi < compat or lo > compat)){
+ throw uhd::runtime_error(str(boost::format(
+ "\nPlease update the firmware and FPGA images for your device.\n"
+ "See the application notes for USRP2/N-Series for instructions.\n"
+ "Expected protocol compatibility number %s, but got %d:\n"
+ "The firmware build is not compatible with the host code build."
+ ) % ((lo == hi)? (boost::format("%d") % hi) : (boost::format("[%d to %d]") % lo % hi)) % compat));
+ }
+ if (len >= sizeof(usrp2_ctrl_data_t) and ntohl(ctrl_data_in->seq) == _ctrl_seq_num){
+ return *ctrl_data_in;
+ }
+ if (len == 0) break; //timeout
+ //didnt get seq or bad packet, continue looking...
+ }
+ throw timeout_error("no control response, possible packet loss");
+ }
+
+ rev_type get_rev(void){
+ std::string hw = mb_eeprom["hardware"];
+ if (hw.empty()) return USRP_NXXX;
+ switch (boost::lexical_cast<boost::uint16_t>(hw)){
+ case 0x0300:
+ case 0x0301: return USRP2_REV3;
+ case 0x0400: return USRP2_REV4;
+ case 0x0A00: return USRP_N200;
+ case 0x0A01: return USRP_N210;
+ case 0x0A10: return USRP_N200_R4;
+ case 0x0A11: return USRP_N210_R4;
+ }
+ return USRP_NXXX; //unknown type
+ }
+
+ const std::string get_cname(void){
+ switch(this->get_rev()){
+ case USRP2_REV3: return "USRP2-REV3";
+ case USRP2_REV4: return "USRP2-REV4";
+ case USRP_N200: return "USRP-N200";
+ case USRP_N210: return "USRP-N210";
+ case USRP_N200_R4: return "USRP-N200-REV4";
+ case USRP_N210_R4: return "USRP-N210-REV4";
+ case USRP_NXXX: return "USRP-N???";
+ }
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+
+ const std::string get_fw_version_string(void){
+ boost::uint32_t minor = this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_PEEK32>(U2_FW_REG_VER_MINOR);
+ return str(boost::format("%u.%u") % _protocol_compat % minor);
+ }
+
+private:
+ //this lovely lady makes it all possible
+ udp_simple::sptr _ctrl_transport;
+
+ //used in send/recv
+ boost::mutex _ctrl_mutex;
+ boost::uint32_t _ctrl_seq_num;
+ boost::uint32_t _protocol_compat;
+
+ //lock thread stuff
+ task::sptr _lock_task;
+};
+
+/***********************************************************************
+ * Public make function for usrp2 interface
+ **********************************************************************/
+usrp2_iface::sptr usrp2_iface::make(udp_simple::sptr ctrl_transport){
+ return usrp2_iface::sptr(new usrp2_iface_impl(ctrl_transport));
+}
+
diff --git a/host/lib/usrp/usrp2/usrp2_iface.hpp b/host/lib/usrp/usrp2/usrp2_iface.hpp
new file mode 100644
index 000000000..9aa1a16aa
--- /dev/null
+++ b/host/lib/usrp/usrp2/usrp2_iface.hpp
@@ -0,0 +1,76 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_USRP2_IFACE_HPP
+#define INCLUDED_USRP2_IFACE_HPP
+
+#include <uhd/transport/udp_simple.hpp>
+#include <uhd/types/serial.hpp>
+#include <uhd/usrp/mboard_eeprom.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+#include <boost/function.hpp>
+#include "usrp2_regs.hpp"
+#include "wb_iface.hpp"
+#include <string>
+
+/*!
+ * The usrp2 interface class:
+ * Provides a set of functions to implementation layer.
+ * Including spi, peek, poke, control...
+ */
+class usrp2_iface : public wb_iface, public uhd::spi_iface, public uhd::i2c_iface{
+public:
+ typedef boost::shared_ptr<usrp2_iface> sptr;
+ /*!
+ * Make a new usrp2 interface with the control transport.
+ * \param ctrl_transport the udp transport object
+ * \return a new usrp2 interface object
+ */
+ static sptr make(uhd::transport::udp_simple::sptr ctrl_transport);
+
+ //! The list of possible revision types
+ enum rev_type {
+ USRP2_REV3 = 3,
+ USRP2_REV4 = 4,
+ USRP_N200 = 200,
+ USRP_N200_R4 = 201,
+ USRP_N210 = 210,
+ USRP_N210_R4 = 211,
+ USRP_NXXX = 0
+ };
+
+ //! Get the revision type for this device
+ virtual rev_type get_rev(void) = 0;
+
+ //! Get the canonical name for this device
+ virtual const std::string get_cname(void) = 0;
+
+ //! Lock the device to this iface
+ virtual void lock_device(bool lock) = 0;
+
+ //! Is this device locked?
+ virtual bool is_device_locked(void) = 0;
+
+ //! A version string for firmware
+ virtual const std::string get_fw_version_string(void) = 0;
+
+ //motherboard eeprom map structure
+ uhd::usrp::mboard_eeprom_t mb_eeprom;
+};
+
+#endif /* INCLUDED_USRP2_IFACE_HPP */
diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp
new file mode 100644
index 000000000..ddae34b53
--- /dev/null
+++ b/host/lib/usrp/usrp2/usrp2_impl.cpp
@@ -0,0 +1,763 @@
+//
+// Copyright 2010-2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "usrp2_impl.hpp"
+#include "fw_common.h"
+#include "apply_corrections.hpp"
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/transport/if_addrs.hpp>
+#include <uhd/transport/udp_zero_copy.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/utils/static.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/safe_call.hpp>
+#include <boost/format.hpp>
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/bind.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/asio/ip/address_v4.hpp>
+#include <boost/asio.hpp> //used for htonl and ntohl
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+namespace asio = boost::asio;
+
+/***********************************************************************
+ * Discovery over the udp transport
+ **********************************************************************/
+static 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){
+ device_addrs_t found_devices;
+ BOOST_FOREACH(const device_addr_t &hint_i, hints){
+ device_addrs_t found_devices_i = usrp2_find(hint_i);
+ if (found_devices_i.size() != 1) throw uhd::value_error(str(boost::format(
+ "Could not resolve device hint \"%s\" to a single device."
+ ) % hint_i.to_string()));
+ found_devices.push_back(found_devices_i[0]);
+ }
+ return device_addrs_t(1, combine_device_addrs(found_devices));
+ }
+
+ //initialize the hint for a single device case
+ UHD_ASSERT_THROW(hints.size() <= 1);
+ hints.resize(1); //in case it was empty
+ device_addr_t hint = hints[0];
+ device_addrs_t usrp2_addrs;
+
+ //return an empty list of addresses when type is set to non-usrp2
+ if (hint.has_key("type") and hint["type"] != "usrp2") return usrp2_addrs;
+
+ //if no address was specified, send a broadcast on each interface
+ if (not hint.has_key("addr")){
+ BOOST_FOREACH(const if_addrs_t &if_addrs, get_if_addrs()){
+ //avoid the loopback device
+ if (if_addrs.inet == asio::ip::address_v4::loopback().to_string()) continue;
+
+ //create a new hint with this broadcast address
+ device_addr_t new_hint = hint;
+ new_hint["addr"] = if_addrs.bcast;
+
+ //call discover with the new hint and append results
+ device_addrs_t new_usrp2_addrs = usrp2_find(new_hint);
+ usrp2_addrs.insert(usrp2_addrs.begin(),
+ new_usrp2_addrs.begin(), new_usrp2_addrs.end()
+ );
+ }
+ return usrp2_addrs;
+ }
+
+ //Create a UDP transport to communicate:
+ //Some devices will cause a throw when opened for a broadcast address.
+ //We print and recover so the caller can loop through all bcast addrs.
+ udp_simple::sptr udp_transport;
+ try{
+ udp_transport = udp_simple::make_broadcast(hint["addr"], BOOST_STRINGIZE(USRP2_UDP_CTRL_PORT));
+ }
+ catch(const std::exception &e){
+ UHD_MSG(error) << boost::format("Cannot open UDP transport on %s\n%s") % hint["addr"] % e.what() << std::endl;
+ return usrp2_addrs; //dont throw, but return empty address so caller can insert
+ }
+
+ //send a hello control packet
+ usrp2_ctrl_data_t ctrl_data_out = usrp2_ctrl_data_t();
+ ctrl_data_out.proto_ver = uhd::htonx<boost::uint32_t>(USRP2_FW_COMPAT_NUM);
+ ctrl_data_out.id = uhd::htonx<boost::uint32_t>(USRP2_CTRL_ID_WAZZUP_BRO);
+ udp_transport->send(boost::asio::buffer(&ctrl_data_out, sizeof(ctrl_data_out)));
+
+ //loop and recieve until the timeout
+ boost::uint8_t usrp2_ctrl_data_in_mem[udp_simple::mtu]; //allocate max bytes for recv
+ const usrp2_ctrl_data_t *ctrl_data_in = reinterpret_cast<const usrp2_ctrl_data_t *>(usrp2_ctrl_data_in_mem);
+ while(true){
+ size_t len = udp_transport->recv(asio::buffer(usrp2_ctrl_data_in_mem));
+ if (len > offsetof(usrp2_ctrl_data_t, data) and ntohl(ctrl_data_in->id) == USRP2_CTRL_ID_WAZZUP_DUDE){
+
+ //make a boost asio ipv4 with the raw addr in host byte order
+ device_addr_t new_addr;
+ new_addr["type"] = "usrp2";
+ //We used to get the address from the control packet.
+ //Now now uses the socket itself to yield the address.
+ //boost::asio::ip::address_v4 ip_addr(ntohl(ctrl_data_in->data.ip_addr));
+ //new_addr["addr"] = ip_addr.to_string();
+ new_addr["addr"] = udp_transport->get_recv_addr();
+
+ //Attempt to read the name from the EEPROM and perform filtering.
+ //This operation can throw due to compatibility mismatch.
+ try{
+ usrp2_iface::sptr iface = usrp2_iface::make(udp_simple::make_connected(
+ new_addr["addr"], BOOST_STRINGIZE(USRP2_UDP_CTRL_PORT)
+ ));
+ if (iface->is_device_locked()) continue; //ignore locked devices
+ mboard_eeprom_t mb_eeprom = iface->mb_eeprom;
+ new_addr["name"] = mb_eeprom["name"];
+ new_addr["serial"] = mb_eeprom["serial"];
+ }
+ catch(const std::exception &){
+ //set these values as empty string so the device may still be found
+ //and the filter's below can still operate on the discovered device
+ new_addr["name"] = "";
+ new_addr["serial"] = "";
+ }
+
+ //filter the discovered device below by matching optional keys
+ if (
+ (not hint.has_key("name") or hint["name"] == new_addr["name"]) and
+ (not hint.has_key("serial") or hint["serial"] == new_addr["serial"])
+ ){
+ usrp2_addrs.push_back(new_addr);
+ }
+
+ //dont break here, it will exit the while loop
+ //just continue on to the next loop iteration
+ }
+ if (len == 0) break; //timeout
+ }
+
+ return usrp2_addrs;
+}
+
+/***********************************************************************
+ * Make
+ **********************************************************************/
+static device::sptr usrp2_make(const device_addr_t &device_addr){
+ return device::sptr(new usrp2_impl(device_addr));
+}
+
+UHD_STATIC_BLOCK(register_usrp2_device){
+ device::register_device(&usrp2_find, &usrp2_make);
+}
+
+/***********************************************************************
+ * MTU Discovery
+ **********************************************************************/
+struct mtu_result_t{
+ size_t recv_mtu, send_mtu;
+};
+
+static mtu_result_t determine_mtu(const std::string &addr, const mtu_result_t &user_mtu){
+ udp_simple::sptr udp_sock = udp_simple::make_connected(
+ addr, BOOST_STRINGIZE(USRP2_UDP_CTRL_PORT)
+ );
+
+ //The FPGA offers 4K buffers, and the user may manually request this.
+ //However, multiple simultaneous receives (2DSP slave + 2DSP master),
+ //require that buffering to be used internally, and this is a safe setting.
+ std::vector<boost::uint8_t> buffer(std::max(user_mtu.recv_mtu, user_mtu.send_mtu));
+ usrp2_ctrl_data_t *ctrl_data = reinterpret_cast<usrp2_ctrl_data_t *>(&buffer.front());
+ static const double echo_timeout = 0.020; //20 ms
+
+ //test holler - check if its supported in this fw version
+ ctrl_data->id = htonl(USRP2_CTRL_ID_HOLLER_AT_ME_BRO);
+ ctrl_data->proto_ver = htonl(USRP2_FW_COMPAT_NUM);
+ ctrl_data->data.echo_args.len = htonl(sizeof(usrp2_ctrl_data_t));
+ udp_sock->send(boost::asio::buffer(buffer, sizeof(usrp2_ctrl_data_t)));
+ udp_sock->recv(boost::asio::buffer(buffer), echo_timeout);
+ if (ntohl(ctrl_data->id) != USRP2_CTRL_ID_HOLLER_BACK_DUDE)
+ throw uhd::not_implemented_error("holler protocol not implemented");
+
+ size_t min_recv_mtu = sizeof(usrp2_ctrl_data_t), max_recv_mtu = user_mtu.recv_mtu;
+ size_t min_send_mtu = sizeof(usrp2_ctrl_data_t), max_send_mtu = user_mtu.send_mtu;
+
+ while (min_recv_mtu < max_recv_mtu){
+
+ size_t test_mtu = (max_recv_mtu/2 + min_recv_mtu/2 + 3) & ~3;
+
+ ctrl_data->id = htonl(USRP2_CTRL_ID_HOLLER_AT_ME_BRO);
+ ctrl_data->proto_ver = htonl(USRP2_FW_COMPAT_NUM);
+ ctrl_data->data.echo_args.len = htonl(test_mtu);
+ udp_sock->send(boost::asio::buffer(buffer, sizeof(usrp2_ctrl_data_t)));
+
+ size_t len = udp_sock->recv(boost::asio::buffer(buffer), echo_timeout);
+
+ if (len >= test_mtu) min_recv_mtu = test_mtu;
+ else max_recv_mtu = test_mtu - 4;
+
+ }
+
+ while (min_send_mtu < max_send_mtu){
+
+ size_t test_mtu = (max_send_mtu/2 + min_send_mtu/2 + 3) & ~3;
+
+ ctrl_data->id = htonl(USRP2_CTRL_ID_HOLLER_AT_ME_BRO);
+ ctrl_data->proto_ver = htonl(USRP2_FW_COMPAT_NUM);
+ ctrl_data->data.echo_args.len = htonl(sizeof(usrp2_ctrl_data_t));
+ udp_sock->send(boost::asio::buffer(buffer, test_mtu));
+
+ size_t len = udp_sock->recv(boost::asio::buffer(buffer), echo_timeout);
+ if (len >= sizeof(usrp2_ctrl_data_t)) len = ntohl(ctrl_data->data.echo_args.len);
+
+ if (len >= test_mtu) min_send_mtu = test_mtu;
+ else max_send_mtu = test_mtu - 4;
+ }
+
+ mtu_result_t mtu;
+ mtu.recv_mtu = min_recv_mtu;
+ mtu.send_mtu = min_send_mtu;
+ return mtu;
+}
+
+/***********************************************************************
+ * Helpers
+ **********************************************************************/
+static zero_copy_if::sptr make_xport(
+ const std::string &addr,
+ const std::string &port,
+ const device_addr_t &hints,
+ const std::string &filter
+){
+
+ //only copy hints that contain the filter word
+ device_addr_t filtered_hints;
+ BOOST_FOREACH(const std::string &key, hints.keys()){
+ if (key.find(filter) == std::string::npos) continue;
+ filtered_hints[key] = hints[key];
+ }
+
+ //make the transport object with the filtered hints
+ zero_copy_if::sptr xport = udp_zero_copy::make(addr, port, filtered_hints);
+
+ //Send a small data packet so the usrp2 knows the udp source port.
+ //This setup must happen before further initialization occurs
+ //or the async update packets will cause ICMP destination unreachable.
+ static const boost::uint32_t data[2] = {
+ uhd::htonx(boost::uint32_t(0 /* don't care seq num */)),
+ uhd::htonx(boost::uint32_t(USRP2_INVALID_VRT_HEADER))
+ };
+ transport::managed_send_buffer::sptr send_buff = xport->get_send_buff();
+ std::memcpy(send_buff->cast<void*>(), &data, sizeof(data));
+ send_buff->commit(sizeof(data));
+
+ return xport;
+}
+
+/***********************************************************************
+ * Structors
+ **********************************************************************/
+usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){
+ UHD_MSG(status) << "Opening a USRP2/N-Series device..." << std::endl;
+ device_addr_t device_addr = _device_addr;
+
+ //setup the dsp transport hints (default to a large recv buff)
+ if (not device_addr.has_key("recv_buff_size")){
+ #if defined(UHD_PLATFORM_MACOS) || defined(UHD_PLATFORM_BSD)
+ //limit buffer resize on macos or it will error
+ device_addr["recv_buff_size"] = "1e6";
+ #elif defined(UHD_PLATFORM_LINUX) || defined(UHD_PLATFORM_WIN32)
+ //set to half-a-second of buffering at max rate
+ device_addr["recv_buff_size"] = "50e6";
+ #endif
+ }
+ if (not device_addr.has_key("send_buff_size")){
+ //The buffer should be the size of the SRAM on the device,
+ //because we will never commit more than the SRAM can hold.
+ device_addr["send_buff_size"] = boost::lexical_cast<std::string>(USRP2_SRAM_BYTES);
+ }
+
+ device_addrs_t device_args = separate_device_addr(device_addr);
+
+ //extract the user's requested MTU size or default
+ mtu_result_t user_mtu;
+ user_mtu.recv_mtu = size_t(device_addr.cast<double>("recv_frame_size", udp_simple::mtu));
+ user_mtu.send_mtu = size_t(device_addr.cast<double>("send_frame_size", udp_simple::mtu));
+
+ try{
+ //calculate the minimum send and recv mtu of all devices
+ mtu_result_t mtu = determine_mtu(device_args[0]["addr"], user_mtu);
+ for (size_t i = 1; i < device_args.size(); i++){
+ mtu_result_t mtu_i = determine_mtu(device_args[i]["addr"], user_mtu);
+ mtu.recv_mtu = std::min(mtu.recv_mtu, mtu_i.recv_mtu);
+ mtu.send_mtu = std::min(mtu.send_mtu, mtu_i.send_mtu);
+ }
+
+ device_addr["recv_frame_size"] = boost::lexical_cast<std::string>(mtu.recv_mtu);
+ device_addr["send_frame_size"] = boost::lexical_cast<std::string>(mtu.send_mtu);
+
+ UHD_MSG(status) << boost::format("Current recv frame size: %d bytes") % mtu.recv_mtu << std::endl;
+ UHD_MSG(status) << boost::format("Current send frame size: %d bytes") % mtu.send_mtu << std::endl;
+ }
+ catch(const uhd::not_implemented_error &){
+ //just ignore this error, makes older fw work...
+ }
+
+ device_args = separate_device_addr(device_addr); //update args for new frame sizes
+
+ ////////////////////////////////////////////////////////////////////
+ // create controller objects and initialize the properties tree
+ ////////////////////////////////////////////////////////////////////
+ _tree = property_tree::make();
+ _tree->create<std::string>("/name").set("USRP2 / N-Series Device");
+
+ for (size_t mbi = 0; mbi < device_args.size(); mbi++){
+ const device_addr_t device_args_i = device_args[mbi];
+ const std::string mb = boost::lexical_cast<std::string>(mbi);
+ const std::string addr = device_args_i["addr"];
+ const fs_path mb_path = "/mboards/" + mb;
+
+ ////////////////////////////////////////////////////////////////
+ // create the iface that controls i2c, spi, uart, and wb
+ ////////////////////////////////////////////////////////////////
+ _mbc[mb].iface = usrp2_iface::make(udp_simple::make_connected(
+ addr, BOOST_STRINGIZE(USRP2_UDP_CTRL_PORT)
+ ));
+ _tree->create<std::string>(mb_path / "name").set(_mbc[mb].iface->get_cname());
+ _tree->create<std::string>(mb_path / "fw_version").set(_mbc[mb].iface->get_fw_version_string());
+
+ //check the fpga compatibility number
+ const boost::uint32_t fpga_compat_num = _mbc[mb].iface->peek32(U2_REG_COMPAT_NUM_RB);
+ boost::uint16_t fpga_major = fpga_compat_num >> 16, fpga_minor = fpga_compat_num & 0xffff;
+ if (fpga_major == 0){ //old version scheme
+ fpga_major = fpga_minor;
+ fpga_minor = 0;
+ }
+ if (fpga_major != USRP2_FPGA_COMPAT_NUM){
+ throw uhd::runtime_error(str(boost::format(
+ "\nPlease update the firmware and FPGA images for your device.\n"
+ "See the application notes for USRP2/N-Series for instructions.\n"
+ "Expected FPGA compatibility number %d, but got %d:\n"
+ "The FPGA build is not compatible with the host code build."
+ ) % int(USRP2_FPGA_COMPAT_NUM) % fpga_major));
+ }
+ _tree->create<std::string>(mb_path / "fpga_version").set(str(boost::format("%u.%u") % fpga_major % fpga_minor));
+
+ //lock the device/motherboard to this process
+ _mbc[mb].iface->lock_device(true);
+
+ ////////////////////////////////////////////////////////////////
+ // construct transports for RX and TX DSPs
+ ////////////////////////////////////////////////////////////////
+ UHD_LOG << "Making transport for RX DSP0..." << std::endl;
+ _mbc[mb].rx_dsp_xports.push_back(make_xport(
+ addr, BOOST_STRINGIZE(USRP2_UDP_RX_DSP0_PORT), device_args_i, "recv"
+ ));
+ UHD_LOG << "Making transport for RX DSP1..." << std::endl;
+ _mbc[mb].rx_dsp_xports.push_back(make_xport(
+ addr, BOOST_STRINGIZE(USRP2_UDP_RX_DSP1_PORT), device_args_i, "recv"
+ ));
+ UHD_LOG << "Making transport for TX DSP0..." << std::endl;
+ _mbc[mb].tx_dsp_xport = make_xport(
+ addr, BOOST_STRINGIZE(USRP2_UDP_TX_DSP0_PORT), device_args_i, "send"
+ );
+ //set the filter on the router to take dsp data from this port
+ _mbc[mb].iface->poke32(U2_REG_ROUTER_CTRL_PORTS, USRP2_UDP_TX_DSP0_PORT);
+
+ ////////////////////////////////////////////////////////////////
+ // setup the mboard eeprom
+ ////////////////////////////////////////////////////////////////
+ _tree->create<mboard_eeprom_t>(mb_path / "eeprom")
+ .set(_mbc[mb].iface->mb_eeprom)
+ .subscribe(boost::bind(&usrp2_impl::set_mb_eeprom, this, mb, _1));
+
+ ////////////////////////////////////////////////////////////////
+ // create clock control objects
+ ////////////////////////////////////////////////////////////////
+ _mbc[mb].clock = usrp2_clock_ctrl::make(_mbc[mb].iface);
+ _tree->create<double>(mb_path / "tick_rate")
+ .publish(boost::bind(&usrp2_clock_ctrl::get_master_clock_rate, _mbc[mb].clock))
+ .subscribe(boost::bind(&usrp2_impl::update_tick_rate, this, _1));
+
+ ////////////////////////////////////////////////////////////////
+ // create codec control objects
+ ////////////////////////////////////////////////////////////////
+ const fs_path rx_codec_path = mb_path / "rx_codecs/A";
+ const fs_path tx_codec_path = mb_path / "tx_codecs/A";
+ _tree->create<int>(rx_codec_path / "gains"); //phony property so this dir exists
+ _tree->create<int>(tx_codec_path / "gains"); //phony property so this dir exists
+ _mbc[mb].codec = usrp2_codec_ctrl::make(_mbc[mb].iface);
+ switch(_mbc[mb].iface->get_rev()){
+ case usrp2_iface::USRP_N200:
+ case usrp2_iface::USRP_N210:
+ case usrp2_iface::USRP_N200_R4:
+ case usrp2_iface::USRP_N210_R4:{
+ _tree->create<std::string>(rx_codec_path / "name").set("ads62p44");
+ _tree->create<meta_range_t>(rx_codec_path / "gains/digital/range").set(meta_range_t(0, 6.0, 0.5));
+ _tree->create<double>(rx_codec_path / "gains/digital/value")
+ .subscribe(boost::bind(&usrp2_codec_ctrl::set_rx_digital_gain, _mbc[mb].codec, _1)).set(0);
+ _tree->create<meta_range_t>(rx_codec_path / "gains/fine/range").set(meta_range_t(0, 0.5, 0.05));
+ _tree->create<double>(rx_codec_path / "gains/fine/value")
+ .subscribe(boost::bind(&usrp2_codec_ctrl::set_rx_digital_fine_gain, _mbc[mb].codec, _1)).set(0);
+ }break;
+
+ case usrp2_iface::USRP2_REV3:
+ case usrp2_iface::USRP2_REV4:
+ _tree->create<std::string>(rx_codec_path / "name").set("ltc2284");
+ break;
+
+ case usrp2_iface::USRP_NXXX:
+ _tree->create<std::string>(rx_codec_path / "name").set("??????");
+ break;
+ }
+ _tree->create<std::string>(tx_codec_path / "name").set("ad9777");
+
+ ////////////////////////////////////////////////////////////////
+ // create gpsdo control objects
+ ////////////////////////////////////////////////////////////////
+ if (_mbc[mb].iface->mb_eeprom["gpsdo"] == "internal"){
+ _mbc[mb].gps = gps_ctrl::make(udp_simple::make_uart(udp_simple::make_connected(
+ addr, BOOST_STRINGIZE(USRP2_UDP_UART_GPS_PORT)
+ )));
+ if(_mbc[mb].gps->gps_detected()) {
+ BOOST_FOREACH(const std::string &name, _mbc[mb].gps->get_sensors()){
+ _tree->create<sensor_value_t>(mb_path / "sensors" / name)
+ .publish(boost::bind(&gps_ctrl::get_sensor, _mbc[mb].gps, name));
+ }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////
+ // and do the misc mboard sensors
+ ////////////////////////////////////////////////////////////////
+ _tree->create<sensor_value_t>(mb_path / "sensors/mimo_locked")
+ .publish(boost::bind(&usrp2_impl::get_mimo_locked, this, mb));
+ _tree->create<sensor_value_t>(mb_path / "sensors/ref_locked")
+ .publish(boost::bind(&usrp2_impl::get_ref_locked, this, mb));
+
+ ////////////////////////////////////////////////////////////////
+ // create frontend control objects
+ ////////////////////////////////////////////////////////////////
+ _mbc[mb].rx_fe = rx_frontend_core_200::make(
+ _mbc[mb].iface, U2_REG_SR_ADDR(SR_RX_FRONT)
+ );
+ _mbc[mb].tx_fe = tx_frontend_core_200::make(
+ _mbc[mb].iface, U2_REG_SR_ADDR(SR_TX_FRONT)
+ );
+
+ _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec")
+ .subscribe(boost::bind(&usrp2_impl::update_rx_subdev_spec, this, mb, _1));
+ _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec")
+ .subscribe(boost::bind(&usrp2_impl::update_tx_subdev_spec, this, mb, _1));
+
+ const fs_path rx_fe_path = mb_path / "rx_frontends" / "A";
+ const fs_path tx_fe_path = mb_path / "tx_frontends" / "A";
+
+ _tree->create<std::complex<double> >(rx_fe_path / "dc_offset" / "value")
+ .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, _mbc[mb].rx_fe, _1))
+ .set(std::complex<double>(0.0, 0.0));
+ _tree->create<bool>(rx_fe_path / "dc_offset" / "enable")
+ .subscribe(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, _mbc[mb].rx_fe, _1))
+ .set(true);
+ _tree->create<std::complex<double> >(rx_fe_path / "iq_balance" / "value")
+ .subscribe(boost::bind(&rx_frontend_core_200::set_iq_balance, _mbc[mb].rx_fe, _1))
+ .set(std::polar<double>(1.0, 0.0));
+ _tree->create<std::complex<double> >(tx_fe_path / "dc_offset" / "value")
+ .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, _mbc[mb].tx_fe, _1))
+ .set(std::complex<double>(0.0, 0.0));
+ _tree->create<std::complex<double> >(tx_fe_path / "iq_balance" / "value")
+ .subscribe(boost::bind(&tx_frontend_core_200::set_iq_balance, _mbc[mb].tx_fe, _1))
+ .set(std::polar<double>(1.0, 0.0));
+
+ ////////////////////////////////////////////////////////////////
+ // create rx dsp control objects
+ ////////////////////////////////////////////////////////////////
+ _mbc[mb].rx_dsps.push_back(rx_dsp_core_200::make(
+ _mbc[mb].iface, U2_REG_SR_ADDR(SR_RX_DSP0), U2_REG_SR_ADDR(SR_RX_CTRL0), USRP2_RX_SID_BASE + 0, true
+ ));
+ _mbc[mb].rx_dsps.push_back(rx_dsp_core_200::make(
+ _mbc[mb].iface, U2_REG_SR_ADDR(SR_RX_DSP1), U2_REG_SR_ADDR(SR_RX_CTRL1), USRP2_RX_SID_BASE + 1, true
+ ));
+ for (size_t dspno = 0; dspno < _mbc[mb].rx_dsps.size(); dspno++){
+ _mbc[mb].rx_dsps[dspno]->set_link_rate(USRP2_LINK_RATE_BPS);
+ _tree->access<double>(mb_path / "tick_rate")
+ .subscribe(boost::bind(&rx_dsp_core_200::set_tick_rate, _mbc[mb].rx_dsps[dspno], _1));
+ fs_path rx_dsp_path = mb_path / str(boost::format("rx_dsps/%u") % dspno);
+ _tree->create<meta_range_t>(rx_dsp_path / "rate/range")
+ .publish(boost::bind(&rx_dsp_core_200::get_host_rates, _mbc[mb].rx_dsps[dspno]));
+ _tree->create<double>(rx_dsp_path / "rate/value")
+ .set(1e6) //some default
+ .coerce(boost::bind(&rx_dsp_core_200::set_host_rate, _mbc[mb].rx_dsps[dspno], _1))
+ .subscribe(boost::bind(&usrp2_impl::update_rx_samp_rate, this, mb, dspno, _1));
+ _tree->create<double>(rx_dsp_path / "freq/value")
+ .coerce(boost::bind(&rx_dsp_core_200::set_freq, _mbc[mb].rx_dsps[dspno], _1));
+ _tree->create<meta_range_t>(rx_dsp_path / "freq/range")
+ .publish(boost::bind(&rx_dsp_core_200::get_freq_range, _mbc[mb].rx_dsps[dspno]));
+ _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd")
+ .subscribe(boost::bind(&rx_dsp_core_200::issue_stream_command, _mbc[mb].rx_dsps[dspno], _1));
+ }
+
+ ////////////////////////////////////////////////////////////////
+ // create tx dsp control objects
+ ////////////////////////////////////////////////////////////////
+ _mbc[mb].tx_dsp = tx_dsp_core_200::make(
+ _mbc[mb].iface, U2_REG_SR_ADDR(SR_TX_DSP), U2_REG_SR_ADDR(SR_TX_CTRL), USRP2_TX_ASYNC_SID
+ );
+ _mbc[mb].tx_dsp->set_link_rate(USRP2_LINK_RATE_BPS);
+ _tree->access<double>(mb_path / "tick_rate")
+ .subscribe(boost::bind(&tx_dsp_core_200::set_tick_rate, _mbc[mb].tx_dsp, _1));
+ _tree->create<meta_range_t>(mb_path / "tx_dsps/0/rate/range")
+ .publish(boost::bind(&tx_dsp_core_200::get_host_rates, _mbc[mb].tx_dsp));
+ _tree->create<double>(mb_path / "tx_dsps/0/rate/value")
+ .set(1e6) //some default
+ .coerce(boost::bind(&tx_dsp_core_200::set_host_rate, _mbc[mb].tx_dsp, _1))
+ .subscribe(boost::bind(&usrp2_impl::update_tx_samp_rate, this, mb, 0, _1));
+ _tree->create<double>(mb_path / "tx_dsps/0/freq/value")
+ .coerce(boost::bind(&usrp2_impl::set_tx_dsp_freq, this, mb, _1));
+ _tree->create<meta_range_t>(mb_path / "tx_dsps/0/freq/range")
+ .publish(boost::bind(&usrp2_impl::get_tx_dsp_freq_range, this, mb));
+
+ //setup dsp flow control
+ const double ups_per_sec = device_args_i.cast<double>("ups_per_sec", 20);
+ const size_t send_frame_size = _mbc[mb].tx_dsp_xport->get_send_frame_size();
+ const double ups_per_fifo = device_args_i.cast<double>("ups_per_fifo", 8.0);
+ _mbc[mb].tx_dsp->set_updates(
+ (ups_per_sec > 0.0)? size_t(100e6/*approx tick rate*//ups_per_sec) : 0,
+ (ups_per_fifo > 0.0)? size_t(USRP2_SRAM_BYTES/ups_per_fifo/send_frame_size) : 0
+ );
+
+ ////////////////////////////////////////////////////////////////
+ // create time control objects
+ ////////////////////////////////////////////////////////////////
+ time64_core_200::readback_bases_type time64_rb_bases;
+ time64_rb_bases.rb_hi_now = U2_REG_TIME64_HI_RB_IMM;
+ time64_rb_bases.rb_lo_now = U2_REG_TIME64_LO_RB_IMM;
+ time64_rb_bases.rb_hi_pps = U2_REG_TIME64_HI_RB_PPS;
+ time64_rb_bases.rb_lo_pps = U2_REG_TIME64_LO_RB_PPS;
+ _mbc[mb].time64 = time64_core_200::make(
+ _mbc[mb].iface, U2_REG_SR_ADDR(SR_TIME64), time64_rb_bases, mimo_clock_sync_delay_cycles
+ );
+ _tree->access<double>(mb_path / "tick_rate")
+ .subscribe(boost::bind(&time64_core_200::set_tick_rate, _mbc[mb].time64, _1));
+ _tree->create<time_spec_t>(mb_path / "time/now")
+ .publish(boost::bind(&time64_core_200::get_time_now, _mbc[mb].time64))
+ .subscribe(boost::bind(&time64_core_200::set_time_now, _mbc[mb].time64, _1));
+ _tree->create<time_spec_t>(mb_path / "time/pps")
+ .publish(boost::bind(&time64_core_200::get_time_last_pps, _mbc[mb].time64))
+ .subscribe(boost::bind(&time64_core_200::set_time_next_pps, _mbc[mb].time64, _1));
+ //setup time source props
+ _tree->create<std::string>(mb_path / "time_source/value")
+ .subscribe(boost::bind(&time64_core_200::set_time_source, _mbc[mb].time64, _1));
+ _tree->create<std::vector<std::string> >(mb_path / "time_source/options")
+ .publish(boost::bind(&time64_core_200::get_time_sources, _mbc[mb].time64));
+ //setup reference source props
+ _tree->create<std::string>(mb_path / "clock_source/value")
+ .subscribe(boost::bind(&usrp2_impl::update_clock_source, this, mb, _1));
+ static const std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("mimo");
+ _tree->create<std::vector<std::string> >(mb_path / "clock_source/options").set(clock_sources);
+
+ ////////////////////////////////////////////////////////////////////
+ // create user-defined control objects
+ ////////////////////////////////////////////////////////////////////
+ _mbc[mb].user = user_settings_core_200::make(_mbc[mb].iface, U2_REG_SR_ADDR(SR_USER_REGS));
+ _tree->create<user_settings_core_200::user_reg_t>(mb_path / "user/regs")
+ .subscribe(boost::bind(&user_settings_core_200::set_reg, _mbc[mb].user, _1));
+
+ ////////////////////////////////////////////////////////////////
+ // create dboard control objects
+ ////////////////////////////////////////////////////////////////
+
+ //read the dboard eeprom to extract the dboard ids
+ dboard_eeprom_t rx_db_eeprom, tx_db_eeprom, gdb_eeprom;
+ rx_db_eeprom.load(*_mbc[mb].iface, USRP2_I2C_ADDR_RX_DB);
+ tx_db_eeprom.load(*_mbc[mb].iface, USRP2_I2C_ADDR_TX_DB);
+ gdb_eeprom.load(*_mbc[mb].iface, USRP2_I2C_ADDR_TX_DB ^ 5);
+
+ //create the properties and register subscribers
+ _tree->create<dboard_eeprom_t>(mb_path / "dboards/A/rx_eeprom")
+ .set(rx_db_eeprom)
+ .subscribe(boost::bind(&usrp2_impl::set_db_eeprom, this, mb, "rx", _1));
+ _tree->create<dboard_eeprom_t>(mb_path / "dboards/A/tx_eeprom")
+ .set(tx_db_eeprom)
+ .subscribe(boost::bind(&usrp2_impl::set_db_eeprom, this, mb, "tx", _1));
+ _tree->create<dboard_eeprom_t>(mb_path / "dboards/A/gdb_eeprom")
+ .set(gdb_eeprom)
+ .subscribe(boost::bind(&usrp2_impl::set_db_eeprom, this, mb, "gdb", _1));
+
+ //create a new dboard interface and manager
+ _mbc[mb].dboard_iface = make_usrp2_dboard_iface(_mbc[mb].iface, _mbc[mb].clock);
+ _tree->create<dboard_iface::sptr>(mb_path / "dboards/A/iface").set(_mbc[mb].dboard_iface);
+ _mbc[mb].dboard_manager = dboard_manager::make(
+ rx_db_eeprom.id, tx_db_eeprom.id, gdb_eeprom.id,
+ _mbc[mb].dboard_iface, _tree->subtree(mb_path / "dboards/A")
+ );
+
+ //bind frontend corrections to the dboard freq props
+ const fs_path db_tx_fe_path = mb_path / "dboards" / "A" / "tx_frontends";
+ BOOST_FOREACH(const std::string &name, _tree->list(db_tx_fe_path)){
+ _tree->access<double>(db_tx_fe_path / name / "freq" / "value")
+ .subscribe(boost::bind(&usrp2_impl::set_tx_fe_corrections, this, mb, _1));
+ }
+ const fs_path db_rx_fe_path = mb_path / "dboards" / "A" / "rx_frontends";
+ BOOST_FOREACH(const std::string &name, _tree->list(db_rx_fe_path)){
+ _tree->access<double>(db_rx_fe_path / name / "freq" / "value")
+ .subscribe(boost::bind(&usrp2_impl::set_rx_fe_corrections, this, mb, _1));
+ }
+ }
+
+ //initialize io handling
+ this->io_init();
+
+ //do some post-init tasks
+ this->update_rates();
+ BOOST_FOREACH(const std::string &mb, _mbc.keys()){
+ fs_path root = "/mboards/" + mb;
+
+ //reset cordic rates and their properties to zero
+ BOOST_FOREACH(const std::string &name, _tree->list(root / "rx_dsps")){
+ _tree->access<double>(root / "rx_dsps" / name / "freq" / "value").set(0.0);
+ }
+ BOOST_FOREACH(const std::string &name, _tree->list(root / "tx_dsps")){
+ _tree->access<double>(root / "tx_dsps" / name / "freq" / "value").set(0.0);
+ }
+
+ _tree->access<subdev_spec_t>(root / "rx_subdev_spec").set(subdev_spec_t("A:" + _tree->list(root / "dboards/A/rx_frontends").at(0)));
+ _tree->access<subdev_spec_t>(root / "tx_subdev_spec").set(subdev_spec_t("A:" + _tree->list(root / "dboards/A/tx_frontends").at(0)));
+ _tree->access<std::string>(root / "clock_source/value").set("internal");
+ _tree->access<std::string>(root / "time_source/value").set("none");
+
+ //GPS installed: use external ref, time, and init time spec
+ if (_mbc[mb].gps.get() and _mbc[mb].gps->gps_detected()){
+ UHD_MSG(status) << "Setting references to the internal GPSDO" << std::endl;
+ _tree->access<std::string>(root / "time_source/value").set("external");
+ _tree->access<std::string>(root / "clock_source/value").set("external");
+ UHD_MSG(status) << "Initializing time to the internal GPSDO" << std::endl;
+ _mbc[mb].time64->set_time_next_pps(time_spec_t(time_t(_mbc[mb].gps->get_sensor("gps_time").to_int()+1)));
+ }
+ }
+
+}
+
+usrp2_impl::~usrp2_impl(void){UHD_SAFE_CALL(
+ BOOST_FOREACH(const std::string &mb, _mbc.keys()){
+ _mbc[mb].tx_dsp->set_updates(0, 0);
+ }
+)}
+
+void usrp2_impl::set_mb_eeprom(const std::string &mb, const uhd::usrp::mboard_eeprom_t &mb_eeprom){
+ mb_eeprom.commit(*(_mbc[mb].iface), mboard_eeprom_t::MAP_N100);
+}
+
+void usrp2_impl::set_db_eeprom(const std::string &mb, const std::string &type, const uhd::usrp::dboard_eeprom_t &db_eeprom){
+ if (type == "rx") db_eeprom.store(*_mbc[mb].iface, USRP2_I2C_ADDR_RX_DB);
+ if (type == "tx") db_eeprom.store(*_mbc[mb].iface, USRP2_I2C_ADDR_TX_DB);
+ if (type == "gdb") db_eeprom.store(*_mbc[mb].iface, USRP2_I2C_ADDR_TX_DB ^ 5);
+}
+
+sensor_value_t usrp2_impl::get_mimo_locked(const std::string &mb){
+ const bool lock = (_mbc[mb].iface->peek32(U2_REG_IRQ_RB) & (1<<10)) != 0;
+ return sensor_value_t("MIMO", lock, "locked", "unlocked");
+}
+
+sensor_value_t usrp2_impl::get_ref_locked(const std::string &mb){
+ const bool lock = (_mbc[mb].iface->peek32(U2_REG_IRQ_RB) & (1<<11)) != 0;
+ return sensor_value_t("Ref", lock, "locked", "unlocked");
+}
+
+void usrp2_impl::set_rx_fe_corrections(const std::string &mb, const double lo_freq){
+ apply_rx_fe_corrections(this->get_tree()->subtree("/mboards/" + mb), "A", lo_freq);
+}
+
+void usrp2_impl::set_tx_fe_corrections(const std::string &mb, const double lo_freq){
+ apply_tx_fe_corrections(this->get_tree()->subtree("/mboards/" + mb), "A", lo_freq);
+}
+
+#include <boost/math/special_functions/round.hpp>
+#include <boost/math/special_functions/sign.hpp>
+
+double usrp2_impl::set_tx_dsp_freq(const std::string &mb, const double freq_){
+ double new_freq = freq_;
+ const double tick_rate = _tree->access<double>("/mboards/"+mb+"/tick_rate").get();
+
+ //calculate the DAC shift (multiples of rate)
+ const int sign = boost::math::sign(new_freq);
+ const int zone = std::min(boost::math::iround(new_freq/tick_rate), 2);
+ const double dac_shift = sign*zone*tick_rate;
+ new_freq -= dac_shift; //update FPGA DSP target freq
+
+ //set the DAC shift (modulation mode)
+ if (zone == 0) _mbc[mb].codec->set_tx_mod_mode(0); //no shift
+ else _mbc[mb].codec->set_tx_mod_mode(sign*4/zone); //DAC interp = 4
+
+ return _mbc[mb].tx_dsp->set_freq(new_freq) + dac_shift; //actual freq
+}
+
+meta_range_t usrp2_impl::get_tx_dsp_freq_range(const std::string &mb){
+ const double tick_rate = _tree->access<double>("/mboards/"+mb+"/tick_rate").get();
+ const meta_range_t dsp_range = _mbc[mb].tx_dsp->get_freq_range();
+ return meta_range_t(dsp_range.start() - tick_rate*2, dsp_range.stop() + tick_rate*2, dsp_range.step());
+}
+
+void usrp2_impl::update_clock_source(const std::string &mb, const std::string &source){
+ //clock source ref 10mhz
+ switch(_mbc[mb].iface->get_rev()){
+ case usrp2_iface::USRP_N200:
+ case usrp2_iface::USRP_N210:
+ case usrp2_iface::USRP_N200_R4:
+ case usrp2_iface::USRP_N210_R4:
+ if (source == "internal") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x12);
+ else if (source == "external") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x1C);
+ else if (source == "mimo") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x15);
+ else throw uhd::value_error("unhandled clock configuration reference source: " + source);
+ _mbc[mb].clock->enable_external_ref(true); //USRP2P has an internal 10MHz TCXO
+ break;
+
+ case usrp2_iface::USRP2_REV3:
+ case usrp2_iface::USRP2_REV4:
+ if (source == "internal") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x10);
+ else if (source == "external") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x1C);
+ else if (source == "mimo") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x15);
+ else throw uhd::value_error("unhandled clock configuration reference source: " + source);
+ _mbc[mb].clock->enable_external_ref(source != "internal");
+ break;
+
+ case usrp2_iface::USRP_NXXX: break;
+ }
+
+ //always drive the clock over serdes if not locking to it
+ _mbc[mb].clock->enable_mimo_clock_out(source != "mimo");
+
+ //set the mimo clock delay over the serdes
+ if (source != "mimo"){
+ switch(_mbc[mb].iface->get_rev()){
+ case usrp2_iface::USRP_N200:
+ case usrp2_iface::USRP_N210:
+ case usrp2_iface::USRP_N200_R4:
+ case usrp2_iface::USRP_N210_R4:
+ _mbc[mb].clock->set_mimo_clock_delay(mimo_clock_delay_usrp_n2xx);
+ break;
+
+ case usrp2_iface::USRP2_REV4:
+ _mbc[mb].clock->set_mimo_clock_delay(mimo_clock_delay_usrp2_rev4);
+ break;
+
+ default: break; //not handled
+ }
+ }
+}
diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp
new file mode 100644
index 000000000..882a61f80
--- /dev/null
+++ b/host/lib/usrp/usrp2/usrp2_impl.hpp
@@ -0,0 +1,134 @@
+//
+// Copyright 2010-2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_USRP2_IMPL_HPP
+#define INCLUDED_USRP2_IMPL_HPP
+
+#include "usrp2_iface.hpp"
+#include "clock_ctrl.hpp"
+#include "codec_ctrl.hpp"
+#include "rx_frontend_core_200.hpp"
+#include "tx_frontend_core_200.hpp"
+#include "rx_dsp_core_200.hpp"
+#include "tx_dsp_core_200.hpp"
+#include "time64_core_200.hpp"
+#include "user_settings_core_200.hpp"
+#include <uhd/property_tree.hpp>
+#include <uhd/usrp/gps_ctrl.hpp>
+#include <uhd/device.hpp>
+#include <uhd/utils/pimpl.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/types/stream_cmd.hpp>
+#include <uhd/types/clock_config.hpp>
+#include <uhd/usrp/dboard_eeprom.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/function.hpp>
+#include <uhd/transport/vrt_if_packet.hpp>
+#include <uhd/transport/udp_simple.hpp>
+#include <uhd/transport/udp_zero_copy.hpp>
+#include <uhd/usrp/dboard_manager.hpp>
+#include <uhd/usrp/subdev_spec.hpp>
+#include <boost/weak_ptr.hpp>
+
+static const double USRP2_LINK_RATE_BPS = 1000e6/8;
+static const double mimo_clock_delay_usrp2_rev4 = 4.18e-9;
+static const double mimo_clock_delay_usrp_n2xx = 3.55e-9;
+static const size_t mimo_clock_sync_delay_cycles = 138;
+static const size_t USRP2_SRAM_BYTES = size_t(1 << 20);
+static const boost::uint32_t USRP2_TX_ASYNC_SID = 2;
+static const boost::uint32_t USRP2_RX_SID_BASE = 3;
+
+/*!
+ * Make a usrp2 dboard interface.
+ * \param iface the usrp2 interface object
+ * \param clk_ctrl the clock control object
+ * \return a sptr to a new dboard interface
+ */
+uhd::usrp::dboard_iface::sptr make_usrp2_dboard_iface(
+ usrp2_iface::sptr iface,
+ usrp2_clock_ctrl::sptr clk_ctrl
+);
+
+/*!
+ * USRP2 implementation guts:
+ * The implementation details are encapsulated here.
+ * Handles device properties and streaming...
+ */
+class usrp2_impl : public uhd::device{
+public:
+ usrp2_impl(const uhd::device_addr_t &);
+ ~usrp2_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);
+
+private:
+ uhd::property_tree::sptr _tree;
+ struct mb_container_type{
+ usrp2_iface::sptr iface;
+ usrp2_clock_ctrl::sptr clock;
+ usrp2_codec_ctrl::sptr codec;
+ uhd::gps_ctrl::sptr gps;
+ rx_frontend_core_200::sptr rx_fe;
+ tx_frontend_core_200::sptr tx_fe;
+ std::vector<rx_dsp_core_200::sptr> rx_dsps;
+ std::vector<boost::weak_ptr<uhd::rx_streamer> > rx_streamers;
+ std::vector<boost::weak_ptr<uhd::tx_streamer> > tx_streamers;
+ tx_dsp_core_200::sptr tx_dsp;
+ time64_core_200::sptr time64;
+ user_settings_core_200::sptr user;
+ std::vector<uhd::transport::zero_copy_if::sptr> rx_dsp_xports;
+ uhd::transport::zero_copy_if::sptr tx_dsp_xport;
+ uhd::usrp::dboard_manager::sptr dboard_manager;
+ uhd::usrp::dboard_iface::sptr dboard_iface;
+ size_t rx_chan_occ, tx_chan_occ;
+ mb_container_type(void): rx_chan_occ(0), tx_chan_occ(0){}
+ };
+ uhd::dict<std::string, mb_container_type> _mbc;
+
+ void set_mb_eeprom(const std::string &, const uhd::usrp::mboard_eeprom_t &);
+ void set_db_eeprom(const std::string &, const std::string &, const uhd::usrp::dboard_eeprom_t &);
+
+ uhd::sensor_value_t get_mimo_locked(const std::string &);
+ uhd::sensor_value_t get_ref_locked(const std::string &);
+
+ void set_rx_fe_corrections(const std::string &mb, const double);
+ void set_tx_fe_corrections(const std::string &mb, const double);
+
+ //device properties interface
+ uhd::property_tree::sptr get_tree(void) const{
+ return _tree;
+ }
+
+ //io impl methods and members
+ UHD_PIMPL_DECL(io_impl) _io_impl;
+ void io_init(void);
+ void update_tick_rate(const double rate);
+ void update_rx_samp_rate(const std::string &, const size_t, const double rate);
+ void update_tx_samp_rate(const std::string &, const size_t, const double rate);
+ void update_rates(void);
+ //update spec methods are coercers until we only accept db_name == A
+ void update_rx_subdev_spec(const std::string &, const uhd::usrp::subdev_spec_t &);
+ void update_tx_subdev_spec(const std::string &, const uhd::usrp::subdev_spec_t &);
+ double set_tx_dsp_freq(const std::string &, const double);
+ uhd::meta_range_t get_tx_dsp_freq_range(const std::string &);
+ void update_clock_source(const std::string &, const std::string &);
+};
+
+#endif /* INCLUDED_USRP2_IMPL_HPP */
diff --git a/host/lib/usrp/usrp2/usrp2_regs.hpp b/host/lib/usrp/usrp2/usrp2_regs.hpp
new file mode 100644
index 000000000..e14798ecb
--- /dev/null
+++ b/host/lib/usrp/usrp2/usrp2_regs.hpp
@@ -0,0 +1,107 @@
+//
+// Copyright 2010-2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_USRP2_REGS_HPP
+#define INCLUDED_USRP2_REGS_HPP
+
+////////////////////////////////////////////////////////////////////////
+// Define slave bases
+////////////////////////////////////////////////////////////////////////
+#define ROUTER_RAM_BASE 0x4000
+#define SPI_BASE 0x5000
+#define I2C_BASE 0x5400
+#define GPIO_BASE 0x5800
+#define READBACK_BASE 0x5C00
+#define ETH_BASE 0x6000
+#define SETTING_REGS_BASE 0x7000
+#define PIC_BASE 0x8000
+#define UART_BASE 0x8800
+#define ATR_BASE 0x8C00
+
+////////////////////////////////////////////////////////////////////////
+// Setting register offsets
+////////////////////////////////////////////////////////////////////////
+#define SR_MISC 0 // 7 regs
+#define SR_SIMTIMER 8 // 2
+#define SR_TIME64 10 // 6
+#define SR_BUF_POOL 16 // 4
+#define SR_USER_REGS 20 // 2
+#define SR_RX_FRONT 24 // 5
+#define SR_RX_CTRL0 32 // 9
+#define SR_RX_DSP0 48 // 7
+#define SR_RX_CTRL1 80 // 9
+#define SR_RX_DSP1 96 // 7
+
+#define SR_TX_FRONT 128 // ?
+#define SR_TX_CTRL 144 // 6
+#define SR_TX_DSP 160 // 5
+
+#define SR_GPIO 184
+#define SR_UDP_SM 192 // 64
+
+#define U2_REG_SR_ADDR(sr) (SETTING_REGS_BASE + (4 * (sr)))
+
+#define U2_REG_ROUTER_CTRL_PORTS U2_REG_SR_ADDR(SR_BUF_POOL) + 8
+
+/////////////////////////////////////////////////
+// SPI Slave Constants
+////////////////////////////////////////////////
+// Masks for controlling different peripherals
+#define SPI_SS_AD9510 1
+#define SPI_SS_AD9777 2
+#define SPI_SS_RX_DAC 4
+#define SPI_SS_RX_ADC 8
+#define SPI_SS_RX_DB 16
+#define SPI_SS_TX_DAC 32
+#define SPI_SS_TX_ADC 64
+#define SPI_SS_TX_DB 128
+#define SPI_SS_ADS62P44 256 //for usrp2p
+
+/////////////////////////////////////////////////
+// Misc Control
+////////////////////////////////////////////////
+#define U2_REG_MISC_CTRL_CLOCK U2_REG_SR_ADDR(0)
+#define U2_REG_MISC_CTRL_SERDES U2_REG_SR_ADDR(1)
+#define U2_REG_MISC_CTRL_ADC U2_REG_SR_ADDR(2)
+#define U2_REG_MISC_CTRL_LEDS U2_REG_SR_ADDR(3)
+#define U2_REG_MISC_CTRL_PHY U2_REG_SR_ADDR(4)
+#define U2_REG_MISC_CTRL_DBG_MUX U2_REG_SR_ADDR(5)
+#define U2_REG_MISC_CTRL_RAM_PAGE U2_REG_SR_ADDR(6)
+#define U2_REG_MISC_CTRL_FLUSH_ICACHE U2_REG_SR_ADDR(7)
+#define U2_REG_MISC_CTRL_LED_SRC U2_REG_SR_ADDR(8)
+
+#define U2_FLAG_MISC_CTRL_SERDES_ENABLE 8
+#define U2_FLAG_MISC_CTRL_SERDES_PRBSEN 4
+#define U2_FLAG_MISC_CTRL_SERDES_LOOPEN 2
+#define U2_FLAG_MISC_CTRL_SERDES_RXEN 1
+
+#define U2_FLAG_MISC_CTRL_ADC_ON 0x0F
+#define U2_FLAG_MISC_CTRL_ADC_OFF 0x00
+
+/////////////////////////////////////////////////
+// Readback regs
+////////////////////////////////////////////////
+#define U2_REG_STATUS READBACK_BASE + 4*8
+#define U2_REG_GPIO_RB READBACK_BASE + 4*9
+#define U2_REG_TIME64_HI_RB_IMM READBACK_BASE + 4*10
+#define U2_REG_TIME64_LO_RB_IMM READBACK_BASE + 4*11
+#define U2_REG_COMPAT_NUM_RB READBACK_BASE + 4*12
+#define U2_REG_IRQ_RB READBACK_BASE + 4*13
+#define U2_REG_TIME64_HI_RB_PPS READBACK_BASE + 4*14
+#define U2_REG_TIME64_LO_RB_PPS READBACK_BASE + 4*15
+
+#endif /* INCLUDED_USRP2_REGS_HPP */
diff --git a/host/lib/utils/CMakeLists.txt b/host/lib/utils/CMakeLists.txt
new file mode 100644
index 000000000..95105f917
--- /dev/null
+++ b/host/lib/utils/CMakeLists.txt
@@ -0,0 +1,142 @@
+#
+# Copyright 2010-2011 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# This file included, use CMake directory variables
+########################################################################
+
+########################################################################
+# Setup defines for process scheduling
+########################################################################
+MESSAGE(STATUS "")
+MESSAGE(STATUS "Configuring priority scheduling...")
+INCLUDE(CheckCXXSourceCompiles)
+
+CHECK_CXX_SOURCE_COMPILES("
+ #include <pthread.h>
+ int main(){
+ struct sched_param sp;
+ pthread_setschedparam(pthread_self(), SCHED_RR, &sp);
+ return 0;
+ }
+ " HAVE_PTHREAD_SETSCHEDPARAM
+)
+
+IF(CYGWIN)
+ #SCHED_RR non-operational on cygwin
+ SET(HAVE_PTHREAD_SETSCHEDPARAM False)
+ENDIF(CYGWIN)
+
+CHECK_CXX_SOURCE_COMPILES("
+ #include <windows.h>
+ int main(){
+ SetThreadPriority(GetCurrentThread(), 0);
+ SetPriorityClass(GetCurrentProcess(), 0);
+ return 0;
+ }
+ " HAVE_WIN_SETTHREADPRIORITY
+)
+
+IF(HAVE_PTHREAD_SETSCHEDPARAM)
+ MESSAGE(STATUS " Priority scheduling supported through pthread_setschedparam.")
+ SET(THREAD_PRIO_DEFS HAVE_PTHREAD_SETSCHEDPARAM)
+ LIBUHD_APPEND_LIBS(pthread)
+ELSEIF(HAVE_WIN_SETTHREADPRIORITY)
+ MESSAGE(STATUS " Priority scheduling supported through windows SetThreadPriority.")
+ SET(THREAD_PRIO_DEFS HAVE_WIN_SETTHREADPRIORITY)
+ELSE()
+ MESSAGE(STATUS " Priority scheduling not supported.")
+ SET(THREAD_PRIO_DEFS HAVE_THREAD_PRIO_DUMMY)
+ENDIF()
+
+SET_SOURCE_FILES_PROPERTIES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/thread_priority.cpp
+ PROPERTIES COMPILE_DEFINITIONS "${THREAD_PRIO_DEFS}"
+)
+
+########################################################################
+# Setup defines for module loading
+########################################################################
+MESSAGE(STATUS "")
+MESSAGE(STATUS "Configuring module loading...")
+INCLUDE(CheckCXXSourceCompiles)
+
+SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_DL_LIBS})
+CHECK_CXX_SOURCE_COMPILES("
+ #include <dlfcn.h>
+ int main(){
+ dlopen(0, 0);
+ return 0;
+ }
+ " HAVE_DLOPEN
+)
+UNSET(CMAKE_REQUIRED_LIBRARIES)
+
+CHECK_CXX_SOURCE_COMPILES("
+ #include <windows.h>
+ int main(){
+ LoadLibrary(0);
+ return 0;
+ }
+ " HAVE_LOAD_LIBRARY
+)
+
+IF(HAVE_DLOPEN)
+ MESSAGE(STATUS " Module loading supported through dlopen.")
+ SET(LOAD_MODULES_DEFS HAVE_DLOPEN)
+ LIBUHD_APPEND_LIBS(${CMAKE_DL_LIBS})
+ELSEIF(HAVE_LOAD_LIBRARY)
+ MESSAGE(STATUS " Module loading supported through LoadLibrary.")
+ SET(LOAD_MODULES_DEFS HAVE_LOAD_LIBRARY)
+ELSE()
+ MESSAGE(STATUS " Module loading not supported.")
+ SET(LOAD_MODULES_DEFS HAVE_LOAD_MODULES_DUMMY)
+ENDIF()
+
+SET_SOURCE_FILES_PROPERTIES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/load_modules.cpp
+ PROPERTIES COMPILE_DEFINITIONS "${LOAD_MODULES_DEFS}"
+)
+
+########################################################################
+# Define UHD_PKG_DATA_PATH for paths.cpp
+########################################################################
+FILE(TO_NATIVE_PATH ${CMAKE_INSTALL_PREFIX}/${PKG_DATA_DIR} UHD_PKG_DATA_PATH)
+STRING(REPLACE "\\" "\\\\" UHD_PKG_DATA_PATH ${UHD_PKG_DATA_PATH})
+MESSAGE(STATUS "Full package data directory: ${UHD_PKG_DATA_PATH}")
+
+SET_SOURCE_FILES_PROPERTIES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/paths.cpp
+ PROPERTIES COMPILE_DEFINITIONS
+ "UHD_PKG_DATA_PATH=\"${UHD_PKG_DATA_PATH}\""
+)
+
+########################################################################
+# Append sources
+########################################################################
+LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/csv.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/gain_group.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/images.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/load_modules.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/log.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/msg.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/paths.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/static.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/tasks.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/thread_priority.cpp
+)
diff --git a/host/lib/utils/csv.cpp b/host/lib/utils/csv.cpp
new file mode 100644
index 000000000..2ffa70196
--- /dev/null
+++ b/host/lib/utils/csv.cpp
@@ -0,0 +1,52 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/utils/csv.hpp>
+#include <boost/foreach.hpp>
+
+using namespace uhd;
+
+csv::rows_type csv::to_rows(std::istream &input){
+ csv::rows_type rows;
+ std::string line;
+ //for each line in the input stream
+ while (std::getline(input, line)){
+ csv::row_type row(1, "");
+ bool in_quote = false;
+ char last_ch, next_ch = ' ';
+ //for each character in the line
+ BOOST_FOREACH(char ch, line){
+ last_ch = next_ch;
+ next_ch = ch;
+ //catch a quote character and change the state
+ //we handle double quotes by checking last_ch
+ if (ch == '"'){
+ in_quote = not in_quote;
+ if (last_ch != '"') continue;
+ }
+ //a comma not inside quotes is a column delimiter
+ if (not in_quote and ch == ','){
+ row.push_back("");
+ continue;
+ }
+ //if we got here we record the character
+ row.back() += ch;
+ }
+ rows.push_back(row);
+ }
+ return rows;
+}
diff --git a/host/lib/utils/gain_group.cpp b/host/lib/utils/gain_group.cpp
new file mode 100644
index 000000000..85f4977a6
--- /dev/null
+++ b/host/lib/utils/gain_group.cpp
@@ -0,0 +1,186 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/utils/gain_group.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <uhd/exception.hpp>
+#include <boost/foreach.hpp>
+#include <boost/bind.hpp>
+#include <algorithm>
+#include <vector>
+
+using namespace uhd;
+
+static const bool verbose = false;
+
+static bool compare_by_step_size(
+ const size_t &rhs, const size_t &lhs, std::vector<gain_fcns_t> &fcns
+){
+ return fcns.at(rhs).get_range().step() > fcns.at(lhs).get_range().step();
+}
+
+/*!
+ * Get a multiple of step with the following relation:
+ * result = step*floor(num/step)
+ *
+ * Due to small doubleing-point inaccuracies:
+ * num = n*step + e, where e is a small inaccuracy.
+ * When e is negative, floor would yeild (n-1)*step,
+ * despite that n*step is really the desired result.
+ * This function is designed to mitigate that issue.
+ *
+ * \param num the number to approximate
+ * \param step the step size to round with
+ * \param e the small inaccuracy to account for
+ * \return a multiple of step approximating num
+ */
+template <typename T> static T floor_step(T num, T step, T e = T(0.001)){
+ return step*int(num/step + e);
+}
+
+/***********************************************************************
+ * gain group implementation
+ **********************************************************************/
+class gain_group_impl : public gain_group{
+public:
+ gain_group_impl(void){
+ /*NOP*/
+ }
+
+ gain_range_t get_range(const std::string &name){
+ if (not name.empty()) return _name_to_fcns[name].get_range();
+
+ double overall_min = 0, overall_max = 0, overall_step = 0;
+ BOOST_FOREACH(const gain_fcns_t &fcns, get_all_fcns()){
+ const gain_range_t range = fcns.get_range();
+ overall_min += range.start();
+ overall_max += range.stop();
+ //the overall step is the min (zero is invalid, first run)
+ if (overall_step == 0) overall_step = range.step();
+ overall_step = std::min(overall_step, range.step());
+ }
+ return gain_range_t(overall_min, overall_max, overall_step);
+ }
+
+ double get_value(const std::string &name){
+ if (not name.empty()) return _name_to_fcns[name].get_value();
+
+ double overall_gain = 0;
+ BOOST_FOREACH(const gain_fcns_t &fcns, get_all_fcns()){
+ overall_gain += fcns.get_value();
+ }
+ return overall_gain;
+ }
+
+ void set_value(double gain, const std::string &name){
+ if (not name.empty()) return _name_to_fcns[name].set_value(gain);
+
+ std::vector<gain_fcns_t> all_fcns = get_all_fcns();
+ if (all_fcns.size() == 0) return; //nothing to set!
+
+ //get the max step size among the gains
+ double max_step = 0;
+ BOOST_FOREACH(const gain_fcns_t &fcns, all_fcns){
+ max_step = std::max(max_step, fcns.get_range().step());
+ }
+
+ //create gain bucket to distribute power
+ std::vector<double> gain_bucket;
+
+ //distribute power according to priority (round to max step)
+ double gain_left_to_distribute = gain;
+ BOOST_FOREACH(const gain_fcns_t &fcns, all_fcns){
+ const gain_range_t range = fcns.get_range();
+ gain_bucket.push_back(floor_step(uhd::clip(
+ gain_left_to_distribute, range.start(), range.stop()
+ ), max_step));
+ gain_left_to_distribute -= gain_bucket.back();
+ }
+
+ //get a list of indexes sorted by step size large to small
+ std::vector<size_t> indexes_step_size_dec;
+ for (size_t i = 0; i < all_fcns.size(); i++){
+ indexes_step_size_dec.push_back(i);
+ }
+ std::sort(
+ indexes_step_size_dec.begin(), indexes_step_size_dec.end(),
+ boost::bind(&compare_by_step_size, _1, _2, all_fcns)
+ );
+ UHD_ASSERT_THROW(
+ all_fcns.at(indexes_step_size_dec.front()).get_range().step() >=
+ all_fcns.at(indexes_step_size_dec.back()).get_range().step()
+ );
+
+ //distribute the remainder (less than max step)
+ //fill in the largest step sizes first that are less than the remainder
+ BOOST_FOREACH(size_t i, indexes_step_size_dec){
+ const gain_range_t range = all_fcns.at(i).get_range();
+ double additional_gain = floor_step(uhd::clip(
+ gain_bucket.at(i) + gain_left_to_distribute, range.start(), range.stop()
+ ), range.step()) - gain_bucket.at(i);
+ gain_bucket.at(i) += additional_gain;
+ gain_left_to_distribute -= additional_gain;
+ }
+ UHD_LOGV(often) << "gain_left_to_distribute " << gain_left_to_distribute << std::endl;
+
+ //now write the bucket out to the individual gain values
+ for (size_t i = 0; i < gain_bucket.size(); i++){
+ UHD_LOGV(often) << i << ": " << gain_bucket.at(i) << std::endl;
+ all_fcns.at(i).set_value(gain_bucket.at(i));
+ }
+ }
+
+ const std::vector<std::string> get_names(void){
+ return _name_to_fcns.keys();
+ }
+
+ void register_fcns(
+ const std::string &name,
+ const gain_fcns_t &gain_fcns,
+ size_t priority
+ ){
+ if (name.empty() or _name_to_fcns.has_key(name)){
+ //ensure the name name is unique and non-empty
+ return register_fcns(name + "_", gain_fcns, priority);
+ }
+ _registry[priority].push_back(gain_fcns);
+ _name_to_fcns[name] = gain_fcns;
+ }
+
+private:
+ //! get the gain function sets in order (highest priority first)
+ std::vector<gain_fcns_t> get_all_fcns(void){
+ std::vector<gain_fcns_t> all_fcns;
+ BOOST_FOREACH(size_t key, uhd::sorted(_registry.keys())){
+ const std::vector<gain_fcns_t> &fcns = _registry[key];
+ all_fcns.insert(all_fcns.begin(), fcns.begin(), fcns.end());
+ }
+ return all_fcns;
+ }
+
+ uhd::dict<size_t, std::vector<gain_fcns_t> > _registry;
+ uhd::dict<std::string, gain_fcns_t> _name_to_fcns;
+};
+
+/***********************************************************************
+ * gain group factory function
+ **********************************************************************/
+gain_group::sptr gain_group::make(void){
+ return sptr(new gain_group_impl());
+}
diff --git a/host/lib/utils/images.cpp b/host/lib/utils/images.cpp
new file mode 100644
index 000000000..a124cc208
--- /dev/null
+++ b/host/lib/utils/images.cpp
@@ -0,0 +1,40 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/utils/images.hpp>
+#include <uhd/exception.hpp>
+#include <boost/foreach.hpp>
+#include <boost/filesystem.hpp>
+#include <vector>
+
+namespace fs = boost::filesystem;
+
+std::vector<fs::path> get_image_paths(void); //defined in paths.cpp
+
+/***********************************************************************
+ * Find a image in the image paths
+ **********************************************************************/
+std::string uhd::find_image_path(const std::string &image_name){
+ if (fs::exists(image_name)){
+ return fs::system_complete(image_name).string();
+ }
+ BOOST_FOREACH(const fs::path &path, get_image_paths()){
+ fs::path image_path = path / image_name;
+ if (fs::exists(image_path)) return image_path.string();
+ }
+ throw uhd::io_error("Could not find path for image: " + image_name);
+}
diff --git a/host/lib/utils/load_modules.cpp b/host/lib/utils/load_modules.cpp
new file mode 100644
index 000000000..bee0d5304
--- /dev/null
+++ b/host/lib/utils/load_modules.cpp
@@ -0,0 +1,109 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/utils/static.hpp>
+#include <uhd/exception.hpp>
+#include <boost/format.hpp>
+#include <boost/foreach.hpp>
+#include <boost/filesystem.hpp>
+#include <iostream>
+#include <string>
+#include <vector>
+
+namespace fs = boost::filesystem;
+
+/***********************************************************************
+ * Module Load Function
+ **********************************************************************/
+#ifdef HAVE_DLOPEN
+#include <dlfcn.h>
+static void load_module(const std::string &file_name){
+ if (dlopen(file_name.c_str(), RTLD_LAZY) == NULL){
+ throw uhd::os_error(str(
+ boost::format("dlopen failed to load \"%s\"") % file_name
+ ));
+ }
+}
+#endif /* HAVE_DLOPEN */
+
+
+#ifdef HAVE_LOAD_LIBRARY
+#include <windows.h>
+static void load_module(const std::string &file_name){
+ if (LoadLibrary(file_name.c_str()) == NULL){
+ throw uhd::os_error(str(
+ boost::format("LoadLibrary failed to load \"%s\"") % file_name
+ ));
+ }
+}
+#endif /* HAVE_LOAD_LIBRARY */
+
+
+#ifdef HAVE_LOAD_MODULES_DUMMY
+static void load_module(const std::string &file_name){
+ throw uhd::not_implemented_error(str(
+ boost::format("Module loading not supported: Cannot load \"%s\"") % file_name
+ ));
+}
+#endif /* HAVE_LOAD_MODULES_DUMMY */
+
+/***********************************************************************
+ * Load Modules
+ **********************************************************************/
+/*!
+ * Load all modules in a given path.
+ * This will recurse into sub-directories.
+ * Does not throw, prints to std error.
+ * \param path the filesystem path
+ */
+static void load_module_path(const fs::path &path){
+ if (not fs::exists(path)){
+ //std::cerr << boost::format("Module path \"%s\" not found.") % path.string() << std::endl;
+ return;
+ }
+
+ //try to load the files in this path
+ if (fs::is_directory(path)){
+ for(
+ fs::directory_iterator dir_itr(path);
+ dir_itr != fs::directory_iterator();
+ ++dir_itr
+ ){
+ load_module_path(dir_itr->path());
+ }
+ return;
+ }
+
+ //its not a directory, try to load it
+ try{
+ load_module(path.string());
+ }
+ catch(const std::exception &err){
+ std::cerr << boost::format("Error: %s") % err.what() << std::endl;
+ }
+}
+
+std::vector<fs::path> get_module_paths(void); //defined in paths.cpp
+
+/*!
+ * Load all the modules given in the module paths.
+ */
+UHD_STATIC_BLOCK(load_modules){
+ BOOST_FOREACH(const fs::path &path, get_module_paths()){
+ load_module_path(path);
+ }
+}
diff --git a/host/lib/utils/log.cpp b/host/lib/utils/log.cpp
new file mode 100644
index 000000000..31d11796e
--- /dev/null
+++ b/host/lib/utils/log.cpp
@@ -0,0 +1,221 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/static.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/format.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#ifdef BOOST_MSVC
+//whoops! https://svn.boost.org/trac/boost/ticket/5287
+//enjoy this useless dummy class instead
+namespace boost{ namespace interprocess{
+ struct file_lock{
+ file_lock(const char * = NULL){}
+ void lock(void){}
+ void unlock(void){}
+ };
+}} //namespace
+#else
+#include <boost/interprocess/sync/file_lock.hpp>
+#endif
+#ifdef BOOST_MSVC
+#define USE_GET_TEMP_PATH
+#include <Windows.h> //GetTempPath
+#endif
+#include <stdio.h> //P_tmpdir
+#include <cstdlib> //getenv
+#include <fstream>
+#include <sstream>
+#include <cctype>
+
+namespace fs = boost::filesystem;
+namespace pt = boost::posix_time;
+namespace ip = boost::interprocess;
+
+/***********************************************************************
+ * Helper function to get the system's temporary path
+ **********************************************************************/
+static fs::path get_temp_path(void){
+ const char *tmp_path = NULL;
+
+ //try the official uhd temp path environment variable
+ tmp_path = std::getenv("UHD_TEMP_PATH");
+ if (tmp_path != NULL) return tmp_path;
+
+ //try the windows function if available
+ #ifdef USE_GET_TEMP_PATH
+ char lpBuffer[2048];
+ if (GetTempPath(sizeof(lpBuffer), lpBuffer)) return lpBuffer;
+ #endif
+
+ //try windows environment variables
+ tmp_path = std::getenv("TMP");
+ if (tmp_path != NULL) return tmp_path;
+
+ tmp_path = std::getenv("TEMP");
+ if (tmp_path != NULL) return tmp_path;
+
+ //try the stdio define if available
+ #ifdef P_tmpdir
+ return P_tmpdir;
+ #endif
+
+ //try unix environment variables
+ tmp_path = std::getenv("TMPDIR");
+ if (tmp_path != NULL) return tmp_path;
+
+ //give up and use the unix default
+ return "/tmp";
+}
+
+/***********************************************************************
+ * Global resources for the logger
+ **********************************************************************/
+class log_resource_type{
+public:
+ uhd::_log::verbosity_t level;
+
+ log_resource_type(void){
+
+ //file lock pointer must be null
+ _file_lock = NULL;
+
+ //set the default log level
+ level = uhd::_log::never;
+
+ //allow override from macro definition
+ #ifdef UHD_LOG_LEVEL
+ _set_log_level(BOOST_STRINGIZE(UHD_LOG_LEVEL));
+ #endif
+
+ //allow override from environment variable
+ const char * log_level_env = std::getenv("UHD_LOG_LEVEL");
+ if (log_level_env != NULL) _set_log_level(log_level_env);
+ }
+
+ ~log_resource_type(void){
+ boost::mutex::scoped_lock lock(_mutex);
+ _file_stream.close();
+ if (_file_lock != NULL) delete _file_lock;
+ }
+
+ void log_to_file(const std::string &log_msg){
+ boost::mutex::scoped_lock lock(_mutex);
+ if (_file_lock == NULL){
+ const std::string log_path = (get_temp_path() / "uhd.log").string();
+ _file_stream.open(log_path.c_str(), std::fstream::out | std::fstream::app);
+ _file_lock = new ip::file_lock(log_path.c_str());
+ }
+ _file_lock->lock();
+ _file_stream << log_msg << std::flush;
+ _file_lock->unlock();
+ }
+
+private:
+ //! set the log level from a string that is either a digit or an enum name
+ void _set_log_level(const std::string &log_level_str){
+ const uhd::_log::verbosity_t log_level_num = uhd::_log::verbosity_t(log_level_str[0]-'0');
+ if (std::isdigit(log_level_str[0]) and log_level_num >= uhd::_log::always and log_level_num <= uhd::_log::never){
+ this->level = log_level_num;
+ return;
+ }
+ #define if_lls_equal(name) else if(log_level_str == #name) this->level = uhd::_log::name
+ if_lls_equal(always);
+ if_lls_equal(often);
+ if_lls_equal(regularly);
+ if_lls_equal(rarely);
+ if_lls_equal(very_rarely);
+ if_lls_equal(never);
+ }
+
+ //file stream and lock:
+ std::ofstream _file_stream;
+ ip::file_lock *_file_lock;
+ boost::mutex _mutex;
+};
+
+UHD_SINGLETON_FCN(log_resource_type, log_rs);
+
+/***********************************************************************
+ * The logger object implementation
+ **********************************************************************/
+//! get the relative file path from the host directory
+static std::string get_rel_file_path(const fs::path &file){
+ fs::path abs_path = file.branch_path();
+ fs::path rel_path = file.leaf();
+ while (not abs_path.empty() and abs_path.leaf() != "host"){
+ rel_path = abs_path.leaf() / rel_path;
+ abs_path = abs_path.branch_path();
+ }
+ return rel_path.string();
+}
+
+struct uhd::_log::log::impl{
+ std::ostringstream ss;
+ verbosity_t verbosity;
+};
+
+uhd::_log::log::log(
+ const verbosity_t verbosity,
+ const std::string &file,
+ const unsigned int line,
+ const std::string &function
+){
+ _impl = UHD_PIMPL_MAKE(impl, ());
+ _impl->verbosity = verbosity;
+ const std::string time = pt::to_simple_string(pt::microsec_clock::local_time());
+ const std::string header1 = str(boost::format("-- %s - level %d") % time % int(verbosity));
+ const std::string header2 = str(boost::format("-- %s") % function).substr(0, 80);
+ const std::string header3 = str(boost::format("-- %s:%u") % get_rel_file_path(file) % line);
+ const std::string border = std::string(std::max(std::max(header1.size(), header2.size()), header3.size()), '-');
+ _impl->ss
+ << std::endl
+ << border << std::endl
+ << header1 << std::endl
+ << header2 << std::endl
+ << header3 << std::endl
+ << border << std::endl
+ ;
+}
+
+uhd::_log::log::~log(void){
+ if (_impl->verbosity < log_rs().level) return;
+ _impl->ss << std::endl;
+ try{
+ log_rs().log_to_file(_impl->ss.str());
+ }
+ catch(const std::exception &e){
+ /*!
+ * Critical behavior below.
+ * The following steps must happen in order to avoid a lock-up condition.
+ * This is because the message facility will call into the logging facility.
+ * Therefore we must disable the logger (level = never) before messaging.
+ */
+ log_rs().level = never;
+ UHD_MSG(error)
+ << "Logging failed: " << e.what() << std::endl
+ << "Logging has been disabled for this process" << std::endl
+ ;
+ }
+}
+
+std::ostream & uhd::_log::log::operator()(void){
+ return _impl->ss;
+}
diff --git a/host/lib/utils/msg.cpp b/host/lib/utils/msg.cpp
new file mode 100644
index 000000000..de98ada64
--- /dev/null
+++ b/host/lib/utils/msg.cpp
@@ -0,0 +1,128 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/static.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/foreach.hpp>
+#include <boost/tokenizer.hpp>
+#include <sstream>
+#include <iostream>
+
+/***********************************************************************
+ * Helper functions
+ **********************************************************************/
+#define tokenizer(inp, sep) \
+ boost::tokenizer<boost::char_separator<char> > \
+ (inp, boost::char_separator<char>(sep))
+
+static void msg_to_cout(const std::string &msg){
+ std::stringstream ss;
+
+ static bool just_had_a_newline = true;
+ BOOST_FOREACH(char ch, msg){
+ if (just_had_a_newline){
+ just_had_a_newline = false;
+ ss << "-- ";
+ }
+ if (ch == '\n'){
+ just_had_a_newline = true;
+ }
+ ss << ch;
+ }
+
+ std::cout << ss.str() << std::flush;
+}
+
+static void msg_to_cerr(const std::string &title, const std::string &msg){
+ std::stringstream ss;
+
+ ss << std::endl << title << ":" << std::endl;
+ BOOST_FOREACH(const std::string &line, tokenizer(msg, "\n")){
+ ss << " " << line << std::endl;
+ }
+
+ std::cerr << ss.str() << std::flush;
+}
+
+/***********************************************************************
+ * Global resources for the messenger
+ **********************************************************************/
+struct msg_resource_type{
+ boost::mutex mutex;
+ uhd::msg::handler_t handler;
+};
+
+UHD_SINGLETON_FCN(msg_resource_type, msg_rs);
+
+/***********************************************************************
+ * Setup the message handlers
+ **********************************************************************/
+void uhd::msg::register_handler(const handler_t &handler){
+ boost::mutex::scoped_lock lock(msg_rs().mutex);
+ msg_rs().handler = handler;
+}
+
+static void default_msg_handler(uhd::msg::type_t type, const std::string &msg){
+ switch(type){
+ case uhd::msg::fastpath:
+ std::cerr << msg << std::flush;
+ break;
+
+ case uhd::msg::status:
+ msg_to_cout(msg);
+ UHD_LOG << "Status message" << std::endl << msg;
+ break;
+
+ case uhd::msg::warning:
+ msg_to_cerr("UHD Warning", msg);
+ UHD_LOG << "Warning message" << std::endl << msg;
+ break;
+
+ case uhd::msg::error:
+ msg_to_cerr("UHD Error", msg);
+ UHD_LOG << "Error message" << std::endl << msg;
+ break;
+ }
+}
+
+UHD_STATIC_BLOCK(msg_register_default_handler){
+ uhd::msg::register_handler(&default_msg_handler);
+}
+
+/***********************************************************************
+ * The message object implementation
+ **********************************************************************/
+struct uhd::msg::_msg::impl{
+ std::ostringstream ss;
+ type_t type;
+};
+
+uhd::msg::_msg::_msg(const type_t type){
+ _impl = UHD_PIMPL_MAKE(impl, ());
+ _impl->type = type;
+}
+
+uhd::msg::_msg::~_msg(void){
+ boost::mutex::scoped_lock lock(msg_rs().mutex);
+ msg_rs().handler(_impl->type, _impl->ss.str());
+}
+
+std::ostream & uhd::msg::_msg::operator()(void){
+ return _impl->ss;
+}
diff --git a/host/lib/utils/paths.cpp b/host/lib/utils/paths.cpp
new file mode 100644
index 000000000..4fc877d5d
--- /dev/null
+++ b/host/lib/utils/paths.cpp
@@ -0,0 +1,108 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/config.hpp>
+#include <uhd/utils/paths.hpp>
+#include <boost/tokenizer.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/foreach.hpp>
+#include <boost/bind.hpp>
+#include <cstdlib>
+#include <string>
+#include <vector>
+#include <cstdlib> //getenv
+#include <cstdio> //P_tmpdir
+
+namespace fs = boost::filesystem;
+
+/***********************************************************************
+ * Determine the paths separator
+ **********************************************************************/
+#ifdef UHD_PLATFORM_WIN32
+ static const std::string env_path_sep = ";";
+#else
+ static const std::string env_path_sep = ":";
+#endif /*UHD_PLATFORM_WIN32*/
+
+#define path_tokenizer(inp) \
+ boost::tokenizer<boost::char_separator<char> > \
+ (inp, boost::char_separator<char>(env_path_sep.c_str()))
+
+/***********************************************************************
+ * Get a list of paths for an environment variable
+ **********************************************************************/
+static std::string get_env_var(const std::string &var_name, const std::string &def_val = ""){
+ const char *var_value_ptr = std::getenv(var_name.c_str());
+ return (var_value_ptr == NULL)? def_val : var_value_ptr;
+}
+
+static std::vector<fs::path> get_env_paths(const std::string &var_name){
+
+ std::string var_value = get_env_var(var_name);
+
+ //convert to filesystem path, filter blank paths
+ std::vector<fs::path> paths;
+ if (var_value.empty()) return paths; //FIXME boost tokenizer throws w/ blank strings on some platforms
+ BOOST_FOREACH(const std::string &path_string, path_tokenizer(var_value)){
+ if (path_string.empty()) continue;
+ paths.push_back(fs::system_complete(path_string));
+ }
+ return paths;
+}
+
+/***********************************************************************
+ * Get a list of special purpose paths
+ **********************************************************************/
+static fs::path get_uhd_pkg_data_path(void){
+ return fs::path(get_env_var("UHD_PKG_DATA_PATH", UHD_PKG_DATA_PATH));
+}
+
+std::vector<fs::path> get_image_paths(void){
+ std::vector<fs::path> paths = get_env_paths("UHD_IMAGE_PATH");
+ paths.push_back(get_uhd_pkg_data_path() / "images");
+ return paths;
+}
+
+std::vector<fs::path> get_module_paths(void){
+ std::vector<fs::path> paths = get_env_paths("UHD_MODULE_PATH");
+ paths.push_back(get_uhd_pkg_data_path() / "modules");
+ return paths;
+}
+
+/***********************************************************************
+ * Implement the functions in paths.hpp
+ **********************************************************************/
+std::string uhd::get_tmp_path(void){
+ const char *tmp_path = std::getenv("TMP");
+ if (tmp_path != NULL) return tmp_path;
+
+ #ifdef P_tmpdir
+ if (P_tmpdir != NULL) return P_tmpdir;
+ #endif
+
+ return "/tmp";
+}
+
+std::string uhd::get_app_path(void){
+ const char *appdata_path = std::getenv("APPDATA");
+ if (appdata_path != NULL) return appdata_path;
+
+ const char *home_path = std::getenv("HOME");
+ if (home_path != NULL) return home_path;
+
+ return uhd::get_tmp_path();
+}
diff --git a/host/lib/utils/static.cpp b/host/lib/utils/static.cpp
new file mode 100644
index 000000000..a0dea3372
--- /dev/null
+++ b/host/lib/utils/static.cpp
@@ -0,0 +1,32 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/utils/static.hpp>
+#include <stdexcept>
+#include <iostream>
+_uhd_static_fixture::_uhd_static_fixture(void (*fcn)(void), const char *name){
+ try{
+ fcn();
+ }
+ catch(const std::exception &e){
+ std::cerr << "Exception in static block " << name << std::endl;
+ std::cerr << " " << e.what() << std::endl;
+ }
+ catch(...){
+ std::cerr << "Exception in static block " << name << std::endl;
+ }
+}
diff --git a/host/lib/utils/tasks.cpp b/host/lib/utils/tasks.cpp
new file mode 100644
index 000000000..1f735de06
--- /dev/null
+++ b/host/lib/utils/tasks.cpp
@@ -0,0 +1,82 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/utils/tasks.hpp>
+#include <uhd/utils/msg.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/thread/barrier.hpp>
+#include <exception>
+#include <iostream>
+
+using namespace uhd;
+
+class task_impl : public task{
+public:
+
+ task_impl(const task_fcn_type &task_fcn):
+ _spawn_barrier(2)
+ {
+ _thread_group.create_thread(boost::bind(&task_impl::task_loop, this, task_fcn));
+ _spawn_barrier.wait();
+ }
+
+ ~task_impl(void){
+ _running = false;
+ _thread_group.interrupt_all();
+ _thread_group.join_all();
+ }
+
+private:
+
+ void task_loop(const task_fcn_type &task_fcn){
+ _running = true;
+ _spawn_barrier.wait();
+
+ try{
+ while (_running){
+ task_fcn();
+ }
+ }
+ catch(const boost::thread_interrupted &){
+ //this is an ok way to exit the task loop
+ }
+ catch(const std::exception &e){
+ do_error_msg(e.what());
+ }
+ catch(...){
+ //FIXME
+ //Unfortunately, this is also an ok way to end a task,
+ //because on some systems boost throws uncatchables.
+ }
+ }
+
+ void do_error_msg(const std::string &msg){
+ UHD_MSG(error)
+ << "An unexpected exception was caught in a task loop." << std::endl
+ << "The task loop will now exit, things may not work." << std::endl
+ << msg << std::endl
+ ;
+ }
+
+ boost::thread_group _thread_group;
+ boost::barrier _spawn_barrier;
+ bool _running;
+};
+
+task::sptr task::make(const task_fcn_type &task_fcn){
+ return task::sptr(new task_impl(task_fcn));
+}
diff --git a/host/lib/utils/thread_priority.cpp b/host/lib/utils/thread_priority.cpp
new file mode 100644
index 000000000..7c3faa37a
--- /dev/null
+++ b/host/lib/utils/thread_priority.cpp
@@ -0,0 +1,110 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/utils/thread_priority.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/exception.hpp>
+#include <boost/format.hpp>
+#include <iostream>
+
+bool uhd::set_thread_priority_safe(float priority, bool realtime){
+ try{
+ set_thread_priority(priority, realtime);
+ return true;
+ }catch(const std::exception &e){
+ UHD_MSG(warning) << boost::format(
+ "Unable to set the thread priority. Performance may be negatively affected.\n"
+ "Please see the general application notes in the manual for instructions.\n"
+ "%s\n"
+ ) % e.what();
+ return false;
+ }
+}
+
+static void check_priority_range(float priority){
+ if (priority > +1.0 or priority < -1.0)
+ throw uhd::value_error("priority out of range [-1.0, +1.0]");
+}
+
+/***********************************************************************
+ * Pthread API to set priority
+ **********************************************************************/
+#ifdef HAVE_PTHREAD_SETSCHEDPARAM
+ #include <pthread.h>
+
+ void uhd::set_thread_priority(float priority, bool realtime){
+ check_priority_range(priority);
+
+ //when realtime is not enabled, use sched other
+ int policy = (realtime)? SCHED_RR : SCHED_OTHER;
+
+ //we cannot have below normal priority, set to zero
+ if (priority < 0) priority = 0;
+
+ //get the priority bounds for the selected policy
+ int min_pri = sched_get_priority_min(policy);
+ int max_pri = sched_get_priority_max(policy);
+ if (min_pri == -1 or max_pri == -1) throw uhd::os_error("error in sched_get_priority_min/max");
+
+ //set the new priority and policy
+ sched_param sp;
+ sp.sched_priority = int(priority*(max_pri - min_pri)) + min_pri;
+ int ret = pthread_setschedparam(pthread_self(), policy, &sp);
+ if (ret != 0) throw uhd::os_error("error in pthread_setschedparam");
+ }
+#endif /* HAVE_PTHREAD_SETSCHEDPARAM */
+
+/***********************************************************************
+ * Windows API to set priority
+ **********************************************************************/
+#ifdef HAVE_WIN_SETTHREADPRIORITY
+ #include <windows.h>
+
+ void uhd::set_thread_priority(float priority, bool realtime){
+ check_priority_range(priority);
+
+ /*
+ * Process wide priority is no longer set.
+ * This is the responsibility of the application.
+ //set the priority class on the process
+ int pri_class = (realtime)? REALTIME_PRIORITY_CLASS : NORMAL_PRIORITY_CLASS;
+ if (SetPriorityClass(GetCurrentProcess(), pri_class) == 0)
+ throw uhd::os_error("error in SetPriorityClass");
+ */
+
+ //scale the priority value to the constants
+ int priorities[] = {
+ THREAD_PRIORITY_IDLE, THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_BELOW_NORMAL, THREAD_PRIORITY_NORMAL,
+ THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_HIGHEST, THREAD_PRIORITY_TIME_CRITICAL
+ };
+ size_t pri_index = size_t((priority+1.0)*6/2.0); // -1 -> 0, +1 -> 6
+
+ //set the thread priority on the thread
+ if (SetThreadPriority(GetCurrentThread(), priorities[pri_index]) == 0)
+ throw uhd::os_error("error in SetThreadPriority");
+ }
+#endif /* HAVE_WIN_SETTHREADPRIORITY */
+
+/***********************************************************************
+ * Unimplemented API to set priority
+ **********************************************************************/
+#ifdef HAVE_THREAD_PRIO_DUMMY
+ void uhd::set_thread_priority(float, bool){
+ throw uhd::not_implemented_error("set thread priority not implemented");
+ }
+
+#endif /* HAVE_THREAD_PRIO_DUMMY */
diff --git a/host/lib/version.cpp b/host/lib/version.cpp
new file mode 100644
index 000000000..42880b3c3
--- /dev/null
+++ b/host/lib/version.cpp
@@ -0,0 +1,37 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/version.hpp>
+#include <uhd/utils/static.hpp>
+#include <boost/version.hpp>
+#include <iostream>
+
+#ifndef UHD_DONT_PRINT_SYSTEM_INFO
+UHD_STATIC_BLOCK(print_system_info){
+ std::cout
+ << BOOST_PLATFORM << "; "
+ << BOOST_COMPILER << "; "
+ << "Boost_" << BOOST_VERSION << "; "
+ << "UHD_" << uhd::get_version_string()
+ << std::endl << std::endl
+ ;
+}
+#endif
+
+std::string uhd::get_version_string(void){
+ return "@UHD_VERSION@";
+}