diff options
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 */  | 
