diff options
Diffstat (limited to 'host/lib/usrp/usrp1/io_impl.cpp')
-rw-r--r-- | host/lib/usrp/usrp1/io_impl.cpp | 293 |
1 files changed, 223 insertions, 70 deletions
diff --git a/host/lib/usrp/usrp1/io_impl.cpp b/host/lib/usrp/usrp1/io_impl.cpp index 90ed17cd8..b596bbd04 100644 --- a/host/lib/usrp/usrp1/io_impl.cpp +++ b/host/lib/usrp/usrp1/io_impl.cpp @@ -15,26 +15,26 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // +#include "validate_subdev_spec.hpp" #define SRPH_DONT_CHECK_SEQUENCE #include "../../transport/super_recv_packet_handler.hpp" #include "../../transport/super_send_packet_handler.hpp" +#include "usrp1_calc_mux.hpp" +#include "fpga_regs_standard.h" #include "usrp_commands.h" #include "usrp1_impl.hpp" #include <uhd/utils/msg.hpp> #include <uhd/utils/safe_call.hpp> -#include <uhd/utils/thread_priority.hpp> -#include <uhd/usrp/dsp_props.hpp> #include <uhd/transport/bounded_buffer.hpp> +#include <boost/math/special_functions/sign.hpp> +#include <boost/math/special_functions/round.hpp> +#include <boost/thread/thread.hpp> #include <boost/bind.hpp> #include <boost/format.hpp> -#include <boost/asio.hpp> -#include <boost/bind.hpp> -#include <boost/thread.hpp> using namespace uhd; using namespace uhd::usrp; using namespace uhd::transport; -namespace asio = boost::asio; static const size_t alignment_padding = 512; @@ -124,8 +124,6 @@ static void usrp1_bs_vrt_unpacker( struct usrp1_impl::io_impl{ io_impl(zero_copy_if::sptr data_transport): data_transport(data_transport), - underflow_poll_samp_count(0), - overflow_poll_samp_count(0), curr_buff(offset_send_buffer(data_transport->get_send_buff())), omsb(boost::bind(&usrp1_impl::io_impl::commit_send_buff, this, _1, _2, _3)) { @@ -133,6 +131,8 @@ struct usrp1_impl::io_impl{ } ~io_impl(void){ + vandal_tribe.interrupt_all(); + vandal_tribe.join_all(); UHD_SAFE_CALL(flush_send_buff();) } @@ -142,10 +142,6 @@ struct usrp1_impl::io_impl{ sph::recv_packet_handler recv_handler; sph::send_packet_handler send_handler; - //state management for overflow and underflow - size_t underflow_poll_samp_count; - size_t overflow_poll_samp_count; - //wrapper around the actual send buffer interface //all of this to ensure only aligned lengths are committed //NOTE: you must commit before getting a new buffer @@ -162,6 +158,9 @@ struct usrp1_impl::io_impl{ //make a new managed buffer with the offset buffs return omsb.get_new(curr_buff, next_buff); } + + boost::thread_group vandal_tribe; + boost::system_time last_send_time; }; /*! @@ -230,51 +229,223 @@ void usrp1_impl::io_init(void){ _io_impl = UHD_PIMPL_MAKE(io_impl, (_data_transport)); - _soft_time_ctrl = soft_time_ctrl::make( - boost::bind(&usrp1_impl::rx_stream_on_off, this, _1) - ); - - this->enable_tx(true); //always enabled - rx_stream_on_off(false); - _io_impl->flush_send_buff(); - - //update mapping here since it didnt b4 when io init not called first - update_xport_channel_mapping(); -} - -void usrp1_impl::update_xport_channel_mapping(void){ - if (_io_impl.get() == NULL) return; //not inited yet + //create a new vandal thread to poll xerflow conditions + boost::barrier spawn_barrier(2); + _io_impl->vandal_tribe.create_thread(boost::bind( + &usrp1_impl::vandal_conquest_loop, + this, boost::ref(spawn_barrier) + )); + spawn_barrier.wait(); - //set all of the relevant properties on the handler - boost::mutex::scoped_lock recv_lock = _io_impl->recv_handler.get_scoped_lock(); + //init some handler stuff + _io_impl->recv_handler.set_tick_rate(_master_clock_rate); _io_impl->recv_handler.set_vrt_unpacker(&usrp1_bs_vrt_unpacker); - _io_impl->recv_handler.set_tick_rate(_clock_ctrl->get_master_clock_freq()); - _io_impl->recv_handler.set_samp_rate(_rx_dsp_proxies[_rx_dsp_proxies.keys().at(0)]->get_link()[DSP_PROP_HOST_RATE].as<double>()); _io_impl->recv_handler.set_xport_chan_get_buff(0, boost::bind( &uhd::transport::zero_copy_if::get_recv_buff, _io_impl->data_transport, _1 )); - _io_impl->recv_handler.set_converter(_rx_otw_type, _rx_subdev_spec.size()); - - //set all of the relevant properties on the handler - boost::mutex::scoped_lock send_lock = _io_impl->send_handler.get_scoped_lock(); + _io_impl->send_handler.set_tick_rate(_master_clock_rate); _io_impl->send_handler.set_vrt_packer(&usrp1_bs_vrt_packer); - _io_impl->send_handler.set_tick_rate(_clock_ctrl->get_master_clock_freq()); - _io_impl->send_handler.set_samp_rate(_tx_dsp_proxies[_tx_dsp_proxies.keys().at(0)]->get_link()[DSP_PROP_HOST_RATE].as<double>()); _io_impl->send_handler.set_xport_chan_get_buff(0, boost::bind( &usrp1_impl::io_impl::get_send_buff, _io_impl.get(), _1 )); - _io_impl->send_handler.set_converter(_tx_otw_type, _tx_subdev_spec.size()); - _io_impl->send_handler.set_max_samples_per_packet(get_max_send_samps_per_packet()); + + //init as disabled, then call the real function (uses restore) + this->enable_rx(false); + this->enable_tx(false); + rx_stream_on_off(false); + tx_stream_on_off(false); + _io_impl->flush_send_buff(); } void usrp1_impl::rx_stream_on_off(bool enb){ - this->enable_rx(enb); + this->restore_rx(enb); //drain any junk in the receive transport after stop streaming command while(not enb and _data_transport->get_recv_buff().get() != NULL){ /* NOP */ } } +void usrp1_impl::tx_stream_on_off(bool enb){ + _io_impl->last_send_time = boost::get_system_time(); + if (_tx_enabled and not enb) _io_impl->flush_send_buff(); + this->restore_tx(enb); +} + +/*! + * Casually poll the overflow and underflow registers. + * On an underflow, push an async message into the queue and print. + * On an overflow, interleave an inline message into recv and print. + * This procedure creates "soft" inline and async user messages. + */ +void usrp1_impl::vandal_conquest_loop(boost::barrier &spawn_barrier){ + spawn_barrier.wait(); + + //initialize the async metadata + async_metadata_t async_metadata; + async_metadata.channel = 0; + async_metadata.has_time_spec = true; + async_metadata.event_code = async_metadata_t::EVENT_CODE_UNDERFLOW; + + //initialize the inline metadata + rx_metadata_t inline_metadata; + inline_metadata.has_time_spec = true; + inline_metadata.error_code = rx_metadata_t::ERROR_CODE_OVERFLOW; + + //start the polling loop... + try{ while (not boost::this_thread::interruption_requested()){ + boost::uint8_t underflow = 0, overflow = 0; + + //shutoff transmit if it has been too long since send() was called + if (_tx_enabled and (boost::get_system_time() - _io_impl->last_send_time) > boost::posix_time::milliseconds(100)){ + this->tx_stream_on_off(false); + } + + //always poll regardless of enabled so we can clear the conditions + _fx2_ctrl->usrp_control_read( + VRQ_GET_STATUS, 0, GS_TX_UNDERRUN, &underflow, sizeof(underflow) + ); + _fx2_ctrl->usrp_control_read( + VRQ_GET_STATUS, 0, GS_RX_OVERRUN, &overflow, sizeof(overflow) + ); + + //handle message generation for xerflow conditions + if (_tx_enabled and underflow){ + async_metadata.time_spec = _soft_time_ctrl->get_time(); + _soft_time_ctrl->get_async_queue().push_with_pop_on_full(async_metadata); + UHD_MSG(fastpath) << "U"; + } + if (_rx_enabled and overflow){ + inline_metadata.time_spec = _soft_time_ctrl->get_time(); + _soft_time_ctrl->get_inline_queue().push_with_pop_on_full(inline_metadata); + UHD_MSG(fastpath) << "O"; + } + + boost::this_thread::sleep(boost::posix_time::milliseconds(50)); + }} + catch(const boost::thread_interrupted &){} //normal exit condition + catch(const std::exception &e){ + UHD_MSG(error) << "The vandal caught an unexpected exception " << e.what() << std::endl; + } +} + +/*********************************************************************** + * Properties callback methods below + **********************************************************************/ +void usrp1_impl::update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){ + boost::mutex::scoped_lock lock = _io_impl->recv_handler.get_scoped_lock(); + + //sanity checking + validate_subdev_spec(_tree, spec, "rx"); + + _rx_subdev_spec = spec; //shadow + _io_impl->recv_handler.resize(spec.size()); + _io_impl->recv_handler.set_converter(_rx_otw_type, spec.size()); + + //set the mux and set the number of rx channels + std::vector<mapping_pair_t> mapping; + BOOST_FOREACH(const subdev_spec_pair_t &pair, spec){ + const std::string conn = _tree->access<std::string>(str(boost::format( + "/mboards/0/dboards/%s/rx_frontends/%s/connection" + ) % pair.db_name % pair.sd_name)).get(); + mapping.push_back(std::make_pair(pair.db_name, conn)); + } + bool s = this->disable_rx(); + _iface->poke32(FR_RX_MUX, calc_rx_mux(mapping)); + this->restore_rx(s); +} + +void usrp1_impl::update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){ + boost::mutex::scoped_lock lock = _io_impl->send_handler.get_scoped_lock(); + + //sanity checking + validate_subdev_spec(_tree, spec, "tx"); + + _tx_subdev_spec = spec; //shadow + _io_impl->send_handler.resize(spec.size()); + _io_impl->send_handler.set_converter(_tx_otw_type, spec.size()); + + //set the mux and set the number of tx channels + std::vector<mapping_pair_t> mapping; + BOOST_FOREACH(const subdev_spec_pair_t &pair, spec){ + const std::string conn = _tree->access<std::string>(str(boost::format( + "/mboards/0/dboards/%s/tx_frontends/%s/connection" + ) % pair.db_name % pair.sd_name)).get(); + mapping.push_back(std::make_pair(pair.db_name, conn)); + } + bool s = this->disable_tx(); + _iface->poke32(FR_TX_MUX, calc_tx_mux(mapping)); + this->restore_tx(s); + + //if the spec changes size, so does the max samples per packet... + _io_impl->send_handler.set_max_samples_per_packet(get_max_send_samps_per_packet()); +} + +double usrp1_impl::update_rx_samp_rate(const double samp_rate){ + boost::mutex::scoped_lock lock = _io_impl->recv_handler.get_scoped_lock(); + + const size_t rate = uhd::clip<size_t>( + boost::math::iround(_master_clock_rate / samp_rate), size_t(std::ceil(_master_clock_rate / 8e6)), 256 + ); + + bool s = this->disable_rx(); + _iface->poke32(FR_DECIM_RATE, rate/2 - 1); + this->restore_rx(s); + + _io_impl->recv_handler.set_samp_rate(_master_clock_rate / rate); + return _master_clock_rate / rate; +} + +double usrp1_impl::update_tx_samp_rate(const double samp_rate){ + boost::mutex::scoped_lock lock = _io_impl->send_handler.get_scoped_lock(); + + const size_t rate = uhd::clip<size_t>( + boost::math::iround(_master_clock_rate / samp_rate), size_t(std::ceil(_master_clock_rate / 8e6)), 256 + ); + + bool s = this->disable_tx(); + _iface->poke32(FR_INTERP_RATE, rate/2 - 1); + this->restore_tx(s); + + _io_impl->send_handler.set_samp_rate(_master_clock_rate / rate); + return _master_clock_rate / rate; +} + +double usrp1_impl::update_rx_dsp_freq(const size_t dspno, const double freq_){ + + //correct for outside of rate (wrap around) + double freq = std::fmod(freq_, _master_clock_rate); + if (std::abs(freq) > _master_clock_rate/2.0) + freq -= boost::math::sign(freq)*_master_clock_rate; + + //calculate the freq register word (signed) + UHD_ASSERT_THROW(std::abs(freq) <= _master_clock_rate/2.0); + static const double scale_factor = std::pow(2.0, 32); + const boost::int32_t freq_word = boost::int32_t(boost::math::round((freq / _master_clock_rate) * scale_factor)); + + static const boost::uint32_t dsp_index_to_reg_val[4] = { + FR_RX_FREQ_0, FR_RX_FREQ_1, FR_RX_FREQ_2, FR_RX_FREQ_3 + }; + _iface->poke32(dsp_index_to_reg_val[dspno], ~freq_word + 1); + + return (double(freq_word) / scale_factor) * _master_clock_rate; +} + +double usrp1_impl::update_tx_dsp_freq(const size_t dspno, const double freq){ + //map the freq shift key to a subdev spec to a particular codec chip + _dbc[_tx_subdev_spec.at(dspno).db_name].codec->set_duc_freq(freq, _master_clock_rate); + return freq; //assume infinite precision +} + +/*********************************************************************** + * Async Data + **********************************************************************/ +bool usrp1_impl::recv_async_msg( + async_metadata_t &async_metadata, double timeout +){ + boost::this_thread::disable_interruption di; //disable because the wait can throw + return _soft_time_ctrl->get_async_queue().pop_with_timed_wait(async_metadata, timeout); +} + /*********************************************************************** * Data send + helper functions **********************************************************************/ @@ -292,29 +463,23 @@ size_t usrp1_impl::send( ){ if (_soft_time_ctrl->send_pre(metadata, timeout)) return 0; + this->tx_stream_on_off(true); //always enable (it will do the right thing) size_t num_samps_sent = _io_impl->send_handler.send( buffs, nsamps_per_buff, metadata, io_type, send_mode, timeout ); - //handle eob flag (commit the buffer, disable the DACs) + //handle eob flag (commit the buffer, /*disable the DACs*/) //check num samps sent to avoid flush on incomplete/timeout if (metadata.end_of_burst and num_samps_sent == nsamps_per_buff){ - _io_impl->flush_send_buff(); - } - - //handle the polling for underflow conditions - _io_impl->underflow_poll_samp_count += num_samps_sent; - if (_io_impl->underflow_poll_samp_count >= _tx_samps_per_poll_interval){ - _io_impl->underflow_poll_samp_count = 0; //reset count - boost::uint8_t underflow = 0; - ssize_t ret = _ctrl_transport->usrp_control_read( - VRQ_GET_STATUS, 0, GS_TX_UNDERRUN, - &underflow, sizeof(underflow) - ); - if (ret < 0) UHD_MSG(error) << "USRP: underflow check failed" << std::endl; - else if (underflow) UHD_MSG(fastpath) << "U"; + async_metadata_t metadata; + metadata.channel = 0; + metadata.has_time_spec = true; + metadata.time_spec = _soft_time_ctrl->get_time(); + metadata.event_code = async_metadata_t::EVENT_CODE_BURST_ACK; + _soft_time_ctrl->get_async_queue().push_with_pop_on_full(metadata); + this->tx_stream_on_off(false); } return num_samps_sent; @@ -335,26 +500,14 @@ size_t usrp1_impl::recv( rx_metadata_t &metadata, const io_type_t &io_type, recv_mode_t recv_mode, double timeout ){ + //interleave a "soft" inline message into the receive stream: + if (_soft_time_ctrl->get_inline_queue().pop_with_haste(metadata)) return 0; + size_t num_samps_recvd = _io_impl->recv_handler.recv( buffs, nsamps_per_buff, metadata, io_type, recv_mode, timeout ); - _soft_time_ctrl->recv_post(metadata, num_samps_recvd); - - //handle the polling for overflow conditions - _io_impl->overflow_poll_samp_count += num_samps_recvd; - if (_io_impl->overflow_poll_samp_count >= _rx_samps_per_poll_interval){ - _io_impl->overflow_poll_samp_count = 0; //reset count - boost::uint8_t overflow = 0; - ssize_t ret = _ctrl_transport->usrp_control_read( - VRQ_GET_STATUS, 0, GS_RX_OVERRUN, - &overflow, sizeof(overflow) - ); - if (ret < 0) UHD_MSG(error) << "USRP: overflow check failed" << std::endl; - else if (overflow) UHD_MSG(fastpath) << "O"; - } - - return num_samps_recvd; + return _soft_time_ctrl->recv_post(metadata, num_samps_recvd); } |