mirror of https://github.com/pvnis/srsRAN_4G.git
Merge branch 'next' into agpl_next
# Conflicts: # srsue/hdr/phy/ue_lte_phy_base.h # srsue/hdr/phy/ue_nr_phy_base.hmaster
commit
21525935ab
@ -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_ENB_RRC_INTERFACE_MAC_H
|
||||||
|
#define SRSRAN_ENB_RRC_INTERFACE_MAC_H
|
||||||
|
|
||||||
|
#include "srsenb/hdr/stack/mac/sched_interface.h"
|
||||||
|
|
||||||
|
namespace srsenb {
|
||||||
|
|
||||||
|
/// RRC interface for MAC
|
||||||
|
class rrc_interface_mac
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/* Radio Link failure */
|
||||||
|
virtual int add_user(uint16_t rnti, const sched_interface::ue_cfg_t& init_ue_cfg) = 0;
|
||||||
|
virtual void upd_user(uint16_t new_rnti, uint16_t old_rnti) = 0;
|
||||||
|
virtual void set_activity_user(uint16_t rnti) = 0;
|
||||||
|
virtual void set_radiolink_dl_state(uint16_t rnti, bool crc_res) = 0;
|
||||||
|
virtual void set_radiolink_ul_state(uint16_t rnti, bool crc_res) = 0;
|
||||||
|
virtual bool is_paging_opportunity(uint32_t tti_tx_dl, uint32_t* payload_len) = 0;
|
||||||
|
virtual void read_pdu_pcch(uint32_t tti_tx_dl, uint8_t* payload, uint32_t payload_size) = 0;
|
||||||
|
|
||||||
|
///< Provide packed SIB to MAC (buffer is managed by RRC)
|
||||||
|
virtual uint8_t* read_pdu_bcch_dlsch(const uint8_t enb_cc_idx, const uint32_t sib_index) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace srsenb
|
||||||
|
|
||||||
|
#endif // SRSRAN_ENB_RRC_INTERFACE_MAC_H
|
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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_ENB_RRC_INTERFACE_PDCP_H
|
||||||
|
#define SRSRAN_ENB_RRC_INTERFACE_PDCP_H
|
||||||
|
|
||||||
|
#include "srsran/common/byte_buffer.h"
|
||||||
|
|
||||||
|
namespace srsenb {
|
||||||
|
|
||||||
|
/// RRC interface for PDCP
|
||||||
|
class rrc_interface_pdcp
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0;
|
||||||
|
virtual void notify_pdcp_integrity_error(uint16_t rnti, uint32_t lcid) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace srsenb
|
||||||
|
|
||||||
|
#endif // SRSRAN_ENB_RRC_INTERFACE_PDCP_H
|
@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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_ENB_RRC_INTERFACE_RLC_H
|
||||||
|
#define SRSRAN_ENB_RRC_INTERFACE_RLC_H
|
||||||
|
|
||||||
|
#include "srsran/common/byte_buffer.h"
|
||||||
|
|
||||||
|
namespace srsenb {
|
||||||
|
|
||||||
|
/// RRC interface for RLC
|
||||||
|
class rrc_interface_rlc
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void max_retx_attempted(uint16_t rnti) = 0;
|
||||||
|
virtual void protocol_failure(uint16_t rnti) = 0;
|
||||||
|
virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace srsenb
|
||||||
|
|
||||||
|
#endif // SRSRAN_ENB_RRC_INTERFACE_RLC_H
|
@ -0,0 +1,180 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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_NR_SCH_H
|
||||||
|
#define SRSRAN_SCHED_NR_SCH_H
|
||||||
|
|
||||||
|
#include "srsenb/hdr/stack/mac/sched_common.h"
|
||||||
|
#include "srsgnb/hdr/stack/mac/sched_nr_cfg.h"
|
||||||
|
|
||||||
|
namespace srsenb {
|
||||||
|
|
||||||
|
namespace sched_nr_impl {
|
||||||
|
|
||||||
|
using pdsch_alloc_result = srsran::expected<pdsch_t*, alloc_result>;
|
||||||
|
|
||||||
|
class pdsch_allocator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
pdsch_allocator(const bwp_params_t& cfg_, uint32_t sl_index, pdsch_list_t& pdsch_lst);
|
||||||
|
|
||||||
|
/// Get available RBGs for allocation
|
||||||
|
rbg_bitmap occupied_rbgs() const
|
||||||
|
{
|
||||||
|
// Note: in case, RBGs are used, dci format is not 1_0
|
||||||
|
return dl_prbs.rbgs();
|
||||||
|
}
|
||||||
|
/// Get available PRBs for allocation
|
||||||
|
prb_bitmap occupied_prbs(uint32_t ss_id, srsran_dci_format_nr_t dci_fmt) const
|
||||||
|
{
|
||||||
|
if (dci_fmt == srsran_dci_format_nr_1_0) {
|
||||||
|
const srsran_search_space_t* ss = bwp_cfg.get_ss(ss_id);
|
||||||
|
if (ss != nullptr and SRSRAN_SEARCH_SPACE_IS_COMMON(ss->type)) {
|
||||||
|
return (dl_prbs | bwp_cfg.dci_fmt_1_0_excluded_prbs(ss->coreset_id)).prbs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dl_prbs.prbs();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verifies if the input arguments are valid for an SI allocation and grant doesnt collide with other grants
|
||||||
|
alloc_result is_si_grant_valid(uint32_t ss_id, const prb_grant& grant) const;
|
||||||
|
|
||||||
|
/// Verifies if the input arguments are valid for an RAR allocation and grant doesnt collide with other grants
|
||||||
|
alloc_result is_rar_grant_valid(const prb_grant& grant) const;
|
||||||
|
|
||||||
|
/// Verifies if the input arguments are valid for an UE allocation and grant doesnt collide with other grants
|
||||||
|
alloc_result is_ue_grant_valid(const ue_carrier_params_t& ue,
|
||||||
|
uint32_t ss_id,
|
||||||
|
srsran_dci_format_nr_t dci_fmt,
|
||||||
|
const prb_grant& grant) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Tries to allocate UE PDSCH grant. Ensures that there are no collisions with other previous PDSCH allocations
|
||||||
|
* @param ss_id[in] SearchSpaceId used for allocation
|
||||||
|
* @param dci_fmt[in] Chosen DL DCI format
|
||||||
|
* @param grant[in] PRBs used for the grant
|
||||||
|
* @param ue[in] UE carrier parameters
|
||||||
|
* @param dci[out] DCI where frequency_assignment and time_assignment get stored.
|
||||||
|
* @return pdsch_t* of allocated PDSCH in case of success. alloc_result error code in case of failure
|
||||||
|
*/
|
||||||
|
pdsch_alloc_result alloc_ue_pdsch(uint32_t ss_id,
|
||||||
|
srsran_dci_format_nr_t dci_fmt,
|
||||||
|
const prb_grant& grant,
|
||||||
|
const ue_carrier_params_t& ue,
|
||||||
|
srsran_dci_dl_nr_t& dci);
|
||||||
|
|
||||||
|
/// Similar to alloc_ue_pdsch, but it doesn't verify if input parameters are valid
|
||||||
|
pdsch_t& alloc_ue_pdsch_unchecked(uint32_t ss_id,
|
||||||
|
srsran_dci_format_nr_t dci_fmt,
|
||||||
|
const prb_grant& grant,
|
||||||
|
const ue_carrier_params_t& ue,
|
||||||
|
srsran_dci_dl_nr_t& dci);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Tries to allocate SI PDSCH grant. Ensures that there are no collisions with other previous PDSCH allocations
|
||||||
|
* @param ss_id[in] SearchSpaceId used for allocation
|
||||||
|
* @param grant[in] PRBs used for the grant
|
||||||
|
* @param dci[out] DCI where frequency_assignment and time_assignment get stored.
|
||||||
|
* @return pdsch_t* of allocated PDSCH in case of success. alloc_result error code in case of failure
|
||||||
|
*/
|
||||||
|
pdsch_alloc_result alloc_si_pdsch(uint32_t ss_id, const prb_grant& grant, srsran_dci_dl_nr_t& dci);
|
||||||
|
/// Similar to alloc_si_pdsch, but it doesn't verify if input parameters are valid
|
||||||
|
pdsch_t& alloc_si_pdsch_unchecked(uint32_t ss_id, const prb_grant& grant, srsran_dci_dl_nr_t& dci);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Tries to allocate RAR PDSCH grant. Ensures that there are no collisions with other previous PDSCH
|
||||||
|
* allocations
|
||||||
|
* @param grant[in] PRBs used for the grant
|
||||||
|
* @param dci[out] DCI where frequency_assignment and time_assignment get stored.
|
||||||
|
* @return pdsch_t* of allocated PDSCH in case of success. alloc_result error code in case of failure
|
||||||
|
*/
|
||||||
|
pdsch_alloc_result alloc_rar_pdsch(const prb_grant& grant, srsran_dci_dl_nr_t& dci);
|
||||||
|
/// Similar to alloc_rar_pdsch, but it doesn't verify if input parameters are valid
|
||||||
|
pdsch_t& alloc_rar_pdsch_unchecked(const prb_grant& grant, srsran_dci_dl_nr_t& dci);
|
||||||
|
|
||||||
|
/// Cancel last PDSCH allocation
|
||||||
|
void cancel_last_pdsch();
|
||||||
|
|
||||||
|
/// Clear all PDSCHs
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
private:
|
||||||
|
alloc_result is_grant_valid_common(srsran_search_space_type_t ss_type,
|
||||||
|
srsran_dci_format_nr_t dci_fmt,
|
||||||
|
uint32_t coreset_id,
|
||||||
|
const prb_grant& grant) const;
|
||||||
|
pdsch_t& alloc_pdsch_unchecked(uint32_t coreset_id,
|
||||||
|
srsran_search_space_type_t ss_type,
|
||||||
|
srsran_dci_format_nr_t dci_fmt,
|
||||||
|
const prb_grant& grant,
|
||||||
|
srsran_dci_dl_nr_t& dci);
|
||||||
|
|
||||||
|
const bwp_params_t& bwp_cfg;
|
||||||
|
uint32_t slot_idx = 0;
|
||||||
|
|
||||||
|
pdsch_list_t& pdschs;
|
||||||
|
bwp_rb_bitmap dl_prbs;
|
||||||
|
};
|
||||||
|
|
||||||
|
using pusch_alloc_result = srsran::expected<pusch_t*, alloc_result>;
|
||||||
|
|
||||||
|
class pusch_allocator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
pusch_allocator(const bwp_params_t& cfg_, uint32_t sl_index, pusch_list_t& pusch_lst);
|
||||||
|
|
||||||
|
/// Get available RBGs for allocation
|
||||||
|
const rbg_bitmap& occupied_rbgs() const { return ul_prbs.rbgs(); }
|
||||||
|
/// Get available PRBs for allocation
|
||||||
|
const prb_bitmap& occupied_prbs() const { return ul_prbs.prbs(); }
|
||||||
|
|
||||||
|
alloc_result has_grant_space(uint32_t nof_grants = 1, bool verbose = true) const;
|
||||||
|
|
||||||
|
/// Checks if provided PDSCH arguments produce a valid PDSCH that fits into cell PRBs and does not collide with other
|
||||||
|
/// allocations
|
||||||
|
alloc_result is_grant_valid(srsran_search_space_type_t ss_type, const prb_grant& grant, bool verbose = true) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Tries to allocate PDSCH grant. Ensures that there are no collisions with other previous PDSCH allocations
|
||||||
|
* @param ss_type[in] PDCCH chosen search space type
|
||||||
|
* @param grant[in] PRBs used for the grant
|
||||||
|
* @param pdcch[out] DCI where frequency_assignment and time_assignment get stored.
|
||||||
|
* @return pdsch_t object pointer in case of success. alloc_result error code in case of failure
|
||||||
|
*/
|
||||||
|
pusch_alloc_result
|
||||||
|
alloc_pusch(const srsran_search_space_type_t ss_type, const prb_grant& grant, srsran_dci_ul_nr_t& dci);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Allocates PDSCH grant without verifying for collisions. Useful to avoid redundant is_grant_valid(...) calls
|
||||||
|
* @param dci_ctx[in] PDCCH DL DCI context information
|
||||||
|
* @param grant[in] PRBs used for the grant
|
||||||
|
* @param pdcch[out] DCI where frequency and time assignment get stored.
|
||||||
|
*/
|
||||||
|
pusch_t& alloc_pusch_unchecked(const prb_grant& grant, srsran_dci_ul_nr_t& dci);
|
||||||
|
|
||||||
|
void cancel_last_pusch();
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
private:
|
||||||
|
const bwp_params_t& bwp_cfg;
|
||||||
|
uint32_t slot_idx = 0;
|
||||||
|
|
||||||
|
pusch_list_t& puschs;
|
||||||
|
bwp_rb_bitmap ul_prbs;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace sched_nr_impl
|
||||||
|
|
||||||
|
} // namespace srsenb
|
||||||
|
|
||||||
|
#endif // SRSRAN_SCHED_NR_SCH_H
|
@ -0,0 +1,362 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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 "srsgnb/hdr/stack/mac/sched_nr_sch.h"
|
||||||
|
#include "srsran/common/string_helpers.h"
|
||||||
|
|
||||||
|
namespace srsenb {
|
||||||
|
namespace sched_nr_impl {
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
void log_alloc_failure(srslog::log_channel& log_ch, const char* cause_fmt, Args&&... args)
|
||||||
|
{
|
||||||
|
if (not log_ch.enabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log allocation failure
|
||||||
|
fmt::memory_buffer fmtbuf;
|
||||||
|
fmt::format_to(fmtbuf, "SCHED: Failure to allocate PDSCH. Cause: ");
|
||||||
|
fmt::format_to(fmtbuf, cause_fmt, std::forward<Args>(args)...);
|
||||||
|
log_ch("%s", srsran::to_c_str(fmtbuf));
|
||||||
|
}
|
||||||
|
|
||||||
|
pdsch_allocator::pdsch_allocator(const bwp_params_t& cfg_, uint32_t slot_index, pdsch_list_t& pdsch_lst) :
|
||||||
|
bwp_cfg(cfg_),
|
||||||
|
slot_idx(slot_index),
|
||||||
|
pdschs(pdsch_lst),
|
||||||
|
dl_prbs(bwp_cfg.cfg.rb_width, bwp_cfg.cfg.start_rb, bwp_cfg.cfg.pdsch.rbg_size_cfg_1)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void pdsch_allocator::reset()
|
||||||
|
{
|
||||||
|
pdschs.clear();
|
||||||
|
dl_prbs.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
alloc_result pdsch_allocator::is_grant_valid_common(srsran_search_space_type_t ss_type,
|
||||||
|
srsran_dci_format_nr_t dci_fmt,
|
||||||
|
uint32_t coreset_id,
|
||||||
|
const prb_grant& grant) const
|
||||||
|
{
|
||||||
|
// DL must be active in given slot
|
||||||
|
if (not bwp_cfg.slots[slot_idx].is_dl) {
|
||||||
|
log_alloc_failure(bwp_cfg.logger.error, "DL is disabled for slot={}", slot_idx);
|
||||||
|
return alloc_result::no_sch_space;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No space in Scheduler PDSCH output list
|
||||||
|
if (pdschs.full()) {
|
||||||
|
log_alloc_failure(bwp_cfg.logger.warning, "Maximum number of PDSCHs={} reached.", pdschs.size());
|
||||||
|
return alloc_result::no_sch_space;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TS 38.214, 5.1.2.2 - "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_fmt == srsran_dci_format_nr_1_0 and not grant.is_alloc_type1()) {
|
||||||
|
log_alloc_failure(bwp_cfg.logger.warning, "DL Resource Allocation type 1 must be used in case of DCI format 1_0.");
|
||||||
|
return alloc_result::invalid_grant_params;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TS 38.214 - 5.1.2.2 - For DCI format 1_0 and Common Search Space, the list of available PRBs is limited by the
|
||||||
|
// rb_start and bandwidth of the coreset
|
||||||
|
if (dci_fmt == srsran_dci_format_nr_1_0 and SRSRAN_SEARCH_SPACE_IS_COMMON(ss_type)) {
|
||||||
|
// Grant PRBs do not collide with CORESET PRB limits (in case of common SearchSpace)
|
||||||
|
if (bwp_cfg.dci_fmt_1_0_excluded_prbs(coreset_id).collides(grant)) {
|
||||||
|
log_alloc_failure(
|
||||||
|
bwp_cfg.logger.debug, "Provided PRB grant={:x} falls outside common CORESET PRB boundaries.", grant);
|
||||||
|
return alloc_result::sch_collision;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grant PRBs do not collide with previous PDSCH allocations
|
||||||
|
if (dl_prbs.collides(grant)) {
|
||||||
|
log_alloc_failure(
|
||||||
|
bwp_cfg.logger.debug, "Provided PRB grant={:x} collides with allocations previously made.", grant);
|
||||||
|
return alloc_result::sch_collision;
|
||||||
|
}
|
||||||
|
|
||||||
|
return alloc_result::success;
|
||||||
|
}
|
||||||
|
|
||||||
|
alloc_result pdsch_allocator::is_si_grant_valid(uint32_t ss_id, const prb_grant& grant) const
|
||||||
|
{
|
||||||
|
// Verify SearchSpace validity
|
||||||
|
const srsran_search_space_t* ss = bwp_cfg.get_ss(ss_id);
|
||||||
|
if (ss == nullptr) {
|
||||||
|
// Couldn't find SearchSpace
|
||||||
|
log_alloc_failure(bwp_cfg.logger.error, "SearchSpace has not been configured.");
|
||||||
|
return alloc_result::invalid_grant_params;
|
||||||
|
}
|
||||||
|
return is_grant_valid_common(ss->type, srsran_dci_format_nr_1_0, ss->coreset_id, grant);
|
||||||
|
}
|
||||||
|
|
||||||
|
alloc_result pdsch_allocator::is_rar_grant_valid(const prb_grant& grant) const
|
||||||
|
{
|
||||||
|
srsran_sanity_check(bwp_cfg.cfg.pdcch.ra_search_space_present,
|
||||||
|
"Attempting RAR allocation in BWP with no raSearchSpace");
|
||||||
|
return is_grant_valid_common(bwp_cfg.cfg.pdcch.ra_search_space.type,
|
||||||
|
srsran_dci_format_nr_1_0,
|
||||||
|
bwp_cfg.cfg.pdcch.ra_search_space.coreset_id,
|
||||||
|
grant);
|
||||||
|
}
|
||||||
|
|
||||||
|
alloc_result pdsch_allocator::is_ue_grant_valid(const ue_carrier_params_t& ue,
|
||||||
|
uint32_t ss_id,
|
||||||
|
srsran_dci_format_nr_t dci_fmt,
|
||||||
|
const prb_grant& grant) const
|
||||||
|
{
|
||||||
|
const srsran_search_space_t* ss = ue.get_ss(ss_id);
|
||||||
|
if (ss == nullptr) {
|
||||||
|
// Couldn't find SearchSpace
|
||||||
|
log_alloc_failure(bwp_cfg.logger.error, "rnti=0x%x,SearchSpaceId={} has not been configured.", ue.rnti, ss_id);
|
||||||
|
return alloc_result::invalid_grant_params;
|
||||||
|
}
|
||||||
|
alloc_result ret = is_grant_valid_common(ss->type, dci_fmt, ss->coreset_id, grant);
|
||||||
|
if (ret != alloc_result::success) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TS 38.214, 5.1.2.2 - "the UE shall use the downlink frequency resource allocation type as defined by the higher
|
||||||
|
// layer parameter resourceAllocation"
|
||||||
|
if (ue.phy().pdsch.alloc != srsran_resource_alloc_dynamic) {
|
||||||
|
if ((ue.phy().pdsch.alloc == srsran_resource_alloc_type0) != grant.is_alloc_type0()) {
|
||||||
|
log_alloc_failure(bwp_cfg.logger.warning,
|
||||||
|
"UE rnti=0x{:x} PDSCH RA configuration type {} doesn't match grant type",
|
||||||
|
ue.rnti,
|
||||||
|
grant.is_alloc_type0() ? 0 : 1);
|
||||||
|
return alloc_result::invalid_grant_params;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return alloc_result::success;
|
||||||
|
}
|
||||||
|
|
||||||
|
pdsch_alloc_result pdsch_allocator::alloc_si_pdsch(uint32_t ss_id, const prb_grant& grant, srsran_dci_dl_nr_t& dci)
|
||||||
|
{
|
||||||
|
alloc_result code = is_si_grant_valid(ss_id, grant);
|
||||||
|
if (code != alloc_result::success) {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
return {&alloc_si_pdsch_unchecked(ss_id, grant, dci)};
|
||||||
|
}
|
||||||
|
|
||||||
|
pdsch_t& pdsch_allocator::alloc_si_pdsch_unchecked(uint32_t ss_id, const prb_grant& grant, srsran_dci_dl_nr_t& dci)
|
||||||
|
{
|
||||||
|
// Verify SearchSpace validity
|
||||||
|
const srsran_search_space_t* ss = bwp_cfg.get_ss(ss_id);
|
||||||
|
srsran_sanity_check(ss != nullptr, "SearchSpace has not been configured");
|
||||||
|
return alloc_pdsch_unchecked(ss->coreset_id, ss->type, srsran_dci_format_nr_1_0, grant, dci);
|
||||||
|
}
|
||||||
|
|
||||||
|
pdsch_alloc_result pdsch_allocator::alloc_rar_pdsch(const prb_grant& grant, srsran_dci_dl_nr_t& dci)
|
||||||
|
{
|
||||||
|
alloc_result code = is_rar_grant_valid(grant);
|
||||||
|
if (code != alloc_result::success) {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
return {&alloc_rar_pdsch_unchecked(grant, dci)};
|
||||||
|
}
|
||||||
|
|
||||||
|
pdsch_t& pdsch_allocator::alloc_rar_pdsch_unchecked(const prb_grant& grant, srsran_dci_dl_nr_t& dci)
|
||||||
|
{
|
||||||
|
// TS 38.213, 8.2 - "In response to a PRACH transmission, a UE attempts to detect a DCI format 1_0"
|
||||||
|
const static srsran_dci_format_nr_t dci_fmt = srsran_dci_format_nr_1_0;
|
||||||
|
|
||||||
|
return alloc_pdsch_unchecked(
|
||||||
|
bwp_cfg.cfg.pdcch.ra_search_space.coreset_id, bwp_cfg.cfg.pdcch.ra_search_space.type, dci_fmt, grant, dci);
|
||||||
|
}
|
||||||
|
|
||||||
|
pdsch_alloc_result pdsch_allocator::alloc_ue_pdsch(uint32_t ss_id,
|
||||||
|
srsran_dci_format_nr_t dci_fmt,
|
||||||
|
const prb_grant& grant,
|
||||||
|
const ue_carrier_params_t& ue,
|
||||||
|
srsran_dci_dl_nr_t& dci)
|
||||||
|
{
|
||||||
|
alloc_result code = is_ue_grant_valid(ue, ss_id, dci_fmt, grant);
|
||||||
|
if (code != alloc_result::success) {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
return {&alloc_ue_pdsch_unchecked(ss_id, dci_fmt, grant, ue, dci)};
|
||||||
|
}
|
||||||
|
|
||||||
|
pdsch_t& pdsch_allocator::alloc_ue_pdsch_unchecked(uint32_t ss_id,
|
||||||
|
srsran_dci_format_nr_t dci_fmt,
|
||||||
|
const prb_grant& grant,
|
||||||
|
const ue_carrier_params_t& ue,
|
||||||
|
srsran_dci_dl_nr_t& dci)
|
||||||
|
{
|
||||||
|
const srsran_search_space_t* ss = ue.get_ss(ss_id);
|
||||||
|
srsran_sanity_check(ss != nullptr, "SearchSpace has not been configured");
|
||||||
|
return alloc_pdsch_unchecked(ss->coreset_id, ss->type, dci_fmt, grant, dci);
|
||||||
|
}
|
||||||
|
|
||||||
|
pdsch_t& pdsch_allocator::alloc_pdsch_unchecked(uint32_t coreset_id,
|
||||||
|
srsran_search_space_type_t ss_type,
|
||||||
|
srsran_dci_format_nr_t dci_fmt,
|
||||||
|
const prb_grant& grant,
|
||||||
|
srsran_dci_dl_nr_t& out_dci)
|
||||||
|
{
|
||||||
|
// Create new PDSCH entry in output PDSCH list
|
||||||
|
pdschs.emplace_back();
|
||||||
|
pdsch_t& pdsch = pdschs.back();
|
||||||
|
|
||||||
|
// Register allocated PRBs in accumulated bitmap
|
||||||
|
dl_prbs |= grant;
|
||||||
|
|
||||||
|
// Fill DCI with PDSCH freq/time allocation information
|
||||||
|
out_dci.time_domain_assigment = 0;
|
||||||
|
if (grant.is_alloc_type0()) {
|
||||||
|
out_dci.freq_domain_assigment = grant.rbgs().to_uint64();
|
||||||
|
} else {
|
||||||
|
uint32_t rb_start = grant.prbs().start(), nof_prb = bwp_cfg.nof_prb();
|
||||||
|
if (SRSRAN_SEARCH_SPACE_IS_COMMON(ss_type)) {
|
||||||
|
prb_interval lims = bwp_cfg.coreset_prb_range(coreset_id);
|
||||||
|
if (dci_fmt == srsran_dci_format_nr_1_0) {
|
||||||
|
srsran_sanity_check(rb_start >= lims.start(), "Invalid PRB grant");
|
||||||
|
rb_start -= lims.start();
|
||||||
|
}
|
||||||
|
if (coreset_id == 0) {
|
||||||
|
nof_prb = lims.length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
srsran_sanity_check(rb_start + grant.prbs().length() <= nof_prb, "Invalid PRB grant");
|
||||||
|
out_dci.freq_domain_assigment = srsran_ra_nr_type1_riv(nof_prb, rb_start, grant.prbs().length());
|
||||||
|
}
|
||||||
|
|
||||||
|
return pdsch;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pdsch_allocator::cancel_last_pdsch()
|
||||||
|
{
|
||||||
|
srsran_assert(not pdschs.empty(), "Trying to abort PDSCH allocation that does not exist");
|
||||||
|
pdschs.pop_back();
|
||||||
|
// TODO: clear bitmap allocated RBs
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
void log_pusch_alloc_failure(srslog::log_channel& log_ch, const char* cause_fmt, Args&&... args)
|
||||||
|
{
|
||||||
|
if (not log_ch.enabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log allocation failure
|
||||||
|
fmt::memory_buffer fmtbuf;
|
||||||
|
fmt::format_to(fmtbuf, "SCHED: Failure to allocate PUSCH. Cause: ");
|
||||||
|
fmt::format_to(fmtbuf, cause_fmt, std::forward<Args>(args)...);
|
||||||
|
log_ch("%s", srsran::to_c_str(fmtbuf));
|
||||||
|
}
|
||||||
|
|
||||||
|
pusch_allocator::pusch_allocator(const bwp_params_t& cfg_, uint32_t sl_index, pusch_list_t& pusch_lst) :
|
||||||
|
bwp_cfg(cfg_),
|
||||||
|
slot_idx(sl_index),
|
||||||
|
puschs(pusch_lst),
|
||||||
|
ul_prbs(bwp_cfg.cfg.rb_width, bwp_cfg.cfg.start_rb, bwp_cfg.cfg.pdsch.rbg_size_cfg_1)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void pusch_allocator::reset()
|
||||||
|
{
|
||||||
|
puschs.clear();
|
||||||
|
ul_prbs.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
alloc_result pusch_allocator::has_grant_space(uint32_t nof_grants, bool verbose) const
|
||||||
|
{
|
||||||
|
// UL must be active in given slot
|
||||||
|
if (not bwp_cfg.slots[slot_idx].is_ul) {
|
||||||
|
if (verbose) {
|
||||||
|
log_pusch_alloc_failure(bwp_cfg.logger.error, "UL is disabled for slot={}", slot_idx);
|
||||||
|
}
|
||||||
|
return alloc_result::no_sch_space;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No space in Scheduler PDSCH output list
|
||||||
|
if (puschs.size() + nof_grants > puschs.capacity()) {
|
||||||
|
if (verbose) {
|
||||||
|
log_pusch_alloc_failure(bwp_cfg.logger.warning, "Maximum number of PUSCHs={} reached.", puschs.capacity());
|
||||||
|
}
|
||||||
|
return alloc_result::no_sch_space;
|
||||||
|
}
|
||||||
|
|
||||||
|
return alloc_result::success;
|
||||||
|
}
|
||||||
|
|
||||||
|
alloc_result
|
||||||
|
pusch_allocator::is_grant_valid(srsran_search_space_type_t ss_type, const prb_grant& grant, bool verbose) const
|
||||||
|
{
|
||||||
|
alloc_result ret = has_grant_space(1, verbose);
|
||||||
|
if (ret != alloc_result::success) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SRSRAN_SEARCH_SPACE_IS_COMMON(ss_type)) {
|
||||||
|
// In case of common SearchSpaces, the PRBs must be contiguous
|
||||||
|
if (grant.is_alloc_type0()) {
|
||||||
|
log_pusch_alloc_failure(bwp_cfg.logger.warning, "AllocType0 not allowed in common SearchSpace.");
|
||||||
|
return alloc_result::invalid_grant_params;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grant PRBs do not collide with previous PDSCH allocations
|
||||||
|
if (ul_prbs.collides(grant)) {
|
||||||
|
if (verbose) {
|
||||||
|
log_pusch_alloc_failure(bwp_cfg.logger.debug, "SCHED: Provided UL PRB mask collides with previous allocations.");
|
||||||
|
}
|
||||||
|
return alloc_result::sch_collision;
|
||||||
|
}
|
||||||
|
|
||||||
|
return alloc_result::success;
|
||||||
|
}
|
||||||
|
|
||||||
|
pusch_alloc_result
|
||||||
|
pusch_allocator::alloc_pusch(const srsran_search_space_type_t ss_type, const prb_grant& grant, srsran_dci_ul_nr_t& dci)
|
||||||
|
{
|
||||||
|
alloc_result code = is_grant_valid(ss_type, grant);
|
||||||
|
if (code != alloc_result::success) {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {&alloc_pusch_unchecked(grant, dci)};
|
||||||
|
}
|
||||||
|
|
||||||
|
pusch_t& pusch_allocator::alloc_pusch_unchecked(const prb_grant& grant, srsran_dci_ul_nr_t& out_dci)
|
||||||
|
{
|
||||||
|
// Create new PUSCH entry in output PUSCH list
|
||||||
|
puschs.emplace_back();
|
||||||
|
pusch_t& pusch = puschs.back();
|
||||||
|
|
||||||
|
// Register allocated PRBs in accumulated bitmap
|
||||||
|
ul_prbs |= grant;
|
||||||
|
|
||||||
|
// Fill DCI with PUSCH freq/time allocation information
|
||||||
|
out_dci.time_domain_assigment = 0;
|
||||||
|
if (grant.is_alloc_type0()) {
|
||||||
|
out_dci.freq_domain_assigment = grant.rbgs().to_uint64();
|
||||||
|
} else {
|
||||||
|
uint32_t nof_prb = bwp_cfg.nof_prb();
|
||||||
|
out_dci.freq_domain_assigment = srsran_ra_nr_type1_riv(nof_prb, grant.prbs().start(), grant.prbs().length());
|
||||||
|
}
|
||||||
|
|
||||||
|
return pusch;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pusch_allocator::cancel_last_pusch()
|
||||||
|
{
|
||||||
|
srsran_assert(not puschs.empty(), "Trying to abort PUSCH allocation that does not exist");
|
||||||
|
puschs.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace sched_nr_impl
|
||||||
|
} // namespace srsenb
|
@ -0,0 +1,309 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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 "sched_nr_cfg_generators.h"
|
||||||
|
#include "sched_nr_common_test.h"
|
||||||
|
#include "srsgnb/hdr/stack/mac/sched_nr_interface_utils.h"
|
||||||
|
#include "srsgnb/hdr/stack/mac/sched_nr_pdcch.h"
|
||||||
|
#include "srsran/common/test_common.h"
|
||||||
|
|
||||||
|
namespace srsenb {
|
||||||
|
|
||||||
|
using namespace sched_nr_impl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for the case CORESET#0 is active.
|
||||||
|
* Given only one PDCCH candidate position is supported, only one PDCCH allocation should take place per slot
|
||||||
|
* The test additionally verifies that the DCI context content is correct for each PDCCH allocation
|
||||||
|
*/
|
||||||
|
void test_coreset0_cfg()
|
||||||
|
{
|
||||||
|
const uint32_t aggr_idx = 2;
|
||||||
|
|
||||||
|
srsran::test_delimit_logger delimiter{"Test PDCCH Allocation in CORESET#0"};
|
||||||
|
|
||||||
|
sched_nr_impl::cell_cfg_t cell_cfg = get_default_sa_cell_cfg_common();
|
||||||
|
sched_nr_interface::sched_args_t sched_args;
|
||||||
|
bwp_params_t bwp_params{cell_cfg, sched_args, 0, 0};
|
||||||
|
|
||||||
|
// UE config
|
||||||
|
ue_cfg_t uecfg = get_rach_ue_cfg(0);
|
||||||
|
uecfg.phy_cfg.pdcch = cell_cfg.bwps[0].pdcch; // Starts without UE-specific PDCCH
|
||||||
|
ue_carrier_params_t ue_cc{0x46, bwp_params, uecfg};
|
||||||
|
|
||||||
|
pdcch_dl_list_t dl_pdcchs;
|
||||||
|
pdcch_ul_list_t ul_pdcchs;
|
||||||
|
pdcch_dl_alloc_result dl_pdcch_result;
|
||||||
|
pdcch_ul_alloc_result ul_pdcch_result;
|
||||||
|
pdcch_dl_t* dl_pdcch = nullptr;
|
||||||
|
pdcch_ul_t* ul_pdcch = nullptr;
|
||||||
|
|
||||||
|
bwp_pdcch_allocator pdcch_sched(bwp_params, 0, dl_pdcchs, ul_pdcchs);
|
||||||
|
for (const srsran_coreset_t& cs : view_active_coresets(cell_cfg.bwps[0].pdcch)) {
|
||||||
|
// Verify nof CCEs is correctly computed
|
||||||
|
TESTASSERT_EQ(coreset_nof_cces(cs), pdcch_sched.nof_cces(cs.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slot with SIB1
|
||||||
|
TESTASSERT_EQ(0, pdcch_sched.nof_allocations());
|
||||||
|
|
||||||
|
// SIB1 allocation should be successful
|
||||||
|
dl_pdcch_result = pdcch_sched.alloc_si_pdcch(0, aggr_idx);
|
||||||
|
TESTASSERT(dl_pdcch_result.has_value());
|
||||||
|
dl_pdcch = dl_pdcch_result.value();
|
||||||
|
TESTASSERT_EQ(1, pdcch_sched.nof_allocations());
|
||||||
|
TESTASSERT_EQ(srsran_rnti_type_si, dl_pdcch->dci.ctx.rnti_type);
|
||||||
|
TESTASSERT_EQ(0, dl_pdcch->dci.ctx.coreset_id);
|
||||||
|
test_dci_ctx_consistency(bwp_params.cfg.pdcch, dl_pdcch->dci.ctx);
|
||||||
|
|
||||||
|
// No space for RAR, UE PDSCH/PUSCH
|
||||||
|
TESTASSERT(pdcch_sched.alloc_rar_pdcch(0x2, aggr_idx).error() == alloc_result::no_cch_space);
|
||||||
|
TESTASSERT(pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 1, aggr_idx, ue_cc).error() == alloc_result::no_cch_space);
|
||||||
|
TESTASSERT(pdcch_sched.alloc_ul_pdcch(1, aggr_idx, ue_cc).error() == alloc_result::no_cch_space);
|
||||||
|
|
||||||
|
srslog::fetch_basic_logger("TEST").info("%s", pdcch_sched.print_allocations());
|
||||||
|
|
||||||
|
// Slot with RAR
|
||||||
|
pdcch_sched.reset();
|
||||||
|
|
||||||
|
// RAR allocation should be successful
|
||||||
|
dl_pdcch_result = pdcch_sched.alloc_rar_pdcch(0x2, aggr_idx);
|
||||||
|
TESTASSERT(dl_pdcch_result.has_value());
|
||||||
|
dl_pdcch = dl_pdcch_result.value();
|
||||||
|
TESTASSERT_EQ(1, pdcch_sched.nof_allocations());
|
||||||
|
TESTASSERT_EQ(srsran_rnti_type_ra, dl_pdcch->dci.ctx.rnti_type);
|
||||||
|
TESTASSERT_EQ(0, dl_pdcch->dci.ctx.coreset_id);
|
||||||
|
TESTASSERT_EQ(srsran_search_space_type_common_1, dl_pdcch->dci.ctx.ss_type);
|
||||||
|
test_dci_ctx_consistency(bwp_params.cfg.pdcch, dl_pdcch->dci.ctx);
|
||||||
|
|
||||||
|
// No space for RAR, UE PDSCH/PUSCH
|
||||||
|
TESTASSERT(pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 1, aggr_idx, ue_cc).error() == alloc_result::no_cch_space);
|
||||||
|
TESTASSERT(pdcch_sched.alloc_ul_pdcch(1, aggr_idx, ue_cc).error() == alloc_result::no_cch_space);
|
||||||
|
|
||||||
|
srslog::fetch_basic_logger("TEST").info("%s", pdcch_sched.print_allocations());
|
||||||
|
|
||||||
|
// Slot with DL PDSCH
|
||||||
|
pdcch_sched.reset();
|
||||||
|
|
||||||
|
// 1st PDCCH allocation for DL should be successful
|
||||||
|
dl_pdcch_result = pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 1, aggr_idx, ue_cc);
|
||||||
|
TESTASSERT(dl_pdcch_result.has_value());
|
||||||
|
dl_pdcch = dl_pdcch_result.value();
|
||||||
|
TESTASSERT_EQ(1, pdcch_sched.nof_allocations());
|
||||||
|
TESTASSERT_EQ(srsran_rnti_type_c, dl_pdcch->dci.ctx.rnti_type);
|
||||||
|
TESTASSERT_EQ(0u, dl_pdcch->dci.ctx.coreset_id);
|
||||||
|
TESTASSERT_EQ(srsran_search_space_type_common_1, dl_pdcch->dci.ctx.ss_type);
|
||||||
|
test_dci_ctx_consistency(bwp_params.cfg.pdcch, dl_pdcch->dci.ctx);
|
||||||
|
|
||||||
|
// No space for 2nd PDCCH allocation
|
||||||
|
TESTASSERT(pdcch_sched.alloc_ul_pdcch(1, aggr_idx, ue_cc).error() == alloc_result::no_cch_space);
|
||||||
|
|
||||||
|
srslog::fetch_basic_logger("TEST").info("%s", pdcch_sched.print_allocations());
|
||||||
|
|
||||||
|
// Slot with UL PDSCH
|
||||||
|
pdcch_sched.reset();
|
||||||
|
|
||||||
|
// 1st PDCCH allocation for UL should be successful
|
||||||
|
ul_pdcch_result = pdcch_sched.alloc_ul_pdcch(1, aggr_idx, ue_cc);
|
||||||
|
TESTASSERT(ul_pdcch_result.has_value());
|
||||||
|
ul_pdcch = ul_pdcch_result.value();
|
||||||
|
TESTASSERT_EQ(1, pdcch_sched.nof_allocations());
|
||||||
|
TESTASSERT_EQ(srsran_rnti_type_c, ul_pdcch->dci.ctx.rnti_type);
|
||||||
|
TESTASSERT_EQ(0u, ul_pdcch->dci.ctx.coreset_id);
|
||||||
|
TESTASSERT_EQ(srsran_search_space_type_common_1, ul_pdcch->dci.ctx.ss_type);
|
||||||
|
test_dci_ctx_consistency(bwp_params.cfg.pdcch, ul_pdcch->dci.ctx);
|
||||||
|
|
||||||
|
// No space for 2nd PDCCH allocation
|
||||||
|
TESTASSERT(pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 1, aggr_idx, ue_cc).error() == alloc_result::no_cch_space);
|
||||||
|
|
||||||
|
srslog::fetch_basic_logger("TEST").info("%s", pdcch_sched.print_allocations());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for the case CORESET#2 is active.
|
||||||
|
* The PDCCH allocator should find enough space to fit SIB1/RAR (in CORESET#0) and UE-dedicated PDCCHs in (CORESET#2)
|
||||||
|
* The test additionally verifies that the DCI context content is correct for each PDCCH allocation and there are no
|
||||||
|
* collisions between PDCCH CCE allocations
|
||||||
|
*/
|
||||||
|
void test_coreset2_cfg()
|
||||||
|
{
|
||||||
|
const uint32_t aggr_idx = 2;
|
||||||
|
|
||||||
|
srsran::test_delimit_logger delimiter{"Test PDCCH Allocation in CORESET#0 and CORESET#2"};
|
||||||
|
|
||||||
|
sched_nr_impl::cell_cfg_t cell_cfg = get_default_sa_cell_cfg_common();
|
||||||
|
cell_cfg.bwps[0].pdcch.search_space_present[2] = true;
|
||||||
|
cell_cfg.bwps[0].pdcch.search_space[2] = get_default_ue_specific_search_space(2, 2);
|
||||||
|
cell_cfg.bwps[0].pdcch.coreset_present[2] = true;
|
||||||
|
cell_cfg.bwps[0].pdcch.coreset[2] = get_default_ue_specific_coreset(2, cell_cfg.carrier.pci);
|
||||||
|
sched_nr_interface::sched_args_t sched_args;
|
||||||
|
bwp_params_t bwp_params{cell_cfg, sched_args, 0, 0};
|
||||||
|
|
||||||
|
// UE config
|
||||||
|
ue_cfg_t uecfg = get_rach_ue_cfg(0);
|
||||||
|
uecfg.phy_cfg = get_common_ue_phy_cfg(cell_cfg);
|
||||||
|
uecfg.phy_cfg.pdcch = cell_cfg.bwps[0].pdcch; // Starts with UE-specific PDCCH
|
||||||
|
ue_carrier_params_t ue_cc{0x46, bwp_params, uecfg};
|
||||||
|
|
||||||
|
pdcch_dl_list_t dl_pdcchs;
|
||||||
|
pdcch_ul_list_t ul_pdcchs;
|
||||||
|
pdcch_dl_t* dl_pdcch = nullptr;
|
||||||
|
pdcch_ul_t* ul_pdcch = nullptr;
|
||||||
|
|
||||||
|
bwp_pdcch_allocator pdcch_sched(bwp_params, 0, dl_pdcchs, ul_pdcchs);
|
||||||
|
for (const srsran_coreset_t& cs : view_active_coresets(cell_cfg.bwps[0].pdcch)) {
|
||||||
|
// Verify nof CCEs is correctly computed
|
||||||
|
TESTASSERT_EQ(coreset_nof_cces(cs), pdcch_sched.nof_cces(cs.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slot with SIB1 + DL PDCCH and UL PDCCH
|
||||||
|
TESTASSERT_EQ(0, pdcch_sched.nof_allocations());
|
||||||
|
|
||||||
|
// SIB1 allocation should be successful
|
||||||
|
dl_pdcch = pdcch_sched.alloc_si_pdcch(0, aggr_idx).value();
|
||||||
|
TESTASSERT(dl_pdcch != nullptr);
|
||||||
|
TESTASSERT_EQ(1, pdcch_sched.nof_allocations());
|
||||||
|
TESTASSERT_EQ(srsran_rnti_type_si, dl_pdcch->dci.ctx.rnti_type);
|
||||||
|
TESTASSERT_EQ(0, dl_pdcch->dci.ctx.coreset_id);
|
||||||
|
srsran_dci_location_t expected_loc{aggr_idx, 0};
|
||||||
|
TESTASSERT(dl_pdcch->dci.ctx.location == expected_loc);
|
||||||
|
test_dci_ctx_consistency(bwp_params.cfg.pdcch, dl_pdcch->dci.ctx);
|
||||||
|
|
||||||
|
// No space for RAR or PDSCH in SS#1
|
||||||
|
TESTASSERT(pdcch_sched.alloc_rar_pdcch(0x2, aggr_idx).error() == alloc_result::no_cch_space);
|
||||||
|
TESTASSERT(pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 1, aggr_idx, ue_cc).error() == alloc_result::no_cch_space);
|
||||||
|
TESTASSERT(pdcch_sched.alloc_ul_pdcch(1, aggr_idx, ue_cc).error() == alloc_result::no_cch_space);
|
||||||
|
|
||||||
|
// there is space for UE DL PDCCH in SS#2
|
||||||
|
dl_pdcch = pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 2, aggr_idx, ue_cc).value();
|
||||||
|
TESTASSERT(dl_pdcch != nullptr);
|
||||||
|
TESTASSERT_EQ(2, pdcch_sched.nof_allocations());
|
||||||
|
TESTASSERT_EQ(srsran_rnti_type_c, dl_pdcch->dci.ctx.rnti_type);
|
||||||
|
TESTASSERT_EQ(2u, dl_pdcch->dci.ctx.coreset_id);
|
||||||
|
TESTASSERT_EQ(srsran_search_space_type_ue, dl_pdcch->dci.ctx.ss_type);
|
||||||
|
expected_loc = srsran_dci_location_t{aggr_idx, 0};
|
||||||
|
TESTASSERT(dl_pdcch->dci.ctx.location == expected_loc);
|
||||||
|
test_dci_ctx_consistency(bwp_params.cfg.pdcch, dl_pdcch->dci.ctx);
|
||||||
|
|
||||||
|
// there is space for UE UL PDCCH in SS#2
|
||||||
|
ul_pdcch = pdcch_sched.alloc_ul_pdcch(2, aggr_idx, ue_cc).value();
|
||||||
|
TESTASSERT(ul_pdcch != nullptr);
|
||||||
|
TESTASSERT_EQ(3, pdcch_sched.nof_allocations());
|
||||||
|
TESTASSERT_EQ(srsran_rnti_type_c, ul_pdcch->dci.ctx.rnti_type);
|
||||||
|
TESTASSERT_EQ(srsran_dci_format_nr_0_0, ul_pdcch->dci.ctx.format);
|
||||||
|
TESTASSERT_EQ(2u, ul_pdcch->dci.ctx.coreset_id);
|
||||||
|
TESTASSERT_EQ(srsran_search_space_type_ue, ul_pdcch->dci.ctx.ss_type);
|
||||||
|
expected_loc = srsran_dci_location_t{aggr_idx, 4};
|
||||||
|
TESTASSERT(ul_pdcch->dci.ctx.location == expected_loc);
|
||||||
|
test_dci_ctx_consistency(bwp_params.cfg.pdcch, ul_pdcch->dci.ctx);
|
||||||
|
|
||||||
|
// No space for 3rd PDCCH allocation in SS#2
|
||||||
|
TESTASSERT(pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 2, aggr_idx, ue_cc).error() == alloc_result::no_cch_space);
|
||||||
|
|
||||||
|
// Verify there are no PDCCH collisions
|
||||||
|
TESTASSERT_EQ(3, pdcch_sched.nof_allocations());
|
||||||
|
TESTASSERT_EQ(2, dl_pdcchs.size());
|
||||||
|
TESTASSERT_EQ(1, ul_pdcchs.size());
|
||||||
|
test_pdcch_collisions(bwp_params.cfg.pdcch, dl_pdcchs, ul_pdcchs);
|
||||||
|
|
||||||
|
srslog::fetch_basic_logger("TEST").info("%s", pdcch_sched.print_allocations());
|
||||||
|
|
||||||
|
// Verify all coresets are correctly cleaned up
|
||||||
|
pdcch_sched.reset();
|
||||||
|
TESTASSERT_EQ(0, pdcch_sched.nof_allocations());
|
||||||
|
TESTASSERT_EQ(0, dl_pdcchs.size());
|
||||||
|
TESTASSERT_EQ(0, ul_pdcchs.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_invalid_params()
|
||||||
|
{
|
||||||
|
const uint32_t aggr_idx = 2;
|
||||||
|
|
||||||
|
srsran::test_delimit_logger delimiter{"Test PDCCH Allocation with Invalid Arguments"};
|
||||||
|
|
||||||
|
sched_nr_impl::cell_cfg_t cell_cfg = get_default_sa_cell_cfg_common();
|
||||||
|
cell_cfg.bwps[0].pdcch.search_space_present[2] = true;
|
||||||
|
cell_cfg.bwps[0].pdcch.search_space[2] = get_default_ue_specific_search_space(2, 2);
|
||||||
|
cell_cfg.bwps[0].pdcch.coreset_present[2] = true;
|
||||||
|
cell_cfg.bwps[0].pdcch.coreset[2] = get_default_ue_specific_coreset(2, cell_cfg.carrier.pci);
|
||||||
|
cell_cfg.bwps[0].pdcch.search_space_present[3] = true;
|
||||||
|
cell_cfg.bwps[0].pdcch.search_space[3] = get_default_ue_specific_search_space(3, 2);
|
||||||
|
cell_cfg.bwps[0].pdcch.search_space[3].nof_formats = 1; // only DL
|
||||||
|
cell_cfg.bwps[0].pdcch.search_space[3].formats[0] = srsran_dci_format_nr_1_0;
|
||||||
|
sched_nr_interface::sched_args_t sched_args;
|
||||||
|
bwp_params_t bwp_params{cell_cfg, sched_args, 0, 0};
|
||||||
|
|
||||||
|
// UE config
|
||||||
|
ue_cfg_t uecfg = get_rach_ue_cfg(0);
|
||||||
|
uecfg.phy_cfg = get_common_ue_phy_cfg(cell_cfg);
|
||||||
|
uecfg.phy_cfg.pdcch = cell_cfg.bwps[0].pdcch; // Starts with UE-specific PDCCH
|
||||||
|
ue_carrier_params_t ue_cc{0x46, bwp_params, uecfg};
|
||||||
|
|
||||||
|
pdcch_dl_list_t dl_pdcchs;
|
||||||
|
pdcch_ul_list_t ul_pdcchs;
|
||||||
|
pdcch_dl_alloc_result dl_res;
|
||||||
|
pdcch_ul_alloc_result ul_res;
|
||||||
|
|
||||||
|
bwp_pdcch_allocator pdcch_sched(bwp_params, 0, dl_pdcchs, ul_pdcchs);
|
||||||
|
|
||||||
|
// Slot with SIB1 + DL PDCCH and UL PDCCH
|
||||||
|
TESTASSERT_EQ(0, pdcch_sched.nof_allocations());
|
||||||
|
|
||||||
|
// Pass UE search space for SI alloc
|
||||||
|
dl_res = pdcch_sched.alloc_si_pdcch(2, aggr_idx);
|
||||||
|
TESTASSERT(dl_res.is_error() and dl_res.error() == alloc_result::invalid_grant_params);
|
||||||
|
|
||||||
|
// Pass aggregation index for which there are no candidates
|
||||||
|
dl_res = pdcch_sched.alloc_si_pdcch(2, 4);
|
||||||
|
TESTASSERT(dl_res.is_error() and dl_res.error() == alloc_result::invalid_grant_params);
|
||||||
|
|
||||||
|
// SearchSpace must exist
|
||||||
|
dl_res = pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 4, aggr_idx, ue_cc);
|
||||||
|
TESTASSERT(dl_res.is_error() and dl_res.error() == alloc_result::invalid_grant_params);
|
||||||
|
|
||||||
|
// TC-RNTI cannot be allocated in Common SearchSpace Type1
|
||||||
|
dl_res = pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_tc, 2, aggr_idx, ue_cc);
|
||||||
|
TESTASSERT(dl_res.is_error() and dl_res.error() == alloc_result::invalid_grant_params);
|
||||||
|
|
||||||
|
// C-RNTI cannot be allocated in Common SearchSpace Type0
|
||||||
|
dl_res = pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 0, aggr_idx, ue_cc);
|
||||||
|
TESTASSERT(dl_res.is_error() and dl_res.error() == alloc_result::invalid_grant_params);
|
||||||
|
|
||||||
|
// UL allocation cannot be made in SearchSpace without DCI format 0_0
|
||||||
|
ul_res = pdcch_sched.alloc_ul_pdcch(3, aggr_idx, ue_cc);
|
||||||
|
TESTASSERT(ul_res.is_error() and ul_res.error() == alloc_result::invalid_grant_params);
|
||||||
|
|
||||||
|
// Success case
|
||||||
|
TESTASSERT(pdcch_sched.nof_allocations() == 0);
|
||||||
|
ul_res = pdcch_sched.alloc_ul_pdcch(2, aggr_idx, ue_cc);
|
||||||
|
TESTASSERT(ul_res.has_value() and ul_res.value()->dci.ctx.format == srsran_dci_format_nr_0_0);
|
||||||
|
TESTASSERT(pdcch_sched.nof_allocations() == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace srsenb
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
auto& test_logger = srslog::fetch_basic_logger("TEST");
|
||||||
|
test_logger.set_level(srslog::basic_levels::info);
|
||||||
|
auto& mac_nr_logger = srslog::fetch_basic_logger("MAC-NR");
|
||||||
|
mac_nr_logger.set_level(srslog::basic_levels::debug);
|
||||||
|
auto& pool_logger = srslog::fetch_basic_logger("POOL");
|
||||||
|
pool_logger.set_level(srslog::basic_levels::debug);
|
||||||
|
|
||||||
|
// Start the log backend.
|
||||||
|
srslog::init();
|
||||||
|
|
||||||
|
srsenb::test_coreset0_cfg();
|
||||||
|
srsenb::test_coreset2_cfg();
|
||||||
|
srsenb::test_invalid_params();
|
||||||
|
}
|
@ -0,0 +1,482 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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 "sched_nr_cfg_generators.h"
|
||||||
|
#include "srsgnb/hdr/stack/mac/sched_nr_interface_utils.h"
|
||||||
|
#include "srsgnb/hdr/stack/mac/sched_nr_sch.h"
|
||||||
|
#include "srsran/common/test_common.h"
|
||||||
|
extern "C" {
|
||||||
|
#include "srsran/phy/common/sliv.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace srsenb {
|
||||||
|
|
||||||
|
using namespace sched_nr_impl;
|
||||||
|
|
||||||
|
sched_nr_interface::cell_cfg_t get_cell_cfg()
|
||||||
|
{
|
||||||
|
sched_nr_impl::cell_cfg_t cell_cfg = get_default_sa_cell_cfg_common();
|
||||||
|
cell_cfg.bwps[0].pdcch.search_space_present[2] = true;
|
||||||
|
cell_cfg.bwps[0].pdcch.search_space[2] = get_default_ue_specific_search_space(2, 2);
|
||||||
|
cell_cfg.bwps[0].pdcch.coreset_present[2] = true;
|
||||||
|
cell_cfg.bwps[0].pdcch.coreset[2] = get_default_ue_specific_coreset(2, cell_cfg.carrier.pci);
|
||||||
|
return cell_cfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
sched_nr_interface::ue_cfg_t get_ue_cfg(const sched_nr_interface::cell_cfg_t& cell_cfg)
|
||||||
|
{
|
||||||
|
ue_cfg_t uecfg = get_rach_ue_cfg(0);
|
||||||
|
uecfg.phy_cfg = get_common_ue_phy_cfg(cell_cfg);
|
||||||
|
uecfg.phy_cfg.pdcch = cell_cfg.bwps[0].pdcch; // Starts with UE-specific PDCCH
|
||||||
|
return uecfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
srsran_dci_ctx_t generate_dci_ctx(const srsran_pdcch_cfg_nr_t& pdcch,
|
||||||
|
uint32_t ss_id,
|
||||||
|
srsran_rnti_type_t rnti_type,
|
||||||
|
uint16_t rnti,
|
||||||
|
srsran_dci_format_nr_t dci_fmt = srsran_dci_format_nr_1_0)
|
||||||
|
{
|
||||||
|
const srsran_search_space_t& ss = pdcch.search_space[ss_id];
|
||||||
|
const srsran_coreset_t& cs = pdcch.coreset[ss.coreset_id];
|
||||||
|
|
||||||
|
srsran_dci_ctx_t ctx;
|
||||||
|
ctx.location = {2, 4};
|
||||||
|
ctx.ss_type = ss.type;
|
||||||
|
ctx.coreset_id = ss.coreset_id;
|
||||||
|
ctx.coreset_start_rb = srsran_coreset_start_rb(&cs);
|
||||||
|
ctx.rnti_type = rnti_type;
|
||||||
|
ctx.format = dci_fmt;
|
||||||
|
ctx.rnti = rnti;
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_dci_freq_assignment(const bwp_params_t& bwp_params, prb_interval grant, const pdcch_dl_t& pdcch)
|
||||||
|
{
|
||||||
|
// Compute BWP PRB limits
|
||||||
|
prb_interval lims{0, bwp_params.nof_prb()};
|
||||||
|
if (SRSRAN_SEARCH_SPACE_IS_COMMON(pdcch.dci.ctx.ss_type) and pdcch.dci.ctx.format == srsran_dci_format_nr_1_0) {
|
||||||
|
lims = bwp_params.dci_fmt_1_0_prb_lims(pdcch.dci.ctx.coreset_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// RB indexing should start from the first PRB of CORESET
|
||||||
|
uint32_t expected_freq_assignment =
|
||||||
|
srsran_ra_nr_type1_riv(lims.length(), grant.start() - lims.start(), grant.length());
|
||||||
|
TESTASSERT_EQ(expected_freq_assignment, pdcch.dci.freq_domain_assigment);
|
||||||
|
|
||||||
|
uint32_t st, len;
|
||||||
|
srsran_sliv_to_s_and_l(lims.length(), pdcch.dci.freq_domain_assigment, &st, &len);
|
||||||
|
prb_interval allocated_prbs{st + lims.start(), st + lims.start() + len};
|
||||||
|
TESTASSERT(allocated_prbs == grant);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_si()
|
||||||
|
{
|
||||||
|
srsran::test_delimit_logger delimiter{"Test PDSCH SI Allocation"};
|
||||||
|
|
||||||
|
static const uint32_t ss_id = 0;
|
||||||
|
|
||||||
|
// Create Cell and UE configs
|
||||||
|
sched_nr_impl::cell_cfg_t cell_cfg = get_cell_cfg();
|
||||||
|
sched_nr_interface::sched_args_t sched_args;
|
||||||
|
bwp_params_t bwp_params{cell_cfg, sched_args, 0, 0};
|
||||||
|
|
||||||
|
pdsch_list_t pdschs;
|
||||||
|
pdsch_alloc_result alloc_res;
|
||||||
|
|
||||||
|
pdsch_allocator pdsch_sched(bwp_params, 0, pdschs);
|
||||||
|
|
||||||
|
pdcch_dl_t pdcch;
|
||||||
|
pdcch.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, ss_id, srsran_rnti_type_si, SRSRAN_SIRNTI);
|
||||||
|
|
||||||
|
uint32_t min_prb = bwp_params.dci_fmt_1_0_prb_lims(pdcch.dci.ctx.coreset_id).start();
|
||||||
|
uint32_t max_prb = bwp_params.dci_fmt_1_0_prb_lims(pdcch.dci.ctx.coreset_id).stop();
|
||||||
|
|
||||||
|
std::array<prb_interval, 3> grant_list = {
|
||||||
|
prb_interval{2, 4}, prb_interval{min_prb, max_prb}, prb_interval{0, bwp_params.nof_prb()}};
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < grant_list.size(); ++i) {
|
||||||
|
pdsch_sched.reset();
|
||||||
|
TESTASSERT_EQ(0, pdschs.size());
|
||||||
|
|
||||||
|
prb_interval grant = grant_list[i];
|
||||||
|
|
||||||
|
bool success_expected = grant.start() >= min_prb and grant.stop() <= max_prb;
|
||||||
|
|
||||||
|
alloc_result check_ret = pdsch_sched.is_si_grant_valid(ss_id, grant);
|
||||||
|
prb_bitmap avail_prbs = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0);
|
||||||
|
TESTASSERT_EQ((int)min_prb, avail_prbs.find_lowest(0, avail_prbs.size(), false));
|
||||||
|
TESTASSERT_EQ((int)max_prb, avail_prbs.find_lowest(min_prb, avail_prbs.size(), true));
|
||||||
|
|
||||||
|
printf("Attempt %d should be %ssuccessful\n", i, success_expected ? "" : "un");
|
||||||
|
alloc_res = pdsch_sched.alloc_si_pdsch(ss_id, grant, pdcch.dci);
|
||||||
|
if (success_expected) {
|
||||||
|
// SIB1 allocation doesnt go outside CORESET#0 BW
|
||||||
|
TESTASSERT(alloc_res.has_value());
|
||||||
|
TESTASSERT_EQ(1, pdschs.size());
|
||||||
|
TESTASSERT(&pdschs.back() == alloc_res.value());
|
||||||
|
TESTASSERT_EQ(0, pdcch.dci.time_domain_assigment);
|
||||||
|
|
||||||
|
TESTASSERT(not avail_prbs.any(grant.start(), grant.stop()));
|
||||||
|
|
||||||
|
test_dci_freq_assignment(bwp_params, grant, pdcch);
|
||||||
|
} else {
|
||||||
|
TESTASSERT(alloc_res.is_error());
|
||||||
|
TESTASSERT(check_ret == alloc_res.error());
|
||||||
|
TESTASSERT_EQ(0, pdschs.size());
|
||||||
|
TESTASSERT(avail_prbs.any(grant.start(), grant.stop()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_rar()
|
||||||
|
{
|
||||||
|
srsran::test_delimit_logger delimiter{"Test PDSCH RAR Allocation"};
|
||||||
|
static const uint32_t ss_id = 1;
|
||||||
|
|
||||||
|
// Create Cell and UE configs
|
||||||
|
sched_nr_impl::cell_cfg_t cell_cfg = get_cell_cfg();
|
||||||
|
sched_nr_interface::sched_args_t sched_args;
|
||||||
|
bwp_params_t bwp_params{cell_cfg, sched_args, 0, 0};
|
||||||
|
|
||||||
|
pdsch_list_t pdschs;
|
||||||
|
pdsch_alloc_result alloc_res;
|
||||||
|
|
||||||
|
pdsch_allocator pdsch_sched(bwp_params, 0, pdschs);
|
||||||
|
|
||||||
|
pdcch_dl_t pdcch;
|
||||||
|
pdcch.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, ss_id, srsran_rnti_type_ra, 0x2);
|
||||||
|
|
||||||
|
uint32_t min_prb = bwp_params.dci_fmt_1_0_prb_lims(pdcch.dci.ctx.coreset_id).start();
|
||||||
|
uint32_t max_prb = bwp_params.dci_fmt_1_0_prb_lims(pdcch.dci.ctx.coreset_id).stop();
|
||||||
|
|
||||||
|
std::array<prb_interval, 3> grant_list = {
|
||||||
|
prb_interval{2, 4}, prb_interval{min_prb, max_prb}, prb_interval{0, bwp_params.nof_prb()}};
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < grant_list.size(); ++i) {
|
||||||
|
pdsch_sched.reset();
|
||||||
|
TESTASSERT_EQ(0, pdschs.size());
|
||||||
|
|
||||||
|
prb_interval grant = grant_list[i];
|
||||||
|
|
||||||
|
bool success_expected = grant.start() >= min_prb and grant.stop() <= max_prb;
|
||||||
|
|
||||||
|
alloc_result check_ret = pdsch_sched.is_rar_grant_valid(grant);
|
||||||
|
prb_bitmap avail_prbs = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0);
|
||||||
|
TESTASSERT_EQ((int)min_prb, avail_prbs.find_lowest(0, avail_prbs.size(), false));
|
||||||
|
TESTASSERT_EQ((int)max_prb, avail_prbs.find_lowest(min_prb, avail_prbs.size(), true));
|
||||||
|
|
||||||
|
printf("Attempt %d should be %ssuccessful\n", i, success_expected ? "" : "un");
|
||||||
|
alloc_res = pdsch_sched.alloc_rar_pdsch(grant, pdcch.dci);
|
||||||
|
if (success_expected) {
|
||||||
|
// SIB1 allocation doesnt go outside CORESET#0 BW
|
||||||
|
TESTASSERT(alloc_res.has_value());
|
||||||
|
TESTASSERT_EQ(1, pdschs.size());
|
||||||
|
TESTASSERT(&pdschs.back() == alloc_res.value());
|
||||||
|
TESTASSERT_EQ(0, pdcch.dci.time_domain_assigment);
|
||||||
|
|
||||||
|
TESTASSERT(not avail_prbs.any(grant.start(), grant.stop()));
|
||||||
|
|
||||||
|
test_dci_freq_assignment(bwp_params, grant, pdcch);
|
||||||
|
} else {
|
||||||
|
TESTASSERT(alloc_res.is_error());
|
||||||
|
TESTASSERT(check_ret == alloc_res.error());
|
||||||
|
TESTASSERT_EQ(0, pdschs.size());
|
||||||
|
TESTASSERT(avail_prbs.any(grant.start(), grant.stop()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_ue_pdsch()
|
||||||
|
{
|
||||||
|
srsran::test_delimit_logger delimiter{"Test PDSCH UE Allocation"};
|
||||||
|
|
||||||
|
// Create Cell and UE configs
|
||||||
|
sched_nr_impl::cell_cfg_t cell_cfg = get_cell_cfg();
|
||||||
|
sched_nr_impl::ue_cfg_t uecfg = get_ue_cfg(cell_cfg);
|
||||||
|
sched_nr_interface::sched_args_t sched_args;
|
||||||
|
bwp_params_t bwp_params{cell_cfg, sched_args, 0, 0};
|
||||||
|
ue_carrier_params_t ue_cc{0x4601, bwp_params, uecfg};
|
||||||
|
|
||||||
|
pdsch_list_t pdschs;
|
||||||
|
pdsch_alloc_result alloc_res;
|
||||||
|
|
||||||
|
pdsch_allocator pdsch_sched(bwp_params, 0, pdschs);
|
||||||
|
|
||||||
|
pdcch_dl_t pdcch_common, pdcch_ue;
|
||||||
|
pdcch_common.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 1, srsran_rnti_type_c, 0x4601);
|
||||||
|
pdcch_ue.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 2, srsran_rnti_type_c, 0x4601);
|
||||||
|
|
||||||
|
prb_interval lims_common = bwp_params.dci_fmt_1_0_prb_lims(pdcch_common.dci.ctx.coreset_id);
|
||||||
|
prb_interval lims_ue{0, bwp_params.nof_prb()};
|
||||||
|
|
||||||
|
std::array<std::pair<uint32_t, prb_interval>, 4> grant_list = {std::make_pair(1, prb_interval{2, 4}),
|
||||||
|
std::make_pair(1, lims_common),
|
||||||
|
std::make_pair(1, lims_ue),
|
||||||
|
std::make_pair(2, lims_common)};
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < grant_list.size(); ++i) {
|
||||||
|
pdsch_sched.reset();
|
||||||
|
TESTASSERT_EQ(0, pdschs.size());
|
||||||
|
|
||||||
|
auto g = grant_list[i];
|
||||||
|
uint32_t ss_id = g.first;
|
||||||
|
prb_interval grant = g.second;
|
||||||
|
prb_interval lims = ss_id == 1 ? lims_common : lims_ue;
|
||||||
|
pdcch_dl_t& pdcch = ss_id == 1 ? pdcch_common : pdcch_ue;
|
||||||
|
|
||||||
|
bool success_expected = grant.start() >= lims.start() and grant.stop() <= lims.stop();
|
||||||
|
|
||||||
|
alloc_result check_ret = pdsch_sched.is_ue_grant_valid(ue_cc, ss_id, srsran_dci_format_nr_1_0, grant);
|
||||||
|
prb_bitmap avail_prbs = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0);
|
||||||
|
int pos = avail_prbs.find_lowest(0, avail_prbs.size(), false);
|
||||||
|
TESTASSERT_EQ((int)lims.start(), pos);
|
||||||
|
pos = avail_prbs.find_lowest(lims.start(), avail_prbs.size(), true);
|
||||||
|
TESTASSERT_EQ((int)lims.stop(), (pos < 0 ? (int)avail_prbs.size() : pos));
|
||||||
|
|
||||||
|
printf("Attempt %d should be %ssuccessful\n", i, success_expected ? "" : "un");
|
||||||
|
alloc_res = pdsch_sched.alloc_ue_pdsch(ss_id, srsran_dci_format_nr_1_0, grant, ue_cc, pdcch.dci);
|
||||||
|
TESTASSERT(success_expected == alloc_res.has_value());
|
||||||
|
if (success_expected) {
|
||||||
|
// SIB1 allocation doesnt go outside CORESET#0 BW
|
||||||
|
TESTASSERT_EQ(1, pdschs.size());
|
||||||
|
TESTASSERT(&pdschs.back() == alloc_res.value());
|
||||||
|
TESTASSERT_EQ(0, pdcch.dci.time_domain_assigment);
|
||||||
|
|
||||||
|
TESTASSERT(not avail_prbs.any(grant.start(), grant.stop()));
|
||||||
|
|
||||||
|
test_dci_freq_assignment(bwp_params, grant, pdcch);
|
||||||
|
} else {
|
||||||
|
TESTASSERT(check_ret == alloc_res.error());
|
||||||
|
TESTASSERT_EQ(0, pdschs.size());
|
||||||
|
TESTASSERT(avail_prbs.any(grant.start(), grant.stop()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_pdsch_fail()
|
||||||
|
{
|
||||||
|
srsran::test_delimit_logger delimiter{"Test PDSCH Allocation Failure"};
|
||||||
|
|
||||||
|
// Create Cell and UE configs
|
||||||
|
sched_nr_impl::cell_cfg_t cell_cfg = get_cell_cfg();
|
||||||
|
sched_nr_impl::ue_cfg_t uecfg = get_ue_cfg(cell_cfg);
|
||||||
|
sched_nr_interface::sched_args_t sched_args;
|
||||||
|
bwp_params_t bwp_params{cell_cfg, sched_args, 0, 0};
|
||||||
|
ue_carrier_params_t ue_cc{0x4601, bwp_params, uecfg};
|
||||||
|
|
||||||
|
pdsch_list_t pdschs;
|
||||||
|
pdsch_alloc_result alloc_res;
|
||||||
|
|
||||||
|
pdsch_allocator pdsch_sched(bwp_params, 0, pdschs);
|
||||||
|
|
||||||
|
pdcch_dl_t pdcch_common, pdcch_ue, pdcch_rar, pdcch_si, pdcch;
|
||||||
|
pdcch_si.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 0, srsran_rnti_type_si, SRSRAN_SIRNTI);
|
||||||
|
pdcch_rar.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 1, srsran_rnti_type_ra, 0x2);
|
||||||
|
pdcch_common.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 1, srsran_rnti_type_c, 0x4601);
|
||||||
|
pdcch_ue.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 2, srsran_rnti_type_c, 0x4601);
|
||||||
|
|
||||||
|
// Allocations of type 0 are not compatible with DCI format 1_0
|
||||||
|
rbg_bitmap rbgs(bwp_params.N_rbg);
|
||||||
|
rbgs.set(1);
|
||||||
|
rbgs.set(3);
|
||||||
|
prb_grant grant_type0 = rbgs;
|
||||||
|
TESTASSERT_EQ(alloc_result::invalid_grant_params, pdsch_sched.alloc_si_pdsch(0, grant_type0, pdcch_si.dci).error());
|
||||||
|
TESTASSERT_EQ(alloc_result::invalid_grant_params, pdsch_sched.alloc_rar_pdsch(grant_type0, pdcch_rar.dci).error());
|
||||||
|
TESTASSERT_EQ(alloc_result::invalid_grant_params,
|
||||||
|
pdsch_sched.alloc_ue_pdsch(1, srsran_dci_format_nr_1_0, grant_type0, ue_cc, pdcch.dci).error());
|
||||||
|
TESTASSERT_EQ(alloc_result::invalid_grant_params,
|
||||||
|
pdsch_sched.alloc_ue_pdsch(2, srsran_dci_format_nr_1_0, grant_type0, ue_cc, pdcch.dci).error());
|
||||||
|
|
||||||
|
// Resource Allocation type must be compatible with UE PDSCH configuration
|
||||||
|
TESTASSERT_EQ(alloc_result::invalid_grant_params,
|
||||||
|
pdsch_sched.alloc_ue_pdsch(2, srsran_dci_format_nr_1_1, grant_type0, ue_cc, pdcch.dci).error());
|
||||||
|
|
||||||
|
// Allocations of DCI format 1_0 should start from CORESET first RB and their BW should be limited by CORESET#0 BW
|
||||||
|
prb_grant grant_type1 = prb_interval{0, bwp_params.coreset_prb_range(0).stop()};
|
||||||
|
TESTASSERT(pdsch_sched.alloc_ue_pdsch(1, srsran_dci_format_nr_1_0, grant_type1, ue_cc, pdcch.dci).is_error());
|
||||||
|
grant_type1 = prb_interval{bwp_params.coreset_prb_range(0).start(), bwp_params.nof_prb()};
|
||||||
|
TESTASSERT(pdsch_sched.alloc_ue_pdsch(1, srsran_dci_format_nr_1_0, grant_type1, ue_cc, pdcch.dci).is_error());
|
||||||
|
TESTASSERT(pdsch_sched.alloc_ue_pdsch(2, srsran_dci_format_nr_1_0, grant_type1, ue_cc, pdcch.dci).has_value());
|
||||||
|
|
||||||
|
// PRB collisions are detected
|
||||||
|
TESTASSERT(pdsch_sched.alloc_ue_pdsch(2, srsran_dci_format_nr_1_0, prb_interval{5, 6}, ue_cc, pdcch.dci).is_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_multi_pdsch()
|
||||||
|
{
|
||||||
|
srsran::test_delimit_logger delimiter{"Test Multiple PDSCH Allocations"};
|
||||||
|
|
||||||
|
// Create Cell and UE configs
|
||||||
|
sched_nr_impl::cell_cfg_t cell_cfg = get_cell_cfg();
|
||||||
|
sched_nr_impl::ue_cfg_t uecfg = get_ue_cfg(cell_cfg);
|
||||||
|
sched_nr_interface::sched_args_t sched_args;
|
||||||
|
bwp_params_t bwp_params{cell_cfg, sched_args, 0, 0};
|
||||||
|
ue_carrier_params_t ue_cc{0x4601, bwp_params, uecfg};
|
||||||
|
ue_carrier_params_t ue_cc2{0x4602, bwp_params, uecfg};
|
||||||
|
|
||||||
|
pdsch_list_t pdschs;
|
||||||
|
pdsch_alloc_result alloc_res;
|
||||||
|
|
||||||
|
pdsch_allocator pdsch_sched(bwp_params, 0, pdschs);
|
||||||
|
|
||||||
|
pdcch_dl_t pdcch_sib, pdcch_common, pdcch_ue;
|
||||||
|
pdcch_sib.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 0, srsran_rnti_type_si, SRSRAN_SIRNTI);
|
||||||
|
pdcch_common.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 1, srsran_rnti_type_c, 0x4601);
|
||||||
|
pdcch_ue.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 2, srsran_rnti_type_c, 0x4602);
|
||||||
|
|
||||||
|
// Allocate SIB1
|
||||||
|
uint32_t ss_id = 0;
|
||||||
|
pdcch_dl_t* pdcch = &pdcch_sib;
|
||||||
|
prb_bitmap used_prbs = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0);
|
||||||
|
fmt::print("No allocations yet. Occupied PRBs for common SearchSpace: {:b}\n", used_prbs);
|
||||||
|
uint32_t sib1_grant_size = 4;
|
||||||
|
prb_bitmap sib_prbs = ~used_prbs;
|
||||||
|
int first_prb = sib_prbs.find_lowest(0, sib_prbs.size(), true);
|
||||||
|
prb_interval sib_grant{(uint32_t)first_prb, sib1_grant_size};
|
||||||
|
TESTASSERT_EQ(alloc_result::success, pdsch_sched.is_si_grant_valid(ss_id, sib_grant));
|
||||||
|
alloc_res = pdsch_sched.alloc_si_pdsch(ss_id, sib_grant, pdcch->dci);
|
||||||
|
TESTASSERT(alloc_res.has_value());
|
||||||
|
test_dci_freq_assignment(bwp_params, sib_grant, *pdcch);
|
||||||
|
prb_bitmap used_prbs_sib1 = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0);
|
||||||
|
TESTASSERT_EQ(used_prbs_sib1.count(), used_prbs.count() + sib_grant.length());
|
||||||
|
TESTASSERT_EQ(alloc_result::sch_collision, pdsch_sched.is_si_grant_valid(ss_id, sib_grant));
|
||||||
|
|
||||||
|
prb_bitmap last_prb_bitmap(used_prbs.size());
|
||||||
|
last_prb_bitmap.fill(sib_grant.start(), sib_grant.stop());
|
||||||
|
fmt::print("SIB1 allocated. Occupied PRBs:\n{:b} -> {:b}\n", last_prb_bitmap, used_prbs_sib1);
|
||||||
|
|
||||||
|
// Allocate UE in common SearchSpace
|
||||||
|
ss_id = 1;
|
||||||
|
pdcch = &pdcch_common;
|
||||||
|
prb_bitmap ue_prbs = ~used_prbs_sib1;
|
||||||
|
first_prb = ue_prbs.find_lowest(0, ue_prbs.size(), true);
|
||||||
|
uint32_t ue_grant_size = 10;
|
||||||
|
prb_interval ue_grant{(uint32_t)first_prb, ue_grant_size};
|
||||||
|
TESTASSERT_EQ(alloc_result::success, pdsch_sched.is_ue_grant_valid(ue_cc, ss_id, srsran_dci_format_nr_1_0, ue_grant));
|
||||||
|
alloc_res = pdsch_sched.alloc_ue_pdsch(ss_id, srsran_dci_format_nr_1_0, ue_grant, ue_cc, pdcch->dci);
|
||||||
|
TESTASSERT(alloc_res.has_value());
|
||||||
|
test_dci_freq_assignment(bwp_params, ue_grant, *pdcch);
|
||||||
|
prb_bitmap used_prbs_ue = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0);
|
||||||
|
TESTASSERT_EQ(used_prbs_ue.count(), used_prbs_sib1.count() + ue_grant.length());
|
||||||
|
TESTASSERT_EQ(alloc_result::sch_collision,
|
||||||
|
pdsch_sched.is_ue_grant_valid(ue_cc, ss_id, srsran_dci_format_nr_1_0, ue_grant));
|
||||||
|
|
||||||
|
last_prb_bitmap.reset();
|
||||||
|
last_prb_bitmap.fill(ue_grant.start(), ue_grant.stop());
|
||||||
|
fmt::print("C-RNTI allocated in Common SearchSpace. Occupied PRBs:\n{:b} -> {:b}\n", last_prb_bitmap, used_prbs_ue);
|
||||||
|
|
||||||
|
// Allocate UE in UE SearchSpace
|
||||||
|
ss_id = 2;
|
||||||
|
pdcch = &pdcch_ue;
|
||||||
|
used_prbs = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0);
|
||||||
|
prb_interval ue_grant2 = find_empty_interval_of_length(used_prbs, used_prbs_ue.size(), 0);
|
||||||
|
TESTASSERT_EQ(bwp_params.nof_prb(), ue_grant2.stop());
|
||||||
|
TESTASSERT_EQ(alloc_result::success,
|
||||||
|
pdsch_sched.is_ue_grant_valid(ue_cc, ss_id, srsran_dci_format_nr_1_0, ue_grant2));
|
||||||
|
alloc_res = pdsch_sched.alloc_ue_pdsch(ss_id, srsran_dci_format_nr_1_0, ue_grant2, ue_cc, pdcch->dci);
|
||||||
|
TESTASSERT(alloc_res.has_value());
|
||||||
|
test_dci_freq_assignment(bwp_params, ue_grant2, *pdcch);
|
||||||
|
prb_bitmap used_prbs_ue2 = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0);
|
||||||
|
TESTASSERT_EQ(used_prbs_ue2.count(), used_prbs.count() + ue_grant2.length());
|
||||||
|
TESTASSERT_EQ(alloc_result::sch_collision,
|
||||||
|
pdsch_sched.is_ue_grant_valid(ue_cc, ss_id, srsran_dci_format_nr_1_0, ue_grant2));
|
||||||
|
|
||||||
|
last_prb_bitmap.reset();
|
||||||
|
last_prb_bitmap.fill(ue_grant2.start(), ue_grant2.stop());
|
||||||
|
fmt::print("C-RNTI allocated in UE-dedicated common SearchSpace. Occupied PRBs:\n{:b} -> {:b}\n",
|
||||||
|
last_prb_bitmap,
|
||||||
|
used_prbs_ue2);
|
||||||
|
|
||||||
|
TESTASSERT_EQ(3, pdschs.size());
|
||||||
|
pdsch_sched.reset();
|
||||||
|
TESTASSERT_EQ(0, pdschs.size());
|
||||||
|
TESTASSERT_EQ(0, pdsch_sched.occupied_prbs(2, srsran_dci_format_nr_1_0).count());
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_multi_pusch()
|
||||||
|
{
|
||||||
|
srsran::test_delimit_logger delimiter{"Test Multiple PUSCH Allocations"};
|
||||||
|
|
||||||
|
// Create Cell and UE configs
|
||||||
|
sched_nr_impl::cell_cfg_t cell_cfg = get_cell_cfg();
|
||||||
|
sched_nr_impl::ue_cfg_t uecfg = get_ue_cfg(cell_cfg);
|
||||||
|
sched_nr_interface::sched_args_t sched_args;
|
||||||
|
bwp_params_t bwp_params{cell_cfg, sched_args, 0, 0};
|
||||||
|
ue_carrier_params_t ue_cc{0x4601, bwp_params, uecfg};
|
||||||
|
ue_carrier_params_t ue_cc2{0x4602, bwp_params, uecfg};
|
||||||
|
|
||||||
|
pusch_list_t puschs;
|
||||||
|
pusch_alloc_result alloc_res;
|
||||||
|
|
||||||
|
pusch_allocator pusch_sched(bwp_params, 0, puschs);
|
||||||
|
|
||||||
|
pdcch_ul_t pdcch_ue1, pdcch_ue2;
|
||||||
|
pdcch_ue1.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 1, srsran_rnti_type_c, 0x4601);
|
||||||
|
pdcch_ue2.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 2, srsran_rnti_type_c, 0x4602);
|
||||||
|
|
||||||
|
// Allocate UE in common SearchSpace
|
||||||
|
uint32_t ss_id = 1;
|
||||||
|
pdcch_ul_t* pdcch = &pdcch_ue1;
|
||||||
|
prb_bitmap used_prbs = pusch_sched.occupied_prbs();
|
||||||
|
uint32_t ue_grant_size = 10;
|
||||||
|
prb_interval ue_grant = find_empty_interval_of_length(used_prbs, ue_grant_size);
|
||||||
|
TESTASSERT_EQ(alloc_result::success, pusch_sched.is_grant_valid(srsran_search_space_type_common_1, ue_grant));
|
||||||
|
alloc_res = pusch_sched.alloc_pusch(pdcch->dci.ctx.ss_type, ue_grant, pdcch->dci);
|
||||||
|
TESTASSERT(alloc_res.has_value());
|
||||||
|
prb_bitmap used_prbs_ue1 = pusch_sched.occupied_prbs();
|
||||||
|
TESTASSERT_EQ(used_prbs_ue1.count(), used_prbs.count() + ue_grant.length());
|
||||||
|
TESTASSERT_EQ(alloc_result::sch_collision,
|
||||||
|
pusch_sched.is_grant_valid(srsran_search_space_type_common_1, ue_grant, false));
|
||||||
|
|
||||||
|
prb_bitmap last_prb_bitmap(used_prbs.size());
|
||||||
|
last_prb_bitmap.fill(ue_grant.start(), ue_grant.stop());
|
||||||
|
fmt::print("C-RNTI allocated in Common SearchSpace. Occupied PRBs:\n{:b} -> {:b}\n", last_prb_bitmap, used_prbs_ue1);
|
||||||
|
|
||||||
|
// Allocate UE in dedicated SearchSpace
|
||||||
|
ss_id = 2;
|
||||||
|
pdcch = &pdcch_ue2;
|
||||||
|
used_prbs = pusch_sched.occupied_prbs();
|
||||||
|
prb_interval ue2_grant = find_empty_interval_of_length(used_prbs, used_prbs.size());
|
||||||
|
TESTASSERT_EQ(alloc_result::success, pusch_sched.is_grant_valid(srsran_search_space_type_ue, ue2_grant));
|
||||||
|
alloc_res = pusch_sched.alloc_pusch(pdcch->dci.ctx.ss_type, ue2_grant, pdcch->dci);
|
||||||
|
TESTASSERT(alloc_res.has_value());
|
||||||
|
prb_bitmap used_prbs_ue2 = pusch_sched.occupied_prbs();
|
||||||
|
TESTASSERT_EQ(used_prbs_ue2.count(), used_prbs.count() + ue2_grant.length());
|
||||||
|
TESTASSERT_EQ(alloc_result::sch_collision, pusch_sched.is_grant_valid(srsran_search_space_type_ue, ue2_grant, false));
|
||||||
|
|
||||||
|
last_prb_bitmap.reset();
|
||||||
|
last_prb_bitmap.fill(ue2_grant.start(), ue2_grant.stop());
|
||||||
|
fmt::print("C-RNTI allocated in Common SearchSpace. Occupied PRBs:\n{:b} -> {:b}\n", last_prb_bitmap, used_prbs_ue2);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace srsenb
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
auto& test_logger = srslog::fetch_basic_logger("TEST");
|
||||||
|
test_logger.set_level(srslog::basic_levels::info);
|
||||||
|
auto& mac_nr_logger = srslog::fetch_basic_logger("MAC-NR");
|
||||||
|
mac_nr_logger.set_level(srslog::basic_levels::debug);
|
||||||
|
auto& pool_logger = srslog::fetch_basic_logger("POOL");
|
||||||
|
pool_logger.set_level(srslog::basic_levels::debug);
|
||||||
|
|
||||||
|
// Start the log backend.
|
||||||
|
srslog::init();
|
||||||
|
|
||||||
|
srsenb::test_si();
|
||||||
|
srsenb::test_rar();
|
||||||
|
srsenb::test_ue_pdsch();
|
||||||
|
srsenb::test_pdsch_fail();
|
||||||
|
srsenb::test_multi_pdsch();
|
||||||
|
srsenb::test_multi_pusch();
|
||||||
|
}
|
@ -0,0 +1,224 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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 "rrc_nr_test_helpers.h"
|
||||||
|
#include "srsenb/hdr/enb.h"
|
||||||
|
#include "srsenb/hdr/stack/upper/gtpu.h"
|
||||||
|
#include "srsenb/test/rrc/test_helpers.h"
|
||||||
|
#include "srsgnb/hdr/stack/ngap/ngap.h"
|
||||||
|
#include "srsgnb/hdr/stack/rrc/rrc_nr_config_utils.h"
|
||||||
|
#include "srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h"
|
||||||
|
#include "srsran/common/network_utils.h"
|
||||||
|
#include "srsran/common/test_common.h"
|
||||||
|
#include "srsran/interfaces/gnb_rrc_nr_interfaces.h"
|
||||||
|
#include "srsran/upper/gtpu.h"
|
||||||
|
#include <boost/program_options.hpp>
|
||||||
|
#include <boost/program_options/parsers.hpp>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace asn1::rrc_nr;
|
||||||
|
namespace bpo = boost::program_options;
|
||||||
|
|
||||||
|
namespace srsenb { // namespace srsenb
|
||||||
|
|
||||||
|
void parse_args(ngap_args_t* ngap_args, int argc, char* argv[])
|
||||||
|
{
|
||||||
|
// temporary helpers for conversion
|
||||||
|
std::string config_file;
|
||||||
|
std::string gnb_id{};
|
||||||
|
std::string gnb_cell_id{};
|
||||||
|
std::string gnb_tac{};
|
||||||
|
std::string gnb_mcc{};
|
||||||
|
std::string gnb_mnc{};
|
||||||
|
|
||||||
|
// Command line only options
|
||||||
|
bpo::options_description general("General options");
|
||||||
|
|
||||||
|
general.add_options()("help,h", "Produce help message");
|
||||||
|
|
||||||
|
// Command line or config file options
|
||||||
|
bpo::options_description common("Configuration options");
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
common.add_options()
|
||||||
|
|
||||||
|
("gnb_id", bpo::value<std::string>(&gnb_id)->default_value("0x0"), "gnb ID")
|
||||||
|
("name", bpo::value<std::string>(&ngap_args->gnb_name)->default_value("srsgnb01"), "gnb Name")
|
||||||
|
("cell_id", bpo::value<std::string>(&gnb_cell_id)->default_value("0x0"), "Cell ID")
|
||||||
|
("tac", bpo::value<std::string>(&gnb_tac)->default_value("0x0"), "Tracking Area Code")
|
||||||
|
("mcc", bpo::value<std::string>(&gnb_mcc)->default_value("001"), "Mobile Country Code")
|
||||||
|
("mnc", bpo::value<std::string>(&gnb_mnc)->default_value("01"), "Mobile Network Code")
|
||||||
|
("amf_addr", bpo::value<std::string>(&ngap_args->amf_addr)->default_value("127.0.0.1"), "IP address of AMF for NG connection")
|
||||||
|
("n1c_bind_addr", bpo::value<std::string>(&ngap_args->ngc_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for NGAP connection")
|
||||||
|
("gtp_bind_addr", bpo::value<std::string>(&ngap_args->gtp_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for GTP connection");
|
||||||
|
|
||||||
|
bpo::options_description position("Positional options");
|
||||||
|
|
||||||
|
// clang-format on
|
||||||
|
bpo::positional_options_description p{};
|
||||||
|
p.add("config_file", -1);
|
||||||
|
|
||||||
|
bpo::options_description cmdline_options;
|
||||||
|
cmdline_options.add(common).add(position).add(general);
|
||||||
|
|
||||||
|
bpo::variables_map vm{};
|
||||||
|
|
||||||
|
try {
|
||||||
|
bpo::store(bpo::command_line_parser(argc, argv).options(cmdline_options).positional(p).run(), vm);
|
||||||
|
bpo::notify(vm);
|
||||||
|
} catch (bpo::error& e) {
|
||||||
|
std::cerr << e.what() << std::endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vm.count("help")) {
|
||||||
|
std::cout << common << std::endl << general << std::endl;
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert hex strings
|
||||||
|
{
|
||||||
|
std::stringstream sstr{};
|
||||||
|
sstr << std::hex << vm["gnb_id"].as<std::string>();
|
||||||
|
sstr >> ngap_args->gnb_id;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::stringstream sstr{};
|
||||||
|
sstr << std::hex << vm["cell_id"].as<std::string>();
|
||||||
|
uint16_t tmp; // Need intermediate uint16_t as uint8_t is treated as char
|
||||||
|
sstr >> tmp;
|
||||||
|
ngap_args->cell_id = tmp;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::stringstream sstr{};
|
||||||
|
sstr << std::hex << vm["tac"].as<std::string>();
|
||||||
|
sstr >> ngap_args->tac;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert MCC/MNC strings
|
||||||
|
if (!srsran::string_to_mcc(gnb_mcc, &ngap_args->mcc)) {
|
||||||
|
std::cout << "Error parsing mcc:" << gnb_mcc << " - must be a 3-digit string." << std::endl;
|
||||||
|
}
|
||||||
|
if (!srsran::string_to_mnc(gnb_mnc, &ngap_args->mnc)) {
|
||||||
|
std::cout << "Error parsing mnc:" << gnb_mnc << " - must be a 2 or 3-digit string." << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_rrc_sa_ngap_integration(ngap_args_t ngap_args)
|
||||||
|
{
|
||||||
|
// This takes the existing RRC-NR tests and exercises the NGAP integration with a real core network.
|
||||||
|
// The test currently runs down untill the RRCReconfiguration.
|
||||||
|
srsran::task_scheduler task_sched;
|
||||||
|
|
||||||
|
phy_nr_dummy phy_obj;
|
||||||
|
mac_nr_dummy mac_obj;
|
||||||
|
rlc_nr_rrc_tester rlc_obj;
|
||||||
|
pdcp_nr_rrc_tester pdcp_obj;
|
||||||
|
rrc_nr rrc_obj(&task_sched);
|
||||||
|
enb_bearer_manager bearer_mapper;
|
||||||
|
|
||||||
|
// NGAP Setup
|
||||||
|
auto& ngap_logger = srslog::fetch_basic_logger("NGAP");
|
||||||
|
ngap_logger.set_level(srslog::basic_levels::debug);
|
||||||
|
ngap_logger.set_hex_dump_max_size(-1);
|
||||||
|
auto& gtpu_logger = srslog::fetch_basic_logger("GTPU");
|
||||||
|
gtpu_logger.set_level(srslog::basic_levels::debug);
|
||||||
|
gtpu_logger.set_hex_dump_max_size(-1);
|
||||||
|
|
||||||
|
srsran::socket_manager rx_sockets;
|
||||||
|
srsenb::ngap ngap_obj(&task_sched, ngap_logger, &rx_sockets);
|
||||||
|
srsenb::gtpu gtpu_obj(&task_sched, gtpu_logger, &rx_sockets);
|
||||||
|
|
||||||
|
gtpu_args_t gtpu_args;
|
||||||
|
gtpu_args.embms_enable = false;
|
||||||
|
gtpu_args.mme_addr = ngap_args.amf_addr;
|
||||||
|
gtpu_args.gtp_bind_addr = ngap_args.gtp_bind_addr;
|
||||||
|
TESTASSERT(gtpu_obj.init(gtpu_args, &pdcp_obj) == SRSRAN_SUCCESS);
|
||||||
|
TESTASSERT(ngap_obj.init(ngap_args, &rrc_obj, >pu_obj) == SRSRAN_SUCCESS);
|
||||||
|
task_sched.run_next_task();
|
||||||
|
|
||||||
|
// set cfg
|
||||||
|
rrc_nr_cfg_t rrc_cfg_nr = rrc_nr_cfg_t{};
|
||||||
|
rrc_cfg_nr.cell_list.emplace_back();
|
||||||
|
generate_default_nr_cell(rrc_cfg_nr.cell_list[0]);
|
||||||
|
rrc_cfg_nr.cell_list[0].phy_cell.carrier.pci = 500;
|
||||||
|
rrc_cfg_nr.cell_list[0].dl_arfcn = 368500;
|
||||||
|
rrc_cfg_nr.cell_list[0].band = 3;
|
||||||
|
rrc_cfg_nr.cell_list[0].phy_cell.carrier.nof_prb = 52;
|
||||||
|
rrc_cfg_nr.cell_list[0].duplex_mode = SRSRAN_DUPLEX_MODE_FDD;
|
||||||
|
rrc_cfg_nr.is_standalone = true;
|
||||||
|
set_derived_nr_cell_params(rrc_cfg_nr.is_standalone, rrc_cfg_nr.cell_list[0]);
|
||||||
|
srsran_assert(check_rrc_nr_cfg_valid(rrc_cfg_nr) == SRSRAN_SUCCESS, "Invalid RRC NR configuration");
|
||||||
|
|
||||||
|
TESTASSERT(rrc_obj.init(rrc_cfg_nr, &phy_obj, &mac_obj, &rlc_obj, &pdcp_obj, &ngap_obj, bearer_mapper, nullptr) ==
|
||||||
|
SRSRAN_SUCCESS);
|
||||||
|
|
||||||
|
sched_nr_ue_cfg_t uecfg = get_default_ue_cfg(1);
|
||||||
|
uecfg.phy_cfg.pdcch = rrc_cfg_nr.cell_list[0].phy_cell.pdcch;
|
||||||
|
uecfg.phy_cfg.pdcch.search_space_present[2] = false;
|
||||||
|
TESTASSERT_SUCCESS(rrc_obj.add_user(0x4601, uecfg));
|
||||||
|
|
||||||
|
// RRCSetupComplete triggers NGAP Initial UE Message with NAS-PDU: Registration Request
|
||||||
|
ngap_rrc_tester ngap_dummy;
|
||||||
|
test_rrc_nr_connection_establishment(task_sched, rrc_obj, rlc_obj, mac_obj, ngap_dummy, 0x4601);
|
||||||
|
task_sched.run_next_task();
|
||||||
|
|
||||||
|
// ULInformationTransfer -> UplinkNASTransport(NAS Authentication Response)
|
||||||
|
srsran::unique_byte_buffer_t auth_resp_pdu;
|
||||||
|
auth_resp_pdu = srsran::make_byte_buffer();
|
||||||
|
asn1::bit_ref bref_ar{auth_resp_pdu->data(), auth_resp_pdu->get_tailroom()};
|
||||||
|
ul_dcch_msg_s ul_dcch_msg_auth_resp;
|
||||||
|
|
||||||
|
ul_dcch_msg_auth_resp.msg.set_c1().set_ul_info_transfer().crit_exts.set_ul_info_transfer();
|
||||||
|
ul_dcch_msg_auth_resp.msg.c1().ul_info_transfer().crit_exts.ul_info_transfer().ded_nas_msg_present = true;
|
||||||
|
ul_dcch_msg_auth_resp.msg.c1().ul_info_transfer().crit_exts.ul_info_transfer().ded_nas_msg.from_string(
|
||||||
|
"7e00572d10db165fffdb7b74c326e3fc3f154117fe");
|
||||||
|
|
||||||
|
TESTASSERT_SUCCESS(ul_dcch_msg_auth_resp.pack(bref_ar));
|
||||||
|
auth_resp_pdu->N_bytes = bref_ar.distance_bytes();
|
||||||
|
rrc_obj.write_pdu(0x4601, 1, std::move(auth_resp_pdu));
|
||||||
|
task_sched.run_next_task();
|
||||||
|
|
||||||
|
// ULInformationTransfer -> UplinkNASTransport(NAS Security Mode Complete)
|
||||||
|
srsran::unique_byte_buffer_t sec_complete_pdu;
|
||||||
|
sec_complete_pdu = srsran::make_byte_buffer();
|
||||||
|
asn1::bit_ref bref_smc{sec_complete_pdu->data(), sec_complete_pdu->get_tailroom()};
|
||||||
|
ul_dcch_msg_s ul_dcch_msg_smc;
|
||||||
|
ul_dcch_msg_smc.msg.set_c1().set_ul_info_transfer().crit_exts.set_ul_info_transfer();
|
||||||
|
ul_dcch_msg_smc.msg.c1().ul_info_transfer().crit_exts.ul_info_transfer().ded_nas_msg_present = true;
|
||||||
|
ul_dcch_msg_smc.msg.c1().ul_info_transfer().crit_exts.ul_info_transfer().ded_nas_msg.from_string(
|
||||||
|
"7e046b3737af017e005e7700093535940096783351f37100237e004179000d0100f11000000000103254760810030000002e02e0602f0201"
|
||||||
|
"01530100");
|
||||||
|
TESTASSERT_SUCCESS(ul_dcch_msg_smc.pack(bref_smc));
|
||||||
|
sec_complete_pdu->N_bytes = bref_smc.distance_bytes();
|
||||||
|
rrc_obj.write_pdu(0x4601, 1, std::move(sec_complete_pdu));
|
||||||
|
task_sched.run_next_task();
|
||||||
|
|
||||||
|
test_rrc_nr_security_mode_cmd(task_sched, rrc_obj, pdcp_obj, 0x4601);
|
||||||
|
}
|
||||||
|
} // namespace srsenb
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
auto& logger = srslog::fetch_basic_logger("ASN1");
|
||||||
|
logger.set_level(srslog::basic_levels::info);
|
||||||
|
auto& rrc_logger = srslog::fetch_basic_logger("RRC-NR");
|
||||||
|
rrc_logger.set_level(srslog::basic_levels::debug);
|
||||||
|
|
||||||
|
srslog::init();
|
||||||
|
ngap_args_t ngap_args = {};
|
||||||
|
srsenb::parse_args(&ngap_args, argc, argv);
|
||||||
|
srsenb::test_rrc_sa_ngap_integration(ngap_args);
|
||||||
|
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SRSRAN_DUMMY_PHY_H
|
||||||
|
#define SRSRAN_DUMMY_PHY_H
|
||||||
|
|
||||||
|
#include "srsran/interfaces/phy_interface_types.h"
|
||||||
|
#include "srsue/hdr/phy/ue_phy_base.h"
|
||||||
|
|
||||||
|
namespace srsue {
|
||||||
|
|
||||||
|
class dummy_phy final : public ue_phy_base, public phy_interface_stack_lte
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// ue_phy_base
|
||||||
|
std::string get_type() final { return "dummy_phy"; }
|
||||||
|
void stop() final {}
|
||||||
|
void wait_initialize() final {}
|
||||||
|
bool is_initialized() final { return false; }
|
||||||
|
void start_plot() final {}
|
||||||
|
void get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* m) final {}
|
||||||
|
|
||||||
|
// phy_interface_stack_lte
|
||||||
|
void prach_send(uint32_t preamble_idx, int allowed_subframe, float target_power_dbm, float ta_base_sec = 0.0f) final
|
||||||
|
{}
|
||||||
|
prach_info_t prach_get_info() final { return {}; }
|
||||||
|
|
||||||
|
/* Indicates the transmission of a SR signal in the next opportunity */
|
||||||
|
void sr_send() final {}
|
||||||
|
int sr_last_tx_tti() final { return 0; }
|
||||||
|
|
||||||
|
void set_mch_period_stop(uint32_t stop) final {}
|
||||||
|
bool set_config(const srsran::phy_cfg_t& config, uint32_t cc_idx = 0) final { return false; }
|
||||||
|
bool set_scell(srsran_cell_t cell_info, uint32_t cc_idx, uint32_t earfcn) final { return false; }
|
||||||
|
void set_config_tdd(srsran_tdd_config_t& tdd_config) final {}
|
||||||
|
void set_config_mbsfn_sib2(srsran::mbsfn_sf_cfg_t* cfg_list, uint32_t nof_cfgs) final {}
|
||||||
|
void set_config_mbsfn_sib13(const srsran::sib13_t& sib13) final {}
|
||||||
|
void set_config_mbsfn_mcch(const srsran::mcch_msg_t& mcch) final {}
|
||||||
|
|
||||||
|
void deactivate_scells() final {}
|
||||||
|
|
||||||
|
/* Measurements interface */
|
||||||
|
void set_cells_to_meas(uint32_t earfcn, const std::set<uint32_t>& pci) final {}
|
||||||
|
void meas_stop() final {}
|
||||||
|
|
||||||
|
/* Cell search and selection procedures */
|
||||||
|
bool cell_search() final { return false; }
|
||||||
|
bool cell_select(phy_cell_t cell) final { return false; }
|
||||||
|
bool cell_is_camping() final { return false; }
|
||||||
|
|
||||||
|
/* Time advance commands */
|
||||||
|
void set_timeadv_rar(uint32_t tti, uint32_t ta_cmd) final {}
|
||||||
|
void set_timeadv(uint32_t tti, uint32_t ta_cmd) final {}
|
||||||
|
|
||||||
|
/* Activate / Disactivate SCell*/
|
||||||
|
void set_activation_deactivation_scell(uint32_t cmd, uint32_t tti) final {}
|
||||||
|
|
||||||
|
/* Sets RAR dci payload */
|
||||||
|
void set_rar_grant(uint8_t grant_payload[SRSRAN_RAR_GRANT_LEN], uint16_t rnti) final {}
|
||||||
|
|
||||||
|
uint32_t get_current_tti() final { return 0; }
|
||||||
|
|
||||||
|
float get_phr() final { return 0.0; }
|
||||||
|
float get_pathloss_db() final { return 0.0; }
|
||||||
|
};
|
||||||
|
} // namespace srsue
|
||||||
|
#endif // SRSRAN_DUMMY_PHY_H
|
@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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 SRSUE_CELL_SEARCH_H
|
||||||
|
#define SRSUE_CELL_SEARCH_H
|
||||||
|
|
||||||
|
#include "srsran/interfaces/radio_interfaces.h"
|
||||||
|
#include "srsran/interfaces/ue_nr_interfaces.h"
|
||||||
|
#include "srsran/srsran.h"
|
||||||
|
|
||||||
|
namespace srsue {
|
||||||
|
namespace nr {
|
||||||
|
class cell_search
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct args_t {
|
||||||
|
double max_srate_hz;
|
||||||
|
srsran_subcarrier_spacing_t ssb_min_scs = srsran_subcarrier_spacing_15kHz;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cfg_t {
|
||||||
|
double srate_hz;
|
||||||
|
double center_freq_hz;
|
||||||
|
double ssb_freq_hz;
|
||||||
|
srsran_subcarrier_spacing_t ssb_scs;
|
||||||
|
srsran_ssb_patern_t ssb_pattern;
|
||||||
|
srsran_duplex_mode_t duplex_mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ret_t {
|
||||||
|
enum { CELL_FOUND = 1, CELL_NOT_FOUND = 0, ERROR = -1 } result;
|
||||||
|
srsran_ssb_search_res_t ssb_res;
|
||||||
|
};
|
||||||
|
|
||||||
|
cell_search(srslog::basic_logger& logger);
|
||||||
|
~cell_search();
|
||||||
|
|
||||||
|
bool init(const args_t& args);
|
||||||
|
|
||||||
|
bool start(const cfg_t& cfg);
|
||||||
|
ret_t run_slot(const cf_t* buffer, uint32_t slot_sz);
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
private:
|
||||||
|
srslog::basic_logger& logger;
|
||||||
|
srsran_ssb_t ssb = {};
|
||||||
|
};
|
||||||
|
} // namespace nr
|
||||||
|
} // namespace srsue
|
||||||
|
|
||||||
|
#endif // SRSUE_CELL_SEARCH_H
|
@ -0,0 +1,62 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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 SRSUE_SLOT_SYNC_H
|
||||||
|
#define SRSUE_SLOT_SYNC_H
|
||||||
|
|
||||||
|
#include "srsran/interfaces/radio_interfaces.h"
|
||||||
|
#include "srsran/interfaces/ue_nr_interfaces.h"
|
||||||
|
#include "srsran/radio/rf_buffer.h"
|
||||||
|
#include "srsran/radio/rf_timestamp.h"
|
||||||
|
#include "srsran/srsran.h"
|
||||||
|
|
||||||
|
namespace srsue {
|
||||||
|
namespace nr {
|
||||||
|
class slot_sync
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct args_t {
|
||||||
|
double max_srate_hz = 1.92e6;
|
||||||
|
uint32_t nof_rx_channels = 1;
|
||||||
|
srsran_subcarrier_spacing_t ssb_min_scs = srsran_subcarrier_spacing_15kHz;
|
||||||
|
bool disable_cfo = false;
|
||||||
|
float pbch_dmrs_thr = 0.0f; ///< PBCH DMRS correlation detection threshold (0 means auto)
|
||||||
|
float cfo_alpha = 0.0f; ///< CFO averaging alpha (0 means auto)
|
||||||
|
int thread_priority = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
slot_sync(srslog::basic_logger& logger);
|
||||||
|
~slot_sync();
|
||||||
|
|
||||||
|
bool init(const args_t& args, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_);
|
||||||
|
|
||||||
|
int recv_callback(srsran::rf_buffer_t& rf_buffer, srsran_timestamp_t* timestamp);
|
||||||
|
void run_stack_tti();
|
||||||
|
|
||||||
|
private:
|
||||||
|
const static int MIN_TTI_JUMP = 1; ///< Time gap reported to stack after receiving subframe
|
||||||
|
const static int MAX_TTI_JUMP = 1000; ///< Maximum time gap tolerance in RF stream metadata
|
||||||
|
enum { SEARCHING = 0, CAMPING } state = SEARCHING;
|
||||||
|
srslog::basic_logger& logger;
|
||||||
|
stack_interface_phy_nr* stack = nullptr;
|
||||||
|
srsran::radio_interface_phy* radio = nullptr;
|
||||||
|
srsran::rf_timestamp_t last_rx_time;
|
||||||
|
srsran_ue_sync_nr_t ue_sync_nr = {};
|
||||||
|
srsran_timestamp_t stack_tti_ts_new = {};
|
||||||
|
srsran_timestamp_t stack_tti_ts = {};
|
||||||
|
bool forced_rx_time_init = true; // Rx time sync after first receive from radio
|
||||||
|
uint32_t tti = 0;
|
||||||
|
};
|
||||||
|
} // namespace nr
|
||||||
|
} // namespace srsue
|
||||||
|
|
||||||
|
#endif // SRSUE_SLOT_SYNC_H
|
@ -0,0 +1,122 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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 SRSUE_SYNC_NR_SA_H
|
||||||
|
#define SRSUE_SYNC_NR_SA_H
|
||||||
|
|
||||||
|
#include "cell_search.h"
|
||||||
|
#include "slot_sync.h"
|
||||||
|
#include "srsran/common/threads.h"
|
||||||
|
#include "srsran/interfaces/radio_interfaces.h"
|
||||||
|
#include "srsran/interfaces/ue_nr_interfaces.h"
|
||||||
|
#include "srsran/radio/rf_buffer.h"
|
||||||
|
#include "srsran/radio/rf_timestamp.h"
|
||||||
|
#include "srsran/srslog/logger.h"
|
||||||
|
#include "srsran/srsran.h"
|
||||||
|
#include "srsue/hdr/phy/sync_state.h"
|
||||||
|
#include "worker_pool.h"
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <mutex>
|
||||||
|
#include <srsran/common/tti_sempahore.h>
|
||||||
|
|
||||||
|
namespace srsue {
|
||||||
|
namespace nr {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief NR Standalone synchronization class
|
||||||
|
*/
|
||||||
|
class sync_sa : public srsran::thread, public srsran::phy_common_interface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct args_t {
|
||||||
|
double srate_hz = 61.44e6;
|
||||||
|
srsran_subcarrier_spacing_t ssb_min_scs = srsran_subcarrier_spacing_15kHz;
|
||||||
|
uint32_t nof_rx_channels = 1;
|
||||||
|
bool disable_cfo = false;
|
||||||
|
float pbch_dmrs_thr = 0.0f; ///< PBCH DMRS correlation detection threshold (0 means auto)
|
||||||
|
float cfo_alpha = 0.0f; ///< CFO averaging alpha (0 means auto)
|
||||||
|
int thread_priority = 1;
|
||||||
|
|
||||||
|
cell_search::args_t get_cell_search() const
|
||||||
|
{
|
||||||
|
cell_search::args_t ret = {};
|
||||||
|
ret.max_srate_hz = srate_hz;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
slot_sync::args_t get_slot_sync() const
|
||||||
|
{
|
||||||
|
slot_sync::args_t ret = {};
|
||||||
|
ret.max_srate_hz = srate_hz;
|
||||||
|
ret.nof_rx_channels = nof_rx_channels;
|
||||||
|
ret.ssb_min_scs = ssb_min_scs;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
sync_sa(srslog::basic_logger& logger, worker_pool& workers_);
|
||||||
|
~sync_sa();
|
||||||
|
|
||||||
|
bool init(const args_t& args_, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_);
|
||||||
|
bool reset();
|
||||||
|
void stop();
|
||||||
|
sync_state::state_t get_state();
|
||||||
|
|
||||||
|
// The following methods control the SYNC state machine
|
||||||
|
void cell_go_idle();
|
||||||
|
cell_search::ret_t cell_search_run(const cell_search::cfg_t& cfg);
|
||||||
|
bool cell_select_run(const phy_interface_rrc_nr::cell_select_args_t& req);
|
||||||
|
|
||||||
|
void worker_end(const worker_context_t& w_ctx, const bool& tx_enable, srsran::rf_buffer_t& buffer) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
stack_interface_phy_nr* stack = nullptr; ///< Stand-Alone RRC interface
|
||||||
|
srsran::radio_interface_phy* radio = nullptr; ///< Radio object
|
||||||
|
srslog::basic_logger& logger; ///< General PHY logger
|
||||||
|
worker_pool& workers;
|
||||||
|
|
||||||
|
// FSM that manages RRC commands for cell search/select/sync procedures
|
||||||
|
std::mutex rrc_mutex;
|
||||||
|
enum { PROC_IDLE = 0, PROC_SELECT_RUNNING, PROC_SEARCH_RUNNING } rrc_proc_state = PROC_IDLE;
|
||||||
|
sync_state phy_state;
|
||||||
|
|
||||||
|
std::atomic<bool> running = {false};
|
||||||
|
cf_t* rx_buffer = nullptr;
|
||||||
|
uint32_t slot_sz = 0; ///< Subframe size (1-ms)
|
||||||
|
uint32_t tti = 0;
|
||||||
|
srsran::tti_semaphore<void*> tti_semaphore;
|
||||||
|
srsran::rf_timestamp_t last_rx_time;
|
||||||
|
bool is_pending_tx_end = false;
|
||||||
|
uint32_t cell_search_nof_trials = 0;
|
||||||
|
const static uint32_t cell_search_max_trials = 100;
|
||||||
|
|
||||||
|
cell_search::ret_t cs_ret;
|
||||||
|
cell_search searcher;
|
||||||
|
slot_sync slot_synchronizer;
|
||||||
|
|
||||||
|
// FSM States
|
||||||
|
bool wait_idle();
|
||||||
|
void run_state_idle();
|
||||||
|
void run_state_cell_search();
|
||||||
|
void run_state_cell_select();
|
||||||
|
void run_state_cell_camping();
|
||||||
|
|
||||||
|
int radio_recv_fnc(srsran::rf_buffer_t& data, srsran_timestamp_t* rx_time);
|
||||||
|
void run_stack_tti();
|
||||||
|
void run_thread() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace nr
|
||||||
|
} // namespace srsue
|
||||||
|
|
||||||
|
#endif // SRSUE_SYNC_NR_SA_H
|
@ -0,0 +1,99 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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 SRSUE_PHY_NR_SA_H
|
||||||
|
#define SRSUE_PHY_NR_SA_H
|
||||||
|
|
||||||
|
#include "phy_common.h"
|
||||||
|
#include "srsran/interfaces/ue_nr_interfaces.h"
|
||||||
|
#include "srsue/hdr/phy/nr/sync_sa.h"
|
||||||
|
#include "srsue/hdr/phy/ue_phy_base.h"
|
||||||
|
|
||||||
|
namespace srsue {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief NR Standalone PHY
|
||||||
|
*/
|
||||||
|
class phy_nr_sa final : public ue_phy_base, public phy_interface_stack_nr, public srsran::phy_interface_radio
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
phy_nr_sa(const char* logname);
|
||||||
|
~phy_nr_sa() final { stop(); }
|
||||||
|
|
||||||
|
int init(const phy_args_nr_t& args_, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_);
|
||||||
|
void wait_initialize() final;
|
||||||
|
bool is_initialized() final;
|
||||||
|
void stop() final;
|
||||||
|
void reset_nr() final;
|
||||||
|
|
||||||
|
void start_plot() final {}
|
||||||
|
|
||||||
|
void radio_overflow() final {}
|
||||||
|
void radio_failure() final {}
|
||||||
|
|
||||||
|
std::string get_type() final { return "nr_soft"; }
|
||||||
|
|
||||||
|
int set_rar_grant(uint32_t rar_slot_idx,
|
||||||
|
std::array<uint8_t, SRSRAN_RAR_UL_GRANT_NBITS> packed_ul_grant,
|
||||||
|
uint16_t rnti,
|
||||||
|
srsran_rnti_type_t rnti_type) final;
|
||||||
|
void send_prach(const uint32_t prach_occasion,
|
||||||
|
const int preamble_index,
|
||||||
|
const float preamble_received_target_power,
|
||||||
|
const float ta_base_sec) final;
|
||||||
|
void set_earfcn(std::vector<uint32_t> earfcns);
|
||||||
|
bool has_valid_sr_resource(uint32_t sr_id) final;
|
||||||
|
void clear_pending_grants() final;
|
||||||
|
bool set_config(const srsran::phy_cfg_nr_t& cfg) final;
|
||||||
|
|
||||||
|
phy_nr_state_t get_state() final;
|
||||||
|
bool start_cell_search(const cell_search_args_t& req) final;
|
||||||
|
bool start_cell_select(const cell_select_args_t& req) final;
|
||||||
|
|
||||||
|
void get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* m) final{};
|
||||||
|
void srsran_phy_logger(phy_logger_level_t log_level, char* str);
|
||||||
|
|
||||||
|
private:
|
||||||
|
srslog::basic_logger& logger;
|
||||||
|
srslog::basic_logger& logger_phy_lib;
|
||||||
|
|
||||||
|
nr::worker_pool workers;
|
||||||
|
nr::sync_sa sync;
|
||||||
|
|
||||||
|
srsran::phy_cfg_nr_t config_nr = {};
|
||||||
|
phy_args_nr_t args = {};
|
||||||
|
srsran_carrier_nr_t selected_cell = {};
|
||||||
|
|
||||||
|
srsran::radio_interface_phy* radio = nullptr;
|
||||||
|
stack_interface_phy_nr* stack = nullptr;
|
||||||
|
|
||||||
|
std::atomic<bool> is_configured = {false};
|
||||||
|
|
||||||
|
// Since cell_search/cell_select operations take a lot of time, we use another queue to process the other commands
|
||||||
|
// in parallel and avoid accumulating in the queue
|
||||||
|
phy_cmd_proc cmd_worker_cell, cmd_worker;
|
||||||
|
|
||||||
|
// Run initialization functions in the background
|
||||||
|
void init_background();
|
||||||
|
std::thread init_thread;
|
||||||
|
|
||||||
|
const static int SF_RECV_THREAD_PRIO = 0;
|
||||||
|
const static int WORKERS_THREAD_PRIO = 2;
|
||||||
|
const static int MAX_WORKERS = 4;
|
||||||
|
const static int DEFAULT_WORKERS = 4;
|
||||||
|
|
||||||
|
static void set_default_args(phy_args_nr_t& args);
|
||||||
|
bool check_args(const phy_args_nr_t& args);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace srsue
|
||||||
|
#endif // SRSUE_PHY_NR_SA_H
|
@ -1,57 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright 2013-2021 Software Radio Systems Limited
|
|
||||||
*
|
|
||||||
* This file is part of srsRAN.
|
|
||||||
*
|
|
||||||
* srsRAN is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* srsRAN is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Affero General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
* File: ue_lte_phy_base.h
|
|
||||||
* Description: Base class for UE LTE PHYs.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef SRSUE_UE_LTE_PHY_BASE_H
|
|
||||||
#define SRSUE_UE_LTE_PHY_BASE_H
|
|
||||||
|
|
||||||
#include "srsran/interfaces/radio_interfaces.h"
|
|
||||||
#include "srsue/hdr/phy/ue_phy_base.h"
|
|
||||||
|
|
||||||
namespace srsue {
|
|
||||||
|
|
||||||
class stack_interface_phy_lte;
|
|
||||||
|
|
||||||
class ue_lte_phy_base : public ue_phy_base, public phy_interface_stack_lte, public srsran::phy_interface_radio
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ue_lte_phy_base(){};
|
|
||||||
virtual ~ue_lte_phy_base(){};
|
|
||||||
|
|
||||||
virtual std::string get_type() = 0;
|
|
||||||
|
|
||||||
virtual int init(const phy_args_t& args_) = 0;
|
|
||||||
virtual int init(const phy_args_t& args_, stack_interface_phy_lte* stack_, srsran::radio_interface_phy* radio_) = 0;
|
|
||||||
virtual void stop() = 0;
|
|
||||||
|
|
||||||
virtual void wait_initialize() = 0;
|
|
||||||
virtual void start_plot() = 0;
|
|
||||||
|
|
||||||
virtual void get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* m) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace srsue
|
|
||||||
|
|
||||||
#endif // SRSUE_UE_LTE_PHY_BASE_H
|
|
@ -1,55 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright 2013-2021 Software Radio Systems Limited
|
|
||||||
*
|
|
||||||
* This file is part of srsRAN.
|
|
||||||
*
|
|
||||||
* srsRAN is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* srsRAN is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Affero General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
* File: ue_nr_phy_base.h
|
|
||||||
* Description: Base class for UE NR PHYs.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef SRSUE_UE_NR_PHY_BASE_H
|
|
||||||
#define SRSUE_UE_NR_PHY_BASE_H
|
|
||||||
|
|
||||||
#include "srsran/interfaces/radio_interfaces.h"
|
|
||||||
#include "srsran/interfaces/ue_nr_interfaces.h"
|
|
||||||
#include "srsue/hdr/phy/ue_phy_base.h"
|
|
||||||
|
|
||||||
namespace srsue {
|
|
||||||
|
|
||||||
class ue_nr_phy_base : public phy_interface_stack_nr
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ue_nr_phy_base(){};
|
|
||||||
virtual ~ue_nr_phy_base() {}
|
|
||||||
|
|
||||||
virtual std::string get_type() = 0;
|
|
||||||
|
|
||||||
virtual int init(const phy_args_t& args_) = 0;
|
|
||||||
virtual int init(const phy_args_nr_t& args_, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_) = 0;
|
|
||||||
virtual void stop() = 0;
|
|
||||||
|
|
||||||
virtual void set_earfcn(std::vector<uint32_t> earfcns) = 0;
|
|
||||||
|
|
||||||
virtual void get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* m) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace srsue
|
|
||||||
|
|
||||||
#endif // SRSUE_UE_NR_PHY_BASE_H
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue