diff --git a/lib/include/srsran/phy/common/phy_common_nr.h b/lib/include/srsran/phy/common/phy_common_nr.h index f92667ec6..51fef7e2d 100644 --- a/lib/include/srsran/phy/common/phy_common_nr.h +++ b/lib/include/srsran/phy/common/phy_common_nr.h @@ -120,6 +120,21 @@ extern "C" { */ #define SRSRAN_MAX_NOF_DL_DATA_TO_UL 8 +/** + * @brief Maximum number of HARQ processes in the DL, signaled through RRC (PDSCH-ServingCellConfig) + */ +#define SRSRAN_MAX_HARQ_PROC_DL_NR 16 // 3GPP TS 38.214 version 15.3.0 Sec. 5.1 or nrofHARQ-ProcessesForPDSCH + +/** + * @brief Default number of HARQ processes in the DL, if config is absent. + */ +#define SRSRAN_DEFAULT_HARQ_PROC_DL_NR 8 + +/** + * @brief Maximum number of HARQ processes in the UL, signaled through RRC (ConfiguredGrantConfig) + */ +#define SRSRAN_MAX_HARQ_PROC_UL_NR 16 // 3GPP TS 38.214 version 15.3.0 Sec. 6.1 + typedef enum SRSRAN_API { srsran_coreset_mapping_type_non_interleaved = 0, srsran_coreset_mapping_type_interleaved, diff --git a/srsue/hdr/stack/mac_nr/mac_nr.h b/srsue/hdr/stack/mac_nr/mac_nr.h index e782973cd..354499434 100644 --- a/srsue/hdr/stack/mac_nr/mac_nr.h +++ b/srsue/hdr/stack/mac_nr/mac_nr.h @@ -25,6 +25,7 @@ #include "srsue/hdr/stack/mac_common/mac_common.h" #include "srsue/hdr/stack/mac_nr/mux_nr.h" #include "srsue/hdr/stack/ue_stack_base.h" +#include "ul_harq_nr.h" namespace srsue { @@ -36,7 +37,8 @@ class mac_nr final : public mac_interface_phy_nr, public mac_interface_rrc_nr, public mac_interface_proc_ra_nr, public mac_interface_sr_nr, - public mac_interface_mux_nr + public mac_interface_mux_nr, + public mac_interface_harq_nr { public: mac_nr(srsran::ext_task_sched_handle task_sched_); @@ -82,9 +84,11 @@ public: int remove_tag_config(const uint32_t tag_id); void start_ra_procedure(); - /// procedure ra nr interface + mux + /// Interface for internal procedures (RA, MUX, HARQ) uint64_t get_contention_id(); uint16_t get_crnti(); + uint16_t get_temp_crnti(); + uint16_t get_csrnti() { return SRSRAN_INVALID_RNTI; }; // SPS not supported /// procedure sr nr interface void start_ra() { proc_ra.start_by_mac(); } @@ -136,7 +140,7 @@ private: srslog::basic_logger& logger; mac_nr_args_t args = {}; - bool started = false; + std::atomic started = {false}; uint16_t c_rnti = SRSRAN_INVALID_RNTI; uint64_t contention_id = 0; @@ -149,11 +153,6 @@ private: /// Rx buffer srsran::mac_sch_pdu_nr rx_pdu; - /// Tx buffer - srsran::unique_byte_buffer_t ul_harq_buffer = nullptr; // store PDU generated from MUX - srsran::unique_byte_buffer_t rlc_buffer = nullptr; - srsran_softbuffer_tx_t softbuffer_tx = {}; /// UL HARQ (temporal) - srsran::task_multiqueue::queue_handle stack_task_dispatch_queue; // MAC Uplink-related procedures @@ -161,6 +160,12 @@ private: proc_sr_nr proc_sr; proc_bsr_nr proc_bsr; mux_nr mux; + + // UL HARQ + ul_harq_entity_nr_vector ul_harq = {}; + ul_harq_cfg_t ul_harq_cfg; + + const uint8_t PCELL_CC_IDX = 0; }; } // namespace srsue diff --git a/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h b/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h index e791bd002..2370eb96e 100644 --- a/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h +++ b/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h @@ -44,7 +44,7 @@ public: class mac_interface_sr_nr { public: - // MUX can query MAC for current C-RNTI for Msg3 transmission + // SR can query MAC (as proxy for RA) to start RA procedure virtual void start_ra() = 0; }; @@ -61,6 +61,22 @@ public: virtual srsran::mac_sch_subpdu_nr::lcg_bsr_t generate_sbsr() = 0; }; +/** + * @brief Interface from MAC NR parent class to HARQ subclass + */ +class mac_interface_harq_nr +{ +public: + // HARQ can query MAC for current C-RNTI + virtual uint16_t get_crnti() = 0; + + // MAC also provides Temp C-RNTI (through RA proc) + virtual uint16_t get_temp_crnti() = 0; + + // MAC provides the Currently Scheduled RNTI (for SPS) + virtual uint16_t get_csrnti() = 0; +}; + } // namespace srsue #endif // SRSUE_MAC_NR_INTERFACES_H \ No newline at end of file diff --git a/srsue/hdr/stack/mac_nr/proc_ra_nr.h b/srsue/hdr/stack/mac_nr/proc_ra_nr.h index f51077dbb..ce684868f 100644 --- a/srsue/hdr/stack/mac_nr/proc_ra_nr.h +++ b/srsue/hdr/stack/mac_nr/proc_ra_nr.h @@ -38,9 +38,9 @@ public: bool is_rar_opportunity(uint32_t tti); bool has_rar_rnti(); uint16_t get_rar_rnti(); - bool has_temp_rnti(); - uint16_t get_temp_rnti(); - + bool has_temp_crnti(); + uint16_t get_temp_crnti(); + // PHY interfaces void prach_sent(uint32_t tti, uint32_t s_id, uint32_t t_id, uint32_t f_id, uint32_t ul_carrier_id); void handle_rar_pdu(mac_interface_phy_nr::mac_nr_grant_dl_t& grant); @@ -59,7 +59,7 @@ private: int ra_window_length = -1, ra_window_start = -1; uint16_t rar_rnti = SRSRAN_INVALID_RNTI; - uint16_t temp_rnti = SRSRAN_INVALID_RNTI; + uint16_t temp_crnti = SRSRAN_INVALID_RNTI; srsran::rach_nr_cfg_t rach_cfg = {}; bool configured = false; diff --git a/srsue/hdr/stack/mac_nr/ul_harq_nr.h b/srsue/hdr/stack/mac_nr/ul_harq_nr.h new file mode 100644 index 000000000..bdb90f632 --- /dev/null +++ b/srsue/hdr/stack/mac_nr/ul_harq_nr.h @@ -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. + * + */ + +#ifndef SRSUE_UL_HARQ_NR_H +#define SRSUE_UL_HARQ_NR_H + +#include "mux_nr.h" +#include "proc_ra_nr.h" +#include "srsran/common/interfaces_common.h" +#include "srsran/common/timers.h" +#include "srsran/interfaces/ue_nr_interfaces.h" + +using namespace srsran; + +namespace srsue { + +class ul_harq_entity_nr +{ +public: + ul_harq_entity_nr(const uint8_t cc_idx_, mac_interface_harq_nr* mac_, proc_ra_nr* ra_proc__, mux_nr* mux_); + + int init(); + + void reset(); + void reset_ndi(); + void set_config(srsran::ul_harq_cfg_t& harq_cfg); + + /***************** PHY->MAC interface for UL processes **************************/ + void new_grant_ul(const mac_interface_phy_nr::mac_nr_grant_ul_t& grant, mac_interface_phy_nr::tb_action_ul_t* action); + + int get_current_tbs(uint32_t pid); + float get_average_retx(); + +private: + class ul_harq_process_nr + { + public: + ul_harq_process_nr(); + ~ul_harq_process_nr(); + + bool init(uint32_t pid_, ul_harq_entity_nr* entity_); + void reset(); + void reset_ndi(); + uint8_t get_ndi(); + bool has_grant(); + + uint32_t get_nof_retx(); + int get_current_tbs(); + + /** + * Implements Section 5.4.2.1 + * + * @param grant The unmodified grant as received from PHY + * @param ndi_toggled The NDI toggled state determined by the entity + * @param action The resulting UL action structure to be filled. + */ + void new_grant_ul(const mac_interface_phy_nr::mac_nr_grant_ul_t& grant, + const bool& ndi_toggled, + mac_interface_phy_nr::tb_action_ul_t* action); + + private: + mac_interface_phy_nr::mac_nr_grant_ul_t current_grant = {}; + bool grant_configured = false; + + uint32_t pid = 0; + uint32_t nof_retx = 0; + bool is_initiated = false; + + srslog::basic_logger& logger; + ul_harq_entity_nr* harq_entity = nullptr; + srsran_softbuffer_tx_t softbuffer; + + std::unique_ptr harq_buffer = nullptr; + + void generate_tx(mac_interface_phy_nr::tb_action_ul_t* action); + void generate_new_tx(const mac_interface_phy_nr::mac_nr_grant_ul_t& grant, + mac_interface_phy_nr::tb_action_ul_t* action); + void generate_retx(const mac_interface_phy_nr::mac_nr_grant_ul_t& grant, + mac_interface_phy_nr::tb_action_ul_t* action); + }; + + std::array harq_procs; + + mac_interface_harq_nr* mac = nullptr; + mux_nr* mux = nullptr; + srslog::basic_logger& logger; + + srsran::ul_harq_cfg_t harq_cfg = {}; + + float average_retx = 0.0; + uint64_t nof_pkts = 0; +}; + +typedef std::unique_ptr ul_harq_entity_nr_ptr; +typedef std::array ul_harq_entity_nr_vector; + +} // namespace srsue + +#endif // SRSUE_UL_HARQ_NR_H diff --git a/srsue/src/phy/nr/cc_worker.cc b/srsue/src/phy/nr/cc_worker.cc index f3626173a..cd33dabec 100644 --- a/srsue/src/phy/nr/cc_worker.cc +++ b/srsue/src/phy/nr/cc_worker.cc @@ -371,6 +371,12 @@ bool cc_worker::work_ul() mac_ul_grant.rv = pusch_cfg.grant.tb[0].rv; phy->stack->new_grant_ul(0, mac_ul_grant, &ul_action); + // Don't process further if MAC can't provide PDU + if (not ul_action.tb.enabled) { + ERROR("No MAC PDU provided by MAC"); + return false; + } + // Set UCI configuration following procedures srsran_ra_ul_set_grant_uci_nr(&phy->cfg.pusch, &uci_data.cfg, &pusch_cfg); diff --git a/srsue/src/stack/mac_nr/CMakeLists.txt b/srsue/src/stack/mac_nr/CMakeLists.txt index e2fc5a822..9553e6fbd 100644 --- a/srsue/src/stack/mac_nr/CMakeLists.txt +++ b/srsue/src/stack/mac_nr/CMakeLists.txt @@ -6,7 +6,7 @@ # the distribution. # -set(SOURCES mac_nr.cc proc_ra_nr.cc proc_bsr_nr.cc proc_sr_nr.cc mux_nr.cc) +set(SOURCES mac_nr.cc proc_ra_nr.cc proc_bsr_nr.cc proc_sr_nr.cc mux_nr.cc ul_harq_nr.cc) add_library(srsue_mac_nr STATIC ${SOURCES}) target_link_libraries(srsue_mac_nr srsue_mac_common srsran_mac) diff --git a/srsue/src/stack/mac_nr/mac_nr.cc b/srsue/src/stack/mac_nr/mac_nr.cc index 75fa30299..e15d1d978 100644 --- a/srsue/src/stack/mac_nr/mac_nr.cc +++ b/srsue/src/stack/mac_nr/mac_nr.cc @@ -25,7 +25,10 @@ mac_nr::mac_nr(srsran::ext_task_sched_handle task_sched_) : proc_bsr(logger), mux(*this, logger), pcap(nullptr) -{} +{ + // Create PCell HARQ entities + ul_harq.at(PCELL_CC_IDX) = ul_harq_entity_nr_ptr(new ul_harq_entity_nr(PCELL_CC_IDX, this, &proc_ra, &mux)); +} mac_nr::~mac_nr() { @@ -59,14 +62,9 @@ int mac_nr::init(const mac_nr_args_t& args_, return SRSRAN_ERROR; } - if (srsran_softbuffer_tx_init_guru(&softbuffer_tx, SRSRAN_SCH_NR_MAX_NOF_CB_LDPC, SRSRAN_LDPC_MAX_LEN_ENCODED_CB) < - SRSRAN_SUCCESS) { - ERROR("Error init soft-buffer"); - return SRSRAN_ERROR; - } - - ul_harq_buffer = srsran::make_byte_buffer(); - if (ul_harq_buffer == nullptr) { + // Configure PCell HARQ entities + if (ul_harq.at(PCELL_CC_IDX)->init() != SRSRAN_SUCCESS) { + logger.error("Couldn't initialize UL HARQ entity."); return SRSRAN_ERROR; } @@ -85,8 +83,6 @@ void mac_nr::stop() if (started) { started = false; } - - srsran_softbuffer_tx_free(&softbuffer_tx); } // Implement Section 5.9 @@ -167,9 +163,9 @@ mac_interface_phy_nr::sched_rnti_t mac_nr::get_dl_sched_rnti_nr(const uint32_t t return {proc_ra.get_rar_rnti(), srsran_rnti_type_ra}; } - if (proc_ra.has_temp_rnti() && has_crnti() == false) { - logger.debug("SCHED: Searching temp C-RNTI=0x%x (proc_ra)", proc_ra.get_temp_rnti()); - return {proc_ra.get_temp_rnti(), srsran_rnti_type_c}; + if (proc_ra.has_temp_crnti() && has_crnti() == false) { + logger.debug("SCHED: Searching temp C-RNTI=0x%x (proc_ra)", proc_ra.get_temp_crnti()); + return {proc_ra.get_temp_crnti(), srsran_rnti_type_c}; } if (has_crnti()) { @@ -191,6 +187,11 @@ uint16_t mac_nr::get_crnti() return c_rnti; } +uint16_t mac_nr::get_temp_crnti() +{ + return proc_ra.get_temp_crnti(); +} + srsran::mac_sch_subpdu_nr::lcg_bsr_t mac_nr::generate_sbsr() { return proc_bsr.generate_sbsr(); @@ -274,6 +275,19 @@ void mac_nr::tb_decoded(const uint32_t cc_idx, mac_nr_grant_dl_t& grant) void mac_nr::new_grant_ul(const uint32_t cc_idx, const mac_nr_grant_ul_t& grant, tb_action_ul_t* action) { + logger.debug("new_grant_ul(): cc_idx=%d, tti=%d, rnti=%d, pid=%d, tbs=%d, ndi=%d, rv=%d, is_rar=%d", + cc_idx, + grant.tti, + grant.rnti, + grant.pid, + grant.tbs, + grant.ndi, + grant.rv, + grant.is_rar_grant); + + // Clear UL action + *action = {}; + // if proc ra is in contention resolution and c_rnti == grant.c_rnti resolve contention resolution if (proc_ra.is_contention_resolution() && grant.rnti == c_rnti) { proc_ra.pdcch_to_crnti(); @@ -282,28 +296,20 @@ void mac_nr::new_grant_ul(const uint32_t cc_idx, const mac_nr_grant_ul_t& grant, // Let BSR know there is a new grant, might have to send a BSR proc_bsr.new_grant_ul(grant.tbs); - // TODO: add proper UL-HARQ - // The code below assumes a single HARQ entity, no retx, every Tx is always a new transmission - ul_harq_buffer = mux.get_pdu(grant.tbs); - - // fill TB action (goes into UL harq eventually) - if (ul_harq_buffer != nullptr) { - action->tb.payload = ul_harq_buffer.get(); // pass handle to PDU to PHY - action->tb.enabled = true; - action->tb.rv = 0; - action->tb.softbuffer = &softbuffer_tx; - srsran_softbuffer_tx_reset(&softbuffer_tx); - } else { - action->tb.enabled = false; + // Assert UL HARQ entity + if (ul_harq.at(cc_idx) == nullptr) { + logger.error("HARQ entity %d has not been created", cc_idx); + return; } + ul_harq.at(cc_idx)->new_grant_ul(grant, action); + metrics[cc_idx].tx_pkts++; + metrics[cc_idx].tx_brate += grant.tbs * 8; + // store PCAP - if (pcap) { - pcap->write_ul_crnti_nr(ul_harq_buffer->msg, ul_harq_buffer->N_bytes, grant.rnti, grant.pid, grant.tti); + if (action->tb.enabled && pcap) { + pcap->write_ul_crnti_nr(action->tb.payload->msg, action->tb.payload->N_bytes, grant.rnti, grant.pid, grant.tti); } - - metrics[cc_idx].tx_brate += grant.tbs * 8; - metrics[cc_idx].tx_pkts++; } void mac_nr::timer_expired(uint32_t timer_id) diff --git a/srsue/src/stack/mac_nr/mux_nr.cc b/srsue/src/stack/mac_nr/mux_nr.cc index d829a3174..039d11040 100644 --- a/srsue/src/stack/mac_nr/mux_nr.cc +++ b/srsue/src/stack/mac_nr/mux_nr.cc @@ -62,14 +62,10 @@ srsran::unique_byte_buffer_t mux_nr::get_pdu(uint32_t max_pdu_len) tx_pdu.init_tx(phy_tx_pdu.get(), max_pdu_len, true); if (msg3_is_pending()) { - // If message 3 is pending pack message 3 for uplink transmission + // If Msg3 is pending, pack it // Use the CRNTI which is provided in the RRC reconfiguration (only for DC mode maybe other) tx_pdu.add_crnti_ce(mac.get_crnti()); - srsran::mac_sch_subpdu_nr::lcg_bsr_t sbsr = {}; - sbsr.lcg_id = 0; - sbsr.buffer_size = 1; - tx_pdu.add_sbsr_ce(sbsr); - logger.info("Generated msg3 with RNTI 0x%x", mac.get_crnti()); + tx_pdu.add_sbsr_ce(mac.generate_sbsr()); msg3_transmitted(); } else { // Pack normal UL data PDU diff --git a/srsue/src/stack/mac_nr/proc_ra_nr.cc b/srsue/src/stack/mac_nr/proc_ra_nr.cc index 67df0fdc7..b70b0d1f7 100644 --- a/srsue/src/stack/mac_nr/proc_ra_nr.cc +++ b/srsue/src/stack/mac_nr/proc_ra_nr.cc @@ -112,14 +112,14 @@ bool proc_ra_nr::has_rar_rnti() return false; } -bool proc_ra_nr::has_temp_rnti() +bool proc_ra_nr::has_temp_crnti() { - return temp_rnti != SRSRAN_INVALID_RNTI; + return temp_crnti != SRSRAN_INVALID_RNTI; } -uint16_t proc_ra_nr::get_temp_rnti() +uint16_t proc_ra_nr::get_temp_crnti() { - return temp_rnti; + return temp_crnti; } void proc_ra_nr::timer_expired(uint32_t timer_id) @@ -201,11 +201,11 @@ void proc_ra_nr::ra_response_reception(const mac_interface_phy_nr::mac_nr_grant_ for (auto& subpdu : pdu.get_subpdus()) { if (subpdu.has_rapid() && subpdu.get_rapid() == preamble_index) { - logger.info("PROC RA NR: Setting ul grant and prepare msg3"); - temp_rnti = subpdu.get_temp_crnti(); + logger.debug("PROC RA NR: Setting UL grant and prepare Msg3"); + temp_crnti = subpdu.get_temp_crnti(); // Set Temporary-C-RNTI if provided, otherwise C-RNTI is ok - phy->set_ul_grant(subpdu.get_ul_grant(), temp_rnti, srsran_rnti_type_c); + phy->set_ul_grant(subpdu.get_ul_grant(), temp_crnti, srsran_rnti_type_c); // reset all parameters that are used before rar rar_rnti = SRSRAN_INVALID_RNTI; @@ -272,13 +272,13 @@ void proc_ra_nr::ra_completion() } srsran::console("Random Access Complete. c-rnti=0x%x, ta=%d\n", mac.get_crnti(), current_ta); logger.info("Random Access Complete. c-rnti=0x%x, ta=%d", mac.get_crnti(), current_ta); - temp_rnti = SRSRAN_INVALID_RNTI; + temp_crnti = SRSRAN_INVALID_RNTI; reset(); } void proc_ra_nr::ra_error() { - temp_rnti = 0; + temp_crnti = SRSRAN_INVALID_RNTI; preamble_transmission_counter++; contention_resolution_timer.stop(); uint32_t backoff_wait; diff --git a/srsue/src/stack/mac_nr/test/mac_nr_test.cc b/srsue/src/stack/mac_nr/test/mac_nr_test.cc index eeb1b8860..65999b38a 100644 --- a/srsue/src/stack/mac_nr/test/mac_nr_test.cc +++ b/srsue/src/stack/mac_nr/test/mac_nr_test.cc @@ -141,9 +141,61 @@ private: std::map ul_queues; }; -// TODO: Add test int msg3_test() { + // UL-SCH PDU for Msg3 with C-RNTI CE and SBSR + const uint8_t tv[] = {0x3a, 0x10, 0x01, 0x3d, 0x00, 0x3f, 0x00, 0x00, 0x00}; + + // dummy layers + dummy_phy phy; + rlc_dummy rlc; + rrc_dummy rrc; + stack_dummy stack; + + // the actual MAC + mac_nr mac(&stack.task_sched); + + mac_nr_args_t args = {}; + mac.init(args, &phy, &rlc, &rrc); + const uint16_t crnti = 0x1001; + + // set C-RNTI and tell MAC to prepare Msg3 transmission + mac.set_crnti(crnti); + mac.msg3_prepare(); + + stack.init(&mac, &phy); + + // create UL action and grant and read MAC PDU + { + mac_interface_phy_nr::tb_action_ul_t ul_action = {}; + mac_interface_phy_nr::mac_nr_grant_ul_t mac_grant = {}; + + mac_grant.rnti = crnti; // make sure MAC picks it up as valid UL grant + mac_grant.pid = 0; + mac_grant.tti = 0; + mac_grant.tbs = 9; + int cc_idx = 0; + + // Send grant to MAC and get action for this TB, 0x + mac.new_grant_ul(cc_idx, mac_grant, &ul_action); + + TESTASSERT(ul_action.tb.enabled == true); + + // print generated PDU + srslog::fetch_basic_logger("MAC").info( + ul_action.tb.payload->msg, mac_grant.tbs, "Generated PDU (%d B)", mac_grant.tbs); +#if HAVE_PCAP + pcap_handle->write_ul_crnti_nr( + ul_action.tb.payload->msg, mac_grant.tbs, mac_grant.rnti, UE_ID, mac_grant.pid, mac_grant.tti); +#endif + + TESTASSERT(memcmp(ul_action.tb.payload->msg, tv, sizeof(tv)) == 0); + } + + // make sure MAC PDU thread picks up before stopping + stack.run_tti(0); + mac.stop(); + return SRSRAN_SUCCESS; } @@ -172,6 +224,7 @@ int mac_nr_ul_logical_channel_prioritization_test1() stack.init(&mac, &phy); const uint16_t crnti = 0x1001; + mac.set_crnti(crnti); // generate config (default DRB2 config for EN-DC) std::vector lcids; @@ -202,14 +255,46 @@ int mac_nr_ul_logical_channel_prioritization_test1() mac_grant.rnti = crnti; // make sure MAC picks it up as valid UL grant mac_grant.pid = 0; - mac_grant.rnti = 0x1001; mac_grant.tti = 0; mac_grant.tbs = 20; + mac_grant.ndi = 0; int cc_idx = 0; // Send grant to MAC and get action for this TB, 0x mac.new_grant_ul(cc_idx, mac_grant, &ul_action); + TESTASSERT(ul_action.tb.enabled == true); + + // print generated PDU + srslog::fetch_basic_logger("MAC").info( + ul_action.tb.payload->msg, mac_grant.tbs, "Generated PDU (%d B)", mac_grant.tbs); +#if HAVE_PCAP + pcap_handle->write_ul_crnti_nr( + ul_action.tb.payload->msg, mac_grant.tbs, mac_grant.rnti, UE_ID, mac_grant.pid, mac_grant.tti); +#endif + + TESTASSERT(memcmp(ul_action.tb.payload->msg, tv, sizeof(tv)) == 0); + } + + stack.run_tti(0); + + // create new grant that indicates/requests a retx of the previous PDU + { + mac_interface_phy_nr::tb_action_ul_t ul_action = {}; + mac_interface_phy_nr::mac_nr_grant_ul_t mac_grant = {}; + + mac_grant.rnti = crnti; // make sure MAC picks it up as valid UL grant + mac_grant.pid = 0; // same PID as above + mac_grant.tbs = 20; + mac_grant.rv = 1; // this is RV=1 + mac_grant.ndi = 0; // NDI keeps zero to request retx + int cc_idx = 0; + + // Send grant to MAC and get action for this TB, 0x + mac.new_grant_ul(cc_idx, mac_grant, &ul_action); + + TESTASSERT(ul_action.tb.enabled == true); + // print generated PDU srslog::fetch_basic_logger("MAC").info( ul_action.tb.payload->msg, mac_grant.tbs, "Generated PDU (%d B)", mac_grant.tbs); @@ -260,11 +345,12 @@ int mac_nr_ul_logical_channel_prioritization_test2() // the actual MAC mac_nr mac(&stack.task_sched); + const uint16_t crnti = 0x1001; mac_nr_args_t args = {}; mac.init(args, &phy, &rlc, &rrc); + mac.set_crnti(crnti); stack.init(&mac, &phy); - const uint16_t crnti = 0x1001; // generate config (default DRB2 config for EN-DC) std::vector lcids; @@ -295,7 +381,6 @@ int mac_nr_ul_logical_channel_prioritization_test2() mac_grant.rnti = crnti; // make sure MAC picks it up as valid UL grant mac_grant.pid = 0; - mac_grant.rnti = 0x1001; mac_grant.tti = 0; mac_grant.tbs = 260; int cc_idx = 0; @@ -343,10 +428,11 @@ int mac_nr_ul_periodic_bsr_test() mac_nr mac(&stack.task_sched); mac_nr_args_t args = {}; + const uint16_t crnti = 0x1001; mac.init(args, &phy, &rlc, &rrc); + mac.set_crnti(crnti); stack.init(&mac, &phy); - const uint16_t crnti = 0x1001; // generate config (default DRB2 config for EN-DC) std::vector lcids; @@ -379,6 +465,8 @@ int mac_nr_ul_periodic_bsr_test() stack.run_tti(tti++); usleep(100); + int ul_ndi = 0; + // create UL action and grant and read MAC PDU { mac_interface_phy_nr::tb_action_ul_t ul_action = {}; @@ -386,14 +474,16 @@ int mac_nr_ul_periodic_bsr_test() mac_grant.rnti = crnti; // make sure MAC picks it up as valid UL grant mac_grant.pid = 0; - mac_grant.rnti = 0x1001; mac_grant.tti = 0; mac_grant.tbs = 10; + mac_grant.ndi = (ul_ndi++) % 2; int cc_idx = 0; // Send grant to MAC and get action for this TB mac.new_grant_ul(cc_idx, mac_grant, &ul_action); + TESTASSERT(ul_action.tb.enabled == true); + // print generated PDU srslog::fetch_basic_logger("MAC").info( ul_action.tb.payload->msg, mac_grant.tbs, "Generated PDU (%d B)", mac_grant.tbs); @@ -417,14 +507,16 @@ int mac_nr_ul_periodic_bsr_test() mac_grant.rnti = crnti; // make sure MAC picks it up as valid UL grant mac_grant.pid = 0; - mac_grant.rnti = 0x1001; mac_grant.tti = 0; mac_grant.tbs = 10; + mac_grant.ndi = (ul_ndi++) % 2; int cc_idx = 0; // Send grant to MAC and get action for this TB mac.new_grant_ul(cc_idx, mac_grant, &ul_action); + TESTASSERT(ul_action.tb.enabled == true); + // print generated PDU srslog::fetch_basic_logger("MAC").info( ul_action.tb.payload->msg, mac_grant.tbs, "Generated PDU (%d B)", mac_grant.tbs); @@ -448,7 +540,6 @@ int mac_nr_ul_periodic_bsr_test() mac_grant.rnti = crnti; // make sure MAC picks it up as valid UL grant mac_grant.pid = 0; - mac_grant.rnti = 0x1001; mac_grant.tti = 0; mac_grant.tbs = 10; int cc_idx = 0; @@ -456,6 +547,8 @@ int mac_nr_ul_periodic_bsr_test() // Send grant to MAC and get action for this TB mac.new_grant_ul(cc_idx, mac_grant, &ul_action); + TESTASSERT(ul_action.tb.enabled == true); + // print generated PDU srslog::fetch_basic_logger("MAC").info( ul_action.tb.payload->msg, mac_grant.tbs, "Generated PDU (%d B)", mac_grant.tbs); diff --git a/srsue/src/stack/mac_nr/ul_harq_nr.cc b/srsue/src/stack/mac_nr/ul_harq_nr.cc new file mode 100644 index 000000000..8a20da657 --- /dev/null +++ b/srsue/src/stack/mac_nr/ul_harq_nr.cc @@ -0,0 +1,268 @@ +/** + * + * \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 "srsue/hdr/stack/mac_nr/ul_harq_nr.h" +#include "srsran/common/buffer_pool.h" +#include "srsran/common/interfaces_common.h" +#include "srsran/common/timers.h" + +namespace srsue { + +ul_harq_entity_nr::ul_harq_entity_nr(const uint8_t cc_idx_, + mac_interface_harq_nr* mac_, + proc_ra_nr* ra_proc_, + mux_nr* mux_) : + mac(mac_), mux(mux_), logger(srslog::fetch_basic_logger("MAC")) +{} + +int ul_harq_entity_nr::init() +{ + uint32_t proc_count = 0; + for (auto& proc : harq_procs) { + if (not proc.init(proc_count++, this)) { + logger.error("Couldn't initialize HARQ procedure"); + return SRSRAN_ERROR; + } + } + return SRSRAN_SUCCESS; +} + +void ul_harq_entity_nr::reset() +{ + for (auto& proc : harq_procs) { + proc.reset(); + } +} + +void ul_harq_entity_nr::reset_ndi() +{ + for (auto& proc : harq_procs) { + proc.reset_ndi(); + } +} + +void ul_harq_entity_nr::set_config(srsran::ul_harq_cfg_t& harq_cfg_) +{ + harq_cfg = harq_cfg_; +} + +/***************** PHY->MAC interface for UL processes **************************/ +void ul_harq_entity_nr::new_grant_ul(const mac_interface_phy_nr::mac_nr_grant_ul_t& grant, + mac_interface_phy_nr::tb_action_ul_t* action) +{ + if (grant.pid >= harq_procs.size()) { + logger.error("Invalid PID: %d", grant.pid); + return; + } + + // Determine whether the NDI has been toggled for this process + bool ndi_toggled = (grant.ndi != harq_procs.at(grant.pid).get_ndi()); + + // 1> if UL MAC entity's C-RNTI or Temporary C-RNTI; or Received in RAR; + if (mac->get_crnti() == grant.rnti || mac->get_temp_crnti() == grant.rnti || grant.is_rar_grant) { + // 2> if UL grant for C-RNTI and if the previous grant delivered to the HARQ entity + // for the same HARQ process was either an uplink grant received for the MAC entity's CS-RNTI or a + // configured uplink grant: + if (mac->get_crnti() == grant.rnti && harq_procs.at(grant.pid).has_grant()) { + // 3> consider the NDI to have been toggled regardless of the value of the NDI. + ndi_toggled = true; + } + + // 2> if the UL grant is for MAC entity's C-RNTI, and the HARQ process is configured for a configured uplink grant: + if (mac->get_crnti() == grant.rnti && harq_procs.at(grant.pid).has_grant()) { + // TODO: add handling for configuredGrantTimer + } + + // 2> deliver the uplink grant and the associated HARQ information to the HARQ entity + harq_procs.at(grant.pid).new_grant_ul(grant, ndi_toggled, action); + } else if (mac->get_csrnti() == grant.rnti) { + // SPS not supported + logger.warning("Ignoring grant for CS-RNTI=0x%x", grant.rnti); + } else { + logger.warning("Received grant for unknown rnti=0x%x", grant.rnti); + printf("Received grant for unknown rnti=0x%x\n", grant.rnti); + } + + srsran_expect(action->tb.enabled ? action->tb.payload != nullptr : true, + "UL action enabled but no HARQ buffer provided"); +} + +int ul_harq_entity_nr::get_current_tbs(uint32_t pid) +{ + if (pid >= harq_procs.size()) { + logger.error("Invalid PID: %d", pid); + return 0; + } + return harq_procs.at(pid).get_current_tbs(); +} + +float ul_harq_entity_nr::get_average_retx() +{ + return average_retx; +} + +ul_harq_entity_nr::ul_harq_process_nr::ul_harq_process_nr() : logger(srslog::fetch_basic_logger("MAC")) {} + +ul_harq_entity_nr::ul_harq_process_nr::~ul_harq_process_nr() +{ + if (is_initiated) { + srsran_softbuffer_tx_free(&softbuffer); + } +} + +bool ul_harq_entity_nr::ul_harq_process_nr::init(uint32_t pid_, ul_harq_entity_nr* entity_) +{ + if (srsran_softbuffer_tx_init_guru(&softbuffer, SRSRAN_SCH_NR_MAX_NOF_CB_LDPC, SRSRAN_LDPC_MAX_LEN_ENCODED_CB) < + SRSRAN_SUCCESS) { + logger.error("Couldn't initialize softbuffer"); + return false; + } + + pid = pid_; + harq_entity = entity_; + + is_initiated = true; + + return true; +} + +void ul_harq_entity_nr::ul_harq_process_nr::reset() +{ + nof_retx = 0; + harq_buffer = nullptr; + current_grant = {}; +} + +void ul_harq_entity_nr::ul_harq_process_nr::reset_ndi() +{ + current_grant.ndi = false; +} + +uint8_t ul_harq_entity_nr::ul_harq_process_nr::get_ndi() +{ + return current_grant.ndi; +} + +bool ul_harq_entity_nr::ul_harq_process_nr::has_grant() +{ + return grant_configured; +} + +// Basic handling of new grant +void ul_harq_entity_nr::ul_harq_process_nr::new_grant_ul(const mac_interface_phy_nr::mac_nr_grant_ul_t& grant, + const bool& ndi_toggled, + mac_interface_phy_nr::tb_action_ul_t* action) +{ + // Get maximum retransmissions + uint32_t max_retx = harq_entity->harq_cfg.max_harq_tx; + + // Check maximum retransmissions, do not consider last retx ACK + if (nof_retx >= max_retx) { + logger.info("UL %d: Maximum number of ReTX reached (%d). Discarding TB.", pid, max_retx); + if (grant.rnti == harq_entity->mac->get_temp_crnti()) { + // FIXME: signal maxRetx to RA? + // harq_entity->ra_proc->harq_max_retx(); + } + reset(); + } + + // Checks in 5.4.2.1 + if ((grant.rnti != harq_entity->mac->get_temp_crnti() && + ndi_toggled) || // If not addressed to T-CRNTI and NDI toggled + (grant.rnti == harq_entity->mac->get_crnti() && + harq_buffer == nullptr) || // If addressed to C-RNTI and buffer is empty + (grant.is_rar_grant) // Grant received in a RAR + ) { + // new transmission + + // generate new PDU (Msg3 or normal UL) + harq_buffer = harq_entity->mux->get_pdu(grant.tbs); + + // 3> if a MAC PDU to transmit has been obtained + if (harq_buffer != nullptr) { + // 4> deliver the MAC PDU + // 4> instruct the identified HARQ process to trigger a new transmission; + generate_new_tx(grant, action); + + // TODO: add handling of configuredGrantTimer + } else { + // HARQ buffer is automatically flushed + } + } else { + // retransmission + if (harq_buffer == nullptr) { + // ignore the UL grant + logger.info("UL %d: HARQ buffer empty. Ignoring grant."); + return; + } + + // 4> instruct the identified HARQ process to trigger a retransmission; + generate_retx(grant, action); + + // TODO: add handling of configuredGrantTimer + } +} + +uint32_t ul_harq_entity_nr::ul_harq_process_nr::get_nof_retx() +{ + return nof_retx; +} + +int ul_harq_entity_nr::ul_harq_process_nr::get_current_tbs() +{ + return current_grant.tbs; +} + +// New transmission (Section 5.4.2.2) +void ul_harq_entity_nr::ul_harq_process_nr::generate_new_tx(const mac_interface_phy_nr::mac_nr_grant_ul_t& grant, + mac_interface_phy_nr::tb_action_ul_t* action) +{ + // Compute average number of retransmissions per packet considering previous packet + harq_entity->average_retx = SRSRAN_VEC_CMA((float)nof_retx, harq_entity->average_retx, harq_entity->nof_pkts++); + current_grant = grant; + nof_retx = 0; + + logger.info("UL %d: New TX%s, rv=%d, tbs=%d", + pid, + grant.rnti == harq_entity->mac->get_temp_crnti() ? " for Msg3" : "", + grant.rv, + grant.tbs); + + generate_tx(action); +} + +// Retransmission (Section 5.4.2.2) +void ul_harq_entity_nr::ul_harq_process_nr::generate_retx(const mac_interface_phy_nr::mac_nr_grant_ul_t& grant, + mac_interface_phy_nr::tb_action_ul_t* action) +{ + logger.info("UL %d: Retx=%d, rv=%d, tbs=%d", pid, nof_retx, grant.rv, grant.tbs); + + // overwrite original grant + current_grant = grant; + + generate_tx(action); +} + +// Transmission of pending frame (Section 5.4.2.2) +void ul_harq_entity_nr::ul_harq_process_nr::generate_tx(mac_interface_phy_nr::tb_action_ul_t* action) +{ + nof_retx++; + + action->tb.rv = current_grant.rv; + action->tb.enabled = true; + action->tb.payload = harq_buffer.get(); + action->tb.softbuffer = &softbuffer; + + srsran_softbuffer_tx_reset(&softbuffer); +} + +} // namespace srsue