diff options
Diffstat (limited to 'host/lib/usrp')
22 files changed, 512 insertions, 121 deletions
diff --git a/host/lib/usrp/cores/rx_dsp_core_200.cpp b/host/lib/usrp/cores/rx_dsp_core_200.cpp index cf8db1927..d0afda769 100644 --- a/host/lib/usrp/cores/rx_dsp_core_200.cpp +++ b/host/lib/usrp/cores/rx_dsp_core_200.cpp @@ -77,7 +77,7 @@ public: } void clear(void){ - _iface->poke32(REG_RX_CTRL_NCHANNELS, 1); //also reset + _iface->poke32(REG_RX_CTRL_NCHANNELS, 0); //also reset _iface->poke32(REG_RX_CTRL_VRT_HDR, 0 | (0x1 << 28) //if data with stream id | (0x1 << 26) //has trailer diff --git a/host/lib/usrp/dboard/db_rfx.cpp b/host/lib/usrp/dboard/db_rfx.cpp index 77f61f862..d934be294 100644 --- a/host/lib/usrp/dboard/db_rfx.cpp +++ b/host/lib/usrp/dboard/db_rfx.cpp @@ -372,7 +372,7 @@ double rfx_xcvr::set_lo_freq( * The goal here to to loop though possible R dividers, * band select clock dividers, and prescaler values. * Calculate the A and B counters for each set of values. - * The loop exists when it meets all of the constraints. + * The loop exits when it meets all of the constraints. * The resulting loop values are loaded into the registers. * * fvco = [P*B + A] * fref/R diff --git a/host/lib/usrp/dboard/db_sbx_common.cpp b/host/lib/usrp/dboard/db_sbx_common.cpp index 07857b98a..a51dee361 100644 --- a/host/lib/usrp/dboard/db_sbx_common.cpp +++ b/host/lib/usrp/dboard/db_sbx_common.cpp @@ -219,8 +219,8 @@ void sbx_xcvr::update_atr(void){ int tx_pga0_iobits = tx_pga0_gain_to_iobits(_tx_gains["PGA0"]); int rx_lo_lpf_en = (_rx_lo_freq == sbx_enable_rx_lo_filter.clip(_rx_lo_freq)) ? LO_LPF_EN : 0; int tx_lo_lpf_en = (_tx_lo_freq == sbx_enable_tx_lo_filter.clip(_tx_lo_freq)) ? LO_LPF_EN : 0; - int rx_ld_led = get_locked(dboard_iface::UNIT_RX).to_bool() ? 0 : RX_LED_LD; - int tx_ld_led = get_locked(dboard_iface::UNIT_TX).to_bool() ? 0 : TX_LED_LD; + int rx_ld_led = _rx_lo_lock_cache ? 0 : RX_LED_LD; + int tx_ld_led = _tx_lo_lock_cache ? 0 : TX_LED_LD; int rx_ant_led = _rx_ant == "TX/RX" ? RX_LED_RX1RX2 : 0; int tx_ant_led = _tx_ant == "TX/RX" ? 0 : TX_LED_TXRX; @@ -289,8 +289,14 @@ void sbx_xcvr::set_tx_ant(const std::string &ant){ **********************************************************************/ double sbx_xcvr::set_lo_freq(dboard_iface::unit_t unit, double target_freq) { const double actual = db_actual->set_lo_freq(unit, target_freq); - if (unit == dboard_iface::UNIT_RX) _rx_lo_freq = actual; - if (unit == dboard_iface::UNIT_TX) _tx_lo_freq = actual; + if (unit == dboard_iface::UNIT_RX){ + _rx_lo_lock_cache = false; + _rx_lo_freq = actual; + } + if (unit == dboard_iface::UNIT_TX){ + _tx_lo_lock_cache = false; + _tx_lo_freq = actual; + } update_atr(); return actual; } @@ -298,6 +304,13 @@ double sbx_xcvr::set_lo_freq(dboard_iface::unit_t unit, double target_freq) { sensor_value_t sbx_xcvr::get_locked(dboard_iface::unit_t unit) { const bool locked = (this->get_iface()->read_gpio(unit) & LOCKDET_MASK) != 0; + + if (unit == dboard_iface::UNIT_RX) _rx_lo_lock_cache = locked; + if (unit == dboard_iface::UNIT_TX) _tx_lo_lock_cache = locked; + + //write the new lock cache setting to atr regs + update_atr(); + return sensor_value_t("LO", locked, "locked", "unlocked"); } diff --git a/host/lib/usrp/dboard/db_sbx_common.hpp b/host/lib/usrp/dboard/db_sbx_common.hpp index 501a7f1fc..2a0e83115 100644 --- a/host/lib/usrp/dboard/db_sbx_common.hpp +++ b/host/lib/usrp/dboard/db_sbx_common.hpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -138,6 +138,7 @@ protected: uhd::dict<std::string, double> _tx_gains, _rx_gains; double _rx_lo_freq, _tx_lo_freq; std::string _tx_ant, _rx_ant; + bool _rx_lo_lock_cache, _tx_lo_lock_cache; void set_rx_ant(const std::string &ant); void set_tx_ant(const std::string &ant); diff --git a/host/lib/usrp/dboard/db_sbx_version3.cpp b/host/lib/usrp/dboard/db_sbx_version3.cpp index 6e20d5882..040bef12f 100644 --- a/host/lib/usrp/dboard/db_sbx_version3.cpp +++ b/host/lib/usrp/dboard/db_sbx_version3.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -75,7 +75,6 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED; //increase RF divider until acceptable VCO frequency - //start with target_freq*2 because mixer has divide by 2 double vco_freq = target_freq; while (vco_freq < 2.2e9) { vco_freq *= 2; @@ -83,7 +82,7 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar } //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) - adf4350_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; + adf4350_regs_t::prescaler_t prescaler = target_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; /* * The goal here is to loop though possible R dividers, @@ -91,7 +90,7 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar * (frac) dividers. * * Calculate the N and F dividers for each set of values. - * The loop exists when it meets all of the constraints. + * The loop exits when it meets all of the constraints. * The resulting loop values are loaded into the registers. * * from pg.21 @@ -110,7 +109,7 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar if (pfd_freq > 25e6) continue; //ignore fractional part of tuning - N = int(std::floor(vco_freq/pfd_freq)); + N = int(std::floor(target_freq/pfd_freq)); //keep N > minimum int divider requirement if (N < prescaler_to_min_int_div[prescaler]) continue; @@ -125,7 +124,7 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar //Fractional-N calculation MOD = 4095; //max fractional accuracy - FRAC = int((vco_freq/pfd_freq - N)*MOD); + FRAC = int((target_freq/pfd_freq - N)*MOD); //Reference divide-by-2 for 50% duty cycle // if R even, move one divide by 2 to to regs.reference_divide_by_2 @@ -135,12 +134,12 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar } //actual frequency calculation - actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv); + actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))); UHD_LOGV(often) << boost::format("SBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - << boost::format("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s" - ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl + << boost::format("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" + ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl << boost::format("SBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; @@ -155,6 +154,9 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar regs.frac_12_bit = FRAC; regs.int_16_bit = N; regs.mod_12_bit = MOD; + regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); + regs.feedback_select = adf4350_regs_t::FEEDBACK_SELECT_DIVIDED; + regs.clock_div_mode = adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; regs.prescaler = prescaler; regs.r_counter_10_bit = R; regs.reference_divide_by_2 = T; diff --git a/host/lib/usrp/dboard/db_sbx_version4.cpp b/host/lib/usrp/dboard/db_sbx_version4.cpp index c8128d5f4..f091caab7 100644 --- a/host/lib/usrp/dboard/db_sbx_version4.cpp +++ b/host/lib/usrp/dboard/db_sbx_version4.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -78,7 +78,6 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar if(ref_freq <= 12.5e6) D = adf4351_regs_t::REFERENCE_DOUBLER_ENABLED; //increase RF divider until acceptable VCO frequency - //start with target_freq*2 because mixer has divide by 2 double vco_freq = target_freq; while (vco_freq < 2.2e9) { vco_freq *= 2; @@ -86,7 +85,7 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar } //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) - adf4351_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; + adf4351_regs_t::prescaler_t prescaler = target_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; /* * The goal here is to loop though possible R dividers, @@ -94,7 +93,7 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar * (frac) dividers. * * Calculate the N and F dividers for each set of values. - * The loop exists when it meets all of the constraints. + * The loop exits when it meets all of the constraints. * The resulting loop values are loaded into the registers. * * from pg.21 @@ -128,7 +127,7 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar //Fractional-N calculation MOD = 4095; //max fractional accuracy - FRAC = int((vco_freq/pfd_freq - N)*MOD); + FRAC = int((target_freq/pfd_freq - N)*MOD); //Reference divide-by-2 for 50% duty cycle // if R even, move one divide by 2 to to regs.reference_divide_by_2 @@ -138,12 +137,12 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar } //actual frequency calculation - actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv); + actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))); UHD_LOGV(often) << boost::format("SBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - << boost::format("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s" - ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl + << boost::format("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" + ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl << boost::format("SBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; @@ -158,6 +157,9 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar regs.frac_12_bit = FRAC; regs.int_16_bit = N; regs.mod_12_bit = MOD; + regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); + regs.feedback_select = adf4351_regs_t::FEEDBACK_SELECT_DIVIDED; + regs.clock_div_mode = adf4351_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; regs.prescaler = prescaler; regs.r_counter_10_bit = R; regs.reference_divide_by_2 = T; diff --git a/host/lib/usrp/dboard/db_wbx_version2.cpp b/host/lib/usrp/dboard/db_wbx_version2.cpp index 9037d5d05..0c0a63fda 100644 --- a/host/lib/usrp/dboard/db_wbx_version2.cpp +++ b/host/lib/usrp/dboard/db_wbx_version2.cpp @@ -166,6 +166,9 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar "WBX tune: target frequency %f Mhz" ) % (target_freq/1e6) << std::endl; + //start with target_freq*2 because mixer has divide by 2 + target_freq *= 2; + //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of (0,23) //adf4350_regs_t::PRESCALER_4_5 @@ -193,15 +196,14 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED; //increase RF divider until acceptable VCO frequency - //start with target_freq*2 because mixer has divide by 2 - double vco_freq = target_freq*2; + double vco_freq = target_freq; while (vco_freq < 2.2e9) { vco_freq *= 2; RFdiv *= 2; } //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) - adf4350_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; + adf4350_regs_t::prescaler_t prescaler = target_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; /* * The goal here is to loop though possible R dividers, @@ -209,7 +211,7 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar * (frac) dividers. * * Calculate the N and F dividers for each set of values. - * The loop exists when it meets all of the constraints. + * The loop exits when it meets all of the constraints. * The resulting loop values are loaded into the registers. * * from pg.21 @@ -228,7 +230,7 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar if (pfd_freq > 25e6) continue; //ignore fractional part of tuning - N = int(std::floor(vco_freq/pfd_freq)); + N = int(std::floor(target_freq/pfd_freq)); //keep N > minimum int divider requirement if (N < prescaler_to_min_int_div[prescaler]) continue; @@ -243,7 +245,7 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar //Fractional-N calculation MOD = 4095; //max fractional accuracy - FRAC = int((vco_freq/pfd_freq - N)*MOD); + FRAC = int((target_freq/pfd_freq - N)*MOD); //Reference divide-by-2 for 50% duty cycle // if R even, move one divide by 2 to to regs.reference_divide_by_2 @@ -253,14 +255,13 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar } //actual frequency calculation - actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv/2); - + actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/2); UHD_LOGV(often) << boost::format("WBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s" - ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl + << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" + ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl << boost::format("WBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; @@ -270,6 +271,9 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar regs.frac_12_bit = FRAC; regs.int_16_bit = N; regs.mod_12_bit = MOD; + regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); + regs.feedback_select = adf4350_regs_t::FEEDBACK_SELECT_DIVIDED; + regs.clock_div_mode = adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; regs.prescaler = prescaler; regs.r_counter_10_bit = R; regs.reference_divide_by_2 = T; diff --git a/host/lib/usrp/dboard/db_wbx_version3.cpp b/host/lib/usrp/dboard/db_wbx_version3.cpp index f0fdc2ffe..1f67b37c9 100644 --- a/host/lib/usrp/dboard/db_wbx_version3.cpp +++ b/host/lib/usrp/dboard/db_wbx_version3.cpp @@ -198,6 +198,9 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar "WBX tune: target frequency %f Mhz" ) % (target_freq/1e6) << std::endl; + //start with target_freq*2 because mixer has divide by 2 + target_freq *= 2; + //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of (0,23) //adf4350_regs_t::PRESCALER_4_5 @@ -225,15 +228,14 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED; //increase RF divider until acceptable VCO frequency - //start with target_freq*2 because mixer has divide by 2 - double vco_freq = target_freq*2; + double vco_freq = target_freq; while (vco_freq < 2.2e9) { vco_freq *= 2; RFdiv *= 2; } //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) - adf4350_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; + adf4350_regs_t::prescaler_t prescaler = target_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; /* * The goal here is to loop though possible R dividers, @@ -241,7 +243,7 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar * (frac) dividers. * * Calculate the N and F dividers for each set of values. - * The loop exists when it meets all of the constraints. + * The loop exits when it meets all of the constraints. * The resulting loop values are loaded into the registers. * * from pg.21 @@ -260,7 +262,7 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar if (pfd_freq > 25e6) continue; //ignore fractional part of tuning - N = int(std::floor(vco_freq/pfd_freq)); + N = int(std::floor(target_freq/pfd_freq)); //keep N > minimum int divider requirement if (N < prescaler_to_min_int_div[prescaler]) continue; @@ -275,7 +277,7 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar //Fractional-N calculation MOD = 4095; //max fractional accuracy - FRAC = int((vco_freq/pfd_freq - N)*MOD); + FRAC = int((target_freq/pfd_freq - N)*MOD); //Reference divide-by-2 for 50% duty cycle // if R even, move one divide by 2 to to regs.reference_divide_by_2 @@ -285,14 +287,13 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar } //actual frequency calculation - actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv/2); - + actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/2); UHD_LOGV(often) << boost::format("WBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s" - ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl + << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" + ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl << boost::format("WBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; @@ -302,6 +303,9 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar regs.frac_12_bit = FRAC; regs.int_16_bit = N; regs.mod_12_bit = MOD; + regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); + regs.feedback_select = adf4350_regs_t::FEEDBACK_SELECT_DIVIDED; + regs.clock_div_mode = adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; regs.prescaler = prescaler; regs.r_counter_10_bit = R; regs.reference_divide_by_2 = T; diff --git a/host/lib/usrp/dboard/db_wbx_version4.cpp b/host/lib/usrp/dboard/db_wbx_version4.cpp index 418b85997..dd6c3c9ef 100644 --- a/host/lib/usrp/dboard/db_wbx_version4.cpp +++ b/host/lib/usrp/dboard/db_wbx_version4.cpp @@ -179,6 +179,9 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar "WBX tune: target frequency %f Mhz" ) % (target_freq/1e6) << std::endl; + //start with target_freq*2 because mixer has divide by 2 + target_freq *= 2; + //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of (0,23) //adf4351_regs_t::PRESCALER_4_5 @@ -208,15 +211,14 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar if(ref_freq <= 12.5e6) D = adf4351_regs_t::REFERENCE_DOUBLER_ENABLED; //increase RF divider until acceptable VCO frequency - //start with target_freq*2 because mixer has divide by 2 - double vco_freq = target_freq*2; + double vco_freq = target_freq; while (vco_freq < 2.2e9) { vco_freq *= 2; RFdiv *= 2; } //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) - adf4351_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; + adf4351_regs_t::prescaler_t prescaler = target_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; /* * The goal here is to loop though possible R dividers, @@ -243,7 +245,7 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar if (pfd_freq > 25e6) continue; //ignore fractional part of tuning - N = int(std::floor(vco_freq/pfd_freq)); + N = int(std::floor(target_freq/pfd_freq)); //keep N > minimum int divider requirement if (N < prescaler_to_min_int_div[prescaler]) continue; @@ -258,7 +260,7 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar //Fractional-N calculation MOD = 4095; //max fractional accuracy - FRAC = int((vco_freq/pfd_freq - N)*MOD); + FRAC = int((target_freq/pfd_freq - N)*MOD); //Reference divide-by-2 for 50% duty cycle // if R even, move one divide by 2 to to regs.reference_divide_by_2 @@ -268,14 +270,13 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar } //actual frequency calculation - actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv/2); - + actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/2); UHD_LOGV(often) << boost::format("WBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s" - ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl + << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" + ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl << boost::format("WBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; @@ -285,6 +286,9 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar regs.frac_12_bit = FRAC; regs.int_16_bit = N; regs.mod_12_bit = MOD; + regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); + regs.feedback_select = adf4351_regs_t::FEEDBACK_SELECT_DIVIDED; + regs.clock_div_mode = adf4351_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; regs.prescaler = prescaler; regs.r_counter_10_bit = R; regs.reference_divide_by_2 = T; diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp index 70a8481db..fe3c923d3 100644 --- a/host/lib/usrp/multi_usrp.cpp +++ b/host/lib/usrp/multi_usrp.cpp @@ -398,12 +398,27 @@ public: return true; } - void set_command_time(const time_spec_t &, size_t){ - throw uhd::not_implemented_error("Not implemented yet, but we have a very good idea of how to do it."); + void set_command_time(const time_spec_t &time_spec, size_t mboard){ + if (mboard != ALL_MBOARDS){ + if (not _tree->exists(mb_root(mboard) / "time/cmd")){ + throw uhd::not_implemented_error("timed command feature not implemented on this hardware"); + } + _tree->access<time_spec_t>(mb_root(mboard) / "time/cmd").set(time_spec); + return; + } + for (size_t m = 0; m < get_num_mboards(); m++){ + set_command_time(time_spec, m); + } } - void clear_command_time(size_t){ - throw uhd::not_implemented_error("Not implemented yet, but we have a very good idea of how to do it."); + void clear_command_time(size_t mboard){ + if (mboard != ALL_MBOARDS){ + _tree->access<time_spec_t>(mb_root(mboard) / "time/cmd").set(time_spec_t(0.0)); + return; + } + for (size_t m = 0; m < get_num_mboards(); m++){ + clear_command_time(m); + } } void issue_stream_cmd(const stream_cmd_t &stream_cmd, size_t chan){ diff --git a/host/lib/usrp/usrp2/CMakeLists.txt b/host/lib/usrp/usrp2/CMakeLists.txt index 10f7407b0..da39d9df1 100644 --- a/host/lib/usrp/usrp2/CMakeLists.txt +++ b/host/lib/usrp/usrp2/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2011 Ettus Research LLC +# Copyright 2011-2012 Ettus Research LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -32,5 +32,6 @@ IF(ENABLE_USRP2) ${CMAKE_CURRENT_SOURCE_DIR}/io_impl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_iface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_impl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_fifo_ctrl.cpp ) ENDIF(ENABLE_USRP2) diff --git a/host/lib/usrp/usrp2/clock_ctrl.cpp b/host/lib/usrp/usrp2/clock_ctrl.cpp index 7d3ffefa2..769795aad 100644 --- a/host/lib/usrp/usrp2/clock_ctrl.cpp +++ b/host/lib/usrp/usrp2/clock_ctrl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -35,8 +35,9 @@ static const bool enb_test_clk = false; */ class usrp2_clock_ctrl_impl : public usrp2_clock_ctrl{ public: - usrp2_clock_ctrl_impl(usrp2_iface::sptr iface){ + usrp2_clock_ctrl_impl(usrp2_iface::sptr iface, uhd::spi_iface::sptr spiface){ _iface = iface; + _spiface = spiface; clk_regs = usrp2_clk_regs_t(_iface->get_rev()); _ad9510_regs.cp_current_setting = ad9510_regs_t::CP_CURRENT_SETTING_3_0MA; @@ -331,7 +332,7 @@ private: */ void write_reg(boost::uint8_t addr){ boost::uint32_t data = _ad9510_regs.get_write_reg(addr); - _iface->write_spi(SPI_SS_AD9510, spi_config_t::EDGE_RISE, data, 24); + _spiface->write_spi(SPI_SS_AD9510, spi_config_t::EDGE_RISE, data, 24); } /*! @@ -377,7 +378,7 @@ private: } usrp2_iface::sptr _iface; - + uhd::spi_iface::sptr _spiface; usrp2_clk_regs_t clk_regs; ad9510_regs_t _ad9510_regs; }; @@ -385,6 +386,6 @@ private: /*********************************************************************** * Public make function for the ad9510 clock control **********************************************************************/ -usrp2_clock_ctrl::sptr usrp2_clock_ctrl::make(usrp2_iface::sptr iface){ - return sptr(new usrp2_clock_ctrl_impl(iface)); +usrp2_clock_ctrl::sptr usrp2_clock_ctrl::make(usrp2_iface::sptr iface, uhd::spi_iface::sptr spiface){ + return sptr(new usrp2_clock_ctrl_impl(iface, spiface)); } diff --git a/host/lib/usrp/usrp2/clock_ctrl.hpp b/host/lib/usrp/usrp2/clock_ctrl.hpp index 9ccbc959e..067e1e35d 100644 --- a/host/lib/usrp/usrp2/clock_ctrl.hpp +++ b/host/lib/usrp/usrp2/clock_ctrl.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010 Ettus Research LLC +// Copyright 2010-2012 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -29,10 +29,11 @@ public: /*! * Make a clock config for the ad9510 ic. - * \param _iface a pointer to the usrp2 interface object + * \param iface a pointer to the usrp2 interface object + * \param spiface the interface to spi * \return a new clock control object */ - static sptr make(usrp2_iface::sptr iface); + static sptr make(usrp2_iface::sptr iface, uhd::spi_iface::sptr spiface); /*! * Get the master clock frequency for the fpga. diff --git a/host/lib/usrp/usrp2/codec_ctrl.cpp b/host/lib/usrp/usrp2/codec_ctrl.cpp index 06bf83b15..b53c4d9df 100644 --- a/host/lib/usrp/usrp2/codec_ctrl.cpp +++ b/host/lib/usrp/usrp2/codec_ctrl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -32,8 +32,9 @@ using namespace uhd; */ class usrp2_codec_ctrl_impl : public usrp2_codec_ctrl{ public: - usrp2_codec_ctrl_impl(usrp2_iface::sptr iface){ + usrp2_codec_ctrl_impl(usrp2_iface::sptr iface, uhd::spi_iface::sptr spiface){ _iface = iface; + _spiface = spiface; //setup the ad9777 dac _ad9777_regs.x_1r_2r_mode = ad9777_regs_t::X_1R_2R_MODE_1R; @@ -189,11 +190,12 @@ private: ad9777_regs_t _ad9777_regs; ads62p44_regs_t _ads62p44_regs; usrp2_iface::sptr _iface; + uhd::spi_iface::sptr _spiface; void send_ad9777_reg(boost::uint8_t addr){ boost::uint16_t reg = _ad9777_regs.get_write_reg(addr); UHD_LOGV(always) << "send_ad9777_reg: " << std::hex << reg << std::endl; - _iface->write_spi( + _spiface->write_spi( SPI_SS_AD9777, spi_config_t::EDGE_RISE, reg, 16 ); @@ -201,7 +203,7 @@ private: void send_ads62p44_reg(boost::uint8_t addr) { boost::uint16_t reg = _ads62p44_regs.get_write_reg(addr); - _iface->write_spi( + _spiface->write_spi( SPI_SS_ADS62P44, spi_config_t::EDGE_FALL, reg, 16 ); @@ -211,6 +213,6 @@ private: /*********************************************************************** * Public make function for the usrp2 codec control **********************************************************************/ -usrp2_codec_ctrl::sptr usrp2_codec_ctrl::make(usrp2_iface::sptr iface){ - return sptr(new usrp2_codec_ctrl_impl(iface)); +usrp2_codec_ctrl::sptr usrp2_codec_ctrl::make(usrp2_iface::sptr iface, uhd::spi_iface::sptr spiface){ + return sptr(new usrp2_codec_ctrl_impl(iface, spiface)); } diff --git a/host/lib/usrp/usrp2/codec_ctrl.hpp b/host/lib/usrp/usrp2/codec_ctrl.hpp index ca300e2b1..b0d815be2 100644 --- a/host/lib/usrp/usrp2/codec_ctrl.hpp +++ b/host/lib/usrp/usrp2/codec_ctrl.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -28,10 +28,11 @@ public: /*! * Make a codec control for the DAC and ADC. - * \param _iface a pointer to the usrp2 interface object + * \param iface a pointer to the usrp2 interface object + * \param spiface the interface to spi * \return a new codec control object */ - static sptr make(usrp2_iface::sptr iface); + static sptr make(usrp2_iface::sptr iface, uhd::spi_iface::sptr spiface); /*! * Set the modulation mode for the DAC. diff --git a/host/lib/usrp/usrp2/dboard_iface.cpp b/host/lib/usrp/usrp2/dboard_iface.cpp index bc510c8a1..edd9ef242 100644 --- a/host/lib/usrp/usrp2/dboard_iface.cpp +++ b/host/lib/usrp/usrp2/dboard_iface.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,7 +16,7 @@ // #include "gpio_core_200.hpp" -#include "usrp2_iface.hpp" +#include <uhd/types/serial.hpp> #include "clock_ctrl.hpp" #include "usrp2_regs.hpp" //wishbone address constants #include <uhd/usrp/dboard_iface.hpp> @@ -35,7 +35,12 @@ using namespace boost::assign; class usrp2_dboard_iface : public dboard_iface{ public: - usrp2_dboard_iface(usrp2_iface::sptr iface, usrp2_clock_ctrl::sptr clock_ctrl); + usrp2_dboard_iface( + wb_iface::sptr wb_iface, + uhd::i2c_iface::sptr i2c_iface, + uhd::spi_iface::sptr spi_iface, + usrp2_clock_ctrl::sptr clock_ctrl + ); ~usrp2_dboard_iface(void); special_props_t get_special_props(void){ @@ -79,7 +84,8 @@ public: ); private: - usrp2_iface::sptr _iface; + uhd::i2c_iface::sptr _i2c_iface; + uhd::spi_iface::sptr _spi_iface; usrp2_clock_ctrl::sptr _clock_ctrl; gpio_core_200::sptr _gpio; @@ -92,22 +98,28 @@ private: * Make Function **********************************************************************/ dboard_iface::sptr make_usrp2_dboard_iface( - usrp2_iface::sptr iface, + wb_iface::sptr wb_iface, + uhd::i2c_iface::sptr i2c_iface, + uhd::spi_iface::sptr spi_iface, usrp2_clock_ctrl::sptr clock_ctrl ){ - return dboard_iface::sptr(new usrp2_dboard_iface(iface, clock_ctrl)); + return dboard_iface::sptr(new usrp2_dboard_iface(wb_iface, i2c_iface, spi_iface, clock_ctrl)); } /*********************************************************************** * Structors **********************************************************************/ usrp2_dboard_iface::usrp2_dboard_iface( - usrp2_iface::sptr iface, + wb_iface::sptr wb_iface, + uhd::i2c_iface::sptr i2c_iface, + uhd::spi_iface::sptr spi_iface, usrp2_clock_ctrl::sptr clock_ctrl -){ - _iface = iface; - _clock_ctrl = clock_ctrl; - _gpio = gpio_core_200::make(_iface, U2_REG_SR_ADDR(SR_GPIO), U2_REG_GPIO_RB); +): + _i2c_iface(i2c_iface), + _spi_iface(spi_iface), + _clock_ctrl(clock_ctrl) +{ + _gpio = gpio_core_200::make(wb_iface, U2_REG_SR_ADDR(SR_GPIO), U2_REG_GPIO_RB); //reset the aux dacs _dac_regs[UNIT_RX] = ad5623_regs_t(); @@ -202,7 +214,7 @@ void usrp2_dboard_iface::write_spi( boost::uint32_t data, size_t num_bits ){ - _iface->write_spi(unit_to_spi_dev[unit], config, data, num_bits); + _spi_iface->write_spi(unit_to_spi_dev[unit], config, data, num_bits); } boost::uint32_t usrp2_dboard_iface::read_write_spi( @@ -211,18 +223,18 @@ boost::uint32_t usrp2_dboard_iface::read_write_spi( boost::uint32_t data, size_t num_bits ){ - return _iface->read_spi(unit_to_spi_dev[unit], config, data, num_bits); + return _spi_iface->read_spi(unit_to_spi_dev[unit], config, data, num_bits); } /*********************************************************************** * I2C **********************************************************************/ void usrp2_dboard_iface::write_i2c(boost::uint8_t addr, const byte_vector_t &bytes){ - return _iface->write_i2c(addr, bytes); + return _i2c_iface->write_i2c(addr, bytes); } byte_vector_t usrp2_dboard_iface::read_i2c(boost::uint8_t addr, size_t num_bytes){ - return _iface->read_i2c(addr, num_bytes); + return _i2c_iface->read_i2c(addr, num_bytes); } /*********************************************************************** @@ -233,7 +245,7 @@ void usrp2_dboard_iface::_write_aux_dac(unit_t unit){ (UNIT_RX, SPI_SS_RX_DAC) (UNIT_TX, SPI_SS_TX_DAC) ; - _iface->write_spi( + _spi_iface->write_spi( unit_to_spi_dac[unit], spi_config_t::EDGE_FALL, _dac_regs[unit].get_reg(), 24 ); @@ -281,11 +293,11 @@ double usrp2_dboard_iface::read_aux_adc(unit_t unit, aux_adc_t which){ } ad7922_regs.chn = ad7922_regs.mod; //normal mode: mod == chn //write and read spi - _iface->write_spi( + _spi_iface->write_spi( unit_to_spi_adc[unit], config, ad7922_regs.get_reg(), 16 ); - ad7922_regs.set_reg(boost::uint16_t(_iface->read_spi( + ad7922_regs.set_reg(boost::uint16_t(_spi_iface->read_spi( unit_to_spi_adc[unit], config, ad7922_regs.get_reg(), 16 ))); diff --git a/host/lib/usrp/usrp2/fw_common.h b/host/lib/usrp/usrp2/fw_common.h index 2add74c47..acd5d1f3a 100644 --- a/host/lib/usrp/usrp2/fw_common.h +++ b/host/lib/usrp/usrp2/fw_common.h @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -30,8 +30,8 @@ extern "C" { #endif //fpga and firmware compatibility numbers -#define USRP2_FPGA_COMPAT_NUM 9 -#define USRP2_FW_COMPAT_NUM 11 +#define USRP2_FPGA_COMPAT_NUM 10 +#define USRP2_FW_COMPAT_NUM 12 #define USRP2_FW_VER_MINOR 2 //used to differentiate control packets over data port @@ -51,6 +51,7 @@ typedef struct{ #define USRP2_UDP_RX_DSP0_PORT 49156 #define USRP2_UDP_TX_DSP0_PORT 49157 #define USRP2_UDP_RX_DSP1_PORT 49158 +#define USRP2_UDP_FIFO_CRTL_PORT 49159 #define USRP2_UDP_UART_BASE_PORT 49170 #define USRP2_UDP_UART_GPS_PORT 49172 diff --git a/host/lib/usrp/usrp2/usrp2_fifo_ctrl.cpp b/host/lib/usrp/usrp2/usrp2_fifo_ctrl.cpp new file mode 100644 index 000000000..3b8d215f5 --- /dev/null +++ b/host/lib/usrp/usrp2/usrp2_fifo_ctrl.cpp @@ -0,0 +1,244 @@ +// +// Copyright 2012 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "usrp2_regs.hpp" +#include <uhd/exception.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/safe_call.hpp> +#include <uhd/transport/vrt_if_packet.hpp> +#include "usrp2_fifo_ctrl.hpp" +#include <boost/thread/mutex.hpp> +#include <boost/thread/thread.hpp> +#include <boost/asio.hpp> //htonl +#include <boost/format.hpp> + +using namespace uhd; +using namespace uhd::transport; + +static const size_t POKE32_CMD = (1 << 8); +static const size_t PEEK32_CMD = 0; +static const double ACK_TIMEOUT = 0.5; +static const double MASSIVE_TIMEOUT = 10.0; //for when we wait on a timed command +static const boost::uint32_t MAX_SEQS_OUT = 16; + +#define SPI_DIV SR_SPI_CORE + 0 +#define SPI_CTRL SR_SPI_CORE + 1 +#define SPI_DATA SR_SPI_CORE + 2 +#define SPI_READBACK 0 +// spi clock rate = master_clock/(div+1)/2 (10MHz in this case) +#define SPI_DIVIDER 4 + +class usrp2_fifo_ctrl_impl : public usrp2_fifo_ctrl{ +public: + + usrp2_fifo_ctrl_impl(zero_copy_if::sptr xport): + _xport(xport), + _seq_out(0), + _seq_ack(0), + _timeout(ACK_TIMEOUT) + { + while (_xport->get_recv_buff(0.0)){} //flush + this->set_time(uhd::time_spec_t(0.0)); + this->set_tick_rate(1.0); //something possible but bogus + this->init_spi(); + } + + ~usrp2_fifo_ctrl_impl(void){ + _timeout = ACK_TIMEOUT; //reset timeout to something small + UHD_SAFE_CALL( + this->peek32(0); //dummy peek with the purpose of ack'ing all packets + ) + } + + /******************************************************************* + * Peek and poke 32 bit implementation + ******************************************************************/ + void poke32(wb_addr_type addr, boost::uint32_t data){ + boost::mutex::scoped_lock lock(_mutex); + + this->send_pkt((addr - SETTING_REGS_BASE)/4, data, POKE32_CMD); + + this->wait_for_ack(_seq_out-MAX_SEQS_OUT); + } + + boost::uint32_t peek32(wb_addr_type addr){ + boost::mutex::scoped_lock lock(_mutex); + + this->send_pkt((addr - READBACK_BASE)/4, 0, PEEK32_CMD); + + return this->wait_for_ack(_seq_out); + } + + /******************************************************************* + * Peek and poke 16 bit not implemented + ******************************************************************/ + void poke16(wb_addr_type, boost::uint16_t){ + throw uhd::not_implemented_error("poke16 not implemented in fifo ctrl module"); + } + + boost::uint16_t peek16(wb_addr_type){ + throw uhd::not_implemented_error("peek16 not implemented in fifo ctrl module"); + } + + /******************************************************************* + * FIFO controlled SPI implementation + ******************************************************************/ + void init_spi(void){ + boost::mutex::scoped_lock lock(_mutex); + + this->send_pkt(SPI_DIV, SPI_DIVIDER, POKE32_CMD); + this->wait_for_ack(_seq_out-MAX_SEQS_OUT); + + _ctrl_word_cache = 0; // force update first time around + } + + boost::uint32_t transact_spi( + int which_slave, + const spi_config_t &config, + boost::uint32_t data, + size_t num_bits, + bool readback + ){ + boost::mutex::scoped_lock lock(_mutex); + + //load control word + boost::uint32_t ctrl_word = 0; + ctrl_word |= ((which_slave & 0xffffff) << 0); + ctrl_word |= ((num_bits & 0x3ff) << 24); + if (config.mosi_edge == spi_config_t::EDGE_FALL) ctrl_word |= (1 << 31); + if (config.miso_edge == spi_config_t::EDGE_RISE) ctrl_word |= (1 << 30); + + //load data word (must be in upper bits) + const boost::uint32_t data_out = data << (32 - num_bits); + + //conditionally send control word + if (_ctrl_word_cache != ctrl_word){ + this->send_pkt(SPI_CTRL, ctrl_word, POKE32_CMD); + this->wait_for_ack(_seq_out-MAX_SEQS_OUT); + _ctrl_word_cache = ctrl_word; + } + + //send data word + this->send_pkt(SPI_DATA, data_out, POKE32_CMD); + this->wait_for_ack(_seq_out-MAX_SEQS_OUT); + + //conditional readback + if (readback){ + this->send_pkt(SPI_READBACK, 0, PEEK32_CMD); + return this->wait_for_ack(_seq_out); + } + + return 0; + } + + /******************************************************************* + * Update methods for time + ******************************************************************/ + void set_time(const uhd::time_spec_t &time){ + boost::mutex::scoped_lock lock(_mutex); + _time = time; + _use_time = _time != uhd::time_spec_t(0.0); + if (_use_time) _timeout = MASSIVE_TIMEOUT; //permanently sets larger timeout + } + + void set_tick_rate(const double rate){ + boost::mutex::scoped_lock lock(_mutex); + _tick_rate = rate; + } + +private: + + /******************************************************************* + * Primary control and interaction private methods + ******************************************************************/ + UHD_INLINE void send_pkt(wb_addr_type addr, boost::uint32_t data, int cmd){ + managed_send_buffer::sptr buff = _xport->get_send_buff(0.0); + if (not buff){ + throw uhd::runtime_error("fifo ctrl timed out getting a send buffer"); + } + boost::uint32_t *trans = buff->cast<boost::uint32_t *>(); + trans[0] = htonl(++_seq_out); + boost::uint32_t *pkt = trans + 1; + + //load packet info + vrt::if_packet_info_t packet_info; + packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_CONTEXT; + packet_info.num_payload_words32 = 2; + packet_info.num_payload_bytes = packet_info.num_payload_words32*sizeof(boost::uint32_t); + packet_info.packet_count = _seq_out; + packet_info.tsf = _time.to_ticks(_tick_rate); + packet_info.sob = false; + packet_info.eob = false; + packet_info.has_sid = false; + packet_info.has_cid = false; + packet_info.has_tsi = false; + packet_info.has_tsf = _use_time; + packet_info.has_tlr = false; + + //load header + vrt::if_hdr_pack_be(pkt, packet_info); + + //load payload + const boost::uint32_t ctrl_word = (addr & 0xff) | cmd | (_seq_out << 16); + pkt[packet_info.num_header_words32+0] = htonl(ctrl_word); + pkt[packet_info.num_header_words32+1] = htonl(data); + + //send the buffer over the interface + buff->commit(sizeof(boost::uint32_t)*(packet_info.num_packet_words32+1)); + } + + UHD_INLINE bool wraparound_lt16(const boost::int16_t i0, const boost::int16_t i1){ + if (((i0 ^ i1) & 0x8000) == 0) //same sign bits + return boost::uint16_t(i0) < boost::uint16_t(i1); + return boost::int16_t(i1 - i0) > 0; + } + + UHD_INLINE boost::uint32_t wait_for_ack(const boost::uint16_t seq_to_ack){ + + while (wraparound_lt16(_seq_ack, seq_to_ack)){ + managed_recv_buffer::sptr buff = _xport->get_recv_buff(_timeout); + if (not buff){ + throw uhd::runtime_error("fifo ctrl timed out looking for acks"); + } + const boost::uint32_t *pkt = buff->cast<const boost::uint32_t *>(); + vrt::if_packet_info_t packet_info; + packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t); + vrt::if_hdr_unpack_be(pkt, packet_info); + _seq_ack = ntohl(pkt[packet_info.num_header_words32+0]) >> 16; + if (_seq_ack == seq_to_ack){ + return ntohl(pkt[packet_info.num_header_words32+1]); + } + } + + return 0; + } + + zero_copy_if::sptr _xport; + boost::mutex _mutex; + boost::uint16_t _seq_out; + boost::uint16_t _seq_ack; + uhd::time_spec_t _time; + bool _use_time; + double _tick_rate; + double _timeout; + boost::uint32_t _ctrl_word_cache; +}; + + +usrp2_fifo_ctrl::sptr usrp2_fifo_ctrl::make(zero_copy_if::sptr xport){ + return sptr(new usrp2_fifo_ctrl_impl(xport)); +} diff --git a/host/lib/usrp/usrp2/usrp2_fifo_ctrl.hpp b/host/lib/usrp/usrp2/usrp2_fifo_ctrl.hpp new file mode 100644 index 000000000..b48d05aa2 --- /dev/null +++ b/host/lib/usrp/usrp2/usrp2_fifo_ctrl.hpp @@ -0,0 +1,47 @@ +// +// Copyright 2012 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_USRP2_FIFO_CTRL_HPP +#define INCLUDED_USRP2_FIFO_CTRL_HPP + +#include <uhd/types/time_spec.hpp> +#include <uhd/types/serial.hpp> +#include <uhd/transport/zero_copy.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include "wb_iface.hpp" +#include <string> + +/*! + * The usrp2 FIFO control class: + * Provide high-speed peek/poke interface. + */ +class usrp2_fifo_ctrl : public wb_iface, public uhd::spi_iface{ +public: + typedef boost::shared_ptr<usrp2_fifo_ctrl> sptr; + + //! Make a new FIFO control object + static sptr make(uhd::transport::zero_copy_if::sptr xport); + + //! Set the command time that will activate + virtual void set_time(const uhd::time_spec_t &time) = 0; + + //! Set the tick rate (converting time into ticks) + virtual void set_tick_rate(const double rate) = 0; +}; + +#endif /* INCLUDED_USRP2_FIFO_CTRL_HPP */ diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index e6e8ca675..0876e36c4 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -391,8 +391,28 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ _mbc[mb].tx_dsp_xport = make_xport( addr, BOOST_STRINGIZE(USRP2_UDP_TX_DSP0_PORT), device_args_i, "send" ); + UHD_LOG << "Making transport for Control..." << std::endl; + _mbc[mb].fifo_ctrl_xport = make_xport( + addr, BOOST_STRINGIZE(USRP2_UDP_FIFO_CRTL_PORT), device_addr_t(), "" + ); //set the filter on the router to take dsp data from this port - _mbc[mb].iface->poke32(U2_REG_ROUTER_CTRL_PORTS, USRP2_UDP_TX_DSP0_PORT); + _mbc[mb].iface->poke32(U2_REG_ROUTER_CTRL_PORTS, (USRP2_UDP_FIFO_CRTL_PORT << 16) | USRP2_UDP_TX_DSP0_PORT); + + //create the fifo control interface for high speed register access + _mbc[mb].fifo_ctrl = usrp2_fifo_ctrl::make(_mbc[mb].fifo_ctrl_xport); + switch(_mbc[mb].iface->get_rev()){ + case usrp2_iface::USRP_N200: + case usrp2_iface::USRP_N210: + case usrp2_iface::USRP_N200_R4: + case usrp2_iface::USRP_N210_R4: + _mbc[mb].wbiface = _mbc[mb].fifo_ctrl; + _mbc[mb].spiface = _mbc[mb].fifo_ctrl; + break; + default: + _mbc[mb].wbiface = _mbc[mb].iface; + _mbc[mb].spiface = _mbc[mb].iface; + break; + } //////////////////////////////////////////////////////////////// // setup the mboard eeprom @@ -404,7 +424,7 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ //////////////////////////////////////////////////////////////// // create clock control objects //////////////////////////////////////////////////////////////// - _mbc[mb].clock = usrp2_clock_ctrl::make(_mbc[mb].iface); + _mbc[mb].clock = usrp2_clock_ctrl::make(_mbc[mb].iface, _mbc[mb].spiface); _tree->create<double>(mb_path / "tick_rate") .publish(boost::bind(&usrp2_clock_ctrl::get_master_clock_rate, _mbc[mb].clock)) .subscribe(boost::bind(&usrp2_impl::update_tick_rate, this, _1)); @@ -416,7 +436,7 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ const fs_path tx_codec_path = mb_path / "tx_codecs/A"; _tree->create<int>(rx_codec_path / "gains"); //phony property so this dir exists _tree->create<int>(tx_codec_path / "gains"); //phony property so this dir exists - _mbc[mb].codec = usrp2_codec_ctrl::make(_mbc[mb].iface); + _mbc[mb].codec = usrp2_codec_ctrl::make(_mbc[mb].iface, _mbc[mb].spiface); switch(_mbc[mb].iface->get_rev()){ case usrp2_iface::USRP_N200: case usrp2_iface::USRP_N210: @@ -469,10 +489,10 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ // create frontend control objects //////////////////////////////////////////////////////////////// _mbc[mb].rx_fe = rx_frontend_core_200::make( - _mbc[mb].iface, U2_REG_SR_ADDR(SR_RX_FRONT) + _mbc[mb].wbiface, U2_REG_SR_ADDR(SR_RX_FRONT) ); _mbc[mb].tx_fe = tx_frontend_core_200::make( - _mbc[mb].iface, U2_REG_SR_ADDR(SR_TX_FRONT) + _mbc[mb].wbiface, U2_REG_SR_ADDR(SR_TX_FRONT) ); _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec") @@ -503,10 +523,10 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ // create rx dsp control objects //////////////////////////////////////////////////////////////// _mbc[mb].rx_dsps.push_back(rx_dsp_core_200::make( - _mbc[mb].iface, U2_REG_SR_ADDR(SR_RX_DSP0), U2_REG_SR_ADDR(SR_RX_CTRL0), USRP2_RX_SID_BASE + 0, true + _mbc[mb].wbiface, U2_REG_SR_ADDR(SR_RX_DSP0), U2_REG_SR_ADDR(SR_RX_CTRL0), USRP2_RX_SID_BASE + 0, true )); _mbc[mb].rx_dsps.push_back(rx_dsp_core_200::make( - _mbc[mb].iface, U2_REG_SR_ADDR(SR_RX_DSP1), U2_REG_SR_ADDR(SR_RX_CTRL1), USRP2_RX_SID_BASE + 1, true + _mbc[mb].wbiface, U2_REG_SR_ADDR(SR_RX_DSP1), U2_REG_SR_ADDR(SR_RX_CTRL1), USRP2_RX_SID_BASE + 1, true )); for (size_t dspno = 0; dspno < _mbc[mb].rx_dsps.size(); dspno++){ _mbc[mb].rx_dsps[dspno]->set_link_rate(USRP2_LINK_RATE_BPS); @@ -531,7 +551,7 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ // create tx dsp control objects //////////////////////////////////////////////////////////////// _mbc[mb].tx_dsp = tx_dsp_core_200::make( - _mbc[mb].iface, U2_REG_SR_ADDR(SR_TX_DSP), U2_REG_SR_ADDR(SR_TX_CTRL), USRP2_TX_ASYNC_SID + _mbc[mb].wbiface, U2_REG_SR_ADDR(SR_TX_DSP), U2_REG_SR_ADDR(SR_TX_CTRL), USRP2_TX_ASYNC_SID ); _mbc[mb].tx_dsp->set_link_rate(USRP2_LINK_RATE_BPS); _tree->access<double>(mb_path / "tick_rate") @@ -565,7 +585,7 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ time64_rb_bases.rb_hi_pps = U2_REG_TIME64_HI_RB_PPS; time64_rb_bases.rb_lo_pps = U2_REG_TIME64_LO_RB_PPS; _mbc[mb].time64 = time64_core_200::make( - _mbc[mb].iface, U2_REG_SR_ADDR(SR_TIME64), time64_rb_bases, mimo_clock_sync_delay_cycles + _mbc[mb].wbiface, U2_REG_SR_ADDR(SR_TIME64), time64_rb_bases, mimo_clock_sync_delay_cycles ); _tree->access<double>(mb_path / "tick_rate") .subscribe(boost::bind(&time64_core_200::set_tick_rate, _mbc[mb].time64, _1)); @@ -585,11 +605,23 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ .subscribe(boost::bind(&usrp2_impl::update_clock_source, this, mb, _1)); static const std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("mimo"); _tree->create<std::vector<std::string> >(mb_path / "clock_source/options").set(clock_sources); + //plug timed commands into tree here + switch(_mbc[mb].iface->get_rev()){ + case usrp2_iface::USRP_N200: + case usrp2_iface::USRP_N210: + case usrp2_iface::USRP_N200_R4: + case usrp2_iface::USRP_N210_R4: + _tree->create<time_spec_t>(mb_path / "time/cmd") + .subscribe(boost::bind(&usrp2_fifo_ctrl::set_time, _mbc[mb].fifo_ctrl, _1)); + default: break; //otherwise, do not register + } + _tree->access<double>(mb_path / "tick_rate") + .subscribe(boost::bind(&usrp2_fifo_ctrl::set_tick_rate, _mbc[mb].fifo_ctrl, _1)); //////////////////////////////////////////////////////////////////// // create user-defined control objects //////////////////////////////////////////////////////////////////// - _mbc[mb].user = user_settings_core_200::make(_mbc[mb].iface, U2_REG_SR_ADDR(SR_USER_REGS)); + _mbc[mb].user = user_settings_core_200::make(_mbc[mb].wbiface, U2_REG_SR_ADDR(SR_USER_REGS)); _tree->create<user_settings_core_200::user_reg_t>(mb_path / "user/regs") .subscribe(boost::bind(&user_settings_core_200::set_reg, _mbc[mb].user, _1)); @@ -615,7 +647,7 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ .subscribe(boost::bind(&usrp2_impl::set_db_eeprom, this, mb, "gdb", _1)); //create a new dboard interface and manager - _mbc[mb].dboard_iface = make_usrp2_dboard_iface(_mbc[mb].iface, _mbc[mb].clock); + _mbc[mb].dboard_iface = make_usrp2_dboard_iface(_mbc[mb].wbiface, _mbc[mb].iface/*i2c*/, _mbc[mb].spiface, _mbc[mb].clock); _tree->create<dboard_iface::sptr>(mb_path / "dboards/A/iface").set(_mbc[mb].dboard_iface); _mbc[mb].dboard_manager = dboard_manager::make( rx_db_eeprom.id, tx_db_eeprom.id, gdb_eeprom.id, @@ -685,12 +717,12 @@ void usrp2_impl::set_db_eeprom(const std::string &mb, const std::string &type, c } sensor_value_t usrp2_impl::get_mimo_locked(const std::string &mb){ - const bool lock = (_mbc[mb].iface->peek32(U2_REG_IRQ_RB) & (1<<10)) != 0; + const bool lock = (_mbc[mb].wbiface->peek32(U2_REG_IRQ_RB) & (1<<10)) != 0; return sensor_value_t("MIMO", lock, "locked", "unlocked"); } sensor_value_t usrp2_impl::get_ref_locked(const std::string &mb){ - const bool lock = (_mbc[mb].iface->peek32(U2_REG_IRQ_RB) & (1<<11)) != 0; + const bool lock = (_mbc[mb].wbiface->peek32(U2_REG_IRQ_RB) & (1<<11)) != 0; return sensor_value_t("Ref", lock, "locked", "unlocked"); } @@ -729,13 +761,14 @@ meta_range_t usrp2_impl::get_tx_dsp_freq_range(const std::string &mb){ } void usrp2_impl::update_clock_source(const std::string &mb, const std::string &source){ + //NOTICE: U2_REG_MISC_CTRL_CLOCK is on the wb clock, and cannot be set from fifo_ctrl //clock source ref 10mhz switch(_mbc[mb].iface->get_rev()){ case usrp2_iface::USRP_N200: case usrp2_iface::USRP_N210: case usrp2_iface::USRP_N200_R4: case usrp2_iface::USRP_N210_R4: - if (source == "internal") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x12); + if (source == "internal") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x12); else if (source == "external") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x1C); else if (source == "mimo") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x15); else throw uhd::value_error("unhandled clock configuration reference source: " + source); @@ -744,7 +777,7 @@ void usrp2_impl::update_clock_source(const std::string &mb, const std::string &s case usrp2_iface::USRP2_REV3: case usrp2_iface::USRP2_REV4: - if (source == "internal") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x10); + if (source == "internal") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x10); else if (source == "external") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x1C); else if (source == "mimo") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x15); else throw uhd::value_error("unhandled clock configuration reference source: " + source); diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index 6053c3890..7ddac380d 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -18,7 +18,9 @@ #ifndef INCLUDED_USRP2_IMPL_HPP #define INCLUDED_USRP2_IMPL_HPP +#include "gpio_core_200.hpp" #include "usrp2_iface.hpp" +#include "usrp2_fifo_ctrl.hpp" #include "clock_ctrl.hpp" #include "codec_ctrl.hpp" #include "rx_frontend_core_200.hpp" @@ -52,14 +54,11 @@ static const size_t USRP2_SRAM_BYTES = size_t(1 << 20); static const boost::uint32_t USRP2_TX_ASYNC_SID = 2; static const boost::uint32_t USRP2_RX_SID_BASE = 3; -/*! - * Make a usrp2 dboard interface. - * \param iface the usrp2 interface object - * \param clk_ctrl the clock control object - * \return a sptr to a new dboard interface - */ +//! Make a usrp2 dboard interface. uhd::usrp::dboard_iface::sptr make_usrp2_dboard_iface( - usrp2_iface::sptr iface, + wb_iface::sptr wb_iface, + uhd::i2c_iface::sptr i2c_iface, + uhd::spi_iface::sptr spi_iface, usrp2_clock_ctrl::sptr clk_ctrl ); @@ -82,6 +81,9 @@ private: uhd::property_tree::sptr _tree; struct mb_container_type{ usrp2_iface::sptr iface; + usrp2_fifo_ctrl::sptr fifo_ctrl; + uhd::spi_iface::sptr spiface; + wb_iface::sptr wbiface; usrp2_clock_ctrl::sptr clock; usrp2_codec_ctrl::sptr codec; uhd::gps_ctrl::sptr gps; @@ -95,6 +97,7 @@ private: user_settings_core_200::sptr user; std::vector<uhd::transport::zero_copy_if::sptr> rx_dsp_xports; uhd::transport::zero_copy_if::sptr tx_dsp_xport; + uhd::transport::zero_copy_if::sptr fifo_ctrl_xport; uhd::usrp::dboard_manager::sptr dboard_manager; uhd::usrp::dboard_iface::sptr dboard_iface; size_t rx_chan_occ, tx_chan_occ; diff --git a/host/lib/usrp/usrp2/usrp2_regs.hpp b/host/lib/usrp/usrp2/usrp2_regs.hpp index e14798ecb..7fe83e709 100644 --- a/host/lib/usrp/usrp2/usrp2_regs.hpp +++ b/host/lib/usrp/usrp2/usrp2_regs.hpp @@ -36,10 +36,10 @@ // Setting register offsets //////////////////////////////////////////////////////////////////////// #define SR_MISC 0 // 7 regs -#define SR_SIMTIMER 8 // 2 +#define SR_USER_REGS 8 // 2 #define SR_TIME64 10 // 6 #define SR_BUF_POOL 16 // 4 -#define SR_USER_REGS 20 // 2 +#define SR_SPI_CORE 20 // 3 #define SR_RX_FRONT 24 // 5 #define SR_RX_CTRL0 32 // 9 #define SR_RX_DSP0 48 // 7 |