Asynchronous NR PHY configuration (#3527)

* Fix a race condition when accessing the NR PHY cfg by the RRC and phy workers.
Rework how the phy cfg is handled, now workers have their own copy that gets updated after a reconfig moving it out of the state class.

* Default initialize sf_len member in sf_worker for consistency.

* Asynchronous NR PHY configuration

* Fix compilation

* Corrected method override and fix unitialised value

* Added carrier equal comparison to avoid aligment byte padding comparison

Co-authored-by: faluco <borja.ferrer@softwareradiosystems.com>
This commit is contained in:
Xavier Arteaga 2021-10-25 10:51:02 +02:00 committed by GitHub
parent 28887a4384
commit ebab12403f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 306 additions and 116 deletions

View File

@ -49,6 +49,11 @@ struct phy_cfg_nr_t {
phy_cfg_nr_t() {}
bool carrier_is_equal(const srsran::phy_cfg_nr_t& other) const
{
return srsran_carrier_nr_equal(&carrier, &other.carrier);
}
/**
* @brief Computes the DCI configuration for the current UE configuration
*/

View File

@ -25,9 +25,10 @@ namespace srsue {
class rrc_interface_phy_nr
{
public:
virtual void in_sync() = 0;
virtual void out_of_sync() = 0;
virtual void run_tti(const uint32_t tti) = 0;
virtual void in_sync() = 0;
virtual void out_of_sync() = 0;
virtual void run_tti(const uint32_t tti) = 0;
virtual void set_phy_config_complete(bool status) = 0;
};
class mac_interface_phy_nr

View File

@ -718,6 +718,14 @@ SRSRAN_API const char* srsran_ssb_pattern_to_str(srsran_ssb_patern_t pattern);
*/
SRSRAN_API srsran_ssb_patern_t srsran_ssb_pattern_fom_str(const char* str);
/**
* @brief Compares if two NR carrier structures are equal
* @param a First carrier to compare
* @param b Second carrier to compare
* @return True if all the carrier structure fields are equal, otherwise false
*/
SRSRAN_API bool srsran_carrier_nr_equal(const srsran_carrier_nr_t* a, const srsran_carrier_nr_t* b);
#ifdef __cplusplus
}
#endif

View File

@ -730,3 +730,22 @@ srsran_ssb_patern_t srsran_ssb_pattern_fom_str(const char* str)
return SRSRAN_SSB_PATTERN_INVALID;
}
bool srsran_carrier_nr_equal(const srsran_carrier_nr_t* a, const srsran_carrier_nr_t* b)
{
if (a == NULL || b == NULL) {
return false;
}
bool ret = (a->pci == b->pci);
ret = ret && (a->dl_center_frequency_hz == b->dl_center_frequency_hz);
ret = ret && (a->ul_center_frequency_hz == b->ul_center_frequency_hz);
ret = ret && (a->ssb_center_freq_hz == b->ssb_center_freq_hz);
ret = ret && (a->offset_to_carrier == b->offset_to_carrier);
ret = ret && (a->scs == b->scs);
ret = ret && (a->nof_prb == b->nof_prb);
ret = ret && (a->start == b->start);
ret = ret && (a->max_mimo_layers == b->max_mimo_layers);
return ret;
}

View File

@ -22,10 +22,10 @@ namespace nr {
class cc_worker
{
public:
cc_worker(uint32_t cc_idx, srslog::basic_logger& log, state& phy_state_);
cc_worker(uint32_t cc_idx, srslog::basic_logger& log, state& phy_state_, const srsran::phy_cfg_nr_t& cfg);
~cc_worker();
bool update_cfg();
void update_cfg(const srsran::phy_cfg_nr_t& new_config);
void set_tti(uint32_t tti);
cf_t* get_rx_buffer(uint32_t antenna_idx);
@ -50,6 +50,7 @@ private:
std::array<cf_t*, SRSRAN_MAX_PORTS> tx_buffer = {};
uint32_t buffer_sz = 0;
state& phy;
srsran::phy_cfg_nr_t cfg;
srsran_ssb_t ssb = {};
srsran_ue_dl_nr_t ue_dl = {};
srsran_ue_ul_nr_t ue_ul = {};

View File

@ -31,17 +31,25 @@ namespace nr {
class sf_worker final : public srsran::thread_pool::worker
{
public:
sf_worker(srsran::phy_common_interface& common_, state& phy_state_, srslog::basic_logger& logger);
sf_worker(srsran::phy_common_interface& common_,
state& phy_state_,
const srsran::phy_cfg_nr_t& cfg,
srslog::basic_logger& logger);
~sf_worker() = default;
bool update_cfg(uint32_t cc_idx);
/* Functions used by main PHY thread */
cf_t* get_buffer(uint32_t cc_idx, uint32_t antenna_idx);
uint32_t get_buffer_len();
void set_context(const srsran::phy_common_interface::worker_context_t& w_ctx);
int read_pdsch_d(cf_t* pdsch_d);
void start_plot();
void set_cfg(const srsran::phy_cfg_nr_t& new_cfg)
{
for (unsigned i = 0, e = cc_workers.size(); i != e; ++i) {
update_cfg(i, new_cfg);
}
sf_len = SRSRAN_SF_LEN_PRB_NR(new_cfg.carrier.nof_prb);
}
void set_prach(cf_t* prach_ptr, float prach_power);
@ -49,6 +57,9 @@ private:
/* Inherited from thread_pool::worker. Function called every subframe to run the DL/UL processing */
void work_imp() override;
void update_cfg(uint32_t cc_idx, const srsran::phy_cfg_nr_t& new_cfg);
private:
std::vector<std::unique_ptr<cc_worker> > cc_workers;
srsran::phy_common_interface& common;
@ -59,6 +70,7 @@ private:
cf_t* prach_ptr = nullptr;
float prach_power = 0;
srsran::phy_common_interface::worker_context_t context = {};
uint32_t sf_len = 0;
};
} // namespace nr

View File

@ -84,9 +84,6 @@ public:
/// Physical layer user configuration
phy_args_nr_t args = {};
/// Physical layer higher layer configuration, provided by higher layers through configuration messages
srsran::phy_cfg_nr_t cfg = {};
/// Semaphore for aligning UL work
srsran::tti_semaphore<void*> dl_ul_semaphore;
@ -101,10 +98,13 @@ public:
/**
* @brief Stores a received UL DCI into the pending UL grant list
* @param cfg Physical layer configuration object
* @param slot_rx The TTI in which the grant was received
* @param dci_ul The UL DCI message to store
*/
void set_ul_pending_grant(const srsran_slot_cfg_t& slot_rx, const srsran_dci_ul_nr_t& dci_ul)
void set_ul_pending_grant(const srsran::phy_cfg_nr_t& cfg,
const srsran_slot_cfg_t& slot_rx,
const srsran_dci_ul_nr_t& dci_ul)
{
// Convert UL DCI to grant
srsran_sch_cfg_nr_t pusch_cfg = {};
@ -160,10 +160,12 @@ public:
/**
* @brief Stores a received DL DCI into the pending DL grant list
* @param cfg Physical layer configuration object
* @param tti_rx The TTI in which the grant was received
* @param dci_dl The DL DCI message to store
*/
void set_dl_pending_grant(const srsran_slot_cfg_t& slot, const srsran_dci_dl_nr_t& dci_dl)
void
set_dl_pending_grant(const srsran::phy_cfg_nr_t& cfg, const srsran_slot_cfg_t& slot, const srsran_dci_dl_nr_t& dci_dl)
{
// Convert DL DCI to grant
srsran_sch_cfg_nr_t pdsch_cfg = {};
@ -289,7 +291,7 @@ public:
reset_measurements();
}
bool has_valid_sr_resource(uint32_t sr_id)
bool has_valid_sr_resource(const srsran::phy_cfg_nr_t& cfg, uint32_t sr_id)
{
for (const srsran_pucch_nr_sr_resource_t& r : cfg.pucch.sr_resources) {
if (r.configured && r.sr_id == sr_id) {
@ -310,7 +312,7 @@ public:
pending_ack = {};
}
void get_pending_sr(const uint32_t& tti, srsran_uci_data_nr_t& uci_data)
void get_pending_sr(const srsran::phy_cfg_nr_t& cfg, const uint32_t& tti, srsran_uci_data_nr_t& uci_data)
{
// Calculate all SR opportunities in the given TTI
uint32_t sr_resource_id[SRSRAN_PUCCH_MAX_NOF_SR_RESOURCES] = {};
@ -344,7 +346,8 @@ public:
uci_data.value.sr = sr_count_positive;
}
void get_periodic_csi(const srsran_slot_cfg_t& slot_cfg, srsran_uci_data_nr_t& uci_data)
void
get_periodic_csi(const srsran::phy_cfg_nr_t& cfg, const srsran_slot_cfg_t& slot_cfg, srsran_uci_data_nr_t& uci_data)
{
// Generate report configurations
int n = srsran_csi_reports_generate(&cfg.csi, &slot_cfg, uci_data.cfg.csi);
@ -451,10 +454,12 @@ public:
/**
* @brief Processes a new NZP-CSI-RS channel measurement
* @param cfg Physical layer configuration object
* @param new_measure New measurement
* @param resource_set_id NZP-CSI-RS resource set identifier used for the channel measurement
*/
void new_nzp_csi_rs_channel_measurement(const srsran_csi_channel_measurements_t& new_measure,
void new_nzp_csi_rs_channel_measurement(const srsran::phy_cfg_nr_t& cfg,
const srsran_csi_channel_measurements_t& new_measure,
uint32_t resource_set_id)
{
std::lock_guard<std::mutex> lock(csi_measurements_mutex);
@ -469,12 +474,14 @@ public:
/**
* @brief Processes a new Tracking Reference Signal (TRS) measurement
* @param new_measure New measurement
* @param cfg Physical layer configuration object
* @param resource_set_id NZP-CSI-RS resource set identifier used for the channel measurement if it is configured from
* a NZP-CSI-RS
* @param K_csi_rs Number of NZP-CSI-RS resources used for the measurement, set to 0 if another type of signal is
* measured (i.e. SSB)
*/
void new_csi_trs_measurement(const srsran_csi_trs_measurements_t& new_meas,
const srsran::phy_cfg_nr_t& cfg,
uint32_t resource_set_id = 0,
uint32_t K_csi_rs = 0)
{
@ -500,7 +507,7 @@ public:
measurements.wideband_snr_db = new_meas.snr_dB;
measurements.nof_ports = 1; // Other values are not supported
measurements.K_csi_rs = K_csi_rs;
new_nzp_csi_rs_channel_measurement(measurements, resource_set_id);
new_nzp_csi_rs_channel_measurement(cfg, measurements, resource_set_id);
// Update tracking information
trs_measurements_mutex.lock();
@ -529,6 +536,7 @@ public:
void set_ul_ext_cfo(float ext_cfo_hz) { ul_ext_cfo_hz = ext_cfo_hz; }
};
} // namespace nr
} // namespace srsue

View File

@ -33,6 +33,9 @@ private:
cf_t* prach_ptr = nullptr;
float prach_target_power = 0.0f;
uint32_t sf_sz = 0;
srsran::phy_cfg_nr_t cfg{};
std::vector<bool> pending_cfgs;
std::mutex cfg_mutex;
public:
sf_worker* operator[](std::size_t pos) { return workers.at(pos).get(); }

View File

@ -194,7 +194,8 @@ private:
srslog::basic_logger& logger_phy;
srslog::basic_logger& logger_phy_lib;
srsue::stack_interface_phy_lte* stack = nullptr;
srsue::stack_interface_phy_lte* stack = nullptr;
srsue::stack_interface_phy_nr* stack_nr = nullptr;
lte::worker_pool lte_workers;
nr::worker_pool nr_workers;
@ -205,8 +206,9 @@ private:
srsran_prach_cfg_t prach_cfg = {};
srsran_tdd_config_t tdd_config = {};
srsran::phy_cfg_t config = {};
phy_args_t args = {};
srsran::phy_cfg_t config = {};
srsran::phy_cfg_nr_t config_nr = {};
phy_args_t args = {};
// Since cell_search/cell_select operations take a lot of time, we use another queue to process the other commands
// in parallel and avoid accumulating in the queue

View File

@ -119,6 +119,8 @@ public:
// STACK interface
void cell_search_completed(const rrc_interface_phy_lte::cell_search_ret_t& cs_ret, const phy_cell_t& found_cell);
void set_phy_config_complete(bool status) final;
private:
srsran::task_sched_handle task_sched;
struct cmd_msg_t {
@ -158,6 +160,13 @@ private:
// rrc_nr_state_t state = RRC_NR_STATE_IDLE;
// Stores the state of the PHy configuration setting
enum {
PHY_CFG_STATE_NONE = 0,
PHY_CFG_STATE_APPLY_SP_CELL,
PHY_CFG_STATE_RA_COMPLETED,
} phy_cfg_state;
rrc_nr_args_t args = {};
const char* get_rb_name(uint32_t lcid) final;

View File

@ -83,6 +83,7 @@ public:
void cell_select_complete(bool status) final;
void set_config_complete(bool status) final;
void set_scell_complete(bool status) final;
void set_phy_config_complete(bool status) final;
// MAC Interface for EUTRA PHY
uint16_t get_dl_sched_rnti(uint32_t tti) final { return mac.get_dl_sched_rnti(tti); }

View File

@ -60,24 +60,25 @@ public:
int init(const stack_args_t& args_, phy_interface_stack_nr* phy_, gw_interface_stack* gw_);
bool switch_on() final;
bool switch_off() final;
void stop();
void stop() final;
// GW srsue stack_interface_gw dummy interface
bool is_registered() { return true; };
bool start_service_request() { return true; };
bool is_registered() final { return true; };
bool start_service_request() final { return true; };
bool get_metrics(stack_metrics_t* metrics);
bool get_metrics(stack_metrics_t* metrics) final;
bool is_rrc_connected();
// RRC interface for PHY
void in_sync() final;
void out_of_sync() final;
void run_tti(const uint32_t tti) final;
void set_phy_config_complete(bool status) override;
// MAC interface for PHY
sched_rnti_t get_dl_sched_rnti_nr(const uint32_t tti) final { return mac->get_dl_sched_rnti_nr(tti); }
sched_rnti_t get_ul_sched_rnti_nr(const uint32_t tti) final { return mac->get_ul_sched_rnti_nr(tti); }
int sf_indication(const uint32_t tti)
int sf_indication(const uint32_t tti) final
{
run_tti(tti);
return SRSRAN_SUCCESS;
@ -94,11 +95,11 @@ public:
{
mac->new_grant_ul(cc_idx, grant, action);
}
void prach_sent(uint32_t tti, uint32_t s_id, uint32_t t_id, uint32_t f_id, uint32_t ul_carrier_id)
void prach_sent(uint32_t tti, uint32_t s_id, uint32_t t_id, uint32_t f_id, uint32_t ul_carrier_id) final
{
mac->prach_sent(tti, s_id, t_id, f_id, ul_carrier_id);
}
bool sr_opportunity(uint32_t tti, uint32_t sr_id, bool meas_gap, bool ul_sch_tx)
bool sr_opportunity(uint32_t tti, uint32_t sr_id, bool meas_gap, bool ul_sch_tx) final
{
return mac->sr_opportunity(tti, sr_id, meas_gap, ul_sch_tx);
}
@ -108,7 +109,7 @@ public:
bool has_active_radio_bearer(uint32_t eps_bearer_id) final { return true; /* TODO: add EPS to LCID mapping */ }
// Interface for RRC
srsran::tti_point get_current_tti() { return srsran::tti_point{0}; }
srsran::tti_point get_current_tti() final { return srsran::tti_point{0}; }
void add_eps_bearer(uint8_t eps_bearer_id, srsran::srsran_rat_t rat, uint32_t lcid) final {}
void remove_eps_bearer(uint8_t eps_bearer_id) final {}
void reset_eps_bearers() final {}

View File

@ -18,8 +18,9 @@
namespace srsue {
namespace nr {
cc_worker::cc_worker(uint32_t cc_idx_, srslog::basic_logger& log, state& phy_state_) :
cc_idx(cc_idx_), phy(phy_state_), logger(log)
cc_worker::cc_worker(uint32_t cc_idx_, srslog::basic_logger& log, state& phy_state_, const srsran::phy_cfg_nr_t& cfg) :
cc_idx(cc_idx_), phy(phy_state_), cfg(cfg), logger(log)
{
cf_t* rx_buffer_c[SRSRAN_MAX_PORTS] = {};
@ -67,34 +68,35 @@ cc_worker::~cc_worker()
}
}
bool cc_worker::update_cfg()
void cc_worker::update_cfg(const srsran::phy_cfg_nr_t& new_config)
{
if (srsran_ue_dl_nr_set_carrier(&ue_dl, &phy.cfg.carrier) < SRSRAN_SUCCESS) {
cfg = new_config;
configured = false;
if (srsran_ue_dl_nr_set_carrier(&ue_dl, &cfg.carrier) < SRSRAN_SUCCESS) {
ERROR("Error setting carrier");
return false;
return;
}
if (srsran_ue_ul_nr_set_carrier(&ue_ul, &phy.cfg.carrier) < SRSRAN_SUCCESS) {
if (srsran_ue_ul_nr_set_carrier(&ue_ul, &cfg.carrier) < SRSRAN_SUCCESS) {
ERROR("Error setting carrier");
return false;
return;
}
srsran_dci_cfg_nr_t dci_cfg = phy.cfg.get_dci_cfg();
if (srsran_ue_dl_nr_set_pdcch_config(&ue_dl, &phy.cfg.pdcch, &dci_cfg) < SRSRAN_SUCCESS) {
srsran_dci_cfg_nr_t dci_cfg = cfg.get_dci_cfg();
if (srsran_ue_dl_nr_set_pdcch_config(&ue_dl, &cfg.pdcch, &dci_cfg) < SRSRAN_SUCCESS) {
logger.error("Error setting NR PDCCH configuration");
return false;
return;
}
srsran_ssb_cfg_t ssb_cfg = phy.cfg.get_ssb_cfg();
ssb_cfg.srate_hz = srsran_min_symbol_sz_rb(phy.cfg.carrier.nof_prb) * SRSRAN_SUBC_SPACING_NR(phy.cfg.carrier.scs);
srsran_ssb_cfg_t ssb_cfg = cfg.get_ssb_cfg();
ssb_cfg.srate_hz = srsran_min_symbol_sz_rb(cfg.carrier.nof_prb) * SRSRAN_SUBC_SPACING_NR(cfg.carrier.scs);
if (srsran_ssb_set_cfg(&ssb, &ssb_cfg) < SRSRAN_SUCCESS) {
logger.error("Error setting SSB configuration");
return false;
return;
}
configured = true;
return true;
}
void cc_worker::set_tti(uint32_t tti)
@ -155,7 +157,7 @@ void cc_worker::decode_pdcch_dl()
}
// Enqueue UL grants
phy.set_dl_pending_grant(dl_slot_cfg, dci_rx[i]);
phy.set_dl_pending_grant(cfg, dl_slot_cfg, dci_rx[i]);
}
if (logger.debug.enabled()) {
@ -202,7 +204,7 @@ void cc_worker::decode_pdcch_ul()
}
// Enqueue UL grants
phy.set_ul_pending_grant(dl_slot_cfg, dci_rx[i]);
phy.set_ul_pending_grant(cfg, dl_slot_cfg, dci_rx[i]);
}
}
@ -344,7 +346,7 @@ bool cc_worker::measure_csi()
srsran_csi_trs_measurements_t meas = {};
// Iterate all possible candidates
const std::array<bool, SRSRAN_SSB_NOF_CANDIDATES> position_in_burst = phy.cfg.ssb.position_in_burst;
const std::array<bool, SRSRAN_SSB_NOF_CANDIDATES>& position_in_burst = cfg.ssb.position_in_burst;
for (uint32_t ssb_idx = 0; ssb_idx < SRSRAN_SSB_NOF_CANDIDATES; ssb_idx++) {
// Skip SSB candidate if not enabled
if (not position_in_burst[ssb_idx]) {
@ -352,7 +354,7 @@ bool cc_worker::measure_csi()
}
// Measure SSB candidate
if (srsran_ssb_csi_measure(&ssb, phy.cfg.carrier.pci, ssb_idx, rx_buffer[0], &meas) < SRSRAN_SUCCESS) {
if (srsran_ssb_csi_measure(&ssb, cfg.carrier.pci, ssb_idx, rx_buffer[0], &meas) < SRSRAN_SUCCESS) {
logger.error("Error measuring SSB");
return false;
}
@ -363,10 +365,10 @@ bool cc_worker::measure_csi()
logger.debug("SSB-CSI: %s", str.data());
}
bool hrf = (dl_slot_cfg.idx % SRSRAN_NSLOTS_PER_FRAME_NR(phy.cfg.carrier.scs) >
SRSRAN_NSLOTS_PER_FRAME_NR(phy.cfg.carrier.scs) / 2);
bool hrf = (dl_slot_cfg.idx % SRSRAN_NSLOTS_PER_FRAME_NR(cfg.carrier.scs) >
SRSRAN_NSLOTS_PER_FRAME_NR(cfg.carrier.scs) / 2);
srsran_pbch_msg_nr_t pbch_msg = {};
if (srsran_ssb_decode_pbch(&ssb, phy.cfg.carrier.pci, hrf, ssb_idx, rx_buffer[0], &pbch_msg) < SRSRAN_SUCCESS) {
if (srsran_ssb_decode_pbch(&ssb, cfg.carrier.pci, hrf, ssb_idx, rx_buffer[0], &pbch_msg) < SRSRAN_SUCCESS) {
logger.error("Error decoding PBCH");
return false;
}
@ -381,10 +383,10 @@ bool cc_worker::measure_csi()
}
// Check if the SFN matches
if (mib.sfn != dl_slot_cfg.idx / SRSRAN_NSLOTS_PER_FRAME_NR(phy.cfg.carrier.scs)) {
if (mib.sfn != dl_slot_cfg.idx / SRSRAN_NSLOTS_PER_FRAME_NR(cfg.carrier.scs)) {
logger.error("PBCH-MIB: NR SFN (%d) does not match current SFN (%d)",
mib.sfn,
dl_slot_cfg.idx / SRSRAN_NSLOTS_PER_FRAME_NR(phy.cfg.carrier.scs));
dl_slot_cfg.idx / SRSRAN_NSLOTS_PER_FRAME_NR(cfg.carrier.scs));
}
// Log MIB information
@ -399,7 +401,7 @@ bool cc_worker::measure_csi()
}
// Report SSB candidate channel measurement to the PHY state
phy.new_csi_trs_measurement(meas);
phy.new_csi_trs_measurement(meas, cfg);
}
}
@ -407,7 +409,7 @@ bool cc_worker::measure_csi()
bool estimate_fft = false;
for (uint32_t resource_set_id = 0; resource_set_id < SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_SETS; resource_set_id++) {
// Select NZP-CSI-RS set
const srsran_csi_rs_nzp_set_t& nzp_set = phy.cfg.pdsch.nzp_csi_rs_sets[resource_set_id];
const srsran_csi_rs_nzp_set_t& nzp_set = cfg.pdsch.nzp_csi_rs_sets[resource_set_id];
// Skip set if not set as TRS (it will be processed later)
if (not nzp_set.trs_info) {
@ -439,13 +441,13 @@ bool cc_worker::measure_csi()
logger.debug("NZP-CSI-RS (TRS): id=%d %s", resource_set_id, str.data());
}
phy.new_csi_trs_measurement(trs_measurements, resource_set_id, (uint32_t)n);
phy.new_csi_trs_measurement(trs_measurements, cfg, resource_set_id, (uint32_t)n);
}
// Iterate all NZP-CSI-RS not marked as TRS and perform channel measurements
for (uint32_t resource_set_id = 0; resource_set_id < SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_SETS; resource_set_id++) {
// Select NZP-CSI-RS set
const srsran_csi_rs_nzp_set_t& nzp_set = phy.cfg.pdsch.nzp_csi_rs_sets[resource_set_id];
const srsran_csi_rs_nzp_set_t& nzp_set = cfg.pdsch.nzp_csi_rs_sets[resource_set_id];
// Skip set if set as TRS (it was processed previously)
if (nzp_set.trs_info) {
@ -478,7 +480,7 @@ bool cc_worker::measure_csi()
measurements.wideband_snr_db);
// Report new measurement to the PHY state
phy.new_nzp_csi_rs_channel_measurement(measurements, resource_set_id);
phy.new_nzp_csi_rs_channel_measurement(cfg, measurements, resource_set_id);
}
return true;
@ -492,7 +494,7 @@ bool cc_worker::work_dl()
}
// Check if it is a DL slot, if not skip
if (!srsran_duplex_nr_is_dl(&phy.cfg.duplex, 0, dl_slot_cfg.idx)) {
if (!srsran_duplex_nr_is_dl(&cfg.duplex, 0, dl_slot_cfg.idx)) {
return true;
}
@ -540,7 +542,7 @@ bool cc_worker::work_ul()
bool has_ul_ack = phy.get_pending_ack(ul_slot_cfg.idx, pdsch_ack);
// Check if it is a UL slot, if not skip
if (!srsran_duplex_nr_is_ul(&phy.cfg.duplex, 0, ul_slot_cfg.idx)) {
if (!srsran_duplex_nr_is_ul(&cfg.duplex, 0, ul_slot_cfg.idx)) {
// No NR signal shall be transmitted
srsran_vec_cf_zero(tx_buffer[0], ue_ul.ifft.sf_sz);
@ -571,7 +573,7 @@ bool cc_worker::work_ul()
}
}
if (srsran_harq_ack_pack(&phy.cfg.harq_ack, &pdsch_ack, &uci_data) < SRSRAN_SUCCESS) {
if (srsran_harq_ack_pack(&cfg.harq_ack, &pdsch_ack, &uci_data) < SRSRAN_SUCCESS) {
ERROR("Filling UCI ACK bits");
return false;
}
@ -579,11 +581,11 @@ bool cc_worker::work_ul()
// Add SR to UCI data only if there is no UL grant!
if (not has_pusch_grant) {
phy.get_pending_sr(ul_slot_cfg.idx, uci_data);
phy.get_pending_sr(cfg, ul_slot_cfg.idx, uci_data);
}
// Add CSI reports to UCI data if available
phy.get_periodic_csi(ul_slot_cfg, uci_data);
phy.get_periodic_csi(cfg, ul_slot_cfg, uci_data);
// Setup frequency offset
srsran_ue_ul_nr_set_freq_offset(&ue_ul, phy.get_ul_cfo());
@ -608,7 +610,7 @@ bool cc_worker::work_ul()
}
// Set UCI configuration following procedures
srsran_ra_ul_set_grant_uci_nr(&phy.cfg.carrier, &phy.cfg.pusch, &uci_data.cfg, &pusch_cfg);
srsran_ra_ul_set_grant_uci_nr(&cfg.carrier, &cfg.pusch, &uci_data.cfg, &pusch_cfg);
// Assigning MAC provided values to PUSCH config structs
pusch_cfg.grant.tb[0].softbuffer.tx = ul_action.tb.softbuffer;
@ -660,14 +662,13 @@ bool cc_worker::work_ul()
} else if (srsran_uci_nr_total_bits(&uci_data.cfg) > 0) {
// Get PUCCH resource
srsran_pucch_nr_resource_t resource = {};
if (srsran_ra_ul_nr_pucch_resource(&phy.cfg.pucch, &uci_data.cfg, &resource) < SRSRAN_SUCCESS) {
if (srsran_ra_ul_nr_pucch_resource(&cfg.pucch, &uci_data.cfg, &resource) < SRSRAN_SUCCESS) {
ERROR("Selecting PUCCH resource");
return false;
}
// Encode PUCCH message
if (srsran_ue_ul_nr_encode_pucch(&ue_ul, &ul_slot_cfg, &phy.cfg.pucch.common, &resource, &uci_data) <
SRSRAN_SUCCESS) {
if (srsran_ue_ul_nr_encode_pucch(&ue_ul, &ul_slot_cfg, &cfg.pucch.common, &resource, &uci_data) < SRSRAN_SUCCESS) {
ERROR("Encoding PUCCH");
return false;
}

View File

@ -27,22 +27,26 @@ static int plot_worker_id = -1;
namespace srsue {
namespace nr {
sf_worker::sf_worker(srsran::phy_common_interface& common_, state& phy_state_, srslog::basic_logger& log) :
phy_state(phy_state_), common(common_), logger(log)
sf_worker::sf_worker(srsran::phy_common_interface& common_,
state& phy_state_,
const srsran::phy_cfg_nr_t& cfg,
srslog::basic_logger& log) :
phy_state(phy_state_), common(common_), logger(log), sf_len(SRSRAN_SF_LEN_PRB_NR(cfg.carrier.nof_prb))
{
for (uint32_t i = 0; i < phy_state.args.nof_carriers; i++) {
cc_worker* w = new cc_worker(i, log, phy_state);
cc_worker* w = new cc_worker(i, log, phy_state, cfg);
cc_workers.push_back(std::unique_ptr<cc_worker>(w));
}
}
bool sf_worker::update_cfg(uint32_t cc_idx)
void sf_worker::update_cfg(uint32_t cc_idx, const srsran::phy_cfg_nr_t& new_cfg)
{
if (cc_idx >= cc_workers.size()) {
return false;
return;
}
return cc_workers[cc_idx]->update_cfg();
cc_workers[cc_idx]->update_cfg(new_cfg);
}
cf_t* sf_worker::get_buffer(uint32_t cc_idx, uint32_t antenna_idx)
@ -86,7 +90,7 @@ void sf_worker::work_imp()
if (prach_ptr != nullptr) {
// PRACH is available, set buffer, transmit and return
tx_buffer.set(phy_state.args.rf_channel_offset, prach_ptr);
tx_buffer.set_nof_samples(SRSRAN_SF_LEN_PRB_NR(phy_state.cfg.carrier.nof_prb));
tx_buffer.set_nof_samples(sf_len);
// Transmit NR PRACH
common.worker_end(context, true, tx_buffer);
@ -106,7 +110,7 @@ void sf_worker::work_imp()
for (uint32_t i = 0; i < (uint32_t)cc_workers.size(); i++) {
tx_buffer.set(i + phy_state.args.rf_channel_offset, cc_workers[i]->get_tx_buffer(0));
}
tx_buffer.set_nof_samples(SRSRAN_SF_LEN_PRB_NR(phy_state.cfg.carrier.nof_prb));
tx_buffer.set_nof_samples(sf_len);
// Always call worker_end before returning
common.worker_end(context, true, tx_buffer);

View File

@ -25,9 +25,16 @@ bool worker_pool::init(const phy_args_nr_t& args,
phy_state.stack = stack_;
phy_state.args = args;
// Set carrier attributes
phy_state.cfg.carrier.pci = 500;
phy_state.cfg.carrier.nof_prb = args.max_nof_prb;
{
std::lock_guard<std::mutex> lock(cfg_mutex);
pending_cfgs.resize(args.nof_phy_threads);
for (auto&& b : pending_cfgs) {
b = false;
}
// Set carrier attributes
cfg.carrier.pci = 500;
cfg.carrier.nof_prb = args.max_nof_prb;
}
// Set NR arguments
phy_state.args.nof_carriers = args.nof_carriers;
@ -47,7 +54,11 @@ bool worker_pool::init(const phy_args_nr_t& args,
log.set_level(srslog::str_to_basic_level(args.log.phy_level));
log.set_hex_dump_max_size(args.log.phy_hex_limit);
auto w = new sf_worker(common, phy_state, log);
sf_worker* w = nullptr;
{
std::lock_guard<std::mutex> lock(cfg_mutex);
w = new sf_worker(common, phy_state, cfg, log);
}
pool.init_worker(i, w, prio, args.worker_cpu_mask);
workers.push_back(std::unique_ptr<sf_worker>(w));
}
@ -76,8 +87,18 @@ sf_worker* worker_pool::wait_worker(uint32_t tti)
logger.set_context(tti);
sf_worker* worker = (sf_worker*)pool.wait_worker(tti);
uint32_t pci = 0;
{
std::lock_guard<std::mutex> lock(cfg_mutex);
pci = cfg.carrier.pci;
if (pending_cfgs[worker->get_id()]) {
pending_cfgs[worker->get_id()] = false;
worker->set_cfg(cfg);
}
}
// Generate PRACH if ready
if (prach_buffer->is_ready_to_send(tti, phy_state.cfg.carrier.pci)) {
if (prach_buffer->is_ready_to_send(tti, pci)) {
prach_ptr = prach_buffer->generate(phy_state.get_ul_cfo() / 15000, &prach_nof_sf, &prach_target_power);
// Scale signal to maximum
@ -90,12 +111,19 @@ sf_worker* worker_pool::wait_worker(uint32_t tti)
}
}
uint32_t config_idx = 0;
srsran_duplex_mode_t mode = SRSRAN_DUPLEX_MODE_FDD;
srsran_subcarrier_spacing_t scs = srsran_subcarrier_spacing_15kHz;
{
std::lock_guard<std::mutex> lock(cfg_mutex);
config_idx = cfg.prach.config_idx;
mode = cfg.duplex.mode;
scs = cfg.carrier.scs;
}
// Notify MAC about PRACH transmission
phy_state.stack->prach_sent(TTI_TX(tti),
srsran_prach_nr_start_symbol(phy_state.cfg.prach.config_idx, phy_state.cfg.duplex.mode),
SRSRAN_SLOT_NR_MOD(phy_state.cfg.carrier.scs, TTI_TX(tti)),
0,
0);
phy_state.stack->prach_sent(
TTI_TX(tti), srsran_prach_nr_start_symbol(config_idx, mode), SRSRAN_SLOT_NR_MOD(scs, TTI_TX(tti)), 0, 0);
}
// Set PRACH transmission buffer in workers if it is pending
@ -162,48 +190,81 @@ int worker_pool::set_ul_grant(uint32_t rar
logger.info("Setting RAR Grant: %s", str.data());
}
phy_state.set_ul_pending_grant(msg3_slot_cfg, dci_ul);
std::lock_guard<std::mutex> lock(cfg_mutex);
phy_state.set_ul_pending_grant(cfg, msg3_slot_cfg, dci_ul);
return SRSRAN_SUCCESS;
}
bool worker_pool::set_config(const srsran::phy_cfg_nr_t& cfg)
{
uint32_t dl_arfcn = srsran::srsran_band_helper().freq_to_nr_arfcn(cfg.carrier.dl_center_frequency_hz);
phy_state.cfg = cfg;
sf_sz = SRSRAN_SF_LEN_PRB_NR(cfg.carrier.nof_prb);
logger.info("Setting new PHY configuration ARFCN=%d, PCI=%d", dl_arfcn, cfg.carrier.pci);
bool worker_pool::set_config(const srsran::phy_cfg_nr_t& new_cfg)
{
uint32_t dl_arfcn = srsran::srsran_band_helper().freq_to_nr_arfcn(new_cfg.carrier.dl_center_frequency_hz);
sf_sz = SRSRAN_SF_LEN_PRB_NR(new_cfg.carrier.nof_prb);
bool carrier_equal;
{
std::lock_guard<std::mutex> lock(cfg_mutex);
// Check if the carrier has changed
carrier_equal = cfg.carrier_is_equal(new_cfg);
// If the carrier has not changed, reset pending flags. Configuration will be copied when the worker is reserved
// from the real-time thread
for (auto&& b : pending_cfgs) {
b = carrier_equal;
}
// Update configuration
cfg = new_cfg;
}
// If the carrier has changed, the configuration cannot be set from the real-time thread
if (not carrier_equal) {
// Configure each worker with the new configuration
for (uint32_t i = 0; i < (uint32_t)workers.size(); i++) {
// Wait for each worker to avoid concurrency issues
sf_worker* w = (sf_worker*)pool.wait_worker_id(i);
if (w == nullptr) {
// Unlikely to happen
continue;
}
// Configure worker
w->set_cfg(new_cfg);
// Release worker
w->release();
// As the worker has been configured, there is no need to load new configuration from the real-time thread
pending_cfgs[i] = false;
}
}
logger.info("Setting new PHY configuration ARFCN=%d, PCI=%d", dl_arfcn, new_cfg.carrier.pci);
// Set carrier information
info_metrics_t info = {};
info.pci = cfg.carrier.pci;
info.pci = new_cfg.carrier.pci;
info.dl_earfcn = dl_arfcn;
phy_state.set_info_metrics(info);
// Best effort to convert NR carrier into LTE cell
srsran_cell_t cell = {};
int ret = srsran_carrier_to_cell(&phy_state.cfg.carrier, &cell);
int ret = srsran_carrier_to_cell(&new_cfg.carrier, &cell);
if (ret < SRSRAN_SUCCESS) {
logger.error("Converting carrier to cell for PRACH (%d)", ret);
return false;
}
// Request workers to run any procedure related to configuration update
for (auto& w : workers) {
if (not w->update_cfg(0)) {
return false;
}
}
// Best effort to set up NR-PRACH config reused for NR
srsran_prach_cfg_t prach_cfg = cfg.prach;
uint32_t lte_nr_prach_offset = (phy_state.cfg.carrier.nof_prb - cell.nof_prb) / 2;
srsran_prach_cfg_t prach_cfg = new_cfg.prach;
uint32_t lte_nr_prach_offset = (new_cfg.carrier.nof_prb - cell.nof_prb) / 2;
if (prach_cfg.freq_offset < lte_nr_prach_offset) {
logger.error("prach_cfg.freq_offset=%d is not compatible with LTE", prach_cfg.freq_offset);
return false;
}
prach_cfg.freq_offset -= lte_nr_prach_offset;
prach_cfg.tdd_config.configured = (cfg.duplex.mode == SRSRAN_DUPLEX_MODE_TDD);
prach_cfg.tdd_config.configured = (new_cfg.duplex.mode == SRSRAN_DUPLEX_MODE_TDD);
// Set the PRACH configuration
if (not prach_buffer->set_cell(cell, prach_cfg)) {
@ -216,7 +277,8 @@ bool worker_pool::set_config(const srsran::phy_cfg_nr_t& cfg)
bool worker_pool::has_valid_sr_resource(uint32_t sr_id)
{
return phy_state.has_valid_sr_resource(sr_id);
std::lock_guard<std::mutex> lock(cfg_mutex);
return phy_state.has_valid_sr_resource(cfg, sr_id);
}
void worker_pool::clear_pending_grants()

View File

@ -623,6 +623,7 @@ void phy::set_mch_period_stop(uint32_t stop)
int phy::init(const phy_args_nr_t& args_, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_)
{
stack_nr = stack_;
if (!nr_workers.init(args_, common, stack_, WORKERS_THREAD_PRIO)) {
return SRSRAN_ERROR;
}
@ -658,17 +659,36 @@ void phy::set_earfcn(std::vector<uint32_t> earfcns)
bool phy::set_config(const srsran::phy_cfg_nr_t& cfg)
{
srsran::srsran_band_helper band_helper;
// Stash NR configuration
config_nr = cfg;
// tune radio
for (uint32_t i = 0; i < common.args->nof_nr_carriers; i++) {
logger_phy.info("Tuning Rx channel %d to %.2f MHz", i + common.args->nof_lte_carriers, cfg.carrier.dl_center_frequency_hz / 1e6);
radio->set_rx_freq(i + common.args->nof_lte_carriers, cfg.carrier.dl_center_frequency_hz);
logger_phy.info("Tuning Tx channel %d to %.2f MHz", i + common.args->nof_lte_carriers, cfg.carrier.ul_center_frequency_hz / 1e6);
radio->set_tx_freq(i + common.args->nof_lte_carriers, cfg.carrier.ul_center_frequency_hz);
}
// Setup carrier configuration asynchronously
cmd_worker.add_cmd([this]() {
srsran::srsran_band_helper band_helper;
return nr_workers.set_config(cfg);
// tune radio
for (uint32_t i = 0; i < common.args->nof_nr_carriers; i++) {
logger_phy.info("Tuning Rx channel %d to %.2f MHz",
i + common.args->nof_lte_carriers,
config_nr.carrier.dl_center_frequency_hz / 1e6);
radio->set_rx_freq(i + common.args->nof_lte_carriers, config_nr.carrier.dl_center_frequency_hz);
logger_phy.info("Tuning Tx channel %d to %.2f MHz",
i + common.args->nof_lte_carriers,
config_nr.carrier.ul_center_frequency_hz / 1e6);
radio->set_tx_freq(i + common.args->nof_lte_carriers, config_nr.carrier.ul_center_frequency_hz);
}
// Set UE configuration
bool ret = nr_workers.set_config(config_nr);
// Notify PHY config completion
if (stack_nr != nullptr) {
stack_nr->set_phy_config_complete(ret);
}
return ret;
});
return true;
}
bool phy::has_valid_sr_resource(uint32_t sr_id)

View File

@ -1326,8 +1326,8 @@ bool rrc_nr::apply_sp_cell_cfg(const sp_cell_cfg_s& sp_cell_cfg)
current_phycfg.csi = prev_csi;
phy->set_config(current_phycfg);
// Start RA procedure
mac->start_ra_procedure();
phy_cfg_state = PHY_CFG_STATE_APPLY_SP_CELL;
return true;
}
@ -1526,6 +1526,7 @@ void rrc_nr::ra_completed()
{
logger.info("RA completed. Applying remaining CSI configuration.");
phy->set_config(phy_cfg);
phy_cfg_state = PHY_CFG_STATE_RA_COMPLETED;
}
void rrc_nr::ra_problem()
{
@ -1538,6 +1539,24 @@ void rrc_nr::release_pucch_srs() {}
void rrc_nr::cell_search_completed(const rrc_interface_phy_lte::cell_search_ret_t& cs_ret, const phy_cell_t& found_cell)
{}
void rrc_nr::set_phy_config_complete(bool status)
{
switch (phy_cfg_state) {
case PHY_CFG_STATE_NONE:
logger.warning("PHY configuration completed without a clear state.");
break;
case PHY_CFG_STATE_APPLY_SP_CELL:
// Start RA procedure
logger.info("PHY configuration completed. Starting RA procedure.");
mac->start_ra_procedure();
break;
case PHY_CFG_STATE_RA_COMPLETED:
logger.info("Remaining CSI configuration completed.");
break;
}
phy_cfg_state = PHY_CFG_STATE_NONE;
}
/* Procedures */
rrc_nr::connection_reconf_no_ho_proc::connection_reconf_no_ho_proc(rrc_nr* parent_) : rrc_ptr(parent_), initiator(nr) {}

View File

@ -487,5 +487,9 @@ void ue_stack_lte::run_tti_impl(uint32_t tti, uint32_t tti_jump)
stack_logger.warning("Detected slow task processing (sync_queue_len=%zd).", sync_task_queue.size());
}
}
void ue_stack_lte::set_phy_config_complete(bool status)
{
cfg_task_queue.push([this, status]() { rrc_nr.set_phy_config_complete(status); });
}
} // namespace srsue

View File

@ -197,4 +197,9 @@ void ue_stack_nr::run_tti_impl(uint32_t tti)
task_sched.tic();
}
void ue_stack_nr::set_phy_config_complete(bool status)
{
sync_task_queue.push([this, status]() { rrc->set_phy_config_complete(status); });
}
} // namespace srsue

View File

@ -123,6 +123,11 @@ public:
bool is_valid() const { return valid; }
metrics_t get_metrics() { return metrics; }
void set_phy_config_complete(bool status) override
{
}
};
#endif // SRSRAN_DUMMY_UE_STACK_H