srsRAN/srsue/src/stack/mac/ul_harq.cc

396 lines
12 KiB
C++

/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#define Error(fmt, ...) log_h->error(fmt, ##__VA_ARGS__)
#define Warning(fmt, ...) log_h->warning(fmt, ##__VA_ARGS__)
#define Info(fmt, ...) log_h->info(fmt, ##__VA_ARGS__)
#define Debug(fmt, ...) log_h->debug(fmt, ##__VA_ARGS__)
#include "srsue/hdr/stack/mac/ul_harq.h"
#include "srslte/common/interfaces_common.h"
#include "srslte/common/log.h"
#include "srslte/common/mac_pcap.h"
#include "srslte/common/timers.h"
#include "srslte/interfaces/ue_interfaces.h"
namespace srsue {
ul_harq_entity::ul_harq_entity(const uint8_t cc_idx_) : proc(SRSLTE_MAX_HARQ_PROC), cc_idx(cc_idx_) {}
bool ul_harq_entity::init(srslte::log_ref log_h_,
mac_interface_rrc_common::ue_rnti_t* rntis_,
ra_proc* ra_procedure_,
mux* mux_unit_)
{
log_h = log_h_;
mux_unit = mux_unit_;
ra_procedure = ra_procedure_;
rntis = rntis_;
for (uint32_t i = 0; i < SRSLTE_MAX_HARQ_PROC; i++) {
if (!proc[i].init(i, this)) {
return false;
}
}
return true;
}
void ul_harq_entity::reset()
{
for (uint32_t i = 0; i < SRSLTE_MAX_HARQ_PROC; i++) {
proc[i].reset();
}
ul_sps_assig.clear();
}
void ul_harq_entity::reset_ndi()
{
for (uint32_t i = 0; i < SRSLTE_MAX_HARQ_PROC; i++) {
proc[i].reset_ndi();
}
}
void ul_harq_entity::set_config(srslte::ul_harq_cfg_t& harq_cfg_)
{
harq_cfg = harq_cfg_;
}
void ul_harq_entity::start_pcap(srslte::mac_pcap* pcap_)
{
pcap = pcap_;
}
/***************** PHY->MAC interface for UL processes **************************/
void ul_harq_entity::new_grant_ul(mac_interface_phy_lte::mac_grant_ul_t grant,
mac_interface_phy_lte::tb_action_ul_t* action)
{
bzero(action, sizeof(mac_interface_phy_lte::tb_action_ul_t));
if (grant.pid >= SRSLTE_MAX_HARQ_PROC) {
Error("Invalid PID: %d\n", grant.pid);
return;
}
if (grant.rnti == rntis->crnti || grant.rnti == rntis->temp_rnti || SRSLTE_RNTI_ISRAR(grant.rnti)) {
if (grant.rnti == rntis->crnti && proc[grant.pid].is_sps()) {
grant.tb.ndi = true;
}
proc[grant.pid].new_grant_ul(grant, action);
} else if (grant.rnti == rntis->sps_rnti) {
if (grant.tb.ndi) {
grant.tb.ndi = proc[grant.pid].get_ndi();
proc[grant.pid].new_grant_ul(grant, action);
} else {
Info("Not implemented\n");
}
} else {
Warning("Received grant for unknown rnti=0x%x\n", grant.rnti);
}
}
int ul_harq_entity::get_current_tbs(uint32_t pid)
{
if (pid >= SRSLTE_MAX_HARQ_PROC) {
Error("Invalid PID: %d\n", pid);
return 0;
}
return proc[pid].get_current_tbs();
}
float ul_harq_entity::get_average_retx()
{
return average_retx;
}
ul_harq_entity::ul_harq_process::ul_harq_process()
{
pdu_ptr = NULL;
payload_buffer = NULL;
bzero(&cur_grant, sizeof(mac_interface_phy_lte::mac_grant_ul_t));
harq_feedback = false;
is_initiated = false;
is_grant_configured = false;
pid = 0;
current_tx_nb = 0;
current_irv = 0;
}
ul_harq_entity::ul_harq_process::~ul_harq_process()
{
if (is_initiated) {
srslte_softbuffer_tx_free(&softbuffer);
}
}
bool ul_harq_entity::ul_harq_process::init(uint32_t pid_, ul_harq_entity* parent)
{
if (srslte_softbuffer_tx_init(&softbuffer, 110)) {
ERROR("Error initiating soft buffer\n");
return false;
}
harq_entity = parent;
log_h = harq_entity->log_h;
is_initiated = true;
pid = pid_;
payload_buffer = std::unique_ptr<byte_buffer_t>(new byte_buffer_t);
if (!payload_buffer) {
Error("Allocating memory\n");
return false;
}
pdu_ptr = payload_buffer->msg;
return true;
}
void ul_harq_entity::ul_harq_process::reset()
{
current_tx_nb = 0;
current_irv = 0;
is_grant_configured = false;
bzero(&cur_grant, sizeof(mac_interface_phy_lte::mac_grant_ul_t));
payload_buffer->clear();
}
void ul_harq_entity::ul_harq_process::reset_ndi()
{
cur_grant.tb.ndi = false;
}
void ul_harq_entity::ul_harq_process::new_grant_ul(mac_interface_phy_lte::mac_grant_ul_t grant,
mac_interface_phy_lte::tb_action_ul_t* action)
{
if (grant.phich_available) {
if (grant.tb.ndi_present && (grant.tb.ndi == get_ndi()) && (grant.tb.tbs != 0)) {
harq_feedback = false;
} else {
harq_feedback = grant.hi_value;
}
// Get maximum retransmissions
uint32_t max_retx;
if (grant.rnti == harq_entity->rntis->temp_rnti) {
max_retx = harq_entity->harq_cfg.max_harq_msg3_tx;
} else {
max_retx = harq_entity->harq_cfg.max_harq_tx;
}
// Check maximum retransmissions, do not consider last retx ACK
if (current_tx_nb >= max_retx && !grant.hi_value) {
Info("UL %d: Maximum number of ReTX reached (%d). Discarding TB.\n", pid, max_retx);
if (grant.rnti == harq_entity->rntis->temp_rnti) {
harq_entity->ra_procedure->harq_max_retx();
}
reset();
} else if (grant.rnti == harq_entity->rntis->temp_rnti && current_tx_nb) {
harq_entity->ra_procedure->harq_retx();
}
}
// Reset HARQ process if TB has changed
if (harq_feedback && has_grant() && grant.tb.ndi_present) {
if (grant.tb.tbs != cur_grant.tb.tbs && cur_grant.tb.tbs > 0 && grant.tb.tbs > 0) {
Debug(
"UL %d: Reset due to change of dci size last_grant=%d, new_grant=%d\n", pid, cur_grant.tb.tbs, grant.tb.tbs);
reset();
}
}
// Receive and route HARQ feedbacks
if (grant.tb.ndi_present) {
/* If TBS != 0, it's a CQI request, don't read PDU from RLC */
if (grant.tb.tbs == 0) {
action->tb.enabled = true;
// Decide if adaptive retx or new tx. 3 checks in 5.4.2.1
} else if ((grant.rnti != harq_entity->rntis->temp_rnti &&
grant.tb.ndi != get_ndi()) || // If not addressed to T-CRNTI and NDI toggled
(grant.rnti == harq_entity->rntis->crnti &&
!has_grant()) || // If addressed to C-RNTI and buffer is empty
(grant.is_rar)) // Grant received in a RAR
{
// New transmission
reset();
if (grant.rnti == harq_entity->rntis->crnti && harq_entity->ra_procedure->is_contention_resolution()) {
harq_entity->ra_procedure->pdcch_to_crnti(true);
}
// Check buffer size
if (grant.tb.tbs > payload_buffer_len) {
Error("Grant size exceeds payload buffer size (%d > %d)\n", grant.tb.tbs, payload_buffer_len);
return;
}
// Uplink dci in a RAR and there is a PDU in the Msg3 buffer
if (grant.is_rar) {
if (harq_entity->mux_unit->msg3_is_pending()) {
Debug("Getting Msg3 buffer payload, dci size=%d bytes\n", grant.tb.tbs);
pdu_ptr = harq_entity->mux_unit->msg3_get(payload_buffer.get(), grant.tb.tbs);
if (pdu_ptr) {
generate_new_tx(grant, action);
} else {
Warning("UL RAR dci available but no Msg3 on buffer\n");
}
} else {
Warning("UL RAR available but no Msg3 pending on buffer\n");
}
// Normal UL dci
} else {
// Request a MAC PDU from the Multiplexing & Assemble Unit
pdu_ptr = harq_entity->mux_unit->pdu_get(payload_buffer.get(), grant.tb.tbs);
if (pdu_ptr) {
generate_new_tx(grant, action);
} else {
Warning("Uplink dci but no MAC PDU in Multiplex Unit buffer\n");
}
}
} else if (has_grant()) {
// Adaptive Re-TX
generate_retx(grant, action);
} else {
Warning("UL %d: Received retransmission but no previous dci available for this PID.\n", pid);
}
if (harq_entity->pcap) {
uint16_t rnti;
if (grant.rnti == harq_entity->rntis->temp_rnti && harq_entity->rntis->temp_rnti) {
rnti = harq_entity->rntis->temp_rnti;
} else {
rnti = harq_entity->rntis->crnti;
}
harq_entity->pcap->write_ul_crnti(pdu_ptr, grant.tb.tbs, rnti, get_nof_retx(), grant.tti_tx, harq_entity->cc_idx);
}
} else if (has_grant()) {
// Non-Adaptive Re-Tx
generate_retx(grant, action);
}
}
uint32_t ul_harq_entity::ul_harq_process::get_rv()
{
int rv_of_irv[4] = {0, 2, 3, 1};
return rv_of_irv[current_irv % 4];
}
bool ul_harq_entity::ul_harq_process::has_grant()
{
return is_grant_configured;
}
bool ul_harq_entity::ul_harq_process::get_ndi()
{
return cur_grant.tb.ndi;
}
bool ul_harq_entity::ul_harq_process::is_sps()
{
return false;
}
uint32_t ul_harq_entity::ul_harq_process::get_nof_retx()
{
return current_tx_nb;
}
int ul_harq_entity::ul_harq_process::get_current_tbs()
{
return cur_grant.tb.tbs;
}
// Retransmission with or w/o dci (Section 5.4.2.2)
void ul_harq_entity::ul_harq_process::generate_retx(mac_interface_phy_lte::mac_grant_ul_t grant,
mac_interface_phy_lte::tb_action_ul_t* action)
{
int irv_of_rv[4] = {0, 3, 1, 2};
// HARQ entity requests an adaptive transmission
if (grant.tb.ndi_present) {
if (grant.tb.rv) {
current_irv = irv_of_rv[grant.tb.rv % 4];
}
Info("UL %d: Adaptive retx=%d, RV=%d, TBS=%d, HI=%s, ndi=%d, prev_ndi=%d\n",
pid,
current_tx_nb,
get_rv(),
grant.tb.tbs,
harq_feedback ? "ACK" : "NACK",
grant.tb.ndi,
cur_grant.tb.ndi);
cur_grant = grant;
harq_feedback = false;
generate_tx(action);
// HARQ entity requests a non-adaptive transmission
} else if (!harq_feedback) {
// Non-adaptive retx are only sent if HI=NACK. If HI=ACK but no dci was received do not reset PID
Info("UL %d: Non-Adaptive retx=%d, RV=%d, TBS=%d, HI=%s\n",
pid,
current_tx_nb,
get_rv(),
cur_grant.tb.tbs,
harq_feedback ? "ACK" : "NACK");
generate_tx(action);
}
}
// New transmission (Section 5.4.2.2)
void ul_harq_entity::ul_harq_process::generate_new_tx(mac_interface_phy_lte::mac_grant_ul_t grant,
mac_interface_phy_lte::tb_action_ul_t* action)
{
// Compute average number of retransmissions per packet considering previous packet
harq_entity->average_retx = SRSLTE_VEC_CMA((float)current_tx_nb, harq_entity->average_retx, harq_entity->nof_pkts++);
cur_grant = grant;
harq_feedback = false;
is_grant_configured = true;
current_tx_nb = 0;
current_irv = 0;
Info("UL %d: New TX%s, RV=%d, TBS=%d\n",
pid,
grant.rnti == harq_entity->rntis->temp_rnti ? " for Msg3" : "",
get_rv(),
cur_grant.tb.tbs);
generate_tx(action);
}
// Transmission of pending frame (Section 5.4.2.2)
void ul_harq_entity::ul_harq_process::generate_tx(mac_interface_phy_lte::tb_action_ul_t* action)
{
current_tx_nb++;
action->current_tx_nb = current_tx_nb;
action->expect_ack = true;
action->tb.rv = cur_grant.tb.rv > 0 ? cur_grant.tb.rv : get_rv();
action->tb.enabled = true;
action->tb.payload = pdu_ptr;
action->tb.softbuffer.tx = &softbuffer;
current_irv = (current_irv + 1) % 4;
}
} // namespace srsue