Merge branch 'next' into agpl_next

# Conflicts:
#	srsue/hdr/stack/bearer_manager.h
#	srsue/src/stack/bearer_manager.cc
master
Codebot 3 years ago committed by Your Name
commit de59c88954

@ -22,11 +22,11 @@
#ifndef SRSRAN_RRC_NR_UTILS_H
#define SRSRAN_RRC_NR_UTILS_H
#include "srsenb/hdr/stack/mac/sched_interface.h"
#include "srsran/common/phy_cfg_nr.h"
#include "srsran/interfaces/mac_interface_types.h"
#include "srsran/interfaces/pdcp_interface_types.h"
#include "srsran/interfaces/rlc_interface_types.h"
#include "srsran/interfaces/sched_interface.h"
/************************
* Forward declarations

@ -22,11 +22,11 @@
#ifndef SRSRAN_RRC_UTILS_H
#define SRSRAN_RRC_UTILS_H
#include "srsenb/hdr/stack/mac/sched_interface.h"
#include "srsran/interfaces/mac_interface_types.h"
#include "srsran/interfaces/pdcp_interface_types.h"
#include "srsran/interfaces/rlc_interface_types.h"
#include "srsran/interfaces/rrc_interface_types.h"
#include "srsran/interfaces/sched_interface.h"
/************************
* Forward declarations

@ -61,6 +61,13 @@ public:
*/
uint16_t get_band_from_dl_freq_Hz(double dl_freq_Hz) const;
/**
* @brief Get the lowest band that includes a given Downlink ARFCN
* @param arfcn Given ARFCN
* @return The band number if the ARFCN is bounded in a band, UINT16_MAX otherwise
*/
uint16_t get_band_from_dl_arfcn(uint32_t arfcn) const;
/**
* @brief Selects the SSB pattern case according to the band number and subcarrier spacing
* @remark Described by TS 38.101-1 Table 5.4.3.3-1: Applicable SS raster entries per operating band

@ -0,0 +1,100 @@
/**
*
* \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_BEARER_MANAGER_H
#define SRSRAN_BEARER_MANAGER_H
#include "srsran/common/common.h"
#include "srsran/common/rwlock_guard.h"
#include "srsran/srslog/srslog.h"
#include <map>
#include <stdint.h>
namespace srsran {
/**
* @brief Helper class to manage the mapping between EPS bearer and radio bearer
*
* The class maps EPS bearers that are known to NAS and GW (UE) or GTPU (eNB)
* to radio bearer (RB) that are only known to RRC.
* Since the lifetime of a EPS bearer is usually longer than the lifetime of a RB,
* the GW/GTPU needs to query the Stack to check whether a
* given EPS bearer is active, i.e. a DRB is established, or not.
*
* The class also maps between RATs since each LCID can exist on either EUTRA or NR RATs, or both.
*
* Since the access of this class is happening from two different threads (GW+RRC/Stack)
* it's public interface is protected.
*
* The class provides two interfaces to be used with RNTI or without. The version without
* RNTI is used by the UE. The version with RNTI in the interface is intented to be
* used by the eNB.
*
*/
class bearer_manager
{
public:
bearer_manager();
~bearer_manager();
struct radio_bearer_t {
srsran::srsran_rat_t rat;
uint32_t lcid;
uint32_t eps_bearer_id;
bool is_valid() const { return rat != srsran_rat_t::nulltype; }
};
/// Single user interface (for UE)
// RRC interface
/// Registers EPS bearer with PDCP RAT type and LCID
void add_eps_bearer(uint8_t eps_bearer_id, srsran::srsran_rat_t rat, uint32_t lcid);
/// Single EPS bearer is removed from map when the associated DRB is deleted
void remove_eps_bearer(uint8_t eps_bearer_id);
/// All registered bearer are removed (e.g. after connection release)
void reset();
// GW interface
bool has_active_radio_bearer(uint32_t eps_bearer_id);
// Stack interface to retrieve active RB
radio_bearer_t get_radio_bearer(uint32_t eps_bearer_id);
radio_bearer_t get_lcid_bearer(uint16_t rnti, uint32_t lcid);
/// Multi-user interface (see comments above)
void add_eps_bearer(uint16_t rnti, uint8_t eps_bearer_id, srsran::srsran_rat_t rat, uint32_t lcid);
void remove_eps_bearer(uint16_t rnti, uint8_t eps_bearer_id);
void reset(uint16_t rnti);
bool has_active_radio_bearer(uint16_t rnti, uint32_t eps_bearer_id);
radio_bearer_t get_radio_bearer(uint16_t rnti, uint32_t eps_bearer_id);
private:
pthread_rwlock_t rwlock = {}; /// RW lock to protect access from RRC/GW threads
srslog::basic_logger& logger;
typedef std::map<uint32_t, radio_bearer_t> eps_rb_map_t;
struct user_bearers {
eps_rb_map_t bearers;
std::map<uint32_t, uint32_t> lcid_to_eps_bearer_id;
};
std::map<uint16_t, user_bearers> users_map;
const uint16_t default_key = 0xffff; // dummy RNTI used for public interface without explicit RNTI
radio_bearer_t invalid_rb = {srsran::srsran_rat_t::nulltype, 0, 0};
};
} // namespace srsran
#endif // SRSRAN_BEARER_MANAGER_H

@ -58,9 +58,10 @@ inline bool is_lte_cell_nof_prb(uint32_t nof_prb)
enum class lte_srb { srb0, srb1, srb2, count };
const uint32_t MAX_LTE_SRB_ID = 2;
enum class lte_drb { drb1 = 1, drb2, drb3, drb4, drb5, drb6, drb7, drb8, drb9, drb10, drb11, invalid };
const uint32_t MAX_LTE_DRB_ID = 11;
const uint32_t MAX_LTE_LCID = 10; // logicalChannelIdentity 3..10 in TS 36.331 v15.3
const uint32_t INVALID_LCID = 99; // random invalid LCID
const uint32_t MAX_LTE_DRB_ID = 11;
const uint32_t MAX_LTE_LCID = 10; // logicalChannelIdentity 3..10 in TS 36.331 v15.3
const uint32_t INVALID_LCID = 99; // random invalid LCID
const uint32_t INVALID_EPS_BEARER_ID = 99; // random invalid eps bearer id
constexpr bool is_lte_rb(uint32_t lcid)
{

@ -64,7 +64,7 @@ enum class nr_drb {
invalid
};
const uint32_t MAX_NR_DRB_ID = 29;
const uint32_t MAX_NR_NOF_BEARERS = MAX_NR_DRB_ID + MAX_NR_SRB_ID;
const uint32_t MAX_NR_NOF_BEARERS = MAX_NR_DRB_ID + MAX_NR_SRB_ID; // 32
constexpr bool is_nr_lcid(uint32_t lcid)
{

@ -166,7 +166,7 @@ struct formatter<srsran::slot_point> {
template <typename FormatContext>
auto format(srsran::slot_point slot, FormatContext& ctx) -> decltype(std::declval<FormatContext>().out())
{
return format_to(ctx.out(), "{}/{}", slot.sfn(), slot.slot_idx());
return format_to(ctx.out(), "{}:{}", slot.sfn(), slot.slot_idx());
}
};
} // namespace fmt

@ -40,7 +40,7 @@ struct gtpu_args_t {
class gtpu_interface_pdcp
{
public:
virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0;
virtual void write_pdu(uint16_t rnti, uint32_t bearer_id, srsran::unique_byte_buffer_t pdu) = 0;
};
// GTPU interface for RRC
@ -54,12 +54,15 @@ public:
uint32_t flush_before_teidin = 0;
};
virtual srsran::expected<uint32_t>
add_bearer(uint16_t rnti, uint32_t lcid, uint32_t addr, uint32_t teid_out, const bearer_props* props = nullptr) = 0;
virtual void set_tunnel_status(uint32_t teidin, bool dl_active) = 0;
virtual void rem_bearer(uint16_t rnti, uint32_t lcid) = 0;
virtual void mod_bearer_rnti(uint16_t old_rnti, uint16_t new_rnti) = 0;
virtual void rem_user(uint16_t rnti) = 0;
virtual srsran::expected<uint32_t> add_bearer(uint16_t rnti,
uint32_t eps_bearer_id,
uint32_t addr,
uint32_t teid_out,
const bearer_props* props = nullptr) = 0;
virtual void set_tunnel_status(uint32_t teidin, bool dl_active) = 0;
virtual void rem_bearer(uint16_t rnti, uint32_t eps_bearer_id) = 0;
virtual void mod_bearer_rnti(uint16_t old_rnti, uint16_t new_rnti) = 0;
virtual void rem_user(uint16_t rnti) = 0;
};
} // namespace srsenb

@ -28,6 +28,14 @@
namespace srsenb {
class stack_interface_rrc
{
public:
virtual void add_eps_bearer(uint16_t rnti, uint8_t eps_bearer_id, srsran::srsran_rat_t rat, uint32_t lcid) = 0;
virtual void remove_eps_bearer(uint16_t rnti, uint8_t eps_bearer_id) = 0;
virtual void reset_eps_bearers(uint16_t rnti) = 0;
};
class stack_interface_phy_lte;
} // namespace srsenb

@ -19,8 +19,8 @@
*
*/
#include "srsenb/hdr/stack/mac/sched_interface.h"
#include "srsran/interfaces/rrc_interface_types.h"
#include "srsran/interfaces/sched_interface.h"
#ifndef SRSRAN_ENB_MAC_INTERFACES_H
#define SRSRAN_ENB_MAC_INTERFACES_H
@ -248,14 +248,14 @@ public:
virtual int ue_set_crnti(uint16_t temp_crnti, uint16_t crnti, const sched_interface::ue_cfg_t& cfg) = 0;
/* Manages UE bearers and associated configuration */
virtual int bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, sched_interface::ue_bearer_cfg_t* cfg) = 0;
virtual int bearer_ue_rem(uint16_t rnti, uint32_t lc_id) = 0;
virtual void phy_config_enabled(uint16_t rnti, bool enabled) = 0;
virtual int bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, mac_lc_ch_cfg_t* cfg) = 0;
virtual int bearer_ue_rem(uint16_t rnti, uint32_t lc_id) = 0;
virtual void phy_config_enabled(uint16_t rnti, bool enabled) = 0;
virtual void write_mcch(const srsran::sib2_mbms_t* sib2_,
const srsran::sib13_t* sib13_,
const srsran::mcch_msg_t* mcch_,
const uint8_t* mcch_payload,
const uint8_t mcch_payload_length) = 0;
const uint8_t mcch_payload_length) = 0;
/**
* Allocate a C-RNTI for a new user, without adding it to the phy layer and scheduler yet

@ -19,9 +19,9 @@
*
*/
#include "srsenb/hdr/stack/mac/sched_interface.h"
#include "srsran/asn1/s1ap_utils.h"
#include "srsran/interfaces/enb_rrc_interface_types.h"
#include "srsran/interfaces/sched_interface.h"
#ifndef SRSRAN_ENB_RRC_INTERFACES_H
#define SRSRAN_ENB_RRC_INTERFACES_H

@ -24,35 +24,18 @@
#include "srsran/srsran.h"
#include "srsenb/hdr/stack/mac/sched_interface.h"
#include "srsran/common/interfaces_common.h"
#include "srsran/common/security.h"
#include "srsran/interfaces/pdcp_interface_types.h"
#include "srsran/interfaces/rlc_interface_types.h"
#include "srsran/interfaces/rrc_interface_types.h"
#include "srsran/interfaces/sched_interface.h"
// EUTRA interfaces that are used unmodified
#include "srsran/interfaces/enb_mac_interfaces.h"
#include "srsran/interfaces/enb_rrc_interfaces.h"
namespace srsenb {
/*****************************
* MAC INTERFACES
****************************/
class mac_interface_rrc_nr
{
public:
// Provides cell configuration including SIB periodicity, etc.
virtual int cell_cfg(srsenb::sched_interface::cell_cfg_t* cell_cfg) = 0;
/// Allocates a new user/RNTI at MAC. Returns RNTI on success or SRSRAN_INVALID_RNTI otherwise.
virtual uint16_t reserve_rnti() = 0;
};
// NR interface is identical to EUTRA interface
class mac_interface_rlc_nr : public mac_interface_rlc
{};
/*****************************
* RLC INTERFACES
****************************/
@ -173,8 +156,7 @@ public:
// NR interface identical to EUTRA version
class rrc_interface_pdcp_nr : public rrc_interface_pdcp
{
};
{};
class phy_interface_rrc_nr
{
@ -304,7 +286,7 @@ public:
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, pusch_info_t& pusch_info) = 0;
virtual int pusch_info(const srsran_slot_cfg_t& slot_cfg, pusch_info_t& pusch_info) = 0;
virtual void rach_detected(const rach_info_t& rach_info) = 0;
};

@ -0,0 +1,39 @@
/**
*
* \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_GNB_MAC_INTERFACES_H
#define SRSRAN_GNB_MAC_INTERFACES_H
#include "srsenb/hdr/stack/mac/nr/sched_nr_interface.h"
namespace srsenb {
class mac_interface_rrc_nr
{
public:
// Provides cell configuration including SIB periodicity, etc.
virtual int cell_cfg(const sched_interface::cell_cfg_t& cell,
srsran::const_span<sched_nr_interface::cell_cfg_t> nr_cells) = 0;
/// Allocates a new user/RNTI at MAC. Returns RNTI on success or SRSRAN_INVALID_RNTI otherwise.
virtual uint16_t reserve_rnti(uint32_t enb_cc_idx) = 0;
virtual int ue_cfg(uint16_t rnti, const sched_nr_interface::ue_cfg_t& ue_cfg) = 0;
};
// NR interface is identical to EUTRA interface
class mac_interface_rlc_nr : public mac_interface_rlc
{};
} // namespace srsenb
#endif // SRSRAN_GNB_MAC_INTERFACES_H

@ -175,17 +175,21 @@ public:
};
struct phy_args_nr_t {
uint32_t rf_channel_offset = 0; ///< Specifies the RF channel the NR carrier shall fill
uint32_t nof_carriers = 1;
uint32_t max_nof_prb = 106;
uint32_t nof_phy_threads = 3;
uint32_t worker_cpu_mask = 0;
srsran::phy_log_args_t log = {};
srsran_ue_dl_nr_args_t dl = {};
srsran_ue_ul_nr_args_t ul = {};
std::set<uint32_t> fixed_sr = {1};
uint32_t fix_wideband_cqi = 15; // Set to a non-zero value for fixing the wide-band CQI report
bool store_pdsch_ko = false;
uint32_t rf_channel_offset = 0; ///< Specifies the RF channel the NR carrier shall fill
uint32_t nof_carriers = 1;
uint32_t max_nof_prb = 106;
uint32_t nof_phy_threads = 3;
uint32_t worker_cpu_mask = 0;
srsran::phy_log_args_t log = {};
srsran_ue_dl_nr_args_t dl = {};
srsran_ue_ul_nr_args_t ul = {};
std::set<uint32_t> fixed_sr = {1};
uint32_t fix_wideband_cqi = 15; // Set to a non-zero value for fixing the wide-band CQI report
bool store_pdsch_ko = false;
float trs_epre_ema_alpha = 0.1f; ///< EPRE measurement exponential average alpha
float trs_rsrp_ema_alpha = 0.1f; ///< RSRP measurement exponential average alpha
float trs_cfo_ema_alpha = 0.1f; ///< RSRP measurement exponential average alpha
bool enable_worker_cfo = true; ///< Enable/Disable open loop CFO correction at the workers
phy_args_nr_t()
{

@ -137,7 +137,7 @@ public:
const mac_sch_subpdu_nr& get_subpdu(const uint32_t& index);
bool is_ulsch();
void init_tx(byte_buffer_t* buffer_, uint32_t pdu_len_, bool is_ulsch_ = false);
int init_tx(byte_buffer_t* buffer_, uint32_t pdu_len_, bool is_ulsch_ = false);
void init_rx(bool ulsch_ = false);
// Add SDU or CEs to PDU

@ -45,7 +45,6 @@ extern "C" {
#define SRSRAN_SF_LEN_PRB_NR(nof_prb) (srsran_min_symbol_sz_rb(nof_prb) * 15)
#define SRSRAN_SLOT_MAX_LEN_RE_NR (SRSRAN_SLOT_LEN_RE_NR(SRSRAN_MAX_PRB_NR))
#define SRSRAN_SLOT_MAX_NOF_BITS_NR (SRSRAN_SLOT_MAX_LEN_RE_NR * SRSRAN_MAX_QM)
#define SRSRAN_MAX_LAYERS_NR 8
/**
@ -123,6 +122,11 @@ extern "C" {
*/
#define SRSRAN_PDSCH_MAX_RE_NR (SRSRAN_MAX_NRE_NR * SRSRAN_MAX_PRB_NR)
/**
* @brief defines the maximum number of bits that can be transmitted in a slot
*/
#define SRSRAN_SLOT_MAX_NOF_BITS_NR (SRSRAN_PDSCH_MAX_RE_NR * SRSRAN_MAX_QM)
/**
* @brief Maximum number of PDSCH time domain resource allocations. This is defined by TS 38.331 v15.10.0
* as maxNrofDL-Allocations
@ -414,17 +418,19 @@ typedef struct SRSRAN_API {
* @brief CORESET parameters as defined in TS 38.331 V15.10.0 - ControlResourceSet
*/
typedef struct SRSRAN_API {
uint32_t id;
srsran_coreset_mapping_type_t mapping_type;
uint32_t duration;
bool freq_resources[SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE];
srsran_coreset_bundle_size_t interleaver_size;
uint32_t id;
srsran_coreset_mapping_type_t mapping_type;
uint32_t duration;
bool freq_resources[SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE];
bool dmrs_scrambling_id_present;
uint32_t dmrs_scrambling_id;
srsran_coreset_precoder_granularity_t precoder_granularity;
srsran_coreset_bundle_size_t reg_bundle_size;
srsran_coreset_bundle_size_t interleaver_size; ///< Referenced in TS 38.211 section 7.3.2.2 as R
srsran_coreset_bundle_size_t reg_bundle_size; ///< Referenced in TS 38.211 section 7.3.2.2 as L
uint32_t shift_index;
uint32_t offset_rb; ///< Integer offset in resource blocks from the pointA (lowest subcarrier of resource grid) to the
///< lowest resource block of the CORESET region (used by CORESET Zero only)
/** Missing TCI parameters */
} srsran_coreset_t;
@ -525,6 +531,18 @@ SRSRAN_API uint32_t srsran_coreset_get_bw(const srsran_coreset_t* coreset);
*/
SRSRAN_API uint32_t srsran_coreset_get_sz(const srsran_coreset_t* coreset);
/**
* @brief Calculates the starting resource block index in the resource grid
*
* @remark Intended to be used for common search space as specifies the lat clause in TS 38.214 section 5.1.2.2 Resource
* allocation in frequency domain
*
* @param coreset provides the given CORESET configuration
* @return The index of the lowest resource block in the resource grid used by the given CORESET if the CORESET
* configuration is valid; Otherwise, 0.
*/
SRSRAN_API uint32_t srsran_coreset_start_rb(const srsran_coreset_t* coreset);
/**
* @brief Get the NR PDSCH mapping type in string
* @param mapping_type Mapping type
@ -610,13 +628,40 @@ SRSRAN_API uint32_t srsran_csi_meas_info(const srsran_csi_trs_measurements_t* me
*/
SRSRAN_API srsran_subcarrier_spacing_t srsran_subcarrier_spacing_from_str(const char* str);
/**
* @brief Converts a given subcarrier spacing to string
* @param scs Subcarrier spacing
* @return A constant string pointer
*/
SRSRAN_API const char* srsran_subcarrier_spacing_to_str(srsran_subcarrier_spacing_t scs);
/**
* @brief Combine Channel State Information from Tracking Reference Signals (CSI-TRS)
* @param[in] a CSI-RS measurement
* @param[in] b CSI-RS measurement
* @param[out] dst Destination of the combined
*/
SRSRAN_API void srsran_combine_csi_trs_measurements(const srsran_csi_trs_measurements_t *a, const srsran_csi_trs_measurements_t *b, srsran_csi_trs_measurements_t *dst);
SRSRAN_API void srsran_combine_csi_trs_measurements(const srsran_csi_trs_measurements_t* a,
const srsran_csi_trs_measurements_t* b,
srsran_csi_trs_measurements_t* dst);
/**
* @brief Setup CORESET Zero from a configuration index
* @remark Defined by TS 38.213 tables 13-1, 13-2, 13-3, 13-4, 13-5, 13-6, 13-7, 13-8, 13-9, 13-10
* @param n_cell_id Physical Cell identifier
* @param ssb_pointA_freq_offset_Hz Integer frequency offset in Hz between the SS/PBCH block center and pointA
* @param ssb_scs SS/PBCH block subcarrier spacing
* @param pdcch_scs PDCCH subcarrier spacing
* @param idx CORESET Zero configuration index
* @param[out] coreset Points to the resultant CORESET
* @return SRSRAN_SUCCESS if the given inputs lead to a valid CORESET configuration, otherise SRSRAN_ERROR code
*/
SRSRAN_API int srsran_coreset_zero(uint32_t n_cell_id,
uint32_t ssb_pointA_freq_offset_Hz,
srsran_subcarrier_spacing_t ssb_scs,
srsran_subcarrier_spacing_t pdcch_scs,
uint32_t idx,
srsran_coreset_t* coreset);
#ifdef __cplusplus
}

@ -0,0 +1,23 @@
/**
*
* \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_SLIV_H
#define SRSRAN_SLIV_H
#include "srsran/config.h"
#include <inttypes.h>
SRSRAN_API void srsran_sliv_to_s_and_l(uint32_t N, uint32_t v, uint32_t* S, uint32_t* L);
SRSRAN_API uint32_t srsran_sliv_from_s_and_l(uint32_t N, uint32_t S, uint32_t L);
#endif // SRSRAN_SLIV_H

@ -41,11 +41,9 @@
#include <stdint.h>
#define SRSRAN_LDPC_BG1_MAX_LEN_CB 8448 /*!< \brief Maximum code block size for LDPC BG1 */
#define SRSRAN_LDPC_BG2_MAX_LEN_CB 3840 /*!< \brief Maximum code block size for LDPC BG2 */
#define SRSRAN_LDPC_MAX_LEN_CB \
SRSRAN_MAX(SRSRAN_LDPC_BG1_MAX_LEN_CB, \
SRSRAN_LDPC_BG2_MAX_LEN_CB) /*!< \brief Maximum code block size for LDPC BG1 or BG2 */
#define SRSRAN_LDPC_BG1_MAX_LEN_CB 8448 /*!< \brief Maximum code block size for LDPC BG1 */
#define SRSRAN_LDPC_BG2_MAX_LEN_CB 3840 /*!< \brief Maximum code block size for LDPC BG2 */
#define SRSRAN_LDPC_MAX_LEN_CB SRSRAN_LDPC_BG1_MAX_LEN_CB /*!< \brief Maximum code block size for LDPC BG1 or BG2 */
#define BG1Nfull 68 /*!< \brief Number of variable nodes in BG1. */
#define BG1N 66 /*!< \brief Number of variable nodes in BG1 after puncturing. */

@ -120,12 +120,13 @@ typedef struct SRSRAN_API {
* @brief Describes the NR DCI search context
*/
typedef struct SRSRAN_API {
srsran_dci_location_t location; ///< DCI location
srsran_search_space_type_t ss_type; ///< Search space type
uint32_t coreset_id; ///< CORESET identifier
srsran_rnti_type_t rnti_type; ///< RNTI type
srsran_dci_format_nr_t format; ///< DCI format
uint16_t rnti; ///< UE temporal RNTI
srsran_dci_location_t location; ///< DCI location
srsran_search_space_type_t ss_type; ///< Search space type
uint32_t coreset_id; ///< CORESET identifier
uint32_t coreset_start_rb; ///< CORESET lowest RB index in the resource grid
srsran_rnti_type_t rnti_type; ///< RNTI type
srsran_dci_format_nr_t format; ///< DCI format
uint16_t rnti; ///< UE temporal RNTI
} srsran_dci_ctx_t;
/**
@ -191,6 +192,9 @@ typedef struct SRSRAN_API {
uint32_t cbg_flush; ///< CBG flushing out information (CBGFI)
uint32_t dmrs_id; ///< DMRS sequence initialization
// DL context from unpacking. Required for resource allocation
uint32_t coreset0_bw; ///< CORESET0 size used for frequency resource allocation
} srsran_dci_dl_nr_t;
/**

@ -107,6 +107,17 @@ int srsran_pdcch_nr_locations_coreset(const srsran_coreset_t* coreset,
SRSRAN_API int srsran_pdcch_nr_max_candidates_coreset(const srsran_coreset_t* coreset, uint32_t aggregation_level);
/**
* @brief Function for generating a RB mask indicating the CCE-to-REG mapping
* @param coreset A given CORESET
* @param dci_location The DCI location for the PDCCH transmission
* @param[out] rb_mask The resultant mask indicating the locations of PDCCH payload
* @return SRSRAN_SUCCESS if the provided parameters are valid, SRSRAN_ERROR code otherwise
*/
SRSRAN_API int srsran_pdcch_nr_cce_to_reg_mapping(const srsran_coreset_t* coreset,
const srsran_dci_location_t* dci_location,
bool rb_mask[SRSRAN_MAX_PRB_NR]);
SRSRAN_API int srsran_pdcch_nr_init_tx(srsran_pdcch_nr_t* q, const srsran_pdcch_nr_args_t* args);
SRSRAN_API int srsran_pdcch_nr_init_rx(srsran_pdcch_nr_t* q, const srsran_pdcch_nr_args_t* args);

@ -38,8 +38,12 @@
#include "srsran/phy/fec/ldpc/ldpc_rm.h"
#include "srsran/phy/phch/phch_cfg_nr.h"
/**
* @brief Maximum number of codeblocks for a NR shared channel transmission. It assumes a rate of 1.0 for the maximum
* amount of bits a resource grid can fit
*/
#define SRSRAN_SCH_NR_MAX_NOF_CB_LDPC \
((SRSRAN_SLOT_MAX_NOF_BITS_NR + (SRSRAN_LDPC_BG2_MAX_LEN_CB - 1)) / SRSRAN_LDPC_BG2_MAX_LEN_CB)
((SRSRAN_SLOT_MAX_NOF_BITS_NR + (SRSRAN_LDPC_MAX_LEN_CB - 1)) / SRSRAN_LDPC_MAX_LEN_CB)
/**
* @brief Groups NR-PUSCH data for reception

@ -57,12 +57,16 @@ typedef struct SRSRAN_API {
srsran_pusch_nr_t pusch;
srsran_pucch_nr_t pucch;
srsran_dmrs_sch_t dmrs;
float freq_offset_hz;
} srsran_ue_ul_nr_t;
SRSRAN_API int srsran_ue_ul_nr_init(srsran_ue_ul_nr_t* q, cf_t* output, const srsran_ue_ul_nr_args_t* args);
SRSRAN_API int srsran_ue_ul_nr_set_carrier(srsran_ue_ul_nr_t* q, const srsran_carrier_nr_t* carrier);
SRSRAN_API void srsran_ue_ul_nr_set_freq_offset(srsran_ue_ul_nr_t* q, float freq_offset_hz);
SRSRAN_API int srsran_ue_ul_nr_encode_pusch(srsran_ue_ul_nr_t* q,
const srsran_slot_cfg_t* slot_cfg,
const srsran_sch_cfg_nr_t* pusch_cfg,

@ -26,31 +26,15 @@
#include "srsran_assert.h"
namespace srsran {
namespace detail {
template <typename T, typename U>
[[gnu::noinline, noreturn]] void assert_eq_failure(const T& expected_val, const U& actual_val)
{
std::string s = fmt::format("Actual value '{}' differs from expected '{}'", actual_val, expected_val);
srsran_assertion_failure("%s", s.c_str());
}
template <typename T>
[[gnu::noinline, noreturn]] void assert_neq_failure(const T& actual_val)
{
std::string s = fmt::format("Value should not be equal to '{}'", actual_val);
srsran_assertion_failure("%s", s.c_str());
}
} // namespace detail
} // namespace srsran
#define TESTASSERT_EQ(EXPECTED, ACTUAL) \
(void)((EXPECTED == ACTUAL) || (::srsran::detail::assert_eq_failure(EXPECTED, ACTUAL), 0))
(void)((EXPECTED == ACTUAL) || \
(srsran_assertion_failure( \
"%s", fmt::format("Actual value '{}' differs from expected '{}'", ACTUAL, EXPECTED).c_str()), \
0))
#define TESTASSERT_NEQ(EXPECTED, ACTUAL) \
(void)((EXPECTED != ACTUAL) || (::srsran::detail::assert_neq_failure(ACTUAL), 0))
(void)((EXPECTED != ACTUAL) || \
(srsran_assertion_failure("%s", fmt::format("Value should not be equal to '{}'", ACTUAL).c_str()), 0))
#define TESTASSERT(cond) srsran_assert((cond), "Fail at \"%s\"", (#cond))
@ -63,7 +47,7 @@ template <typename T>
#define TESTASSERT(cond) \
do { \
if (!(cond)) { \
printf("[%s][Line %d] Fail at \"%s\"\n", __FUNCTION__, __LINE__, (#cond)); \
fprintf(stderr, "%s:%d: Assertion Failure: \"%s\"\n", __FUNCTION__, __LINE__, (#cond)); \
return -1; \
} \
} while (0)

@ -18,12 +18,12 @@
# and at http://www.gnu.org/licenses/.
#
set(SOURCES arch_select.cc
enb_events.cc
backtrace.c
byte_buffer.cc
band_helper.cc
bearer_manager.cc
buffer_pool.cc
crash_handler.cc
gen_mch_tables.c

@ -76,6 +76,18 @@ uint16_t srsran_band_helper::get_band_from_dl_freq_Hz(double freq) const
return UINT16_MAX;
}
uint16_t srsran_band_helper::get_band_from_dl_arfcn(uint32_t arfcn) const
{
for (const nr_band& band : nr_band_table_fr1) {
// Check given ARFCN is between the first and last possible ARFCN and matches step
if (arfcn >= band.dl_nref_first and arfcn <= band.dl_nref_last and
(arfcn - band.dl_nref_first) % band.dl_nref_step == 0) {
return band.band;
}
}
return UINT16_MAX;
}
srsran_ssb_patern_t srsran_band_helper::get_ssb_pattern(uint16_t band, srsran_subcarrier_spacing_t scs) const
{
// Look for the given band and SCS

@ -0,0 +1,153 @@
/**
*
* \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 "srsran/common/bearer_manager.h"
namespace srsran {
bearer_manager::bearer_manager() : logger(srslog::fetch_basic_logger("STCK", false))
{
pthread_rwlock_init(&rwlock, nullptr);
}
bearer_manager::~bearer_manager()
{
pthread_rwlock_destroy(&rwlock);
}
void bearer_manager::add_eps_bearer(uint8_t eps_bearer_id, srsran::srsran_rat_t rat, uint32_t lcid)
{
add_eps_bearer(default_key, eps_bearer_id, rat, lcid);
}
void bearer_manager::add_eps_bearer(uint16_t rnti, uint8_t eps_bearer_id, srsran::srsran_rat_t rat, uint32_t lcid)
{
srsran::rwlock_write_guard rw_lock(rwlock);
auto user_it = users_map.find(rnti);
if (user_it == users_map.end()) {
// add empty bearer map
auto p = users_map.emplace(rnti, user_bearers{});
user_it = p.first;
}
auto bearer_it = user_it->second.bearers.find(eps_bearer_id);
if (bearer_it != user_it->second.bearers.end()) {
logger.error("EPS bearer ID %d already registered", eps_bearer_id);
return;
}
user_it->second.bearers.emplace(eps_bearer_id, radio_bearer_t{rat, lcid, eps_bearer_id});
user_it->second.lcid_to_eps_bearer_id.emplace(lcid, eps_bearer_id);
logger.info("Registered EPS bearer ID %d for lcid=%d over %s-PDCP", eps_bearer_id, lcid, to_string(rat).c_str());
}
void bearer_manager::remove_eps_bearer(uint8_t eps_bearer_id)
{
remove_eps_bearer(default_key, eps_bearer_id);
}
void bearer_manager::remove_eps_bearer(uint16_t rnti, uint8_t eps_bearer_id)
{
srsran::rwlock_write_guard rw_lock(rwlock);
auto user_it = users_map.find(rnti);
if (user_it == users_map.end()) {
logger.error("No EPS bearer registered for rnti=%x", rnti);
return;
}
auto bearer_it = user_it->second.bearers.find(eps_bearer_id);
if (bearer_it == user_it->second.bearers.end()) {
logger.error("Can't remove EPS bearer ID %d", eps_bearer_id);
return;
}
uint32_t lcid = bearer_it->second.lcid;
user_it->second.bearers.erase(bearer_it);
user_it->second.lcid_to_eps_bearer_id.erase(lcid);
logger.info("Removed mapping for EPS bearer ID %d", eps_bearer_id);
}
void bearer_manager::reset()
{
reset(default_key);
}
void bearer_manager::reset(uint16_t rnti)
{
srsran::rwlock_write_guard rw_lock(rwlock);
auto user_it = users_map.find(rnti);
if (user_it == users_map.end()) {
logger.error("No EPS bearer registered for rnti=%x", rnti);
return;
}
user_it->second.lcid_to_eps_bearer_id.clear();
user_it->second.bearers.clear();
logger.info("Reset EPS bearer manager");
}
// GW interface
bool bearer_manager::has_active_radio_bearer(uint32_t eps_bearer_id)
{
return has_active_radio_bearer(default_key, eps_bearer_id);
}
bool bearer_manager::has_active_radio_bearer(uint16_t rnti, uint32_t eps_bearer_id)
{
srsran::rwlock_read_guard rw_lock(rwlock);
auto user_it = users_map.find(rnti);
if (user_it == users_map.end()) {
return false;
}
return user_it->second.bearers.find(eps_bearer_id) != user_it->second.bearers.end();
}
// Stack interface
bearer_manager::radio_bearer_t bearer_manager::get_radio_bearer(uint32_t eps_bearer_id)
{
return get_radio_bearer(default_key, eps_bearer_id);
}
bearer_manager::radio_bearer_t bearer_manager::get_lcid_bearer(uint16_t rnti, uint32_t lcid)
{
srsran::rwlock_read_guard rw_lock(rwlock);
auto user_it = users_map.find(rnti);
if (user_it == users_map.end()) {
return invalid_rb;
}
auto lcid_it = user_it->second.lcid_to_eps_bearer_id.find(lcid);
if (lcid_it != user_it->second.lcid_to_eps_bearer_id.end()) {
return user_it->second.bearers.at(lcid_it->second);
}
return invalid_rb;
}
bearer_manager::radio_bearer_t bearer_manager::get_radio_bearer(uint16_t rnti, uint32_t eps_bearer_id)
{
srsran::rwlock_read_guard rw_lock(rwlock);
auto user_it = users_map.find(rnti);
if (user_it == users_map.end()) {
return invalid_rb;
}
if (user_it->second.bearers.find(eps_bearer_id) != user_it->second.bearers.end()) {
return user_it->second.bearers.at(eps_bearer_id);
}
return invalid_rb;
}
} // namespace srsran

@ -232,12 +232,13 @@ bool phy_cfg_nr_t::get_dci_ctx_pdsch_rnti_c(uint32_t ss_id,
const srsran_search_space_t& ss = pdcch.search_space[ss_id];
// Fill context
ctx.location = location;
ctx.ss_type = ss.type;
ctx.coreset_id = ss.coreset_id;
ctx.rnti_type = srsran_rnti_type_c;
ctx.format = format;
ctx.rnti = rnti;
ctx.location = location;
ctx.ss_type = ss.type;
ctx.coreset_id = ss.coreset_id;
ctx.coreset_start_rb = srsran_coreset_start_rb(&pdcch.coreset[ss.coreset_id]);
ctx.rnti_type = srsran_rnti_type_c;
ctx.format = format;
ctx.rnti = rnti;
return true;
}
@ -257,12 +258,13 @@ bool phy_cfg_nr_t::get_dci_ctx_pusch_rnti_c(uint32_t ss_id,
const srsran_search_space_t& ss = pdcch.search_space[ss_id];
// Fill context
ctx.location = location;
ctx.ss_type = ss.type;
ctx.coreset_id = ss.coreset_id;
ctx.rnti_type = srsran_rnti_type_c;
ctx.format = format;
ctx.rnti = rnti;
ctx.location = location;
ctx.ss_type = ss.type;
ctx.coreset_id = ss.coreset_id;
ctx.coreset_start_rb = srsran_coreset_start_rb(&pdcch.coreset[ss.coreset_id]);
ctx.rnti_type = srsran_rnti_type_c;
ctx.format = format;
ctx.rnti = rnti;
return true;
}

@ -343,13 +343,18 @@ bool mac_sch_pdu_nr::is_ulsch()
return ulsch;
}
void mac_sch_pdu_nr::init_tx(byte_buffer_t* buffer_, uint32_t pdu_len_, bool ulsch_)
int mac_sch_pdu_nr::init_tx(byte_buffer_t* buffer_, uint32_t pdu_len_, bool ulsch_)
{
if (buffer_ == nullptr || buffer_->msg == nullptr) {
logger.error("Invalid buffer");
return SRSRAN_ERROR;
}
buffer = buffer_;
subpdus.clear();
pdu_len = pdu_len_;
remaining_len = pdu_len_;
ulsch = ulsch_;
return SRSRAN_SUCCESS;
}
void mac_sch_pdu_nr::init_rx(bool ulsch_)

@ -705,7 +705,7 @@ int srsran_csi_rs_nzp_measure_trs(const srsran_carrier_nr_t* carrier,
float cfo_max = 0.0f;
for (uint32_t i = 1; i < count; i++) {
float time_diff = srsran_symbol_distance_s(measurements[i - 1].l0, measurements[i].l0, carrier->scs);
float phase_diff = cargf(measurements[i].corr * conjf(measurements[i - 1].corr));
float phase_diff = cargf(measurements[i - 1].corr * conjf(measurements[i].corr));
float cfo_max_temp = 0.0f;
// Avoid zero division

@ -22,15 +22,18 @@
#include "srsran/phy/ch_estimation/dmrs_pdcch.h"
#include "srsran/phy/ch_estimation/chest_common.h"
#include "srsran/phy/common/sequence.h"
#include "srsran/phy/phch/pdcch_nr.h"
#include "srsran/phy/utils/convolution.h"
#include "srsran/phy/utils/debug.h"
#include "srsran/phy/utils/vector.h"
#include <complex.h>
#include <math.h>
#define NOF_PILOTS_X_RB 3
/// @brief Every frequency resource is 6 Resource blocks, every resource block carries 3 pilots. So 18 possible pilots
/// per frequency resource.
#define NOF_PILOTS_X_FREQ_RES 18
#define NOF_PILOTS_X_FREQ_RES (NOF_PILOTS_X_RB * 6)
///@brief Maximum number of pilots in a PDCCH candidate location
#define DMRS_PDCCH_MAX_NOF_PILOTS_CANDIDATE \
@ -60,13 +63,12 @@ static uint32_t dmrs_pdcch_get_cinit(uint32_t slot_idx, uint32_t symbol_idx, uin
2UL * n_id);
}
static void dmrs_pdcch_put_symbol_noninterleaved(const srsran_carrier_nr_t* carrier,
const srsran_coreset_t* coreset,
const srsran_dci_location_t* dci_location,
uint32_t cinit,
cf_t* sf_symbol)
static void dmrs_pdcch_put_symbol(const srsran_carrier_nr_t* carrier,
const srsran_coreset_t* coreset,
const bool rb_mask[SRSRAN_MAX_PRB_NR],
uint32_t cinit,
cf_t* sf_symbol)
{
uint32_t L = 1U << dci_location->L;
uint32_t nof_freq_res = SRSRAN_MIN(carrier->nof_prb / 6, SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE);
// Initialise sequence for this symbol
@ -74,67 +76,45 @@ static void dmrs_pdcch_put_symbol_noninterleaved(const srsran_carrier_nr_t* ca
srsran_sequence_state_init(&sequence_state, cinit);
uint32_t sequence_skip = 0; // Accumulates pilot locations to skip
// Calculate Resource block indexes range, every CCE is 6 REG, 1 REG is 6 RE in resource blocks
uint32_t rb_coreset_idx_begin = (dci_location->ncce * 6) / coreset->duration;
uint32_t rb_coreset_idx_end = ((dci_location->ncce + L) * 6) / coreset->duration;
// CORESET Resource Block counter
uint32_t rb_coreset_idx = 0;
for (uint32_t i = 0; i < nof_freq_res; i++) {
// Skip frequency resource if outside of the CORESET
if (!coreset->freq_resources[i]) {
// Skip possible DMRS locations in this region
sequence_skip += NOF_PILOTS_X_FREQ_RES;
continue;
}
// Skip if the frequency resource highest RB is lower than the first CCE resource block.
if ((rb_coreset_idx + 6) <= rb_coreset_idx_begin) {
// For each frequency resource (6 RB groups)
for (uint32_t res_idx = 0; res_idx < nof_freq_res; res_idx++) {
// Skip frequency resource if outside of the CORESET
if (!coreset->freq_resources[res_idx]) {
// Skip possible DMRS locations in this region
sequence_skip += NOF_PILOTS_X_FREQ_RES;
// Since this is part of the CORESET, count the RB as CORESET
rb_coreset_idx += 6;
continue;
}
// Return if the first RB of the frequency resource is greater than the last CCE resource block
if (rb_coreset_idx > rb_coreset_idx_end) {
return;
}
// Skip all discarded possible pilot locations
srsran_sequence_state_advance(&sequence_state, 2 * sequence_skip);
sequence_skip = 0;
// Generate pilots
cf_t rl[NOF_PILOTS_X_FREQ_RES];
srsran_sequence_state_gen_f(&sequence_state, M_SQRT1_2, (float*)rl, NOF_PILOTS_X_FREQ_RES * 2);
// For each RB in the frequency resource
for (uint32_t j = 0; j < 6; j++) {
// Calculate absolute RB index
uint32_t n = i * 6 + j;
// Skip if lower than begin
if (rb_coreset_idx < rb_coreset_idx_begin) {
rb_coreset_idx++;
// For each RB in the enabled frequency resource
for (uint32_t rb = 0; rb < 6; rb++, rb_coreset_idx++) {
// Skip if mask is disabled
if (!rb_mask[rb_coreset_idx]) {
sequence_skip += NOF_PILOTS_X_RB;
continue;
}
// Return if greater than end
if (rb_coreset_idx >= rb_coreset_idx_end) {
return;
}
// Skip all discarded possible pilot locations
srsran_sequence_state_advance(&sequence_state, 2 * sequence_skip);
sequence_skip = 0;
// Generate pilots for the given RB
cf_t rl[NOF_PILOTS_X_RB];
srsran_sequence_state_gen_f(&sequence_state, M_SQRT1_2, (float*)rl, NOF_PILOTS_X_RB * 2);
// Absolute RB index in the resource grid
uint32_t n = res_idx * 6 + rb;
// Write pilots in the symbol
for (uint32_t k_prime = 0; k_prime < 3; k_prime++) {
// Calculate sub-carrier index
for (uint32_t k_prime = 0; k_prime < NOF_PILOTS_X_RB; k_prime++) {
// Calculate absolute sub-carrier index in the resource grid
uint32_t k = n * SRSRAN_NRE + 4 * k_prime + 1;
sf_symbol[k] = rl[3 * j + k_prime];
// Write DMRS
sf_symbol[k] = rl[k_prime];
}
rb_coreset_idx++;
}
}
}
@ -149,11 +129,6 @@ int srsran_dmrs_pdcch_put(const srsran_carrier_nr_t* carrier,
return SRSRAN_ERROR_INVALID_INPUTS;
}
if (coreset->mapping_type == srsran_coreset_mapping_type_interleaved) {
ERROR("Error interleaved CORESET mapping is not currently implemented");
return SRSRAN_ERROR;
}
if (coreset->duration < SRSRAN_CORESET_DURATION_MIN || coreset->duration > SRSRAN_CORESET_DURATION_MAX) {
ERROR("Error CORESET duration %d is out-of-bounds (%d,%d)",
coreset->duration,
@ -162,6 +137,13 @@ int srsran_dmrs_pdcch_put(const srsran_carrier_nr_t* carrier,
return SRSRAN_ERROR;
}
// Calculate CCE-to-REG mapping mask
bool rb_mask[SRSRAN_MAX_PRB_NR] = {};
if (srsran_pdcch_nr_cce_to_reg_mapping(coreset, dci_location, rb_mask) < SRSRAN_SUCCESS) {
ERROR("Error in CCE-to-REG mapping");
return SRSRAN_SUCCESS;
}
// Use cell id if the DMR scrambling id is not provided by higher layers
uint32_t n_id = carrier->pci;
if (coreset->dmrs_scrambling_id_present) {
@ -178,8 +160,7 @@ int srsran_dmrs_pdcch_put(const srsran_carrier_nr_t* carrier,
DMRS_PDCCH_INFO_TX("n=%d; l=%d; cinit=%08x", slot_idx, l, cinit);
// Put data
dmrs_pdcch_put_symbol_noninterleaved(
carrier, coreset, dci_location, cinit, &sf_symbols[carrier->nof_prb * SRSRAN_NRE * l]);
dmrs_pdcch_put_symbol(carrier, coreset, rb_mask, cinit, &sf_symbols[carrier->nof_prb * SRSRAN_NRE * l]);
}
return SRSRAN_SUCCESS;
@ -299,6 +280,9 @@ void srsran_dmrs_pdcch_estimator_free(srsran_dmrs_pdcch_estimator_t* q)
static void
srsran_dmrs_pdcch_extract(srsran_dmrs_pdcch_estimator_t* q, uint32_t cinit, const cf_t* sf_symbol, cf_t* lse)
{
// Get CORESET offset
uint32_t offset_k = q->coreset.offset_rb * SRSRAN_NRE;
// Initialise pseudo-random sequence
srsran_sequence_state_t sequence_state = {};
srsran_sequence_state_init(&sequence_state, cinit);
@ -307,7 +291,7 @@ srsran_dmrs_pdcch_extract(srsran_dmrs_pdcch_estimator_t* q, uint32_t cinit, cons
uint32_t sequence_skip = 0;
// Counts enabled frequency domain resources
uint32_t rb_coreset_idx = 0;
uint32_t lse_count = 0;
// Iterate over all possible frequency resources
uint32_t freq_domain_res_size = SRSRAN_MIN(q->carrier.nof_prb / 6, SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE);
@ -327,26 +311,23 @@ srsran_dmrs_pdcch_extract(srsran_dmrs_pdcch_estimator_t* q, uint32_t cinit, cons
srsran_sequence_state_gen_f(&sequence_state, M_SQRT1_2, (float*)rl, NOF_PILOTS_X_FREQ_RES * 2);
// Iterate all PRBs in the enabled frequency domain resource
for (uint32_t j = 0, idx = rb_coreset_idx * NOF_PILOTS_X_FREQ_RES; j < 6; j++) {
cf_t* lse_ptr = &lse[lse_count];
for (uint32_t j = 0; j < 6; j++) {
// Calculate Grid PRB index (n)
uint32_t n = i * 6 + j;
// For each pilot in the PRB
for (uint32_t k_prime = 0; k_prime < 3; k_prime++, idx++) {
for (uint32_t k_prime = 0; k_prime < 3; k_prime++) {
// Calculate sub-carrier index
uint32_t k = n * SRSRAN_NRE + 4 * k_prime + 1;
// Extract symbol
lse[idx] = sf_symbol[k];
lse[lse_count++] = sf_symbol[k + offset_k];
}
}
// Calculate least squared estimates
cf_t* lse_ptr = &lse[rb_coreset_idx * NOF_PILOTS_X_FREQ_RES];
srsran_vec_prod_conj_ccc(lse_ptr, rl, lse_ptr, NOF_PILOTS_X_FREQ_RES);
// Increment frequency domain resource counter
rb_coreset_idx++;
}
}
@ -430,21 +411,18 @@ int srsran_dmrs_pdcch_get_measure(const srsran_dmrs_pdcch_estimator_t* q,
return SRSRAN_ERROR_INVALID_INPUTS;
}
uint32_t L = 1U << dci_location->L;
if (q->coreset.mapping_type == srsran_coreset_mapping_type_interleaved) {
ERROR("Error interleaved mapping not implemented");
return SRSRAN_ERROR;
}
// Check that CORESET duration is not less than minimum
if (q->coreset.duration < SRSRAN_CORESET_DURATION_MIN) {
ERROR("Invalid CORESET duration");
return SRSRAN_ERROR;
}
// Get base pilot;
uint32_t pilot_idx = (dci_location->ncce * 18) / q->coreset.duration;
uint32_t nof_pilots = (L * 18) / q->coreset.duration;
// Calculate CCE-to-REG mapping mask
bool rb_mask[SRSRAN_MAX_PRB_NR] = {};
if (srsran_pdcch_nr_cce_to_reg_mapping(&q->coreset, dci_location, rb_mask) < SRSRAN_SUCCESS) {
ERROR("Error in CCE-to-REG mapping");
return SRSRAN_SUCCESS;
}
// Initialise measurements
float rsrp = 0.0f; //< Averages linear RSRP
@ -453,24 +431,36 @@ int srsran_dmrs_pdcch_get_measure(const srsran_dmrs_pdcch_estimator_t* q,
float sync_err_avg = 0.0f; //< Averages synchronization
cf_t corr[SRSRAN_CORESET_DURATION_MAX] = {}; //< Saves correlation for the different symbols
// Iterate the CORESET duration
// For each CORESET symbol
for (uint32_t l = 0; l < q->coreset.duration; l++) {
// Temporal least square estimates
cf_t tmp[DMRS_PDCCH_MAX_NOF_PILOTS_CANDIDATE];
uint32_t nof_pilots = 0;
// For each RB in the CORESET
for (uint32_t rb = 0; rb < q->coreset_bw; rb++) {
// Skip RB if unused
if (!rb_mask[rb]) {
continue;
}
// Copy LSE
srsran_vec_cf_copy(&tmp[nof_pilots], &q->lse[l][rb * NOF_PILOTS_X_RB], NOF_PILOTS_X_RB);
nof_pilots += NOF_PILOTS_X_RB;
}
if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
DMRS_PDCCH_DEBUG_RX("Measuring PDCCH l=%d; lse=", l);
srsran_vec_fprint_c(stdout, &q->lse[l][pilot_idx], nof_pilots);
srsran_vec_fprint_c(stdout, tmp, nof_pilots);
}
// Measure synchronization error and accumulate for average
float tmp_sync_err = srsran_vec_estimate_frequency(&q->lse[l][pilot_idx], nof_pilots);
float tmp_sync_err = srsran_vec_estimate_frequency(tmp, nof_pilots);
sync_err_avg += tmp_sync_err;
#if DMRS_PDCCH_SYNC_PRECOMPENSATE_MEAS
cf_t tmp[DMRS_PDCCH_MAX_NOF_PILOTS_CANDIDATE];
// Pre-compensate synchronization error
srsran_vec_apply_cfo(&q->lse[l][pilot_idx], tmp_sync_err, tmp, nof_pilots);
#else // DMRS_PDCCH_SYNC_PRECOMPENSATE_MEAS
const cf_t* tmp = &q->lse[l][pilot_idx];
srsran_vec_apply_cfo(tmp, tmp_sync_err, tmp, nof_pilots);
#endif // DMRS_PDCCH_SYNC_PRECOMPENSATE_MEAS
// Correlate DMRS
@ -536,10 +526,6 @@ int srsran_dmrs_pdcch_get_ce(const srsran_dmrs_pdcch_estimator_t* q,
}
uint32_t L = 1U << dci_location->L;
if (q->coreset.mapping_type == srsran_coreset_mapping_type_interleaved) {
ERROR("Error interleaved mapping not implemented");
return SRSRAN_ERROR;
}
// Check that CORESET duration is not less than minimum
if (q->coreset.duration < SRSRAN_CORESET_DURATION_MIN) {
@ -547,16 +533,30 @@ int srsran_dmrs_pdcch_get_ce(const srsran_dmrs_pdcch_estimator_t* q,
return SRSRAN_ERROR;
}
// Calculate begin and end sub-carrier index for the selected candidate
uint32_t k_begin = (dci_location->ncce * SRSRAN_NRE * 6) / q->coreset.duration;
uint32_t k_end = k_begin + (L * 6 * SRSRAN_NRE) / q->coreset.duration;
// Calculate CCE-to-REG mapping mask
bool rb_mask[SRSRAN_MAX_PRB_NR] = {};
if (srsran_pdcch_nr_cce_to_reg_mapping(&q->coreset, dci_location, rb_mask) < SRSRAN_SUCCESS) {
ERROR("Error in CCE-to-REG mapping");
return SRSRAN_SUCCESS;
}
// Extract CE for PDCCH
uint32_t count = 0;
// For each PDCCH symbol
for (uint32_t l = 0; l < q->coreset.duration; l++) {
for (uint32_t k = k_begin; k < k_end; k++) {
if (k % 4 != 1) {
ce->ce[count++] = q->ce[q->coreset_bw * SRSRAN_NRE * l + k];
// For each CORESET RB
for (uint32_t rb = 0; rb < q->coreset_bw; rb++) {
// Skip RB if unused
if (!rb_mask[rb]) {
continue;
}
// Copy RB, skipping DMRS
for (uint32_t k = rb * SRSRAN_NRE; k < (rb + 1) * SRSRAN_NRE; k++) {
if (k % 4 != 1) {
ce->ce[count++] = q->ce[q->coreset_bw * SRSRAN_NRE * l + k];
}
}
}
}

@ -99,7 +99,8 @@ add_nr_test(dmrs_pdsch_test dmrs_pdsch_test)
add_executable(dmrs_pdcch_test dmrs_pdcch_test.c)
target_link_libraries(dmrs_pdcch_test srsran_phy)
add_nr_test(dmrs_pdcch_test dmrs_pdcch_test)
add_nr_test(dmrs_pdcch_test_non_interleaved dmrs_pdcch_test)
add_nr_test(dmrs_pdcch_test_interleaved dmrs_pdcch_test -I)
########################################################################

@ -28,26 +28,25 @@
#include <strings.h>
#include <unistd.h>
static srsran_carrier_nr_t carrier = {};
static srsran_dmrs_pdcch_ce_t pdcch_ce = {};
static uint16_t rnti = 0x1234;
static srsran_carrier_nr_t carrier = {};
static srsran_dmrs_pdcch_ce_t pdcch_ce = {};
static uint16_t rnti = 0x1234;
static bool interleaved = false;
void usage(char* prog)
{
printf("Usage: %s [recov]\n", prog);
printf("Usage: %s [recoIv]\n", prog);
printf("\t-r nof_prb [Default %d]\n", carrier.nof_prb);
printf("\t-e extended cyclic prefix [Default normal]\n");
printf("\t-c cell_id [Default %d]\n", carrier.pci);
printf("\t-I Enable interleaved CCE-to-REG [Default %s]\n", interleaved ? "Enabled" : "Disabled");
printf("\t-v increase verbosity\n");
}
static void parse_args(int argc, char** argv)
{
int opt;
while ((opt = getopt(argc, argv, "recov")) != -1) {
while ((opt = getopt(argc, argv, "recoIv")) != -1) {
switch (opt) {
case 'r':
carrier.nof_prb = (uint32_t)strtol(argv[optind], NULL, 10);
@ -55,6 +54,9 @@ static void parse_args(int argc, char** argv)
case 'c':
carrier.pci = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'I':
interleaved ^= true;
break;
case 'v':
srsran_verbose++;
break;
@ -129,7 +131,18 @@ int main(int argc, char** argv)
parse_args(argc, argv);
srsran_coreset_t coreset = {};
srsran_coreset_t coreset = {};
if (interleaved) {
coreset.mapping_type = srsran_coreset_mapping_type_interleaved;
coreset.reg_bundle_size = srsran_coreset_bundle_size_n6;
coreset.interleaver_size = srsran_coreset_bundle_size_n2;
coreset.precoder_granularity = srsran_coreset_precoder_granularity_reg_bundle;
coreset.shift_index = carrier.pci;
carrier.nof_prb = 52;
carrier.pci = 500;
}
srsran_search_space_t search_space = {};
srsran_dmrs_pdcch_estimator_t estimator = {};
@ -139,8 +152,6 @@ int main(int argc, char** argv)
uint32_t test_counter = 0;
uint32_t test_passed = 0;
coreset.mapping_type = srsran_coreset_mapping_type_non_interleaved;
uint32_t nof_frequency_resource = SRSRAN_MIN(SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE, carrier.nof_prb / 6);
for (uint32_t frequency_resources = 1; frequency_resources < (1U << nof_frequency_resource); frequency_resources++) {
uint32_t nof_freq_resources = 0;
@ -150,7 +161,14 @@ int main(int argc, char** argv)
nof_freq_resources += mask;
}
for (coreset.duration = 1; coreset.duration <= 3; coreset.duration++) {
for (coreset.duration = SRSRAN_CORESET_DURATION_MIN; coreset.duration <= SRSRAN_CORESET_DURATION_MAX;
coreset.duration++) {
// Skip case if CORESET bandwidth is not enough
uint32_t N = srsran_coreset_get_bw(&coreset) * coreset.duration;
if (interleaved && N % 12 != 0) {
continue;
}
for (search_space.type = srsran_search_space_type_common_0; search_space.type <= srsran_search_space_type_ue;
search_space.type++) {
for (uint32_t i = 0; i < SRSRAN_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR; i++) {

@ -18,7 +18,7 @@
# and at http://www.gnu.org/licenses/.
#
set(SOURCES phy_common.c phy_common_sl.c phy_common_nr.c sequence.c timestamp.c zc_sequence.c)
set(SOURCES phy_common.c phy_common_sl.c phy_common_nr.c sequence.c timestamp.c zc_sequence.c sliv.c)
add_library(srsran_phy_common OBJECT ${SOURCES})
add_subdirectory(test)

@ -20,6 +20,7 @@
*/
#include "srsran/phy/common/phy_common_nr.h"
#include "srsran/phy/utils/debug.h"
#include "srsran/phy/utils/vector.h"
#include <stdlib.h>
#include <string.h>
@ -123,6 +124,26 @@ uint32_t srsran_coreset_get_sz(const srsran_coreset_t* coreset)
return srsran_coreset_get_bw(coreset) * SRSRAN_NRE * coreset->duration;
}
uint32_t srsran_coreset_start_rb(const srsran_coreset_t* coreset)
{
// Protect CORESET access
if (coreset == NULL) {
return 0;
}
// Iterates all the possible frequency resources trying to find the first enabled
for (uint32_t res = 0; res < SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE; res++) {
// If the frequency resource is enabled...
if (coreset->freq_resources[res]) {
// ... return the lowest resource block index
return 6 * res + coreset->offset_rb;
}
}
// Returns the start resource index
return 0;
}
const char* srsran_sch_mapping_type_to_str(srsran_sch_mapping_type_t mapping_type)
{
switch (mapping_type) {
@ -179,17 +200,31 @@ uint32_t srsran_min_symbol_sz_rb(uint32_t nof_prb)
return 0;
}
// Select all valid symbol sizes by default
const uint32_t* symbol_table = phy_common_nr_valid_symbol_sz;
// Select standard LTE symbol sizes table
if (srsran_symbol_size_is_standard()) {
symbol_table = phy_common_nr_valid_std_symbol_sz;
// As the selected symbol size is the minimum that fits the entire number of RE, 106 RB would select into 1536 point
// symbol size. However, it shall use 2048 point symbol size to match LTE standard rate. Because of this, it forces
// bandwidths bigger than 79 RB that would use 1536 symbol size to select 2048.
if (nof_prb > 79 && nof_prb < 1536 / SRSRAN_NRE) {
return 2048;
}
}
// For each symbol size in the table...
for (uint32_t i = 0; i < PHY_COMMON_NR_NOF_VALID_SYMB_SZ; i++) {
// Check if the number of RE fit in the symbol
if (symbol_table[i] > nof_re) {
// Returns the smallest symbol size that fits the number of RE
return symbol_table[i];
}
}
// The number of RE exceeds the maximum symbol size
return 0;
}
@ -331,12 +366,13 @@ uint32_t srsran_csi_meas_info(const srsran_csi_trs_measurements_t* meas, char* s
return srsran_print_check(str,
str_len,
0,
"rsrp=%+.1f epre=%+.1f n0=%+.1f snr=%+.1f cfo=%+.1f delay=%+.1f",
"rsrp=%+.1f epre=%+.1f n0=%+.1f snr=%+.1f cfo=%+.1f cfo_max=%.0f delay=%+.1f",
meas->rsrp_dB,
meas->epre_dB,
meas->n0_dB,
meas->snr_dB,
meas->cfo_hz,
meas->cfo_hz_max,
meas->delay_us);
}
@ -369,6 +405,25 @@ srsran_subcarrier_spacing_t srsran_subcarrier_spacing_from_str(const char* str)
return srsran_subcarrier_spacing_invalid;
}
const char* srsran_subcarrier_spacing_to_str(srsran_subcarrier_spacing_t scs)
{
switch (scs) {
case srsran_subcarrier_spacing_15kHz:
return "15kHz";
case srsran_subcarrier_spacing_30kHz:
return "30kHz";
case srsran_subcarrier_spacing_60kHz:
return "60kHz";
case srsran_subcarrier_spacing_120kHz:
return "120kHz";
case srsran_subcarrier_spacing_240kHz:
return "240kHz";
case srsran_subcarrier_spacing_invalid:
default:
return "invalid";
}
}
void srsran_combine_csi_trs_measurements(const srsran_csi_trs_measurements_t* a,
const srsran_csi_trs_measurements_t* b,
srsran_csi_trs_measurements_t* dst)
@ -398,3 +453,157 @@ void srsran_combine_csi_trs_measurements(const srsran_csi_trs_measurements_t* a,
dst->delay_us = SRSRAN_VEC_PMA(a->delay_us, a->nof_re, b->delay_us, b->nof_re);
dst->nof_re = nof_re_sum;
}
typedef struct {
uint32_t mux_pattern;
uint32_t nof_prb;
uint32_t nof_symb;
uint32_t offset_rb; ///< Defined by TS 36.213 section 13 UE procedure for monitoring Type0-PDCCH CSS sets:
///< Offset respect to the SCS of the CORESET for Type0-PDCCH CSS set, provided by
///< subCarrierSpacingCommon, from the smallest RB index of the CORESET for Type0-PDCCH CSS set
///< to the smallest RB index of the common RB overlapping with the first RB of the
///< corresponding SS/PBCH block.
} coreset_zero_entry_t;
static const coreset_zero_entry_t coreset_zero_15_15[16] = {
{1, 24, 2, 0},
{1, 24, 2, 2},
{1, 24, 2, 4},
{1, 24, 3, 0},
{1, 24, 3, 2},
{1, 24, 3, 4},
{1, 48, 1, 12},
{1, 48, 1, 16},
{1, 48, 2, 12},
{1, 48, 2, 16},
{1, 48, 3, 12},
{1, 48, 3, 16},
{1, 96, 1, 38},
{1, 96, 2, 38},
{1, 96, 3, 38},
{},
};
static const coreset_zero_entry_t coreset_zero_15_30[16] = {
{1, 24, 2, 5},
{1, 24, 2, 6},
{1, 24, 2, 7},
{1, 24, 2, 8},
{1, 24, 3, 5},
{1, 24, 3, 6},
{1, 24, 3, 7},
{1, 24, 3, 8},
{1, 48, 1, 18},
{1, 48, 1, 20},
{1, 48, 2, 18},
{1, 48, 2, 20},
{1, 48, 3, 18},
{1, 48, 3, 20},
{},
{},
};
static const coreset_zero_entry_t coreset_zero_30_15[16] = {
{1, 48, 1, 2},
{1, 48, 1, 6},
{1, 48, 2, 2},
{1, 48, 2, 6},
{1, 48, 3, 2},
{1, 48, 3, 6},
{1, 96, 1, 28},
{1, 96, 2, 28},
{1, 96, 3, 28},
{},
{},
{},
{},
{},
{},
{},
};
int srsran_coreset_zero(uint32_t n_cell_id,
uint32_t ssb_pointA_freq_offset_Hz,
srsran_subcarrier_spacing_t ssb_scs,
srsran_subcarrier_spacing_t pdcch_scs,
uint32_t idx,
srsran_coreset_t* coreset)
{
// Verify inputs
if (coreset == NULL || idx >= 16) {
ERROR("Invalid CORESET Zero inputs. coreset=%p, idx=%d", coreset, idx);
return SRSRAN_ERROR_INVALID_INPUTS;
}
// Default entry to NULL
const coreset_zero_entry_t* entry = NULL;
// Table 13-1: Set of resource blocks and slot symbols of CORESET for Type0-PDCCH search space set
// when {SS/PBCH block, PDCCH} SCS is {15, 15} kHz for frequency bands with minimum channel
// bandwidth 5 MHz or 10 MHz
if (ssb_scs == srsran_subcarrier_spacing_15kHz && pdcch_scs == srsran_subcarrier_spacing_15kHz) {
entry = &coreset_zero_15_15[idx];
}
// Table 13-2: Set of resource blocks and slot symbols of CORESET for Type0-PDCCH search space set
// when {SS/PBCH block, PDCCH} SCS is {15, 30} kHz for frequency bands with minimum channel
// bandwidth 5 MHz or 10 MHz
if (ssb_scs == srsran_subcarrier_spacing_15kHz && pdcch_scs == srsran_subcarrier_spacing_30kHz) {
entry = &coreset_zero_15_30[idx];
}
// Table 13-3: Set of resource blocks and slot symbols of CORESET for Type0-PDCCH search space set
// when {SS/PBCH block, PDCCH} SCS is {30, 15} kHz for frequency bands with minimum channel
// bandwidth 5 MHz or 10 MHz
if (ssb_scs == srsran_subcarrier_spacing_30kHz && pdcch_scs == srsran_subcarrier_spacing_15kHz) {
entry = &coreset_zero_30_15[idx];
}
// Check a valid entry has been selected
if (entry == NULL) {
ERROR("Unhandled case ssb_scs=%s, pdcch_scs=%s",
srsran_subcarrier_spacing_to_str(ssb_scs),
srsran_subcarrier_spacing_to_str(pdcch_scs));
return SRSRAN_ERROR;
}
if (entry->nof_prb == 0) {
ERROR("Reserved case ssb_scs=%s, pdcch_scs=%s, idx=%d",
srsran_subcarrier_spacing_to_str(ssb_scs),
srsran_subcarrier_spacing_to_str(pdcch_scs),
idx);
return SRSRAN_ERROR;
}
// Calculate CORESET offset in RB
uint32_t ssb_half_bw_Hz = SRSRAN_SUBC_SPACING_NR(ssb_scs) * (SRSRAN_SSB_BW_SUBC / 2U);
if (ssb_pointA_freq_offset_Hz > ssb_half_bw_Hz) {
// Move SSB center to lowest SSB subcarrier
ssb_pointA_freq_offset_Hz -= ssb_half_bw_Hz;
} else {
ssb_pointA_freq_offset_Hz = 0;
}
uint32_t ssb_pointA_freq_offset_rb =
SRSRAN_FLOOR(ssb_pointA_freq_offset_Hz, SRSRAN_NRE * SRSRAN_SUBC_SPACING_NR(pdcch_scs));
uint32_t offset_rb =
(ssb_pointA_freq_offset_rb > entry->offset_rb) ? (ssb_pointA_freq_offset_rb - entry->offset_rb) : 0;
// Set CORESET fields
coreset->id = 0;
coreset->dmrs_scrambling_id_present = false;
coreset->duration = entry->nof_symb;
coreset->offset_rb = offset_rb;
// Set CCE-to-REG mapping according to TS 38.211 section 7.3.2.2
coreset->mapping_type = srsran_coreset_mapping_type_interleaved;
coreset->reg_bundle_size = srsran_coreset_bundle_size_n6;
coreset->interleaver_size = srsran_coreset_bundle_size_n2;
coreset->precoder_granularity = srsran_coreset_precoder_granularity_reg_bundle;
coreset->shift_index = n_cell_id;
// Set CORESET frequency resource mask
for (uint32_t i = 0; i < SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE; i++) {
coreset->freq_resources[i] = (i < (entry->nof_prb / 6));
}
return SRSRAN_SUCCESS;
}

@ -0,0 +1,34 @@
/**
*
* \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 "srsran/phy/common/sliv.h"
void srsran_sliv_to_s_and_l(uint32_t N, uint32_t v, uint32_t* S, uint32_t* L)
{
uint32_t low = v % N;
uint32_t high = v / N;
if (high + 1 + low <= N) {
*S = low;
*L = high + 1;
} else {
*S = N - 1 - low;
*L = N - high + 1;
}
}
uint32_t srsran_sliv_from_s_and_l(uint32_t N, uint32_t S, uint32_t L)
{
if ((L - 1) <= N / 2) {
return N * (L - 1) + S;
}
return N * (N - L + 1) + (N - 1 - S);
}

@ -26,3 +26,14 @@ add_executable(sequence_test sequence_test.c)
target_link_libraries(sequence_test srsran_phy)
add_test(sequence_test sequence_test)
########################################################################
# SLIV TEST
########################################################################
add_executable(sliv_test sliv_test.c)
target_link_libraries(sliv_test srsran_phy)
add_test(sliv_test_14 sliv_test 14)
add_test(sliv_test_52 sliv_test 48)
add_test(sliv_test_52 sliv_test 52)

@ -0,0 +1,71 @@
/**
*
* \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 "srsran/common/test_common.h"
#include "srsran/phy/common/sliv.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
static uint32_t N = 48;
static int test()
{
for (uint32_t s = 0; s < N; s++) {
for (uint32_t l = 1; l < N - s; l++) {
uint32_t sliv = srsran_sliv_from_s_and_l(N, s, l);
uint32_t S = 0;
uint32_t L = 0;
srsran_sliv_to_s_and_l(N, sliv, &S, &L);
if (s != S || l != L) {
printf("s=%d; l=%d; SLIV=%d; Start: %d; Length: %d;\n", s, l, sliv, S, L);
return SRSRAN_ERROR;
}
}
}
return SRSRAN_SUCCESS;
}
int main(int argc, char** argv)
{
// Parse N
if (argc >= 2) {
N = (uint32_t)strtol(argv[1], NULL, 10);
}
// If two arguments, run brute force test
if (argc == 2) {
return test();
}
// if three arguments, calculate start and length from sliv
if (argc == 3) {
uint32_t sliv = (uint32_t)strtol(argv[2], NULL, 10);
uint32_t S = 0;
uint32_t L = 0;
srsran_sliv_to_s_and_l(N, sliv, &S, &L);
printf("SLIV=%d; Start: %d; Length: %d;\n", sliv, S, L);
return SRSRAN_SUCCESS;
}
// if four arguments, calculate sliv from start and length
if (argc == 4) {
uint32_t s = (uint32_t)strtol(argv[2], NULL, 10);
uint32_t l = (uint32_t)strtol(argv[3], NULL, 10);
uint32_t sliv = srsran_sliv_from_s_and_l(N, s, l);
printf("SLIV=%d; Start: %d; Length: %d;\n", sliv, s, l);
return SRSRAN_SUCCESS;
}
}

@ -1999,7 +1999,8 @@ int srsran_dci_nr_dl_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_t* msg,
}
// Copy DCI MSG fields
dci->ctx = msg->ctx;
dci->ctx = msg->ctx;
dci->coreset0_bw = q->cfg.coreset0_bw;
// Pack DCI
switch (msg->ctx.format) {

@ -313,33 +313,155 @@ int srsran_pdcch_nr_set_carrier(srsran_pdcch_nr_t* q,
return SRSRAN_SUCCESS;
}
static uint32_t pdcch_nr_bundle_size(srsran_coreset_bundle_size_t x)
{
switch (x) {
case srsran_coreset_bundle_size_n2:
return 2;
case srsran_coreset_bundle_size_n3:
return 3;
case srsran_coreset_bundle_size_n6:
return 6;
}
return 0;
}
static int pdcch_nr_cce_to_reg_mapping_non_interleaved(const srsran_coreset_t* coreset,
const srsran_dci_location_t* dci_location,
bool rb_mask[SRSRAN_MAX_PRB_NR])
{
uint32_t nof_cce = 1U << dci_location->L;
uint32_t L = 6;
uint32_t nof_reg_bundle = 6 / L;
// For each CCE j in the PDCCH transmission
for (uint32_t j = dci_location->ncce; j < dci_location->ncce + nof_cce; j++) {
// For each REG bundle i in the CCE j
for (uint32_t reg_bundle = 0; reg_bundle < nof_reg_bundle; reg_bundle++) {
// Calculate x variable
uint32_t x = (6 * j) / L + reg_bundle;
// For non interleaved f(x) = x
uint32_t i = x;
// For each REG in the REG bundle
uint32_t rb_start = (i * L) / coreset->duration;
uint32_t rb_end = ((i + 1) * L) / coreset->duration;
for (uint32_t rb = rb_start; rb < rb_end; rb++) {
rb_mask[rb] = true;
}
}
}
return SRSRAN_SUCCESS;
}
static int pdcch_nr_cce_to_reg_mapping_interleaved(const srsran_coreset_t* coreset,
const srsran_dci_location_t* dci_location,
bool rb_mask[SRSRAN_MAX_PRB_NR])
{
// Calculate CORESET constants
uint32_t N_CORESET_REG = coreset->duration * srsran_coreset_get_bw(coreset);
uint32_t L = pdcch_nr_bundle_size(coreset->reg_bundle_size);
uint32_t R = pdcch_nr_bundle_size(coreset->interleaver_size);
uint32_t C = N_CORESET_REG / (L * R);
uint32_t n_shift = coreset->shift_index;
// Validate
if (N_CORESET_REG == 0 || N_CORESET_REG % (L * R) != 0 || L % coreset->duration != 0) {
ERROR("Invalid CORESET configuration N=%d; L=%d; R=%d;", N_CORESET_REG, L, R);
return 0;
}
uint32_t nof_cce = 1U << dci_location->L;
uint32_t nof_reg_bundle = 6 / L;
// For each CCE j in the PDCCH transmission
for (uint32_t j = dci_location->ncce; j < dci_location->ncce + nof_cce; j++) {
// For each REG bundle i in the CCE j
for (uint32_t reg_bundle = 0; reg_bundle < nof_reg_bundle; reg_bundle++) {
// Calculate x variable
uint32_t x = (6 * j) / L + reg_bundle;
// For non interleaved f(x) = x
uint32_t r = x % R;
uint32_t c = x / R;
uint32_t i = (r * C + c + n_shift) % (N_CORESET_REG / L);
// For each REG in the REG bundle
uint32_t rb_start = (i * L) / coreset->duration;
uint32_t rb_end = ((i + 1) * L) / coreset->duration;
for (uint32_t rb = rb_start; rb < rb_end; rb++) {
rb_mask[rb] = true;
}
}
}
return SRSRAN_SUCCESS;
}
int srsran_pdcch_nr_cce_to_reg_mapping(const srsran_coreset_t* coreset,
const srsran_dci_location_t* dci_location,
bool rb_mask[SRSRAN_MAX_PRB_NR])
{
if (coreset == NULL || dci_location == NULL) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
// Non-interleaved case
if (coreset->mapping_type == srsran_coreset_mapping_type_non_interleaved) {
return pdcch_nr_cce_to_reg_mapping_non_interleaved(coreset, dci_location, rb_mask);
}
// Interleaved case
return pdcch_nr_cce_to_reg_mapping_interleaved(coreset, dci_location, rb_mask);
}
static uint32_t pdcch_nr_cp(const srsran_pdcch_nr_t* q,
const srsran_dci_location_t* dci_location,
cf_t* slot_grid,
cf_t* symbols,
bool put)
{
uint32_t L = 1U << dci_location->L;
uint32_t offset_k = q->coreset.offset_rb * SRSRAN_NRE;
// Calculate begin and end sub-carrier index for the selected candidate
uint32_t k_begin = (dci_location->ncce * SRSRAN_NRE * 6) / q->coreset.duration;
uint32_t k_end = k_begin + (L * 6 * SRSRAN_NRE) / q->coreset.duration;
// Compute REG list
bool rb_mask[SRSRAN_MAX_PRB_NR] = {};
if (srsran_pdcch_nr_cce_to_reg_mapping(&q->coreset, dci_location, rb_mask) < SRSRAN_SUCCESS) {
return 0;
}
uint32_t count = 0;
// Iterate over symbols
for (uint32_t l = 0; l < q->coreset.duration; l++) {
// Iterate over frequency resource groups
uint32_t k = 0;
uint32_t rb = 0;
for (uint32_t r = 0; r < SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE; r++) {
if (q->coreset.freq_resources[r]) {
for (uint32_t i = r * 6 * SRSRAN_NRE; i < (r + 1) * 6 * SRSRAN_NRE; i++, k++) {
if (k >= k_begin && k < k_end && k % 4 != 1) {
if (put) {
slot_grid[q->carrier.nof_prb * SRSRAN_NRE * l + i] = symbols[count++];
} else {
symbols[count++] = slot_grid[q->carrier.nof_prb * SRSRAN_NRE * l + i];
}
// Skip frequency resource if not set
if (!q->coreset.freq_resources[r]) {
continue;
}
// For each RB in the frequency resource
for (uint32_t i = r * 6; i < (r + 1) * 6; i++, rb++) {
// Skip if this RB is not marked as mapped
if (!rb_mask[rb]) {
continue;
}
// For each RE in the RB
for (uint32_t k = i * SRSRAN_NRE; k < (i + 1) * SRSRAN_NRE; k++) {
// Skip if it is a DMRS
if (k % 4 == 1) {
continue;
}
// Read or write in the grid
if (put) {
slot_grid[q->carrier.nof_prb * SRSRAN_NRE * l + k + offset_k] = symbols[count++];
} else {
symbols[count++] = slot_grid[q->carrier.nof_prb * SRSRAN_NRE * l + k + offset_k];
}
}
}

@ -124,7 +124,7 @@ int srsran_ra_dl_nr_time_default_A(uint32_t m, srsran_dmrs_sch_typeA_pos_t dmrs_
static void ra_dl_nr_time_hl(const srsran_sch_time_ra_t* hl_ra_cfg, srsran_sch_grant_nr_t* grant)
{
// Compute S and L from SLIV from higher layers
ra_helper_compute_s_and_l(SRSRAN_NSYMB_PER_SLOT_NR, hl_ra_cfg->sliv, &grant->S, &grant->L);
srsran_sliv_to_s_and_l(SRSRAN_NSYMB_PER_SLOT_NR, hl_ra_cfg->sliv, &grant->S, &grant->L);
grant->k = hl_ra_cfg->k;
grant->mapping = hl_ra_cfg->mapping_type;
@ -301,10 +301,26 @@ int srsran_ra_dl_nr_freq(const srsran_carrier_nr_t* carrier,
return SRSRAN_ERROR_INVALID_INPUTS;
}
// For a PDSCH scheduled with a DCI format 1_0 in any type of PDCCH common search space, regardless of which
// bandwidth part is the active bandwidth part, RB numbering starts from the lowest RB of the CORESET in which the
// DCI was received; otherwise RB numbering starts from the lowest RB in the determined downlink bandwidth part.
uint32_t start_rb = 0;
if (dci_dl->ctx.format == srsran_dci_format_nr_1_0 && SRSRAN_SEARCH_SPACE_IS_COMMON(dci_dl->ctx.ss_type)) {
start_rb = dci_dl->ctx.coreset_start_rb;
}
// when DCI format 1_0 is decoded in any common search space in which case the size of CORESET 0 shall be used if
// CORESET 0 is configured for the cell and the size of initial DL bandwidth part shall be used if CORESET 0 is not
// configured for the cell.
uint32_t type1_bwp_sz = carrier->nof_prb;
if (SRSRAN_SEARCH_SPACE_IS_COMMON(dci_dl->ctx.ss_type) && dci_dl->coreset0_bw != 0) {
type1_bwp_sz = dci_dl->coreset0_bw;
}
// The UE shall assume that when the scheduling grant is received with DCI format 1_0 , then downlink resource
// allocation type 1 is used.
if (dci_dl->ctx.format == srsran_dci_format_nr_1_0) {
return ra_helper_freq_type1(carrier->nof_prb, dci_dl->freq_domain_assigment, grant);
return ra_helper_freq_type1(type1_bwp_sz, start_rb, dci_dl->freq_domain_assigment, grant);
}
// If the scheduling DCI is configured to indicate the downlink resource allocation type as part of the Frequency
@ -318,7 +334,7 @@ int srsran_ra_dl_nr_freq(const srsran_carrier_nr_t* carrier,
// Otherwise the UE shall use the downlink frequency resource allocation type as defined by the higher layer parameter
// resourceAllocation.
if (cfg->alloc == srsran_resource_alloc_type1) {
return ra_helper_freq_type1(carrier->nof_prb, dci_dl->freq_domain_assigment, grant);
return ra_helper_freq_type1(type1_bwp_sz, start_rb, dci_dl->freq_domain_assigment, grant);
}
if (cfg->alloc == srsran_resource_alloc_type0) {
@ -331,5 +347,6 @@ int srsran_ra_dl_nr_freq(const srsran_carrier_nr_t* carrier,
uint32_t srsran_ra_nr_type1_riv(uint32_t N_prb, uint32_t start_rb, uint32_t length_rb)
{
return ra_helper_from_s_and_l(N_prb, start_rb, length_rb);
}
return srsran_sliv_from_s_and_l(N_prb, start_rb, length_rb);
}

@ -22,6 +22,7 @@
#ifndef SRSRAN_RA_HELPER_H
#define SRSRAN_RA_HELPER_H
#include "srsran/phy/common/sliv.h"
#include "srsran/phy/utils/debug.h"
#include "srsran/phy/utils/vector.h"
#include <stdint.h>
@ -69,38 +70,20 @@ static int ra_helper_freq_type0(const srsran_carrier_nr_t* carrier,
return 0;
}
static inline void ra_helper_compute_s_and_l(uint32_t N, uint32_t v, uint32_t* S, uint32_t* L)
{
uint32_t low = v % N;
uint32_t high = v / N;
if (high + 1 + low <= N) {
*S = low;
*L = high + 1;
} else {
*S = N - 1 - low;
*L = N - high + 1;
}
}
static inline uint32_t ra_helper_from_s_and_l(uint32_t N, uint32_t S, uint32_t L)
{
if ((L - 1) <= N / 2) {
return N * (L - 1) + S;
}
return N * (N - L + 1) + (N - 1 - S);
}
static int ra_helper_freq_type1(uint32_t N_bwp_size, uint32_t riv, srsran_sch_grant_nr_t* grant)
static int ra_helper_freq_type1(uint32_t N_bwp_size, uint32_t start_rb, uint32_t riv, srsran_sch_grant_nr_t* grant)
{
uint32_t start = 0;
uint32_t len = 0;
ra_helper_compute_s_and_l(N_bwp_size, riv, &start, &len);
srsran_sliv_to_s_and_l(N_bwp_size, riv, &start, &len);
if (start + len > N_bwp_size) {
ERROR("RIV 0x%x for BWP size %d resulted in freq=%d:%d", riv, N_bwp_size, start, len);
return SRSRAN_ERROR;
}
// Apply numbering start
start += start_rb;
for (uint32_t i = 0; i < start; i++) {
grant->prb_idx[i] = false;
}

@ -82,7 +82,7 @@ int srsran_ra_ul_nr_pusch_time_resource_default_A(uint32_t scs_cfg, uint32_t m,
static void ra_ul_nr_time_hl(const srsran_sch_time_ra_t* hl_ra_cfg, srsran_sch_grant_nr_t* grant)
{
// Compute S and L from SLIV from higher layers
ra_helper_compute_s_and_l(SRSRAN_NSYMB_PER_SLOT_NR, hl_ra_cfg->sliv, &grant->S, &grant->L);
srsran_sliv_to_s_and_l(SRSRAN_NSYMB_PER_SLOT_NR, hl_ra_cfg->sliv, &grant->S, &grant->L);
grant->k = hl_ra_cfg->k;
grant->mapping = hl_ra_cfg->mapping_type;
@ -449,13 +449,13 @@ int srsran_ra_ul_nr_freq(const srsran_carrier_nr_t* carrier,
// TS 38.213 PUSCH scheduled by RAR UL grant
if (dci_ul->ctx.format == srsran_dci_format_nr_rar) {
return ra_helper_freq_type1(carrier->nof_prb, dci_ul->freq_domain_assigment, grant);
return ra_helper_freq_type1(carrier->nof_prb, 0, dci_ul->freq_domain_assigment, grant);
}
// The UE shall assume that when the scheduling PDCCH is received with DCI format 0_0, then uplink resource
// allocation type 1 is used.
if (dci_ul->ctx.format == srsran_dci_format_nr_0_0) {
return ra_helper_freq_type1(carrier->nof_prb, dci_ul->freq_domain_assigment, grant);
return ra_helper_freq_type1(carrier->nof_prb, 0, dci_ul->freq_domain_assigment, grant);
}
// If the scheduling DCI is configured to indicate the uplink resource allocation type as part of the Frequency domain
@ -468,7 +468,7 @@ int srsran_ra_ul_nr_freq(const srsran_carrier_nr_t* carrier,
// Otherwise the UE shall use the uplink frequency resource allocation type as defined by the higher layer parameter
// resourceAllocation.
if (cfg->alloc == srsran_resource_alloc_type1) {
return ra_helper_freq_type1(carrier->nof_prb, dci_ul->freq_domain_assigment, grant);
return ra_helper_freq_type1(carrier->nof_prb, 0, dci_ul->freq_domain_assigment, grant);
}
if (cfg->alloc == srsran_resource_alloc_type0) {

@ -675,4 +675,5 @@ add_nr_test(pusch_nr_ack20_csi4_test pusch_nr_test -p 50 -m 20 -A 20 -C 4)
add_executable(pdcch_nr_test pdcch_nr_test.c)
target_link_libraries(pdcch_nr_test srsran_phy)
add_nr_test(pdcch_nr_test pdcch_nr_test)
add_nr_test(pdcch_nr_test_non_interleaved pdcch_nr_test)
add_nr_test(pdcch_nr_test_interleaved pdcch_nr_test -I)

@ -35,8 +35,9 @@ static srsran_carrier_nr_t carrier = {
1 // max_mimo_layers
};
static uint16_t rnti = 0x1234;
static bool fast_sweep = true;
static uint16_t rnti = 0x1234;
static bool fast_sweep = true;
static bool interleaved = false;
typedef struct {
uint64_t time_us;
@ -78,16 +79,17 @@ static int test(srsran_pdcch_nr_t* tx,
static void usage(char* prog)
{
printf("Usage: %s [pFv] \n", prog);
printf("Usage: %s [pFIv] \n", prog);
printf("\t-p Number of carrier PRB [Default %d]\n", carrier.nof_prb);
printf("\t-F Fast CORESET frequency resource sweeping [Default %s]\n", fast_sweep ? "Enabled" : "Disabled");
printf("\t-I Enable interleaved CCE-to-REG [Default %s]\n", interleaved ? "Enabled" : "Disabled");
printf("\t-v [set srsran_verbose to debug, default none]\n");
}
static int parse_args(int argc, char** argv)
{
int opt;
while ((opt = getopt(argc, argv, "pFv")) != -1) {
while ((opt = getopt(argc, argv, "pFIv")) != -1) {
switch (opt) {
case 'p':
carrier.nof_prb = (uint32_t)strtol(argv[optind], NULL, 10);
@ -95,6 +97,9 @@ static int parse_args(int argc, char** argv)
case 'F':
fast_sweep ^= true;
break;
case 'I':
interleaved ^= true;
break;
case 'v':
srsran_verbose++;
break;
@ -144,16 +149,34 @@ int main(int argc, char** argv)
goto clean_exit;
}
srsran_coreset_t coreset = {};
uint32_t nof_frequency_resource = SRSRAN_MIN(SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE, carrier.nof_prb / 6);
srsran_coreset_t coreset = {};
if (interleaved) {
coreset.mapping_type = srsran_coreset_mapping_type_interleaved;
coreset.reg_bundle_size = srsran_coreset_bundle_size_n6;
coreset.interleaver_size = srsran_coreset_bundle_size_n2;
coreset.precoder_granularity = srsran_coreset_precoder_granularity_reg_bundle;
coreset.shift_index = carrier.pci;
carrier.nof_prb = 52;
carrier.pci = 500;
}
uint32_t nof_frequency_resource = SRSRAN_MIN(SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE, carrier.nof_prb / 6);
for (uint32_t frequency_resources = 1; frequency_resources < (1U << nof_frequency_resource);
frequency_resources = (fast_sweep) ? ((frequency_resources << 1U) | 1U) : (frequency_resources + 1)) {
for (uint32_t i = 0; i < nof_frequency_resource; i++) {
uint32_t mask = ((frequency_resources >> i) & 1U);
coreset.freq_resources[i] = (mask == 1);
}
for (coreset.duration = SRSRAN_CORESET_DURATION_MIN; coreset.duration <= SRSRAN_CORESET_DURATION_MAX;
coreset.duration++) {
// Skip case if CORESET bandwidth is not enough
uint32_t N = srsran_coreset_get_bw(&coreset) * coreset.duration;
if (interleaved && N % 12 != 0) {
continue;
}
srsran_search_space_t search_space = {};
search_space.type = srsran_search_space_type_ue;
search_space.formats[search_space.nof_formats++] = srsran_dci_format_nr_0_0;

@ -396,6 +396,7 @@ static int ue_dl_nr_find_dci_ss(srsran_ue_dl_nr_t* q,
ctx.location.ncce = candidates[ncce_idx];
ctx.ss_type = search_space->type;
ctx.coreset_id = search_space->coreset_id;
ctx.coreset_start_rb = srsran_coreset_start_rb(&q->cfg.coreset[search_space->coreset_id]);
ctx.rnti_type = rnti_type;
ctx.rnti = rnti;
ctx.format = dci_format;

@ -104,6 +104,15 @@ int srsran_ue_ul_nr_set_carrier(srsran_ue_ul_nr_t* q, const srsran_carrier_nr_t*
return SRSRAN_SUCCESS;
}
void srsran_ue_ul_nr_set_freq_offset(srsran_ue_ul_nr_t* q, float freq_offset_hz)
{
if (q == NULL) {
return;
}
q->freq_offset_hz = freq_offset_hz;
}
int srsran_ue_ul_nr_encode_pusch(srsran_ue_ul_nr_t* q,
const srsran_slot_cfg_t* slot_cfg,
const srsran_sch_cfg_nr_t* pusch_cfg,
@ -139,6 +148,12 @@ int srsran_ue_ul_nr_encode_pusch(srsran_ue_ul_nr_t* q,
srsran_vec_sc_prod_cfc(q->ifft.cfg.out_buffer, 0.99f / max_peak, q->ifft.cfg.out_buffer, q->ifft.sf_sz);
}
// Apply frequency offset
if (isnormal(q->freq_offset_hz)) {
srsran_vec_apply_cfo(
q->ifft.cfg.out_buffer, -q->freq_offset_hz / (1000.0f * q->ifft.sf_sz), q->ifft.cfg.out_buffer, q->ifft.sf_sz);
}
return SRSRAN_SUCCESS;
}
@ -228,6 +243,19 @@ int srsran_ue_ul_nr_encode_pucch(srsran_ue_ul_nr_t* q,
// Generate signal
srsran_ofdm_tx_sf(&q->ifft);
// Normalise to peak
uint32_t max_idx = srsran_vec_max_abs_ci(q->ifft.cfg.out_buffer, q->ifft.sf_sz);
float max_peak = cabsf(q->ifft.cfg.out_buffer[max_idx]);
if (isnormal(max_peak)) {
srsran_vec_sc_prod_cfc(q->ifft.cfg.out_buffer, 0.99f / max_peak, q->ifft.cfg.out_buffer, q->ifft.sf_sz);
}
// Apply frequency offset
if (isnormal(q->freq_offset_hz)) {
srsran_vec_apply_cfo(
q->ifft.cfg.out_buffer, -q->freq_offset_hz / (1000.0f * q->ifft.sf_sz), q->ifft.cfg.out_buffer, q->ifft.sf_sz);
}
return SRSRAN_SUCCESS;
}
@ -258,6 +286,8 @@ int srsran_ue_ul_nr_pusch_info(const srsran_ue_ul_nr_t* q,
// Append PDSCH info
len += srsran_pusch_nr_tx_info(&q->pusch, cfg, &cfg->grant, uci_value, &str[len], str_len - len);
len = srsran_print_check(str, str_len, len, " cfo=%.0f", q->freq_offset_hz);
return len;
}

@ -66,11 +66,25 @@ add_lte_test(pucch_ca_test pucch_ca_test)
add_executable(phy_dl_nr_test phy_dl_nr_test.c)
target_link_libraries(phy_dl_nr_test srsran_phy srsran_common srsran_phy ${SEC_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
add_nr_test(phy_dl_nr_test phy_dl_nr_test -p 100 -m 28 )
add_nr_test(phy_dl_nr_test_rvd phy_dl_nr_test -P 52 -p 52 -m 0
-R 0 52 1 010010010010 00000000010000
-R 0 52 1 100100100100 00000010000000)
add_nr_test(phy_dl_nr_test_cfo_delay phy_dl_nr_test -P 52 -p 52 -m 27 -C 100.0 -D 4 -n 10)
add_nr_test(phy_dl_nr_test_52prb phy_dl_nr_test -P 52 -p 52 -m 27 -T 256qam -d 1 1 -n 10)
add_nr_test(phy_dl_nr_test_270prb phy_dl_nr_test -P 270 -p 270 -m 27 -T 256qam -d 1 1 -n 10)
# Create a test case for each possible bandwidth described in TS 38.104 Table 5.3.2-1: Transmission bandwidth
# configuration N RB for FR1
foreach(rb 25 52 79 106 133 160 216 270)
# Basic test with 25 RB grant, maximum MCS, 64QAM
add_nr_test(phy_dl_nr_test_${rb}prb phy_dl_nr_test -P ${rb} -p 25 -m 28)
# Full BW grant, minimum MCS, with reserved RE
add_nr_test(phy_dl_nr_test_${rb}prb_rvd phy_dl_nr_test -P ${rb} -p ${rb} -m 0
-R 0 ${rb} 1 010010010010 00000000010000
-R 0 ${rb} 1 100100100100 00000010000000)
# 25 RB grant with interleaved PDCCH
add_nr_test(phy_dl_nr_test_${rb}prb_interleaved phy_dl_nr_test -P ${rb} -p 25 -m 10 -I)
# Maximum throughput with 256QAM
add_nr_test(phy_dl_nr_test_${rb}prb_256qam phy_dl_nr_test -P ${rb} -p ${rb} -m 27 -T 256qam -v -d 1 1 -n 10)
# Maximum throughput with 64QAM and CFO+Delay impairments
add_nr_test(phy_dl_nr_test_${rb}prb_cfo_delay phy_dl_nr_test -P ${rb} -p ${rb} -m 27 -C 100.0 -D 4 -n 10)
endforeach()

@ -39,19 +39,20 @@ static srsran_carrier_nr_t carrier = {
1 // max_mimo_layers
};
static uint32_t n_prb = 0; // Set to 0 for steering
static uint32_t mcs = 30; // Set to 30 for steering
static srsran_sch_cfg_nr_t pdsch_cfg = {};
static uint32_t nof_slots = 10;
static uint32_t rv_idx = 0;
static uint32_t delay_n = 0; // Integer delay
static float cfo_hz = 0.0f; // CFO Hz
static srsran_dmrs_sch_type_t dmrs_type = srsran_dmrs_sch_type_1;
static srsran_dmrs_sch_add_pos_t dmrs_add_pos = srsran_dmrs_sch_add_pos_2;
static uint32_t n_prb = 0; // Set to 0 for steering
static uint32_t mcs = 30; // Set to 30 for steering
static srsran_sch_cfg_nr_t pdsch_cfg = {};
static uint32_t nof_slots = 10;
static uint32_t rv_idx = 0;
static uint32_t delay_n = 0; // Integer delay
static float cfo_hz = 0.0f; // CFO Hz
static srsran_dmrs_sch_type_t dmrs_type = srsran_dmrs_sch_type_1;
static srsran_dmrs_sch_add_pos_t dmrs_add_pos = srsran_dmrs_sch_add_pos_2;
static bool interleaved_pdcch = false;
static void usage(char* prog)
{
printf("Usage: %s [rRPdpmnTLDCv] \n", prog);
printf("Usage: %s [rRPdpmnTILDCv] \n", prog);
printf("\t-P Number of BWP (Carrier) PRB [Default %d]\n", carrier.nof_prb);
printf("\t-p Number of grant PRB, set to 0 for steering [Default %d]\n", n_prb);
printf("\t-n Number of slots to simulate [Default %d]\n", nof_slots);
@ -61,6 +62,7 @@ static void usage(char* prog)
printf("\t-T Provide MCS table (64qam, 256qam, 64qamLowSE) [Default %s]\n",
srsran_mcs_table_to_str(pdsch_cfg.sch_cfg.mcs_table));
printf("\t-R Reserve RE: [rb_begin] [rb_end] [rb_stride] [sc_mask] [symbol_mask]\n");
printf("\t-I Enable interleaved CCE-to-REG [Default %s]\n", interleaved_pdcch ? "Enabled" : "Disabled");
printf("\t-L Provide number of layers [Default %d]\n", carrier.max_mimo_layers);
printf("\t-D Delay signal an integer number of samples [Default %d samples]\n", delay_n);
printf("\t-C Frequency shift (CFO) signal in Hz [Default %+.0f Hz]\n", cfo_hz);
@ -70,7 +72,7 @@ static void usage(char* prog)
static int parse_args(int argc, char** argv)
{
int opt;
while ((opt = getopt(argc, argv, "rRPdpmnTLDCv")) != -1) {
while ((opt = getopt(argc, argv, "rRIPdpmnTLDCv")) != -1) {
switch (opt) {
case 'P':
carrier.nof_prb = (uint32_t)strtol(argv[optind], NULL, 10);
@ -132,6 +134,9 @@ static int parse_args(int argc, char** argv)
return SRSRAN_ERROR;
}
} break;
case 'I':
interleaved_pdcch ^= true;
break;
case 'L':
carrier.max_mimo_layers = (uint32_t)strtol(argv[optind], NULL, 10);
break;
@ -172,6 +177,7 @@ static int work_gnb_dl(srsran_gnb_dl_t* gnb_dl,
dci_dl.ctx.location = *dci_location;
dci_dl.ctx.ss_type = search_space->type;
dci_dl.ctx.coreset_id = 1;
dci_dl.ctx.coreset_start_rb = 0;
dci_dl.freq_domain_assigment = 0;
dci_dl.time_domain_assigment = 0;
dci_dl.vrb_to_prb_mapping = 0;
@ -272,8 +278,20 @@ int main(int argc, char** argv)
srsran_coreset_t* coreset = &pdcch_cfg.coreset[1];
pdcch_cfg.coreset_present[1] = true;
coreset->duration = 1;
uint32_t coreset_bw_rb = carrier.nof_prb;
if (interleaved_pdcch) {
coreset->mapping_type = srsran_coreset_mapping_type_interleaved;
coreset->reg_bundle_size = srsran_coreset_bundle_size_n6;
coreset->interleaver_size = srsran_coreset_bundle_size_n2;
coreset->precoder_granularity = srsran_coreset_precoder_granularity_reg_bundle;
coreset->shift_index = carrier.pci;
coreset_bw_rb = SRSRAN_FLOOR(carrier.nof_prb, 12) * 12;
}
for (uint32_t i = 0; i < SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE; i++) {
coreset->freq_resources[i] = i < carrier.nof_prb / 6;
coreset->freq_resources[i] = i < coreset_bw_rb / 6;
}
// Configure Search Space

@ -40,6 +40,7 @@
#include "srsenb/hdr/stack/enb_stack_base.h"
#include "srsenb/hdr/stack/rrc/rrc_config.h"
#include "srsenb/hdr/stack/mac/sched_interface.h"
#include "srsran/common/bcd_helpers.h"
#include "srsran/common/buffer_pool.h"
#include "srsran/common/interfaces_common.h"
@ -47,7 +48,6 @@
#include "srsran/common/security.h"
#include "srsran/interfaces/enb_command_interface.h"
#include "srsran/interfaces/enb_metrics_interface.h"
#include "srsran/interfaces/sched_interface.h"
#include "srsran/interfaces/ue_interfaces.h"
#include "srsran/srslog/srslog.h"
#include "srsran/system/sys_metrics_processor.h"

@ -38,15 +38,19 @@
#include "upper/rlc.h"
#include "enb_stack_base.h"
#include "srsran/common/bearer_manager.h"
#include "srsran/common/mac_pcap_net.h"
#include "srsran/interfaces/enb_interfaces.h"
#include "srsran/srslog/srslog.h"
namespace srsenb {
class gtpu_pdcp_adapter;
class enb_stack_lte final : public enb_stack_base,
public stack_interface_phy_lte,
public stack_interface_phy_nr,
public stack_interface_rrc,
public srsran::thread
{
public:
@ -141,6 +145,11 @@ public:
}
void rach_detected(const rach_info_t& rach_info) override { mac_nr.rach_detected(rach_info); }
// interface for bearer manager
void add_eps_bearer(uint16_t rnti, uint8_t eps_bearer_id, srsran::srsran_rat_t rat, uint32_t lcid) override;
void remove_eps_bearer(uint16_t rnti, uint8_t eps_bearer_id) override;
void reset_eps_bearers(uint16_t rnti) override;
private:
static const int STACK_MAIN_THREAD_PRIO = 4;
// thread loop
@ -175,6 +184,10 @@ private:
srsran::task_scheduler task_sched;
srsran::task_queue_handle enb_task_queue, sync_task_queue, metrics_task_queue;
// bearer management
srsran::bearer_manager bearers; // helper to manage mapping between EPS and radio bearers
std::unique_ptr<gtpu_pdcp_adapter> gtpu_adapter;
srsenb::mac mac;
srsenb::rlc rlc;
srsenb::pdcp pdcp;
@ -189,7 +202,7 @@ private:
srsenb::rrc_nr rrc_nr;
// RAT-specific interfaces
phy_interface_stack_lte* phy = nullptr;
phy_interface_stack_lte* phy = nullptr;
phy_interface_stack_nr* phy_nr = nullptr;
// state

@ -0,0 +1,59 @@
/**
*
* \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_SCHED_CONFIG_H
#define SRSRAN_SCHED_CONFIG_H
#include <cstdint>
namespace srsenb {
/**
* Structure used in UE logical channel configuration
*/
struct mac_lc_ch_cfg_t {
enum direction_t { IDLE = 0, UL, DL, BOTH } direction = IDLE;
int priority = 1; // channel priority (1 is highest)
uint32_t bsd = 1000; // msec
uint32_t pbr = -1; // prioritised bit rate
int group = 0; // logical channel group
bool is_active() const { return direction != IDLE; }
bool is_dl() const { return direction == DL or direction == BOTH; }
bool is_ul() const { return direction == UL or direction == BOTH; }
bool operator==(const mac_lc_ch_cfg_t& other) const
{
return direction == other.direction and priority == other.priority and bsd == other.bsd and pbr == other.pbr and
group == other.group;
}
bool operator!=(const mac_lc_ch_cfg_t& other) const { return not(*this == other); }
};
inline const char* to_string(mac_lc_ch_cfg_t::direction_t dir)
{
switch (dir) {
case mac_lc_ch_cfg_t::direction_t::IDLE:
return "idle";
case mac_lc_ch_cfg_t::direction_t::BOTH:
return "bi-dir";
case mac_lc_ch_cfg_t::direction_t::DL:
return "DL";
case mac_lc_ch_cfg_t::direction_t::UL:
return "UL";
default:
return "unrecognized direction";
}
}
} // namespace srsenb
#endif // SRSRAN_SCHED_CONFIG_H

@ -0,0 +1,96 @@
/**
*
* \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_UE_BUFFER_MANAGER_H
#define SRSRAN_UE_BUFFER_MANAGER_H
#include "sched_config.h"
#include "srsran/common/common_lte.h"
#include "srsran/common/common_nr.h"
#include "srsran/srslog/srslog.h"
#include "srsran/support/srsran_assert.h"
namespace srsenb {
/**
* Class to handle UE DL+UL RLC and MAC buffers state
*/
template <bool isNR>
class ue_buffer_manager
{
protected:
const static uint32_t MAX_LC_ID = isNR ? srsran::MAX_NR_NOF_BEARERS : srsran::MAX_LTE_LCID;
const static uint32_t MAX_LCG_ID = isNR ? 7 : 3;
const static uint32_t MAX_SRB_LC_ID = isNR ? srsran::MAX_NR_SRB_ID : srsran::MAX_LTE_SRB_ID;
const static uint32_t MAX_NOF_LCIDS = MAX_LC_ID + 1;
const static uint32_t MAX_NOF_LCGS = MAX_LCG_ID + 1;
constexpr static uint32_t pbr_infinity = -1;
public:
explicit ue_buffer_manager(srslog::basic_logger& logger_);
// Bearer configuration
void config_lcid(uint32_t lcid, const mac_lc_ch_cfg_t& bearer_cfg);
// Buffer Status update
void ul_bsr(uint32_t lcg_id, uint32_t val);
void dl_buffer_state(uint8_t lcid, uint32_t tx_queue, uint32_t retx_queue);
// Configuration getters
bool is_bearer_active(uint32_t lcid) const { return get_cfg(lcid).is_active(); }
bool is_bearer_ul(uint32_t lcid) const { return get_cfg(lcid).is_ul(); }
bool is_bearer_dl(uint32_t lcid) const { return get_cfg(lcid).is_dl(); }
const mac_lc_ch_cfg_t& get_cfg(uint32_t lcid) const
{
srsran_assert(is_lcid_valid(lcid), "Provided LCID=%d is above limit=%d", lcid, MAX_LC_ID);
return channels[lcid].cfg;
}
/// DL newtx buffer status for given LCID (no RLC overhead included)
int get_dl_tx(uint32_t lcid) const { return is_bearer_dl(lcid) ? channels[lcid].buf_tx : 0; }
/// DL retx buffer status for given LCID (no RLC overhead included)
int get_dl_retx(uint32_t lcid) const { return is_bearer_dl(lcid) ? channels[lcid].buf_retx : 0; }
/// Sum of DL RLC newtx and retx buffer status for given LCID (no RLC overhead included)
int get_dl_tx_total(uint32_t lcid) const { return get_dl_tx(lcid) + get_dl_retx(lcid); }
/// Sum of DL RLC newtx and retx buffer status for all LCIDS
int get_dl_tx_total() const;
// UL BSR methods
bool is_lcg_active(uint32_t lcg) const;
int get_bsr(uint32_t lcg) const;
int get_bsr() const;
const std::array<int, MAX_NOF_LCGS>& get_bsr_state() const { return lcg_bsr; }
static bool is_lcid_valid(uint32_t lcid) { return lcid <= MAX_LC_ID; }
static bool is_lcg_valid(uint32_t lcg) { return lcg <= MAX_LCG_ID; }
protected:
srslog::basic_logger& logger;
struct logical_channel {
mac_lc_ch_cfg_t cfg;
int buf_tx = 0;
int buf_retx = 0;
int Bj = 0;
int bucket_size = 0;
};
std::array<logical_channel, MAX_NOF_LCIDS> channels;
std::array<int, MAX_NOF_LCGS> lcg_bsr;
};
} // namespace srsenb
#endif // SRSRAN_UE_BUFFER_MANAGER_H

@ -23,6 +23,7 @@
#define SRSENB_MAC_H
#include "sched.h"
#include "sched_interface.h"
#include "srsenb/hdr/common/rnti_pool.h"
#include "srsenb/hdr/stack/mac/schedulers/sched_time_rr.h"
#include "srsran/adt/circular_map.h"
@ -35,7 +36,6 @@
#include "srsran/interfaces/enb_mac_interfaces.h"
#include "srsran/interfaces/enb_metrics_interface.h"
#include "srsran/interfaces/enb_rrc_interface_types.h"
#include "srsran/interfaces/sched_interface.h"
#include "srsran/srslog/srslog.h"
#include "ta.h"
#include "ue.h"
@ -95,7 +95,7 @@ public:
void phy_config_enabled(uint16_t rnti, bool enabled) override;
/* Manages UE bearers and associated configuration */
int bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, sched_interface::ue_bearer_cfg_t* cfg) override;
int bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, mac_lc_ch_cfg_t* cfg) override;
int bearer_ue_rem(uint16_t rnti, uint32_t lc_id) override;
int rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue) override;
@ -148,7 +148,7 @@ private:
/* Map of active UEs */
static const uint16_t FIRST_RNTI = 0x46;
rnti_map_t<unique_rnti_ptr<ue> > ue_db;
std::atomic<uint16_t> ue_counter;
std::atomic<uint16_t> ue_counter{0};
uint8_t* assemble_rar(sched_interface::dl_sched_rar_grant_t* grants,
uint32_t enb_cc_idx,

@ -32,21 +32,21 @@
#include "srsran/common/task_scheduler.h"
#include "srsran/interfaces/enb_metrics_interface.h"
#include "srsran/interfaces/enb_rlc_interfaces.h"
#include "srsran/interfaces/gnb_interfaces.h"
#include "srsran/interfaces/gnb_mac_interfaces.h"
namespace srsenb {
struct mac_nr_args_t {
srsran::phy_cfg_nr_t phy_base_cfg = {};
int fixed_dl_mcs = -1;
int fixed_ul_mcs = -1;
srsenb::pcap_args_t pcap;
int fixed_dl_mcs = -1;
int fixed_ul_mcs = -1;
srsenb::pcap_args_t pcap;
};
class mac_nr final : public mac_interface_phy_nr, public mac_interface_rrc_nr, public mac_interface_rlc_nr
{
public:
mac_nr(srsran::task_sched_handle task_sched_);
mac_nr(srsran::task_sched_handle task_sched_, const srsenb::sched_nr_interface::sched_cfg_t& sched_cfg = {});
~mac_nr();
int init(const mac_nr_args_t& args_,
@ -59,9 +59,11 @@ public:
void get_metrics(srsenb::mac_metrics_t& metrics);
// MAC interface for RRC
int cell_cfg(srsenb::sched_interface::cell_cfg_t* cell_cfg) override;
uint16_t reserve_rnti() override;
int read_pdu_bcch_bch(uint8_t* payload);
int cell_cfg(const sched_interface::cell_cfg_t& cell,
srsran::const_span<sched_nr_interface::cell_cfg_t> nr_cells) override;
uint16_t reserve_rnti(uint32_t enb_cc_idx) override;
int read_pdu_bcch_bch(uint8_t* payload);
int ue_cfg(uint16_t rnti, const sched_nr_interface::ue_cfg_t& ue_cfg) override;
// MAC interface for RLC
// TODO:
@ -77,7 +79,8 @@ public:
void rach_detected(const rach_info_t& rach_info) override;
private:
uint16_t add_ue(uint32_t enb_cc_idx);
uint16_t add_ue_(uint32_t enb_cc_idx);
uint16_t alloc_ue(uint32_t enb_cc_idx);
int remove_ue(uint16_t rnti);
// internal misc helpers
@ -90,6 +93,9 @@ private:
// PDU processing
int handle_pdu(srsran::unique_byte_buffer_t pdu);
// Encoding
srsran::byte_buffer_t* assemble_rar(srsran::const_span<sched_nr_interface::sched_rar_grant_t> grants);
// Interaction with other components
phy_interface_stack_nr* phy = nullptr;
stack_interface_mac* stack = nullptr;
@ -97,7 +103,7 @@ private:
rrc_interface_mac_nr* rrc = nullptr;
// args
srsran::task_sched_handle task_sched;
srsran::task_sched_handle task_sched;
srsran::task_multiqueue::queue_handle stack_task_queue;
std::unique_ptr<srsran::mac_pcap> pcap = nullptr;
@ -106,17 +112,17 @@ private:
std::atomic<bool> started = {false};
const static uint32_t NUMEROLOGY_IDX = 0; /// only 15kHz supported at this stage
const static uint32_t NUMEROLOGY_IDX = 0; /// only 15kHz supported at this stage
srsran::slot_point pdsch_slot, pusch_slot;
srsenb::sched_nr sched;
srsenb::sched_interface::cell_cfg_t cfg = {};
// Map of active UEs
pthread_rwlock_t rwlock = {};
static const uint16_t FIRST_RNTI = 0x4601;
pthread_rwlock_t rwlock = {};
static const uint16_t FIRST_RNTI = 0x4601;
srsran::static_circular_map<uint16_t, std::unique_ptr<ue_nr>, SRSENB_MAX_UES> ue_db;
std::atomic<uint16_t> ue_counter;
std::atomic<uint16_t> ue_counter{0};
// BCH buffers
struct sib_info_t {

@ -40,7 +40,7 @@ class serv_cell_manager;
} // namespace sched_nr_impl
class ue_event_manager;
class sched_result_manager;
class ul_sched_result_buffer;
class sched_nr final : public sched_nr_interface
{
@ -55,12 +55,13 @@ public:
void dl_ack_info(uint16_t rnti, uint32_t cc, uint32_t pid, uint32_t tb_idx, bool ack) override;
void ul_crc_info(uint16_t rnti, uint32_t cc, uint32_t pid, bool crc) override;
void ul_sr_info(slot_point slot_rx, uint16_t rnti) override;
void ul_bsr(uint16_t rnti, uint32_t lcg_id, uint32_t bsr);
void dl_buffer_state(uint16_t rnti, uint32_t lcid, uint32_t newtx, uint32_t retx);
int get_dl_sched(slot_point pdsch_tti, uint32_t cc, dl_sched_t& result) override;
int get_dl_sched(slot_point pdsch_tti, uint32_t cc, dl_sched_res_t& result) override;
int get_ul_sched(slot_point pusch_tti, uint32_t cc, ul_sched_t& result) override;
private:
int generate_slot_result(slot_point pdcch_tti, uint32_t cc);
void ue_cfg_impl(uint16_t rnti, const ue_cfg_t& cfg);
// args
@ -75,7 +76,7 @@ private:
ue_map_t ue_db;
// management of Sched Result buffering
std::unique_ptr<sched_result_manager> pending_results;
std::unique_ptr<ul_sched_result_buffer> pending_results;
// management of cell resources
std::vector<std::unique_ptr<sched_nr_impl::serv_cell_manager> > cells;

@ -62,8 +62,13 @@ class ra_sched
public:
explicit ra_sched(const bwp_params& bwp_cfg_);
int dl_rach_info(const dl_sched_rar_info_t& rar_info);
void run_slot(bwp_slot_allocator& slot_grid, slot_ue_map_t& slot_ues);
/// Addition of detected PRACH into the queue
int dl_rach_info(const dl_sched_rar_info_t& rar_info);
/// Allocate pending RARs
void run_slot(bwp_slot_allocator& slot_alloc);
/// Check if there are pending RARs
bool empty() const { return pending_rars.empty(); }
private:
@ -74,10 +79,8 @@ private:
srsran::bounded_vector<dl_sched_rar_info_t, sched_interface::MAX_RAR_LIST> msg3_grant;
};
alloc_result allocate_pending_rar(bwp_slot_allocator& slot_grid,
const pending_rar_t& rar,
slot_ue_map_t& slot_ues,
uint32_t& nof_grants_alloc);
alloc_result
allocate_pending_rar(bwp_slot_allocator& slot_grid, const pending_rar_t& rar, uint32_t& nof_grants_alloc);
const bwp_params* bwp_cfg = nullptr;
srslog::basic_logger& logger;

@ -36,10 +36,14 @@ namespace sched_nr_impl {
const static size_t MAX_GRANTS = sched_nr_interface::MAX_GRANTS;
using pucch_t = mac_interface_phy_nr::pucch_t;
using pucch_list_t = srsran::bounded_vector<pucch_t, MAX_GRANTS>;
using pusch_t = mac_interface_phy_nr::pusch_t;
using pusch_list_t = srsran::bounded_vector<pusch_t, MAX_GRANTS>;
using pdcch_dl_t = mac_interface_phy_nr::pdcch_dl_t;
using pdcch_ul_t = mac_interface_phy_nr::pdcch_ul_t;
using pdcch_dl_list_t = srsran::bounded_vector<pdcch_dl_t, MAX_GRANTS>;
using pdcch_ul_list_t = srsran::bounded_vector<pdcch_ul_t, MAX_GRANTS>;
using pucch_t = mac_interface_phy_nr::pucch_t;
using pucch_list_t = srsran::bounded_vector<pucch_t, MAX_GRANTS>;
using pusch_t = mac_interface_phy_nr::pusch_t;
using pusch_list_t = srsran::bounded_vector<pusch_t, MAX_GRANTS>;
using sched_cfg_t = sched_nr_interface::sched_cfg_t;
using cell_cfg_t = sched_nr_interface::cell_cfg_t;
@ -62,8 +66,9 @@ struct bwp_params {
const sched_cfg_t& sched_cfg;
// derived params
uint32_t P;
uint32_t N_rbg;
srslog::basic_logger& logger;
uint32_t P;
uint32_t N_rbg;
struct slot_cfg {
bool is_dl;
@ -79,9 +84,9 @@ struct bwp_params {
};
std::vector<pusch_ra_time_cfg> pusch_ra_list;
bwp_params(const cell_cfg_t& cell, const sched_cfg_t& sched_cfg_, uint32_t cc, uint32_t bwp_id);
bwp_cce_pos_list rar_cce_list;
bwp_params(const cell_cfg_t& cell, const sched_cfg_t& sched_cfg_, uint32_t cc, uint32_t bwp_id);
};
struct sched_cell_params {
@ -167,48 +172,6 @@ public:
std::vector<cc_params> cc_params;
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct resource_guard {
public:
resource_guard() = default;
resource_guard(const resource_guard& other) = delete;
resource_guard(resource_guard&& other) = delete;
resource_guard& operator=(const resource_guard& other) = delete;
resource_guard& operator=(resource_guard&& other) = delete;
bool busy() const { return flag; }
struct token {
token() = default;
token(resource_guard& parent) : flag(parent.busy() ? nullptr : &parent.flag)
{
if (flag != nullptr) {
*flag = true;
}
}
token(token&&) noexcept = default;
token& operator=(token&&) noexcept = default;
void release() { flag.reset(); }
bool owns_token() const { return flag != nullptr; }
bool empty() const { return flag == nullptr; }
private:
struct release_deleter {
void operator()(bool* ptr)
{
if (ptr != nullptr) {
srsran_assert(*ptr == true, "resource token: detected inconsistency token state");
*ptr = false;
}
}
};
std::unique_ptr<bool, release_deleter> flag;
};
private:
bool flag = false;
};
} // namespace sched_nr_impl
} // namespace srsenb

@ -39,8 +39,9 @@ using dl_sched_rar_info_t = sched_nr_interface::dl_sched_rar_info_t;
const static size_t MAX_CORESET_PER_BWP = 3;
using slot_coreset_list = std::array<srsran::optional<coreset_region>, MAX_CORESET_PER_BWP>;
using pdsch_t = mac_interface_phy_nr::pdsch_t;
using pdsch_list_t = srsran::bounded_vector<pdsch_t, MAX_GRANTS>;
using pdsch_t = mac_interface_phy_nr::pdsch_t;
using pdsch_list_t = srsran::bounded_vector<pdsch_t, MAX_GRANTS>;
using sched_rar_list_t = sched_nr_interface::sched_rar_list_t;
struct harq_ack_t {
const srsran::phy_cfg_nr_t* phy_cfg;
@ -49,18 +50,21 @@ struct harq_ack_t {
using harq_ack_list_t = srsran::bounded_vector<harq_ack_t, MAX_GRANTS>;
struct bwp_slot_grid {
uint32_t slot_idx;
const bwp_params* cfg;
uint32_t slot_idx = 0;
const bwp_params* cfg = nullptr;
bwp_rb_bitmap dl_prbs;
bwp_rb_bitmap ul_prbs;
pdcch_dl_list_t dl_pdcchs;
pdcch_ul_list_t ul_pdcchs;
pdsch_list_t pdschs;
sched_rar_list_t rar;
slot_coreset_list coresets;
pusch_list_t puschs;
harq_ack_list_t pending_acks;
srsran::unique_pool_ptr<tx_harq_softbuffer> rar_softbuffer;
bwp_slot_grid() = default;
explicit bwp_slot_grid(const bwp_params& bwp_params, uint32_t slot_idx_);
void reset();
@ -70,7 +74,7 @@ struct bwp_slot_grid {
};
struct bwp_res_grid {
bwp_res_grid(const bwp_params& bwp_cfg_);
explicit bwp_res_grid(const bwp_params& bwp_cfg_);
bwp_slot_grid& operator[](slot_point tti) { return slots[tti.to_uint() % slots.capacity()]; };
const bwp_slot_grid& operator[](slot_point tti) const { return slots[tti.to_uint() % slots.capacity()]; };
@ -83,6 +87,8 @@ private:
srsran::bounded_vector<bwp_slot_grid, TTIMOD_SZ> slots;
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Class responsible for jointly filling the DL/UL sched result fields and allocate RB/PDCCH resources in the RB grid
* to avoid potential RB/PDCCH collisions
@ -92,13 +98,16 @@ class bwp_slot_allocator
public:
explicit bwp_slot_allocator(bwp_res_grid& bwp_grid_);
void new_slot(slot_point pdcch_slot_) { pdcch_slot = pdcch_slot_; }
void new_slot(slot_point pdcch_slot_, slot_ue_map_t& ues_)
{
pdcch_slot = pdcch_slot_;
slot_ues = &ues_;
}
alloc_result alloc_si(uint32_t aggr_idx, uint32_t si_idx, uint32_t si_ntx, const prb_interval& prbs);
alloc_result alloc_rar_and_msg3(uint16_t ra_rnti,
uint32_t aggr_idx,
prb_interval interv,
slot_ue_map_t& ues,
srsran::const_span<dl_sched_rar_info_t> pending_rars);
alloc_result alloc_pdsch(slot_ue& ue, const prb_grant& dl_grant);
alloc_result alloc_pusch(slot_ue& ue, const prb_grant& dl_mask);
@ -110,12 +119,14 @@ public:
const bwp_params& cfg;
private:
alloc_result verify_pdsch_space(bwp_slot_grid& pdsch_grid, bwp_slot_grid& pdcch_grid) const;
alloc_result verify_pusch_space(bwp_slot_grid& pusch_grid, bwp_slot_grid* pdcch_grid = nullptr) const;
srslog::basic_logger& logger;
bwp_res_grid& bwp_grid;
slot_point pdcch_slot;
slot_point pdcch_slot;
slot_ue_map_t* slot_ues;
};
} // namespace sched_nr_impl

@ -50,6 +50,7 @@ public:
uint32_t ndi() const { return tb[0].ndi; }
uint32_t mcs() const { return tb[0].mcs; }
const prb_grant& prbs() const { return prbs_; }
slot_point harq_slot_tx() const { return slot_tx; }
slot_point harq_slot_ack() const { return slot_ack; }
bool ack_info(uint32_t tb_idx, bool ack);
@ -65,12 +66,11 @@ public:
bool new_retx(slot_point slot_tx, slot_point slot_ack, const prb_grant& grant);
bool new_retx(slot_point slot_tx, slot_point slot_ack);
const uint32_t pid;
protected:
// NOTE: Has to be used before first tx is dispatched
bool set_tbs(uint32_t tbs);
const uint32_t pid;
private:
struct tb_t {
bool active = false;
@ -93,16 +93,15 @@ class dl_harq_proc : public harq_proc
public:
dl_harq_proc(uint32_t id_, uint32_t nprb);
tx_harq_softbuffer& get_softbuffer() { return *softbuffer; }
tx_harq_softbuffer& get_softbuffer() { return *softbuffer; }
srsran::unique_byte_buffer_t* get_tx_pdu() { return &pdu; }
// clear and reset softbuffer and PDU for new tx
bool set_tbs(uint32_t tbs)
{
softbuffer->reset();
pdu->clear();
return harq_proc::set_tbs(tbs);
}
bool new_tx(slot_point slot_tx,
slot_point slot_ack,
const prb_grant& grant,
uint32_t mcs,
uint32_t tbs,
uint32_t max_retx);
private:
srsran::unique_pool_ptr<tx_harq_softbuffer> softbuffer;
@ -137,6 +136,11 @@ public:
void dl_ack_info(uint32_t pid, uint32_t tb_idx, bool ack) { dl_harqs[pid].ack_info(tb_idx, ack); }
void ul_crc_info(uint32_t pid, bool ack) { ul_harqs[pid].ack_info(0, ack); }
uint32_t nof_dl_harqs() const { return dl_harqs.size(); }
uint32_t nof_ul_harqs() const { return ul_harqs.size(); }
const dl_harq_proc& dl_harq(uint32_t pid) const { return dl_harqs[pid]; }
const ul_harq_proc& ul_harq(uint32_t pid) const { return ul_harqs[pid]; }
dl_harq_proc* find_pending_dl_retx()
{
return find_dl([this](const dl_harq_proc& h) { return h.has_pending_retx(slot_rx); });

@ -23,12 +23,14 @@
#define SRSRAN_SCHED_NR_HELPERS_H
#include "sched_nr_cfg.h"
#include "sched_nr_ue.h"
namespace srsenb {
namespace sched_nr_impl {
class slot_ue;
class ul_harq_proc;
struct bwp_res_grid;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -50,6 +52,12 @@ void fill_ul_dci_ue_fields(const slot_ue& ue,
srsran_dci_location_t dci_pos,
srsran_dci_ul_nr_t& dci);
/// Log Scheduling Result for a given BWP and slot
void log_sched_bwp_result(srslog::basic_logger& logger,
slot_point pdcch_slot,
const bwp_res_grid& res_grid,
const slot_ue_map_t& slot_ues);
} // namespace sched_nr_impl
} // namespace srsenb

@ -63,7 +63,7 @@ public:
srsran_pdcch_cfg_nr_t pdcch = {};
srsran_sch_hl_cfg_nr_t pdsch = {};
srsran_sch_hl_cfg_nr_t pusch = {};
uint32_t rar_window_size = 8;
uint32_t rar_window_size = 10; // See TS 38.331, ra-ResponseWindow: {1, 2, 4, 8, 10, 20, 40, 80}
uint32_t numerology_idx = 0;
};
@ -75,8 +75,10 @@ public:
};
struct sched_cfg_t {
bool pdsch_enabled = true;
bool pusch_enabled = true;
bool pdsch_enabled = true;
bool pusch_enabled = true;
bool auto_refill_buffer = true;
std::string logger_name = "MAC";
};
struct ue_cc_cfg_t {
@ -100,7 +102,7 @@ public:
uint32_t freq_idx;
uint32_t ta_cmd;
uint16_t temp_crnti;
uint32_t msg3_size;
uint32_t msg3_size = 7;
slot_point prach_slot;
};
@ -109,11 +111,24 @@ public:
using dl_sched_t = mac_interface_phy_nr::dl_sched_t;
using ul_sched_t = mac_interface_phy_nr::ul_sched_t;
virtual ~sched_nr_interface() = default;
virtual int cell_cfg(srsran::const_span<sched_nr_interface::cell_cfg_t> ue_cfg) = 0;
virtual void ue_cfg(uint16_t rnti, const ue_cfg_t& ue_cfg) = 0;
virtual int get_dl_sched(slot_point slot_rx, uint32_t cc, dl_sched_t& result) = 0;
virtual int get_ul_sched(slot_point slot_rx, uint32_t cc, ul_sched_t& result) = 0;
struct sched_rar_grant_t {
dl_sched_rar_info_t data;
srsran_dci_rar_grant_t grant = {};
};
struct sched_rar_t {
srsran::bounded_vector<sched_rar_grant_t, MAX_GRANTS> grants;
};
using sched_rar_list_t = srsran::bounded_vector<sched_rar_t, MAX_GRANTS>;
struct dl_sched_res_t {
sched_rar_list_t rar;
dl_sched_t dl_sched;
};
virtual ~sched_nr_interface() = default;
virtual int cell_cfg(srsran::const_span<sched_nr_interface::cell_cfg_t> ue_cfg) = 0;
virtual void ue_cfg(uint16_t rnti, const ue_cfg_t& ue_cfg) = 0;
virtual int get_dl_sched(slot_point slot_rx, uint32_t cc, dl_sched_res_t& result) = 0;
virtual int get_ul_sched(slot_point slot_rx, uint32_t cc, ul_sched_t& result) = 0;
virtual void dl_ack_info(uint16_t rnti, uint32_t cc, uint32_t pid, uint32_t tb_idx, bool ack) = 0;
virtual void ul_crc_info(uint16_t rnti, uint32_t cc, uint32_t pid, bool crc) = 0;

@ -38,11 +38,7 @@ enum class pdcch_grant_type_t { sib, rar, dl_data, ul_data };
class slot_ue;
using bwp_cfg_t = sched_nr_interface::bwp_cfg_t;
using pdcch_dl_t = mac_interface_phy_nr::pdcch_dl_t;
using pdcch_ul_t = mac_interface_phy_nr::pdcch_ul_t;
using pdcch_dl_list_t = srsran::bounded_vector<pdcch_dl_t, MAX_GRANTS>;
using pdcch_ul_list_t = srsran::bounded_vector<pdcch_ul_t, MAX_GRANTS>;
using bwp_cfg_t = sched_nr_interface::bwp_cfg_t;
class coreset_region
{

@ -243,4 +243,21 @@ inline prb_interval find_empty_interval_of_length(const prb_bitmap& mask, size_t
} // namespace sched_nr_impl
} // namespace srsenb
namespace fmt {
template <>
struct formatter<srsenb::sched_nr_impl::prb_grant> : public formatter<srsenb::sched_nr_impl::rbg_bitmap> {
template <typename FormatContext>
auto format(const srsenb::sched_nr_impl::prb_grant& grant, FormatContext& ctx)
-> decltype(std::declval<FormatContext>().out())
{
if (grant.is_alloc_type1()) {
return formatter<srsran::interval<uint32_t> >{}.format(grant.prbs(), ctx);
}
return formatter<srsenb::sched_nr_impl::rbg_bitmap>::format(grant.rbgs(), ctx);
}
};
} // namespace fmt
#endif // SRSRAN_SCHED_NR_RB_H

@ -25,6 +25,7 @@
#include "sched_nr_cfg.h"
#include "sched_nr_harq.h"
#include "sched_nr_interface.h"
#include "srsenb/hdr/stack/mac/common/ue_buffer_manager.h"
#include "srsran/adt/circular_map.h"
#include "srsran/adt/move_callback.h"
#include "srsran/adt/pool/cached_alloc.h"
@ -50,7 +51,7 @@ public:
uint32_t cc = SCHED_NR_MAX_CARRIERS;
// UE parameters common to all sectors
bool pending_sr = false;
int dl_pending_bytes = 0, ul_pending_bytes = 0;
// UE parameters that are sector specific
const bwp_ue_cfg* cfg = nullptr;
@ -61,16 +62,15 @@ public:
slot_point uci_slot;
uint32_t dl_cqi = 0;
uint32_t ul_cqi = 0;
dl_harq_proc* h_dl = nullptr;
ul_harq_proc* h_ul = nullptr;
dl_harq_proc* h_dl = nullptr;
ul_harq_proc* h_ul = nullptr;
};
class ue_carrier
{
public:
ue_carrier(uint16_t rnti, const ue_cfg_t& cfg, const sched_cell_params& cell_params_);
void new_slot(slot_point pdcch_slot, const ue_cfg_t& uecfg_);
slot_ue try_reserve(slot_point pdcch_slot);
slot_ue try_reserve(slot_point pdcch_slot, const ue_cfg_t& uecfg_, uint32_t dl_harq_bytes, uint32_t ul_harq_bytes);
const uint16_t rnti;
const uint32_t cc;
@ -91,23 +91,34 @@ class ue
public:
ue(uint16_t rnti, const ue_cfg_t& cfg, const sched_params& sched_cfg_);
void new_slot(slot_point pdcch_slot);
slot_ue try_reserve(slot_point pdcch_slot, uint32_t cc);
void set_cfg(const ue_cfg_t& cfg);
const ue_cfg_t& cfg() const { return ue_cfg; }
void ul_sr_info(slot_point slot_rx) { pending_sr = true; }
void rlc_buffer_state(uint32_t lcid, uint32_t newtx, uint32_t retx) { buffers.dl_buffer_state(lcid, newtx, retx); }
void ul_bsr(uint32_t lcg, uint32_t bsr_val) { buffers.ul_bsr(lcg, bsr_val); }
void ul_sr_info(slot_point slot_rx) { last_sr_slot = slot_rx; }
bool has_ca() const { return ue_cfg.carriers.size() > 1; }
bool has_ca() const
{
return ue_cfg.carriers.size() > 1 and std::count_if(ue_cfg.carriers.begin() + 1,
ue_cfg.carriers.end(),
[](const ue_cc_cfg_t& cc) { return cc.active; }) > 0;
}
uint32_t pcell_cc() const { return ue_cfg.carriers[0].cc; }
ue_buffer_manager<true> buffers;
std::array<std::unique_ptr<ue_carrier>, SCHED_NR_MAX_CARRIERS> carriers;
private:
const uint16_t rnti;
const sched_params& sched_cfg;
bool pending_sr = false;
slot_point last_sr_slot;
int ul_pending_bytes = 0, dl_pending_bytes = 0;
ue_cfg_t ue_cfg;
};

@ -36,8 +36,9 @@
namespace srsenb {
namespace sched_nr_impl {
using dl_sched_t = sched_nr_interface::dl_sched_t;
using ul_sched_t = sched_nr_interface::ul_sched_t;
using dl_sched_t = sched_nr_interface::dl_sched_t;
using ul_sched_t = sched_nr_interface::ul_sched_t;
using dl_sched_res_t = sched_nr_interface::dl_sched_res_t;
class slot_cc_worker
{
@ -46,9 +47,7 @@ public:
explicit slot_cc_worker(serv_cell_manager& sched);
void start(slot_point pdcch_slot, ue_map_t& ue_db_);
void run();
void finish();
void run(slot_point pdcch_slot, ue_map_t& ue_db_);
bool running() const { return slot_rx.valid(); }
void enqueue_cc_event(srsran::move_callback<void()> ev);
@ -62,7 +61,6 @@ private:
void alloc_dl_ues();
void alloc_ul_ues();
void log_result() const;
const sched_cell_params& cfg;
serv_cell_manager& cell;
@ -102,7 +100,7 @@ public:
sched_worker_manager(sched_worker_manager&&) = delete;
~sched_worker_manager();
void run_slot(slot_point slot_tx, uint32_t cc, dl_sched_t& dl_res, ul_sched_t& ul_res);
void run_slot(slot_point slot_tx, uint32_t cc, dl_sched_res_t& dl_res, ul_sched_t& ul_res);
void enqueue_event(uint16_t rnti, srsran::move_callback<void()> ev);
void enqueue_cc_event(uint32_t cc, srsran::move_callback<void()> ev);
@ -112,7 +110,9 @@ public:
}
private:
bool save_sched_result(slot_point pdcch_slot, uint32_t cc, dl_sched_t& dl_res, ul_sched_t& ul_res);
void update_ue_db(slot_point slot_tx, bool update_ca_users);
bool save_sched_result(slot_point pdcch_slot, uint32_t cc, dl_sched_res_t& dl_res, ul_sched_t& ul_res);
const sched_params& cfg;
ue_map_t& ue_db;

@ -23,10 +23,10 @@
#define SRSENB_UE_NR_H
#include "srsenb/hdr/stack/mac/common/mac_metrics.h"
#include "srsenb/hdr/stack/mac/sched_interface.h"
#include "srsran/common/block_queue.h"
#include "srsran/common/interfaces_common.h"
#include "srsran/interfaces/enb_rlc_interfaces.h"
#include "srsran/interfaces/sched_interface.h"
#include "srsran/mac/mac_sch_pdu_nr.h"
#include <mutex>
#include <vector>

@ -23,9 +23,9 @@
#define SRSENB_SCHEDULER_H
#include "sched_grid.h"
#include "sched_interface.h"
#include "sched_ue.h"
#include "srsenb/hdr/common/common_enb.h"
#include "srsran/interfaces/sched_interface.h"
#include <atomic>
#include <map>
#include <mutex>
@ -56,7 +56,7 @@ public:
void phy_config_enabled(uint16_t rnti, bool enabled);
int bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, const ue_bearer_cfg_t& cfg) final;
int bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, const mac_lc_ch_cfg_t& cfg) final;
int bearer_ue_rem(uint16_t rnti, uint32_t lc_id) final;
uint32_t get_ul_buffer(uint16_t rnti) final;

@ -22,8 +22,8 @@
#ifndef SRSRAN_SCHED_GRID_H
#define SRSRAN_SCHED_GRID_H
#include "lib/include/srsran/interfaces/sched_interface.h"
#include "sched_common.h"
#include "sched_interface.h"
#include "sched_phy_ch/sched_result.h"
#include "sched_phy_ch/sf_cch_allocator.h"
#include "sched_ue.h"

@ -22,8 +22,8 @@
#ifndef SRSRAN_SCHED_HELPERS_H
#define SRSRAN_SCHED_HELPERS_H
#include "sched_interface.h"
#include "srsenb/hdr/stack/mac/sched_lte_common.h"
#include "srsran/interfaces/sched_interface.h"
#include "srsran/srslog/srslog.h"
namespace srsenb {
@ -105,8 +105,6 @@ void log_phich_cc_results(srslog::basic_logger& logger,
uint32_t enb_cc_idx,
const sched_interface::ul_sched_res_t& result);
const char* to_string(sched_interface::ue_bearer_cfg_t::direction_t dir);
} // namespace srsenb
#endif // SRSRAN_SCHED_HELPERS_H

@ -19,6 +19,7 @@
*
*/
#include "common/sched_config.h"
#include "srsran/adt/bounded_vector.h"
#include "srsran/common/common.h"
#include "srsran/srsran.h"
@ -125,14 +126,6 @@ public:
std::vector<scell_cfg_t> scell_list;
};
struct ue_bearer_cfg_t {
int priority = 1;
uint32_t bsd = 1000; // msec
uint32_t pbr = -1;
int group = 0;
enum direction_t { IDLE = 0, UL, DL, BOTH } direction = IDLE;
};
struct ant_info_ded_t {
enum class tx_mode_t { tm1, tm2, tm3, tm4, tm5, tm6, tm7, tm8_v920, nulltype } tx_mode = tx_mode_t::tm1;
enum class codebook_t {
@ -162,7 +155,7 @@ public:
bool continuous_pusch = false;
srsran_uci_offset_cfg_t uci_offset = {15, 12, 10};
srsran_pucch_cfg_t pucch_cfg = {};
std::array<ue_bearer_cfg_t, MAX_LC> ue_bearers = {};
std::array<mac_lc_ch_cfg_t, MAX_LC> ue_bearers = {};
std::vector<cc_cfg_t> supported_cc_list; ///< list of UE supported CCs. First index for PCell
ant_info_ded_t dl_ant_info;
bool use_tbs_index_alt = false;
@ -267,7 +260,7 @@ public:
virtual bool ue_exists(uint16_t rnti) = 0;
/* Manages UE bearers and associated configuration */
virtual int bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, const ue_bearer_cfg_t& cfg) = 0;
virtual int bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, const mac_lc_ch_cfg_t& cfg) = 0;
virtual int bearer_ue_rem(uint16_t rnti, uint32_t lc_id) = 0;
virtual uint32_t get_ul_buffer(uint16_t rnti) = 0;

@ -22,9 +22,9 @@
#ifndef SRSRAN_SCHED_LTE_COMMON_H
#define SRSRAN_SCHED_LTE_COMMON_H
#include "sched_interface.h"
#include "srsran/adt/bounded_bitset.h"
#include "srsran/common/tti_point.h"
#include "srsran/interfaces/sched_interface.h"
namespace srsenb {

@ -41,8 +41,7 @@ typedef enum { UCI_PUSCH_NONE = 0, UCI_PUSCH_CQI, UCI_PUSCH_ACK, UCI_PUSCH_ACK_C
*/
class sched_ue
{
using ue_cfg_t = sched_interface::ue_cfg_t;
using bearer_cfg_t = sched_interface::ue_bearer_cfg_t;
using ue_cfg_t = sched_interface::ue_cfg_t;
public:
sched_ue(uint16_t rnti, const std::vector<sched_cell_params_t>& cell_list_params_, const ue_cfg_t& cfg);
@ -57,7 +56,7 @@ public:
void phy_config_enabled(tti_point tti_rx, bool enabled);
void set_cfg(const ue_cfg_t& cfg);
void set_bearer_cfg(uint32_t lc_id, const bearer_cfg_t& cfg);
void set_bearer_cfg(uint32_t lc_id, const mac_lc_ch_cfg_t& cfg);
void rem_bearer(uint32_t lc_id);
void dl_buffer_state(uint8_t lc_id, uint32_t tx_queue, uint32_t retx_queue);

@ -22,9 +22,9 @@
#ifndef SRSENB_SCHEDULER_HARQ_H
#define SRSENB_SCHEDULER_HARQ_H
#include "srsenb/hdr/stack/mac/sched_interface.h"
#include "srsran/adt/bounded_bitset.h"
#include "srsran/common/tti_point.h"
#include "srsran/interfaces/sched_interface.h"
#include "srsran/srslog/srslog.h"
namespace srsenb {

@ -22,70 +22,58 @@
#ifndef SRSRAN_SCHED_LCH_H
#define SRSRAN_SCHED_LCH_H
#include "srsenb/hdr/stack/mac/common/ue_buffer_manager.h"
#include "srsenb/hdr/stack/mac/sched_interface.h"
#include "srsran/adt/pool/cached_alloc.h"
#include "srsran/interfaces/sched_interface.h"
#include "srsran/mac/pdu.h"
#include "srsran/srslog/srslog.h"
namespace srsenb {
class lch_ue_manager
class lch_ue_manager : private ue_buffer_manager<false>
{
constexpr static uint32_t pbr_infinity = -1;
constexpr static uint32_t MAX_LC = sched_interface::MAX_LC;
using base_type = ue_buffer_manager<false>;
public:
lch_ue_manager() : logger(srslog::fetch_basic_logger("MAC")) {}
lch_ue_manager() : ue_buffer_manager(srslog::fetch_basic_logger("MAC")) {}
void set_cfg(const sched_interface::ue_cfg_t& cfg_);
void new_tti();
void config_lcid(uint32_t lcg_id, const sched_interface::ue_bearer_cfg_t& bearer_cfg);
void ul_bsr(uint8_t lcg_id, uint32_t bsr);
// Inherited methods from ue_buffer_manager base class
using base_type::config_lcid;
using base_type::dl_buffer_state;
using base_type::get_bsr;
using base_type::get_bsr_state;
using base_type::get_dl_retx;
using base_type::get_dl_tx;
using base_type::get_dl_tx_total;
using base_type::is_bearer_active;
using base_type::is_bearer_dl;
using base_type::is_bearer_ul;
using base_type::is_lcg_active;
using base_type::ul_bsr;
void ul_buffer_add(uint8_t lcid, uint32_t bytes);
void dl_buffer_state(uint8_t lcid, uint32_t tx_queue, uint32_t retx_queue);
int alloc_rlc_pdu(sched_interface::dl_sched_pdu_t* lcid, int rem_bytes);
bool is_bearer_active(uint32_t lcid) const;
bool is_bearer_ul(uint32_t lcid) const;
bool is_bearer_dl(uint32_t lcid) const;
bool has_pending_dl_txs() const;
int get_dl_tx_total() const;
int get_dl_tx_total(uint32_t lcid) const { return get_dl_tx(lcid) + get_dl_retx(lcid); }
int get_dl_tx_total_with_overhead(uint32_t lcid) const;
int get_dl_tx(uint32_t lcid) const;
int get_dl_tx_with_overhead(uint32_t lcid) const;
int get_dl_retx(uint32_t lcid) const;
int get_dl_retx_with_overhead(uint32_t lcid) const;
bool is_lcg_active(uint32_t lcg) const;
int get_bsr(uint32_t lcid) const;
int get_bsr_with_overhead(uint32_t lcid) const;
int get_max_prio_lcid() const;
const std::array<int, 4>& get_bsr_state() const;
int get_bsr_with_overhead(uint32_t lcid) const;
int get_max_prio_lcid() const;
// Control Element Command queue
using ce_cmd = srsran::dl_sch_lcid;
srsran::deque<ce_cmd> pending_ces;
private:
struct ue_bearer_t {
sched_interface::ue_bearer_cfg_t cfg = {};
int bucket_size = 0;
int buf_tx = 0;
int buf_retx = 0;
int Bj = 0;
};
int alloc_retx_bytes(uint8_t lcid, int rem_bytes);
int alloc_tx_bytes(uint8_t lcid, int rem_bytes);
size_t prio_idx = 0;
srslog::basic_logger& logger;
std::array<ue_bearer_t, sched_interface::MAX_LC> lch = {};
std::array<int, 4> lcg_bsr = {};
size_t prio_idx = 0;
};
/**

@ -23,6 +23,7 @@
#define SRSENB_UE_H
#include "common/mac_metrics.h"
#include "sched_interface.h"
#include "srsran/adt/circular_array.h"
#include "srsran/adt/circular_map.h"
#include "srsran/adt/pool/pool_interface.h"
@ -30,7 +31,6 @@
#include "srsran/common/mac_pcap.h"
#include "srsran/common/mac_pcap_net.h"
#include "srsran/common/tti_point.h"
#include "srsran/interfaces/sched_interface.h"
#include "srsran/mac/pdu.h"
#include "srsran/mac/pdu_queue.h"
#include "srsran/srslog/srslog.h"

@ -24,8 +24,8 @@
#include "rrc_bearer_cfg.h"
#include "rrc_cell_cfg.h"
#include "srsenb/hdr/stack/mac/sched_interface.h"
#include "srsran/interfaces/rrc_interface_types.h"
#include "srsran/interfaces/sched_interface.h"
#include <bitset>
namespace srsenb {
@ -64,7 +64,7 @@ public:
const srsran::rrc_ue_capabilities_t& uecaps);
void handle_ho_prep(const asn1::rrc::ho_prep_info_r8_ies_s& ho_prep);
void set_radio_bearer_state(sched_interface::ue_bearer_cfg_t::direction_t dir);
void set_radio_bearer_state(mac_lc_ch_cfg_t::direction_t dir);
const ue_cfg_t& get_ue_sched_cfg() const { return current_sched_ue_cfg; }
bool is_crnti_set() const { return crnti_set; }

@ -62,7 +62,7 @@ class rrc final : public rrc_interface_pdcp,
public rrc_eutra_interface_rrc_nr
{
public:
explicit rrc(srsran::task_sched_handle task_sched_);
explicit rrc(stack_interface_rrc* stack_, srsran::task_sched_handle task_sched_);
~rrc();
int32_t init(const rrc_cfg_t& cfg_,
@ -175,6 +175,7 @@ public:
private:
// args
srsran::task_sched_handle task_sched;
stack_interface_rrc* stack = nullptr;
phy_interface_rrc_lte* phy = nullptr;
mac_interface_rrc* mac = nullptr;
rlc_interface_rrc* rlc = nullptr;

@ -37,6 +37,7 @@
#include "srsran/interfaces/enb_rlc_interfaces.h"
#include "srsran/interfaces/enb_rrc_interfaces.h"
#include "srsran/interfaces/gnb_interfaces.h"
#include "srsran/interfaces/gnb_mac_interfaces.h"
#include "srsran/interfaces/gnb_ngap_interfaces.h"
#include "srsran/interfaces/gnb_rrc_nr_interfaces.h"
#include <map>
@ -127,8 +128,8 @@ public:
int pack_nr_radio_bearer_config(asn1::dyn_octstring& packed_nr_bearer_config);
// state
rrc_nr_state_t state = rrc_nr_state_t::RRC_IDLE;
uint8_t transaction_id = 0;
rrc_nr_state_t state = rrc_nr_state_t::RRC_IDLE;
uint8_t transaction_id = 0;
uint32_t drb1_lcid = 4;
};
@ -137,12 +138,12 @@ private:
rrc_nr_cfg_t cfg = {};
// interfaces
phy_interface_stack_nr* phy = nullptr;
mac_interface_rrc_nr* mac = nullptr;
phy_interface_stack_nr* phy = nullptr;
mac_interface_rrc_nr* mac = nullptr;
rlc_interface_rrc* rlc = nullptr;
pdcp_interface_rrc* pdcp = nullptr;
gtpu_interface_rrc_nr* gtpu = nullptr;
ngap_interface_rrc_nr* ngap = nullptr;
gtpu_interface_rrc_nr* gtpu = nullptr;
ngap_interface_rrc_nr* ngap = nullptr;
rrc_eutra_interface_rrc_nr* rrc_eutra = nullptr;
// args

@ -58,7 +58,7 @@ public:
void rlf_timer_expired(uint32_t timeout_id);
void max_rlc_retx_reached();
void protocol_failure();
void deactivate_bearers() { mac_ctrl.set_radio_bearer_state(sched_interface::ue_bearer_cfg_t::IDLE); }
void deactivate_bearers() { mac_ctrl.set_radio_bearer_state(mac_lc_ch_cfg_t::IDLE); }
rrc_state_t get_state();
void get_metrics(rrc_ue_metrics_t& ue_metrics) const;

@ -59,14 +59,14 @@ public:
// A UE should have <= 3 DRBs active, and each DRB should have two tunnels active at the same time at most
const static size_t MAX_TUNNELS_PER_UE = 6;
enum class tunnel_state { pdcp_active, buffering, forward_to, forwarded_from };
enum class tunnel_state { pdcp_active, buffering, forward_to, forwarded_from, inactive };
struct tunnel {
uint16_t rnti = SRSRAN_INVALID_RNTI;
uint32_t lcid = srsran::INVALID_LCID;
uint32_t teid_in = 0;
uint32_t teid_out = 0;
uint32_t spgw_addr = 0;
uint16_t rnti = SRSRAN_INVALID_RNTI;
uint32_t eps_bearer_id = srsran::INVALID_EPS_BEARER_ID;
uint32_t teid_in = 0;
uint32_t teid_out = 0;
uint32_t spgw_addr = 0;
tunnel_state state = tunnel_state::pdcp_active;
srsran::unique_timer rx_timer;
@ -85,38 +85,41 @@ public:
}
};
struct lcid_tunnel {
uint32_t lcid;
struct bearer_teid_pair {
uint32_t eps_bearer_id;
uint32_t teid;
bool operator<(const lcid_tunnel& other) const
bool operator<(const bearer_teid_pair& other) const
{
return lcid < other.lcid or (lcid == other.lcid and teid < other.teid);
return eps_bearer_id < other.eps_bearer_id or (eps_bearer_id == other.eps_bearer_id and teid < other.teid);
}
bool operator==(const bearer_teid_pair& other) const
{
return eps_bearer_id == other.eps_bearer_id and teid == other.teid;
}
bool operator==(const lcid_tunnel& other) const { return lcid == other.lcid and teid == other.teid; }
};
using ue_lcid_tunnel_list = srsran::bounded_vector<lcid_tunnel, MAX_TUNNELS_PER_UE>;
using ue_bearer_tunnel_list = srsran::bounded_vector<bearer_teid_pair, MAX_TUNNELS_PER_UE>;
explicit gtpu_tunnel_manager(srsran::task_sched_handle task_sched_, srslog::basic_logger& logger);
void init(const gtpu_args_t& gtpu_args, pdcp_interface_gtpu* pdcp_);
bool has_teid(uint32_t teid) const { return tunnels.contains(teid); }
const tunnel* find_tunnel(uint32_t teid);
ue_lcid_tunnel_list* find_rnti_tunnels(uint16_t rnti);
srsran::span<lcid_tunnel> find_rnti_lcid_tunnels(uint16_t rnti, uint32_t lcid);
bool has_teid(uint32_t teid) const { return tunnels.contains(teid); }
const tunnel* find_tunnel(uint32_t teid);
ue_bearer_tunnel_list* find_rnti_tunnels(uint16_t rnti);
srsran::span<bearer_teid_pair> find_rnti_bearer_tunnels(uint16_t rnti, uint32_t eps_bearer_id);
const tunnel* add_tunnel(uint16_t rnti, uint32_t lcid, uint32_t teidout, uint32_t spgw_addr);
const tunnel* add_tunnel(uint16_t rnti, uint32_t eps_bearer_id, uint32_t teidout, uint32_t spgw_addr);
bool update_rnti(uint16_t old_rnti, uint16_t new_rnti);
void activate_tunnel(uint32_t teid);
void suspend_tunnel(uint32_t teid);
void deactivate_tunnel(uint32_t teid);
void set_tunnel_priority(uint32_t first_teid, uint32_t second_teid);
void handle_rx_pdcp_sdu(uint32_t teid);
void buffer_pdcp_sdu(uint32_t teid, uint32_t pdcp_sn, srsran::unique_byte_buffer_t sdu);
void setup_forwarding(uint32_t rx_teid, uint32_t tx_teid);
bool remove_tunnel(uint32_t teid);
bool remove_bearer(uint16_t rnti, uint32_t lcid);
bool remove_rnti(uint16_t rnti);
private:
@ -128,8 +131,8 @@ private:
pdcp_interface_gtpu* pdcp = nullptr;
srslog::basic_logger& logger;
rnti_map_t<ue_lcid_tunnel_list> ue_teidin_db;
tunnel_list_t tunnels;
rnti_map_t<ue_bearer_tunnel_list> ue_teidin_db;
tunnel_list_t tunnels;
};
using gtpu_tunnel_state = gtpu_tunnel_manager::tunnel_state;
@ -148,17 +151,17 @@ public:
// gtpu_interface_rrc
srsran::expected<uint32_t> add_bearer(uint16_t rnti,
uint32_t lcid,
uint32_t eps_bearer_id,
uint32_t addr,
uint32_t teid_out,
const bearer_props* props = nullptr) override;
void set_tunnel_status(uint32_t teidin, bool dl_active) override;
void rem_bearer(uint16_t rnti, uint32_t lcid) override;
void rem_bearer(uint16_t rnti, uint32_t eps_bearer_id) override;
void mod_bearer_rnti(uint16_t old_rnti, uint16_t new_rnti) override;
void rem_user(uint16_t rnti) override;
// gtpu_interface_pdcp
void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) override;
void write_pdu(uint16_t rnti, uint32_t eps_bearer_id, srsran::unique_byte_buffer_t pdu) override;
// stack interface
void handle_gtpu_s1u_rx_packet(srsran::unique_byte_buffer_t pdu, const sockaddr_in& addr);
@ -199,9 +202,9 @@ private:
std::string m1u_multiaddr;
std::string m1u_if_addr;
bool initiated = false;
int m1u_sd = -1;
int lcid_counter = 0;
bool initiated = false;
int m1u_sd = -1;
int bearer_counter = 0;
};
m1u_handler m1u;

@ -23,6 +23,7 @@
#define SRSENB_RLC_NR_H
#include "srsran/interfaces/gnb_interfaces.h"
#include "srsran/interfaces/gnb_mac_interfaces.h"
#include "srsran/rlc/rlc.h"
#include <map>

@ -31,6 +31,60 @@ using namespace srsran;
namespace srsenb {
class gtpu_pdcp_adapter final : public gtpu_interface_pdcp, public pdcp_interface_gtpu
{
public:
gtpu_pdcp_adapter(srslog::basic_logger& logger_,
pdcp* pdcp_lte,
pdcp* pdcp_nr,
gtpu* gtpu_,
srsran::bearer_manager& bearers_) :
logger(logger_), pdcp_obj(pdcp_lte), pdcp_nr_obj(pdcp_nr), gtpu_obj(gtpu_), bearers(&bearers_)
{}
/// Converts LCID to EPS-BearerID and sends corresponding PDU to GTPU
void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) override
{
auto bearer = bearers->get_lcid_bearer(rnti, lcid);
if (not bearer.is_valid()) {
logger.error("Bearer rnti=0x%x, lcid=%d not found", rnti, lcid);
return;
}
gtpu_obj->write_pdu(rnti, bearer.eps_bearer_id, std::move(pdu));
}
void write_sdu(uint16_t rnti, uint32_t eps_bearer_id, srsran::unique_byte_buffer_t sdu, int pdcp_sn = -1) override
{
auto bearer = bearers->get_radio_bearer(rnti, eps_bearer_id);
// route SDU to PDCP entity
if (bearer.rat == srsran_rat_t::lte) {
pdcp_obj->write_sdu(rnti, bearer.lcid, std::move(sdu), pdcp_sn);
} else if (bearer.rat == srsran_rat_t::nr) {
pdcp_nr_obj->write_sdu(rnti, bearer.lcid, std::move(sdu), pdcp_sn);
} else {
logger.warning("Can't deliver SDU for EPS bearer %d. Dropping it.", eps_bearer_id);
}
}
std::map<uint32_t, srsran::unique_byte_buffer_t> get_buffered_pdus(uint16_t rnti, uint32_t eps_bearer_id) override
{
auto bearer = bearers->get_radio_bearer(rnti, eps_bearer_id);
// route SDU to PDCP entity
if (bearer.rat == srsran_rat_t::lte) {
return pdcp_obj->get_buffered_pdus(rnti, bearer.lcid);
} else if (bearer.rat == srsran_rat_t::nr) {
return pdcp_nr_obj->get_buffered_pdus(rnti, bearer.lcid);
}
logger.error("Bearer rnti=0x%x, eps-BearerID=%d not found", rnti, eps_bearer_id);
return {};
}
private:
srslog::basic_logger& logger;
gtpu* gtpu_obj = nullptr;
pdcp* pdcp_obj = nullptr;
pdcp* pdcp_nr_obj = nullptr;
srsran::bearer_manager* bearers = nullptr;
};
enb_stack_lte::enb_stack_lte(srslog::sink& log_sink) :
thread("STACK"),
mac_logger(srslog::fetch_basic_logger("MAC", log_sink)),
@ -53,7 +107,7 @@ enb_stack_lte::enb_stack_lte(srslog::sink& log_sink) :
rlc_nr(rlc_nr_logger),
gtpu(&task_sched, gtpu_logger, &rx_sockets),
s1ap(&task_sched, s1ap_logger, &rx_sockets),
rrc(&task_sched),
rrc(this, &task_sched),
rrc_nr(&task_sched),
mac_pcap(),
pending_stack_metrics(64)
@ -154,13 +208,16 @@ int enb_stack_lte::init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_)
// add sync queue
sync_task_queue = task_sched.make_task_queue(args.sync_queue_size);
// setup bearer managers
gtpu_adapter.reset(new gtpu_pdcp_adapter(stack_logger, &pdcp, &pdcp_nr, &gtpu, bearers));
// Init all LTE layers
if (!mac.init(args.mac, rrc_cfg.cell_list, phy, &rlc, &rrc)) {
stack_logger.error("Couldn't initialize MAC");
return SRSRAN_ERROR;
}
rlc.init(&pdcp, &rrc, &mac, task_sched.get_timer_handler());
pdcp.init(&rlc, &rrc, &gtpu);
pdcp.init(&rlc, &rrc, gtpu_adapter.get());
if (rrc.init(rrc_cfg, phy, &mac, &rlc, &pdcp, &s1ap, &gtpu, &rrc_nr) != SRSRAN_SUCCESS) {
stack_logger.error("Couldn't initialize RRC");
return SRSRAN_ERROR;
@ -187,7 +244,7 @@ int enb_stack_lte::init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_)
return SRSRAN_ERROR;
}
rlc_nr.init(&pdcp_nr, &rrc_nr, &mac_nr, task_sched.get_timer_handler());
pdcp_nr.init(&rlc_nr, &rrc_nr, &gtpu);
pdcp_nr.init(&rlc_nr, &rrc_nr, gtpu_adapter.get());
gtpu_args_t gtpu_args;
gtpu_args.embms_enable = args.embms.enable;
@ -196,7 +253,7 @@ int enb_stack_lte::init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_)
gtpu_args.mme_addr = args.s1ap.mme_addr;
gtpu_args.gtp_bind_addr = args.s1ap.gtp_bind_addr;
gtpu_args.indirect_tunnel_timeout_msec = args.gtpu_indirect_tunnel_timeout_msec;
if (gtpu.init(gtpu_args, &pdcp) != SRSRAN_SUCCESS) {
if (gtpu.init(gtpu_args, gtpu_adapter.get()) != SRSRAN_SUCCESS) {
stack_logger.error("Couldn't initialize GTPU");
return SRSRAN_ERROR;
}
@ -287,4 +344,19 @@ void enb_stack_lte::run_thread()
}
}
void enb_stack_lte::add_eps_bearer(uint16_t rnti, uint8_t eps_bearer_id, srsran::srsran_rat_t rat, uint32_t lcid)
{
bearers.add_eps_bearer(rnti, eps_bearer_id, rat, lcid);
}
void enb_stack_lte::remove_eps_bearer(uint16_t rnti, uint8_t eps_bearer_id)
{
bearers.remove_eps_bearer(rnti, eps_bearer_id);
}
void enb_stack_lte::reset_eps_bearers(uint16_t rnti)
{
bearers.reset(rnti);
}
} // namespace srsenb

@ -18,6 +18,8 @@
# and at http://www.gnu.org/licenses/.
#
add_subdirectory(common)
add_subdirectory(schedulers)
set(SOURCES mac.cc ue.cc sched.cc sched_carrier.cc sched_grid.cc sched_ue_ctrl/sched_harq.cc sched_ue.cc
@ -25,5 +27,6 @@ set(SOURCES mac.cc ue.cc sched.cc sched_carrier.cc sched_grid.cc sched_ue_ctrl/s
sched_phy_ch/sf_cch_allocator.cc sched_phy_ch/sched_dci.cc sched_phy_ch/sched_phy_resource.cc
sched_helpers.cc)
add_library(srsenb_mac STATIC ${SOURCES} $<TARGET_OBJECTS:mac_schedulers>)
target_link_libraries(srsenb_mac srsenb_mac_common)
add_subdirectory(nr)

@ -0,0 +1,10 @@
#
# 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.
#
set(SOURCES ue_buffer_manager.cc)
add_library(srsenb_mac_common STATIC ${SOURCES})

@ -0,0 +1,134 @@
/**
*
* \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/stack/mac/common/ue_buffer_manager.h"
#include "srsran/common/string_helpers.h"
#include "srsran/srslog/bundled/fmt/format.h"
#include "srsran/srslog/bundled/fmt/ranges.h"
namespace srsenb {
template <bool isNR>
ue_buffer_manager<isNR>::ue_buffer_manager(srslog::basic_logger& logger_) : logger(logger_)
{
std::fill(lcg_bsr.begin(), lcg_bsr.end(), 0);
}
template <bool isNR>
void ue_buffer_manager<isNR>::config_lcid(uint32_t lcid, const mac_lc_ch_cfg_t& bearer_cfg)
{
if (not is_lcid_valid(lcid)) {
logger.warning("Configuring bearer with invalid logical channel id=%d", lcid);
return;
}
if (not is_lcg_valid(bearer_cfg.group)) {
logger.warning("Configuring bearer with invalid logical channel group id=%d", bearer_cfg.group);
return;
}
// update bearer config
if (bearer_cfg != channels[lcid].cfg) {
channels[lcid].cfg = bearer_cfg;
if (channels[lcid].cfg.pbr == pbr_infinity) {
channels[lcid].bucket_size = std::numeric_limits<int>::max();
channels[lcid].Bj = std::numeric_limits<int>::max();
} else {
channels[lcid].bucket_size = channels[lcid].cfg.bsd * channels[lcid].cfg.pbr;
channels[lcid].Bj = 0;
}
logger.info("SCHED: bearer configured: lcid=%d, mode=%s, prio=%d",
lcid,
to_string(channels[lcid].cfg.direction),
channels[lcid].cfg.priority);
}
}
template <bool isNR>
int ue_buffer_manager<isNR>::get_dl_tx_total() const
{
int sum = 0;
for (size_t lcid = 0; is_lcid_valid(lcid); ++lcid) {
sum += get_dl_tx_total(lcid);
}
return sum;
}
template <bool isNR>
bool ue_buffer_manager<isNR>::is_lcg_active(uint32_t lcg) const
{
if (lcg == 0) {
return true;
}
for (uint32_t lcid = 0; is_lcid_valid(lcid); ++lcid) {
if (is_bearer_ul(lcid) and channels[lcid].cfg.group == (int)lcg) {
return true;
}
}
return false;
}
template <bool isNR>
int ue_buffer_manager<isNR>::get_bsr(uint32_t lcg) const
{
return is_lcg_active(lcg) ? lcg_bsr[lcg] : 0;
}
template <bool isNR>
int ue_buffer_manager<isNR>::get_bsr() const
{
uint32_t count = 0;
for (uint32_t lcg = 0; is_lcg_valid(lcg); ++lcg) {
if (is_lcg_active(lcg)) {
count += lcg_bsr[lcg];
}
}
return count;
}
template <bool isNR>
void ue_buffer_manager<isNR>::ul_bsr(uint32_t lcg_id, uint32_t val)
{
if (not is_lcg_valid(lcg_id)) {
logger.warning("The provided logical channel group id=%d is not valid", lcg_id);
return;
}
lcg_bsr[lcg_id] = val;
if (logger.debug.enabled()) {
fmt::memory_buffer str_buffer;
fmt::format_to(str_buffer, "{}", lcg_bsr);
logger.debug("SCHED: lcg_id=%d, bsr=%d. Current state=%s", lcg_id, val, srsran::to_c_str(str_buffer));
}
}
template <bool isNR>
void ue_buffer_manager<isNR>::dl_buffer_state(uint8_t lcid, uint32_t tx_queue, uint32_t retx_queue)
{
if (not is_lcid_valid(lcid)) {
logger.warning("The provided lcid=%d is not valid", lcid);
return;
}
if (lcid <= MAX_SRB_LC_ID and
(channels[lcid].buf_tx != (int)tx_queue or channels[lcid].buf_retx != (int)retx_queue)) {
logger.info("SCHED: DL lcid=%d buffer_state=%d,%d", lcid, tx_queue, retx_queue);
} else {
logger.debug("SCHED: DL lcid=%d buffer_state=%d,%d", lcid, tx_queue, retx_queue);
}
channels[lcid].buf_retx = retx_queue;
channels[lcid].buf_tx = tx_queue;
}
// Explicit instantiation
template class ue_buffer_manager<true>;
template class ue_buffer_manager<false>;
} // namespace srsenb

@ -156,7 +156,7 @@ int mac::rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint
return ret;
}
int mac::bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, sched_interface::ue_bearer_cfg_t* cfg)
int mac::bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, mac_lc_ch_cfg_t* cfg)
{
srsran::rwlock_read_guard lock(rwlock);
return check_ue_active(rnti) ? scheduler.bearer_ue_cfg(rnti, lc_id, *cfg) : -1;
@ -561,7 +561,7 @@ void mac::rach_detected(uint32_t tti, uint32_t enb_cc_idx, uint32_t preamble_idx
uecfg.supported_cc_list.emplace_back();
uecfg.supported_cc_list.back().active = true;
uecfg.supported_cc_list.back().enb_cc_idx = enb_cc_idx;
uecfg.ue_bearers[0].direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH;
uecfg.ue_bearers[0].direction = mac_lc_ch_cfg_t::BOTH;
uecfg.supported_cc_list[0].dl_cfg.tm = SRSRAN_TM1;
if (ue_cfg(rnti, &uecfg) != SRSRAN_SUCCESS) {
return;

@ -23,7 +23,7 @@ set(SOURCES mac_nr.cc
sched_nr.cc
sched_nr_ue.cc
sched_nr_worker.cc
sched_nr_grant_allocator.cc
sched_nr_grant_allocator.cc
sched_nr_harq.cc
sched_nr_pdcch.cc
sched_nr_cfg.cc
@ -34,3 +34,4 @@ set(SOURCES mac_nr.cc
harq_softbuffer.cc)
add_library(srsgnb_mac STATIC ${SOURCES})
target_link_libraries(srsgnb_mac srsenb_mac_common)

@ -33,10 +33,10 @@
namespace srsenb {
mac_nr::mac_nr(srsran::task_sched_handle task_sched_) :
mac_nr::mac_nr(srsran::task_sched_handle task_sched_, const sched_nr_interface::sched_cfg_t& sched_cfg) :
logger(srslog::fetch_basic_logger("MAC-NR")),
task_sched(task_sched_),
sched(srsenb::sched_nr_interface::sched_cfg_t{}),
sched(sched_cfg),
bcch_bch_payload(srsran::make_byte_buffer())
{
stack_task_queue = task_sched.make_task_queue();
@ -65,12 +65,6 @@ int mac_nr::init(const mac_nr_args_t& args_,
pcap->open(args.pcap.filename);
}
// configure scheduler for 1 carrier
std::vector<srsenb::sched_nr_interface::cell_cfg_t> cells_cfg = srsenb::get_default_cells_cfg(1);
sched.cell_cfg(cells_cfg);
detected_rachs.resize(cells_cfg.size());
logger.info("Started");
started = true;
@ -91,16 +85,19 @@ void mac_nr::stop()
void mac_nr::get_metrics(srsenb::mac_metrics_t& metrics) {}
int mac_nr::cell_cfg(srsenb::sched_interface::cell_cfg_t* cell_cfg)
int mac_nr::cell_cfg(const sched_interface::cell_cfg_t& cell,
srsran::const_span<sched_nr_interface::cell_cfg_t> nr_cells)
{
cfg = *cell_cfg;
cfg = cell;
sched.cell_cfg(nr_cells);
detected_rachs.resize(nr_cells.size());
// read SIBs from RRC (SIB1 for now only)
for (int i = 0; i < 1 /* srsenb::sched_interface::MAX_SIBS */; i++) {
if (cell_cfg->sibs->len > 0) {
if (cell.sibs->len > 0) {
sib_info_t sib = {};
sib.index = i;
sib.periodicity = cell_cfg->sibs->period_rf;
sib.periodicity = cell.sibs->period_rf;
sib.payload = srsran::make_byte_buffer();
if (sib.payload == nullptr) {
logger.error("Couldn't allocate PDU in %s().", __FUNCTION__);
@ -118,6 +115,28 @@ int mac_nr::cell_cfg(srsenb::sched_interface::cell_cfg_t* cell_cfg)
return SRSRAN_SUCCESS;
}
int mac_nr::ue_cfg(uint16_t rnti, const sched_nr_interface::ue_cfg_t& ue_cfg)
{
sched.ue_cfg(rnti, ue_cfg);
return SRSRAN_SUCCESS;
}
uint16_t mac_nr::reserve_rnti(uint32_t enb_cc_idx)
{
uint16_t rnti = alloc_ue(enb_cc_idx);
if (rnti == SRSRAN_INVALID_RNTI) {
return rnti;
}
// Add new user to the scheduler so that it can RX/TX SRB0
srsenb::sched_nr_interface::ue_cfg_t ue_cfg = srsenb::get_default_ue_cfg(1);
ue_cfg.fixed_dl_mcs = args.fixed_dl_mcs;
ue_cfg.fixed_ul_mcs = args.fixed_ul_mcs;
sched.ue_cfg(rnti, ue_cfg);
return rnti;
}
void mac_nr::rach_detected(const rach_info_t& rach_info)
{
static srsran::mutexed_tprof<srsran::avg_time_stats> rach_tprof("rach_tprof", "MAC-NR", 1);
@ -126,12 +145,8 @@ void mac_nr::rach_detected(const rach_info_t& rach_info)
uint32_t enb_cc_idx = 0;
stack_task_queue.push([this, rach_info, enb_cc_idx, rach_tprof_meas]() mutable {
uint16_t rnti = add_ue(enb_cc_idx);
if (rnti == SRSRAN_INVALID_RNTI) {
return;
}
rach_tprof_meas.defer_stop();
uint16_t rnti = reserve_rnti(enb_cc_idx);
// TODO: Generate RAR data
// ..
@ -139,25 +154,14 @@ void mac_nr::rach_detected(const rach_info_t& rach_info)
// Log this event.
++detected_rachs[enb_cc_idx];
// Add new user to the scheduler so that it can RX/TX SRB0
srsenb::sched_nr_interface::ue_cfg_t ue_cfg = srsenb::get_default_ue_cfg(1);
ue_cfg.fixed_dl_mcs = args.fixed_dl_mcs;
ue_cfg.fixed_ul_mcs = args.fixed_ul_mcs;
sched.ue_cfg(rnti, ue_cfg);
// Register new user in RRC
if (rrc->add_user(rnti) == SRSRAN_ERROR) {
// ue_rem(rnti);
return;
}
// Trigger scheduler RACH
srsenb::sched_nr_interface::dl_sched_rar_info_t rar_info = {};
rar_info.preamble_idx = rach_info.preamble;
rar_info.temp_crnti = rnti;
rar_info.ta_cmd = rach_info.time_adv;
rar_info.prach_slot = slot_point{NUMEROLOGY_IDX, rach_info.slot_index};
// TODO: fill remaining fields as required
// sched.dl_rach_info(enb_cc_idx, rar_info);
sched.dl_rach_info(enb_cc_idx, rar_info);
logger.info("RACH: slot=%d, cc=%d, preamble=%d, offset=%d, temp_crnti=0x%x",
rach_info.slot_index,
@ -174,7 +178,7 @@ void mac_nr::rach_detected(const rach_info_t& rach_info)
});
}
uint16_t mac_nr::add_ue(uint32_t enb_cc_idx)
uint16_t mac_nr::alloc_ue(uint32_t enb_cc_idx)
{
ue_nr* inserted_ue = nullptr;
uint16_t rnti = SRSRAN_INVALID_RNTI;
@ -225,16 +229,6 @@ int mac_nr::remove_ue(uint16_t rnti)
return SRSRAN_SUCCESS;
}
uint16_t mac_nr::reserve_rnti()
{
uint16_t rnti = add_ue(0);
if (rnti == SRSRAN_INVALID_RNTI) {
return rnti;
}
return rnti;
}
bool mac_nr::is_rnti_valid_unsafe(uint16_t rnti)
{
if (not started) {
@ -268,29 +262,43 @@ int mac_nr::slot_indication(const srsran_slot_cfg_t& slot_cfg)
int mac_nr::get_dl_sched(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched)
{
logger.set_context(slot_cfg.idx);
if (not pdsch_slot.valid()) {
pdsch_slot = srsran::slot_point{NUMEROLOGY_IDX, slot_cfg.idx};
} else {
pdsch_slot++;
}
int ret = sched.get_dl_sched(pdsch_slot, 0, dl_sched);
for (pdsch_t& pdsch : dl_sched.pdsch) {
for (auto& tb_data : pdsch.data) {
if (tb_data != nullptr) {
// TODO: exclude retx from packing
uint16_t rnti = pdsch.sch.grant.rnti;
srsran::rwlock_read_guard rw_lock(rwlock);
if (not is_rnti_active_unsafe(rnti)) {
continue;
}
ue_db[rnti]->generate_pdu(tb_data, pdsch.sch.grant.tb->tbs / 8);
sched_nr_interface::dl_sched_res_t dl_res;
int ret = sched.get_dl_sched(pdsch_slot, 0, dl_res);
if (ret != SRSRAN_SUCCESS) {
return ret;
}
dl_sched = dl_res.dl_sched;
if (pcap != nullptr) {
uint32_t pid = 0; // TODO: get PID from PDCCH struct?
pcap->write_dl_crnti_nr(tb_data->msg, tb_data->N_bytes, rnti, pid, slot_cfg.idx);
uint32_t rar_count = 0;
srsran::rwlock_read_guard rw_lock(rwlock);
for (pdsch_t& pdsch : dl_sched.pdsch) {
if (pdsch.sch.grant.rnti_type == srsran_rnti_type_c) {
uint16_t rnti = pdsch.sch.grant.rnti;
if (not is_rnti_active_unsafe(rnti)) {
continue;
}
for (auto& tb_data : pdsch.data) {
if (tb_data != nullptr and tb_data->N_bytes == 0) {
// TODO: exclude retx from packing
ue_db[rnti]->generate_pdu(tb_data, pdsch.sch.grant.tb->tbs / 8);
if (pcap != nullptr) {
uint32_t pid = 0; // TODO: get PID from PDCCH struct?
pcap->write_dl_crnti_nr(tb_data->msg, tb_data->N_bytes, rnti, pid, slot_cfg.idx);
}
}
}
} else if (pdsch.sch.grant.rnti_type == srsran_rnti_type_ra) {
sched_nr_interface::sched_rar_t& rar = dl_res.rar[rar_count++];
pdsch.data[0] = assemble_rar(rar.grants);
}
}
return SRSRAN_SUCCESS;
@ -316,11 +324,11 @@ int mac_nr::pucch_info(const srsran_slot_cfg_t& slot_cfg, const mac_interface_ph
return SRSRAN_SUCCESS;
}
bool mac_nr::handle_uci_data(const uint16_t rnti, const srsran_uci_cfg_nr_t& cfg, const srsran_uci_value_nr_t& value)
bool mac_nr::handle_uci_data(const uint16_t rnti, const srsran_uci_cfg_nr_t& cfg_, const srsran_uci_value_nr_t& value)
{
// Process HARQ-ACK
for (uint32_t i = 0; i < cfg.ack.count; i++) {
const srsran_harq_ack_bit_t* ack_bit = &cfg.ack.bits[i];
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;
sched.dl_ack_info(rnti, 0, ack_bit->pid, 0, is_ok);
}
@ -360,4 +368,9 @@ int mac_nr::pusch_info(const srsran_slot_cfg_t& slot_cfg, mac_interface_phy_nr::
return SRSRAN_SUCCESS;
}
srsran::byte_buffer_t* mac_nr::assemble_rar(srsran::const_span<sched_nr_interface::sched_rar_grant_t> grants)
{
return nullptr;
}
} // namespace srsenb

@ -33,24 +33,16 @@ static int assert_ue_cfg_valid(uint16_t rnti, const sched_nr_interface::ue_cfg_t
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class sched_result_manager
class ul_sched_result_buffer
{
public:
explicit sched_result_manager(uint32_t nof_cc_)
explicit ul_sched_result_buffer(uint32_t nof_cc_)
{
for (auto& v : results) {
v.resize(nof_cc_);
}
}
dl_sched_t& add_dl_result(slot_point tti, uint32_t cc)
{
if (not has_dl_result(tti, cc)) {
results[tti.to_uint()][cc].slot_dl = tti;
results[tti.to_uint()][cc].dl_res = {};
}
return results[tti.to_uint()][cc].dl_res;
}
ul_sched_t& add_ul_result(slot_point tti, uint32_t cc)
{
if (not has_ul_result(tti, cc)) {
@ -60,19 +52,8 @@ public:
return results[tti.to_uint()][cc].ul_res;
}
bool has_dl_result(slot_point tti, uint32_t cc) const { return results[tti.to_uint()][cc].slot_dl == tti; }
bool has_ul_result(slot_point tti, uint32_t cc) const { return results[tti.to_uint()][cc].slot_ul == tti; }
dl_sched_t pop_dl_result(slot_point tti, uint32_t cc)
{
if (has_dl_result(tti, cc)) {
results[tti.to_uint()][cc].slot_dl.clear();
return results[tti.to_uint()][cc].dl_res;
}
return {};
}
ul_sched_t pop_ul_result(slot_point tti, uint32_t cc)
{
if (has_ul_result(tti, cc)) {
@ -84,9 +65,7 @@ public:
private:
struct slot_result_t {
slot_point slot_dl;
slot_point slot_ul;
dl_sched_t dl_res;
ul_sched_t ul_res;
};
@ -113,7 +92,7 @@ int sched_nr::cell_cfg(srsran::const_span<cell_cfg_t> cell_list)
cells.emplace_back(new serv_cell_manager{cfg.cells[cc]});
}
pending_results.reset(new sched_result_manager(cell_list.size()));
pending_results.reset(new ul_sched_result_buffer(cell_list.size()));
sched_workers.reset(new sched_nr_impl::sched_worker_manager(ue_db, cfg, cells));
return SRSRAN_SUCCESS;
@ -134,36 +113,28 @@ void sched_nr::ue_cfg_impl(uint16_t rnti, const ue_cfg_t& uecfg)
}
}
/// Generate {tti,cc} scheduling decision
int sched_nr::generate_slot_result(slot_point pdcch_tti, uint32_t cc)
/// Generate {pdcch_slot,cc} scheduling decision
int sched_nr::get_dl_sched(slot_point slot_dl, uint32_t cc, dl_sched_res_t& result)
{
// Copy results to intermediate buffer
dl_sched_t& dl_res = pending_results->add_dl_result(pdcch_tti, cc);
ul_sched_t& ul_res = pending_results->add_ul_result(pdcch_tti, cc);
// Copy UL results to intermediate buffer
ul_sched_t& ul_res = pending_results->add_ul_result(slot_dl, cc);
// Generate {slot_idx,cc} result
sched_workers->run_slot(pdcch_tti, cc, dl_res, ul_res);
sched_workers->run_slot(slot_dl, cc, result, ul_res);
return SRSRAN_SUCCESS;
}
int sched_nr::get_dl_sched(slot_point slot_tx, uint32_t cc, dl_sched_t& result)
{
if (not pending_results->has_dl_result(slot_tx, cc)) {
generate_slot_result(slot_tx, cc);
}
result = pending_results->pop_dl_result(slot_tx, cc);
return SRSRAN_SUCCESS;
}
int sched_nr::get_ul_sched(slot_point pusch_tti, uint32_t cc, ul_sched_t& result)
/// Fetch {ul_slot,cc} UL scheduling decision
int sched_nr::get_ul_sched(slot_point slot_ul, uint32_t cc, ul_sched_t& result)
{
if (not pending_results->has_ul_result(pusch_tti, cc)) {
if (not pending_results->has_ul_result(slot_ul, cc)) {
// sched result hasn't been generated
result = {};
return SRSRAN_SUCCESS;
}
result = pending_results->pop_ul_result(pusch_tti, cc);
result = pending_results->pop_ul_result(slot_ul, cc);
return SRSRAN_SUCCESS;
}
@ -189,6 +160,17 @@ void sched_nr::ul_sr_info(slot_point slot_rx, uint16_t rnti)
sched_workers->enqueue_event(rnti, [this, rnti, slot_rx]() { ue_db[rnti]->ul_sr_info(slot_rx); });
}
void sched_nr::ul_bsr(uint16_t rnti, uint32_t lcg_id, uint32_t bsr)
{
sched_workers->enqueue_event(rnti, [this, rnti, lcg_id, bsr]() { ue_db[rnti]->ul_bsr(lcg_id, bsr); });
}
void sched_nr::dl_buffer_state(uint16_t rnti, uint32_t lcid, uint32_t newtx, uint32_t retx)
{
sched_workers->enqueue_event(rnti,
[this, rnti, lcid, newtx, retx]() { ue_db[rnti]->rlc_buffer_state(lcid, newtx, retx); });
}
#define VERIFY_INPUT(cond, msg, ...) \
do { \
if (not(cond)) { \

@ -86,10 +86,8 @@ void si_sched::run_slot(bwp_slot_allocator& slot_alloc)
ra_sched::ra_sched(const bwp_params& bwp_cfg_) : bwp_cfg(&bwp_cfg_), logger(srslog::fetch_basic_logger("MAC")) {}
alloc_result ra_sched::allocate_pending_rar(bwp_slot_allocator& slot_grid,
const pending_rar_t& rar,
slot_ue_map_t& slot_ues,
uint32_t& nof_grants_alloc)
alloc_result
ra_sched::allocate_pending_rar(bwp_slot_allocator& slot_grid, const pending_rar_t& rar, uint32_t& nof_grants_alloc)
{
const uint32_t rar_aggr_level = 2;
const prb_bitmap& prbs = slot_grid.res_grid()[slot_grid.get_pdcch_tti()].dl_prbs.prbs();
@ -103,8 +101,8 @@ alloc_result ra_sched::allocate_pending_rar(bwp_slot_allocator& slot_grid,
prb_interval interv = find_empty_interval_of_length(prbs, nprb, start_prb_idx);
start_prb_idx = interv.start();
if (interv.length() == nprb) {
ret = slot_grid.alloc_rar_and_msg3(
rar.ra_rnti, rar_aggr_level, interv, slot_ues, msg3_grants.subspan(0, nof_grants_alloc));
ret =
slot_grid.alloc_rar_and_msg3(rar.ra_rnti, rar_aggr_level, interv, msg3_grants.subspan(0, nof_grants_alloc));
} else {
ret = alloc_result::no_sch_space;
}
@ -121,9 +119,9 @@ alloc_result ra_sched::allocate_pending_rar(bwp_slot_allocator& slot_grid,
return ret;
}
void ra_sched::run_slot(bwp_slot_allocator& slot_grid, slot_ue_map_t& slot_ues)
void ra_sched::run_slot(bwp_slot_allocator& slot_alloc)
{
slot_point pdcch_slot = slot_grid.get_pdcch_tti();
slot_point pdcch_slot = slot_alloc.get_pdcch_tti();
slot_point msg3_slot = pdcch_slot + bwp_cfg->pusch_ra_list[0].msg3_delay;
if (not bwp_cfg->slots[pdcch_slot.slot_idx()].is_dl or not bwp_cfg->slots[msg3_slot.slot_idx()].is_ul) {
// RAR only allowed if PDCCH is available and respective Msg3 slot is available for UL
@ -154,7 +152,7 @@ void ra_sched::run_slot(bwp_slot_allocator& slot_grid, slot_ue_map_t& slot_ues)
// Try to schedule DCIs + RBGs for RAR Grants
uint32_t nof_rar_allocs = 0;
alloc_result ret = allocate_pending_rar(slot_grid, rar, slot_ues, nof_rar_allocs);
alloc_result ret = allocate_pending_rar(slot_alloc, rar, nof_rar_allocs);
if (ret == alloc_result::success) {
// If RAR allocation was successful:
@ -218,6 +216,7 @@ int ra_sched::dl_rach_info(const dl_sched_rar_info_t& rar_info)
for (slot_point t = rar_info.prach_slot + prach_duration; t < rar_info.prach_slot + bwp_cfg->slots.size(); ++t) {
if (bwp_cfg->slots[t.slot_idx()].is_dl) {
p.rar_win = {t, t + bwp_cfg->cfg.rar_window_size};
break;
}
}
p.msg3_grant.push_back(rar_info);

@ -30,7 +30,12 @@ namespace srsenb {
namespace sched_nr_impl {
bwp_params::bwp_params(const cell_cfg_t& cell, const sched_cfg_t& sched_cfg_, uint32_t cc_, uint32_t bwp_id_) :
cell_cfg(cell), sched_cfg(sched_cfg_), cc(cc_), bwp_id(bwp_id_), cfg(cell.bwps[bwp_id_])
cell_cfg(cell),
sched_cfg(sched_cfg_),
cc(cc_),
bwp_id(bwp_id_),
cfg(cell.bwps[bwp_id_]),
logger(srslog::fetch_basic_logger(sched_cfg_.logger_name))
{
srsran_assert(bwp_id != 0 or cfg.pdcch.coreset_present[0], "CORESET#0 has to be active for initial BWP");

@ -30,7 +30,8 @@ bwp_slot_grid::bwp_slot_grid(const bwp_params& bwp_cfg_, uint32_t slot_idx_) :
dl_prbs(bwp_cfg_.cfg.rb_width, bwp_cfg_.cfg.start_rb, bwp_cfg_.cfg.pdsch.rbg_size_cfg_1),
ul_prbs(bwp_cfg_.cfg.rb_width, bwp_cfg_.cfg.start_rb, bwp_cfg_.cfg.pdsch.rbg_size_cfg_1),
slot_idx(slot_idx_),
cfg(&bwp_cfg_)
cfg(&bwp_cfg_),
rar_softbuffer(harq_softbuffer_pool::get_instance().get_tx(bwp_cfg_.cfg.rb_width))
{
for (uint32_t cs_idx = 0; cs_idx < SRSRAN_UE_DL_NR_MAX_NOF_CORESET; ++cs_idx) {
if (cfg->cfg.pdcch.coreset_present[cs_idx]) {
@ -66,7 +67,7 @@ bwp_res_grid::bwp_res_grid(const bwp_params& bwp_cfg_) : cfg(&bwp_cfg_)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bwp_slot_allocator::bwp_slot_allocator(bwp_res_grid& bwp_grid_) :
logger(srslog::fetch_basic_logger("MAC")), cfg(*bwp_grid_.cfg), bwp_grid(bwp_grid_)
logger(bwp_grid_.cfg->logger), cfg(*bwp_grid_.cfg), bwp_grid(bwp_grid_)
{}
alloc_result bwp_slot_allocator::alloc_si(uint32_t aggr_idx, uint32_t si_idx, uint32_t si_ntx, const prb_interval& prbs)
@ -93,7 +94,6 @@ alloc_result bwp_slot_allocator::alloc_si(uint32_t aggr_idx, uint32_t si_idx, ui
alloc_result bwp_slot_allocator::alloc_rar_and_msg3(uint16_t ra_rnti,
uint32_t aggr_idx,
prb_interval interv,
slot_ue_map_t& ues,
srsran::const_span<dl_sched_rar_info_t> pending_rars)
{
static const uint32_t msg3_nof_prbs = 3, m = 0;
@ -105,17 +105,13 @@ alloc_result bwp_slot_allocator::alloc_rar_and_msg3(uint16_t
if (ret != alloc_result::success) {
return ret;
}
if (bwp_pdcch_slot.dl_pdcchs.full()) {
logger.warning("SCHED: Maximum number of DL allocations reached");
return alloc_result::no_grant_space;
ret = verify_pdsch_space(bwp_pdcch_slot, bwp_pdcch_slot);
if (ret != alloc_result::success) {
return ret;
}
// Check DL RB collision
const prb_bitmap& pdsch_mask = bwp_pdcch_slot.dl_prbs.prbs();
prb_bitmap dl_mask(pdsch_mask.size());
dl_mask.fill(interv.start(), interv.stop());
if ((pdsch_mask & dl_mask).any()) {
if (bwp_pdcch_slot.dl_prbs.collides(interv)) {
logger.debug("SCHED: Provided RBG mask collides with allocation previously made.");
return alloc_result::sch_collision;
}
@ -139,7 +135,7 @@ alloc_result bwp_slot_allocator::alloc_rar_and_msg3(uint16_t
}
// RAR allocation successful.
bwp_pdcch_slot.dl_prbs |= interv;
// Generate DCI for RAR with given RA-RNTI
pdcch_dl_t& pdcch = bwp_pdcch_slot.dl_pdcchs.back();
if (not fill_dci_rar(interv, ra_rnti, *bwp_grid.cfg, pdcch.dci)) {
@ -147,28 +143,45 @@ alloc_result bwp_slot_allocator::alloc_rar_and_msg3(uint16_t
bwp_pdcch_slot.coresets[coreset_id]->rem_last_dci();
return alloc_result::invalid_coderate;
}
auto& phy_cfg = (*slot_ues)[pending_rars[0].temp_crnti].cfg->phy();
pdcch.dci_cfg = phy_cfg.get_dci_cfg();
// Generate RAR PDSCH
bwp_pdcch_slot.dl_prbs |= interv;
// TODO: Properly fill Msg3 grants
bwp_pdcch_slot.pdschs.emplace_back();
pdsch_t& pdsch = bwp_pdcch_slot.pdschs.back();
srsran_slot_cfg_t slot_cfg;
slot_cfg.idx = pdcch_slot.slot_idx();
bool success = phy_cfg.get_pdsch_cfg(slot_cfg, pdcch.dci, pdsch.sch);
srsran_assert(success, "Error converting DCI to grant");
pdsch.sch.grant.tb[0].softbuffer.tx = bwp_pdcch_slot.rar_softbuffer->get();
// Generate Msg3 grants in PUSCH
uint32_t last_msg3 = msg3_rbs.start();
const int mcs = 0, max_harq_msg3_retx = 4;
int dai = 0;
srsran_slot_cfg_t slot_cfg;
slot_cfg.idx = msg3_slot.slot_idx();
uint32_t last_msg3 = msg3_rbs.start();
const int mcs = 0, max_harq_msg3_retx = 4;
int dai = 0;
slot_cfg.idx = msg3_slot.slot_idx();
bwp_pdcch_slot.rar.emplace_back();
sched_nr_interface::sched_rar_t& rar_out = bwp_pdcch_slot.rar.back();
for (const dl_sched_rar_info_t& grant : pending_rars) {
slot_ue& ue = ues[grant.temp_crnti];
slot_ue& ue = (*slot_ues)[grant.temp_crnti];
// Allocate Msg3
prb_interval msg3_interv{last_msg3, last_msg3 + msg3_nof_prbs};
ue.h_ul = ue.harq_ent->find_empty_ul_harq();
bool success = ue.h_ul->new_tx(msg3_slot, msg3_slot, msg3_interv, mcs, 100, max_harq_msg3_retx);
ue.h_ul = ue.harq_ent->find_empty_ul_harq();
success = ue.h_ul->new_tx(msg3_slot, msg3_slot, msg3_interv, mcs, 100, max_harq_msg3_retx);
srsran_assert(success, "Failed to allocate Msg3");
last_msg3 += msg3_nof_prbs;
pdcch_ul_t msg3_pdcch; // dummy PDCCH for retx=0
fill_dci_msg3(ue, *bwp_grid.cfg, msg3_pdcch.dci);
msg3_pdcch.dci.time_domain_assigment = dai++;
// Generate RAR grant
rar_out.grants.emplace_back();
auto& rar_grant = rar_out.grants.back();
rar_grant.data = grant;
rar_grant.grant.rba = msg3_pdcch.dci.freq_domain_assigment;
rar_grant.grant.trunc_mcs = msg3_pdcch.dci.mcs;
// Generate PUSCH
bwp_msg3_slot.puschs.emplace_back();
pusch_t& pusch = bwp_msg3_slot.puschs.back();
@ -196,14 +209,9 @@ alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, const prb_grant& dl_gr
bwp_slot_grid& bwp_pdcch_slot = bwp_grid[ue.pdcch_slot];
bwp_slot_grid& bwp_pdsch_slot = bwp_grid[ue.pdsch_slot];
bwp_slot_grid& bwp_uci_slot = bwp_grid[ue.uci_slot];
if (not bwp_pdsch_slot.is_dl()) {
logger.warning("SCHED: Trying to allocate PDSCH in TDD non-DL slot index=%d", bwp_pdsch_slot.slot_idx);
return alloc_result::no_sch_space;
}
pdcch_dl_list_t& pdsch_grants = bwp_pdsch_slot.dl_pdcchs;
if (pdsch_grants.full()) {
logger.warning("SCHED: Maximum number of DL allocations reached");
return alloc_result::no_grant_space;
alloc_result result = verify_pdsch_space(bwp_pdsch_slot, bwp_pdcch_slot);
if (result != alloc_result::success) {
return result;
}
if (bwp_pdcch_slot.dl_prbs.collides(dl_grant)) {
return alloc_result::sch_collision;
@ -327,6 +335,23 @@ alloc_result bwp_slot_allocator::alloc_pusch(slot_ue& ue, const prb_grant& ul_pr
return alloc_result::success;
}
alloc_result bwp_slot_allocator::verify_pdsch_space(bwp_slot_grid& bwp_pdsch, bwp_slot_grid& bwp_pdcch) const
{
if (not bwp_pdsch.is_dl() or not bwp_pdcch.is_dl()) {
logger.warning("SCHED: Trying to allocate PDSCH in TDD non-DL slot index=%d", bwp_pdsch.slot_idx);
return alloc_result::no_sch_space;
}
if (bwp_pdcch.dl_pdcchs.full()) {
logger.warning("SCHED: Maximum number of DL PDCCH allocations reached");
return alloc_result::no_cch_space;
}
if (bwp_pdsch.pdschs.full()) {
logger.warning("SCHED: Maximum number of DL PDSCH grants reached");
return alloc_result::no_sch_space;
}
return alloc_result::success;
}
alloc_result bwp_slot_allocator::verify_pusch_space(bwp_slot_grid& pusch_grid, bwp_slot_grid* pdcch_grid) const
{
if (not pusch_grid.is_ul()) {

@ -114,6 +114,21 @@ dl_harq_proc::dl_harq_proc(uint32_t id_, uint32_t nprb) :
harq_proc(id_), softbuffer(harq_softbuffer_pool::get_instance().get_tx(nprb)), pdu(srsran::make_byte_buffer())
{}
bool dl_harq_proc::new_tx(slot_point slot_tx,
slot_point slot_ack,
const prb_grant& grant,
uint32_t mcs,
uint32_t tbs,
uint32_t max_retx)
{
if (harq_proc::new_tx(slot_tx, slot_ack, grant, mcs, tbs, max_retx)) {
softbuffer->reset();
pdu->clear();
return true;
}
return false;
}
harq_entity::harq_entity(uint32_t nprb, uint32_t nof_harq_procs)
{
// Create HARQs

@ -20,8 +20,10 @@
*/
#include "srsenb/hdr/stack/mac/nr/sched_nr_helpers.h"
#include "srsenb/hdr/stack/mac/nr/sched_nr_grant_allocator.h"
#include "srsenb/hdr/stack/mac/nr/sched_nr_harq.h"
#include "srsenb/hdr/stack/mac/nr/sched_nr_ue.h"
#include "srsran/common/string_helpers.h"
namespace srsenb {
namespace sched_nr_impl {
@ -58,7 +60,7 @@ bool fill_dci_rar(prb_interval interv, uint16_t ra_rnti, const bwp_params& bwp_c
{
dci.mcs = 5;
dci.ctx.format = srsran_dci_format_nr_1_0;
dci.ctx.ss_type = srsran_search_space_type_rar;
dci.ctx.ss_type = srsran_search_space_type_common_1;
dci.ctx.rnti_type = srsran_rnti_type_ra;
dci.ctx.rnti = ra_rnti;
dci.ctx.coreset_id = bwp_cfg.cfg.pdcch.ra_search_space.coreset_id;
@ -119,5 +121,82 @@ void fill_ul_dci_ue_fields(const slot_ue& ue,
fill_dci_common(ue, bwp_cfg, dci);
}
void log_sched_bwp_result(srslog::basic_logger& logger,
slot_point pdcch_slot,
const bwp_res_grid& res_grid,
const slot_ue_map_t& slot_ues)
{
const bwp_slot_grid& bwp_slot = res_grid[pdcch_slot];
size_t rar_count = 0;
for (const pdcch_dl_t& pdcch : bwp_slot.dl_pdcchs) {
fmt::memory_buffer fmtbuf;
if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_c) {
const slot_ue& ue = slot_ues[pdcch.dci.ctx.rnti];
fmt::format_to(fmtbuf,
"SCHED: DL {}, cc={}, rnti=0x{:x}, pid={}, f={}, prbs={}, nrtx={}, dai={}, tbs={}, "
"pdsch_slot={}, tti_ack={}",
ue.h_dl->nof_retx() == 0 ? "tx" : "retx",
res_grid.cfg->cc,
ue.rnti,
pdcch.dci.pid,
srsran_dci_format_nr_string(pdcch.dci.ctx.format),
ue.h_dl->prbs(),
ue.h_dl->nof_retx(),
pdcch.dci.dai,
ue.h_dl->tbs(),
ue.pdsch_slot,
ue.uci_slot);
} else if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_ra) {
const pdsch_t& pdsch = bwp_slot.pdschs[std::distance(bwp_slot.dl_pdcchs.data(), &pdcch)];
srsran::const_span<bool> prbs{pdsch.sch.grant.prb_idx, pdsch.sch.grant.prb_idx + pdsch.sch.grant.nof_prb};
uint32_t start_idx = std::distance(prbs.begin(), std::find(prbs.begin(), prbs.end(), true));
uint32_t end_idx = std::distance(prbs.begin(), std::find(prbs.begin() + start_idx, prbs.end(), false));
fmt::format_to(fmtbuf,
"SCHED: DL RAR, cc={}, ra-rnti=0x{:x}, prbs={}, pdsch_slot={}, msg3_slot={}, nof_grants={}",
res_grid.cfg->cc,
pdcch.dci.ctx.rnti,
srsran::interval<uint32_t>{start_idx, end_idx},
pdcch_slot,
pdcch_slot + res_grid.cfg->pusch_ra_list[0].msg3_delay,
bwp_slot.rar[rar_count].grants.size());
rar_count++;
} else {
fmt::format_to(fmtbuf, "SCHED: unknown format");
}
logger.info("%s", srsran::to_c_str(fmtbuf));
}
for (const pdcch_ul_t& pdcch : bwp_slot.ul_pdcchs) {
fmt::memory_buffer fmtbuf;
if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_c) {
const slot_ue& ue = slot_ues[pdcch.dci.ctx.rnti];
fmt::format_to(fmtbuf,
"SCHED: UL {}, cc={}, rnti=0x{:x}, pid={}, f={}, nrtx={}, tbs={}, tti_pusch={}",
ue.h_dl->nof_retx() == 0 ? "tx" : "retx",
res_grid.cfg->cc,
ue.rnti,
pdcch.dci.pid,
srsran_dci_format_nr_string(pdcch.dci.ctx.format),
ue.h_dl->nof_retx(),
ue.h_ul->tbs(),
ue.pusch_slot);
} else if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_tc) {
const slot_ue& ue = slot_ues[pdcch.dci.ctx.rnti];
fmt::format_to(fmtbuf,
"SCHED: UL Msg3, cc={}, tc-rnti=0x{:x}, pid={}, nrtx={}, f={}, tti_pusch={}",
res_grid.cfg->cc,
ue.rnti,
pdcch.dci.pid,
ue.h_dl->nof_retx(),
srsran_dci_format_nr_string(pdcch.dci.ctx.format),
ue.pusch_slot);
} else {
fmt::format_to(fmtbuf, "SCHED: unknown rnti format");
}
logger.info("%s", srsran::to_c_str(fmtbuf));
}
}
} // namespace sched_nr_impl
} // namespace srsenb
} // namespace srsenb

@ -21,6 +21,7 @@
#include "srsenb/hdr/stack/mac/nr/sched_nr_ue.h"
#include "srsenb/hdr/stack/mac/nr/sched_nr_pdcch.h"
#include "srsran/common/string_helpers.h"
namespace srsenb {
namespace sched_nr_impl {
@ -37,23 +38,23 @@ ue_carrier::ue_carrier(uint16_t rnti_, const ue_cfg_t& uecfg_, const sched_cell_
harq_ent(cell_params_.nof_prb())
{}
void ue_carrier::new_slot(slot_point pdcch_slot, const ue_cfg_t& uecfg_)
slot_ue ue_carrier::try_reserve(slot_point pdcch_slot,
const ue_cfg_t& uecfg_,
uint32_t dl_pending_bytes,
uint32_t ul_pending_bytes)
{
slot_point slot_rx = pdcch_slot - TX_ENB_DELAY;
// update CC/BWP config if there were changes
if (bwp_cfg.ue_cfg() != &uecfg_) {
bwp_cfg = bwp_ue_cfg(rnti, cell_params.bwps[0], uecfg_);
}
harq_ent.new_slot(pdcch_slot - TX_ENB_DELAY);
}
slot_ue ue_carrier::try_reserve(slot_point pdcch_slot)
{
slot_point slot_rx = pdcch_slot - TX_ENB_DELAY;
// copy cc-specific parameters and find available HARQs
slot_ue sfu(rnti, slot_rx, cc);
sfu.cfg = &bwp_cfg;
sfu.harq_ent = &harq_ent;
sfu.pdcch_slot = pdcch_slot;
sfu.harq_ent = &harq_ent;
const uint32_t k0 = 0;
sfu.pdsch_slot = sfu.pdcch_slot + k0;
uint32_t k1 =
@ -65,45 +66,81 @@ slot_ue ue_carrier::try_reserve(slot_point pdcch_slot)
sfu.dl_cqi = dl_cqi;
sfu.ul_cqi = ul_cqi;
// set UE-common parameters
sfu.dl_pending_bytes = dl_pending_bytes;
sfu.ul_pending_bytes = ul_pending_bytes;
const srsran_tdd_config_nr_t& tdd_cfg = cell_params.cell_cfg.tdd;
if (srsran_tdd_nr_is_dl(&tdd_cfg, 0, sfu.pdsch_slot.slot_idx())) {
// If DL enabled
sfu.h_dl = harq_ent.find_pending_dl_retx();
if (sfu.h_dl == nullptr) {
if (sfu.h_dl == nullptr and sfu.dl_pending_bytes > 0) {
sfu.h_dl = harq_ent.find_empty_dl_harq();
}
}
if (srsran_tdd_nr_is_ul(&tdd_cfg, 0, sfu.pusch_slot.slot_idx())) {
// If UL enabled
sfu.h_ul = harq_ent.find_pending_ul_retx();
if (sfu.h_ul == nullptr) {
if (sfu.h_ul == nullptr and sfu.ul_pending_bytes > 0) {
sfu.h_ul = harq_ent.find_empty_ul_harq();
}
}
if (sfu.h_dl == nullptr and sfu.h_ul == nullptr) {
// there needs to be at least one available HARQ for newtx/retx
sfu.release();
return sfu;
}
return sfu;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ue::ue(uint16_t rnti_, const ue_cfg_t& cfg, const sched_params& sched_cfg_) :
rnti(rnti_), sched_cfg(sched_cfg_), ue_cfg(cfg)
rnti(rnti_), sched_cfg(sched_cfg_), buffers(srslog::fetch_basic_logger(sched_cfg_.sched_cfg.logger_name))
{
for (uint32_t cc = 0; cc < cfg.carriers.size(); ++cc) {
if (cfg.carriers[cc].active) {
carriers[cc].reset(new ue_carrier(rnti, cfg, sched_cfg.cells[cc]));
}
}
set_cfg(cfg);
}
void ue::set_cfg(const ue_cfg_t& cfg)
{
ue_cfg = cfg;
for (auto& ue_cc_cfg : cfg.carriers) {
if (ue_cc_cfg.active and carriers[ue_cc_cfg.cc] == nullptr) {
carriers[ue_cc_cfg.cc].reset(new ue_carrier(rnti, cfg, sched_cfg.cells[ue_cc_cfg.cc]));
}
}
}
void ue::new_slot(slot_point pdcch_slot)
{
for (auto& ue_cc_cfg : ue_cfg.carriers) {
auto& cc = carriers[ue_cc_cfg.cc];
if (cc != nullptr) {
// Update CC HARQ state
cc->harq_ent.new_slot(pdcch_slot - TX_ENB_DELAY);
}
}
// Compute pending DL/UL bytes for {rnti, pdcch_slot}
if (sched_cfg.sched_cfg.auto_refill_buffer) {
dl_pending_bytes = 1000000;
ul_pending_bytes = 1000000;
} else {
dl_pending_bytes = buffers.get_dl_tx_total();
ul_pending_bytes = buffers.get_bsr();
for (auto& ue_cc_cfg : ue_cfg.carriers) {
auto& cc = carriers[ue_cc_cfg.cc];
if (cc != nullptr) {
// Discount UL HARQ pending bytes to BSR
for (uint32_t pid = 0; pid < cc->harq_ent.nof_dl_harqs(); ++pid) {
ul_pending_bytes -= cc->harq_ent.ul_harq(pid).tbs();
if (last_sr_slot.valid() and cc->harq_ent.ul_harq(pid).harq_slot_tx() > last_sr_slot) {
last_sr_slot.clear();
}
}
}
}
if (ul_pending_bytes == 0 and last_sr_slot.valid()) {
// If unanswered SR is pending
ul_pending_bytes = 512;
}
}
}
slot_ue ue::try_reserve(slot_point pdcch_slot, uint32_t cc)
@ -111,14 +148,12 @@ slot_ue ue::try_reserve(slot_point pdcch_slot, uint32_t cc)
if (carriers[cc] == nullptr) {
return slot_ue();
}
slot_ue sfu = carriers[cc]->try_reserve(pdcch_slot);
slot_ue sfu = carriers[cc]->try_reserve(pdcch_slot, cfg(), dl_pending_bytes, ul_pending_bytes);
if (sfu.empty()) {
return slot_ue();
}
// set UE-common parameters
sfu.pending_sr = pending_sr;
return sfu;
}

@ -67,8 +67,8 @@ void slot_cc_worker::run_feedback(ue_map_t& ue_db)
tmp_feedback_to_run.clear();
}
/// Called at the beginning of TTI in a locked context, to reserve available UE resources
void slot_cc_worker::start(slot_point pdcch_slot, ue_map_t& ue_db)
/// Called within a locked context, to generate {slot, cc} scheduling decision
void slot_cc_worker::run(slot_point pdcch_slot, ue_map_t& ue_db)
{
srsran_assert(not running(), "scheduler worker::start() called for active worker");
slot_rx = pdcch_slot - TX_ENB_DELAY;
@ -84,8 +84,6 @@ void slot_cc_worker::start(slot_point pdcch_slot, ue_map_t& ue_db)
continue;
}
u.carriers[cfg.cc]->new_slot(pdcch_slot, u.cfg());
slot_ues.insert(rnti, u.try_reserve(pdcch_slot, cfg.cc));
if (slot_ues[rnti].empty()) {
// Failed to generate slot UE because UE has no conditions for DL/UL tx
@ -94,34 +92,25 @@ void slot_cc_worker::start(slot_point pdcch_slot, ue_map_t& ue_db)
}
// UE acquired successfully for scheduling in this {slot, cc}
}
}
void slot_cc_worker::run()
{
srsran_assert(running(), "scheduler worker::run() called for non-active worker");
bwp_alloc.new_slot(slot_rx + TX_ENB_DELAY);
// Create an BWP allocator object that will passed along to RA, SI, Data schedulers
bwp_alloc.new_slot(slot_rx + TX_ENB_DELAY, slot_ues);
// Allocate pending RARs
cell.bwps[0].ra.run_slot(bwp_alloc, slot_ues);
cell.bwps[0].ra.run_slot(bwp_alloc);
// TODO: Prioritize PDCCH scheduling for DL and UL data in a Round-Robin fashion
alloc_dl_ues();
alloc_ul_ues();
// Log CC scheduler result
log_result();
log_sched_bwp_result(logger, bwp_alloc.get_pdcch_tti(), cell.bwps[0].grid, slot_ues);
// releases UE resources
slot_ues.clear();
slot_rx = {};
}
void slot_cc_worker::finish()
{
// synchronize results
}
void slot_cc_worker::alloc_dl_ues()
{
if (not cfg.sched_cfg.pdsch_enabled) {
@ -138,66 +127,6 @@ void slot_cc_worker::alloc_ul_ues()
cell.bwps[0].data_sched->sched_ul_users(slot_ues, bwp_alloc);
}
void slot_cc_worker::log_result() const
{
const bwp_slot_grid& bwp_slot = cell.bwps[0].grid[slot_rx + TX_ENB_DELAY];
for (const pdcch_dl_t& pdcch : bwp_slot.dl_pdcchs) {
fmt::memory_buffer fmtbuf;
if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_c) {
const slot_ue& ue = slot_ues[pdcch.dci.ctx.rnti];
fmt::format_to(
fmtbuf,
"SCHED: DL {}, cc={}, rnti=0x{:x}, pid={}, f={}, nrtx={}, dai={}, tbs={}, tti_pdsch={}, tti_ack={}",
ue.h_dl->nof_retx() == 0 ? "tx" : "retx",
cell.cfg.cc,
ue.rnti,
pdcch.dci.pid,
srsran_dci_format_nr_string(pdcch.dci.ctx.format),
ue.h_dl->nof_retx(),
pdcch.dci.dai,
ue.h_dl->tbs(),
ue.pdsch_slot,
ue.uci_slot);
} else if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_ra) {
fmt::format_to(fmtbuf, "SCHED: DL RAR, cc={}, ra-rnti=0x{:x}", cell.cfg.cc, pdcch.dci.ctx.rnti);
} else {
fmt::format_to(fmtbuf, "SCHED: unknown format");
}
logger.info("%s", srsran::to_c_str(fmtbuf));
}
for (const pdcch_ul_t& pdcch : bwp_slot.ul_pdcchs) {
fmt::memory_buffer fmtbuf;
if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_c) {
const slot_ue& ue = slot_ues[pdcch.dci.ctx.rnti];
fmt::format_to(fmtbuf,
"SCHED: UL {}, cc={}, rnti=0x{:x}, pid={}, f={}, nrtx={}, tbs={}, tti_pusch={}",
ue.h_dl->nof_retx() == 0 ? "tx" : "retx",
cell.cfg.cc,
ue.rnti,
pdcch.dci.pid,
srsran_dci_format_nr_string(pdcch.dci.ctx.format),
ue.h_dl->nof_retx(),
ue.h_ul->tbs(),
ue.pusch_slot);
} else if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_tc) {
const slot_ue& ue = slot_ues[pdcch.dci.ctx.rnti];
fmt::format_to(fmtbuf,
"SCHED: UL Msg3, cc={}, tc-rnti=0x{:x}, pid={}, nrtx={}, f={}, tti_pusch={}",
cell.cfg.cc,
ue.rnti,
pdcch.dci.pid,
ue.h_dl->nof_retx(),
srsran_dci_format_nr_string(pdcch.dci.ctx.format),
ue.pusch_slot);
} else {
fmt::format_to(fmtbuf, "SCHED: unknown rnti format");
}
logger.info("%s", srsran::to_c_str(fmtbuf));
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
sched_worker_manager::sched_worker_manager(ue_map_t& ue_db_,
@ -224,7 +153,29 @@ void sched_worker_manager::enqueue_cc_event(uint32_t cc, srsran::move_callback<v
cc_worker_list[cc]->worker.enqueue_cc_event(std::move(ev));
}
void sched_worker_manager::run_slot(slot_point slot_tx, uint32_t cc, dl_sched_t& dl_res, ul_sched_t& ul_res)
/**
* Update UEs state that is non-CC specific (e.g. SRs, buffer status, UE configuration)
* @param slot_tx
* @param update_ca_users to update only UEs with CA enabled or not
*/
void sched_worker_manager::update_ue_db(slot_point slot_tx, bool update_ca_users)
{
// process non-cc specific feedback if pending (e.g. SRs, buffer updates, UE config)
for (ue_event_t& ev : slot_events) {
if (not ue_db.contains(ev.rnti) or ue_db[ev.rnti]->has_ca() == update_ca_users) {
ev.callback();
}
}
// prepare UEs internal state for new slot
for (auto& u : ue_db) {
if (u.second->has_ca() == update_ca_users) {
u.second->new_slot(slot_tx);
}
}
}
void sched_worker_manager::run_slot(slot_point slot_tx, uint32_t cc, dl_sched_res_t& dl_res, ul_sched_t& ul_res)
{
srsran::bounded_vector<std::condition_variable*, SRSRAN_MAX_CARRIERS> waiting_cvars;
{
@ -238,18 +189,14 @@ void sched_worker_manager::run_slot(slot_point slot_tx, uint32_t cc, dl_sched_t&
if (not current_slot.valid()) {
/* First Worker to start slot */
// process non-cc specific feedback if pending (e.g. SRs, buffer updates, UE config) for UEs with CA
// process non-cc specific feedback if pending for UEs with CA
// NOTE: there is no parallelism in these operations
slot_events.clear();
{
std::lock_guard<std::mutex> ev_lock(event_mutex);
next_slot_events.swap(slot_events);
}
for (ue_event_t& ev : slot_events) {
if (not ue_db.contains(ev.rnti) or ue_db[ev.rnti]->has_ca()) {
ev.callback();
}
}
update_ue_db(slot_tx, true);
// mark the start of slot. awake remaining workers if locking on the mutex
current_slot = slot_tx;
@ -270,17 +217,10 @@ void sched_worker_manager::run_slot(slot_point slot_tx, uint32_t cc, dl_sched_t&
/* Parallel Region */
// process non-cc specific feedback if pending (e.g. SRs, buffer updates, UE config) for UEs without CA
for (ue_event_t& ev : slot_events) {
if (ue_db.contains(ev.rnti) and not ue_db[ev.rnti]->has_ca() and ue_db[ev.rnti]->pcell_cc() == cc) {
ev.callback();
}
}
// process pending feedback and pre-cache UE state for slot decision
cc_worker_list[cc]->worker.start(slot_tx, ue_db);
update_ue_db(slot_tx, false);
// Get {slot, cc} scheduling decision
cc_worker_list[cc]->worker.run();
// process pending feedback, generate {slot, cc} scheduling decision
cc_worker_list[cc]->worker.run(slot_tx, ue_db);
// decrement the number of active workers
int rem_workers = worker_count.fetch_sub(1, std::memory_order_release) - 1;
@ -294,7 +234,6 @@ void sched_worker_manager::run_slot(slot_point slot_tx, uint32_t cc, dl_sched_t&
// All the workers of the same slot have finished. Synchronize scheduling decisions with UEs state
for (auto& c : cc_worker_list) {
c->worker.finish();
if (c->waiting > 0) {
waiting_cvars.push_back(&c->cvar);
}
@ -307,19 +246,23 @@ void sched_worker_manager::run_slot(slot_point slot_tx, uint32_t cc, dl_sched_t&
}
}
// Copy results to intermediate buffer
// Post-process and copy results to intermediate buffer
save_sched_result(slot_tx, cc, dl_res, ul_res);
}
bool sched_worker_manager::save_sched_result(slot_point pdcch_slot, uint32_t cc, dl_sched_t& dl_res, ul_sched_t& ul_res)
bool sched_worker_manager::save_sched_result(slot_point pdcch_slot,
uint32_t cc,
dl_sched_res_t& dl_res,
ul_sched_t& ul_res)
{
// NOTE: Unlocked region
auto& bwp_slot = cells[cc]->bwps[0].grid[pdcch_slot];
dl_res.pdcch_dl = bwp_slot.dl_pdcchs;
dl_res.pdcch_ul = bwp_slot.ul_pdcchs;
dl_res.pdsch = bwp_slot.pdschs;
ul_res.pusch = bwp_slot.puschs;
dl_res.dl_sched.pdcch_dl = bwp_slot.dl_pdcchs;
dl_res.dl_sched.pdcch_ul = bwp_slot.ul_pdcchs;
dl_res.dl_sched.pdsch = bwp_slot.pdschs;
dl_res.rar = bwp_slot.rar;
ul_res.pusch = bwp_slot.puschs;
// Group pending HARQ ACKs
srsran_pdsch_ack_nr_t ack = {};

@ -120,7 +120,10 @@ int ue_nr::generate_pdu(srsran::byte_buffer_t* pdu, uint32_t grant_size)
{
std::lock_guard<std::mutex> lock(mutex);
mac_pdu_dl.init_tx(pdu, grant_size);
if (mac_pdu_dl.init_tx(pdu, grant_size) != SRSRAN_SUCCESS) {
logger.error("Couldn't initialize MAC PDU buffer");
return SRSRAN_ERROR;
}
// read RLC PDU
ue_rlc_buffer->clear();

@ -145,7 +145,7 @@ void sched::phy_config_enabled(uint16_t rnti, bool enabled)
rnti, [this, enabled](sched_ue& ue) { ue.phy_config_enabled(last_tti, enabled); }, __PRETTY_FUNCTION__);
}
int sched::bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, const sched_interface::ue_bearer_cfg_t& cfg_)
int sched::bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, const mac_lc_ch_cfg_t& cfg_)
{
return ue_db_access_locked(rnti, [lc_id, cfg_](sched_ue& ue) { ue.set_bearer_cfg(lc_id, cfg_); });
}

@ -459,20 +459,4 @@ int check_ue_cfg_correctness(const sched_interface::ue_cfg_t& ue_cfg)
return ret;
}
const char* to_string(sched_interface::ue_bearer_cfg_t::direction_t dir)
{
switch (dir) {
case sched_interface::ue_bearer_cfg_t::IDLE:
return "idle";
case sched_interface::ue_bearer_cfg_t::BOTH:
return "bi-dir";
case sched_interface::ue_bearer_cfg_t::DL:
return "DL";
case sched_interface::ue_bearer_cfg_t::UL:
return "UL";
default:
return "unrecognized direction";
}
}
} // namespace srsenb

@ -113,7 +113,7 @@ void sched_ue::new_subframe(tti_point tti_rx, uint32_t enb_cc_idx)
*
*******************************************************/
void sched_ue::set_bearer_cfg(uint32_t lc_id, const bearer_cfg_t& cfg_)
void sched_ue::set_bearer_cfg(uint32_t lc_id, const mac_lc_ch_cfg_t& cfg_)
{
cfg.ue_bearers[lc_id] = cfg_;
lch_handler.config_lcid(lc_id, cfg_);
@ -121,8 +121,8 @@ void sched_ue::set_bearer_cfg(uint32_t lc_id, const bearer_cfg_t& cfg_)
void sched_ue::rem_bearer(uint32_t lc_id)
{
cfg.ue_bearers[lc_id] = sched_interface::ue_bearer_cfg_t{};
lch_handler.config_lcid(lc_id, sched_interface::ue_bearer_cfg_t{});
cfg.ue_bearers[lc_id] = mac_lc_ch_cfg_t{};
lch_handler.config_lcid(lc_id, mac_lc_ch_cfg_t{});
}
void sched_ue::phy_config_enabled(tti_point tti_rx, bool enabled)

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save