diff options
author | Martin Braun <martin.braun@ettus.com> | 2017-06-05 16:08:44 -0700 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2017-12-22 15:03:59 -0800 |
commit | 14f0e113d967c9a9098be8cfea7f333c4bf5373f (patch) | |
tree | 79c07977235466a1c033fd925ce3aafd264acdc4 /host/lib/usrp | |
parent | 45012fa48cd82fcc757ca46ce9ee270e670f590f (diff) | |
download | uhd-14f0e113d967c9a9098be8cfea7f333c4bf5373f.tar.gz uhd-14f0e113d967c9a9098be8cfea7f333c4bf5373f.tar.bz2 uhd-14f0e113d967c9a9098be8cfea7f333c4bf5373f.zip |
eiscat: Added more APIs for more fine-grained control
Diffstat (limited to 'host/lib/usrp')
-rw-r--r-- | host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.cpp | 207 | ||||
-rw-r--r-- | host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.hpp | 46 |
2 files changed, 199 insertions, 54 deletions
diff --git a/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.cpp b/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.cpp index 5a7a5907f..ea1e31102 100644 --- a/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.cpp +++ b/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.cpp @@ -69,7 +69,9 @@ UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(eiscat_radio_ctrl) UHD_LOG_TRACE("EISCAT", "eiscat_radio_ctrl_impl::ctor() "); _num_ports = get_output_ports().size(); UHD_LOG_TRACE("EISCAT", "Number of channels: " << _num_ports); - UHD_LOG_TRACE("EISCAT", "Setting tick rate to " << EISCAT_TICK_RATE/1e6 << " MHz"); + UHD_LOG_TRACE("EISCAT", + "Tick rate is " << EISCAT_TICK_RATE/1e6 << " MHz" + ); /**** Configure the radio_ctrl itself ***********************************/ radio_ctrl_impl::set_rate(EISCAT_TICK_RATE); @@ -80,18 +82,72 @@ UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(eiscat_radio_ctrl) radio_ctrl_impl::set_rx_bandwidth(EISCAT_DEFAULT_BANDWIDTH, chan); } - /**** Configure the digital gain controls *******************************/ - for (size_t i = 0; i < EISCAT_NUM_ANTENNAS; i++) { - _tree->access<double>(get_arg_path("gain", i) / "value") - .set_coercer([](double gain){ return std::max(-1.0, std::min(1.0, gain)); }) - .add_coerced_subscriber([this, i](double gain){ - this->set_antenna_gain(i, gain); - }) - .set(EISCAT_DEFAULT_NORM_GAIN) + /**** Set up arg-based control API **************************************/ + // None of these properties are defined in the XML file. Some of them have + // non-Noc-Script-compatible types. + _tree->create<bool>(get_arg_path("sysref", 0) / "value") + .set(true) + .add_coerced_subscriber([this](bool){ + try { + this->send_sysref(); + } catch (const uhd::exception &ex) { + UHD_LOGGER_WARNING("EISCAT") + << "Failed to send SYSREF: " << ex.what(); + throw uhd::runtime_error(str( + boost::format("Failed to send SYSREF: %s") + % ex.what() + )); + } + }) + .set_publisher([](){ return true; }) + ; + _tree->create<bool>(get_arg_path("assert_adcs_deframers", 0) / "value") + .set(true) + .set_publisher([this](){ return this->assert_adcs_deframers(); }) + ; + _tree->create<bool>(get_arg_path("assert_deframer_status", 0) / "value") + .set(true) + .set_publisher([this](){ return this->assert_adcs_deframers(); }) + ; + _tree->create<time_spec_t>(get_arg_path("fir_ctrl_time", 0) / "value") + .add_coerced_subscriber([this](time_spec_t switch_time){ + this->set_fir_ctrl_time(switch_time); + }) + .set(time_spec_t(0.0)) + ; + for (size_t beam = 0; beam < EISCAT_NUM_BEAMS; beam++) { + for (size_t ant = 0; ant < EISCAT_NUM_ANTENNAS; ant++) { + const size_t fir_index = beam * EISCAT_NUM_ANTENNAS + ant; + // These are not in the XML file + _tree->create<int>(get_arg_path("fir_select", fir_index) / "value") + .add_coerced_subscriber([beam, ant, this](const size_t ram_idx){ + UHD_ASSERT_THROW(ram_idx < EISCAT_NUM_FIR_SETS); + this->select_filter( + beam, + ant, + ram_idx, + this->get_arg<time_spec_t>("fir_ctrl_time", 0), + false + ); + }) + ; + } + } + for (size_t fir_set = 0; fir_set < EISCAT_NUM_FIR_SETS; fir_set++) { + _tree->create<std::vector<fir_tap_t>>( + get_arg_path("fir_taps", fir_set) / "value") + .add_coerced_subscriber( + [this, fir_set](const std::vector<fir_tap_t> &taps){ + this->write_fir_taps(fir_set, taps); + } + ) ; } + /**** Add subscribers for our special properties ************************/ + // The difference between this block and the previous that these *are* + // defined in the XML file, and can have defaults set there. _tree->access<int>(get_arg_path("choose_beams", 0) / "value") .add_coerced_subscriber([this](int choose_beams){ this->set_beam_selection(choose_beams); @@ -116,20 +172,29 @@ UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(eiscat_radio_ctrl) }) // No update! ; - /**** Set up legacy compatible properties ******************************/ + /**** Configure the digital gain controls *******************************/ + for (size_t i = 0; i < EISCAT_NUM_ANTENNAS; i++) { + _tree->access<double>(get_arg_path("gain", i) / "value") + .set_coercer([](double gain){ + return std::max(-1.0, std::min(1.0, gain)); + }) + .add_coerced_subscriber([this, i](double gain){ + this->set_antenna_gain(i, gain); + }) + .update() + ; + } + + /**** Set up legacy compatible properties *******************************/ // For use with multi_usrp APIs etc. // For legacy prop tree init: fs_path fe_path = fs_path("dboards") / "A" / "rx_frontends"; - auto antenna_options = std::vector<std::string>{"BF"}; - for (size_t i = 0; i < EISCAT_NUM_ANTENNAS; i++) { - antenna_options.push_back(str(boost::format("Rx%d") % i)); - } - - // The EISCAT dboards have 16 frontends total, but they map to 5 channels - // each through a matrix of FIR filters and summations. UHD will get much - // less confused if we create 5 fake frontends, because that's also the - // number of channels. Since we have no control over the frontends, + // The EISCAT dboards have 16 frontends total, but they map to 10 beams + // each through a matrix of FIR filters and summations, and then only 5 of + // those channels go out through the Noc-Shell. + // UHD will thus get much less confused if we create 5 fake frontends (i.e., + // number of Noc-Block-ports). Since we have no control over the frontends, // nothing is lost here. for (size_t fe_idx = 0; fe_idx < _num_ports; fe_idx++) { _tree->create<std::string>(fe_path / fe_idx / "name") @@ -138,13 +203,6 @@ UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(eiscat_radio_ctrl) _tree->create<std::string>(fe_path / fe_idx / "connection") .set("I") ; - _tree->create<std::string>(fe_path / fe_idx / "antenna" / "value") - .add_coerced_subscriber(boost::bind(&eiscat_radio_ctrl_impl::set_rx_antenna, this, _1, 0)) - .set_publisher(boost::bind(&radio_ctrl_impl::get_rx_antenna, this, 0)) - ; - _tree->create<std::vector<std::string>>(fe_path / fe_idx / "antenna" / "options") - .set(antenna_options) - ; _tree->create<double>(fe_path / fe_idx / "freq" / "value") .set(EISCAT_CENTER_FREQ) //.set_coercer(boost::bind(&eiscat_radio_ctrl_impl::set_rx_frequency, this, _1, 0)) @@ -174,6 +232,27 @@ UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(eiscat_radio_ctrl) ; } + auto antenna_options = std::vector<std::string>{"BF"}; + for (size_t i = 0; i < EISCAT_NUM_ANTENNAS; i++) { + antenna_options.push_back(str(boost::format("Rx%d") % i)); + antenna_options.push_back(str(boost::format("BF%d") % i)); + } + for (size_t beam_idx = 0; beam_idx < _num_ports; beam_idx++) { + _tree->create<std::string>(fe_path / beam_idx / "antenna" / "value") + .set(EISCAT_DEFAULT_ANTENNA) + .add_coerced_subscriber([this, beam_idx](const std::string &name){ + this->set_rx_antenna(name, beam_idx); + }) + .set_publisher([this, beam_idx](){ + return this->get_rx_antenna(beam_idx); + }) + ; + _tree->create<std::vector<std::string>>( + fe_path / beam_idx / "antenna" / "options") + .set(antenna_options) + ; + } + // We can actually stream data to an EISCAT board, so it needs some tx // frontends too: fe_path = fs_path("dboards") / "A" / "tx_frontends"; @@ -187,8 +266,8 @@ UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(eiscat_radio_ctrl) // when we reach this line: UHD_ASSERT_THROW(not _tree->exists("tick_rate")); _tree->create<double>("tick_rate") - //.set_coercer(boost::bind(&eiscat_radio_ctrl_impl::set_rate, this, _1)) .set(EISCAT_TICK_RATE) + .set_coercer(boost::bind(&eiscat_radio_ctrl_impl::set_rate, this, _1)) ; if (not _tree->exists(fs_path("clock_source/value"))) { @@ -396,24 +475,21 @@ void eiscat_radio_ctrl_impl::set_rpc_client( const uhd::device_addr_t &block_args ) { _rpcc = rpcc; - std::function<void()> send_sysref; - if (block_args.has_key("use_mpm_sysref")) { - send_sysref = [rpcc](){ rpcc->notify("db_0_send_sysref"); }; - } else { - send_sysref = [this](){ this->sr_write("SR_SYSREF", 1); }; - } + _block_args = block_args; UHD_LOG_INFO( "EISCAT", "Finalizing dboard initialization using internal PPS" ); send_sysref(); - rpcc->request_with_token<bool>("db_0_init_adcs_and_deframers"); - rpcc->request_with_token<bool>("db_1_init_adcs_and_deframers"); + if (not assert_adcs_deframers()) { + throw uhd::runtime_error("Failed to initialize ADCs and JESD cores!"); + } send_sysref(); std::this_thread::sleep_for(std::chrono::milliseconds(500)); - rpcc->request_with_token<bool>("db_0_check_deframer_status"); - rpcc->request_with_token<bool>("db_1_check_deframer_status"); + if (not assert_deframer_status()) { + throw uhd::runtime_error("Failed to finalize JESD core setup!"); + } } /**************************************************************************** @@ -458,7 +534,8 @@ void eiscat_radio_ctrl_impl::select_filter( const size_t beam_index, const size_t antenna_index, const size_t fir_index, - const uhd::time_spec_t &time_spec + const uhd::time_spec_t &time_spec, + const bool write_time ) { if (antenna_index >= EISCAT_NUM_ANTENNAS) { throw uhd::value_error(str( @@ -468,8 +545,10 @@ void eiscat_radio_ctrl_impl::select_filter( } if (beam_index >= EISCAT_NUM_BEAMS) { throw uhd::value_error(str( - boost::format("Beam index %d out of range. There are %d beam channels in EISCAT.") - % beam_index % EISCAT_NUM_BEAMS + boost::format("Beam index %d out of range. " + "There are %d beam channels in EISCAT.") + % beam_index + % EISCAT_NUM_BEAMS )); } @@ -487,18 +566,35 @@ void eiscat_radio_ctrl_impl::select_filter( ; if (not send_now) { - uint64_t cmd_time_ticks = time_spec.to_ticks(EISCAT_TICK_RATE); UHD_LOG_TRACE("EISCAT", str( - boost::format("Filter selection will be applied at time %f (0x%016X == %u)") + boost::format("Filter selection will be applied at " + "time %f (0x%016X == %u). %s") % time_spec.get_full_secs() - % cmd_time_ticks + % time_spec.to_ticks(EISCAT_TICK_RATE) + % (write_time ? "Writing time regs now." + : "Assuming time regs already up-to-date.") )); - sr_write("SR_FIR_COMMANDS_CTRL_TIME_LO", cmd_time_ticks & 0xFFFFFFFF); - sr_write("SR_FIR_COMMANDS_CTRL_TIME_HI", (cmd_time_ticks >> 32) & 0xFFFFFFFF); + if (write_time) { + set_fir_ctrl_time(time_spec); + } } sr_write("SR_FIR_COMMANDS_RELOAD", reg_value); } +void eiscat_radio_ctrl_impl::set_fir_ctrl_time( + const uhd::time_spec_t &time_spec +) { + const uint64_t cmd_time_ticks = time_spec.to_ticks(EISCAT_TICK_RATE); + sr_write( + "SR_FIR_COMMANDS_CTRL_TIME_LO", + uint32_t(cmd_time_ticks & 0xFFFFFFFF) + ); + sr_write( + "SR_FIR_COMMANDS_CTRL_TIME_HI", + uint32_t((cmd_time_ticks >> 32) & 0xFFFFFFFF) + ); +} + void eiscat_radio_ctrl_impl::set_antenna_gain( const size_t antenna_idx, const double normalized_gain @@ -567,6 +663,15 @@ void eiscat_radio_ctrl_impl::enable_firs(bool enable) configure_beams(new_value); } +void eiscat_radio_ctrl_impl::send_sysref() +{ + if (_block_args.has_key("use_mpm_sysref")) { + _rpcc->notify_with_token("db_0_send_sysref"); + } else { + this->sr_write("SR_SYSREF", 1); + } +} + void eiscat_radio_ctrl_impl::enable_counter(bool enable) { const uint32_t old_value = user_reg_read32(RB_CHOOSE_BEAMS); @@ -577,6 +682,18 @@ void eiscat_radio_ctrl_impl::enable_counter(bool enable) configure_beams(new_value); } +bool eiscat_radio_ctrl_impl::assert_adcs_deframers() +{ + return _rpcc->request_with_token<bool>("db_0_init_adcs_and_deframers") + and _rpcc->request_with_token<bool>("db_1_init_adcs_and_deframers"); +} + +bool eiscat_radio_ctrl_impl::assert_deframer_status() +{ + return _rpcc->request_with_token<bool>("db_0_check_deframer_status") + and _rpcc->request_with_token<bool>("db_1_check_deframer_status"); +} + /**************************************************************************** * Registry ***************************************************************************/ diff --git a/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.hpp b/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.hpp index 556a32611..2da01ed03 100644 --- a/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.hpp +++ b/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.hpp @@ -49,7 +49,7 @@ namespace uhd { * - fir_select (int): Will queue a filter for manipulating a specific * contribution. The value is the filter index in the BRAM. * The port parameter specifies which filter; filters are - * indexed 0...159 using the equation beam_index * 10 + + * indexed 0...159 using the equation beam_index * 16 + * antenna_idx. Example: `set_arg("fir_select", 357, 16)` * will apply filter number 357 to the zeroth antenna for * beam number 1 (i.e. the second beam). Returns the last @@ -67,12 +67,12 @@ namespace uhd { * - choose_beams (int): Configures beam selection (upper, lower, are neighbour * contributions included). See set_beam_selection() for * details. - * - enable_firs (bool): Can be used to disable fir FIR matrix. This routes the - * JESD output directly to the noc_shell. - * - enable_counter (bool): If the feature is available in the given FPGA image, - * setting this to true will disable the JESD core - * output and will input a counter signal (ramp) - * instead. + * - enable_firs (int): Can be used to disable fir FIR matrix. This routes the + * JESD output directly to the noc_shell. + * - enable_counter (int): If the feature is available in the given FPGA image, + * setting this to true will disable the JESD core + * output and will input a counter signal (ramp) + * instead. * - configure_beams (int): Danger, danger: Directly writes the * SR_BEAMS_TO_NEIGHBOR register. Writing this can put * some of the other properties out of sync, because @@ -200,15 +200,32 @@ private: * \param beam_index Beam index * \param antenna_index Antenna index * \param fir_index The index of the FIR filter taps that get applied - * \param time_spec If non-zero, the taps get applied at this time + * \param time_spec If non-zero, the taps get applied at this time. + * Otherwise, they get sent out now. + * \param write_time If false, time will never get written *even if* it is + * non-zero. The assumption is that someone else wrote + * the value previously + * \param write_time If false, time will never get written *even if* it is + * non-zero. The assumption is that someone else wrote + * the value previously */ void select_filter( const size_t beam_index, const size_t antenna_index, const size_t fir_index, - const uhd::time_spec_t &time_spec + const uhd::time_spec_t &time_spec, + const bool write_time=true ); + + /*! Sets the command time for the next call to select_filter() + * + * \param time_spec This value gets written to the FPGA and is applied to + * *all* subsequent filter selections. To request + * immediate application of filters, set this to zero. + */ + void set_fir_ctrl_time(const uhd::time_spec_t &time_spec); + /*! Sets the digital gain on a specific antenna * * \param antenna_idx Antenna for which this gain setting applies @@ -244,6 +261,14 @@ private: */ void enable_counter(bool enable); + void send_sysref(); + + //! Run initialization of ADCs and deframers; returns success status + bool assert_adcs_deframers(); + + //! Run final step of JESD core setup; returns success status + bool assert_deframer_status(); + /*! The number of channels this block outputs * * This is *not* the number of antennas, but the number of streams a single @@ -251,6 +276,9 @@ private: */ size_t _num_ports; + //! Additional block args; gets set during set_rpc_client() + uhd::device_addr_t _block_args; + /*! Reference to the RPC client */ uhd::rpc_client::sptr _rpcc; |