Reviewed UHD Tx FSM

This commit is contained in:
Xavier Arteaga 2021-04-26 19:07:42 +02:00 committed by Xavier Arteaga
parent 3413f29cc8
commit 9a4fcb605b
1 changed files with 93 additions and 51 deletions

View File

@ -37,22 +37,25 @@
* - BURST: A burst has started
* - END_OF_BURST: An underflow, overflow or late has been detected, the next transmission shall be aborted and an end
* of burst will be send in the next transmission;
* - WAIT_EOB_ACK: Waits for either an end of burst ACK event or a transmission after EOB_ACK_TIMEOUT_S the
* Late/Underflow occurred.
* - START_BURST: The next transmission will be flagged as start of burst.
*
* +-------+ L/O/U detected +--------------+ EoB Sent +-------------+
* | Burst |--------------->| End-of-burst |------------->| Start burst |<--- Initial state
* +-------+ | +--------------+ ^ +-------------+
* ^ | | |
* | | Burst ACK | |
* | +-------------------------------------+ |
* | |
* | Start of burst is transmitted |
* +----------------------------------------------------------+
* +-------+ L/O/U detected +--------------+ EoB Sent +--------------+ EOB ACK Rx +-------------+
* | Burst |--------------->| End-of-burst |------------>| Wait EOB ACK |------------>| Start burst |<-- Initial state
* +-------+ +--------------+ +--------------+ +-------------+
* ^ | |
* | | New Transmission | New Transmission
* | | (TS timed out) |
* | | |
* | Start of burst is transmitted | |
* +---------------------------------------------------------+----------------------------+
*/
typedef enum {
RF_UHD_IMP_TX_STATE_START_BURST = 0,
RF_UHD_IMP_TX_STATE_BURST,
RF_UHD_IMP_TX_STATE_END_OF_BURST,
RF_UHD_IMP_TX_STATE_WAIT_EOB_ACK ///< Wait for enb-of-burst ACK
} rf_uhd_imp_underflow_state_t;
/**
@ -68,6 +71,11 @@ const std::set<std::string> RF_UHD_IMP_PROHIBITED_STREAM_REMAKE = {DEVNAME_X300,
DEVNAME_E3X0,
DEVNAME_B200};
/**
* List of devices that do NOT support end of burst flushing
*/
const std::set<std::string> RF_UHD_IMP_PROHIBITED_EOB_FLUSH = {DEVNAME_X300, DEVNAME_N300};
/**
* List of devices that do NOT require/support to restart streaming after rate changes/timeouts
*/
@ -110,6 +118,11 @@ static const std::chrono::milliseconds RF_UHD_IMP_ASYNCH_MSG_SLEEP_MS = std::chr
*/
static const uint32_t RF_UHD_IMP_MAX_RX_TRIALS = 100;
/**
* Timeout for end of burst ack.
*/
static const double RF_UHD_IMP_WAIT_EOB_ACK_TIMEOUT_S = 2.0;
struct rf_uhd_handler_t {
size_t id;
@ -130,6 +143,7 @@ struct rf_uhd_handler_t {
srsran_rf_error_handler_t uhd_error_handler = nullptr;
void* uhd_error_handler_arg = nullptr;
rf_uhd_imp_underflow_state_t tx_state = RF_UHD_IMP_TX_STATE_START_BURST;
uhd::time_spec_t eob_ack_timeout = {}; //< Set when a Underflow/Late happens
double current_master_clock = 0.0;
@ -241,8 +255,10 @@ static void log_rx_error(rf_uhd_handler_t* h)
#if HAVE_ASYNC_THREAD
static void* async_thread(void* h)
{
rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h;
uhd::async_metadata_t md;
rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h;
uhd::async_metadata_t md = {};
uhd::time_spec_t last_underflow_ts = {};
uhd::time_spec_t last_late_ts = {};
while (handler->async_thread_running) {
std::unique_lock<std::mutex> lock(handler->async_mutex);
@ -264,12 +280,20 @@ static void* async_thread(void* h)
const uhd::async_metadata_t::event_code_t& event_code = md.event_code;
if (event_code == uhd::async_metadata_t::EVENT_CODE_UNDERFLOW ||
event_code == uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET) {
log_underflow(handler);
if (md.time_spec != last_underflow_ts) {
last_underflow_ts = md.time_spec;
handler->eob_ack_timeout = md.time_spec + RF_UHD_IMP_WAIT_EOB_ACK_TIMEOUT_S;
log_underflow(handler);
}
} else if (event_code == uhd::async_metadata_t::EVENT_CODE_TIME_ERROR) {
log_late(handler, false);
if (md.time_spec != last_late_ts) {
last_late_ts = md.time_spec;
handler->eob_ack_timeout = md.time_spec + RF_UHD_IMP_WAIT_EOB_ACK_TIMEOUT_S;
log_late(handler, false);
}
} else if (event_code == uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
// Makes sure next block will be start of burst
if (handler->tx_state == RF_UHD_IMP_TX_STATE_BURST) {
if (handler->tx_state == RF_UHD_IMP_TX_STATE_WAIT_EOB_ACK) {
handler->tx_state = RF_UHD_IMP_TX_STATE_START_BURST;
}
} else {
@ -970,8 +994,8 @@ static inline int rf_uhd_imp_end_burst(rf_uhd_handler_t* handler)
return SRSRAN_ERROR;
}
// Update TX state
handler->tx_state = RF_UHD_IMP_TX_STATE_START_BURST;
// Update TX state to wait for end of burst ACK
handler->tx_state = RF_UHD_IMP_TX_STATE_WAIT_EOB_ACK;
return SRSRAN_SUCCESS;
}
@ -1287,7 +1311,7 @@ int rf_uhd_recv_with_time_multi(void* h,
}
// Receive stream in multiple blocks
while (rxd_samples_total < nsamples && trials < RF_UHD_IMP_MAX_RX_TRIALS) {
while (rxd_samples_total < nsamples and trials < RF_UHD_IMP_MAX_RX_TRIALS) {
void* buffs_ptr[SRSRAN_MAX_CHANNELS] = {};
for (uint32_t i = 0; i < handler->nof_rx_channels; i++) {
cf_t* data_c = (cf_t*)data[i];
@ -1380,36 +1404,14 @@ int rf_uhd_send_timed_multi(void* h,
std::unique_lock<std::mutex> lock(handler->tx_mutex);
uhd::tx_metadata_t md;
void* buffs_ptr[SRSRAN_MAX_CHANNELS] = {};
size_t txd_samples = 0;
int n = 0;
int n = 0; //< Counts transmitted samples
// Check Tx stream has been created
if (not handler->uhd->is_tx_ready()) {
return SRSRAN_ERROR;
}
// Run Underflow recovery state machine
switch (handler->tx_state) {
case RF_UHD_IMP_TX_STATE_BURST:
// Normal case, do nothing
break;
case RF_UHD_IMP_TX_STATE_END_OF_BURST:
// Send end of burst and ignore transmission
if (rf_uhd_imp_end_burst(handler) != SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
// Flush receiver
rf_uhd_flush_buffer(h);
return SRSRAN_ERROR;
case RF_UHD_IMP_TX_STATE_START_BURST:
// Set tart of burst to false if recovering from the Underflow
is_start_of_burst = true;
handler->tx_state = RF_UHD_IMP_TX_STATE_BURST;
break;
}
// Set Tx timestamp
if (not has_time_spec) {
// If it the beginning of a burst, set timestamp
if (is_start_of_burst) {
@ -1441,15 +1443,40 @@ int rf_uhd_send_timed_multi(void* h,
do {
size_t tx_samples = handler->tx_nof_samples;
// Set start of burst. Time spec only for the first packet in the burst
md.start_of_burst = is_start_of_burst;
// If an Underflow or a Late has been detected, end the burst immediately
if (handler->tx_state == RF_UHD_IMP_TX_STATE_END_OF_BURST) {
// Send end of burst and ignore transmission
if (rf_uhd_imp_end_burst(handler) != SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
// some devices don't like timestamps in each call
// Flush receiver only if allowed
if (RF_UHD_IMP_PROHIBITED_EOB_FLUSH.count(handler->devname) == 0) {
rf_uhd_flush_buffer(h);
}
return SRSRAN_ERROR;
}
// If the state is waiting for EOB ACK and the metadata of the current packet has passed the timeout, then start the
// burst
if (handler->tx_state == RF_UHD_IMP_TX_STATE_WAIT_EOB_ACK and md.time_spec >= handler->eob_ack_timeout) {
Info("Tx while waiting for EOB, timed out... " << md.time_spec.get_real_secs()
<< " >= " << handler->eob_ack_timeout.get_real_secs()
<< ". Starting new burst...");
handler->tx_state = RF_UHD_IMP_TX_STATE_START_BURST;
}
// Set start of burst, ignore function argument and set the flag based on the current Tx state
md.start_of_burst = (handler->tx_state == RF_UHD_IMP_TX_STATE_START_BURST);
// Time spec only for the first packet in the burst, some devices are not capable of handling like timestamps for
// each baseband packet
if (RF_UHD_IMP_TIMESPEC_AT_BURST_START_ONLY.count(handler->devname) == 0) {
md.has_time_spec = is_start_of_burst or has_time_spec;
md.has_time_spec = md.start_of_burst or has_time_spec;
} else {
// only set time for start
md.has_time_spec = is_start_of_burst and has_time_spec;
md.has_time_spec = md.start_of_burst and has_time_spec;
}
// middle packets are never end of burst, last one as defined
@ -1460,20 +1487,35 @@ int rf_uhd_send_timed_multi(void* h,
md.end_of_burst = is_end_of_burst;
}
// Update data pointers
for (int i = 0; i < SRSRAN_MAX_CHANNELS; i++) {
void* buff = (void*)&data_c[i][n];
buffs_ptr[i] = buff;
}
if (handler->uhd->send(buffs_ptr, tx_samples, md, RF_UHD_IMP_TRX_TIMEOUT_S, txd_samples) != UHD_ERROR_NONE) {
print_usrp_error(handler);
return SRSRAN_ERROR;
size_t txd_samples = tx_samples; //< Stores the number of transmitted samples in this packet
// Skip baseband packet transmission if it is waiting for the enb of burst ACK
if (handler->tx_state != RF_UHD_IMP_TX_STATE_WAIT_EOB_ACK) {
// Actual transmission
if (handler->uhd->send(buffs_ptr, tx_samples, md, RF_UHD_IMP_TRX_TIMEOUT_S, txd_samples) != UHD_ERROR_NONE) {
print_usrp_error(handler);
return SRSRAN_ERROR;
}
// Tx state is now in burst
if (md.start_of_burst) {
handler->tx_state = RF_UHD_IMP_TX_STATE_BURST;
}
} else {
Debug("Tx while waiting for EOB, aborting block... " << md.time_spec.get_real_secs() << " < "
<< handler->eob_ack_timeout.get_real_secs());
}
// Next packets are not start of burst
is_start_of_burst = false;
// Increase the metadata transmit time
md.time_spec += txd_samples / handler->tx_rate;
// Increase number of transmitted samples
n += txd_samples;
} while (n < nsamples);