// // Copyright 2020 Ettus Research, a National Instruments Brand // // SPDX-License-Identifier: GPL-3.0-or-later // #pragma once #include "x400_dboard_iface.hpp" #include #include #include #include #include #include #define UHD_LOG_SKIP_CFG() \ UHD_LOG_TRACE( \ "RFNOC::DEBUG_DB", "Skipping unsupported debug db config for " << __FUNCTION__); namespace uhd { namespace rfnoc { const static uint16_t EMPTY_DB_PID = 0x0; const static uint16_t DEBUG_DB_PID = 0x4001; const static uint16_t IF_TEST_DBOARD_PID = 0x4006; /*! \brief Implementation of common dboard_iface for IF Test and Debug dboards. */ class debug_dboard_common_impl : public uhd::usrp::x400::x400_dboard_iface { public: using sptr = std::shared_ptr; rf_control::gain_profile_iface::sptr get_tx_gain_profile_api() override { return rf_control::gain_profile_iface::sptr(); } rf_control::gain_profile_iface::sptr get_rx_gain_profile_api() override { return rf_control::gain_profile_iface::sptr(); } bool is_adc_self_cal_supported() override { return false; } uhd::usrp::x400::adc_self_cal_params_t get_adc_self_cal_params(double) override { return { 0.0, 0.0, 0.0, 0.0, }; } size_t get_chan_from_dboard_fe(const std::string& fe, direction_t) const override { if (fe == "0") { return 0; } if (fe == "1") { return 1; } throw uhd::key_error(std::string("[X400] Invalid frontend: ") + fe); } std::string get_dboard_fe_from_chan(size_t chan, direction_t) const override { if (chan == 0) { return "0"; } if (chan == 1) { return "1"; } throw uhd::lookup_error( std::string("[X400] Invalid channel: ") + std::to_string(chan)); } std::vector& get_pwr_mgr(direction_t) override { static std::vector empty_vtr; return empty_vtr; } eeprom_map_t get_db_eeprom() override { return {}; } std::string get_tx_antenna(const size_t) const override { return ""; } std::vector get_tx_antennas(const size_t) const override { return {}; } void set_tx_antenna(const std::string&, const size_t) override{UHD_LOG_SKIP_CFG()} std::string get_rx_antenna(const size_t) const override { return ""; } std::vector get_rx_antennas(const size_t) const override { return {}; } void set_rx_antenna(const std::string&, const size_t) override { UHD_LOG_SKIP_CFG() } double get_tx_frequency(const size_t) override { return 0; } double set_tx_frequency(const double, size_t) override { UHD_LOG_SKIP_CFG() return 0; } void set_tx_tune_args(const device_addr_t&, const size_t) override{UHD_LOG_SKIP_CFG()} freq_range_t get_tx_frequency_range(const size_t) const override { return meta_range_t(0.0, 0.0); } double get_rx_frequency(const size_t) override { return 0; } double set_rx_frequency(const double, const size_t) override { UHD_LOG_SKIP_CFG() return 0; } void set_rx_tune_args(const device_addr_t&, const size_t) override{UHD_LOG_SKIP_CFG()} freq_range_t get_rx_frequency_range(const size_t) const override { return meta_range_t(0.0, 0.0); } std::vector get_tx_gain_names(const size_t) const override { return {}; } gain_range_t get_tx_gain_range(const size_t) const override { return meta_range_t(0.0, 0.0); } gain_range_t get_tx_gain_range(const std::string&, const size_t) const override { return meta_range_t(0.0, 0.0); } double get_tx_gain(const size_t) override { return 0; } double get_tx_gain(const std::string&, const size_t) override { UHD_LOG_SKIP_CFG() return 0; } double set_tx_gain(const double, const size_t) override { UHD_LOG_SKIP_CFG() return 0; } double set_tx_gain(const double, const std::string&, const size_t) override { UHD_LOG_SKIP_CFG() return 0; } std::vector get_rx_gain_names(const size_t) const override { return {}; } gain_range_t get_rx_gain_range(const size_t) const override { UHD_LOG_SKIP_CFG() return meta_range_t(0.0, 0.0); } gain_range_t get_rx_gain_range(const std::string&, const size_t) const override { UHD_LOG_SKIP_CFG() return meta_range_t(0.0, 0.0); } double get_rx_gain(const size_t) override { return 0; } double get_rx_gain(const std::string&, const size_t) override { return 0; } double set_rx_gain(const double, const size_t) override { UHD_LOG_SKIP_CFG() return 0; } double set_rx_gain(const double, const std::string&, const size_t) override { UHD_LOG_SKIP_CFG() return 0; } void set_rx_agc(const bool, const size_t) override{UHD_LOG_SKIP_CFG()} meta_range_t get_tx_bandwidth_range(size_t) const override { return meta_range_t(0.0, 0.0); } double get_tx_bandwidth(const size_t) override { return 0; } double set_tx_bandwidth(const double, const size_t) override { UHD_LOG_SKIP_CFG() return 0; } meta_range_t get_rx_bandwidth_range(size_t) const override { return meta_range_t(0.0, 0.0); } double get_rx_bandwidth(const size_t) override { return 0; } double set_rx_bandwidth(const double, const size_t) override { UHD_LOG_SKIP_CFG() return 0; } std::vector get_rx_lo_names(const size_t) const override { return {}; } std::vector get_rx_lo_sources( const std::string&, const size_t) const override { UHD_LOG_SKIP_CFG() return {}; } freq_range_t get_rx_lo_freq_range(const std::string&, const size_t) const override { return meta_range_t(0.0, 0.0); } void set_rx_lo_source(const std::string&, const std::string&, const size_t) override { UHD_LOG_SKIP_CFG() } const std::string get_rx_lo_source(const std::string&, const size_t) override { return ""; } void set_rx_lo_export_enabled(bool, const std::string&, const size_t) override { UHD_LOG_SKIP_CFG() } bool get_rx_lo_export_enabled(const std::string&, const size_t) override { return false; } double set_rx_lo_freq(double, const std::string&, const size_t) override { UHD_LOG_SKIP_CFG() return 0; } double get_rx_lo_freq(const std::string&, const size_t) override { return 0; } std::vector get_tx_lo_names(const size_t) const override { return {}; } std::vector get_tx_lo_sources(const std::string&, const size_t) const override { return {}; } freq_range_t get_tx_lo_freq_range(const std::string&, const size_t) override { return meta_range_t(0.0, 0.0); } void set_tx_lo_source(const std::string&, const std::string&, const size_t) override { UHD_LOG_SKIP_CFG() } const std::string get_tx_lo_source(const std::string&, const size_t) override { return ""; } void set_tx_lo_export_enabled(const bool, const std::string&, const size_t) override { UHD_LOG_SKIP_CFG() } bool get_tx_lo_export_enabled(const std::string&, const size_t) override { return false; } double set_tx_lo_freq(const double, const std::string&, const size_t) override { UHD_LOG_SKIP_CFG() return 0; } double get_tx_lo_freq(const std::string&, const size_t) override { return 0; } void set_command_time(uhd::time_spec_t, const size_t) override { // nop } }; /*! \brief Implementation of dboard_iface for debug_db. */ class debug_dboard_impl : public debug_dboard_common_impl { // Just an empty class for conveniently organizing class hierarchy. }; /*! \brief Fake dboard implementation for an empty slot */ class empty_slot_dboard_impl : public debug_dboard_common_impl { // Just an empty class for conveniently organizing class hierarchy. }; /*! \brief Implementation of dboard_iface for IF Test dboard. */ class if_test_dboard_impl : public debug_dboard_common_impl { public: /****************************************************************************** * Structors *****************************************************************************/ if_test_dboard_impl(const size_t db_idx, const std::string& rpc_prefix, const std::string& unique_id, std::shared_ptr mb_controller, uhd::property_tree::sptr tree) : _unique_id(unique_id) , _db_idx(db_idx) , _rpc_prefix(rpc_prefix) , _mb_control(mb_controller) , _tree(tree) { RFNOC_LOG_TRACE("Entering " << __FUNCTION__); RFNOC_LOG_TRACE("DB ID: " << _db_idx); UHD_ASSERT_THROW(_mb_control); _rpcc = _mb_control->get_rpc_client(); UHD_ASSERT_THROW(_rpcc); _init_frontend_subtree(); } ~if_test_dboard_impl() { RFNOC_LOG_TRACE(__FUNCTION__); } // The IF Test dboard muxes a single SMA port (for each of RX and TX) like so: // /---> dac0 // /----> dac1 // TX SMA port -- [mux] -----> dac2 // \----> dac3 // // (and similarly with the RX SMA port and the adcs) std::vector get_tx_muxes(void) { return {"DAC0", "DAC1", "DAC2", "DAC3"}; } void set_tx_mux(const std::string& mux) { RFNOC_LOG_TRACE("Setting TX mux to " << mux); _rpcc->notify_with_token( _rpc_prefix + "config_tx_path", _get_tx_path_from_mux(mux)); } std::string get_tx_mux(void) { return _rpcc->request_with_token(_rpc_prefix + "get_tx_path"); } std::vector get_rx_muxes(void) { return {"ADC0", "ADC1", "ADC2", "ADC3"}; } void set_rx_mux(const std::string& mux) { RFNOC_LOG_TRACE("Setting RX mux to " << mux); _rpcc->notify_with_token( _rpc_prefix + "config_rx_path", _get_rx_path_from_mux(mux)); } std::string get_rx_mux(void) { return _rpcc->request_with_token(_rpc_prefix + "get_rx_path"); } eeprom_map_t get_db_eeprom() override { return _rpcc->request_with_token("get_db_eeprom", _db_idx); } private: //! Used by the RFNOC_LOG_* macros. const std::string _unique_id; std::string get_unique_id() const { return _unique_id; } //! Index of this daughterboard const size_t _db_idx; //! Prepended for all dboard RPC calls const std::string _rpc_prefix; //! Reference to the MB controller uhd::rfnoc::mpmd_mb_controller::sptr _mb_control; //! Reference to the RPC client uhd::rpc_client::sptr _rpcc; //! Reference to this block's subtree // // It is mutable because _tree->access<>(..).get() is not const, but we // need to do just that in some const contexts mutable uhd::property_tree::sptr _tree; std::string _get_tx_path_from_mux(const std::string mux) { if (mux == "DAC0") { return "dac0"; } else if (mux == "DAC1") { return "dac1"; } else if (mux == "DAC2") { return "dac2"; } else if (mux == "DAC3") { return "dac3"; } else { throw uhd::value_error( std::string("[RFNOC::IF_TEST_DBOARD] Invalid TX Mux Name: ") + mux); } } std::string _get_rx_path_from_mux(const std::string mux) { if (mux == "ADC0") { return "adc0"; } else if (mux == "ADC1") { return "adc1"; } else if (mux == "ADC2") { return "adc2"; } else if (mux == "ADC3") { return "adc3"; } else { throw uhd::value_error( std::string("[RFNOC::IF_TEST_DBOARD] Invalid RX Mux Name: ") + mux); } } void _init_frontend_subtree() { auto subtree = _tree->subtree(fs_path("dboard")); // DB EEPROM subtree->create("eeprom") .add_coerced_subscriber([](const eeprom_map_t&) { throw uhd::runtime_error("Attempting to update daughterboard eeprom!"); }) .set_publisher([this]() { return get_db_eeprom(); }); static const char IF_TEST_FE_NAME[] = "IF_TEST"; const fs_path tx_fe_path = fs_path("tx_frontends/0"); const fs_path rx_fe_path = fs_path("rx_frontends/0"); RFNOC_LOG_TRACE("Adding non-RFNoC block properties" << " to prop tree path " << tx_fe_path << " and " << rx_fe_path); subtree->create(tx_fe_path / "name").set(IF_TEST_FE_NAME); subtree->create(rx_fe_path / "name").set(IF_TEST_FE_NAME); // TX Mux subtree->create(tx_fe_path / "mux" / "value") .add_coerced_subscriber( [this](const std::string& mux) { this->set_tx_mux(mux); }) .set_publisher([this]() { return this->get_tx_mux(); }); subtree->create>(tx_fe_path / "mux" / "options") .set(get_tx_muxes()) .add_coerced_subscriber([](const std::vector&) { throw uhd::runtime_error("Attempting to update mux options!"); }); // RX Mux subtree->create(rx_fe_path / "mux" / "value") .add_coerced_subscriber( [this](const std::string& mux) { this->set_rx_mux(mux); }) .set_publisher([this]() { return this->get_rx_mux(); }); subtree->create>(rx_fe_path / "mux" / "options") .set(get_rx_muxes()) .add_coerced_subscriber([](const std::vector&) { throw uhd::runtime_error("Attempting to update mux options!"); }); for (auto fe_path : {tx_fe_path, rx_fe_path}) { // Antennas const std::vector antenna_options = {"SMA"}; subtree->create>(fe_path / "antenna" / "options") .set(antenna_options) .add_coerced_subscriber([](const std::vector&) { throw uhd::runtime_error("Attempting to update antenna options!"); }); // Frequency range const uhd::freq_range_t freq_range(0.0, 0.0); subtree->create(fe_path / "freq" / "range") .set(freq_range) .add_coerced_subscriber([](const meta_range_t&) { throw uhd::runtime_error("Attempting to update freq range!"); }); // Gains const uhd::gain_range_t gain_range(0.0, 0.0, 1.0); subtree->create(fe_path / "gains" / "all" / "range") .set(gain_range) .add_coerced_subscriber([](const meta_range_t&) { throw uhd::runtime_error("Attempting to update gain range!"); }); // Connection subtree->create(fe_path / "connection").set("IQ"); } } }; }} // namespace uhd::rfnoc