diff options
| author | Martin Braun <martin.braun@ettus.com> | 2017-06-27 12:00:18 -0700 | 
|---|---|---|
| committer | Martin Braun <martin.braun@ettus.com> | 2017-06-27 12:00:18 -0700 | 
| commit | 5a0be9b1de7be7ba6c266dd742456a96e25373fc (patch) | |
| tree | 6bcd3b02f399cc2677a6c747411d7e2ced1923f5 /host/lib | |
| parent | e48659cec5207b598d877d8beca7e0b3807b617e (diff) | |
| parent | 24db48766dff45b8fe8ed3031e09617ca92b84b6 (diff) | |
| download | uhd-5a0be9b1de7be7ba6c266dd742456a96e25373fc.tar.gz uhd-5a0be9b1de7be7ba6c266dd742456a96e25373fc.tar.bz2 uhd-5a0be9b1de7be7ba6c266dd742456a96e25373fc.zip  | |
Merge branch 'maint'
Diffstat (limited to 'host/lib')
| -rw-r--r-- | host/lib/rfnoc/legacy_compat.cpp | 34 | ||||
| -rw-r--r-- | host/lib/usrp/common/async_packet_handler.hpp | 6 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_twinrx.cpp | 2 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_xcvr2450.cpp | 2 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/twinrx/twinrx_ctrl.cpp | 4 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/twinrx/twinrx_ctrl.hpp | 4 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/twinrx/twinrx_experts.cpp | 29 | ||||
| -rw-r--r-- | host/lib/usrp/device3/device3_impl.hpp | 1 | ||||
| -rw-r--r-- | host/lib/usrp/device3/device3_io_impl.cpp | 214 | ||||
| -rw-r--r-- | host/lib/usrp/e300/e300_fifo_config.cpp | 39 | ||||
| -rw-r--r-- | host/lib/usrp/x300/x300_adc_dac_utils.cpp | 47 | ||||
| -rw-r--r-- | host/lib/usrp/x300/x300_impl.cpp | 63 | ||||
| -rw-r--r-- | host/lib/usrp/x300/x300_impl.hpp | 7 | ||||
| -rw-r--r-- | host/lib/usrp/x300/x300_radio_ctrl_impl.cpp | 20 | 
14 files changed, 260 insertions, 212 deletions
diff --git a/host/lib/rfnoc/legacy_compat.cpp b/host/lib/rfnoc/legacy_compat.cpp index dc711b909..70fe067f8 100644 --- a/host/lib/rfnoc/legacy_compat.cpp +++ b/host/lib/rfnoc/legacy_compat.cpp @@ -45,6 +45,7 @@ using uhd::stream_cmd_t;   ***********************************************************************/  static const std::string RADIO_BLOCK_NAME = "Radio";  static const std::string DFIFO_BLOCK_NAME = "DmaFIFO"; +static const std::string SFIFO_BLOCK_NAME = "FIFO";  static const std::string DDC_BLOCK_NAME = "DDC";  static const std::string DUC_BLOCK_NAME = "DUC";  static const size_t MAX_BYTES_PER_HEADER = @@ -122,6 +123,7 @@ public:          _has_ducs(not args.has_key("skip_duc") and not device->find_blocks(DUC_BLOCK_NAME).empty()),          _has_ddcs(not args.has_key("skip_ddc") and not device->find_blocks(DDC_BLOCK_NAME).empty()),          _has_dmafifo(not args.has_key("skip_dram") and not device->find_blocks(DFIFO_BLOCK_NAME).empty()), +        _has_sramfifo(not args.has_key("skip_sram") and not device->find_blocks(SFIFO_BLOCK_NAME).empty()),          _num_mboards(_tree->list("/mboards").size()),          _num_radios_per_board(device->find_blocks<radio_ctrl>("0/Radio").size()), // These might throw, maybe we catch that and provide a nicer error message.          _num_tx_chans_per_radio( @@ -155,9 +157,13 @@ public:              UHD_LOGGER_WARNING("RFNOC") << "[legacy_compat] No DUCs detected. You will only be able to transmit at the radio frontend rate." ;          }          if (args.has_key("skip_dram")) { -            UHD_LEGACY_LOG() << "[legacy_compat] Skipping DRAM by user request." ; -        } else if (not _has_dmafifo) { -            UHD_LOGGER_WARNING("RFNOC") << "[legacy_compat] No DMA FIFO detected. You will only be able to transmit at slow rates." ; +            UHD_LEGACY_LOG() << "[legacy_compat] Skipping DRAM by user request." << std::endl; +        } +        if (args.has_key("skip_sram")) { +            UHD_LEGACY_LOG() << "[legacy_compat] Skipping SRAM by user request." << std::endl; +        } +        if (not _has_dmafifo and not _has_sramfifo) { +            UHD_LOGGER_WARNING("RFNOC") << "[legacy_compat] No FIFO detected. Higher transmit rates may encounter errors.";          }          for (size_t mboard = 0; mboard < _num_mboards; mboard++) { @@ -513,7 +519,9 @@ private: // methods              size_t &port_index      ) {          if (dir == uhd::TX_DIRECTION) { -            if (_has_dmafifo) { +            if (_has_sramfifo) { +                return block_id_t(mboard_idx, SFIFO_BLOCK_NAME, radio_index).to_string(); +            } else if (_has_dmafifo) {                  port_index = radio_index;                  return block_id_t(mboard_idx, DFIFO_BLOCK_NAME, 0).to_string();              } else { @@ -727,7 +735,15 @@ private: // methods                              block_id_t(mboard, RADIO_BLOCK_NAME, radio), chan,                              tx_bpp                          ); -                        if (_has_dmafifo) { +                        // Prioritize SRAM over DRAM for performance +                        if (_has_sramfifo) { +                            // We have SRAM FIFO *and* DUCs +                            _graph->connect( +                                block_id_t(mboard, SFIFO_BLOCK_NAME, radio), chan, +                                block_id_t(mboard, DUC_BLOCK_NAME, radio), chan, +                                tx_bpp +                            ); +                        } else if (_has_dmafifo) {                              // We have DMA FIFO *and* DUCs                              _graph->connect(                                  block_id_t(mboard, DFIFO_BLOCK_NAME, 0), radio, @@ -735,6 +751,13 @@ private: // methods                                  tx_bpp                              );                          } +                    } else if (_has_sramfifo) { +                            // We have SRAM FIFO, *no* DUCs +                            _graph->connect( +                                block_id_t(mboard, SFIFO_BLOCK_NAME, radio), radio, +                                block_id_t(mboard, RADIO_BLOCK_NAME, radio), chan, +                                tx_bpp +                            );                      } else if (_has_dmafifo) {                              // We have DMA FIFO, *no* DUCs                              _graph->connect( @@ -834,6 +857,7 @@ private: // attributes      const bool _has_ducs;      const bool _has_ddcs;      const bool _has_dmafifo; +    const bool _has_sramfifo;      const size_t _num_mboards;      const size_t _num_radios_per_board;      const size_t _num_tx_chans_per_radio; diff --git a/host/lib/usrp/common/async_packet_handler.hpp b/host/lib/usrp/common/async_packet_handler.hpp index fd4fed81e..01594be0b 100644 --- a/host/lib/usrp/common/async_packet_handler.hpp +++ b/host/lib/usrp/common/async_packet_handler.hpp @@ -40,7 +40,11 @@ namespace uhd{ namespace usrp{          //load into metadata          metadata.channel = channel;          metadata.has_time_spec = if_packet_info.has_tsf; -        metadata.time_spec = time_spec_t::from_ticks(if_packet_info.tsf, tick_rate); +        if (tick_rate == 0.0) { +            metadata.time_spec = 0.0; +        } else { +            metadata.time_spec = time_spec_t::from_ticks(if_packet_info.tsf, tick_rate); +        }          metadata.event_code = async_metadata_t::event_code_t(to_host(payload[0]) & 0xff);          //load user payload diff --git a/host/lib/usrp/dboard/db_twinrx.cpp b/host/lib/usrp/dboard/db_twinrx.cpp index 91b38f90c..cedc26c36 100644 --- a/host/lib/usrp/dboard/db_twinrx.cpp +++ b/host/lib/usrp/dboard/db_twinrx.cpp @@ -112,7 +112,7 @@ public:              "los/LO2/freq/value", prepend_ch("los/LO2/freq/desired", _ch_name), prepend_ch("los/LO2/freq/coerced", _ch_name),              0.0, AUTO_RESOLVE_ON_READ_WRITE);          get_rx_subtree()->create<std::vector<std::string> >("los/all/source/options") -            .set(boost::assign::list_of("internal")("external")("companion")("disabled")); +            .set(boost::assign::list_of("internal")("external")("companion")("disabled")("reimport"));          expert_factory::add_prop_node<std::string>(_expert, get_rx_subtree(),              "los/all/source/value", prepend_ch("los/all/source", _ch_name),              "internal", AUTO_RESOLVE_ON_WRITE); diff --git a/host/lib/usrp/dboard/db_xcvr2450.cpp b/host/lib/usrp/dboard/db_xcvr2450.cpp index 62c5c39fb..fe6eca07b 100644 --- a/host/lib/usrp/dboard/db_xcvr2450.cpp +++ b/host/lib/usrp/dboard/db_xcvr2450.cpp @@ -371,7 +371,7 @@ double xcvr2450::set_lo_freq_core(double target_freq){      //variables used in the calculation below      double scaler = xcvr2450::is_highband(target_freq)? (4.0/5.0) : (4.0/3.0); -    double ref_freq = this->get_iface()->get_clock_rate(dboard_iface::UNIT_TX); +    double ref_freq = this->get_iface()->get_codec_rate(dboard_iface::UNIT_TX);      int R, intdiv = 131, fracdiv = 0;      //loop through values until we get a match diff --git a/host/lib/usrp/dboard/twinrx/twinrx_ctrl.cpp b/host/lib/usrp/dboard/twinrx/twinrx_ctrl.cpp index ddbbfaefb..00be82e1a 100644 --- a/host/lib/usrp/dboard/twinrx/twinrx_ctrl.cpp +++ b/host/lib/usrp/dboard/twinrx/twinrx_ctrl.cpp @@ -309,7 +309,7 @@ public:          boost::lock_guard<boost::mutex> lock(_mutex);          if (ch == CH1 or ch == BOTH) {              _cpld_regs->rf1_reg5.set(rm::rf1_reg5_t::SW14_CTRL_CH2, bool2bin(source!=LO_COMPANION)); -            _cpld_regs->rf1_reg1.set(rm::rf1_reg1_t::SW15_CTRL_CH1, bool2bin(source==LO_EXTERNAL)); +            _cpld_regs->rf1_reg1.set(rm::rf1_reg1_t::SW15_CTRL_CH1, bool2bin(source==LO_EXTERNAL||source==LO_REIMPORT));              _cpld_regs->rf1_reg1.set(rm::rf1_reg1_t::SW16_CTRL_CH1, bool2bin(source!=LO_INTERNAL));              _lo1_src[size_t(CH1)] = source;          } @@ -332,7 +332,7 @@ public:              _lo2_src[size_t(CH1)] = source;          }          if (ch == CH2 or ch == BOTH) { -            _cpld_regs->if0_reg4.set(rm::if0_reg4_t::SW19_CTRL_CH1, bool2bin(source==LO_EXTERNAL)); +            _cpld_regs->if0_reg4.set(rm::if0_reg4_t::SW19_CTRL_CH1, bool2bin(source==LO_EXTERNAL||source==LO_REIMPORT));              _cpld_regs->if0_reg0.set(rm::if0_reg0_t::SW20_CTRL_CH2, bool2bin(source==LO_INTERNAL||source==LO_DISABLED));              _cpld_regs->if0_reg4.set(rm::if0_reg4_t::SW21_CTRL_CH2, bool2bin(source==LO_INTERNAL));              _lo2_src[size_t(CH2)] = source; diff --git a/host/lib/usrp/dboard/twinrx/twinrx_ctrl.hpp b/host/lib/usrp/dboard/twinrx/twinrx_ctrl.hpp index 5537d00ab..2439addc4 100644 --- a/host/lib/usrp/dboard/twinrx/twinrx_ctrl.hpp +++ b/host/lib/usrp/dboard/twinrx/twinrx_ctrl.hpp @@ -35,7 +35,7 @@ public:      virtual ~twinrx_ctrl() {} -    enum channel_t { CH1 = 0, CH2 = 1, BOTH = 2}; +    enum channel_t { CH1 = 0, CH2 = 1, BOTH = 2 };      enum preamp_state_t { PREAMP_LOWBAND, PREAMP_HIGHBAND, PREAMP_BYPASS }; @@ -43,7 +43,7 @@ public:      enum preselector_path_t { PRESEL_PATH1, PRESEL_PATH2, PRESEL_PATH3, PRESEL_PATH4 }; -    enum lo_source_t { LO_INTERNAL, LO_EXTERNAL, LO_COMPANION, LO_DISABLED }; +    enum lo_source_t { LO_INTERNAL, LO_EXTERNAL, LO_COMPANION, LO_DISABLED, LO_REIMPORT };      enum lo_export_source_t { LO_CH1_SYNTH, LO_CH2_SYNTH, LO_EXPORT_DISABLED }; diff --git a/host/lib/usrp/dboard/twinrx/twinrx_experts.cpp b/host/lib/usrp/dboard/twinrx/twinrx_experts.cpp index 1a66e8fe7..c6a2d42c0 100644 --- a/host/lib/usrp/dboard/twinrx/twinrx_experts.cpp +++ b/host/lib/usrp/dboard/twinrx/twinrx_experts.cpp @@ -309,17 +309,18 @@ void twinrx_lo_config_expert::resolve()          ("internal",  twinrx_ctrl::LO_INTERNAL)          ("external",  twinrx_ctrl::LO_EXTERNAL)          ("companion", twinrx_ctrl::LO_COMPANION) -        ("disabled",  twinrx_ctrl::LO_DISABLED); +        ("disabled",  twinrx_ctrl::LO_DISABLED) +        ("reimport",  twinrx_ctrl::LO_REIMPORT);      if (src_lookup.has_key(_lo_source_ch0)) {          _lo1_src_ch0 = _lo2_src_ch0 = src_lookup[_lo_source_ch0];      } else { -        throw uhd::value_error("Invalid LO source for channel 0.Choose from {internal, external, companion}"); +        throw uhd::value_error("Invalid LO source for channel 0.Choose from {internal, external, companion, reimport}");      }      if (src_lookup.has_key(_lo_source_ch1)) {          _lo1_src_ch1 = _lo2_src_ch1 = src_lookup[_lo_source_ch1];      } else { -        throw uhd::value_error("Invalid LO source for channel 1.Choose from {internal, external, companion}"); +        throw uhd::value_error("Invalid LO source for channel 1.Choose from {internal, external, companion, reimport}");      }      twinrx_ctrl::lo_export_source_t export_src = twinrx_ctrl::LO_EXPORT_DISABLED; @@ -329,14 +330,16 @@ void twinrx_lo_config_expert::resolve()      if (_lo_export_ch1 and (_lo_source_ch1 == "external")) {          throw uhd::value_error("Cannot export an external LO for channel 1");      } + +    // Determine which channel will provide the exported LO      if (_lo_export_ch0 and _lo_export_ch1) {          throw uhd::value_error("Cannot export LOs for both channels");      } else if (_lo_export_ch0) { -        export_src = (_lo1_src_ch0 == twinrx_ctrl::LO_INTERNAL) ? -            twinrx_ctrl::LO_CH1_SYNTH : twinrx_ctrl::LO_CH2_SYNTH; -    } else if (_lo_export_ch1) { -        export_src = (_lo1_src_ch1 == twinrx_ctrl::LO_INTERNAL) ? +        export_src = (_lo1_src_ch0 == twinrx_ctrl::LO_COMPANION) ?              twinrx_ctrl::LO_CH2_SYNTH : twinrx_ctrl::LO_CH1_SYNTH; +    } else if (_lo_export_ch1) { +        export_src = (_lo1_src_ch1 == twinrx_ctrl::LO_COMPANION) ? +            twinrx_ctrl::LO_CH1_SYNTH : twinrx_ctrl::LO_CH2_SYNTH;      }      _lo1_export_src = _lo2_export_src = export_src;  } @@ -351,14 +354,16 @@ void twinrx_lo_mapping_expert::resolve()      static const size_t CH1_MSK = 0x2;      // Determine which channels are "driving" each synthesizer -    // First check for explicit requests i.e. lo_source "internal" or "companion" +    // First check for explicit requests +    // "internal" or "reimport" -> this channel +    // "companion" -> other channel      size_t synth_map[] = {0, 0}; -    if (_lox_src_ch0 == twinrx_ctrl::LO_INTERNAL) { +    if (_lox_src_ch0 == twinrx_ctrl::LO_INTERNAL or _lox_src_ch0 == twinrx_ctrl::LO_REIMPORT) {          synth_map[0] = synth_map[0] | CH0_MSK;      } else if (_lox_src_ch0 == twinrx_ctrl::LO_COMPANION) {          synth_map[1] = synth_map[1] | CH0_MSK;      } -    if (_lox_src_ch1 == twinrx_ctrl::LO_INTERNAL) { +    if (_lox_src_ch1 == twinrx_ctrl::LO_INTERNAL or _lox_src_ch1 == twinrx_ctrl::LO_REIMPORT) {          synth_map[1] = synth_map[1] | CH1_MSK;      } else if (_lox_src_ch1 == twinrx_ctrl::LO_COMPANION) {          synth_map[0] = synth_map[0] | CH1_MSK; @@ -370,7 +375,7 @@ void twinrx_lo_mapping_expert::resolve()      // to overlap tuning with signal dwell time.      bool hopping_enabled = false;      if (_lox_src_ch0 == twinrx_ctrl::LO_DISABLED) { -        if (_lox_src_ch1 == twinrx_ctrl::LO_INTERNAL) { +        if (_lox_src_ch1 == twinrx_ctrl::LO_INTERNAL or _lox_src_ch1 == twinrx_ctrl::LO_REIMPORT) {              synth_map[0] = synth_map[0] | CH0_MSK;              hopping_enabled = true;          } else if (_lox_src_ch1 == twinrx_ctrl::LO_COMPANION) { @@ -379,7 +384,7 @@ void twinrx_lo_mapping_expert::resolve()          }      }      if (_lox_src_ch1 == twinrx_ctrl::LO_DISABLED) { -        if (_lox_src_ch0 == twinrx_ctrl::LO_INTERNAL) { +        if (_lox_src_ch0 == twinrx_ctrl::LO_INTERNAL or _lox_src_ch0 == twinrx_ctrl::LO_REIMPORT) {              synth_map[1] = synth_map[1] | CH1_MSK;              hopping_enabled = true;          } else if (_lox_src_ch0 == twinrx_ctrl::LO_COMPANION) { diff --git a/host/lib/usrp/device3/device3_impl.hpp b/host/lib/usrp/device3/device3_impl.hpp index c2ec26f80..196d1fd4e 100644 --- a/host/lib/usrp/device3/device3_impl.hpp +++ b/host/lib/usrp/device3/device3_impl.hpp @@ -56,6 +56,7 @@ public:      //! The purpose of a transport      enum xport_type_t {          CTRL = 0, +        ASYNC_MSG,          TX_DATA,          RX_DATA      }; diff --git a/host/lib/usrp/device3/device3_io_impl.cpp b/host/lib/usrp/device3/device3_io_impl.cpp index 1668846c2..374232972 100644 --- a/host/lib/usrp/device3/device3_io_impl.cpp +++ b/host/lib/usrp/device3/device3_io_impl.cpp @@ -299,23 +299,17 @@ static void handle_rx_flowctrl(  /***********************************************************************   * TX Flow Control Functions   **********************************************************************/ +#define DEVICE3_ASYNC_EVENT_CODE_FLOW_CTRL 0 +  //! Stores the state of TX flow control  struct tx_fc_cache_t  { -    tx_fc_cache_t(void): -        stream_channel(0), -        device_channel(0), -        last_seq_out(0), +    tx_fc_cache_t(size_t capacity):          last_seq_ack(0), -        last_seq_ack_cache(0) {} +        space(capacity) {} -    size_t stream_channel; -    size_t device_channel; -    size_t last_seq_out; -    boost::atomic_size_t last_seq_ack; -    size_t last_seq_ack_cache; -    boost::shared_ptr<device3_impl::async_md_type> async_queue; -    boost::shared_ptr<device3_impl::async_md_type> old_async_queue; +    size_t last_seq_ack; +    size_t space;  };  /*! Return the size of the flow control window in packets. @@ -341,79 +335,77 @@ static size_t get_tx_flow_control_window(      return window_in_pkts;  } -// TODO: Remove this function -// This function only exists to make sure the transport is not destroyed -// until it is no longer needed. -static managed_send_buffer::sptr get_tx_buff( -    zero_copy_if::sptr xport, -    const double timeout -){ -    return xport->get_send_buff(timeout); -} -  static bool tx_flow_ctrl( -    task::sptr /*holds ref*/,      boost::shared_ptr<tx_fc_cache_t> fc_cache, -    size_t fc_window, +    zero_copy_if::sptr async_xport, +    uint32_t (*endian_conv)(uint32_t), +    void (*unpack)(const uint32_t *packet_buff, vrt::if_packet_info_t &),      managed_buffer::sptr  ) { -    bool refresh_cache = false; - -    // Busy loop waiting for flow control update.  This is necessary because -    // at this point there is data trying to be sent and it must be sent as -    // quickly as possible when the flow control update arrives to avoid -    // underruns at high rates.  This is also OK because it only occurs when -    // data needs to be sent and flow control is holding it back.      while (true)      { -        if (refresh_cache) +        // If there is space +        if (fc_cache->space)          { -            // update the cached value from the atomic -            fc_cache->last_seq_ack_cache = fc_cache->last_seq_ack; -        } - -        // delta is the amount of FC credit we've used up -        const size_t delta = (fc_cache->last_seq_out & HW_SEQ_NUM_MASK) - -            (fc_cache->last_seq_ack_cache & HW_SEQ_NUM_MASK); -        // If we want to send another packet, we must have FC credit left -        if ((delta & HW_SEQ_NUM_MASK) < fc_window) -        { -            // Packet will be sent -            fc_cache->last_seq_out++; //update seq +            // All is good - packet will be sent +            fc_cache->space--;              return true;          } -        else + +        // Look for a flow control message to update the space available in the buffer. +        // A minimal timeout is used because larger timeouts can cause the thread to be +        // scheduled out for too long at high data rates and result in underruns. +        managed_recv_buffer::sptr buff = async_xport->get_recv_buff(0.000001); +        if (buff)          { -            if (refresh_cache) +            vrt::if_packet_info_t if_packet_info; +            if_packet_info.num_packet_words32 = buff->size()/sizeof(uint32_t); +            const uint32_t *packet_buff = buff->cast<const uint32_t *>(); +            try { +                unpack(packet_buff, if_packet_info); +            } +            catch(const std::exception &ex)              { -                // We have already refreshed the cache and still -                // lack flow control permission to send new data. - -                // A true busy loop choked out the message handler -                // thread on machines with processor limitations -                // (too few cores).  Yield to allow flow control -                // receiver thread to operate. -                boost::this_thread::yield(); +                UHD_LOG_ERROR("TX FLOW CTRL", "Error unpacking async flow control packet: " << ex.what()); +                continue;              } -            else + +            if (if_packet_info.packet_type != vrt::if_packet_info_t::PACKET_TYPE_FC)              { -                // Allow the cache to refresh and try again to -                // see if the device has granted flow control permission. -                refresh_cache = true; +                UHD_LOG_ERROR( +                    "TX FLOW CTRL", +                    "Unexpected packet type received by flow control handler: " << if_packet_info.packet_type +                ); +                continue;              } + +            // update the amount of space +            size_t seq_ack = endian_conv(packet_buff[if_packet_info.num_header_words32+1]); +            fc_cache->space += (seq_ack - fc_cache->last_seq_ack) & HW_SEQ_NUM_MASK; +            fc_cache->last_seq_ack = seq_ack;          }      }      return false;  } -#define DEVICE3_ASYNC_EVENT_CODE_FLOW_CTRL 0 -/*! Handle incoming messages. If they're flow control, update the TX FC cache. - * Otherwise, send them to the async message queue for the user to poll. +/*********************************************************************** + * TX Async Message Functions + **********************************************************************/ +struct async_tx_info_t +{ +    size_t stream_channel; +    size_t device_channel; +    boost::shared_ptr<device3_impl::async_md_type> async_queue; +    boost::shared_ptr<device3_impl::async_md_type> old_async_queue; +}; + +/*! Handle incoming messages. + *  Send them to the async message queue for the user to poll.   *   * This is run inside a uhd::task as long as this streamer lives.   */  static void handle_tx_async_msgs( -        boost::shared_ptr<tx_fc_cache_t> fc_cache, +        boost::shared_ptr<async_tx_info_t> async_info,          zero_copy_if::sptr xport,          endianness_t endianness,          boost::function<double(void)> get_tick_rate @@ -463,31 +455,24 @@ static void handle_tx_async_msgs(              if_packet_info,              packet_buff,              tick_rate, -            fc_cache->stream_channel +            async_info->stream_channel      ); -    // TODO: Shouldn't we be polling if_packet_info.packet_type == PACKET_TYPE_FC? -    //       Thing is, on X300, packet_type == 0, so that wouldn't work. But it seems it should. -    //The FC response and the burst ack are two indicators that the radio -    //consumed packets. Use them to update the FC metadata -    if (metadata.event_code == DEVICE3_ASYNC_EVENT_CODE_FLOW_CTRL) { -        fc_cache->last_seq_ack = metadata.user_payload[0]; -    } - -    //FC responses don't propagate up to the user so filter them here -    if (metadata.event_code != DEVICE3_ASYNC_EVENT_CODE_FLOW_CTRL) { -        fc_cache->async_queue->push_with_pop_on_full(metadata); -        metadata.channel = fc_cache->device_channel; -        fc_cache->old_async_queue->push_with_pop_on_full(metadata); +    // Filter out any flow control messages and cache the rest +    if (metadata.event_code == DEVICE3_ASYNC_EVENT_CODE_FLOW_CTRL) +    { +        UHD_LOG_ERROR( +            "TX ASYNC", +            "Unexpected flow control message found in async message handling" +        ); +    } else { +        async_info->async_queue->push_with_pop_on_full(metadata); +        metadata.channel = async_info->device_channel; +        async_info->old_async_queue->push_with_pop_on_full(metadata);          standard_async_msg_prints(metadata);      }  } - - -/*********************************************************************** - * Async Data - **********************************************************************/  bool device3_impl::recv_async_msg(      async_metadata_t &async_metadata, double timeout  ) @@ -727,6 +712,20 @@ void device3_impl::update_tx_streamers(double /* rate */)      }  } +// This class manages the lifetime of the TX async message handler task and transports +class device3_send_packet_streamer : public sph::send_packet_streamer +{ +public: +	device3_send_packet_streamer(const size_t max_num_samps) : sph::send_packet_streamer(max_num_samps) {}; +	~device3_send_packet_streamer() { +		_tx_async_msg_task.reset();	// Make sure the async task is destroyed before the transports +	}; + +	both_xports_t _xport; +	both_xports_t _async_xport; +	task::sptr _tx_async_msg_task; +}; +  tx_streamer::sptr device3_impl::get_tx_stream(const uhd::stream_args_t &args_)  {      boost::mutex::scoped_lock lock(_transport_setup_mutex); @@ -742,7 +741,7 @@ tx_streamer::sptr device3_impl::get_tx_stream(const uhd::stream_args_t &args_)      boost::shared_ptr<async_md_type> async_md(new async_md_type(1000/*messages deep*/));      // II. Iterate over all channels -    boost::shared_ptr<sph::send_packet_streamer> my_streamer; +    boost::shared_ptr<device3_send_packet_streamer> my_streamer;      // The terminator's lifetime is coupled to the streamer.      // There is only one terminator. If the streamer has multiple channels,      // it will be connected to each downstream block. @@ -780,7 +779,8 @@ tx_streamer::sptr device3_impl::get_tx_stream(const uhd::stream_args_t &args_)          uhd::sid_t stream_address = blk_ctrl->get_address(block_port);          UHD_TX_STREAMER_LOG() << "creating tx stream " << tx_hints.to_string() ;          both_xports_t xport = make_transport(stream_address, TX_DATA, tx_hints); -        UHD_TX_STREAMER_LOG() << std::hex << "data_sid = " << xport.send_sid << std::dec ; +        both_xports_t async_xport = make_transport(stream_address, ASYNC_MSG, device_addr_t("")); +        UHD_TX_STREAMER_LOG() << std::hex << "[TX Streamer] data_sid = " << xport.send_sid << std::dec << std::endl;          // To calculate the max number of samples per packet, we assume the maximum header length          // to avoid fragmentation should the entire header be used. @@ -791,8 +791,10 @@ tx_streamer::sptr device3_impl::get_tx_stream(const uhd::stream_args_t &args_)          //make the new streamer given the samples per packet          if (not my_streamer) -            my_streamer = boost::make_shared<sph::send_packet_streamer>(spp); +            my_streamer = boost::make_shared<device3_send_packet_streamer>(spp);          my_streamer->resize(chan_list.size()); +        my_streamer->_xport = xport; +        my_streamer->_async_xport = async_xport;          //init some streamer stuff          std::string conv_endianness; @@ -828,29 +830,30 @@ tx_streamer::sptr device3_impl::get_tx_stream(const uhd::stream_args_t &args_)                  block_port          ); -        boost::shared_ptr<tx_fc_cache_t> fc_cache(new tx_fc_cache_t()); -        fc_cache->stream_channel = stream_i; -        fc_cache->device_channel = mb_index; -        fc_cache->async_queue = async_md; -        fc_cache->old_async_queue = _async_md; +        boost::shared_ptr<async_tx_info_t> async_tx_info(new async_tx_info_t()); +        async_tx_info->stream_channel = args.channels[stream_i]; +        async_tx_info->device_channel = mb_index; +        async_tx_info->async_queue = async_md; +        async_tx_info->old_async_queue = _async_md;          boost::function<double(void)> tick_rate_retriever = boost::bind(                  &rfnoc::tick_node_ctrl::get_tick_rate,                  send_terminator,                  std::set< rfnoc::node_ctrl_base::sptr >() // Need to specify default args with bind          ); -        task::sptr task = task::make( + +        my_streamer->_tx_async_msg_task = task::make(                  boost::bind(                      &handle_tx_async_msgs, -                    fc_cache, -                    xport.recv, +                    async_tx_info, +                    my_streamer->_async_xport.recv,                      xport.endianness,                      tick_rate_retriever                  )          );          blk_ctrl->sr_write(uhd::rfnoc::SR_CLEAR_RX_FC, 0xc1ea12, block_port); -        blk_ctrl->sr_write(uhd::rfnoc::SR_RESP_IN_DST_SID, xport.recv_sid.get_dst(), block_port); +        blk_ctrl->sr_write(uhd::rfnoc::SR_RESP_IN_DST_SID, my_streamer->_async_xport.recv_sid.get_dst(), block_port);          UHD_TX_STREAMER_LOG() << "resp_in_dst_sid == " << boost::format("0x%04X") % xport.recv_sid.get_dst() ;          // FIXME: Once there is a better way to map the radio block and port @@ -865,7 +868,7 @@ tx_streamer::sptr device3_impl::get_tx_stream(const uhd::stream_args_t &args_)              UHD_TX_STREAMER_LOG() << "Number of downstream radio nodes: " << downstream_radio_nodes.size();              for(const boost::shared_ptr<uhd::rfnoc::radio_ctrl> &node:  downstream_radio_nodes) {                  if (node->get_block_id() == radio_id) { -                    node->sr_write(uhd::rfnoc::SR_RESP_IN_DST_SID, xport.send_sid.get_src(), radio_port); +                    node->sr_write(uhd::rfnoc::SR_RESP_IN_DST_SID, my_streamer->_async_xport.recv_sid.get_dst(), radio_port);                  }              }          } else { @@ -878,24 +881,27 @@ tx_streamer::sptr device3_impl::get_tx_stream(const uhd::stream_args_t &args_)              std::vector<boost::shared_ptr<uhd::rfnoc::radio_ctrl> > downstream_radio_nodes = blk_ctrl->find_downstream_node<uhd::rfnoc::radio_ctrl>();              UHD_TX_STREAMER_LOG() << "Number of downstream radio nodes: " << downstream_radio_nodes.size();              for(const boost::shared_ptr<uhd::rfnoc::radio_ctrl> &node:  downstream_radio_nodes) { -                node->sr_write(uhd::rfnoc::SR_RESP_IN_DST_SID, xport.send_sid.get_src(), block_port); +                node->sr_write(uhd::rfnoc::SR_RESP_IN_DST_SID, my_streamer->_async_xport.recv_sid.get_dst(), block_port);              }          }          // Add flow control -        xport.send = zero_copy_flow_ctrl::make( -            xport.send, -            boost::bind(&tx_flow_ctrl, task, fc_cache, fc_window, _1), -            0 -        ); +        boost::shared_ptr<tx_fc_cache_t> fc_cache(new tx_fc_cache_t(fc_window)); +        my_streamer->_xport.send = zero_copy_flow_ctrl::make( +            my_streamer->_xport.send, +            boost::bind( +                &tx_flow_ctrl, +                fc_cache, +                my_streamer->_xport.recv, +                (xport.endianness == ENDIANNESS_BIG ? uhd::ntohx<uint32_t> : uhd::wtohx<uint32_t>), +                (xport.endianness == ENDIANNESS_BIG ? vrt::chdr::if_hdr_unpack_be : vrt::chdr::if_hdr_unpack_le), +                _1), +            NULL);          //Give the streamer a functor to get the send buffer -        //get_tx_buff is static so bind has no lifetime issues -        //xport.send (sptr) is required to add streamer->data-transport lifetime dependency -        //task (sptr) is required to add  a streamer->async-handler lifetime dependency          my_streamer->set_xport_chan_get_buff(              stream_i, -            boost::bind(&get_tx_buff, xport.send, _1) +            boost::bind(&zero_copy_if::get_send_buff, my_streamer->_xport.send, _1)          );          //Give the streamer a functor handled received async messages          my_streamer->set_async_receiver( diff --git a/host/lib/usrp/e300/e300_fifo_config.cpp b/host/lib/usrp/e300/e300_fifo_config.cpp index c4c563af6..89162a048 100644 --- a/host/lib/usrp/e300/e300_fifo_config.cpp +++ b/host/lib/usrp/e300/e300_fifo_config.cpp @@ -96,8 +96,8 @@ static UHD_INLINE size_t ZF_STREAM_OFF(const size_t which)  struct e300_fifo_poll_waiter  {      e300_fifo_poll_waiter(const int fd): -        fd(fd), -       _poll_claimed(false) +        _fd(fd), +        _poll_claimed(false)      {          //NOP      } @@ -108,29 +108,36 @@ struct e300_fifo_poll_waiter       */      void wait(const double timeout)      { -        if (_poll_claimed.exchange(true)) +        if (timeout == 0) { +            return; +        } + +        boost::mutex::scoped_lock l(_mutex); +        if (_poll_claimed)          { -            boost::mutex::scoped_lock l(mutex); -            cond.wait(l); +            _cond.timed_wait(l, boost::posix_time::microseconds(timeout*1000000));          }          else          { +            _poll_claimed = true; +            l.unlock();              struct pollfd fds[1]; -            fds[0].fd = fd; +            fds[0].fd = _fd;              fds[0].events = POLLIN;              ::poll(fds, 1, long(timeout*1000));              if (fds[0].revents & POLLIN) -                ::read(fd, NULL, 0); +                ::read(_fd, NULL, 0); -            _poll_claimed = false; -            cond.notify_all(); +            l.lock(); +            _poll_claimed = 0; +            _cond.notify_all();          }      } -    std::atomic_bool _poll_claimed; -    boost::condition_variable cond; -    boost::mutex mutex; -    int fd; +    boost::condition_variable _cond; +    boost::mutex _mutex; +    int _fd; +    bool _poll_claimed;  };  static const size_t DEFAULT_FRAME_SIZE = 2048; @@ -242,7 +249,7 @@ public:      UHD_INLINE typename T::sptr get_buff(const double timeout)      {          const time_spec_t exit_time = time_spec_t::get_system_time() + time_spec_t(timeout); -        do +        while (1)          {              if (zf_peek32(_addrs.ctrl + ARBITER_RB_STATUS_OCC))              { @@ -254,10 +261,12 @@ public:                      _index = 0;                  return _buffs[_index++]->get_new<T>();              } +            if (time_spec_t::get_system_time() > exit_time) { +                break; +            }              _waiter->wait(timeout);              //boost::this_thread::sleep(boost::posix_time::milliseconds(1));          } -        while (time_spec_t::get_system_time() < exit_time);          return typename T::sptr();      } diff --git a/host/lib/usrp/x300/x300_adc_dac_utils.cpp b/host/lib/usrp/x300/x300_adc_dac_utils.cpp index fda0bb6c9..05228a309 100644 --- a/host/lib/usrp/x300/x300_adc_dac_utils.cpp +++ b/host/lib/usrp/x300/x300_adc_dac_utils.cpp @@ -21,53 +21,6 @@  using namespace uhd::usrp::x300;  /*********************************************************************** - * DAC: Reset and synchronization operations - **********************************************************************/ - -void x300_impl::synchronize_dacs(const std::vector<radio_perifs_t*>& radios) -{ -    if (radios.size() < 2) return;  //Nothing to synchronize - -    //**PRECONDITION** -    //This function assumes that all the VITA times in "radios" are synchronized -    //to a common reference. Currently, this function is called in get_tx_stream -    //which also has the same precondition. - -    //Reinitialize and resync all DACs -    for (size_t i = 0; i < radios.size(); i++) { -        radios[i]->dac->reset(); -    } - -    //Get a rough estimate of the cumulative command latency -    boost::posix_time::ptime t_start = boost::posix_time::microsec_clock::local_time(); -    for (size_t i = 0; i < radios.size(); i++) { -        radios[i]->ctrl->peek64(uhd::usrp::radio::RB64_TIME_NOW); //Discard value. We are just timing the call -    } -    boost::posix_time::time_duration t_elapsed = -        boost::posix_time::microsec_clock::local_time() - t_start; - -    //Add 100% of headroom + uncertaintly to the command time -    uint64_t t_sync_us = (t_elapsed.total_microseconds() * 2) + 13000 /*Scheduler latency*/; - -    //Pick radios[0] as the time reference. -    uhd::time_spec_t sync_time = -        radios[0]->time64->get_time_now() + uhd::time_spec_t(((double)t_sync_us)/1e6); - -    //Send the sync command -    for (size_t i = 0; i < radios.size(); i++) { -        radios[i]->ctrl->set_time(sync_time); -        radios[i]->ctrl->poke32(uhd::usrp::radio::sr_addr(uhd::usrp::radio::DACSYNC), 0x1);    //Arm FRAMEP/N sync pulse -        radios[i]->ctrl->set_time(uhd::time_spec_t(0.0));   //Clear command time -    } - -    //Wait and check status -    boost::this_thread::sleep(boost::posix_time::microseconds(t_sync_us)); -    for (size_t i = 0; i < radios.size(); i++) { -        radios[i]->dac->verify_sync(); -    } -} - -/***********************************************************************   * ADC: Self-test operations   **********************************************************************/ diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp index 08865f9a6..4601b2789 100644 --- a/host/lib/usrp/x300/x300_impl.cpp +++ b/host/lib/usrp/x300/x300_impl.cpp @@ -542,6 +542,8 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)      eth_addrs.push_back(eth0_addr);      mb.next_src_addr = 0;   //Host source address for blocks +    mb.next_tx_src_addr = 0; +    mb.next_rx_src_addr = 0;      if (dev_addr.has_key("second_addr")) {          std::string eth1_addr = dev_addr["second_addr"]; @@ -1057,9 +1059,12 @@ x300_impl::~x300_impl(void)  uint32_t x300_impl::mboard_members_t::allocate_pcie_dma_chan(const uhd::sid_t &tx_sid, const xport_type_t xport_type)  {      static const uint32_t CTRL_CHANNEL       = 0; -    static const uint32_t FIRST_DATA_CHANNEL = 1; +    static const uint32_t ASYNC_MSG_CHANNEL  = 1; +    static const uint32_t FIRST_DATA_CHANNEL = 2;      if (xport_type == CTRL) {          return CTRL_CHANNEL; +    } else if (xport_type == ASYNC_MSG) { +        return ASYNC_MSG_CHANNEL;      } else {          // sid_t has no comparison defined, so we need to convert it uint32_t          uint32_t raw_sid = tx_sid.get(); @@ -1081,6 +1086,24 @@ static uint32_t extract_sid_from_pkt(void* pkt, size_t) {      return uhd::sid_t(uhd::wtohx(static_cast<const uint32_t*>(pkt)[1])).get_dst();  } +static uhd::transport::muxed_zero_copy_if::sptr make_muxed_pcie_msg_xport +( +    uhd::niusrprio::niusrprio_session::sptr rio_fpga_interface, +    uint32_t dma_channel_num, +    size_t max_muxed_ports +) { +    zero_copy_xport_params buff_args; +    buff_args.send_frame_size = X300_PCIE_MSG_FRAME_SIZE; +    buff_args.recv_frame_size = X300_PCIE_MSG_FRAME_SIZE; +    buff_args.num_send_frames = X300_PCIE_MSG_NUM_FRAMES * max_muxed_ports; +    buff_args.num_recv_frames = X300_PCIE_MSG_NUM_FRAMES * max_muxed_ports; + +    zero_copy_if::sptr base_xport = nirio_zero_copy::make( +        rio_fpga_interface, dma_channel_num, +        buff_args, uhd::device_addr_t()); +    return muxed_zero_copy_if::make(base_xport, extract_sid_from_pkt, max_muxed_ports); +} +  uhd::both_xports_t x300_impl::make_transport(      const uhd::sid_t &address,      const xport_type_t xport_type, @@ -1103,19 +1126,25 @@ uhd::both_xports_t x300_impl::make_transport(              if (not mb.ctrl_dma_xport) {                  //One underlying DMA channel will handle                  //all control traffic -                zero_copy_xport_params ctrl_buff_args; -                ctrl_buff_args.send_frame_size = X300_PCIE_MSG_FRAME_SIZE; -                ctrl_buff_args.recv_frame_size = X300_PCIE_MSG_FRAME_SIZE; -                ctrl_buff_args.num_send_frames = X300_PCIE_MSG_NUM_FRAMES * X300_PCIE_MAX_MUXED_XPORTS; -                ctrl_buff_args.num_recv_frames = X300_PCIE_MSG_NUM_FRAMES * X300_PCIE_MAX_MUXED_XPORTS; - -                zero_copy_if::sptr base_xport = nirio_zero_copy::make( -                    mb.rio_fpga_interface, dma_channel_num, -                    ctrl_buff_args, uhd::device_addr_t()); -                mb.ctrl_dma_xport = muxed_zero_copy_if::make(base_xport, extract_sid_from_pkt, X300_PCIE_MAX_MUXED_XPORTS); +                mb.ctrl_dma_xport = make_muxed_pcie_msg_xport( +                    mb.rio_fpga_interface, +                    dma_channel_num, +                    X300_PCIE_MAX_MUXED_CTRL_XPORTS);              }              //Create a virtual control transport              xports.recv = mb.ctrl_dma_xport->make_stream(xports.recv_sid.get_dst()); +        } else if (xport_type == ASYNC_MSG) { +            //Transport for async message stream +            if (not mb.async_msg_dma_xport) { +                //One underlying DMA channel will handle +                //all async message traffic +                mb.async_msg_dma_xport = make_muxed_pcie_msg_xport( +                    mb.rio_fpga_interface, +                    dma_channel_num, +                    X300_PCIE_MAX_MUXED_ASYNC_XPORTS); +            } +            //Create a virtual async message transport +            xports.recv = mb.async_msg_dma_xport->make_stream(xports.recv_sid.get_dst());          } else {              //Transport for data stream              default_buff_args.send_frame_size = @@ -1157,12 +1186,16 @@ uhd::both_xports_t x300_impl::make_transport(      } else if (mb.xport_path == "eth") {          // Decide on the IP/Interface pair based on the endpoint index -        std::string interface_addr = mb.eth_conns[mb.next_src_addr].addr; +        size_t &next_src_addr = +            xport_type == TX_DATA ? mb.next_tx_src_addr : +            xport_type == RX_DATA ? mb.next_rx_src_addr : +            mb.next_src_addr; +        std::string interface_addr = mb.eth_conns[next_src_addr].addr;          const uint32_t xbar_src_addr = -            mb.next_src_addr==0 ? X300_SRC_ADDR0 : X300_SRC_ADDR1; +            next_src_addr==0 ? X300_SRC_ADDR0 : X300_SRC_ADDR1;          const uint32_t xbar_src_dst = -            mb.eth_conns[mb.next_src_addr].type==X300_IFACE_ETH0 ? X300_XB_DST_E0 : X300_XB_DST_E1; -        mb.next_src_addr = (mb.next_src_addr + 1) % mb.eth_conns.size(); +            mb.eth_conns[next_src_addr].type==X300_IFACE_ETH0 ? X300_XB_DST_E0 : X300_XB_DST_E1; +        next_src_addr = (next_src_addr + 1) % mb.eth_conns.size();          xports.send_sid = this->allocate_sid(mb, address, xbar_src_addr, xbar_src_dst);          xports.recv_sid = xports.send_sid.reversed(); diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp index 27f3f130e..7186e5f4f 100644 --- a/host/lib/usrp/x300/x300_impl.hpp +++ b/host/lib/usrp/x300/x300_impl.hpp @@ -63,7 +63,8 @@ static const size_t X300_PCIE_TX_DATA_NUM_FRAMES	    = 4096;  static const size_t X300_PCIE_MSG_FRAME_SIZE            = 256;      //bytes  static const size_t X300_PCIE_MSG_NUM_FRAMES            = 64;  static const size_t X300_PCIE_MAX_CHANNELS              = 6; -static const size_t X300_PCIE_MAX_MUXED_XPORTS          = 32; +static const size_t X300_PCIE_MAX_MUXED_CTRL_XPORTS     = 32; +static const size_t X300_PCIE_MAX_MUXED_ASYNC_XPORTS    = 4;  static const size_t X300_10GE_DATA_FRAME_MAX_SIZE   = 8000;     // CHDR packet size in bytes  static const size_t X300_1GE_DATA_FRAME_MAX_SIZE    = 1472;     // CHDR packet size in bytes @@ -166,6 +167,8 @@ private:          std::vector<x300_eth_conn_t> eth_conns;          size_t next_src_addr; +        size_t next_tx_src_addr; +        size_t next_rx_src_addr;          // Discover the ethernet connections per motherboard          void discover_eth(const uhd::usrp::mboard_eeprom_t mb_eeprom, @@ -207,6 +210,8 @@ private:          std::map<uint32_t, uint32_t> _dma_chan_pool;          //! Control transport for one PCIe connection          uhd::transport::muxed_zero_copy_if::sptr ctrl_dma_xport; +        //! Async message transport +        uhd::transport::muxed_zero_copy_if::sptr async_msg_dma_xport;          /*! Allocate or return a previously allocated PCIe channel pair           * diff --git a/host/lib/usrp/x300/x300_radio_ctrl_impl.cpp b/host/lib/usrp/x300/x300_radio_ctrl_impl.cpp index daae309c3..1a37cbdd1 100644 --- a/host/lib/usrp/x300/x300_radio_ctrl_impl.cpp +++ b/host/lib/usrp/x300/x300_radio_ctrl_impl.cpp @@ -870,8 +870,14 @@ void x300_radio_ctrl_impl::synchronize_dacs(const std::vector<x300_radio_ctrl_im      boost::posix_time::time_duration t_elapsed =          boost::posix_time::microsec_clock::local_time() - t_start; -    //Add 100% of headroom + uncertaintly to the command time -    uint64_t t_sync_us = (t_elapsed.total_microseconds() * 2) + 13000 /*Scheduler latency*/; +    //Set tick rate and make sure FRAMEP/N is 0 +    for (size_t i = 0; i < radios.size(); i++) { +        radios[i]->set_command_tick_rate(radios[i]->_radio_clk_rate, IO_MASTER_RADIO); +        radios[i]->_regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::DAC_SYNC, 0); +    } + +    //Add 100% of headroom + uncertainty to the command time +    uint64_t t_sync_us = (t_elapsed.total_microseconds() * 2) + 16000 /*Scheduler latency*/;      //Pick radios[0] as the time reference.      uhd::time_spec_t sync_time = @@ -879,15 +885,17 @@ void x300_radio_ctrl_impl::synchronize_dacs(const std::vector<x300_radio_ctrl_im      //Send the sync command      for (size_t i = 0; i < radios.size(); i++) { -        radios[i]->set_command_tick_rate(radios[i]->_radio_clk_rate, IO_MASTER_RADIO); -        radios[i]->_regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_SYNC, 0);          radios[i]->set_command_time(sync_time, IO_MASTER_RADIO);          //Arm FRAMEP/N sync pulse by asserting a rising edge -        radios[i]->_regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_SYNC, 1); -        radios[i]->_regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_SYNC, 0); +        radios[i]->_regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::DAC_SYNC, 1);          radios[i]->set_command_time(uhd::time_spec_t(0.0), IO_MASTER_RADIO);      } +    //Reset FRAMEP/N to 0 +    for (size_t i = 0; i < radios.size(); i++) { +        radios[i]->_regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::DAC_SYNC, 0); +    } +      //Wait and check status      boost::this_thread::sleep(boost::posix_time::microseconds(t_sync_us));      for (size_t i = 0; i < radios.size(); i++) {  | 
