Merge branch 'next' into agpl_next

This commit is contained in:
Codebot 2021-11-28 23:12:22 +01:00 committed by Your Name
commit dd2c1f7695
126 changed files with 4456 additions and 1094 deletions

View File

@ -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,

View File

@ -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")

View File

@ -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_;

View File

@ -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) {

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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);

View File

@ -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)
{

View File

@ -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
****************************/

View File

@ -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 {

View File

@ -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

View File

@ -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 = {};

View File

@ -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;

View File

@ -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();

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -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;
}
}

View File

@ -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)

View File

@ -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();

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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];
}
}
}

View File

@ -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

View File

@ -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
########################################################################

View File

@ -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(&regs, 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, &regs, 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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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 {

View File

@ -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();

View File

@ -20,7 +20,6 @@
add_subdirectory(asn1)
add_subdirectory(common)
add_subdirectory(mac)
add_subdirectory(phy)
add_subdirectory(srslog)
add_subdirectory(rlc)

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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;
};

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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);
}

View File

@ -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) {

View File

@ -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})

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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());

View File

@ -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()};
}

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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};

View File

@ -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

View File

@ -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; }

View File

@ -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,

View File

@ -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;

View File

@ -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

View File

@ -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 {

View File

@ -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;

View File

@ -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

View File

@ -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:

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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)

View File

@ -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?

View File

@ -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;

View File

@ -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;

View File

@ -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};

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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)

View File

@ -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;
}

View File

@ -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