// // Copyright 2014-2016 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // #include "wb_iface_adapter.hpp" #include #include #include #include #include #include #include "radio_ctrl_impl.hpp" #include "../../transport/super_recv_packet_handler.hpp" using namespace uhd; using namespace uhd::rfnoc; static const size_t BYTES_PER_SAMPLE = 4; const std::string radio_ctrl::ALL_LOS = "all"; /**************************************************************************** * Structors and init ***************************************************************************/ // Note: block_ctrl_base must be called before this, but has to be called by // the derived class because of virtual inheritance radio_ctrl_impl::radio_ctrl_impl() : _tick_rate(rfnoc::rate_node_ctrl::RATE_UNDEFINED) { _num_rx_channels = get_output_ports().size(); _num_tx_channels = get_input_ports().size(); _continuous_streaming = std::vector(2, false); for (size_t i = 0; i < _num_rx_channels; i++) { _rx_streamer_active[i] = false; } for (size_t i = 0; i < _num_tx_channels; i++) { _tx_streamer_active[i] = false; } ///////////////////////////////////////////////////////////////////////// // Setup peripherals ///////////////////////////////////////////////////////////////////////// for (size_t i = 0; i < _get_num_radios(); i++) { _register_loopback_self_test(i); _perifs[i].ctrl = boost::make_shared( // poke32 functor boost::bind( static_cast< void (block_ctrl_base::*)(const uint32_t, const uint32_t, const size_t) >(&block_ctrl_base::sr_write), this, _1, _2, i ), // peek32 functor boost::bind( static_cast< uint32_t (block_ctrl_base::*)(const uint32_t, const size_t) >(&block_ctrl_base::user_reg_read32), this, _1, i ), // peek64 functor boost::bind( static_cast< uint64_t (block_ctrl_base::*)(const uint32_t, const size_t) >(&block_ctrl_base::user_reg_read64), this, _1, i ), // get_time functor boost::bind( static_cast< time_spec_t (block_ctrl_base::*)(const size_t) >(&block_ctrl_base::get_command_time), this, i ), // set_time functor boost::bind( static_cast< void (block_ctrl_base::*)(const time_spec_t&, const size_t) >(&block_ctrl_base::set_command_time), this, _1, i ) ); // FIXME there's currently no way to set the underflow policy if (i == 0) { time_core_3000::readback_bases_type time64_rb_bases; time64_rb_bases.rb_now = regs::RB_TIME_NOW; time64_rb_bases.rb_pps = regs::RB_TIME_PPS; _time64 = time_core_3000::make(_perifs[i].ctrl, regs::sr_addr(regs::TIME), time64_rb_bases); this->set_time_now(0.0); } //Reset the RX control engine sr_write(regs::RX_CTRL_HALT, 1, i); } //////////////////////////////////////////////////////////////////// // Register the time keeper //////////////////////////////////////////////////////////////////// if (not _tree->exists(fs_path("time") / "now")) { _tree->create(fs_path("time") / "now") .set_publisher(boost::bind(&radio_ctrl_impl::get_time_now, this)) ; } if (not _tree->exists(fs_path("time") / "pps")) { _tree->create(fs_path("time") / "pps") .set_publisher(boost::bind(&radio_ctrl_impl::get_time_last_pps, this)) ; } if (not _tree->exists(fs_path("time") / "cmd")) { _tree->create(fs_path("time") / "cmd"); } _tree->access(fs_path("time") / "now") .add_coerced_subscriber(boost::bind(&radio_ctrl_impl::set_time_now, this, _1)) ; _tree->access(fs_path("time") / "pps") .add_coerced_subscriber(boost::bind(&radio_ctrl_impl::set_time_next_pps, this, _1)) ; for (size_t i = 0; i < _get_num_radios(); i++) { _tree->access("time/cmd") .add_coerced_subscriber(boost::bind(&block_ctrl_base::set_command_tick_rate, this, boost::ref(_tick_rate), i)) .add_coerced_subscriber(boost::bind(&block_ctrl_base::set_command_time, this, _1, i)) ; } // spp gets created in the XML file _tree->access(get_arg_path("spp") / "value") .add_coerced_subscriber(boost::bind(&radio_ctrl_impl::_update_spp, this, _1)) .update() ; } void radio_ctrl_impl::_register_loopback_self_test(size_t chan) { size_t hash = size_t(time(NULL)); for (size_t i = 0; i < 100; i++) { boost::hash_combine(hash, i); sr_write(regs::TEST, uint32_t(hash), chan); uint32_t result = user_reg_read32(regs::RB_TEST, chan); if (result != uint32_t(hash)) { UHD_LOGGER_ERROR("RFNOC RADIO") << "Register loopback test failed"; UHD_LOGGER_ERROR("RFNOC RADIO") << boost::format("expected: %x result: %x") % uint32_t(hash) % result ; return; // exit on any failure } } UHD_LOGGER_INFO("RFNOC RADIO") << "Register loopback test passed"; } /**************************************************************************** * API calls ***************************************************************************/ double radio_ctrl_impl::set_rate(double rate) { boost::mutex::scoped_lock lock(_mutex); _tick_rate = rate; _time64->set_tick_rate(_tick_rate); _time64->self_test(); set_command_tick_rate(rate); return _tick_rate; } void radio_ctrl_impl::set_tx_antenna(const std::string &ant, const size_t chan) { _tx_antenna[chan] = ant; } void radio_ctrl_impl::set_rx_antenna(const std::string &ant, const size_t chan) { _rx_antenna[chan] = ant; } double radio_ctrl_impl::set_tx_frequency(const double freq, const size_t chan) { return _tx_freq[chan] = freq; } double radio_ctrl_impl::set_rx_frequency(const double freq, const size_t chan) { return _rx_freq[chan] = freq; } double radio_ctrl_impl::set_tx_gain(const double gain, const size_t chan) { return _tx_gain[chan] = gain; } double radio_ctrl_impl::set_rx_gain(const double gain, const size_t chan) { return _rx_gain[chan] = gain; } double radio_ctrl_impl::set_rx_bandwidth(const double bandwidth, const size_t chan) { return _rx_bandwidth[chan] = bandwidth; } void radio_ctrl_impl::set_time_sync(const uhd::time_spec_t &time) { _time64->set_time_sync(time); } double radio_ctrl_impl::get_rate() const { return _tick_rate; } std::string radio_ctrl_impl::get_tx_antenna(const size_t chan) /* const */ { return _tx_antenna[chan]; } std::string radio_ctrl_impl::get_rx_antenna(const size_t chan) /* const */ { return _rx_antenna[chan]; } double radio_ctrl_impl::get_tx_frequency(const size_t chan) /* const */ { return _tx_freq[chan]; } double radio_ctrl_impl::get_rx_frequency(const size_t chan) /* const */ { return _rx_freq[chan]; } double radio_ctrl_impl::get_tx_gain(const size_t chan) /* const */ { return _tx_gain[chan]; } double radio_ctrl_impl::get_rx_gain(const size_t chan) /* const */ { return _rx_gain[chan]; } double radio_ctrl_impl::get_rx_bandwidth(const size_t chan) /* const */ { return _rx_bandwidth[chan]; } std::vector radio_ctrl_impl::get_rx_lo_names(const size_t /* chan */) { return std::vector(); } std::vector radio_ctrl_impl::get_rx_lo_sources(const std::string & /* name */, const size_t /* chan */) { return std::vector(); } freq_range_t radio_ctrl_impl::get_rx_lo_freq_range(const std::string & /* name */, const size_t /* chan */) { return freq_range_t(); } void radio_ctrl_impl::set_rx_lo_source(const std::string & /* src */, const std::string & /* name */, const size_t /* chan */) { throw uhd::not_implemented_error("set_rx_lo_source is not supported on this radio"); } const std::string radio_ctrl_impl::get_rx_lo_source(const std::string & /* name */, const size_t /* chan */) { return "internal"; } void radio_ctrl_impl::set_rx_lo_export_enabled(bool /* enabled */, const std::string & /* name */, const size_t /* chan */) { throw uhd::not_implemented_error("set_rx_lo_export_enabled is not supported on this radio"); } bool radio_ctrl_impl::get_rx_lo_export_enabled(const std::string & /* name */, const size_t /* chan */) { return false; // Not exporting non-existant LOs } double radio_ctrl_impl::set_rx_lo_freq(double /* freq */, const std::string & /* name */, const size_t /* chan */) { throw uhd::not_implemented_error("set_rx_lo_freq is not supported on this radio"); } double radio_ctrl_impl::get_rx_lo_freq(const std::string & /* name */, const size_t /* chan */) { return 0; } /*********************************************************************** * RX Streamer-related methods (from source_block_ctrl_base) **********************************************************************/ //! Pass stream commands to the radio void radio_ctrl_impl::issue_stream_cmd(const uhd::stream_cmd_t &stream_cmd, const size_t chan) { boost::mutex::scoped_lock lock(_mutex); UHD_RFNOC_BLOCK_TRACE() << "radio_ctrl_impl::issue_stream_cmd() " << chan << " " << char(stream_cmd.stream_mode) ; if (not _is_streamer_active(uhd::RX_DIRECTION, chan)) { UHD_RFNOC_BLOCK_TRACE() << "radio_ctrl_impl::issue_stream_cmd() called on inactive channel. Skipping." ; return; } UHD_ASSERT_THROW(stream_cmd.num_samps <= 0x0fffffff); _continuous_streaming[chan] = (stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_START_CONTINUOUS); //setup the mode to instruction flags typedef boost::tuple inst_t; static const uhd::dict mode_to_inst = boost::assign::map_list_of //reload, chain, samps, stop (stream_cmd_t::STREAM_MODE_START_CONTINUOUS, inst_t(true, true, false, false)) (stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS, inst_t(false, false, false, true)) (stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE, inst_t(false, false, true, false)) (stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE, inst_t(false, true, true, false)) ; //setup the instruction flag values bool inst_reload, inst_chain, inst_samps, inst_stop; boost::tie(inst_reload, inst_chain, inst_samps, inst_stop) = mode_to_inst[stream_cmd.stream_mode]; //calculate the word from flags and length uint32_t cmd_word = 0; cmd_word |= uint32_t((stream_cmd.stream_now)? 1 : 0) << 31; cmd_word |= uint32_t((inst_chain)? 1 : 0) << 30; cmd_word |= uint32_t((inst_reload)? 1 : 0) << 29; cmd_word |= uint32_t((inst_stop)? 1 : 0) << 28; cmd_word |= (inst_samps)? stream_cmd.num_samps : ((inst_stop)? 0 : 1); //issue the stream command const uint64_t ticks = (stream_cmd.stream_now)? 0 : stream_cmd.time_spec.to_ticks(get_rate()); sr_write(regs::RX_CTRL_CMD, cmd_word, chan); sr_write(regs::RX_CTRL_TIME_HI, uint32_t(ticks >> 32), chan); sr_write(regs::RX_CTRL_TIME_LO, uint32_t(ticks >> 0), chan); //latches the command } std::vector radio_ctrl_impl::get_active_rx_ports() { std::vector active_rx_ports; typedef std::map map_t; for(map_t::value_type &m: _rx_streamer_active) { if (m.second) { active_rx_ports.push_back(m.first); } } return active_rx_ports; } /*********************************************************************** * Radio controls (radio_ctrl specific) **********************************************************************/ void radio_ctrl_impl::set_rx_streamer(bool active, const size_t port) { UHD_RFNOC_BLOCK_TRACE() << "radio_ctrl_impl::set_rx_streamer() " << port << " -> " << active ; if (port > _num_rx_channels) { throw uhd::value_error(str( boost::format("[%s] Can't (un)register RX streamer on port %d (invalid port)") % unique_id() % port )); } _rx_streamer_active[port] = active; if (not check_radio_config()) { throw std::runtime_error(str( boost::format("[%s]: Invalid radio configuration.") % unique_id() )); } } void radio_ctrl_impl::set_tx_streamer(bool active, const size_t port) { UHD_RFNOC_BLOCK_TRACE() << "radio_ctrl_impl::set_tx_streamer() " << port << " -> " << active ; if (port > _num_tx_channels) { throw uhd::value_error(str( boost::format("[%s] Can't (un)register TX streamer on port %d (invalid port)") % unique_id() % port )); } _tx_streamer_active[port] = active; if (not check_radio_config()) { throw std::runtime_error(str( boost::format("[%s]: Invalid radio configuration.") % unique_id() )); } } // Subscribers to block args: // TODO move to nocscript void radio_ctrl_impl::_update_spp(int spp) { boost::mutex::scoped_lock lock(_mutex); UHD_RFNOC_BLOCK_TRACE() << "radio_ctrl_impl::_update_spp(): Requested spp: " << spp ; if (spp == 0) { spp = DEFAULT_PACKET_SIZE / BYTES_PER_SAMPLE; } UHD_RFNOC_BLOCK_TRACE() << "radio_ctrl_impl::_update_spp(): Setting spp to: " << spp ; for (size_t i = 0; i < _num_rx_channels; i++) { sr_write(regs::RX_CTRL_MAXLEN, uint32_t(spp), i); } } void radio_ctrl_impl::set_time_now(const time_spec_t &time_spec) { _time64->set_time_now(time_spec); } void radio_ctrl_impl::set_time_next_pps(const time_spec_t &time_spec) { _time64->set_time_next_pps(time_spec); } time_spec_t radio_ctrl_impl::get_time_now() { return _time64->get_time_now(); } time_spec_t radio_ctrl_impl::get_time_last_pps() { return _time64->get_time_last_pps(); } void radio_ctrl_impl::set_time_source(const std::string &source) { _tree->access("time_source/value").set(source); } std::string radio_ctrl_impl::get_time_source() { return _tree->access("time_source/value").get(); } std::vector radio_ctrl_impl::get_time_sources() { return _tree->access>("time_source/options").get(); } void radio_ctrl_impl::set_clock_source(const std::string &source) { _tree->access("clock_source/value").set(source); } std::string radio_ctrl_impl::get_clock_source() { return _tree->access("clock_source/value").get(); } std::vector radio_ctrl_impl::get_clock_sources() { return _tree->access>("clock_source/options").get(); }