diff options
| author | Josh Blum <josh@joshknows.com> | 2011-01-30 22:10:49 +0000 | 
|---|---|---|
| committer | Josh Blum <josh@joshknows.com> | 2011-01-30 22:10:49 +0000 | 
| commit | 7b03f4144a0dc7a1e745ac43a3997b0eab7042c0 (patch) | |
| tree | be448bf0b605a550c60eff7364953abc57295799 | |
| parent | 4afdcd180e5ee6b93fefe2fb07b071452fb7ef3f (diff) | |
| download | uhd-7b03f4144a0dc7a1e745ac43a3997b0eab7042c0.tar.gz uhd-7b03f4144a0dc7a1e745ac43a3997b0eab7042c0.tar.bz2 uhd-7b03f4144a0dc7a1e745ac43a3997b0eab7042c0.zip | |
usrp-e100: work on clock control
added vco calibration routine and readback to check for calibrated
changed the counters/dividers calculation to be event driven and more
mathematically calculated.
| -rwxr-xr-x | host/lib/ic_reg_maps/gen_ad9522_regs.py | 8 | ||||
| -rw-r--r-- | host/lib/usrp/usrp_e100/clock_ctrl.cpp | 151 | 
2 files changed, 106 insertions, 53 deletions
| diff --git a/host/lib/ic_reg_maps/gen_ad9522_regs.py b/host/lib/ic_reg_maps/gen_ad9522_regs.py index a5debe568..86605c34a 100755 --- a/host/lib/ic_reg_maps/gen_ad9522_regs.py +++ b/host/lib/ic_reg_maps/gen_ad9522_regs.py @@ -80,6 +80,14 @@ external_zero_delay_fcds    0x01E[4:3]               0  enable_external_zero_delay  0x01E[2]                 0  enable_zero_delay           0x01E[1]                 0  ######################################################################## +vco_calibration_finished    0x01F[6]                 0 +holdover_active             0x01F[5]                 0 +ref2_selected               0x01F[4]                 0 +vco_freq_gt_thresh          0x01F[3]                 0 +ref2_freq_gt_thresh         0x01F[2]                 0 +ref1_freq_gt_thresh         0x01F[1]                 0 +digital_lock_detect         0x01F[0]                 0 +########################################################################  #for $i in range(12)  #set $addr = ($i + 0x0F0)  out$(i)_format              $(addr)[7]             0             lvds, cmos diff --git a/host/lib/usrp/usrp_e100/clock_ctrl.cpp b/host/lib/usrp/usrp_e100/clock_ctrl.cpp index ef5e9b5ec..36ab1a8be 100644 --- a/host/lib/usrp/usrp_e100/clock_ctrl.cpp +++ b/host/lib/usrp/usrp_e100/clock_ctrl.cpp @@ -23,7 +23,7 @@  #include <boost/assign/list_of.hpp>  #include <boost/foreach.hpp>  #include <boost/format.hpp> -#include <boost/operators.hpp> +#include <boost/thread/thread.hpp>  #include <boost/math/common_factor_rt.hpp> //gcd  #include <algorithm>  #include <utility> @@ -34,7 +34,8 @@ using namespace uhd;  /***********************************************************************   * Constants   **********************************************************************/ -static const bool ENABLE_THE_TEST_OUT = false; +static const bool CLOCK_SETTINGS_DEBUG = false; +static const bool ENABLE_THE_TEST_OUT = true;  static const double REFERENCE_INPUT_RATE = 10e6;  static const double DEFAULT_OUTPUT_RATE = 64e6; @@ -53,7 +54,7 @@ template <typename div_type, typename bypass_type> static void set_clock_divider   * Clock rate calculation stuff:   *   Using the internal VCO between 1400 and 1800 MHz   **********************************************************************/ -struct clock_settings_type : boost::totally_ordered<clock_settings_type>{ +struct clock_settings_type{      size_t ref_clock_doubler, r_counter, a_counter, b_counter, prescaler, vco_divider, chan_divider;      size_t get_n_counter(void) const{return prescaler * b_counter + a_counter;}      double get_ref_rate(void) const{return REFERENCE_INPUT_RATE * ref_clock_doubler;} @@ -85,44 +86,78 @@ struct clock_settings_type : boost::totally_ordered<clock_settings_type>{      }  }; -bool operator<(const clock_settings_type &lhs, const clock_settings_type &rhs){ -    if (lhs.get_out_rate() != rhs.get_out_rate()) //sort small to large out rates -        return lhs.get_out_rate() < rhs.get_out_rate(); - -    if (lhs.r_counter != rhs.r_counter) //sort small to large r dividers -        return lhs.r_counter < rhs.r_counter; - -    if (lhs.get_vco_rate() != rhs.get_vco_rate()) //sort large to small vco rates -        return lhs.get_vco_rate() > rhs.get_vco_rate(); - -    return false; //whatever case +//! gives the greatest divisor of num between 1 and max inclusive +template<typename T> static inline T greatest_divisor(T num, T max){ +    for (T i = max; i > 1; i--) if (num%i == 0) return i; return 1;  } -static std::vector<clock_settings_type> _get_clock_settings(void){ -    std::vector<clock_settings_type> clock_settings; +//! gives the least divisor of num between min and num exclusive +template<typename T> static inline T least_divisor(T num, T min){ +    for (T i = min; i < num; i++) if (num%i == 0) return i; return 1; +} +static clock_settings_type get_clock_settings(double rate){      clock_settings_type cs;      cs.ref_clock_doubler = 2; //always doubling      cs.prescaler = 8; //set to 8 when input is under 2400 MHz -    for (cs.r_counter = 1; cs.r_counter <= 3; cs.r_counter++){ -    for (cs.b_counter = 3; cs.b_counter <= 10; cs.b_counter++){ -    for (cs.a_counter = 0; cs.a_counter <= 10; cs.a_counter++){ -    for (cs.vco_divider = 2; cs.vco_divider <= 6; cs.vco_divider++){ -    for (cs.chan_divider = 1; cs.chan_divider <= 32; cs.chan_divider++){ -        if (cs.get_vco_rate() > 1800e6) continue; -        if (cs.get_vco_rate() < 1400e6) continue; -        if (cs.get_out_rate() < 32e6) continue; //lowest we allow for GPMC interface -        clock_settings.push_back(cs); -    }}}}} - -    std::sort(clock_settings.begin(), clock_settings.end()); -    return clock_settings; -} +    //basic formulas used below: +    //out_rate*X = ref_rate*Y +    //X = i*ref_rate/gcd +    //Y = i*out_rate/gcd +    //X = chan_div * vco_div * R +    //Y = P*B + A + +    const boost::uint64_t out_rate = boost::uint64_t(rate); +    const boost::uint64_t ref_rate = boost::uint64_t(cs.get_ref_rate()); +    const size_t gcd = size_t(boost::math::gcd(ref_rate, out_rate)); + +    for (size_t i = 1; i <= 100; i++){ +        const size_t X = i*ref_rate/gcd; +        const size_t Y = i*out_rate/gcd; + +        //determine chan_div, vco_div, and r_div +        //and fill in that order of preference +        cs.chan_divider = greatest_divisor<size_t>(X, 32); +        cs.vco_divider = greatest_divisor<size_t>(X/cs.chan_divider, 6); +        cs.r_counter = X/cs.chan_divider/cs.vco_divider; + +        //avoid a vco divider of 1 (if possible) +        if (cs.vco_divider == 1){ +            cs.vco_divider = least_divisor<size_t>(cs.chan_divider, 2); +            cs.chan_divider /= cs.vco_divider; +        } + +        //determine A and B (P is fixed) +        cs.b_counter = Y/cs.prescaler; +        cs.a_counter = Y - cs.b_counter*cs.prescaler; + +        if (CLOCK_SETTINGS_DEBUG){ +            std::cout << "X " << X << std::endl; +            std::cout << "Y " << Y << std::endl; +            std::cout << cs.to_pp_string() << std::endl; +        } + +        //filter limits on the counters +        if (cs.vco_divider == 1) continue; +        if (cs.r_counter >= (1<<14)) continue; +        if (cs.b_counter == 2) continue; +        if (cs.b_counter == 1 and cs.a_counter != 0) continue; +        if (cs.b_counter >= (1<<13)) continue; +        if (cs.a_counter >= (1<<6)) continue; + +        //check the bounds on the vco +        static const double vco_bound_pad = 100e6; +        if (cs.get_vco_rate() > (1800e6 - vco_bound_pad)) continue; +        if (cs.get_vco_rate() < (1400e6 + vco_bound_pad)) continue; + +        std::cout << "USRP-E100 clock control:" << std::endl << cs.to_pp_string() << std::endl; +        return cs; +    } -static std::vector<clock_settings_type> &get_clock_settings(void){ -    static std::vector<clock_settings_type> clock_settings = _get_clock_settings(); -    return clock_settings; +    throw std::runtime_error(str(boost::format( +        "USRP-E100 clock control: could not calculate settings for clock rate %fMHz" +    ) % (rate/1e6)));  }  /*********************************************************************** @@ -139,10 +174,10 @@ public:          //Note: out0 should already be clocking the FPGA or this isnt going to work          _ad9522_regs.sdo_active = ad9522_regs_t::SDO_ACTIVE_SDO_SDIO;          _ad9522_regs.enb_stat_eeprom_at_stat_pin = 0; //use status pin -        _ad9522_regs.status_pin_control = 0x2; //r divider +        _ad9522_regs.status_pin_control = 0x1; //n divider          _ad9522_regs.ld_pin_control = 0x00; //dld          _ad9522_regs.refmon_pin_control = 0x12; //show ref2 -        _ad9522_regs.lock_detect_counter = ad9522_regs_t::LOCK_DETECT_COUNTER_255CYC; +        _ad9522_regs.lock_detect_counter = ad9522_regs_t::LOCK_DETECT_COUNTER_16CYC;          this->use_internal_ref(); @@ -164,7 +199,9 @@ public:       *  - set clock rate w/ internal VCO       *  - set clock rate w/ external VCXO       **********************************************************************/ -    void set_clock_settings_with_internal_vco(const clock_settings_type &cs){ +    void set_clock_settings_with_internal_vco(double rate){ +        const clock_settings_type cs = get_clock_settings(rate); +          //set the rates to private variables so the implementation knows!          _chan_rate = cs.get_chan_rate();          _out_rate = cs.get_out_rate(); @@ -180,7 +217,6 @@ public:          _ad9522_regs.pll_power_down = ad9522_regs_t::PLL_POWER_DOWN_NORMAL;          _ad9522_regs.cp_current = ad9522_regs_t::CP_CURRENT_1_2MA; -        _ad9522_regs.vco_calibration_now = 1; //calibrate it!          _ad9522_regs.bypass_vco_divider = 0;          switch(cs.vco_divider){          case 1: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV1; break; @@ -209,6 +245,7 @@ public:          );          this->send_all_regs(); +        calibrate_now();      }      void set_clock_settings_with_external_vcxo(double rate){ @@ -245,22 +282,8 @@ public:      void set_fpga_clock_rate(double rate){          if (_out_rate == rate) return; - -        if (rate == 61.44e6){ -            set_clock_settings_with_external_vcxo(rate); -        } -        else{ -            BOOST_FOREACH(const clock_settings_type &cs, get_clock_settings()){ -                //std::cout << cs.to_pp_string() << std::endl; -                if (rate != cs.get_out_rate()) continue; -                std::cout << "USRP-E100 clock control:" << std::endl << cs.to_pp_string() << std::endl; -                set_clock_settings_with_internal_vco(cs); -                return; //done here, exits loop -            } -            throw std::runtime_error(str(boost::format( -                "USRP-E100 clock control: could not find settings for clock rate %fMHz" -            ) % (rate/1e6))); -        } +        if (rate == 61.44e6) set_clock_settings_with_external_vcxo(rate); +        else                 set_clock_settings_with_internal_vco(rate);      }      double get_fpga_clock_rate(void){ @@ -390,6 +413,28 @@ private:          );      } +    void calibrate_now(void){ +        //vco calibration routine: +        _ad9522_regs.vco_calibration_now = 0; +        this->send_reg(0x18); +        this->latch_regs(); +        _ad9522_regs.vco_calibration_now = 1; +        this->send_reg(0x18); +        this->latch_regs(); +        //wait for calibration done: +        static const boost::uint8_t addr = 0x01F; +        for (size_t ms10 = 0; ms10 < 100; ms10++){ +            boost::uint32_t reg = _iface->transact_spi( +                UE_SPI_SS_AD9522, spi_config_t::EDGE_RISE, +                _ad9522_regs.get_read_reg(addr), 24, true /*rb*/ +            ); +            _ad9522_regs.set_reg(addr, reg); +            if (_ad9522_regs.vco_calibration_finished) return; +            boost::this_thread::sleep(boost::posix_time::milliseconds(10)); +        } +        std::cerr << "USRP-E100 clock control: VCO calibration timeout" << std::endl; +    } +      void send_all_regs(void){          //setup a list of register ranges to write          typedef std::pair<boost::uint16_t, boost::uint16_t> range_t; | 
