diff options
Diffstat (limited to 'host')
23 files changed, 1583 insertions, 191 deletions
| diff --git a/host/lib/gain_group.cpp b/host/lib/gain_group.cpp new file mode 100644 index 000000000..1be09dee2 --- /dev/null +++ b/host/lib/gain_group.cpp @@ -0,0 +1,149 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/utils/gain_group.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/utils/algorithm.hpp> +#include <uhd/utils/assert.hpp> +#include <boost/foreach.hpp> +#include <boost/bind.hpp> +#include <algorithm> +#include <vector> +#include <iostream> + +using namespace uhd; + +static const bool verbose = false; + +static bool compare_by_step_size( +    const size_t &rhs, const size_t &lhs, std::vector<gain_fcns_t> &fcns +){ +    return fcns.at(rhs).get_range().step > fcns.at(lhs).get_range().step; +} + +/*********************************************************************** + * gain group implementation + **********************************************************************/ +class gain_group_impl : public gain_group{ +public: +    gain_group_impl(void){ +        /*NOP*/ +    } + +    gain_range_t get_range(void){ +        float overall_min = 0, overall_max = 0, overall_step = 0; +        BOOST_FOREACH(const gain_fcns_t &fcns, get_all_fcns()){ +            const gain_range_t range = fcns.get_range(); +            overall_min += range.min; +            overall_max += range.max; +            //the overall step is the min (zero is invalid, first run) +            if (overall_step == 0) overall_step = range.step; +            overall_step = std::min(overall_step, range.step); +        } +        return gain_range_t(overall_min, overall_max, overall_step); +    } + +    float get_value(void){ +        float overall_gain = 0; +        BOOST_FOREACH(const gain_fcns_t &fcns, get_all_fcns()){ +            overall_gain += fcns.get_value(); +        } +        return overall_gain; +    } + +    void set_value(float gain){ +        std::vector<gain_fcns_t> all_fcns = get_all_fcns(); +        if (all_fcns.size() == 0) return; //nothing to set! + +        //get the max step size among the gains +        float max_step = 0; +        BOOST_FOREACH(const gain_fcns_t &fcns, all_fcns){ +            max_step = std::max(max_step, fcns.get_range().step); +        } + +        //create gain bucket to distribute power +        std::vector<float> gain_bucket; + +        //distribute power according to priority (round to max step) +        float gain_left_to_distribute = gain; +        BOOST_FOREACH(const gain_fcns_t &fcns, all_fcns){ +            const gain_range_t range = fcns.get_range(); +            gain_bucket.push_back( +                max_step*int(std::clip(gain_left_to_distribute, range.min, range.max)/max_step) +            ); +            gain_left_to_distribute -= gain_bucket.back(); +        } + +        //get a list of indexes sorted by step size large to small +        std::vector<size_t> indexes_step_size_dec; +        for (size_t i = 0; i < all_fcns.size(); i++){ +            indexes_step_size_dec.push_back(i); +        } +        std::sort( +            indexes_step_size_dec.begin(), indexes_step_size_dec.end(), +            boost::bind(&compare_by_step_size, _1, _2, all_fcns) +        ); +        UHD_ASSERT_THROW( +            all_fcns.at(indexes_step_size_dec.front()).get_range().step >= +            all_fcns.at(indexes_step_size_dec.back()).get_range().step +        ); + +        //distribute the remainder (less than max step) +        //fill in the largest step sizes first that are less than the remainder +        BOOST_FOREACH(size_t i, indexes_step_size_dec){ +            const gain_range_t range = all_fcns.at(i).get_range(); +            float additional_gain = range.step*int( +                std::clip(gain_bucket.at(i) + gain_left_to_distribute, range.min, range.max +            )/range.step) - gain_bucket.at(i); +            gain_bucket.at(i) += additional_gain; +            gain_left_to_distribute -= additional_gain; +        } +        if (verbose) std::cout << "gain_left_to_distribute " << gain_left_to_distribute << std::endl; + +        //now write the bucket out to the individual gain values +        for (size_t i = 0; i < gain_bucket.size(); i++){ +            if (verbose) std::cout << gain_bucket.at(i) << std::endl; +            all_fcns.at(i).set_value(gain_bucket.at(i)); +        } +    } + +    void register_fcns( +        const gain_fcns_t &gain_fcns, size_t priority +    ){ +        _registry[priority].push_back(gain_fcns); +    } + +private: +    //! get the gain function sets in order (highest priority first) +    std::vector<gain_fcns_t> get_all_fcns(void){ +        std::vector<gain_fcns_t> all_fcns; +        BOOST_FOREACH(ssize_t key, std::sorted(_registry.keys())){ +            const std::vector<gain_fcns_t> &fcns = _registry[key]; +            all_fcns.insert(all_fcns.begin(), fcns.begin(), fcns.end()); +        } +        return all_fcns; +    } + +    uhd::dict<size_t, std::vector<gain_fcns_t> > _registry; +}; + +/*********************************************************************** + * gain group factory function + **********************************************************************/ +gain_group::sptr gain_group::make(void){ +    return sptr(new gain_group_impl()); +} diff --git a/host/lib/ic_reg_maps/CMakeLists.txt b/host/lib/ic_reg_maps/CMakeLists.txt index a328fa033..ac051b843 100644 --- a/host/lib/ic_reg_maps/CMakeLists.txt +++ b/host/lib/ic_reg_maps/CMakeLists.txt @@ -80,6 +80,11 @@ LIBUHD_PYTHON_GEN_SOURCE(  )  LIBUHD_PYTHON_GEN_SOURCE( +    ${CMAKE_SOURCE_DIR}/lib/ic_reg_maps/gen_ads62p44_regs.py +    ${CMAKE_BINARY_DIR}/lib/ic_reg_maps/ads62p44_regs.hpp +    ) + +LIBUHD_PYTHON_GEN_SOURCE(      ${CMAKE_SOURCE_DIR}/lib/ic_reg_maps/gen_tuner_4937di5_regs.py      ${CMAKE_BINARY_DIR}/lib/ic_reg_maps/tuner_4937di5_regs.hpp  ) diff --git a/host/lib/ic_reg_maps/gen_ads62p44_regs.py b/host/lib/ic_reg_maps/gen_ads62p44_regs.py new file mode 100755 index 000000000..f0a84d940 --- /dev/null +++ b/host/lib/ic_reg_maps/gen_ads62p44_regs.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python +# +# Copyright 2010 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. +# + +######################################################################## +# Template for raw text data describing registers +# name addr[bit range inclusive] default optional enums +######################################################################## +REGS_TMPL="""\ +######################################################################## +## address 0 +######################################################################## +reset                   0[1]     0       +serial_readout          0[0]     0       +######################################################################## +## address 16 +######################################################################## +clkout_strength         16[6:7]   0      weaker=1, default=0, stronger=3 +######################################################################## +## address 17 +######################################################################## +dataout_strength        17[0:1]   0      weaker=1, default=0, stronger=3, maximum=2 +lvds_current            17[2:3]   0      3_5ma, 2_5ma, 4_5ma, 1_75ma +lvds_current_double     17[4:5]   0      default, dblclk, dbldataclock +######################################################################## +## address 18 +######################################################################## +lvds_clk_term           18[0:2]   0      none, 300, 180, 110, 150, 100, 81, 60 +lvds_data_term          18[3:5]   0      none, 300, 180, 110, 150, 100, 81, 60 +######################################################################## +## address 19 +######################################################################## +offset_freeze           19[4]     0 +######################################################################## +## address 20 +######################################################################## +power_down              20[0:2]   0      normal, a_dis, b_dis, ab_dis, global_pd, a_sby, b_sby, mux +ref_select              20[3]     0      internal, external +coarse_gain             20[4]     0      0db, 3_5db +output_interface        20[5]     0      cmos, lvds +override                20[7]     0 +######################################################################## +## address 22 +######################################################################## +test_patterns           22[0:2]   0      normal, zeros, ones, toggle, ramp, custom +lvds_bytewise           22[3]     0 +data_format             22[4]     0      twos_complement, binary +######################################################################## +## address 23 +######################################################################## +fine_gain               23[0:3]   0 +######################################################################## +## address 24 and 25 +######################################################################## +custom_low              24[0:7]   0 +custom_high             25[0:5]   0 +######################################################################## +## address 26 +######################################################################## +gain_correction         26[0:3]   0 +offset_tc               26[4:6]   0      1_1s, 0_55s, 0_27s, 0_13s, 2_15s, 4_3s +low_latency             26[7]     0 +######################################################################## +## address 27 +######################################################################## +decimation              27[0:2]   0      decimate_2, decimate_4, decimate_1, decimate_8 +odd_tap_enable          27[3]     0 +filter_enable           27[4]     0 +filter_coeff_sel        27[5]     0      predefined, userdefined +offset_enable           27[7]     0 +######################################################################## +## address 29 +######################################################################## +decimation_filter_bands 29[0:1]   0 +""" + +######################################################################## +# Template for methods in the body of the struct +######################################################################## +BODY_TMPL="""\ +boost::uint8_t get_reg(boost::uint8_t addr){ +    boost::uint8_t reg = 0; +    switch(addr){ +    #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs))) +    case $addr: +        #for $reg in filter(lambda r: r.get_addr() == addr, $regs) +        reg |= (boost::uint8_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); +        #end for +        break; +    #end for +    } +    return reg; +} + +boost::uint16_t get_write_reg(boost::uint8_t addr){ +    return (boost::uint16_t(addr) << 8) | get_reg(addr); +} + +boost::uint16_t get_read_reg(boost::uint8_t addr){ +    return (boost::uint16_t(addr) << 8) | (1 << 7); +} +""" + +if __name__ == '__main__': +    import common; common.generate( +        name='ads62p44_regs', +        regs_tmpl=REGS_TMPL, +        body_tmpl=BODY_TMPL, +        file=__file__, +    ) diff --git a/host/lib/usrp/usrp2/CMakeLists.txt b/host/lib/usrp/usrp2/CMakeLists.txt index 078485d6a..f7984fce5 100644 --- a/host/lib/usrp/usrp2/CMakeLists.txt +++ b/host/lib/usrp/usrp2/CMakeLists.txt @@ -44,6 +44,8 @@ IF(ENABLE_USRP2)          ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/dboard_impl.cpp          ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/dboard_iface.cpp          ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/dsp_impl.cpp +        ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/gps_ctrl.hpp +        ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/gps_ctrl.cpp          ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/io_impl.cpp          ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/mboard_impl.cpp          ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/serdes_ctrl.cpp @@ -53,6 +55,7 @@ IF(ENABLE_USRP2)          ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/usrp2_impl.cpp          ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/usrp2_impl.hpp          ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/usrp2_regs.hpp +        ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/usrp2_regs.cpp      )  ELSE(ENABLE_USRP2)      MESSAGE(STATUS "  Skipping USRP2 support.") diff --git a/host/lib/usrp/usrp2/clock_ctrl.cpp b/host/lib/usrp/usrp2/clock_ctrl.cpp index 232f3b32a..1f8e65ce6 100644 --- a/host/lib/usrp/usrp2/clock_ctrl.cpp +++ b/host/lib/usrp/usrp2/clock_ctrl.cpp @@ -18,6 +18,7 @@  #include "clock_ctrl.hpp"  #include "ad9510_regs.hpp"  #include "usrp2_regs.hpp" //spi slave constants +#include "usrp2_clk_regs.hpp"  #include <uhd/utils/assert.hpp>  #include <boost/cstdint.hpp>  #include <boost/lexical_cast.hpp> @@ -32,9 +33,10 @@ 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_hw_rev());          _ad9510_regs.cp_current_setting = ad9510_regs_t::CP_CURRENT_SETTING_3_0MA; -        this->write_reg(0x09); +        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). @@ -44,20 +46,20 @@ public:          _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(0x0A); +        this->write_reg(clk_regs.pll_4);          _ad9510_regs.acounter = 0; -        this->write_reg(0x04); +        this->write_reg(clk_regs.acounter);          _ad9510_regs.bcounter_msb = 0;          _ad9510_regs.bcounter_lsb = 5; -        this->write_reg(0x05); -        this->write_reg(0x06); +        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(0x0B); -        this->write_reg(0x0C); +        this->write_reg(clk_regs.ref_counter_msb); +        this->write_reg(clk_regs.ref_counter_lsb);          /* regs will be updated in commands below */ @@ -84,16 +86,13 @@ public:      }      void enable_mimo_clock_out(bool enb){ -        boost::uint16_t rev = boost::lexical_cast<boost::uint16_t>(_iface->mb_eeprom["rev"]); -        boost::uint8_t rev_hi = boost::uint8_t(rev >> 8); -          //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(rev_hi){ -        case 3: //clock 2 +        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; @@ -102,11 +101,9 @@ public:              _ad9510_regs.divider_low_cycles_out2 = low - 1;              _ad9510_regs.divider_high_cycles_out2 = high - 1;              _ad9510_regs.bypass_divider_out2 = 0; -            this->write_reg(0x3e); -            this->write_reg(0x4c);              break; -        case 4: //clock 5 +        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; @@ -114,14 +111,23 @@ public:              _ad9510_regs.divider_low_cycles_out5 = low - 1;              _ad9510_regs.divider_high_cycles_out5 = high - 1;              _ad9510_regs.bypass_divider_out5 = 0; -            this->write_reg(0x41); -            this->write_reg(0x52); +            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; -        //TODO FIXME do i want to throw, what about uninitialized boards? -        //default: throw std::runtime_error("unknown rev hi in mboard eeprom"); -        default: std::cerr << "unknown rev hi: " << rev_hi << std::endl; +        default: +            break;          } +        this->write_reg(clk_regs.output(clk_regs.exp)); +        this->write_reg(clk_regs.div_lo(clk_regs.exp));          this->update_regs();      } @@ -130,7 +136,7 @@ public:          _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(0x43); +        this->write_reg(clk_regs.output(clk_regs.rx_db));          this->update_regs();      } @@ -146,8 +152,8 @@ public:          _ad9510_regs.divider_low_cycles_out7 = low - 1;          _ad9510_regs.divider_high_cycles_out7 = high - 1;          //write the registers -        this->write_reg(0x56); -        this->write_reg(0x57); +        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();      } @@ -157,12 +163,22 @@ public:          return rates;      } -    //uses output clock 6 (cmos) +    //uses output clock 6 (cmos) on USRP2 and output clock 5 (cmos) on USRP2+      void enable_tx_dboard_clock(bool enb){ -        _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; -        this->write_reg(0x42); +        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();      } @@ -174,18 +190,44 @@ public:          //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_out6 = low - 1; -        _ad9510_regs.divider_high_cycles_out6 = high - 1; + +        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(0x54); -        this->write_reg(0x55); +        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. @@ -197,7 +239,7 @@ public:              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(0x08); +        this->write_reg(clk_regs.pll_2);          this->update_regs();      } @@ -220,33 +262,46 @@ private:       */      void update_regs(void){          _ad9510_regs.update_registers = 1; -        this->write_reg(0x5a); +        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(0x3F); -        this->write_reg(0x4F); +        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) +    //uses output clock 4 (lvds) on USRP2 and output clock 2 (lvpecl) on USRP2+      void enable_adc_clock(bool enb){ -        _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; -        this->write_reg(0x40); -        this->write_reg(0x51); +        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;  }; diff --git a/host/lib/usrp/usrp2/clock_ctrl.hpp b/host/lib/usrp/usrp2/clock_ctrl.hpp index 70a104a81..db6c52c83 100644 --- a/host/lib/usrp/usrp2/clock_ctrl.hpp +++ b/host/lib/usrp/usrp2/clock_ctrl.hpp @@ -83,6 +83,12 @@ public:       * \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;      /*!       * TODO other clock control api here.... diff --git a/host/lib/usrp/usrp2/codec_ctrl.cpp b/host/lib/usrp/usrp2/codec_ctrl.cpp index 32cc13ded..8680c285a 100644 --- a/host/lib/usrp/usrp2/codec_ctrl.cpp +++ b/host/lib/usrp/usrp2/codec_ctrl.cpp @@ -17,10 +17,12 @@  #include "codec_ctrl.hpp"  #include "ad9777_regs.hpp" +#include "ads62p44_regs.hpp"  #include "usrp2_regs.hpp"  #include <boost/cstdint.hpp>  #include <boost/foreach.hpp>  #include <iostream> +#include <uhd/utils/exception.hpp>  static const bool codec_ctrl_debug = false; @@ -57,7 +59,16 @@ public:          }          //power-up adc -        _iface->poke32(U2_REG_MISC_CTRL_ADC, U2_FLAG_MISC_CTRL_ADC_ON); +        if(!_iface->is_usrp2p()) { //if we're on a USRP2 +          _iface->poke32(_iface->regs.misc_ctrl_adc, U2_FLAG_MISC_CTRL_ADC_ON); +        } else { //we're on a USRP2+ +          _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); +        }      }      ~usrp2_codec_ctrl_impl(void){ @@ -66,11 +77,39 @@ public:          this->send_ad9777_reg(0);          //power-down adc -        _iface->poke32(U2_REG_MISC_CTRL_ADC, U2_FLAG_MISC_CTRL_ADC_OFF); +        if(!_iface->is_usrp2p()) { //if we're on a USRP2 +          _iface->poke32(_iface->regs.misc_ctrl_adc, U2_FLAG_MISC_CTRL_ADC_OFF); +        } else { //we're on a USRP2+ +          //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); +        } +    } + +    void set_rx_digital_gain(float gain) {  //fine digital gain +      if(_iface->is_usrp2p()) { +        _ads62p44_regs.fine_gain = int(gain/0.5); +        this->send_ads62p44_reg(0x17); +      } else UHD_THROW_INVALID_CODE_PATH(); //should never have been called for USRP2 +    } + +    void set_rx_digital_fine_gain(float gain) { //gain correction       +      if(_iface->is_usrp2p()) { +        _ads62p44_regs.gain_correction = int(gain / 0.05); +        this->send_ads62p44_reg(0x1A); +      } else UHD_THROW_INVALID_CODE_PATH(); //should never have been called for USRP2 +    } + +    void set_rx_analog_gain(bool gain) { //turns on/off analog 3.5dB preamp +      if(_iface->is_usrp2p()) { +        _ads62p44_regs.coarse_gain = gain ? ads62p44_regs_t::COARSE_GAIN_3_5DB : ads62p44_regs_t::COARSE_GAIN_0DB; +        this->send_ads62p44_reg(0x14); +      } else 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){ @@ -81,6 +120,14 @@ private:              reg, 16, false /*no rb*/          );      } + +    void send_ads62p44_reg(boost::uint8_t addr) { +        boost::uint16_t reg = _ads62p44_regs.get_write_reg(addr); +        _iface->transact_spi( +            SPI_SS_ADS62P44, spi_config_t::EDGE_FALL, +            reg, 16, false /*no rb*/ +        ); +    }  };  /*********************************************************************** diff --git a/host/lib/usrp/usrp2/codec_ctrl.hpp b/host/lib/usrp/usrp2/codec_ctrl.hpp index ad014e0e1..57a37b94b 100644 --- a/host/lib/usrp/usrp2/codec_ctrl.hpp +++ b/host/lib/usrp/usrp2/codec_ctrl.hpp @@ -33,6 +33,27 @@ public:       */      static sptr make(usrp2_iface::sptr iface); +    /*! +     * 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(float 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(float gain) = 0; +  };  #endif /* INCLUDED_CODEC_CTRL_HPP */ diff --git a/host/lib/usrp/usrp2/codec_impl.cpp b/host/lib/usrp/usrp2/codec_impl.cpp index fc917b102..1c1f60765 100644 --- a/host/lib/usrp/usrp2/codec_impl.cpp +++ b/host/lib/usrp/usrp2/codec_impl.cpp @@ -17,10 +17,23 @@  #include "usrp2_impl.hpp"  #include <uhd/usrp/codec_props.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/types/ranges.hpp>  #include <boost/bind.hpp> +#include <boost/assign/list_of.hpp> +#include <uhd/utils/assert.hpp> +#include <uhd/utils/exception.hpp>  using namespace uhd;  using namespace uhd::usrp; +using namespace boost::assign; + +//this only applies to USRP2P +static const uhd::dict<std::string, gain_range_t> codec_rx_gain_ranges = map_list_of +                                  ("analog", gain_range_t(0, 3.5, 3.5)) +                                  ("digital", gain_range_t(0, 6.0, 0.5)) +				  ("digital-fine", gain_range_t(0, 0.5, 0.05)); +  /***********************************************************************   * Helper Methods @@ -40,7 +53,8 @@ void usrp2_mboard_impl::codec_init(void){  /***********************************************************************   * RX Codec Properties   **********************************************************************/ -void usrp2_mboard_impl::rx_codec_get(const wax::obj &key, wax::obj &val){ +void usrp2_mboard_impl::rx_codec_get(const wax::obj &key_, wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_);      //handle the get request conditioned on the key      switch(key.as<codec_prop_t>()){ @@ -53,21 +67,74 @@ void usrp2_mboard_impl::rx_codec_get(const wax::obj &key, wax::obj &val){          return;      case CODEC_PROP_GAIN_NAMES: -        val = prop_names_t(); //no gain elements to be controlled +        if(_iface->is_usrp2p()) { +          val = prop_names_t(codec_rx_gain_ranges.keys()); +        } else val = prop_names_t(); +        return; + +    case CODEC_PROP_GAIN_I: +    case CODEC_PROP_GAIN_Q: +        assert_has(_codec_rx_gains.keys(), key.name, "codec rx gain name"); +        val = _codec_rx_gains[key.name];          return; +    case CODEC_PROP_GAIN_RANGE: +      assert_has(codec_rx_gain_ranges.keys(), key.name, "codec rx gain range name"); +      val = codec_rx_gain_ranges[key.name]; +      return; +      default: UHD_THROW_PROP_GET_ERROR();      }  } -void usrp2_mboard_impl::rx_codec_set(const wax::obj &, const wax::obj &){ -    UHD_THROW_PROP_SET_ERROR(); +void usrp2_mboard_impl::rx_codec_set(const wax::obj &key_, const wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); +    float gain; + +  switch(key.as<codec_prop_t>()) { +    case CODEC_PROP_GAIN_I: +    case CODEC_PROP_GAIN_Q: +      if(!_iface->is_usrp2p()) UHD_THROW_PROP_SET_ERROR();//this capability is only found in USRP2P + +      gain = val.as<float>(); +      this->rx_codec_set_gain(gain, key.name); +      return; + +    default: +      UHD_THROW_PROP_SET_ERROR(); +  } +} + +/*********************************************************************** + * Helper function to set RX codec gain + ***********************************************************************/ + +void usrp2_mboard_impl::rx_codec_set_gain(float gain, const std::string &name){ +  assert_has(codec_rx_gain_ranges.keys(), name, "codec rx gain name"); + +  _codec_rx_gains[name] = gain; + +  if(name == "analog") { +    _codec_ctrl->set_rx_analog_gain(gain > 0); //just turn it on or off +    return; +  } +  if(name == "digital") { +    _codec_ctrl->set_rx_digital_gain(gain); +    return; +  } +  if(name == "digital-fine") { +    _codec_ctrl->set_rx_digital_fine_gain(gain); +    return; +  } +  UHD_THROW_PROP_SET_ERROR();  } +  /***********************************************************************   * TX Codec Properties   **********************************************************************/ -void usrp2_mboard_impl::tx_codec_get(const wax::obj &key, wax::obj &val){ +void usrp2_mboard_impl::tx_codec_get(const wax::obj &key_, wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_);      //handle the get request conditioned on the key      switch(key.as<codec_prop_t>()){ diff --git a/host/lib/usrp/usrp2/dboard_iface.cpp b/host/lib/usrp/usrp2/dboard_iface.cpp index fdfbf0d17..ab5f62355 100644 --- a/host/lib/usrp/usrp2/dboard_iface.cpp +++ b/host/lib/usrp/usrp2/dboard_iface.cpp @@ -180,8 +180,8 @@ void usrp2_dboard_iface::set_pin_ctrl(unit_t unit, boost::uint16_t value){      //write the selection mux value to register      switch(unit){ -    case UNIT_RX: _iface->poke32(U2_REG_GPIO_RX_SEL, new_sels); return; -    case UNIT_TX: _iface->poke32(U2_REG_GPIO_TX_SEL, new_sels); return; +    case UNIT_RX: _iface->poke32(_iface->regs.gpio_rx_sel, new_sels); return; +    case UNIT_TX: _iface->poke32(_iface->regs.gpio_tx_sel, new_sels); return;      }  } @@ -189,18 +189,18 @@ void usrp2_dboard_iface::set_gpio_ddr(unit_t unit, boost::uint16_t value){      _ddr_shadow = \          (_ddr_shadow & ~(0xffff << unit_to_shift[unit])) |          (boost::uint32_t(value) << unit_to_shift[unit]); -    _iface->poke32(U2_REG_GPIO_DDR, _ddr_shadow); +    _iface->poke32(_iface->regs.gpio_ddr, _ddr_shadow);  }  void usrp2_dboard_iface::write_gpio(unit_t unit, boost::uint16_t value){      _gpio_shadow = \          (_gpio_shadow & ~(0xffff << unit_to_shift[unit])) |          (boost::uint32_t(value) << unit_to_shift[unit]); -    _iface->poke32(U2_REG_GPIO_IO, _gpio_shadow); +    _iface->poke32(_iface->regs.gpio_io, _gpio_shadow);  }  boost::uint16_t usrp2_dboard_iface::read_gpio(unit_t unit){ -    return boost::uint16_t(_iface->peek32(U2_REG_GPIO_IO) >> unit_to_shift[unit]); +    return boost::uint16_t(_iface->peek32(_iface->regs.gpio_io) >> unit_to_shift[unit]);  }  void usrp2_dboard_iface::set_atr_reg(unit_t unit, atr_reg_t atr, boost::uint16_t value){ @@ -209,16 +209,16 @@ void usrp2_dboard_iface::set_atr_reg(unit_t unit, atr_reg_t atr, boost::uint16_t          unit_t, uhd::dict<atr_reg_t, boost::uint32_t>      > unit_to_atr_to_addr = map_list_of          (UNIT_RX, map_list_of -            (ATR_REG_IDLE,        U2_REG_ATR_IDLE_RXSIDE) -            (ATR_REG_TX_ONLY,     U2_REG_ATR_INTX_RXSIDE) -            (ATR_REG_RX_ONLY,     U2_REG_ATR_INRX_RXSIDE) -            (ATR_REG_FULL_DUPLEX, U2_REG_ATR_FULL_RXSIDE) +            (ATR_REG_IDLE,        _iface->regs.atr_idle_rxside) +            (ATR_REG_TX_ONLY,     _iface->regs.atr_intx_rxside) +            (ATR_REG_RX_ONLY,     _iface->regs.atr_inrx_rxside) +            (ATR_REG_FULL_DUPLEX, _iface->regs.atr_full_rxside)          )          (UNIT_TX, map_list_of -            (ATR_REG_IDLE,        U2_REG_ATR_IDLE_TXSIDE) -            (ATR_REG_TX_ONLY,     U2_REG_ATR_INTX_TXSIDE) -            (ATR_REG_RX_ONLY,     U2_REG_ATR_INRX_TXSIDE) -            (ATR_REG_FULL_DUPLEX, U2_REG_ATR_FULL_TXSIDE) +            (ATR_REG_IDLE,        _iface->regs.atr_idle_txside) +            (ATR_REG_TX_ONLY,     _iface->regs.atr_intx_txside) +            (ATR_REG_RX_ONLY,     _iface->regs.atr_inrx_txside) +            (ATR_REG_FULL_DUPLEX, _iface->regs.atr_full_txside)          )      ;      _iface->poke16(unit_to_atr_to_addr[unit][atr], value); @@ -238,8 +238,8 @@ void usrp2_dboard_iface::set_gpio_debug(unit_t unit, int which){      //write the selection mux value to register      switch(unit){ -    case UNIT_RX: _iface->poke32(U2_REG_GPIO_RX_SEL, new_sels); return; -    case UNIT_TX: _iface->poke32(U2_REG_GPIO_TX_SEL, new_sels); return; +    case UNIT_RX: _iface->poke32(_iface->regs.gpio_rx_sel, new_sels); return; +    case UNIT_TX: _iface->poke32(_iface->regs.gpio_tx_sel, new_sels); return;      }  } diff --git a/host/lib/usrp/usrp2/dsp_impl.cpp b/host/lib/usrp/usrp2/dsp_impl.cpp index 0c85e643f..c8da03955 100644 --- a/host/lib/usrp/usrp2/dsp_impl.cpp +++ b/host/lib/usrp/usrp2/dsp_impl.cpp @@ -95,7 +95,7 @@ void usrp2_mboard_impl::ddc_set(const wax::obj &key_, const wax::obj &val){      case DSP_PROP_FREQ_SHIFT:{              double new_freq = val.as<double>(); -            _iface->poke32(U2_REG_DSP_RX_FREQ, +            _iface->poke32(_iface->regs.dsp_rx_freq,                  dsp_type1::calc_cordic_word_and_update(new_freq, get_master_clock_freq())              );              _ddc_freq = new_freq; //shadow @@ -107,11 +107,11 @@ void usrp2_mboard_impl::ddc_set(const wax::obj &key_, const wax::obj &val){              _ddc_decim = pick_closest_rate(extact_rate, _allowed_decim_and_interp_rates);              //set the decimation -            _iface->poke32(U2_REG_DSP_RX_DECIM_RATE, dsp_type1::calc_cic_filter_word(_ddc_decim)); +            _iface->poke32(_iface->regs.dsp_rx_decim_rate, dsp_type1::calc_cic_filter_word(_ddc_decim));              //set the scaling              static const boost::int16_t default_rx_scale_iq = 1024; -            _iface->poke32(U2_REG_DSP_RX_SCALE_IQ, +            _iface->poke32(_iface->regs.dsp_rx_scale_iq,                  dsp_type1::calc_iq_scale_word(default_rx_scale_iq, default_rx_scale_iq)              );          } @@ -178,7 +178,7 @@ void usrp2_mboard_impl::duc_set(const wax::obj &key_, const wax::obj &val){      case DSP_PROP_FREQ_SHIFT:{              double new_freq = val.as<double>(); -            _iface->poke32(U2_REG_DSP_TX_FREQ, +            _iface->poke32(_iface->regs.dsp_tx_freq,                  dsp_type1::calc_cordic_word_and_update(new_freq, get_master_clock_freq())              );              _duc_freq = new_freq; //shadow @@ -190,10 +190,10 @@ void usrp2_mboard_impl::duc_set(const wax::obj &key_, const wax::obj &val){              _duc_interp = pick_closest_rate(extact_rate, _allowed_decim_and_interp_rates);              //set the interpolation -            _iface->poke32(U2_REG_DSP_TX_INTERP_RATE, dsp_type1::calc_cic_filter_word(_duc_interp)); +            _iface->poke32(_iface->regs.dsp_tx_interp_rate, dsp_type1::calc_cic_filter_word(_duc_interp));              //set the scaling -            _iface->poke32(U2_REG_DSP_TX_SCALE_IQ, dsp_type1::calc_iq_scale_word(_duc_interp)); +            _iface->poke32(_iface->regs.dsp_tx_scale_iq, dsp_type1::calc_iq_scale_word(_duc_interp));          }          return; diff --git a/host/lib/usrp/usrp2/fw_common.h b/host/lib/usrp/usrp2/fw_common.h index 4ff31ddfd..6c9596092 100644 --- a/host/lib/usrp/usrp2/fw_common.h +++ b/host/lib/usrp/usrp2/fw_common.h @@ -52,6 +52,14 @@ extern "C" {  #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 @@ -75,6 +83,12 @@ typedef enum{      USRP2_CTRL_ID_PEEK_AT_THIS_REGISTER_FOR_ME_BRO = 'r',      USRP2_CTRL_ID_WOAH_I_DEFINITELY_PEEKED_IT_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_PEACE_OUT = '~'  } usrp2_ctrl_id_t; @@ -115,6 +129,11 @@ typedef struct{              __stdint(uint32_t) datahi;              __stdint(uint8_t) num_bytes; //1, 2, 4, 8          } poke_args; +        struct { +            __stdint(uint8_t) dev; +            __stdint(uint8_t) bytes; +            __stdint(uint8_t) data[20]; +        } uart_args;      } data;  } usrp2_ctrl_data_t; diff --git a/host/lib/usrp/usrp2/gps_ctrl.cpp b/host/lib/usrp/usrp2/gps_ctrl.cpp new file mode 100644 index 000000000..2273b2cd9 --- /dev/null +++ b/host/lib/usrp/usrp2/gps_ctrl.cpp @@ -0,0 +1,207 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "gps_ctrl.hpp" +#include <uhd/utils/assert.hpp> +#include <boost/cstdint.hpp> +#include <string> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/thread.hpp> +#include <boost/algorithm/string/trim.hpp> +#include <boost/tokenizer.hpp> + +using namespace uhd; +using namespace boost::gregorian; +using namespace boost::posix_time; +using namespace boost::algorithm; + +/*! + * A usrp2 GPS control for Jackson Labs devices + */ + +//TODO: multiple baud rate support (requires mboard_impl changes for poking UART registers) +class usrp2_gps_ctrl_impl : public usrp2_gps_ctrl{ +public: +  usrp2_gps_ctrl_impl(usrp2_iface::sptr iface){ +    _iface = iface; + +    std::string reply; +    bool i_heard_some_nmea = false, i_heard_something_weird = false; + +    gps_type = GPS_TYPE_NONE; + +//    set_uart_baud_rate(GPS_UART, 115200); +    //first we look for a Jackson Labs Firefly (since that's what we sell with the USRP2+...) + +    _iface->read_uart(GPS_UART); //get whatever junk is in the rx buffer right now, and throw it away +    _iface->write_uart(GPS_UART, "HAAAY GUYYYYS\n"); //to elicit a response from the Firefly + +    //then we loop until we either timeout, or until we get a response that indicates we're a JL device +    int timeout = GPS_TIMEOUT_TRIES; +    while(timeout--) { +      reply = safe_gps_read(); +      if(trim_right_copy(reply) == "Command Error") { +        gps_type = GPS_TYPE_JACKSON_LABS; +        break; +      } +      else if(reply.substr(0, 3) == "$GP") i_heard_some_nmea = true; //but keep looking for that "Command Error" response +      else if(reply.length() != 0) i_heard_something_weird = true; //probably wrong baud rate +    } + +    if((i_heard_some_nmea) && (gps_type != GPS_TYPE_JACKSON_LABS)) gps_type = GPS_TYPE_GENERIC_NMEA; + +    //otherwise, we can try some other common baud rates looking to see if a GPS is connected (todo, later) +    if((gps_type == GPS_TYPE_NONE) && i_heard_something_weird) { +      std::cout << "Invalid reply, possible incorrect baud rate" << std::endl; +    } + +    bool found_gprmc = false; + +    switch(gps_type) { +    case GPS_TYPE_JACKSON_LABS: +      std::cout << "Found a Jackson Labs GPS" << std::endl; +      //issue some setup stuff so it spits out the appropriate data +      //none of these should issue replies so we don't bother looking for them +      //we have to sleep between commands because the JL device, despite not acking, takes considerable time to process each command. +       boost::this_thread::sleep(boost::posix_time::milliseconds(FIREFLY_STUPID_DELAY_MS)); +      _iface->write_uart(GPS_UART, "SYST:COMM:SER:ECHO OFF\n"); +       boost::this_thread::sleep(boost::posix_time::milliseconds(FIREFLY_STUPID_DELAY_MS)); +      _iface->write_uart(GPS_UART, "SYST:COMM:SER:PRO OFF\n"); +       boost::this_thread::sleep(boost::posix_time::milliseconds(FIREFLY_STUPID_DELAY_MS)); +      _iface->write_uart(GPS_UART, "GPS:GPGGA 0\n"); +       boost::this_thread::sleep(boost::posix_time::milliseconds(FIREFLY_STUPID_DELAY_MS)); +      _iface->write_uart(GPS_UART, "GPS:GGAST 0\n"); +       boost::this_thread::sleep(boost::posix_time::milliseconds(FIREFLY_STUPID_DELAY_MS)); +      _iface->write_uart(GPS_UART, "GPS:GPRMC 1\n"); +       boost::this_thread::sleep(boost::posix_time::milliseconds(FIREFLY_STUPID_DELAY_MS)); + +//      break; + +    case GPS_TYPE_GENERIC_NMEA: +      if(gps_type == GPS_TYPE_GENERIC_NMEA) std::cout << "Found a generic NMEA GPS device" << std::endl; +      found_gprmc = false; +      //here we loop around looking for a GPRMC packet. if we don't get one, we don't have a usable GPS. +      timeout = GPS_TIMEOUT_TRIES; +      while(timeout--) { +        reply = safe_gps_read(); +        if(reply.substr(0, 6) == "$GPRMC") { +          found_gprmc = true; +          break; +        } +      } +      if(!found_gprmc) { +        if(gps_type == GPS_TYPE_JACKSON_LABS) std::cout << "Firefly GPS not locked or warming up." << std::endl; +        else std::cout << "GPS does not output GPRMC packets. Cannot retrieve time." << std::endl; +        gps_type = GPS_TYPE_NONE; +      } +      break; + +    case GPS_TYPE_NONE: +    default: +      break; + +    } + + +  } + +  ~usrp2_gps_ctrl_impl(void){ + +  } + +  std::string safe_gps_read() { +    std::string reply; +    try { +        reply = _iface->read_uart(GPS_UART); +  	    //std::cerr << "Got reply from GPS: " << reply.c_str() << " with length = " << reply.length() << std::endl; +    } catch (std::runtime_error err) { +      if(err.what() != std::string("usrp2 no control response")) throw; //sorry can't cope with that +      else { //we don't actually have a GPS installed +        reply = std::string(); +      } +    } +    return reply; +  } + +  ptime get_time(void) { +    std::string reply; +    ptime now; +    boost::tokenizer<boost::escaped_list_separator<char> > tok(reply); +    std::vector<std::string> toked; +    int timeout = GPS_TIMEOUT_TRIES; +    bool found_gprmc = false; +    switch(gps_type) { +    case GPS_TYPE_JACKSON_LABS: //deprecated in favor of a single NMEA parser +    case GPS_TYPE_GENERIC_NMEA: + +      while(timeout--) { +        reply = safe_gps_read(); +        if(reply.substr(0, 6) == "$GPRMC") { +          found_gprmc = true; +          break; +        } +      } +      UHD_ASSERT_THROW(found_gprmc); + +      tok.assign(reply); +      toked.assign(tok.begin(), tok.end()); + +      UHD_ASSERT_THROW(toked.size() == 11); //if it's not we got something weird in there + +      now = ptime( date(  +                         greg_year(boost::lexical_cast<int>(toked[8].substr(4, 2)) + 2000), //just trust me on this one +                         greg_month(boost::lexical_cast<int>(toked[8].substr(2, 2))),  +                         greg_day(boost::lexical_cast<int>(toked[8].substr(0, 2)))  +                       ), +                   hours(  boost::lexical_cast<int>(toked[1].substr(0, 2))) +                 + minutes(boost::lexical_cast<int>(toked[1].substr(2, 2))) +                 + seconds(boost::lexical_cast<int>(toked[1].substr(4, 2))) +                 ); +      break; +    case GPS_TYPE_NONE: +    default: +      throw std::runtime_error("get_time(): Unsupported GPS or no GPS detected\n"); +      break; +    } +    return now; +  } + +  bool gps_detected(void) { +    return (gps_type != GPS_TYPE_NONE); +  } + +private: +  usrp2_iface::sptr _iface; + +  enum { +    GPS_TYPE_JACKSON_LABS, +    GPS_TYPE_GENERIC_NMEA, +    GPS_TYPE_NONE +  } gps_type; + +  static const int GPS_UART = 2; //TODO: this should be plucked from fw_common.h or memory_map.h or somewhere in common with the firmware +  static const int GPS_TIMEOUT_TRIES = 5; +  static const int FIREFLY_STUPID_DELAY_MS = 200; + +}; + +/*********************************************************************** + * Public make function for the GPS control + **********************************************************************/ +usrp2_gps_ctrl::sptr usrp2_gps_ctrl::make(usrp2_iface::sptr iface){ +    return sptr(new usrp2_gps_ctrl_impl(iface)); +} diff --git a/host/lib/usrp/usrp2/gps_ctrl.hpp b/host/lib/usrp/usrp2/gps_ctrl.hpp new file mode 100644 index 000000000..5936a6fb6 --- /dev/null +++ b/host/lib/usrp/usrp2/gps_ctrl.hpp @@ -0,0 +1,53 @@ +// +// 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_GPS_CTRL_HPP +#define INCLUDED_GPS_CTRL_HPP + +#include "usrp2_iface.hpp" +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include <boost/date_time/posix_time/posix_time_types.hpp> + +using namespace boost::posix_time; + +class usrp2_gps_ctrl : boost::noncopyable{ +public: +  typedef boost::shared_ptr<usrp2_gps_ctrl> sptr; + +  /*! +   * Make a GPS config for Jackson Labs or generic NMEA GPS devices +   */ +  static sptr make(usrp2_iface::sptr iface); + +  /*! +   * Get the current GPS time and date +   * \return current GPS time and date as boost::posix_time::ptime object +   */ +  virtual ptime get_time(void) = 0; + +  /*! +   * Tell you if there's a supported GPS connected or not +   * \return true if a supported GPS is connected +   */ +  virtual bool gps_detected(void) = 0; + +  //TODO: other fun things you can do with a GPS. + +}; + +#endif /* INCLUDED_CLOCK_CTRL_HPP */ diff --git a/host/lib/usrp/usrp2/mboard_impl.cpp b/host/lib/usrp/usrp2/mboard_impl.cpp index a6ca7f2d3..5b9dd1fa3 100644 --- a/host/lib/usrp/usrp2/mboard_impl.cpp +++ b/host/lib/usrp/usrp2/mboard_impl.cpp @@ -24,9 +24,11 @@  #include <uhd/utils/algorithm.hpp>  #include <boost/bind.hpp>  #include <iostream> +#include <boost/date_time/posix_time/posix_time.hpp>  using namespace uhd;  using namespace uhd::usrp; +using namespace boost::posix_time;  /***********************************************************************   * Structors @@ -44,6 +46,9 @@ usrp2_mboard_impl::usrp2_mboard_impl(      _clock_ctrl = usrp2_clock_ctrl::make(_iface);      _codec_ctrl = usrp2_codec_ctrl::make(_iface);      _serdes_ctrl = usrp2_serdes_ctrl::make(_iface); +    //_gps_ctrl = usrp2_gps_ctrl::make(_iface); + +    //if(_gps_ctrl->gps_detected()) std::cout << "GPS time: " << _gps_ctrl->get_time() << std::endl;      //TODO move to dsp impl...      //load the allowed decim/interp rates @@ -64,25 +69,25 @@ usrp2_mboard_impl::usrp2_mboard_impl(      //most if not all junk packets will never make it to the socket.      this->issue_ddc_stream_cmd(stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); -    //init the rx control registers -    _iface->poke32(U2_REG_RX_CTRL_NSAMPS_PER_PKT, _recv_frame_size); -    _iface->poke32(U2_REG_RX_CTRL_NCHANNELS, 1); -    _iface->poke32(U2_REG_RX_CTRL_CLEAR_OVERRUN, 1); //reset -    _iface->poke32(U2_REG_RX_CTRL_VRT_HEADER, 0 +    //setup the vrt rx registers +    _iface->poke32(_iface->regs.rx_ctrl_nsamps_per_pkt, _recv_frame_size); +    _iface->poke32(_iface->regs.rx_ctrl_nchannels, 1); +    _iface->poke32(_iface->regs.rx_ctrl_clear_overrun, 1); //reset +    _iface->poke32(_iface->regs.rx_ctrl_vrt_header, 0          | (0x1 << 28) //if data with stream id          | (0x1 << 26) //has trailer          | (0x3 << 22) //integer time other          | (0x1 << 20) //fractional time sample count      ); -    _iface->poke32(U2_REG_RX_CTRL_VRT_STREAM_ID, 0); -    _iface->poke32(U2_REG_RX_CTRL_VRT_TRAILER, 0); -    _iface->poke32(U2_REG_TIME64_TPS, size_t(get_master_clock_freq())); +    _iface->poke32(_iface->regs.rx_ctrl_vrt_stream_id, 0); +    _iface->poke32(_iface->regs.rx_ctrl_vrt_trailer, 0); +    _iface->poke32(_iface->regs.time64_tps, size_t(get_master_clock_freq()));      //init the tx control registers -    _iface->poke32(U2_REG_TX_CTRL_NUM_CHAN, 0);    //1 channel -    _iface->poke32(U2_REG_TX_CTRL_CLEAR_STATE, 1); //reset -    _iface->poke32(U2_REG_TX_CTRL_REPORT_SID, 1);  //sid 1 (different from rx) -    _iface->poke32(U2_REG_TX_CTRL_POLICY, U2_FLAG_TX_CTRL_POLICY_NEXT_PACKET); +    _iface->poke32(_iface->regs.tx_ctrl_num_chan, 0);    //1 channel +    _iface->poke32(_iface->regs.tx_ctrl_clear_state, 1); //reset +    _iface->poke32(_iface->regs.tx_ctrl_report_sid, 1);  //sid 1 (different from rx) +    _iface->poke32(_iface->regs.tx_ctrl_policy, U2_FLAG_TX_CTRL_POLICY_NEXT_PACKET);      //init the ddc      init_ddc_config(); @@ -139,35 +144,45 @@ void usrp2_mboard_impl::update_clock_config(void){      }      //set the pps flags -    _iface->poke32(U2_REG_TIME64_FLAGS, pps_flags); +    _iface->poke32(_iface->regs.time64_flags, pps_flags);      //clock source ref 10mhz -    switch(_clock_config.ref_source){ -    case clock_config_t::REF_INT : _iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x10); break; -    case clock_config_t::REF_SMA : _iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x1C); break; -    case clock_config_t::REF_MIMO: _iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x15); break; -    default: throw std::runtime_error("usrp2: unhandled clock configuration reference source"); +    if(_iface->is_usrp2p()) { +        switch(_clock_config.ref_source){ +        case clock_config_t::REF_INT : _iface->poke32(_iface->regs.misc_ctrl_clock, 0x12); break; +        case clock_config_t::REF_SMA : _iface->poke32(_iface->regs.misc_ctrl_clock, 0x1C); break; +        case clock_config_t::REF_MIMO: _iface->poke32(_iface->regs.misc_ctrl_clock, 0x15); break; +        default: throw std::runtime_error("usrp2: unhandled clock configuration reference source"); +        } +    } else { +        switch(_clock_config.ref_source){ +        case clock_config_t::REF_INT : _iface->poke32(_iface->regs.misc_ctrl_clock, 0x10); break; +        case clock_config_t::REF_SMA : _iface->poke32(_iface->regs.misc_ctrl_clock, 0x1C); break; +        case clock_config_t::REF_MIMO: _iface->poke32(_iface->regs.misc_ctrl_clock, 0x15); break; +        default: throw std::runtime_error("usrp2: unhandled clock configuration reference source"); +        }      }      //clock source ref 10mhz -    bool use_external = _clock_config.ref_source != clock_config_t::REF_INT; +    bool use_external = (_clock_config.ref_source != clock_config_t::REF_INT) +                     || (_iface->is_usrp2p()); //USRP2P has an internal 10MHz TCXO      _clock_ctrl->enable_external_ref(use_external);  }  void usrp2_mboard_impl::set_time_spec(const time_spec_t &time_spec, bool now){      //set the ticks -    _iface->poke32(U2_REG_TIME64_TICKS, time_spec.get_tick_count(get_master_clock_freq())); +    _iface->poke32(_iface->regs.time64_ticks, time_spec.get_tick_count(get_master_clock_freq()));      //set the flags register      boost::uint32_t imm_flags = (now)? U2_FLAG_TIME64_LATCH_NOW : U2_FLAG_TIME64_LATCH_NEXT_PPS; -    _iface->poke32(U2_REG_TIME64_IMM, imm_flags); +    _iface->poke32(_iface->regs.time64_imm, imm_flags);      //set the seconds (latches in all 3 registers) -    _iface->poke32(U2_REG_TIME64_SECS, boost::uint32_t(time_spec.get_full_secs())); +    _iface->poke32(_iface->regs.time64_secs, boost::uint32_t(time_spec.get_full_secs()));  }  void usrp2_mboard_impl::handle_overflow(void){ -    _iface->poke32(U2_REG_RX_CTRL_CLEAR_OVERRUN, 1); +    _iface->poke32(_iface->regs.rx_ctrl_clear_overrun, 1);      if (_continuous_streaming){ //re-issue the stream command if already continuous          this->issue_ddc_stream_cmd(stream_cmd_t::STREAM_MODE_START_CONTINUOUS);      } @@ -175,11 +190,11 @@ void usrp2_mboard_impl::handle_overflow(void){  void usrp2_mboard_impl::issue_ddc_stream_cmd(const stream_cmd_t &stream_cmd){      _continuous_streaming = stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_START_CONTINUOUS; -    _iface->poke32(U2_REG_RX_CTRL_STREAM_CMD, dsp_type1::calc_stream_cmd_word( +    _iface->poke32(_iface->regs.rx_ctrl_stream_cmd, dsp_type1::calc_stream_cmd_word(          stream_cmd, _recv_frame_size      )); -    _iface->poke32(U2_REG_RX_CTRL_TIME_SECS,  boost::uint32_t(stream_cmd.time_spec.get_full_secs())); -    _iface->poke32(U2_REG_RX_CTRL_TIME_TICKS, stream_cmd.time_spec.get_tick_count(get_master_clock_freq())); +    _iface->poke32(_iface->regs.rx_ctrl_time_secs,  boost::uint32_t(stream_cmd.time_spec.get_full_secs())); +    _iface->poke32(_iface->regs.rx_ctrl_time_ticks, stream_cmd.time_spec.get_tick_count(get_master_clock_freq()));  }  /*********************************************************************** @@ -189,7 +204,6 @@ static const std::string dboard_name = "0";  void usrp2_mboard_impl::get(const wax::obj &key_, wax::obj &val){      named_prop_t key = named_prop_t::extract(key_); -      //handle the get request conditioned on the key      switch(key.as<mboard_prop_t>()){      case MBOARD_PROP_NAME: @@ -242,7 +256,7 @@ void usrp2_mboard_impl::get(const wax::obj &key_, wax::obj &val){      case MBOARD_PROP_TIME_NOW:{              usrp2_iface::pair64 time64( -                _iface->peek64(U2_REG_TIME64_SECS_RB, U2_REG_TIME64_TICKS_RB) +                _iface->peek64(_iface->regs.time64_secs_rb, _iface->regs.time64_ticks_rb)              );              val = time_spec_t(                  time64.first, time64.second, get_master_clock_freq() @@ -270,8 +284,7 @@ void usrp2_mboard_impl::get(const wax::obj &key_, wax::obj &val){   * MBoard Set Properties   **********************************************************************/  void usrp2_mboard_impl::set(const wax::obj &key, const wax::obj &val){ - -    //handle the get request conditioned on the key +    //handle the set request conditioned on the key      switch(key.as<mboard_prop_t>()){      case MBOARD_PROP_CLOCK_CONFIG: @@ -297,7 +310,7 @@ void usrp2_mboard_impl::set(const wax::obj &key, const wax::obj &val){          //sanity check          UHD_ASSERT_THROW(_rx_subdev_spec.size() == 1);          //set the mux -        _iface->poke32(U2_REG_DSP_RX_MUX, dsp_type1::calc_rx_mux_word( +        _iface->poke32(_iface->regs.dsp_rx_mux, dsp_type1::calc_rx_mux_word(              _dboard_manager->get_rx_subdev(_rx_subdev_spec.front().sd_name)[SUBDEV_PROP_CONNECTION].as<subdev_conn_t>()          ));          return; @@ -308,7 +321,7 @@ void usrp2_mboard_impl::set(const wax::obj &key, const wax::obj &val){          //sanity check          UHD_ASSERT_THROW(_tx_subdev_spec.size() == 1);          //set the mux -        _iface->poke32(U2_REG_DSP_TX_MUX, dsp_type1::calc_tx_mux_word( +        _iface->poke32(_iface->regs.dsp_tx_mux, dsp_type1::calc_tx_mux_word(              _dboard_manager->get_tx_subdev(_tx_subdev_spec.front().sd_name)[SUBDEV_PROP_CONNECTION].as<subdev_conn_t>()          ));          return; diff --git a/host/lib/usrp/usrp2/serdes_ctrl.cpp b/host/lib/usrp/usrp2/serdes_ctrl.cpp index e83dceb96..1cda22f45 100644 --- a/host/lib/usrp/usrp2/serdes_ctrl.cpp +++ b/host/lib/usrp/usrp2/serdes_ctrl.cpp @@ -27,11 +27,11 @@ class usrp2_serdes_ctrl_impl : public usrp2_serdes_ctrl{  public:      usrp2_serdes_ctrl_impl(usrp2_iface::sptr iface){          _iface = iface; -        _iface->poke32(U2_REG_MISC_CTRL_SERDES, U2_FLAG_MISC_CTRL_SERDES_ENABLE | U2_FLAG_MISC_CTRL_SERDES_RXEN); +        _iface->poke32(_iface->regs.misc_ctrl_serdes, U2_FLAG_MISC_CTRL_SERDES_ENABLE | U2_FLAG_MISC_CTRL_SERDES_RXEN);      }      ~usrp2_serdes_ctrl_impl(void){ -        _iface->poke32(U2_REG_MISC_CTRL_SERDES, 0); //power-down +        _iface->poke32(_iface->regs.misc_ctrl_serdes, 0); //power-down      }  private: diff --git a/host/lib/usrp/usrp2/usrp2_clk_regs.hpp b/host/lib/usrp/usrp2/usrp2_clk_regs.hpp new file mode 100644 index 000000000..d5f80a919 --- /dev/null +++ b/host/lib/usrp/usrp2/usrp2_clk_regs.hpp @@ -0,0 +1,78 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_USRP2_CLK_REGS_HPP +#define INCLUDED_USRP2_CLK_REGS_HPP + +#include "usrp2_regs.hpp" + +class usrp2_clk_regs_t { +public: +  usrp2_clk_regs_t(void) { ; } +  usrp2_clk_regs_t(boost::uint16_t hw_rev) { +    test = 0; +    fpga = 1; +    adc = (hw_rev >= usrp2_rev_nums(N2XX)) ? 2 : 4; +    dac = 3; +    serdes = (hw_rev >= usrp2_rev_nums(N2XX)) ? 4 : 2; //only used by usrp2+ +    tx_db = (hw_rev >= usrp2_rev_nums(N2XX)) ? 5 : 6; +     +    switch(hw_rev) { +    case usrp2_rev_nums(USRP2_REV3): +        exp = 2; +        break; +    case usrp2_rev_nums(USRP2_REV4): +        exp = 5; +        break; +    case usrp2_rev_nums(N2XX): +        exp = 6; +        break; +    default: +        throw std::runtime_error("Unknown hardware revision"); +        break; +    } +     +    rx_db = 7; +  } + +  static int output(int clknum) { return 0x3C + clknum; } +  static int div_lo(int clknum) { return 0x48 + 2 * clknum; } +  static int div_hi(int clknum) { return 0x49 + 2 * clknum; } + +  const static int acounter = 0x04; +  const static int bcounter_msb = 0x05; +  const static int bcounter_lsb = 0x06; +  const static int pll_1 = 0x07; +  const static int pll_2 = 0x08; +  const static int pll_3 = 0x09; +  const static int pll_4 = 0x0A; +  const static int ref_counter_msb = 0x0B; +  const static int ref_counter_lsb = 0x0C; +  const static int pll_5 = 0x0D; +  const static int update = 0x5A; + +  int test; +  int fpga; +  int adc; +  int dac; +  int serdes; +  int exp; +  int tx_db; +  int rx_db; +}; + +#endif //INCLUDED_USRP2_CLK_REGS_HPP diff --git a/host/lib/usrp/usrp2/usrp2_iface.cpp b/host/lib/usrp/usrp2/usrp2_iface.cpp index f237d6377..10cb86962 100644 --- a/host/lib/usrp/usrp2/usrp2_iface.cpp +++ b/host/lib/usrp/usrp2/usrp2_iface.cpp @@ -24,6 +24,7 @@  #include <boost/asio.hpp> //used for htonl and ntohl  #include <boost/assign/list_of.hpp>  #include <boost/format.hpp> +#include <boost/tokenizer.hpp>  #include <stdexcept>  #include <algorithm> @@ -50,17 +51,18 @@ public:   **********************************************************************/      usrp2_iface_impl(udp_simple::sptr ctrl_transport){          _ctrl_transport = ctrl_transport; +         +        mb_eeprom = mboard_eeprom_t(*this, mboard_eeprom_t::MAP_N100); +        regs = usrp2_get_regs(get_hw_rev());           //check the fpga compatibility number -        const boost::uint32_t fpga_compat_num = this->peek32(U2_REG_COMPAT_NUM_RB); +        const boost::uint32_t fpga_compat_num = this->peek32(this->regs.compat_num_rb);          if (fpga_compat_num != USRP2_FPGA_COMPAT_NUM){              throw std::runtime_error(str(boost::format(                  "Expected fpga compatibility number %d, but got %d:\n"                  "The fpga build is not compatible with the host code build."              ) % int(USRP2_FPGA_COMPAT_NUM) % fpga_compat_num));          } - -        mb_eeprom = mboard_eeprom_t(*this, mboard_eeprom_t::MAP_N100);      }      ~usrp2_iface_impl(void){ @@ -175,6 +177,58 @@ public:      }  /*********************************************************************** + * 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, 1, 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; +        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); +        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; +        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); +        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; +    } + +/***********************************************************************   * Send/Recv over control   **********************************************************************/      usrp2_ctrl_data_t ctrl_send_and_recv(const usrp2_ctrl_data_t &out_data){ @@ -205,6 +259,15 @@ public:          }          throw std::runtime_error("usrp2 no control response");      } +     +    bool is_usrp2p(void) { +        return (get_hw_rev() >= usrp2_rev_nums(N2XX)); +    } + +    boost::uint16_t get_hw_rev(void) { +        return boost::lexical_cast<boost::uint16_t>(mb_eeprom["rev"]); +    } +  private:      //this lovely lady makes it all possible @@ -242,7 +305,6 @@ private:          UHD_ASSERT_THROW(ntohl(in_data.id) == USRP2_CTRL_ID_WOAH_I_DEFINITELY_PEEKED_IT_DUDE);          return T(ntohl(in_data.data.poke_args.data));      } -  };  /*********************************************************************** @@ -251,3 +313,4 @@ private:  usrp2_iface::sptr usrp2_iface::make(udp_simple::sptr ctrl_transport){      return usrp2_iface::sptr(new usrp2_iface_impl(ctrl_transport));  } + diff --git a/host/lib/usrp/usrp2/usrp2_iface.hpp b/host/lib/usrp/usrp2/usrp2_iface.hpp index bf36cbf6e..d7e5df9f5 100644 --- a/host/lib/usrp/usrp2/usrp2_iface.hpp +++ b/host/lib/usrp/usrp2/usrp2_iface.hpp @@ -26,6 +26,7 @@  #include <boost/cstdint.hpp>  #include <utility>  #include "fw_common.h" +#include "usrp2_regs.hpp"  /*!   * The usrp2 interface class: @@ -104,6 +105,19 @@ public:          bool readback      ) = 0; +    virtual void write_uart(boost::uint8_t dev, const std::string &buf) = 0; + +    virtual std::string read_uart(boost::uint8_t dev) = 0; +     +    virtual boost::uint16_t get_hw_rev(void) = 0; +     +    virtual bool is_usrp2p(void) = 0; + +    /*! +     * Register map selected from USRP2/USRP2+. +     */ +    usrp2_regs_t regs; +          //motherboard eeprom map structure      uhd::usrp::mboard_eeprom_t mb_eeprom;  }; diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index 71f52878c..738c398d9 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -21,6 +21,7 @@  #include "usrp2_iface.hpp"  #include "clock_ctrl.hpp"  #include "codec_ctrl.hpp" +#include "gps_ctrl.hpp"  #include "serdes_ctrl.hpp"  #include <uhd/device.hpp>  #include <uhd/utils/pimpl.hpp> @@ -104,6 +105,7 @@ private:      usrp2_clock_ctrl::sptr _clock_ctrl;      usrp2_codec_ctrl::sptr _codec_ctrl;      usrp2_serdes_ctrl::sptr _serdes_ctrl; +    usrp2_gps_ctrl::sptr _gps_ctrl;      //properties for this mboard      void get(const wax::obj &, wax::obj &); @@ -130,6 +132,9 @@ private:      wax_obj_proxy::sptr _rx_codec_proxy;      wax_obj_proxy::sptr _tx_codec_proxy; +    void rx_codec_set_gain(float, const std::string &); +    uhd::dict<std::string, float> _codec_rx_gains; +      //properties interface for rx dboard      void rx_dboard_get(const wax::obj &, wax::obj &);      void rx_dboard_set(const wax::obj &, const wax::obj &); diff --git a/host/lib/usrp/usrp2/usrp2_regs.cpp b/host/lib/usrp/usrp2/usrp2_regs.cpp new file mode 100644 index 000000000..5853e91e5 --- /dev/null +++ b/host/lib/usrp/usrp2/usrp2_regs.cpp @@ -0,0 +1,98 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "usrp2_regs.hpp" + +int sr_addr(int misc_output_base, int sr) { +	return misc_output_base + 4 * sr; +} + +usrp2_regs_t usrp2_get_regs(boost::uint16_t hw_rev) { +  //how about you just make this dependent on hw_rev instead of doing the init before main, and give up the const globals, since the application won't ever need both. +  const int misc_output_base = (hw_rev >= usrp2_rev_nums(N2XX)) ? USRP2P_MISC_OUTPUT_BASE : USRP2_MISC_OUTPUT_BASE, +            gpio_base        = (hw_rev >= usrp2_rev_nums(N2XX)) ? USRP2P_GPIO_BASE        : USRP2_GPIO_BASE, +            atr_base         = (hw_rev >= usrp2_rev_nums(N2XX)) ? USRP2P_ATR_BASE         : USRP2_ATR_BASE, +            bp_base          = (hw_rev >= usrp2_rev_nums(N2XX)) ? USRP2P_BP_STATUS_BASE   : USRP2_BP_STATUS_BASE; + +  usrp2_regs_t x; +  x.sr_misc = 0; +  x.sr_tx_prot_eng = 32; +  x.sr_rx_prot_eng = 48; +  x.sr_buffer_pool_ctrl = 64; +  x.sr_udp_sm = 96; +  x.sr_tx_dsp = 208; +  x.sr_tx_ctrl = 224; +  x.sr_rx_dsp = 160; +  x.sr_rx_ctrl = 176; +  x.sr_time64 = 192; +  x.sr_simtimer = 198; +  x.sr_last = 255; +  x.misc_ctrl_clock = sr_addr(misc_output_base, 0); +  x.misc_ctrl_serdes = sr_addr(misc_output_base, 1); +  x.misc_ctrl_adc = sr_addr(misc_output_base, 2); +  x.misc_ctrl_leds = sr_addr(misc_output_base, 3); +  x.misc_ctrl_phy = sr_addr(misc_output_base, 4); +  x.misc_ctrl_dbg_mux = sr_addr(misc_output_base, 5); +  x.misc_ctrl_ram_page = sr_addr(misc_output_base, 6); +  x.misc_ctrl_flush_icache = sr_addr(misc_output_base, 7); +  x.misc_ctrl_led_src = sr_addr(misc_output_base, 8); +  x.time64_secs = sr_addr(misc_output_base, x.sr_time64 + 0); +  x.time64_ticks = sr_addr(misc_output_base, x.sr_time64 + 1); +  x.time64_flags = sr_addr(misc_output_base, x.sr_time64 + 2); +  x.time64_imm = sr_addr(misc_output_base, x.sr_time64 + 3); +  x.time64_tps = sr_addr(misc_output_base, x.sr_time64 + 4); +  x.time64_secs_rb = bp_base + 4*10; +  x.time64_ticks_rb = bp_base + 4*11; +  x.compat_num_rb = bp_base + 4*12; +  x.dsp_tx_freq = sr_addr(misc_output_base, x.sr_tx_dsp + 0); +  x.dsp_tx_scale_iq = sr_addr(misc_output_base, x.sr_tx_dsp + 1); +  x.dsp_tx_interp_rate = sr_addr(misc_output_base, x.sr_tx_dsp + 2); +  x.dsp_tx_mux = sr_addr(misc_output_base, x.sr_tx_dsp + 4); +  x.dsp_rx_freq = sr_addr(misc_output_base, x.sr_rx_dsp + 0); +  x.dsp_rx_scale_iq = sr_addr(misc_output_base, x.sr_rx_dsp + 1); +  x.dsp_rx_decim_rate = sr_addr(misc_output_base, x.sr_rx_dsp + 2); +  x.dsp_rx_dcoffset_i = sr_addr(misc_output_base, x.sr_rx_dsp + 3); +  x.dsp_rx_dcoffset_q = sr_addr(misc_output_base, x.sr_rx_dsp + 4); +  x.dsp_rx_mux = sr_addr(misc_output_base, x.sr_rx_dsp + 5); +  x.gpio_io = gpio_base + 0; +  x.gpio_ddr = gpio_base + 4; +  x.gpio_tx_sel = gpio_base + 8; +  x.gpio_rx_sel = gpio_base + 12; +  x.atr_idle_txside = atr_base + 0; +  x.atr_idle_rxside = atr_base + 2; +  x.atr_intx_txside = atr_base + 4; +  x.atr_intx_rxside = atr_base + 6; +  x.atr_inrx_txside = atr_base + 8; +  x.atr_inrx_rxside = atr_base + 10; +  x.atr_full_txside = atr_base + 12; +  x.atr_full_rxside = atr_base + 14; +  x.rx_ctrl_stream_cmd = sr_addr(misc_output_base, x.sr_rx_ctrl + 0); +  x.rx_ctrl_time_secs = sr_addr(misc_output_base, x.sr_rx_ctrl + 1); +  x.rx_ctrl_time_ticks = sr_addr(misc_output_base, x.sr_rx_ctrl + 2); +  x.rx_ctrl_clear_overrun = sr_addr(misc_output_base, x.sr_rx_ctrl + 3); +  x.rx_ctrl_vrt_header = sr_addr(misc_output_base, x.sr_rx_ctrl + 4); +  x.rx_ctrl_vrt_stream_id = sr_addr(misc_output_base, x.sr_rx_ctrl + 5); +  x.rx_ctrl_vrt_trailer = sr_addr(misc_output_base, x.sr_rx_ctrl + 6); +  x.rx_ctrl_nsamps_per_pkt = sr_addr(misc_output_base, x.sr_rx_ctrl + 7); +  x.rx_ctrl_nchannels = sr_addr(misc_output_base, x.sr_rx_ctrl + 8); +  x.tx_ctrl_num_chan = sr_addr(misc_output_base, x.sr_tx_ctrl + 0); +  x.tx_ctrl_clear_state = sr_addr(misc_output_base, x.sr_tx_ctrl + 1); +  x.tx_ctrl_report_sid = sr_addr(misc_output_base, x.sr_tx_ctrl + 2); +  x.tx_ctrl_policy = sr_addr(misc_output_base, x.sr_tx_ctrl + 3); + +  return x; +} diff --git a/host/lib/usrp/usrp2/usrp2_regs.hpp b/host/lib/usrp/usrp2/usrp2_regs.hpp index 064ad4e95..d84106f36 100644 --- a/host/lib/usrp/usrp2/usrp2_regs.hpp +++ b/host/lib/usrp/usrp2/usrp2_regs.hpp @@ -18,6 +18,97 @@  #ifndef INCLUDED_USRP2_REGS_HPP  #define INCLUDED_USRP2_REGS_HPP +#include <boost/cstdint.hpp> + +enum usrp2_rev_nums { +    USRP2_REV3 = 0x0003, +    USRP2_REV4 = 0x0004, +    N2XX       = 0x0A00 +}; + +#define USRP2_MISC_OUTPUT_BASE  0xD400 +#define USRP2_GPIO_BASE         0xC800 +#define USRP2_ATR_BASE          0xE400 +#define USRP2_BP_STATUS_BASE    0xCC00 + +#define USRP2P_MISC_OUTPUT_BASE 0x2000 +#define USRP2P_GPIO_BASE        0x3200 +#define USRP2P_ATR_BASE         0x3800 +#define USRP2P_BP_STATUS_BASE   0x3300 + +typedef struct { +    int sr_misc; +    int sr_tx_prot_eng; +    int sr_rx_prot_eng; +    int sr_buffer_pool_ctrl; +    int sr_udp_sm; +    int sr_tx_dsp; +    int sr_tx_ctrl; +    int sr_rx_dsp; +    int sr_rx_ctrl; +    int sr_time64; +    int sr_simtimer; +    int sr_last; +    int misc_ctrl_clock; +    int misc_ctrl_serdes; +    int misc_ctrl_adc; +    int misc_ctrl_leds; +    int misc_ctrl_phy; +    int misc_ctrl_dbg_mux; +    int misc_ctrl_ram_page; +    int misc_ctrl_flush_icache; +    int misc_ctrl_led_src; +    int time64_secs; // value to set absolute secs to on next PPS +    int time64_ticks; // value to set absolute ticks to on next PPS +    int time64_flags; // flags -- see chart below +    int time64_imm; // set immediate (0=latch on next pps, 1=latch immediate, default=0) +    int time64_tps; // ticks per second rollover count +    int time64_secs_rb; +    int time64_ticks_rb; +    int compat_num_rb; +    int dsp_tx_freq; +    int dsp_tx_scale_iq; +    int dsp_tx_interp_rate; +    int dsp_tx_mux; +    int dsp_rx_freq; +    int dsp_rx_scale_iq; +    int dsp_rx_decim_rate; +    int dsp_rx_dcoffset_i; +    int dsp_rx_dcoffset_q; +    int dsp_rx_mux; +    int gpio_base; +    int gpio_io; +    int gpio_ddr; +    int gpio_tx_sel; +    int gpio_rx_sel; +    int atr_base; +    int atr_idle_txside; +    int atr_idle_rxside; +    int atr_intx_txside; +    int atr_intx_rxside; +    int atr_inrx_txside; +    int atr_inrx_rxside; +    int atr_full_txside; +    int atr_full_rxside; +    int rx_ctrl_stream_cmd; +    int rx_ctrl_time_secs; +    int rx_ctrl_time_ticks; +    int rx_ctrl_clear_overrun; +    int rx_ctrl_vrt_header; +    int rx_ctrl_vrt_stream_id; +    int rx_ctrl_vrt_trailer; +    int rx_ctrl_nsamps_per_pkt; +    int rx_ctrl_nchannels; +    int tx_ctrl_num_chan; +    int tx_ctrl_clear_state; +    int tx_ctrl_report_sid; +    int tx_ctrl_policy; +} usrp2_regs_t; + +extern const usrp2_regs_t usrp2_regs; //the register definitions, set in usrp2_regs.cpp and usrp2p_regs.cpp + +usrp2_regs_t usrp2_get_regs(boost::uint16_t hw_rev); +  ////////////////////////////////////////////////////  // Settings Bus, Slave #7, Not Byte Addressable!  // @@ -25,27 +116,12 @@  // 1KB of address space (== 256 32-bit write-only regs) -#define MISC_OUTPUT_BASE        0xD400 +//#define MISC_OUTPUT_BASE        0xD400  //#define TX_PROTOCOL_ENGINE_BASE 0xD480  //#define RX_PROTOCOL_ENGINE_BASE 0xD4C0  //#define BUFFER_POOL_CTRL_BASE   0xD500  //#define LAST_SETTING_REG        0xD7FC  // last valid setting register -#define SR_MISC 0 -#define SR_TX_PROT_ENG 32 -#define SR_RX_PROT_ENG 48 -#define SR_BUFFER_POOL_CTRL 64 -#define SR_UDP_SM 96 -#define SR_TX_DSP 208 -#define SR_TX_CTRL 224 -#define SR_RX_DSP 160 -#define SR_RX_CTRL 176 -#define SR_TIME64 192 -#define SR_SIMTIMER 198 -#define SR_LAST 255 - -#define _SR_ADDR(sr)    ((MISC_OUTPUT_BASE) + (4*(sr))) -  /////////////////////////////////////////////////  // SPI Slave Constants  //////////////////////////////////////////////// @@ -58,20 +134,11 @@  #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           _SR_ADDR(0) -#define U2_REG_MISC_CTRL_SERDES          _SR_ADDR(1) -#define U2_REG_MISC_CTRL_ADC             _SR_ADDR(2) -#define U2_REG_MISC_CTRL_LEDS            _SR_ADDR(3) -#define U2_REG_MISC_CTRL_PHY             _SR_ADDR(4) // LSB is reset line to eth phy -#define U2_REG_MISC_CTRL_DBG_MUX         _SR_ADDR(5) -#define U2_REG_MISC_CTRL_RAM_PAGE        _SR_ADDR(6) // FIXME should go somewhere else... -#define U2_REG_MISC_CTRL_FLUSH_ICACHE    _SR_ADDR(7) // Flush the icache -#define U2_REG_MISC_CTRL_LED_SRC         _SR_ADDR(8) // HW or SW control for LEDs -  #define U2_FLAG_MISC_CTRL_SERDES_ENABLE 8  #define U2_FLAG_MISC_CTRL_SERDES_PRBSEN 4  #define U2_FLAG_MISC_CTRL_SERDES_LOOPEN 2 @@ -99,15 +166,6 @@     *     * </pre>     */ -#define U2_REG_TIME64_SECS  _SR_ADDR(SR_TIME64 + 0)  // value to set absolute secs to on next PPS -#define U2_REG_TIME64_TICKS _SR_ADDR(SR_TIME64 + 1)  // value to set absolute ticks to on next PPS -#define U2_REG_TIME64_FLAGS _SR_ADDR(SR_TIME64 + 2)  // flags - see chart above -#define U2_REG_TIME64_IMM   _SR_ADDR(SR_TIME64 + 3)  // set immediate (0=latch on next pps, 1=latch immediate, default=0) -#define U2_REG_TIME64_TPS   _SR_ADDR(SR_TIME64 + 4)  // the ticks per second rollover count - -#define U2_REG_TIME64_SECS_RB  (0xCC00 + 4*10) -#define U2_REG_TIME64_TICKS_RB (0xCC00 + 4*11) -#define U2_REG_COMPAT_NUM_RB   (0xCC00 + 4*12)  //pps flags (see above)  #define U2_FLAG_TIME64_PPS_NEGEDGE (0 << 0) @@ -121,34 +179,72 @@  /////////////////////////////////////////////////  // DSP TX Regs  //////////////////////////////////////////////// -#define U2_REG_DSP_TX_FREQ         _SR_ADDR(SR_TX_DSP + 0) -#define U2_REG_DSP_TX_SCALE_IQ     _SR_ADDR(SR_TX_DSP + 1) // {scale_i,scale_q} -#define U2_REG_DSP_TX_INTERP_RATE  _SR_ADDR(SR_TX_DSP + 2) -#define U2_REG_DSP_TX_MUX  _SR_ADDR(SR_TX_DSP + 4) + +  /*! +   * \brief output mux configuration. +   * +   * <pre> +   *     3                   2                   1                        +   *   1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +   *  +-------------------------------+-------+-------+-------+-------+ +   *  |                                               | DAC1  |  DAC0 | +   *  +-------------------------------+-------+-------+-------+-------+ +   *  +   *  There are N DUCs (1 now) with complex inputs and outputs. +   *  There are two DACs. +   *  +   *  Each 4-bit DACx field specifies the source for the DAC +   *  Each subfield is coded like this:  +   *  +   *     3 2 1 0 +   *    +-------+ +   *    |   N   | +   *    +-------+ +   *  +   *  N specifies which DUC output is connected to this DAC. +   *  +   *   N   which interp output +   *  ---  ------------------- +   *   0   DUC 0 I +   *   1   DUC 0 Q +   *   2   DUC 1 I +   *   3   DUC 1 Q +   *   F   All Zeros +   *    +   * The default value is 0x10 +   * </pre> +   */ +  /////////////////////////////////////////////////  // DSP RX Regs  //////////////////////////////////////////////// -#define U2_REG_DSP_RX_FREQ         _SR_ADDR(SR_RX_DSP + 0) -#define U2_REG_DSP_RX_SCALE_IQ     _SR_ADDR(SR_RX_DSP + 1) // {scale_i,scale_q} -#define U2_REG_DSP_RX_DECIM_RATE   _SR_ADDR(SR_RX_DSP + 2) -#define U2_REG_DSP_RX_DCOFFSET_I   _SR_ADDR(SR_RX_DSP + 3) // Bit 31 high sets fixed offset mode, using lower 14 bits, -                                                       // otherwise it is automatic  -#define U2_REG_DSP_RX_DCOFFSET_Q   _SR_ADDR(SR_RX_DSP + 4) // Bit 31 high sets fixed offset mode, using lower 14 bits -#define U2_REG_DSP_RX_MUX  _SR_ADDR(SR_RX_DSP + 5)         // called adc_mux in dsp_core_rx.v + +  /*! +   * \brief input mux configuration. +   * +   * This determines which ADC (or constant zero) is connected to  +   * each DDC input.  There are N DDCs (1 now).  Each has two inputs. +   * +   * <pre> +   * Mux value: +   * +   *    3                   2                   1                        +   *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +   * +-------+-------+-------+-------+-------+-------+-------+-------+ +   * |                                                       |Q0 |I0 | +   * +-------+-------+-------+-------+-------+-------+-------+-------+ +   * +   * Each 2-bit I field is either 00 (A/D A), 01 (A/D B) or 1X (const zero) +   * Each 2-bit Q field is either 00 (A/D A), 01 (A/D B) or 1X (const zero) +   * +   * The default value is 0x4 +   * </pre> +   */  ////////////////////////////////////////////////  // GPIO, Slave 4  //////////////////////////////////////////////// -// -// These go to the daughterboard i/o pins -// -#define U2_REG_GPIO_BASE 0xC800 - -#define U2_REG_GPIO_IO         U2_REG_GPIO_BASE + 0  // 32 bits, gpio io pins (tx high 16 bits, rx low 16 bits) -#define U2_REG_GPIO_DDR        U2_REG_GPIO_BASE + 4  // 32 bits, gpio ddr, 1 means output (tx high 16 bits, rx low 16 bits) -#define U2_REG_GPIO_TX_SEL     U2_REG_GPIO_BASE + 8  // 16 2-bit fields select which source goes to TX DB -#define U2_REG_GPIO_RX_SEL     U2_REG_GPIO_BASE + 12 // 16 2-bit fields select which source goes to RX DB  // 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 @@ -159,40 +255,20 @@  ///////////////////////////////////////////////////  // ATR Controller, Slave 11  //////////////////////////////////////////////// -#define U2_REG_ATR_BASE  0xE400 -#define U2_REG_ATR_IDLE_TXSIDE  U2_REG_ATR_BASE + 0 -#define U2_REG_ATR_IDLE_RXSIDE  U2_REG_ATR_BASE + 2 -#define U2_REG_ATR_INTX_TXSIDE  U2_REG_ATR_BASE + 4 -#define U2_REG_ATR_INTX_RXSIDE  U2_REG_ATR_BASE + 6 -#define U2_REG_ATR_INRX_TXSIDE  U2_REG_ATR_BASE + 8 -#define U2_REG_ATR_INRX_RXSIDE  U2_REG_ATR_BASE + 10 -#define U2_REG_ATR_FULL_TXSIDE  U2_REG_ATR_BASE + 12 -#define U2_REG_ATR_FULL_RXSIDE  U2_REG_ATR_BASE + 14  ///////////////////////////////////////////////////  // RX CTRL regs  /////////////////////////////////////////////////// -// The following 3 are logically a single command register. -// They are clocked into the underlying fifo when time_ticks is written. -#define U2_REG_RX_CTRL_STREAM_CMD        _SR_ADDR(SR_RX_CTRL + 0) // {now, chain, num_samples(30) -#define U2_REG_RX_CTRL_TIME_SECS         _SR_ADDR(SR_RX_CTRL + 1) -#define U2_REG_RX_CTRL_TIME_TICKS        _SR_ADDR(SR_RX_CTRL + 2) - -#define U2_REG_RX_CTRL_CLEAR_OVERRUN     _SR_ADDR(SR_RX_CTRL + 3) // write anything to clear overrun -#define U2_REG_RX_CTRL_VRT_HEADER        _SR_ADDR(SR_RX_CTRL + 4) // word 0 of packet.  FPGA fills in packet counter -#define U2_REG_RX_CTRL_VRT_STREAM_ID     _SR_ADDR(SR_RX_CTRL + 5) // word 1 of packet. -#define U2_REG_RX_CTRL_VRT_TRAILER       _SR_ADDR(SR_RX_CTRL + 6) -#define U2_REG_RX_CTRL_NSAMPS_PER_PKT    _SR_ADDR(SR_RX_CTRL + 7) -#define U2_REG_RX_CTRL_NCHANNELS         _SR_ADDR(SR_RX_CTRL + 8) // 1 in basic case, up to 4 for vector sources +  ///////////////////////////////////////////////////  // TX CTRL regs  /////////////////////////////////////////////////// -#define U2_REG_TX_CTRL_NUM_CHAN          _SR_ADDR(SR_TX_CTRL + 0) -#define U2_REG_TX_CTRL_CLEAR_STATE       _SR_ADDR(SR_TX_CTRL + 1) -#define U2_REG_TX_CTRL_REPORT_SID        _SR_ADDR(SR_TX_CTRL + 2) -#define U2_REG_TX_CTRL_POLICY            _SR_ADDR(SR_TX_CTRL + 3) +//#define U2_REG_TX_CTRL_NUM_CHAN          _SR_ADDR(SR_TX_CTRL + 0) +//#define U2_REG_TX_CTRL_CLEAR_STATE       _SR_ADDR(SR_TX_CTRL + 1) +//#define U2_REG_TX_CTRL_REPORT_SID        _SR_ADDR(SR_TX_CTRL + 2) +//#define U2_REG_TX_CTRL_POLICY            _SR_ADDR(SR_TX_CTRL + 3)  #define U2_FLAG_TX_CTRL_POLICY_WAIT          (0x1 << 0)  #define U2_FLAG_TX_CTRL_POLICY_NEXT_PACKET   (0x1 << 1) diff --git a/host/utils/usrp2p_fw_update.py b/host/utils/usrp2p_fw_update.py new file mode 100755 index 000000000..0c07c398d --- /dev/null +++ b/host/utils/usrp2p_fw_update.py @@ -0,0 +1,289 @@ +#!/usr/bin/env python +# +# Copyright 2010 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. +# + +# TODO: make it work +# TODO: make it autodetect UHD devices +# TODO: you should probably watch sequence numbers + +import optparse +import math +import os +import re +import struct +import socket +import sys + +######################################################################## +# constants +######################################################################## +UDP_FW_UPDATE_PORT = 49154 +UDP_MAX_XFER_BYTES = 1024 +UDP_TIMEOUT = 3 +UDP_POLL_INTERVAL = 0.10 #in seconds + +USRP2_FW_PROTO_VERSION = 6 + +#from bootloader_utils.h + +FPGA_IMAGE_SIZE_BYTES = 1572864 +FW_IMAGE_SIZE_BYTES = 31744 +SAFE_FPGA_IMAGE_LOCATION_ADDR = 0x00000000 +SAFE_FW_IMAGE_LOCATION_ADDR = 0x003F0000 +PROD_FPGA_IMAGE_LOCATION_ADDR = 0x00180000 +PROD_FW_IMAGE_LOCATION_ADDR = 0x00300000 + +FLASH_DATA_PACKET_SIZE = 256 + +#see fw_common.h +FLASH_ARGS_FMT = '!LLLLL256s' +FLASH_INFO_FMT = '!LLLLL256x' +FLASH_IP_FMT =   '!LLLL260x' + +class update_id_t: +  USRP2_FW_UPDATE_ID_WAT = ord(' ') +  USRP2_FW_UPDATE_ID_OHAI_LOL = ord('a') +  USRP2_FW_UPDATE_ID_OHAI_OMG = ord('A') +  USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL = ord('f') +  USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG = ord('F') +  USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL = ord('e') +  USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG = ord('E') +  USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL = ord('d') +  USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG = ord('D') +  USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG = ord('B') +  USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL = ord('w') +  USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG = ord('W') +  USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL = ord('r') +  USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG = ord('R') +  USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL = ord('s') +  USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG = ord('S') +  USRP2_FW_UPDATE_ID_KTHXBAI = ord('~') + +_seq = -1 +def seq(): +  global _seq +  _seq = _seq+1 +  return _seq  + +######################################################################## +# helper functions +######################################################################## +def unpack_flash_args_fmt(s): +  return struct.unpack(FLASH_ARGS_FMT, s) #(proto_ver, pktid, seq, flash_addr, length, data) + +def unpack_flash_info_fmt(s): +  return struct.unpack(FLASH_INFO_FMT, s) #(proto_ver, pktid, seq, sector_size_bytes, memory_size_bytes) + +def unpack_flash_ip_fmt(s): +  return struct.unpack(FLASH_IP_FMT, s) #(proto_ver, pktid, seq, ip_addr) + +def pack_flash_args_fmt(proto_ver, pktid, seq, flash_addr, length, data): +  return struct.pack(FLASH_ARGS_FMT, proto_ver, pktid, seq, flash_addr, length, data) + +def pack_flash_info_fmt(proto_ver, pktid, seq, sector_size_bytes, memory_size_bytes): +  return struct.pack(FLASH_INFO_FMT, proto_ver, pktid, seq, sector_size_bytes, memory_size_bytes) + +def send_and_recv(pkt, ip): +  update_socket = create_socket() + +  try: +    update_socket.sendto(pkt, (ip, UDP_FW_UPDATE_PORT)) +  except Exception, e:  +    print e +    sys.exit(1) + +  try: +    (recv_pkt, recv_addr) = update_socket.recvfrom(UDP_MAX_XFER_BYTES) +  except Exception, e:  +    print e +    sys.exit(1) + +  if recv_addr != (options.ip, UDP_FW_UPDATE_PORT): +    raise Exception, "Packet received from invalid IP %s, expected %s" % (recv_addr, options.ip) + +  return recv_pkt + +def create_socket(): +  socket.setdefaulttimeout(UDP_TIMEOUT) +  update_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +  return update_socket + +#just here to validate comms +def init_update(ip): +  out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_OHAI_LOL, seq(), 0, 0, "") +  in_pkt = send_and_recv(out_pkt, ip) +  (proto_ver, pktid, rxseq, ip_addr) = unpack_flash_ip_fmt(in_pkt) +  if pktid == update_id_t.USRP2_FW_UPDATE_ID_OHAI_OMG: +    print "USRP2P found." +  else: +    raise Exception, "Invalid reply received from device." + +#  print "Incoming:\n\tVer: %i\n\tID: %c\n\tSeq: %i\n\tIP: %i\n" % (proto_ver, chr(pktid), rxseq, ip_addr) + +def get_flash_info(ip): +  out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL, seq(), 0, 0, "") +  in_pkt = send_and_recv(out_pkt, ip) + +  (proto_ver, pktid, rxseq, sector_size_bytes, memory_size_bytes) = unpack_flash_info_fmt(in_pkt) + +  if pktid != update_id_t.USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG: +    raise Exception, "Invalid reply %c from device." % (chr(pktid)) + + +  return (memory_size_bytes, sector_size_bytes) + +def burn_fw(ip, fw, fpga, reset, safe): +  init_update(ip) +  (flash_size, sector_size) = get_flash_info(ip) + +  print "Flash size: %i\nSector size: %i" % (flash_size, sector_size) + +  if fpga: +    if safe: +        image_location = SAFE_FPGA_IMAGE_LOCATION_ADDR +    else: +        image_location = PROD_FPGA_IMAGE_LOCATION_ADDR +     +    fpga_file = open(fpga, 'rb') +    fpga_image = fpga_file.read() +    erase_image(ip, image_location, FPGA_IMAGE_SIZE_BYTES) +    write_image(ip, fpga_image, image_location) +    verify_image(ip, fpga_image, image_location) + +  if fw: +    if safe: +        image_location = SAFE_FW_IMAGE_LOCATION_ADDR +    else: +        image_location = PROD_FW_IMAGE_LOCATION_ADDR +         +    fw_file = open(fw, 'rb') +    fw_image = fw_file.read() +    erase_image(ip, image_location, FW_IMAGE_SIZE_BYTES) +    write_image(ip, fw_image, image_location) +    verify_image(ip, fw_image, image_location) +     +  if reset: +    reset_usrp(ip) + +def write_image(ip, image, addr): +#we split the image into smaller (256B) bits and send them down the wire +  while image: +    out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL, seq(), addr, FLASH_DATA_PACKET_SIZE, image[:FLASH_DATA_PACKET_SIZE]) +    in_pkt = send_and_recv(out_pkt, ip) + +    (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt) + +    if pktid != update_id_t.USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG: +      raise Exception, "Invalid reply %c from device." % (chr(pktid)) + +    image = image[FLASH_DATA_PACKET_SIZE:] +    addr += FLASH_DATA_PACKET_SIZE + +def verify_image(ip, image, addr): +  readsize = len(image) +  readdata = str() +  while readsize > 0: +    if readsize < FLASH_DATA_PACKET_SIZE: thisreadsize = readsize +    else: thisreadsize = FLASH_DATA_PACKET_SIZE +    out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL, seq(), addr, thisreadsize, "") +    in_pkt = send_and_recv(out_pkt, ip) +     +    (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt) + +    if pktid != update_id_t.USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG: +      raise Exception, "Invalid reply %c from device." % (chr(pktid)) + +    readdata += data[:thisreadsize] +    readsize -= FLASH_DATA_PACKET_SIZE +    addr += FLASH_DATA_PACKET_SIZE + +  print "Read back %i bytes" % len(readdata) +#  print readdata + +#  for i in range(256, 512): +#    print "out: %i in: %i" % (ord(image[i]), ord(readdata[i])) + +  if readdata != image: +    print "Verify failed. Image did not write correctly." +  else: +    print "Success." +     +def reset_usrp(ip): +    out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL, seq(), 0, 0, "") +    in_pkt = send_and_recv(out_pkt, ip) +     +    (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt) +    if pktid == update_id_t.USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG: +        raise Exception, "Device failed to reset." + +def erase_image(ip, addr, length): +  #get flash info first +  out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL, seq(), addr, length, "") +  in_pkt = send_and_recv(out_pkt, ip) + +  (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt) + +  if pktid != update_id_t.USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG: +    raise Exception, "Invalid reply %c from device." % (chr(pktid)) + +  print "Erasing %i bytes at %i" % (length, addr) + +  #now wait for it to finish +  while(1): +    out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL, seq(), 0, 0, "") +    in_pkt = send_and_recv(out_pkt, ip) + +    (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt) + +    if pktid == update_id_t.USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG: break +    elif pktid != update_id_t.USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG: +      raise Exception, "Invalid reply %c from device." % (chr(pktid)) + +  print "\tFinished." + + +######################################################################## +# command line options +######################################################################## +def get_options(): +    parser = optparse.OptionParser() +    parser.add_option("--ip",   type="string",                 help="USRP2P firmware address",        default='') +    parser.add_option("--fw",   type="string",                 help="firmware image path (optional)", default='') +    parser.add_option("--fpga", type="string",                 help="fpga image path (optional)",     default='') +    parser.add_option("--reset", action="store_true",          help="reset the device after writing", default=False) +    parser.add_option("--overwrite-safe", action="store_true", help="never ever use this option", default=False) +    (options, args) = parser.parse_args() + +    return options + +######################################################################## +# main +######################################################################## +if __name__=='__main__': +    options = get_options() +    if not options.ip: raise Exception, 'no ip address specified' + +    if not options.fpga and not options.fw and not options.reset: raise Exception, 'Must specify either a firmware image or FPGA image, and/or reset.' +     +    if options.overwrite_safe: +        print("Are you REALLY, REALLY sure you want to overwrite the safe image? This is ALMOST ALWAYS a terrible idea.") +        print("If your image is faulty, your USRP2+ will become a brick until reprogrammed via JTAG.") +        response = raw_input("""Type "yes" to continue, or anything else to quit: """) +        if response != "yes": +            sys.exit(0) +     +    burn_fw(ip=options.ip, fw=options.fw, fpga=options.fpga, reset=options.reset, safe=options.overwrite_safe) | 
