diff options
Diffstat (limited to 'host/lib/usrp/usrp1/io_impl.cpp')
-rw-r--r-- | host/lib/usrp/usrp1/io_impl.cpp | 375 |
1 files changed, 275 insertions, 100 deletions
diff --git a/host/lib/usrp/usrp1/io_impl.cpp b/host/lib/usrp/usrp1/io_impl.cpp index de325ea5d..937706fdd 100644 --- a/host/lib/usrp/usrp1/io_impl.cpp +++ b/host/lib/usrp/usrp1/io_impl.cpp @@ -22,6 +22,7 @@ #include "../../transport/super_send_packet_handler.hpp" #include "usrp1_calc_mux.hpp" #include "fpga_regs_standard.h" +#include "fpga_regs_common.h" #include "usrp_commands.h" #include "usrp1_impl.hpp" #include <uhd/utils/msg.hpp> @@ -33,6 +34,7 @@ #include <boost/thread/thread.hpp> #include <boost/bind.hpp> #include <boost/format.hpp> +#include <boost/make_shared.hpp> using namespace uhd; using namespace uhd::usrp; @@ -109,6 +111,7 @@ static void usrp1_bs_vrt_unpacker( ){ if_packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_DATA; if_packet_info.num_payload_words32 = if_packet_info.num_packet_words32; + if_packet_info.num_payload_bytes = if_packet_info.num_packet_words32*sizeof(boost::uint32_t); if_packet_info.num_header_words32 = 0; if_packet_info.packet_count = 0; if_packet_info.sob = false; @@ -138,10 +141,6 @@ struct usrp1_impl::io_impl{ zero_copy_if::sptr data_transport; - //state management for the vrt packet handler code - sph::recv_packet_handler recv_handler; - sph::send_packet_handler send_handler; - //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 @@ -219,13 +218,6 @@ void usrp1_impl::io_impl::flush_send_buff(void){ * Initialize internals within this file **********************************************************************/ void usrp1_impl::io_init(void){ - _rx_otw_type.width = 16; - _rx_otw_type.shift = 0; - _rx_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; - - _tx_otw_type.width = 16; - _tx_otw_type.shift = 0; - _tx_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; _io_impl = UHD_PIMPL_MAKE(io_impl, (_data_transport)); @@ -234,18 +226,6 @@ void usrp1_impl::io_init(void){ &usrp1_impl::vandal_conquest_loop, this )); - //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_xport_chan_get_buff(0, boost::bind( - &uhd::transport::zero_copy_if::get_recv_buff, _io_impl->data_transport, _1 - )); - _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_xport_chan_get_buff(0, boost::bind( - &usrp1_impl::io_impl::get_send_buff, _io_impl.get(), _1 - )); - //init as disabled, then call the real function (uses restore) this->enable_rx(false); this->enable_tx(false); @@ -325,17 +305,109 @@ void usrp1_impl::vandal_conquest_loop(void){ } /*********************************************************************** + * RX streamer wrapper that talks to soft time control + **********************************************************************/ +class usrp1_recv_packet_streamer : public sph::recv_packet_handler, public rx_streamer{ +public: + usrp1_recv_packet_streamer(const size_t max_num_samps, soft_time_ctrl::sptr stc){ + _max_num_samps = max_num_samps; + _stc = stc; + } + + size_t get_num_channels(void) const{ + return this->size(); + } + + size_t get_max_num_samps(void) const{ + return _max_num_samps; + } + + size_t recv( + const rx_streamer::buffs_type &buffs, + const size_t nsamps_per_buff, + uhd::rx_metadata_t &metadata, + const double timeout, + const bool one_packet + ){ + //interleave a "soft" inline message into the receive stream: + if (_stc->get_inline_queue().pop_with_haste(metadata)) return 0; + + size_t num_samps_recvd = sph::recv_packet_handler::recv( + buffs, nsamps_per_buff, metadata, timeout, one_packet + ); + + return _stc->recv_post(metadata, num_samps_recvd); + } + +private: + size_t _max_num_samps; + soft_time_ctrl::sptr _stc; +}; + +/*********************************************************************** + * TX streamer wrapper that talks to soft time control + **********************************************************************/ +class usrp1_send_packet_streamer : public sph::send_packet_handler, public tx_streamer{ +public: + usrp1_send_packet_streamer(const size_t max_num_samps, soft_time_ctrl::sptr stc, boost::function<void(bool)> tx_enb_fcn){ + _max_num_samps = max_num_samps; + this->set_max_samples_per_packet(_max_num_samps); + _stc = stc; + _tx_enb_fcn = tx_enb_fcn; + } + + size_t get_num_channels(void) const{ + return this->size(); + } + + size_t get_max_num_samps(void) const{ + return _max_num_samps; + } + + size_t send( + const tx_streamer::buffs_type &buffs, + const size_t nsamps_per_buff, + const uhd::tx_metadata_t &metadata, + const double timeout_ + ){ + double timeout = timeout_; //rw copy + _stc->send_pre(metadata, timeout); + + _tx_enb_fcn(true); //always enable (it will do the right thing) + size_t num_samps_sent = sph::send_packet_handler::send( + buffs, nsamps_per_buff, metadata, timeout + ); + + //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){ + async_metadata_t metadata; + metadata.channel = 0; + metadata.has_time_spec = true; + metadata.time_spec = _stc->get_time(); + metadata.event_code = async_metadata_t::EVENT_CODE_BURST_ACK; + _stc->get_async_queue().push_with_pop_on_full(metadata); + _tx_enb_fcn(false); + } + + return num_samps_sent; + } + +private: + size_t _max_num_samps; + soft_time_ctrl::sptr _stc; + boost::function<void(bool)> _tx_enb_fcn; +}; + +/*********************************************************************** * 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()); //always 1 - _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; @@ -351,14 +423,11 @@ void usrp1_impl::update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){ } 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()); //always 1 - _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; @@ -371,41 +440,94 @@ void usrp1_impl::update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){ 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()); +void usrp1_impl::update_tick_rate(const double rate){ + //updating this variable should: + //update dboard iface -> it has a reference + //update dsp freq bounds -> publisher + _master_clock_rate = rate; } -double usrp1_impl::update_rx_samp_rate(const double samp_rate){ - boost::mutex::scoped_lock lock = _io_impl->recv_handler.get_scoped_lock(); +uhd::meta_range_t usrp1_impl::get_rx_dsp_host_rates(void){ + meta_range_t range; + const size_t div = this->has_rx_halfband()? 2 : 1; + for (int rate = 256; rate >= 4; rate -= div){ + range.push_back(range_t(_master_clock_rate/rate)); + } + return range; +} - 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 - ); +uhd::meta_range_t usrp1_impl::get_tx_dsp_host_rates(void){ + meta_range_t range; + const size_t div = this->has_tx_halfband()? 2 : 1; + for (int rate = 256; rate >= 8; rate -= div){ + range.push_back(range_t(_master_clock_rate/rate)); + } + return range; +} - bool s = this->disable_rx(); - _iface->poke32(FR_DECIM_RATE, rate/2 - 1); - this->restore_rx(s); +double usrp1_impl::update_rx_samp_rate(size_t dspno, const double samp_rate){ + + const size_t div = this->has_rx_halfband()? 2 : 1; + const size_t rate = boost::math::iround(_master_clock_rate/this->get_rx_dsp_host_rates().clip(samp_rate, true)); + + if (rate < 8 and this->has_rx_halfband()) UHD_MSG(warning) << + "USRP1 cannot achieve decimations below 8 when the half-band filter is present.\n" + "The usrp1_fpga_4rx.rbf file is a special FPGA image without RX half-band filters.\n" + "To load this image, set the device address key/value pair: fpga=usrp1_fpga_4rx.rbf\n" + << std::endl; + + if (dspno == 0){ //only care if dsp0 is set since its homogeneous + bool s = this->disable_rx(); + _iface->poke32(FR_RX_SAMPLE_RATE_DIV, div - 1); + _iface->poke32(FR_DECIM_RATE, rate/div - 1); + this->restore_rx(s); + + //update the streamer if created + boost::shared_ptr<usrp1_recv_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<usrp1_recv_packet_streamer>(_rx_streamer.lock()); + if (my_streamer.get() != NULL){ + my_streamer->set_samp_rate(_master_clock_rate / rate); + } + } - _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(); +double usrp1_impl::update_tx_samp_rate(size_t dspno, const double samp_rate){ - 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 - ); + const size_t div = this->has_tx_halfband()? 2 : 1; + const size_t rate = boost::math::iround(_master_clock_rate/this->get_tx_dsp_host_rates().clip(samp_rate, true)); - bool s = this->disable_tx(); - _iface->poke32(FR_INTERP_RATE, rate/2 - 1); - this->restore_tx(s); + if (dspno == 0){ //only care if dsp0 is set since its homogeneous + bool s = this->disable_tx(); + _iface->poke32(FR_TX_SAMPLE_RATE_DIV, div - 1); + _iface->poke32(FR_INTERP_RATE, rate/div - 1); + this->restore_tx(s); + + //update the streamer if created + boost::shared_ptr<usrp1_send_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<usrp1_send_packet_streamer>(_tx_streamer.lock()); + if (my_streamer.get() != NULL){ + my_streamer->set_samp_rate(_master_clock_rate / rate); + } + } - _io_impl->send_handler.set_samp_rate(_master_clock_rate / rate); return _master_clock_rate / rate; } +void usrp1_impl::update_rates(void){ + const fs_path mb_path = "/mboards/0"; + this->update_tick_rate(_master_clock_rate); + BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "rx_dsps")){ + _tree->access<double>(mb_path / "rx_dsps" / name / "rate" / "value").update(); + } + BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "tx_dsps")){ + _tree->access<double>(mb_path / "tx_dsps" / name / "rate" / "value").update(); + } +} + double usrp1_impl::update_rx_dsp_freq(const size_t dspno, const double freq_){ //correct for outside of rate (wrap around) @@ -443,67 +565,120 @@ bool usrp1_impl::recv_async_msg( } /*********************************************************************** - * Data send + helper functions + * Receive streamer **********************************************************************/ -size_t usrp1_impl::get_max_send_samps_per_packet(void) const { - return (_data_transport->get_send_frame_size() - alignment_padding) - / _tx_otw_type.get_sample_size() - / _tx_subdev_spec.size() - ; -} +rx_streamer::sptr usrp1_impl::get_rx_stream(const uhd::stream_args_t &args_){ + stream_args_t args = args_; + + //setup defaults for unspecified values + args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format; + args.channels.clear(); //NOTE: we have no choice about the channel mapping + for (size_t ch = 0; ch < _rx_subdev_spec.size(); ch++){ + args.channels.push_back(ch); + } -size_t usrp1_impl::send( - const send_buffs_type &buffs, size_t nsamps_per_buff, - const tx_metadata_t &metadata, const io_type_t &io_type, - send_mode_t send_mode, double timeout -){ - if (_soft_time_ctrl->send_pre(metadata, timeout)) return 0; + if (args.otw_format == "sc16"){ + _iface->poke32(FR_RX_FORMAT, 0 + | (0 << bmFR_RX_FORMAT_SHIFT_SHIFT) + | (16 << bmFR_RX_FORMAT_WIDTH_SHIFT) + | bmFR_RX_FORMAT_WANT_Q + ); + } + else if (args.otw_format == "sc8"){ + _iface->poke32(FR_RX_FORMAT, 0 + | (8 << bmFR_RX_FORMAT_SHIFT_SHIFT) + | (8 << bmFR_RX_FORMAT_WIDTH_SHIFT) + | bmFR_RX_FORMAT_WANT_Q + ); + } + else{ + throw uhd::value_error("USRP1 RX cannot handle requested wire format: " + args.otw_format); + } - 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 - ); + //calculate packet size + const size_t bpp = _data_transport->get_recv_frame_size()/args.channels.size(); + const size_t spp = bpp/convert::get_bytes_per_item(args.otw_format); - //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){ - 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); - } + //make the new streamer given the samples per packet + boost::shared_ptr<usrp1_recv_packet_streamer> my_streamer = + boost::make_shared<usrp1_recv_packet_streamer>(spp, _soft_time_ctrl); + + //special scale factor change for sc8 + if (args.otw_format == "sc8") + my_streamer->set_scale_factor(1.0/127); + + //init some streamer stuff + my_streamer->set_tick_rate(_master_clock_rate); + my_streamer->set_vrt_unpacker(&usrp1_bs_vrt_unpacker); + my_streamer->set_xport_chan_get_buff(0, boost::bind( + &uhd::transport::zero_copy_if::get_recv_buff, _io_impl->data_transport, _1 + )); + + //set the converter + uhd::convert::id_type id; + id.input_format = args.otw_format + "_item16_usrp1"; + id.num_inputs = 1; + id.output_format = args.cpu_format; + id.num_outputs = args.channels.size(); + my_streamer->set_converter(id); - return num_samps_sent; + //save as weak ptr for update access + _rx_streamer = my_streamer; + + //sets all tick and samp rates on this streamer + this->update_rates(); + + return my_streamer; } /*********************************************************************** - * Data recv + helper functions + * Transmit streamer **********************************************************************/ -size_t usrp1_impl::get_max_recv_samps_per_packet(void) const { - return _data_transport->get_recv_frame_size() - / _rx_otw_type.get_sample_size() - / _rx_subdev_spec.size() - ; -} +tx_streamer::sptr usrp1_impl::get_tx_stream(const uhd::stream_args_t &args_){ + stream_args_t args = args_; + + //setup defaults for unspecified values + args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format; + args.channels.clear(); //NOTE: we have no choice about the channel mapping + for (size_t ch = 0; ch < _tx_subdev_spec.size(); ch++){ + args.channels.push_back(ch); + } -size_t usrp1_impl::recv( - const recv_buffs_type &buffs, size_t nsamps_per_buff, - 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; + if (args.otw_format != "sc16"){ + throw uhd::value_error("USRP1 TX cannot handle requested wire format: " + args.otw_format); + } - size_t num_samps_recvd = _io_impl->recv_handler.recv( - buffs, nsamps_per_buff, - metadata, io_type, - recv_mode, timeout - ); + _iface->poke32(FR_TX_FORMAT, bmFR_TX_FORMAT_16_IQ); + + //calculate packet size + const size_t bpp = _data_transport->get_send_frame_size()/args.channels.size(); + const size_t spp = bpp/convert::get_bytes_per_item(args.otw_format); + + //make the new streamer given the samples per packet + boost::function<void(bool)> tx_fcn = boost::bind(&usrp1_impl::tx_stream_on_off, this, _1); + boost::shared_ptr<usrp1_send_packet_streamer> my_streamer = + boost::make_shared<usrp1_send_packet_streamer>(spp, _soft_time_ctrl, tx_fcn); + + //init some streamer stuff + my_streamer->set_tick_rate(_master_clock_rate); + my_streamer->set_vrt_packer(&usrp1_bs_vrt_packer); + my_streamer->set_xport_chan_get_buff(0, boost::bind( + &usrp1_impl::io_impl::get_send_buff, _io_impl.get(), _1 + )); + + //set the converter + uhd::convert::id_type id; + id.input_format = args.cpu_format; + id.num_inputs = args.channels.size(); + id.output_format = args.otw_format + "_item16_usrp1"; + id.num_outputs = 1; + my_streamer->set_converter(id); + + //save as weak ptr for update access + _tx_streamer = my_streamer; + + //sets all tick and samp rates on this streamer + this->update_rates(); - return _soft_time_ctrl->recv_post(metadata, num_samps_recvd); + return my_streamer; } |