mac_nr: add UL HARQ

this patch adds a basic UL HARQ entity for NR.

The patch also updates some interfaces between MAC/RA/HARQ,
i.e. get_temp_crnti().

It also adds a Msg3 unit test.
This commit is contained in:
Andre Puschmann 2021-04-13 22:29:40 +02:00
parent b7146c41b2
commit 44baea6666
12 changed files with 582 additions and 69 deletions

View File

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

View File

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

View File

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

View File

@ -38,8 +38,8 @@ 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);
@ -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;

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.
*
*/
#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<byte_buffer_t> 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<ul_harq_process_nr, SRSRAN_MAX_HARQ_PROC_UL_NR> 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> ul_harq_entity_nr_ptr;
typedef std::array<ul_harq_entity_nr_ptr, SRSRAN_MAX_CARRIERS> ul_harq_entity_nr_vector;
} // namespace srsue
#endif // SRSUE_UL_HARQ_NR_H

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -141,9 +141,61 @@ private:
std::map<uint32_t, uint32_t> 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<srsran::logical_channel_config_t> 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<srsran::logical_channel_config_t> 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<srsran::logical_channel_config_t> 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);

View File

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