Refactored gNb PHY

master
Xavier Arteaga 4 years ago committed by Xavier Arteaga
parent c4e13f70a2
commit 8378c85ec4

@ -212,52 +212,50 @@ class mac_interface_phy_nr
public: public:
const static int MAX_GRANTS = 64; const static int MAX_GRANTS = 64;
/** struct pdcch_dl_t {
* DL grant structure per UE srsran_dci_cfg_nr_t dci_cfg = {};
*/ srsran_dci_dl_nr_t dci = {};
struct dl_sched_grant_t { };
srsran_dci_dl_nr_t dci = {};
uint8_t* data[SRSRAN_MAX_TB] = {}; struct pdcch_ul_t {
srsran_softbuffer_tx_t* softbuffer_tx[SRSRAN_MAX_TB] = {}; srsran_dci_cfg_nr_t dci_cfg = {};
srsran_dci_ul_nr_t dci = {};
};
struct pdsch_t {
srsran_sch_cfg_nr_t sch = {}; ///< PDSCH configuration
std::array<uint8_t*, SRSRAN_MAX_TB> data = {}; ///< Data pointer
}; };
/**
* DL Scheduling result per cell/carrier
*/
struct dl_sched_t { struct dl_sched_t {
dl_sched_grant_t pdsch[MAX_GRANTS]; //< DL Grants std::array<pdcch_dl_t, MAX_GRANTS> pdcch_dl;
uint32_t nof_grants; //< Number of DL grants uint32_t pdcch_dl_count = 0;
std::array<pdcch_ul_t, MAX_GRANTS> pdcch_ul;
uint32_t pdcch_ul_count = 0;
std::array<pdsch_t, MAX_GRANTS> pdsch;
uint32_t pdsch_count = 0;
}; };
/** struct pusch_t {
* List of DL scheduling results, one entry per cell/carrier srsran_sch_cfg_nr_t sch = {}; ///< PUSCH configuration
*/ std::array<uint8_t*, SRSRAN_MAX_TB> data = {}; ///< Data pointer
typedef std::vector<dl_sched_t> dl_sched_list_t; std::array<srsran_softbuffer_tx_t*, SRSRAN_MAX_TB> softbuffer_tx = {}; ///< Tx Softbuffer
/**
* UL grant structure per UE
*/
struct ul_sched_grant_t {
srsran_dci_ul_nr_t dci = {};
uint8_t* data = nullptr;
srsran_softbuffer_rx_t* softbuffer_rx = nullptr;
}; };
/** struct uci_t {
* UL Scheduling result per cell/carrier srsran_uci_cfg_nr_t cfg;
*/
struct ul_sched_t {
ul_sched_grant_t pusch[MAX_GRANTS]; //< UL Grants
uint32_t nof_grants; //< Number of UL grants
}; };
/** struct ul_sched_t {
* List of UL scheduling results, one entry per cell/carrier std::array<pusch_t, MAX_GRANTS> pusch;
*/ uint32_t pusch_count = 0;
typedef std::vector<ul_sched_t> ul_sched_list_t; std::array<uci_t, MAX_GRANTS> uci;
uint32_t uci_count;
};
virtual int get_dl_sched(uint32_t tti, dl_sched_list_t& dl_sched_res) = 0; virtual int slot_indication(const srsran_slot_cfg_t& slot_cfg) = 0;
virtual int get_ul_sched(uint32_t tti, ul_sched_list_t& ul_sched_res) = 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;
}; };
class stack_interface_phy_nr : public mac_interface_phy_nr, public srsran::stack_interface_phy_nr class stack_interface_phy_nr : public mac_interface_phy_nr, public srsran::stack_interface_phy_nr

@ -57,8 +57,13 @@ SRSRAN_API int srsran_enb_dl_nr_base_zero(srsran_enb_dl_nr_t* q);
SRSRAN_API void srsran_enb_dl_nr_gen_signal(srsran_enb_dl_nr_t* q); SRSRAN_API void srsran_enb_dl_nr_gen_signal(srsran_enb_dl_nr_t* q);
SRSRAN_API int SRSRAN_API int srsran_enb_dl_nr_pdcch_put_dl(srsran_enb_dl_nr_t* q,
srsran_enb_dl_nr_pdcch_put(srsran_enb_dl_nr_t* q, const srsran_slot_cfg_t* slot_cfg, const srsran_dci_dl_nr_t* dci_dl); const srsran_slot_cfg_t* slot_cfg,
const srsran_dci_dl_nr_t* dci_dl);
SRSRAN_API int srsran_enb_dl_nr_pdcch_put_ul(srsran_enb_dl_nr_t* q,
const srsran_slot_cfg_t* slot_cfg,
const srsran_dci_ul_nr_t* dci_ul);
SRSRAN_API int srsran_enb_dl_nr_pdsch_put(srsran_enb_dl_nr_t* q, SRSRAN_API int srsran_enb_dl_nr_pdsch_put(srsran_enb_dl_nr_t* q,
const srsran_slot_cfg_t* slot, const srsran_slot_cfg_t* slot,
@ -68,4 +73,10 @@ SRSRAN_API int srsran_enb_dl_nr_pdsch_put(srsran_enb_dl_nr_t* q,
SRSRAN_API int SRSRAN_API int
srsran_enb_dl_nr_pdsch_info(const srsran_enb_dl_nr_t* q, const srsran_sch_cfg_nr_t* cfg, char* str, uint32_t str_len); srsran_enb_dl_nr_pdsch_info(const srsran_enb_dl_nr_t* q, const srsran_sch_cfg_nr_t* cfg, char* str, uint32_t str_len);
SRSRAN_API int
srsran_enb_dl_nr_pdcch_dl_info(const srsran_enb_dl_nr_t* q, const srsran_dci_dl_nr_t* dci, char* str, uint32_t str_len);
SRSRAN_API int
srsran_enb_dl_nr_pdcch_ul_info(const srsran_enb_dl_nr_t* q, const srsran_dci_ul_nr_t* dci, char* str, uint32_t str_len);
#endif // SRSRAN_ENB_DL_NR_H #endif // SRSRAN_ENB_DL_NR_H

@ -180,20 +180,15 @@ int srsran_enb_dl_nr_base_zero(srsran_enb_dl_nr_t* q)
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
int srsran_enb_dl_nr_pdcch_put(srsran_enb_dl_nr_t* q, static int
const srsran_slot_cfg_t* slot_cfg, enb_dl_nr_pdcch_put_msg(srsran_enb_dl_nr_t* q, const srsran_slot_cfg_t* slot_cfg, const srsran_dci_msg_nr_t* dci_msg)
const srsran_dci_dl_nr_t* dci_dl)
{ {
if (q == NULL || slot_cfg == NULL || dci_dl == NULL) { if (dci_msg->ctx.coreset_id >= SRSRAN_UE_DL_NR_MAX_NOF_CORESET ||
return SRSRAN_ERROR_INVALID_INPUTS; !q->pdcch_cfg.coreset_present[dci_msg->ctx.coreset_id]) {
} ERROR("Invalid CORESET ID %d", dci_msg->ctx.coreset_id);
if (dci_dl->ctx.coreset_id >= SRSRAN_UE_DL_NR_MAX_NOF_CORESET ||
!q->pdcch_cfg.coreset_present[dci_dl->ctx.coreset_id]) {
ERROR("Invalid CORESET ID %d", dci_dl->ctx.coreset_id);
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
srsran_coreset_t* coreset = &q->pdcch_cfg.coreset[dci_dl->ctx.coreset_id]; srsran_coreset_t* coreset = &q->pdcch_cfg.coreset[dci_msg->ctx.coreset_id];
if (srsran_pdcch_nr_set_carrier(&q->pdcch, &q->carrier, coreset) < SRSRAN_SUCCESS) { if (srsran_pdcch_nr_set_carrier(&q->pdcch, &q->carrier, coreset) < SRSRAN_SUCCESS) {
ERROR("Error setting PDCCH carrier/CORESET"); ERROR("Error setting PDCCH carrier/CORESET");
@ -201,11 +196,31 @@ int srsran_enb_dl_nr_pdcch_put(srsran_enb_dl_nr_t* q,
} }
// Put DMRS // Put DMRS
if (srsran_dmrs_pdcch_put(&q->carrier, coreset, slot_cfg, &dci_dl->ctx.location, q->sf_symbols[0]) < SRSRAN_SUCCESS) { if (srsran_dmrs_pdcch_put(&q->carrier, coreset, slot_cfg, &dci_msg->ctx.location, q->sf_symbols[0]) <
SRSRAN_SUCCESS) {
ERROR("Error putting PDCCH DMRS"); ERROR("Error putting PDCCH DMRS");
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
// PDCCH Encode
if (srsran_pdcch_nr_encode(&q->pdcch, dci_msg, q->sf_symbols[0]) < SRSRAN_SUCCESS) {
ERROR("Error encoding PDCCH");
return SRSRAN_ERROR;
}
INFO("DCI DL NR: L=%d; ncce=%d;", dci_msg->ctx.location.L, dci_msg->ctx.location.ncce);
return SRSRAN_SUCCESS;
}
int srsran_enb_dl_nr_pdcch_put_dl(srsran_enb_dl_nr_t* q,
const srsran_slot_cfg_t* slot_cfg,
const srsran_dci_dl_nr_t* dci_dl)
{
if (q == NULL || slot_cfg == NULL || dci_dl == NULL) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
// Pack DCI // Pack DCI
srsran_dci_msg_nr_t dci_msg = {}; srsran_dci_msg_nr_t dci_msg = {};
if (srsran_dci_nr_dl_pack(&q->dci, dci_dl, &dci_msg) < SRSRAN_SUCCESS) { if (srsran_dci_nr_dl_pack(&q->dci, dci_dl, &dci_msg) < SRSRAN_SUCCESS) {
@ -213,15 +228,29 @@ int srsran_enb_dl_nr_pdcch_put(srsran_enb_dl_nr_t* q,
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
// PDCCH Encode INFO("DCI DL NR: L=%d; ncce=%d;", dci_dl->ctx.location.L, dci_dl->ctx.location.ncce);
if (srsran_pdcch_nr_encode(&q->pdcch, &dci_msg, q->sf_symbols[0]) < SRSRAN_SUCCESS) {
ERROR("Error encoding PDCCH"); return enb_dl_nr_pdcch_put_msg(q, slot_cfg, &dci_msg);
}
int srsran_enb_dl_nr_pdcch_put_ul(srsran_enb_dl_nr_t* q,
const srsran_slot_cfg_t* slot_cfg,
const srsran_dci_ul_nr_t* dci_ul)
{
if (q == NULL || slot_cfg == NULL || dci_ul == NULL) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
// Pack DCI
srsran_dci_msg_nr_t dci_msg = {};
if (srsran_dci_nr_ul_pack(&q->dci, dci_ul, &dci_msg) < SRSRAN_SUCCESS) {
ERROR("Error packing UL DCI");
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
INFO("DCI DL NR: L=%d; ncce=%d;", dci_dl->ctx.location.L, dci_dl->ctx.location.ncce); INFO("DCI DL NR: L=%d; ncce=%d;", dci_ul->ctx.location.L, dci_ul->ctx.location.ncce);
return SRSRAN_SUCCESS; return enb_dl_nr_pdcch_put_msg(q, slot_cfg, &dci_msg);
} }
int srsran_enb_dl_nr_pdsch_put(srsran_enb_dl_nr_t* q, int srsran_enb_dl_nr_pdsch_put(srsran_enb_dl_nr_t* q,
@ -252,3 +281,29 @@ int srsran_enb_dl_nr_pdsch_info(const srsran_enb_dl_nr_t* q,
return len; return len;
} }
int srsran_enb_dl_nr_pdcch_dl_info(const srsran_enb_dl_nr_t* q,
const srsran_dci_dl_nr_t* dci,
char* str,
uint32_t str_len)
{
int len = 0;
// Append PDCCH info
len += srsran_dci_dl_nr_to_str(&q->dci, dci, &str[len], str_len - len);
return len;
}
int srsran_enb_dl_nr_pdcch_ul_info(const srsran_enb_dl_nr_t* q,
const srsran_dci_ul_nr_t* dci,
char* str,
uint32_t str_len)
{
int len = 0;
// Append PDCCH info
len += srsran_dci_ul_nr_to_str(&q->dci, dci, &str[len], str_len - len);
return len;
}

@ -170,7 +170,7 @@ static int work_gnb_dl(srsran_enb_dl_nr_t* enb_dl,
dci_dl.rv = 0; dci_dl.rv = 0;
// Put actual DCI // Put actual DCI
if (srsran_enb_dl_nr_pdcch_put(enb_dl, slot, &dci_dl) < SRSRAN_SUCCESS) { if (srsran_enb_dl_nr_pdcch_put_dl(enb_dl, slot, &dci_dl) < SRSRAN_SUCCESS) {
ERROR("Error putting PDCCH"); ERROR("Error putting PDCCH");
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }

@ -1,69 +0,0 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#ifndef SRSENB_NR_CC_WORKER_H
#define SRSENB_NR_CC_WORKER_H
#include "phy_nr_state.h"
#include "srsenb/hdr/phy/phy_interfaces.h"
#include "srsran/interfaces/gnb_interfaces.h"
#include "srsran/interfaces/rrc_nr_interface_types.h"
#include "srsran/phy/enb/enb_dl_nr.h"
#include "srsran/srslog/srslog.h"
#include "srsran/srsran.h"
#include <array>
#include <vector>
namespace srsenb {
namespace nr {
class cc_worker
{
public:
struct args_t {
uint32_t cc_idx = 0;
srsran_carrier_nr_t carrier = {};
srsran_enb_dl_nr_args_t dl = {};
};
cc_worker(const args_t& args, srslog::basic_logger& logger, phy_nr_state& phy_state_);
~cc_worker();
void set_tti(uint32_t tti);
cf_t* get_tx_buffer(uint32_t antenna_idx);
cf_t* get_rx_buffer(uint32_t antenna_idx);
uint32_t get_buffer_len();
bool work_dl(stack_interface_phy_nr::dl_sched_t& dl_grants, stack_interface_phy_nr::ul_sched_t& ul_grants);
bool work_ul();
private:
int encode_pdsch(stack_interface_phy_nr::dl_sched_grant_t* grants, uint32_t nof_grants);
int encode_pdcch_dl(stack_interface_phy_nr::dl_sched_grant_t* grants, uint32_t nof_grants);
uint32_t nof_tx_antennas = 0;
srsran_slot_cfg_t dl_slot_cfg = {};
srsran_slot_cfg_t ul_slot_cfg = {};
uint32_t cc_idx = 0;
std::array<cf_t*, SRSRAN_MAX_PORTS> tx_buffer = {};
std::array<cf_t*, SRSRAN_MAX_PORTS> rx_buffer = {};
uint32_t buffer_sz = 0;
phy_nr_state& phy_state;
srsran_enb_dl_nr_t gnb_dl = {};
srslog::basic_logger& logger;
};
} // namespace nr
} // namespace srsenb
#endif // SRSENB_NR_CC_WORKER_H

@ -1,103 +0,0 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2020 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#ifndef SRSENB_PHY_NR_UE_DB_H_
#define SRSENB_PHY_NR_UE_DB_H_
#include <map>
#include <mutex>
#include <srsenb/hdr/phy/phy_interfaces.h>
#include <srsran/adt/circular_array.h>
#include <srsran/interfaces/gnb_interfaces.h>
#include <srsran/interfaces/rrc_nr_interface_types.h>
namespace srsenb {
namespace nr {
class phy_nr_state
{
private:
/**
* UE object stored in the PHY common database
*/
struct common_ue {
srsran::phy_cfg_nr_t cfg;
};
/**
* UE database indexed by RNTI
*/
std::map<uint16_t, common_ue> ue_db;
/**
* Concurrency protection mutex, allowed modifications from const methods.
*/
mutable std::mutex mutex;
/**
* Stack interface
*/
stack_interface_phy_nr& stack;
/**
* Cell list
*/
const phy_cell_cfg_list_nr_t& cell_cfg_list;
/**
* Internal RNTI addition, it is not thread safe protected
*
* @param rnti identifier of the UE
* @return SRSRAN_SUCCESS if the RNTI is not duplicated and is added successfully, SRSRAN_ERROR code if it exists
*/
inline int _add_rnti(uint16_t rnti);
/**
* Checks if a given RNTI exists in the database
* @param rnti provides UE identifier
* @return SRSRAN_SUCCESS if the indicated RNTI exists, otherwise it returns SRSRAN_ERROR
*/
inline int _assert_rnti(uint16_t rnti) const;
/**
* Internal eNb general configuration getter, returns default configuration if the UE does not exist in the given cell
*
* @param rnti provides UE identifier
* @param[out] phy_cfg The PHY configuration of the indicated UE for the indicated eNb carrier/call index.
* @return SRSRAN_SUCCESS if provided context is correct, SRSRAN_ERROR code otherwise
*/
inline int _get_rnti_config(uint16_t rnti, srsran::phy_cfg_nr_t& phy_cfg) const;
public:
phy_nr_state(const phy_cell_cfg_list_nr_t& cell_cfg_list_, stack_interface_phy_nr& stack_);
void addmod_rnti(uint16_t rnti, const srsran::phy_cfg_nr_t& phy_cfg);
/**
* Removes a whole UE entry from the UE database
*
* @param rnti identifier of the UE
* @return SRSRAN_SUCCESS if provided RNTI exists, SRSRAN_ERROR code otherwise
*/
int rem_rnti(uint16_t rnti);
int get_config(uint16_t rnti, srsran::phy_cfg_nr_t& phy_cfg);
const phy_cell_cfg_list_nr_t& get_carrier_list() const { return cell_cfg_list; }
stack_interface_phy_nr& get_stack() { return stack; }
};
} // namespace nr
} // namespace srsenb
#endif // SRSENB_PHY_NR_UE_DB_H_

@ -1,66 +0,0 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#ifndef SRSENB_NR_PHCH_WORKER_H
#define SRSENB_NR_PHCH_WORKER_H
#include "cc_worker.h"
#include "srsran/common/thread_pool.h"
#include "srsran/interfaces/phy_common_interface.h"
#include "srsran/srslog/srslog.h"
namespace srsenb {
namespace nr {
/**
* The sf_worker class handles the PHY processing, UL and DL procedures associated with 1 subframe.
* It contains multiple cc_worker objects, one for each component carrier which may be executed in
* one or multiple threads.
*
* A sf_worker object is executed by a thread within the thread_pool.
*/
class sf_worker final : public srsran::thread_pool::worker
{
public:
sf_worker(srsran::phy_common_interface& common_, phy_nr_state& phy_state_, srslog::basic_logger& logger);
~sf_worker();
/* Functions used by main PHY thread */
cf_t* get_buffer_rx(uint32_t cc_idx, uint32_t antenna_idx);
cf_t* get_buffer_tx(uint32_t cc_idx, uint32_t antenna_idx);
uint32_t get_buffer_len();
void set_time(const uint32_t& tti, const srsran::rf_timestamp_t& timestamp);
private:
/* Inherited from thread_pool::worker. Function called every subframe to run the DL/UL processing */
void work_imp() override;
std::vector<std::unique_ptr<cc_worker> > cc_workers;
srsran::phy_common_interface& common;
phy_nr_state& phy_state;
srslog::basic_logger& logger;
srsran_slot_cfg_t dl_slot_cfg = {};
srsran_slot_cfg_t ul_slot_cfg = {};
srsran::rf_timestamp_t tx_time = {};
uint32_t sf_len = 0;
// Temporal attributes
srsran_softbuffer_tx_t softbuffer_tx = {};
std::vector<uint8_t> data;
};
} // namespace nr
} // namespace srsenb
#endif // SRSENB_NR_PHCH_WORKER_H

@ -0,0 +1,90 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#ifndef SRSENB_NR_SLOT_WORKER_H
#define SRSENB_NR_SLOT_WORKER_H
#include "srsran/common/thread_pool.h"
#include "srsran/interfaces/gnb_interfaces.h"
#include "srsran/interfaces/phy_common_interface.h"
#include "srsran/srslog/srslog.h"
#include "srsran/srsran.h"
namespace srsenb {
namespace nr {
/**
* The slot_worker class handles the PHY processing, UL and DL procedures associated with 1 slot.
*
* A slot_worker object is executed by a thread within the thread_pool.
*/
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
};
slot_worker(srsran::phy_common_interface& common_, stack_interface_phy_nr& stack_, srslog::basic_logger& logger);
~slot_worker();
bool init(const args_t& args);
/* Functions used by main PHY thread */
cf_t* get_buffer_rx(uint32_t antenna_idx);
cf_t* get_buffer_tx(uint32_t antenna_idx);
uint32_t get_buffer_len();
void set_time(const uint32_t& tti, const srsran::rf_timestamp_t& timestamp);
private:
/**
* @brief Inherited from thread_pool::worker. Function called every slot to run the DL/UL processing
*/
void work_imp() override;
/**
* @brief Retrieves the scheduling results for the UL processing and performs reception
* @return True if no error occurs, false otherwise
*/
bool work_ul();
/**
* @brief Retrieves the scheduling results for the DL processing and performs transmission
* @return True if no error occurs, false otherwise
*/
bool work_dl();
srsran::phy_common_interface& common;
stack_interface_phy_nr& stack;
srslog::basic_logger& logger;
uint32_t sf_len = 0;
uint32_t cell_index = 0;
srsran_slot_cfg_t dl_slot_cfg = {};
srsran_slot_cfg_t ul_slot_cfg = {};
srsran_pdcch_cfg_nr_t pdcch_cfg = {};
srsran::rf_timestamp_t tx_time = {};
srsran_enb_dl_nr_t gnb_dl = {};
std::vector<cf_t*> tx_buffer; ///< Baseband transmit buffers
std::vector<cf_t*> rx_buffer; ///< Baseband receive buffers
};
} // namespace nr
} // namespace srsenb
#endif // SRSENB_NR_PHCH_WORKER_H

@ -13,40 +13,42 @@
#ifndef SRSENB_NR_WORKER_POOL_H #ifndef SRSENB_NR_WORKER_POOL_H
#define SRSENB_NR_WORKER_POOL_H #define SRSENB_NR_WORKER_POOL_H
#include "phy_nr_state.h" #include "slot_worker.h"
#include "sf_worker.h"
#include "srsenb/hdr/phy/phy_interfaces.h" #include "srsenb/hdr/phy/phy_interfaces.h"
#include "srsran/common/thread_pool.h" #include "srsran/common/thread_pool.h"
#include "srsran/interfaces/gnb_interfaces.h"
namespace srsenb { namespace srsenb {
namespace nr { namespace nr {
class worker_pool class worker_pool
{ {
srsran::thread_pool pool; srsran::phy_common_interface& common;
std::vector<std::unique_ptr<sf_worker> > workers; stack_interface_phy_nr& stack;
phy_nr_state phy_state; srslog::sink& log_sink;
srsran::thread_pool pool;
std::vector<std::unique_ptr<slot_worker> > workers;
public: public:
struct args_t { struct args_t {
uint32_t nof_workers = 3; uint32_t nof_phy_threads = 3;
uint32_t prio = 52; uint32_t prio = 52;
std::string log_level = "info"; std::string log_level = "info";
uint32_t log_hex_limit = 64; uint32_t log_hex_limit = 64;
std::string log_id_preamble = ""; std::string log_id_preamble = "";
uint32_t pusch_max_nof_iter = 10;
}; };
sf_worker* operator[](std::size_t pos) { return workers.at(pos).get(); } slot_worker* operator[](std::size_t pos) { return workers.at(pos).get(); }
worker_pool(const phy_cell_cfg_list_nr_t& cell_list, worker_pool(srsran::phy_common_interface& common,
const args_t& args,
srsran::phy_common_interface& common,
stack_interface_phy_nr& stack, stack_interface_phy_nr& stack,
srslog::sink& log_sink); srslog::sink& log_sink,
sf_worker* wait_worker(uint32_t tti); uint32_t max_workers);
sf_worker* wait_worker_id(uint32_t id); bool init(const args_t& args, const phy_cell_cfg_list_nr_t& cell_list);
void start_worker(sf_worker* w); slot_worker* wait_worker(uint32_t tti);
void stop(); slot_worker* wait_worker_id(uint32_t id);
bool addmod_rnti(uint16_t rnti, const srsran::phy_cfg_nr_t& phy_cfg); void start_worker(slot_worker* w);
void stop();
}; };
} // namespace nr } // namespace nr

@ -16,7 +16,7 @@
#include "srsran/asn1/rrc/rr_common.h" #include "srsran/asn1/rrc/rr_common.h"
#include "srsran/common/interfaces_common.h" #include "srsran/common/interfaces_common.h"
#include "srsran/phy/channel/channel.h" #include "srsran/phy/channel/channel.h"
#include "srsran/phy/common/phy_common_nr.h" #include "srsran/srsran.h"
#include <inttypes.h> #include <inttypes.h>
#include <vector> #include <vector>
@ -34,14 +34,15 @@ struct phy_cell_cfg_t {
}; };
struct phy_cell_cfg_nr_t { struct phy_cell_cfg_nr_t {
srsran_carrier_nr_t carrier; srsran_carrier_nr_t carrier;
uint32_t rf_port; uint32_t rf_port;
uint32_t cell_id; uint32_t cell_id;
double dl_freq_hz; double dl_freq_hz;
double ul_freq_hz; double ul_freq_hz;
uint32_t root_seq_idx; uint32_t root_seq_idx;
uint32_t num_ra_preambles; uint32_t num_ra_preambles;
float gain_db; float gain_db;
srsran_pdcch_cfg_nr_t pdcch = {}; ///< Common CORESET and Search Space configuration
}; };
typedef std::vector<phy_cell_cfg_t> phy_cell_cfg_list_t; typedef std::vector<phy_cell_cfg_t> phy_cell_cfg_list_t;
@ -51,18 +52,18 @@ struct phy_args_t {
std::string type; std::string type;
srsran::phy_log_args_t log; srsran::phy_log_args_t log;
float max_prach_offset_us = 10; float max_prach_offset_us = 10;
int pusch_max_its = 10; int pusch_max_its = 10;
bool pusch_8bit_decoder = false; bool pusch_8bit_decoder = false;
float tx_amplitude = 1.0f; float tx_amplitude = 1.0f;
uint32_t nof_phy_threads = 1; uint32_t nof_phy_threads = 1;
std::string equalizer_mode = "mmse"; std::string equalizer_mode = "mmse";
float estimator_fil_w = 1.0f; float estimator_fil_w = 1.0f;
bool pusch_meas_epre = true; bool pusch_meas_epre = true;
bool pusch_meas_evm = false; bool pusch_meas_evm = false;
bool pusch_meas_ta = true; bool pusch_meas_ta = true;
bool pucch_meas_ta = true; bool pucch_meas_ta = true;
uint32_t nof_prach_threads = 1; uint32_t nof_prach_threads = 1;
bool extended_cp = false; bool extended_cp = false;
srsran::channel::args_t dl_channel_args; srsran::channel::args_t dl_channel_args;
srsran::channel::args_t ul_channel_args; srsran::channel::args_t ul_channel_args;

@ -55,12 +55,12 @@ public:
bool get_metrics(srsenb::stack_metrics_t* metrics) final; bool get_metrics(srsenb::stack_metrics_t* metrics) final;
// GW srsue stack_interface_gw dummy interface // GW srsue stack_interface_gw dummy interface
bool is_registered() { return true; }; bool is_registered() override { return true; };
bool start_service_request() { return true; }; bool start_service_request() override { return true; };
// PHY->MAC interface // PHY->MAC interface
int sf_indication(const uint32_t tti); int sf_indication(const uint32_t tti) override;
int rx_data_indication(rx_data_ind_t& grant); int rx_data_indication(rx_data_ind_t& grant) override;
// Temporary GW interface // Temporary GW interface
void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu); void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu);
@ -71,9 +71,11 @@ public:
// MAC interface to trigger processing of received PDUs // MAC interface to trigger processing of received PDUs
void process_pdus() final; void process_pdus() final;
void toggle_padding() { srsran::console("padding not available for NR\n"); } void toggle_padding() override { srsran::console("padding not available for NR\n"); }
int get_dl_sched(uint32_t tti, dl_sched_list_t& dl_sched_res) override;
int get_ul_sched(uint32_t tti, ul_sched_list_t& ul_sched_res) override; int slot_indication(const srsran_slot_cfg_t& slot_cfg) override;
int get_dl_sched(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched) override;
int get_ul_sched(const srsran_slot_cfg_t& slot_cfg, ul_sched_t& ul_sched) override;
private: private:
void run_thread() final; void run_thread() final;

@ -65,8 +65,9 @@ public:
int rx_data_indication(stack_interface_phy_nr::rx_data_ind_t& grant); int rx_data_indication(stack_interface_phy_nr::rx_data_ind_t& grant);
void process_pdus(); void process_pdus();
int get_dl_sched(uint32_t tti, dl_sched_list_t& dl_sched_res) override; int slot_indication(const srsran_slot_cfg_t& slot_cfg) override;
int get_ul_sched(uint32_t tti, ul_sched_list_t& ul_sched_res) override; int get_dl_sched(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched) override;
int get_ul_sched(const srsran_slot_cfg_t& slot_cfg, ul_sched_t& ul_sched) override;
private: private:
void get_dl_config(const uint32_t tti, void get_dl_config(const uint32_t tti,

@ -10,10 +10,8 @@ set(SOURCES
lte/cc_worker.cc lte/cc_worker.cc
lte/sf_worker.cc lte/sf_worker.cc
lte/worker_pool.cc lte/worker_pool.cc
nr/cc_worker.cc nr/slot_worker.cc
nr/sf_worker.cc
nr/worker_pool.cc nr/worker_pool.cc
nr/phy_nr_state.cc
phy.cc phy.cc
phy_common.cc phy_common.cc
phy_ue_db.cc phy_ue_db.cc

@ -1,186 +0,0 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#include "srsenb/hdr/phy/nr/cc_worker.h"
#include "srsran/srsran.h"
namespace srsenb {
namespace nr {
cc_worker::cc_worker(const args_t& args, srslog::basic_logger& log, phy_nr_state& phy_state_) :
cc_idx(args.cc_idx),
phy_state(phy_state_),
logger(log),
nof_tx_antennas(args.dl.nof_tx_antennas)
{
cf_t* buffer_c[SRSRAN_MAX_PORTS] = {};
// Allocate buffers
buffer_sz = SRSRAN_SF_LEN_PRB(args.dl.nof_max_prb);
for (uint32_t i = 0; i < args.dl.nof_tx_antennas; i++) {
tx_buffer[i] = srsran_vec_cf_malloc(buffer_sz);
rx_buffer[i] = srsran_vec_cf_malloc(buffer_sz);
buffer_c[i] = tx_buffer[i];
}
if (srsran_enb_dl_nr_init(&gnb_dl, buffer_c, &args.dl)) {
ERROR("Error initiating GNB DL NR");
return;
}
if (srsran_enb_dl_nr_set_carrier(&gnb_dl, &args.carrier) < SRSRAN_SUCCESS) {
ERROR("Error setting carrier");
}
}
cc_worker::~cc_worker()
{
srsran_enb_dl_nr_free(&gnb_dl);
for (cf_t* p : rx_buffer) {
if (p != nullptr) {
free(p);
}
}
for (cf_t* p : tx_buffer) {
if (p != nullptr) {
free(p);
}
}
}
void cc_worker::set_tti(uint32_t tti)
{
ul_slot_cfg.idx = tti;
dl_slot_cfg.idx = TTI_ADD(tti, FDD_HARQ_DELAY_UL_MS);
}
cf_t* cc_worker::get_tx_buffer(uint32_t antenna_idx)
{
if (antenna_idx >= nof_tx_antennas) {
return nullptr;
}
return tx_buffer.at(antenna_idx);
}
cf_t* cc_worker::get_rx_buffer(uint32_t antenna_idx)
{
if (antenna_idx >= nof_tx_antennas) {
return nullptr;
}
return rx_buffer.at(antenna_idx);
}
uint32_t cc_worker::get_buffer_len()
{
return tx_buffer.size();
}
int cc_worker::encode_pdcch_dl(stack_interface_phy_nr::dl_sched_grant_t* grants, uint32_t nof_grants)
{
for (uint32_t i = 0; i < nof_grants; i++) {
uint16_t rnti = grants->dci.ctx.rnti;
// Get PHY config for UE
srsran::phy_cfg_nr_t cfg = {};
if (phy_state.get_config(rnti, cfg) < SRSRAN_SUCCESS) {
logger.error("Invalid RNTI 0x%x", rnti);
return SRSRAN_ERROR;
}
srsran_dci_cfg_nr_t dci_cfg = cfg.get_dci_cfg();
if (srsran_enb_dl_nr_set_pdcch_config(&gnb_dl, &cfg.pdcch, &dci_cfg) < SRSRAN_SUCCESS) {
logger.error("Invalid CORESET setting");
return SRSRAN_ERROR;
}
// Put actual DCI
if (srsran_enb_dl_nr_pdcch_put(&gnb_dl, &dl_slot_cfg, &grants[i].dci) < SRSRAN_SUCCESS) {
ERROR("Error putting PDCCH");
return SRSRAN_ERROR;
}
if (logger.info.enabled()) {
std::array<char, 512> str = {};
srsran_dci_dl_nr_to_str(&gnb_dl.dci, &grants[i].dci, str.data(), (uint32_t)str.size());
if (logger.info.enabled()) {
logger.info("PDCCH: cc=%d %s tti_tx=%d", cc_idx, str.data(), dl_slot_cfg.idx);
}
}
}
return SRSRAN_SUCCESS;
}
int cc_worker::encode_pdsch(stack_interface_phy_nr::dl_sched_grant_t* grants, uint32_t nof_grants)
{
for (uint32_t i = 0; i < nof_grants; i++) {
// Get PHY config for UE
srsran::phy_cfg_nr_t cfg = {};
if (phy_state.get_config(grants[i].dci.ctx.rnti, cfg) < SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
// Compute DL grant
srsran_sch_cfg_nr_t pdsch_cfg = {};
if (srsran_ra_dl_dci_to_grant_nr(
&gnb_dl.carrier, &dl_slot_cfg, &cfg.pdsch, &grants[i].dci, &pdsch_cfg, &pdsch_cfg.grant) < SRSRAN_SUCCESS) {
ERROR("Computing DL grant");
return SRSRAN_ERROR;
}
// Set soft buffer
for (uint32_t j = 0; j < SRSRAN_MAX_CODEWORDS; j++) {
pdsch_cfg.grant.tb[j].softbuffer.tx = grants[i].softbuffer_tx[j];
}
if (srsran_enb_dl_nr_pdsch_put(&gnb_dl, &dl_slot_cfg, &pdsch_cfg, grants[i].data) < SRSRAN_SUCCESS) {
ERROR("Error putting PDSCH");
return SRSRAN_ERROR;
}
// Logging
if (logger.info.enabled()) {
char str[512];
srsran_enb_dl_nr_pdsch_info(&gnb_dl, &pdsch_cfg, str, sizeof(str));
logger.info("PDSCH: cc=%d, %s tti_tx=%d", cc_idx, str, dl_slot_cfg.idx);
}
}
return SRSRAN_SUCCESS;
}
bool cc_worker::work_ul()
{
return true;
}
bool cc_worker::work_dl(stack_interface_phy_nr::dl_sched_t& dl_grants, stack_interface_phy_nr::ul_sched_t& ul_grants)
{
// Reset resource grid
if (srsran_enb_dl_nr_base_zero(&gnb_dl) < SRSRAN_SUCCESS) {
ERROR("Error setting base to zero");
return SRSRAN_ERROR;
}
// Put DL grants to resource grid. PDSCH data will be encoded as well.
encode_pdcch_dl(dl_grants.pdsch, dl_grants.nof_grants);
encode_pdsch(dl_grants.pdsch, dl_grants.nof_grants);
// Generate signal
srsran_enb_dl_nr_gen_signal(&gnb_dl);
return true;
}
} // namespace nr
} // namespace srsenb

@ -1,85 +0,0 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#include "srsenb/hdr/phy/nr/phy_nr_state.h"
namespace srsenb {
namespace nr {
int phy_nr_state::_add_rnti(uint16_t rnti)
{
if (ue_db.count(rnti) > 0) {
return SRSRAN_ERROR;
}
// Access UE to create
// Set defaults
ue_db[rnti] = {};
return SRSRAN_SUCCESS;
}
int phy_nr_state::_assert_rnti(uint16_t rnti) const
{
return ue_db.count(rnti) > 0 ? SRSRAN_SUCCESS : SRSRAN_ERROR;
}
int phy_nr_state::_get_rnti_config(uint16_t rnti, srsran::phy_cfg_nr_t& phy_cfg) const
{
if (_assert_rnti(rnti) < SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
phy_cfg = ue_db.at(rnti).cfg;
return SRSRAN_SUCCESS;
}
phy_nr_state::phy_nr_state(const phy_cell_cfg_list_nr_t& cell_cfg_list_, stack_interface_phy_nr& stack_) :
cell_cfg_list(cell_cfg_list_),
stack(stack_)
{}
void phy_nr_state::addmod_rnti(uint16_t rnti, const srsran::phy_cfg_nr_t& phy_cfg)
{
std::lock_guard<std::mutex> lock(mutex);
// Create UE if it does not exist
if (_assert_rnti(rnti) < SRSRAN_SUCCESS) {
_add_rnti(rnti);
}
// Set UE configuration
ue_db[rnti].cfg = phy_cfg;
}
int phy_nr_state::rem_rnti(uint16_t rnti)
{
std::lock_guard<std::mutex> lock(mutex);
if (_assert_rnti(rnti) < SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
ue_db.erase(rnti);
return SRSRAN_SUCCESS;
}
int phy_nr_state::get_config(uint16_t rnti, srsran::phy_cfg_nr_t& phy_cfg)
{
std::lock_guard<std::mutex> lock(mutex);
return _get_rnti_config(rnti, phy_cfg);
}
} // namespace nr
} // namespace srsenb

@ -1,125 +0,0 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#include "srsenb/hdr/phy/nr/sf_worker.h"
namespace srsenb {
namespace nr {
sf_worker::sf_worker(srsran::phy_common_interface& common_, phy_nr_state& phy_state_, srslog::basic_logger& logger_) :
common(common_),
phy_state(phy_state_),
logger(logger_)
{
// Set subframe length
sf_len = SRSRAN_SF_LEN_PRB_NR(phy_state.get_carrier_list()[0].carrier.nof_prb);
const phy_cell_cfg_list_nr_t& carrier_list = phy_state.get_carrier_list();
for (uint32_t i = 0; i < (uint32_t)carrier_list.size(); i++) {
cc_worker::args_t cc_args = {};
cc_args.cc_idx = i;
cc_args.carrier = carrier_list[i].carrier;
cc_args.dl.nof_tx_antennas = 1;
cc_args.dl.nof_max_prb = cc_args.carrier.nof_prb;
cc_args.dl.pdsch.max_prb = cc_args.carrier.nof_prb;
cc_args.dl.pdsch.max_layers = 1;
cc_worker* w = new cc_worker(cc_args, logger_, phy_state);
cc_workers.push_back(std::unique_ptr<cc_worker>(w));
}
if (srsran_softbuffer_tx_init_guru(&softbuffer_tx, SRSRAN_SCH_NR_MAX_NOF_CB_LDPC, SRSRAN_LDPC_MAX_LEN_ENCODED_CB) <
SRSRAN_SUCCESS) {
ERROR("Error init soft-buffer");
return;
}
data.resize(SRSRAN_SCH_NR_MAX_NOF_CB_LDPC * SRSRAN_LDPC_MAX_LEN_ENCODED_CB / 8);
srsran_vec_u8_zero(data.data(), SRSRAN_SCH_NR_MAX_NOF_CB_LDPC * SRSRAN_LDPC_MAX_LEN_ENCODED_CB / 8);
snprintf((char*)data.data(), SRSRAN_SCH_NR_MAX_NOF_CB_LDPC * SRSRAN_LDPC_MAX_LEN_ENCODED_CB / 8, "hello world!");
}
sf_worker::~sf_worker()
{
srsran_softbuffer_tx_free(&softbuffer_tx);
}
cf_t* sf_worker::get_buffer_rx(uint32_t cc_idx, uint32_t antenna_idx)
{
if (cc_idx >= cc_workers.size()) {
return nullptr;
}
return cc_workers.at(cc_idx)->get_rx_buffer(antenna_idx);
}
cf_t* sf_worker::get_buffer_tx(uint32_t cc_idx, uint32_t antenna_idx)
{
if (cc_idx >= cc_workers.size()) {
return nullptr;
}
return cc_workers.at(cc_idx)->get_tx_buffer(antenna_idx);
}
uint32_t sf_worker::get_buffer_len()
{
return cc_workers.at(0)->get_buffer_len();
}
void sf_worker::set_time(const uint32_t& tti, const srsran::rf_timestamp_t& timestamp)
{
logger.set_context(tti);
for (auto& w : cc_workers) {
w->set_tti(tti);
}
ul_slot_cfg.idx = tti;
dl_slot_cfg.idx = TTI_ADD(tti, FDD_HARQ_DELAY_UL_MS);
tx_time.copy(timestamp);
}
void sf_worker::work_imp()
{
// Get Transmission buffers
srsran::rf_buffer_t tx_buffer = {};
for (uint32_t cc = 0; cc < (uint32_t)phy_state.get_carrier_list().size(); cc++) {
tx_buffer.set(cc, 0, 1, cc_workers[cc]->get_tx_buffer(0));
}
// Set number of samples
tx_buffer.set_nof_samples(sf_len);
// Get UL Scheduling
mac_interface_phy_nr::ul_sched_list_t ul_sched_list = {};
ul_sched_list.resize(1);
if (phy_state.get_stack().get_ul_sched(ul_slot_cfg.idx, ul_sched_list) < SRSRAN_SUCCESS) {
logger.error("DL Scheduling error");
common.worker_end(this, true, tx_buffer, tx_time, true);
return;
}
// Get DL scheduling
mac_interface_phy_nr::dl_sched_list_t dl_sched_list = {};
dl_sched_list.resize(1);
if (phy_state.get_stack().get_dl_sched(ul_slot_cfg.idx, dl_sched_list) < SRSRAN_SUCCESS) {
logger.error("DL Scheduling error");
common.worker_end(this, true, tx_buffer, tx_time, true);
return;
}
for (auto& w : cc_workers) {
w->work_dl(dl_sched_list[0], ul_sched_list[0]);
}
common.worker_end(this, true, tx_buffer, tx_time, true);
}
} // namespace nr
} // namespace srsenb

@ -0,0 +1,262 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#include "srsenb/hdr/phy/nr/slot_worker.h"
#include <srsran/common/common.h>
namespace srsenb {
namespace nr {
slot_worker::slot_worker(srsran::phy_common_interface& common_,
stack_interface_phy_nr& stack_,
srslog::basic_logger& logger_) :
common(common_), stack(stack_), logger(logger_)
{
// Do nothing
}
bool slot_worker::init(const args_t& args)
{
// Calculate subframe length
sf_len = SRSRAN_SF_LEN_PRB_NR(args.carrier.nof_prb);
// Copy common configurations
cell_index = args.cell_index;
pdcch_cfg = args.pdcch_cfg;
// Allocate Tx buffers
tx_buffer.resize(args.nof_tx_ports);
for (uint32_t i = 0; i < args.nof_tx_ports; i++) {
tx_buffer[i] = srsran_vec_cf_malloc(sf_len);
if (tx_buffer[i] == nullptr) {
logger.error("Error allocating Tx buffer");
return false;
}
}
// Allocate Tx buffers
rx_buffer.resize(args.nof_rx_ports);
for (uint32_t i = 0; i < args.nof_rx_ports; i++) {
rx_buffer[i] = srsran_vec_cf_malloc(sf_len);
if (rx_buffer[i] == nullptr) {
logger.error("Error allocating Rx buffer");
return false;
}
}
// Prepare DL arguments
srsran_enb_dl_nr_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.nof_tx_antennas = args.nof_tx_ports;
dl_args.nof_max_prb = args.carrier.nof_prb;
// Initialise DL
if (srsran_enb_dl_nr_init(&gnb_dl, tx_buffer.data(), &dl_args) < SRSRAN_SUCCESS) {
logger.error("Error gNb PHY init");
return false;
}
// Set gNb carrier
if (srsran_enb_dl_nr_set_carrier(&gnb_dl, &args.carrier) < SRSRAN_SUCCESS) {
logger.error("Error setting carrier");
return false;
}
return true;
}
slot_worker::~slot_worker()
{
for (auto& b : tx_buffer) {
if (b) {
free(b);
b = nullptr;
}
}
for (auto& b : rx_buffer) {
if (b) {
free(b);
b = nullptr;
}
}
srsran_enb_dl_nr_free(&gnb_dl);
}
cf_t* slot_worker::get_buffer_rx(uint32_t antenna_idx)
{
if (antenna_idx >= (uint32_t)rx_buffer.size()) {
return nullptr;
}
return rx_buffer[antenna_idx];
}
cf_t* slot_worker::get_buffer_tx(uint32_t antenna_idx)
{
if (antenna_idx >= (uint32_t)tx_buffer.size()) {
return nullptr;
}
return tx_buffer[antenna_idx];
}
uint32_t slot_worker::get_buffer_len()
{
return sf_len;
}
void slot_worker::set_time(const uint32_t& tti, const srsran::rf_timestamp_t& timestamp)
{
logger.set_context(tti);
ul_slot_cfg.idx = tti;
dl_slot_cfg.idx = TTI_ADD(tti, FDD_HARQ_DELAY_UL_MS);
tx_time.copy(timestamp);
}
bool slot_worker::work_ul()
{
stack_interface_phy_nr::ul_sched_t ul_sched = {};
if (stack.get_ul_sched(ul_slot_cfg, ul_sched) < SRSRAN_SUCCESS) {
logger.error("Error retrieving UL scheduling");
return false;
}
// Decode PUCCH
for (uint32_t i = 0; i < ul_sched.uci_count; i++) {
const stack_interface_phy_nr::uci_t& uci = ul_sched.uci[i];
// ...
}
// Decode PUSCH
for (uint32_t i = 0; i < ul_sched.pusch_count; i++) {
const stack_interface_phy_nr::pusch_t& pusch = ul_sched.pusch[i];
// ...
}
return true;
}
bool slot_worker::work_dl()
{
stack_interface_phy_nr::dl_sched_t dl_sched = {};
if (stack.get_dl_sched(ul_slot_cfg, dl_sched) < SRSRAN_SUCCESS) {
logger.error("Error retrieving DL scheduling");
return false;
}
// Encode PDCCH for DL transmissions
for (uint32_t i = 0; i < dl_sched.pdcch_dl_count; i++) {
// Select PDCCH from scheduler result
const stack_interface_phy_nr::pdcch_dl_t& pdcch = dl_sched.pdcch_dl[i];
// Set PDCCH configuration, including DCI dedicated
if (srsran_enb_dl_nr_set_pdcch_config(&gnb_dl, &pdcch_cfg, &pdcch.dci_cfg) < SRSRAN_SUCCESS) {
logger.error("PDCCH: Error setting DL configuration");
return false;
}
// Put PDCCH message
if (srsran_enb_dl_nr_pdcch_put_dl(&gnb_dl, &dl_slot_cfg, &pdcch.dci) < SRSRAN_SUCCESS) {
logger.error("PDCCH: Error putting DL message");
return false;
}
// Log PDCCH information
if (logger.info.enabled()) {
std::array<char, 512> str = {};
srsran_enb_dl_nr_pdcch_dl_info(&gnb_dl, &pdcch.dci, str.data(), (uint32_t)str.size());
logger.info("PDCCH: cc=%d %s tti_tx=%d", cell_index, str.data(), dl_slot_cfg.idx);
}
}
// Encode PDCCH for UL transmissions
for (uint32_t i = 0; i < dl_sched.pdcch_ul_count; i++) {
// Select PDCCH from scheduler result
const stack_interface_phy_nr::pdcch_ul_t& pdcch = dl_sched.pdcch_ul[i];
// Set PDCCH configuration, including DCI dedicated
if (srsran_enb_dl_nr_set_pdcch_config(&gnb_dl, &pdcch_cfg, &pdcch.dci_cfg) < SRSRAN_SUCCESS) {
logger.error("PDCCH: Error setting DL configuration");
return false;
}
// Put PDCCH message
if (srsran_enb_dl_nr_pdcch_put_ul(&gnb_dl, &dl_slot_cfg, &pdcch.dci) < SRSRAN_SUCCESS) {
logger.error("PDCCH: Error putting DL message");
return false;
}
// Log PDCCH information
if (logger.info.enabled()) {
std::array<char, 512> str = {};
srsran_enb_dl_nr_pdcch_ul_info(&gnb_dl, &pdcch.dci, str.data(), (uint32_t)str.size());
logger.info("PDCCH: cc=%d %s tti_tx=%d", cell_index, str.data(), dl_slot_cfg.idx);
}
}
// Encode PDSCH
for (uint32_t i = 0; i < dl_sched.pdsch_count; i++) {
// Select PDSCH from scheduler result
stack_interface_phy_nr::pdsch_t& pdsch = dl_sched.pdsch[i];
// Put PDSCH message
if (srsran_enb_dl_nr_pdsch_put(&gnb_dl, &dl_slot_cfg, &pdsch.sch, pdsch.data.data()) < SRSRAN_SUCCESS) {
logger.error("PDSCH: Error putting DL message");
return false;
}
// Log PDSCH information
if (logger.info.enabled()) {
std::array<char, 512> str = {};
srsran_enb_dl_nr_pdsch_info(&gnb_dl, &pdsch.sch, str.data(), (uint32_t)str.size());
logger.info("PDSCH: cc=%d %s tti_tx=%d", cell_index, str.data(), dl_slot_cfg.idx);
}
}
// Generate baseband signal
srsran_enb_dl_nr_gen_signal(&gnb_dl);
return true;
}
void slot_worker::work_imp()
{
// Inform Scheduler about new slot
stack.sf_indication(dl_slot_cfg.idx);
// Get Transmission buffers
srsran::rf_buffer_t tx_rf_buffer = {};
for (uint32_t i = 0; i < (uint32_t)tx_buffer.size(); i++) {
tx_rf_buffer.set(i, tx_buffer[i]);
}
// Set number of samples
tx_rf_buffer.set_nof_samples(sf_len);
// Process uplink
if (not work_ul()) {
common.worker_end(this, false, tx_rf_buffer, tx_time, true);
return;
}
// Process downlink
if (not work_dl()) {
common.worker_end(this, false, tx_rf_buffer, tx_time, true);
return;
}
common.worker_end(this, true, tx_rf_buffer, tx_time, true);
}
} // namespace nr
} // namespace srsenb

@ -14,40 +14,58 @@
namespace srsenb { namespace srsenb {
namespace nr { namespace nr {
worker_pool::worker_pool(const phy_cell_cfg_list_nr_t& cell_list, worker_pool::worker_pool(srsran::phy_common_interface& common_,
const args_t& args, stack_interface_phy_nr& stack_,
srsran::phy_common_interface& common, srslog::sink& log_sink_,
stack_interface_phy_nr& stack, uint32_t max_workers) :
srslog::sink& log_sink) : pool(max_workers), common(common_), stack(stack_), log_sink(log_sink_)
pool(args.nof_workers), {
phy_state(cell_list, 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 // Add workers to workers pool and start threads
srslog::basic_levels log_level = srslog::str_to_basic_level(args.log_level); srslog::basic_levels log_level = srslog::str_to_basic_level(args.log_level);
for (uint32_t i = 0; i < args.nof_workers; i++) { 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); auto& log = srslog::fetch_basic_logger(fmt::format("{}PHY{}-NR", args.log_id_preamble, i), log_sink);
log.set_level(log_level); log.set_level(log_level);
log.set_hex_dump_max_size(args.log_hex_limit); log.set_hex_dump_max_size(args.log_hex_limit);
auto w = new sf_worker(common, phy_state, log); auto w = new slot_worker(common, stack, log);
pool.init_worker(i, w, args.prio); pool.init_worker(i, w, args.prio);
workers.push_back(std::unique_ptr<sf_worker>(w)); workers.push_back(std::unique_ptr<slot_worker>(w));
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_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;
}
} }
return true;
} }
void worker_pool::start_worker(sf_worker* w) void worker_pool::start_worker(slot_worker* w)
{ {
pool.start_worker(w); pool.start_worker(w);
} }
sf_worker* worker_pool::wait_worker(uint32_t tti) slot_worker* worker_pool::wait_worker(uint32_t tti)
{ {
return (sf_worker*)pool.wait_worker(tti); return (slot_worker*)pool.wait_worker(tti);
} }
sf_worker* worker_pool::wait_worker_id(uint32_t id) slot_worker* worker_pool::wait_worker_id(uint32_t id)
{ {
return (sf_worker*)pool.wait_worker_id(id); return (slot_worker*)pool.wait_worker_id(id);
} }
void worker_pool::stop() void worker_pool::stop()
@ -55,12 +73,5 @@ void worker_pool::stop()
pool.stop(); pool.stop();
} }
bool worker_pool::addmod_rnti(uint16_t rnti, const srsran::phy_cfg_nr_t& phy_cfg)
{
phy_state.addmod_rnti(rnti, phy_cfg);
return true;
}
} // namespace nr } // namespace nr
} // namespace srsenb } // namespace srsenb

@ -124,7 +124,7 @@ void txrx::run_thread()
} }
} }
nr::sf_worker* nr_worker = nullptr; nr::slot_worker* nr_worker = nullptr;
if (worker_com->get_nof_carriers_nr() > 0) { if (worker_com->get_nof_carriers_nr() > 0) {
nr_worker = nr_workers->wait_worker(tti); nr_worker = nr_workers->wait_worker(tti);
if (nr_worker == nullptr) { if (nr_worker == nullptr) {
@ -144,12 +144,14 @@ void txrx::run_thread()
buffer.set(rf_port, p, worker_com->get_nof_ports(0), lte_worker->get_buffer_rx(cc_lte, p)); buffer.set(rf_port, p, worker_com->get_nof_ports(0), lte_worker->get_buffer_rx(cc_lte, p));
} }
} }
for (uint32_t cc_nr = 0; cc_nr < worker_com->get_nof_carriers_lte(); cc_nr++, cc++) { for (uint32_t cc_nr = 0; cc_nr < worker_com->get_nof_carriers_nr(); cc_nr++, cc++) {
uint32_t rf_port = worker_com->get_rf_port(cc); uint32_t rf_port = worker_com->get_rf_port(cc);
for (uint32_t p = 0; p < worker_com->get_nof_ports(cc); p++) { for (uint32_t p = 0; p < worker_com->get_nof_ports(cc); p++) {
// WARNING: The number of ports for all cells must be the same // WARNING:
buffer.set(rf_port, p, worker_com->get_nof_ports(0), nr_worker->get_buffer_rx(cc_nr, p)); // - The number of ports for all cells must be the same
// - Only one NR cell is currently supported
buffer.set(rf_port, p, worker_com->get_nof_ports(0), nr_worker->get_buffer_rx(p));
} }
} }
} }

@ -182,13 +182,17 @@ bool gnb_stack_nr::has_active_radio_bearer(uint32_t eps_bearer_id)
{ {
return (eps_bearer_id == args.coreless.drb_lcid); return (eps_bearer_id == args.coreless.drb_lcid);
} }
int gnb_stack_nr::get_dl_sched(uint32_t tti, mac_interface_phy_nr::dl_sched_list_t& dl_sched_res) int gnb_stack_nr::slot_indication(const srsran_slot_cfg_t& slot_cfg)
{ {
return m_mac->get_dl_sched(tti, dl_sched_res); return m_mac->slot_indication(slot_cfg);
} }
int gnb_stack_nr::get_ul_sched(uint32_t tti, mac_interface_phy_nr::ul_sched_list_t& ul_sched_res) int gnb_stack_nr::get_dl_sched(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched)
{ {
return m_mac->get_ul_sched(tti, ul_sched_res); return m_mac->get_dl_sched(slot_cfg, dl_sched);
}
int gnb_stack_nr::get_ul_sched(const srsran_slot_cfg_t& slot_cfg, ul_sched_t& ul_sched)
{
return m_mac->get_ul_sched(slot_cfg, ul_sched);
} }
} // namespace srsenb } // namespace srsenb

@ -267,11 +267,16 @@ int mac_nr::cell_cfg(srsenb::sched_interface::cell_cfg_t* cell_cfg)
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
int mac_nr::get_dl_sched(uint32_t tti, mac_interface_phy_nr::dl_sched_list_t& dl_sched_res) int mac_nr::slot_indication(const srsran_slot_cfg_t& slot_cfg)
{ {
return 0; return 0;
} }
int mac_nr::get_ul_sched(uint32_t tti, mac_interface_phy_nr::ul_sched_list_t& ul_sched_res)
int mac_nr::get_dl_sched(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched)
{
return 0;
}
int mac_nr::get_ul_sched(const srsran_slot_cfg_t& slot_cfg, ul_sched_t& ul_sched)
{ {
return 0; return 0;
} }

@ -11,9 +11,9 @@
*/ */
#include "srsran/common/threads.h" #include "srsran/common/threads.h"
#include "srsran/phy/common/phy_common.h"
#include "srsran/phy/utils/random.h" #include "srsran/phy/utils/random.h"
#include "srsran/srslog/srslog.h" #include "srsran/srslog/srslog.h"
#include "srsran/srsran.h"
#include <boost/program_options.hpp> #include <boost/program_options.hpp>
#include <boost/program_options/options_description.hpp> #include <boost/program_options/options_description.hpp>
#include <boost/program_options/parsers.hpp> #include <boost/program_options/parsers.hpp>
@ -22,7 +22,6 @@
#include <srsenb/hdr/phy/phy.h> #include <srsenb/hdr/phy/phy.h>
#include <srsran/common/string_helpers.h> #include <srsran/common/string_helpers.h>
#include <srsran/common/test_common.h> #include <srsran/common/test_common.h>
#include <srsran/phy/phch/pusch_cfg.h>
static inline bool dl_ack_value(uint32_t ue_cc_idx, uint32_t tti) static inline bool dl_ack_value(uint32_t ue_cc_idx, uint32_t tti)
{ {

@ -0,0 +1,239 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#ifndef SRSRAN_DUMMY_GNB_STACK_H
#define SRSRAN_DUMMY_GNB_STACK_H
#include <srsran/adt/circular_array.h>
#include <srsran/interfaces/gnb_interfaces.h>
#include <srsran/interfaces/rrc_nr_interface_types.h>
class gnb_dummy_stack : public srsenb::stack_interface_phy_nr
{
private:
srslog::basic_logger& logger = srslog::fetch_basic_logger("GNB STK");
const uint16_t rnti = 0x1234;
const uint32_t mcs = 1;
srsran::circular_array<srsran_dci_location_t, SRSRAN_NOF_SF_X_FRAME> dci_dl_location;
srsran::circular_array<srsran_dci_location_t, SRSRAN_NOF_SF_X_FRAME> dci_ul_location;
srsran::circular_array<uint32_t, SRSRAN_NOF_SF_X_FRAME> dl_data_to_ul_ack;
srsran_search_space_t ss = {};
srsran_dci_format_nr_t dci_format_ul = SRSRAN_DCI_FORMAT_NR_COUNT;
srsran_dci_format_nr_t dci_format_dl = SRSRAN_DCI_FORMAT_NR_COUNT;
uint32_t dl_freq_res = 0;
uint32_t dl_time_res = 0;
srsran_random_t random_gen = nullptr;
srsran::phy_cfg_nr_t phy_cfg = {};
bool valid = false;
struct dummy_harq_proc {
static const uint32_t MAX_TB_SZ = SRSRAN_LDPC_MAX_LEN_CB * SRSRAN_SCH_NR_MAX_NOF_CB_LDPC;
std::vector<uint8_t> data;
srsran_softbuffer_tx_t softbuffer = {};
dummy_harq_proc()
{
// Allocate data
data.resize(MAX_TB_SZ);
// Initialise softbuffer
if (srsran_softbuffer_tx_init_guru(&softbuffer, SRSRAN_SCH_NR_MAX_NOF_CB_LDPC, SRSRAN_LDPC_MAX_LEN_ENCODED_CB) <
SRSRAN_SUCCESS) {
ERROR("Error Tx buffer");
}
}
~dummy_harq_proc() { srsran_softbuffer_tx_free(&softbuffer); }
};
srsran::circular_array<dummy_harq_proc, SRSRAN_MAX_HARQ_PROC_DL_NR> tx_harq_proc;
public:
struct args_t {
srsran::phy_cfg_nr_t phy_cfg; ///< Physical layer configuration
uint16_t rnti = 0x1234; ///< C-RNTI
uint32_t mcs = 10; ///< Modulation code scheme
uint32_t ss_id = 1; ///< Search Space identifier
uint32_t pdcch_aggregation_level = 0; ///< PDCCH aggregation level
uint32_t pdcch_dl_candidate_index = 0; ///< PDCCH DL DCI candidate index
uint32_t pdcch_ul_candidate_index = 0; ///< PDCCH UL DCI candidate index
uint32_t dl_start_rb = 0; ///< Start resource block
uint32_t dl_length_rb = 0l; ///< Number of resource blocks
uint32_t dl_time_res = 0; ///< PDSCH time resource
};
gnb_dummy_stack(args_t args) : mcs(args.mcs), rnti(args.rnti), dl_time_res(args.dl_time_res), phy_cfg(args.phy_cfg)
{
random_gen = srsran_random_init(0x1234);
// Select search space
if (args.ss_id >= SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE) {
logger.error("Search Space Id (%d) is out-of-range (%d)", args.ss_id, SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE);
return;
}
if (not args.phy_cfg.pdcch.search_space_present[args.ss_id]) {
logger.error("Search Space Id (%d) is not present", args.ss_id);
return;
}
ss = args.phy_cfg.pdcch.search_space[args.ss_id];
// Select CORESET
if (ss.coreset_id >= SRSRAN_UE_DL_NR_MAX_NOF_CORESET) {
logger.error("CORESET Id (%d) is out-of-range (%d)", ss.coreset_id, SRSRAN_UE_DL_NR_MAX_NOF_CORESET);
return;
}
if (not args.phy_cfg.pdcch.coreset_present[ss.coreset_id]) {
logger.error("CORESET Id (%d) is not present", args.ss_id);
return;
}
const srsran_coreset_t& coreset = args.phy_cfg.pdcch.coreset[ss.coreset_id];
// Select DCI locations
for (uint32_t slot = 0; slot < SRSRAN_NOF_SF_X_FRAME; slot++) {
std::array<uint32_t, SRSRAN_SEARCH_SPACE_MAX_NOF_CANDIDATES_NR> ncce = {};
int n = srsran_pdcch_nr_locations_coreset(&coreset, &ss, rnti, args.pdcch_aggregation_level, slot++, ncce.data());
if (n < SRSRAN_SUCCESS) {
logger.error(
"Error generating locations for slot %d and aggregation level %d", slot, args.pdcch_aggregation_level);
return;
}
uint32_t nof_candidates = (uint32_t)n;
// DCI DL
if (args.pdcch_dl_candidate_index >= nof_candidates or
args.pdcch_dl_candidate_index >= SRSRAN_SEARCH_SPACE_MAX_NOF_CANDIDATES_NR) {
logger.error("Candidate index %d exceeds the number of candidates %d for aggregation level %d",
args.pdcch_dl_candidate_index,
n,
args.pdcch_aggregation_level);
return;
}
dci_dl_location[slot].L = args.pdcch_aggregation_level;
dci_dl_location[slot].ncce = ncce[args.pdcch_dl_candidate_index];
// DCI UL
if (args.pdcch_ul_candidate_index >= nof_candidates or
args.pdcch_ul_candidate_index >= SRSRAN_SEARCH_SPACE_MAX_NOF_CANDIDATES_NR) {
logger.error("Candidate index %d exceeds the number of candidates %d for aggregation level %d",
args.pdcch_ul_candidate_index,
n,
args.pdcch_aggregation_level);
return;
}
dci_ul_location[slot].L = args.pdcch_aggregation_level;
dci_ul_location[slot].ncce = ncce[args.pdcch_ul_candidate_index];
}
// Select DCI formats
for (uint32_t i = 0; i < ss.nof_formats; i++) {
// Select DL format
if (ss.formats[i] == srsran_dci_format_nr_1_0 or ss.formats[i] == srsran_dci_format_nr_1_1) {
dci_format_dl = ss.formats[i];
}
// Select DL format
if (ss.formats[i] == srsran_dci_format_nr_0_0 or ss.formats[i] == srsran_dci_format_nr_0_1) {
dci_format_ul = ss.formats[i];
}
}
// Validate that a DCI format is selected
if (dci_format_dl == SRSRAN_DCI_FORMAT_NR_COUNT or dci_format_ul == SRSRAN_DCI_FORMAT_NR_COUNT) {
logger.error("Missing valid DL or UL DCI format in search space");
return;
}
// Select DL frequency domain resources
dl_freq_res = srsran_ra_nr_type1_riv(args.phy_cfg.carrier.nof_prb, args.dl_start_rb, args.dl_length_rb);
// Setup DL Data to ACK timing
for (uint32_t i = 0; i < SRSRAN_NOF_SF_X_FRAME; i++) {
dl_data_to_ul_ack[i] = args.phy_cfg.harq_ack.dl_data_to_ul_ack[i % SRSRAN_MAX_NOF_DL_DATA_TO_UL];
}
// If reached this point the configuration is valid
valid = true;
}
~gnb_dummy_stack() { srsran_random_free(random_gen); }
bool is_valid() const { return valid; }
int sf_indication(const uint32_t tti) override { return 0; }
int rx_data_indication(rx_data_ind_t& grant) override { return 0; }
int slot_indication(const srsran_slot_cfg_t& slot_cfg) override { return 0; }
int get_dl_sched(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched) override
{
// Check if it is TDD DL slot and PDSCH mask, if no PDSCH shall be scheduled, do not set any grant and skip
if (not srsran_tdd_nr_is_dl(&phy_cfg.tdd, phy_cfg.carrier.scs, slot_cfg.idx)) {
return SRSRAN_SUCCESS;
}
// Select PDCCH and PDSCH from scheduling results
pdcch_dl_t& pdcch = dl_sched.pdcch_dl[0];
pdsch_t& pdsch = dl_sched.pdsch[0];
// Select grant and set data
pdsch.data[0] = tx_harq_proc[slot_cfg.idx].data.data();
// Second TB is not used
pdsch.data[1] = nullptr;
// Generate random data
srsran_random_byte_vector(random_gen, pdsch.data[0], SRSRAN_LDPC_MAX_LEN_CB * SRSRAN_SCH_NR_MAX_NOF_CB_LDPC / 8);
// Fill DCI configuration
pdcch.dci_cfg = phy_cfg.get_dci_cfg();
// Fill DCI
srsran_dci_dl_nr_t& dci = pdcch.dci;
dci.ctx.location = dci_dl_location[slot_cfg.idx];
dci.ctx.ss_type = ss.type;
dci.ctx.coreset_id = ss.coreset_id;
dci.ctx.rnti_type = srsran_rnti_type_c;
dci.ctx.format = dci_format_dl;
dci.ctx.rnti = rnti;
dci.freq_domain_assigment = dl_freq_res;
dci.time_domain_assigment = dl_time_res;
dci.mcs = mcs;
dci.rv = 0;
dci.ndi = (slot_cfg.idx / SRSRAN_NOF_SF_X_FRAME) % 2;
dci.pid = slot_cfg.idx % SRSRAN_NOF_SF_X_FRAME;
dci.dai = slot_cfg.idx % SRSRAN_NOF_SF_X_FRAME;
dci.tpc = 1;
dci.pucch_resource = 0;
dci.harq_feedback = dl_data_to_ul_ack[TTI_TX(slot_cfg.idx)];
// It currently support only one grant
dl_sched.pdcch_dl_count = 1;
dl_sched.pdsch_count = 1;
// Create PDSCH configuration
if (srsran_ra_dl_dci_to_grant_nr(&phy_cfg.carrier, &slot_cfg, &phy_cfg.pdsch, &dci, &pdsch.sch, &pdsch.sch.grant) <
SRSRAN_SUCCESS) {
logger.error("Error converting DCI to grant");
return SRSRAN_ERROR;
}
// Set softbuffer
pdsch.sch.grant.tb[0].softbuffer.tx = &tx_harq_proc[slot_cfg.idx].softbuffer;
// Reset Tx softbuffer always
srsran_softbuffer_tx_reset(pdsch.sch.grant.tb[0].softbuffer.tx);
return SRSRAN_SUCCESS;
}
int get_ul_sched(const srsran_slot_cfg_t& slot_cfg, ul_sched_t& ul_sched) override { return 0; }
};
#endif // SRSRAN_DUMMY_GNB_STACK_H

@ -23,13 +23,14 @@ class phy_common : public srsran::phy_common_interface
{ {
private: private:
const uint32_t RINGBUFFER_TIMEOUT_MS = 10; const uint32_t RINGBUFFER_TIMEOUT_MS = 10;
bool quit = false; std::atomic<bool> quit = {false};
srslog::basic_logger& logger; srslog::basic_logger& logger;
double srate_hz; double srate_hz;
uint64_t write_ts = 0; uint64_t write_ts = 0;
uint64_t read_ts = 0; uint64_t read_ts = 0;
std::vector<cf_t> zero_buffer; ///< Zero buffer for Tx std::vector<cf_t> zero_buffer; ///< Zero buffer for Tx
std::vector<cf_t> sink_buffer; ///< Dummy buffer for Rx std::vector<cf_t> sink_buffer; ///< Dummy buffer for Rx
std::mutex ringbuffers_mutex;
std::vector<srsran_ringbuffer_t> ringbuffers; std::vector<srsran_ringbuffer_t> ringbuffers;
srsran::tti_semaphore<void*> semaphore; srsran::tti_semaphore<void*> semaphore;
@ -145,9 +146,7 @@ public:
uint32_t nof_channels = 1; uint32_t nof_channels = 1;
args_t(double srate_hz_, uint32_t buffer_sz_ms_, uint32_t nof_channels_) : args_t(double srate_hz_, uint32_t buffer_sz_ms_, uint32_t nof_channels_) :
srate_hz(srate_hz_), srate_hz(srate_hz_), buffer_sz_ms(buffer_sz_ms_), nof_channels(nof_channels_)
buffer_sz_ms(buffer_sz_ms_),
nof_channels(nof_channels_)
{} {}
}; };
@ -186,6 +185,9 @@ public:
// Synchronize worker // Synchronize worker
semaphore.wait(h); semaphore.wait(h);
// Protect internal buffers and states
std::unique_lock<std::mutex> lock(ringbuffers_mutex);
uint64_t tx_ts = srsran_timestamp_uint64(&tx_time.get(0), srate_hz); uint64_t tx_ts = srsran_timestamp_uint64(&tx_time.get(0), srate_hz);
// Check transmit timestamp is not in the past // Check transmit timestamp is not in the past
@ -213,6 +215,9 @@ public:
void read(std::vector<cf_t*>& buffers, uint32_t nof_samples, srsran::rf_timestamp_t& timestamp) void read(std::vector<cf_t*>& buffers, uint32_t nof_samples, srsran::rf_timestamp_t& timestamp)
{ {
// Protect internal buffers and states
std::unique_lock<std::mutex> lock(ringbuffers_mutex);
// Detect if zero padding is necessary // Detect if zero padding is necessary
if (read_ts + nof_samples > write_ts) { if (read_ts + nof_samples > write_ts) {
uint32_t nof_zero_pading = (uint32_t)((read_ts + nof_samples) - write_ts); uint32_t nof_zero_pading = (uint32_t)((read_ts + nof_samples) - write_ts);

@ -10,6 +10,7 @@
* *
*/ */
#include "dummy_gnb_stack.h"
#include "srsran/common/test_common.h" #include "srsran/common/test_common.h"
#include "test_bench.h" #include "test_bench.h"
@ -18,18 +19,13 @@ test_bench::args_t::args_t(int argc, char** argv)
// Flag configuration as valid // Flag configuration as valid
valid = true; valid = true;
cell_list.resize(1); phy_cfg.carrier.nof_prb = 52;
cell_list[0].carrier.nof_prb = 52; phy_cfg.carrier.max_mimo_layers = 1;
cell_list[0].carrier.max_mimo_layers = 1; phy_cfg.carrier.pci = 500;
cell_list[0].carrier.pci = 500;
phy_cfg.carrier = cell_list[0].carrier;
phy_cfg.carrier.absolute_frequency_point_a = 633928; phy_cfg.carrier.absolute_frequency_point_a = 633928;
phy_cfg.carrier.absolute_frequency_ssb = 634176; phy_cfg.carrier.absolute_frequency_ssb = 634176;
phy_cfg.carrier.offset_to_carrier = 0; phy_cfg.carrier.offset_to_carrier = 0;
phy_cfg.carrier.scs = srsran_subcarrier_spacing_15kHz; phy_cfg.carrier.scs = srsran_subcarrier_spacing_15kHz;
phy_cfg.carrier.nof_prb = 52;
phy_cfg.ssb.periodicity_ms = 5; phy_cfg.ssb.periodicity_ms = 5;
phy_cfg.ssb.position_in_burst[0] = true; phy_cfg.ssb.position_in_burst[0] = true;
@ -212,8 +208,15 @@ test_bench::args_t::args_t(int argc, char** argv)
phy_cfg.harq_ack.dl_data_to_ul_ack[4] = 4; phy_cfg.harq_ack.dl_data_to_ul_ack[4] = 4;
phy_cfg.harq_ack.dl_data_to_ul_ack[5] = 12; phy_cfg.harq_ack.dl_data_to_ul_ack[5] = 12;
phy_cfg.harq_ack.dl_data_to_ul_ack[6] = 11; phy_cfg.harq_ack.dl_data_to_ul_ack[6] = 11;
phy_cfg.harq_ack.harq_ack_codebook = srsran_pdsch_harq_ack_codebook_dynamic;
phy_cfg.prach.freq_offset = 2; phy_cfg.prach.freq_offset = 2;
cell_list.resize(1);
cell_list[0].carrier = phy_cfg.carrier;
cell_list[0].rf_port = 0;
cell_list[0].cell_id = 0;
cell_list[0].pdcch = phy_cfg.pdcch;
} }
class ue_dummy_stack : public srsue::stack_interface_phy_nr class ue_dummy_stack : public srsue::stack_interface_phy_nr
@ -262,220 +265,6 @@ public:
bool is_valid() const { return valid; } bool is_valid() const { return valid; }
}; };
class gnb_dummy_stack : public srsenb::stack_interface_phy_nr
{
private:
srslog::basic_logger& logger = srslog::fetch_basic_logger("GNB STK");
const uint16_t rnti = 0x1234;
const uint32_t mcs = 1;
const srsran::circular_array<bool, SRSRAN_NOF_SF_X_FRAME> pdsch_mask;
srsran::circular_array<srsran_dci_location_t, SRSRAN_NOF_SF_X_FRAME> dci_dl_location;
srsran::circular_array<srsran_dci_location_t, SRSRAN_NOF_SF_X_FRAME> dci_ul_location;
srsran::circular_array<uint32_t, SRSRAN_NOF_SF_X_FRAME> dl_data_to_ul_ack;
bool valid = false;
srsran_search_space_t ss = {};
srsran_dci_format_nr_t dci_format_ul = SRSRAN_DCI_FORMAT_NR_COUNT;
srsran_dci_format_nr_t dci_format_dl = SRSRAN_DCI_FORMAT_NR_COUNT;
uint32_t dl_freq_res = 0;
uint32_t dl_time_res = 0;
srsran_random_t random_gen = nullptr;
struct dummy_harq_proc {
static const uint32_t MAX_TB_SZ = SRSRAN_LDPC_MAX_LEN_CB * SRSRAN_SCH_NR_MAX_NOF_CB_LDPC;
std::vector<uint8_t> data;
srsran_softbuffer_tx_t softbuffer = {};
dummy_harq_proc()
{
// Allocate data
data.resize(MAX_TB_SZ);
// Initialise softbuffer
if (srsran_softbuffer_tx_init_guru(&softbuffer, SRSRAN_SCH_NR_MAX_NOF_CB_LDPC, SRSRAN_LDPC_MAX_LEN_ENCODED_CB) <
SRSRAN_SUCCESS) {
ERROR("Error Tx buffer");
}
}
~dummy_harq_proc() { srsran_softbuffer_tx_free(&softbuffer); }
};
srsran::circular_array<dummy_harq_proc, SRSRAN_MAX_HARQ_PROC_DL_NR> tx_harq_proc;
public:
struct args_t {
srsran::phy_cfg_nr_t phy_cfg; ///< Physical layer configuration
uint16_t rnti = 0x1234; ///< C-RNTI
uint32_t mcs = 10; ///< Modulation code scheme
srsran::circular_array<bool, SRSRAN_NOF_SF_X_FRAME> pdsch_mask = {}; ///< PDSCH scheduling mask
uint32_t ss_id = 1; ///< Search Space identifier
uint32_t pdcch_aggregation_level = 0; ///< PDCCH aggregation level
uint32_t pdcch_dl_candidate_index = 0; ///< PDCCH DL DCI candidate index
uint32_t pdcch_ul_candidate_index = 0; ///< PDCCH UL DCI candidate index
uint32_t dl_start_rb = 0; ///< Start resource block
uint32_t dl_length_rb = 0l; ///< Number of resource blocks
uint32_t dl_time_res = 0; ///< PDSCH time resource
};
gnb_dummy_stack(args_t args) :
pdsch_mask(args.pdsch_mask),
mcs(args.mcs),
rnti(args.rnti),
dl_time_res(args.dl_time_res)
{
random_gen = srsran_random_init(0x1234);
// Select search space
if (args.ss_id >= SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE) {
logger.error("Search Space Id (%d) is out-of-range (%d)", args.ss_id, SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE);
return;
}
if (not args.phy_cfg.pdcch.search_space_present[args.ss_id]) {
logger.error("Search Space Id (%d) is not present", args.ss_id);
return;
}
ss = args.phy_cfg.pdcch.search_space[args.ss_id];
// Select CORESET
if (ss.coreset_id >= SRSRAN_UE_DL_NR_MAX_NOF_CORESET) {
logger.error("CORESET Id (%d) is out-of-range (%d)", ss.coreset_id, SRSRAN_UE_DL_NR_MAX_NOF_CORESET);
return;
}
if (not args.phy_cfg.pdcch.coreset_present[ss.coreset_id]) {
logger.error("CORESET Id (%d) is not present", args.ss_id);
return;
}
const srsran_coreset_t& coreset = args.phy_cfg.pdcch.coreset[ss.coreset_id];
// Select DCI locations
for (uint32_t slot = 0; slot < SRSRAN_NOF_SF_X_FRAME; slot++) {
std::array<uint32_t, SRSRAN_SEARCH_SPACE_MAX_NOF_CANDIDATES_NR> ncce = {};
int n = srsran_pdcch_nr_locations_coreset(&coreset, &ss, rnti, args.pdcch_aggregation_level, slot++, ncce.data());
if (n < SRSRAN_SUCCESS) {
logger.error(
"Error generating locations for slot %d and aggregation level %d", slot, args.pdcch_aggregation_level);
return;
}
uint32_t nof_candidates = (uint32_t)n;
// DCI DL
if (args.pdcch_dl_candidate_index >= nof_candidates or
args.pdcch_dl_candidate_index >= SRSRAN_SEARCH_SPACE_MAX_NOF_CANDIDATES_NR) {
logger.error("Candidate index %d exceeds the number of candidates %d for aggregation level %d",
args.pdcch_dl_candidate_index,
n,
args.pdcch_aggregation_level);
return;
}
dci_dl_location[slot].L = args.pdcch_aggregation_level;
dci_dl_location[slot].ncce = ncce[args.pdcch_dl_candidate_index];
// DCI UL
if (args.pdcch_ul_candidate_index >= nof_candidates or
args.pdcch_ul_candidate_index >= SRSRAN_SEARCH_SPACE_MAX_NOF_CANDIDATES_NR) {
logger.error("Candidate index %d exceeds the number of candidates %d for aggregation level %d",
args.pdcch_ul_candidate_index,
n,
args.pdcch_aggregation_level);
return;
}
dci_ul_location[slot].L = args.pdcch_aggregation_level;
dci_ul_location[slot].ncce = ncce[args.pdcch_ul_candidate_index];
}
// Select DCI formats
for (uint32_t i = 0; i < ss.nof_formats; i++) {
// Select DL format
if (ss.formats[i] == srsran_dci_format_nr_1_0 or ss.formats[i] == srsran_dci_format_nr_1_1) {
dci_format_dl = ss.formats[i];
}
// Select DL format
if (ss.formats[i] == srsran_dci_format_nr_0_0 or ss.formats[i] == srsran_dci_format_nr_0_1) {
dci_format_ul = ss.formats[i];
}
}
// Validate that a DCI format is selected
if (dci_format_dl == SRSRAN_DCI_FORMAT_NR_COUNT or dci_format_ul == SRSRAN_DCI_FORMAT_NR_COUNT) {
logger.error("Missing valid DL or UL DCI format in search space");
return;
}
// Select DL frequency domain resources
dl_freq_res = srsran_ra_nr_type1_riv(args.phy_cfg.carrier.nof_prb, args.dl_start_rb, args.dl_length_rb);
// Setup DL Data to ACK timing
for (uint32_t i = 0; i < SRSRAN_NOF_SF_X_FRAME; i++) {
dl_data_to_ul_ack[i] = args.phy_cfg.harq_ack.dl_data_to_ul_ack[i % SRSRAN_MAX_NOF_DL_DATA_TO_UL];
}
// If reached this point the configuration is valid
valid = true;
}
~gnb_dummy_stack() { srsran_random_free(random_gen); }
bool is_valid() const { return valid; }
int sf_indication(const uint32_t tti) override { return 0; }
int rx_data_indication(rx_data_ind_t& grant) override { return 0; }
int get_dl_sched(uint32_t tti, dl_sched_list_t& dl_sched_res) override
{
// Check input
if (dl_sched_res.size() == 0) {
return SRSRAN_ERROR;
}
// Check PDSCH mask, if no PDSCH shall be scheduled, do not set any grant and skip
if (not pdsch_mask[tti]) {
dl_sched_res[0].nof_grants = 0;
return SRSRAN_SUCCESS;
}
// Select grant and set data
dl_sched_grant_t& grant = dl_sched_res[0].pdsch[0];
grant.data[0] = tx_harq_proc[tti].data.data();
grant.softbuffer_tx[0] = &tx_harq_proc[tti].softbuffer;
// Second TB is not used
grant.data[1] = nullptr;
grant.softbuffer_tx[1] = nullptr;
// Reset Tx softbuffer always
srsran_softbuffer_tx_reset(grant.softbuffer_tx[0]);
// Generate random data
srsran_random_byte_vector(random_gen, grant.data[0], SRSRAN_LDPC_MAX_LEN_CB * SRSRAN_SCH_NR_MAX_NOF_CB_LDPC / 8);
// It currently support only one grant
dl_sched_res[0].nof_grants = 1;
// Fill DCI
srsran_dci_dl_nr_t& dci = grant.dci;
dci.ctx.location = dci_dl_location[tti];
dci.ctx.ss_type = ss.type;
dci.ctx.coreset_id = ss.coreset_id;
dci.ctx.rnti_type = srsran_rnti_type_c;
dci.ctx.format = dci_format_dl;
dci.ctx.rnti = rnti;
dci.freq_domain_assigment = dl_freq_res;
dci.time_domain_assigment = dl_time_res;
dci.mcs = mcs;
dci.rv = 0;
dci.ndi = (tti / SRSRAN_NOF_SF_X_FRAME) % 2;
dci.pid = tti % SRSRAN_NOF_SF_X_FRAME;
dci.dai = tti % SRSRAN_NOF_SF_X_FRAME;
dci.tpc = 1;
dci.pucch_resource = 0;
dci.harq_feedback = dl_data_to_ul_ack[tti];
return SRSRAN_SUCCESS;
}
int get_ul_sched(uint32_t tti, ul_sched_list_t& ul_sched_res) override { return 0; }
};
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
srslog::init(); srslog::init();
@ -484,7 +273,7 @@ int main(int argc, char** argv)
test_bench::args_t args(argc, argv); test_bench::args_t args(argc, argv);
args.gnb_args.log_id_preamble = "GNB/"; args.gnb_args.log_id_preamble = "GNB/";
args.gnb_args.log_level = "warning"; args.gnb_args.log_level = "warning";
args.gnb_args.nof_workers = 1; args.gnb_args.nof_phy_threads = 1;
args.ue_args.log.id_preamble = " UE/"; args.ue_args.log.id_preamble = " UE/";
args.ue_args.log.phy_level = "warning"; args.ue_args.log.phy_level = "warning";
args.ue_args.log.phy_hex_limit = 0; args.ue_args.log.phy_hex_limit = 0;
@ -505,12 +294,9 @@ int main(int argc, char** argv)
gnb_dummy_stack::args_t gnb_stack_args = {}; gnb_dummy_stack::args_t gnb_stack_args = {};
gnb_stack_args.rnti = 0x1234; gnb_stack_args.rnti = 0x1234;
gnb_stack_args.mcs = 10; gnb_stack_args.mcs = 10;
for (bool& mask : gnb_stack_args.pdsch_mask) { gnb_stack_args.phy_cfg = args.phy_cfg;
mask = true; gnb_stack_args.dl_start_rb = 0;
} gnb_stack_args.dl_length_rb = args.phy_cfg.carrier.nof_prb;
gnb_stack_args.phy_cfg = args.phy_cfg;
gnb_stack_args.dl_start_rb = 0;
gnb_stack_args.dl_length_rb = args.phy_cfg.carrier.nof_prb;
// Create GNB stack // Create GNB stack
gnb_dummy_stack gnb_stack(gnb_stack_args); gnb_dummy_stack gnb_stack(gnb_stack_args);

@ -33,7 +33,6 @@ private:
public: public:
struct args_t { struct args_t {
double srate_hz = 11.52e6; double srate_hz = 11.52e6;
uint32_t nof_threads = 6;
uint32_t nof_channels = 1; uint32_t nof_channels = 1;
uint32_t buffer_sz_ms = 10; uint32_t buffer_sz_ms = 10;
bool valid = false; bool valid = false;
@ -48,18 +47,20 @@ public:
}; };
test_bench(const args_t& args, srsenb::stack_interface_phy_nr& gnb_stack, srsue::stack_interface_phy_nr& ue_stack) : test_bench(const args_t& args, srsenb::stack_interface_phy_nr& gnb_stack, srsue::stack_interface_phy_nr& ue_stack) :
ue_phy(args.nof_threads), ue_phy(args.ue_args.nof_phy_threads),
gnb_phy(args.cell_list, args.gnb_args, gnb_phy_com, gnb_stack, srslog::get_default_sink()), gnb_phy(gnb_phy_com, gnb_stack, srslog::get_default_sink(), args.gnb_args.nof_phy_threads),
ue_phy_com(phy_common::args_t(args.srate_hz, args.buffer_sz_ms, args.nof_channels), ue_phy_com(phy_common::args_t(args.srate_hz, args.buffer_sz_ms, args.nof_channels),
srslog::fetch_basic_logger(UE_PHY_COM_LOG_NAME, srslog::get_default_sink(), false)), srslog::fetch_basic_logger(UE_PHY_COM_LOG_NAME, srslog::get_default_sink(), false)),
gnb_phy_com(phy_common::args_t(args.srate_hz, args.buffer_sz_ms, args.nof_channels), gnb_phy_com(phy_common::args_t(args.srate_hz, args.buffer_sz_ms, args.nof_channels),
srslog::fetch_basic_logger(GNB_PHY_COM_LOG_NAME, srslog::get_default_sink(), false)) srslog::fetch_basic_logger(GNB_PHY_COM_LOG_NAME, srslog::get_default_sink(), false)),
sf_sz((uint32_t)std::round(args.srate_hz * 1e-3))
{ {
srslog::fetch_basic_logger(UE_PHY_COM_LOG_NAME).set_level(srslog::str_to_basic_level(args.phy_com_log_level)); srslog::fetch_basic_logger(UE_PHY_COM_LOG_NAME).set_level(srslog::str_to_basic_level(args.phy_com_log_level));
srslog::fetch_basic_logger(GNB_PHY_COM_LOG_NAME).set_level(srslog::str_to_basic_level(args.phy_com_log_level)); srslog::fetch_basic_logger(GNB_PHY_COM_LOG_NAME).set_level(srslog::str_to_basic_level(args.phy_com_log_level));
// Calculate subframe length if (not gnb_phy.init(args.gnb_args, args.cell_list)) {
sf_sz = (uint32_t)std::round(args.srate_hz * 1e-3); return;
}
// Initialise UE PHY // Initialise UE PHY
if (not ue_phy.init(args.ue_args, ue_phy_com, &ue_stack, 31)) { if (not ue_phy.init(args.ue_args, ue_phy_com, &ue_stack, 31)) {
@ -71,11 +72,6 @@ public:
return; return;
} }
// Set UE configuration in gNb
if (not gnb_phy.addmod_rnti(args.rnti, args.phy_cfg)) {
return;
}
initialised = true; initialised = true;
} }
@ -92,7 +88,7 @@ public:
bool run_tti() bool run_tti()
{ {
// Get gNb worker // Get gNb worker
srsenb::nr::sf_worker* gnb_worker = gnb_phy.wait_worker(tti); srsenb::nr::slot_worker* gnb_worker = gnb_phy.wait_worker(tti);
if (gnb_worker == nullptr) { if (gnb_worker == nullptr) {
return false; return false;
} }
@ -100,7 +96,7 @@ public:
// Feed gNb the UE transmitted signal // Feed gNb the UE transmitted signal
srsran::rf_timestamp_t gnb_time = {}; srsran::rf_timestamp_t gnb_time = {};
std::vector<cf_t*> gnb_rx_buffers(1); std::vector<cf_t*> gnb_rx_buffers(1);
gnb_rx_buffers[0] = gnb_worker->get_buffer_rx(0, 0); gnb_rx_buffers[0] = gnb_worker->get_buffer_rx(0);
ue_phy_com.read(gnb_rx_buffers, sf_sz, gnb_time); ue_phy_com.read(gnb_rx_buffers, sf_sz, gnb_time);
// Set gNb time // Set gNb time

Loading…
Cancel
Save