mirror of https://github.com/PentHertz/srsLTE.git
Merge branch 'next' into agpl_next
This commit is contained in:
commit
dd2c1f7695
|
@ -23,6 +23,7 @@ Checks: '*,-fuchsia-*,
|
|||
-google-runtime-references,-google-readability-casting,-google-build-using-namespace,
|
||||
google-default-arguments,-cppcoreguidelines-pro-bounds-pointer-arithmetic,
|
||||
-cert-err58-cpp,
|
||||
-altera-unroll-loops,
|
||||
-readability-function-cognitive-complexity,-readability-isolate-declaration,
|
||||
-misc-non-private-member-variables-in-classes,-altera-struct-pack-align,-readability-uppercase-literal-suffix,
|
||||
-cppcoreguidelines-non-private-member-variables-in-classes,
|
||||
|
|
|
@ -384,7 +384,6 @@ endmacro(ADD_C_COMPILER_FLAG_IF_AVAILABLE)
|
|||
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-comment -Wno-reorder -Wno-unused-variable -Wtype-limits -std=c++11 -fno-strict-aliasing")
|
||||
|
||||
ADD_C_COMPILER_FLAG_IF_AVAILABLE("-Wno-unused-but-set-variable" HAVE_WNO_UNUSED_BUT_SET_VARIABLE)
|
||||
ADD_CXX_COMPILER_FLAG_IF_AVAILABLE("-Wno-unused-but-set-variable" HAVE_WNO_UNUSED_BUT_SET_VARIABLE)
|
||||
|
||||
if (AUTO_DETECT_ISA)
|
||||
|
@ -424,6 +423,7 @@ ADD_C_COMPILER_FLAG_IF_AVAILABLE("-Werror=incompatible-pointer-types" HAVE_ERROR
|
|||
if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-comment -Wno-write-strings -Wno-unused-result -Wformat -Wmissing-field-initializers -Wtype-limits -std=c99 -fno-strict-aliasing -D_GNU_SOURCE")
|
||||
|
||||
ADD_C_COMPILER_FLAG_IF_AVAILABLE("-Wno-unused-but-set-variable" HAVE_WNO_UNUSED_BUT_SET_VARIABLE)
|
||||
if(${CMAKE_BUILD_TYPE} STREQUAL "Debug")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb -O0 -DDEBUG_MODE -DBUILD_TYPE_DEBUG")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb -O0 -DDEBUG_MODE -DBUILD_TYPE_DEBUG")
|
||||
|
|
|
@ -86,6 +86,17 @@ public:
|
|||
|
||||
bool contains(T point) const { return start_ <= point and point < stop_; }
|
||||
|
||||
interval<T>& intersect(const interval<T>& other)
|
||||
{
|
||||
if (not overlaps(other)) {
|
||||
*this = interval<T>{};
|
||||
} else {
|
||||
start_ = std::max(start(), other.start());
|
||||
stop_ = std::min(stop(), other.stop());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
T start_;
|
||||
T stop_;
|
||||
|
|
|
@ -308,6 +308,7 @@ public:
|
|||
return size() == other.size() and std::equal(data_, data_ + size(), other.data_);
|
||||
}
|
||||
void resize(uint32_t new_size) { current_size = new_size; }
|
||||
void clear() { resize(0); }
|
||||
void push_back(const T& elem)
|
||||
{
|
||||
if (current_size >= MAX_N) {
|
||||
|
|
|
@ -152,6 +152,8 @@ int make_rlc_config_t(const asn1::rrc_nr::rlc_cfg_c& asn1_type, uint8_t bearer_i
|
|||
/***************************
|
||||
* PDCP Config
|
||||
**************************/
|
||||
pdcp_config_t make_srb_pdcp_config_t(const uint8_t bearer_id, bool is_ue);
|
||||
pdcp_config_t make_nr_srb_pdcp_config_t(const uint8_t bearer_id, bool is_ue);
|
||||
pdcp_config_t make_drb_pdcp_config_t(const uint8_t bearer_id, bool is_ue, const asn1::rrc_nr::pdcp_cfg_s& pdcp_cfg);
|
||||
|
||||
} // namespace srsran
|
||||
|
|
|
@ -187,6 +187,23 @@ inline unique_byte_buffer_t make_byte_buffer(const char* debug_ctxt) noexcept
|
|||
return buffer;
|
||||
}
|
||||
|
||||
inline unique_byte_buffer_t make_byte_buffer(const uint8_t* payload, uint32_t len, const char* debug_ctxt) noexcept
|
||||
{
|
||||
std::unique_ptr<byte_buffer_t> buffer(new (std::nothrow) byte_buffer_t());
|
||||
if (buffer == nullptr) {
|
||||
srslog::fetch_basic_logger("POOL").error("Failed to allocate byte buffer in %s", debug_ctxt);
|
||||
} else {
|
||||
if (buffer->get_tailroom() >= len) {
|
||||
memcpy(buffer->msg, payload, len);
|
||||
buffer->N_bytes = len;
|
||||
} else {
|
||||
srslog::fetch_basic_logger("POOL").error(
|
||||
"Failed to create byte buffer in %s. Payload too large (%d > %d)", debug_ctxt, len, buffer->get_tailroom());
|
||||
}
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct byte_buffer_pool_deleter {
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
*
|
||||
* \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_NGAP_PCAP_H
|
||||
#define SRSRAN_NGAP_PCAP_H
|
||||
|
||||
#include "srsran/common/pcap.h"
|
||||
#include <string>
|
||||
|
||||
namespace srsran {
|
||||
|
||||
class ngap_pcap
|
||||
{
|
||||
public:
|
||||
ngap_pcap();
|
||||
~ngap_pcap() = default;
|
||||
ngap_pcap(const ngap_pcap& other) = delete;
|
||||
ngap_pcap& operator=(const ngap_pcap& other) = delete;
|
||||
ngap_pcap(ngap_pcap&& other) = delete;
|
||||
ngap_pcap& operator=(ngap_pcap&& other) = delete;
|
||||
|
||||
void enable();
|
||||
void open(const char* filename_);
|
||||
void close();
|
||||
void write_ngap(uint8_t* pdu, uint32_t pdu_len_bytes);
|
||||
|
||||
private:
|
||||
bool enable_write = false;
|
||||
std::string filename;
|
||||
FILE* pcap_file = nullptr;
|
||||
};
|
||||
|
||||
} // namespace srsran
|
||||
|
||||
#endif // SRSRAN_NGAP_PCAP_H
|
|
@ -33,6 +33,7 @@
|
|||
#define UDP_DLT 149 // UDP needs to be selected as protocol
|
||||
#define S1AP_LTE_DLT 150
|
||||
#define NAS_5G_DLT 151
|
||||
#define NGAP_5G_DLT 152
|
||||
|
||||
/* This structure gets written to the start of the file */
|
||||
typedef struct pcap_hdr_s {
|
||||
|
@ -189,6 +190,12 @@ typedef struct S1AP_Context_Info_s {
|
|||
unsigned char dummy;
|
||||
} S1AP_Context_Info_t;
|
||||
|
||||
/* Context information for every S1AP PDU that will be logged */
|
||||
typedef struct NGAP_Context_Info_s {
|
||||
// No Context yet
|
||||
unsigned char dummy;
|
||||
} NGAP_Context_Info_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
@ -213,6 +220,9 @@ int LTE_PCAP_RLC_WritePDU(FILE* fd, RLC_Context_Info_t* context, const unsigned
|
|||
/* Write an individual S1AP PDU (PCAP packet header + s1ap-context + s1ap-pdu) */
|
||||
int LTE_PCAP_S1AP_WritePDU(FILE* fd, S1AP_Context_Info_t* context, const unsigned char* PDU, unsigned int length);
|
||||
|
||||
/* Write an individual S1AP PDU (PCAP packet header + s1ap-context + s1ap-pdu) */
|
||||
int LTE_PCAP_NGAP_WritePDU(FILE* fd, NGAP_Context_Info_t* context, const unsigned char* PDU, unsigned int length);
|
||||
|
||||
/* Write an individual NR MAC PDU (PCAP packet header + UDP header + nr-mac-context + mac-pdu) */
|
||||
int NR_PCAP_MAC_UDP_WritePDU(FILE* fd, mac_nr_context_info_t* context, const unsigned char* PDU, unsigned int length);
|
||||
int NR_PCAP_PACK_MAC_CONTEXT_TO_BUFFER(mac_nr_context_info_t* context, uint8_t* buffer, unsigned int length);
|
||||
|
|
|
@ -68,6 +68,28 @@ static const char integrity_algorithm_id_text[INTEGRITY_ALGORITHM_ID_N_ITEMS][20
|
|||
"128-EIA2",
|
||||
"128-EIA3"};
|
||||
|
||||
typedef enum {
|
||||
CIPHERING_ALGORITHM_ID_NR_NEA0 = 0,
|
||||
CIPHERING_ALGORITHM_ID_NR_128_NEA1,
|
||||
CIPHERING_ALGORITHM_ID_NR_128_NEA2,
|
||||
CIPHERING_ALGORITHM_ID_NR_128_NEA3,
|
||||
CIPHERING_ALGORITHM_ID_NR_N_ITEMS,
|
||||
} CIPHERING_ALGORITHM_ID_NR_ENUM;
|
||||
static const char ciphering_algorithm_id_nr_text[CIPHERING_ALGORITHM_ID_N_ITEMS][20] = {"NEA0",
|
||||
"128-NEA1",
|
||||
"128-NEA2",
|
||||
"128-NEA3"};
|
||||
typedef enum {
|
||||
INTEGRITY_ALGORITHM_ID_NR_NIA0 = 0,
|
||||
INTEGRITY_ALGORITHM_ID_NR_128_NIA1,
|
||||
INTEGRITY_ALGORITHM_ID_NR_128_NIA2,
|
||||
INTEGRITY_ALGORITHM_ID_NR_128_NIA3,
|
||||
INTEGRITY_ALGORITHM_ID_NR_N_ITEMS,
|
||||
} INTEGRITY_ALGORITHM_ID_NR_ENUM;
|
||||
static const char integrity_algorithm_id_nr_text[INTEGRITY_ALGORITHM_ID_N_ITEMS][20] = {"NIA0",
|
||||
"128-NIA1",
|
||||
"128-NIA2",
|
||||
"128-NIA3"};
|
||||
typedef enum {
|
||||
SECURITY_DIRECTION_UPLINK = 0,
|
||||
SECURITY_DIRECTION_DOWNLINK = 1,
|
||||
|
@ -96,6 +118,15 @@ struct as_security_config_t {
|
|||
CIPHERING_ALGORITHM_ID_ENUM cipher_algo;
|
||||
};
|
||||
|
||||
struct nr_as_security_config_t {
|
||||
as_key_t k_nr_rrc_int;
|
||||
as_key_t k_nr_rrc_enc;
|
||||
as_key_t k_nr_up_int;
|
||||
as_key_t k_nr_up_enc;
|
||||
INTEGRITY_ALGORITHM_ID_NR_ENUM integ_algo;
|
||||
CIPHERING_ALGORITHM_ID_NR_ENUM cipher_algo;
|
||||
};
|
||||
|
||||
template <typename... Args>
|
||||
void log_error(const char* format, Args&&... args)
|
||||
{
|
||||
|
|
|
@ -24,14 +24,12 @@
|
|||
|
||||
#include "srsran/srsran.h"
|
||||
|
||||
#include "srsenb/hdr/stack/mac/sched_interface.h"
|
||||
#include "srsran/common/interfaces_common.h"
|
||||
#include "srsran/common/security.h"
|
||||
#include "srsran/interfaces/pdcp_interface_types.h"
|
||||
#include "srsran/interfaces/rlc_interface_types.h"
|
||||
#include "srsran/interfaces/rrc_interface_types.h"
|
||||
// EUTRA interfaces that are used unmodified
|
||||
#include "srsran/interfaces/enb_mac_interfaces.h"
|
||||
#include "srsran/interfaces/enb_rrc_interfaces.h"
|
||||
|
||||
namespace srsenb {
|
||||
|
@ -132,6 +130,17 @@ public:
|
|||
virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0;
|
||||
};
|
||||
|
||||
/*****************************
|
||||
* MAC internal INTERFACES
|
||||
****************************/
|
||||
|
||||
class mac_interface_pdu_demux_nr
|
||||
{
|
||||
public:
|
||||
// Called by PDU handler from Stack thread to store Msg3 content (According to O-RAN WG8 v3.0, Sec. 9.2.2.3.5 MAC)
|
||||
virtual void store_msg3(uint16_t rnti, srsran::unique_byte_buffer_t pdu) = 0;
|
||||
};
|
||||
|
||||
/*****************************
|
||||
* RRC INTERFACES
|
||||
****************************/
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#define SRSRAN_GNB_MAC_INTERFACES_H
|
||||
|
||||
#include "srsgnb/hdr/stack/mac/sched_nr_interface.h"
|
||||
#include "srsran/interfaces/enb_mac_interfaces.h"
|
||||
|
||||
namespace srsenb {
|
||||
|
||||
|
|
|
@ -33,12 +33,12 @@ public:
|
|||
virtual int ue_set_bitrates(uint16_t rnti, const asn1::ngap_nr::ue_aggregate_maximum_bit_rate_s& rates) = 0;
|
||||
virtual int set_aggregate_max_bitrate(uint16_t rnti, const asn1::ngap_nr::ue_aggregate_maximum_bit_rate_s& rates) = 0;
|
||||
virtual int ue_set_security_cfg_capabilities(uint16_t rnti, const asn1::ngap_nr::ue_security_cap_s& caps) = 0;
|
||||
virtual int start_security_mode_procedure(uint16_t rnti) = 0;
|
||||
virtual int start_security_mode_procedure(uint16_t rnti, srsran::unique_byte_buffer_t nas_pdu) = 0;
|
||||
virtual int
|
||||
establish_rrc_bearer(uint16_t rnti, uint16_t pdu_session_id, srsran::const_byte_span nas_pdu, uint32_t lcid) = 0;
|
||||
virtual int allocate_lcid(uint16_t rnti) = 0;
|
||||
virtual int release_bearers(uint16_t rnti) = 0;
|
||||
virtual void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) = 0;
|
||||
establish_rrc_bearer(uint16_t rnti, uint16_t pdu_session_id, srsran::const_byte_span nas_pdu, uint32_t lcid) = 0;
|
||||
virtual int allocate_lcid(uint16_t rnti) = 0;
|
||||
virtual int release_bearers(uint16_t rnti) = 0;
|
||||
virtual void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) = 0;
|
||||
};
|
||||
|
||||
} // namespace srsenb
|
||||
|
|
|
@ -96,6 +96,26 @@ struct rlc_am_config_t {
|
|||
int32_t t_status_prohibit; // Timer used by rx to prohibit tx of status PDU (ms)
|
||||
};
|
||||
|
||||
struct rlc_am_nr_config_t {
|
||||
/****************************************************************************
|
||||
* Configurable parameters
|
||||
* Ref: 3GPP TS 38.322 Section 7
|
||||
***************************************************************************/
|
||||
|
||||
rlc_am_nr_sn_size_t tx_sn_field_length; // Number of bits used for tx (UL) sequence number
|
||||
rlc_am_nr_sn_size_t rx_sn_field_length; // Number of bits used for rx (DL) sequence number
|
||||
|
||||
// Timers Ref: 3GPP TS 38.322 Section 7.3
|
||||
int32_t t_poll_retx; // Poll retx timeout (ms)
|
||||
int32_t t_reassembly; // Timer used by rx to detect PDU loss (ms)
|
||||
int32_t t_status_prohibit; // Timer used by rx to prohibit tx of status PDU (ms)
|
||||
|
||||
// Configurable Parameters. Ref: 3GPP TS 38.322 Section 7.4
|
||||
uint32_t max_retx_thresh; // Max number of retx
|
||||
int32_t poll_pdu; // Insert poll bit after this many PDUs
|
||||
int32_t poll_byte; // Insert poll bit after this much data (KB)
|
||||
};
|
||||
|
||||
struct rlc_um_config_t {
|
||||
/****************************************************************************
|
||||
* Configurable parameters
|
||||
|
@ -131,12 +151,13 @@ public:
|
|||
srsran_rat_t rat;
|
||||
rlc_mode_t rlc_mode;
|
||||
rlc_am_config_t am;
|
||||
rlc_am_nr_config_t am_nr;
|
||||
rlc_um_config_t um;
|
||||
rlc_um_nr_config_t um_nr;
|
||||
uint32_t tx_queue_length;
|
||||
|
||||
rlc_config_t() :
|
||||
rat(srsran_rat_t::lte), rlc_mode(rlc_mode_t::tm), am(), um(), um_nr(), tx_queue_length(RLC_TX_QUEUE_LEN){};
|
||||
rat(srsran_rat_t::lte), rlc_mode(rlc_mode_t::tm), am(), am_nr(), um(), um_nr(), tx_queue_length(RLC_TX_QUEUE_LEN){};
|
||||
|
||||
// Factory for MCH
|
||||
static rlc_config_t mch_config()
|
||||
|
@ -207,6 +228,16 @@ public:
|
|||
rlc_cnfg.am.t_poll_retx = 5;
|
||||
return rlc_cnfg;
|
||||
}
|
||||
static rlc_config_t default_rlc_am_nr_config()
|
||||
{
|
||||
rlc_config_t rlc_cnfg = {};
|
||||
rlc_cnfg.rat = srsran_rat_t::nr;
|
||||
rlc_cnfg.rlc_mode = rlc_mode_t::am;
|
||||
rlc_cnfg.am_nr.t_status_prohibit = 8;
|
||||
rlc_cnfg.am_nr.t_reassembly = 35;
|
||||
rlc_cnfg.am_nr.poll_pdu = 4;
|
||||
return rlc_cnfg;
|
||||
}
|
||||
static rlc_config_t default_rlc_um_nr_config(uint32_t sn_size = 6)
|
||||
{
|
||||
rlc_config_t cnfg = {};
|
||||
|
|
|
@ -45,6 +45,9 @@ public:
|
|||
uint32_t pid;
|
||||
uint16_t rnti;
|
||||
bool is_sps_release;
|
||||
bool is_pdcch_order;
|
||||
uint32_t preamble_idx;
|
||||
uint32_t prach_mask_idx;
|
||||
uint32_t tti;
|
||||
} mac_grant_dl_t;
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ public:
|
|||
bool is_sdu() const;
|
||||
bool is_valid_lcid();
|
||||
bool is_var_len_ce(uint32_t lcid);
|
||||
bool is_ul_ccch();
|
||||
bool is_ul_ccch() const;
|
||||
|
||||
int32_t read_subheader(const uint8_t* ptr);
|
||||
uint32_t get_total_length() const;
|
||||
|
@ -103,6 +103,11 @@ public:
|
|||
};
|
||||
ta_t get_ta();
|
||||
|
||||
// UE contention resolution identity CE
|
||||
static const uint8_t ue_con_res_id_len = 6;
|
||||
typedef std::array<uint8_t, ue_con_res_id_len> ue_con_res_id_t;
|
||||
ue_con_res_id_t get_ue_con_res_id_ce();
|
||||
|
||||
// setters
|
||||
void set_sdu(const uint32_t lcid_, const uint8_t* payload_, const uint32_t len_);
|
||||
void set_padding(const uint32_t len_);
|
||||
|
@ -110,6 +115,7 @@ public:
|
|||
void set_se_phr(const uint8_t phr_, const uint8_t pcmax_);
|
||||
void set_sbsr(const lcg_bsr_t bsr_);
|
||||
void set_lbsr(const std::array<mac_sch_subpdu_nr::lcg_bsr_t, max_num_lcg_lbsr> bsr_);
|
||||
void set_ue_con_res_id_ce(const ue_con_res_id_t id);
|
||||
|
||||
uint32_t write_subpdu(const uint8_t* start_);
|
||||
|
||||
|
@ -212,6 +218,7 @@ public:
|
|||
uint32_t add_se_phr_ce(const uint8_t phr_, const uint8_t pcmax_);
|
||||
uint32_t add_sbsr_ce(const mac_sch_subpdu_nr::lcg_bsr_t bsr_);
|
||||
uint32_t add_lbsr_ce(const std::array<mac_sch_subpdu_nr::lcg_bsr_t, mac_sch_subpdu_nr::max_num_lcg_lbsr> bsr_);
|
||||
uint32_t add_ue_con_res_id_ce(const mac_sch_subpdu_nr::ue_con_res_id_t id);
|
||||
|
||||
uint32_t get_remaing_len();
|
||||
|
||||
|
|
|
@ -79,7 +79,6 @@ typedef struct SRSRAN_API {
|
|||
} srsran_dci_tb_t;
|
||||
|
||||
typedef struct SRSRAN_API {
|
||||
|
||||
uint16_t rnti;
|
||||
srsran_dci_format_t format;
|
||||
srsran_dci_location_t location;
|
||||
|
@ -103,10 +102,10 @@ typedef struct SRSRAN_API {
|
|||
bool power_offset;
|
||||
uint8_t tpc_pucch;
|
||||
|
||||
// RA order
|
||||
bool is_ra_order;
|
||||
uint32_t ra_preamble;
|
||||
uint32_t ra_mask_idx;
|
||||
// PDCCH order
|
||||
bool is_pdcch_order;
|
||||
uint32_t preamble_idx;
|
||||
uint32_t prach_mask_idx;
|
||||
|
||||
// Release 10
|
||||
uint32_t cif;
|
||||
|
@ -130,7 +129,6 @@ typedef struct SRSRAN_API {
|
|||
|
||||
/** Unpacked DCI Format0 message */
|
||||
typedef struct SRSRAN_API {
|
||||
|
||||
uint16_t rnti;
|
||||
srsran_dci_format_t format;
|
||||
srsran_dci_location_t location;
|
||||
|
|
|
@ -192,11 +192,19 @@ public:
|
|||
srslog::basic_logger* logger = nullptr;
|
||||
byte_buffer_pool* pool = nullptr;
|
||||
rlc_am* parent = nullptr;
|
||||
|
||||
protected:
|
||||
std::atomic<bool> do_status = {false}; // light-weight access from Tx entity
|
||||
};
|
||||
|
||||
protected:
|
||||
std::unique_ptr<rlc_am_base_tx> tx_base = {};
|
||||
std::unique_ptr<rlc_am_base_rx> rx_base = {};
|
||||
|
||||
public:
|
||||
// Getters for TX/RX entities. Useful for testing.
|
||||
rlc_am_base_rx* get_rx() { return rx_base.get(); }
|
||||
rlc_am_base_tx* get_tx() { return tx_base.get(); }
|
||||
};
|
||||
|
||||
} // namespace srsran
|
||||
|
|
|
@ -126,8 +126,8 @@ public:
|
|||
|
||||
const uint32_t rlc_sn = invalid_rlc_sn;
|
||||
uint32_t retx_count = 0;
|
||||
HeaderType header;
|
||||
unique_byte_buffer_t buf;
|
||||
HeaderType header = {};
|
||||
unique_byte_buffer_t buf = nullptr;
|
||||
|
||||
explicit rlc_amd_tx_pdu(uint32_t rlc_sn_) : rlc_sn(rlc_sn_) {}
|
||||
rlc_amd_tx_pdu(const rlc_amd_tx_pdu&) = delete;
|
||||
|
@ -309,6 +309,59 @@ private:
|
|||
uint32_t count = 0;
|
||||
};
|
||||
|
||||
struct rlc_amd_retx_t {
|
||||
uint32_t sn;
|
||||
bool is_segment;
|
||||
uint32_t so_start;
|
||||
uint32_t so_end;
|
||||
};
|
||||
|
||||
template <std::size_t WINDOW_SIZE>
|
||||
class pdu_retx_queue
|
||||
{
|
||||
public:
|
||||
rlc_amd_retx_t& push()
|
||||
{
|
||||
assert(not full());
|
||||
rlc_amd_retx_t& p = buffer[wpos];
|
||||
wpos = (wpos + 1) % WINDOW_SIZE;
|
||||
return p;
|
||||
}
|
||||
|
||||
void pop() { rpos = (rpos + 1) % WINDOW_SIZE; }
|
||||
|
||||
rlc_amd_retx_t& front()
|
||||
{
|
||||
assert(not empty());
|
||||
return buffer[rpos];
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
wpos = 0;
|
||||
rpos = 0;
|
||||
}
|
||||
|
||||
bool has_sn(uint32_t sn) const
|
||||
{
|
||||
for (size_t i = rpos; i != wpos; i = (i + 1) % WINDOW_SIZE) {
|
||||
if (buffer[i].sn == sn) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t size() const { return (wpos >= rpos) ? wpos - rpos : WINDOW_SIZE + wpos - rpos; }
|
||||
bool empty() const { return wpos == rpos; }
|
||||
bool full() const { return size() == WINDOW_SIZE - 1; }
|
||||
|
||||
private:
|
||||
std::array<rlc_amd_retx_t, WINDOW_SIZE> buffer;
|
||||
size_t wpos = 0;
|
||||
size_t rpos = 0;
|
||||
};
|
||||
|
||||
} // namespace srsran
|
||||
|
||||
#endif // SRSRAN_RLC_AM_DATA_STRUCTS_H
|
||||
|
|
|
@ -44,51 +44,6 @@ namespace srsran {
|
|||
|
||||
#undef RLC_AM_BUFFER_DEBUG
|
||||
|
||||
class pdu_retx_queue
|
||||
{
|
||||
public:
|
||||
rlc_amd_retx_t& push()
|
||||
{
|
||||
assert(not full());
|
||||
rlc_amd_retx_t& p = buffer[wpos];
|
||||
wpos = (wpos + 1) % RLC_AM_WINDOW_SIZE;
|
||||
return p;
|
||||
}
|
||||
|
||||
void pop() { rpos = (rpos + 1) % RLC_AM_WINDOW_SIZE; }
|
||||
|
||||
rlc_amd_retx_t& front()
|
||||
{
|
||||
assert(not empty());
|
||||
return buffer[rpos];
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
wpos = 0;
|
||||
rpos = 0;
|
||||
}
|
||||
|
||||
bool has_sn(uint32_t sn) const
|
||||
{
|
||||
for (size_t i = rpos; i != wpos; i = (i + 1) % RLC_AM_WINDOW_SIZE) {
|
||||
if (buffer[i].sn == sn) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t size() const { return (wpos >= rpos) ? wpos - rpos : RLC_AM_WINDOW_SIZE + wpos - rpos; }
|
||||
bool empty() const { return wpos == rpos; }
|
||||
bool full() const { return size() == RLC_AM_WINDOW_SIZE - 1; }
|
||||
|
||||
private:
|
||||
std::array<rlc_amd_retx_t, RLC_AM_WINDOW_SIZE> buffer;
|
||||
size_t wpos = 0;
|
||||
size_t rpos = 0;
|
||||
};
|
||||
|
||||
/******************************
|
||||
*
|
||||
* RLC AM LTE entity
|
||||
|
@ -192,7 +147,7 @@ private:
|
|||
|
||||
// Tx windows
|
||||
rlc_ringbuffer_t<rlc_amd_tx_pdu<rlc_amd_pdu_header_t>, RLC_AM_WINDOW_SIZE> tx_window;
|
||||
pdu_retx_queue retx_queue;
|
||||
pdu_retx_queue<RLC_AM_WINDOW_SIZE> retx_queue;
|
||||
pdcp_sn_vector_t notify_info_vec;
|
||||
|
||||
// Mutexes
|
||||
|
|
|
@ -29,13 +29,6 @@
|
|||
|
||||
namespace srsran {
|
||||
|
||||
struct rlc_amd_retx_t {
|
||||
uint32_t sn;
|
||||
bool is_segment;
|
||||
uint32_t so_start;
|
||||
uint32_t so_end;
|
||||
};
|
||||
|
||||
struct rlc_sn_info_t {
|
||||
uint32_t sn;
|
||||
bool is_acked;
|
||||
|
|
|
@ -44,7 +44,41 @@ namespace srsran {
|
|||
class rlc_am_nr_tx;
|
||||
class rlc_am_nr_rx;
|
||||
|
||||
// Transmitter sub-class
|
||||
/****************************************************************************
|
||||
* Tx state variables
|
||||
* Ref: 3GPP TS 38.322 v16.2.0 Section 7.1
|
||||
***************************************************************************/
|
||||
struct rlc_am_nr_tx_state_t {
|
||||
/*
|
||||
* TX_Next_Ack: This state variable holds the value of the SN of the next RLC SDU for which a positive
|
||||
* acknowledgment is to be received in-sequence, and it serves as the lower edge of the transmitting window. It is
|
||||
* initially set to 0, and is updated whenever the AM RLC entity receives a positive acknowledgment for an RLC SDU
|
||||
* with SN = TX_Next_Ack.
|
||||
*/
|
||||
uint32_t tx_next_ack;
|
||||
/*
|
||||
* TX_Next: This state variable holds the value of the SN to be assigned for the next newly generated AMD PDU. It is
|
||||
* initially set to 0, and is updated whenever the AM RLC entity constructs an AMD PDU with SN = TX_Next and
|
||||
* contains an RLC SDU or the last segment of a RLC SDU.
|
||||
*/
|
||||
uint32_t tx_next;
|
||||
/*
|
||||
* POLL_SN: This state variable holds the value of the highest SN of the AMD PDU among the AMD PDUs submitted to
|
||||
* lower layer when POLL_SN is set according to sub clause 5.3.3.2. It is initially set to 0.
|
||||
*/
|
||||
uint32_t poll_sn;
|
||||
/*
|
||||
* PDU_WITHOUT_POLL: This counter is initially set to 0. It counts the number of AMD PDUs sent since the most recent
|
||||
* poll bit was transmitted.
|
||||
*/
|
||||
uint32_t pdu_without_poll;
|
||||
/*
|
||||
* BYTE_WITHOUT_POLL: This counter is initially set to 0. It counts the number of data bytes sent since the most
|
||||
* recent poll bit was transmitted.
|
||||
*/
|
||||
uint32_t byte_without_poll;
|
||||
};
|
||||
|
||||
class rlc_am_nr_tx : public rlc_am::rlc_am_base_tx
|
||||
{
|
||||
public:
|
||||
|
@ -64,7 +98,14 @@ public:
|
|||
void empty_queue() final;
|
||||
bool has_data() final;
|
||||
uint32_t get_buffer_state() final;
|
||||
void get_buffer_state(uint32_t& tx_queue, uint32_t& prio_tx_queue);
|
||||
void get_buffer_state(uint32_t& tx_queue, uint32_t& prio_tx_queue) final;
|
||||
|
||||
bool do_status();
|
||||
uint32_t build_status_pdu(byte_buffer_t* payload, uint32_t nof_bytes);
|
||||
|
||||
uint8_t get_pdu_poll();
|
||||
|
||||
int build_retx_pdu(unique_byte_buffer_t& tx_pdu, uint32_t nof_bytes);
|
||||
|
||||
void stop() final;
|
||||
|
||||
|
@ -74,24 +115,24 @@ private:
|
|||
|
||||
/****************************************************************************
|
||||
* Configurable parameters
|
||||
* Ref: 3GPP TS 38.322 v10.0.0 Section 7.4
|
||||
* Ref: 3GPP TS 38.322 v16.2.0 Section 7.4
|
||||
***************************************************************************/
|
||||
rlc_am_config_t cfg = {};
|
||||
rlc_am_nr_config_t cfg = {};
|
||||
|
||||
/****************************************************************************
|
||||
* Tx state variables
|
||||
* Ref: 3GPP TS 38.322 v10.0.0 Section 7.1
|
||||
* Ref: 3GPP TS 38.322 v16.2.0 Section 7.1
|
||||
***************************************************************************/
|
||||
struct rlc_nr_tx_state_t {
|
||||
uint32_t tx_next_ack;
|
||||
uint32_t tx_next;
|
||||
uint32_t poll_sn;
|
||||
uint32_t pdu_without_poll;
|
||||
uint32_t byte_without_poll;
|
||||
} st = {};
|
||||
struct rlc_am_nr_tx_state_t st = {};
|
||||
|
||||
using rlc_amd_tx_pdu_nr = rlc_amd_tx_pdu<rlc_am_nr_pdu_header_t>;
|
||||
rlc_ringbuffer_t<rlc_amd_tx_pdu_nr, RLC_AM_WINDOW_SIZE> tx_window;
|
||||
pdu_retx_queue<RLC_AM_WINDOW_SIZE> retx_queue;
|
||||
|
||||
public:
|
||||
// Getters/Setters
|
||||
rlc_am_nr_tx_state_t get_tx_state() { return st; } // This should only be used for testing.
|
||||
uint32_t get_tx_window_size() { return tx_window.size(); } // This should only be used for testing.
|
||||
};
|
||||
|
||||
// Receiver sub-class
|
||||
|
@ -109,19 +150,75 @@ public:
|
|||
void stop();
|
||||
void reestablish();
|
||||
|
||||
uint32_t get_sdu_rx_latency_ms();
|
||||
uint32_t get_rx_buffered_bytes();
|
||||
// Status PDU
|
||||
bool get_do_status();
|
||||
uint32_t get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t len);
|
||||
uint32_t get_status_pdu_length();
|
||||
|
||||
// Data handling methods
|
||||
void handle_data_pdu_full(uint8_t* payload, uint32_t nof_bytes, rlc_am_nr_pdu_header_t& header);
|
||||
bool inside_rx_window(uint32_t sn);
|
||||
void write_to_upper_layers(uint32_t lcid, unique_byte_buffer_t sdu);
|
||||
|
||||
// Metrics
|
||||
uint32_t get_sdu_rx_latency_ms() final;
|
||||
uint32_t get_rx_buffered_bytes() final;
|
||||
|
||||
// Timers
|
||||
void timer_expired(uint32_t timeout_id);
|
||||
|
||||
// Helpers
|
||||
void debug_state();
|
||||
|
||||
private:
|
||||
rlc_am* parent = nullptr;
|
||||
rlc_am_nr_tx* tx = nullptr;
|
||||
byte_buffer_pool* pool = nullptr;
|
||||
|
||||
// RX Window
|
||||
rlc_ringbuffer_t<rlc_amd_rx_sdu_nr_t, RLC_AM_WINDOW_SIZE> rx_window;
|
||||
|
||||
// Mutexes
|
||||
std::mutex mutex;
|
||||
|
||||
/****************************************************************************
|
||||
* State Variables
|
||||
* Ref: 3GPP TS 38.322 v16.2.0 Section 7.1
|
||||
***************************************************************************/
|
||||
/*
|
||||
* RX_Next: This state variable holds the value of the SN following the last in-sequence completely received RLC
|
||||
* SDU, and it serves as the lower edge of the receiving window. It is initially set to 0, and is updated whenever
|
||||
* the AM RLC entity receives an RLC SDU with SN = RX_Next.
|
||||
*/
|
||||
uint32_t rx_next = 0;
|
||||
/*
|
||||
* RX_Next_Status_Trigger: This state variable holds the value of the SN following the SN of the RLC SDU which
|
||||
* triggered t-Reassembly.
|
||||
*/
|
||||
uint32_t rx_next_status_trigger = 0;
|
||||
/*
|
||||
* RX_Next_Highest: This state variable holds the highest possible value of the SN which can be indicated by
|
||||
*"ACK_SN" when a STATUS PDU needs to be constructed. It is initially set to 0.
|
||||
*/
|
||||
uint32_t rx_highest_status = 0;
|
||||
/*
|
||||
* RX_Next_Highest: This state variable holds the value of the SN following the SN of the RLC SDU with the
|
||||
* highest SN among received RLC SDUs. It is initially set to 0.
|
||||
*/
|
||||
uint32_t rx_next_highest = 0;
|
||||
|
||||
/****************************************************************************
|
||||
* Rx timers
|
||||
* Ref: 3GPP TS 38.322 v16.2.0 Section 7.3
|
||||
***************************************************************************/
|
||||
srsran::timer_handler::unique_timer status_prohibit_timer;
|
||||
srsran::timer_handler::unique_timer reassembly_timer;
|
||||
|
||||
/****************************************************************************
|
||||
* Configurable parameters
|
||||
* Ref: 3GPP TS 38.322 v10.0.0 Section 7.4
|
||||
* Ref: 3GPP TS 38.322 v16.2.0 Section 7.4
|
||||
***************************************************************************/
|
||||
rlc_am_config_t cfg = {};
|
||||
rlc_am_nr_config_t cfg = {};
|
||||
};
|
||||
|
||||
} // namespace srsran
|
||||
|
|
|
@ -22,25 +22,53 @@
|
|||
#ifndef SRSRAN_RLC_AM_NR_PACKING_H
|
||||
#define SRSRAN_RLC_AM_NR_PACKING_H
|
||||
|
||||
#include "srsran/common/string_helpers.h"
|
||||
#include "srsran/rlc/rlc_am_base.h"
|
||||
|
||||
namespace srsran {
|
||||
|
||||
///< AM NR PDU header
|
||||
typedef struct {
|
||||
rlc_dc_field_t dc; ///< Data/Control (D/C) field
|
||||
uint8_t p; ///< Polling bit
|
||||
rlc_nr_si_field_t si; ///< Segmentation info
|
||||
rlc_am_nr_sn_size_t sn_size; ///< Sequence number size (12 or 18 bits)
|
||||
uint32_t sn; ///< Sequence number
|
||||
uint16_t so; ///< Sequence offset
|
||||
} rlc_am_nr_pdu_header_t;
|
||||
struct rlc_am_nr_pdu_header_t {
|
||||
rlc_am_nr_pdu_header_t() = default;
|
||||
rlc_am_nr_pdu_header_t(const rlc_am_nr_pdu_header_t& h) = default;
|
||||
rlc_am_nr_pdu_header_t& operator=(const rlc_am_nr_pdu_header_t&) = default;
|
||||
rlc_am_nr_pdu_header_t(rlc_am_nr_pdu_header_t&& h) = default;
|
||||
~rlc_am_nr_pdu_header_t() = default;
|
||||
|
||||
rlc_am_nr_pdu_header_t& operator=(rlc_am_nr_pdu_header_t&& h) = default;
|
||||
|
||||
rlc_dc_field_t dc = {}; ///< Data/Control (D/C) field
|
||||
uint8_t p = {}; ///< Polling bit
|
||||
rlc_nr_si_field_t si = {}; ///< Segmentation info
|
||||
rlc_am_nr_sn_size_t sn_size = {}; ///< Sequence number size (12 or 18 bits)
|
||||
uint32_t sn = {}; ///< Sequence number
|
||||
uint16_t so = {}; ///< Sequence offset
|
||||
};
|
||||
|
||||
struct rlc_amd_pdu_nr_t {
|
||||
rlc_am_nr_pdu_header_t header;
|
||||
unique_byte_buffer_t buf;
|
||||
};
|
||||
|
||||
struct rlc_amd_rx_pdu_nr {
|
||||
rlc_am_nr_pdu_header_t header = {};
|
||||
unique_byte_buffer_t buf = nullptr;
|
||||
uint32_t rlc_sn = {};
|
||||
|
||||
rlc_amd_rx_pdu_nr() = default;
|
||||
explicit rlc_amd_rx_pdu_nr(uint32_t rlc_sn_) : rlc_sn(rlc_sn_) {}
|
||||
};
|
||||
|
||||
struct rlc_amd_rx_sdu_nr_t {
|
||||
uint32_t rlc_sn = 0;
|
||||
bool fully_received = false;
|
||||
unique_byte_buffer_t buf;
|
||||
std::list<rlc_amd_rx_pdu_nr> segments;
|
||||
|
||||
rlc_amd_rx_sdu_nr_t() = default;
|
||||
explicit rlc_amd_rx_sdu_nr_t(uint32_t rlc_sn_) : rlc_sn(rlc_sn_) {}
|
||||
};
|
||||
|
||||
///< AM NR Status PDU header (perhaps merge with LTE version)
|
||||
typedef struct {
|
||||
rlc_am_nr_control_pdu_type_t cpt;
|
||||
|
@ -79,6 +107,55 @@ int32_t rlc_am_nr_write_status_pdu(const rlc_am_nr_status_pdu_t& status_pdu,
|
|||
const rlc_am_nr_sn_size_t sn_size,
|
||||
byte_buffer_t* pdu);
|
||||
|
||||
/**
|
||||
* Logs Status PDU into provided log channel, using fmt_str as format string
|
||||
*/
|
||||
template <typename... Args>
|
||||
void log_rlc_am_nr_status_pdu_to_string(srslog::log_channel& log_ch,
|
||||
const char* fmt_str,
|
||||
rlc_am_nr_status_pdu_t* status,
|
||||
Args&&... args)
|
||||
{
|
||||
if (not log_ch.enabled()) {
|
||||
return;
|
||||
}
|
||||
fmt::memory_buffer buffer;
|
||||
fmt::format_to(buffer, "ACK_SN = {}, N_nack = {}", status->ack_sn, status->N_nack);
|
||||
if (status->N_nack > 0) {
|
||||
fmt::format_to(buffer, ", NACK_SN = ");
|
||||
for (uint32_t i = 0; i < status->N_nack; ++i) {
|
||||
if (status->nacks[i].has_so) {
|
||||
fmt::format_to(
|
||||
buffer, "[{} {}:{}]", status->nacks[i].nack_sn, status->nacks[i].so_start, status->nacks[i].so_end);
|
||||
} else {
|
||||
fmt::format_to(buffer, "[{}]", status->nacks[i].nack_sn);
|
||||
}
|
||||
}
|
||||
}
|
||||
log_ch(fmt_str, std::forward<Args>(args)..., to_c_str(buffer));
|
||||
}
|
||||
|
||||
/*
|
||||
* Log NR AMD PDUs
|
||||
*/
|
||||
inline void log_rlc_am_nr_pdu_header_to_string(srslog::log_channel& log_ch, const rlc_am_nr_pdu_header_t& header)
|
||||
{
|
||||
if (not log_ch.enabled()) {
|
||||
return;
|
||||
}
|
||||
fmt::memory_buffer buffer;
|
||||
fmt::format_to(buffer,
|
||||
"[{}, P={}, SI={}, SN_SIZE={}, SN={}, SO={}",
|
||||
rlc_dc_field_text[header.dc],
|
||||
(header.p ? "1" : "0"),
|
||||
to_string_short(header.si),
|
||||
header.sn,
|
||||
header.sn,
|
||||
header.so);
|
||||
fmt::format_to(buffer, "]");
|
||||
|
||||
log_ch("%s", to_c_str(buffer));
|
||||
}
|
||||
} // namespace srsran
|
||||
|
||||
#endif // SRSRAN_RLC_AM_NR_PACKING_H
|
||||
|
|
|
@ -117,8 +117,25 @@ int make_rlc_config_t(const rlc_cfg_c& asn1_type, uint8_t bearer_id, rlc_config_
|
|||
rlc_cfg.rat = srsran_rat_t::nr;
|
||||
switch (asn1_type.type().value) {
|
||||
case rlc_cfg_c::types_opts::am:
|
||||
asn1::log_warning("NR RLC type %s is not supported", asn1_type.type().to_string());
|
||||
return SRSRAN_ERROR;
|
||||
if (asn1_type.am().dl_am_rlc.sn_field_len_present && asn1_type.am().ul_am_rlc.sn_field_len_present &&
|
||||
asn1_type.am().dl_am_rlc.sn_field_len != asn1_type.am().ul_am_rlc.sn_field_len) {
|
||||
asn1::log_warning("NR RLC sequence number length is not the same in uplink and downlink");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
rlc_cfg.rlc_mode = rlc_mode_t::am;
|
||||
switch (asn1_type.am().dl_am_rlc.sn_field_len.value) {
|
||||
case asn1::rrc_nr::sn_field_len_am_opts::options::size12:
|
||||
rlc_cfg.am_nr.tx_sn_field_length = rlc_am_nr_sn_size_t::size12bits;
|
||||
rlc_cfg.am_nr.rx_sn_field_length = rlc_am_nr_sn_size_t::size12bits;
|
||||
break;
|
||||
case asn1::rrc_nr::sn_field_len_am_opts::options::size18:
|
||||
rlc_cfg.am_nr.tx_sn_field_length = rlc_am_nr_sn_size_t::size18bits;
|
||||
rlc_cfg.am_nr.rx_sn_field_length = rlc_am_nr_sn_size_t::size18bits;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case rlc_cfg_c::types_opts::um_bi_dir:
|
||||
rlc_cfg.rlc_mode = rlc_mode_t::um;
|
||||
rlc_cfg.um_nr.t_reassembly_ms = asn1_type.um_bi_dir().dl_um_rlc.t_reassembly.to_number();
|
||||
|
@ -155,6 +172,20 @@ int make_rlc_config_t(const rlc_cfg_c& asn1_type, uint8_t bearer_id, rlc_config_
|
|||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
srsran::pdcp_config_t make_nr_srb_pdcp_config_t(const uint8_t bearer_id, bool is_ue)
|
||||
{
|
||||
pdcp_config_t cfg(bearer_id,
|
||||
PDCP_RB_IS_SRB,
|
||||
is_ue ? SECURITY_DIRECTION_UPLINK : SECURITY_DIRECTION_DOWNLINK,
|
||||
is_ue ? SECURITY_DIRECTION_DOWNLINK : SECURITY_DIRECTION_UPLINK,
|
||||
PDCP_SN_LEN_12,
|
||||
pdcp_t_reordering_t::ms500,
|
||||
pdcp_discard_timer_t::infinity,
|
||||
false,
|
||||
srsran_rat_t::lte);
|
||||
return cfg;
|
||||
}
|
||||
|
||||
srsran::pdcp_config_t make_drb_pdcp_config_t(const uint8_t bearer_id, bool is_ue, const pdcp_cfg_s& pdcp_cfg)
|
||||
{
|
||||
// TODO: complete config processing
|
||||
|
|
|
@ -139,12 +139,12 @@ srsran::rlc_config_t make_rlc_config_t(const asn1::rrc::rlc_cfg_c& asn1_type)
|
|||
srsran::rlc_config_t rlc_cfg;
|
||||
switch (asn1_type.type().value) {
|
||||
case asn1::rrc::rlc_cfg_c::types_opts::am:
|
||||
rlc_cfg.rlc_mode = rlc_mode_t::am;
|
||||
rlc_cfg.am.t_poll_retx = asn1_type.am().ul_am_rlc.t_poll_retx.to_number();
|
||||
rlc_cfg.am.poll_pdu = asn1_type.am().ul_am_rlc.poll_pdu.to_number();
|
||||
rlc_cfg.am.poll_byte = asn1_type.am().ul_am_rlc.poll_byte.to_number() < 0
|
||||
? -1
|
||||
: asn1_type.am().ul_am_rlc.poll_byte.to_number() * 1000; // KB
|
||||
rlc_cfg.rlc_mode = rlc_mode_t::am;
|
||||
rlc_cfg.am.t_poll_retx = asn1_type.am().ul_am_rlc.t_poll_retx.to_number();
|
||||
rlc_cfg.am.poll_pdu = asn1_type.am().ul_am_rlc.poll_pdu.to_number();
|
||||
rlc_cfg.am.poll_byte = asn1_type.am().ul_am_rlc.poll_byte.to_number() < 0
|
||||
? -1
|
||||
: asn1_type.am().ul_am_rlc.poll_byte.to_number() * 1000; // KB
|
||||
rlc_cfg.am.max_retx_thresh = asn1_type.am().ul_am_rlc.max_retx_thres.to_number();
|
||||
rlc_cfg.am.t_reordering = asn1_type.am().dl_am_rlc.t_reordering.to_number();
|
||||
rlc_cfg.am.t_status_prohibit = asn1_type.am().dl_am_rlc.t_status_prohibit.to_number();
|
||||
|
|
|
@ -39,6 +39,7 @@ set(SOURCES arch_select.cc
|
|||
rrc_common.cc
|
||||
rlc_pcap.cc
|
||||
s1ap_pcap.cc
|
||||
ngap_pcap.cc
|
||||
security.cc
|
||||
standard_streams.cc
|
||||
thread_pool.cc
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "srsran/common/ngap_pcap.h"
|
||||
#include "srsran/common/pcap.h"
|
||||
#include "srsran/srsran.h"
|
||||
#include "srsran/support/emergency_handlers.h"
|
||||
#include <stdint.h>
|
||||
|
||||
namespace srsran {
|
||||
|
||||
/// Try to flush the contents of the pcap class before the application is killed.
|
||||
static void emergency_cleanup_handler(void* data)
|
||||
{
|
||||
reinterpret_cast<ngap_pcap*>(data)->close();
|
||||
}
|
||||
|
||||
ngap_pcap::ngap_pcap()
|
||||
{
|
||||
add_emergency_cleanup_handler(emergency_cleanup_handler, this);
|
||||
}
|
||||
|
||||
void ngap_pcap::enable()
|
||||
{
|
||||
enable_write = true;
|
||||
}
|
||||
void ngap_pcap::open(const char* filename_)
|
||||
{
|
||||
filename = filename_;
|
||||
pcap_file = DLT_PCAP_Open(NGAP_5G_DLT, filename.c_str());
|
||||
enable_write = true;
|
||||
}
|
||||
void ngap_pcap::close()
|
||||
{
|
||||
if (!enable_write) {
|
||||
return;
|
||||
}
|
||||
fprintf(stdout, "Saving NGAP PCAP file (DLT=%d) to %s\n", NGAP_5G_DLT, filename.c_str());
|
||||
DLT_PCAP_Close(pcap_file);
|
||||
}
|
||||
|
||||
void ngap_pcap::write_ngap(uint8_t* pdu, uint32_t pdu_len_bytes)
|
||||
{
|
||||
if (enable_write) {
|
||||
NGAP_Context_Info_t context;
|
||||
if (pdu) {
|
||||
LTE_PCAP_NGAP_WritePDU(pcap_file, &context, pdu, pdu_len_bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace srsran
|
|
@ -345,6 +345,34 @@ int LTE_PCAP_S1AP_WritePDU(FILE* fd, S1AP_Context_Info_t* context, const unsigne
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* Write an individual PDU (PCAP packet header + ngap-context + ngap-pdu) */
|
||||
int LTE_PCAP_NGAP_WritePDU(FILE* fd, NGAP_Context_Info_t* context, const unsigned char* PDU, unsigned int length)
|
||||
{
|
||||
pcaprec_hdr_t packet_header;
|
||||
|
||||
/* Can't write if file wasn't successfully opened */
|
||||
if (fd == NULL) {
|
||||
printf("Error: Can't write to empty file handle\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************/
|
||||
/* PCAP Header */
|
||||
struct timeval t;
|
||||
gettimeofday(&t, NULL);
|
||||
packet_header.ts_sec = t.tv_sec;
|
||||
packet_header.ts_usec = t.tv_usec;
|
||||
packet_header.incl_len = length;
|
||||
packet_header.orig_len = length;
|
||||
|
||||
/***************************************************************/
|
||||
/* Now write everything to the file */
|
||||
fwrite(&packet_header, sizeof(pcaprec_hdr_t), 1, fd);
|
||||
fwrite(PDU, 1, length, fd);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
* API functions for writing MAC-NR PCAP files *
|
||||
**************************************************************************/
|
||||
|
@ -452,4 +480,4 @@ int NR_PCAP_MAC_UDP_WritePDU(FILE* fd, mac_nr_context_info_t* context, const uns
|
|||
fwrite(PDU, 1, length, fd);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,4 +22,6 @@ SET(SOURCES pdu.cc pdu_queue.cc mac_sch_pdu_nr.cc mac_rar_pdu_nr.cc)
|
|||
|
||||
add_library(srsran_mac STATIC ${SOURCES})
|
||||
target_link_libraries(srsran_mac srsran_common)
|
||||
INSTALL(TARGETS srsran_mac DESTINATION ${LIBRARY_DIR})
|
||||
INSTALL(TARGETS srsran_mac DESTINATION ${LIBRARY_DIR})
|
||||
|
||||
add_subdirectory(test)
|
|
@ -34,7 +34,8 @@ mac_sch_subpdu_nr::nr_lcid_sch_t mac_sch_subpdu_nr::get_type()
|
|||
|
||||
bool mac_sch_subpdu_nr::is_sdu() const
|
||||
{
|
||||
return (lcid <= 32);
|
||||
// UL-CCCH handling in done as CE
|
||||
return (lcid <= 32 && !is_ul_ccch());
|
||||
}
|
||||
|
||||
bool mac_sch_subpdu_nr::has_length_field()
|
||||
|
@ -56,12 +57,18 @@ bool mac_sch_subpdu_nr::is_valid_lcid()
|
|||
|
||||
bool mac_sch_subpdu_nr::is_var_len_ce(uint32_t lcid)
|
||||
{
|
||||
switch (lcid) {
|
||||
case LONG_TRUNC_BSR:
|
||||
case LONG_BSR:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
if (parent->is_ulsch()) {
|
||||
// UL fixed-size CE
|
||||
switch (lcid) {
|
||||
case LONG_TRUNC_BSR:
|
||||
case LONG_BSR:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// all currently supported CEs in the DL are fixed-size
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -162,6 +169,18 @@ void mac_sch_subpdu_nr::set_sbsr(const lcg_bsr_t bsr_)
|
|||
// Turn a subPDU into a long BSR with variable size
|
||||
void mac_sch_subpdu_nr::set_lbsr(const std::array<mac_sch_subpdu_nr::lcg_bsr_t, max_num_lcg_lbsr> bsr_) {}
|
||||
|
||||
// Turn subPDU into a Con
|
||||
void mac_sch_subpdu_nr::set_ue_con_res_id_ce(const mac_sch_subpdu_nr::ue_con_res_id_t id)
|
||||
{
|
||||
lcid = CON_RES_ID;
|
||||
header_length = 1;
|
||||
sdu_length = sizeof_ce(lcid, parent->is_ulsch());
|
||||
uint8_t* ptr = sdu.use_internal_storage();
|
||||
for (int32_t i = 0; i < sdu_length; ++i) {
|
||||
ptr[i] = id.at(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Section 6.1.2
|
||||
uint32_t mac_sch_subpdu_nr::write_subpdu(const uint8_t* start_)
|
||||
{
|
||||
|
@ -312,6 +331,16 @@ mac_sch_subpdu_nr::lbsr_t mac_sch_subpdu_nr::get_lbsr() const
|
|||
return lbsr;
|
||||
}
|
||||
|
||||
mac_sch_subpdu_nr::ue_con_res_id_t mac_sch_subpdu_nr::get_ue_con_res_id_ce()
|
||||
{
|
||||
mac_sch_subpdu_nr::ue_con_res_id_t id;
|
||||
if (!parent->is_ulsch() && lcid == CON_RES_ID) {
|
||||
const uint8_t* ptr = sdu.ptr();
|
||||
memcpy(id.data(), ptr, id.size());
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
uint32_t mac_sch_subpdu_nr::sizeof_ce(uint32_t lcid, bool is_ul)
|
||||
{
|
||||
if (is_ul) {
|
||||
|
@ -348,7 +377,7 @@ uint32_t mac_sch_subpdu_nr::sizeof_ce(uint32_t lcid, bool is_ul)
|
|||
return 0;
|
||||
}
|
||||
|
||||
inline bool mac_sch_subpdu_nr::is_ul_ccch()
|
||||
bool mac_sch_subpdu_nr::is_ul_ccch() const
|
||||
{
|
||||
return (parent->is_ulsch() && (lcid == CCCH_SIZE_48 || lcid == CCCH_SIZE_64));
|
||||
}
|
||||
|
@ -362,6 +391,12 @@ void mac_sch_subpdu_nr::to_string(fmt::memory_buffer& buffer)
|
|||
if (parent->is_ulsch()) {
|
||||
// UL-SCH case
|
||||
switch (get_lcid()) {
|
||||
case mac_sch_subpdu_nr::CCCH_SIZE_48:
|
||||
fmt::format_to(buffer, " CCCH48: len={}", get_sdu_length());
|
||||
break;
|
||||
case mac_sch_subpdu_nr::CCCH_SIZE_64:
|
||||
fmt::format_to(buffer, " CCCH64: len={}", get_sdu_length());
|
||||
break;
|
||||
case mac_sch_subpdu_nr::CRNTI:
|
||||
fmt::format_to(buffer, " C-RNTI: {:#04x}", get_c_rnti());
|
||||
break;
|
||||
|
@ -389,7 +424,7 @@ void mac_sch_subpdu_nr::to_string(fmt::memory_buffer& buffer)
|
|||
fmt::format_to(buffer, " PAD: len={}", get_sdu_length());
|
||||
break;
|
||||
default:
|
||||
fmt::format_to(buffer, " CE={}", get_lcid());
|
||||
fmt::format_to(buffer, " CE={} total_len={}", get_lcid(), get_total_length());
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
|
@ -398,14 +433,22 @@ void mac_sch_subpdu_nr::to_string(fmt::memory_buffer& buffer)
|
|||
case mac_sch_subpdu_nr::TA_CMD:
|
||||
fmt::format_to(buffer, " TA: id={} command={}", get_ta().tag_id, get_ta().ta_command);
|
||||
break;
|
||||
case mac_sch_subpdu_nr::CON_RES_ID:
|
||||
fmt::format_to(buffer, " CONRES: len={}", get_total_length());
|
||||
break;
|
||||
case mac_sch_subpdu_nr::CON_RES_ID: {
|
||||
ue_con_res_id_t id = get_ue_con_res_id_ce();
|
||||
fmt::format_to(buffer,
|
||||
" CON_RES: id={:x}{:x}{:x}{:x}{:x}{:x}",
|
||||
id.at(0),
|
||||
id.at(1),
|
||||
id.at(2),
|
||||
id.at(3),
|
||||
id.at(4),
|
||||
id.at(5));
|
||||
} break;
|
||||
case mac_sch_subpdu_nr::PADDING:
|
||||
fmt::format_to(buffer, " PAD: len={}", get_sdu_length());
|
||||
break;
|
||||
default:
|
||||
fmt::format_to(buffer, " CE={}", get_lcid());
|
||||
fmt::format_to(buffer, " CE={} total_len={}", get_lcid(), get_total_length());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -546,6 +589,13 @@ mac_sch_pdu_nr::add_lbsr_ce(const std::array<mac_sch_subpdu_nr::lcg_bsr_t, mac_s
|
|||
return add_sudpdu(ce);
|
||||
}
|
||||
|
||||
uint32_t mac_sch_pdu_nr::add_ue_con_res_id_ce(const mac_sch_subpdu_nr::ue_con_res_id_t id)
|
||||
{
|
||||
mac_sch_subpdu_nr ce(this);
|
||||
ce.set_ue_con_res_id_ce(id);
|
||||
return add_sudpdu(ce);
|
||||
}
|
||||
|
||||
uint32_t mac_sch_pdu_nr::add_sudpdu(mac_sch_subpdu_nr& subpdu)
|
||||
{
|
||||
uint32_t subpdu_len = subpdu.get_total_length();
|
||||
|
|
|
@ -261,6 +261,71 @@ int mac_dl_sch_pdu_unpack_test6()
|
|||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
int mac_dl_sch_pdu_unpack_pack_test7()
|
||||
{
|
||||
// MAC PDU with DL-SCH subheader with ConRes CE and dummy 8B SDU on SRB1
|
||||
uint8_t tv[] = {
|
||||
0x3e, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x04, 0x08, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x3f, 0x00};
|
||||
uint8_t con_res_id_tv[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
|
||||
|
||||
// unpack
|
||||
srsran::mac_sch_pdu_nr rx_pdu;
|
||||
rx_pdu.unpack(tv, sizeof(tv));
|
||||
|
||||
TESTASSERT(rx_pdu.get_num_subpdus() == 3);
|
||||
|
||||
// Read ConRes CE
|
||||
mac_sch_subpdu_nr subpdu = rx_pdu.get_subpdu(0);
|
||||
TESTASSERT(subpdu.get_total_length() == 7);
|
||||
TESTASSERT(subpdu.get_sdu_length() == 6);
|
||||
TESTASSERT(subpdu.get_lcid() == 0x3e);
|
||||
mac_sch_subpdu_nr::ue_con_res_id_t con_res = subpdu.get_ue_con_res_id_ce();
|
||||
TESTASSERT(memcmp(con_res.data(), con_res_id_tv, con_res.size()) == 0);
|
||||
|
||||
// skip other subPDUs ..
|
||||
|
||||
// pack again
|
||||
const uint32_t sdu_len = 8;
|
||||
uint8_t sdu[sdu_len] = {};
|
||||
|
||||
// populate SDU payload
|
||||
for (uint32_t i = 0; i < sdu_len; i++) {
|
||||
sdu[i] = i % 256;
|
||||
}
|
||||
|
||||
// pack buffer
|
||||
byte_buffer_t tx_buffer;
|
||||
|
||||
srsran::mac_sch_pdu_nr tx_pdu;
|
||||
tx_pdu.init_tx(&tx_buffer, sizeof(tv));
|
||||
|
||||
// add ConRes CE
|
||||
srsran::mac_sch_subpdu_nr::ue_con_res_id_t id = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
|
||||
TESTASSERT(tx_pdu.add_ue_con_res_id_ce(id) == SRSRAN_SUCCESS);
|
||||
|
||||
// Add SDU
|
||||
tx_pdu.add_sdu(4, sdu, sizeof(sdu));
|
||||
|
||||
tx_pdu.pack();
|
||||
|
||||
TESTASSERT(tx_buffer.N_bytes == sizeof(tv));
|
||||
TESTASSERT(memcmp(tx_buffer.msg, tv, tx_buffer.N_bytes) == 0);
|
||||
|
||||
if (pcap_handle) {
|
||||
pcap_handle->write_dl_crnti_nr(tx_buffer.msg, tx_buffer.N_bytes, PCAP_CRNTI, true, PCAP_TTI);
|
||||
}
|
||||
|
||||
// pretty print PDU
|
||||
fmt::memory_buffer buff;
|
||||
tx_pdu.to_string(buff);
|
||||
|
||||
auto& mac_logger = srslog::fetch_basic_logger("MAC");
|
||||
mac_logger.info(
|
||||
tx_buffer.msg, tx_buffer.N_bytes, "Generated MAC PDU (%d B): %s", tx_buffer.N_bytes, srsran::to_c_str(buff));
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
int mac_rar_pdu_test7()
|
||||
{
|
||||
// MAC PDU with RAR PDU with single RAPID=0
|
||||
|
@ -791,6 +856,35 @@ int mac_ul_sch_pdu_unpack_test6()
|
|||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
int mac_ul_sch_pdu_unpack_test7()
|
||||
{
|
||||
// TV1 - MAC PDU with UL-SCH with CCCH (48 bits) subPDU (LCID=0x34) and padding
|
||||
uint8_t mac_ul_sch_pdu_1[] = {0x34, 0x10, 0xb7, 0xcd, 0x6e, 0x38, 0xa6, 0x3f, 0x21, 0x21, 0x21};
|
||||
const uint8_t* ccch_sdu = &mac_ul_sch_pdu_1[1];
|
||||
const uint32_t ccch_sdu_len = 6;
|
||||
|
||||
if (pcap_handle) {
|
||||
pcap_handle->write_ul_crnti_nr(mac_ul_sch_pdu_1, sizeof(mac_ul_sch_pdu_1), PCAP_CRNTI, true, PCAP_TTI);
|
||||
}
|
||||
|
||||
srsran::mac_sch_pdu_nr pdu(true);
|
||||
pdu.unpack(mac_ul_sch_pdu_1, sizeof(mac_ul_sch_pdu_1));
|
||||
TESTASSERT(pdu.get_num_subpdus() == 2);
|
||||
|
||||
// 1st is CCCH
|
||||
mac_sch_subpdu_nr subpdu = pdu.get_subpdu(0);
|
||||
TESTASSERT(subpdu.get_total_length() == 7);
|
||||
TESTASSERT(subpdu.get_sdu_length() == 6);
|
||||
TESTASSERT(subpdu.get_lcid() == 0x34);
|
||||
TESTASSERT(memcmp(subpdu.get_sdu(), ccch_sdu, ccch_sdu_len) == 0);
|
||||
|
||||
// 2nd is padding
|
||||
subpdu = pdu.get_subpdu(1);
|
||||
TESTASSERT(subpdu.get_lcid() == mac_sch_subpdu_nr::PADDING);
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
#if PCAP
|
||||
|
@ -834,6 +928,11 @@ int main(int argc, char** argv)
|
|||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
if (mac_dl_sch_pdu_unpack_pack_test7()) {
|
||||
fprintf(stderr, "mac_dl_sch_pdu_unpack_pack_test7() failed.\n");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
if (mac_rar_pdu_test7()) {
|
||||
fprintf(stderr, "mac_rar_pdu_unpack_test7() failed.\n");
|
||||
return SRSRAN_ERROR;
|
||||
|
@ -889,6 +988,11 @@ int main(int argc, char** argv)
|
|||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
if (mac_ul_sch_pdu_unpack_test7()) {
|
||||
fprintf(stderr, "mac_ul_sch_pdu_unpack_test7() failed.\n");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
if (pcap_handle) {
|
||||
pcap_handle->close();
|
||||
}
|
|
@ -815,10 +815,6 @@ bool pdcp_entity_lte::check_valid_config()
|
|||
logger.error("Trying to configure SRB or RLC AM bearer with SN LEN of 7");
|
||||
return false;
|
||||
}
|
||||
if (cfg.sn_len == PDCP_SN_LEN_12 && is_srb()) {
|
||||
logger.error("Trying to configure SRB with SN LEN of 12.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -79,6 +79,9 @@ static void dmrs_pdcch_put_symbol(const srsran_carrier_nr_t* carrier,
|
|||
// CORESET Resource Block counter
|
||||
uint32_t rb_coreset_idx = 0;
|
||||
|
||||
// Get CORESET offset
|
||||
uint32_t offset_k = coreset->offset_rb * SRSRAN_NRE;
|
||||
|
||||
// For each frequency resource (6 RB groups)
|
||||
for (uint32_t res_idx = 0; res_idx < nof_freq_res; res_idx++) {
|
||||
// Skip frequency resource if outside of the CORESET
|
||||
|
@ -113,7 +116,7 @@ static void dmrs_pdcch_put_symbol(const srsran_carrier_nr_t* carrier,
|
|||
uint32_t k = n * SRSRAN_NRE + 4 * k_prime + 1;
|
||||
|
||||
// Write DMRS
|
||||
sf_symbol[k] = rl[k_prime];
|
||||
sf_symbol[k + offset_k] = rl[k_prime];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -705,7 +705,7 @@ static int dci_format1_unpack(srsran_cell_t* cell,
|
|||
|
||||
/* Packs DCI format 1A for compact scheduling of PDSCH words according to 36.212 5.3.3.1.3
|
||||
*
|
||||
* TODO: RA procedure initiated by PDCCH, TPC commands
|
||||
* TODO: TPC commands
|
||||
*/
|
||||
static int dci_format1As_pack(srsran_cell_t* cell,
|
||||
srsran_dl_sf_cfg_t* sf,
|
||||
|
@ -723,49 +723,64 @@ static int dci_format1As_pack(srsran_cell_t* cell,
|
|||
|
||||
*y++ = 1; // format differentiation
|
||||
|
||||
if (dci->alloc_type != SRSRAN_RA_ALLOC_TYPE2) {
|
||||
ERROR("Format 1A accepts type2 resource allocation only");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
// random access procedure initiated by a PDCCH order
|
||||
if (dci->is_pdcch_order) {
|
||||
*y++ = 0; // localized or distributed VRB assignment is always 0 for PDCCH order
|
||||
|
||||
*y++ = dci->type2_alloc.mode; // localized or distributed VRB assignment
|
||||
|
||||
/* pack RIV according to 7.1.6.3 of 36.213 */
|
||||
uint32_t riv = dci->type2_alloc.riv;
|
||||
uint32_t nb_gap = 0;
|
||||
if (SRSRAN_RNTI_ISUSER(dci->rnti) && dci->type2_alloc.mode == SRSRAN_RA_TYPE2_DIST && nof_prb >= 50) {
|
||||
nb_gap = 1;
|
||||
*y++ = dci->type2_alloc.n_gap;
|
||||
}
|
||||
srsran_bit_unpack(riv, &y, riv_nbits(nof_prb) - nb_gap);
|
||||
|
||||
// in format1A, MCS = TBS according to 7.1.7.2 of 36.213
|
||||
srsran_bit_unpack(dci->tb[0].mcs_idx, &y, 5);
|
||||
|
||||
srsran_bit_unpack(dci->pid, &y, HARQ_PID_LEN);
|
||||
|
||||
if (!SRSRAN_RNTI_ISUSER(dci->rnti)) {
|
||||
if (nof_prb >= 50 && dci->type2_alloc.mode == SRSRAN_RA_TYPE2_DIST) {
|
||||
*y++ = dci->type2_alloc.n_gap;
|
||||
} else {
|
||||
y++; // bit reserved
|
||||
// RIV values all set to 1 for PDCCH order
|
||||
int nof_bits = riv_nbits(cell->nof_prb);
|
||||
int i = 0;
|
||||
while (i < nof_bits) {
|
||||
*y++ = 1;
|
||||
i++;
|
||||
}
|
||||
|
||||
srsran_bit_unpack(dci->preamble_idx, &y, 6); // preamble index
|
||||
srsran_bit_unpack(dci->prach_mask_idx, &y, 4); // PRACH mask index
|
||||
} else {
|
||||
*y++ = dci->tb[0].ndi;
|
||||
if (dci->alloc_type != SRSRAN_RA_ALLOC_TYPE2) {
|
||||
ERROR("Format 1A accepts type2 resource allocation only");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
*y++ = dci->type2_alloc.mode; // localized or distributed VRB assignment
|
||||
|
||||
/* pack RIV according to 7.1.6.3 of 36.213 */
|
||||
uint32_t riv = dci->type2_alloc.riv;
|
||||
uint32_t nb_gap = 0;
|
||||
if (SRSRAN_RNTI_ISUSER(dci->rnti) && dci->type2_alloc.mode == SRSRAN_RA_TYPE2_DIST && nof_prb >= 50) {
|
||||
nb_gap = 1;
|
||||
*y++ = dci->type2_alloc.n_gap;
|
||||
}
|
||||
srsran_bit_unpack(riv, &y, riv_nbits(nof_prb) - nb_gap);
|
||||
|
||||
// in format1A, MCS = TBS according to 7.1.7.2 of 36.213
|
||||
srsran_bit_unpack(dci->tb[0].mcs_idx, &y, 5);
|
||||
|
||||
srsran_bit_unpack(dci->pid, &y, HARQ_PID_LEN);
|
||||
|
||||
if (!SRSRAN_RNTI_ISUSER(dci->rnti)) {
|
||||
if (nof_prb >= 50 && dci->type2_alloc.mode == SRSRAN_RA_TYPE2_DIST) {
|
||||
*y++ = dci->type2_alloc.n_gap;
|
||||
} else {
|
||||
y++; // bit reserved
|
||||
}
|
||||
} else {
|
||||
*y++ = dci->tb[0].ndi;
|
||||
}
|
||||
|
||||
// rv version
|
||||
srsran_bit_unpack(dci->tb[0].rv, &y, 2);
|
||||
|
||||
if (SRSRAN_RNTI_ISUSER(dci->rnti)) {
|
||||
// TPC not implemented
|
||||
*y++ = 0;
|
||||
*y++ = 0;
|
||||
} else {
|
||||
y++; // MSB of TPC is reserved
|
||||
*y++ = dci->type2_alloc.n_prb1a; // LSB indicates N_prb_1a for TBS
|
||||
}
|
||||
}
|
||||
|
||||
// rv version
|
||||
srsran_bit_unpack(dci->tb[0].rv, &y, 2);
|
||||
|
||||
if (SRSRAN_RNTI_ISUSER(dci->rnti)) {
|
||||
// TPC not implemented
|
||||
*y++ = 0;
|
||||
*y++ = 0;
|
||||
} else {
|
||||
y++; // MSB of TPC is reserved
|
||||
*y++ = dci->type2_alloc.n_prb1a; // LSB indicates N_prb_1a for TBS
|
||||
}
|
||||
|
||||
// Padding with zeros
|
||||
uint32_t n = srsran_dci_format_sizeof(cell, sf, cfg, SRSRAN_DCI_FORMAT1A);
|
||||
while (y - msg->payload < n) {
|
||||
|
@ -819,16 +834,16 @@ static int dci_format1As_unpack(srsran_cell_t* cell,
|
|||
// This is a Random access order
|
||||
y += 1 + nof_bits;
|
||||
|
||||
dci->is_ra_order = true;
|
||||
dci->ra_preamble = srsran_bit_pack(&y, 6);
|
||||
dci->ra_mask_idx = srsran_bit_pack(&y, 4);
|
||||
dci->is_pdcch_order = true;
|
||||
dci->preamble_idx = srsran_bit_pack(&y, 6);
|
||||
dci->prach_mask_idx = srsran_bit_pack(&y, 4);
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dci->is_ra_order = false;
|
||||
dci->is_pdcch_order = false;
|
||||
|
||||
dci->alloc_type = SRSRAN_RA_ALLOC_TYPE2;
|
||||
dci->type2_alloc.mode = *y++;
|
||||
|
@ -1553,36 +1568,46 @@ static char* freq_hop_fl_string(int freq_hop)
|
|||
|
||||
void srsran_dci_dl_fprint(FILE* f, srsran_dci_dl_t* dci, uint32_t nof_prb)
|
||||
{
|
||||
fprintf(f, " - Resource Allocation Type:\t\t%s\n", ra_type_string(dci->alloc_type));
|
||||
switch (dci->alloc_type) {
|
||||
case SRSRAN_RA_ALLOC_TYPE0:
|
||||
fprintf(f, " + Resource Block Group Size:\t\t%d\n", srsran_ra_type0_P(nof_prb));
|
||||
fprintf(f, " + RBG Bitmap:\t\t\t0x%x\n", dci->type0_alloc.rbg_bitmask);
|
||||
break;
|
||||
case SRSRAN_RA_ALLOC_TYPE1:
|
||||
fprintf(f, " + Resource Block Group Size:\t\t%d\n", srsran_ra_type0_P(nof_prb));
|
||||
fprintf(f, " + RBG Bitmap:\t\t\t0x%x\n", dci->type1_alloc.vrb_bitmask);
|
||||
fprintf(f, " + RBG Subset:\t\t\t%d\n", dci->type1_alloc.rbg_subset);
|
||||
fprintf(f, " + RBG Shift:\t\t\t\t%s\n", dci->type1_alloc.shift ? "Yes" : "No");
|
||||
break;
|
||||
case SRSRAN_RA_ALLOC_TYPE2:
|
||||
fprintf(f, " + Type:\t\t\t\t%s\n", dci->type2_alloc.mode == SRSRAN_RA_TYPE2_LOC ? "Localized" : "Distributed");
|
||||
fprintf(f, " + Resource Indicator Value:\t\t%d\n", dci->type2_alloc.riv);
|
||||
break;
|
||||
}
|
||||
if (dci->cif_present) {
|
||||
fprintf(f, " - Carrier idx:\t\t\t\t%d\n", dci->cif);
|
||||
}
|
||||
fprintf(f, " - HARQ process:\t\t\t%d\n", dci->pid);
|
||||
fprintf(f, " - TPC command for PUCCH:\t\t--\n");
|
||||
fprintf(f, " - Transport blocks swapped:\t\t%s\n", (dci->tb_cw_swap) ? "true" : "false");
|
||||
if (dci->is_pdcch_order) {
|
||||
fprintf(f, "PDCCH order:\n");
|
||||
if (dci->cif_present) {
|
||||
fprintf(f, " - Carrier idx:\t\t\t\t%d\n", dci->cif);
|
||||
}
|
||||
fprintf(f, " - Preamble index:\t\t%d\n", dci->preamble_idx);
|
||||
fprintf(f, " - PRACH mask index:\t\t%d\n", dci->prach_mask_idx);
|
||||
} else {
|
||||
fprintf(f, " - Resource Allocation Type:\t\t%s\n", ra_type_string(dci->alloc_type));
|
||||
switch (dci->alloc_type) {
|
||||
case SRSRAN_RA_ALLOC_TYPE0:
|
||||
fprintf(f, " + Resource Block Group Size:\t\t%d\n", srsran_ra_type0_P(nof_prb));
|
||||
fprintf(f, " + RBG Bitmap:\t\t\t0x%x\n", dci->type0_alloc.rbg_bitmask);
|
||||
break;
|
||||
case SRSRAN_RA_ALLOC_TYPE1:
|
||||
fprintf(f, " + Resource Block Group Size:\t\t%d\n", srsran_ra_type0_P(nof_prb));
|
||||
fprintf(f, " + RBG Bitmap:\t\t\t0x%x\n", dci->type1_alloc.vrb_bitmask);
|
||||
fprintf(f, " + RBG Subset:\t\t\t%d\n", dci->type1_alloc.rbg_subset);
|
||||
fprintf(f, " + RBG Shift:\t\t\t\t%s\n", dci->type1_alloc.shift ? "Yes" : "No");
|
||||
break;
|
||||
case SRSRAN_RA_ALLOC_TYPE2:
|
||||
fprintf(
|
||||
f, " + Type:\t\t\t\t%s\n", dci->type2_alloc.mode == SRSRAN_RA_TYPE2_LOC ? "Localized" : "Distributed");
|
||||
fprintf(f, " + Resource Indicator Value:\t\t%d\n", dci->type2_alloc.riv);
|
||||
break;
|
||||
}
|
||||
if (dci->cif_present) {
|
||||
fprintf(f, " - Carrier idx:\t\t\t\t%d\n", dci->cif);
|
||||
}
|
||||
fprintf(f, " - HARQ process:\t\t\t%d\n", dci->pid);
|
||||
fprintf(f, " - TPC command for PUCCH:\t\t--\n");
|
||||
fprintf(f, " - Transport blocks swapped:\t\t%s\n", (dci->tb_cw_swap) ? "true" : "false");
|
||||
|
||||
for (uint32_t i = 0; i < SRSRAN_MAX_CODEWORDS; i++) {
|
||||
fprintf(f, " - Transport block %d enabled:\t\t%s\n", i, SRSRAN_DCI_IS_TB_EN(dci->tb[i]) ? "true" : "false");
|
||||
if (SRSRAN_DCI_IS_TB_EN(dci->tb[i])) {
|
||||
fprintf(f, " + Modulation and coding scheme index:\t%d\n", dci->tb[i].mcs_idx);
|
||||
fprintf(f, " + New data indicator:\t\t\t%s\n", dci->tb[i].ndi ? "Yes" : "No");
|
||||
fprintf(f, " + Redundancy version:\t\t\t%d\n", dci->tb[i].rv);
|
||||
for (uint32_t i = 0; i < SRSRAN_MAX_CODEWORDS; i++) {
|
||||
fprintf(f, " - Transport block %d enabled:\t\t%s\n", i, SRSRAN_DCI_IS_TB_EN(dci->tb[i]) ? "true" : "false");
|
||||
if (SRSRAN_DCI_IS_TB_EN(dci->tb[i])) {
|
||||
fprintf(f, " + Modulation and coding scheme index:\t%d\n", dci->tb[i].mcs_idx);
|
||||
fprintf(f, " + New data indicator:\t\t\t%s\n", dci->tb[i].ndi ? "Yes" : "No");
|
||||
fprintf(f, " + Redundancy version:\t\t\t%d\n", dci->tb[i].rv);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1627,46 +1652,51 @@ uint32_t srsran_dci_dl_info(const srsran_dci_dl_t* dci_dl, char* info_str, uint3
|
|||
n = srsran_print_check(info_str, len, n, ", cif=%d", dci_dl->cif);
|
||||
}
|
||||
|
||||
switch (dci_dl->alloc_type) {
|
||||
case SRSRAN_RA_ALLOC_TYPE0:
|
||||
n = srsran_print_check(info_str, len, n, ", rbg=0x%x", dci_dl->type0_alloc.rbg_bitmask);
|
||||
break;
|
||||
case SRSRAN_RA_ALLOC_TYPE1:
|
||||
n = srsran_print_check(info_str,
|
||||
len,
|
||||
n,
|
||||
", vrb=0x%x, rbg_s=%d, sh=%d",
|
||||
dci_dl->type1_alloc.vrb_bitmask,
|
||||
dci_dl->type1_alloc.rbg_subset,
|
||||
dci_dl->type1_alloc.shift);
|
||||
break;
|
||||
case SRSRAN_RA_ALLOC_TYPE2:
|
||||
n = srsran_print_check(info_str, len, n, ", riv=%d", dci_dl->type2_alloc.riv);
|
||||
break;
|
||||
}
|
||||
if (dci_dl->is_pdcch_order) {
|
||||
n = srsran_print_check(info_str, len, n, ", preamb_idx=%d", dci_dl->preamble_idx);
|
||||
n = srsran_print_check(info_str, len, n, ", prach_mask_idx=%d", dci_dl->prach_mask_idx);
|
||||
} else {
|
||||
switch (dci_dl->alloc_type) {
|
||||
case SRSRAN_RA_ALLOC_TYPE0:
|
||||
n = srsran_print_check(info_str, len, n, ", rbg=0x%x", dci_dl->type0_alloc.rbg_bitmask);
|
||||
break;
|
||||
case SRSRAN_RA_ALLOC_TYPE1:
|
||||
n = srsran_print_check(info_str,
|
||||
len,
|
||||
n,
|
||||
", vrb=0x%x, rbg_s=%d, sh=%d",
|
||||
dci_dl->type1_alloc.vrb_bitmask,
|
||||
dci_dl->type1_alloc.rbg_subset,
|
||||
dci_dl->type1_alloc.shift);
|
||||
break;
|
||||
case SRSRAN_RA_ALLOC_TYPE2:
|
||||
n = srsran_print_check(info_str, len, n, ", riv=%d", dci_dl->type2_alloc.riv);
|
||||
break;
|
||||
}
|
||||
|
||||
n = srsran_print_check(info_str, len, n, ", pid=%d", dci_dl->pid);
|
||||
n = srsran_print_check(info_str, len, n, ", pid=%d", dci_dl->pid);
|
||||
|
||||
n = srsran_print_check(info_str, len, n, ", mcs={");
|
||||
n = print_multi(info_str, n, len, dci_dl, 0);
|
||||
n = srsran_print_check(info_str, len, n, "}");
|
||||
n = srsran_print_check(info_str, len, n, ", ndi={");
|
||||
n = print_multi(info_str, n, len, dci_dl, 2);
|
||||
n = srsran_print_check(info_str, len, n, "}");
|
||||
n = srsran_print_check(info_str, len, n, ", mcs={");
|
||||
n = print_multi(info_str, n, len, dci_dl, 0);
|
||||
n = srsran_print_check(info_str, len, n, "}");
|
||||
n = srsran_print_check(info_str, len, n, ", ndi={");
|
||||
n = print_multi(info_str, n, len, dci_dl, 2);
|
||||
n = srsran_print_check(info_str, len, n, "}");
|
||||
|
||||
if (dci_dl->format == SRSRAN_DCI_FORMAT1 || dci_dl->format == SRSRAN_DCI_FORMAT1A ||
|
||||
dci_dl->format == SRSRAN_DCI_FORMAT1B || dci_dl->format == SRSRAN_DCI_FORMAT2 ||
|
||||
dci_dl->format == SRSRAN_DCI_FORMAT2A || dci_dl->format == SRSRAN_DCI_FORMAT2B) {
|
||||
n = srsran_print_check(info_str, len, n, ", tpc_pucch=%d", dci_dl->tpc_pucch);
|
||||
}
|
||||
if (dci_dl->format == SRSRAN_DCI_FORMAT1 || dci_dl->format == SRSRAN_DCI_FORMAT1A ||
|
||||
dci_dl->format == SRSRAN_DCI_FORMAT1B || dci_dl->format == SRSRAN_DCI_FORMAT2 ||
|
||||
dci_dl->format == SRSRAN_DCI_FORMAT2A || dci_dl->format == SRSRAN_DCI_FORMAT2B) {
|
||||
n = srsran_print_check(info_str, len, n, ", tpc_pucch=%d", dci_dl->tpc_pucch);
|
||||
}
|
||||
|
||||
if (dci_dl->is_tdd) {
|
||||
n = srsran_print_check(info_str, len, n, ", dai=%d", dci_dl->dai);
|
||||
}
|
||||
if (dci_dl->is_tdd) {
|
||||
n = srsran_print_check(info_str, len, n, ", dai=%d", dci_dl->dai);
|
||||
}
|
||||
|
||||
if (dci_dl->format == SRSRAN_DCI_FORMAT2 || dci_dl->format == SRSRAN_DCI_FORMAT2A ||
|
||||
dci_dl->format == SRSRAN_DCI_FORMAT2B) {
|
||||
n = srsran_print_check(info_str, len, n, ", tb_sw=%d, pinfo=%d", dci_dl->tb_cw_swap, dci_dl->pinfo);
|
||||
if (dci_dl->format == SRSRAN_DCI_FORMAT2 || dci_dl->format == SRSRAN_DCI_FORMAT2A ||
|
||||
dci_dl->format == SRSRAN_DCI_FORMAT2B) {
|
||||
n = srsran_print_check(info_str, len, n, ", tb_sw=%d, pinfo=%d", dci_dl->tb_cw_swap, dci_dl->pinfo);
|
||||
}
|
||||
}
|
||||
|
||||
#if SRSRAN_DCI_HEXDEBUG
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
set(CTEST_LABELS "lib;phy;phch")
|
||||
|
||||
########################################################################
|
||||
# PBCH TEST
|
||||
# PBCH TEST
|
||||
########################################################################
|
||||
|
||||
add_executable(pbch_test pbch_test.c)
|
||||
|
@ -235,6 +235,14 @@ foreach (nof_prb 6 15 25 50 75 100)
|
|||
endforeach ()
|
||||
endforeach ()
|
||||
|
||||
########################################################################
|
||||
# DCI TEST
|
||||
########################################################################
|
||||
|
||||
add_executable(dci_test dci_test.c)
|
||||
target_link_libraries(dci_test srsran_phy)
|
||||
add_test(dci_test dci_test)
|
||||
|
||||
########################################################################
|
||||
# PDSCH TEST
|
||||
########################################################################
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
/**
|
||||
*
|
||||
* \section COPYRIGHT
|
||||
*
|
||||
* Copyright 2013-2021 Software Radio Systems Limited
|
||||
*
|
||||
* By using this file, you agree to the terms and conditions set
|
||||
* forth in the LICENSE file which can be found at the top level of
|
||||
* the distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "srsran/phy/phch/dci.h"
|
||||
#include "srsran/phy/utils/debug.h"
|
||||
#include "srsran/phy/utils/random.h"
|
||||
#include "srsran/srsran.h"
|
||||
#include "srsran/support/srsran_test.h"
|
||||
#include <getopt.h>
|
||||
|
||||
#define UE_CRNTI 0x1234
|
||||
|
||||
static int test_pdcch_orders()
|
||||
{
|
||||
static srsran_cell_t cell = {
|
||||
52, // nof_prb
|
||||
1, // nof_ports
|
||||
0, // cell_id
|
||||
SRSRAN_CP_NORM, // cyclic prefix
|
||||
SRSRAN_PHICH_NORM, // PHICH length
|
||||
SRSRAN_PHICH_R_1, // PHICH resources
|
||||
SRSRAN_FDD,
|
||||
};
|
||||
|
||||
srsran_dl_sf_cfg_t dl_sf;
|
||||
ZERO_OBJECT(dl_sf);
|
||||
|
||||
srsran_dci_location_t locations[SRSRAN_NOF_SF_X_FRAME][30];
|
||||
static uint32_t cfi = 2;
|
||||
|
||||
static srsran_pdcch_t pdcch;
|
||||
static srsran_regs_t regs;
|
||||
|
||||
if (srsran_regs_init(®s, cell)) {
|
||||
ERROR("Error initiating regs");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (srsran_pdcch_init_enb(&pdcch, cell.nof_prb)) {
|
||||
ERROR("Error creating PDCCH object");
|
||||
exit(-1);
|
||||
}
|
||||
if (srsran_pdcch_set_cell(&pdcch, ®s, cell)) {
|
||||
ERROR("Error creating PDCCH object");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/* Initiate valid DCI locations */
|
||||
for (int i = 0; i < SRSRAN_NOF_SF_X_FRAME; i++) {
|
||||
dl_sf.cfi = cfi;
|
||||
dl_sf.tti = i;
|
||||
srsran_pdcch_ue_locations(&pdcch, &dl_sf, locations[i], 30, UE_CRNTI);
|
||||
}
|
||||
|
||||
srsran_dci_dl_t dci_tx;
|
||||
bzero(&dci_tx, sizeof(srsran_dci_dl_t));
|
||||
dci_tx.rnti = UE_CRNTI;
|
||||
dci_tx.location = locations[1][0];
|
||||
dci_tx.format = SRSRAN_DCI_FORMAT1A;
|
||||
dci_tx.cif_present = false;
|
||||
dci_tx.is_pdcch_order = true;
|
||||
dci_tx.preamble_idx = 0;
|
||||
dci_tx.prach_mask_idx = 0;
|
||||
|
||||
srsran_dci_cfg_t cfg = {};
|
||||
cfg.cif_enabled = false;
|
||||
cfg.srs_request_enabled = false;
|
||||
|
||||
// Pack
|
||||
srsran_dci_msg_t dci_msg = {};
|
||||
TESTASSERT(srsran_dci_msg_pack_pdsch(&cell, &dl_sf, &cfg, &dci_tx, &dci_msg) == SRSRAN_SUCCESS);
|
||||
|
||||
// Unpack
|
||||
srsran_dci_dl_t dci_rx = {};
|
||||
TESTASSERT(srsran_dci_msg_unpack_pdsch(&cell, &dl_sf, &cfg, &dci_msg, &dci_rx) == SRSRAN_SUCCESS);
|
||||
|
||||
// To string
|
||||
char str[128];
|
||||
srsran_dci_dl_info(&dci_tx, str, sizeof(str));
|
||||
printf("Tx: %s\n", str);
|
||||
srsran_dci_dl_info(&dci_rx, str, sizeof(str));
|
||||
printf("Rx: %s\n", str);
|
||||
|
||||
// Assert
|
||||
TESTASSERT(memcmp(&dci_tx, &dci_rx, srsran_dci_format_sizeof(&cell, &dl_sf, &cfg, dci_tx.format)) == 0);
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (test_pdcch_orders() != SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
printf("Success!\n");
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
#include "srsran/rlc/rlc.h"
|
||||
#include "srsran/common/rwlock_guard.h"
|
||||
#include "srsran/rlc/rlc_am_lte.h"
|
||||
#include "srsran/rlc/rlc_am_base.h"
|
||||
#include "srsran/rlc/rlc_tm.h"
|
||||
#include "srsran/rlc/rlc_um_lte.h"
|
||||
#include "srsran/rlc/rlc_um_nr.h"
|
||||
|
@ -407,6 +407,9 @@ int rlc::add_bearer(uint32_t lcid, const rlc_config_t& cnfg)
|
|||
case srsran_rat_t::lte:
|
||||
rlc_entity = std::unique_ptr<rlc_common>(new rlc_am(cnfg.rat, logger, lcid, pdcp, rrc, timers));
|
||||
break;
|
||||
case srsran_rat_t::nr:
|
||||
rlc_entity = std::unique_ptr<rlc_common>(new rlc_am(cnfg.rat, logger, lcid, pdcp, rrc, timers));
|
||||
break;
|
||||
default:
|
||||
logger.error("AM not supported for this RAT");
|
||||
return SRSRAN_ERROR;
|
||||
|
|
|
@ -116,9 +116,11 @@ void rlc_am::reestablish()
|
|||
***************************************************************************/
|
||||
void rlc_am::write_sdu(unique_byte_buffer_t sdu)
|
||||
{
|
||||
uint32_t nof_bytes = sdu->N_bytes;
|
||||
if (tx_base->write_sdu(std::move(sdu)) == SRSRAN_SUCCESS) {
|
||||
std::lock_guard<std::mutex> lock(metrics_mutex);
|
||||
metrics.num_tx_sdus++;
|
||||
metrics.num_tx_sdu_bytes += nof_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -216,7 +218,7 @@ int rlc_am::rlc_am_base_tx::write_sdu(unique_byte_buffer_t sdu)
|
|||
}
|
||||
|
||||
if (sdu.get() == nullptr) {
|
||||
srslog::fetch_basic_logger("RLC").warning("NULL SDU pointer in write_sdu()");
|
||||
logger->warning("NULL SDU pointer in write_sdu()");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
|
@ -228,16 +230,15 @@ int rlc_am::rlc_am_base_tx::write_sdu(unique_byte_buffer_t sdu)
|
|||
uint32_t nof_bytes = sdu->N_bytes;
|
||||
srsran::error_type<unique_byte_buffer_t> ret = tx_sdu_queue.try_write(std::move(sdu));
|
||||
if (ret) {
|
||||
srslog::fetch_basic_logger("RLC").info(
|
||||
msg_ptr, nof_bytes, "%s Tx SDU (%d B, tx_sdu_queue_len=%d)", rb_name, nof_bytes, tx_sdu_queue.size());
|
||||
logger->info(msg_ptr, nof_bytes, "%s Tx SDU (%d B, tx_sdu_queue_len=%d)", rb_name, nof_bytes, tx_sdu_queue.size());
|
||||
} else {
|
||||
// in case of fail, the try_write returns back the sdu
|
||||
srslog::fetch_basic_logger("RLC").warning(ret.error()->msg,
|
||||
ret.error()->N_bytes,
|
||||
"[Dropped SDU] %s Tx SDU (%d B, tx_sdu_queue_len=%d)",
|
||||
rb_name,
|
||||
ret.error()->N_bytes,
|
||||
tx_sdu_queue.size());
|
||||
logger->warning(ret.error()->msg,
|
||||
ret.error()->N_bytes,
|
||||
"[Dropped SDU] %s Tx SDU (%d B, tx_sdu_queue_len=%d)",
|
||||
rb_name,
|
||||
ret.error()->N_bytes,
|
||||
tx_sdu_queue.size());
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
|
@ -256,6 +257,7 @@ void rlc_am::rlc_am_base_tx::set_bsr_callback(bsr_callback_t callback)
|
|||
*******************************************************/
|
||||
void rlc_am::rlc_am_base_rx::write_pdu(uint8_t* payload, const uint32_t nof_bytes)
|
||||
{
|
||||
logger->info("Rx PDU -- N bytes %d", nof_bytes);
|
||||
if (nof_bytes < 1) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -20,12 +20,19 @@
|
|||
*/
|
||||
|
||||
#include "srsran/rlc/rlc_am_nr.h"
|
||||
#include "srsran/common/standard_streams.h"
|
||||
#include "srsran/common/string_helpers.h"
|
||||
#include "srsran/interfaces/ue_pdcp_interfaces.h"
|
||||
#include "srsran/interfaces/ue_rrc_interfaces.h"
|
||||
#include "srsran/rlc/rlc_am_nr_packing.h"
|
||||
#include "srsran/srslog/event_trace.h"
|
||||
#include <iostream>
|
||||
|
||||
#define RLC_AM_NR_WINDOW_SIZE 2048
|
||||
#define MOD_NR 4096
|
||||
#define RX_MOD_BASE_NR(x) (((x)-rx_next) % MOD_NR)
|
||||
//#define TX_MOD_BASE_NR(x) (((x)-vt_a) % MOD_NR)
|
||||
|
||||
namespace srsran {
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -35,14 +42,17 @@ namespace srsran {
|
|||
/***************************************************************************
|
||||
* Tx subclass implementation
|
||||
***************************************************************************/
|
||||
rlc_am_nr_tx::rlc_am_nr_tx(rlc_am* parent_) : parent(parent_), rlc_am_base_tx(&parent_->logger)
|
||||
{
|
||||
parent->logger.debug("Initializing RLC AM NR TX: Tx_Next: %d",
|
||||
st.tx_next); // Temporarly silence unused variable warning
|
||||
}
|
||||
rlc_am_nr_tx::rlc_am_nr_tx(rlc_am* parent_) : parent(parent_), rlc_am_base_tx(&parent_->logger) {}
|
||||
|
||||
bool rlc_am_nr_tx::configure(const rlc_config_t& cfg_)
|
||||
{
|
||||
cfg = cfg_.am_nr;
|
||||
|
||||
if (cfg.tx_sn_field_length != rlc_am_nr_sn_size_t::size12bits) {
|
||||
logger->warning("RLC AM NR only supporst 12 bit SN length.");
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
if (cfg_.tx_queue_length > MAX_SDUS_PER_RLC_PDU) {
|
||||
logger.error("Configuring Tx queue length of %d PDUs too big. Maximum value is %d.",
|
||||
|
@ -51,33 +61,333 @@ bool rlc_am_nr_tx::configure(const rlc_config_t& cfg_)
|
|||
return false;
|
||||
}
|
||||
*/
|
||||
cfg = cfg_.am;
|
||||
tx_enabled = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rlc_am_nr_tx::has_data()
|
||||
{
|
||||
return true;
|
||||
return do_status() || // if we have a status PDU to transmit
|
||||
tx_sdu_queue.get_n_sdus() != 1; // or if there is a SDU queued up for transmission
|
||||
}
|
||||
|
||||
uint32_t rlc_am_nr_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes)
|
||||
{
|
||||
return 0;
|
||||
logger->debug("MAC opportunity - %d bytes", nof_bytes);
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
|
||||
if (not tx_enabled) {
|
||||
logger->debug("RLC entity not active. Not generating PDU.");
|
||||
return 0;
|
||||
}
|
||||
// logger.debug("tx_window size - %zu PDUs", tx_window.size());
|
||||
|
||||
// Tx STATUS if requested
|
||||
if (do_status()) {
|
||||
unique_byte_buffer_t tx_pdu = srsran::make_byte_buffer();
|
||||
if (tx_pdu == nullptr) {
|
||||
logger->error("Couldn't allocate PDU in %s().", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
build_status_pdu(tx_pdu.get(), nof_bytes);
|
||||
memcpy(payload, tx_pdu->msg, tx_pdu->N_bytes);
|
||||
logger->debug("Status PDU built - %d bytes", tx_pdu->N_bytes);
|
||||
return tx_pdu->N_bytes;
|
||||
}
|
||||
|
||||
// Section 5.2.2.3 in TS 36.311, if tx_window is full and retx_queue empty, retransmit PDU
|
||||
// TODO
|
||||
|
||||
// RETX if required
|
||||
if (not retx_queue.empty()) {
|
||||
logger->info("Retx required. Retx queue size: %d", retx_queue.size());
|
||||
unique_byte_buffer_t tx_pdu = srsran::make_byte_buffer();
|
||||
if (tx_pdu == nullptr) {
|
||||
logger->error("Couldn't allocate PDU in %s().", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
int retx_err = build_retx_pdu(tx_pdu, nof_bytes);
|
||||
if (retx_err >= 0 && tx_pdu->N_bytes <= nof_bytes) {
|
||||
memcpy(payload, tx_pdu->msg, tx_pdu->N_bytes);
|
||||
return tx_pdu->N_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
// Read new SDU from TX queue
|
||||
if (tx_sdu_queue.is_empty()) {
|
||||
logger->info("No data available to be sent");
|
||||
return 0;
|
||||
}
|
||||
|
||||
unique_byte_buffer_t tx_sdu;
|
||||
logger->debug("Reading from RLC SDU queue. Queue size %d", tx_sdu_queue.size());
|
||||
do {
|
||||
tx_sdu = tx_sdu_queue.read();
|
||||
} while (tx_sdu == nullptr && tx_sdu_queue.size() != 0);
|
||||
|
||||
if (tx_sdu != nullptr) {
|
||||
logger->debug("Read RLC SDU - %d bytes", tx_sdu->N_bytes);
|
||||
}
|
||||
|
||||
uint16_t hdr_size = 2;
|
||||
if (tx_sdu->N_bytes + hdr_size > nof_bytes) {
|
||||
logger->warning("Segmentation not supported yet");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// insert newly assigned SN into window and use reference for in-place operations
|
||||
// NOTE: from now on, we can't return from this function anymore before increasing tx_next
|
||||
rlc_amd_tx_pdu_nr& tx_pdu = tx_window.add_pdu(st.tx_next);
|
||||
tx_pdu.buf = srsran::make_byte_buffer();
|
||||
if (tx_pdu.buf == nullptr) {
|
||||
logger->error("Couldn't allocate PDU in %s().", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(tx_pdu.buf->msg, tx_sdu->msg, tx_sdu->N_bytes);
|
||||
tx_pdu.buf->N_bytes = tx_sdu->N_bytes;
|
||||
|
||||
// Prepare header
|
||||
rlc_am_nr_pdu_header_t hdr = {};
|
||||
hdr.dc = RLC_DC_FIELD_DATA_PDU;
|
||||
hdr.p = get_pdu_poll();
|
||||
hdr.si = rlc_nr_si_field_t::full_sdu;
|
||||
hdr.sn_size = rlc_am_nr_sn_size_t::size12bits;
|
||||
hdr.sn = st.tx_next;
|
||||
tx_pdu.header = hdr;
|
||||
log_rlc_am_nr_pdu_header_to_string(logger->info, hdr);
|
||||
|
||||
// Write header
|
||||
uint32_t len = rlc_am_nr_write_data_pdu_header(hdr, tx_sdu.get());
|
||||
if (len > nof_bytes) {
|
||||
logger->error("Error writing AMD PDU header");
|
||||
}
|
||||
|
||||
// Update TX Next
|
||||
st.tx_next = (st.tx_next + 1) % MOD;
|
||||
|
||||
memcpy(payload, tx_sdu->msg, tx_sdu->N_bytes);
|
||||
logger->debug("Wrote RLC PDU - %d bytes", tx_sdu->N_bytes);
|
||||
|
||||
return tx_sdu->N_bytes;
|
||||
}
|
||||
|
||||
void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) {}
|
||||
int rlc_am_nr_tx::build_retx_pdu(unique_byte_buffer_t& tx_pdu, uint32_t nof_bytes)
|
||||
{
|
||||
// Check there is at least 1 element before calling front()
|
||||
if (retx_queue.empty()) {
|
||||
logger->error("In build_retx_pdu(): retx_queue is empty");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
rlc_amd_retx_t retx = retx_queue.front();
|
||||
|
||||
// Sanity check - drop any retx SNs not present in tx_window
|
||||
while (not tx_window.has_sn(retx.sn)) {
|
||||
logger->warning("%s SN=%d not in Tx window. Ignoring retx.", parent->rb_name, retx.sn);
|
||||
retx_queue.pop();
|
||||
if (!retx_queue.empty()) {
|
||||
retx = retx_queue.front();
|
||||
} else {
|
||||
logger->warning("%s empty retx queue, cannot provide retx PDU", parent->rb_name);
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
}
|
||||
// Update & write header
|
||||
rlc_am_nr_pdu_header_t new_header = tx_window[retx.sn].header;
|
||||
new_header.p = 0;
|
||||
uint32_t hdr_len = rlc_am_nr_write_data_pdu_header(new_header, tx_pdu.get());
|
||||
|
||||
// Check if we exceed allocated number of bytes
|
||||
if (hdr_len + tx_window[retx.sn].buf->N_bytes > nof_bytes) {
|
||||
logger->warning("%s segmentation not supported yet. Cannot provide retx PDU", parent->rb_name);
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
// TODO Consider re-segmentation
|
||||
|
||||
memcpy(&tx_pdu->msg[hdr_len], tx_window[retx.sn].buf->msg, tx_window[retx.sn].buf->N_bytes);
|
||||
tx_pdu->N_bytes += tx_window[retx.sn].buf->N_bytes;
|
||||
|
||||
retx_queue.pop();
|
||||
|
||||
logger->info(tx_window[retx.sn].buf->msg,
|
||||
tx_window[retx.sn].buf->N_bytes,
|
||||
"%s Original SDU SN=%d (%d B) (attempt %d/%d)",
|
||||
parent->rb_name,
|
||||
retx.sn,
|
||||
tx_window[retx.sn].buf->N_bytes,
|
||||
tx_window[retx.sn].retx_count + 1,
|
||||
cfg.max_retx_thresh);
|
||||
logger->info(tx_pdu->msg, tx_pdu->N_bytes, "%s ReTx PDU SN=%d (%d B)", parent->rb_name, retx.sn, tx_pdu->N_bytes);
|
||||
log_rlc_am_nr_pdu_header_to_string(logger->debug, new_header);
|
||||
|
||||
// debug_state();
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
uint32_t rlc_am_nr_tx::build_status_pdu(byte_buffer_t* payload, uint32_t nof_bytes)
|
||||
{
|
||||
logger->info("Generating Status PDU. Bytes available:%d", nof_bytes);
|
||||
rlc_am_nr_status_pdu_t tx_status;
|
||||
int pdu_len = rx->get_status_pdu(&tx_status, nof_bytes);
|
||||
if (pdu_len == SRSRAN_ERROR) {
|
||||
logger->debug("%s Deferred Status PDU. Cause: Failed to acquire Rx lock", rb_name);
|
||||
pdu_len = 0;
|
||||
} else if (pdu_len > 0 && nof_bytes >= static_cast<uint32_t>(pdu_len)) {
|
||||
logger->debug("Generated Status PDU. Bytes:%d", pdu_len);
|
||||
log_rlc_am_nr_status_pdu_to_string(logger->info, "%s Tx status PDU - %s", &tx_status, rb_name);
|
||||
pdu_len = rlc_am_nr_write_status_pdu(tx_status, rlc_am_nr_sn_size_t::size12bits, payload);
|
||||
} else {
|
||||
logger->info("%s Cannot tx status PDU - %d bytes available, %d bytes required", rb_name, nof_bytes, pdu_len);
|
||||
pdu_len = 0;
|
||||
}
|
||||
|
||||
return payload->N_bytes;
|
||||
}
|
||||
|
||||
void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes)
|
||||
{
|
||||
if (not tx_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
rlc_am_nr_status_pdu_t status = {};
|
||||
logger->debug(payload, nof_bytes, "%s Rx control PDU", parent->rb_name);
|
||||
rlc_am_nr_read_status_pdu(payload, nof_bytes, rlc_am_nr_sn_size_t::size12bits, &status);
|
||||
log_rlc_am_nr_status_pdu_to_string(logger->info, "%s Rx Status PDU: %s", &status, parent->rb_name);
|
||||
// Local variables for handling Status PDU will be updated with lock
|
||||
/*
|
||||
* - if the SN of the corresponding RLC SDU falls within the range
|
||||
* TX_Next_Ack <= SN < = the highest SN of the AMD PDU among the AMD PDUs submitted to lower layer:
|
||||
* - consider the RLC SDU or the RLC SDU segment for which a negative acknowledgement was received for
|
||||
* retransmission.
|
||||
*/
|
||||
// Process ACKs
|
||||
uint32_t stop_sn = status.N_nack == 0
|
||||
? status.ack_sn
|
||||
: status.nacks[0].nack_sn - 1; // Stop processing ACKs at the first NACK, if it exists.
|
||||
if (stop_sn > st.tx_next) {
|
||||
logger->error("Rx'ed ACK or NACK larger than TX_NEXT. Ignoring status report");
|
||||
return;
|
||||
}
|
||||
for (uint32_t sn = st.tx_next_ack; sn < stop_sn; sn++) {
|
||||
if (tx_window.has_sn(sn)) {
|
||||
tx_window.remove_pdu(sn);
|
||||
st.tx_next_ack = sn + 1;
|
||||
// TODO notify PDCP
|
||||
} else {
|
||||
logger->error("Missing ACKed SN from TX window");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Process N_acks
|
||||
for (uint32_t nack_idx = 0; nack_idx < status.N_nack; nack_idx++) {
|
||||
if (st.tx_next_ack <= status.nacks[nack_idx].nack_sn && status.nacks[nack_idx].nack_sn <= st.tx_next) {
|
||||
uint32_t nack_sn = status.nacks[nack_idx].nack_sn;
|
||||
if (tx_window.has_sn(nack_sn)) {
|
||||
auto& pdu = tx_window[nack_sn];
|
||||
|
||||
// add to retx queue if it's not already there
|
||||
if (not retx_queue.has_sn(nack_sn)) {
|
||||
// increment Retx counter and inform upper layers if needed
|
||||
pdu.retx_count++;
|
||||
// check_sn_reached_max_retx(nack_sn);
|
||||
|
||||
rlc_amd_retx_t& retx = retx_queue.push();
|
||||
srsran_expect(tx_window[nack_sn].rlc_sn == nack_sn,
|
||||
"Incorrect RLC SN=%d!=%d being accessed",
|
||||
tx_window[nack_sn].rlc_sn,
|
||||
nack_sn);
|
||||
retx.sn = nack_sn;
|
||||
retx.is_segment = false;
|
||||
retx.so_start = 0;
|
||||
retx.so_end = pdu.buf->N_bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Section 5.3.3.3: Reception of a STATUS report
|
||||
* - if the STATUS report comprises a positive or negative acknowledgement for the RLC SDU with sequence
|
||||
* number equal to POLL_SN:
|
||||
* - if t-PollRetransmit is running:
|
||||
* - stop and reset t-PollRetransmit.
|
||||
*/
|
||||
}
|
||||
|
||||
uint32_t rlc_am_nr_tx::get_buffer_state()
|
||||
{
|
||||
return 0;
|
||||
uint32_t tx_queue = 0;
|
||||
uint32_t prio_tx_queue = 0;
|
||||
get_buffer_state(tx_queue, prio_tx_queue);
|
||||
return tx_queue + prio_tx_queue;
|
||||
}
|
||||
|
||||
void rlc_am_nr_tx::get_buffer_state(uint32_t& tx_queue, uint32_t& prio_tx_queue) {}
|
||||
|
||||
int rlc_am_nr_tx::write_sdu(unique_byte_buffer_t sdu)
|
||||
void rlc_am_nr_tx::get_buffer_state(uint32_t& n_bytes_new, uint32_t& n_bytes_prio)
|
||||
{
|
||||
return 0;
|
||||
logger->debug("Buffer state requested, %s", rb_name);
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
|
||||
logger->debug("%s Buffer state - do_status=%s", rb_name, do_status() ? "yes" : "no");
|
||||
|
||||
// Bytes needed for status report
|
||||
if (do_status()) {
|
||||
n_bytes_prio += rx->get_status_pdu_length();
|
||||
logger->debug("%s Buffer state - total status report: %d bytes", rb_name, n_bytes_prio);
|
||||
}
|
||||
|
||||
// Bytes needed for retx
|
||||
if (not retx_queue.empty()) {
|
||||
rlc_amd_retx_t& retx = retx_queue.front();
|
||||
logger->debug("%s Buffer state - retx - SN=%d, Segment: %s, %d:%d",
|
||||
parent->rb_name,
|
||||
retx.sn,
|
||||
retx.is_segment ? "true" : "false",
|
||||
retx.so_start,
|
||||
retx.so_end);
|
||||
if (tx_window.has_sn(retx.sn)) {
|
||||
int req_bytes = retx.so_end - retx.so_start;
|
||||
int hdr_req_bytes = retx.is_segment ? 4 : 2; // Segmentation not supported yet
|
||||
if (req_bytes <= 0) {
|
||||
logger->error("In get_buffer_state(): Removing retx.sn=%d from queue", retx.sn);
|
||||
retx_queue.pop();
|
||||
} else {
|
||||
n_bytes_prio += (req_bytes + hdr_req_bytes);
|
||||
logger->debug("Buffer state - retx: %d bytes", n_bytes_prio);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bytes needed for tx SDUs
|
||||
uint32_t n_sdus = tx_sdu_queue.get_n_sdus();
|
||||
n_bytes_new += tx_sdu_queue.size_bytes();
|
||||
|
||||
// Room needed for fixed header of data PDUs
|
||||
n_bytes_new += 2 * n_sdus; // TODO make header size configurable
|
||||
logger->debug("%s Total buffer state - %d SDUs (%d B)", rb_name, n_sdus, n_bytes_new + n_bytes_prio);
|
||||
|
||||
if (bsr_callback) {
|
||||
logger->debug("%s Calling BSR callback - %d new_tx, %d prio bytes", parent->rb_name, n_bytes_new, n_bytes_prio);
|
||||
bsr_callback(parent->lcid, n_bytes_new, n_bytes_prio);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t rlc_am_nr_tx::get_pdu_poll()
|
||||
{
|
||||
uint8_t poll = 0;
|
||||
if (cfg.poll_pdu > 0) {
|
||||
if (st.pdu_without_poll >= (uint32_t)cfg.poll_pdu) {
|
||||
poll = 1;
|
||||
st.pdu_without_poll = 0;
|
||||
} else {
|
||||
st.pdu_without_poll++;
|
||||
}
|
||||
}
|
||||
return poll;
|
||||
}
|
||||
|
||||
void rlc_am_nr_tx::reestablish()
|
||||
|
@ -94,26 +404,43 @@ bool rlc_am_nr_tx::sdu_queue_is_full()
|
|||
|
||||
void rlc_am_nr_tx::empty_queue() {}
|
||||
|
||||
bool rlc_am_nr_tx::do_status()
|
||||
{
|
||||
return rx->get_do_status();
|
||||
}
|
||||
|
||||
void rlc_am_nr_tx::stop() {}
|
||||
|
||||
/****************************************************************************
|
||||
* Rx subclass implementation
|
||||
***************************************************************************/
|
||||
rlc_am_nr_rx::rlc_am_nr_rx(rlc_am* parent_) :
|
||||
parent(parent_), pool(byte_buffer_pool::get_instance()), rlc_am_base_rx(parent_, &parent_->logger)
|
||||
{
|
||||
parent->logger.debug("Initializing RLC AM NR RX"); // Temporarly silence unused variable warning
|
||||
}
|
||||
parent(parent_),
|
||||
pool(byte_buffer_pool::get_instance()),
|
||||
status_prohibit_timer(parent->timers->get_unique_timer()),
|
||||
reassembly_timer(parent->timers->get_unique_timer()),
|
||||
rlc_am_base_rx(parent_, &parent_->logger)
|
||||
{}
|
||||
|
||||
bool rlc_am_nr_rx::configure(const rlc_config_t& cfg_)
|
||||
{
|
||||
cfg = cfg_.am;
|
||||
cfg = cfg_.am_nr;
|
||||
|
||||
// Configure status prohibit timer
|
||||
if (cfg.t_status_prohibit > 0) {
|
||||
status_prohibit_timer.set(static_cast<uint32_t>(cfg.t_status_prohibit),
|
||||
[this](uint32_t timerid) { timer_expired(timerid); });
|
||||
}
|
||||
|
||||
// Configure t_reassembly timer
|
||||
if (cfg.t_reassembly > 0) {
|
||||
reassembly_timer.set(static_cast<uint32_t>(cfg.t_reassembly), [this](uint32_t timerid) { timer_expired(timerid); });
|
||||
logger->info("Configured reassembly timer. t-Reassembly=%d ms", cfg.t_reassembly);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) {}
|
||||
|
||||
void rlc_am_nr_rx::stop() {}
|
||||
|
||||
void rlc_am_nr_rx::reestablish()
|
||||
|
@ -121,6 +448,277 @@ void rlc_am_nr_rx::reestablish()
|
|||
stop();
|
||||
}
|
||||
|
||||
void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes)
|
||||
{
|
||||
// Get AMD PDU Header
|
||||
rlc_am_nr_pdu_header_t header = {};
|
||||
uint32_t hdr_len = rlc_am_nr_read_data_pdu_header(payload, nof_bytes, rlc_am_nr_sn_size_t::size12bits, &header);
|
||||
|
||||
logger->info(payload, nof_bytes, "%s Rx data PDU SN=%d (%d B)", parent->rb_name, header.sn, nof_bytes);
|
||||
log_rlc_am_nr_pdu_header_to_string(logger->debug, header);
|
||||
|
||||
// Check wether SDU is within Rx Window
|
||||
if (!inside_rx_window(header.sn)) {
|
||||
logger->info("%s SN=%d outside rx window [%d:%d] - discarding",
|
||||
parent->rb_name,
|
||||
header.sn,
|
||||
rx_next,
|
||||
rx_next + RLC_AM_NR_WINDOW_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Section 5.2.3.2.2, discard duplicate PDUs
|
||||
if (rx_window.has_sn(header.sn)) {
|
||||
logger->info("%s Discarding duplicate SN=%d", parent->rb_name, header.sn);
|
||||
return;
|
||||
}
|
||||
|
||||
// Write to rx window
|
||||
if (header.si == rlc_nr_si_field_t::full_sdu) {
|
||||
// Full SDU received. Add SDU to Rx Window and copy full PDU into SDU buffer.
|
||||
rlc_amd_rx_sdu_nr_t& rx_sdu = rx_window.add_pdu(header.sn);
|
||||
rx_sdu.buf = srsran::make_byte_buffer();
|
||||
if (rx_sdu.buf == nullptr) {
|
||||
logger->error("Fatal Error: Couldn't allocate PDU in %s.", __FUNCTION__);
|
||||
rx_window.remove_pdu(header.sn);
|
||||
return;
|
||||
}
|
||||
rx_sdu.buf->set_timestamp();
|
||||
|
||||
// check available space for payload
|
||||
if (nof_bytes > rx_sdu.buf->get_tailroom()) {
|
||||
logger->error("%s Discarding SN=%d of size %d B (available space %d B)",
|
||||
parent->rb_name,
|
||||
header.sn,
|
||||
nof_bytes,
|
||||
rx_sdu.buf->get_tailroom());
|
||||
return;
|
||||
}
|
||||
memcpy(rx_sdu.buf->msg, payload + hdr_len, nof_bytes - hdr_len); // Don't copy header
|
||||
rx_sdu.buf->N_bytes = nof_bytes - hdr_len;
|
||||
rx_sdu.fully_received = true;
|
||||
write_to_upper_layers(parent->lcid, std::move(rx_window[header.sn].buf));
|
||||
} else {
|
||||
// Check if all bytes of the RLC SDU with SN = x are received:
|
||||
// TODO
|
||||
if (header.si == rlc_nr_si_field_t::first_segment) { // Check whether it's a full SDU
|
||||
} else if (header.si == rlc_nr_si_field_t::last_segment) {
|
||||
} else if (header.si == rlc_nr_si_field_t::neither_first_nor_last_segment) {
|
||||
}
|
||||
}
|
||||
|
||||
// Check poll bit
|
||||
if (header.p) {
|
||||
logger->info("%s Status packet requested through polling bit", parent->rb_name);
|
||||
do_status = true;
|
||||
}
|
||||
|
||||
debug_state();
|
||||
|
||||
// 5.2.3.2.3 Actions when an AMD PDU is placed in the reception buffer
|
||||
// Update Rx_Next_Highest
|
||||
if (RX_MOD_BASE_NR(header.sn) >= RX_MOD_BASE_NR(rx_next_highest)) {
|
||||
rx_next_highest = (header.sn + 1) % MOD;
|
||||
}
|
||||
|
||||
// Update RX_Highest_Status
|
||||
/*
|
||||
* - if x = RX_Highest_Status,
|
||||
* - update RX_Highest_Status to the SN of the first RLC SDU with SN > current RX_Highest_Status for which not
|
||||
* all bytes have been received.
|
||||
*/
|
||||
if (RX_MOD_BASE_NR(header.sn) == RX_MOD_BASE_NR(rx_highest_status)) {
|
||||
uint32_t sn_upd = 0;
|
||||
uint32_t window_top = rx_next + RLC_AM_WINDOW_SIZE;
|
||||
for (sn_upd = rx_highest_status; sn_upd < window_top; ++sn_upd) {
|
||||
if (rx_window.has_sn(sn_upd)) {
|
||||
if (not rx_window[sn_upd].fully_received) {
|
||||
break; // first SDU not fully received
|
||||
}
|
||||
} else {
|
||||
break; // first SDU not fully received
|
||||
}
|
||||
}
|
||||
// Update to the SN of the first SDU with missing bytes.
|
||||
// If it not exists, update to the end of the rx_window.
|
||||
rx_highest_status = sn_upd;
|
||||
}
|
||||
|
||||
/*
|
||||
* - if x = RX_Next:
|
||||
* - update RX_Next to the SN of the first RLC SDU with SN > current RX_Next for which not all bytes
|
||||
* have been received.
|
||||
*/
|
||||
if (RX_MOD_BASE_NR(header.sn) == RX_MOD_BASE_NR(rx_next)) {
|
||||
uint32_t sn_upd = 0;
|
||||
uint32_t window_top = rx_next + RLC_AM_WINDOW_SIZE;
|
||||
for (sn_upd = rx_next; sn_upd < window_top; ++sn_upd) {
|
||||
if (rx_window.has_sn(sn_upd)) {
|
||||
if (not rx_window[sn_upd].fully_received) {
|
||||
break; // first SDU not fully received
|
||||
}
|
||||
// RX_Next serves as the lower edge of the receiving window
|
||||
// As such, we remove any SDU from the window if we update this value
|
||||
rx_window.remove_pdu(sn_upd);
|
||||
} else {
|
||||
break; // first SDU not fully received
|
||||
}
|
||||
}
|
||||
// Update to the SN of the first SDU with missing bytes.
|
||||
// If it not exists, update to the end of the rx_window.
|
||||
rx_next = sn_upd;
|
||||
}
|
||||
|
||||
if (reassembly_timer.is_running()) {
|
||||
// if t-Reassembly is running:
|
||||
/*
|
||||
* - if RX_Next_Status_Trigger = RX_Next; or
|
||||
* - if RX_Next_Status_Trigger = RX_Next + 1 and there is no missing byte segment of the SDU associated with
|
||||
* SN = RX_Next before the last byte of all received segments of this SDU; or
|
||||
* - if RX_Next_Status_Trigger falls outside of the receiving window and RX_Next_Status_Trigger is not equal
|
||||
* to RX_Next + AM_Window_Size:
|
||||
* - stop and reset t-Reassembly.
|
||||
*/
|
||||
} else {
|
||||
/*
|
||||
* - if RX_Next_Highest> RX_Next +1; or
|
||||
* - if RX_Next_Highest = RX_Next + 1 and there is at least one missing byte segment of the SDU associated
|
||||
* with SN = RX_Next before the last byte of all received segments of this SDU:
|
||||
* - start t-Reassembly;
|
||||
* - set RX_Next_Status_Trigger to RX_Next_Highest.
|
||||
*/
|
||||
bool restart_reassembly_timer = false;
|
||||
if (rx_next_highest > rx_next + 1) {
|
||||
restart_reassembly_timer = true;
|
||||
}
|
||||
if (rx_next_highest == rx_next + 1 &&
|
||||
rx_window[rx_next + 1].fully_received == false) { // TODO: does the last by need to be received?
|
||||
restart_reassembly_timer = true;
|
||||
}
|
||||
if (restart_reassembly_timer) {
|
||||
reassembly_timer.run();
|
||||
rx_next_status_trigger = rx_next_highest;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool rlc_am_nr_rx::inside_rx_window(uint32_t sn)
|
||||
{
|
||||
return (RX_MOD_BASE_NR(sn) >= RX_MOD_BASE_NR(rx_next)) &&
|
||||
(RX_MOD_BASE_NR(sn) < RX_MOD_BASE_NR(rx_next + RLC_AM_NR_WINDOW_SIZE));
|
||||
}
|
||||
|
||||
/*
|
||||
* Status PDU
|
||||
*/
|
||||
uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t max_len)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex, std::try_to_lock);
|
||||
if (not lock.owns_lock()) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
status->N_nack = 0;
|
||||
status->ack_sn = rx_next; // Start with the lower end of the window
|
||||
byte_buffer_t tmp_buf;
|
||||
uint32_t len;
|
||||
|
||||
uint32_t i = status->ack_sn;
|
||||
while (RX_MOD_BASE_NR(i) <= RX_MOD_BASE_NR(rx_highest_status)) {
|
||||
if (rx_window.has_sn(i) || i == rx_highest_status) {
|
||||
// only update ACK_SN if this SN has been received, or if we reached the maximum possible SN
|
||||
status->ack_sn = i;
|
||||
} else {
|
||||
status->nacks[status->N_nack].nack_sn = i;
|
||||
status->N_nack++;
|
||||
}
|
||||
|
||||
// make sure we don't exceed grant size (FIXME)
|
||||
rlc_am_nr_write_status_pdu(*status, rlc_am_nr_sn_size_t::size12bits, &tmp_buf);
|
||||
// TODO
|
||||
i = (i + 1) % MOD;
|
||||
}
|
||||
|
||||
if (max_len != UINT32_MAX) {
|
||||
status_prohibit_timer.run(); // UINT32_MAX is used just to querry the status PDU length
|
||||
}
|
||||
return tmp_buf.N_bytes;
|
||||
}
|
||||
|
||||
uint32_t rlc_am_nr_rx::get_status_pdu_length()
|
||||
{
|
||||
rlc_am_nr_status_pdu_t tmp_status; // length for no NACKs
|
||||
return get_status_pdu(&tmp_status, UINT32_MAX);
|
||||
}
|
||||
|
||||
bool rlc_am_nr_rx::get_do_status()
|
||||
{
|
||||
return do_status.load(std::memory_order_relaxed) && not status_prohibit_timer.is_running();
|
||||
}
|
||||
|
||||
void rlc_am_nr_rx::timer_expired(uint32_t timeout_id)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
|
||||
// Status Prohibit
|
||||
if (status_prohibit_timer.is_valid() && status_prohibit_timer.id() == timeout_id) {
|
||||
logger->debug("%s Status prohibit timer expired after %dms", parent->rb_name, status_prohibit_timer.duration());
|
||||
return;
|
||||
}
|
||||
|
||||
// Reassembly
|
||||
if (reassembly_timer.is_valid() && reassembly_timer.id() == timeout_id) {
|
||||
logger->debug("%s Reassembly timer expired after %dms", parent->rb_name, reassembly_timer.duration());
|
||||
/*
|
||||
* 5.2.3.2.4 Actions when t-Reassembly expires:
|
||||
* - update RX_Highest_Status to the SN of the first RLC SDU with SN >= RX_Next_Status_Trigger for which not
|
||||
* all bytes have been received;
|
||||
* - if RX_Next_Highest> RX_Highest_Status +1: or
|
||||
* - if RX_Next_Highest = RX_Highest_Status + 1 and there is at least one missing byte segment of the SDU
|
||||
* associated with SN = RX_Highest_Status before the last byte of all received segments of this SDU:
|
||||
* - start t-Reassembly;
|
||||
* - set RX_Next_Status_Trigger to RX_Next_Highest.
|
||||
*/
|
||||
for (uint32_t tmp_sn = rx_next_status_trigger; tmp_sn < rx_next_status_trigger + RLC_AM_WINDOW_SIZE; tmp_sn++) {
|
||||
if (not rx_window.has_sn(tmp_sn) || not rx_window[tmp_sn].fully_received) {
|
||||
rx_highest_status = tmp_sn;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool restart_reassembly_timer = false;
|
||||
if (rx_next_highest > rx_highest_status + 1) {
|
||||
restart_reassembly_timer = true;
|
||||
}
|
||||
if (rx_next_highest == rx_highest_status + 1 && not rx_window[rx_next_highest].fully_received) {
|
||||
restart_reassembly_timer = true;
|
||||
}
|
||||
if (restart_reassembly_timer) {
|
||||
reassembly_timer.run();
|
||||
rx_next_status_trigger = rx_next_highest;
|
||||
}
|
||||
|
||||
/* 5.3.4 Status reporting:
|
||||
* - The receiving side of an AM RLC entity shall trigger a STATUS report when t-Reassembly expires.
|
||||
* NOTE 2: The expiry of t-Reassembly triggers both RX_Highest_Status to be updated and a STATUS report to be
|
||||
* triggered, but the STATUS report shall be triggered after RX_Highest_Status is updated.
|
||||
*/
|
||||
do_status = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void rlc_am_nr_rx::write_to_upper_layers(uint32_t lcid, unique_byte_buffer_t sdu)
|
||||
{
|
||||
uint32_t nof_bytes = sdu->N_bytes;
|
||||
parent->pdcp->write_pdu(lcid, std::move(sdu));
|
||||
std::lock_guard<std::mutex> lock(parent->metrics_mutex);
|
||||
parent->metrics.num_rx_sdus++;
|
||||
parent->metrics.num_rx_sdu_bytes += nof_bytes;
|
||||
}
|
||||
|
||||
/*
|
||||
* Metrics
|
||||
*/
|
||||
uint32_t rlc_am_nr_rx::get_sdu_rx_latency_ms()
|
||||
{
|
||||
return 0;
|
||||
|
@ -130,4 +728,16 @@ uint32_t rlc_am_nr_rx::get_rx_buffered_bytes()
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helpers
|
||||
*/
|
||||
void rlc_am_nr_rx::debug_state()
|
||||
{
|
||||
logger->debug("RX entity state: Rx_Next %d, Rx_Next_Status_Trigger %d, Rx_Highest_Status %d, Rx_Next_Highest",
|
||||
rx_next,
|
||||
rx_next_status_trigger,
|
||||
rx_highest_status,
|
||||
rx_next_highest);
|
||||
}
|
||||
} // namespace srsran
|
||||
|
|
|
@ -238,7 +238,7 @@ int32_t rlc_am_nr_write_status_pdu(const rlc_am_nr_status_pdu_t& status_pdu,
|
|||
ptr++;
|
||||
|
||||
// write remaining 4 bits of NACK_SN
|
||||
*ptr = status_pdu.nacks[0].nack_sn & 0xf0;
|
||||
*ptr = (status_pdu.nacks[0].nack_sn & 0x0f) << 4;
|
||||
ptr++;
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -184,7 +184,6 @@ uint32_t rlc_tm::read_pdu(uint8_t* payload, uint32_t nof_bytes)
|
|||
metrics.num_tx_pdu_bytes += pdu_size;
|
||||
return pdu_size;
|
||||
} else {
|
||||
logger.warning("Queue empty while trying to read");
|
||||
if (ul_queue.size_bytes() > 0) {
|
||||
logger.warning("Corrupted queue: empty but size_bytes > 0. Resetting queue");
|
||||
ul_queue.reset();
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
|
||||
add_subdirectory(asn1)
|
||||
add_subdirectory(common)
|
||||
add_subdirectory(mac)
|
||||
add_subdirectory(phy)
|
||||
add_subdirectory(srslog)
|
||||
add_subdirectory(rlc)
|
||||
|
|
|
@ -33,6 +33,26 @@
|
|||
using namespace asn1;
|
||||
using namespace asn1::rrc_nr;
|
||||
|
||||
void test_rrc_setup_complete()
|
||||
{
|
||||
uint8_t msg[] = {0x10, 0xc0, 0x10, 0x00, 0x20, 0x25, 0x97, 0xe0, 0x1e, 0x1e, 0x34, 0xb5, 0x30, 0xb7, 0xe0, 0x04,
|
||||
0x10, 0x90, 0x00, 0xbf, 0x20, 0x0f, 0x11, 0x08, 0x00, 0x10, 0x15, 0x66, 0x75, 0xf7, 0x12, 0xe0,
|
||||
0x4f, 0x07, 0x0f, 0x07, 0x07, 0x10, 0x03, 0x87, 0xe0, 0x04, 0x10, 0x90, 0x00, 0xbf, 0x20, 0x0f,
|
||||
0x11, 0x08, 0x00, 0x10, 0x15, 0x66, 0x75, 0xf7, 0x11, 0x00, 0x10, 0x32, 0xe0, 0x4f, 0x07, 0x0f,
|
||||
0x07, 0x02, 0xf0, 0x20, 0x10, 0x15, 0x20, 0x0f, 0x11, 0x00, 0x00, 0x06, 0x41, 0x70, 0x7f, 0x07,
|
||||
0x00, 0x00, 0x01, 0x88, 0x0b, 0x01, 0x80, 0x10, 0x17, 0x40, 0x00, 0x09, 0x05, 0x30, 0x10, 0x10};
|
||||
// 10c01000202597e01e1e34b530b7e004109000bf200f11080010156675f712e04f070f0707100387e004109000bf200f11080010156675f711001032e04f070f0702f0201015200f1100000641707f07000001880b0180101740000905301010
|
||||
|
||||
asn1::cbit_ref bref{msg, sizeof(msg)};
|
||||
asn1::rrc_nr::ul_dcch_msg_s ul_dcch_msg;
|
||||
TESTASSERT_SUCCESS(ul_dcch_msg.unpack(bref));
|
||||
|
||||
TESTASSERT_EQ(ul_dcch_msg_type_c::types_opts::c1, ul_dcch_msg.msg.type().value);
|
||||
TESTASSERT_EQ(ul_dcch_msg_type_c::c1_c_::types_opts::rrc_setup_complete, ul_dcch_msg.msg.c1().type().value);
|
||||
|
||||
TESTASSERT_SUCCESS(test_pack_unpack_consistency(ul_dcch_msg));
|
||||
}
|
||||
|
||||
int test_eutra_nr_capabilities()
|
||||
{
|
||||
struct ue_mrdc_cap_s mrdc_cap;
|
||||
|
@ -220,7 +240,7 @@ int test_ue_rrc_reconfiguration()
|
|||
|
||||
int test_radio_bearer_config()
|
||||
{
|
||||
uint8_t rrc_msg[] = "\x14\x09\x28\x17\x87\xc0\x0c\x28";
|
||||
uint8_t rrc_msg[] = "\x14\x09\x28\x17\x87\xc0\x0c\x28";
|
||||
cbit_ref bref(&rrc_msg[0], sizeof(rrc_msg));
|
||||
radio_bearer_cfg_s radio_bearer_cfg;
|
||||
TESTASSERT(radio_bearer_cfg.unpack(bref) == SRSASN_SUCCESS);
|
||||
|
@ -1166,9 +1186,8 @@ int test_cell_group_config_fdd()
|
|||
ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack.resize(1);
|
||||
ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack[0] = 4;
|
||||
|
||||
|
||||
//TODO?
|
||||
// PUCCH resources (only one format1 for the moment)
|
||||
// TODO?
|
||||
// PUCCH resources (only one format1 for the moment)
|
||||
ul_config.init_ul_bwp.pucch_cfg.setup().res_to_add_mod_list_present = true;
|
||||
ul_config.init_ul_bwp.pucch_cfg.setup().res_to_add_mod_list.resize(1);
|
||||
auto& pucch_res1 = ul_config.init_ul_bwp.pucch_cfg.setup().res_to_add_mod_list[0];
|
||||
|
@ -1233,8 +1252,7 @@ int test_cell_group_config_fdd()
|
|||
// nzp-CSI-RS Resource
|
||||
cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().nzp_csi_rs_res_to_add_mod_list_present = true;
|
||||
cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().nzp_csi_rs_res_to_add_mod_list.resize(5);
|
||||
auto& nzp_csi_res =
|
||||
cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup();
|
||||
auto& nzp_csi_res = cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup();
|
||||
// item 0
|
||||
nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[0].nzp_csi_rs_res_id = 0;
|
||||
nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[0].res_map.freq_domain_alloc.set_row2();
|
||||
|
@ -1345,8 +1363,7 @@ int test_cell_group_config_fdd()
|
|||
cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().nzp_csi_rs_res_set_to_add_mod_list_present =
|
||||
true;
|
||||
cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().nzp_csi_rs_res_set_to_add_mod_list.resize(2);
|
||||
auto& nzp_csi_res_set =
|
||||
cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup();
|
||||
auto& nzp_csi_res_set = cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup();
|
||||
// item 0
|
||||
nzp_csi_res_set.nzp_csi_rs_res_set_to_add_mod_list[0].nzp_csi_res_set_id = 0;
|
||||
nzp_csi_res_set.nzp_csi_rs_res_set_to_add_mod_list[0].nzp_csi_rs_res.resize(1);
|
||||
|
@ -1396,7 +1413,7 @@ int test_cell_group_config_fdd()
|
|||
// Reconfig with Sync
|
||||
cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync_present = true;
|
||||
cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.new_ue_id = 17933;
|
||||
cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.t304 = recfg_with_sync_s::t304_opts::ms1000;
|
||||
cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.t304 = recfg_with_sync_s::t304_opts::ms1000;
|
||||
|
||||
cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common_present = true;
|
||||
cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ss_pbch_block_pwr = -36;
|
||||
|
@ -1625,7 +1642,6 @@ int test_cell_group_config_fdd()
|
|||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
auto& asn1_logger = srslog::fetch_basic_logger("ASN1", false);
|
||||
|
@ -1643,6 +1659,7 @@ int main()
|
|||
pcap_handle->open("srsran_asn1_rrc_nr_test.pcap");
|
||||
#endif
|
||||
|
||||
test_rrc_setup_complete();
|
||||
TESTASSERT(test_eutra_nr_capabilities() == SRSRAN_SUCCESS);
|
||||
TESTASSERT(test_ue_mrdc_capabilities() == SRSRAN_SUCCESS);
|
||||
TESTASSERT(test_ue_rrc_reconfiguration() == SRSRAN_SUCCESS);
|
||||
|
|
|
@ -466,13 +466,13 @@ int main(int argc, char** argv)
|
|||
dci.rnti = rnti;
|
||||
dci.is_tdd = false;
|
||||
dci.is_dwpts = false;
|
||||
dci.is_ra_order = false;
|
||||
dci.is_pdcch_order = false;
|
||||
dci.tb_cw_swap = false;
|
||||
dci.pconf = false;
|
||||
dci.power_offset = false;
|
||||
dci.tpc_pucch = false;
|
||||
dci.ra_preamble = false;
|
||||
dci.ra_mask_idx = false;
|
||||
dci.preamble_idx = 0;
|
||||
dci.prach_mask_idx = 0;
|
||||
dci.srs_request = false;
|
||||
dci.srs_request_present = false;
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ int basic_test_tx(rlc_am* rlc, byte_buffer_t pdu_bufs[NBUFS])
|
|||
rlc->write_sdu(std::move(sdu_bufs[i]));
|
||||
}
|
||||
|
||||
TESTASSERT(13 == rlc->get_buffer_state()); // 2 Bytes for fixed header + 6 for LIs + 5 for payload
|
||||
TESTASSERT(15 == rlc->get_buffer_state()); // 2 Bytes * NBUFFS (header size) + NBUFFS (data) = 15
|
||||
|
||||
// Read 5 PDUs from RLC1 (1 byte each)
|
||||
for (int i = 0; i < NBUFS; i++) {
|
||||
|
@ -60,27 +60,230 @@ int basic_test_tx(rlc_am* rlc, byte_buffer_t pdu_bufs[NBUFS])
|
|||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Test the transmission and acknowledgement of 5 SDUs.
|
||||
*
|
||||
* Each SDU is transmitted as a single PDU.
|
||||
* There are no lost PDUs, and the byte size is small, so the Poll_PDU configuration
|
||||
* will trigger the status report.
|
||||
* Poll PDU is configured to 4, so the 5th PDU should set the polling bit.
|
||||
*/
|
||||
int basic_test()
|
||||
{
|
||||
rlc_am_tester tester;
|
||||
timer_handler timers(8);
|
||||
byte_buffer_t pdu_bufs[NBUFS];
|
||||
|
||||
auto& test_logger = srslog::fetch_basic_logger("TESTER ");
|
||||
test_logger.info("====================");
|
||||
test_logger.info("==== Basic Test ====");
|
||||
test_logger.info("====================");
|
||||
rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
||||
rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers);
|
||||
|
||||
rlc_am_nr_tx* tx1 = dynamic_cast<rlc_am_nr_tx*>(rlc1.get_tx());
|
||||
rlc_am_nr_rx* rx1 = dynamic_cast<rlc_am_nr_rx*>(rlc1.get_rx());
|
||||
rlc_am_nr_tx* tx2 = dynamic_cast<rlc_am_nr_tx*>(rlc2.get_tx());
|
||||
rlc_am_nr_rx* rx2 = dynamic_cast<rlc_am_nr_rx*>(rlc2.get_rx());
|
||||
|
||||
// before configuring entity
|
||||
TESTASSERT(0 == rlc1.get_buffer_state());
|
||||
|
||||
if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config())) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (not rlc2.configure(rlc_config_t::default_rlc_am_nr_config())) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
basic_test_tx(&rlc1, pdu_bufs);
|
||||
|
||||
// Write 5 PDUs into RLC2
|
||||
for (int i = 0; i < NBUFS; i++) {
|
||||
rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
||||
}
|
||||
|
||||
TESTASSERT(3 == rlc2.get_buffer_state());
|
||||
// Read status PDU from RLC2
|
||||
byte_buffer_t status_buf;
|
||||
int len = rlc2.read_pdu(status_buf.msg, 3);
|
||||
status_buf.N_bytes = len;
|
||||
|
||||
TESTASSERT(0 == rlc2.get_buffer_state());
|
||||
|
||||
// Assert status is correct
|
||||
rlc_am_nr_status_pdu_t status_check = {};
|
||||
rlc_am_nr_read_status_pdu(&status_buf, rlc_am_nr_sn_size_t::size12bits, &status_check);
|
||||
TESTASSERT(status_check.ack_sn == 5); // 5 is the last SN that was not received.
|
||||
|
||||
// Write status PDU to RLC1
|
||||
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
||||
|
||||
// Check TX_NEXT_ACK
|
||||
rlc_am_nr_tx_state_t st = tx1->get_tx_state();
|
||||
TESTASSERT_EQ(5, st.tx_next_ack);
|
||||
TESTASSERT_EQ(0, tx1->get_tx_window_size());
|
||||
|
||||
// Check statistics
|
||||
rlc_bearer_metrics_t metrics1 = rlc1.get_metrics();
|
||||
rlc_bearer_metrics_t metrics2 = rlc2.get_metrics();
|
||||
|
||||
// RLC1 PDU metrics
|
||||
TESTASSERT_EQ(5, metrics1.num_tx_sdus);
|
||||
TESTASSERT_EQ(0, metrics1.num_rx_sdus);
|
||||
TESTASSERT_EQ(5, metrics1.num_tx_sdu_bytes);
|
||||
TESTASSERT_EQ(0, metrics1.num_rx_sdu_bytes);
|
||||
TESTASSERT_EQ(0, metrics1.num_lost_sdus);
|
||||
// RLC1 SDU metrics
|
||||
TESTASSERT_EQ(5, metrics1.num_tx_pdus);
|
||||
TESTASSERT_EQ(1, metrics1.num_rx_pdus); // One status PDU
|
||||
TESTASSERT_EQ(15, metrics1.num_tx_pdu_bytes); // 2 Bytes * NBUFFS (header size) + NBUFFS (data) = 15
|
||||
TESTASSERT_EQ(3, metrics1.num_rx_pdu_bytes); // One status PDU
|
||||
TESTASSERT_EQ(0, metrics1.num_lost_sdus); // No lost SDUs
|
||||
|
||||
// RLC2 PDU metrics
|
||||
TESTASSERT_EQ(0, metrics2.num_tx_sdus);
|
||||
TESTASSERT_EQ(5, metrics2.num_rx_sdus);
|
||||
TESTASSERT_EQ(0, metrics2.num_tx_sdu_bytes);
|
||||
TESTASSERT_EQ(5, metrics2.num_rx_sdu_bytes);
|
||||
TESTASSERT_EQ(0, metrics2.num_lost_sdus);
|
||||
// RLC2 SDU metrics
|
||||
TESTASSERT_EQ(1, metrics2.num_tx_pdus); // One status PDU
|
||||
TESTASSERT_EQ(5, metrics2.num_rx_pdus); // 5 SDUs
|
||||
TESTASSERT_EQ(3, metrics2.num_tx_pdu_bytes); // One status PDU
|
||||
TESTASSERT_EQ(15, metrics2.num_rx_pdu_bytes); // 2 Bytes * NBUFFS (header size) + NBUFFS (data) = 15
|
||||
TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Test the loss of a single PDU.
|
||||
* NACK should be visible in the status report.
|
||||
* Retx after NACK should be present too.
|
||||
*/
|
||||
int lost_pdu_test()
|
||||
{
|
||||
rlc_am_tester tester;
|
||||
timer_handler timers(8);
|
||||
byte_buffer_t pdu_bufs[NBUFS];
|
||||
|
||||
auto& test_logger = srslog::fetch_basic_logger("TESTER ");
|
||||
test_logger.info("=======================");
|
||||
test_logger.info("==== Lost PDU Test ====");
|
||||
test_logger.info("=======================");
|
||||
rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
||||
rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers);
|
||||
|
||||
// before configuring entity
|
||||
TESTASSERT(0 == rlc1.get_buffer_state());
|
||||
|
||||
if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) {
|
||||
if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config())) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (not rlc2.configure(rlc_config_t::default_rlc_am_config())) {
|
||||
if (not rlc2.configure(rlc_config_t::default_rlc_am_nr_config())) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// basic_test_tx(&rlc1, pdu_bufs);
|
||||
basic_test_tx(&rlc1, pdu_bufs);
|
||||
|
||||
// Write 5 PDUs into RLC2
|
||||
for (int i = 0; i < NBUFS; i++) {
|
||||
if (i != 3) {
|
||||
rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); // Don't write RLC_SN=3.
|
||||
}
|
||||
}
|
||||
|
||||
// Only after t-reassembly has expired, will the status report include NACKs.
|
||||
TESTASSERT(3 == rlc2.get_buffer_state());
|
||||
{
|
||||
// Read status PDU from RLC2
|
||||
byte_buffer_t status_buf;
|
||||
int len = rlc2.read_pdu(status_buf.msg, 5);
|
||||
status_buf.N_bytes = len;
|
||||
|
||||
TESTASSERT(0 == rlc2.get_buffer_state());
|
||||
|
||||
// Assert status is correct
|
||||
rlc_am_nr_status_pdu_t status_check = {};
|
||||
rlc_am_nr_read_status_pdu(&status_buf, rlc_am_nr_sn_size_t::size12bits, &status_check);
|
||||
TESTASSERT(status_check.ack_sn == 3); // 3 is the next expected SN (i.e. the lost packet.)
|
||||
|
||||
// Write status PDU to RLC1
|
||||
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
||||
}
|
||||
|
||||
// Step timers until reassambly timeout expires
|
||||
for (int cnt = 0; cnt < 35; cnt++) {
|
||||
timers.step_all();
|
||||
}
|
||||
|
||||
// t-reassembly has expired. There should be a NACK in the status report.
|
||||
TESTASSERT(5 == rlc2.get_buffer_state());
|
||||
{
|
||||
// Read status PDU from RLC2
|
||||
byte_buffer_t status_buf;
|
||||
int len = rlc2.read_pdu(status_buf.msg, 5);
|
||||
status_buf.N_bytes = len;
|
||||
|
||||
TESTASSERT(0 == rlc2.get_buffer_state());
|
||||
|
||||
// Assert status is correct
|
||||
rlc_am_nr_status_pdu_t status_check = {};
|
||||
rlc_am_nr_read_status_pdu(&status_buf, rlc_am_nr_sn_size_t::size12bits, &status_check);
|
||||
TESTASSERT(status_check.ack_sn == 5); // 5 is the next expected SN.
|
||||
TESTASSERT(status_check.N_nack == 1); // We lost one PDU.
|
||||
TESTASSERT(status_check.nacks[0].nack_sn == 3); // Lost PDU SN=3.
|
||||
|
||||
// Write status PDU to RLC1
|
||||
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
||||
|
||||
// Check there is an Retx of SN=3
|
||||
TESTASSERT(3 == rlc1.get_buffer_state());
|
||||
}
|
||||
|
||||
{
|
||||
// Check correct re-transmission
|
||||
byte_buffer_t retx_buf;
|
||||
int len = rlc1.read_pdu(retx_buf.msg, 3);
|
||||
retx_buf.N_bytes = len;
|
||||
TESTASSERT(3 == len);
|
||||
|
||||
rlc2.write_pdu(retx_buf.msg, retx_buf.N_bytes);
|
||||
|
||||
TESTASSERT(0 == rlc2.get_buffer_state());
|
||||
}
|
||||
|
||||
// Check statistics
|
||||
rlc_bearer_metrics_t metrics1 = rlc1.get_metrics();
|
||||
rlc_bearer_metrics_t metrics2 = rlc2.get_metrics();
|
||||
|
||||
// SDU metrics
|
||||
TESTASSERT_EQ(5, metrics1.num_tx_sdus);
|
||||
TESTASSERT_EQ(0, metrics1.num_rx_sdus);
|
||||
TESTASSERT_EQ(5, metrics1.num_tx_sdu_bytes);
|
||||
TESTASSERT_EQ(0, metrics1.num_rx_sdu_bytes);
|
||||
TESTASSERT_EQ(0, metrics1.num_lost_sdus);
|
||||
// PDU metrics
|
||||
TESTASSERT_EQ(5 + 1, metrics1.num_tx_pdus); // One re-transmission
|
||||
TESTASSERT_EQ(2, metrics1.num_rx_pdus); // One status PDU
|
||||
TESTASSERT_EQ(18, metrics1.num_tx_pdu_bytes); // 2 Bytes * NBUFFS (header size) + NBUFFS (data) + 1 rext (3) = 18
|
||||
TESTASSERT_EQ(3 + 5, metrics1.num_rx_pdu_bytes); // Two status PDU (one with a NACK)
|
||||
TESTASSERT_EQ(0, metrics1.num_lost_sdus); // No lost SDUs
|
||||
|
||||
// PDU metrics
|
||||
TESTASSERT_EQ(0, metrics2.num_tx_sdus);
|
||||
TESTASSERT_EQ(5, metrics2.num_rx_sdus);
|
||||
TESTASSERT_EQ(0, metrics2.num_tx_sdu_bytes);
|
||||
TESTASSERT_EQ(5, metrics2.num_rx_sdu_bytes);
|
||||
TESTASSERT_EQ(0, metrics2.num_lost_sdus);
|
||||
// SDU metrics
|
||||
TESTASSERT_EQ(2, metrics2.num_tx_pdus); // Two status PDUs
|
||||
TESTASSERT_EQ(5, metrics2.num_rx_pdus); // 5 PDUs (6 tx'ed, but one was lost)
|
||||
TESTASSERT_EQ(5 + 3, metrics2.num_tx_pdu_bytes); // Two status PDU (one with a NACK)
|
||||
TESTASSERT_EQ(15, metrics2.num_rx_pdu_bytes); // 2 Bytes * NBUFFS (header size) + NBUFFS (data) = 15
|
||||
TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -99,8 +302,8 @@ int main(int argc, char** argv)
|
|||
}
|
||||
srslog::set_default_sink(*spy);
|
||||
|
||||
auto& logger_rlc1 = srslog::fetch_basic_logger("RLC_NR_AM_1", *spy, false);
|
||||
auto& logger_rlc2 = srslog::fetch_basic_logger("RLC_NR_AM_2", *spy, false);
|
||||
auto& logger_rlc1 = srslog::fetch_basic_logger("RLC_AM_1", *spy, false);
|
||||
auto& logger_rlc2 = srslog::fetch_basic_logger("RLC_AM_2", *spy, false);
|
||||
logger_rlc1.set_hex_dump_max_size(100);
|
||||
logger_rlc2.set_hex_dump_max_size(100);
|
||||
logger_rlc1.set_level(srslog::basic_levels::debug);
|
||||
|
@ -109,10 +312,8 @@ int main(int argc, char** argv)
|
|||
// start log backend
|
||||
srslog::init();
|
||||
|
||||
if (basic_test()) {
|
||||
printf("basic_test failed\n");
|
||||
exit(-1);
|
||||
};
|
||||
TESTASSERT(basic_test() == SRSRAN_SUCCESS);
|
||||
TESTASSERT(lost_pdu_test() == SRSRAN_SUCCESS);
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -346,7 +346,6 @@ enable = false
|
|||
# tracing_filename: File path to use for tracing information
|
||||
# tracing_buffcapacity: Maximum capacity in bytes the tracing framework can store
|
||||
# stdout_ts_enable: Prints once per second the timestamp into stdout
|
||||
# pregenerate_signals: Pregenerate uplink signals after attach. Improves CPU performance
|
||||
# tx_amplitude: Transmit amplitude factor (set 0-1 to reduce PAPR)
|
||||
# rrc_inactivity_timer Inactivity timeout used to remove UE context from RRC (in milliseconds)
|
||||
# max_mac_dl_kos: Maximum number of consecutive KOs in DL before triggering the UE's release (default: 100)
|
||||
|
@ -381,7 +380,6 @@ enable = false
|
|||
#tracing_filename = /tmp/enb_tracing.log
|
||||
#tracing_buffcapacity = 1000000
|
||||
#stdout_ts_enable = false
|
||||
#pregenerate_signals = false
|
||||
#tx_amplitude = 0.6
|
||||
#rrc_inactivity_timer = 30000
|
||||
#max_mac_dl_kos = 100
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#ifndef SRSENB_PHY_INTERFACES_H_
|
||||
#define SRSENB_PHY_INTERFACES_H_
|
||||
|
||||
#include "srsgnb/hdr/phy/phy_nr_interfaces.h"
|
||||
#include "srsran/asn1/rrc/rr_common.h"
|
||||
#include "srsran/common/interfaces_common.h"
|
||||
#include "srsran/phy/channel/channel.h"
|
||||
|
@ -42,22 +43,7 @@ struct phy_cell_cfg_t {
|
|||
float gain_db;
|
||||
};
|
||||
|
||||
struct phy_cell_cfg_nr_t {
|
||||
srsran_carrier_nr_t carrier;
|
||||
uint32_t rf_port;
|
||||
uint32_t cell_id;
|
||||
double dl_freq_hz;
|
||||
double ul_freq_hz;
|
||||
uint32_t root_seq_idx;
|
||||
uint32_t num_ra_preambles;
|
||||
float gain_db;
|
||||
srsran_pdcch_cfg_nr_t pdcch = {}; ///< Common CORESET and Search Space configuration
|
||||
srsran_pdsch_cfg_t pdsch = {};
|
||||
srsran_prach_cfg_t prach = {};
|
||||
};
|
||||
|
||||
typedef std::vector<phy_cell_cfg_t> phy_cell_cfg_list_t;
|
||||
typedef std::vector<phy_cell_cfg_nr_t> phy_cell_cfg_list_nr_t;
|
||||
typedef std::vector<phy_cell_cfg_t> phy_cell_cfg_list_t;
|
||||
|
||||
struct phy_args_t {
|
||||
std::string type;
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#ifndef SRSRAN_UE_BUFFER_MANAGER_H
|
||||
#define SRSRAN_UE_BUFFER_MANAGER_H
|
||||
#ifndef SRSRAN_BASE_UE_BUFFER_MANAGER_H
|
||||
#define SRSRAN_BASE_UE_BUFFER_MANAGER_H
|
||||
|
||||
#include "sched_config.h"
|
||||
#include "srsran/adt/span.h"
|
||||
|
@ -35,7 +35,7 @@ namespace srsenb {
|
|||
* Class to handle UE DL+UL RLC and MAC buffers state
|
||||
*/
|
||||
template <bool isNR>
|
||||
class ue_buffer_manager
|
||||
class base_ue_buffer_manager
|
||||
{
|
||||
protected:
|
||||
const static uint32_t MAX_LC_ID = isNR ? (srsran::MAX_NR_NOF_BEARERS - 1) : srsran::MAX_LTE_LCID;
|
||||
|
@ -46,7 +46,7 @@ protected:
|
|||
constexpr static uint32_t pbr_infinity = -1;
|
||||
|
||||
public:
|
||||
explicit ue_buffer_manager(uint16_t rnti, srslog::basic_logger& logger_);
|
||||
explicit base_ue_buffer_manager(uint16_t rnti, srslog::basic_logger& logger_);
|
||||
|
||||
// Bearer configuration
|
||||
void config_lcids(srsran::const_span<mac_lc_ch_cfg_t> bearer_cfg_list);
|
||||
|
@ -89,6 +89,8 @@ public:
|
|||
static bool is_lcg_valid(uint32_t lcg) { return lcg <= MAX_LCG_ID; }
|
||||
|
||||
protected:
|
||||
~base_ue_buffer_manager() = default;
|
||||
|
||||
bool config_lcid_internal(uint32_t lcid, const mac_lc_ch_cfg_t& bearer_cfg);
|
||||
|
||||
srslog::basic_logger& logger;
|
||||
|
@ -108,4 +110,4 @@ protected:
|
|||
|
||||
} // namespace srsenb
|
||||
|
||||
#endif // SRSRAN_UE_BUFFER_MANAGER_H
|
||||
#endif // SRSRAN_BASE_UE_BUFFER_MANAGER_H
|
|
@ -119,6 +119,16 @@ private:
|
|||
uint16_t allocate_ue(uint32_t enb_cc_idx);
|
||||
bool is_valid_rnti_unprotected(uint16_t rnti);
|
||||
|
||||
/* helper function for PDCCH orders */
|
||||
/**
|
||||
* @brief Checks if the current RACH is a RACH triggered by a PDCCH order.
|
||||
*
|
||||
* @param[in] preamble_idx RACH preamble idx
|
||||
* @param rnti is the rnti where the crnti of the RACH is written
|
||||
* @return true if this is a RACH triggered by a PDCCH order, otherwise it returns false
|
||||
*/
|
||||
bool is_pending_pdcch_order_prach(const uint32_t preamble_idx, uint16_t& rnti);
|
||||
|
||||
srslog::basic_logger& logger;
|
||||
|
||||
// We use a rwlock in MAC to allow multiple workers to access MAC simultaneously. No conflicts will happen since
|
||||
|
@ -190,6 +200,9 @@ private:
|
|||
// Number of rach preambles detected for a cc.
|
||||
std::vector<uint32_t> detected_rachs;
|
||||
|
||||
// PDCCH order
|
||||
std::vector<sched_interface::dl_sched_po_info_t> pending_po_prachs = {};
|
||||
|
||||
// Softbuffer pool
|
||||
std::unique_ptr<srsran::obj_pool_itf<ue_cc_softbuffers> > softbuffer_pool;
|
||||
};
|
||||
|
|
|
@ -80,6 +80,8 @@ public:
|
|||
int dl_sched(uint32_t tti, uint32_t enb_cc_idx, dl_sched_res_t& sched_result) final;
|
||||
int ul_sched(uint32_t tti, uint32_t enb_cc_idx, ul_sched_res_t& sched_result) final;
|
||||
|
||||
int set_pdcch_order(uint32_t enb_cc_idx, dl_sched_po_info_t pdcch_order_info) final;
|
||||
|
||||
/* Custom functions
|
||||
*/
|
||||
void set_dl_tti_mask(uint8_t* tti_mask, uint32_t nof_sfs) final;
|
||||
|
|
|
@ -45,6 +45,7 @@ public:
|
|||
void set_dl_tti_mask(uint8_t* tti_mask, uint32_t nof_sfs);
|
||||
const cc_sched_result& generate_tti_result(srsran::tti_point tti_rx);
|
||||
int dl_rach_info(dl_sched_rar_info_t rar_info);
|
||||
int pdcch_order_info(dl_sched_po_info_t pdcch_order_info);
|
||||
|
||||
// getters
|
||||
const ra_sched* get_ra_sched() const { return ra_sched_ptr.get(); }
|
||||
|
@ -58,6 +59,8 @@ private:
|
|||
int alloc_ul_users(sf_sched* tti_sched);
|
||||
//! Get sf_sched for a given TTI
|
||||
sf_sched* get_sf_sched(srsran::tti_point tti_rx);
|
||||
//! Schedule PDCCH orders
|
||||
void pdcch_order_sched(sf_sched* tti_sched);
|
||||
|
||||
// args
|
||||
const sched_cell_params_t* cc_cfg = nullptr;
|
||||
|
@ -77,6 +80,11 @@ private:
|
|||
std::unique_ptr<bc_sched> bc_sched_ptr;
|
||||
std::unique_ptr<ra_sched> ra_sched_ptr;
|
||||
std::unique_ptr<sched_base> sched_algo;
|
||||
|
||||
// pending pdcch orders
|
||||
std::vector<dl_sched_po_info_t> pending_pdcch_orders;
|
||||
|
||||
uint32_t po_aggr_level = 2;
|
||||
};
|
||||
|
||||
//! Broadcast (SIB + paging) scheduler
|
||||
|
|
|
@ -150,6 +150,9 @@ public:
|
|||
struct bc_alloc_t : public ctrl_alloc_t {
|
||||
sched_interface::dl_sched_bc_t bc_grant;
|
||||
};
|
||||
struct po_alloc_t : public ctrl_alloc_t {
|
||||
sched_interface::dl_sched_po_t po_grant;
|
||||
};
|
||||
struct dl_alloc_t {
|
||||
size_t dci_idx;
|
||||
uint16_t rnti;
|
||||
|
@ -183,6 +186,8 @@ public:
|
|||
alloc_result alloc_sib(uint32_t aggr_lvl, uint32_t sib_idx, uint32_t sib_ntx, rbg_interval rbgs);
|
||||
alloc_result alloc_paging(uint32_t aggr_lvl, uint32_t paging_payload, rbg_interval rbgs);
|
||||
alloc_result alloc_rar(uint32_t aggr_lvl, const pending_rar_t& rar_grant, rbg_interval rbgs, uint32_t nof_grants);
|
||||
alloc_result
|
||||
alloc_pdcch_order(const sched_interface::dl_sched_po_info_t& po_cfg, uint32_t aggr_lvl, rbg_interval rbgs);
|
||||
bool reserve_dl_rbgs(uint32_t rbg_start, uint32_t rbg_end) { return tti_alloc.reserve_dl_rbgs(rbg_start, rbg_end); }
|
||||
|
||||
// UL alloc methods
|
||||
|
@ -232,6 +237,7 @@ private:
|
|||
|
||||
srsran::bounded_vector<bc_alloc_t, sched_interface::MAX_BC_LIST> bc_allocs;
|
||||
srsran::bounded_vector<rar_alloc_t, sched_interface::MAX_RAR_LIST> rar_allocs;
|
||||
srsran::bounded_vector<po_alloc_t, sched_interface::MAX_PO_LIST> po_allocs;
|
||||
srsran::bounded_vector<dl_alloc_t, sched_interface::MAX_DATA_LIST> data_allocs;
|
||||
srsran::bounded_vector<ul_alloc_t, sched_interface::MAX_DATA_LIST> ul_data_allocs;
|
||||
uint32_t last_msg3_prb = 0, max_msg3_prb = 0;
|
||||
|
|
|
@ -46,6 +46,7 @@ public:
|
|||
const static int MAX_DATA_LIST = 32;
|
||||
const static int MAX_RAR_LIST = 8;
|
||||
const static int MAX_BC_LIST = 8;
|
||||
const static int MAX_PO_LIST = 8;
|
||||
const static int MAX_RLC_PDU_LIST = 8;
|
||||
const static int MAX_PHICH_LIST = 8;
|
||||
|
||||
|
@ -224,20 +225,31 @@ public:
|
|||
|
||||
typedef struct {
|
||||
srsran_dci_dl_t dci;
|
||||
|
||||
enum bc_type { BCCH, PCCH } type;
|
||||
|
||||
uint32_t index;
|
||||
|
||||
uint32_t tbs;
|
||||
|
||||
} dl_sched_bc_t;
|
||||
|
||||
struct dl_sched_po_info_t {
|
||||
uint32_t preamble_idx;
|
||||
uint32_t prach_mask_idx;
|
||||
uint16_t crnti;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
srsran_dci_dl_t dci;
|
||||
uint32_t tbs;
|
||||
uint16_t crnti;
|
||||
uint32_t preamble_idx;
|
||||
uint32_t prach_mask_idx;
|
||||
} dl_sched_po_t;
|
||||
|
||||
struct dl_sched_res_t {
|
||||
uint32_t cfi;
|
||||
srsran::bounded_vector<dl_sched_data_t, MAX_DATA_LIST> data;
|
||||
srsran::bounded_vector<dl_sched_rar_t, MAX_RAR_LIST> rar;
|
||||
srsran::bounded_vector<dl_sched_bc_t, MAX_BC_LIST> bc;
|
||||
srsran::bounded_vector<dl_sched_po_t, MAX_PO_LIST> po;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
|
@ -310,6 +322,9 @@ public:
|
|||
virtual int dl_sched(uint32_t tti, uint32_t enb_cc_idx, dl_sched_res_t& sched_result) = 0;
|
||||
virtual int ul_sched(uint32_t tti, uint32_t enb_cc_idx, ul_sched_res_t& sched_result) = 0;
|
||||
|
||||
/* PDCCH order */
|
||||
virtual int set_pdcch_order(uint32_t enb_cc_idx, dl_sched_po_info_t pdcch_order_info) = 0;
|
||||
|
||||
/* Custom */
|
||||
virtual void set_dl_tti_mask(uint8_t* tti_mask, uint32_t nof_sfs) = 0;
|
||||
virtual std::array<int, SRSRAN_MAX_CARRIERS> get_enb_ue_cc_map(uint16_t rnti) = 0;
|
||||
|
|
|
@ -91,10 +91,11 @@ public:
|
|||
};
|
||||
|
||||
/// Type of Allocation stored in PDSCH/PUSCH
|
||||
enum class alloc_type_t { DL_BC, DL_PCCH, DL_RAR, DL_DATA, UL_DATA };
|
||||
enum class alloc_type_t { DL_BC, DL_PCCH, DL_RAR, DL_PDCCH_ORDER, DL_DATA, UL_DATA };
|
||||
inline bool is_dl_ctrl_alloc(alloc_type_t a)
|
||||
{
|
||||
return a == alloc_type_t::DL_BC or a == alloc_type_t::DL_PCCH or a == alloc_type_t::DL_RAR;
|
||||
return a == alloc_type_t::DL_BC or a == alloc_type_t::DL_PCCH or a == alloc_type_t::DL_RAR or
|
||||
a == alloc_type_t::DL_PDCCH_ORDER;
|
||||
}
|
||||
|
||||
} // namespace srsenb
|
||||
|
|
|
@ -113,6 +113,11 @@ bool generate_rar_dci(sched_interface::dl_sched_rar_t& rar,
|
|||
const sched_cell_params_t& cell_params,
|
||||
uint32_t current_cfi);
|
||||
|
||||
void generate_pdcch_order_dci(sched_interface::dl_sched_po_t& pdcch_order,
|
||||
tti_point tti_tx_dl,
|
||||
const sched_cell_params_t& cell_params,
|
||||
uint32_t current_cfi);
|
||||
|
||||
void log_broadcast_allocation(const sched_interface::dl_sched_bc_t& bc,
|
||||
rbg_interval rbg_range,
|
||||
const sched_cell_params_t& cell_params);
|
||||
|
@ -123,6 +128,10 @@ void log_rar_allocation(const sched_interface::dl_sched_rar_t& rar,
|
|||
|
||||
void log_rar_allocation(const sched_interface::dl_sched_rar_t& rar, rbg_interval rbg_range);
|
||||
|
||||
void log_po_allocation(const sched_interface::dl_sched_po_t& pdcch_order,
|
||||
rbg_interval rbg_range,
|
||||
const sched_cell_params_t& cell_params);
|
||||
|
||||
} // namespace srsenb
|
||||
|
||||
#endif // SRSRAN_SCHED_DCI_H
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#ifndef SRSRAN_SCHED_LCH_H
|
||||
#define SRSRAN_SCHED_LCH_H
|
||||
|
||||
#include "srsenb/hdr/stack/mac/common/ue_buffer_manager.h"
|
||||
#include "srsenb/hdr/stack/mac/common/base_ue_buffer_manager.h"
|
||||
#include "srsenb/hdr/stack/mac/sched_interface.h"
|
||||
#include "srsran/adt/pool/cached_alloc.h"
|
||||
#include "srsran/mac/pdu.h"
|
||||
|
@ -30,12 +30,12 @@
|
|||
|
||||
namespace srsenb {
|
||||
|
||||
class lch_ue_manager : private ue_buffer_manager<false>
|
||||
class lch_ue_manager : private base_ue_buffer_manager<false>
|
||||
{
|
||||
using base_type = ue_buffer_manager<false>;
|
||||
using base_type = base_ue_buffer_manager<false>;
|
||||
|
||||
public:
|
||||
explicit lch_ue_manager(uint16_t rnti) : ue_buffer_manager(rnti, srslog::fetch_basic_logger("MAC")) {}
|
||||
explicit lch_ue_manager(uint16_t rnti) : base_ue_buffer_manager(rnti, srslog::fetch_basic_logger("MAC")) {}
|
||||
void set_cfg(const sched_interface::ue_cfg_t& cfg_);
|
||||
void new_tti();
|
||||
|
||||
|
|
|
@ -949,7 +949,16 @@ static int parse_cell_list(all_args_t* args, rrc_cfg_t* rrc_cfg, Setting& root)
|
|||
}
|
||||
|
||||
// Configuration check
|
||||
// counter for every RF port used by the eNB to avoid misconfiguration/mapping of cells
|
||||
uint32_t next_rf_port = 0;
|
||||
for (auto it = rrc_cfg->cell_list.begin(); it != rrc_cfg->cell_list.end(); it++) {
|
||||
// Make sure RF ports are assigned in order
|
||||
if (it->rf_port != next_rf_port) {
|
||||
ERROR("RF ports need to be in order starting with 0 (%d != %d)", it->rf_port, next_rf_port);
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
next_rf_port++;
|
||||
|
||||
for (auto it2 = it + 1; it2 != rrc_cfg->cell_list.end(); it2++) {
|
||||
// Check RF port is not repeated
|
||||
if (it->rf_port == it2->rf_port) {
|
||||
|
@ -995,8 +1004,17 @@ static int parse_nr_cell_list(all_args_t* args, rrc_nr_cfg_t* rrc_cfg_nr, rrc_cf
|
|||
|
||||
srsran::srsran_band_helper band_helper;
|
||||
// Configuration check
|
||||
// counter for every RF port used by the eNB to avoid misconfiguration/mapping of cells
|
||||
uint32_t next_rf_port = rrc_cfg_eutra->cell_list.size();
|
||||
for (auto it = rrc_cfg_nr->cell_list.begin(); it != rrc_cfg_nr->cell_list.end(); ++it) {
|
||||
// check against NR cells
|
||||
// Make sure RF ports are assigned in order
|
||||
if (it->phy_cell.rf_port != next_rf_port) {
|
||||
ERROR("RF ports need to be in order starting with 0 (%d != %d)", it->phy_cell.rf_port, next_rf_port);
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
next_rf_port++;
|
||||
|
||||
// check against other NR cells
|
||||
for (auto it2 = it + 1; it2 != rrc_cfg_nr->cell_list.end(); it2++) {
|
||||
// Check RF port is not repeated
|
||||
if (it->phy_cell.rf_port == it2->phy_cell.rf_port) {
|
||||
|
@ -1205,9 +1223,24 @@ int parse_cfg_files(all_args_t* args_, rrc_cfg_t* rrc_cfg_, rrc_nr_cfg_t* rrc_nr
|
|||
// NR cells available.
|
||||
if (rrc_nr_cfg_->is_standalone) {
|
||||
// SA mode. Update NGAP args
|
||||
args_->nr_stack.ngap.gnb_id = args_->enb.enb_id;
|
||||
args_->nr_stack.ngap.cell_id = rrc_nr_cfg_->cell_list[0].phy_cell.cell_id;
|
||||
args_->nr_stack.ngap.tac = rrc_nr_cfg_->cell_list[0].tac;
|
||||
// take equivalent S1AP params to update NGAP params
|
||||
args_->nr_stack.ngap.gnb_name = args_->stack.s1ap.enb_name;
|
||||
args_->nr_stack.ngap.gnb_id = args_->enb.enb_id;
|
||||
args_->nr_stack.ngap.mcc = args_->stack.s1ap.mcc;
|
||||
args_->nr_stack.ngap.mnc = args_->stack.s1ap.mnc;
|
||||
args_->nr_stack.ngap.gtp_bind_addr = args_->stack.s1ap.gtp_bind_addr;
|
||||
args_->nr_stack.ngap.gtp_advertise_addr = args_->stack.s1ap.gtp_advertise_addr;
|
||||
args_->nr_stack.ngap.amf_addr = args_->stack.s1ap.mme_addr;
|
||||
args_->nr_stack.ngap.ngc_bind_addr = args_->stack.s1ap.gtp_bind_addr;
|
||||
|
||||
// Parse NIA/NEA preference list (use same as LTE for now)
|
||||
for (uint32_t i = 0; i < rrc_cfg_->eea_preference_list.size(); i++) {
|
||||
rrc_nr_cfg_->nea_preference_list[i] = (srsran::CIPHERING_ALGORITHM_ID_NR_ENUM)rrc_cfg_->eea_preference_list[i];
|
||||
rrc_nr_cfg_->nia_preference_list[i] = (srsran::INTEGRITY_ALGORITHM_ID_NR_ENUM)rrc_cfg_->eia_preference_list[i];
|
||||
}
|
||||
|
||||
} else {
|
||||
// NSA mode.
|
||||
// update EUTRA RRC params for ENDC
|
||||
|
@ -1477,60 +1510,6 @@ int set_derived_args(all_args_t* args_, rrc_cfg_t* rrc_cfg_, phy_cfg_t* phy_cfg_
|
|||
*/
|
||||
int set_derived_args_nr(all_args_t* args_, rrc_nr_cfg_t* rrc_nr_cfg_, phy_cfg_t* phy_cfg_)
|
||||
{
|
||||
// set rach cfg common
|
||||
auto& rach_cfg_common = rrc_nr_cfg_->rach_cfg_common;
|
||||
auto& rach_cfg_generic = rach_cfg_common.rach_cfg_generic;
|
||||
|
||||
uint8_t msg1_fdm = 1; // TODO read from config
|
||||
if (!asn1::number_to_enum(rach_cfg_generic.msg1_fdm, msg1_fdm)) {
|
||||
ERROR("Config Error: Invalid msg1_fdm (%d)\n", msg1_fdm);
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
rach_cfg_generic.preamb_rx_target_pwr = -110; // TODO read from config
|
||||
|
||||
uint8_t preamb_trans_max = 7; // TODO read from config
|
||||
if (!asn1::number_to_enum(rach_cfg_generic.preamb_trans_max, preamb_trans_max)) {
|
||||
ERROR("Config Error: Invalid preamble_trans_max (%d)\n", preamb_trans_max);
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
uint8_t pwr_ramp_step = 4; // TODO read from config
|
||||
if (!asn1::number_to_enum(rach_cfg_generic.pwr_ramp_step, pwr_ramp_step)) {
|
||||
ERROR("Config Error: Invalid pwr_ramp_step (%d)\n", pwr_ramp_step);
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
uint8_t ra_resp_win_size = 10; // TODO read from config
|
||||
if (!asn1::number_to_enum(rach_cfg_generic.ra_resp_win, ra_resp_win_size)) {
|
||||
ERROR("Config Error: Invalid ra_resp_win_size (%d)\n", ra_resp_win_size);
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
uint8_t ra_contention_resolution_timer = 64; // TODO read from config
|
||||
if (!asn1::number_to_enum(rach_cfg_common.ra_contention_resolution_timer, ra_contention_resolution_timer)) {
|
||||
ERROR("Config Error: Invalid mac_con_res_timer (%d)\n", ra_contention_resolution_timer);
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
rrc_nr_cfg_->prach_root_seq_idx_type = 839; // TODO read from config
|
||||
|
||||
std::string restricted_set_cfg = "unrestrictedSet"; // TODO read from config
|
||||
asn1::rrc_nr::rach_cfg_common_s::prach_root_seq_idx_c_::types_opts root_seq_idx_type;
|
||||
if (!asn1::string_to_enum(rach_cfg_common.restricted_set_cfg, restricted_set_cfg)) {
|
||||
ERROR("Config Error: Invalid restricted_set_cfg (%s)\n", restricted_set_cfg.c_str());
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
rach_cfg_common.ssb_per_rach_occasion_and_cb_preambs_per_ssb_present = true;
|
||||
rach_cfg_common.ssb_per_rach_occasion_and_cb_preambs_per_ssb.set_one(); // TODO read from config
|
||||
|
||||
uint8_t one_opts = 64; // TODO read from config
|
||||
if (!asn1::number_to_enum(rach_cfg_common.ssb_per_rach_occasion_and_cb_preambs_per_ssb.one(), one_opts)) {
|
||||
ERROR("Config Error: Invalid one_opts (%d)\n", one_opts);
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Use helper class to derive NR carrier parameters
|
||||
srsran::srsran_band_helper band_helper;
|
||||
|
||||
|
@ -1541,9 +1520,7 @@ int set_derived_args_nr(all_args_t* args_, rrc_nr_cfg_t* rrc_nr_cfg_, phy_cfg_t*
|
|||
}
|
||||
|
||||
// Create NR dedicated cell configuration from RRC configuration
|
||||
for (auto it = rrc_nr_cfg_->cell_list.begin(); it != rrc_nr_cfg_->cell_list.end(); ++it) {
|
||||
auto& cfg = *it;
|
||||
|
||||
for (auto& cfg : rrc_nr_cfg_->cell_list) {
|
||||
cfg.phy_cell.carrier.max_mimo_layers = args_->enb.nof_ports;
|
||||
|
||||
// NR cells have the same bandwidth as EUTRA cells, adjust PRB sizes
|
||||
|
@ -1562,12 +1539,6 @@ int set_derived_args_nr(all_args_t* args_, rrc_nr_cfg_t* rrc_nr_cfg_, phy_cfg_t*
|
|||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Derive cross-dependent cell params
|
||||
if (set_derived_nr_cell_params(rrc_nr_cfg_->is_standalone, cfg) != SRSRAN_SUCCESS) {
|
||||
ERROR("Failed to derive NR cell params.");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// phy_cell_cfg.root_seq_idx = cfg.root_seq_idx;
|
||||
|
||||
// PRACH
|
||||
|
@ -1576,7 +1547,16 @@ int set_derived_args_nr(all_args_t* args_, rrc_nr_cfg_t* rrc_nr_cfg_, phy_cfg_t*
|
|||
// PDSCH
|
||||
cfg.phy_cell.pdsch.rs_power = phy_cfg_->pdsch_cnfg.ref_sig_pwr;
|
||||
cfg.phy_cell.pdsch.p_b = phy_cfg_->pdsch_cnfg.p_b;
|
||||
}
|
||||
|
||||
// Derive cross-dependent cell params
|
||||
if (set_derived_nr_rrc_params(*rrc_nr_cfg_) != SRSRAN_SUCCESS) {
|
||||
ERROR("Failed to derive NR cell params.");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Update PHY with RRC cell configs
|
||||
for (auto& cfg : rrc_nr_cfg_->cell_list) {
|
||||
phy_cfg_->phy_cell_cfg_nr.push_back(cfg.phy_cell);
|
||||
}
|
||||
|
||||
|
|
|
@ -146,6 +146,8 @@ void parse_args(all_args_t* args, int argc, char* argv[])
|
|||
("pcap.nr_filename", bpo::value<string>(&args->nr_stack.mac.pcap.filename)->default_value("/tmp/enb_mac_nr.pcap"), "NR MAC layer capture filename")
|
||||
("pcap.s1ap_enable", bpo::value<bool>(&args->stack.s1ap_pcap.enable)->default_value(false), "Enable S1AP packet captures for wireshark")
|
||||
("pcap.s1ap_filename", bpo::value<string>(&args->stack.s1ap_pcap.filename)->default_value("/tmp/enb_s1ap.pcap"), "S1AP layer capture filename")
|
||||
("pcap.ngap_enable", bpo::value<bool>(&args->nr_stack.ngap_pcap.enable)->default_value(false), "Enable NGAP packet captures for wireshark")
|
||||
("pcap.ngap_filename", bpo::value<string>(&args->nr_stack.ngap_pcap.filename)->default_value("/tmp/enb_ngap.pcap"), "NGAP layer capture filename")
|
||||
("pcap.mac_net_enable", bpo::value<bool>(&args->stack.mac_pcap_net.enable)->default_value(false), "Enable MAC network captures")
|
||||
("pcap.bind_ip", bpo::value<string>(&args->stack.mac_pcap_net.bind_ip)->default_value("0.0.0.0"), "Bind IP address for MAC network trace")
|
||||
("pcap.bind_port", bpo::value<uint16_t>(&args->stack.mac_pcap_net.bind_port)->default_value(5687), "Bind port for MAC network trace")
|
||||
|
@ -345,6 +347,12 @@ void parse_args(all_args_t* args, int argc, char* argv[])
|
|||
if (!srsran::string_to_mnc(mnc, &args->stack.s1ap.mnc)) {
|
||||
cout << "Error parsing enb.mnc:" << mnc << " - must be a 2 or 3-digit string." << endl;
|
||||
}
|
||||
if (!srsran::string_to_mcc(mcc, &args->nr_stack.ngap.mcc)) {
|
||||
cout << "Error parsing enb.mcc:" << mcc << " - must be a 3-digit string." << endl;
|
||||
}
|
||||
if (!srsran::string_to_mnc(mnc, &args->nr_stack.ngap.mnc)) {
|
||||
cout << "Error parsing enb.mnc:" << mnc << " - must be a 2 or 3-digit string." << endl;
|
||||
}
|
||||
|
||||
if (args->stack.embms.enable) {
|
||||
if (args->stack.mac.sched.max_nof_ctrl_symbols == 3) {
|
||||
|
|
|
@ -18,5 +18,5 @@
|
|||
# and at http://www.gnu.org/licenses/.
|
||||
#
|
||||
|
||||
set(SOURCES ue_buffer_manager.cc)
|
||||
set(SOURCES base_ue_buffer_manager.cc)
|
||||
add_library(srsenb_mac_common STATIC ${SOURCES})
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include "srsenb/hdr/stack/mac/common/ue_buffer_manager.h"
|
||||
#include "srsenb/hdr/stack/mac/common/base_ue_buffer_manager.h"
|
||||
#include "srsran/adt/bounded_vector.h"
|
||||
#include "srsran/common/string_helpers.h"
|
||||
#include "srsran/srslog/bundled/fmt/format.h"
|
||||
|
@ -31,13 +31,14 @@ extern "C" {
|
|||
namespace srsenb {
|
||||
|
||||
template <bool isNR>
|
||||
ue_buffer_manager<isNR>::ue_buffer_manager(uint16_t rnti_, srslog::basic_logger& logger_) : logger(logger_), rnti(rnti_)
|
||||
base_ue_buffer_manager<isNR>::base_ue_buffer_manager(uint16_t rnti_, srslog::basic_logger& logger_) :
|
||||
logger(logger_), rnti(rnti_)
|
||||
{
|
||||
std::fill(lcg_bsr.begin(), lcg_bsr.end(), 0);
|
||||
}
|
||||
|
||||
template <bool isNR>
|
||||
void ue_buffer_manager<isNR>::config_lcids(srsran::const_span<mac_lc_ch_cfg_t> bearer_cfg_list)
|
||||
void base_ue_buffer_manager<isNR>::config_lcids(srsran::const_span<mac_lc_ch_cfg_t> bearer_cfg_list)
|
||||
{
|
||||
bool log_enabled = logger.info.enabled();
|
||||
srsran::bounded_vector<uint32_t, MAX_NOF_LCIDS> changed_list;
|
||||
|
@ -67,7 +68,7 @@ void ue_buffer_manager<isNR>::config_lcids(srsran::const_span<mac_lc_ch_cfg_t> b
|
|||
}
|
||||
|
||||
template <bool isNR>
|
||||
void ue_buffer_manager<isNR>::config_lcid(uint32_t lcid, const mac_lc_ch_cfg_t& bearer_cfg)
|
||||
void base_ue_buffer_manager<isNR>::config_lcid(uint32_t lcid, const mac_lc_ch_cfg_t& bearer_cfg)
|
||||
{
|
||||
bool cfg_changed = config_lcid_internal(lcid, bearer_cfg);
|
||||
if (cfg_changed) {
|
||||
|
@ -86,7 +87,7 @@ void ue_buffer_manager<isNR>::config_lcid(uint32_t lcid, const mac_lc_ch_cfg_t&
|
|||
* @return true if the lcid was updated with new parameters. False in case of case of error or no update.
|
||||
*/
|
||||
template <bool isNR>
|
||||
bool ue_buffer_manager<isNR>::config_lcid_internal(uint32_t lcid, const mac_lc_ch_cfg_t& bearer_cfg)
|
||||
bool base_ue_buffer_manager<isNR>::config_lcid_internal(uint32_t lcid, const mac_lc_ch_cfg_t& bearer_cfg)
|
||||
{
|
||||
if (not is_lcid_valid(lcid)) {
|
||||
logger.warning("SCHED: Configuring rnti=0x%x bearer with invalid lcid=%d", rnti, lcid);
|
||||
|
@ -114,7 +115,7 @@ bool ue_buffer_manager<isNR>::config_lcid_internal(uint32_t lcid, const mac_lc_c
|
|||
}
|
||||
|
||||
template <bool isNR>
|
||||
int ue_buffer_manager<isNR>::get_dl_tx_total() const
|
||||
int base_ue_buffer_manager<isNR>::get_dl_tx_total() const
|
||||
{
|
||||
int sum = 0;
|
||||
for (size_t lcid = 0; is_lcid_valid(lcid); ++lcid) {
|
||||
|
@ -124,7 +125,7 @@ int ue_buffer_manager<isNR>::get_dl_tx_total() const
|
|||
}
|
||||
|
||||
template <bool isNR>
|
||||
bool ue_buffer_manager<isNR>::is_lcg_active(uint32_t lcg) const
|
||||
bool base_ue_buffer_manager<isNR>::is_lcg_active(uint32_t lcg) const
|
||||
{
|
||||
if (lcg == 0) {
|
||||
return true;
|
||||
|
@ -138,13 +139,13 @@ bool ue_buffer_manager<isNR>::is_lcg_active(uint32_t lcg) const
|
|||
}
|
||||
|
||||
template <bool isNR>
|
||||
int ue_buffer_manager<isNR>::get_bsr(uint32_t lcg) const
|
||||
int base_ue_buffer_manager<isNR>::get_bsr(uint32_t lcg) const
|
||||
{
|
||||
return is_lcg_active(lcg) ? lcg_bsr[lcg] : 0;
|
||||
}
|
||||
|
||||
template <bool isNR>
|
||||
int ue_buffer_manager<isNR>::get_bsr() const
|
||||
int base_ue_buffer_manager<isNR>::get_bsr() const
|
||||
{
|
||||
uint32_t count = 0;
|
||||
for (uint32_t lcg = 0; is_lcg_valid(lcg); ++lcg) {
|
||||
|
@ -156,7 +157,7 @@ int ue_buffer_manager<isNR>::get_bsr() const
|
|||
}
|
||||
|
||||
template <bool isNR>
|
||||
int ue_buffer_manager<isNR>::ul_bsr(uint32_t lcg_id, uint32_t val)
|
||||
int base_ue_buffer_manager<isNR>::ul_bsr(uint32_t lcg_id, uint32_t val)
|
||||
{
|
||||
if (not is_lcg_valid(lcg_id)) {
|
||||
logger.warning("SCHED: The provided lcg_id=%d for rnti=0x%x is not valid", lcg_id, rnti);
|
||||
|
@ -167,7 +168,7 @@ int ue_buffer_manager<isNR>::ul_bsr(uint32_t lcg_id, uint32_t val)
|
|||
}
|
||||
|
||||
template <bool isNR>
|
||||
int ue_buffer_manager<isNR>::dl_buffer_state(uint8_t lcid, uint32_t tx_queue, uint32_t prio_tx_queue)
|
||||
int base_ue_buffer_manager<isNR>::dl_buffer_state(uint8_t lcid, uint32_t tx_queue, uint32_t prio_tx_queue)
|
||||
{
|
||||
if (not is_lcid_valid(lcid)) {
|
||||
logger.warning("The provided lcid=%d is not valid", lcid);
|
||||
|
@ -179,7 +180,7 @@ int ue_buffer_manager<isNR>::dl_buffer_state(uint8_t lcid, uint32_t tx_queue, ui
|
|||
}
|
||||
|
||||
// Explicit instantiation
|
||||
template class ue_buffer_manager<true>;
|
||||
template class ue_buffer_manager<false>;
|
||||
template class base_ue_buffer_manager<true>;
|
||||
template class base_ue_buffer_manager<false>;
|
||||
|
||||
} // namespace srsenb
|
|
@ -525,6 +525,21 @@ uint16_t mac::allocate_ue(uint32_t enb_cc_idx)
|
|||
return rnti;
|
||||
}
|
||||
|
||||
bool mac::is_pending_pdcch_order_prach(const uint32_t preamble_idx, uint16_t& rnti)
|
||||
{
|
||||
for (auto it = pending_po_prachs.begin(); it != pending_po_prachs.end();) {
|
||||
auto& pending_po_prach = *it;
|
||||
if (pending_po_prach.preamble_idx == preamble_idx) {
|
||||
rnti = pending_po_prach.crnti;
|
||||
// delete pending PDCCH PRACH from vector
|
||||
it = pending_po_prachs.erase(it);
|
||||
return true;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t mac::reserve_new_crnti(const sched_interface::ue_cfg_t& uecfg)
|
||||
{
|
||||
uint16_t rnti = allocate_ue(uecfg.supported_cc_list[0].enb_cc_idx);
|
||||
|
@ -546,9 +561,14 @@ void mac::rach_detected(uint32_t tti, uint32_t enb_cc_idx, uint32_t preamble_idx
|
|||
auto rach_tprof_meas = rach_tprof.start();
|
||||
|
||||
stack_task_queue.push([this, tti, enb_cc_idx, preamble_idx, time_adv, rach_tprof_meas]() mutable {
|
||||
uint16_t rnti = allocate_ue(enb_cc_idx);
|
||||
if (rnti == SRSRAN_INVALID_RNTI) {
|
||||
return;
|
||||
uint16_t rnti = 0;
|
||||
// check if this is a PRACH from a PDCCH order
|
||||
bool is_po_prach = is_pending_pdcch_order_prach(preamble_idx, rnti);
|
||||
if (!is_po_prach) {
|
||||
rnti = allocate_ue(enb_cc_idx);
|
||||
if (rnti == SRSRAN_INVALID_RNTI) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
rach_tprof_meas.defer_stop();
|
||||
|
@ -563,21 +583,24 @@ void mac::rach_detected(uint32_t tti, uint32_t enb_cc_idx, uint32_t preamble_idx
|
|||
// Log this event.
|
||||
++detected_rachs[enb_cc_idx];
|
||||
|
||||
// Add new user to the scheduler so that it can RX/TX SRB0
|
||||
sched_interface::ue_cfg_t uecfg = {};
|
||||
uecfg.supported_cc_list.emplace_back();
|
||||
uecfg.supported_cc_list.back().active = true;
|
||||
uecfg.supported_cc_list.back().enb_cc_idx = enb_cc_idx;
|
||||
uecfg.ue_bearers[0].direction = mac_lc_ch_cfg_t::BOTH;
|
||||
uecfg.supported_cc_list[0].dl_cfg.tm = SRSRAN_TM1;
|
||||
if (ue_cfg(rnti, &uecfg) != SRSRAN_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
// If this is a PRACH from a PDCCH order, the user already exists
|
||||
if (not is_po_prach) {
|
||||
// Add new user to the scheduler so that it can RX/TX SRB0
|
||||
sched_interface::ue_cfg_t uecfg = {};
|
||||
uecfg.supported_cc_list.emplace_back();
|
||||
uecfg.supported_cc_list.back().active = true;
|
||||
uecfg.supported_cc_list.back().enb_cc_idx = enb_cc_idx;
|
||||
uecfg.ue_bearers[0].direction = mac_lc_ch_cfg_t::BOTH;
|
||||
uecfg.supported_cc_list[0].dl_cfg.tm = SRSRAN_TM1;
|
||||
if (ue_cfg(rnti, &uecfg) != SRSRAN_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Register new user in RRC
|
||||
if (rrc_h->add_user(rnti, uecfg) == SRSRAN_ERROR) {
|
||||
ue_rem(rnti);
|
||||
return;
|
||||
// Register new user in RRC
|
||||
if (rrc_h->add_user(rnti, uecfg) == SRSRAN_ERROR) {
|
||||
ue_rem(rnti);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger scheduler RACH
|
||||
|
@ -588,14 +611,16 @@ void mac::rach_detected(uint32_t tti, uint32_t enb_cc_idx, uint32_t preamble_idx
|
|||
return (enb_cc_idx < cell_config.size()) ? cell_config[enb_cc_idx].cell.id : 0;
|
||||
};
|
||||
uint32_t pci = get_pci();
|
||||
logger.info("RACH: tti=%d, cc=%d, pci=%d, preamble=%d, offset=%d, temp_crnti=0x%x",
|
||||
logger.info("%sRACH: tti=%d, cc=%d, pci=%d, preamble=%d, offset=%d, temp_crnti=0x%x",
|
||||
(is_po_prach) ? "PDCCH order " : "",
|
||||
tti,
|
||||
enb_cc_idx,
|
||||
pci,
|
||||
preamble_idx,
|
||||
time_adv,
|
||||
rnti);
|
||||
srsran::console("RACH: tti=%d, cc=%d, pci=%d, preamble=%d, offset=%d, temp_crnti=0x%x\n",
|
||||
srsran::console("%sRACH: tti=%d, cc=%d, pci=%d, preamble=%d, offset=%d, temp_crnti=0x%x\n",
|
||||
(is_po_prach) ? "PDCCH order " : "",
|
||||
tti,
|
||||
enb_cc_idx,
|
||||
pci,
|
||||
|
@ -758,6 +783,21 @@ int mac::get_dl_sched(uint32_t tti_tx_dl, dl_sched_list_t& dl_sched_res_list)
|
|||
n++;
|
||||
}
|
||||
|
||||
// Copy PDCCH order grants
|
||||
for (uint32_t i = 0; i < sched_result.po.size(); i++) {
|
||||
// Copy dci info
|
||||
dl_sched_res->pdsch[n].dci = sched_result.po[i].dci;
|
||||
|
||||
if (pcap) {
|
||||
pcap->write_dl_pch(dl_sched_res->pdsch[n].data[0], sched_result.po[i].tbs, true, tti_tx_dl, enb_cc_idx);
|
||||
}
|
||||
if (pcap_net) {
|
||||
pcap_net->write_dl_pch(dl_sched_res->pdsch[n].data[0], sched_result.po[i].tbs, true, tti_tx_dl, enb_cc_idx);
|
||||
}
|
||||
|
||||
n++;
|
||||
}
|
||||
|
||||
dl_sched_res->nof_grants = n;
|
||||
|
||||
// Number of CCH symbols
|
||||
|
@ -838,7 +878,6 @@ int mac::get_mch_sched(uint32_t tti, bool is_mcch, dl_sched_list_t& dl_sched_res
|
|||
ue_db[SRSRAN_MRNTI]->metrics_tx(true, mcs.tbs);
|
||||
dl_sched_res->pdsch[0].data[0] =
|
||||
ue_db[SRSRAN_MRNTI]->generate_mch_pdu(tti % SRSRAN_FDD_NOF_HARQ, mch, mch.num_mtch_sched + 1, mcs.tbs / 8);
|
||||
|
||||
} else {
|
||||
uint32_t current_lcid = 1;
|
||||
uint32_t mtch_index = 0;
|
||||
|
@ -974,7 +1013,6 @@ int mac::get_ul_sched(uint32_t tti_tx_ul, ul_sched_list_t& ul_sched_res_list)
|
|||
} else {
|
||||
logger.warning("Invalid UL scheduling result. User 0x%x does not exist", rnti);
|
||||
}
|
||||
|
||||
} else {
|
||||
logger.warning("Grant %d for rnti=0x%x has zero TBS", i, sched_result.pusch[i].dci.rnti);
|
||||
}
|
||||
|
|
|
@ -301,6 +301,12 @@ std::array<int, SRSRAN_MAX_CARRIERS> sched::get_enb_ue_activ_cc_map(uint16_t rnt
|
|||
return ret;
|
||||
}
|
||||
|
||||
int sched::set_pdcch_order(uint32_t enb_cc_idx, dl_sched_po_info_t pdcch_order_info)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(sched_mutex);
|
||||
return carrier_schedulers[enb_cc_idx]->pdcch_order_info(pdcch_order_info);
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
*
|
||||
* Main sched functions
|
||||
|
|
|
@ -349,6 +349,7 @@ void sched::carrier_sched::reset()
|
|||
{
|
||||
ra_sched_ptr.reset();
|
||||
bc_sched_ptr.reset();
|
||||
pending_pdcch_orders.clear();
|
||||
}
|
||||
|
||||
void sched::carrier_sched::carrier_cfg(const sched_cell_params_t& cell_params_)
|
||||
|
@ -411,6 +412,9 @@ const cc_sched_result& sched::carrier_sched::generate_tti_result(tti_point tti_r
|
|||
/* Schedule Msg3 */
|
||||
sf_sched* sf_msg3_sched = get_sf_sched(tti_rx + MSG3_DELAY_MS);
|
||||
ra_sched_ptr->ul_sched(tti_sched, sf_msg3_sched);
|
||||
|
||||
/* Schedule PDCCH orders */
|
||||
pdcch_order_sched(tti_sched);
|
||||
}
|
||||
|
||||
/* Prioritize PDCCH scheduling for DL and UL data in a RoundRobin fashion */
|
||||
|
@ -490,4 +494,38 @@ int sched::carrier_sched::dl_rach_info(dl_sched_rar_info_t rar_info)
|
|||
return ra_sched_ptr->dl_rach_info(rar_info);
|
||||
}
|
||||
|
||||
int sched::carrier_sched::pdcch_order_info(dl_sched_po_info_t pdcch_order_info)
|
||||
{
|
||||
logger.info("SCHED: New PDCCH order preamble=%d, prach_mask_idx=%d crnti=0x%x",
|
||||
pdcch_order_info.preamble_idx,
|
||||
pdcch_order_info.prach_mask_idx,
|
||||
pdcch_order_info.crnti);
|
||||
|
||||
// create new PDCCH order
|
||||
pending_pdcch_orders.push_back(pdcch_order_info);
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
void sched::carrier_sched::pdcch_order_sched(sf_sched* tti_sched)
|
||||
{
|
||||
for (auto it = pending_pdcch_orders.begin(); it != pending_pdcch_orders.end();) {
|
||||
auto& pending_pdcch_order = *it;
|
||||
|
||||
alloc_result ret = alloc_result::no_sch_space;
|
||||
|
||||
rbg_interval rbg_interv = find_empty_rbg_interval(1, tti_sched->get_dl_mask());
|
||||
if (rbg_interv.length() == 1) {
|
||||
ret = tti_sched->alloc_pdcch_order(pending_pdcch_order, po_aggr_level, rbg_interv);
|
||||
}
|
||||
|
||||
if (ret == alloc_result::success) {
|
||||
it = pending_pdcch_orders.erase(it);
|
||||
} else {
|
||||
logger.warning("SCHED: Could not allocate PDCCH order, cause=%s", to_string(ret));
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace srsenb
|
||||
|
|
|
@ -168,7 +168,7 @@ alloc_result sf_grid_t::alloc_dl(uint32_t aggr_idx,
|
|||
alloc_result sf_grid_t::alloc_dl_ctrl(uint32_t aggr_idx, rbg_interval rbg_range, alloc_type_t alloc_type)
|
||||
{
|
||||
if (alloc_type != alloc_type_t::DL_RAR and alloc_type != alloc_type_t::DL_BC and
|
||||
alloc_type != alloc_type_t::DL_PCCH) {
|
||||
alloc_type != alloc_type_t::DL_PCCH and alloc_type != alloc_type_t::DL_PDCCH_ORDER) {
|
||||
logger.error("SCHED: DL control allocations must be RAR/BC/PDCCH");
|
||||
return alloc_result::other_cause;
|
||||
}
|
||||
|
@ -325,6 +325,7 @@ void sf_sched::new_tti(tti_point tti_rx_, sf_sched_result* cc_results_)
|
|||
// reset internal state
|
||||
bc_allocs.clear();
|
||||
rar_allocs.clear();
|
||||
po_allocs.clear();
|
||||
data_allocs.clear();
|
||||
ul_data_allocs.clear();
|
||||
|
||||
|
@ -452,6 +453,40 @@ alloc_result sf_sched::alloc_rar(uint32_t aggr_lvl, const pending_rar_t& rar, rb
|
|||
return ret;
|
||||
}
|
||||
|
||||
alloc_result
|
||||
sf_sched::alloc_pdcch_order(const sched_interface::dl_sched_po_info_t& po_cfg, uint32_t aggr_lvl, rbg_interval rbgs)
|
||||
{
|
||||
if (po_allocs.full()) {
|
||||
logger.warning("SCHED: Maximum number of PDCCH order allocations per TTI reached.");
|
||||
return alloc_result::no_grant_space;
|
||||
}
|
||||
|
||||
uint32_t buf_pdcch_order = 7; // TODO get actual size
|
||||
|
||||
// Allocate RBGs and PDCCH
|
||||
alloc_result ret = tti_alloc.alloc_dl_ctrl(aggr_lvl, rbgs, alloc_type_t::DL_PDCCH_ORDER);
|
||||
if (ret != alloc_result::success) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
po_alloc_t po_alloc;
|
||||
po_alloc.po_grant.crnti = po_cfg.crnti;
|
||||
po_alloc.po_grant.preamble_idx = po_cfg.preamble_idx;
|
||||
po_alloc.po_grant.prach_mask_idx = po_cfg.prach_mask_idx;
|
||||
po_alloc.po_grant.tbs = buf_pdcch_order;
|
||||
|
||||
// Generate DCI for PDCCH order message
|
||||
generate_pdcch_order_dci(po_alloc.po_grant, get_tti_tx_dl(), *cc_cfg, tti_alloc.get_cfi());
|
||||
|
||||
// Allocation Successful
|
||||
po_alloc.dci_idx = tti_alloc.get_pdcch_grid().nof_allocs() - 1;
|
||||
po_alloc.rbg_range = rbgs;
|
||||
po_alloc.req_bytes = buf_pdcch_order;
|
||||
po_allocs.push_back(po_alloc);
|
||||
|
||||
return alloc_result::success;
|
||||
}
|
||||
|
||||
bool is_periodic_cqi_expected(const sched_interface::ue_cfg_t& ue_cfg, tti_point tti_tx_ul)
|
||||
{
|
||||
for (const sched_interface::ue_cfg_t::cc_cfg_t& cc : ue_cfg.supported_cc_list) {
|
||||
|
@ -951,6 +986,12 @@ void sf_sched::generate_sched_results(sched_ue_list& ue_db)
|
|||
log_rar_allocation(cc_result->dl_sched_result.rar.back(), rar_alloc.alloc_data.rbg_range);
|
||||
}
|
||||
|
||||
for (const auto& po_alloc : po_allocs) {
|
||||
cc_result->dl_sched_result.po.emplace_back(po_alloc.po_grant);
|
||||
cc_result->dl_sched_result.po.back().dci.location = dci_result[po_alloc.dci_idx]->dci_pos;
|
||||
log_po_allocation(cc_result->dl_sched_result.po.back(), po_alloc.rbg_range, *cc_cfg);
|
||||
}
|
||||
|
||||
set_dl_data_sched_result(dci_result, &cc_result->dl_sched_result, ue_db);
|
||||
|
||||
set_ul_sched_result(dci_result, &cc_result->ul_sched_result, ue_db);
|
||||
|
|
|
@ -331,6 +331,22 @@ bool generate_rar_dci(sched_interface::dl_sched_rar_t& rar,
|
|||
return true;
|
||||
}
|
||||
|
||||
void generate_pdcch_order_dci(sched_interface::dl_sched_po_t& pdcch_order,
|
||||
tti_point tti_tx_dl,
|
||||
const sched_cell_params_t& cell_params,
|
||||
uint32_t current_cfi)
|
||||
{
|
||||
// Generate DCI Format1A PDCCH order content
|
||||
pdcch_order.dci.format = SRSRAN_DCI_FORMAT1A;
|
||||
pdcch_order.dci.alloc_type = SRSRAN_RA_ALLOC_TYPE2; // TODO: is this correct?
|
||||
pdcch_order.dci.rnti = pdcch_order.crnti;
|
||||
pdcch_order.dci.is_pdcch_order = true;
|
||||
pdcch_order.dci.preamble_idx = pdcch_order.preamble_idx;
|
||||
pdcch_order.dci.prach_mask_idx = pdcch_order.prach_mask_idx;
|
||||
|
||||
get_mac_logger().debug("PDCCH order: rnti=0x%x", pdcch_order.dci.rnti);
|
||||
}
|
||||
|
||||
void log_broadcast_allocation(const sched_interface::dl_sched_bc_t& bc,
|
||||
rbg_interval rbg_range,
|
||||
const sched_cell_params_t& cell_params)
|
||||
|
@ -393,4 +409,24 @@ void log_rar_allocation(const sched_interface::dl_sched_rar_t& rar, rbg_interval
|
|||
srsran::to_c_str(str_buffer2));
|
||||
}
|
||||
|
||||
void log_po_allocation(const sched_interface::dl_sched_po_t& pdcch_order,
|
||||
rbg_interval rbg_range,
|
||||
const sched_cell_params_t& cell_params)
|
||||
{
|
||||
if (not get_mac_logger().info.enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
fmt::memory_buffer str_buffer;
|
||||
fmt::format_to(str_buffer, "{}", rbg_range);
|
||||
|
||||
get_mac_logger().info("SCHED: PDCCH order, cc=%d, rbgs=%s, dci=(%d,%d), tbs=%d, mcs=%d",
|
||||
cell_params.enb_cc_idx,
|
||||
srsran::to_c_str(str_buffer),
|
||||
pdcch_order.dci.location.L,
|
||||
pdcch_order.dci.location.ncce,
|
||||
pdcch_order.tbs,
|
||||
pdcch_order.dci.tb[0].mcs_idx);
|
||||
}
|
||||
|
||||
} // namespace srsenb
|
||||
|
|
|
@ -61,6 +61,8 @@ sf_cch_allocator::get_cce_loc_table(alloc_type_t alloc_type, sched_ue* user, uin
|
|||
return &cc_cfg->common_locations[cfix];
|
||||
case alloc_type_t::DL_RAR:
|
||||
return &cc_cfg->rar_locations[to_tx_dl(tti_rx).sf_idx()][cfix];
|
||||
case alloc_type_t::DL_PDCCH_ORDER:
|
||||
return &cc_cfg->common_locations[cfix];
|
||||
case alloc_type_t::DL_DATA:
|
||||
case alloc_type_t::UL_DATA:
|
||||
return user->get_locations(cc_cfg->enb_cc_idx, cfix + 1, to_tx_dl(tti_rx).sf_idx());
|
||||
|
|
|
@ -748,8 +748,9 @@ rbg_interval sched_ue::get_required_dl_rbgs(uint32_t enb_cc_idx)
|
|||
int pending_prbs = get_required_prb_dl(cells[enb_cc_idx], to_tx_dl(current_tti), get_dci_format(), req_bytes.start());
|
||||
if (pending_prbs < 0) {
|
||||
// Cannot fit allocation in given PRBs
|
||||
logger.error("SCHED: DL CQI does now allow fitting %d non-segmentable DL tx bytes into the cell bandwidth. "
|
||||
logger.error("SCHED: DL CQI=%d does now allow fitting %d non-segmentable DL tx bytes into the cell bandwidth. "
|
||||
"Consider increasing initial CQI value.",
|
||||
cells[enb_cc_idx].get_dl_cqi(),
|
||||
req_bytes.start());
|
||||
return {cellparams->nof_prb(), cellparams->nof_prb()};
|
||||
}
|
||||
|
|
|
@ -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_PHY_NR_INTERFACES_H
|
||||
#define SRSRAN_PHY_NR_INTERFACES_H
|
||||
|
||||
#include "srsran/srsran.h"
|
||||
#include <vector>
|
||||
|
||||
namespace srsenb {
|
||||
|
||||
struct phy_cell_cfg_nr_t {
|
||||
srsran_carrier_nr_t carrier;
|
||||
uint32_t rf_port;
|
||||
uint32_t cell_id;
|
||||
double dl_freq_hz;
|
||||
double ul_freq_hz;
|
||||
uint32_t root_seq_idx;
|
||||
uint32_t num_ra_preambles;
|
||||
float gain_db;
|
||||
srsran_pdcch_cfg_nr_t pdcch = {}; ///< Common CORESET and Search Space configuration
|
||||
srsran_pdsch_cfg_t pdsch = {};
|
||||
srsran_prach_cfg_t prach = {};
|
||||
};
|
||||
|
||||
using phy_cell_cfg_list_nr_t = std::vector<phy_cell_cfg_nr_t>;
|
||||
|
||||
} // namespace srsenb
|
||||
|
||||
#endif // SRSRAN_PHY_NR_INTERFACES_H
|
|
@ -78,11 +78,18 @@ public:
|
|||
}
|
||||
uint16_t reserve_rnti(uint32_t enb_cc_idx, const sched_nr_ue_cfg_t& uecfg) override { return 0x4601; }
|
||||
|
||||
int ue_cfg(uint16_t rnti, const sched_nr_interface::ue_cfg_t& ue_cfg) override { return SRSRAN_SUCCESS; }
|
||||
int ue_cfg(uint16_t rnti, const sched_nr_interface::ue_cfg_t& ue_cfg) override
|
||||
{
|
||||
last_ue_cfg_rnti = rnti;
|
||||
last_ue_cfg = ue_cfg;
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
int remove_ue(uint16_t rnti) override { return SRSRAN_SUCCESS; }
|
||||
|
||||
std::vector<srsenb::sched_nr_interface::cell_cfg_t> nr_cells;
|
||||
uint16_t last_ue_cfg_rnti = SRSRAN_INVALID_RNTI;
|
||||
sched_nr_interface::ue_cfg_t last_ue_cfg{};
|
||||
};
|
||||
|
||||
class phy_nr_dummy : public phy_interface_stack_nr
|
||||
|
|
|
@ -36,6 +36,8 @@
|
|||
#include "srsenb/hdr/stack/enb_stack_base.h"
|
||||
#include "srsran/interfaces/gnb_interfaces.h"
|
||||
|
||||
#include "srsran/common/ngap_pcap.h"
|
||||
|
||||
namespace srsenb {
|
||||
|
||||
class ngap;
|
||||
|
@ -45,6 +47,7 @@ struct gnb_stack_args_t {
|
|||
stack_log_args_t log;
|
||||
mac_nr_args_t mac;
|
||||
ngap_args_t ngap;
|
||||
pcap_args_t ngap_pcap;
|
||||
};
|
||||
|
||||
class gnb_stack_nr final : public srsenb::enb_stack_base,
|
||||
|
@ -138,6 +141,8 @@ private:
|
|||
srslog::basic_logger& gtpu_logger;
|
||||
srslog::basic_logger& stack_logger;
|
||||
|
||||
srsran::ngap_pcap ngap_pcap;
|
||||
|
||||
// task scheduling
|
||||
static const int STACK_MAIN_THREAD_PRIO = 4;
|
||||
srsran::task_scheduler task_sched;
|
||||
|
|
|
@ -46,7 +46,10 @@ struct mac_nr_args_t {
|
|||
class sched_nr;
|
||||
class mac_nr_rx;
|
||||
|
||||
class mac_nr final : public mac_interface_phy_nr, public mac_interface_rrc_nr, public mac_interface_rlc_nr
|
||||
class mac_nr final : public mac_interface_phy_nr,
|
||||
public mac_interface_rrc_nr,
|
||||
public mac_interface_rlc_nr,
|
||||
public mac_interface_pdu_demux_nr
|
||||
{
|
||||
public:
|
||||
explicit mac_nr(srsran::task_sched_handle task_sched_);
|
||||
|
@ -80,6 +83,9 @@ public:
|
|||
int pusch_info(const srsran_slot_cfg_t& slot_cfg, pusch_info_t& pusch_info) override;
|
||||
void rach_detected(const rach_info_t& rach_info) override;
|
||||
|
||||
// MAC-internal interface
|
||||
void store_msg3(uint16_t rnti, srsran::unique_byte_buffer_t pdu) override;
|
||||
|
||||
// Test interface
|
||||
void ul_bsr(uint16_t rnti, uint32_t lcid, uint32_t bsr);
|
||||
|
||||
|
@ -111,10 +117,13 @@ private:
|
|||
// args
|
||||
srsran::task_sched_handle task_sched;
|
||||
srsran::task_queue_handle stack_task_queue;
|
||||
mac_nr_args_t args = {};
|
||||
srslog::basic_logger& logger;
|
||||
|
||||
// initial UE config, before RRC setup (without UE-dedicated)
|
||||
srsran::phy_cfg_nr_t default_ue_phy_cfg;
|
||||
|
||||
std::unique_ptr<srsran::mac_pcap> pcap = nullptr;
|
||||
mac_nr_args_t args = {};
|
||||
srslog::basic_logger& logger;
|
||||
|
||||
std::atomic<bool> started = {false};
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#ifndef SRSRAN_SCHED_NR_CFG_H
|
||||
#define SRSRAN_SCHED_NR_CFG_H
|
||||
|
||||
#include "sched_nr_interface.h"
|
||||
#include "sched_nr_interface_utils.h"
|
||||
#include "sched_nr_rb.h"
|
||||
#include "srsenb/hdr/common/common_enb.h"
|
||||
#include "srsran/adt/optional_array.h"
|
||||
|
@ -155,6 +155,7 @@ public:
|
|||
{
|
||||
return cce_positions_list[ss_id_to_cce_idx[search_id]];
|
||||
}
|
||||
|
||||
uint32_t get_k1(slot_point pdsch_slot) const
|
||||
{
|
||||
if (phy().duplex.mode == SRSRAN_DUPLEX_MODE_TDD) {
|
||||
|
@ -174,7 +175,6 @@ private:
|
|||
};
|
||||
|
||||
} // namespace sched_nr_impl
|
||||
|
||||
} // namespace srsenb
|
||||
|
||||
#endif // SRSRAN_SCHED_NR_CFG_H
|
||||
|
|
|
@ -108,8 +108,8 @@ public:
|
|||
uint32_t aggr_idx,
|
||||
prb_interval interv,
|
||||
srsran::const_span<dl_sched_rar_info_t> pending_rars);
|
||||
alloc_result alloc_pdsch(slot_ue& ue, const prb_grant& dl_grant);
|
||||
alloc_result alloc_pusch(slot_ue& ue, const prb_grant& dl_mask);
|
||||
alloc_result alloc_pdsch(slot_ue& ue, prb_grant dl_grant);
|
||||
alloc_result alloc_pusch(slot_ue& ue, prb_grant dl_mask);
|
||||
|
||||
slot_point get_pdcch_tti() const { return pdcch_slot; }
|
||||
slot_point get_tti_rx() const { return pdcch_slot - TX_ENB_DELAY; }
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include "sched_nr_cfg.h"
|
||||
#include "sched_nr_ue.h"
|
||||
#include "srsran/adt/optional_array.h"
|
||||
|
||||
namespace srsenb {
|
||||
namespace sched_nr_impl {
|
||||
|
@ -32,7 +33,11 @@ class slot_ue;
|
|||
class ul_harq_proc;
|
||||
struct bwp_res_grid;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// In case of Common SearchSpace, not all PRBs might be available
|
||||
void reduce_to_dl_coreset_bw(const bwp_params_t& bwp_cfg,
|
||||
uint32_t ss_id,
|
||||
srsran_dci_format_nr_t dci_fmt,
|
||||
prb_grant& grant);
|
||||
|
||||
bool fill_dci_sib(prb_interval interv,
|
||||
uint32_t sib_idx,
|
||||
|
|
|
@ -58,19 +58,23 @@ struct sched_nr_ue_cfg_t {
|
|||
class sched_nr_interface
|
||||
{
|
||||
public:
|
||||
static const size_t MAX_GRANTS = mac_interface_phy_nr::MAX_GRANTS;
|
||||
static const size_t MAX_SIBS = 2;
|
||||
static const size_t MAX_GRANTS = mac_interface_phy_nr::MAX_GRANTS;
|
||||
static const size_t MAX_SIBS = 2;
|
||||
static const size_t MAX_SUBPDUS = 8;
|
||||
|
||||
///// Configuration /////
|
||||
|
||||
struct bwp_cfg_t {
|
||||
uint32_t start_rb = 0;
|
||||
uint32_t rb_width = 100;
|
||||
srsran_pdcch_cfg_nr_t pdcch = {};
|
||||
srsran_sch_hl_cfg_nr_t pdsch = {};
|
||||
srsran_sch_hl_cfg_nr_t pusch = {};
|
||||
uint32_t rar_window_size = 10; // See TS 38.331, ra-ResponseWindow: {1, 2, 4, 8, 10, 20, 40, 80}
|
||||
uint32_t numerology_idx = 0;
|
||||
uint32_t start_rb = 0;
|
||||
uint32_t rb_width = 100;
|
||||
srsran_pdcch_cfg_nr_t pdcch = {};
|
||||
srsran_sch_hl_cfg_nr_t pdsch = {};
|
||||
srsran_sch_hl_cfg_nr_t pusch = {};
|
||||
srsran_pucch_nr_hl_cfg_t pucch = {};
|
||||
srsran_prach_cfg_t prach = {};
|
||||
srsran_harq_ack_cfg_hl_t harq_ack = {};
|
||||
uint32_t rar_window_size = 10; // See TS 38.331, ra-ResponseWindow: {1, 2, 4, 8, 10, 20, 40, 80}
|
||||
uint32_t numerology_idx = 0;
|
||||
};
|
||||
|
||||
struct cell_cfg_sib_t {
|
||||
|
@ -119,17 +123,25 @@ public:
|
|||
srsran::bounded_vector<msg3_grant_t, MAX_GRANTS> grants;
|
||||
};
|
||||
|
||||
////// DL data signalling //////
|
||||
|
||||
struct dl_pdu_t {
|
||||
srsran::bounded_vector<uint32_t, MAX_SUBPDUS> subpdus;
|
||||
};
|
||||
|
||||
///// Sched Result /////
|
||||
|
||||
using dl_sched_t = mac_interface_phy_nr::dl_sched_t;
|
||||
using ul_res_t = mac_interface_phy_nr::ul_sched_t;
|
||||
|
||||
using sched_rar_list_t = srsran::bounded_vector<rar_t, MAX_GRANTS>;
|
||||
using sched_sib_list_t = srsran::bounded_vector<uint32_t, MAX_GRANTS>; /// list of SI indexes
|
||||
using sched_rar_list_t = srsran::bounded_vector<rar_t, MAX_GRANTS>;
|
||||
using sched_dl_pdu_list_t = srsran::bounded_vector<dl_pdu_t, MAX_GRANTS>;
|
||||
struct dl_res_t {
|
||||
dl_sched_t phy;
|
||||
sched_rar_list_t rar;
|
||||
|
||||
srsran::bounded_vector<uint32_t, MAX_GRANTS> sib_idxs;
|
||||
dl_sched_t phy;
|
||||
sched_dl_pdu_list_t data;
|
||||
sched_rar_list_t rar;
|
||||
sched_sib_list_t sib_idxs;
|
||||
};
|
||||
|
||||
virtual ~sched_nr_interface() = default;
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
*
|
||||
* \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_INTERFACE_HELPERS_H
|
||||
#define SRSRAN_SCHED_NR_INTERFACE_HELPERS_H
|
||||
|
||||
#include "sched_nr_interface.h"
|
||||
#include "srsran/adt/optional_array.h"
|
||||
|
||||
namespace srsenb {
|
||||
|
||||
// Helpers to handle PHY struct types
|
||||
|
||||
/// Get a range of active search spaces in a PDCCH configuration
|
||||
inline srsran::split_optional_span<srsran_search_space_t> view_active_search_spaces(srsran_pdcch_cfg_nr_t& pdcch)
|
||||
{
|
||||
return srsran::split_optional_span<srsran_search_space_t>{pdcch.search_space, pdcch.search_space_present};
|
||||
}
|
||||
inline srsran::split_optional_span<const srsran_search_space_t>
|
||||
view_active_search_spaces(const srsran_pdcch_cfg_nr_t& pdcch)
|
||||
{
|
||||
return srsran::split_optional_span<const srsran_search_space_t>{pdcch.search_space, pdcch.search_space_present};
|
||||
}
|
||||
|
||||
srsran::phy_cfg_nr_t get_common_ue_phy_cfg(const sched_nr_interface::cell_cfg_t& cfg);
|
||||
|
||||
} // namespace srsenb
|
||||
|
||||
#endif // SRSRAN_SCHED_NR_INTERFACE_HELPERS_H
|
|
@ -115,6 +115,16 @@ struct prb_grant {
|
|||
return alloc.interv;
|
||||
}
|
||||
|
||||
prb_grant& operator&=(const prb_interval interv)
|
||||
{
|
||||
if (is_alloc_type0()) {
|
||||
alloc.rbgs &= rbg_bitmap{alloc.rbgs.size()}.fill(interv.start(), interv.stop());
|
||||
} else {
|
||||
alloc.interv.intersect(interv);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
bool alloc_type_0 = false;
|
||||
union alloc_t {
|
||||
|
|
|
@ -25,8 +25,8 @@
|
|||
#include "sched_nr_cfg.h"
|
||||
#include "sched_nr_harq.h"
|
||||
#include "sched_nr_interface.h"
|
||||
#include "srsenb/hdr/stack/mac/common/base_ue_buffer_manager.h"
|
||||
#include "srsenb/hdr/stack/mac/common/mac_metrics.h"
|
||||
#include "srsenb/hdr/stack/mac/common/ue_buffer_manager.h"
|
||||
#include "srsran/adt/circular_map.h"
|
||||
#include "srsran/adt/move_callback.h"
|
||||
#include "srsran/adt/pool/cached_alloc.h"
|
||||
|
@ -35,12 +35,60 @@ namespace srsenb {
|
|||
|
||||
namespace sched_nr_impl {
|
||||
|
||||
class ue_buffer_manager : public base_ue_buffer_manager<true>
|
||||
{
|
||||
using base_type = base_ue_buffer_manager<true>;
|
||||
|
||||
public:
|
||||
// Inherited methods from base_ue_buffer_manager base class
|
||||
using base_type::base_type;
|
||||
using base_type::config_lcid;
|
||||
using base_type::dl_buffer_state;
|
||||
using base_type::get_bsr;
|
||||
using base_type::get_bsr_state;
|
||||
using base_type::get_dl_prio_tx;
|
||||
using base_type::get_dl_tx;
|
||||
using base_type::get_dl_tx_total;
|
||||
using base_type::is_bearer_active;
|
||||
using base_type::is_bearer_dl;
|
||||
using base_type::is_bearer_ul;
|
||||
using base_type::is_lcg_active;
|
||||
using base_type::ul_bsr;
|
||||
|
||||
int get_dl_tx_total() const;
|
||||
|
||||
// Control Element Command queue
|
||||
struct ce_t {
|
||||
uint32_t lcid;
|
||||
uint32_t cc;
|
||||
};
|
||||
srsran::deque<ce_t> pending_ces;
|
||||
|
||||
/// Protected, thread-safe interface of "ue_buffer_manager" for "slot_ue"
|
||||
struct pdu_builder {
|
||||
pdu_builder() = default;
|
||||
explicit pdu_builder(uint32_t cc_, ue_buffer_manager& parent_) : cc(cc_), parent(&parent_) {}
|
||||
void alloc_subpdus(uint32_t rem_bytes, sched_nr_interface::dl_pdu_t& pdu);
|
||||
|
||||
private:
|
||||
uint32_t cc = SRSRAN_MAX_CARRIERS;
|
||||
ue_buffer_manager* parent = nullptr;
|
||||
};
|
||||
|
||||
private:
|
||||
/// Update of buffers is mutexed when carrier aggreg. is in place
|
||||
std::mutex mutex;
|
||||
};
|
||||
|
||||
class slot_ue;
|
||||
|
||||
class ue_carrier
|
||||
{
|
||||
public:
|
||||
ue_carrier(uint16_t rnti, const ue_cfg_t& cfg, const cell_params_t& cell_params_);
|
||||
ue_carrier(uint16_t rnti,
|
||||
const ue_cfg_t& cfg,
|
||||
const cell_params_t& cell_params_,
|
||||
const ue_buffer_manager::pdu_builder& pdu_builder_);
|
||||
|
||||
void set_cfg(const ue_cfg_t& ue_cfg);
|
||||
const ue_carrier_params_t& cfg() const { return bwp_cfg; }
|
||||
|
@ -58,6 +106,8 @@ public:
|
|||
|
||||
harq_entity harq_ent;
|
||||
|
||||
ue_buffer_manager::pdu_builder pdu_builder;
|
||||
|
||||
// metrics
|
||||
mac_ue_metrics_t metrics = {};
|
||||
|
||||
|
@ -77,11 +127,14 @@ public:
|
|||
|
||||
slot_ue make_slot_ue(slot_point pdcch_slot, uint32_t cc);
|
||||
|
||||
/// Update UE CC configuration
|
||||
void set_cfg(const ue_cfg_t& cfg);
|
||||
const ue_cfg_t& cfg() const { return ue_cfg; }
|
||||
|
||||
void mac_buffer_state(uint32_t ce_lcid, uint32_t nof_cmds = 1);
|
||||
void rlc_buffer_state(uint32_t lcid, uint32_t newtx, uint32_t retx);
|
||||
|
||||
/// UE state feedback
|
||||
void rlc_buffer_state(uint32_t lcid, uint32_t newtx, uint32_t retx) { buffers.dl_buffer_state(lcid, newtx, retx); }
|
||||
void ul_bsr(uint32_t lcg, uint32_t bsr_val) { buffers.ul_bsr(lcg, bsr_val); }
|
||||
void ul_sr_info() { last_sr_slot = last_pdcch_slot - TX_ENB_DELAY; }
|
||||
|
||||
|
@ -93,7 +146,6 @@ public:
|
|||
}
|
||||
uint32_t pcell_cc() const { return ue_cfg.carriers[0].cc; }
|
||||
|
||||
ue_buffer_manager<true> buffers;
|
||||
std::array<std::unique_ptr<ue_carrier>, SCHED_NR_MAX_CARRIERS> carriers;
|
||||
|
||||
const uint16_t rnti;
|
||||
|
@ -101,11 +153,13 @@ public:
|
|||
private:
|
||||
const sched_params_t& sched_cfg;
|
||||
|
||||
ue_cfg_t ue_cfg;
|
||||
|
||||
slot_point last_pdcch_slot;
|
||||
slot_point last_sr_slot;
|
||||
int ul_pending_bytes = 0, dl_pending_bytes = 0;
|
||||
|
||||
ue_cfg_t ue_cfg;
|
||||
ue_buffer_manager buffers;
|
||||
};
|
||||
|
||||
class slot_ue
|
||||
|
@ -126,6 +180,11 @@ public:
|
|||
dl_harq_proc* find_empty_dl_harq() { return ue->harq_ent.find_empty_dl_harq(); }
|
||||
ul_harq_proc* find_empty_ul_harq() { return ue->harq_ent.find_empty_ul_harq(); }
|
||||
|
||||
void build_pdu(uint32_t rem_bytes, sched_nr_interface::dl_pdu_t& pdu)
|
||||
{
|
||||
ue->pdu_builder.alloc_subpdus(rem_bytes, pdu);
|
||||
}
|
||||
|
||||
// UE parameters common to all sectors
|
||||
uint32_t dl_bytes = 0, ul_bytes = 0;
|
||||
|
||||
|
|
|
@ -57,8 +57,9 @@ public:
|
|||
uint16_t get_rnti() const { return rnti; }
|
||||
void set_active(bool active) { active_state.store(active, std::memory_order_relaxed); }
|
||||
bool is_active() const { return active_state.load(std::memory_order_relaxed); }
|
||||
void store_msg3(srsran::unique_byte_buffer_t pdu);
|
||||
|
||||
int generate_pdu(srsran::byte_buffer_t* pdu, uint32_t grant_size);
|
||||
int generate_pdu(srsran::byte_buffer_t* pdu, uint32_t grant_size, srsran::const_span<uint32_t> subpdu_lcids);
|
||||
|
||||
std::mutex metrics_mutex = {};
|
||||
void metrics_read(mac_ue_metrics_t* metrics_);
|
||||
|
@ -107,6 +108,8 @@ private:
|
|||
ue_rx_pdu_queue; ///< currently only DCH PDUs supported (add BCH, PCH, etc)
|
||||
srsran::unique_byte_buffer_t ue_rlc_buffer;
|
||||
|
||||
srsran::unique_byte_buffer_t last_msg3; ///< holds UE ID received in Msg3 for ConRes CE
|
||||
|
||||
static constexpr int32_t MIN_RLC_PDU_LEN =
|
||||
5; ///< minimum bytes that need to be available in a MAC PDU for attempting to add another RLC SDU
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "srsran/common/buffer_pool.h"
|
||||
#include "srsran/common/common.h"
|
||||
#include "srsran/common/network_utils.h"
|
||||
#include "srsran/common/ngap_pcap.h"
|
||||
#include "srsran/common/stack_procedure.h"
|
||||
#include "srsran/common/standard_streams.h"
|
||||
#include "srsran/common/task_scheduler.h"
|
||||
|
@ -79,10 +80,13 @@ public:
|
|||
|
||||
// Stack interface
|
||||
bool
|
||||
handle_amf_rx_msg(srsran::unique_byte_buffer_t pdu, const sockaddr_in& from, const sctp_sndrcvinfo& sri, int flags);
|
||||
handle_amf_rx_msg(srsran::unique_byte_buffer_t pdu, const sockaddr_in& from, const sctp_sndrcvinfo& sri, int flags);
|
||||
void get_metrics(ngap_metrics_t& m);
|
||||
void get_args(ngap_args_t& args_);
|
||||
|
||||
// PCAP
|
||||
void start_pcap(srsran::ngap_pcap* pcap_);
|
||||
|
||||
private:
|
||||
static const int AMF_PORT = 38412;
|
||||
static const int ADDR_FAMILY = AF_INET;
|
||||
|
@ -137,6 +141,9 @@ private:
|
|||
// TS 38.413 - Section 9.2.1.1 - PDU Session Resource Setup Request
|
||||
bool handle_ue_pdu_session_res_setup_request(const asn1::ngap_nr::pdu_session_res_setup_request_s& msg);
|
||||
|
||||
// PCAP
|
||||
srsran::ngap_pcap* pcap = nullptr;
|
||||
|
||||
class user_list
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -30,6 +30,9 @@ namespace srsenb {
|
|||
|
||||
using rlc_bearer_list_t = asn1::rrc_nr::cell_group_cfg_s::rlc_bearer_to_add_mod_list_l_;
|
||||
|
||||
// PHY helpers
|
||||
void set_search_space_from_phy_cfg(const srsran_search_space_t& ss, asn1::rrc_nr::search_space_s& out);
|
||||
|
||||
// NSA helpers
|
||||
int fill_sp_cell_cfg_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, asn1::rrc_nr::sp_cell_cfg_s& sp_cell);
|
||||
|
||||
|
|
|
@ -103,7 +103,7 @@ public:
|
|||
int ue_set_security_cfg_key(uint16_t rnti, const asn1::fixed_bitstring<256, false, true>& key) final;
|
||||
int ue_set_bitrates(uint16_t rnti, const asn1::ngap_nr::ue_aggregate_maximum_bit_rate_s& rates) final;
|
||||
int ue_set_security_cfg_capabilities(uint16_t rnti, const asn1::ngap_nr::ue_security_cap_s& caps) final;
|
||||
int start_security_mode_procedure(uint16_t rnti) final;
|
||||
int start_security_mode_procedure(uint16_t rnti, srsran::unique_byte_buffer_t nas_pdu) final;
|
||||
int establish_rrc_bearer(uint16_t rnti,
|
||||
uint16_t pdu_session_id,
|
||||
srsran::const_byte_span nas_pdu,
|
||||
|
@ -171,12 +171,28 @@ private:
|
|||
|
||||
// Helper to create PDU from RRC message
|
||||
template <class T>
|
||||
srsran::unique_byte_buffer_t pack_into_pdu(const T& msg);
|
||||
void log_rx_pdu_fail(uint16_t rnti,
|
||||
uint32_t lcid,
|
||||
srsran::const_byte_span pdu,
|
||||
const char* cause_str,
|
||||
bool log_hex = true);
|
||||
srsran::unique_byte_buffer_t pack_into_pdu(const T& msg, const char* context_name = nullptr)
|
||||
{
|
||||
context_name = context_name == nullptr ? __FUNCTION__ : context_name;
|
||||
// Allocate a new PDU buffer and pack the
|
||||
srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer();
|
||||
if (pdu == nullptr) {
|
||||
logger.error("Couldn't allocate PDU in %s.", context_name);
|
||||
return nullptr;
|
||||
}
|
||||
asn1::bit_ref bref(pdu->msg, pdu->get_tailroom());
|
||||
if (msg.pack(bref) == asn1::SRSASN_ERROR_ENCODE_FAIL) {
|
||||
logger.error("Failed to pack message in %s. Discarding it.", context_name);
|
||||
return nullptr;
|
||||
}
|
||||
pdu->N_bytes = bref.distance_bytes();
|
||||
return pdu;
|
||||
}
|
||||
void log_rx_pdu_fail(uint16_t rnti,
|
||||
uint32_t lcid,
|
||||
srsran::const_byte_span pdu,
|
||||
const char* cause_str,
|
||||
bool log_hex = true);
|
||||
};
|
||||
|
||||
} // namespace srsenb
|
||||
|
|
|
@ -22,11 +22,11 @@
|
|||
#ifndef SRSRAN_RRC_NR_CONFIG_H
|
||||
#define SRSRAN_RRC_NR_CONFIG_H
|
||||
|
||||
#include "srsenb/hdr/phy/phy_interfaces.h"
|
||||
#include "srsenb/hdr/stack/rrc/rrc_config_common.h"
|
||||
#include "srsgnb/hdr/phy/phy_nr_interfaces.h"
|
||||
#include "srsran/asn1/rrc_nr.h"
|
||||
#include "srsran/common/security.h"
|
||||
#include "srsran/interfaces/gnb_rrc_nr_interfaces.h"
|
||||
#include "srsue/hdr/phy/phy_common.h"
|
||||
|
||||
namespace srsenb {
|
||||
|
||||
|
@ -57,12 +57,13 @@ struct rrc_cell_cfg_nr_t {
|
|||
typedef std::vector<rrc_cell_cfg_nr_t> rrc_cell_list_nr_t;
|
||||
|
||||
struct rrc_nr_cfg_t {
|
||||
rrc_nr_cfg_sr_t sr_cfg;
|
||||
rrc_cfg_cqi_t cqi_cfg;
|
||||
rrc_cell_list_nr_t cell_list;
|
||||
asn1::rrc_nr::rach_cfg_common_s rach_cfg_common;
|
||||
uint16_t prach_root_seq_idx_type;
|
||||
bool is_standalone;
|
||||
rrc_nr_cfg_sr_t sr_cfg;
|
||||
rrc_cfg_cqi_t cqi_cfg;
|
||||
rrc_cell_list_nr_t cell_list;
|
||||
bool is_standalone;
|
||||
|
||||
std::array<srsran::CIPHERING_ALGORITHM_ID_NR_ENUM, srsran::CIPHERING_ALGORITHM_ID_NR_N_ITEMS> nea_preference_list;
|
||||
std::array<srsran::INTEGRITY_ALGORITHM_ID_NR_ENUM, srsran::INTEGRITY_ALGORITHM_ID_NR_N_ITEMS> nia_preference_list;
|
||||
|
||||
std::string log_name = "RRC-NR";
|
||||
std::string log_level;
|
||||
|
|
|
@ -29,8 +29,14 @@ namespace srsenb {
|
|||
void generate_default_nr_cell(rrc_cell_cfg_nr_t& cell);
|
||||
|
||||
int set_derived_nr_cell_params(bool is_sa, rrc_cell_cfg_nr_t& cell);
|
||||
int set_derived_nr_rrc_params(rrc_nr_cfg_t& rrc_cfg);
|
||||
|
||||
// Tests to ensure validity of config
|
||||
|
||||
int check_nr_cell_cfg_valid(const rrc_cell_cfg_nr_t& cell, bool is_sa);
|
||||
int check_nr_phy_cell_cfg_valid(const phy_cell_cfg_nr_t& phy_cell);
|
||||
int check_nr_pdcch_cfg_valid(const srsran_pdcch_cfg_nr_t& pdcch);
|
||||
int check_rrc_nr_cfg_valid(const rrc_nr_cfg_t& cfg);
|
||||
|
||||
} // namespace srsenb
|
||||
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
*
|
||||
* \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_RRC_NR_SECURITY_CONTEXT_H
|
||||
#define SRSRAN_RRC_NR_SECURITY_CONTEXT_H
|
||||
|
||||
#include "srsgnb/hdr/stack/rrc/rrc_nr_config.h"
|
||||
#include "srsran/asn1/ngap.h"
|
||||
#include "srsran/interfaces/gnb_interfaces.h"
|
||||
#include "srsran/srslog/srslog.h"
|
||||
|
||||
namespace srsgnb {
|
||||
|
||||
class nr_security_context
|
||||
{
|
||||
public:
|
||||
explicit nr_security_context(const srsenb::rrc_nr_cfg_t& cfg_) :
|
||||
cfg(cfg_), logger(srslog::fetch_basic_logger("RRC_NR"))
|
||||
{}
|
||||
|
||||
nr_security_context(const nr_security_context& other) : cfg(other.cfg), logger(srslog::fetch_basic_logger("RRC_NR"))
|
||||
{
|
||||
k_gnb_present = other.k_gnb_present;
|
||||
security_capabilities = other.security_capabilities;
|
||||
std::copy(other.k_gnb, other.k_gnb + 32, k_gnb);
|
||||
sec_cfg = other.sec_cfg;
|
||||
ncc = other.ncc;
|
||||
}
|
||||
|
||||
nr_security_context& operator=(const nr_security_context& other)
|
||||
{
|
||||
k_gnb_present = other.k_gnb_present;
|
||||
security_capabilities = other.security_capabilities;
|
||||
std::copy(other.k_gnb, other.k_gnb + 32, k_gnb);
|
||||
sec_cfg = other.sec_cfg;
|
||||
ncc = other.ncc;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool set_security_capabilities(const asn1::ngap_nr::ue_security_cap_s& caps);
|
||||
void set_security_key(const asn1::fixed_bitstring<256, false, true>& key);
|
||||
void set_ncc(uint8_t ncc_) { ncc = ncc_; }
|
||||
|
||||
asn1::rrc_nr::security_algorithm_cfg_s get_security_algorithm_cfg() const;
|
||||
const srsran::nr_as_security_config_t& get_as_sec_cfg() const { return sec_cfg; }
|
||||
uint8_t get_ncc() const { return ncc; }
|
||||
bool is_as_sec_cfg_valid() const { return k_gnb_present; }
|
||||
|
||||
void regenerate_keys_handover(uint32_t new_pci, uint32_t new_dl_earfcn);
|
||||
|
||||
private:
|
||||
void generate_as_keys();
|
||||
|
||||
srslog::basic_logger& logger;
|
||||
const srsenb::rrc_nr_cfg_t& cfg;
|
||||
bool k_gnb_present = false;
|
||||
asn1::ngap_nr::ue_security_cap_s security_capabilities = {};
|
||||
uint8_t k_gnb[32] = {}; // Provided by MME
|
||||
srsran::nr_as_security_config_t sec_cfg = {};
|
||||
uint8_t ncc = 0;
|
||||
};
|
||||
} // namespace srsgnb
|
||||
#endif
|
|
@ -23,6 +23,7 @@
|
|||
#define SRSRAN_RRC_NR_UE_H
|
||||
|
||||
#include "rrc_nr.h"
|
||||
#include "rrc_nr_security_context.h"
|
||||
|
||||
namespace srsenb {
|
||||
|
||||
|
@ -52,6 +53,11 @@ public:
|
|||
void get_metrics(rrc_ue_metrics_t& ue_metrics) { ue_metrics = {}; /*TODO fill RRC metrics*/ };
|
||||
|
||||
// setters
|
||||
void set_security_key(const asn1::fixed_bitstring<256, false, true>& key) { sec_ctx.set_security_key(key); }
|
||||
void set_security_capabilities(const asn1::ngap_nr::ue_security_cap_s& caps)
|
||||
{
|
||||
sec_ctx.set_security_capabilities(caps);
|
||||
}
|
||||
|
||||
void deactivate_bearers();
|
||||
|
||||
|
@ -61,39 +67,52 @@ public:
|
|||
void set_activity(bool enabled = true);
|
||||
void activity_timer_expired(const activity_timeout_type_t type);
|
||||
|
||||
/* TS 38.331 - 5.3.3 RRC connection establishment */
|
||||
/** TS 38.331 - 5.3.3 RRC connection establishment */
|
||||
void handle_rrc_setup_request(const asn1::rrc_nr::rrc_setup_request_s& msg);
|
||||
void handle_rrc_setup_complete(const asn1::rrc_nr::rrc_setup_complete_s& msg);
|
||||
|
||||
/* TS 38.331 - 5.3.4 Initial AS security activation */
|
||||
/** TS 38.331 - 5.3.4 Initial AS security activation */
|
||||
void handle_security_mode_complete(const asn1::rrc_nr::security_mode_complete_s& msg);
|
||||
|
||||
/* TS 38.331 - 5.3.5 RRC reconfiguration */
|
||||
/** TS 38.331 - 5.3.5 RRC reconfiguration */
|
||||
void handle_rrc_reconfiguration_complete(const asn1::rrc_nr::rrc_recfg_complete_s& msg);
|
||||
|
||||
/* TS 38.331 - 5.7.1 DL information transfer */
|
||||
/** TS 38.331 - 5.3.8 Connection Release */
|
||||
void send_rrc_release();
|
||||
|
||||
/** TS 38.331 - 5.7.1 DL information transfer */
|
||||
void send_dl_information_transfer(srsran::unique_byte_buffer_t sdu);
|
||||
|
||||
/* TS 38.331 - 5.7.2 UL information transfer */
|
||||
/** TS 38.331 - 5.7.2 UL information transfer */
|
||||
void handle_ul_information_transfer(const asn1::rrc_nr::ul_info_transfer_s& msg);
|
||||
|
||||
// NGAP interface
|
||||
void establish_eps_bearer(uint32_t pdu_session_id, srsran::const_byte_span nas_pdu, uint32_t lcid);
|
||||
|
||||
/* TS 38.331 - 5.3.4 Initial AS security activation */
|
||||
void send_security_mode_command(srsran::unique_byte_buffer_t nas_pdu);
|
||||
|
||||
private:
|
||||
void send_dl_ccch(const asn1::rrc_nr::dl_ccch_msg_s& dl_ccch_msg);
|
||||
void send_dl_dcch(srsran::nr_srb srb, const asn1::rrc_nr::dl_dcch_msg_s& dl_dcch_msg);
|
||||
int send_dl_ccch(const asn1::rrc_nr::dl_ccch_msg_s& dl_ccch_msg);
|
||||
int send_dl_dcch(srsran::nr_srb srb, const asn1::rrc_nr::dl_dcch_msg_s& dl_dcch_msg);
|
||||
|
||||
/* TS 38.331 - 5.3.3 RRC connection establishment */
|
||||
void send_rrc_setup();
|
||||
void send_rrc_reject(uint8_t reject_wait_time_secs);
|
||||
|
||||
/* TS 38.331 - 5.3.4 Initial AS security activation */
|
||||
void send_security_mode_command();
|
||||
|
||||
/* TS 38.331 - 5.3.5 RRC reconfiguration */
|
||||
void send_rrc_reconfiguration();
|
||||
|
||||
/// Update PDCP bearers based on ASN1 structs passed to the UE
|
||||
int update_pdcp_bearers(const asn1::rrc_nr::radio_bearer_cfg_s& radio_bearer_diff,
|
||||
const asn1::rrc_nr::cell_group_cfg_s& cell_group_diff);
|
||||
|
||||
/// Update RLC bearers based on ASN1 structs passed to the UE
|
||||
int update_rlc_bearers(const asn1::rrc_nr::cell_group_cfg_s& cell_group_diff);
|
||||
|
||||
/// Update MAC based on ASN1 message
|
||||
int update_mac(const asn1::rrc_nr::cell_group_cfg_s& cell_group_diff, bool is_config_complete);
|
||||
|
||||
int pack_rrc_reconfiguration(asn1::dyn_octstring& packed_rrc_reconfig);
|
||||
int pack_secondary_cell_group_cfg(asn1::dyn_octstring& packed_secondary_cell_config);
|
||||
|
||||
|
@ -157,14 +176,18 @@ private:
|
|||
srsran::unique_timer activity_timer; /// for basic DL/UL activity timeout
|
||||
|
||||
// RRC configs for UEs
|
||||
asn1::rrc_nr::cell_group_cfg_s cell_group_cfg;
|
||||
asn1::rrc_nr::radio_bearer_cfg_s radio_bearer_cfg, next_radio_bearer_cfg;
|
||||
asn1::rrc_nr::cell_group_cfg_s cell_group_cfg, next_cell_group_cfg;
|
||||
asn1::rrc_nr::radio_bearer_cfg_s radio_bearer_cfg, next_radio_bearer_cfg;
|
||||
std::vector<srsran::unique_byte_buffer_t> nas_pdu_queue;
|
||||
|
||||
// MAC controller
|
||||
sched_nr_interface::ue_cfg_t uecfg{};
|
||||
|
||||
const uint32_t drb1_lcid = 4;
|
||||
|
||||
// Security helper
|
||||
srsgnb::nr_security_context sec_ctx;
|
||||
|
||||
// SA specific variables
|
||||
struct ctxt_t {
|
||||
uint64_t setup_ue_id = -1;
|
||||
|
|
|
@ -107,6 +107,11 @@ int gnb_stack_nr::init(const gnb_stack_args_t& args_,
|
|||
}
|
||||
|
||||
if (ngap != nullptr) {
|
||||
if (args.ngap_pcap.enable) {
|
||||
ngap_pcap.open(args.ngap_pcap.filename.c_str());
|
||||
ngap->start_pcap(&ngap_pcap);
|
||||
}
|
||||
|
||||
ngap->init(args.ngap, &rrc, nullptr);
|
||||
gtpu_args_t gtpu_args;
|
||||
gtpu_args.embms_enable = false;
|
||||
|
@ -240,4 +245,4 @@ void gnb_stack_nr::rach_detected(const rach_info_t& rach_info)
|
|||
mac.rach_detected(rach_info);
|
||||
}
|
||||
|
||||
} // namespace srsenb
|
||||
} // namespace srsenb
|
||||
|
|
|
@ -28,14 +28,15 @@ set(SOURCES mac_nr.cc
|
|||
sched_nr_pdcch.cc
|
||||
sched_nr_cfg.cc
|
||||
sched_nr_helpers.cc
|
||||
sched_nr_bwp.cc
|
||||
sched_nr_bwp.cc
|
||||
sched_nr_rb.cc
|
||||
sched_nr_time_rr.cc
|
||||
harq_softbuffer.cc
|
||||
sched_nr_signalling.cc)
|
||||
sched_nr_signalling.cc
|
||||
sched_nr_interface_utils.cc)
|
||||
|
||||
add_library(srsgnb_mac STATIC ${SOURCES})
|
||||
target_link_libraries(srsgnb_mac srsenb_mac_common)
|
||||
target_link_libraries(srsgnb_mac srsenb_mac_common srsran_mac)
|
||||
include_directories(${PROJECT_SOURCE_DIR})
|
||||
|
||||
add_subdirectory(test)
|
|
@ -33,15 +33,28 @@
|
|||
|
||||
namespace srsenb {
|
||||
|
||||
/**
|
||||
* @brief Handles UL PDU processing
|
||||
*
|
||||
* This class implements the demuxing of UL PDUs received at the MAC layer.
|
||||
* When the PHY decodes a valid PUSCH it passes the PDU to the MAC which
|
||||
* in turn puts them in a thread-safe task queue to return to the calling
|
||||
* thread as quick as possible.
|
||||
*
|
||||
* The demuxing of the PDUs for all users takes place on the Stack thread
|
||||
* which calls RLC and RRC for SDUs, or the MAC/scheduler for control elements.
|
||||
*
|
||||
*/
|
||||
class mac_nr_rx
|
||||
{
|
||||
public:
|
||||
explicit mac_nr_rx(rlc_interface_mac* rlc_,
|
||||
rrc_interface_mac_nr* rrc_,
|
||||
srsran::task_queue_handle& stack_task_queue_,
|
||||
sched_nr_interface* sched_,
|
||||
srslog::basic_logger& logger_) :
|
||||
task_queue(stack_task_queue_), rlc(rlc_), rrc(rrc_), sched(sched_), logger(logger_)
|
||||
explicit mac_nr_rx(rlc_interface_mac* rlc_,
|
||||
rrc_interface_mac_nr* rrc_,
|
||||
srsran::task_queue_handle& stack_task_queue_,
|
||||
sched_nr_interface* sched_,
|
||||
mac_interface_pdu_demux_nr& mac_,
|
||||
srslog::basic_logger& logger_) :
|
||||
task_queue(stack_task_queue_), rlc(rlc_), rrc(rrc_), sched(sched_), mac(mac_), logger(logger_)
|
||||
{}
|
||||
|
||||
void handle_pdu(uint16_t rnti, srsran::unique_byte_buffer_t pdu)
|
||||
|
@ -96,6 +109,14 @@ private:
|
|||
{
|
||||
// Handle MAC CEs
|
||||
switch (subpdu.get_lcid()) {
|
||||
case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::CCCH_SIZE_48:
|
||||
case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::CCCH_SIZE_64: {
|
||||
srsran::mac_sch_subpdu_nr& ccch_subpdu = const_cast<srsran::mac_sch_subpdu_nr&>(subpdu);
|
||||
rlc->write_pdu(rnti, 0, ccch_subpdu.get_sdu(), ccch_subpdu.get_sdu_length());
|
||||
// store content for ConRes CE
|
||||
mac.store_msg3(rnti,
|
||||
srsran::make_byte_buffer(ccch_subpdu.get_sdu(), ccch_subpdu.get_sdu_length(), __FUNCTION__));
|
||||
} break;
|
||||
case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::CRNTI: {
|
||||
uint16_t ce_crnti = subpdu.get_c_rnti();
|
||||
uint16_t prev_rnti = rnti;
|
||||
|
@ -174,6 +195,7 @@ private:
|
|||
rlc_interface_mac* rlc;
|
||||
rrc_interface_mac_nr* rrc;
|
||||
sched_nr_interface* sched;
|
||||
mac_interface_pdu_demux_nr& mac;
|
||||
srslog::basic_logger& logger;
|
||||
srsran::task_queue_handle& task_queue;
|
||||
|
||||
|
@ -283,7 +305,9 @@ int mac_nr::cell_cfg(const std::vector<srsenb::sched_nr_interface::cell_cfg_t>&
|
|||
bcch_dlsch_payload.push_back(std::move(sib));
|
||||
}
|
||||
|
||||
rx.reset(new mac_nr_rx{rlc, rrc, stack_task_queue, sched.get(), logger});
|
||||
rx.reset(new mac_nr_rx{rlc, rrc, stack_task_queue, sched.get(), *this, logger});
|
||||
|
||||
default_ue_phy_cfg = get_common_ue_phy_cfg(cell_config[0]);
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
@ -322,12 +346,7 @@ void mac_nr::rach_detected(const rach_info_t& rach_info)
|
|||
uecfg.carriers[0].active = true;
|
||||
uecfg.carriers[0].cc = enb_cc_idx;
|
||||
uecfg.ue_bearers[0].direction = mac_lc_ch_cfg_t::BOTH;
|
||||
srsran::phy_cfg_nr_default_t::reference_cfg_t ref_args{};
|
||||
ref_args.duplex = cell_config[0].duplex.mode == SRSRAN_DUPLEX_MODE_TDD
|
||||
? srsran::phy_cfg_nr_default_t::reference_cfg_t::R_DUPLEX_TDD_CUSTOM_6_4
|
||||
: srsran::phy_cfg_nr_default_t::reference_cfg_t::R_DUPLEX_FDD;
|
||||
uecfg.phy_cfg = srsran::phy_cfg_nr_default_t{ref_args};
|
||||
uecfg.phy_cfg.csi = {}; // disable CSI until RA is complete
|
||||
uecfg.phy_cfg = default_ue_phy_cfg;
|
||||
|
||||
uint16_t rnti = alloc_ue(enb_cc_idx);
|
||||
|
||||
|
@ -452,6 +471,16 @@ int mac_nr::slot_indication(const srsran_slot_cfg_t& slot_cfg)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void mac_nr::store_msg3(uint16_t rnti, srsran::unique_byte_buffer_t pdu)
|
||||
{
|
||||
srsran::rwlock_read_guard rw_lock(rwmutex);
|
||||
if (is_rnti_active_nolock(rnti)) {
|
||||
ue_db[rnti]->store_msg3(std::move(pdu));
|
||||
} else {
|
||||
logger.error("User rnti=0x%x not found. Can't store Msg3.", rnti);
|
||||
}
|
||||
}
|
||||
|
||||
mac_nr::dl_sched_t* mac_nr::get_dl_sched(const srsran_slot_cfg_t& slot_cfg)
|
||||
{
|
||||
slot_point pdsch_slot = srsran::slot_point{NUMEROLOGY_IDX, slot_cfg.idx};
|
||||
|
@ -468,7 +497,7 @@ mac_nr::dl_sched_t* mac_nr::get_dl_sched(const srsran_slot_cfg_t& slot_cfg)
|
|||
}
|
||||
|
||||
// Generate MAC DL PDUs
|
||||
uint32_t rar_count = 0, si_count = 0;
|
||||
uint32_t rar_count = 0, si_count = 0, data_count = 0;
|
||||
srsran::rwlock_read_guard rw_lock(rwmutex);
|
||||
for (pdsch_t& pdsch : dl_res->phy.pdsch) {
|
||||
if (pdsch.sch.grant.rnti_type == srsran_rnti_type_c) {
|
||||
|
@ -479,7 +508,8 @@ mac_nr::dl_sched_t* mac_nr::get_dl_sched(const srsran_slot_cfg_t& slot_cfg)
|
|||
for (auto& tb_data : pdsch.data) {
|
||||
if (tb_data != nullptr and tb_data->N_bytes == 0) {
|
||||
// TODO: exclude retx from packing
|
||||
ue_db[rnti]->generate_pdu(tb_data, pdsch.sch.grant.tb->tbs / 8);
|
||||
const sched_nr_interface::dl_pdu_t& pdu = dl_res->data[data_count++];
|
||||
ue_db[rnti]->generate_pdu(tb_data, pdsch.sch.grant.tb->tbs / 8, pdu.subpdus);
|
||||
|
||||
if (pcap != nullptr) {
|
||||
uint32_t pid = 0; // TODO: get PID from PDCCH struct?
|
||||
|
|
|
@ -428,7 +428,6 @@ int sched_nr::dl_rach_info(const rar_info_t& rar_info, const ue_cfg_t& uecfg)
|
|||
auto add_ue = [this, uecfg, rar_info](event_manager::logger& ev_logger) {
|
||||
// create user
|
||||
// Note: UEs being created in sched main thread, which has higher priority
|
||||
logger->info("SCHED: New user rnti=0x%x, cc=%d", rar_info.temp_crnti, uecfg.carriers[0].cc);
|
||||
std::unique_ptr<ue> u{new ue{rar_info.temp_crnti, uecfg, cfg}};
|
||||
|
||||
uint16_t rnti = rar_info.temp_crnti;
|
||||
|
|
|
@ -26,6 +26,64 @@
|
|||
namespace srsenb {
|
||||
namespace sched_nr_impl {
|
||||
|
||||
using candidate_ss_list_t =
|
||||
srsran::bounded_vector<const srsran_search_space_t*, SRSRAN_SEARCH_SPACE_MAX_NOF_CANDIDATES_NR>;
|
||||
|
||||
candidate_ss_list_t find_ss(const srsran_pdcch_cfg_nr_t& pdcch,
|
||||
uint32_t aggr_idx,
|
||||
srsran_rnti_type_t rnti_type,
|
||||
srsran::const_span<srsran_dci_format_nr_t> prio_dcis)
|
||||
{
|
||||
candidate_ss_list_t ret;
|
||||
auto active_ss_lst = view_active_search_spaces(pdcch);
|
||||
|
||||
auto contains_dci_fmt = [prio_dcis, aggr_idx](const srsran_search_space_t& ss) {
|
||||
if (ss.nof_candidates[aggr_idx] > 0 and ss.nof_formats > 0) {
|
||||
for (uint32_t i = 0; i < prio_dcis.size(); ++i) {
|
||||
for (uint32_t j = 0; j < prio_dcis.size(); ++j) {
|
||||
if (ss.formats[j] == prio_dcis[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
auto is_common_ss_allowed = [rnti_type](srsran_search_space_type_t ss_type) {
|
||||
switch (rnti_type) {
|
||||
case srsran_rnti_type_c:
|
||||
return ss_type == srsran_search_space_type_common_1 or ss_type == srsran_search_space_type_common_3;
|
||||
case srsran_rnti_type_tc:
|
||||
case srsran_rnti_type_ra:
|
||||
// TODO: Fix UE config to not use common3
|
||||
return ss_type == srsran_search_space_type_common_1 or ss_type == srsran_search_space_type_common_3;
|
||||
case srsran_rnti_type_si:
|
||||
return ss_type == srsran_search_space_type_common_0;
|
||||
default:
|
||||
// TODO: Remaining cases
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (rnti_type == srsran_rnti_type_c) {
|
||||
// First search UE-specific
|
||||
for (const srsran_search_space_t& ss : active_ss_lst) {
|
||||
if (ss.type == srsran_search_space_type_ue and contains_dci_fmt(ss)) {
|
||||
ret.push_back(&ss);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const srsran_search_space_t& ss : active_ss_lst) {
|
||||
if (is_common_ss_allowed(ss.type) and contains_dci_fmt(ss)) {
|
||||
ret.push_back(&ss);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bwp_slot_grid::bwp_slot_grid(const bwp_params_t& bwp_cfg_, uint32_t slot_idx_) :
|
||||
dl_prbs(bwp_cfg_.cfg.rb_width, bwp_cfg_.cfg.start_rb, bwp_cfg_.cfg.pdsch.rbg_size_cfg_1),
|
||||
ul_prbs(bwp_cfg_.cfg.rb_width, bwp_cfg_.cfg.start_rb, bwp_cfg_.cfg.pdsch.rbg_size_cfg_1),
|
||||
|
@ -55,6 +113,7 @@ void bwp_slot_grid::reset()
|
|||
dl.phy.pdcch_dl.clear();
|
||||
dl.phy.pdcch_ul.clear();
|
||||
dl.phy.pdsch.clear();
|
||||
dl.data.clear();
|
||||
dl.rar.clear();
|
||||
dl.sib_idxs.clear();
|
||||
ul.pusch.clear();
|
||||
|
@ -99,8 +158,9 @@ alloc_result bwp_slot_allocator::alloc_si(uint32_t aggr_idx,
|
|||
|
||||
// RAR allocation successful.
|
||||
bwp_pdcch_slot.dl_prbs |= prbs;
|
||||
// Generate DCI for RAR with given RA-RNTI
|
||||
pdcch_dl_t& pdcch = bwp_pdcch_slot.dl.phy.pdcch_dl.back();
|
||||
// Generate DCI for SIB
|
||||
pdcch_dl_t& pdcch = bwp_pdcch_slot.dl.phy.pdcch_dl.back();
|
||||
pdcch.dci_cfg.coreset0_bw = srsran_coreset_get_bw(&cfg.cfg.pdcch.coreset[0]);
|
||||
if (not fill_dci_sib(prbs, si_idx, si_ntx, *bwp_grid.cfg, pdcch.dci)) {
|
||||
// Cancel on-going PDCCH allocation
|
||||
bwp_pdcch_slot.coresets[coreset_id]->rem_last_dci();
|
||||
|
@ -244,8 +304,11 @@ alloc_result bwp_slot_allocator::alloc_rar_and_msg3(uint16_t
|
|||
|
||||
// ue is the UE (1 only) that will be allocated
|
||||
// func computes the grant allocation for this UE
|
||||
alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, const prb_grant& dl_grant)
|
||||
alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, prb_grant dl_grant)
|
||||
{
|
||||
static const uint32_t aggr_idx = 2;
|
||||
static const std::array<srsran_dci_format_nr_t, 2> dci_fmt_list{srsran_dci_format_nr_1_1, srsran_dci_format_nr_1_0};
|
||||
|
||||
bwp_slot_grid& bwp_pdcch_slot = bwp_grid[ue.pdcch_slot];
|
||||
bwp_slot_grid& bwp_pdsch_slot = bwp_grid[ue.pdsch_slot];
|
||||
bwp_slot_grid& bwp_uci_slot = bwp_grid[ue.uci_slot]; // UCI : UL control info
|
||||
|
@ -269,23 +332,28 @@ alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, const prb_grant& dl_gr
|
|||
// Find space in PUCCH
|
||||
// TODO
|
||||
|
||||
// Find space and allocate PDCCH
|
||||
const uint32_t aggr_idx = 2;
|
||||
// Choose SearchSpace + DCI format
|
||||
srsran_rnti_type_t rnti_type =
|
||||
ue->ue_cfg().ue_bearers[1].direction == mac_lc_ch_cfg_t::IDLE ? srsran_rnti_type_tc : srsran_rnti_type_c;
|
||||
// Choose the ss_id the highest number of candidates
|
||||
uint32_t ss_id = 0, max_nof_candidates = 0;
|
||||
for (uint32_t i = 0; i < 3; ++i) {
|
||||
uint32_t nof_candidates = ue->cce_pos_list(i, pdcch_slot.slot_idx(), aggr_idx).size();
|
||||
if (nof_candidates > max_nof_candidates) {
|
||||
ss_id = i;
|
||||
max_nof_candidates = nof_candidates;
|
||||
}
|
||||
candidate_ss_list_t ss_candidates = find_ss(ue->phy().pdcch, aggr_idx, rnti_type, dci_fmt_list);
|
||||
if (ss_candidates.empty()) {
|
||||
// Could not find space in PDCCH
|
||||
logger.warning("SCHED: No PDCCH candidates for any of the rnti=0x%x search spaces", ue->rnti);
|
||||
return alloc_result::no_cch_space;
|
||||
}
|
||||
uint32_t coreset_id = ue->phy().pdcch.search_space[ss_id].coreset_id;
|
||||
if (not bwp_pdcch_slot.coresets[coreset_id]->alloc_dci(pdcch_grant_type_t::dl_data, aggr_idx, ss_id, &ue.cfg())) {
|
||||
const srsran_search_space_t& ss = *ss_candidates[0];
|
||||
|
||||
// Find space and allocate PDCCH
|
||||
uint32_t coreset_id = ss.coreset_id;
|
||||
if (not bwp_pdcch_slot.coresets[coreset_id]->alloc_dci(pdcch_grant_type_t::dl_data, aggr_idx, ss.id, &ue.cfg())) {
|
||||
// Could not find space in PDCCH
|
||||
return alloc_result::no_cch_space;
|
||||
}
|
||||
|
||||
// Update PRB grant based on the start and end of CORESET RBs
|
||||
reduce_to_dl_coreset_bw(cfg, ss.id, srsran_dci_format_nr_1_0, dl_grant);
|
||||
|
||||
// Allocate HARQ
|
||||
int mcs = ue->fixed_pdsch_mcs();
|
||||
if (ue.h_dl->empty()) {
|
||||
|
@ -303,7 +371,7 @@ alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, const prb_grant& dl_gr
|
|||
while (true) {
|
||||
// Generate PDCCH
|
||||
pdcch_dl_t& pdcch = bwp_pdcch_slot.dl.phy.pdcch_dl.back();
|
||||
fill_dl_dci_ue_fields(ue, *bwp_grid.cfg, ss_id, pdcch.dci.ctx.location, pdcch.dci);
|
||||
fill_dl_dci_ue_fields(ue, *bwp_grid.cfg, ss.id, pdcch.dci.ctx.location, pdcch.dci);
|
||||
pdcch.dci.pucch_resource = 0;
|
||||
pdcch.dci.dai = std::count_if(bwp_uci_slot.pending_acks.begin(),
|
||||
bwp_uci_slot.pending_acks.end(),
|
||||
|
@ -346,11 +414,18 @@ alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, const prb_grant& dl_gr
|
|||
logger.warning("Couldn't find mcs that leads to R<0.9");
|
||||
}
|
||||
|
||||
// Select scheduled LCIDs and update UE buffer state
|
||||
bwp_pdsch_slot.dl.data.emplace_back();
|
||||
ue.build_pdu(ue.h_dl->tbs(), bwp_pdsch_slot.dl.data.back());
|
||||
|
||||
return alloc_result::success;
|
||||
}
|
||||
|
||||
alloc_result bwp_slot_allocator::alloc_pusch(slot_ue& ue, const prb_grant& ul_prbs)
|
||||
alloc_result bwp_slot_allocator::alloc_pusch(slot_ue& ue, prb_grant ul_grant)
|
||||
{
|
||||
static const uint32_t aggr_idx = 2;
|
||||
static const std::array<srsran_dci_format_nr_t, 2> dci_fmt_list{srsran_dci_format_nr_0_1, srsran_dci_format_nr_0_0};
|
||||
|
||||
auto& bwp_pdcch_slot = bwp_grid[ue.pdcch_slot];
|
||||
auto& bwp_pusch_slot = bwp_grid[ue.pusch_slot];
|
||||
alloc_result ret = verify_pusch_space(bwp_pusch_slot, &bwp_pdcch_slot);
|
||||
|
@ -362,22 +437,24 @@ alloc_result bwp_slot_allocator::alloc_pusch(slot_ue& ue, const prb_grant& ul_pr
|
|||
return ret;
|
||||
}
|
||||
pdcch_ul_list_t& pdcchs = bwp_pdcch_slot.dl.phy.pdcch_ul;
|
||||
if (bwp_pusch_slot.ul_prbs.collides(ul_prbs)) {
|
||||
if (bwp_pusch_slot.ul_prbs.collides(ul_grant)) {
|
||||
return alloc_result::sch_collision;
|
||||
}
|
||||
const uint32_t aggr_idx = 2;
|
||||
// Choose the ss_id the highest number of candidates
|
||||
uint32_t ss_id = 0, max_nof_candidates = 0;
|
||||
for (uint32_t i = 0; i < 3; ++i) {
|
||||
uint32_t nof_candidates = ue->cce_pos_list(i, pdcch_slot.slot_idx(), aggr_idx).size();
|
||||
if (nof_candidates > max_nof_candidates) {
|
||||
ss_id = i;
|
||||
max_nof_candidates = nof_candidates;
|
||||
}
|
||||
|
||||
// Choose SearchSpace + DCI format
|
||||
srsran_rnti_type_t rnti_type =
|
||||
ue->ue_cfg().ue_bearers[1].direction == mac_lc_ch_cfg_t::IDLE ? srsran_rnti_type_tc : srsran_rnti_type_c;
|
||||
candidate_ss_list_t ss_candidates = find_ss(ue->phy().pdcch, aggr_idx, rnti_type, dci_fmt_list);
|
||||
if (ss_candidates.empty()) {
|
||||
// Could not find space in PDCCH
|
||||
logger.warning("SCHED: No PDCCH candidates for any of the rnti=0x%x search spaces", ue->rnti);
|
||||
return alloc_result::no_cch_space;
|
||||
}
|
||||
uint32_t coreset_id = ue->phy().pdcch.search_space[ss_id].coreset_id;
|
||||
const srsran_search_space_t& ss = *ss_candidates[0];
|
||||
|
||||
uint32_t coreset_id = ss.coreset_id;
|
||||
if (not bwp_pdcch_slot.coresets[coreset_id].value().alloc_dci(
|
||||
pdcch_grant_type_t::ul_data, aggr_idx, ss_id, &ue.cfg())) {
|
||||
pdcch_grant_type_t::ul_data, aggr_idx, ss.id, &ue.cfg())) {
|
||||
// Could not find space in PDCCH
|
||||
return alloc_result::no_cch_space;
|
||||
}
|
||||
|
@ -386,19 +463,19 @@ alloc_result bwp_slot_allocator::alloc_pusch(slot_ue& ue, const prb_grant& ul_pr
|
|||
|
||||
if (ue.h_ul->empty()) {
|
||||
int mcs = ue->fixed_pusch_mcs();
|
||||
bool success = ue.h_ul->new_tx(ue.pusch_slot, ue.pusch_slot, ul_prbs, mcs, ue->ue_cfg().maxharq_tx);
|
||||
bool success = ue.h_ul->new_tx(ue.pusch_slot, ue.pusch_slot, ul_grant, mcs, ue->ue_cfg().maxharq_tx);
|
||||
srsran_assert(success, "Failed to allocate UL HARQ");
|
||||
} else {
|
||||
bool success = ue.h_ul->new_retx(ue.pusch_slot, ue.pusch_slot, ul_prbs);
|
||||
bool success = ue.h_ul->new_retx(ue.pusch_slot, ue.pusch_slot, ul_grant);
|
||||
srsran_assert(success, "Failed to allocate UL HARQ retx");
|
||||
}
|
||||
|
||||
// Generate PDCCH
|
||||
pdcch_ul_t& pdcch = pdcchs.back();
|
||||
fill_ul_dci_ue_fields(ue, *bwp_grid.cfg, ss_id, pdcch.dci.ctx.location, pdcch.dci);
|
||||
fill_ul_dci_ue_fields(ue, *bwp_grid.cfg, ss.id, pdcch.dci.ctx.location, pdcch.dci);
|
||||
pdcch.dci_cfg = ue->phy().get_dci_cfg();
|
||||
// Generate PUSCH
|
||||
bwp_pusch_slot.ul_prbs |= ul_prbs;
|
||||
bwp_pusch_slot.ul_prbs |= ul_grant;
|
||||
bwp_pusch_slot.ul.pusch.emplace_back();
|
||||
pusch_t& pusch = bwp_pusch_slot.ul.pusch.back();
|
||||
srsran_slot_cfg_t slot_cfg;
|
||||
|
|
|
@ -30,45 +30,104 @@ namespace sched_nr_impl {
|
|||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void reduce_to_dl_coreset_bw(const bwp_params_t& bwp_cfg,
|
||||
uint32_t ss_id,
|
||||
srsran_dci_format_nr_t dci_fmt,
|
||||
prb_grant& grant)
|
||||
{
|
||||
const srsran_search_space_t& ss =
|
||||
dci_fmt == srsran_dci_format_nr_rar ? bwp_cfg.cfg.pdcch.ra_search_space : bwp_cfg.cfg.pdcch.search_space[ss_id];
|
||||
if (not SRSRAN_SEARCH_SPACE_IS_COMMON(ss.type)) {
|
||||
return;
|
||||
}
|
||||
uint32_t rb_start = 0, nof_prbs = bwp_cfg.nof_prb();
|
||||
if (dci_fmt == srsran_dci_format_nr_1_0) {
|
||||
rb_start = srsran_coreset_start_rb(&bwp_cfg.cfg.pdcch.coreset[ss.coreset_id]);
|
||||
}
|
||||
if (ss.coreset_id == 0) {
|
||||
nof_prbs = srsran_coreset_get_bw(&bwp_cfg.cfg.pdcch.coreset[0]);
|
||||
}
|
||||
grant &= prb_interval{rb_start, rb_start + nof_prbs};
|
||||
}
|
||||
|
||||
void fill_dci_common(const bwp_params_t& bwp_cfg, srsran_dci_dl_nr_t& dci)
|
||||
{
|
||||
dci.bwp_id = bwp_cfg.bwp_id;
|
||||
dci.cc_id = bwp_cfg.cc;
|
||||
dci.tpc = 1;
|
||||
dci.coreset0_bw = bwp_cfg.cfg.pdcch.coreset_present[0] ? srsran_coreset_get_bw(&bwp_cfg.cfg.pdcch.coreset[0]) : 0;
|
||||
}
|
||||
|
||||
void fill_dci_common(const bwp_params_t& bwp_cfg, srsran_dci_ul_nr_t& dci)
|
||||
{
|
||||
dci.bwp_id = bwp_cfg.bwp_id;
|
||||
dci.cc_id = bwp_cfg.cc;
|
||||
dci.tpc = 1;
|
||||
}
|
||||
|
||||
template <typename DciDlOrUl>
|
||||
void fill_dci_common(const slot_ue& ue, const bwp_params_t& bwp_cfg, DciDlOrUl& dci)
|
||||
void fill_dci_harq(const slot_ue& ue, DciDlOrUl& dci)
|
||||
{
|
||||
const static uint32_t rv_idx[4] = {0, 2, 3, 1};
|
||||
|
||||
dci.bwp_id = ue->active_bwp().bwp_id;
|
||||
dci.cc_id = ue->cc;
|
||||
dci.tpc = 1;
|
||||
// harq
|
||||
harq_proc* h = std::is_same<DciDlOrUl, srsran_dci_dl_nr_t>::value ? static_cast<harq_proc*>(ue.h_dl)
|
||||
: static_cast<harq_proc*>(ue.h_ul);
|
||||
|
||||
dci.pid = h->pid;
|
||||
dci.ndi = h->ndi();
|
||||
dci.mcs = h->mcs();
|
||||
dci.rv = rv_idx[h->nof_retx() % 4];
|
||||
// PRB assignment
|
||||
const prb_grant& grant = h->prbs();
|
||||
}
|
||||
|
||||
void fill_dci_grant(const bwp_params_t& bwp_cfg, const prb_grant& grant, srsran_dci_dl_nr_t& dci)
|
||||
{
|
||||
dci.time_domain_assigment = 0;
|
||||
if (grant.is_alloc_type0()) {
|
||||
srsran_assert(not SRSRAN_SEARCH_SPACE_IS_COMMON(dci.ctx.ss_type), "AllocType0 for common search space");
|
||||
dci.freq_domain_assigment = grant.rbgs().to_uint64();
|
||||
} else {
|
||||
uint32_t rb_start = 0, nof_prb = bwp_cfg.nof_prb();
|
||||
if (dci.ctx.format == srsran_dci_format_nr_1_0 && SRSRAN_SEARCH_SPACE_IS_COMMON(dci.ctx.ss_type)) {
|
||||
rb_start = dci.ctx.coreset_start_rb;
|
||||
}
|
||||
if (dci.ctx.coreset_id == 0 and SRSRAN_SEARCH_SPACE_IS_COMMON(dci.ctx.ss_type)) {
|
||||
nof_prb = dci.coreset0_bw;
|
||||
}
|
||||
uint32_t grant_start = grant.prbs().start() - rb_start;
|
||||
dci.freq_domain_assigment = srsran_ra_nr_type1_riv(nof_prb, grant_start, grant.prbs().length());
|
||||
}
|
||||
}
|
||||
|
||||
void fill_dci_grant(const bwp_params_t& bwp_cfg, const prb_grant& grant, srsran_dci_ul_nr_t& dci)
|
||||
{
|
||||
dci.time_domain_assigment = 0;
|
||||
if (grant.is_alloc_type0()) {
|
||||
dci.freq_domain_assigment = grant.rbgs().to_uint64();
|
||||
} else {
|
||||
dci.freq_domain_assigment =
|
||||
srsran_ra_nr_type1_riv(bwp_cfg.cfg.rb_width, grant.prbs().start(), grant.prbs().length());
|
||||
uint32_t nof_prb = bwp_cfg.nof_prb();
|
||||
dci.freq_domain_assigment = srsran_ra_nr_type1_riv(nof_prb, grant.prbs().start(), grant.prbs().length());
|
||||
}
|
||||
dci.time_domain_assigment = 0;
|
||||
}
|
||||
|
||||
void fill_rar_dci_context(const bwp_params_t& bwp_cfg, uint16_t ra_rnti, srsran_dci_ctx_t& dci_ctx)
|
||||
{
|
||||
uint32_t cs_id = bwp_cfg.cfg.pdcch.ra_search_space.coreset_id;
|
||||
|
||||
dci_ctx.format = srsran_dci_format_nr_1_0;
|
||||
dci_ctx.ss_type = srsran_search_space_type_common_1;
|
||||
dci_ctx.rnti_type = srsran_rnti_type_ra;
|
||||
dci_ctx.rnti = ra_rnti;
|
||||
dci_ctx.coreset_id = cs_id;
|
||||
dci_ctx.coreset_start_rb = srsran_coreset_start_rb(&bwp_cfg.cfg.pdcch.coreset[cs_id]);
|
||||
}
|
||||
|
||||
bool fill_dci_rar(prb_interval interv, uint16_t ra_rnti, const bwp_params_t& bwp_cfg, srsran_dci_dl_nr_t& dci)
|
||||
{
|
||||
dci.mcs = 5;
|
||||
dci.ctx.format = srsran_dci_format_nr_1_0;
|
||||
dci.ctx.ss_type = srsran_search_space_type_common_1;
|
||||
dci.ctx.rnti_type = srsran_rnti_type_ra;
|
||||
dci.ctx.rnti = ra_rnti;
|
||||
dci.ctx.coreset_id = bwp_cfg.cfg.pdcch.ra_search_space.coreset_id;
|
||||
dci.freq_domain_assigment = srsran_ra_nr_type1_riv(bwp_cfg.cfg.rb_width, interv.start(), interv.length());
|
||||
dci.time_domain_assigment = 0;
|
||||
dci.tpc = 1;
|
||||
dci.bwp_id = bwp_cfg.bwp_id;
|
||||
dci.cc_id = bwp_cfg.cc;
|
||||
fill_rar_dci_context(bwp_cfg, ra_rnti, dci.ctx);
|
||||
|
||||
dci.mcs = 5;
|
||||
fill_dci_common(bwp_cfg, dci);
|
||||
fill_dci_grant(bwp_cfg, interv, dci);
|
||||
// TODO: Fill
|
||||
|
||||
return true;
|
||||
|
@ -76,7 +135,7 @@ bool fill_dci_rar(prb_interval interv, uint16_t ra_rnti, const bwp_params_t& bwp
|
|||
|
||||
bool fill_dci_msg3(const slot_ue& ue, const bwp_params_t& bwp_cfg, srsran_dci_ul_nr_t& msg3_dci)
|
||||
{
|
||||
fill_dci_common(ue, bwp_cfg, msg3_dci);
|
||||
// Fill DCI context
|
||||
msg3_dci.ctx.coreset_id = ue->phy().pdcch.ra_search_space.coreset_id;
|
||||
msg3_dci.ctx.rnti_type = srsran_rnti_type_tc;
|
||||
msg3_dci.ctx.rnti = ue->rnti;
|
||||
|
@ -87,6 +146,11 @@ bool fill_dci_msg3(const slot_ue& ue, const bwp_params_t& bwp_cfg, srsran_dci_ul
|
|||
msg3_dci.ctx.format = srsran_dci_format_nr_0_0;
|
||||
}
|
||||
|
||||
// Fill DCI content
|
||||
fill_dci_common(bwp_cfg, msg3_dci);
|
||||
fill_dci_harq(ue, msg3_dci);
|
||||
fill_dci_grant(bwp_cfg, ue.h_ul->prbs(), msg3_dci);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -101,7 +165,9 @@ void fill_dl_dci_ue_fields(const slot_ue& ue,
|
|||
bool ret = ue->phy().get_dci_ctx_pdsch_rnti_c(ss_id, dci_pos, ue->rnti, dci.ctx);
|
||||
srsran_assert(ret, "Invalid DL DCI format");
|
||||
|
||||
fill_dci_common(ue, bwp_cfg, dci);
|
||||
fill_dci_common(bwp_cfg, dci);
|
||||
fill_dci_harq(ue, dci);
|
||||
fill_dci_grant(bwp_cfg, ue.h_dl->prbs(), dci);
|
||||
if (dci.ctx.format == srsran_dci_format_nr_1_0) {
|
||||
dci.harq_feedback = (ue.uci_slot - ue.pdsch_slot) - 1;
|
||||
} else {
|
||||
|
@ -118,9 +184,13 @@ void fill_ul_dci_ue_fields(const slot_ue& ue,
|
|||
bool ret = ue->phy().get_dci_ctx_pusch_rnti_c(ss_id, dci_pos, ue->rnti, dci.ctx);
|
||||
srsran_assert(ret, "Invalid DL DCI format");
|
||||
|
||||
fill_dci_common(ue, bwp_cfg, dci);
|
||||
fill_dci_common(bwp_cfg, dci);
|
||||
fill_dci_harq(ue, dci);
|
||||
fill_dci_grant(bwp_cfg, ue.h_ul->prbs(), dci);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void log_sched_slot_ues(srslog::basic_logger& logger, slot_point pdcch_slot, uint32_t cc, const slot_ue_map_t& slot_ues)
|
||||
{
|
||||
if (not logger.debug.enabled() or slot_ues.empty()) {
|
||||
|
@ -147,14 +217,14 @@ void log_sched_bwp_result(srslog::basic_logger& logger,
|
|||
const slot_ue_map_t& slot_ues)
|
||||
{
|
||||
const bwp_slot_grid& bwp_slot = res_grid[pdcch_slot];
|
||||
size_t rar_count = 0, si_count = 0;
|
||||
size_t rar_count = 0, si_count = 0, data_count = 0;
|
||||
for (const pdcch_dl_t& pdcch : bwp_slot.dl.phy.pdcch_dl) {
|
||||
fmt::memory_buffer fmtbuf;
|
||||
if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_c) {
|
||||
const slot_ue& ue = slot_ues[pdcch.dci.ctx.rnti];
|
||||
fmt::format_to(fmtbuf,
|
||||
"SCHED: DL {}, cc={}, rnti=0x{:x}, pid={}, cs={}, f={}, prbs={}, nrtx={}, dai={}, "
|
||||
"tbs={}, bs={}, pdsch_slot={}, ack_slot={}",
|
||||
"lcids=[{}], tbs={}, bs={}, pdsch_slot={}, ack_slot={}",
|
||||
ue.h_dl->nof_retx() == 0 ? "tx" : "retx",
|
||||
res_grid.cfg->cc,
|
||||
ue->rnti,
|
||||
|
@ -164,10 +234,12 @@ void log_sched_bwp_result(srslog::basic_logger& logger,
|
|||
ue.h_dl->prbs(),
|
||||
ue.h_dl->nof_retx(),
|
||||
pdcch.dci.dai,
|
||||
fmt::join(bwp_slot.dl.data[data_count].subpdus, ", "),
|
||||
ue.h_dl->tbs() / 8u,
|
||||
ue.dl_bytes,
|
||||
ue.pdsch_slot,
|
||||
ue.uci_slot);
|
||||
data_count++;
|
||||
} else if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_ra) {
|
||||
const pdsch_t& pdsch = bwp_slot.dl.phy.pdsch[std::distance(bwp_slot.dl.phy.pdcch_dl.data(), &pdcch)];
|
||||
srsran::const_span<bool> prbs{pdsch.sch.grant.prb_idx, pdsch.sch.grant.prb_idx + pdsch.sch.grant.nof_prb};
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
*
|
||||
* \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_interface_utils.h"
|
||||
|
||||
namespace srsenb {
|
||||
|
||||
srsran::phy_cfg_nr_t get_common_ue_phy_cfg(const sched_nr_interface::cell_cfg_t& cfg)
|
||||
{
|
||||
srsran::phy_cfg_nr_t ue_phy_cfg;
|
||||
|
||||
ue_phy_cfg.csi = {}; // disable CSI until RA is complete
|
||||
|
||||
ue_phy_cfg.carrier = cfg.carrier;
|
||||
ue_phy_cfg.duplex = cfg.duplex;
|
||||
ue_phy_cfg.ssb = cfg.ssb;
|
||||
ue_phy_cfg.pdcch = cfg.bwps[0].pdcch;
|
||||
ue_phy_cfg.pdsch = cfg.bwps[0].pdsch;
|
||||
ue_phy_cfg.pusch = cfg.bwps[0].pusch;
|
||||
ue_phy_cfg.pucch = cfg.bwps[0].pucch;
|
||||
ue_phy_cfg.prach = cfg.bwps[0].prach;
|
||||
ue_phy_cfg.harq_ack = cfg.bwps[0].harq_ack;
|
||||
|
||||
// remove UE-specific SearchSpaces (they will be added later via RRC)
|
||||
for (uint32_t i = 0; i < SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE; ++i) {
|
||||
if (ue_phy_cfg.pdcch.search_space_present[i] and
|
||||
ue_phy_cfg.pdcch.search_space[i].type == srsran_search_space_type_ue) {
|
||||
ue_phy_cfg.pdcch.search_space_present[i] = false;
|
||||
ue_phy_cfg.pdcch.search_space[i] = {};
|
||||
}
|
||||
}
|
||||
|
||||
return ue_phy_cfg;
|
||||
}
|
||||
|
||||
} // namespace srsenb
|
|
@ -123,7 +123,10 @@ bool fill_dci_sib(prb_interval interv,
|
|||
dci.ctx.rnti_type = srsran_rnti_type_si;
|
||||
dci.ctx.rnti = SRSRAN_SIRNTI;
|
||||
dci.ctx.coreset_id = 0;
|
||||
dci.freq_domain_assigment = srsran_ra_nr_type1_riv(bwp_cfg.cfg.rb_width, interv.start(), interv.length());
|
||||
dci.ctx.coreset_start_rb = bwp_cfg.cfg.pdcch.coreset[0].offset_rb;
|
||||
dci.coreset0_bw = srsran_coreset_get_bw(&bwp_cfg.cfg.pdcch.coreset[0]);
|
||||
dci.freq_domain_assigment =
|
||||
srsran_ra_nr_type1_riv(srsran_coreset_get_bw(&bwp_cfg.cfg.pdcch.coreset[0]), interv.start(), interv.length());
|
||||
dci.time_domain_assigment = 0;
|
||||
dci.tpc = 1;
|
||||
dci.bwp_id = bwp_cfg.bwp_id;
|
||||
|
|
|
@ -22,10 +22,46 @@
|
|||
#include "srsgnb/hdr/stack/mac/sched_nr_ue.h"
|
||||
#include "srsgnb/hdr/stack/mac/sched_nr_pdcch.h"
|
||||
#include "srsran/common/string_helpers.h"
|
||||
#include "srsran/mac/mac_sch_pdu_nr.h"
|
||||
|
||||
namespace srsenb {
|
||||
namespace sched_nr_impl {
|
||||
|
||||
int ue_buffer_manager::get_dl_tx_total() const
|
||||
{
|
||||
int total_bytes = base_type::get_dl_tx_total();
|
||||
for (ue_buffer_manager::ce_t ce : pending_ces) {
|
||||
total_bytes += srsran::mac_sch_subpdu_nr::sizeof_ce(ce.lcid, false);
|
||||
}
|
||||
return total_bytes;
|
||||
}
|
||||
|
||||
void ue_buffer_manager::pdu_builder::alloc_subpdus(uint32_t rem_bytes, sched_nr_interface::dl_pdu_t& pdu)
|
||||
{
|
||||
for (ce_t ce : parent->pending_ces) {
|
||||
if (ce.cc == cc) {
|
||||
// Note: This check also avoids thread collisions across UE carriers
|
||||
uint32_t size_ce = srsran::mac_sch_subpdu_nr::sizeof_ce(ce.lcid, false);
|
||||
if (size_ce > rem_bytes) {
|
||||
break;
|
||||
}
|
||||
rem_bytes -= size_ce;
|
||||
pdu.subpdus.push_back(ce.lcid);
|
||||
parent->pending_ces.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t lcid = 0; rem_bytes > 0 and is_lcid_valid(lcid); ++lcid) {
|
||||
uint32_t pending_lcid_bytes = parent->get_dl_tx_total(lcid);
|
||||
if (pending_lcid_bytes > 0) {
|
||||
rem_bytes -= std::min(rem_bytes, pending_lcid_bytes);
|
||||
pdu.subpdus.push_back(lcid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
slot_ue::slot_ue(ue_carrier& ue_, slot_point slot_tx_, uint32_t dl_pending_bytes, uint32_t ul_pending_bytes) :
|
||||
ue(&ue_), pdcch_slot(slot_tx_)
|
||||
{
|
||||
|
@ -58,12 +94,16 @@ slot_ue::slot_ue(ue_carrier& ue_, slot_point slot_tx_, uint32_t dl_pending_bytes
|
|||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ue_carrier::ue_carrier(uint16_t rnti_, const ue_cfg_t& uecfg_, const cell_params_t& cell_params_) :
|
||||
ue_carrier::ue_carrier(uint16_t rnti_,
|
||||
const ue_cfg_t& uecfg_,
|
||||
const cell_params_t& cell_params_,
|
||||
const ue_buffer_manager::pdu_builder& pdu_builder_) :
|
||||
rnti(rnti_),
|
||||
cc(cell_params_.cc),
|
||||
logger(srslog::fetch_basic_logger(cell_params_.sched_args.logger_name)),
|
||||
bwp_cfg(rnti_, cell_params_.bwps[0], uecfg_),
|
||||
cell_params(cell_params_),
|
||||
pdu_builder(pdu_builder_),
|
||||
harq_ent(rnti_, cell_params_.nof_prb(), SCHED_NR_MAX_HARQ, cell_params_.bwps[0].logger)
|
||||
{}
|
||||
|
||||
|
@ -111,7 +151,8 @@ void ue::set_cfg(const ue_cfg_t& cfg)
|
|||
for (auto& ue_cc_cfg : cfg.carriers) {
|
||||
if (ue_cc_cfg.active) {
|
||||
if (carriers[ue_cc_cfg.cc] == nullptr) {
|
||||
carriers[ue_cc_cfg.cc].reset(new ue_carrier(rnti, ue_cfg, sched_cfg.cells[ue_cc_cfg.cc]));
|
||||
carriers[ue_cc_cfg.cc].reset(new ue_carrier(
|
||||
rnti, ue_cfg, sched_cfg.cells[ue_cc_cfg.cc], ue_buffer_manager::pdu_builder{ue_cc_cfg.cc, buffers}));
|
||||
} else {
|
||||
carriers[ue_cc_cfg.cc]->set_cfg(ue_cfg);
|
||||
}
|
||||
|
@ -121,6 +162,26 @@ void ue::set_cfg(const ue_cfg_t& cfg)
|
|||
buffers.config_lcids(cfg.ue_bearers);
|
||||
}
|
||||
|
||||
void ue::mac_buffer_state(uint32_t ce_lcid, uint32_t nof_cmds)
|
||||
{
|
||||
for (uint32_t i = 0; i < nof_cmds; ++i) {
|
||||
// If not specified otherwise, the CE is transmitted in PCell
|
||||
buffers.pending_ces.push_back(ue_buffer_manager::ce_t{ce_lcid, cfg().carriers[0].cc});
|
||||
}
|
||||
}
|
||||
|
||||
void ue::rlc_buffer_state(uint32_t lcid, uint32_t newtx, uint32_t retx)
|
||||
{
|
||||
if (lcid == 0 and buffers.get_dl_tx_total(0) == 0) {
|
||||
// In case of DL-CCCH, schedule ConRes CE
|
||||
// Note1: rlc_buffer_state may be called multiple times for the same CCCH. Thus, we need to confirm lcid=0 buffer
|
||||
// state is zero to avoid that multiple CEs being scheduled.
|
||||
// Note2: use push_front because ConRes CE has priority
|
||||
buffers.pending_ces.push_front({srsran::mac_sch_subpdu_nr::CON_RES_ID, cfg().carriers[0].cc});
|
||||
}
|
||||
buffers.dl_buffer_state(lcid, newtx, retx);
|
||||
}
|
||||
|
||||
void ue::new_slot(slot_point pdcch_slot)
|
||||
{
|
||||
last_pdcch_slot = pdcch_slot;
|
||||
|
|
|
@ -18,16 +18,17 @@
|
|||
# and at http://www.gnu.org/licenses/.
|
||||
#
|
||||
|
||||
add_library(sched_nr_test_suite sched_nr_common_test.cc sched_nr_ue_ded_test_suite.cc)
|
||||
add_library(sched_nr_test_suite sched_nr_common_test.cc sched_nr_ue_ded_test_suite.cc sched_nr_sim_ue.cc)
|
||||
target_link_libraries(sched_nr_test_suite srsgnb_mac srsran_common)
|
||||
|
||||
add_executable(sched_nr_test sched_nr_test.cc sched_nr_sim_ue.cc)
|
||||
target_link_libraries(sched_nr_test
|
||||
add_executable(sched_nr_parallel_test sched_nr_parallel_test.cc)
|
||||
target_link_libraries(sched_nr_parallel_test
|
||||
srsgnb_mac
|
||||
sched_nr_test_suite
|
||||
srsran_common
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${Boost_LIBRARIES})
|
||||
add_nr_test(sched_nr_test sched_nr_test)
|
||||
add_nr_test(sched_nr_parallel_test sched_nr_parallel_test)
|
||||
|
||||
add_executable(sched_nr_prb_test sched_nr_prb_test.cc)
|
||||
target_link_libraries(sched_nr_prb_test
|
||||
|
@ -39,4 +40,12 @@ add_nr_test(sched_nr_prb_test sched_nr_prb_test)
|
|||
|
||||
add_executable(sched_nr_rar_test sched_nr_rar_test.cc)
|
||||
target_link_libraries(sched_nr_rar_test srsgnb_mac sched_nr_test_suite srsran_common)
|
||||
add_nr_test(sched_nr_rar_test sched_nr_rar_test)
|
||||
add_nr_test(sched_nr_rar_test sched_nr_rar_test)
|
||||
|
||||
add_executable(sched_nr_test sched_nr_test.cc)
|
||||
target_link_libraries(sched_nr_test
|
||||
srsgnb_mac
|
||||
sched_nr_test_suite
|
||||
srsran_common ${CMAKE_THREAD_LIBS_INIT}
|
||||
${Boost_LIBRARIES})
|
||||
add_nr_test(sched_nr_test sched_nr_test)
|
|
@ -22,7 +22,7 @@
|
|||
#ifndef SRSRAN_SCHED_NR_CFG_GENERATORS_H
|
||||
#define SRSRAN_SCHED_NR_CFG_GENERATORS_H
|
||||
|
||||
#include "srsgnb/hdr/stack/mac/sched_nr_interface.h"
|
||||
#include "srsgnb/hdr/stack/mac/sched_nr_interface_utils.h"
|
||||
#include "srsran/common/phy_cfg_nr_default.h"
|
||||
|
||||
namespace srsenb {
|
||||
|
@ -54,6 +54,9 @@ inline sched_nr_interface::cell_cfg_t get_default_cell_cfg(
|
|||
cell_cfg.bwps[0].pdcch = phy_cfg.pdcch;
|
||||
cell_cfg.bwps[0].pdsch = phy_cfg.pdsch;
|
||||
cell_cfg.bwps[0].pusch = phy_cfg.pusch;
|
||||
cell_cfg.bwps[0].pucch = phy_cfg.pucch;
|
||||
cell_cfg.bwps[0].prach = phy_cfg.prach;
|
||||
cell_cfg.bwps[0].harq_ack = phy_cfg.harq_ack;
|
||||
cell_cfg.bwps[0].rb_width = phy_cfg.carrier.nof_prb;
|
||||
|
||||
return cell_cfg;
|
||||
|
@ -71,11 +74,13 @@ inline std::vector<sched_nr_interface::cell_cfg_t> get_default_cells_cfg(
|
|||
return cells;
|
||||
}
|
||||
|
||||
inline sched_nr_interface::ue_cfg_t get_rach_ue_cfg(uint32_t cc)
|
||||
inline sched_nr_interface::ue_cfg_t get_rach_ue_cfg(uint32_t cc,
|
||||
const srsran::phy_cfg_nr_t& phy_cfg = srsran::phy_cfg_nr_default_t{
|
||||
srsran::phy_cfg_nr_default_t::reference_cfg_t{}})
|
||||
{
|
||||
sched_nr_interface::ue_cfg_t uecfg{};
|
||||
|
||||
// set Pcell
|
||||
// set PCell
|
||||
uecfg.carriers.resize(1);
|
||||
uecfg.carriers[0].active = true;
|
||||
uecfg.carriers[0].cc = cc;
|
||||
|
@ -86,6 +91,12 @@ inline sched_nr_interface::ue_cfg_t get_rach_ue_cfg(uint32_t cc)
|
|||
// set basic PHY config
|
||||
uecfg.phy_cfg = srsran::phy_cfg_nr_default_t{srsran::phy_cfg_nr_default_t::reference_cfg_t{}};
|
||||
uecfg.phy_cfg.csi = {};
|
||||
for (srsran_search_space_t& ss : view_active_search_spaces(uecfg.phy_cfg.pdcch)) {
|
||||
// disable UE-specific search spaces
|
||||
if (ss.type == srsran_search_space_type_ue) {
|
||||
uecfg.phy_cfg.pdcch.search_space_present[ss.id] = false;
|
||||
}
|
||||
}
|
||||
|
||||
return uecfg;
|
||||
}
|
||||
|
|
|
@ -25,13 +25,23 @@
|
|||
|
||||
namespace srsenb {
|
||||
|
||||
void test_dl_pdcch_consistency(srsran::const_span<sched_nr_impl::pdcch_dl_t> dl_pdcchs)
|
||||
void test_dl_pdcch_consistency(const sched_nr_interface::cell_cfg_t& cell_cfg,
|
||||
srsran::const_span<sched_nr_impl::pdcch_dl_t> dl_pdcchs)
|
||||
{
|
||||
for (const auto& pdcch : dl_pdcchs) {
|
||||
TESTASSERT(pdcch.dci.bwp_id < cell_cfg.bwps.size());
|
||||
const srsran_pdcch_cfg_nr_t& pdcch_cfg = cell_cfg.bwps[pdcch.dci.bwp_id].pdcch;
|
||||
TESTASSERT(pdcch_cfg.coreset_present[pdcch.dci.ctx.coreset_id]);
|
||||
|
||||
if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_ra) {
|
||||
TESTASSERT_EQ(pdcch.dci.ctx.format, srsran_dci_format_nr_1_0);
|
||||
TESTASSERT_EQ(pdcch.dci.ctx.ss_type, srsran_search_space_type_common_1);
|
||||
TESTASSERT(pdcch.dci.ctx.location.L > 0);
|
||||
TESTASSERT(pdcch.dci.ctx.location.L < SRSRAN_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR);
|
||||
|
||||
// check consistency with cell_cfg
|
||||
TESTASSERT(pdcch_cfg.ra_search_space_present);
|
||||
TESTASSERT_EQ(pdcch_cfg.ra_search_space.coreset_id, pdcch.dci.ctx.coreset_id);
|
||||
TESTASSERT(pdcch_cfg.ra_search_space.nof_candidates[pdcch.dci.ctx.location.L] > 0);
|
||||
} else if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_c) {
|
||||
TESTASSERT(pdcch.dci.ctx.format == srsran_dci_format_nr_1_0 or pdcch.dci.ctx.format == srsran_dci_format_nr_1_1);
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue