diff options
-rw-r--r-- | CHANGELOG | 4 | ||||
m--------- | fpga-src | 0 | ||||
-rw-r--r-- | host/CMakeLists.txt | 4 | ||||
-rw-r--r-- | host/docs/rd_testing.dox | 95 | ||||
-rw-r--r-- | host/docs/uhd.dox | 2 | ||||
-rw-r--r-- | host/docs/uhd_semvar.dox | 143 | ||||
-rw-r--r-- | host/lib/experts/expert_container.cpp | 13 | ||||
-rw-r--r-- | host/lib/rfnoc/legacy_compat.cpp | 49 | ||||
-rw-r--r-- | host/lib/usrp/dboard/db_twinrx.cpp | 6 | ||||
-rw-r--r-- | host/lib/usrp/dboard/db_xcvr2450.cpp | 2 | ||||
-rw-r--r-- | host/lib/usrp/dboard/twinrx/twinrx_experts.cpp | 32 | ||||
-rw-r--r-- | host/lib/usrp/dboard/twinrx/twinrx_experts.hpp | 39 | ||||
-rw-r--r-- | host/lib/usrp/x300/x300_radio_ctrl_impl.cpp | 39 | ||||
-rw-r--r-- | host/lib/usrp/x300/x300_radio_ctrl_impl.hpp | 2 |
14 files changed, 402 insertions, 28 deletions
@@ -9,7 +9,7 @@ Change Log for Releases now properly written. ignore-cal-file no longer ignored. Fixed case where too large recv_frame_size settings could break things. Reduced ZPU clock speed (helps FPGA timing). Added area constraints for AXI interconnect. Improved - halfband scaling in rx_frontend. + halfband scaling in rx_frontend. Improved PCIe streaming reliability - B2xx: Clear sequence numbers in idle state. - RFNoC: Nodes disconnect on destruction. Fixed setting of correct bits on sr_error_policy. DDC does no longer clear timed commands on EOB. DUC fixed @@ -17,7 +17,7 @@ Change Log for Releases commands on multiple channels). - UBX: Changed default performance parameters - TwinRX: LEDs properly light up depending on channels. Fixed issue of multiple - (redundant) writes. + (redundant) writes. Simplified API steps for phase synchronization - XCVR: Query dboard clock instead of DAC clock. Helps in X3x0s. - GPS: Fixed message for case when no GPS is present. Fixed multiple GPS-related issues. diff --git a/fpga-src b/fpga-src -Subproject 93808e8d5b182c0eb00aabd4ca1030cf46777ac +Subproject a32119007d7fff31aace083eed94b6a4979e9c0 diff --git a/host/CMakeLists.txt b/host/CMakeLists.txt index 1d3a6f5fc..4947b3fa0 100644 --- a/host/CMakeLists.txt +++ b/host/CMakeLists.txt @@ -338,8 +338,8 @@ UHD_INSTALL(FILES #{{{IMG_SECTION # This section is written automatically by /images/create_imgs_package.py # Any manual changes in here will be overwritten. -SET(UHD_IMAGES_MD5SUM "586c6f48f65ecfaeec3403a8b2780d72") -SET(UHD_IMAGES_DOWNLOAD_SRC "uhd-images_003.010.001.000-rc1.zip") +SET(UHD_IMAGES_MD5SUM "078b883192ef8d68465e4681537c1a8a") +SET(UHD_IMAGES_DOWNLOAD_SRC "uhd-images_003.010.001.000-rc2.zip") #}}} ######################################################################## diff --git a/host/docs/rd_testing.dox b/host/docs/rd_testing.dox new file mode 100644 index 000000000..9c712b084 --- /dev/null +++ b/host/docs/rd_testing.dox @@ -0,0 +1,95 @@ +/*! \page page_rdtesting R&D Testing Procedures + +All defined R&D test procedures are listed here. These tests are meant as a tool +for Ettus R&D to enable faster and more reliable development. Note these tests +are no replacement for manufacturing or production tests, and should not be +treated as such. Instead, they are meant to catch common failure modes during +development. As a result, test definitions are fairly light-weight. + +\section rdtesting_phase Phase Alignment Tests + +tbd + +\section rdtesting_gpsdo GPSDO Tests + +| Test Code | Device | Peripherals | Manual Test Procedure | Automatic Test Procedure | +|------------------|-----------|-------------------|------------------------------|---------------------------| +| GPS-X310-TCXO-v1 | USRP X310 | Jackson Labs TCXO | \ref rdtesting_gpsdo_manual | \ref rdtesting_gpsdo_auto | +| GPS-X310-OCXO-v1 | USRP X310 | Jackson Labs OCXO | \ref rdtesting_gpsdo_manual | \ref rdtesting_gpsdo_auto | +| GPS-X300-TCXO-v1 | USRP X300 | Jackson Labs TCXO | \ref rdtesting_gpsdo_manual | \ref rdtesting_gpsdo_auto | +| GPS-X300-OCXO-v1 | USRP X300 | Jackson Labs OCXO | \ref rdtesting_gpsdo_manual | \ref rdtesting_gpsdo_auto | +| GPS-B200-TCXO-v1 | USRP B200 | Jackson Labs TCXO | \ref rdtesting_gpsdo_manual | \ref rdtesting_gpsdo_auto | +| GPS-B210-TCXO-v1 | USRP B210 | Jackson Labs TCXO | \ref rdtesting_gpsdo_manual | \ref rdtesting_gpsdo_auto | + + +\subsection rdtesting_gpsdo_recommendations Recommendations + +For cursory testing, not all tests within a device family are required (e.g., +only testing the OCXO on any X-Series, and testing the TCXO on any B-Series is +sufficient). + +The following tests are recommended for a minimum test (N stands for the latest +version of this test): +- One of GPS-X310-OCXO-vN or GPS-X300-OCXO-vN +- One of GPS-B210-TCXO-vN or GPS-B200-TCXO-vN + +\subsection rdtesting_gpsdo_requirements Requirements + +All of these tests require a device that is GPSDO capable (e.g., X3x0, B2x0, +N2x0). For those devices that have a separate GPS component (such as the Jackson +Labs GPSDOs), this component is also required (called the "peripheral" in the +following). + +\subsection rdtesting_gpsdo_manual GPSDO: Manual Test Procedure + +1. Without connecting the peripheral to the device, run `uhd_usrp_probe` on the + device and verify that the lack of GPSDO is correctly reported. No warning + or error must be printed. +2. This and the following tests are run with the peripheral connected: Run + `uhd_usrp_probe` and verify that the GPSDO is correctly reported. Power down + the device before connecting the peripheral. The GPSDO must be reported + found, and no error or warning must be printed. +3. Without connecting the GPS antenna input, run `query_gpsdo_sensors`. To pass, + it must report the GPSDO as found, lock to the external reference, but then + report not being locked to GPS. The tool will report a valid GPS time, and + a string such as "GPS and UHD Device time are aligned" in case of success. +4. Connect a GPS antenna to the input and make sure it is in a position to + receive GPS satellite data. Confirm that GPS lock is reported using + `query_gpsdo_sensors` within 20 minutes of connecting the antenna. + The tool `query_gpsdo_sensors` will print a string such as "GPS Locked" in + case of success. + +All of these tests must pass for a 'pass' validation. + +\subsection rdtesting_gpsdo_auto GPSDO: Automatic Test Procedure + +tbd + +\section rdtesting_defining Defining R&D Tests + +Tests can be added any time to define procedures for pass/fail validation. Any +test must include the following: + +- An unambiguous test code. This code consists of three characters that + identify the test, a short description of the devices required, and a version + suffix. Example: `GPS-X310-OCXO-v1` is a GPS-related test, requires an X310 + and an OCXO to run, and is version 1 of this test. +- A manual testing procedure. This must unambiguously define a set of tasks, + and clearly identify whether or not a test has failed or passed. Tests do not + require any other defined outcome other than 'pass' and 'fail'. +- Optional, but highly recommended: An automatic test procedure. This must + consist of a command, or a script, or a set of commands that can be + automatically executed, and that will report a failure condition by means of + returning a non-zero return value. + +Basic understanding of the operation of USRPs by the test operator should be +assumed when authoring test procedures. The descriptions should be as short as +possible to fully describe, unambiguously, how to reach a pass/fail conclusion. + +Test procedures may be updated at any time. If this happens, a new test code +must be generated, with the version number increased. Old test codes are +considered deprecated (if there exists a version 2 of a test, version 1 should +not be run any more). + +*/ +// vim:ft=doxygen: diff --git a/host/docs/uhd.dox b/host/docs/uhd.dox index fcd0a25b0..5474d42e2 100644 --- a/host/docs/uhd.dox +++ b/host/docs/uhd.dox @@ -13,6 +13,8 @@ Some additional pages on developing UHD are also available here: \li \subpage page_converters \li \subpage page_stream \li \subpage page_rtp +\li \subpage page_semver +\li \subpage page_rdtesting */ // vim:ft=doxygen: diff --git a/host/docs/uhd_semvar.dox b/host/docs/uhd_semvar.dox new file mode 100644 index 000000000..2a43b92f1 --- /dev/null +++ b/host/docs/uhd_semvar.dox @@ -0,0 +1,143 @@ +/*! \page page_semver UHD Semantic Versioning + +\section semver_summary Summary + +Given a version number MAJOR.API.ABI.PATCH, increment the: + +1. MAJOR version as necessitated by product generation & architecture. +2. API version when you make incompatible API changes, +3. ABI version when you make incompatible ABI changes, +4. PATCH version when you make backwards-compatible bug fixes. + +Additional labels for other metadata may be appended to the version +number as ``extensions``. + +\section semver_intro Introduction + +Version numbers play an important role in communicating the +compatibility and restrictions of particular releases of software +libraries. By defining formal semantics for the library versioning, +users of the library can immediately and precisely comprehend the +implications of updating that particular library in their application or +system. This information is especially important to developers in +environments where a very high degree of Reliability, Availability, +Serviceability, and Manageability (\ref footnote_rasm "1") are operational +requirements. + +Additionally, without strict versioning semantics, version numbers are +effectively useless for dependency management. By adhering to a +versioning specification, application developers can easily specify +which existing & future versions of the library their software is +compatible with. As a dependency, this makes the library easier to use, +integrate, maintain, and plan around. + +The [*Semantic Versioning*](http://semver.org/) (SemVer) specification +was introduced in 2009 and is now a requirement of the Linux +Foundation's +[*Core Infrastructure Initiative's*](https://www.coreinfrastructure.org/) +[*Best Practices Badge*](https://www.coreinfrastructure.org/programs/badge-program). +The UHD Semantic Versioning (UHD-SemVer) specification is based on SemVer, +but has been modified to better reflect the requirements of the Ettus +Research user-space device driver workflow, project history, and +application ecosystem. + +\section semver_spec UHD-SemVer Specification + +The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, +“SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this +document are to be interpreted as described in [*RFC +2119*](http://tools.ietf.org/html/rfc2119). + +1. Software using Semantic Versioning MUST declare a public API. This + API could be declared in the code itself or exist strictly + in documentation. However it is done, it should be precise + and comprehensive. + +2. A normal version number MUST take the form W.X.Y.Z where W, X, Y, + and Z are non-negative integers, and MUST NOT contain + leading zeroes. W is the major version, X is the API version, Y + is the ABI version, and Z is the patch version. Each element + MUST increase numerically. For instance: 3.1.9.0 -> 3.1.10.0 + -> 3.1.11.0. + +3. Once a versioned package has been released, the contents of that + version MUST NOT be modified. Any modifications MUST be released + as a new version. + +4. Patch version Z (w.x.y.Z) MUST be incremented if only backwards + API-compatible & ABI-compatible changes are introduced. + +5. ABI version Y (w.x.Y.z) MUST be incremented if changes break ABI + compatibility with the previous release. + +6. API version X (w.X.y.z) MUST be incremented if changes break public + API compatibility with the previous release. It MAY include ABI + and patch level changes. It MAY be incremented if substantial new + functionality or improvements are introduced within private code. + ABI and PATCH version MUST be reset to 0 when API version + is incremented. An API breakage is defined as the case where + recompiling software against UHD without modifications may yield + different results. The following cases, for example, are typically + not API-breaking, but are ABI-breaking: adding new public methods, + adding new default parameters to public methods if the default + case is identical to the previous case. + +7. MAJOR version W (W.x.y.z) MAY be incremented if significant + architectural or technological changes are made that warrant + identifying the software as a new generation of product. + +8. A pre-release version MAY be denoted by appending a hyphen and a + series of dot separated identifiers immediately following the + patch version. Identifiers MUST comprise only ASCII alphanumerics + and hyphen [0-9A-Za-z-]. Identifiers MUST NOT be empty. Numeric + identifiers MUST NOT include leading zeroes. Pre-release versions + have a lower precedence than the associated normal version. A + pre-release version indicates that the version is unstable and + might not satisfy the intended compatibility requirements as + denoted by its associated normal version. Examples: 3.1.0.0-alpha, + 3.1.0.0-alpha.1, 3.1.0.0-0.3.7, 3.1.0.0-x.7.z.92. + +9. Build metadata MAY be denoted by appending a plus sign and a series + of dot separated identifiers immediately following the patch or + pre-release version. Identifiers MUST comprise only ASCII + alphanumerics and hyphen [0-9A-Za-z-]. Identifiers MUST NOT + be empty. Build metadata SHOULD be ignored when determining + version precedence. Thus two versions that differ only in the + build metadata, have the same precedence. Examples: + 3.1.0.0-alpha+001, 3.1.0.0+20130313144700, 3.1.0.0-beta+exp.sha.5114f85. + +10. Precedence refers to how versions are compared to each other + when ordered. Precedence MUST be calculated by separating the + version into major, API, ABI, patch and pre-release + identifiers in that order (Build metadata does not figure + into precedence). Precedence is determined by the first difference + when comparing each of these identifiers from left to right as + follows: Major, API, ABI, and patch versions are always + compared numerically. Example: 3.1.0.0 < 3.2.0.0 < 3.2.1.0 + < 3.2.1.1. When major, API, ABI, and patch are equal, a + pre-release version has lower precedence than a normal version. + Example: 3.1.0.0-alpha < 3.1.0.0. Precedence for two + pre-release versions with the same major, API ABI, and patch + version MUST be determined by comparing each dot separated + identifier from left to right until a difference is found as + follows: identifiers consisting of only digits are compared + numerically and identifiers with letters or hyphens are compared + lexically in ASCII sort order. Numeric identifiers always have + lower precedence than non-numeric identifiers. A larger set of + pre-release fields has a higher precedence than a smaller set, if + all of the preceding identifiers are equal. Example: 3.1.0.0-alpha + < 3.1.0.0-alpha.1 < 3.1.0.0-alpha.beta < 3.1.0.0-beta + < 4.1.0.0-beta.2 < 3.1.0.0-beta.11 < 3.1.0.0-rc.1 < 3.1.0.0. + +\section semver_license License + +Based on [*SemVer 2.0.0*](http://semver.org/). +[*Creative Commons - CC BY 3.0*](http://creativecommons.org/licenses/by/3.0/) + +\anchor footnote_rasm 1. For more information on enterprise RASM, see +[*Wikipedia’s article on RAS*](https://en.wikipedia.org/wiki/Reliability,_availability_and_serviceability_(computing)) +and [*National Instrument’s whitepaper*](http://www.ni.com/white-paper/14410/en/) +[*on RASM*](http://www.ni.com/white-paper/14410/en/). + +*/ +// vim:ft=doxygen: diff --git a/host/lib/experts/expert_container.cpp b/host/lib/experts/expert_container.cpp index edfc2ebe3..78c783790 100644 --- a/host/lib/experts/expert_container.cpp +++ b/host/lib/experts/expert_container.cpp @@ -92,23 +92,28 @@ public: boost::lock_guard<boost::recursive_mutex> resolve_lock(_resolve_mutex); boost::lock_guard<boost::mutex> lock(_mutex); EX_LOG(0, str(boost::format("resolve_all(%s)") % (force?"force":""))); + // Do a full resolve of the graph _resolve_helper("", "", force); } - void resolve_from(const std::string& node_name) + void resolve_from(const std::string&) { boost::lock_guard<boost::recursive_mutex> resolve_lock(_resolve_mutex); boost::lock_guard<boost::mutex> lock(_mutex); EX_LOG(0, str(boost::format("resolve_from(%s)") % node_name)); - _resolve_helper(node_name, "", false); + // Do a full resolve of the graph + // Not optimizing the traversal using node_name to reduce experts complexity + _resolve_helper("", "", false); } - void resolve_to(const std::string& node_name) + void resolve_to(const std::string&) { boost::lock_guard<boost::recursive_mutex> resolve_lock(_resolve_mutex); boost::lock_guard<boost::mutex> lock(_mutex); EX_LOG(0, str(boost::format("resolve_to(%s)") % node_name)); - _resolve_helper("", node_name, false); + // Do a full resolve of the graph + // Not optimizing the traversal using node_name to reduce experts complexity + _resolve_helper("", "", false); } dag_vertex_t& retrieve(const std::string& name) const diff --git a/host/lib/rfnoc/legacy_compat.cpp b/host/lib/rfnoc/legacy_compat.cpp index 2c8e10c4a..20553062f 100644 --- a/host/lib/rfnoc/legacy_compat.cpp +++ b/host/lib/rfnoc/legacy_compat.cpp @@ -16,6 +16,7 @@ // #include "legacy_compat.hpp" +#include "../usrp/device3/device3_impl.hpp" #include <uhd/property_tree.hpp> #include <uhd/rfnoc/radio_ctrl.hpp> #include <uhd/rfnoc/ddc_block_ctrl.hpp> @@ -171,6 +172,13 @@ public: /************************************************************************ * API Calls ***********************************************************************/ + inline uhd::fs_path rx_dsp_root(const size_t mboard_idx, const size_t dsp_index, const size_t port_index) + { + return mb_root(mboard_idx) / "xbar" / + str(boost::format("%s_%d") % DDC_BLOCK_NAME % dsp_index) / + "legacy_api" / port_index; + } + uhd::fs_path rx_dsp_root(const size_t mboard_idx, const size_t chan) { // The DSP index is the same as the radio index @@ -181,8 +189,13 @@ public: return mb_root(mboard_idx) / "rx_dsps" / dsp_index / port_index; } + return rx_dsp_root(mboard_idx, dsp_index, port_index); + } + + inline uhd::fs_path tx_dsp_root(const size_t mboard_idx, const size_t dsp_index, const size_t port_index) + { return mb_root(mboard_idx) / "xbar" / - str(boost::format("%s_%d") % DDC_BLOCK_NAME % dsp_index) / + str(boost::format("%s_%d") % DUC_BLOCK_NAME % dsp_index) / "legacy_api" / port_index; } @@ -196,9 +209,7 @@ public: return mb_root(mboard_idx) / "tx_dsps" / dsp_index / port_index; } - return mb_root(mboard_idx) / "xbar" / - str(boost::format("%s_%d") % DUC_BLOCK_NAME % dsp_index) / - "legacy_api" / port_index; + return tx_dsp_root(mboard_idx, dsp_index, port_index); } uhd::fs_path rx_fe_root(const size_t mboard_idx, const size_t chan) @@ -511,6 +522,20 @@ private: // methods ; } } + } else { + for (size_t dsp_idx = 0; dsp_idx < _num_radios_per_board; dsp_idx++) { + for (size_t chan = 0; chan < _num_rx_chans_per_radio; chan++) { + _tree->access<double>(rx_dsp_root(mboard_idx, dsp_idx, chan) / "rate/value") + .add_coerced_subscriber( + boost::bind( + &uhd::usrp::device3_impl::update_rx_streamers, + boost::dynamic_pointer_cast<uhd::usrp::device3_impl>(_device), + _1 + ) + ) + ; + } + } } if (not _has_ducs) { for (size_t radio_idx = 0; radio_idx < _num_radios_per_board; radio_idx++) { @@ -544,7 +569,21 @@ private: // methods ; } } - } + } else { + for (size_t dsp_idx = 0; dsp_idx < _num_radios_per_board; dsp_idx++) { + for (size_t chan = 0; chan < _num_tx_chans_per_radio; chan++) { + _tree->access<double>(tx_dsp_root(mboard_idx, dsp_idx, chan) / "rate/value") + .add_coerced_subscriber( + boost::bind( + &uhd::usrp::device3_impl::update_tx_streamers, + boost::dynamic_pointer_cast<uhd::usrp::device3_impl>(_device), + _1 + ) + ) + ; + } + } + } /* if not _has_ducs */ } } diff --git a/host/lib/usrp/dboard/db_twinrx.cpp b/host/lib/usrp/dboard/db_twinrx.cpp index e960f134f..477412de0 100644 --- a/host/lib/usrp/dboard/db_twinrx.cpp +++ b/host/lib/usrp/dboard/db_twinrx.cpp @@ -75,6 +75,11 @@ public: get_rx_subtree()->create<meta_range_t>("bandwidth/range") .set(freq_range_t(BW, BW)); + // Command Time + expert_factory::add_data_node<time_spec_t>(_expert, prepend_ch("time/rx_frontend", _ch_name), time_spec_t(0.0)); + expert_factory::add_prop_node<time_spec_t>(_expert, get_rx_subtree(), + "time/cmd", prepend_ch("time/cmd", _ch_name), time_spec_t(0.0)); + //Frequency Specific get_rx_subtree()->create<meta_range_t>("freq/range") .set(freq_range_t(10e6, 6.0e9)); @@ -263,6 +268,7 @@ public: expert_factory::add_worker_node<twinrx_freq_path_expert>(_expert, _expert->node_retriever(), fe); expert_factory::add_worker_node<twinrx_freq_coercion_expert>(_expert, _expert->node_retriever(), fe); expert_factory::add_worker_node<twinrx_chan_gain_expert>(_expert, _expert->node_retriever(), fe); + expert_factory::add_worker_node<twinrx_scheduling_expert>(_expert, _expert->node_retriever(), fe); expert_factory::add_worker_node<twinrx_nyquist_expert>(_expert, _expert->node_retriever(), fe, _db_iface); } diff --git a/host/lib/usrp/dboard/db_xcvr2450.cpp b/host/lib/usrp/dboard/db_xcvr2450.cpp index aa0bae8b8..0f839b03e 100644 --- a/host/lib/usrp/dboard/db_xcvr2450.cpp +++ b/host/lib/usrp/dboard/db_xcvr2450.cpp @@ -372,7 +372,7 @@ double xcvr2450::set_lo_freq_core(double target_freq){ //variables used in the calculation below double scaler = xcvr2450::is_highband(target_freq)? (4.0/5.0) : (4.0/3.0); double ref_freq = this->get_iface()->get_clock_rate(dboard_iface::UNIT_TX); - int R, intdiv, fracdiv; + int R, intdiv = 131, fracdiv = 0; //loop through values until we get a match for(_ad9515div = 2; _ad9515div <= 3; _ad9515div++){ diff --git a/host/lib/usrp/dboard/twinrx/twinrx_experts.cpp b/host/lib/usrp/dboard/twinrx/twinrx_experts.cpp index ddaa4211e..3b41972da 100644 --- a/host/lib/usrp/dboard/twinrx/twinrx_experts.cpp +++ b/host/lib/usrp/dboard/twinrx/twinrx_experts.cpp @@ -30,6 +30,17 @@ using namespace uhd::math; using namespace uhd::usrp::dboard::twinrx; /*!--------------------------------------------------------- + * twinrx_scheduling_expert::resolve + * --------------------------------------------------------- + */ +void twinrx_scheduling_expert::resolve() +{ + // Currently a straight pass-through. To be expanded as needed + // when more advanced scheduling is needed + _rx_frontend_time = _command_time; +} + +/*!--------------------------------------------------------- * twinrx_freq_path_expert::resolve * --------------------------------------------------------- */ @@ -222,6 +233,19 @@ void twinrx_freq_coercion_expert::resolve() */ void twinrx_nyquist_expert::resolve() { + // Do not execute when clear_command_time is called. + // This is a transition of the command time from non-zero to zero. + if (_rx_frontend_time == time_spec_t(0.0) and _cached_cmd_time != time_spec_t(0.0)) { + _cached_cmd_time = _rx_frontend_time; + return; + } + + // Do not execute twice for the same command time unless untimed + if (_rx_frontend_time == _cached_cmd_time and _rx_frontend_time != time_spec_t(0.0)) { + return; + } + _cached_cmd_time = _rx_frontend_time; + double if_freq_sign = 1.0; if (_lo1_inj_side == INJ_HIGH_SIDE) if_freq_sign *= -1.0; if (_lo2_inj_side == INJ_HIGH_SIDE) if_freq_sign *= -1.0; @@ -570,10 +594,6 @@ void twinrx_settings_expert::_resolve_lox_freq( ch0_freq_c = _set_lox_synth_freq(lo_stage, twinrx_ctrl::CH2, ch0_freq_d); } else if (synth0_mapping == MAPPING_SHARED or synth1_mapping == MAPPING_SHARED) { // If any synthesizer is being shared then we are not in hopping mode - if (rf_freq_ppm_t(ch0_freq_d) != ch1_freq_d) { - UHD_MSG(warning) << - "Incompatible RF/LO frequencies for LO sharing. Using Ch0 settings for both channels."; - } twinrx_ctrl::channel_t ch = (synth0_mapping == MAPPING_SHARED) ? twinrx_ctrl::CH1 : twinrx_ctrl::CH2; ch0_freq_c = _set_lox_synth_freq(lo_stage, ch, ch0_freq_d); ch1_freq_c = ch0_freq_c; @@ -597,10 +617,6 @@ void twinrx_settings_expert::_resolve_lox_freq( ch1_freq_c = _set_lox_synth_freq(lo_stage, twinrx_ctrl::CH2, ch1_freq_d); } else if (synth0_mapping == MAPPING_SHARED or synth1_mapping == MAPPING_SHARED) { // If any synthesizer is being shared then we are not in hopping mode - if (rf_freq_ppm_t(ch0_freq_d) != ch1_freq_d) { - UHD_MSG(warning) << - "Incompatible RF/LO frequencies for LO sharing. Using Ch0 settings for both channels."; - } twinrx_ctrl::channel_t ch = (synth0_mapping == MAPPING_SHARED) ? twinrx_ctrl::CH1 : twinrx_ctrl::CH2; ch0_freq_c = _set_lox_synth_freq(lo_stage, ch, ch0_freq_d); ch1_freq_c = ch0_freq_c; diff --git a/host/lib/usrp/dboard/twinrx/twinrx_experts.hpp b/host/lib/usrp/dboard/twinrx/twinrx_experts.hpp index 1617c9c80..9c83dbfa8 100644 --- a/host/lib/usrp/dboard/twinrx/twinrx_experts.hpp +++ b/host/lib/usrp/dboard/twinrx/twinrx_experts.hpp @@ -52,6 +52,38 @@ static const std::string lo_stage_str(lo_stage_t stage, bool lower = false) { return prefix + ((stage == STAGE_LO1) ? "1" : "2"); } + +/*!--------------------------------------------------------- + * twinrx_scheduling_expert + * + * This expert is responsible for scheduling time sensitive actions + * in other experts. It responds to changes in the command time and + * selectively causes experts to run in order to ensure a synchronized + * system. + * + * --------------------------------------------------------- + */ +class twinrx_scheduling_expert : public experts::worker_node_t { +public: + twinrx_scheduling_expert(const experts::node_retriever_t& db, std::string ch) + : experts::worker_node_t(prepend_ch("twinrx_scheduling_expert", ch)), + _command_time (db, prepend_ch("time/cmd", ch)), + _rx_frontend_time (db, prepend_ch("time/rx_frontend", ch)) + { + bind_accessor(_command_time); + bind_accessor(_rx_frontend_time); + } + +private: + virtual void resolve(); + + //Inputs + experts::data_reader_t<time_spec_t> _command_time; + + //Outputs + experts::data_writer_t<time_spec_t> _rx_frontend_time; +}; + /*!--------------------------------------------------------- * twinrx_freq_path_expert * @@ -271,6 +303,7 @@ public: _if_freq_d (db, prepend_ch("if_freq/desired", ch)), _lo1_inj_side (db, prepend_ch("ch/LO1/inj_side", ch)), _lo2_inj_side (db, prepend_ch("ch/LO2/inj_side", ch)), + _rx_frontend_time (db, prepend_ch("time/rx_frontend", ch)), _if_freq_c (db, prepend_ch("if_freq/coerced", ch)), _db_iface (db_iface) { @@ -280,6 +313,7 @@ public: bind_accessor(_lo1_inj_side); bind_accessor(_lo2_inj_side); bind_accessor(_if_freq_c); + bind_accessor(_rx_frontend_time); } private: @@ -293,9 +327,14 @@ private: experts::data_reader_t<double> _if_freq_d; experts::data_reader_t<lo_inj_side_t> _lo1_inj_side; experts::data_reader_t<lo_inj_side_t> _lo2_inj_side; + experts::data_reader_t<time_spec_t> _rx_frontend_time; + //Outputs experts::data_writer_t<double> _if_freq_c; dboard_iface::sptr _db_iface; + + //Misc + time_spec_t _cached_cmd_time; }; /*!--------------------------------------------------------- diff --git a/host/lib/usrp/x300/x300_radio_ctrl_impl.cpp b/host/lib/usrp/x300/x300_radio_ctrl_impl.cpp index f14ea3fd9..991b97696 100644 --- a/host/lib/usrp/x300/x300_radio_ctrl_impl.cpp +++ b/host/lib/usrp/x300/x300_radio_ctrl_impl.cpp @@ -120,6 +120,15 @@ UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(x300_radio_ctrl) _tx_fe_map[i].core->set_dc_offset(tx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE); _tx_fe_map[i].core->set_iq_balance(tx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE); _tx_fe_map[i].core->populate_subtree(_tree->subtree(_root_path / "tx_fe_corrections" / i)); + + //////////////////////////////////////////////////////////////// + // Bind the daughterboard command time to the motherboard level property + //////////////////////////////////////////////////////////////// + + if (_tree->exists(fs_path("time") / "cmd")) { + _tree->access<time_spec_t>(fs_path("time") / "cmd") + .add_coerced_subscriber(boost::bind(&x300_radio_ctrl_impl::set_fe_cmd_time, this, _1, i)); + } } //////////////////////////////////////////////////////////////// @@ -163,6 +172,13 @@ double x300_radio_ctrl_impl::set_rate(double /* rate */) return get_rate(); } +time_spec_t x300_radio_ctrl_impl::set_fe_cmd_time(const time_spec_t &time, const size_t chan) +{ + return _tree->access<time_spec_t>( + fs_path("dboards" / _radio_slot / "rx_frontends" / _rx_fe_map.at(chan).db_fe_name / "time" / "cmd") + ).set(time).get(); +} + void x300_radio_ctrl_impl::set_tx_antenna(const std::string &ant, const size_t chan) { _tree->access<std::string>( @@ -399,14 +415,25 @@ void x300_radio_ctrl_impl::setup_radio( //bind frontend corrections to the dboard freq props const fs_path db_tx_fe_path = db_path / "tx_frontends"; - BOOST_FOREACH(const std::string &name, _tree->list(db_tx_fe_path)) { - _tree->access<double>(db_tx_fe_path / name / "freq" / "value") - .add_coerced_subscriber(boost::bind(&x300_radio_ctrl_impl::set_tx_fe_corrections, this, db_path, _root_path / "tx_fe_corrections" / name, _1)); + if (not _tx_fe_map.empty()) { + for (size_t i = 0; i < _get_num_radios(); i++) { + if (_tree->exists(db_tx_fe_path / _tx_fe_map[i].db_fe_name / "freq" / "value")) { + _tree->access<double>(db_tx_fe_path / _tx_fe_map[i].db_fe_name / "freq" / "value") + .add_coerced_subscriber(boost::bind(&x300_radio_ctrl_impl::set_tx_fe_corrections, this, db_path, + _root_path / "tx_fe_corrections" / _tx_fe_map[i].db_fe_name, _1)); + } + } } const fs_path db_rx_fe_path = db_path / "rx_frontends"; - BOOST_FOREACH(const std::string &name, _tree->list(db_rx_fe_path)) { - _tree->access<double>(db_rx_fe_path / name / "freq" / "value") - .add_coerced_subscriber(boost::bind(&x300_radio_ctrl_impl::set_rx_fe_corrections, this, db_path, _root_path / "rx_fe_corrections" / name,_1)); + if (not _rx_fe_map.empty()) { + for (size_t i = 0; i < _get_num_radios(); i++) { + if (_tree->exists(db_rx_fe_path / _tx_fe_map[i].db_fe_name / "freq" / "value")) { + _tree->access<double>(db_rx_fe_path / _tx_fe_map[i].db_fe_name / "freq" / "value") + .add_coerced_subscriber(boost::bind(&x300_radio_ctrl_impl::set_rx_fe_corrections, this, db_path, + _root_path / "rx_fe_corrections" / _tx_fe_map[i].db_fe_name, + _1)); + } + } } //////////////////////////////////////////////////////////////// diff --git a/host/lib/usrp/x300/x300_radio_ctrl_impl.hpp b/host/lib/usrp/x300/x300_radio_ctrl_impl.hpp index 65d4443eb..5b9d47841 100644 --- a/host/lib/usrp/x300/x300_radio_ctrl_impl.hpp +++ b/host/lib/usrp/x300/x300_radio_ctrl_impl.hpp @@ -161,6 +161,8 @@ private: void set_rx_fe_corrections(const uhd::fs_path &db_path, const uhd::fs_path &rx_fe_corr_path, const double lo_freq); void set_tx_fe_corrections(const uhd::fs_path &db_path, const uhd::fs_path &tx_fe_corr_path, const double lo_freq); + time_spec_t set_fe_cmd_time(const time_spec_t &time, const size_t chan); + private: // members enum radio_connection_t { PRIMARY, SECONDARY }; |