gnb,prach: add PRACH to gNB

rebase of Xavier's feature_gnb_prach branch
master
Xavier Arteaga 3 years ago committed by Andre Puschmann
parent fb92118bb7
commit b25681f9de

@ -162,42 +162,31 @@ public:
virtual void notify_pdcp_integrity_error(uint16_t rnti, uint32_t lcid) = 0;
};
class phy_interface_stack_nr
class phy_interface_rrc_nr
{
public:
const static int MAX_DL_GRANTS = 4;
typedef struct {
// TODO: include NR related fields
} dl_sched_grant_t;
typedef struct {
bool mib_present;
} bch_sched_t;
typedef struct {
uint32_t tti;
uint32_t nof_grants;
dl_sched_grant_t pdsch[MAX_DL_GRANTS];
int beam_id;
} dl_config_request_t;
typedef struct {
bch_sched_t pbch;
uint16_t length;
uint16_t index; // index indicated in dl_config
uint8_t* data[SRSRAN_MAX_TB]; // always a pointer in our case
} tx_request_pdu_t;
typedef struct {
uint32_t tti;
uint32_t tb_len;
uint32_t nof_pdus;
tx_request_pdu_t pdus[MAX_DL_GRANTS];
} tx_request_t;
virtual int dl_config_request(const dl_config_request_t& request) = 0;
virtual int tx_request(const tx_request_t& request) = 0;
/**
* @brief Describes physical layer configuration common among all the UEs for a given cell
*/
struct common_cfg_t {
srsran_carrier_nr_t carrier;
srsran_pdcch_cfg_nr_t pdcch;
srsran_prach_cfg_t prach;
};
virtual int set_common_cfg(const common_cfg_t& common_cfg) = 0;
};
class phy_interface_mac_nr
{
public:
// TBD
};
class phy_interface_stack_nr : public phy_interface_rrc_nr, public phy_interface_mac_nr
{
public:
// TBD
};
class stack_interface_mac
@ -271,11 +260,17 @@ public:
// ... add signal measurements here
};
virtual int slot_indication(const srsran_slot_cfg_t& slot_cfg) = 0;
virtual int get_dl_sched(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched) = 0;
virtual int get_ul_sched(const srsran_slot_cfg_t& slot_cfg, ul_sched_t& ul_sched) = 0;
virtual int pucch_info(const srsran_slot_cfg_t& slot_cfg, const pucch_info_t& pucch_info) = 0;
virtual int pusch_info(const srsran_slot_cfg_t& slot_cfg, const pusch_info_t& pusch_info) = 0;
struct rach_info_t {
uint32_t preamble;
uint32_t time_adv;
};
virtual int slot_indication(const srsran_slot_cfg_t& slot_cfg) = 0;
virtual int get_dl_sched(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched) = 0;
virtual int get_ul_sched(const srsran_slot_cfg_t& slot_cfg, ul_sched_t& ul_sched) = 0;
virtual int pucch_info(const srsran_slot_cfg_t& slot_cfg, const pucch_info_t& pucch_info) = 0;
virtual int pusch_info(const srsran_slot_cfg_t& slot_cfg, const pusch_info_t& pusch_info) = 0;
virtual void rach_detected(const rach_info_t& rach_info) = 0;
};
class stack_interface_phy_nr : public mac_interface_phy_nr, public srsran::stack_interface_phy_nr

@ -147,8 +147,14 @@ typedef struct SRSRAN_API {
uint32_t pid; ///< HARQ process number
uint32_t dai; ///< Downlink assignment index
uint32_t tpc; ///< TPC command for scheduled PUCCH
uint32_t pucch_resource; ///< PUCCH resource indicator
uint32_t pucch_resource; ///< PUCCH resource indicator for HARQ feedback
///< @note PUCCH resource is selected from PUCCH-ResourceSet if available, otherwise the UE
///< shall pick a pucch-ResourceCommon from Table 9.2.1-1.
uint32_t harq_feedback; ///< PDSCH-to-HARQ_feedback timing indicator
///< @note harq_feedback for format 1_0 indicates the delay between the PDSCH reception and
///< the UL transmission timing
///< @note harq_feedback for format 1_1 is index of the delay indicated in DL data to UL ACK
///< dedicated configuration table
// P-RNTI specific fields
uint32_t smi; ///< Short Messages Indicator

@ -197,6 +197,7 @@ void phy_cfg_nr_default_t::make_prach_default_lte(srsran_prach_cfg_t& prach)
prach.config_idx = 0;
prach.freq_offset = 2;
prach.root_seq_idx = 0;
prach.is_nr = true;
}
phy_cfg_nr_default_t::phy_cfg_nr_default_t(const reference_cfg_t& reference_cfg)
@ -250,4 +251,4 @@ phy_cfg_nr_default_t::phy_cfg_nr_default_t(const reference_cfg_t& reference_cfg)
}
}
} // namespace srsran
} // namespace srsran

@ -32,12 +32,11 @@ class slot_worker final : public srsran::thread_pool::worker
{
public:
struct args_t {
uint32_t cell_index = 0;
srsran_carrier_nr_t carrier = {};
uint32_t nof_tx_ports = 1;
uint32_t nof_rx_ports = 1;
uint32_t pusch_max_nof_iter = 10;
srsran_pdcch_cfg_nr_t pdcch_cfg = {}; ///< PDCCH configuration
uint32_t cell_index = 0;
uint32_t nof_max_prb = SRSRAN_MAX_PRB_NR;
uint32_t nof_tx_ports = 1;
uint32_t nof_rx_ports = 1;
uint32_t pusch_max_nof_iter = 10;
};
slot_worker(srsran::phy_common_interface& common_, stack_interface_phy_nr& stack_, srslog::basic_logger& logger);
@ -45,6 +44,8 @@ public:
bool init(const args_t& args);
bool set_common_cfg(const srsran_carrier_nr_t& carrier, const srsran_pdcch_cfg_nr_t& pdcch_cfg_);
/* Functions used by main PHY thread */
cf_t* get_buffer_rx(uint32_t antenna_idx);
cf_t* get_buffer_tx(uint32_t antenna_idx);

@ -15,7 +15,9 @@
#include "slot_worker.h"
#include "srsenb/hdr/phy/phy_interfaces.h"
#include "srsenb/hdr/phy/prach_worker.h"
#include "srsran/common/thread_pool.h"
#include "srsran/interfaces/enb_mac_interfaces.h"
#include "srsran/interfaces/gnb_interfaces.h"
namespace srsenb {
@ -23,11 +25,64 @@ namespace nr {
class worker_pool
{
private:
class prach_stack_adaptor_t : public stack_interface_phy_lte
{
private:
stack_interface_phy_nr& stack;
public:
prach_stack_adaptor_t(stack_interface_phy_nr& stack_) : stack(stack_)
{
// Do nothing
}
int sr_detected(uint32_t tti, uint16_t rnti) override { return 0; }
void rach_detected(uint32_t tti, uint32_t primary_cc_idx, uint32_t preamble_idx, uint32_t time_adv) override
{
stack_interface_phy_nr::rach_info_t rach_info = {};
rach_info.preamble = preamble_idx;
rach_info.time_adv = time_adv;
stack.rach_detected(rach_info);
}
int ri_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t ri_value) override { return 0; }
int pmi_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t pmi_value) override { return 0; }
int cqi_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t cqi_value) override { return 0; }
int snr_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, float snr_db, ul_channel_t ch) override { return 0; }
int ta_info(uint32_t tti, uint16_t rnti, float ta_us) override { return 0; }
int ack_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t tb_idx, bool ack) override { return 0; }
int crc_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t nof_bytes, bool crc_res) override { return 0; }
int push_pdu(uint32_t tti_rx,
uint16_t rnti,
uint32_t enb_cc_idx,
uint32_t nof_bytes,
bool crc_res,
uint32_t ul_nof_prbs) override
{
return 0;
}
int get_dl_sched(uint32_t tti, dl_sched_list_t& dl_sched_res) override { return 0; }
int get_mch_sched(uint32_t tti, bool is_mcch, dl_sched_list_t& dl_sched_res) override { return 0; }
int get_ul_sched(uint32_t tti, ul_sched_list_t& ul_sched_res) override { return 0; }
void set_sched_dl_tti_mask(uint8_t* tti_mask, uint32_t nof_sfs) override {}
void tti_clock() override {}
};
srsran::phy_common_interface& common;
stack_interface_phy_nr& stack;
srslog::sink& log_sink;
srsran::thread_pool pool;
std::vector<std::unique_ptr<slot_worker> > workers;
prach_worker_pool prach;
uint32_t current_tti = 0; ///< Current TTI, read and write from same thread
srslog::basic_logger& logger;
prach_stack_adaptor_t prach_stack_adaptor;
// Current configuration
std::mutex common_cfg_mutex;
srsran_carrier_nr_t carrier = {};
srsran_pdcch_cfg_nr_t pdcch_cfg = {};
public:
struct args_t {
@ -47,6 +102,7 @@ public:
slot_worker* wait_worker_id(uint32_t id);
void start_worker(slot_worker* w);
void stop();
int set_common_cfg(const phy_interface_rrc_nr::common_cfg_t& common_cfg);
};
} // namespace nr

@ -77,6 +77,7 @@ public:
int get_ul_sched(const srsran_slot_cfg_t& slot_cfg, ul_sched_t& ul_sched) override;
int pucch_info(const srsran_slot_cfg_t& slot_cfg, const pucch_info_t& pucch_info) override;
int pusch_info(const srsran_slot_cfg_t& slot_cfg, const pusch_info_t& pusch_info) override;
void rach_detected(const rach_info_t& rach_info) override;
private:
void run_thread() final;

@ -71,12 +71,9 @@ public:
int get_ul_sched(const srsran_slot_cfg_t& slot_cfg, ul_sched_t& ul_sched) override;
int pucch_info(const srsran_slot_cfg_t& slot_cfg, const pucch_info_t& pucch_info) override;
int pusch_info(const srsran_slot_cfg_t& slot_cfg, const pusch_info_t& pusch_info) override;
void rach_detected(const rach_info_t& rach_info) override;
private:
void get_dl_config(const uint32_t tti,
phy_interface_stack_nr::dl_config_request_t& config_request,
phy_interface_stack_nr::tx_request_t& tx_request);
// PDU processing
int handle_pdu(srsran::unique_byte_buffer_t pdu);

@ -26,11 +26,12 @@ slot_worker::slot_worker(srsran::phy_common_interface& common_,
bool slot_worker::init(const args_t& args)
{
// Calculate subframe length
sf_len = SRSRAN_SF_LEN_PRB_NR(args.carrier.nof_prb);
sf_len = SRSRAN_SF_LEN_PRB_NR(args.nof_max_prb);
// Copy common configurations
cell_index = args.cell_index;
pdcch_cfg = args.pdcch_cfg;
// FIXME:
// pdcch_cfg = args.pdcch_cfg;
// Allocate Tx buffers
tx_buffer.resize(args.nof_tx_ports);
@ -55,10 +56,10 @@ bool slot_worker::init(const args_t& args)
// Prepare DL arguments
srsran_gnb_dl_args_t dl_args = {};
dl_args.pdsch.measure_time = true;
dl_args.pdsch.max_layers = args.carrier.max_mimo_layers;
dl_args.pdsch.max_prb = args.carrier.nof_prb;
dl_args.pdsch.max_layers = args.nof_tx_ports;
dl_args.pdsch.max_prb = args.nof_max_prb;
dl_args.nof_tx_antennas = args.nof_tx_ports;
dl_args.nof_max_prb = args.carrier.nof_prb;
dl_args.nof_max_prb = args.nof_max_prb;
// Initialise DL
if (srsran_gnb_dl_init(&gnb_dl, tx_buffer.data(), &dl_args) < SRSRAN_SUCCESS) {
@ -66,18 +67,13 @@ bool slot_worker::init(const args_t& args)
return false;
}
// Set gNb DL carrier
if (srsran_gnb_dl_set_carrier(&gnb_dl, &args.carrier) < SRSRAN_SUCCESS) {
logger.error("Error setting DL carrier");
return false;
}
// Prepare UL arguments
srsran_gnb_ul_args_t ul_args = {};
ul_args.pusch.measure_time = true;
ul_args.pusch.max_layers = args.carrier.max_mimo_layers;
ul_args.pusch.max_prb = args.carrier.nof_prb;
ul_args.nof_max_prb = args.carrier.nof_prb;
ul_args.pusch.max_layers = args.nof_rx_ports;
ul_args.pusch.max_prb = args.nof_max_prb;
ul_args.nof_max_prb = args.nof_max_prb;
// Initialise UL
if (srsran_gnb_ul_init(&gnb_ul, rx_buffer[0], &ul_args) < SRSRAN_SUCCESS) {
@ -85,12 +81,6 @@ bool slot_worker::init(const args_t& args)
return false;
}
// Set gNb UL carrier
if (srsran_gnb_ul_set_carrier(&gnb_ul, &args.carrier) < SRSRAN_SUCCESS) {
logger.error("Error setting UL carrier");
return false;
}
return true;
}
@ -157,7 +147,7 @@ bool slot_worker::work_ul()
return false;
}
// Decode PUCCH
// For each PUCCH...
for (stack_interface_phy_nr::pucch_t& pucch : ul_sched.pucch) {
stack_interface_phy_nr::pucch_info_t pucch_info = {};
pucch_info.uci_data.cfg = pucch.uci_cfg;
@ -188,7 +178,7 @@ bool slot_worker::work_ul()
}
}
// Decode PUSCH
// For each PUSCH...
for (stack_interface_phy_nr::pusch_t& pusch : ul_sched.pusch) {
// Get payload PDU
stack_interface_phy_nr::pusch_info_t pusch_info = {};
@ -197,7 +187,7 @@ bool slot_worker::work_ul()
pusch_info.pusch_data.tb[0].payload = pusch.data[0];
pusch_info.pusch_data.tb[1].payload = pusch.data[1];
// Decode PUCCH
// Decode PUSCH
if (srsran_gnb_ul_get_pusch(&gnb_ul, &ul_slot_cfg, &pusch.sch, &pusch.sch.grant, &pusch_info.pusch_data) <
SRSRAN_SUCCESS) {
logger.error("Error getting PUSCH");
@ -340,6 +330,27 @@ void slot_worker::work_imp()
common.worker_end(this, true, tx_rf_buffer, tx_time, true);
}
bool slot_worker::set_common_cfg(const srsran_carrier_nr_t& carrier, const srsran_pdcch_cfg_nr_t& pdcch_cfg_)
{
// Set gNb DL carrier
if (srsran_gnb_dl_set_carrier(&gnb_dl, &carrier) < SRSRAN_SUCCESS) {
logger.error("Error setting DL carrier");
return false;
}
// Set gNb UL carrier
if (srsran_gnb_ul_set_carrier(&gnb_ul, &carrier) < SRSRAN_SUCCESS) {
logger.error("Error setting UL carrier");
return false;
}
pdcch_cfg = pdcch_cfg_;
// Update subframe length
sf_len = SRSRAN_SF_LEN_PRB_NR(carrier.nof_prb);
return true;
}
} // namespace nr
} // namespace srsenb
} // namespace srsenb

@ -18,15 +18,23 @@ worker_pool::worker_pool(srsran::phy_common_interface& common_,
stack_interface_phy_nr& stack_,
srslog::sink& log_sink_,
uint32_t max_workers) :
pool(max_workers), common(common_), stack(stack_), log_sink(log_sink_)
pool(max_workers),
common(common_),
stack(stack_),
log_sink(log_sink_),
logger(srslog::fetch_basic_logger("PHY-NR", log_sink)),
prach_stack_adaptor(stack_)
{
// Do nothing
}
bool worker_pool::init(const args_t& args, const phy_cell_cfg_list_nr_t& cell_list)
{
// Add workers to workers pool and start threads
// Configure logger
srslog::basic_levels log_level = srslog::str_to_basic_level(args.log.phy_level);
logger.set_level(log_level);
// Add workers to workers pool and start threads
for (uint32_t i = 0; i < args.nof_phy_threads; i++) {
auto& log = srslog::fetch_basic_logger(fmt::format("{}PHY{}-NR", args.log.id_preamble, i), log_sink);
log.set_level(log_level);
@ -39,11 +47,10 @@ bool worker_pool::init(const args_t& args, const phy_cell_cfg_list_nr_t& cell_li
slot_worker::args_t w_args = {};
uint32_t cell_index = 0;
w_args.cell_index = cell_index;
w_args.carrier = cell_list[cell_index].carrier;
w_args.nof_max_prb = cell_list[cell_index].carrier.nof_prb;
w_args.nof_tx_ports = cell_list[cell_index].carrier.max_mimo_layers;
w_args.nof_rx_ports = cell_list[cell_index].carrier.max_mimo_layers;
w_args.pusch_max_nof_iter = args.pusch_max_nof_iter;
w_args.pdcch_cfg = cell_list[cell_index].pdcch;
if (not w->init(w_args)) {
return false;
@ -55,12 +62,41 @@ bool worker_pool::init(const args_t& args, const phy_cell_cfg_list_nr_t& cell_li
void worker_pool::start_worker(slot_worker* w)
{
// Feed PRACH detection before start processing
prach.new_tti(0, current_tti, w->get_buffer_rx(0));
// Start actual worker
pool.start_worker(w);
}
slot_worker* worker_pool::wait_worker(uint32_t tti)
{
return (slot_worker*)pool.wait_worker(tti);
slot_worker* w = (slot_worker*)pool.wait_worker(tti);
// Only if a worker was available
if (w != nullptr) {
srsran_carrier_nr_t carrier_;
srsran_pdcch_cfg_nr_t pdcch_cfg_;
// Copy configuration
{
std::unique_lock<std::mutex> lock(common_cfg_mutex);
carrier_ = carrier;
pdcch_cfg_ = pdcch_cfg;
}
// Set worker configuration
if (not w->set_common_cfg(carrier_, pdcch_cfg_)) {
logger.error("Error setting common config");
return nullptr;
}
}
// Save current TTI
current_tti = tti;
// Return worker
return w;
}
slot_worker* worker_pool::wait_worker_id(uint32_t id)
@ -71,7 +107,42 @@ slot_worker* worker_pool::wait_worker_id(uint32_t id)
void worker_pool::stop()
{
pool.stop();
prach.stop();
}
int worker_pool::set_common_cfg(const phy_interface_rrc_nr::common_cfg_t& common_cfg)
{
// Best effort to convert NR carrier into LTE cell
srsran_cell_t cell = {};
int ret = srsran_carrier_to_cell(&common_cfg.carrier, &cell);
if (ret < SRSRAN_SUCCESS) {
logger.error("Converting carrier to cell for PRACH (%d)", ret);
return SRSRAN_ERROR;
}
// Best effort to set up NR-PRACH config reused for NR
srsran_prach_cfg_t prach_cfg = common_cfg.prach;
uint32_t lte_nr_prach_offset = (common_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 SRSRAN_ERROR;
}
prach_cfg.freq_offset -= lte_nr_prach_offset;
prach_cfg.is_nr = true;
// Set the PRACH configuration
prach.init(0, cell, prach_cfg, &prach_stack_adaptor, logger, 0, 1);
prach.set_max_prach_offset_us(1000);
// Save current configuration
{
std::unique_lock<std::mutex> lock(common_cfg_mutex);
carrier = common_cfg.carrier;
pdcch_cfg = common_cfg.pdcch;
}
return SRSRAN_SUCCESS;
}
} // namespace nr
} // namespace srsenb
} // namespace srsenb

@ -198,4 +198,6 @@ int gnb_stack_nr::pusch_info(const srsran_slot_cfg_t& slot_cfg, const mac_interf
return m_mac->pusch_info(slot_cfg, pusch_info);
}
void gnb_stack_nr::rach_detected(const rach_info_t& rach_info) {}
} // namespace srsenb

@ -89,114 +89,6 @@ void mac_nr::stop()
void mac_nr::get_metrics(srsenb::mac_metrics_t& metrics) {}
// Fills both, DL_CONFIG.request and TX.request structs
void mac_nr::get_dl_config(const uint32_t tti,
phy_interface_stack_nr::dl_config_request_t& config_request,
phy_interface_stack_nr::tx_request_t& tx_request)
{
// send MIB over BCH every 80ms
if (tti % 80 == 0) {
// try to read BCH PDU from RRC
if (rrc_h->read_pdu_bcch_bch(tti, bcch_bch_payload) == SRSRAN_SUCCESS) {
logger.info("Adding BCH in TTI=%d", tti);
tx_request.pdus[tx_request.nof_pdus].pbch.mib_present = true;
tx_request.pdus[tx_request.nof_pdus].data[0] = bcch_bch_payload->msg;
tx_request.pdus[tx_request.nof_pdus].length = bcch_bch_payload->N_bytes;
tx_request.pdus[tx_request.nof_pdus].index = tx_request.nof_pdus;
tx_request.nof_pdus++;
if (pcap) {
pcap->write_dl_bch(bcch_bch_payload->msg, bcch_bch_payload->N_bytes, 0xffff, 0, tti);
}
} else {
logger.error("Couldn't read BCH payload from RRC");
}
}
// Schedule SIBs
for (auto& sib : bcch_dlsch_payload) {
if (sib.payload->N_bytes > 0) {
if (tti % (sib.periodicity * 10) == 0) {
logger.info("Adding SIB %d in TTI=%d", sib.index, tti);
tx_request.pdus[tx_request.nof_pdus].data[0] = sib.payload->msg;
tx_request.pdus[tx_request.nof_pdus].length = sib.payload->N_bytes;
tx_request.pdus[tx_request.nof_pdus].index = tx_request.nof_pdus;
if (pcap) {
pcap->write_dl_si_rnti_nr(sib.payload->msg, sib.payload->N_bytes, 0xffff, 0, tti);
}
tx_request.nof_pdus++;
}
}
}
// Add MAC padding if TTI is empty
if (tx_request.nof_pdus == 0) {
uint32_t buffer_index = tti % SRSRAN_FDD_NOF_HARQ;
ue_tx_buffer.at(buffer_index)->clear();
ue_tx_pdu.init_tx(ue_tx_buffer.at(buffer_index).get(), args.tb_size);
// read RLC PDU
ue_rlc_buffer->clear();
int pdu_len = rlc_h->read_pdu(args.rnti, 4, ue_rlc_buffer->msg, args.tb_size - 2);
// Only create PDU if RLC has something to tx
if (pdu_len > 0) {
logger.info("Adding MAC PDU for RNTI=%d", args.rnti);
ue_rlc_buffer->N_bytes = pdu_len;
logger.info(ue_rlc_buffer->msg, ue_rlc_buffer->N_bytes, "Read %d B from RLC", ue_rlc_buffer->N_bytes);
// add to MAC PDU and pack
ue_tx_pdu.add_sdu(4, ue_rlc_buffer->msg, ue_rlc_buffer->N_bytes);
ue_tx_pdu.pack();
logger.debug(ue_tx_buffer.at(buffer_index)->msg,
ue_tx_buffer.at(buffer_index)->N_bytes,
"Generated MAC PDU (%d B)",
ue_tx_buffer.at(buffer_index)->N_bytes);
tx_request.pdus[tx_request.nof_pdus].data[0] = ue_tx_buffer.at(buffer_index)->msg;
tx_request.pdus[tx_request.nof_pdus].length = ue_tx_buffer.at(buffer_index)->N_bytes;
tx_request.pdus[tx_request.nof_pdus].index = tx_request.nof_pdus;
if (pcap) {
pcap->write_dl_crnti_nr(tx_request.pdus[tx_request.nof_pdus].data[0],
tx_request.pdus[tx_request.nof_pdus].length,
args.rnti,
buffer_index,
tti);
}
tx_request.nof_pdus++;
}
}
config_request.tti = tti;
tx_request.tti = tti;
}
int mac_nr::slot_indication(const srsran_slot_cfg_t& slot_cfg)
{
phy_interface_stack_nr::dl_config_request_t config_request = {};
phy_interface_stack_nr::tx_request_t tx_request = {};
// step MAC TTI
logger.set_context(slot_cfg.idx);
get_dl_config(slot_cfg.idx, config_request, tx_request);
// send DL_CONFIG.request
phy_h->dl_config_request(config_request);
// send TX.request
phy_h->tx_request(tx_request);
return SRSRAN_SUCCESS;
}
int mac_nr::rx_data_indication(stack_interface_phy_nr::rx_data_ind_t& rx_data)
{
// push received PDU on queue
@ -270,6 +162,11 @@ int mac_nr::cell_cfg(srsenb::sched_interface::cell_cfg_t* cell_cfg)
return SRSRAN_SUCCESS;
}
int mac_nr::slot_indication(const srsran_slot_cfg_t& slot_cfg)
{
return 0;
}
int mac_nr::get_dl_sched(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched)
{
return 0;
@ -286,5 +183,6 @@ int mac_nr::pusch_info(const srsran_slot_cfg_t& slot_cfg, const mac_interface_ph
{
return 0;
}
void mac_nr::rach_detected(const mac_interface_phy_nr::rach_info_t& rach_info) {}
} // namespace srsenb

@ -20,7 +20,7 @@
namespace srsue {
namespace nr {
class worker_pool
class worker_pool : public srsue::phy_interface_stack_nr
{
private:
srslog::basic_logger& logger;
@ -37,12 +37,18 @@ public:
sf_worker* wait_worker(uint32_t tti);
void start_worker(sf_worker* w);
void stop();
void send_prach(uint32_t prach_occasion, uint32_t preamble_index, int preamble_received_target_power);
int set_ul_grant(std::array<uint8_t, SRSRAN_RAR_UL_GRANT_NBITS> array, uint16_t rnti, srsran_rnti_type_t rnti_type);
bool set_config(const srsran::phy_cfg_nr_t& cfg);
bool has_valid_sr_resource(uint32_t sr_id);
void clear_pending_grants();
void send_prach(const uint32_t prach_occasion,
const int preamble_index,
const float preamble_received_target_power,
const float ta_base_sec = 0.0f) override;
int set_ul_grant(std::array<uint8_t, SRSRAN_RAR_UL_GRANT_NBITS> array,
uint16_t rnti,
srsran_rnti_type_t rnti_type) override;
bool set_config(const srsran::phy_cfg_nr_t& cfg) override;
bool has_valid_sr_resource(uint32_t sr_id) override;
void clear_pending_grants() override;
void get_metrics(phy_metrics_t& m);
int tx_request(const tx_request_t& request) override;
};
} // namespace nr

@ -90,6 +90,7 @@ void sf_worker::work_imp()
if (prach_ptr != nullptr) {
// PRACH is available, set buffer, transmit and return
tx_buffer.set(0, prach_ptr);
tx_buffer.set_nof_samples(SRSRAN_SF_LEN_PRB_NR(phy_state.cfg.carrier.nof_prb));
// Notify MAC about PRACH transmission
phy_state.stack->prach_sent(TTI_TX(tti_rx),

@ -91,7 +91,10 @@ void worker_pool::stop()
pool.stop();
}
void worker_pool::send_prach(uint32_t prach_occasion, uint32_t preamble_index, int preamble_received_target_power)
void worker_pool::send_prach(const uint32_t prach_occasion,
const int preamble_index,
const float preamble_received_target_power,
const float ta_base_sec)
{
prach_buffer->prepare_to_send(preamble_index);
}
@ -186,6 +189,10 @@ void worker_pool::get_metrics(phy_metrics_t& m)
{
phy_state.get_metrics(m);
}
int worker_pool::tx_request(const phy_interface_mac_nr::tx_request_t& request)
{
return 0;
}
} // namespace nr
} // namespace srsue

@ -20,7 +20,11 @@ if (RF_FOUND AND ENABLE_SRSUE AND ENABLE_SRSENB)
${Boost_LIBRARIES}
${ATOMIC_LIBS})
add_nr_test(nr_phy_test_10MHz_dl_only nr_phy_test --duration=100 --gnb.stack.pdsch.slots=\"0,1,2,3,4,5\" --gnb.stack.pusch.slots=\"\")
add_nr_test(nr_phy_test_10MHz_dl_only nr_phy_test
--duration=100
--gnb.stack.pdsch.slots=\"0,1,2,3,4,5\"
--gnb.stack.pusch.slots=\"\")
add_nr_test(nr_phy_test_10MHz_ul_only nr_phy_test
--duration=100 # 100 slots
--gnb.stack.pdsch.slots=6 # No PDSCH
@ -29,6 +33,7 @@ if (RF_FOUND AND ENABLE_SRSUE AND ENABLE_SRSENB)
--gnb.stack.pusch.length=52 # Full 10 MHz BW
--gnb.stack.pusch.mcs=28 # Maximum MCS
)
add_nr_test(nr_phy_test_10MHz_bidir nr_phy_test
--duration=100 # 100 slots
--gnb.stack.pdsch.slots=0,1,2,3,4,5 # All possible DL slots
@ -40,4 +45,12 @@ if (RF_FOUND AND ENABLE_SRSUE AND ENABLE_SRSENB)
--gnb.stack.pusch.length=52 # Full 10 MHz BW
--gnb.stack.pusch.mcs=28 # Maximum MCS
)
add_nr_test(nr_phy_test_10MHz_prach nr_phy_test
--duration=1000 # 100 slots
--gnb.stack.pdsch.slots=6 # No PDSCH
--gnb.stack.pusch.slots=0 # No PUSCH
--ue.stack.prach.period=30 # Transmit PRACH every 30 radio frames
--ue.stack.prach.preamble=10 # Use preamble 10
)
endif ()

@ -26,6 +26,17 @@
class gnb_dummy_stack : public srsenb::stack_interface_phy_nr
{
public:
struct prach_metrics_t {
uint32_t count;
float avg_ta;
};
struct metrics_t {
std::map<uint32_t, prach_metrics_t> prach = {}; ///< PRACH metrics indexed with premable index
srsenb::mac_ue_metrics_t mac = {}; ///< MAC metrics
};
private:
srslog::basic_logger& logger = srslog::fetch_basic_logger("GNB STK");
const uint16_t rnti = 0x1234;
@ -41,8 +52,8 @@ private:
srsran::phy_cfg_nr_t phy_cfg = {};
bool valid = false;
std::mutex mac_metrics_mutex;
srsenb::mac_ue_metrics_t mac_metrics = {};
std::mutex metrics_mutex;
metrics_t metrics = {};
// HARQ feedback
class pending_ack_t
@ -245,16 +256,16 @@ private:
bool handle_uci_data(const srsran_uci_cfg_nr_t& cfg, const srsran_uci_value_nr_t& value)
{
std::unique_lock<std::mutex> lock(mac_metrics_mutex);
std::unique_lock<std::mutex> lock(metrics_mutex);
for (uint32_t i = 0; i < cfg.ack.count; i++) {
const srsran_harq_ack_bit_t* ack_bit = &cfg.ack.bits[i];
bool is_ok = (value.ack[i] == 1) and value.valid;
uint32_t tb_count = (ack_bit->tb0 ? 1 : 0) + (ack_bit->tb1 ? 1 : 0);
mac_metrics.tx_brate += tx_harq_proc[ack_bit->pid].get_tbs();
mac_metrics.tx_pkts += tb_count;
metrics.mac.tx_brate += tx_harq_proc[ack_bit->pid].get_tbs();
metrics.mac.tx_pkts += tb_count;
if (not is_ok) {
mac_metrics.tx_errors += tb_count;
metrics.mac.tx_errors += tb_count;
logger.debug("NACK received!");
}
}
@ -452,19 +463,27 @@ public:
}
if (not pusch_info.pusch_data.tb[0].crc) {
mac_metrics.rx_errors++;
metrics.mac.rx_errors++;
}
mac_metrics.rx_brate += rx_harq_proc[pusch_info.pid].get_tbs();
mac_metrics.rx_pkts++;
metrics.mac.rx_brate += rx_harq_proc[pusch_info.pid].get_tbs();
metrics.mac.rx_pkts++;
return SRSRAN_SUCCESS;
}
srsenb::mac_ue_metrics_t get_metrics()
void rach_detected(const rach_info_t& rach_info) override
{
std::unique_lock<std::mutex> lock(metrics_mutex);
prach_metrics_t& prach_metrics = metrics.prach[rach_info.preamble];
prach_metrics.avg_ta = SRSRAN_VEC_SAFE_CMA((float)rach_info.time_adv, prach_metrics.avg_ta, prach_metrics.count);
prach_metrics.count++;
}
metrics_t get_metrics()
{
std::unique_lock<std::mutex> lock(mac_metrics_mutex);
std::unique_lock<std::mutex> lock(metrics_mutex);
return mac_metrics;
return metrics;
}
};

@ -17,26 +17,60 @@
class ue_dummy_stack : public srsue::stack_interface_phy_nr
{
public:
struct prach_metrics_t {
uint32_t count;
};
struct metrics_t {
std::map<uint32_t, prach_metrics_t> prach = {}; ///< PRACH metrics indexed with premable index
};
private:
srsran_random_t random_gen = srsran_random_init(0x4567);
uint16_t rnti = 0;
bool valid = false;
uint32_t sr_period = 0;
uint32_t sr_count = 0;
srsran_random_t random_gen = srsran_random_init(0x4567);
uint16_t rnti = 0;
bool valid = false;
uint32_t sr_period = 0;
uint32_t sr_count = 0;
uint32_t prach_period = 0;
uint32_t prach_preamble = 0;
metrics_t metrics = {};
srsue::phy_interface_stack_nr& phy;
srsran::circular_array<dummy_tx_harq_proc, SRSRAN_MAX_HARQ_PROC_DL_NR> tx_harq_proc;
srsran::circular_array<dummy_rx_harq_proc, SRSRAN_MAX_HARQ_PROC_DL_NR> rx_harq_proc;
public:
struct args_t {
uint16_t rnti = 0x1234; ///< C-RNTI for PUSCH and PDSCH transmissions
uint32_t sr_period = 0; ///< Indicates positive SR period in number of opportunities. Set to 0 to disable.
uint16_t rnti = 0x1234; ///< C-RNTI for PUSCH and PDSCH transmissions
uint32_t sr_period = 0; ///< Indicates positive SR period in number of opportunities. Set to 0 to disable.
uint32_t prach_period = 0; ///< Requests PHY to transmit PRACH periodically in frames. Set to 0 to disable.
uint32_t prach_preamble = 0; ///< Requests PHY to transmit PRACH periodically in frames. Set to 0 to disable.
};
ue_dummy_stack(const args_t& args) : rnti(args.rnti), sr_period(args.sr_period) { valid = true; }
ue_dummy_stack(const args_t& args, srsue::phy_interface_stack_nr& phy_) :
rnti(args.rnti),
sr_period(args.sr_period),
prach_period(args.prach_period),
prach_preamble(args.prach_preamble),
phy(phy_)
{
valid = true;
}
~ue_dummy_stack() { srsran_random_free(random_gen); }
void in_sync() override {}
void out_of_sync() override {}
void run_tti(const uint32_t tti) override {}
void in_sync() override {}
void out_of_sync() override {}
void run_tti(const uint32_t tti) override
{
// Run PRACH
if (prach_period != 0) {
uint32_t slot_idx = tti % SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz);
uint32_t sfn = tti / SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz);
if (slot_idx == 0 and sfn % prach_period == 0) {
phy.send_prach(0, prach_preamble, 0.0f, 0.0f);
metrics.prach[prach_preamble].count++;
}
}
}
int sf_indication(const uint32_t tti) override { return 0; }
sched_rnti_t get_dl_sched_rnti_nr(const uint32_t tti) override { return {rnti, srsran_rnti_type_c}; }
sched_rnti_t get_ul_sched_rnti_nr(const uint32_t tti) override { return {rnti, srsran_rnti_type_c}; }
@ -70,6 +104,8 @@ public:
return ret;
}
bool is_valid() const { return valid; }
metrics_t get_metrics() { return metrics; }
};
#endif // SRSRAN_DUMMY_UE_STACK_H

@ -72,7 +72,9 @@ test_bench::args_t::args_t(int argc, char** argv)
;
options_ue_stack.add_options()
("ue.stack.sr.period", bpo::value<uint32_t>(&ue_stack.sr_period)->default_value(ue_stack.sr_period), "SR period in number of opportunities. Set 0 to disable and 1 for all.")
("ue.stack.sr.period", bpo::value<uint32_t>(&ue_stack.sr_period)->default_value(ue_stack.sr_period), "SR period in number of opportunities. Set 0 to disable and 1 for all.")
("ue.stack.prach.period", bpo::value<uint32_t>(&ue_stack.prach_period)->default_value(ue_stack.prach_period), "PRACH period in SFN. Set 0 to disable and 1 for all.")
("ue.stack.prach.preamble", bpo::value<uint32_t>(&ue_stack.prach_preamble)->default_value(ue_stack.prach_preamble), "PRACH preamble. Set 0 to disable and 1 for all.")
;
options.add(options_gnb_stack).add(options_gnb_phy).add(options_ue_stack).add(options_ue_phy).add_options()
@ -153,18 +155,18 @@ int main(int argc, char** argv)
srslog::flush();
// Retrieve MAC metrics
srsenb::mac_ue_metrics_t mac_metrics = tb.get_gnb_metrics();
test_bench::metrics_t metrics = tb.get_gnb_metrics();
// Print PDSCH metrics if scheduled
if (mac_metrics.tx_pkts > 0) {
if (metrics.gnb_stack.mac.tx_pkts > 0) {
float pdsch_bler = 0.0f;
pdsch_bler = (float)mac_metrics.tx_errors / (float)mac_metrics.tx_pkts;
pdsch_bler = (float)metrics.gnb_stack.mac.tx_errors / (float)metrics.gnb_stack.mac.tx_pkts;
float pdsch_shed_rate = 0.0f;
pdsch_shed_rate = (float)mac_metrics.tx_brate / (float)mac_metrics.tx_pkts / 1000.0f;
pdsch_shed_rate = (float)metrics.gnb_stack.mac.tx_brate / (float)metrics.gnb_stack.mac.tx_pkts / 1000.0f;
srsran::console("PDSCH:\n");
srsran::console(" Count: %d\n", mac_metrics.tx_pkts);
srsran::console(" Count: %d\n", metrics.gnb_stack.mac.tx_pkts);
srsran::console(" BLER: %f\n", pdsch_bler);
srsran::console(" Sched Rate: %f Mbps\n", pdsch_shed_rate);
srsran::console(" Net Rate: %f Mbps\n", (1.0f - pdsch_bler) * pdsch_shed_rate);
@ -173,19 +175,19 @@ int main(int argc, char** argv)
}
// Print PUSCH metrics if scheduled
if (mac_metrics.rx_pkts > 0) {
if (metrics.gnb_stack.mac.rx_pkts > 0) {
float pusch_bler = 0.0f;
if (mac_metrics.rx_pkts != 0) {
pusch_bler = (float)mac_metrics.rx_errors / (float)mac_metrics.rx_pkts;
if (metrics.gnb_stack.mac.rx_pkts != 0) {
pusch_bler = (float)metrics.gnb_stack.mac.rx_errors / (float)metrics.gnb_stack.mac.rx_pkts;
}
float pusch_shed_rate = 0.0f;
if (mac_metrics.rx_pkts != 0) {
pusch_shed_rate = (float)mac_metrics.rx_brate / (float)mac_metrics.rx_pkts / 1000.0f;
if (metrics.gnb_stack.mac.rx_pkts != 0) {
pusch_shed_rate = (float)metrics.gnb_stack.mac.rx_brate / (float)metrics.gnb_stack.mac.rx_pkts / 1000.0f;
}
srsran::console("PUSCH:\n");
srsran::console(" Count: %d\n", mac_metrics.rx_pkts);
srsran::console(" Count: %d\n", metrics.gnb_stack.mac.rx_pkts);
srsran::console(" BLER: %f\n", pusch_bler);
srsran::console(" Sched Rate: %f Mbps\n", pusch_shed_rate);
srsran::console(" Net Rate: %f Mbps\n", (1.0f - pusch_bler) * pusch_shed_rate);
@ -193,9 +195,43 @@ int main(int argc, char** argv)
srsran::console("\n");
}
// Print PRACH
if (metrics.ue_stack.prach.size() > 0) {
srsran::console("PRACH:\n");
srsran::console(" UE transmitted:\n");
srsran::console(" +------------+------------+\n");
srsran::console(" | %10s | %10s |\n", "preamble", "count");
srsran::console(" +------------+------------+\n");
for (const auto& p : metrics.ue_stack.prach) {
srsran::console(" | %10d | %10d |\n", p.first, p.second.count);
// Ensure the detected count matches with transmission
TESTASSERT(metrics.gnb_stack.prach.count(p.first));
TESTASSERT(metrics.gnb_stack.prach[p.first].count == p.second.count);
}
srsran::console(" +------------+------------+\n\n");
srsran::console(" GNB detected:\n");
srsran::console(" +------------+------------+------------+\n");
srsran::console(" | %10s | %10s | %10s |\n", "preamble", "count", "avg TA");
srsran::console(" +------------+------------+------------+\n");
for (const auto& p : metrics.gnb_stack.prach) {
srsran::console(" | %10d | %10d | %10.1f |\n", p.first, p.second.count, p.second.avg_ta);
// Ensure all detected preambles were transmitted
TESTASSERT(metrics.ue_stack.prach.count(p.first) > 0);
}
srsran::console(" +------------+------------+------------+\n\n");
} else {
// In this case no PRACH should
TESTASSERT(metrics.gnb_stack.prach.empty());
}
// Assert metrics
TESTASSERT(mac_metrics.tx_errors == 0);
TESTASSERT(mac_metrics.rx_errors == 0);
TESTASSERT(metrics.gnb_stack.mac.tx_errors == 0);
TESTASSERT(metrics.gnb_stack.mac.rx_errors == 0);
// If reached here, the test is successful
return SRSRAN_SUCCESS;

@ -24,7 +24,7 @@ private:
const std::string GNB_PHY_COM_LOG_NAME = "GNB/PHY/COM";
uint32_t slot_idx = 0;
uint64_t slot_count = 0;
uint64_t duration_slots;
uint64_t duration_slots = 0;
gnb_dummy_stack gnb_stack;
srsenb::nr::worker_pool gnb_phy;
phy_common gnb_phy_com;
@ -53,10 +53,15 @@ public:
args_t(int argc, char** argv);
};
struct metrics_t {
gnb_dummy_stack::metrics_t gnb_stack = {};
ue_dummy_stack::metrics_t ue_stack = {};
};
test_bench(const args_t& args) :
gnb_stack(args.gnb_stack),
gnb_phy(gnb_phy_com, gnb_stack, srslog::get_default_sink(), args.gnb_phy.nof_phy_threads),
ue_stack(args.ue_stack),
ue_stack(args.ue_stack, ue_phy),
ue_phy(args.ue_phy.nof_phy_threads),
ue_phy_com(phy_common::args_t(args.srate_hz, args.buffer_sz_ms, args.nof_channels),
@ -73,6 +78,15 @@ public:
return;
}
srsenb::phy_interface_rrc_nr::common_cfg_t common_cfg = {};
common_cfg.carrier = args.phy_cfg.carrier;
common_cfg.pdcch = args.phy_cfg.pdcch;
common_cfg.prach = args.phy_cfg.prach;
if (gnb_phy.set_common_cfg(common_cfg) < SRSRAN_SUCCESS) {
return;
}
// Initialise UE PHY
if (not ue_phy.init(args.ue_phy, ue_phy_com, &ue_stack, 31)) {
return;
@ -147,7 +161,10 @@ public:
ue_worker->set_tti(slot_idx);
ue_worker->set_tx_time(ue_time);
// Start gNb work
// Run UE stack
ue_stack.run_tti(slot_idx);
// Start UE work
ue_phy_com.push_semaphore(ue_worker);
ue_phy.start_worker(ue_worker);
@ -156,7 +173,13 @@ public:
return slot_count <= duration_slots;
}
srsenb::mac_ue_metrics_t get_gnb_metrics() { return gnb_stack.get_metrics(); }
metrics_t get_gnb_metrics()
{
metrics_t metrics = {};
metrics.gnb_stack = gnb_stack.get_metrics();
metrics.ue_stack = ue_stack.get_metrics();
return metrics;
}
};
#endif // SRSRAN_TEST_BENCH_H

Loading…
Cancel
Save