aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib')
-rw-r--r--host/lib/rfnoc/legacy_compat.cpp34
-rw-r--r--host/lib/usrp/common/async_packet_handler.hpp6
-rw-r--r--host/lib/usrp/dboard/db_twinrx.cpp2
-rw-r--r--host/lib/usrp/dboard/db_xcvr2450.cpp2
-rw-r--r--host/lib/usrp/dboard/twinrx/twinrx_ctrl.cpp4
-rw-r--r--host/lib/usrp/dboard/twinrx/twinrx_ctrl.hpp4
-rw-r--r--host/lib/usrp/dboard/twinrx/twinrx_experts.cpp29
-rw-r--r--host/lib/usrp/device3/device3_impl.hpp1
-rw-r--r--host/lib/usrp/device3/device3_io_impl.cpp214
-rw-r--r--host/lib/usrp/e300/e300_fifo_config.cpp39
-rw-r--r--host/lib/usrp/x300/x300_adc_dac_utils.cpp47
-rw-r--r--host/lib/usrp/x300/x300_impl.cpp63
-rw-r--r--host/lib/usrp/x300/x300_impl.hpp7
-rw-r--r--host/lib/usrp/x300/x300_radio_ctrl_impl.cpp20
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++) {