From ff1546f8137f7f92bb250f685561b0c34cc0e053 Mon Sep 17 00:00:00 2001 From: Ben Hilburn Date: Fri, 14 Feb 2014 12:05:07 -0800 Subject: Pushing the bulk of UHD-3.7.0 code. --- host/lib/usrp/common/adf435x_common.cpp | 35 ++-- host/lib/usrp/common/adf435x_common.hpp | 6 +- host/lib/usrp/dboard/db_cbx.cpp | 6 +- host/lib/usrp/dboard/db_sbx_version3.cpp | 8 +- host/lib/usrp/dboard/db_sbx_version4.cpp | 8 +- host/lib/usrp/dboard/db_wbx_version2.cpp | 33 +++- host/lib/usrp/dboard/db_wbx_version3.cpp | 33 +++- host/lib/usrp/dboard/db_wbx_version4.cpp | 34 +++- host/lib/usrp/gps_ctrl.cpp | 47 +++--- host/lib/usrp/x300/x300_clock_ctrl.cpp | 6 +- host/lib/usrp/x300/x300_impl.cpp | 275 +++++++++++++++++++++---------- host/lib/usrp/x300/x300_impl.hpp | 51 +++--- 12 files changed, 359 insertions(+), 183 deletions(-) (limited to 'host/lib') diff --git a/host/lib/usrp/common/adf435x_common.cpp b/host/lib/usrp/common/adf435x_common.cpp index e9d018fec..f0df6a334 100644 --- a/host/lib/usrp/common/adf435x_common.cpp +++ b/host/lib/usrp/common/adf435x_common.cpp @@ -25,8 +25,8 @@ using namespace uhd; * ADF 4350/4351 Tuning Utility **********************************************************************/ adf435x_tuning_settings tune_adf435x_synth( - double target_freq, - double ref_freq, + const double target_freq, + const double ref_freq, const adf435x_tuning_constraints& constraints, double& actual_freq) { @@ -66,9 +66,10 @@ adf435x_tuning_settings tune_adf435x_synth( * f_pfd = f_ref*(1+D)/(R*(1+T)) * f_vco = (N + (FRAC/MOD))*f_pfd * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD - * f_rf = f_vco/RFdiv) - * f_actual = f_rf/2 + * f_actual = f_vco/RFdiv) */ + double feedback_freq = constraints.feedback_after_divider ? target_freq : vco_freq; + for(R = 1; R <= 1023; R+=1){ //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T) pfd_freq = ref_freq*(D?2:1)/(R*(T?2:1)); @@ -76,10 +77,8 @@ adf435x_tuning_settings tune_adf435x_synth( //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth) if (pfd_freq > constraints.pfd_freq_max) continue; - //ignore fractional part of tuning - //N is computed from target_freq and not vco_freq because the feedback - //mode is set to FEEDBACK_SELECT_DIVIDED - N = boost::uint16_t(std::floor(target_freq/pfd_freq)); + //First, ignore fractional part of tuning + N = boost::uint16_t(std::floor(feedback_freq/pfd_freq)); //keep N > minimum int divider requirement if (N < static_cast(constraints.int_range.start())) continue; @@ -94,9 +93,7 @@ adf435x_tuning_settings tune_adf435x_synth( //Fractional-N calculation MOD = 4095; //max fractional accuracy - //N is computed from target_freq and not vco_freq because the feedback - //mode is set to FEEDBACK_SELECT_DIVIDED - FRAC = static_cast((target_freq/pfd_freq - N)*MOD); + FRAC = static_cast((feedback_freq/pfd_freq - N)*MOD); if (constraints.force_frac0) { if (FRAC > (MOD / 2)) { //Round integer such that actual freq is closest to target N++; @@ -114,8 +111,14 @@ adf435x_tuning_settings tune_adf435x_synth( //Typical phase resync time documented in data sheet pg.24 static const double PHASE_RESYNC_TIME = 400e-6; - //actual frequency calculation - actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(D?2:1)/(R*(T?2:1))); + //If feedback after divider, then compensation for the divider is pulled into the INT value + int rf_div_compensation = constraints.feedback_after_divider ? 1 : RFdiv; + + //Compute the actual frequency in terms of ref_freq, N, FRAC, MOD, D, R and T. + actual_freq = ( + double((N + (double(FRAC)/double(MOD))) * + (ref_freq*(D?2:1)/(R*(T?2:1)))) + ) / rf_div_compensation; //load the settings adf435x_tuning_settings settings; @@ -128,15 +131,13 @@ adf435x_tuning_settings tune_adf435x_synth( settings.r_doubler_en = D; settings.band_select_clock_div = BS; settings.rf_divider = RFdiv; - settings.feedback_after_divider = true; std::string tuning_str = (constraints.force_frac0) ? "Integer-N" : "Fractional"; - UHD_LOGV(often) << boost::format("ADF 435X Frequencies (MHz): REQUESTED=%0.9f, ACTUAL=%0.9f" ) % (target_freq/1e6) % (actual_freq/1e6) << std::endl - << boost::format("ADF 435X Intermediates (MHz): VCO=%0.2f, PFD=%0.2f, BAND=%0.2f, REF=%0.2f" - ) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) % (ref_freq/1e6) << std::endl + << boost::format("ADF 435X Intermediates (MHz): Feedback=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f, REF=%0.2f" + ) % (feedback_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) % (ref_freq/1e6) << std::endl << boost::format("ADF 435X Tuning: %s") % tuning_str.c_str() << std::endl << boost::format("ADF 435X Settings: 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; diff --git a/host/lib/usrp/common/adf435x_common.hpp b/host/lib/usrp/common/adf435x_common.hpp index 715b1fd53..617b9d97f 100644 --- a/host/lib/usrp/common/adf435x_common.hpp +++ b/host/lib/usrp/common/adf435x_common.hpp @@ -33,6 +33,7 @@ struct adf435x_tuning_constraints { bool force_frac0; + bool feedback_after_divider; double ref_doubler_threshold; double pfd_freq_max; double band_sel_freq_max; @@ -50,12 +51,11 @@ struct adf435x_tuning_settings { boost::uint16_t clock_divider_12_bit; boost::uint8_t band_select_clock_div; boost::uint16_t rf_divider; - bool feedback_after_divider; }; adf435x_tuning_settings tune_adf435x_synth( - double target_freq, - double ref_freq, + const double target_freq, + const double ref_freq, const adf435x_tuning_constraints& constraints, double& actual_freq ); diff --git a/host/lib/usrp/dboard/db_cbx.cpp b/host/lib/usrp/dboard/db_cbx.cpp index ae41a7971..78ecd9794 100644 --- a/host/lib/usrp/dboard/db_cbx.cpp +++ b/host/lib/usrp/dboard/db_cbx.cpp @@ -18,7 +18,7 @@ #include "max2870_regs.hpp" #include "db_sbx_common.hpp" - +#include using namespace uhd; using namespace uhd::usrp; @@ -47,14 +47,14 @@ double sbx_xcvr::cbx::set_lo_freq(dboard_iface::unit_t unit, double target_freq) ) % (target_freq/1e6) << std::endl; /* - * If the user sets 'mode_n=int-n' in the tuning args, the user wishes to + * If the user sets 'mode_n=integer' in the tuning args, the user wishes to * tune in Integer-N mode, which can result in better spur * performance on some mixers. The default is fractional tuning. */ property_tree::sptr subtree = (unit == dboard_iface::UNIT_RX) ? self_base->get_rx_subtree() : self_base->get_tx_subtree(); device_addr_t tune_args = subtree->access("tune_args").get(); - bool is_int_n = (tune_args.get("mode_n","") == "int-n"); + bool is_int_n = boost::iequals(tune_args.get("mode_n",""), "integer"); //clip the input target_freq = cbx_freq_range.clip(target_freq); diff --git a/host/lib/usrp/dboard/db_sbx_version3.cpp b/host/lib/usrp/dboard/db_sbx_version3.cpp index ef0126557..463de5e15 100644 --- a/host/lib/usrp/dboard/db_sbx_version3.cpp +++ b/host/lib/usrp/dboard/db_sbx_version3.cpp @@ -20,6 +20,7 @@ #include "db_sbx_common.hpp" #include "../common/adf435x_common.hpp" #include +#include using namespace uhd; using namespace uhd::usrp; @@ -47,14 +48,14 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar ) % (target_freq/1e6) << std::endl; /* - * If the user sets 'mode_n=int-n' in the tuning args, the user wishes to + * If the user sets 'mode_n=integer' in the tuning args, the user wishes to * tune in Integer-N mode, which can result in better spur * performance on some mixers. The default is fractional tuning. */ property_tree::sptr subtree = (unit == dboard_iface::UNIT_RX) ? self_base->get_rx_subtree() : self_base->get_tx_subtree(); device_addr_t tune_args = subtree->access("tune_args").get(); - bool is_int_n = (tune_args.get("mode_n","") == "int-n"); + bool is_int_n = boost::iequals(tune_args.get("mode_n",""), "integer"); //clip the input target_freq = sbx_freq_range.clip(target_freq); @@ -84,6 +85,7 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar tuning_constraints.int_range = uhd::range_t(prescaler_to_min_int_div[prescaler], 4095); //INT is a 12-bit field tuning_constraints.pfd_freq_max = 25e6; tuning_constraints.rf_divider_range = uhd::range_t(1, 16); + tuning_constraints.feedback_after_divider = true; double actual_freq; adf435x_tuning_settings tuning_settings = tune_adf435x_synth( @@ -102,7 +104,7 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar regs.int_16_bit = tuning_settings.int_16_bit; regs.mod_12_bit = tuning_settings.mod_12_bit; regs.clock_divider_12_bit = tuning_settings.clock_divider_12_bit; - regs.feedback_select = tuning_settings.feedback_after_divider ? + regs.feedback_select = tuning_constraints.feedback_after_divider ? adf4350_regs_t::FEEDBACK_SELECT_DIVIDED : adf4350_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; regs.clock_div_mode = adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; diff --git a/host/lib/usrp/dboard/db_sbx_version4.cpp b/host/lib/usrp/dboard/db_sbx_version4.cpp index 6c0cebb4b..ff4e19163 100644 --- a/host/lib/usrp/dboard/db_sbx_version4.cpp +++ b/host/lib/usrp/dboard/db_sbx_version4.cpp @@ -20,6 +20,7 @@ #include "db_sbx_common.hpp" #include "../common/adf435x_common.hpp" #include +#include using namespace uhd; using namespace uhd::usrp; @@ -48,14 +49,14 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar ) % (target_freq/1e6) << std::endl; /* - * If the user sets 'mode_n=int-n' in the tuning args, the user wishes to + * If the user sets 'mode_n=integer' in the tuning args, the user wishes to * tune in Integer-N mode, which can result in better spur * performance on some mixers. The default is fractional tuning. */ property_tree::sptr subtree = (unit == dboard_iface::UNIT_RX) ? self_base->get_rx_subtree() : self_base->get_tx_subtree(); device_addr_t tune_args = subtree->access("tune_args").get(); - bool is_int_n = (tune_args.get("mode_n","") == "int-n"); + bool is_int_n = boost::iequals(tune_args.get("mode_n",""), "integer"); //clip the input target_freq = sbx_freq_range.clip(target_freq); @@ -87,6 +88,7 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar tuning_constraints.int_range = uhd::range_t(prescaler_to_min_int_div[prescaler], 4095); //INT is a 12-bit field tuning_constraints.pfd_freq_max = 25e6; tuning_constraints.rf_divider_range = uhd::range_t(1, 64); + tuning_constraints.feedback_after_divider = true; double actual_freq; adf435x_tuning_settings tuning_settings = tune_adf435x_synth( @@ -105,7 +107,7 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar regs.int_16_bit = tuning_settings.int_16_bit; regs.mod_12_bit = tuning_settings.mod_12_bit; regs.clock_divider_12_bit = tuning_settings.clock_divider_12_bit; - regs.feedback_select = tuning_settings.feedback_after_divider ? + regs.feedback_select = tuning_constraints.feedback_after_divider ? adf4351_regs_t::FEEDBACK_SELECT_DIVIDED : adf4351_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; regs.clock_div_mode = adf4351_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; diff --git a/host/lib/usrp/dboard/db_wbx_version2.cpp b/host/lib/usrp/dboard/db_wbx_version2.cpp index 2afdce4cd..c5945483d 100644 --- a/host/lib/usrp/dboard/db_wbx_version2.cpp +++ b/host/lib/usrp/dboard/db_wbx_version2.cpp @@ -30,6 +30,7 @@ #include #include #include +#include using namespace uhd; using namespace uhd::usrp; @@ -169,14 +170,14 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar ) % (target_freq/1e6) << std::endl; /* - * If the user sets 'mode_n=int-n' in the tuning args, the user wishes to + * If the user sets 'mode_n=integer' in the tuning args, the user wishes to * tune in Integer-N mode, which can result in better spur * performance on some mixers. The default is fractional tuning. */ property_tree::sptr subtree = (unit == dboard_iface::UNIT_RX) ? self_base->get_rx_subtree() : self_base->get_tx_subtree(); device_addr_t tune_args = subtree->access("tune_args").get(); - bool is_int_n = (tune_args.get("mode_n","") == "int-n"); + bool is_int_n = boost::iequals(tune_args.get("mode_n",""), "integer"); //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) static const uhd::dict prescaler_to_min_int_div = map_list_of @@ -193,7 +194,15 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar (16, adf4350_regs_t::RF_DIVIDER_SELECT_DIV16) ; - adf4350_regs_t::prescaler_t prescaler = target_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; + double reference_freq = self_base->get_iface()->get_clock_rate(unit); + //The mixer has a divide-by-2 stage on the LO port so the synthesizer + //frequency must 2x the target frequency + double synth_target_freq = target_freq * 2; + //TODO: Document why the following has to be true + bool div_resync_enabled = (target_freq > reference_freq); + + adf4350_regs_t::prescaler_t prescaler = + synth_target_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; adf435x_tuning_constraints tuning_constraints; tuning_constraints.force_frac0 = is_int_n; @@ -202,11 +211,17 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar tuning_constraints.int_range = uhd::range_t(prescaler_to_min_int_div[prescaler], 4095); tuning_constraints.pfd_freq_max = 25e6; tuning_constraints.rf_divider_range = uhd::range_t(1, 16); + //When divider resync is enabled, a 180 deg phase error is introduced when syncing + //multiple WBX boards. Switching to fundamental mode works arounds this issue. + tuning_constraints.feedback_after_divider = div_resync_enabled; - double actual_freq; + double synth_actual_freq = 0; adf435x_tuning_settings tuning_settings = tune_adf435x_synth( - target_freq, self_base->get_iface()->get_clock_rate(unit), - tuning_constraints, actual_freq); + synth_target_freq, reference_freq, tuning_constraints, synth_actual_freq); + + //The mixer has a divide-by-2 stage on the LO port so the synthesizer + //actual_freq must /2 the synth_actual_freq + double actual_freq = synth_actual_freq / 2; //load the register values adf4350_regs_t regs; @@ -222,10 +237,12 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar regs.int_16_bit = tuning_settings.int_16_bit; regs.mod_12_bit = tuning_settings.mod_12_bit; regs.clock_divider_12_bit = tuning_settings.clock_divider_12_bit; - regs.feedback_select = tuning_settings.feedback_after_divider ? + regs.feedback_select = tuning_constraints.feedback_after_divider ? adf4350_regs_t::FEEDBACK_SELECT_DIVIDED : adf4350_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; - regs.clock_div_mode = adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; + regs.clock_div_mode = div_resync_enabled ? + adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE : + adf4350_regs_t::CLOCK_DIV_MODE_FAST_LOCK; regs.prescaler = prescaler; regs.r_counter_10_bit = tuning_settings.r_counter_10_bit; regs.reference_divide_by_2 = tuning_settings.r_divide_by_2_en ? diff --git a/host/lib/usrp/dboard/db_wbx_version3.cpp b/host/lib/usrp/dboard/db_wbx_version3.cpp index e30d6c665..80ecb426b 100644 --- a/host/lib/usrp/dboard/db_wbx_version3.cpp +++ b/host/lib/usrp/dboard/db_wbx_version3.cpp @@ -29,6 +29,7 @@ #include #include #include +#include using namespace uhd; using namespace uhd::usrp; @@ -200,14 +201,14 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar ) % (target_freq/1e6) << std::endl; /* - * If the user sets 'mode_n=int-n' in the tuning args, the user wishes to + * If the user sets 'mode_n=integer' in the tuning args, the user wishes to * tune in Integer-N mode, which can result in better spur * performance on some mixers. The default is fractional tuning. */ property_tree::sptr subtree = (unit == dboard_iface::UNIT_RX) ? self_base->get_rx_subtree() : self_base->get_tx_subtree(); device_addr_t tune_args = subtree->access("tune_args").get(); - bool is_int_n = (tune_args.get("mode_n","") == "int-n"); + bool is_int_n = boost::iequals(tune_args.get("mode_n",""), "integer"); //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) static const uhd::dict prescaler_to_min_int_div = map_list_of @@ -224,7 +225,15 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar (16, adf4350_regs_t::RF_DIVIDER_SELECT_DIV16) ; - adf4350_regs_t::prescaler_t prescaler = target_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; + double reference_freq = self_base->get_iface()->get_clock_rate(unit); + //The mixer has a divide-by-2 stage on the LO port so the synthesizer + //frequency must 2x the target frequency + double synth_target_freq = target_freq * 2; + //TODO: Document why the following has to be true + bool div_resync_enabled = (target_freq > reference_freq); + + adf4350_regs_t::prescaler_t prescaler = + synth_target_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; adf435x_tuning_constraints tuning_constraints; tuning_constraints.force_frac0 = is_int_n; @@ -233,11 +242,17 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar tuning_constraints.int_range = uhd::range_t(prescaler_to_min_int_div[prescaler], 4095); tuning_constraints.pfd_freq_max = 25e6; tuning_constraints.rf_divider_range = uhd::range_t(1, 16); + //When divider resync is enabled, a 180 deg phase error is introduced when syncing + //multiple WBX boards. Switching to fundamental mode works arounds this issue. + tuning_constraints.feedback_after_divider = div_resync_enabled; - double actual_freq; + double synth_actual_freq = 0; adf435x_tuning_settings tuning_settings = tune_adf435x_synth( - target_freq, self_base->get_iface()->get_clock_rate(unit), - tuning_constraints, actual_freq); + synth_target_freq, reference_freq, tuning_constraints, synth_actual_freq); + + //The mixer has a divide-by-2 stage on the LO port so the synthesizer + //actual_freq must /2 the synth_actual_freq + double actual_freq = synth_actual_freq / 2; //load the register values adf4350_regs_t regs; @@ -253,10 +268,12 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar regs.int_16_bit = tuning_settings.int_16_bit; regs.mod_12_bit = tuning_settings.mod_12_bit; regs.clock_divider_12_bit = tuning_settings.clock_divider_12_bit; - regs.feedback_select = tuning_settings.feedback_after_divider ? + regs.feedback_select = tuning_constraints.feedback_after_divider ? adf4350_regs_t::FEEDBACK_SELECT_DIVIDED : adf4350_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; - regs.clock_div_mode = adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; + regs.clock_div_mode = div_resync_enabled ? + adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE : + adf4350_regs_t::CLOCK_DIV_MODE_FAST_LOCK; regs.prescaler = prescaler; regs.r_counter_10_bit = tuning_settings.r_counter_10_bit; regs.reference_divide_by_2 = tuning_settings.r_divide_by_2_en ? diff --git a/host/lib/usrp/dboard/db_wbx_version4.cpp b/host/lib/usrp/dboard/db_wbx_version4.cpp index dc1ae4df8..80ff3f998 100644 --- a/host/lib/usrp/dboard/db_wbx_version4.cpp +++ b/host/lib/usrp/dboard/db_wbx_version4.cpp @@ -29,6 +29,7 @@ #include #include #include +#include using namespace uhd; using namespace uhd::usrp; @@ -208,14 +209,14 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar ) % (target_freq/1e6) << std::endl; /* - * If the user sets 'mode_n=int-n' in the tuning args, the user wishes to + * If the user sets 'mode_n=integer' in the tuning args, the user wishes to * tune in Integer-N mode, which can result in better spur * performance on some mixers. The default is fractional tuning. */ property_tree::sptr subtree = (unit == dboard_iface::UNIT_RX) ? self_base->get_rx_subtree() : self_base->get_tx_subtree(); device_addr_t tune_args = subtree->access("tune_args").get(); - bool is_int_n = (tune_args.get("mode_n","") == "int-n"); + bool is_int_n = boost::iequals(tune_args.get("mode_n",""), "integer"); //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) static const uhd::dict prescaler_to_min_int_div = map_list_of @@ -234,7 +235,15 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar (64, adf4351_regs_t::RF_DIVIDER_SELECT_DIV64) ; - adf4351_regs_t::prescaler_t prescaler = target_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; + double reference_freq = self_base->get_iface()->get_clock_rate(unit); + //The mixer has a divide-by-2 stage on the LO port so the synthesizer + //frequency must 2x the target frequency + double synth_target_freq = target_freq * 2; + //TODO: Document why the following has to be true + bool div_resync_enabled = (target_freq > reference_freq); + + adf4351_regs_t::prescaler_t prescaler = + synth_target_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; adf435x_tuning_constraints tuning_constraints; tuning_constraints.force_frac0 = is_int_n; @@ -243,11 +252,17 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar tuning_constraints.int_range = uhd::range_t(prescaler_to_min_int_div[prescaler], 4095); tuning_constraints.pfd_freq_max = 25e6; tuning_constraints.rf_divider_range = uhd::range_t(1, 64); + //When divider resync is enabled, a 180 deg phase error is introduced when syncing + //multiple WBX boards. Switching to fundamental mode works arounds this issue. + tuning_constraints.feedback_after_divider = div_resync_enabled; - double actual_freq; + double synth_actual_freq = 0; adf435x_tuning_settings tuning_settings = tune_adf435x_synth( - target_freq, self_base->get_iface()->get_clock_rate(unit), - tuning_constraints, actual_freq); + synth_target_freq, reference_freq, tuning_constraints, synth_actual_freq); + + //The mixer has a divide-by-2 stage on the LO port so the synthesizer + //actual_freq must /2 the synth_actual_freq + double actual_freq = synth_actual_freq / 2; //load the register values adf4351_regs_t regs; @@ -263,10 +278,12 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar regs.int_16_bit = tuning_settings.int_16_bit; regs.mod_12_bit = tuning_settings.mod_12_bit; regs.clock_divider_12_bit = tuning_settings.clock_divider_12_bit; - regs.feedback_select = tuning_settings.feedback_after_divider ? + regs.feedback_select = tuning_constraints.feedback_after_divider ? adf4351_regs_t::FEEDBACK_SELECT_DIVIDED : adf4351_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; - regs.clock_div_mode = adf4351_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; + regs.clock_div_mode = div_resync_enabled ? + adf4351_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE : + adf4351_regs_t::CLOCK_DIV_MODE_FAST_LOCK; regs.prescaler = prescaler; regs.r_counter_10_bit = tuning_settings.r_counter_10_bit; regs.reference_divide_by_2 = tuning_settings.r_divide_by_2_en ? @@ -307,5 +324,6 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar UHD_LOGV(often) << boost::format( "%s tune: actual frequency %f Mhz" ) % board_name.c_str() % (actual_freq/1e6) << std::endl; + return actual_freq; } diff --git a/host/lib/usrp/gps_ctrl.cpp b/host/lib/usrp/gps_ctrl.cpp index 4bf8eb3a3..c0d44abd5 100644 --- a/host/lib/usrp/gps_ctrl.cpp +++ b/host/lib/usrp/gps_ctrl.cpp @@ -30,6 +30,7 @@ #include "boost/tuple/tuple.hpp" #include "boost/foreach.hpp" +#include using namespace uhd; using namespace boost::gregorian; @@ -68,34 +69,36 @@ private: return std::string(); } - std::string msg = _recv(); + const std::list list = boost::assign::list_of("GPGGA")("GPRMC")("SERVO"); static const boost::regex status_regex("\\d\\d-\\d\\d-\\d\\d"); + std::map msgs; + + // Get all GPSDO messages available + // Creating a map here because we only want the latest of each message type + for (std::string msg = _recv(); msg.length() > 6; msg = _recv()) + { + // Look for SERVO message + if (boost::regex_search(msg, status_regex, boost::regex_constants::match_continuous)) + msgs["SERVO"] = msg; + else + msgs[msg.substr(1,5)] = msg; + } + boost::system_time time = boost::get_system_time(); - if(msg.size() < 6) - return std::string(); - std::string nmea = msg.substr(1,5); - const std::list list = boost::assign::list_of("GPGGA")("GPRMC"); + // Update sensors with newly read data BOOST_FOREACH(std::string key, list) { - // beginning matches one of the NMEA keys - if(!nmea.compare(key)) { - sensors[key] = boost::make_tuple(msg, time, !sensor.compare(key)); - // if this was what we're looking for return it - return (!sensor.compare(key))? msg : std::string(); - } + if (msgs[key].length()) + sensors[key] = boost::make_tuple(msgs[key], time, !sensor.compare(key)); } - //We're still here so it's not one of the NMEA strings from above - if(boost::regex_search(msg, status_regex, boost::regex_constants::match_continuous)) { - trim(msg); - sensors["SERVO"] = boost::make_tuple(msg, time, false); - if(!sensor.compare("SERVO")) - return msg; - else - return std::string(); - } + // Return requested sensor if it was updated + if (msgs[sensor].length()) + return msgs[sensor]; + return std::string(); } + public: gps_ctrl_impl(uart_iface::sptr uart){ _uart = uart; @@ -332,8 +335,8 @@ private: } } - std::string _recv(void){ - return _uart->read_uart(GPS_TIMEOUT_DELAY_MS/1000.); + std::string _recv(double timeout = GPS_TIMEOUT_DELAY_MS/1000.){ + return _uart->read_uart(timeout); } void _send(const std::string &buf){ diff --git a/host/lib/usrp/x300/x300_clock_ctrl.cpp b/host/lib/usrp/x300/x300_clock_ctrl.cpp index 1a4cd4668..a986928a7 100644 --- a/host/lib/usrp/x300/x300_clock_ctrl.cpp +++ b/host/lib/usrp/x300/x300_clock_ctrl.cpp @@ -24,6 +24,8 @@ #include #include +static const double X300_REF_CLK_OUT_RATE = 10e6; + using namespace uhd; class x300_clock_ctrl_impl : public x300_clock_ctrl { @@ -66,7 +68,7 @@ double get_sysref_clock_rate(void) { double get_refout_clock_rate(void) { //We support only one reference output rate - return 10e6; + return X300_REF_CLK_OUT_RATE; } void set_dboard_rate(const x300_clock_which_t, double rate) { @@ -292,7 +294,7 @@ void set_master_clock_rate(double clock_rate) { _lmk04816_regs.CLKout8_9_DIV = vco_div; // Register 5 _lmk04816_regs.CLKout10_11_PD = lmk04816_regs_t::CLKOUT10_11_PD_NORMAL; - _lmk04816_regs.CLKout10_11_DIV = vco_div; + _lmk04816_regs.CLKout10_11_DIV = vco_div * static_cast(clock_rate/X300_REF_CLK_OUT_RATE); // Register 6 _lmk04816_regs.CLKout0_TYPE = lmk04816_regs_t::CLKOUT0_TYPE_LVDS; //FPGA diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp index f62967018..b20897fc6 100644 --- a/host/lib/usrp/x300/x300_impl.cpp +++ b/host/lib/usrp/x300/x300_impl.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -399,34 +400,25 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) if (key.find("send") != std::string::npos) mb.send_args[key] = dev_addr[key]; } - const std::vector DB_NAMES = boost::assign::list_of("A")("B"); - - //create basic communication - UHD_MSG(status) << "Setup basic communication..." << std::endl; - if (mb.xport_path == "nirio") { - mb.zpu_ctrl = x300_make_ctrl_iface_pcie(mb.rio_fpga_interface->get_kernel_proxy()); - } else { - mb.zpu_ctrl = x300_make_ctrl_iface_enet(udp_simple::make_connected(mb.addr, - BOOST_STRINGIZE(X300_FW_COMMS_UDP_PORT))); - } - - if (mb.xport_path == "eth") - { - mtu_result_t user_set; - user_set.recv_mtu = dev_addr.has_key("recv_frame_size") \ - ? boost::lexical_cast(dev_addr["recv_frame_size"]) \ - : X300_ETH_DATA_FRAME_SIZE; - user_set.send_mtu = dev_addr.has_key("send_frame_size") \ - ? boost::lexical_cast(dev_addr["send_frame_size"]) \ - : X300_ETH_DATA_FRAME_SIZE; - - // Detect the MTU on the path to the USRP - mtu_result_t result; - try { - result = determine_mtu(mb.addr, user_set); - } catch(std::exception &e) { - UHD_MSG(error) << e.what() << std::endl; - } + if (mb.xport_path == "eth" ) { + /* This is an ETH connection. Figure out what the maximum supported frame + * size is for the transport in the up and down directions. The frame size + * depends on the host PIC's NIC's MTU settings. To determine the frame size, + * we test for support up to an expected "ceiling". If the user + * specified a frame size, we use that frame size as the ceiling. If no + * frame size was specified, we use the maximum UHD frame size. + * + * To optimize performance, the frame size should be greater than or equal + * to the frame size that UHD uses so that frames don't get split across + * multiple transmission units - this is why the limits passed into the + * 'determine_max_frame_size' function are actually frame sizes. */ + frame_size_t req_max_frame_size; + req_max_frame_size.recv_frame_size = (mb.recv_args.has_key("recv_frame_size")) \ + ? boost::lexical_cast(mb.recv_args["recv_frame_size"]) \ + : X300_10GE_DATA_FRAME_MAX_SIZE; + req_max_frame_size.send_frame_size = (mb.send_args.has_key("send_frame_size")) \ + ? boost::lexical_cast(mb.send_args["send_frame_size"]) \ + : X300_10GE_DATA_FRAME_MAX_SIZE; #if defined UHD_PLATFORM_LINUX const std::string mtu_tool("ip link"); @@ -436,23 +428,47 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) const std::string mtu_tool("ifconfig"); #endif - if(result.recv_mtu < user_set.recv_mtu) { + // Detect the frame size on the path to the USRP + try { + max_frame_sizes = determine_max_frame_size(mb.addr, req_max_frame_size); + } catch(std::exception &e) { + UHD_MSG(error) << e.what() << std::endl; + } + + if ((mb.recv_args.has_key("recv_frame_size")) + && (req_max_frame_size.recv_frame_size < max_frame_sizes.recv_frame_size)) { UHD_MSG(warning) - << boost::format("The receive path contains entities that do not support MTUs >= one recv frame's size (%lu).") - % user_set.recv_mtu << std::endl - << boost::format("Please verify your NIC's MTU setting using '%s' or set the recv_frame_size argument.") - % mtu_tool << std::endl; + << boost::format("You requested a receive frame size of (%lu) but your NIC's max frame size is (%lu).") + % req_max_frame_size.recv_frame_size << max_frame_sizes.recv_frame_size << std::endl + << boost::format("Please verify your NIC's MTU setting using '%s' or set the recv_frame_size argument appropriately.") + % mtu_tool << std::endl + << "UHD will use the auto-detected max frame size for this connection." + << std::endl; } - if(result.send_mtu < user_set.send_mtu) { + if ((mb.recv_args.has_key("send_frame_size")) + && (req_max_frame_size.send_frame_size < max_frame_sizes.send_frame_size)) { UHD_MSG(warning) - << boost::format("The send path contains entities that do not support MTUs >= one send frame's size (%lu).") - % user_set.send_mtu << std::endl - << boost::format("Please verify your NIC's MTU setting using '%s' or set the send_frame_size argument.") - % mtu_tool << std::endl; + << boost::format("You requested a send frame size of (%lu) but your NIC's max frame size is (%lu).") + % req_max_frame_size.send_frame_size << max_frame_sizes.send_frame_size << std::endl + << boost::format("Please verify your NIC's MTU setting using '%s' or set the send_frame_size argument appropriately.") + % mtu_tool << std::endl + << "UHD will use the auto-detected max frame size for this connection." + << std::endl; } } + const std::vector DB_NAMES = boost::assign::list_of("A")("B"); + + //create basic communication + UHD_MSG(status) << "Setup basic communication..." << std::endl; + if (mb.xport_path == "nirio") { + mb.zpu_ctrl = x300_make_ctrl_iface_pcie(mb.rio_fpga_interface->get_kernel_proxy()); + } else { + mb.zpu_ctrl = x300_make_ctrl_iface_enet(udp_simple::make_connected(mb.addr, + BOOST_STRINGIZE(X300_FW_COMMS_UDP_PORT))); + } + mb.claimer_task = uhd::task::make(boost::bind(&x300_impl::claimer_loop, this, mb.zpu_ctrl)); //extract the FW path for the X300 @@ -469,6 +485,9 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) this->check_fw_compat(mb_path, mb.zpu_ctrl); this->check_fpga_compat(mb_path, mb.zpu_ctrl); + //store which FPGA image is loaded + mb.loaded_fpga_image = get_fpga_option(mb.zpu_ctrl); + //low speed perif access mb.zpu_spi = spi_core_3000::make(mb.zpu_ctrl, SR_ADDR(SET0_BASE, ZPU_SR_SPI), SR_ADDR(SET0_BASE, ZPU_RB_SPI)); @@ -1030,8 +1049,6 @@ x300_impl::both_xports_t x300_impl::make_transport( ) { mboard_members_t &mb = _mb[mb_index]; - const std::string& addr = mb.addr; - const std::string& xport_path = mb.xport_path; both_xports_t xports; sid_config_t config; @@ -1047,47 +1064,122 @@ x300_impl::both_xports_t x300_impl::make_transport( (prefix != X300_RADIO_DEST_PREFIX_CTRL) ? args : DEFAULT_XPORT_ARGS; zero_copy_xport_params default_buff_args; - if (xport_path == "nirio") { + + if (mb.xport_path == "nirio") { default_buff_args.send_frame_size = - (prefix == X300_RADIO_DEST_PREFIX_TX) ? X300_PCIE_DATA_FRAME_SIZE : X300_PCIE_MSG_FRAME_SIZE; + (prefix == X300_RADIO_DEST_PREFIX_TX) + ? X300_PCIE_DATA_FRAME_SIZE + : X300_PCIE_MSG_FRAME_SIZE; + default_buff_args.recv_frame_size = - (prefix == X300_RADIO_DEST_PREFIX_RX) ? X300_PCIE_DATA_FRAME_SIZE : X300_PCIE_MSG_FRAME_SIZE; + (prefix == X300_RADIO_DEST_PREFIX_RX) + ? X300_PCIE_DATA_FRAME_SIZE + : X300_PCIE_MSG_FRAME_SIZE; + default_buff_args.num_send_frames = - (prefix == X300_RADIO_DEST_PREFIX_TX) ? X300_PCIE_DATA_NUM_FRAMES : X300_PCIE_MSG_NUM_FRAMES; + (prefix == X300_RADIO_DEST_PREFIX_TX) + ? X300_PCIE_DATA_NUM_FRAMES + : X300_PCIE_MSG_NUM_FRAMES; + default_buff_args.num_recv_frames = - (prefix == X300_RADIO_DEST_PREFIX_RX) ? X300_PCIE_DATA_NUM_FRAMES : X300_PCIE_MSG_NUM_FRAMES; + (prefix == X300_RADIO_DEST_PREFIX_RX) + ? X300_PCIE_DATA_NUM_FRAMES + : X300_PCIE_MSG_NUM_FRAMES; xports.recv = nirio_zero_copy::make( - mb.rio_fpga_interface, get_pcie_dma_channel(destination, prefix), default_buff_args, xport_args); + mb.rio_fpga_interface, + get_pcie_dma_channel(destination, prefix), + default_buff_args, + xport_args); + xports.send = xports.recv; //For the nirio transport, buffer size is depends on the frame size and num frames xports.recv_buff_size = xports.recv->get_num_recv_frames() * xports.recv->get_recv_frame_size(); xports.send_buff_size = xports.send->get_num_send_frames() * xports.send->get_send_frame_size(); - } else { + } else if (mb.xport_path == "eth") { + + /* Determine what the recommended frame size is for this + * connection type.*/ + size_t eth_data_rec_frame_size = 0; + + if (mb.loaded_fpga_image == "HGS") { + if (mb.router_dst_here == X300_XB_DST_E0) { + eth_data_rec_frame_size = X300_1GE_DATA_FRAME_MAX_SIZE; + } else if (mb.router_dst_here == X300_XB_DST_E1) { + eth_data_rec_frame_size = X300_10GE_DATA_FRAME_MAX_SIZE; + } + } else if (mb.loaded_fpga_image == "XGS") { + eth_data_rec_frame_size = X300_10GE_DATA_FRAME_MAX_SIZE; + } + + if (eth_data_rec_frame_size == 0) { + throw uhd::runtime_error("Unable to determine ETH link type."); + } + + /* Print a warning if the system's max available frame size is less than the most optimal + * frame size for this type of connection. */ + if (max_frame_sizes.send_frame_size < eth_data_rec_frame_size) { + UHD_MSG(warning) + << boost::format("For this connection, UHD recommends a send frame size of at least %lu for best\nperformance, but your system's MTU will only allow %lu.") + % eth_data_rec_frame_size + % max_frame_sizes.send_frame_size + << std::endl + << "This will negatively impact your maximum achievable sample rate." + << std::endl; + } + + if (max_frame_sizes.recv_frame_size < eth_data_rec_frame_size) { + UHD_MSG(warning) + << boost::format("For this connection, UHD recommends a receive frame size of at least %lu for best\nperformance, but your system's MTU will only allow %lu.") + % eth_data_rec_frame_size + % max_frame_sizes.recv_frame_size + << std::endl + << "This will negatively impact your maximum achievable sample rate." + << std::endl; + } + + // Account for headers + size_t system_max_send_frame_size = (size_t) max_frame_sizes.send_frame_size - 64; + size_t system_max_recv_frame_size = (size_t) max_frame_sizes.recv_frame_size - 64; + + // Make sure frame sizes do not exceed the max available value supported by UHD default_buff_args.send_frame_size = - (prefix == X300_RADIO_DEST_PREFIX_TX) ? X300_ETH_DATA_FRAME_SIZE : X300_ETH_MSG_FRAME_SIZE; + (prefix == X300_RADIO_DEST_PREFIX_TX) + ? std::min(system_max_send_frame_size, X300_10GE_DATA_FRAME_MAX_SIZE) + : std::min(system_max_send_frame_size, X300_ETH_MSG_FRAME_SIZE); + default_buff_args.recv_frame_size = - (prefix == X300_RADIO_DEST_PREFIX_RX) ? X300_ETH_DATA_FRAME_SIZE : X300_ETH_MSG_FRAME_SIZE; + (prefix == X300_RADIO_DEST_PREFIX_RX) + ? std::min(system_max_recv_frame_size, X300_10GE_DATA_FRAME_MAX_SIZE) + : std::min(system_max_recv_frame_size, X300_ETH_MSG_FRAME_SIZE); + default_buff_args.num_send_frames = - (prefix == X300_RADIO_DEST_PREFIX_TX) ? X300_ETH_DATA_NUM_FRAMES : X300_ETH_MSG_NUM_FRAMES; + (prefix == X300_RADIO_DEST_PREFIX_TX) + ? X300_ETH_DATA_NUM_FRAMES + : X300_ETH_MSG_NUM_FRAMES; + default_buff_args.num_recv_frames = - (prefix == X300_RADIO_DEST_PREFIX_RX) ? X300_ETH_DATA_NUM_FRAMES : X300_ETH_MSG_NUM_FRAMES; + (prefix == X300_RADIO_DEST_PREFIX_RX) + ? X300_ETH_DATA_NUM_FRAMES + : X300_ETH_MSG_NUM_FRAMES; //make a new transport - fpga has no idea how to talk to use on this yet udp_zero_copy::buff_params buff_params; - xports.recv = udp_zero_copy::make(addr, BOOST_STRINGIZE(X300_VITA_UDP_PORT), default_buff_args, buff_params, xport_args); + xports.recv = udp_zero_copy::make(mb.addr, + BOOST_STRINGIZE(X300_VITA_UDP_PORT), + default_buff_args, + buff_params, + xport_args); + xports.send = xports.recv; - //For the UDP transport the buffer size if the size of the socket buffer in the kernel + //For the UDP transport the buffer size if the size of the socket buffer + //in the kernel xports.recv_buff_size = buff_params.recv_buff_size; xports.send_buff_size = buff_params.send_buff_size; - } - //always program the framer if this is a socket, even its caching was used - if (xport_path != "nirio") - { //clear the ethernet dispatcher's udp port //NOT clearing this, the dispatcher is now intelligent //_zpu_ctrl->poke32(SR_ADDR(SET0_BASE, (ZPU_SR_ETHINT0+8+3)), 0); @@ -1095,7 +1187,7 @@ x300_impl::both_xports_t x300_impl::make_transport( //send a mini packet with SID into the ZPU //ZPU will reprogram the ethernet framer UHD_LOG << "programming packet for new xport on " - << addr << std::hex << "sid 0x" << sid << std::dec << std::endl; + << mb.addr << std::hex << "sid 0x" << sid << std::dec << std::endl; //YES, get a __send__ buffer from the __recv__ socket //-- this is the only way to program the framer for recv: managed_send_buffer::sptr buff = xports.recv->get_send_buff(); @@ -1113,6 +1205,7 @@ x300_impl::both_xports_t x300_impl::make_transport( //ethernet framer has been programmed before we return. mb.zpu_ctrl->peek32(0); } + return xports; } @@ -1299,13 +1392,15 @@ bool x300_impl::is_claimed(wb_iface::sptr iface) } /*********************************************************************** - * MTU detection + * Frame size detection **********************************************************************/ -x300_impl::mtu_result_t x300_impl::determine_mtu(const std::string &addr, const mtu_result_t &user_mtu) +x300_impl::frame_size_t x300_impl::determine_max_frame_size(const std::string &addr, + const frame_size_t &user_frame_size) { - udp_simple::sptr udp = udp_simple::make_connected(addr, BOOST_STRINGIZE(X300_MTU_DETECT_UDP_PORT)); + udp_simple::sptr udp = udp_simple::make_connected(addr, + BOOST_STRINGIZE(X300_MTU_DETECT_UDP_PORT)); - std::vector buffer(std::max(user_mtu.recv_mtu, user_mtu.send_mtu)); + std::vector buffer(std::max(user_frame_size.recv_frame_size, user_frame_size.send_frame_size)); x300_mtu_t *request = reinterpret_cast(&buffer.front()); static const double echo_timeout = 0.020; //20 ms @@ -1317,54 +1412,62 @@ x300_impl::mtu_result_t x300_impl::determine_mtu(const std::string &addr, const if (!(uhd::ntohx(request->flags) & X300_MTU_DETECT_ECHO_REPLY)) throw uhd::not_implemented_error("Holler protocol not implemented"); - size_t min_recv_mtu = sizeof(x300_mtu_t); - size_t max_recv_mtu = user_mtu.recv_mtu; - size_t min_send_mtu = sizeof(x300_mtu_t); - size_t max_send_mtu = user_mtu.send_mtu; + size_t min_recv_frame_size = sizeof(x300_mtu_t); + size_t max_recv_frame_size = user_frame_size.recv_frame_size; + size_t min_send_frame_size = sizeof(x300_mtu_t); + size_t max_send_frame_size = user_frame_size.send_frame_size; - UHD_MSG(status) << "Determining receive MTU ... "; - while (min_recv_mtu < max_recv_mtu) + UHD_MSG(status) << "Determining maximum frame size... "; + while (min_recv_frame_size < max_recv_frame_size) { - size_t test_mtu = (max_recv_mtu/2 + min_recv_mtu/2 + 3) & ~3; + size_t test_frame_size = (max_recv_frame_size/2 + min_recv_frame_size/2 + 3) & ~3; request->flags = uhd::htonx(X300_MTU_DETECT_ECHO_REQUEST); - request->size = uhd::htonx(test_mtu); + request->size = uhd::htonx(test_frame_size); udp->send(boost::asio::buffer(buffer, sizeof(x300_mtu_t))); size_t len = udp->recv(boost::asio::buffer(buffer), echo_timeout); - if (len >= test_mtu) - min_recv_mtu = test_mtu; + if (len >= test_frame_size) + min_recv_frame_size = test_frame_size; else - max_recv_mtu = test_mtu - 4; + max_recv_frame_size = test_frame_size - 4; + } + if(min_recv_frame_size < IP_PROTOCOL_MIN_MTU_SIZE-IP_PROTOCOL_UDP_PLUS_IP_HEADER) { + throw uhd::runtime_error("System receive MTU size is less than the minimum required by the IP protocol."); } - UHD_MSG(status) << min_recv_mtu << std::endl; - UHD_MSG(status) << "Determining send MTU ... "; - while (min_send_mtu < max_send_mtu) + while (min_send_frame_size < max_send_frame_size) { - size_t test_mtu = (max_send_mtu/2 + min_send_mtu/2 + 3) & ~3; + size_t test_frame_size = (max_send_frame_size/2 + min_send_frame_size/2 + 3) & ~3; request->flags = uhd::htonx(X300_MTU_DETECT_ECHO_REQUEST); request->size = uhd::htonx(sizeof(x300_mtu_t)); - udp->send(boost::asio::buffer(buffer, test_mtu)); + udp->send(boost::asio::buffer(buffer, test_frame_size)); size_t len = udp->recv(boost::asio::buffer(buffer), echo_timeout); if (len >= sizeof(x300_mtu_t)) len = uhd::ntohx(request->size); - if (len >= test_mtu) - min_send_mtu = test_mtu; + if (len >= test_frame_size) + min_send_frame_size = test_frame_size; else - max_send_mtu = test_mtu - 4; + max_send_frame_size = test_frame_size - 4; + } + + if(min_send_frame_size < IP_PROTOCOL_MIN_MTU_SIZE-IP_PROTOCOL_UDP_PLUS_IP_HEADER) { + throw uhd::runtime_error("System send MTU size is less than the minimum required by the IP protocol."); } - UHD_MSG(status) << min_send_mtu << std::endl; - mtu_result_t mtu; - mtu.recv_mtu = min_recv_mtu; - mtu.send_mtu = min_send_mtu; - return mtu; + frame_size_t frame_size; + // There are cases when NICs accept oversized packets, in which case we'd falsely + // detect a larger-than-possible frame size. A safe and sensible value is the minimum + // of the recv and send frame sizes. + frame_size.recv_frame_size = std::min(min_recv_frame_size, min_send_frame_size); + frame_size.send_frame_size = std::min(min_recv_frame_size, min_send_frame_size); + UHD_MSG(status) << frame_size.send_frame_size << " bytes." << std::endl; + return frame_size; } /*********************************************************************** diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp index 37f8cc468..1fb3676a0 100644 --- a/host/lib/usrp/x300/x300_impl.hpp +++ b/host/lib/usrp/x300/x300_impl.hpp @@ -58,21 +58,23 @@ static const double X300_BUS_CLOCK_RATE = 175e6; //Hz static const size_t X300_TX_HW_BUFF_SIZE = 0x90000; //576KiB static const size_t X300_TX_FC_RESPONSE_FREQ = 8; //per flow-control window -static const size_t X300_RX_SW_BUFF_SIZE_ETH = 0x2000000; //32MiB For an ~8k MTU any size >32MiB is just wasted buffer space -static const size_t X300_RX_SW_BUFF_SIZE_ETH_MACOS = 0x100000; //1Mib -static const double X300_RX_SW_BUFF_FULL_FACTOR = 0.90; //Buffer should ideally be 90% full. -static const size_t X300_RX_FC_REQUEST_FREQ = 32; //per flow-control window - -static const size_t X300_PCIE_DATA_FRAME_SIZE = 8192; //bytes -static const size_t X300_PCIE_DATA_NUM_FRAMES = 2048; -static const size_t X300_PCIE_MSG_FRAME_SIZE = 256; //bytes -static const size_t X300_PCIE_MSG_NUM_FRAMES = 32; - -static const size_t X300_ETH_DATA_FRAME_SIZE = 8000; //bytes -static const size_t X300_ETH_DATA_NUM_FRAMES = 32; -static const size_t X300_ETH_MSG_FRAME_SIZE = uhd::transport::udp_simple::mtu; //bytes -static const size_t X300_ETH_MSG_NUM_FRAMES = 32; -static const double X300_DEFAULT_SYSREF_RATE = 10e6; +static const size_t X300_RX_SW_BUFF_SIZE_ETH = 0x2000000;//32MiB For an ~8k frame size any size >32MiB is just wasted buffer space +static const size_t X300_RX_SW_BUFF_SIZE_ETH_MACOS = 0x100000; //1Mib +static const double X300_RX_SW_BUFF_FULL_FACTOR = 0.90; //Buffer should ideally be 90% full. +static const size_t X300_RX_FC_REQUEST_FREQ = 32; //per flow-control window + +static const size_t X300_PCIE_DATA_FRAME_SIZE = 8192; //bytes +static const size_t X300_PCIE_DATA_NUM_FRAMES = 2048; +static const size_t X300_PCIE_MSG_FRAME_SIZE = 256; //bytes +static const size_t X300_PCIE_MSG_NUM_FRAMES = 32; + +static const size_t X300_10GE_DATA_FRAME_MAX_SIZE = 8000; //bytes +static const size_t X300_1GE_DATA_FRAME_MAX_SIZE = 1472; //bytes +static const size_t X300_ETH_MSG_FRAME_SIZE = uhd::transport::udp_simple::mtu; //bytes + +static const size_t X300_ETH_MSG_NUM_FRAMES = 32; +static const size_t X300_ETH_DATA_NUM_FRAMES = 32; +static const double X300_DEFAULT_SYSREF_RATE = 10e6; #define X300_RADIO_DEST_PREFIX_TX 0 #define X300_RADIO_DEST_PREFIX_CTRL 1 @@ -207,6 +209,9 @@ private: int clock_control_regs__pps_out_enb; int clock_control_regs__tcxo_enb; int clock_control_regs__gpsdo_pwr; + + //which FPGA image is loaded + std::string loaded_fpga_image; }; std::vector _mb; @@ -243,13 +248,19 @@ private: const uhd::device_addr_t& args, boost::uint32_t& sid); - struct mtu_result_t + struct frame_size_t { - size_t recv_mtu; - size_t send_mtu; + size_t recv_frame_size; + size_t send_frame_size; }; - - mtu_result_t determine_mtu(const std::string &addr, const mtu_result_t &user_mtu); + frame_size_t max_frame_sizes; + + /*! + * Automatically determine the maximum frame size available by sending a UDP packet + * to the device and see which packet sizes actually work. This way, we can take + * switches etc. into account which might live between the device and the host. + */ + frame_size_t determine_max_frame_size(const std::string &addr, const frame_size_t &user_mtu); //////////////////////////////////////////////////////////////////// // -- cgit v1.2.3