mirror of https://github.com/pvnis/srsRAN_4G.git
mac_nr: add DL HARQ
this commit adds a complete DL HARQ entity to the MAC of the UE. It also refactors demux into an own class and adapts the PHY-MAC interface to use the new MAC capabilities.master
parent
e3e4564a7e
commit
b9ae064338
@ -0,0 +1,60 @@
|
||||
/**
|
||||
*
|
||||
* \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 SRSLTE_DEMUX_NR_H
|
||||
#define SRSLTE_DEMUX_NR_H
|
||||
|
||||
#include "mac_nr_interfaces.h"
|
||||
#include "srsran/common/block_queue.h"
|
||||
#include "srsran/interfaces/ue_rlc_interfaces.h"
|
||||
|
||||
namespace srsue {
|
||||
|
||||
/**
|
||||
* @brief Logical Channel Demultiplexing and MAC CE dissassemble according to TS 38.321
|
||||
*
|
||||
* Currently only SDU handling for SCH PDU processing is implemented.
|
||||
* Downlink CE are parsed but not handled.
|
||||
*
|
||||
* PDUs can be pushed by multiple HARQ processes in parallel.
|
||||
* Handling of the PDUs is done from Stack thread which reads the enqueued PDUs
|
||||
* from the thread-safe queue.
|
||||
*/
|
||||
class demux_nr : public demux_interface_harq_nr
|
||||
{
|
||||
public:
|
||||
demux_nr(srslog::basic_logger& logger_);
|
||||
~demux_nr();
|
||||
|
||||
int32_t init(rlc_interface_mac* rlc_);
|
||||
|
||||
void process_pdus(); /// Called by MAC to process received PDUs
|
||||
|
||||
// HARQ interface
|
||||
void push_pdu(srsran::unique_byte_buffer_t pdu, uint32_t tti);
|
||||
|
||||
private:
|
||||
// internal helpers
|
||||
void handle_pdu(srsran::unique_byte_buffer_t pdu);
|
||||
|
||||
srslog::basic_logger& logger;
|
||||
rlc_interface_mac* rlc = nullptr;
|
||||
|
||||
///< currently only DCH PDUs supported (add BCH, PCH, etc)
|
||||
srsran::block_queue<srsran::unique_byte_buffer_t> pdu_queue;
|
||||
|
||||
srsran::mac_sch_pdu_nr rx_pdu;
|
||||
};
|
||||
|
||||
} // namespace srsue
|
||||
|
||||
#endif // SRSLTE_DEMUX_NR_H
|
@ -0,0 +1,103 @@
|
||||
/**
|
||||
*
|
||||
* \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 SRSLTE_DL_HARQ_NR_H
|
||||
#define SRSLTE_DL_HARQ_NR_H
|
||||
|
||||
#include "srsran/interfaces/mac_interface_types.h"
|
||||
#include "srsran/interfaces/ue_nr_interfaces.h"
|
||||
#include "srsran/srslog/logger.h"
|
||||
#include "srsue/hdr/stack/mac_nr/mac_nr_interfaces.h"
|
||||
#include <mutex>
|
||||
|
||||
namespace srsue {
|
||||
|
||||
/**
|
||||
* @brief Downlink HARQ entity as defined in 5.3.2 of 38.321
|
||||
*
|
||||
* The class supports a configurable number of HARQ processes (up to 16).
|
||||
*
|
||||
* The class is configured (init and reset) by the MAC class from the
|
||||
* Stack thread context. Main functionality, however, is carried
|
||||
* out from a PHY worker context.
|
||||
*
|
||||
* Concurrent access from threads is protected through rwlocks.
|
||||
*
|
||||
*/
|
||||
class dl_harq_entity_nr
|
||||
{
|
||||
using mac_nr_grant_dl_t = mac_interface_phy_nr::mac_nr_grant_dl_t;
|
||||
|
||||
public:
|
||||
dl_harq_entity_nr(uint8_t cc_idx_, mac_interface_harq_nr* mac_, demux_interface_harq_nr* demux_unit_);
|
||||
~dl_harq_entity_nr();
|
||||
|
||||
int32_t set_config(const srsran::dl_harq_cfg_nr_t& cfg_);
|
||||
void reset();
|
||||
|
||||
/// PHY->MAC interface for DL processes
|
||||
void new_grant_dl(const mac_nr_grant_dl_t& grant, mac_interface_phy_nr::tb_action_dl_t* action);
|
||||
void tb_decoded(const mac_nr_grant_dl_t& grant, mac_interface_phy_nr::tb_action_dl_result_t result);
|
||||
|
||||
float get_average_retx();
|
||||
|
||||
private:
|
||||
class dl_harq_process_nr
|
||||
{
|
||||
public:
|
||||
dl_harq_process_nr(dl_harq_entity_nr* parent);
|
||||
~dl_harq_process_nr();
|
||||
bool init(int pid);
|
||||
void reset(void);
|
||||
uint8_t get_ndi();
|
||||
|
||||
void
|
||||
new_grant_dl(const mac_nr_grant_dl_t& grant, const bool& ndi_toggled, mac_interface_phy_nr::tb_action_dl_t* action);
|
||||
void tb_decoded(const mac_nr_grant_dl_t& grant, mac_interface_phy_nr::tb_action_dl_result_t result);
|
||||
|
||||
private:
|
||||
dl_harq_entity_nr* harq_entity = nullptr;
|
||||
srslog::basic_logger& logger;
|
||||
|
||||
bool is_first_tb = true;
|
||||
|
||||
bool is_bcch = false;
|
||||
uint32_t pid = 0; // HARQ Proccess ID
|
||||
bool acked = false;
|
||||
uint32_t n_retx = 0;
|
||||
|
||||
mac_nr_grant_dl_t current_grant = {};
|
||||
std::unique_ptr<srsran_softbuffer_rx_t> softbuffer_rx;
|
||||
};
|
||||
|
||||
// Private members of dl_harq_entity_nr
|
||||
mac_interface_harq_nr* mac = nullptr;
|
||||
srsran::dl_harq_cfg_nr_t cfg = {};
|
||||
std::array<std::unique_ptr<dl_harq_process_nr>, SRSRAN_MAX_HARQ_PROC_DL_NR> harq_procs;
|
||||
dl_harq_process_nr bcch_proc;
|
||||
demux_interface_harq_nr* demux_unit = nullptr;
|
||||
srslog::basic_logger& logger;
|
||||
uint16_t last_temporal_crnti = SRSRAN_INVALID_RNTI;
|
||||
|
||||
float average_retx = 0.0;
|
||||
uint64_t nof_pkts = 0;
|
||||
uint8_t cc_idx = 0;
|
||||
|
||||
pthread_rwlock_t rwlock;
|
||||
};
|
||||
|
||||
typedef std::unique_ptr<dl_harq_entity_nr> dl_harq_entity_nr_ptr;
|
||||
typedef std::array<dl_harq_entity_nr_ptr, SRSRAN_MAX_CARRIERS> dl_harq_entity_nr_vector;
|
||||
|
||||
} // namespace srsue
|
||||
|
||||
#endif // SRSLTE_DL_HARQ_NR_H
|
@ -0,0 +1,81 @@
|
||||
/**
|
||||
*
|
||||
* \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/demux_nr.h"
|
||||
#include "srsran/common/buffer_pool.h"
|
||||
#include "srsran/interfaces/ue_rlc_interfaces.h"
|
||||
|
||||
namespace srsue {
|
||||
|
||||
demux_nr::demux_nr(srslog::basic_logger& logger_) : logger(logger_) {}
|
||||
|
||||
demux_nr::~demux_nr() {}
|
||||
|
||||
int32_t demux_nr::init(rlc_interface_mac* rlc_)
|
||||
{
|
||||
rlc = rlc_;
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
// Enqueues PDU and returns quickly
|
||||
void demux_nr::push_pdu(srsran::unique_byte_buffer_t pdu, uint32_t tti)
|
||||
{
|
||||
pdu_queue.push(std::move(pdu));
|
||||
}
|
||||
|
||||
void demux_nr::process_pdus()
|
||||
{
|
||||
while (not pdu_queue.empty()) {
|
||||
srsran::unique_byte_buffer_t pdu = pdu_queue.wait_pop();
|
||||
handle_pdu(std::move(pdu));
|
||||
}
|
||||
}
|
||||
|
||||
/// Handling of DLSCH PDUs only
|
||||
void demux_nr::handle_pdu(srsran::unique_byte_buffer_t pdu)
|
||||
{
|
||||
logger.info(pdu->msg, pdu->N_bytes, "Handling MAC PDU (%d B)", pdu->N_bytes);
|
||||
|
||||
rx_pdu.init_rx();
|
||||
if (rx_pdu.unpack(pdu->msg, pdu->N_bytes) != SRSRAN_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < rx_pdu.get_num_subpdus(); ++i) {
|
||||
srsran::mac_sch_subpdu_nr subpdu = rx_pdu.get_subpdu(i);
|
||||
logger.info("Handling subPDU %d/%d: rnti=0x%x lcid=%d, sdu_len=%d",
|
||||
i + 1,
|
||||
rx_pdu.get_num_subpdus(),
|
||||
subpdu.get_c_rnti(),
|
||||
subpdu.get_lcid(),
|
||||
subpdu.get_sdu_length());
|
||||
|
||||
// Handle Timing Advance CE
|
||||
switch (subpdu.get_lcid()) {
|
||||
case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::DRX_CMD:
|
||||
logger.info("DRX CE not implemented.");
|
||||
break;
|
||||
case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::TA_CMD:
|
||||
logger.info("Timing Advance CE not implemented.");
|
||||
break;
|
||||
case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::CON_RES_ID:
|
||||
logger.info("Contention Resolution CE not implemented.");
|
||||
break;
|
||||
default:
|
||||
if (subpdu.is_sdu()) {
|
||||
rlc->write_pdu(subpdu.get_lcid(), subpdu.get_sdu(), subpdu.get_sdu_length());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace srsue
|
@ -0,0 +1,243 @@
|
||||
/**
|
||||
*
|
||||
* \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/dl_harq_nr.h"
|
||||
#include "srsran/common/mac_pcap.h"
|
||||
#include "srsran/common/rwlock_guard.h"
|
||||
#include "srsran/srslog/logger.h"
|
||||
#include "srsue/hdr/stack/mac_nr/demux_nr.h"
|
||||
|
||||
namespace srsue {
|
||||
|
||||
dl_harq_entity_nr::dl_harq_entity_nr(uint8_t cc_idx_,
|
||||
mac_interface_harq_nr* mac_,
|
||||
demux_interface_harq_nr* demux_unit_) :
|
||||
logger(srslog::fetch_basic_logger("MAC-NR")), cc_idx(cc_idx_), mac(mac_), demux_unit(demux_unit_), bcch_proc(this)
|
||||
{
|
||||
// Init broadcast HARQ process
|
||||
bcch_proc.init(-1);
|
||||
pthread_rwlock_init(&rwlock, NULL);
|
||||
}
|
||||
|
||||
dl_harq_entity_nr::~dl_harq_entity_nr()
|
||||
{
|
||||
pthread_rwlock_destroy(&rwlock);
|
||||
}
|
||||
|
||||
// Called from Stack thread through MAC (TODO: add shared::mutex)
|
||||
int32_t dl_harq_entity_nr::set_config(const srsran::dl_harq_cfg_nr_t& cfg_)
|
||||
{
|
||||
srsran::rwlock_write_guard lock(rwlock);
|
||||
if (cfg_.nof_procs < 1 || cfg_.nof_procs > SRSRAN_MAX_HARQ_PROC_DL_NR) {
|
||||
logger.error("Invalid configuration: %d HARQ processes not supported", cfg_.nof_procs);
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// clear old processees
|
||||
for (auto& proc : harq_procs) {
|
||||
proc = nullptr;
|
||||
}
|
||||
|
||||
// Allocate and init configured HARQ processes
|
||||
for (uint32_t i = 0; i < cfg.nof_procs; i++) {
|
||||
harq_procs[i] = std::unique_ptr<dl_harq_process_nr>(new dl_harq_process_nr(this));
|
||||
if (!harq_procs.at(i)->init(i)) {
|
||||
logger.error("Error while initializing DL-HARQ process %d", i);
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
cfg = cfg_;
|
||||
|
||||
logger.debug("cc_idx=%d, set number of HARQ processes for DL to %d", cc_idx, cfg.nof_procs);
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
// Called from PHY workers
|
||||
void dl_harq_entity_nr::new_grant_dl(const mac_nr_grant_dl_t& grant, mac_interface_phy_nr::tb_action_dl_t* action)
|
||||
{
|
||||
srsran::rwlock_read_guard lock(rwlock);
|
||||
|
||||
*action = {};
|
||||
|
||||
// Fetch HARQ process
|
||||
dl_harq_process_nr* proc_ptr = nullptr;
|
||||
if (grant.rnti == SRSRAN_SIRNTI) {
|
||||
// Set BCCH PID for SI RNTI
|
||||
proc_ptr = &bcch_proc;
|
||||
} else {
|
||||
if (grant.pid >= cfg.nof_procs) {
|
||||
logger.error("Invalid PID: %d", grant.pid);
|
||||
return;
|
||||
}
|
||||
proc_ptr = harq_procs.at(grant.pid).get();
|
||||
}
|
||||
|
||||
// Check NDI toggled state before forwarding to process
|
||||
bool ndi_toggled = (grant.ndi != harq_procs.at(grant.pid)->get_ndi());
|
||||
|
||||
if (grant.rnti == mac->get_temp_crnti() && last_temporal_crnti != mac->get_temp_crnti()) {
|
||||
// Consider the NDI to have been toggled
|
||||
last_temporal_crnti = mac->get_temp_crnti();
|
||||
logger.info("Considering NDI in pid=%d to be toggled for first Temporal C-RNTI", grant.pid);
|
||||
ndi_toggled = true;
|
||||
}
|
||||
|
||||
proc_ptr->new_grant_dl(std::move(grant), ndi_toggled, action);
|
||||
}
|
||||
|
||||
/// Called from PHY workers
|
||||
void dl_harq_entity_nr::tb_decoded(const mac_nr_grant_dl_t& grant, mac_interface_phy_nr::tb_action_dl_result_t result)
|
||||
{
|
||||
srsran::rwlock_read_guard lock(rwlock);
|
||||
|
||||
if (grant.rnti == SRSRAN_SIRNTI) {
|
||||
bcch_proc.tb_decoded(grant, std::move(result));
|
||||
} else {
|
||||
if (grant.pid >= cfg.nof_procs) {
|
||||
logger.error("Invalid PID: %d", grant.pid);
|
||||
return;
|
||||
}
|
||||
harq_procs.at(grant.pid)->tb_decoded(grant, std::move(result));
|
||||
}
|
||||
}
|
||||
|
||||
/// Called from MAC (Stack thread after, e.g. time alignment expire)
|
||||
void dl_harq_entity_nr::reset()
|
||||
{
|
||||
srsran::rwlock_write_guard lock(rwlock);
|
||||
for (const auto& proc : harq_procs) {
|
||||
if (proc != nullptr) {
|
||||
proc->reset();
|
||||
}
|
||||
}
|
||||
bcch_proc.reset();
|
||||
}
|
||||
|
||||
float dl_harq_entity_nr::get_average_retx()
|
||||
{
|
||||
return average_retx;
|
||||
}
|
||||
|
||||
dl_harq_entity_nr::dl_harq_process_nr::dl_harq_process_nr(dl_harq_entity_nr* parent_) :
|
||||
harq_entity(parent_),
|
||||
softbuffer_rx(std::unique_ptr<srsran_softbuffer_rx_t>(new srsran_softbuffer_rx_t())),
|
||||
logger(srslog::fetch_basic_logger("MAC-NR"))
|
||||
{}
|
||||
|
||||
dl_harq_entity_nr::dl_harq_process_nr::~dl_harq_process_nr()
|
||||
{
|
||||
if (softbuffer_rx != nullptr) {
|
||||
srsran_softbuffer_rx_free(softbuffer_rx.get());
|
||||
}
|
||||
}
|
||||
|
||||
bool dl_harq_entity_nr::dl_harq_process_nr::init(int pid_)
|
||||
{
|
||||
if (softbuffer_rx == nullptr || srsran_softbuffer_rx_init_guru(softbuffer_rx.get(),
|
||||
SRSRAN_SCH_NR_MAX_NOF_CB_LDPC,
|
||||
SRSRAN_LDPC_MAX_LEN_ENCODED_CB) != SRSRAN_SUCCESS) {
|
||||
logger.error("Couldn't allocate and/or initialize softbuffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pid_ < 0) {
|
||||
is_bcch = true;
|
||||
pid = 0;
|
||||
} else {
|
||||
pid = (uint32_t)pid_;
|
||||
is_bcch = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void dl_harq_entity_nr::dl_harq_process_nr::reset(void)
|
||||
{
|
||||
current_grant = {};
|
||||
is_first_tb = true;
|
||||
n_retx = 0;
|
||||
}
|
||||
|
||||
uint8_t dl_harq_entity_nr::dl_harq_process_nr::get_ndi()
|
||||
{
|
||||
return current_grant.ndi;
|
||||
}
|
||||
|
||||
void dl_harq_entity_nr::dl_harq_process_nr::new_grant_dl(const mac_nr_grant_dl_t& grant,
|
||||
const bool& ndi_toggled,
|
||||
mac_interface_phy_nr::tb_action_dl_t* action)
|
||||
{
|
||||
// Determine if it's a new transmission 5.3.2.2
|
||||
if (ndi_toggled || // 1st condition (NDI has changed)
|
||||
(is_bcch && grant.rv == 0) || // 2nd condition (Broadcast and 1st transmission)
|
||||
is_first_tb) // 3rd condition (is first tx for this tb)
|
||||
{
|
||||
// New transmission
|
||||
n_retx = 0;
|
||||
srsran_softbuffer_rx_reset_tbs(softbuffer_rx.get(), grant.tbs * 8);
|
||||
|
||||
action->tb.enabled = true;
|
||||
action->tb.softbuffer = softbuffer_rx.get();
|
||||
|
||||
// reset conditions
|
||||
is_first_tb = false;
|
||||
} else {
|
||||
// This is a retransmission
|
||||
if (not acked) {
|
||||
// If data has not yet been successfully decoded, instruct the PHY to combine the received data
|
||||
action->tb.enabled = true;
|
||||
action->tb.softbuffer = softbuffer_rx.get();
|
||||
} else {
|
||||
logger.info("DL %d: Received duplicate. Discarding and retransmitting ACK (n_retx=%d)", pid, n_retx);
|
||||
}
|
||||
}
|
||||
|
||||
// increment counter and store grant
|
||||
n_retx++;
|
||||
current_grant = grant;
|
||||
}
|
||||
|
||||
void dl_harq_entity_nr::dl_harq_process_nr::tb_decoded(const mac_nr_grant_dl_t& grant,
|
||||
mac_interface_phy_nr::tb_action_dl_result_t result)
|
||||
{
|
||||
acked = result.ack;
|
||||
|
||||
if (acked and result.payload != nullptr) {
|
||||
if (is_bcch) {
|
||||
logger.warning("Delivering PDU=%d bytes to Dissassemble and Demux unit (BCCH) not implemented", grant.tbs);
|
||||
reset();
|
||||
} else {
|
||||
if (grant.rnti == harq_entity->mac->get_temp_crnti()) {
|
||||
logger.debug("Delivering PDU=%d bytes to Dissassemble and Demux unit (Temporal C-RNTI) not implemented",
|
||||
grant.tbs);
|
||||
} else {
|
||||
logger.debug("Delivering PDU=%d bytes to Dissassemble and Demux unit", grant.tbs);
|
||||
harq_entity->demux_unit->push_pdu(std::move(result.payload), grant.tti);
|
||||
|
||||
// Compute average number of retransmissions per packet
|
||||
harq_entity->average_retx = SRSRAN_VEC_CMA((float)n_retx, harq_entity->average_retx, harq_entity->nof_pkts++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("DL %d: %s tbs=%d, rv=%d, ack=%s, ndi=%d",
|
||||
pid,
|
||||
grant.rv == 0 ? "newTX" : "reTX ",
|
||||
grant.tbs,
|
||||
grant.rv,
|
||||
acked ? "OK" : "KO",
|
||||
grant.ndi);
|
||||
}
|
||||
|
||||
} // namespace srsue
|
Loading…
Reference in New Issue