diff options
-rw-r--r-- | firmware/microblaze/apps/txrx_uhd.c | 2 | ||||
-rw-r--r-- | host/lib/usrp/usrp2/fw_common.h | 2 | ||||
-rw-r--r-- | host/lib/usrp/usrp2/io_impl.cpp | 103 | ||||
-rw-r--r-- | host/lib/usrp/usrp2/mboard_impl.cpp | 14 | ||||
-rw-r--r-- | host/lib/usrp/usrp2/usrp2_impl.cpp | 4 | ||||
-rw-r--r-- | host/lib/usrp/usrp2/usrp2_impl.hpp | 7 |
6 files changed, 110 insertions, 22 deletions
diff --git a/firmware/microblaze/apps/txrx_uhd.c b/firmware/microblaze/apps/txrx_uhd.c index 1dd6e80ac..e38eb621d 100644 --- a/firmware/microblaze/apps/txrx_uhd.c +++ b/firmware/microblaze/apps/txrx_uhd.c @@ -372,7 +372,7 @@ eth_pkt_inspector(dbsm_t *sm, int bufno) // In the future, a hardware state machine will do this... if ( //warning! magic numbers approaching.... (((buff + ((2 + 14 + 20)/sizeof(uint32_t)))[0] & 0xffff) == USRP2_UDP_DATA_PORT) && - ((buff + ((2 + 14 + 20 + 8)/sizeof(uint32_t)))[0] != USRP2_INVALID_VRT_HEADER) + ((buff + ((2 + 14 + 20 + 8)/sizeof(uint32_t)))[1] != USRP2_INVALID_VRT_HEADER) ) return false; //test if its an ip recovery packet diff --git a/host/lib/usrp/usrp2/fw_common.h b/host/lib/usrp/usrp2/fw_common.h index e812e1221..783e5c772 100644 --- a/host/lib/usrp/usrp2/fw_common.h +++ b/host/lib/usrp/usrp2/fw_common.h @@ -34,7 +34,7 @@ extern "C" { //fpga and firmware compatibility numbers #define USRP2_FPGA_COMPAT_NUM 2 -#define USRP2_FW_COMPAT_NUM 6 +#define USRP2_FW_COMPAT_NUM 7 //used to differentiate control packets over data port #define USRP2_INVALID_VRT_HEADER 0 diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp index b7585afe9..6d5eb488c 100644 --- a/host/lib/usrp/usrp2/io_impl.cpp +++ b/host/lib/usrp/usrp2/io_impl.cpp @@ -32,6 +32,9 @@ using namespace uhd::usrp; using namespace uhd::transport; namespace asio = boost::asio; +/*********************************************************************** + * constants + **********************************************************************/ static const int underflow_flags = 0 | async_metadata_t::EVENT_CODE_UNDERFLOW | async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET @@ -40,6 +43,67 @@ static const int underflow_flags = 0 static const size_t vrt_send_header_offset_words32 = 1; /*********************************************************************** + * flow control monitor for a single tx channel + * - the pirate thread calls update + * - the get send buffer calls check + **********************************************************************/ +class flow_control_monitor{ +public: + typedef boost::shared_ptr<flow_control_monitor> sptr; + + /*! + * Make a new flow control monitor. + * \param max_seqs_out num seqs before throttling + */ + flow_control_monitor(size_t max_seqs_out){ + _last_seq_out = 0; + _last_seq_ack = 0; + _max_seqs_out = max_seqs_out; + } + + /*! + * Check the flow control condition. + * \param seq the sequence to go out + * \param timeout the timeout in seconds + * \return false on timeout + */ + UHD_INLINE bool check_fc_condition(boost::uint16_t seq, double timeout){ + boost::unique_lock<boost::mutex> lock(_fc_mutex); + _last_seq_out = seq; + return _fc_cond.timed_wait( + lock, + boost::posix_time::microseconds(long(timeout*1e6)), + boost::bind(&flow_control_monitor::ready, this) + ); + } + + /*! + * Update the flow control condition. + * \param seq the last sequence number to be ACK'd + */ + UHD_INLINE void update_fc_condition(boost::uint16_t seq){ + boost::unique_lock<boost::mutex> lock(_fc_mutex); + _last_seq_ack = seq; + lock.unlock(); + _fc_cond.notify_one(); + } + +private: + bool ready(void){ + //return true; + //std::cout << "_last_seq_out " << _last_seq_out << std::endl; + //std::cout << "_last_seq_ack " << _last_seq_ack << std::endl; + //std::cout << "boost::uint16_t(_last_seq_out -_last_seq_ack) " << boost::uint16_t(_last_seq_out -_last_seq_ack) << std::endl; + return boost::uint16_t(_last_seq_out -_last_seq_ack) < boost::uint16_t(_max_seqs_out); + } + + boost::mutex _fc_mutex; + boost::condition _fc_cond; + boost::uint16_t _last_seq_out, _last_seq_ack; + size_t _max_seqs_out; +}; + +/*********************************************************************** * io impl details (internal to this file) * - pirate crew * - alignment buffer @@ -49,12 +113,14 @@ static const size_t vrt_send_header_offset_words32 = 1; struct usrp2_impl::io_impl{ typedef alignment_buffer<managed_recv_buffer::sptr, time_spec_t> alignment_buffer_type; - io_impl(size_t num_frames, size_t width): + io_impl(size_t num_recv_frames, size_t send_frame_size, size_t width): packet_handler_recv_state(width), - recv_pirate_booty(alignment_buffer_type::make(num_frames, width)), + recv_pirate_booty(alignment_buffer_type::make(num_recv_frames, width)), async_msg_fifo(bounded_buffer<async_metadata_t>::make(100/*messages deep*/)) { - /* NOP */ + for (size_t i = 0; i < width; i++) fc_mons.push_back( + flow_control_monitor::sptr(new flow_control_monitor(usrp2_impl::sram_bytes/send_frame_size)) + ); } ~io_impl(void){ @@ -76,19 +142,22 @@ struct usrp2_impl::io_impl{ UHD_ASSERT_THROW(trans.size() == buffs.size()); //calculate the 16-bit sequence number for the special header - const boost::uint32_t next_seq = uhd::htonx(boost::uint32_t( - packet_handler_send_state.next_packet_seq & 0xffff - )); + const boost::uint16_t seq_num = boost::uint16_t(packet_handler_send_state.next_packet_seq & 0xffff); + const boost::uint32_t fc_word32 = uhd::htonx(boost::uint32_t(seq_num)); //grab a managed buffer for each index for (size_t i = 0; i < buffs.size(); i++){ + if (not fc_mons[i]->check_fc_condition(seq_num, timeout)) return false; buffs[i] = trans[i]->get_send_buff(timeout); if (not buffs[i].get()) return false; - buffs[i]->cast<boost::uint32_t *>()[0] = next_seq; + buffs[i]->cast<boost::uint32_t *>()[0] = fc_word32; } return true; } + //flow control monitors + std::vector<flow_control_monitor::sptr> fc_mons; + //state management for the vrt packet handler code vrt_packet_handler::recv_state packet_handler_recv_state; vrt_packet_handler::send_state packet_handler_send_state; @@ -138,6 +207,13 @@ void usrp2_impl::io_impl::recv_pirate_loop( ); metadata.event_code = vrt_packet_handler::get_context_code<async_metadata_t::event_code_t>(vrt_hdr, if_packet_info); + //catch the flow control packets and react + if (metadata.event_code == 0){ + boost::uint32_t fc_word32 = uhd::ntohx((vrt_hdr + if_packet_info.num_header_words32)[1]); + this->fc_mons[index]->update_fc_condition(fc_word32 & 0xffff); + continue; + } + //print the famous U, and push the metadata into the message queue if (metadata.event_code & underflow_flags) std::cerr << "U" << std::flush; async_msg_fifo->push_with_pop_on_full(metadata); @@ -172,21 +248,22 @@ void usrp2_impl::io_init(void){ //send a small data packet so the usrp2 knows the udp source port BOOST_FOREACH(zero_copy_if::sptr data_transport, _data_transports){ managed_send_buffer::sptr send_buff = data_transport->get_send_buff(); - static const boost::uint32_t data = uhd::htonx( - boost::uint32_t(USRP2_INVALID_VRT_HEADER) - ); + static const boost::uint32_t data[2] = { + uhd::htonx(boost::uint32_t(0 /* don't care seq num */)), + uhd::htonx(boost::uint32_t(USRP2_INVALID_VRT_HEADER)) + }; std::memcpy(send_buff->cast<void*>(), &data, sizeof(data)); send_buff->commit(sizeof(data)); //drain the recv buffers (may have junk) while (data_transport->get_recv_buff().get()){}; } - //the number of recv frames is the number for the first transport //the assumption is that all data transports should be identical - size_t num_frames = _data_transports.front()->get_num_recv_frames(); + const size_t num_recv_frames = _data_transports.front()->get_num_recv_frames(); + const size_t send_frame_size = _data_transports.front()->get_send_frame_size(); //create new io impl - _io_impl = UHD_PIMPL_MAKE(io_impl, (num_frames, _data_transports.size())); + _io_impl = UHD_PIMPL_MAKE(io_impl, (num_recv_frames, send_frame_size, _data_transports.size())); //create a new pirate thread for each zc if (yarr!!) for (size_t i = 0; i < _data_transports.size(); i++){ diff --git a/host/lib/usrp/usrp2/mboard_impl.cpp b/host/lib/usrp/usrp2/mboard_impl.cpp index a0e6adfad..37e69b39d 100644 --- a/host/lib/usrp/usrp2/mboard_impl.cpp +++ b/host/lib/usrp/usrp2/mboard_impl.cpp @@ -38,10 +38,11 @@ using namespace uhd::usrp; usrp2_mboard_impl::usrp2_mboard_impl( size_t index, transport::udp_simple::sptr ctrl_transport, - size_t recv_frame_size + size_t recv_samps_per_packet, + size_t send_bytes_per_packet ): _index(index), - _recv_frame_size(recv_frame_size) + _recv_samps_per_packet(recv_samps_per_packet) { //make a new interface for usrp2 stuff _iface = usrp2_iface::make(ctrl_transport); @@ -75,7 +76,7 @@ usrp2_mboard_impl::usrp2_mboard_impl( this->issue_ddc_stream_cmd(stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); //init the rx control registers - _iface->poke32(U2_REG_RX_CTRL_NSAMPS_PER_PKT, _recv_frame_size); + _iface->poke32(U2_REG_RX_CTRL_NSAMPS_PER_PKT, _recv_samps_per_packet); _iface->poke32(U2_REG_RX_CTRL_NCHANNELS, 1); _iface->poke32(U2_REG_RX_CTRL_CLEAR_OVERRUN, 1); //reset _iface->poke32(U2_REG_RX_CTRL_VRT_HEADER, 0 @@ -93,6 +94,11 @@ usrp2_mboard_impl::usrp2_mboard_impl( _iface->poke32(U2_REG_TX_CTRL_CLEAR_STATE, 1); //reset _iface->poke32(U2_REG_TX_CTRL_REPORT_SID, 1); //sid 1 (different from rx) _iface->poke32(U2_REG_TX_CTRL_POLICY, U2_FLAG_TX_CTRL_POLICY_NEXT_PACKET); + const size_t cycles_per_ack = size_t(_clock_ctrl->get_master_clock_rate()/100); //100 aps + //_iface->poke32(U2_REG_TX_CTRL_CYCLES_PER_ACK, U2_FLAG_TX_CTRL_ACK_ENB | cycles_per_ack); //FIXME total pause frames + static const double sram_frac = 1.0/8.0; //fraction of sram to fill before ack + const size_t packets_per_ack = size_t(usrp2_impl::sram_bytes*sram_frac/send_bytes_per_packet); + _iface->poke32(U2_REG_TX_CTRL_PACKETS_PER_ACK, U2_FLAG_TX_CTRL_ACK_ENB | packets_per_ack); //init the ddc init_ddc_config(); @@ -178,7 +184,7 @@ void usrp2_mboard_impl::set_time_spec(const time_spec_t &time_spec, bool now){ void usrp2_mboard_impl::issue_ddc_stream_cmd(const stream_cmd_t &stream_cmd){ _iface->poke32(U2_REG_RX_CTRL_STREAM_CMD, dsp_type1::calc_stream_cmd_word( - stream_cmd, _recv_frame_size + stream_cmd, _recv_samps_per_packet )); _iface->poke32(U2_REG_RX_CTRL_TIME_SECS, boost::uint32_t(stream_cmd.time_spec.get_full_secs())); _iface->poke32(U2_REG_RX_CTRL_TIME_TICKS, stream_cmd.time_spec.get_tick_count(get_master_clock_freq())); diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index 8429a2593..79bf2b260 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -173,7 +173,9 @@ usrp2_impl::usrp2_impl( //create a new mboard handler for each control transport for(size_t i = 0; i < ctrl_transports.size(); i++){ _mboards.push_back(usrp2_mboard_impl::sptr(new usrp2_mboard_impl( - i, ctrl_transports[i], this->get_max_recv_samps_per_packet() + i, ctrl_transports[i], + this->get_max_recv_samps_per_packet(), + _data_transports[i]->get_send_frame_size() ))); //use an empty name when there is only one mboard std::string name = (ctrl_transports.size() > 1)? boost::lexical_cast<std::string>(i) : ""; diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index 6d35e925d..2077b0a50 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -84,7 +84,8 @@ public: usrp2_mboard_impl( size_t index, uhd::transport::udp_simple::sptr, - size_t recv_frame_size + size_t recv_samps_per_packet, + size_t send_bytes_per_packet ); ~usrp2_mboard_impl(void); @@ -95,7 +96,7 @@ public: private: size_t _index; int _rev_hi, _rev_lo; - const size_t _recv_frame_size; + const size_t _recv_samps_per_packet; //properties for this mboard void get(const wax::obj &, wax::obj &); @@ -171,6 +172,8 @@ private: */ class usrp2_impl : public uhd::device{ public: + static const size_t sram_bytes = size_t(1 << 20); + /*! * Create a new usrp2 impl base. * \param ctrl_transports the udp transports for control |