summaryrefslogtreecommitdiffstats
path: root/host
diff options
context:
space:
mode:
authorJosh Blum <josh@joshknows.com>2011-06-27 12:49:38 -0700
committerJosh Blum <josh@joshknows.com>2011-06-27 12:49:38 -0700
commitd9630428843f2510d99cb494435e4dc273652250 (patch)
treed7083a6c804dd0f7466b0ef0416c7183766f5fe4 /host
parent99eabb0a6b70a3bdb4f1cb0844894c4023dcd629 (diff)
downloaduhd-d9630428843f2510d99cb494435e4dc273652250.tar.gz
uhd-d9630428843f2510d99cb494435e4dc273652250.tar.bz2
uhd-d9630428843f2510d99cb494435e4dc273652250.zip
usrp2: work on setting up controllers
Diffstat (limited to 'host')
-rw-r--r--host/include/uhd/property.hpp10
-rw-r--r--host/include/uhd/usrp/mboard_eeprom.hpp2
-rw-r--r--host/lib/CMakeLists.txt1
-rw-r--r--host/lib/usrp/CMakeLists.txt2
-rw-r--r--host/lib/usrp/cores/i2c_core_100.hpp2
-rw-r--r--host/lib/usrp/cores/rx_dsp_core_200.hpp2
-rw-r--r--host/lib/usrp/cores/rx_frontend_core_200.hpp2
-rw-r--r--host/lib/usrp/cores/spi_core_100.hpp2
-rw-r--r--host/lib/usrp/cores/time64_core_200.hpp2
-rw-r--r--host/lib/usrp/cores/tx_dsp_core_200.hpp2
-rw-r--r--host/lib/usrp/cores/tx_frontend_core_200.hpp2
-rw-r--r--host/lib/usrp/mboard_eeprom.cpp2
-rw-r--r--host/lib/usrp2/CMakeLists.txt33
-rw-r--r--host/lib/usrp2/clock_ctrl.cpp377
-rw-r--r--host/lib/usrp2/clock_ctrl.hpp109
-rw-r--r--host/lib/usrp2/codec_ctrl.cpp216
-rw-r--r--host/lib/usrp2/codec_ctrl.hpp68
-rw-r--r--host/lib/usrp2/fw_common.h154
-rw-r--r--host/lib/usrp2/usrp2_clk_regs.hpp87
-rw-r--r--host/lib/usrp2/usrp2_iface.cpp412
-rw-r--r--host/lib/usrp2/usrp2_iface.hpp80
-rw-r--r--host/lib/usrp2/usrp2_impl.cpp206
-rw-r--r--host/lib/usrp2/usrp2_impl.hpp82
-rw-r--r--host/lib/usrp2/usrp2_regs.hpp221
24 files changed, 2066 insertions, 10 deletions
diff --git a/host/include/uhd/property.hpp b/host/include/uhd/property.hpp
index 6f337cb52..10ce463e0 100644
--- a/host/include/uhd/property.hpp
+++ b/host/include/uhd/property.hpp
@@ -35,6 +35,16 @@ public:
typedef boost::function<T(void)> publisher_type;
typedef boost::function<T(const T &)> master_type;
+ //! Default constructor
+ property(void){
+ /* NOP */
+ }
+
+ //! Value initializer constructor
+ property(const T &value){
+ _value = value;
+ }
+
/*!
* Register a master subscriber into the property.
* A master is a special subscriber that coerces the value.
diff --git a/host/include/uhd/usrp/mboard_eeprom.hpp b/host/include/uhd/usrp/mboard_eeprom.hpp
index 52363b95c..ea66bb2e0 100644
--- a/host/include/uhd/usrp/mboard_eeprom.hpp
+++ b/host/include/uhd/usrp/mboard_eeprom.hpp
@@ -56,7 +56,7 @@ namespace uhd{ namespace usrp{
* \param iface the interface to i2c
* \param map the map type enum
*/
- void commit(i2c_iface &iface, map_type map);
+ void commit(i2c_iface &iface, map_type map) const;
};
diff --git a/host/lib/CMakeLists.txt b/host/lib/CMakeLists.txt
index 60ddbce5b..a8f490f0a 100644
--- a/host/lib/CMakeLists.txt
+++ b/host/lib/CMakeLists.txt
@@ -72,6 +72,7 @@ INCLUDE_SUBDIRECTORY(types)
INCLUDE_SUBDIRECTORY(convert)
INCLUDE_SUBDIRECTORY(transport)
INCLUDE_SUBDIRECTORY(usrp)
+INCLUDE_SUBDIRECTORY(usrp2)
INCLUDE_SUBDIRECTORY(utils)
########################################################################
diff --git a/host/lib/usrp/CMakeLists.txt b/host/lib/usrp/CMakeLists.txt
index 0c964825c..32ba79fbb 100644
--- a/host/lib/usrp/CMakeLists.txt
+++ b/host/lib/usrp/CMakeLists.txt
@@ -37,6 +37,6 @@ INCLUDE_SUBDIRECTORY(cores)
INCLUDE_SUBDIRECTORY(dboard)
INCLUDE_SUBDIRECTORY(fx2)
INCLUDE_SUBDIRECTORY(usrp1)
-INCLUDE_SUBDIRECTORY(usrp2)
+#INCLUDE_SUBDIRECTORY(usrp2)
INCLUDE_SUBDIRECTORY(b100)
INCLUDE_SUBDIRECTORY(e100)
diff --git a/host/lib/usrp/cores/i2c_core_100.hpp b/host/lib/usrp/cores/i2c_core_100.hpp
index 4eb1b8086..f7a5ae4f7 100644
--- a/host/lib/usrp/cores/i2c_core_100.hpp
+++ b/host/lib/usrp/cores/i2c_core_100.hpp
@@ -29,7 +29,7 @@ public:
typedef boost::shared_ptr<i2c_core_100> sptr;
//! makes a new i2c core from iface and slave base
- sptr make(wb_iface::sptr iface, const size_t 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.hpp b/host/lib/usrp/cores/rx_dsp_core_200.hpp
index 75935bd1e..a88c39a2f 100644
--- a/host/lib/usrp/cores/rx_dsp_core_200.hpp
+++ b/host/lib/usrp/cores/rx_dsp_core_200.hpp
@@ -29,7 +29,7 @@ class rx_dsp_core_200 : boost::noncopyable{
public:
typedef boost::shared_ptr<rx_dsp_core_200> sptr;
- sptr make(
+ static sptr make(
wb_iface::sptr iface,
const size_t dsp_base, const size_t ctrl_base,
const boost::uint32_t sid, const size_t nsamps
diff --git a/host/lib/usrp/cores/rx_frontend_core_200.hpp b/host/lib/usrp/cores/rx_frontend_core_200.hpp
index 19fcce884..a950e2bb7 100644
--- a/host/lib/usrp/cores/rx_frontend_core_200.hpp
+++ b/host/lib/usrp/cores/rx_frontend_core_200.hpp
@@ -29,7 +29,7 @@ class rx_frontend_core_200 : boost::noncopyable{
public:
typedef boost::shared_ptr<rx_frontend_core_200> sptr;
- sptr make(wb_iface::sptr iface, const size_t base);
+ static sptr make(wb_iface::sptr iface, const size_t base);
virtual void set_mux(const bool swap) = 0;
diff --git a/host/lib/usrp/cores/spi_core_100.hpp b/host/lib/usrp/cores/spi_core_100.hpp
index 2fd6b5206..87d328aaa 100644
--- a/host/lib/usrp/cores/spi_core_100.hpp
+++ b/host/lib/usrp/cores/spi_core_100.hpp
@@ -29,7 +29,7 @@ public:
typedef boost::shared_ptr<spi_core_100> sptr;
//! makes a new spi core from iface and slave base
- sptr make(wb_iface::sptr iface, const size_t 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.hpp b/host/lib/usrp/cores/time64_core_200.hpp
index c6a4cf7e5..4145d1f93 100644
--- a/host/lib/usrp/cores/time64_core_200.hpp
+++ b/host/lib/usrp/cores/time64_core_200.hpp
@@ -36,7 +36,7 @@ public:
};
//! makes a new time64 core from iface and slave base
- sptr make(
+ 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
diff --git a/host/lib/usrp/cores/tx_dsp_core_200.hpp b/host/lib/usrp/cores/tx_dsp_core_200.hpp
index 3176d495f..479023caa 100644
--- a/host/lib/usrp/cores/tx_dsp_core_200.hpp
+++ b/host/lib/usrp/cores/tx_dsp_core_200.hpp
@@ -27,7 +27,7 @@ class tx_dsp_core_200 : boost::noncopyable{
public:
typedef boost::shared_ptr<tx_dsp_core_200> sptr;
- sptr make(
+ static sptr make(
wb_iface::sptr iface,
const size_t dsp_base, const size_t ctrl_base,
const boost::uint32_t sid
diff --git a/host/lib/usrp/cores/tx_frontend_core_200.hpp b/host/lib/usrp/cores/tx_frontend_core_200.hpp
index c4a06a14a..9e4a7bc79 100644
--- a/host/lib/usrp/cores/tx_frontend_core_200.hpp
+++ b/host/lib/usrp/cores/tx_frontend_core_200.hpp
@@ -29,7 +29,7 @@ class tx_frontend_core_200 : boost::noncopyable{
public:
typedef boost::shared_ptr<tx_frontend_core_200> sptr;
- sptr make(wb_iface::sptr iface, const size_t base);
+ static sptr make(wb_iface::sptr iface, const size_t base);
virtual void set_mux(const std::string &mode) = 0;
diff --git a/host/lib/usrp/mboard_eeprom.cpp b/host/lib/usrp/mboard_eeprom.cpp
index 2ee4a9284..79cc2f5b2 100644
--- a/host/lib/usrp/mboard_eeprom.cpp
+++ b/host/lib/usrp/mboard_eeprom.cpp
@@ -335,7 +335,7 @@ mboard_eeprom_t::mboard_eeprom_t(i2c_iface &iface, map_type map){
}
}
-void mboard_eeprom_t::commit(i2c_iface &iface, map_type map){
+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;
diff --git a/host/lib/usrp2/CMakeLists.txt b/host/lib/usrp2/CMakeLists.txt
new file mode 100644
index 000000000..d7d6cc69f
--- /dev/null
+++ b/host/lib/usrp2/CMakeLists.txt
@@ -0,0 +1,33 @@
+#
+# 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
+########################################################################
+LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/clock_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/clock_ctrl.hpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/codec_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/codec_ctrl.hpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/fw_common.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_iface.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_iface.hpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_impl.hpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_regs.hpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_clk_regs.hpp
+)
diff --git a/host/lib/usrp2/clock_ctrl.cpp b/host/lib/usrp2/clock_ctrl.cpp
new file mode 100644
index 000000000..66c7a6c28
--- /dev/null
+++ b/host/lib/usrp2/clock_ctrl.cpp
@@ -0,0 +1,377 @@
+//
+// 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 and output clock 5 (cmos) on USRP2+
+ void enable_tx_dboard_clock(bool enb){
+ switch(clk_regs.tx_db) {
+ case 5: //USRP2+
+ _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 6: //USRP2
+ _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;
+ }
+
+ 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/usrp2/clock_ctrl.hpp b/host/lib/usrp2/clock_ctrl.hpp
new file mode 100644
index 000000000..9ccbc959e
--- /dev/null
+++ b/host/lib/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/usrp2/codec_ctrl.cpp b/host/lib/usrp2/codec_ctrl.cpp
new file mode 100644
index 000000000..06bf83b15
--- /dev/null
+++ b/host/lib/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/usrp2/codec_ctrl.hpp b/host/lib/usrp2/codec_ctrl.hpp
new file mode 100644
index 000000000..ca300e2b1
--- /dev/null
+++ b/host/lib/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/usrp2/fw_common.h b/host/lib/usrp2/fw_common.h
new file mode 100644
index 000000000..21abc6aed
--- /dev/null
+++ b/host/lib/usrp2/fw_common.h
@@ -0,0 +1,154 @@
+//
+// 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 7
+#define USRP2_FW_COMPAT_NUM 10
+
+//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_DSP0_PORT 49156
+#define USRP2_UDP_ERR0_PORT 49157
+#define USRP2_UDP_DSP1_PORT 49158
+
+////////////////////////////////////////////////////////////////////////
+// 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_HEY_WRITE_THIS_UART_FOR_ME_BRO = 'u',
+ USRP2_CTRL_ID_MAN_I_TOTALLY_WROTE_THAT_UART_DUDE = 'U',
+
+ USRP2_CTRL_ID_SO_LIKE_CAN_YOU_READ_THIS_UART_BRO = 'v',
+ USRP2_CTRL_ID_I_HELLA_READ_THAT_UART_DUDE = 'V',
+
+ 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 {
+ uint8_t dev;
+ uint8_t bytes;
+ uint8_t data[20];
+ } uart_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/usrp2/usrp2_clk_regs.hpp b/host/lib/usrp2/usrp2_clk_regs.hpp
new file mode 100644
index 000000000..8b185eac0
--- /dev/null
+++ b/host/lib/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/usrp2/usrp2_iface.cpp b/host/lib/usrp2/usrp2_iface.cpp
new file mode 100644
index 000000000..0db9e5d58
--- /dev/null
+++ b/host/lib/usrp2/usrp2_iface.cpp
@@ -0,0 +1,412 @@
+//
+// 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/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/tokenizer.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/thread/barrier.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 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 = USRP2_FW_COMPAT_NUM;
+static const boost::uint32_t MIN_PROTO_COMPAT_UART = 7;
+
+// 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 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;
+ 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){
+ this->lock_device(false);
+ }
+
+/***********************************************************************
+ * Device locking
+ **********************************************************************/
+
+ void lock_device(bool lock){
+ if (lock){
+ boost::barrier spawn_barrier(2);
+ _lock_thread_group.create_thread(boost::bind(&usrp2_iface_impl::lock_loop, this, boost::ref(spawn_barrier)));
+ spawn_barrier.wait();
+ }
+ else{
+ _lock_thread_group.interrupt_all();
+ _lock_thread_group.join_all();
+ }
+ }
+
+ bool is_device_locked(void){
+ boost::uint32_t lock_secs = 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);
+ boost::uint32_t curr_secs = this->peek32(U2_REG_TIME64_SECS_RB_IMM);
+
+ //if the difference is larger, assume not locked anymore
+ if (curr_secs - lock_secs >= 3) return false;
+
+ //otherwise only lock if the device hash is different that ours
+ return lock_gpid != boost::uint32_t(get_gpid());
+ }
+
+ void lock_loop(boost::barrier &spawn_barrier){
+ spawn_barrier.wait();
+
+ try{
+ this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_POKE32>(U2_FW_REG_LOCK_GPID, boost::uint32_t(get_gpid()));
+ while(true){
+ //re-lock in loop
+ boost::uint32_t curr_secs = this->peek32(U2_REG_TIME64_SECS_RB_IMM);
+ this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_POKE32>(U2_FW_REG_LOCK_TIME, curr_secs);
+ //sleep for a bit
+ boost::this_thread::sleep(boost::posix_time::milliseconds(1500));
+ }
+ }
+ catch(const boost::thread_interrupted &){
+ this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_POKE32>(U2_FW_REG_LOCK_TIME, 0); //unlock on exit
+ }
+ catch(const std::exception &e){
+ UHD_MSG(error)
+ << "An unexpected exception was caught in the locker loop." << std::endl
+ << "The device will automatically unlock from this process." << std::endl
+ << e.what() << std::endl
+ ;
+ }
+ }
+
+/***********************************************************************
+ * 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;
+ }
+
+/***********************************************************************
+ * UART
+ **********************************************************************/
+ void write_uart(boost::uint8_t dev, const std::string &buf){
+ //first tokenize the string into 20-byte substrings
+ boost::offset_separator f(20, 20, true, true);
+ boost::tokenizer<boost::offset_separator> tok(buf, f);
+ std::vector<std::string> queue(tok.begin(), tok.end());
+
+ BOOST_FOREACH(std::string item, queue) {
+ //setup the out data
+ usrp2_ctrl_data_t out_data = usrp2_ctrl_data_t();
+ out_data.id = htonl(USRP2_CTRL_ID_HEY_WRITE_THIS_UART_FOR_ME_BRO);
+ out_data.data.uart_args.dev = dev;
+ out_data.data.uart_args.bytes = item.size();
+
+ //limitation of uart transaction size
+ UHD_ASSERT_THROW(item.size() <= sizeof(out_data.data.uart_args.data));
+
+ //copy in the data
+ std::copy(item.begin(), item.end(), out_data.data.uart_args.data);
+
+ //send and recv
+ usrp2_ctrl_data_t in_data = this->ctrl_send_and_recv(out_data, MIN_PROTO_COMPAT_UART);
+ UHD_ASSERT_THROW(ntohl(in_data.id) == USRP2_CTRL_ID_MAN_I_TOTALLY_WROTE_THAT_UART_DUDE);
+ }
+ }
+
+ std::string read_uart(boost::uint8_t dev){
+ int readlen = 20;
+ std::string result;
+ while(readlen == 20) { //while we keep receiving full packets
+ //setup the out data
+ usrp2_ctrl_data_t out_data = usrp2_ctrl_data_t();
+ out_data.id = htonl(USRP2_CTRL_ID_SO_LIKE_CAN_YOU_READ_THIS_UART_BRO);
+ out_data.data.uart_args.dev = dev;
+ out_data.data.uart_args.bytes = 20;
+
+ //limitation of uart transaction size
+ //UHD_ASSERT_THROW(num_bytes <= sizeof(out_data.data.uart_args.data));
+
+ //send and recv
+ usrp2_ctrl_data_t in_data = this->ctrl_send_and_recv(out_data, MIN_PROTO_COMPAT_UART);
+ UHD_ASSERT_THROW(ntohl(in_data.id) == USRP2_CTRL_ID_I_HELLA_READ_THAT_UART_DUDE);
+ readlen = in_data.data.uart_args.bytes;
+
+ //copy out the data
+ result += std::string((const char *)in_data.data.uart_args.data, (size_t)readlen);
+ }
+ return result;
+ }
+
+ gps_send_fn_t get_gps_write_fn(void) {
+ return boost::bind(&usrp2_iface_impl::write_uart, this, 2, _1); //2 is the GPS UART port on USRP2
+ }
+
+ gps_recv_fn_t get_gps_read_fn(void) {
+ return boost::bind(&usrp2_iface_impl::read_uart, this, 2); //2 is the GPS UART port on USRP2
+ }
+
+/***********************************************************************
+ * 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);
+
+ //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), CTRL_RECV_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 uhd::runtime_error("no control response");
+ }
+
+ rev_type get_rev(void){
+ switch (boost::lexical_cast<boost::uint16_t>(mb_eeprom["rev"])){
+ 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();
+ }
+
+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
+ boost::thread_group _lock_thread_group;
+};
+
+/***********************************************************************
+ * 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/usrp2/usrp2_iface.hpp b/host/lib/usrp2/usrp2_iface.hpp
new file mode 100644
index 000000000..f36febce4
--- /dev/null
+++ b/host/lib/usrp2/usrp2_iface.hpp
@@ -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/>.
+//
+
+#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>
+
+//TODO: kill this crap when you have the top level GPS include file
+typedef boost::function<void(std::string)> gps_send_fn_t;
+typedef boost::function<std::string(void)> gps_recv_fn_t;
+
+/*!
+ * 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 uhd::uart_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);
+
+ virtual gps_recv_fn_t get_gps_read_fn(void) = 0;
+ virtual gps_send_fn_t get_gps_write_fn(void) = 0;
+
+ //! 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;
+
+ //motherboard eeprom map structure
+ uhd::usrp::mboard_eeprom_t mb_eeprom;
+};
+
+#endif /* INCLUDED_USRP2_IFACE_HPP */
diff --git a/host/lib/usrp2/usrp2_impl.cpp b/host/lib/usrp2/usrp2_impl.cpp
new file mode 100644
index 000000000..9e08093dd
--- /dev/null
+++ b/host/lib/usrp2/usrp2_impl.cpp
@@ -0,0 +1,206 @@
+//
+// 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_impl.hpp"
+#include "fw_common.h"
+#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/usrp/device_props.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/utils/static.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/format.hpp>
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/bind.hpp>
+#include <boost/asio/ip/address_v4.hpp>
+#include <boost/asio.hpp> //used for htonl and ntohl
+#include <vector>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+namespace asio = boost::asio;
+
+/***********************************************************************
+ * 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;
+}
+
+/***********************************************************************
+ * 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
+ }
+
+ 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();
+
+ _mboard_stuff.resize(device_args.size());
+ for (size_t mb = 0; mb < _mboard_stuff.size(); mb++){
+ property_tree::path_type mb_path = str(boost::format("/mboard%u") % mb);
+
+ //create the iface that controls i2c, spi, uart, and wb
+ _mboard_stuff[mb].iface = usrp2_iface::make(udp_simple::make_connected(
+ device_args[mb]["addr"], BOOST_STRINGIZE(USRP2_UDP_CTRL_PORT)
+ ));
+
+ //setup the mboard eeprom
+ //TODO perform eeprom read here (not in iface)
+ //TODO move the iface REV checks out of here
+ property<usrp::mboard_eeprom_t> mb_eeprom_prop;
+ mb_eeprom_prop.set(_mboard_stuff[mb].iface->mb_eeprom);
+ mb_eeprom_prop.subscribe(boost::bind(&usrp2_impl::set_mb_eeprom, this, mb, _1));
+ _tree->create(mb_path / "eeprom", mb_eeprom_prop);
+
+ //create clock control objects
+ _mboard_stuff[mb].clock = usrp2_clock_ctrl::make(_mboard_stuff[mb].iface);
+ property<double> tick_rate_prop(_mboard_stuff[mb].clock->get_master_clock_rate());
+ _tree->create(mb_path / "tick_rate", tick_rate_prop);
+
+ //create codec control objects
+ _mboard_stuff[mb].codec = usrp2_codec_ctrl::make(_mboard_stuff[mb].iface);
+
+ //create frontend control objects
+ _mboard_stuff[mb].rx_fe = rx_frontend_core_200::make(
+ _mboard_stuff[mb].iface, U2_REG_SR_ADDR(SR_RX_FRONT)
+ );
+ _mboard_stuff[mb].tx_fe = tx_frontend_core_200::make(
+ _mboard_stuff[mb].iface, U2_REG_SR_ADDR(SR_TX_FRONT)
+ );
+
+ //create dsp control objects
+
+ //create time control objects
+
+ }
+
+}
+
+void usrp2_impl::set_mb_eeprom(const size_t which_mb, const uhd::usrp::mboard_eeprom_t &mb_eeprom){
+ mb_eeprom.commit(*(_mboard_stuff[which_mb].iface), mboard_eeprom_t::MAP_N100);
+}
diff --git a/host/lib/usrp2/usrp2_impl.hpp b/host/lib/usrp2/usrp2_impl.hpp
new file mode 100644
index 000000000..c3a859851
--- /dev/null
+++ b/host/lib/usrp2/usrp2_impl.hpp
@@ -0,0 +1,82 @@
+//
+// 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_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 <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/otw_type.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>
+
+/*!
+ * 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:
+ usrp2_impl(const uhd::device_addr_t &_device_addr);
+ uhd::property_tree::sptr _tree;
+private:
+ struct mboard_stuff_type{
+ usrp2_iface::sptr iface;
+ usrp2_clock_ctrl::sptr clock;
+ usrp2_codec_ctrl::sptr codec;
+ rx_frontend_core_200::sptr rx_fe;
+ tx_frontend_core_200::sptr tx_fe;
+ rx_dsp_core_200::sptr rx_dsp;
+ tx_dsp_core_200::sptr tx_dsp;
+ //TODO time core
+ };
+ std::vector<mboard_stuff_type> _mboard_stuff;
+
+ void set_mb_eeprom(const size_t which_mb, const uhd::usrp::mboard_eeprom_t &mb_eeprom);
+
+};
+
+#endif /* INCLUDED_USRP2_IMPL_HPP */
diff --git a/host/lib/usrp2/usrp2_regs.hpp b/host/lib/usrp2/usrp2_regs.hpp
new file mode 100644
index 000000000..19c1b45f1
--- /dev/null
+++ b/host/lib/usrp2/usrp2_regs.hpp
@@ -0,0 +1,221 @@
+//
+// 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_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_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_UDP_SM 192 // 64
+
+#define U2_REG_SR_ADDR(sr) (SETTING_REGS_BASE + (4 * (sr)))
+
+/////////////////////////////////////////////////
+// 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
+
+/////////////////////////////////////////////////
+// VITA49 64 bit time (write only)
+////////////////////////////////////////////////
+#define U2_REG_TIME64_SECS U2_REG_SR_ADDR(SR_TIME64 + 0)
+#define U2_REG_TIME64_TICKS U2_REG_SR_ADDR(SR_TIME64 + 1)
+#define U2_REG_TIME64_FLAGS U2_REG_SR_ADDR(SR_TIME64 + 2)
+#define U2_REG_TIME64_IMM U2_REG_SR_ADDR(SR_TIME64 + 3)
+#define U2_REG_TIME64_TPS U2_REG_SR_ADDR(SR_TIME64 + 4)
+#define U2_REG_TIME64_MIMO_SYNC U2_REG_SR_ADDR(SR_TIME64 + 5)
+
+//pps flags (see above)
+#define U2_FLAG_TIME64_PPS_NEGEDGE (0 << 0)
+#define U2_FLAG_TIME64_PPS_POSEDGE (1 << 0)
+#define U2_FLAG_TIME64_PPS_SMA (0 << 1)
+#define U2_FLAG_TIME64_PPS_MIMO (1 << 1)
+
+#define U2_FLAG_TIME64_LATCH_NOW 1
+#define U2_FLAG_TIME64_LATCH_NEXT_PPS 0
+
+/////////////////////////////////////////////////
+// Readback regs
+////////////////////////////////////////////////
+#define U2_REG_STATUS READBACK_BASE + 4*8
+#define U2_REG_TIME64_SECS_RB_IMM READBACK_BASE + 4*10
+#define U2_REG_TIME64_TICKS_RB_IMM READBACK_BASE + 4*11
+#define U2_REG_COMPAT_NUM_RB READBACK_BASE + 4*12
+#define U2_REG_IRQ_RB READBACK_BASE + 4*13
+#define U2_REG_TIME64_SECS_RB_PPS READBACK_BASE + 4*14
+#define U2_REG_TIME64_TICKS_RB_PPS READBACK_BASE + 4*15
+
+/////////////////////////////////////////////////
+// RX FE
+////////////////////////////////////////////////
+#define U2_REG_RX_FE_SWAP_IQ U2_REG_SR_ADDR(SR_RX_FRONT + 0) //lower bit
+#define U2_REG_RX_FE_MAG_CORRECTION U2_REG_SR_ADDR(SR_RX_FRONT + 1) //18 bits
+#define U2_REG_RX_FE_PHASE_CORRECTION U2_REG_SR_ADDR(SR_RX_FRONT + 2) //18 bits
+#define U2_REG_RX_FE_OFFSET_I U2_REG_SR_ADDR(SR_RX_FRONT + 3) //18 bits
+#define U2_REG_RX_FE_OFFSET_Q U2_REG_SR_ADDR(SR_RX_FRONT + 4) //18 bits
+
+/////////////////////////////////////////////////
+// TX FE
+////////////////////////////////////////////////
+#define U2_REG_TX_FE_DC_OFFSET_I U2_REG_SR_ADDR(SR_TX_FRONT + 0) //24 bits
+#define U2_REG_TX_FE_DC_OFFSET_Q U2_REG_SR_ADDR(SR_TX_FRONT + 1) //24 bits
+#define U2_REG_TX_FE_MAC_CORRECTION U2_REG_SR_ADDR(SR_TX_FRONT + 2) //18 bits
+#define U2_REG_TX_FE_PHASE_CORRECTION U2_REG_SR_ADDR(SR_TX_FRONT + 3) //18 bits
+#define U2_REG_TX_FE_MUX U2_REG_SR_ADDR(SR_TX_FRONT + 4) //8 bits (std output = 0x10, reversed = 0x01)
+
+/////////////////////////////////////////////////
+// DSP TX Regs
+////////////////////////////////////////////////
+#define U2_REG_DSP_TX_FREQ U2_REG_SR_ADDR(SR_TX_DSP + 0)
+#define U2_REG_DSP_TX_SCALE_IQ U2_REG_SR_ADDR(SR_TX_DSP + 1)
+#define U2_REG_DSP_TX_INTERP_RATE U2_REG_SR_ADDR(SR_TX_DSP + 2)
+
+/////////////////////////////////////////////////
+// DSP RX Regs
+////////////////////////////////////////////////
+#define U2_REG_DSP_RX_HELPER(which, offset) ((which == 0)? \
+ (U2_REG_SR_ADDR(SR_RX_DSP0 + offset)) : \
+ (U2_REG_SR_ADDR(SR_RX_DSP1 + offset)))
+
+#define U2_REG_DSP_RX_FREQ(which) U2_REG_DSP_RX_HELPER(which, 0)
+#define U2_REG_DSP_RX_DECIM(which) U2_REG_DSP_RX_HELPER(which, 2)
+#define U2_REG_DSP_RX_MUX(which) U2_REG_DSP_RX_HELPER(which, 3)
+
+#define U2_FLAG_DSP_RX_MUX_SWAP_IQ (1 << 0)
+#define U2_FLAG_DSP_RX_MUX_REAL_MODE (1 << 1)
+
+////////////////////////////////////////////////
+// GPIO
+////////////////////////////////////////////////
+#define U2_REG_GPIO_IO GPIO_BASE + 0
+#define U2_REG_GPIO_DDR GPIO_BASE + 4
+#define U2_REG_GPIO_TX_SEL GPIO_BASE + 8
+#define U2_REG_GPIO_RX_SEL GPIO_BASE + 12
+
+// each 2-bit sel field is layed out this way
+#define U2_FLAG_GPIO_SEL_GPIO 0 // if pin is an output, set by GPIO register
+#define U2_FLAG_GPIO_SEL_ATR 1 // if pin is an output, set by ATR logic
+#define U2_FLAG_GPIO_SEL_DEBUG_0 2 // if pin is an output, debug lines from FPGA fabric
+#define U2_FLAG_GPIO_SEL_DEBUG_1 3 // if pin is an output, debug lines from FPGA fabric
+
+///////////////////////////////////////////////////
+// ATR Controller
+////////////////////////////////////////////////
+#define U2_REG_ATR_IDLE_TXSIDE ATR_BASE + 0
+#define U2_REG_ATR_IDLE_RXSIDE ATR_BASE + 2
+#define U2_REG_ATR_INTX_TXSIDE ATR_BASE + 4
+#define U2_REG_ATR_INTX_RXSIDE ATR_BASE + 6
+#define U2_REG_ATR_INRX_TXSIDE ATR_BASE + 8
+#define U2_REG_ATR_INRX_RXSIDE ATR_BASE + 10
+#define U2_REG_ATR_FULL_TXSIDE ATR_BASE + 12
+#define U2_REG_ATR_FULL_RXSIDE ATR_BASE + 14
+
+///////////////////////////////////////////////////
+// RX CTRL regs
+///////////////////////////////////////////////////
+#define U2_REG_RX_CTRL_HELPER(which, offset) ((which == 0)? \
+ (U2_REG_SR_ADDR(SR_RX_CTRL0 + offset)) : \
+ (U2_REG_SR_ADDR(SR_RX_CTRL1 + offset)))
+
+#define U2_REG_RX_CTRL_STREAM_CMD(which) U2_REG_RX_CTRL_HELPER(which, 0)
+#define U2_REG_RX_CTRL_TIME_SECS(which) U2_REG_RX_CTRL_HELPER(which, 1)
+#define U2_REG_RX_CTRL_TIME_TICKS(which) U2_REG_RX_CTRL_HELPER(which, 2)
+#define U2_REG_RX_CTRL_CLEAR(which) U2_REG_RX_CTRL_HELPER(which, 3)
+#define U2_REG_RX_CTRL_VRT_HDR(which) U2_REG_RX_CTRL_HELPER(which, 4)
+#define U2_REG_RX_CTRL_VRT_SID(which) U2_REG_RX_CTRL_HELPER(which, 5)
+#define U2_REG_RX_CTRL_VRT_TLR(which) U2_REG_RX_CTRL_HELPER(which, 6)
+#define U2_REG_RX_CTRL_NSAMPS_PP(which) U2_REG_RX_CTRL_HELPER(which, 7)
+#define U2_REG_RX_CTRL_NCHANNELS(which) U2_REG_RX_CTRL_HELPER(which, 8)
+
+///////////////////////////////////////////////////
+// TX CTRL regs
+///////////////////////////////////////////////////
+#define U2_REG_TX_CTRL_NUM_CHAN U2_REG_SR_ADDR(SR_TX_CTRL + 0)
+#define U2_REG_TX_CTRL_CLEAR_STATE U2_REG_SR_ADDR(SR_TX_CTRL + 1)
+#define U2_REG_TX_CTRL_REPORT_SID U2_REG_SR_ADDR(SR_TX_CTRL + 2)
+#define U2_REG_TX_CTRL_POLICY U2_REG_SR_ADDR(SR_TX_CTRL + 3)
+#define U2_REG_TX_CTRL_CYCLES_PER_UP U2_REG_SR_ADDR(SR_TX_CTRL + 4)
+#define U2_REG_TX_CTRL_PACKETS_PER_UP U2_REG_SR_ADDR(SR_TX_CTRL + 5)
+
+#define U2_FLAG_TX_CTRL_POLICY_WAIT (0x1 << 0)
+#define U2_FLAG_TX_CTRL_POLICY_NEXT_PACKET (0x1 << 1)
+#define U2_FLAG_TX_CTRL_POLICY_NEXT_BURST (0x1 << 2)
+
+//enable flag for registers: cycles and packets per update packet
+#define U2_FLAG_TX_CTRL_UP_ENB (1ul << 31)
+
+#endif /* INCLUDED_USRP2_REGS_HPP */