Files
openairinterface5g/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c
T

4251 lines
181 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
/*
* \brief procedures related to UE
*/
#include <stdio.h>
#include <math.h>
/* exe */
#include "executables/nr-softmodem.h"
/* RRC*/
#include "RRC/NR_UE/L2_interface_ue.h"
/* MAC */
#include "NR_MAC_COMMON/nr_mac.h"
#include "NR_MAC_UE/mac_proto.h"
#include "common/utils/nr/nr_common.h"
#include "openair2/NR_UE_PHY_INTERFACE/NR_Packet_Drop.h"
/* PHY */
#include "executables/softmodem-common.h"
/* utils */
#include "assertions.h"
#include "bits.h"
#include "oai_asn1.h"
#include "common/utils/LOG/log.h"
#include "LAYER2/nr_rlc/nr_rlc_oai_api.h"
// #define DEBUG_MIB
// #define ENABLE_MAC_PAYLOAD_DEBUG 1
// #define DEBUG_RAR
// table 7.2-1 TS 38.321
const uint16_t table_7_2_1[16] = {
5, // row index 0
10, // row index 1
20, // row index 2
30, // row index 3
40, // row index 4
60, // row index 5
80, // row index 6
120, // row index 7
160, // row index 8
240, // row index 9
320, // row index 10
480, // row index 11
960, // row index 12
1920, // row index 13
};
typedef struct {
uint32_t ssb_index;
short ssb_rsrp_dBm;
float_t ssb_sinr_dB;
} NR_RSRP_meas_t;
/* TS 36.213 Table 9.2.3-3: Mapping of values for one HARQ-ACK bit to sequences */
static const int sequence_cyclic_shift_1_harq_ack_bit[2]
/* HARQ-ACK Value 0 1 */
/* Sequence cyclic shift */ = { 0, 6 };
/* TS 36.213 Table 9.2.5-1: Mapping of values for one HARQ-ACK bit and positive SR to sequences */
static const int sequence_cyclic_shift_1_harq_ack_bit_positive_sr[2]
/* HARQ-ACK Value 0 1 */
/* Sequence cyclic shift */ = { 3, 9 };
/* TS 36.213 Table 9.2.5-2: Mapping of values for two HARQ-ACK bits and positive SR to sequences */
static const int sequence_cyclic_shift_2_harq_ack_bits_positive_sr[4]
/* HARQ-ACK Value (0,0) (0,1) (1,0) (1,1) */
/* Sequence cyclic shift */ = { 1, 4, 10, 7 };
/* TS 38.213 Table 9.2.3-4: Mapping of values for two HARQ-ACK bits to sequences */
static const int sequence_cyclic_shift_2_harq_ack_bits[4]
/* HARQ-ACK Value (0,0) (0,1) (1,0) (1,1) */
/* Sequence cyclic shift */ = { 0, 3, 9, 6 };
static int get_pucch0_mcs(const int O_ACK, const int O_SR, const int ack_payload, const int sr_payload)
{
int mcs = 0;
if (O_SR == 0 || sr_payload == 0) { /* only ack is transmitted TS 36.213 9.2.3 UE procedure for reporting HARQ-ACK */
if (O_ACK == 1)
mcs = sequence_cyclic_shift_1_harq_ack_bit[ack_payload & 0x1]; /* only harq of 1 bit */
else
mcs = sequence_cyclic_shift_2_harq_ack_bits[ack_payload & 0x3]; /* only harq with 2 bits */
} else { /* SR + eventually ack are transmitted TS 36.213 9.2.5.1 UE procedure for multiplexing HARQ-ACK or CSI and SR */
if (sr_payload == 1) { /* positive scheduling request */
if (O_ACK == 1)
mcs = sequence_cyclic_shift_1_harq_ack_bit_positive_sr[ack_payload & 0x1]; /* positive SR and harq of 1 bit */
else if (O_ACK == 2)
mcs = sequence_cyclic_shift_2_harq_ack_bits_positive_sr[ack_payload & 0x3]; /* positive SR and harq with 2 bits */
else
mcs = 0; /* only positive SR */
}
}
return mcs;
}
/* TS 36.213 Table 9.2.1-1: PUCCH resource sets before dedicated PUCCH resource configuration */
const initial_pucch_resource_t initial_pucch_resource[16] = {
/* format first symbol Number of symbols PRB offset nb index for set of initial CS */
/* 0 */ { 0, 12, 2, 0, 2, { 0, 3, 0, 0 } },
/* 1 */ { 0, 12, 2, 0, 3, { 0, 4, 8, 0 } },
/* 2 */ { 0, 12, 2, 3, 3, { 0, 4, 8, 0 } },
/* 3 */ { 1, 10, 4, 0, 2, { 0, 6, 0, 0 } },
/* 4 */ { 1, 10, 4, 0, 4, { 0, 3, 6, 9 } },
/* 5 */ { 1, 10, 4, 2, 4, { 0, 3, 6, 9 } },
/* 6 */ { 1, 10, 4, 4, 4, { 0, 3, 6, 9 } },
/* 7 */ { 1, 4, 10, 0, 2, { 0, 6, 0, 0 } },
/* 8 */ { 1, 4, 10, 0, 4, { 0, 3, 6, 9 } },
/* 9 */ { 1, 4, 10, 2, 4, { 0, 3, 6, 9 } },
/* 10 */ { 1, 4, 10, 4, 4, { 0, 3, 6, 9 } },
/* 11 */ { 1, 0, 14, 0, 2, { 0, 6, 0, 0 } },
/* 12 */ { 1, 0, 14, 0, 4, { 0, 3, 6, 9 } },
/* 13 */ { 1, 0, 14, 2, 4, { 0, 3, 6, 9 } },
/* 14 */ { 1, 0, 14, 4, 4, { 0, 3, 6, 9 } },
/* 15 */ { 1, 0, 14, 0, 4, { 0, 3, 6, 9 } },
};
int get_rnti_type(const NR_UE_MAC_INST_t *mac, const uint16_t rnti)
{
const RA_config_t *ra = &mac->ra;
nr_rnti_type_t rnti_type;
if (rnti == ra->ra_rnti) {
rnti_type = TYPE_RA_RNTI_;
} else if (rnti == ra->MsgB_rnti && (ra->ra_state == nrRA_WAIT_MSGB || ra->ra_state == nrRA_WAIT_CONTENTION_RESOLUTION)) {
rnti_type = TYPE_MSGB_RNTI_;
} else if (rnti == ra->t_crnti && (ra->ra_state == nrRA_WAIT_RAR || ra->ra_state == nrRA_WAIT_CONTENTION_RESOLUTION)) {
rnti_type = TYPE_TC_RNTI_;
} else if (rnti == mac->crnti) {
rnti_type = TYPE_C_RNTI_;
} else if (rnti == 0xFFFE) {
rnti_type = TYPE_P_RNTI_;
} else if (rnti == 0xFFFF) {
rnti_type = TYPE_SI_RNTI_;
} else {
AssertFatal(1 == 0, "Not identified/handled rnti %d \n", rnti);
}
LOG_D(MAC, "Returning rnti_type %s \n", rnti_types(rnti_type));
return rnti_type;
}
void nr_ue_decode_mib(NR_UE_MAC_INST_t *mac, int cc_id)
{
LOG_D(MAC,"[L2][MAC] decode mib\n");
if (mac->mib->cellBarred == NR_MIB__cellBarred_barred) {
LOG_W(MAC, "Cell is barred. Going back to sync mode.\n");
mac->synch_request.Mod_id = mac->ue_id;
mac->synch_request.CC_id = cc_id;
mac->synch_request.synch_req.target_Nid_cell = -1;
mac->if_module->synch_request(&mac->synch_request);
return;
}
uint16_t frame = (mac->mib->systemFrameNumber.buf[0] >> mac->mib->systemFrameNumber.bits_unused);
uint16_t frame_number_4lsb = 0;
int extra_bits = mac->mib_additional_bits;
for (int i = 0; i < 4; i++)
frame_number_4lsb |= ((extra_bits >> i) & 1) << (3 - i);
uint8_t ssb_subcarrier_offset_msb = (extra_bits >> 5) & 0x1; // extra bits[5]
uint8_t ssb_subcarrier_offset = (uint8_t)mac->mib->ssb_SubcarrierOffset;
frame = frame << 4;
mac->mib_frame = frame | frame_number_4lsb;
if (mac->frequency_range == FR1) {
if(ssb_subcarrier_offset_msb)
ssb_subcarrier_offset = ssb_subcarrier_offset | 0x10;
}
#ifdef DEBUG_MIB
uint8_t half_frame_bit = (extra_bits >> 4) & 0x1; // extra bits[4]
LOG_I(MAC,"system frame number(6 MSB bits): %d\n", mac->mib->systemFrameNumber.buf[0]);
LOG_I(MAC,"system frame number(with LSB): %d\n", (int) mac->mib_frame);
LOG_I(MAC,"subcarrier spacing (0=15or60, 1=30or120): %d\n", (int)mac->mib->subCarrierSpacingCommon);
LOG_I(MAC,"ssb carrier offset(with MSB): %d\n", (int)ssb_subcarrier_offset);
LOG_I(MAC,"dmrs type A position (0=pos2,1=pos3): %d\n", (int)mac->mib->dmrs_TypeA_Position);
LOG_I(MAC,"controlResourceSetZero: %d\n", (int)mac->mib->pdcch_ConfigSIB1.controlResourceSetZero);
LOG_I(MAC,"searchSpaceZero: %d\n", (int)mac->mib->pdcch_ConfigSIB1.searchSpaceZero);
LOG_I(MAC,"cell barred (0=barred,1=notBarred): %d\n", (int)mac->mib->cellBarred);
LOG_I(MAC,"intra frequency reselection (0=allowed,1=notAllowed): %d\n", (int)mac->mib->intraFreqReselection);
LOG_I(MAC,"half frame bit(extra bits): %d\n", (int)half_frame_bit);
LOG_I(MAC,"ssb index(extra bits): %d\n", (int)mac->mib_ssb);
#endif
mac->ssb_subcarrier_offset = ssb_subcarrier_offset;
mac->dmrs_TypeA_Position = mac->mib->dmrs_TypeA_Position;
}
static void configure_ratematching_csi(fapi_nr_dl_config_dlsch_pdu_rel15_t *dlsch_pdu,
const fapi_nr_dl_config_request_t *dl_config,
int rnti_type,
int frame,
int slot,
int mu,
int slots_per_frame,
const NR_PDSCH_Config_t *pdsch_config,
NR_CSI_MeasConfig_t *csi_MeasConfig)
{
// only for C-RNTI, MCS-C-RNTI, CS-RNTI (and only C-RNTI is supported for now)
if (rnti_type != TYPE_C_RNTI_)
return;
if (pdsch_config && pdsch_config->zp_CSI_RS_ResourceToAddModList) {
bool found = false;
const NR_SetupRelease_ZP_CSI_RS_ResourceSet_t *zp_set = pdsch_config->p_ZP_CSI_RS_ResourceSet;
AssertFatal(zp_set && zp_set->choice.setup, "Only periodic ZP resource set is implemented\n");
const NR_ZP_CSI_RS_Resource_t *zp_res = NULL;
for (int j = 0; j < zp_set->choice.setup->zp_CSI_RS_ResourceIdList.list.count; j++) {
for (int i = 0; i < pdsch_config->zp_CSI_RS_ResourceToAddModList->list.count; i++) {
zp_res = pdsch_config->zp_CSI_RS_ResourceToAddModList->list.array[i];
NR_ZP_CSI_RS_ResourceId_t id = zp_res->zp_CSI_RS_ResourceId;
if (*zp_set->choice.setup->zp_CSI_RS_ResourceIdList.list.array[j] == id) {
found = true;
break;
}
}
AssertFatal(found, "Couldn't find periodic ZP resouce in set\n");
AssertFatal(zp_res->periodicityAndOffset, "periodicityAndOffset cannot be null for periodic ZP resource\n");
int period, offset;
csi_period_offset(NULL, zp_res->periodicityAndOffset, &period, &offset);
if((frame * slots_per_frame + slot - offset) % period != 0)
continue;
AssertFatal(dlsch_pdu->numCsiRsForRateMatching < NFAPI_MAX_NUM_CSI_RATEMATCH, "csiRsForRateMatching out of bounds\n");
fapi_nr_dl_config_csirs_pdu_rel15_t *csi_pdu = &dlsch_pdu->csiRsForRateMatching[dlsch_pdu->numCsiRsForRateMatching];
csi_pdu->csi_type = 2; // ZP-CSI
csi_pdu->subcarrier_spacing = mu;
configure_csi_resource_mapping(csi_pdu, &zp_res->resourceMapping, dlsch_pdu->BWPSize, dlsch_pdu->BWPStart);
dlsch_pdu->numCsiRsForRateMatching++;
}
}
// Handle NZP CSI-RS for rate matching
if (csi_MeasConfig && csi_MeasConfig->nzp_CSI_RS_ResourceToAddModList) {
for (int i = 0; i < csi_MeasConfig->nzp_CSI_RS_ResourceToAddModList->list.count; i++) {
NR_NZP_CSI_RS_Resource_t *nzp_res = csi_MeasConfig->nzp_CSI_RS_ResourceToAddModList->list.array[i];
if (nzp_res->periodicityAndOffset) {
int period, offset;
csi_period_offset(NULL, nzp_res->periodicityAndOffset, &period, &offset);
if ((frame * slots_per_frame + slot - offset) % period != 0)
continue;
AssertFatal(dlsch_pdu->numCsiRsForRateMatching < NFAPI_MAX_NUM_CSI_RATEMATCH, "csiRsForRateMatching out of bounds\n");
fapi_nr_dl_config_csirs_pdu_rel15_t *csi_pdu = &dlsch_pdu->csiRsForRateMatching[dlsch_pdu->numCsiRsForRateMatching];
csi_pdu->csi_type = 1; // NZP CSI-RS
csi_pdu->subcarrier_spacing = mu;
configure_csi_resource_mapping(csi_pdu, &nzp_res->resourceMapping, dlsch_pdu->BWPSize, dlsch_pdu->BWPStart);
dlsch_pdu->numCsiRsForRateMatching++;
}
}
}
for (int i = 0; i < dl_config->number_pdus; i++) {
// This assumes that CSI-RS are scheduled before this moment which is true in current implementation
const fapi_nr_dl_config_request_pdu_t *csi_req = &dl_config->dl_config_list[i];
if (csi_req->pdu_type == FAPI_NR_DL_CONFIG_TYPE_CSI_RS) {
AssertFatal(dlsch_pdu->numCsiRsForRateMatching < NFAPI_MAX_NUM_CSI_RATEMATCH, "csiRsForRateMatching out of bounds\n");
dlsch_pdu->csiRsForRateMatching[dlsch_pdu->numCsiRsForRateMatching] = csi_req->csirs_config_pdu.csirs_config_rel15;
dlsch_pdu->numCsiRsForRateMatching++;
}
}
}
void nr_ue_decode_BCCH_DL_SCH(NR_UE_MAC_INST_t *mac,
unsigned int gNB_index,
uint8_t ack_nack,
uint8_t *pduP,
uint32_t pdu_len,
int hfn,
int frame,
int slot)
{
if(ack_nack) {
LOG_D(NR_MAC, "Decoding NR-BCCH-DL-SCH-Message (SIB1 or SI)\n");
nr_mac_rrc_data_ind_ue(mac->ue_id, gNB_index, hfn, frame, slot, mac->physCellId, 0, NR_BCCH_DL_SCH, (uint8_t *) pduP, pdu_len);
if (mac->get_sib1)
mac->get_sib1 = false;
for (int i = 0; i < MAX_SI_GROUPS; i++) {
if (mac->get_otherSI[i])
mac->get_otherSI[i] = false;
}
mac->si_SchedInfo.si_window_start = -1;
T(T_NRUE_MAC_DL_PDU_WITH_DATA,
T_INT(SI_RNTI),
T_INT(-1 /* frame, unavailable here */),
T_INT(-1 /* slot, unavailable here */),
T_INT(0 /* harq_pid */),
T_BUFFER(pduP, pdu_len));
}
else {
LOG_E(NR_MAC, "Got NACK on NR-BCCH-DL-SCH-Message (%s)\n", mac->get_sib1 ? "SIB1" : "other SI");
nr_mac_rrc_data_ind_ue(mac->ue_id, gNB_index, hfn, frame, slot, mac->physCellId, 0, NR_BCCH_DL_SCH, NULL, 0);
}
}
/*
* This code contains all the functions needed to process all dci fields.
* These tables and functions are going to be called by function nr_ue_process_dci
*/
static inline int writeBit(uint8_t *bitmap, int offset, int value, int size)
{
for (int i = offset; i < offset + size; i++)
bitmap[i / 8] |= value << (i % 8);
return size;
}
// 38.214 Section 5.1.3.1
static uint8_t get_dlsch_mcs_table(nr_dci_format_t format,
nr_rnti_type_t rnti_type,
int ss_type,
long *mcs_Table,
long *sps_mcs_Table,
bool sps_transmission,
long *mcs_C_RNTI)
{
// TODO procedures for SPS-Config (semi-persistent scheduling) not implemented
if (mcs_Table && *mcs_Table == NR_PDSCH_Config__mcs_Table_qam256 && format == NR_DL_DCI_FORMAT_1_1 && rnti_type == TYPE_C_RNTI_)
return 1; // Table 5.1.3.1-2: MCS index table 2 for PDSCH
else if (!mcs_C_RNTI
&& mcs_Table
&& *mcs_Table == NR_PDSCH_Config__mcs_Table_qam64LowSE
&& ss_type == NR_SearchSpace__searchSpaceType_PR_ue_Specific
&& rnti_type == TYPE_C_RNTI_)
return 2; // Table 5.1.3.1-3: MCS index table 3 for PDSCH
else if (mcs_C_RNTI && rnti_type == TYPE_MCS_C_RNTI_)
return 2; // Table 5.1.3.1-3: MCS index table 3 for PDSCH
else if (!sps_mcs_Table && mcs_Table && *mcs_Table == NR_PDSCH_Config__mcs_Table_qam256) {
if ((format == NR_DL_DCI_FORMAT_1_1 && rnti_type == TYPE_CS_RNTI_) || sps_transmission)
return 1; // Table 5.1.3.1-2: MCS index table 2 for PDSCH
}
else if (sps_mcs_Table) {
if (rnti_type == TYPE_CS_RNTI_ || sps_transmission)
return 2; // Table 5.1.3.1-3: MCS index table 3 for PDSCH
}
// otherwise
return 0; // Table 5.1.3.1-1: MCS index table 1 for PDSCH
}
int8_t nr_ue_process_dci_freq_dom_resource_assignment(nfapi_nr_ue_pusch_pdu_t *pusch_config_pdu,
fapi_nr_dl_config_dlsch_pdu_rel15_t *dlsch_config_pdu,
NR_PDSCH_Config_t *pdsch_Config,
uint16_t n_RB_ULBWP,
uint16_t n_RB_DLBWP,
int start_DLBWP,
dci_field_t frequency_domain_assignment)
{
/*
* TS 38.214 subclause 5.1.2.2 Resource allocation in frequency domain (downlink)
* when the scheduling grant is received with DCI format 1_0, then downlink resource allocation type 1 is used
*/
if(dlsch_config_pdu != NULL) {
if (pdsch_Config &&
pdsch_Config->resourceAllocation == NR_PDSCH_Config__resourceAllocation_resourceAllocationType0) {
// TS 38.214 subclause 5.1.2.2.1 Downlink resource allocation type 0
dlsch_config_pdu->resource_alloc = 0;
uint8_t *rb_bitmap = dlsch_config_pdu->rb_bitmap;
memset(rb_bitmap, 0, sizeof(dlsch_config_pdu->rb_bitmap));
int P = getRBGSize(n_RB_DLBWP, pdsch_Config->rbg_Size);
int currentBit = 0;
// write first bit sepcial case
const int n_RBG = frequency_domain_assignment.nbits;
int first_bit_rbg = (frequency_domain_assignment.val >> (n_RBG - 1)) & 0x01;
currentBit += writeBit(rb_bitmap, currentBit, first_bit_rbg, P - (start_DLBWP % P));
// write all bits until last bit special case
for (int i = 1; i < n_RBG - 1; i++) {
// The order of RBG bitmap is such that RBG 0 to RBG n_RBG − 1 are mapped from MSB to LSB
int bit_rbg = (frequency_domain_assignment.val >> (n_RBG - 1 - i)) & 0x01;
currentBit += writeBit(rb_bitmap, currentBit, bit_rbg, P);
}
// write last bit (only if more than 1 RBG)
if (n_RBG > 1) {
int last_bit_rbg = frequency_domain_assignment.val & 0x01;
const int tmp = (start_DLBWP + n_RB_DLBWP) % P;
int last_RBG = tmp ? tmp : P;
writeBit(rb_bitmap, currentBit, last_bit_rbg, last_RBG);
}
} else if (pdsch_Config && pdsch_Config->resourceAllocation == NR_PDSCH_Config__resourceAllocation_dynamicSwitch)
AssertFatal(false, "DLSCH dynamic switch allocation not yet supported\n");
else {
// TS 38.214 subclause 5.1.2.2.2 Downlink resource allocation type 1
dlsch_config_pdu->resource_alloc = 1;
int riv = frequency_domain_assignment.val;
dlsch_config_pdu->number_rbs = NRRIV2BW(riv,n_RB_DLBWP);
dlsch_config_pdu->start_rb = NRRIV2PRBOFFSET(riv,n_RB_DLBWP);
// Sanity check in case a false or erroneous DCI is received
if ((dlsch_config_pdu->number_rbs < 1) || (dlsch_config_pdu->number_rbs > n_RB_DLBWP - dlsch_config_pdu->start_rb)) {
// DCI is invalid!
LOG_W(MAC, "Frequency domain assignment values are invalid! #RBs: %d, Start RB: %d, n_RB_DLBWP: %d \n", dlsch_config_pdu->number_rbs, dlsch_config_pdu->start_rb, n_RB_DLBWP);
return -1;
}
LOG_D(MAC,"DLSCH riv = %i\n", riv);
LOG_D(MAC,"DLSCH n_RB_DLBWP = %i\n", n_RB_DLBWP);
LOG_D(MAC,"DLSCH number_rbs = %i\n", dlsch_config_pdu->number_rbs);
LOG_D(MAC,"DLSCH start_rb = %i\n", dlsch_config_pdu->start_rb);
}
}
if(pusch_config_pdu != NULL) {
/*
* TS 38.214 subclause 6.1.2.2 Resource allocation in frequency domain (uplink)
*/
/*
* TS 38.214 subclause 6.1.2.2.1 Uplink resource allocation type 0
*/
/*
* TS 38.214 subclause 6.1.2.2.2 Uplink resource allocation type 1
*/
int riv = frequency_domain_assignment.val;
pusch_config_pdu->rb_size = NRRIV2BW(riv,n_RB_ULBWP);
pusch_config_pdu->rb_start = NRRIV2PRBOFFSET(riv,n_RB_ULBWP);
// Sanity check in case a false or erroneous DCI is received
if ((pusch_config_pdu->rb_size < 1) || (pusch_config_pdu->rb_size > n_RB_ULBWP - pusch_config_pdu->rb_start)) {
// DCI is invalid!
LOG_W(MAC, "Frequency domain assignment values are invalid! #RBs: %d, Start RB: %d, n_RB_ULBWP: %d \n",pusch_config_pdu->rb_size, pusch_config_pdu->rb_start, n_RB_ULBWP);
return -1;
}
}
return 0;
}
static void set_harq_status(NR_UE_MAC_INST_t *mac,
uint8_t pucch_id,
uint8_t harq_id,
int8_t delta_pucch,
uint16_t data_toul_fb,
uint8_t dai,
int n_CCE,
int N_CCE,
frame_t frame,
int slot)
{
NR_UE_DL_HARQ_STATUS_t *current_harq = &mac->dl_harq_info[harq_id];
current_harq->active = true;
current_harq->ack_received = false;
current_harq->pucch_resource_indicator = pucch_id;
current_harq->n_CCE = n_CCE;
current_harq->N_CCE = N_CCE;
current_harq->dai_cumul = 0;
current_harq->delta_pucch = delta_pucch;
// FIXME k0 != 0 currently not taken into consideration
int slots_per_frame = mac->frame_structure.numb_slots_frame;
current_harq->ul_frame = frame;
current_harq->ul_slot = slot + data_toul_fb;
if (current_harq->ul_slot >= slots_per_frame) {
current_harq->ul_frame = (frame + current_harq->ul_slot / slots_per_frame) % MAX_FRAME_NUMBER;
current_harq->ul_slot %= slots_per_frame;
}
// counter DAI in DCI ranges from 0 to 3
// we might have more than 4 HARQ processes to report per PUCCH
// we need to keep track of how many DAI we received in a slot (dai_cumul) despite the modulo operation
int highest_dai = -1;
int temp_dai = dai;
const int num_dl_harq = get_nrofHARQ_ProcessesForPDSCH(&mac->sc_info);
for (int i = 0; i < num_dl_harq; i++) {
// looking for other active HARQ processes with feedback in the same frame/slot
if (i == harq_id)
continue;
NR_UE_DL_HARQ_STATUS_t *harq = &mac->dl_harq_info[i];
if (harq->active &&
harq->ul_frame == current_harq->ul_frame &&
harq->ul_slot == current_harq->ul_slot) {
// highest_dai is the largest cumulative dai in the set of HARQ allocations for a given slot
if (harq->dai_cumul > highest_dai)
highest_dai = harq->dai_cumul - 1;
}
}
current_harq->dai_cumul = temp_dai + 1; // DAI = 0 (temp_dai) corresponds to 1st assignment and so on
// if temp_dai is less or equal than cumulative highest dai for given slot
// it's an indication dai was reset due to modulo 4 operation
if (temp_dai <= highest_dai) {
int mod4_count = (highest_dai + 1) / 4; // to take into account how many times dai wrapped up (modulo 4)
current_harq->dai_cumul += (mod4_count * 4);
}
LOG_D(NR_MAC,
"Setting harq_status for harq_id %d, dl %d.%d, sched ul %d.%d fb time %d total dai %d\n",
harq_id,
frame,
slot,
current_harq->ul_frame,
current_harq->ul_slot,
data_toul_fb,
current_harq->dai_cumul);
}
static int nr_ue_process_dci_ul_00(NR_UE_MAC_INST_t *mac,
frame_t frame,
int slot,
dci_pdu_rel15_t *dci,
fapi_nr_dci_indication_pdu_t *dci_ind)
{
/*
* with CRC scrambled by C-RNTI or CS-RNTI or new-RNTI or TC-RNTI
* 0 IDENTIFIER_DCI_FORMATS:
* 10 FREQ_DOM_RESOURCE_ASSIGNMENT_UL: PUSCH hopping with resource allocation type 1 not considered
* 12 TIME_DOM_RESOURCE_ASSIGNMENT: 0, 1, 2, 3, or 4 bits as defined in Subclause 6.1.2.1 of [6, TS 38.214]. The bitwidth for this field is determined as log2(I) bits,
* 17 FREQ_HOPPING_FLAG: 0 bit if only resource allocation type 0
* 24 MCS:
* 25 NDI:
* 26 RV:
* 27 HARQ_PROCESS_NUMBER:
* 32 TPC_PUSCH:
* 49 PADDING_NR_DCI: (Note 2) If DCI format 0_0 is monitored in common search space
* 50 SUL_IND_0_0:
*/
// Calculate the slot in which ULSCH should be scheduled. This is current slot + K2,
// where K2 is the offset between the slot in which UL DCI is received and the slot
// in which ULSCH should be scheduled. K2 is configured in RRC configuration.
// todo:
// - SUL_IND_0_0
// Schedule PUSCH
const int coreset_type = dci_ind->coreset_type == NFAPI_NR_CSET_CONFIG_PDCCH_CONFIG; // 0 for coreset0, 1 otherwise;
NR_tda_info_t tda_info = get_ul_tda_info(mac->current_UL_BWP,
coreset_type,
dci_ind->ss_type,
get_rnti_type(mac, dci_ind->rnti),
dci->time_domain_assignment.val);
if (!tda_info.valid_tda || tda_info.nrOfSymbols == 0)
return -1;
frame_t frame_tx;
int slot_tx;
const int ntn_ue_koffset = GET_NTN_UE_K_OFFSET(&mac->phy_config.config_req.ntn_config, mac->current_UL_BWP->scs);
if (-1 == nr_ue_pusch_scheduler(mac, 0, frame, slot, &frame_tx, &slot_tx, tda_info.k2 + ntn_ue_koffset)) {
LOG_E(MAC, "Cannot schedule PUSCH\n");
return -1;
}
fapi_nr_ul_config_request_pdu_t *pdu = lockGet_ul_config(mac, frame_tx, slot_tx, FAPI_NR_UL_CONFIG_TYPE_PUSCH);
if (!pdu)
return -1;
int ret = nr_config_pusch_pdu(mac,
&tda_info,
&pdu->pusch_config_pdu,
dci,
NULL,
NULL,
dci_ind->rnti,
dci_ind->ss_type,
NR_UL_DCI_FORMAT_0_0);
if (ret != 0)
remove_ul_config_last_item(pdu);
release_ul_config(pdu, false);
return ret;
}
static int nr_ue_process_dci_ul_01(NR_UE_MAC_INST_t *mac,
frame_t frame,
int slot,
dci_pdu_rel15_t *dci,
fapi_nr_dci_indication_pdu_t *dci_ind)
{
/*
* with CRC scrambled by C-RNTI or CS-RNTI or SP-CSI-RNTI or new-RNTI
* 0 IDENTIFIER_DCI_FORMATS:
* 1 CARRIER_IND
* 2 SUL_IND_0_1
* 7 BANDWIDTH_PART_IND
* 10 FREQ_DOM_RESOURCE_ASSIGNMENT_UL: PUSCH hopping with resource allocation type 1 not considered
* 12 TIME_DOM_RESOURCE_ASSIGNMENT: 0, 1, 2, 3, or 4 bits as defined in Subclause 6.1.2.1 of [6, TS 38.214]. The bitwidth for this field is determined as log2(I) bits,
* 17 FREQ_HOPPING_FLAG: 0 bit if only resource allocation type 0
* 24 MCS:
* 25 NDI:
* 26 RV:
* 27 HARQ_PROCESS_NUMBER:
* 29 FIRST_DAI
* 30 SECOND_DAI
* 32 TPC_PUSCH:
* 36 SRS_RESOURCE_IND:
* 37 PRECOD_NBR_LAYERS:
* 38 ANTENNA_PORTS:
* 40 SRS_REQUEST:
* 42 CSI_REQUEST:
* 43 CBGTI
* 45 PTRS_DMRS
* 46 BETA_OFFSET_IND
* 47 DMRS_SEQ_INI
* 48 UL_SCH_IND
* 49 PADDING_NR_DCI: (Note 2) If DCI format 0_0 is monitored in common search space
*/
// TODO:
// - FIRST_DAI
// - SECOND_DAI
// - SRS_RESOURCE_IND
/* CSI_REQUEST */
long csi_K2 = -1;
nfapi_nr_ue_csi_payload_t csi_report = {0};
if (dci->csi_request.nbits > 0 && dci->csi_request.val > 0)
csi_report = nr_ue_aperiodic_csi_reporting(mac, dci->csi_request, dci->time_domain_assignment.val, &csi_K2);
/* SRS_REQUEST */
AssertFatal(dci->srs_request.nbits == 2, "If SUL is supported in the cell, there is an additional bit in SRS request field\n");
if (dci->srs_request.val > 0)
nr_ue_aperiodic_srs_scheduling(mac, dci->srs_request.val, frame, slot);
// Schedule PUSCH
frame_t frame_tx;
int slot_tx;
const int coreset_type = dci_ind->coreset_type == NFAPI_NR_CSET_CONFIG_PDCCH_CONFIG; // 0 for coreset0, 1 otherwise;
NR_tda_info_t tda_info = get_ul_tda_info(mac->current_UL_BWP,
coreset_type,
dci_ind->ss_type,
get_rnti_type(mac, dci_ind->rnti),
dci->time_domain_assignment.val);
if (!tda_info.valid_tda || tda_info.nrOfSymbols == 0)
return -1;
if (dci->ulsch_indicator == 0) {
// in case of CSI on PUSCH and no ULSCH we need to use reportSlotOffset in trigger state
if (csi_K2 <= 0) {
LOG_E(MAC, "Invalid CSI K2 value %ld\n", csi_K2);
return -1;
}
tda_info.k2 = csi_K2;
}
const int ntn_ue_koffset = GET_NTN_UE_K_OFFSET(&mac->phy_config.config_req.ntn_config, mac->current_UL_BWP->scs);
if (-1 == nr_ue_pusch_scheduler(mac, 0, frame, slot, &frame_tx, &slot_tx, tda_info.k2 + ntn_ue_koffset)) {
LOG_E(MAC, "Cannot schedule PUSCH\n");
return -1;
}
fapi_nr_ul_config_request_pdu_t *pdu = lockGet_ul_config(mac, frame_tx, slot_tx, FAPI_NR_UL_CONFIG_TYPE_PUSCH);
if (!pdu)
return -1;
int ret = nr_config_pusch_pdu(mac,
&tda_info,
&pdu->pusch_config_pdu,
dci,
&csi_report,
NULL,
dci_ind->rnti,
dci_ind->ss_type,
NR_UL_DCI_FORMAT_0_1);
LOG_D(NR_MAC_DCI,
"add ul dci harq %d for %d.%d %d.%d round %d\n",
pdu->pusch_config_pdu.pusch_data.harq_process_id,
frame,
slot,
frame_tx,
slot_tx,
0);
if (ret != 0)
remove_ul_config_last_item(pdu);
release_ul_config(pdu, false);
return ret;
}
static int nr_ue_process_dci_dl_10(NR_UE_MAC_INST_t *mac,
frame_t frame,
int slot,
dci_pdu_rel15_t *dci,
fapi_nr_dci_indication_pdu_t *dci_ind)
{
/*
* with CRC scrambled by C-RNTI or CS-RNTI or new-RNTI
* 0 IDENTIFIER_DCI_FORMATS:
* 11 FREQ_DOM_RESOURCE_ASSIGNMENT_DL:
* 12 TIME_DOM_RESOURCE_ASSIGNMENT: 0, 1, 2, 3, or 4 bits as defined in Subclause 5.1.2.1 of [6, TS 38.214]. The bitwidth for this field is determined as log2(I) bits,
* 13 VRB_TO_PRB_MAPPING: 0 bit if only resource allocation type 0
* 24 MCS:
* 25 NDI:
* 26 RV:
* 27 HARQ_PROCESS_NUMBER:
* 28 DAI_: For format1_1: 4 if more than one serving cell are configured in the DL and the higher layer parameter HARQ-ACK-codebook=dynamic, where the 2 MSB bits are the counter DAI and the 2 LSB bits are the total DAI
* 33 TPC_PUCCH:
* 34 PUCCH_RESOURCE_IND:
* 35 PDSCH_TO_HARQ_FEEDBACK_TIME_IND:
* 55 RESERVED_NR_DCI
* with CRC scrambled by P-RNTI
* 8 SHORT_MESSAGE_IND
* 9 SHORT_MESSAGES
* 11 FREQ_DOM_RESOURCE_ASSIGNMENT_DL:
* 12 TIME_DOM_RESOURCE_ASSIGNMENT: 0, 1, 2, 3, or 4 bits as defined in Subclause 5.1.2.1 of [6, TS 38.214]. The bitwidth for this field is determined as log2(I) bits,
* 13 VRB_TO_PRB_MAPPING: 0 bit if only resource allocation type 0
* 24 MCS:
* 31 TB_SCALING
* 55 RESERVED_NR_DCI
* with CRC scrambled by SI-RNTI
* 11 FREQ_DOM_RESOURCE_ASSIGNMENT_DL:
* 12 TIME_DOM_RESOURCE_ASSIGNMENT: 0, 1, 2, 3, or 4 bits as defined in Subclause 5.1.2.1 of [6, TS 38.214]. The bitwidth for this field is determined as log2(I) bits,
* 13 VRB_TO_PRB_MAPPING: 0 bit if only resource allocation type 0
* 24 MCS:
* 26 RV:
* 55 RESERVED_NR_DCI
* with CRC scrambled by RA-RNTI
* 11 FREQ_DOM_RESOURCE_ASSIGNMENT_DL:
* 12 TIME_DOM_RESOURCE_ASSIGNMENT: 0, 1, 2, 3, or 4 bits as defined in Subclause 5.1.2.1 of [6, TS 38.214]. The bitwidth for this field is determined as log2(I) bits,
* 13 VRB_TO_PRB_MAPPING: 0 bit if only resource allocation type 0
* 24 MCS:
* 31 TB_SCALING
* 55 RESERVED_NR_DCI
* with CRC scrambled by TC-RNTI
* 0 IDENTIFIER_DCI_FORMATS:
* 11 FREQ_DOM_RESOURCE_ASSIGNMENT_DL:
* 12 TIME_DOM_RESOURCE_ASSIGNMENT: 0, 1, 2, 3, or 4 bits as defined in Subclause 5.1.2.1 of [6, TS 38.214]. The bitwidth for this field is determined as log2(I) bits,
* 13 VRB_TO_PRB_MAPPING: 0 bit if only resource allocation type 0
* 24 MCS:
* 25 NDI:
* 26 RV:
* 27 HARQ_PROCESS_NUMBER:
* 28 DAI_: For format1_1: 4 if more than one serving cell are configured in the DL and the higher layer parameter HARQ-ACK-codebook=dynamic, where the 2 MSB bits are the counter DAI and the 2 LSB bits are the total DAI
* 33 TPC_PUCCH:
*/
fapi_nr_dl_config_request_t *dl_config = get_dl_config_request(mac, slot);
fapi_nr_dl_config_request_pdu_t *dl_conf_req = &dl_config->dl_config_list[dl_config->number_pdus];
dl_conf_req->dlsch_config_pdu.rnti = dci_ind->rnti;
fapi_nr_dl_config_dlsch_pdu_rel15_t *dlsch_pdu = &dl_conf_req->dlsch_config_pdu.dlsch_config_rel15;
dlsch_pdu->pduBitmap = 0;
NR_UE_DL_BWP_t *current_DL_BWP = mac->current_DL_BWP;
NR_PDSCH_Config_t *pdsch_config = (current_DL_BWP && !mac->get_sib1) ? current_DL_BWP->pdsch_Config : NULL;
if (dci_ind->ss_type == NR_SearchSpace__searchSpaceType_PR_common) {
dlsch_pdu->BWPSize =
mac->type0_PDCCH_CSS_config.num_rbs ? mac->type0_PDCCH_CSS_config.num_rbs : mac->sc_info.initial_dl_BWPSize;
dlsch_pdu->BWPStart = dci_ind->cset_start;
} else {
dlsch_pdu->BWPSize = current_DL_BWP->BWPSize;
dlsch_pdu->BWPStart = current_DL_BWP->BWPStart;
}
nr_rnti_type_t rnti_type = get_rnti_type(mac, dci_ind->rnti);
int mux_pattern = 1;
if (rnti_type == TYPE_SI_RNTI_) {
NR_Type0_PDCCH_CSS_config_t type0_PDCCH_CSS_config = mac->type0_PDCCH_CSS_config;
mux_pattern = type0_PDCCH_CSS_config.type0_pdcch_ss_mux_pattern;
dl_conf_req->pdu_type = FAPI_NR_DL_CONFIG_TYPE_SI_DLSCH;
// in MIB SCS is signaled as 15or60 and 30or120
dlsch_pdu->SubcarrierSpacing = mac->mib->subCarrierSpacingCommon;
if (mac->frequency_range == FR2)
dlsch_pdu->SubcarrierSpacing = mac->mib->subCarrierSpacingCommon + 2;
} else {
dlsch_pdu->SubcarrierSpacing = current_DL_BWP->scs;
if (rnti_type == TYPE_RA_RNTI_) {
if (nr_timer_is_active(&mac->ra.response_window_timer)) {
dl_conf_req->pdu_type = FAPI_NR_DL_CONFIG_TYPE_RA_DLSCH;
} else {
// Discard the DCI
return -1;
}
} else {
dl_conf_req->pdu_type = FAPI_NR_DL_CONFIG_TYPE_DLSCH;
}
}
dlsch_pdu->numCsiRsForRateMatching = 0;
int slots_frame = mac->frame_structure.numb_slots_frame;
configure_ratematching_csi(dlsch_pdu,
dl_config,
rnti_type,
frame,
slot,
dlsch_pdu->SubcarrierSpacing,
slots_frame,
pdsch_config,
mac->sc_info.csi_MeasConfig);
/* IDENTIFIER_DCI_FORMATS */
/* FREQ_DOM_RESOURCE_ASSIGNMENT_DL */
if (nr_ue_process_dci_freq_dom_resource_assignment(NULL,
dlsch_pdu,
NULL,
0,
dlsch_pdu->BWPSize,
0,
dci->frequency_domain_assignment)
< 0) {
LOG_W(MAC, "[%d.%d] Invalid frequency_domain_assignment. Possibly due to false DCI. Ignoring DCI!\n", frame, slot);
return -1;
}
dlsch_pdu->refPoint = mac->get_sib1 ? 1 : 0;
/* TIME_DOM_RESOURCE_ASSIGNMENT */
int dmrs_typeA_pos = mac->dmrs_TypeA_Position;
const int coreset_type = dci_ind->coreset_type == NFAPI_NR_CSET_CONFIG_PDCCH_CONFIG; // 0 for coreset0, 1 otherwise;
NR_tda_info_t tda_info = get_dl_tda_info(current_DL_BWP,
dci_ind->ss_type,
dci->time_domain_assignment.val,
dmrs_typeA_pos,
mux_pattern,
rnti_type,
coreset_type,
mac->get_sib1);
if (!tda_info.valid_tda)
return -1;
dlsch_pdu->number_symbols = tda_info.nrOfSymbols;
dlsch_pdu->start_symbol = tda_info.startSymbolIndex;
mappingType_t type = tda_info.mapping_type;
NR_DMRS_DownlinkConfig_t *dl_dmrs_config = NULL;
if (pdsch_config) {
if (type == typeA) {
if (!pdsch_config->dmrs_DownlinkForPDSCH_MappingTypeA) {
LOG_E(MAC, "Invalid PDSCH DMRS configuration, expected typeA but not configured\n");
return -1;
} else
dl_dmrs_config = pdsch_config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup;
} else { // typeB
if (!pdsch_config->dmrs_DownlinkForPDSCH_MappingTypeB) {
LOG_E(MAC, "Invalid PDSCH DMRS configuration, expected typeB but not configured\n");
return -1;
} else
dl_dmrs_config = pdsch_config->dmrs_DownlinkForPDSCH_MappingTypeB->choice.setup;
}
}
dlsch_pdu->nscid = 0;
if (dl_dmrs_config && dl_dmrs_config->scramblingID0)
dlsch_pdu->dlDmrsScramblingId = *dl_dmrs_config->scramblingID0;
else
dlsch_pdu->dlDmrsScramblingId = mac->physCellId;
if (rnti_type == TYPE_C_RNTI_
&& dci_ind->ss_type != NR_SearchSpace__searchSpaceType_PR_common
&& pdsch_config->dataScramblingIdentityPDSCH)
dlsch_pdu->dlDataScramblingId = *pdsch_config->dataScramblingIdentityPDSCH;
else
dlsch_pdu->dlDataScramblingId = mac->physCellId;
/* dmrs symbol positions*/
dlsch_pdu->dlDmrsSymbPos = fill_dmrs_mask(pdsch_config,
NR_DL_DCI_FORMAT_1_0,
mac->dmrs_TypeA_Position,
dlsch_pdu->number_symbols,
dlsch_pdu->start_symbol,
tda_info.mapping_type,
1);
dlsch_pdu->dmrsConfigType = (dl_dmrs_config != NULL) ? (dl_dmrs_config->dmrs_Type == NULL ? 0 : 1) : 0;
/* number of DM-RS CDM groups without data according to subclause 5.1.6.2 of 3GPP TS 38.214 version 15.9.0 Release 15 */
if (dlsch_pdu->number_symbols == 2)
dlsch_pdu->n_dmrs_cdm_groups = 1;
else
dlsch_pdu->n_dmrs_cdm_groups = 2;
dlsch_pdu->dmrs_ports = 1; // only port 0 in case of DCI 1_0
/* VRB_TO_PRB_MAPPING */
dlsch_pdu->vrb_to_prb_mapping =
(dci->vrb_to_prb_mapping.val == 0) ? vrb_to_prb_mapping_non_interleaved : vrb_to_prb_mapping_interleaved;
/* MCS TABLE INDEX */
dlsch_pdu->mcs_table = get_dlsch_mcs_table(NR_DL_DCI_FORMAT_1_0,
rnti_type,
dci_ind->ss_type,
pdsch_config ? pdsch_config->mcs_Table : NULL,
NULL, // SPS not implemented,
false, // as above
NULL); // MCS-C-RNTI not implemented
/* MCS */
dlsch_pdu->mcs = dci->mcs;
NR_UE_DL_HARQ_STATUS_t *current_harq = &mac->dl_harq_info[dci->harq_pid.val];
/* NDI (only if CRC scrambled by C-RNTI or CS-RNTI or new-RNTI or TC-RNTI)*/
if (dl_conf_req->pdu_type == FAPI_NR_DL_CONFIG_TYPE_SI_DLSCH ||
dl_conf_req->pdu_type == FAPI_NR_DL_CONFIG_TYPE_RA_DLSCH ||
dci->ndi != current_harq->last_ndi) {
// new data
dlsch_pdu->new_data_indicator = true;
current_harq->R = 0;
current_harq->TBS = 0;
} else {
dlsch_pdu->new_data_indicator = false;
}
if (dl_conf_req->pdu_type != FAPI_NR_DL_CONFIG_TYPE_SI_DLSCH &&
dl_conf_req->pdu_type != FAPI_NR_DL_CONFIG_TYPE_RA_DLSCH) {
current_harq->last_ndi = dci->ndi;
}
dlsch_pdu->qamModOrder = nr_get_Qm_dl(dlsch_pdu->mcs, dlsch_pdu->mcs_table);
if (dlsch_pdu->qamModOrder == 0) {
LOG_W(MAC, "Invalid code rate or Mod order, likely due to unexpected DL DCI.\n");
return -1;
}
int R = nr_get_code_rate_dl(dlsch_pdu->mcs, dlsch_pdu->mcs_table);
if (R > 0) {
dlsch_pdu->targetCodeRate = R;
int nb_rb_oh;
if (mac->sc_info.xOverhead_PDSCH)
nb_rb_oh = 6 * (1 + *mac->sc_info.xOverhead_PDSCH);
else
nb_rb_oh = 0;
int nb_re_dmrs = ((dlsch_pdu->dmrsConfigType == NFAPI_NR_DMRS_TYPE1) ? 6 : 4) * dlsch_pdu->n_dmrs_cdm_groups;
dlsch_pdu->TBS = nr_compute_tbs(dlsch_pdu->qamModOrder,
R,
dlsch_pdu->number_rbs,
dlsch_pdu->number_symbols,
nb_re_dmrs * get_num_dmrs(dlsch_pdu->dlDmrsSymbPos),
nb_rb_oh,
0,
1);
// storing for possible retransmissions
current_harq->R = dlsch_pdu->targetCodeRate;
if (!dlsch_pdu->new_data_indicator && current_harq->TBS != dlsch_pdu->TBS) {
LOG_W(NR_MAC, "NDI indicates re-transmission but computed TBS %d doesn't match with what previously stored %d\n",
dlsch_pdu->TBS, current_harq->TBS);
dlsch_pdu->new_data_indicator = true; // treated as new data
}
current_harq->TBS = dlsch_pdu->TBS;
}
else {
dlsch_pdu->targetCodeRate = current_harq->R;
dlsch_pdu->TBS = current_harq->TBS;
}
dlsch_pdu->ldpcBaseGraph = get_BG(dlsch_pdu->TBS, dlsch_pdu->targetCodeRate);
if (dlsch_pdu->TBS == 0) {
LOG_E(MAC, "Invalid TBS = 0. Probably caused by missed detection of DCI\n");
return -1;
}
int bw_tbslbrm = current_DL_BWP ? mac->sc_info.dl_bw_tbslbrm : dlsch_pdu->BWPSize;
dlsch_pdu->tbslbrm = nr_compute_tbslbrm(dlsch_pdu->mcs_table, bw_tbslbrm, 1);
/* RV (only if CRC scrambled by C-RNTI or CS-RNTI or new-RNTI or TC-RNTI)*/
dlsch_pdu->rv = dci->rv;
/* HARQ_PROCESS_NUMBER (only if CRC scrambled by C-RNTI or CS-RNTI or new-RNTI or TC-RNTI)*/
dlsch_pdu->harq_process_nbr = dci->harq_pid.val;
/* TB_SCALING (only if CRC scrambled by P-RNTI or RA-RNTI) */
// according to TS 38.214 Table 5.1.3.2-3
if (dci->tb_scaling > 3) {
LOG_E(MAC, "invalid tb_scaling %d\n", dci->tb_scaling);
return -1;
}
const float factor[] = {1, 0.5, 0.25, 0};
dlsch_pdu->scaling_factor_S = factor[dci->tb_scaling];
/* TPC_PUCCH (only if CRC scrambled by C-RNTI or CS-RNTI or new-RNTI or TC-RNTI)*/
// according to TS 38.213 Table 7.2.1-1
if (dci->tpc > 3) {
LOG_E(MAC, "invalid tpc %d\n", dci->tpc);
return -1;
}
// Sanity check for pucch_resource_indicator value received to check for false DCI.
bool valid = false;
NR_PUCCH_Config_t *pucch_Config = mac->current_UL_BWP ? mac->current_UL_BWP->pucch_Config : NULL;
if (pucch_Config && pucch_Config->resourceSetToAddModList) {
int pucch_res_set_cnt = pucch_Config->resourceSetToAddModList->list.count;
for (int id = 0; id < pucch_res_set_cnt; id++) {
if (dci->pucch_resource_indicator < pucch_Config->resourceSetToAddModList->list.array[id]->resourceList.list.count) {
valid = true;
break;
}
}
} else
valid = true;
if (!valid) {
LOG_W(MAC,
"[%d.%d] pucch_resource_indicator value %d is out of bounds. Possibly due to false DCI. Ignoring DCI!\n",
frame,
slot,
dci->pucch_resource_indicator);
return -1;
}
/* PDSCH_TO_HARQ_FEEDBACK_TIME_IND */
// according to TS 38.213 9.2.3
const int ntn_ue_koffset = GET_NTN_UE_K_OFFSET(&mac->phy_config.config_req.ntn_config, dlsch_pdu->SubcarrierSpacing);
uint16_t feedback_ti = 0;
if (rnti_type == TYPE_RA_RNTI_) {
// RA-RNTI indicates RAR. Ensure we can decode RAR before the earliest UL scheduler call
// that can process the MSG3. Also assume that RAR is sent in the same slot as DCI (k0 == 0)
// This is not perfect as the MSG3 might end up being scheduled later, so we could be
// halting the UL scheduler for a longer time than necessary.
feedback_ti = max(1 + GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, dlsch_pdu->SubcarrierSpacing),
get_delta_for_k2(mac->current_UL_BWP->scs) + get_j_for_k2(mac->current_UL_BWP->scs));
}
if (rnti_type != TYPE_RA_RNTI_ && rnti_type != TYPE_SI_RNTI_) {
if (!get_FeedbackDisabled(mac->sc_info.downlinkHARQ_FeedbackDisabled_r17, dci->harq_pid.val)) {
feedback_ti = 1 + dci->pdsch_to_harq_feedback_timing_indicator.val + ntn_ue_koffset;
AssertFatal(feedback_ti >= GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, dlsch_pdu->SubcarrierSpacing),
"PDSCH to HARQ feedback time (%d) needs to be higher than DURATION_RX_TO_TX (%ld).\n",
feedback_ti,
GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, dlsch_pdu->SubcarrierSpacing));
// set the harq status at MAC for feedback
const int tpc[] = {-1, 0, 1, 3};
set_harq_status(mac,
dci->pucch_resource_indicator,
dci->harq_pid.val,
tpc[dci->tpc],
feedback_ti,
dci->dai[0].val,
dci_ind->n_CCE,
dci_ind->N_CCE,
frame,
slot);
}
if (dlsch_pdu->new_data_indicator)
current_harq->round = 0;
else
current_harq->round++;
if (current_harq->round < sizeofArray(mac->stats.dl.rounds))
mac->stats.dl.rounds[current_harq->round]++;
}
LOG_D(MAC,
"(nr_ue_procedures.c) rnti = %x dl_config->number_pdus = %d\n",
dl_conf_req->dlsch_config_pdu.rnti,
dl_config->number_pdus);
LOG_D(MAC,
"(nr_ue_procedures.c) frequency_domain_resource_assignment=%d \t number_rbs=%d \t start_rb=%d\n",
dci->frequency_domain_assignment.val,
dlsch_pdu->number_rbs,
dlsch_pdu->start_rb);
LOG_D(MAC,
"(nr_ue_procedures.c) time_domain_resource_assignment=%d \t number_symbols=%d \t start_symbol=%d\n",
dci->time_domain_assignment.val,
dlsch_pdu->number_symbols,
dlsch_pdu->start_symbol);
LOG_D(MAC,
"(nr_ue_procedures.c) vrb_to_prb_mapping=%d \n>>> mcs=%d\n>>> ndi=%d\n>>> rv=%d\n>>> harq_process_nbr=%d\n>>> dai=%d\n>>> "
"scaling_factor_S=%f\n>>> tpc_pucch=%d\n>>> pucch_res_ind=%d\n>>> pdsch_to_harq_feedback_time_ind=%d\n",
dlsch_pdu->vrb_to_prb_mapping,
dlsch_pdu->mcs,
dlsch_pdu->new_data_indicator,
dlsch_pdu->rv,
dlsch_pdu->harq_process_nbr,
dci->dai[0].val,
dlsch_pdu->scaling_factor_S,
dci->tpc,
dci->pucch_resource_indicator,
feedback_ti);
dlsch_pdu->k1_feedback = feedback_ti;
LOG_D(MAC, "(nr_ue_procedures.c) pdu_type=%d\n\n", dl_conf_req->pdu_type);
// the prepared dci is valid, we add it in the list
dl_config->number_pdus++;
return 0;
}
static int nr_ue_process_dci_dl_11(NR_UE_MAC_INST_t *mac,
frame_t frame,
int slot,
dci_pdu_rel15_t *dci,
fapi_nr_dci_indication_pdu_t *dci_ind)
{
/*
* with CRC scrambled by C-RNTI or CS-RNTI or new-RNTI
* 0 IDENTIFIER_DCI_FORMATS:
* 1 CARRIER_IND:
* 7 BANDWIDTH_PART_IND:
* 11 FREQ_DOM_RESOURCE_ASSIGNMENT_DL:
* 12 TIME_DOM_RESOURCE_ASSIGNMENT: 0, 1, 2, 3, or 4 bits as defined in Subclause 5.1.2.1 of [6, TS 38.214]. The bitwidth for this field is determined as log2(I) bits,
* 13 VRB_TO_PRB_MAPPING: 0 bit if only resource allocation type 0
* 14 PRB_BUNDLING_SIZE_IND:
* 15 RATE_MATCHING_IND:
* 16 ZP_CSI_RS_TRIGGER:
* 18 TB1_MCS:
* 19 TB1_NDI:
* 20 TB1_RV:
* 21 TB2_MCS:
* 22 TB2_NDI:
* 23 TB2_RV:
* 27 HARQ_PROCESS_NUMBER:
* 28 DAI_: For format1_1: 4 if more than one serving cell are configured in the DL and the higher layer parameter HARQ-ACK-codebook=dynamic, where the 2 MSB bits are the counter DAI and the 2 LSB bits are the total DAI
* 33 TPC_PUCCH:
* 34 PUCCH_RESOURCE_IND:
* 35 PDSCH_TO_HARQ_FEEDBACK_TIME_IND:
* 38 ANTENNA_PORTS:
* 39 TCI:
* 40 SRS_REQUEST:
* 43 CBGTI:
* 44 CBGFI:
* 47 DMRS_SEQ_INI:
*/
if (dci->bwp_indicator.val > NR_MAX_NUM_BWP) {
LOG_W(NR_MAC,
"[%d.%d] bwp_indicator %d > NR_MAX_NUM_BWP Possibly due to false DCI. Ignoring DCI!\n",
frame,
slot,
dci->bwp_indicator.val);
return -1;
}
NR_UE_DL_BWP_t *current_DL_BWP = mac->current_DL_BWP;
NR_PDSCH_Config_t *pdsch_Config = current_DL_BWP->pdsch_Config;
fapi_nr_dl_config_request_t *dl_config = get_dl_config_request(mac, slot);
fapi_nr_dl_config_request_pdu_t *dl_conf_req = &dl_config->dl_config_list[dl_config->number_pdus];
dl_conf_req->dlsch_config_pdu.rnti = dci_ind->rnti;
fapi_nr_dl_config_dlsch_pdu_rel15_t *dlsch_pdu = &dl_conf_req->dlsch_config_pdu.dlsch_config_rel15;
dlsch_pdu->BWPSize = current_DL_BWP->BWPSize;
dlsch_pdu->BWPStart = current_DL_BWP->BWPStart;
dlsch_pdu->SubcarrierSpacing = current_DL_BWP->scs;
nr_rnti_type_t rnti_type = get_rnti_type(mac, dci_ind->rnti);
dlsch_pdu->numCsiRsForRateMatching = 0;
int slots_frame = mac->frame_structure.numb_slots_frame;
configure_ratematching_csi(dlsch_pdu,
dl_config,
rnti_type,
frame,
slot,
current_DL_BWP->scs,
slots_frame,
pdsch_Config,
mac->sc_info.csi_MeasConfig);
/* IDENTIFIER_DCI_FORMATS */
/* CARRIER_IND */
/* BANDWIDTH_PART_IND */
// dlsch_pdu->bandwidth_part_ind = dci->bandwidth_part_ind;
/* FREQ_DOM_RESOURCE_ASSIGNMENT_DL */
if (nr_ue_process_dci_freq_dom_resource_assignment(NULL,
dlsch_pdu,
pdsch_Config,
0,
current_DL_BWP->BWPSize,
current_DL_BWP->BWPStart,
dci->frequency_domain_assignment)
< 0) {
LOG_W(MAC, "[%d.%d] Invalid frequency_domain_assignment. Possibly due to false DCI. Ignoring DCI!\n", frame, slot);
return -1;
}
dlsch_pdu->refPoint = 0;
/* TIME_DOM_RESOURCE_ASSIGNMENT */
int dmrs_typeA_pos = mac->dmrs_TypeA_Position;
int mux_pattern = 1;
const int coreset_type = dci_ind->coreset_type == NFAPI_NR_CSET_CONFIG_PDCCH_CONFIG; // 0 for coreset0, 1 otherwise;
NR_tda_info_t tda_info = get_dl_tda_info(current_DL_BWP,
dci_ind->ss_type,
dci->time_domain_assignment.val,
dmrs_typeA_pos,
mux_pattern,
rnti_type,
coreset_type,
false);
if (!tda_info.valid_tda)
return -1;
dlsch_pdu->number_symbols = tda_info.nrOfSymbols;
dlsch_pdu->start_symbol = tda_info.startSymbolIndex;
mappingType_t type = tda_info.mapping_type;
NR_DMRS_DownlinkConfig_t *dl_dmrs_config = NULL;
if (pdsch_Config) {
if (type == typeA) {
if (!pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA) {
LOG_E(MAC, "Invalid PDSCH DMRS configuration, expected typeA but not configured\n");
return -1;
} else
dl_dmrs_config = pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup;
} else { // typeB
if (!pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeB) {
LOG_E(MAC, "Invalid PDSCH DMRS configuration, expected typeB but not configured\n");
return -1;
} else
dl_dmrs_config = pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeB->choice.setup;
}
}
switch (dci->dmrs_sequence_initialization.val) {
case 0:
dlsch_pdu->nscid = 0;
if (dl_dmrs_config->scramblingID0)
dlsch_pdu->dlDmrsScramblingId = *dl_dmrs_config->scramblingID0;
else
dlsch_pdu->dlDmrsScramblingId = mac->physCellId;
break;
case 1:
dlsch_pdu->nscid = 1;
if (dl_dmrs_config->scramblingID1)
dlsch_pdu->dlDmrsScramblingId = *dl_dmrs_config->scramblingID1;
else
dlsch_pdu->dlDmrsScramblingId = mac->physCellId;
break;
default:
LOG_E(MAC, "Invalid dmrs sequence initialization value %d\n", dci->dmrs_sequence_initialization.val);
return -1;
}
if (pdsch_Config->dataScramblingIdentityPDSCH)
dlsch_pdu->dlDataScramblingId = *pdsch_Config->dataScramblingIdentityPDSCH;
else
dlsch_pdu->dlDataScramblingId = mac->physCellId;
dlsch_pdu->dmrsConfigType = dl_dmrs_config->dmrs_Type == NULL ? NFAPI_NR_DMRS_TYPE1 : NFAPI_NR_DMRS_TYPE2;
/* TODO: fix number of DM-RS CDM groups without data according to subclause 5.1.6.2 of 3GPP TS 38.214,
using tables 7.3.1.2.2-1, 7.3.1.2.2-2, 7.3.1.2.2-3, 7.3.1.2.2-4 of 3GPP TS 38.212 */
dlsch_pdu->n_dmrs_cdm_groups = 1;
/* VRB_TO_PRB_MAPPING */
if ((pdsch_Config->resourceAllocation == 1) && (pdsch_Config->vrb_ToPRB_Interleaver != NULL))
dlsch_pdu->vrb_to_prb_mapping =
(dci->vrb_to_prb_mapping.val == 0) ? vrb_to_prb_mapping_non_interleaved : vrb_to_prb_mapping_interleaved;
/* PRB_BUNDLING_SIZE_IND */
dlsch_pdu->prb_bundling_size_ind = dci->prb_bundling_size_indicator.val;
/* RATE_MATCHING_IND */
dlsch_pdu->rate_matching_ind = dci->rate_matching_indicator.val;
/* ZP_CSI_RS_TRIGGER */
dlsch_pdu->zp_csi_rs_trigger = dci->zp_csi_rs_trigger.val;
/* MCS (for transport block 1)*/
dlsch_pdu->mcs = dci->mcs;
/* NDI (for transport block 1)*/
NR_UE_DL_HARQ_STATUS_t *current_harq = &mac->dl_harq_info[dci->harq_pid.val];
if (dci->ndi != current_harq->last_ndi) {
// new data
dlsch_pdu->new_data_indicator = true;
current_harq->R = 0;
current_harq->TBS = 0;
}
else {
dlsch_pdu->new_data_indicator = false;
}
current_harq->last_ndi = dci->ndi;
/* RV (for transport block 1)*/
dlsch_pdu->rv = dci->rv;
/* MCS (for transport block 2)*/
dlsch_pdu->tb2_mcs = dci->mcs2.val;
/* NDI (for transport block 2)*/
dlsch_pdu->tb2_new_data_indicator = dci->ndi2.val;
/* RV (for transport block 2)*/
dlsch_pdu->tb2_rv = dci->rv2.val;
/* HARQ_PROCESS_NUMBER */
dlsch_pdu->harq_process_nbr = dci->harq_pid.val;
/* TPC_PUCCH */
// according to TS 38.213 Table 7.2.1-1
if (dci->tpc > 3) {
LOG_E(MAC, "invalid tpc %d\n", dci->tpc);
return -1;
}
// Sanity check for pucch_resource_indicator value received to check for false DCI.
bool valid = false;
NR_PUCCH_Config_t *pucch_Config = mac->current_UL_BWP->pucch_Config;
int pucch_res_set_cnt = pucch_Config->resourceSetToAddModList->list.count;
for (int id = 0; id < pucch_res_set_cnt; id++) {
if (dci->pucch_resource_indicator < pucch_Config->resourceSetToAddModList->list.array[id]->resourceList.list.count) {
valid = true;
break;
}
}
if (!valid) {
LOG_W(MAC,
"[%d.%d] pucch_resource_indicator value %d is out of bounds. Possibly due to false DCI. Ignoring DCI!\n",
frame,
slot,
dci->pucch_resource_indicator);
return -1;
}
/* ANTENNA_PORTS */
uint8_t n_codewords = 1; // FIXME!!!
long *max_length = dl_dmrs_config->maxLength;
long *dmrs_type = dl_dmrs_config->dmrs_Type;
dlsch_pdu->n_front_load_symb = 1; // default value
set_antenna_port_parameters(dlsch_pdu, n_codewords, max_length, dmrs_type, dci->antenna_ports.val);
/* dmrs symbol positions*/
dlsch_pdu->dlDmrsSymbPos = fill_dmrs_mask(pdsch_Config,
NR_DL_DCI_FORMAT_1_1,
mac->dmrs_TypeA_Position,
dlsch_pdu->number_symbols,
dlsch_pdu->start_symbol,
tda_info.mapping_type,
dlsch_pdu->n_front_load_symb);
/* TCI */
if (dl_conf_req->dci_config_pdu.dci_config_rel15.coreset.tci_present_in_dci == 1) {
// 0 bit if higher layer parameter tci-PresentInDCI is not enabled
// otherwise 3 bits as defined in Subclause 5.1.5 of [6, TS38.214]
dlsch_pdu->tci_state = dci->transmission_configuration_indication.val;
}
/* SRS_REQUEST */
AssertFatal(dci->srs_request.nbits == 2, "If SUL is supported in the cell, there is an additional bit in SRS request field\n");
if (dci->srs_request.val > 0)
nr_ue_aperiodic_srs_scheduling(mac, dci->srs_request.val, frame, slot);
/* CBGTI */
dlsch_pdu->cbgti = dci->cbgti.val;
/* CBGFI */
dlsch_pdu->codeBlockGroupFlushIndicator = dci->cbgfi.val;
/* DMRS_SEQ_INI */
// FIXME!!!
/* PDSCH_TO_HARQ_FEEDBACK_TIME_IND */
// according to TS 38.213 Table 9.2.3-1
const int ntn_ue_koffset = GET_NTN_UE_K_OFFSET(&mac->phy_config.config_req.ntn_config, dlsch_pdu->SubcarrierSpacing);
uint16_t feedback_ti = 0;
if (!get_FeedbackDisabled(mac->sc_info.downlinkHARQ_FeedbackDisabled_r17, dci->harq_pid.val)) {
feedback_ti = pucch_Config->dl_DataToUL_ACK->list.array[dci->pdsch_to_harq_feedback_timing_indicator.val][0] + ntn_ue_koffset;
AssertFatal(feedback_ti >= GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, dlsch_pdu->SubcarrierSpacing),
"PDSCH to HARQ feedback time (%d) needs to be higher than DURATION_RX_TO_TX (%ld). Min feedback time set in config "
"file (min_rxtxtime).\n",
feedback_ti,
GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, dlsch_pdu->SubcarrierSpacing));
// set the harq status at MAC for feedback
const int tpc[] = {-1, 0, 1, 3};
set_harq_status(mac,
dci->pucch_resource_indicator,
dci->harq_pid.val,
tpc[dci->tpc],
feedback_ti,
dci->dai[0].val,
dci_ind->n_CCE,
dci_ind->N_CCE,
frame,
slot);
}
if (dlsch_pdu->new_data_indicator)
current_harq->round = 0;
else
current_harq->round++;
if (current_harq->round < sizeofArray(mac->stats.dl.rounds))
mac->stats.dl.rounds[current_harq->round]++;
// send the ack/nack slot number to phy to indicate tx thread to wait for DLSCH decoding
dlsch_pdu->k1_feedback = feedback_ti;
dlsch_pdu->mcs_table = get_dlsch_mcs_table(NR_DL_DCI_FORMAT_1_1,
rnti_type,
dci_ind->ss_type,
pdsch_Config ? pdsch_Config->mcs_Table : NULL,
NULL, // SPS not implemented,
false, // as above
NULL); // MCS-C-RNTI not implemented
dlsch_pdu->qamModOrder = nr_get_Qm_dl(dlsch_pdu->mcs, dlsch_pdu->mcs_table);
if (dlsch_pdu->qamModOrder == 0) {
LOG_W(MAC, "Invalid code rate or Mod order, likely due to unexpected DL DCI.\n");
return -1;
}
uint8_t Nl = 0;
for (int i = 0; i < 12; i++) { // max 12 ports
if ((dlsch_pdu->dmrs_ports >> i) & 0x01)
Nl += 1;
}
NR_UE_ServingCell_Info_t *sc_info = &mac->sc_info;
int nb_rb_oh;
if (sc_info->xOverhead_PDSCH)
nb_rb_oh = 6 * (1 + *sc_info->xOverhead_PDSCH);
else
nb_rb_oh = 0;
int nb_re_dmrs = ((dmrs_type == NULL) ? 6 : 4) * dlsch_pdu->n_dmrs_cdm_groups;
int number_rbs;
if (dlsch_pdu->resource_alloc == 1) {
number_rbs = dlsch_pdu->number_rbs;
} else {
uint32_t temp_bitmap[9];
memcpy(temp_bitmap, dlsch_pdu->rb_bitmap, 36);
number_rbs = count_bits(temp_bitmap, 9);
}
int R = nr_get_code_rate_dl(dlsch_pdu->mcs, dlsch_pdu->mcs_table);
if (R > 0) {
dlsch_pdu->targetCodeRate = R;
dlsch_pdu->TBS = nr_compute_tbs(dlsch_pdu->qamModOrder,
R,
number_rbs,
dlsch_pdu->number_symbols,
nb_re_dmrs * get_num_dmrs(dlsch_pdu->dlDmrsSymbPos),
nb_rb_oh,
0,
Nl);
// storing for possible retransmissions
if (!dlsch_pdu->new_data_indicator && current_harq->TBS != dlsch_pdu->TBS) {
LOG_W(NR_MAC, "NDI indicates re-transmission but computed TBS %d doesn't match with what previously stored %d\n",
dlsch_pdu->TBS, current_harq->TBS);
dlsch_pdu->new_data_indicator = true; // treated as new data
}
current_harq->R = dlsch_pdu->targetCodeRate;
current_harq->TBS = dlsch_pdu->TBS;
}
else {
dlsch_pdu->targetCodeRate = current_harq->R;
dlsch_pdu->TBS = current_harq->TBS;
}
dlsch_pdu->ldpcBaseGraph = get_BG(dlsch_pdu->TBS, dlsch_pdu->targetCodeRate);
if (dlsch_pdu->TBS == 0) {
LOG_E(MAC, "Invalid TBS = 0. Probably caused by missed detection of DCI\n");
return -1;
}
// TBS_LBRM according to section 5.4.2.1 of 38.212
int max_mimo_layers = 0;
if (sc_info->maxMIMO_Layers_PDSCH)
max_mimo_layers = *sc_info->maxMIMO_Layers_PDSCH;
else
max_mimo_layers = mac->uecap_maxMIMO_PDSCH_layers;
AssertFatal(max_mimo_layers > 0, "Invalid number of max MIMO layers for PDSCH\n");
int nl_tbslbrm = max_mimo_layers < 4 ? max_mimo_layers : 4;
dlsch_pdu->tbslbrm = nr_compute_tbslbrm(dlsch_pdu->mcs_table, sc_info->dl_bw_tbslbrm, nl_tbslbrm);
/*PTRS configuration */
dlsch_pdu->pduBitmap = 0;
if (dl_dmrs_config->phaseTrackingRS != NULL) {
bool valid_ptrs_setup =
set_dl_ptrs_values(dl_dmrs_config->phaseTrackingRS->choice.setup,
number_rbs,
dlsch_pdu->mcs,
dlsch_pdu->mcs_table,
&dlsch_pdu->PTRSFreqDensity,
&dlsch_pdu->PTRSTimeDensity,
&dlsch_pdu->PTRSPortIndex,
&dlsch_pdu->nEpreRatioOfPDSCHToPTRS,
&dlsch_pdu->PTRSReOffset,
dlsch_pdu->number_symbols);
if (valid_ptrs_setup == true) {
dlsch_pdu->pduBitmap |= 0x1;
LOG_D(MAC, "DL PTRS values: PTRS time den: %d, PTRS freq den: %d\n", dlsch_pdu->PTRSTimeDensity, dlsch_pdu->PTRSFreqDensity);
}
}
// the prepared dci is valid, we add it in the list
dl_conf_req->pdu_type = FAPI_NR_DL_CONFIG_TYPE_DLSCH;
dl_config->number_pdus++; // The DCI configuration is valid, we add it in the list
return 0;
}
static int8_t nr_ue_process_dci(NR_UE_MAC_INST_t *mac,
frame_t frame,
int slot,
dci_pdu_rel15_t *dci,
fapi_nr_dci_indication_pdu_t *dci_ind,
const nr_dci_format_t format)
{
const char *dci_formats[] = {"1_0", "1_1", "2_0", "2_1", "2_2", "2_3", "0_0", "0_1"};
LOG_D(MAC, "Processing received DCI format %s\n", dci_formats[format]);
switch (format) {
case NR_UL_DCI_FORMAT_0_0:
return nr_ue_process_dci_ul_00(mac, frame, slot, dci, dci_ind);
break;
case NR_UL_DCI_FORMAT_0_1:
return nr_ue_process_dci_ul_01(mac, frame, slot, dci, dci_ind);
break;
case NR_DL_DCI_FORMAT_1_0:
return nr_ue_process_dci_dl_10(mac, frame, slot, dci, dci_ind);
break;
case NR_DL_DCI_FORMAT_1_1:
return nr_ue_process_dci_dl_11(mac, frame, slot, dci, dci_ind);
break;
case NR_DL_DCI_FORMAT_2_0:
AssertFatal(false, "DCI Format 2-0 handling not implemented\n");
break;
case NR_DL_DCI_FORMAT_2_1:
AssertFatal(false, "DCI Format 2-1 handling not implemented\n");
break;
case NR_DL_DCI_FORMAT_2_2:
AssertFatal(false, "DCI Format 2-2 handling not implemented\n");
break;
case NR_DL_DCI_FORMAT_2_3:
AssertFatal(false, "DCI Format 2-3 handling not implemented\n");
break;
default:
break;
}
return -1;
}
void nr_ue_process_l1_measurements(NR_UE_MAC_INST_t *mac, frame_t frame, int slot, fapi_nr_l1_measurements_t *l1_measurements)
{
LOG_D(NR_MAC, "(%d.%d) Received measurements from L1\n", frame, slot);
bool csi_meas = l1_measurements->meas_type == NFAPI_NR_CSI_MEAS;
if (!csi_meas && !l1_measurements->is_neighboring_cell) {
int ssb_index = l1_measurements->ssb_index;
mac->ssb_measurements[ssb_index].ssb_rsrp_dBm = l1_measurements->rsrp_dBm;
mac->ssb_measurements[ssb_index].ssb_sinr_dB = l1_measurements->sinr_dB;
} else if (csi_meas) {
mac->csirs_measurements.rsrp_dBm = l1_measurements->rsrp_dBm;
mac->csirs_measurements.i1 = l1_measurements->i1;
mac->csirs_measurements.i2 = l1_measurements->i2;
mac->csirs_measurements.cqi = l1_measurements->cqi;
mac->csirs_measurements.ri = l1_measurements->rank_indicator;
}
nr_mac_rrc_meas_ind_ue(mac->ue_id,
l1_measurements->gNB_index,
l1_measurements->Nid_cell,
csi_meas,
l1_measurements->is_neighboring_cell,
l1_measurements->rsrp_dBm);
}
initial_pucch_resource_t get_initial_pucch_resource(const int idx)
{
return initial_pucch_resource[idx];
}
int nr_ue_configure_pucch(NR_UE_MAC_INST_t *mac,
int slot,
frame_t frame,
uint16_t rnti,
PUCCH_sched_t *pucch,
fapi_nr_ul_config_pucch_pdu *pucch_pdu)
{
NR_UE_UL_BWP_t *current_UL_BWP = mac->current_UL_BWP;
NR_UE_ServingCell_Info_t *sc_info = &mac->sc_info;
NR_PUCCH_FormatConfig_t *pucchfmt;
long *pusch_id = NULL;
long *id0 = NULL;
const int scs = current_UL_BWP->scs;
int subframe_number = slot / (mac->frame_structure.numb_slots_frame / 10);
pucch_pdu->rnti = rnti;
LOG_D(NR_MAC, "initial_pucch_id %d, pucch_resource %p\n", pucch->initial_pucch_id, pucch->pucch_resource);
// configure pucch from Table 9.2.1-1
// only for ack/nack
if (pucch->initial_pucch_id > -1 && pucch->pucch_resource == NULL) {
const initial_pucch_resource_t pucch_resourcecommon = get_initial_pucch_resource(pucch->pucch_ResourceCommon);
pucch_pdu->format_type = pucch_resourcecommon.format;
pucch_pdu->start_symbol_index = pucch_resourcecommon.startingSymbolIndex;
pucch_pdu->nr_of_symbols = pucch_resourcecommon.nrofSymbols;
pucch_pdu->bwp_size = current_UL_BWP->BWPSize;
pucch_pdu->bwp_start = current_UL_BWP->BWPStart;
pucch_pdu->prb_size = 1; // format 0 or 1
int RB_BWP_offset;
if (pucch->pucch_ResourceCommon == 15)
RB_BWP_offset = pucch_pdu->bwp_size >> 2;
else
RB_BWP_offset = pucch_resourcecommon.PRB_offset;
int N_CS = pucch_resourcecommon.nb_CS_indexes;
if (pucch->initial_pucch_id >> 3 == 0) {
const int tmp = pucch->initial_pucch_id / N_CS;
pucch_pdu->prb_start = RB_BWP_offset + tmp;
pucch_pdu->second_hop_prb = pucch_pdu->bwp_size - 1 - RB_BWP_offset - tmp;
pucch_pdu->initial_cyclic_shift = pucch_resourcecommon.initial_CS_indexes[pucch->initial_pucch_id % N_CS];
} else {
const int tmp = (pucch->initial_pucch_id - 8) / N_CS;
pucch_pdu->prb_start = pucch_pdu->bwp_size - 1 - RB_BWP_offset - tmp;
pucch_pdu->second_hop_prb = RB_BWP_offset + tmp;
pucch_pdu->initial_cyclic_shift = pucch_resourcecommon.initial_CS_indexes[(pucch->initial_pucch_id - 8) % N_CS];
}
pucch_pdu->freq_hop_flag = 1;
pucch_pdu->time_domain_occ_idx = 0;
// Only HARQ transmitted in default PUCCH
pucch_pdu->mcs = get_pucch0_mcs(pucch->n_harq, 0, pucch->ack_payload, 0);
pucch_pdu->payload = pucch->ack_payload;
pucch_pdu->n_bit = 1;
}
else if (pucch->pucch_resource != NULL) {
NR_PUCCH_Resource_t *pucchres = pucch->pucch_resource;
if (mac->harq_ACK_SpatialBundlingPUCCH ||
mac->pdsch_HARQ_ACK_Codebook != NR_PhysicalCellGroupConfig__pdsch_HARQ_ACK_Codebook_dynamic) {
LOG_E(NR_MAC, "PUCCH Unsupported cell group configuration\n");
return -1;
} else if (sc_info->pdsch_CGB_Transmission) {
LOG_E(NR_MAC, "PUCCH Unsupported code block group for serving cell config\n");
return -1;
}
NR_PUSCH_Config_t *pusch_Config = current_UL_BWP ? current_UL_BWP->pusch_Config : NULL;
if (pusch_Config) {
pusch_id = pusch_Config->dataScramblingIdentityPUSCH;
struct NR_SetupRelease_DMRS_UplinkConfig *tmp = pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA;
if (tmp && tmp->choice.setup->transformPrecodingDisabled != NULL)
id0 = tmp->choice.setup->transformPrecodingDisabled->scramblingID0;
else {
struct NR_SetupRelease_DMRS_UplinkConfig *tmp = pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB;
if (tmp && tmp->choice.setup->transformPrecodingDisabled != NULL)
id0 = tmp->choice.setup->transformPrecodingDisabled->scramblingID0;
}
}
NR_PUCCH_Config_t *pucch_Config = current_UL_BWP->pucch_Config;
AssertFatal(pucch_Config, "no pucch_Config\n");
pucch_pdu->bwp_size = current_UL_BWP->BWPSize;
pucch_pdu->bwp_start = current_UL_BWP->BWPStart;
pucch_pdu->prb_start = pucchres->startingPRB;
pucch_pdu->freq_hop_flag = pucchres->intraSlotFrequencyHopping!= NULL ? 1 : 0;
pucch_pdu->second_hop_prb = pucchres->secondHopPRB!= NULL ? *pucchres->secondHopPRB : 0;
pucch_pdu->prb_size = 1; // format 0 or 1
int n_uci = pucch->n_sr + pucch->n_harq + pucch->csi_payload.p1_bits;
if (n_uci > (sizeof(uint64_t) * 8)) {
LOG_E(NR_MAC, "PUCCH number of UCI bits exceeds payload size\n");
return -1;
}
switch(pucchres->format.present) {
case NR_PUCCH_Resource__format_PR_format0 :
pucch_pdu->format_type = 0;
pucch_pdu->initial_cyclic_shift = pucchres->format.choice.format0->initialCyclicShift;
pucch_pdu->nr_of_symbols = pucchres->format.choice.format0->nrofSymbols;
pucch_pdu->start_symbol_index = pucchres->format.choice.format0->startingSymbolIndex;
pucch_pdu->mcs = get_pucch0_mcs(pucch->n_harq, pucch->n_sr, pucch->ack_payload, pucch->sr_payload);
break;
case NR_PUCCH_Resource__format_PR_format1 :
pucch_pdu->format_type = 1;
pucch_pdu->initial_cyclic_shift = pucchres->format.choice.format1->initialCyclicShift;
pucch_pdu->nr_of_symbols = pucchres->format.choice.format1->nrofSymbols;
pucch_pdu->start_symbol_index = pucchres->format.choice.format1->startingSymbolIndex;
pucch_pdu->time_domain_occ_idx = pucchres->format.choice.format1->timeDomainOCC;
if (pucch->n_harq > 0) {
// only HARQ bits are transmitted, resource selection depends on SR
// resource selection handled in function multiplex_pucch_resource
pucch_pdu->n_bit = pucch->n_harq;
pucch_pdu->payload = pucch->ack_payload;
}
else {
// For a positive SR transmission using PUCCH format 1,
// the UE transmits the PUCCH as described in 38.211 by setting b(0) = 0
pucch_pdu->n_bit = pucch->n_sr;
pucch_pdu->payload = 0;
}
break;
case NR_PUCCH_Resource__format_PR_format2 :
pucch_pdu->format_type = 2;
pucch_pdu->n_bit = n_uci;
pucch_pdu->nr_of_symbols = pucchres->format.choice.format2->nrofSymbols;
pucch_pdu->start_symbol_index = pucchres->format.choice.format2->startingSymbolIndex;
pucch_pdu->data_scrambling_id = pusch_id != NULL ? *pusch_id : mac->physCellId;
pucch_pdu->dmrs_scrambling_id = id0 != NULL ? *id0 : mac->physCellId;
pucch_pdu->prb_size = compute_pucch_prb_size(2,
pucchres->format.choice.format2->nrofPRBs,
pucch->csi_payload.p1_bits,
pucch->n_harq,
pucch->n_sr,
pucch_Config->format2->choice.setup->maxCodeRate,
2,
pucchres->format.choice.format2->nrofSymbols,
8);
pucch_pdu->payload = (pucch->csi_payload.part1_payload << (pucch->n_harq + pucch->n_sr))
| (pucch->sr_payload << pucch->n_harq) | pucch->ack_payload;
break;
case NR_PUCCH_Resource__format_PR_format3 :
pucch_pdu->format_type = 3;
pucch_pdu->n_bit = n_uci;
pucch_pdu->nr_of_symbols = pucchres->format.choice.format3->nrofSymbols;
pucch_pdu->start_symbol_index = pucchres->format.choice.format3->startingSymbolIndex;
pucch_pdu->data_scrambling_id = pusch_id != NULL ? *pusch_id : mac->physCellId;
if (pucch_Config->format3 == NULL) {
pucch_pdu->pi_2bpsk = 0;
pucch_pdu->add_dmrs_flag = 0;
}
else {
pucchfmt = pucch_Config->format3->choice.setup;
pucch_pdu->pi_2bpsk = pucchfmt->pi2BPSK!= NULL ? 1 : 0;
pucch_pdu->add_dmrs_flag = pucchfmt->additionalDMRS!= NULL ? 1 : 0;
}
int f3_dmrs_symbols;
if (pucchres->format.choice.format3->nrofSymbols==4)
f3_dmrs_symbols = 1<<pucch_pdu->freq_hop_flag;
else {
if(pucchres->format.choice.format3->nrofSymbols<10)
f3_dmrs_symbols = 2;
else
f3_dmrs_symbols = 2<<pucch_pdu->add_dmrs_flag;
}
pucch_pdu->prb_size = compute_pucch_prb_size(3,
pucchres->format.choice.format3->nrofPRBs,
pucch->csi_payload.p1_bits,
pucch->n_harq,
pucch->n_sr,
pucch_Config->format3->choice.setup->maxCodeRate,
2 - pucch_pdu->pi_2bpsk,
pucchres->format.choice.format3->nrofSymbols - f3_dmrs_symbols,
12);
pucch_pdu->payload = (pucch->csi_payload.part1_payload << (pucch->n_harq + pucch->n_sr))
| (pucch->sr_payload << pucch->n_harq) | pucch->ack_payload;
break;
case NR_PUCCH_Resource__format_PR_format4 :
pucch_pdu->format_type = 4;
pucch_pdu->nr_of_symbols = pucchres->format.choice.format4->nrofSymbols;
pucch_pdu->start_symbol_index = pucchres->format.choice.format4->startingSymbolIndex;
pucch_pdu->pre_dft_occ_len = pucchres->format.choice.format4->occ_Length;
pucch_pdu->pre_dft_occ_idx = pucchres->format.choice.format4->occ_Index;
pucch_pdu->data_scrambling_id = pusch_id!= NULL ? *pusch_id : mac->physCellId;
if (pucch_Config->format3 == NULL) {
pucch_pdu->pi_2bpsk = 0;
pucch_pdu->add_dmrs_flag = 0;
}
else {
pucchfmt = pucch_Config->format3->choice.setup;
pucch_pdu->pi_2bpsk = pucchfmt->pi2BPSK!= NULL ? 1 : 0;
pucch_pdu->add_dmrs_flag = pucchfmt->additionalDMRS!= NULL ? 1 : 0;
}
pucch_pdu->payload = (pucch->csi_payload.part1_payload << (pucch->n_harq + pucch->n_sr))
| (pucch->sr_payload << pucch->n_harq) | pucch->ack_payload;
break;
default :
LOG_E(NR_MAC, "Undefined PUCCH format \n");
return -1;
}
int sum_delta_pucch = get_sum_delta_pucch(mac, slot, frame);
pucch_pdu->pucch_tx_power = get_pucch_tx_power_ue(mac,
scs,
pucch_Config,
sum_delta_pucch,
pucch_pdu->format_type,
pucch_pdu->prb_size,
pucch_pdu->freq_hop_flag,
pucch_pdu->add_dmrs_flag,
pucch_pdu->nr_of_symbols,
subframe_number,
n_uci,
pucch_pdu->prb_start);
} else {
LOG_E(NR_MAC, "problem with pucch configuration\n");
return -1;
}
NR_PUCCH_ConfigCommon_t *pucch_ConfigCommon = current_UL_BWP->pucch_ConfigCommon;
if (pucch_ConfigCommon->hoppingId != NULL)
pucch_pdu->hopping_id = *pucch_ConfigCommon->hoppingId;
else
pucch_pdu->hopping_id = mac->physCellId;
switch (pucch_ConfigCommon->pucch_GroupHopping){
case 0 :
// if neither, both disabled
pucch_pdu->group_hop_flag = 0;
pucch_pdu->sequence_hop_flag = 0;
break;
case 1 :
// if enable, group enabled
pucch_pdu->group_hop_flag = 1;
pucch_pdu->sequence_hop_flag = 0;
break;
case 2 :
// if disable, sequence disabled
pucch_pdu->group_hop_flag = 0;
pucch_pdu->sequence_hop_flag = 1;
break;
default:
LOG_E(NR_MAC, "Group hopping flag undefined (0,1,2) \n");
return -1;
}
return 0;
}
static int find_pucch_resource_set(NR_PUCCH_Config_t *pucch_Config, int size)
{
// Procedure described in 38.213 Section 9.2.1
AssertFatal(pucch_Config && pucch_Config->resourceSetToAddModList, "pucch-Config NULL, this function shouldn't have been called\n");
AssertFatal(size <= 1706, "O_UCI cannot be larger that 1706 bits\n");
// a first set of PUCCH resources with pucch-ResourceSetId = 0 if O UCI ≤ 2 including 1 or 2 HARQ-ACK information bits
if (size <= 2)
return 0;
const int n_set = pucch_Config->resourceSetToAddModList->list.count;
int N2 = 1706;
int N3 = 1706;
for (int i = 0; i < n_set; i++) {
NR_PUCCH_ResourceSet_t *pucchresset = pucch_Config->resourceSetToAddModList->list.array[i];
NR_PUCCH_ResourceId_t id = pucchresset->pucch_ResourceSetId;
if (id == 1)
N2 = pucchresset->maxPayloadSize ? *pucchresset->maxPayloadSize : 1706;
if (id == 2)
N3 = pucchresset->maxPayloadSize ? *pucchresset->maxPayloadSize : 1706;
}
// a second set of PUCCH resources with pucch-ResourceSetId = 1, if provided by higher layers, if 2 < O UCI ≤ N 2
if (size <= N2)
return 1;
// a third set of PUCCH resources with pucch-ResourceSetId = 2, if provided by higher layers, if N 2 < O UCI ≤ N 3
if (size <= N3)
return 2;
// a fourth set of PUCCH resources with pucch-ResourceSetId = 3, if provided by higher layers, if N 3 < O UCI ≤ 1706
return 3;
}
NR_PUCCH_Resource_t *find_pucch_resource_from_list(struct NR_PUCCH_Config__resourceToAddModList *resourceToAddModList,
long resource_id)
{
NR_PUCCH_Resource_t *pucch_resource = NULL;
int n_list = resourceToAddModList->list.count;
for (int i = 0; i < n_list; i++) {
if (resourceToAddModList->list.array[i]->pucch_ResourceId == resource_id)
pucch_resource = resourceToAddModList->list.array[i];
}
return pucch_resource;
}
static bool check_mux_acknack_csi(NR_PUCCH_Resource_t *csi_res, NR_PUCCH_Config_t *pucch_Config)
{
bool ret;
switch (csi_res->format.present) {
case NR_PUCCH_Resource__format_PR_format2:
ret = pucch_Config->format2->choice.setup->simultaneousHARQ_ACK_CSI ? true : false;
break;
case NR_PUCCH_Resource__format_PR_format3:
ret = pucch_Config->format3->choice.setup->simultaneousHARQ_ACK_CSI ? true : false;
break;
case NR_PUCCH_Resource__format_PR_format4:
ret = pucch_Config->format4->choice.setup->simultaneousHARQ_ACK_CSI ? true : false;
break;
default:
AssertFatal(false, "Invalid PUCCH format for CSI\n");
}
return ret;
}
void get_pucch_start_symbol_length(NR_PUCCH_Resource_t *pucch_resource, int *start, int *length)
{
switch (pucch_resource->format.present) {
case NR_PUCCH_Resource__format_PR_format0:
*length = pucch_resource->format.choice.format0->nrofSymbols;
*start = pucch_resource->format.choice.format0->startingSymbolIndex;
break;
case NR_PUCCH_Resource__format_PR_format1:
*length = pucch_resource->format.choice.format1->nrofSymbols;
*start = pucch_resource->format.choice.format1->startingSymbolIndex;
break;
case NR_PUCCH_Resource__format_PR_format2:
*length = pucch_resource->format.choice.format2->nrofSymbols;
*start = pucch_resource->format.choice.format2->startingSymbolIndex;
break;
case NR_PUCCH_Resource__format_PR_format3:
*length = pucch_resource->format.choice.format3->nrofSymbols;
*start = pucch_resource->format.choice.format3->startingSymbolIndex;
break;
case NR_PUCCH_Resource__format_PR_format4:
*length = pucch_resource->format.choice.format4->nrofSymbols;
*start = pucch_resource->format.choice.format4->startingSymbolIndex;
break;
default:
AssertFatal(false, "Invalid PUCCH format\n");
}
}
// Ref. 38.213 section 9.2.5 order(Q)
void order_resources(PUCCH_sched_t *pucch, int num_res)
{
int k = 0;
while (k < num_res - 1) {
int l = 0;
while (l < num_res - 1 - k) {
NR_PUCCH_Resource_t *pucch_resource = pucch[l].pucch_resource;
int curr_start, curr_length;
get_pucch_start_symbol_length(pucch_resource, &curr_start, &curr_length);
pucch_resource = pucch[l + 1].pucch_resource;
int next_start, next_length;
get_pucch_start_symbol_length(pucch_resource, &next_start, &next_length);
if (curr_start > next_start || (curr_start == next_start && curr_length < next_length)) {
// swap resources
PUCCH_sched_t temp_res = pucch[l];
pucch[l] = pucch[l + 1];
pucch[l + 1] = temp_res;
}
l++;
}
k++;
}
}
bool check_overlapping_resources(PUCCH_sched_t *pucch, int j, int o)
{
// assuming overlapping means if two resources overlaps in time,
// ie share a symbol in the slot regardless of PRB
NR_PUCCH_Resource_t *pucch_resource = pucch[j - o].pucch_resource;
int curr_start, curr_length;
get_pucch_start_symbol_length(pucch_resource, &curr_start, &curr_length);
pucch_resource = pucch[j + 1].pucch_resource;
int next_start, next_length;
get_pucch_start_symbol_length(pucch_resource, &next_start, &next_length);
if (curr_start == next_start)
return true;
if (curr_start + curr_length - 1 < next_start)
return false;
else
return true;
}
// 38.213 section 9.2.5
void merge_resources(PUCCH_sched_t *res, int num_res, NR_PUCCH_Config_t *pucch_Config)
{
PUCCH_sched_t empty = {0};
for (int i = 0; i < num_res - 1; i++) {
NR_PUCCH_Resource_t *curr_resource = res[i].pucch_resource;
NR_PUCCH_Resource_t *next_resource = res[i + 1].pucch_resource;
switch (curr_resource->format.present) {
case NR_PUCCH_Resource__format_PR_format0:
switch (next_resource->format.present) {
case NR_PUCCH_Resource__format_PR_format0:
if (res[i].n_sr > 0 && res[i].n_harq == 0 && res[i + 1].n_sr == 0 && res[i + 1].n_harq > 0) {
// we multiplex SR and HARQ in the HARQ resource
res[i + 1].n_sr = res[i].n_sr;
res[i + 1].sr_payload = res[i].sr_payload;
res[i] = empty;
} else if (res[i].n_sr == 0 && res[i].n_harq > 0 && res[i + 1].n_sr > 0 && res[i + 1].n_harq == 0) {
// we multiplex SR and HARQ in the HARQ resource and move it to the i+1 spot
res[i].n_sr = res[i + 1].n_sr;
res[i].sr_payload = res[i + 1].sr_payload;
res[i + 1] = res[i];
res[i] = empty;
} else
AssertFatal(
false,
"We cannot multiplex more than 1 SR into a PUCCH F0 and we don't expect more than 1 PUCCH with HARQ per slot\n");
break;
case NR_PUCCH_Resource__format_PR_format1:
if (res[i].n_sr > 0 && res[i].n_harq == 0 && res[i + 1].n_sr == 0 && res[i + 1].n_harq > 0)
// we transmit only HARQ in F1
res[i] = empty;
else if (res[i].n_sr == 0 && res[i].n_harq > 0 && res[i + 1].n_sr > 0 && res[i + 1].n_harq == 0) {
// we multiplex SR and HARQ in the HARQ F0 resource and move it to the i+1 spot
res[i].n_sr = res[i + 1].n_sr;
res[i].sr_payload = res[i + 1].sr_payload;
res[i + 1] = res[i];
res[i] = empty;
} else
AssertFatal(
false,
"We cannot multiplex more than 1 SR into a PUCCH F0 and we don't expect more than 1 PUCCH with HARQ per slot\n");
break;
case NR_PUCCH_Resource__format_PR_format2:
case NR_PUCCH_Resource__format_PR_format3:
case NR_PUCCH_Resource__format_PR_format4:
if (res[i].n_sr > 0 && res[i].n_harq == 0) {
AssertFatal(res[i + 1].n_sr == 0, "We don't support multiple SR in a slot\n");
// we multiplex SR in the F2 or above resource
res[i + 1].n_sr = res[i].n_sr;
res[i + 1].sr_payload = res[i].sr_payload;
res[i] = empty;
} else if (res[i].n_harq > 0) {
AssertFatal(res[i + 1].n_harq == 0, "The standard doesn't allow for more the 1 PUCCH in a slot with HARQ\n");
if (check_mux_acknack_csi(next_resource, pucch_Config)) {
// we multiplex what in F0 to CSI resource
if (res[i + 1].n_sr == 0) {
res[i + 1].n_sr = res[i].n_sr;
res[i + 1].sr_payload = res[i].sr_payload;
} else
AssertFatal(res[i].n_sr == 0, "We don't support more than 1 SR in a slot\n");
res[i + 1].n_harq = res[i].n_harq;
res[i + 1].ack_payload = res[i].ack_payload;
res[i] = empty;
} else {
// if we can't multiplex HARQ and CSI we discard CSI
if (res[i + 1].n_sr == 0) {
res[i + 1] = res[i];
res[i] = empty;
} else {
AssertFatal(res[i].n_sr == 0, "We don't support more than 1 SR in a slot\n");
// we take SR from F2 or above into F0 and drop CSI
res[i].n_sr = res[i + 1].n_sr;
res[i].sr_payload = res[i + 1].sr_payload;
res[i + 1] = res[i];
res[i] = empty;
}
}
}
break;
default:
AssertFatal(false, "Invalid PUCCH format %d\n", next_resource->format.present);
}
break;
case NR_PUCCH_Resource__format_PR_format1:
switch (next_resource->format.present) {
case NR_PUCCH_Resource__format_PR_format0:
if (res[i].n_sr > 0 && res[i].n_harq == 0 && res[i + 1].n_sr == 0 && res[i + 1].n_harq > 0) {
// we multiplex SR and HARQ in the HARQ F0 resource and move it to the i+1 spot
res[i + 1].n_sr = res[i].n_sr;
res[i + 1].sr_payload = res[i].sr_payload;
res[i] = empty;
} else if (res[i].n_sr == 0 && res[i].n_harq > 0 && res[i + 1].n_sr > 0 && res[i + 1].n_harq == 0) {
// we transmit only HARQ in F1
res[i + 1] = res[i];
res[i] = empty;
} else
AssertFatal(
false,
"We cannot multiplex more than 1 SR into a PUCCH F0 and we don't expect more than 1 PUCCH with HARQ per slot\n");
break;
case NR_PUCCH_Resource__format_PR_format1:
if (res[i].n_sr > 0 && res[i].n_harq == 0 && res[i + 1].n_sr == 0 && res[i + 1].n_harq > 0) {
if (res[i].sr_payload == 0) {
// negative SR -> transmit HARQ only in HARQ resource
res[i] = empty;
} else {
// positive SR -> transmit HARQ only in SR resource
res[i].n_harq = res[i + 1].n_harq;
res[i].ack_payload = res[i + 1].ack_payload;
res[i].n_sr = 0;
res[i].sr_payload = 0;
res[i + 1] = res[i];
res[i] = empty;
}
} else if (res[i].n_sr == 0 && res[i].n_harq > 0 && res[i + 1].n_sr > 0 && res[i + 1].n_harq == 0) {
if (res[i].sr_payload == 0) {
// negative SR -> transmit HARQ only in HARQ resource
res[i + 1] = res[i];
res[i] = empty;
} else {
// positive SR -> transmit HARQ only in SR resource
res[i + 1].n_harq = res[i].n_harq;
res[i + 1].ack_payload = res[i].ack_payload;
res[i + 1].n_sr = 0;
res[i + 1].sr_payload = 0;
res[i] = empty;
}
} else
AssertFatal(
false,
"We cannot multiplex more than 1 SR into a PUCCH F0 and we don't expect more than 1 PUCCH with HARQ per slot\n");
break;
case NR_PUCCH_Resource__format_PR_format2:
case NR_PUCCH_Resource__format_PR_format4:
case NR_PUCCH_Resource__format_PR_format3:
if (res[i].n_sr > 0 && res[i].n_harq == 0) {
AssertFatal(res[i + 1].n_sr == 0, "We don't support multiple SR in a slot\n");
// we multiplex SR in the F2 or above resource
res[i + 1].n_sr = res[i].n_sr;
res[i + 1].sr_payload = res[i].sr_payload;
res[i] = empty;
} else if (res[i].n_harq > 0) {
AssertFatal(res[i + 1].n_harq == 0, "The standard doesn't allow for more the 1 PUCCH in a slot with HARQ\n");
if (check_mux_acknack_csi(next_resource, pucch_Config)) {
// we multiplex what in F0 to CSI resource
if (res[i + 1].n_sr == 0) {
res[i + 1].n_sr = res[i].n_sr;
res[i + 1].sr_payload = res[i].sr_payload;
} else
AssertFatal(res[i].n_sr == 0, "We don't support more than 1 SR in a slot\n");
res[i + 1].n_harq = res[i].n_harq;
res[i + 1].ack_payload = res[i].ack_payload;
res[i] = empty;
} else {
// if we can't multiplex HARQ and CSI we discard CSI
if (res[i + 1].n_sr == 0) {
res[i + 1] = res[i];
res[i] = empty;
} else {
AssertFatal(res[i].n_sr == 0, "We don't support more than 1 SR in a slot\n");
if (res[i + 1].n_sr > 0)
LOG_E(MAC, "Not sure what to do here because you can't easily mux HARQ and SR in F1\n");
res[i + 1] = res[i];
res[i] = empty;
}
}
}
break;
default:
AssertFatal(false, "Invalid PUCCH format %d\n", next_resource->format.present);
}
break;
case NR_PUCCH_Resource__format_PR_format2:
case NR_PUCCH_Resource__format_PR_format4:
case NR_PUCCH_Resource__format_PR_format3:
switch (next_resource->format.present) {
case NR_PUCCH_Resource__format_PR_format0:
if (res[i + 1].n_sr > 0 && res[i + 1].n_harq == 0) {
AssertFatal(res[i].n_sr == 0, "We don't support multiple SR in a slot\n");
// we multiplex SR in the F2 or above resource
res[i].n_sr = res[i + 1].n_sr;
res[i].sr_payload = res[i + 1].sr_payload;
res[i + 1] = res[i];
res[i] = empty;
} else if (res[i + 1].n_harq > 0) {
AssertFatal(res[i].n_harq == 0, "The standard doesn't allow for more the 1 PUCCH in a slot with HARQ\n");
if (check_mux_acknack_csi(curr_resource, pucch_Config)) {
// we multiplex what in F0 to CSI resource
if (res[i].n_sr == 0) {
res[i].n_sr = res[i + 1].n_sr;
res[i].sr_payload = res[i + 1].sr_payload;
} else
AssertFatal(res[i + 1].n_sr == 0, "We don't support more than 1 SR in a slot\n");
res[i].n_harq = res[i + 1].n_harq;
res[i].ack_payload = res[i + 1].ack_payload;
res[i + 1] = res[i];
res[i] = empty;
} else {
// if we can't multiplex HARQ and CSI we discard CSI
if (res[i].n_sr == 0)
res[i] = empty;
else {
AssertFatal(res[i + 1].n_sr == 0, "We don't support more than 1 SR in a slot\n");
// we take SR from F2 or above into F0 and drop CSI
res[i + 1].n_sr = res[i].n_sr;
res[i + 1].sr_payload = res[i].sr_payload;
res[i] = empty;
}
}
}
break;
case NR_PUCCH_Resource__format_PR_format1:
if (res[i + 1].n_sr > 0 && res[i + 1].n_harq == 0) {
AssertFatal(res[i].n_sr == 0, "We don't support multiple SR in a slot\n");
// we multiplex SR in the F2 or above resource
res[i].n_sr = res[i + 1].n_sr;
res[i].sr_payload = res[i + 1].sr_payload;
res[i + 1] = res[i];
res[i] = empty;
} else if (res[i + 1].n_harq > 0) {
AssertFatal(res[i].n_harq == 0, "The standard doesn't allow for more the 1 PUCCH in a slot with HARQ\n");
if (check_mux_acknack_csi(curr_resource, pucch_Config)) {
// we multiplex what in F0 to CSI resource
if (res[i].n_sr == 0) {
res[i].n_sr = res[i + 1].n_sr;
res[i].sr_payload = res[i + 1].sr_payload;
} else
AssertFatal(res[i + 1].n_sr == 0, "We don't support more than 1 SR in a slot\n");
res[i].n_harq = res[i + 1].n_harq;
res[i].ack_payload = res[i + 1].ack_payload;
res[i + 1] = res[i];
res[i] = empty;
} else {
// if we can't multiplex HARQ and CSI we discard CSI
if (res[i].n_sr == 0)
res[i] = empty;
else {
AssertFatal(res[i + 1].n_sr == 0, "We don't support more than 1 SR in a slot\n");
if (res[i].n_sr > 0)
LOG_E(MAC, "Not sure what to do here because you can't easily mux HARQ and SR in F1\n");
res[i] = empty;
}
}
}
break;
case NR_PUCCH_Resource__format_PR_format2:
case NR_PUCCH_Resource__format_PR_format4:
case NR_PUCCH_Resource__format_PR_format3:
if (res[i + 1].csi_payload.p1_bits > 0) {
AssertFatal(res[i].csi_payload.p1_bits == 0, "Multiplexing multiple CSI report in a single PUCCH not supported yet\n");
AssertFatal(res[i].n_harq > 0 && res[i + 1].n_harq == 0,
"There is CSI in next F2 or above resource, since there is no CSI in current one, we expect HARQ in "
"there and not in next\n");
// the UE expects to be provided a same configuration for simultaneousHARQ-ACK-CSI each of PUCCH formats 2, 3, and 4
// we can check next or current
if (check_mux_acknack_csi(next_resource, pucch_Config)) {
// We need to use HARQ resource
if (res[i + 1].n_sr > 0) {
AssertFatal(res[i].n_sr == 0, "We don't support more than 1 SR in a slot\n");
res[i].n_sr = res[i + 1].n_sr;
res[i].sr_payload = res[i + 1].sr_payload;
}
res[i].csi_payload.p1_bits = res[i + 1].csi_payload.p1_bits;
res[i].csi_payload.part1_payload = res[i + 1].csi_payload.part1_payload;
res[i + 1] = res[i];
res[i] = empty;
} else {
if (res[i].n_sr > 0) {
AssertFatal(res[i + 1].n_sr == 0, "We don't support more than 1 SR in a slot\n");
// if we can't multiplex HARQ and CSI we discard CSI
res[i + 1] = res[i];
res[i] = empty;
} else if (res[i + 1].n_sr == 0) {
// if we can't multiplex HARQ and CSI we discard CSI
res[i + 1] = res[i];
res[i] = empty;
} else {
// we move SR (assuming it was previously multiplexed with CSI) into HARQ resource and discard CSI
res[i].n_sr = res[i + 1].n_sr;
res[i].sr_payload = res[i + 1].sr_payload;
res[i + 1] = res[i];
res[i] = empty;
}
}
} else if (res[i].csi_payload.p1_bits > 0) {
AssertFatal(res[i + 1].csi_payload.p1_bits == 0, "Multiplexing multiple CSI report in a single PUCCH not supported yet\n");
AssertFatal(res[i + 1].n_harq > 0 && res[i].n_harq == 0,
"There is CSI in next F2 or above resource, since there is no CSI in current one, we expect HARQ in "
"there and not in next\n");
// the UE expects to be provided a same configuration for simultaneousHARQ-ACK-CSI each of PUCCH formats 2, 3, and 4
// we can check next or current
if (check_mux_acknack_csi(next_resource, pucch_Config)) {
// We need to use HARQ resource
if (res[i].n_sr > 0) {
AssertFatal(res[i + 1].n_sr == 0, "We don't support more than 1 SR in a slot\n");
res[i + 1].n_sr = res[i].n_sr;
res[i + 1].sr_payload = res[i].sr_payload;
}
res[i + 1].csi_payload.p1_bits = res[i].csi_payload.p1_bits;
res[i + 1].csi_payload.part1_payload = res[i].csi_payload.part1_payload;
res[i] = empty;
} else {
if (res[i + 1].n_sr > 0) {
AssertFatal(res[i].n_sr == 0, "We don't support more than 1 SR in a slot\n");
// if we can't multiplex HARQ and CSI we discard CSI
res[i] = empty;
} else if (res[i].n_sr == 0) {
// if we can't multiplex HARQ and CSI we discard CSI
res[i] = empty;
} else {
// we move SR (assuming it was previously multiplexed with CSI) into HARQ resource and discard CSI
res[i + 1].n_sr = res[i].n_sr;
res[i + 1].sr_payload = res[i].sr_payload;
res[i] = empty;
}
}
} else
AssertFatal(false, "We expect at least one of the 2 PUCCH F2 or above resources with CSI\n");
break;
default:
AssertFatal(false, "Invalid PUCCH format %d\n", next_resource->format.present);
}
break;
default:
AssertFatal(false, "Invalid PUCCH format %d\n", curr_resource->format.present);
}
}
}
void multiplex_pucch_resource(NR_UE_MAC_INST_t *mac, PUCCH_sched_t *pucch, int num_res)
{
NR_PUCCH_Config_t *pucch_Config = mac->current_UL_BWP->pucch_Config;
order_resources(pucch, num_res);
// following pseudocode in Ref. 38.213 section 9.2.5 to multiplex resources
int j = 0;
int o = 0;
while (j <= num_res - 1) {
if ((j < num_res - 1) && check_overlapping_resources(pucch, j, o)) {
o++;
j++;
} else {
if (o > 0) {
merge_resources(&pucch[j - o], o + 1, pucch_Config);
// move the resources to occupy the places left empty
int num_empty = o;
for (int i = j; i < num_res; i++)
pucch[i - num_empty] = pucch[i];
for (int e = num_res - num_empty; e < num_res; e++)
memset(&pucch[e], 0, sizeof(pucch[e]));
j = 0;
o = 0;
num_res = num_res - num_empty;
order_resources(pucch, num_res);
} else
j++;
}
}
}
void configure_initial_pucch(PUCCH_sched_t *pucch, int res_ind, long *pucch_ResourceCommon)
{
/* see TS 38.213 9.2.1 PUCCH Resource Sets */
int delta_PRI = res_ind;
int n_CCE_0 = pucch->n_CCE;
int N_CCE_0 = pucch->N_CCE;
if (N_CCE_0 == 0)
AssertFatal(1 == 0, "PUCCH No compatible pucch format found\n");
int r_PUCCH = ((2 * n_CCE_0) / N_CCE_0) + (2 * delta_PRI);
pucch->initial_pucch_id = r_PUCCH;
pucch->pucch_resource = NULL;
AssertFatal(pucch_ResourceCommon, "pucch_ResourceCommon NULL\n");
pucch->pucch_ResourceCommon = *pucch_ResourceCommon;
}
/*******************************************************************
*
* NAME : get_downlink_ack
*
* PARAMETERS : ue context
* processing slots of reception/transmission
* gNB_id identifier
*
* RETURN : o_ACK acknowledgment data
* o_ACK_number_bits number of bits for acknowledgment
*
* DESCRIPTION : return acknowledgment value
* TS 38.213 9.1.3 Type-2 HARQ-ACK codebook determination
*
* --+--------+-------+--------+-------+--- ---+-------+--
* | PDCCH1 | | PDCCH2 |PDCCH3 | | PUCCH |
* --+--------+-------+--------+-------+--- ---+-------+--
* DAI_DL 1 2 3 ACK for
* V V V PDCCH1, PDDCH2 and PCCH3
* | | | ^
* +-----------------+-------+---------------+
*
* PDCCH1, PDCCH2 and PDCCH3 are PDCCH monitoring occasions
* M is the total of monitoring occasions
*
*********************************************************************/
bool get_downlink_ack(NR_UE_MAC_INST_t *mac, frame_t frame, int slot, PUCCH_sched_t *pucch)
{
uint32_t ack_data[NR_DL_MAX_NB_CW][NR_MAX_HARQ_PROCESSES] = {{0},{0}};
uint32_t dai[NR_DL_MAX_NB_CW][NR_MAX_HARQ_PROCESSES] = {{0},{0}}; /* for serving cell */
uint32_t dai_total[NR_DL_MAX_NB_CW][NR_MAX_HARQ_PROCESSES] = {{0},{0}}; /* for multiple cells */
int number_harq_feedback = 0;
uint32_t dai_max = 0;
NR_UE_DL_BWP_t *current_DL_BWP = mac->current_DL_BWP;
NR_UE_UL_BWP_t *current_UL_BWP = mac->current_UL_BWP;
bool two_transport_blocks = false;
int number_of_code_word = 1;
if (current_DL_BWP
&& current_DL_BWP->pdsch_Config
&& current_DL_BWP->pdsch_Config->maxNrofCodeWordsScheduledByDCI
&& current_DL_BWP->pdsch_Config->maxNrofCodeWordsScheduledByDCI[0] == 2) {
two_transport_blocks = true;
number_of_code_word = 2;
}
const int num_dl_harq = get_nrofHARQ_ProcessesForPDSCH(&mac->sc_info);
int res_ind = -1;
/* look for dl acknowledgment which should be done on current uplink slot */
for (int code_word = 0; code_word < number_of_code_word; code_word++) {
for (int dl_harq_pid = 0; dl_harq_pid < num_dl_harq; dl_harq_pid++) {
NR_UE_DL_HARQ_STATUS_t *current_harq = &mac->dl_harq_info[dl_harq_pid];
if (current_harq->active) {
LOG_D(PHY, "HARQ pid %d is active for %d.%d\n",
dl_harq_pid, current_harq->ul_frame, current_harq->ul_slot);
/* check if current tx slot should transmit downlink acknowlegment */
if (current_harq->ul_frame == frame && current_harq->ul_slot == slot) {
if (res_ind != -1 && res_ind != current_harq->pucch_resource_indicator)
LOG_E(NR_MAC,
"Value of pucch_resource_indicator %d not matching with what set before %d (Possibly due to a false DCI) \n",
current_harq->pucch_resource_indicator,
res_ind);
else {
if (!current_harq->ack_received)
LOG_E(NR_MAC, "DLSCH ACK/NACK reporting initiated for harq pid %d before DLSCH decoding completed\n", dl_harq_pid);
if (get_FeedbackDisabled(mac->sc_info.downlinkHARQ_FeedbackDisabled_r17, dl_harq_pid)) {
LOG_W(NR_MAC, "skipping DLSCH ACK/NACK reporting for harq pid %d\n", dl_harq_pid);
current_harq->active = false;
current_harq->ack_received = false;
continue;
}
if (current_harq->dai_cumul == 0) {
LOG_E(NR_MAC,"PUCCH Downlink DAI is invalid\n");
return false;
} else if (current_harq->dai_cumul > dai_max) {
dai_max = current_harq->dai_cumul;
}
number_harq_feedback++;
int dai_index = current_harq->dai_cumul - 1;
if (current_harq->ack_received) {
ack_data[code_word][dai_index] = current_harq->ack;
current_harq->active = false;
current_harq->ack_received = false;
} else {
ack_data[code_word][dai_index] = 0;
}
dai[code_word][dai_index] = (dai_index % 4) + 1; // value between 1 and 4
int temp_ind = current_harq->pucch_resource_indicator;
AssertFatal(res_ind == -1 || res_ind == temp_ind,
"Current resource index %d does not match with previous resource index %d\n",
temp_ind,
res_ind);
res_ind = temp_ind;
pucch->n_CCE = current_harq->n_CCE;
pucch->N_CCE = current_harq->N_CCE;
LOG_D(NR_MAC,"%4d.%2d Sent %d ack on harq pid %d\n", frame, slot, current_harq->ack, dl_harq_pid);
}
}
}
}
}
/* no any ack to transmit */
if (number_harq_feedback == 0) {
return false;
}
else if (number_harq_feedback > (sizeof(uint32_t)*8)) {
LOG_E(MAC,"PUCCH number of ack bits exceeds payload size\n");
return false;
}
for (int code_word = 0; code_word < number_of_code_word; code_word++) {
for (uint32_t i = 0; i < dai_max ; i++ ) {
if (dai[code_word][i] == 0) {
dai[code_word][i] = (i % 4) + 1; // it covers case for which PDCCH DCI has not been successfully decoded and so it has been missed
ack_data[code_word][i] = 0; // nack data transport block which has been missed
number_harq_feedback++;
}
if (two_transport_blocks == true) {
dai_total[code_word][i] = dai[code_word][i]; /* for a single cell, dai_total is the same as dai of first cell */
}
}
}
int M = dai_max;
int j = 0;
uint32_t V_temp = 0;
uint32_t V_temp2 = 0;
int O_ACK = 0;
uint8_t o_ACK = 0;
int O_bit_number_cw0 = 0;
int O_bit_number_cw1 = 0;
for (int m = 0; m < M ; m++) {
if (dai[0][m] <= V_temp) {
j = j + 1;
}
V_temp = dai[0][m]; /* value of the counter DAI for format 1_0 and format 1_1 on serving cell c */
if (dai_total[0][m] == 0) {
V_temp2 = dai[0][m];
} else {
V_temp2 = dai[1][m]; /* second code word has been received */
O_bit_number_cw1 = (8 * j) + 2*(V_temp - 1) + 1;
o_ACK = o_ACK | (ack_data[1][m] << O_bit_number_cw1);
}
if (two_transport_blocks == true) {
O_bit_number_cw0 = (8 * j) + 2*(V_temp - 1);
}
else {
O_bit_number_cw0 = (4 * j) + (V_temp - 1);
}
o_ACK = o_ACK | (ack_data[0][m] << O_bit_number_cw0);
LOG_D(MAC,"m %d bit number %d o_ACK %d\n",m,O_bit_number_cw0,o_ACK);
}
if (V_temp2 < V_temp) {
j = j + 1;
}
if (two_transport_blocks == true) {
O_ACK = 2 * ( 4 * j + V_temp2); /* for two transport blocks */
}
else {
O_ACK = 4 * j + V_temp2; /* only one transport block */
}
if (number_harq_feedback != O_ACK) {
LOG_E(MAC,"PUCCH Error for number of bits for acknowledgment\n");
return false;
}
NR_PUCCH_Config_t *pucch_Config = current_UL_BWP ? current_UL_BWP->pucch_Config : NULL;
if (!(pucch_Config && pucch_Config->resourceSetToAddModList && pucch_Config->resourceSetToAddModList->list.array[0]))
configure_initial_pucch(pucch, res_ind, current_UL_BWP->pucch_ConfigCommon->pucch_ResourceCommon);
else {
int resource_set_id = find_pucch_resource_set(pucch_Config, O_ACK);
int n_list = pucch_Config->resourceSetToAddModList->list.count;
AssertFatal(resource_set_id < n_list, "Invalid PUCCH resource set id %d\n", resource_set_id);
n_list = pucch_Config->resourceSetToAddModList->list.array[resource_set_id]->resourceList.list.count;
AssertFatal(res_ind < n_list, "Invalid PUCCH resource id %d\n", res_ind);
long *acknack_resource_id =
pucch_Config->resourceSetToAddModList->list.array[resource_set_id]->resourceList.list.array[res_ind];
AssertFatal(acknack_resource_id != NULL, "Couldn't find PUCCH Resource ID in ResourceSet\n");
NR_PUCCH_Resource_t *acknack_resource = find_pucch_resource_from_list(pucch_Config->resourceToAddModList, *acknack_resource_id);
AssertFatal(acknack_resource != NULL, "Couldn't find PUCCH Resource ID for ACK/NACK in PUCCH resource list\n");
pucch->pucch_resource = acknack_resource;
LOG_D(MAC, "frame %d slot %d pucch acknack payload %d\n", frame, slot, o_ACK);
}
pucch->ack_payload = reverse_bits(o_ACK, number_harq_feedback);
pucch->n_harq = number_harq_feedback;
return (number_harq_feedback > 0);
}
/*! \fn int nr_ue_get_SR(NR_UE_MAC_INST_t *mac, frame_t frame, slot_t slot, NR_SchedulingRequestId_t sr_id);
\brief This function schedules a positive or negative SR for schedulingRequestID sr_id
depending on the presence of any active SR and the prohibit timer.
If the max number of retransmissions is reached, it triggers a new RA */
static int nr_ue_get_SR(NR_UE_MAC_INST_t *mac, frame_t frame, slot_t slot, NR_SchedulingRequestId_t sr_id)
{
// no UL-SCH resources available for this tti && UE has a valid PUCCH resources for SR configuration for this tti
NR_UE_SCHEDULING_INFO *si = &mac->scheduling_info;
nr_sr_info_t *sr_info = &si->sr_info[sr_id];
if (!sr_info->active_SR_ID) {
LOG_E(NR_MAC, "SR triggered for an inactive SR with ID %ld\n", sr_id);
return 0;
}
LOG_D(NR_MAC,
"[UE %d] Frame %d slot %d SR %s for ID %ld, timer %d\n",
mac->ue_id,
frame,
slot,
sr_info->pending ? "pending" : "not pending",
sr_id, // todo
nr_timer_is_active(&sr_info->prohibitTimer));
// TODO check if the PUCCH resource for the SR transmission occasion does not overlap with a UL-SCH resource
if (!sr_info->pending || nr_timer_is_active(&sr_info->prohibitTimer))
return 0;
if (sr_info->counter < sr_info->maxTransmissions) {
sr_info->counter++;
// start the sr-prohibittimer
nr_timer_start(&sr_info->prohibitTimer);
LOG_D(NR_MAC, "[UE %d] Frame %d slot %d instruct the physical layer to signal the SR (counter/sr_TransMax %d/%d)\n",
mac->ue_id,
frame,
slot,
sr_info->counter,
sr_info->maxTransmissions);
return 1;
}
LOG_W(NR_MAC,
"[UE %d] SR not served! SR counter %d reached sr_MaxTransmissions %d\n",
mac->ue_id,
sr_info->counter,
sr_info->maxTransmissions);
// initiate a Random Access procedure (see clause 5.1) on the SpCell and cancel all pending SRs.
sr_info->pending = false;
sr_info->counter = 0;
nr_timer_stop(&sr_info->prohibitTimer);
schedule_RA_after_SR_failure(mac);
return -1;
}
bool trigger_periodic_scheduling_request(NR_UE_MAC_INST_t *mac, PUCCH_sched_t *pucch, frame_t frame, int slot)
{
NR_UE_UL_BWP_t *current_UL_BWP = mac->current_UL_BWP;
NR_PUCCH_Config_t *pucch_Config = current_UL_BWP ? current_UL_BWP->pucch_Config : NULL;
if(!pucch_Config ||
!pucch_Config->schedulingRequestResourceToAddModList ||
pucch_Config->schedulingRequestResourceToAddModList->list.count == 0)
return false; // SR not configured
int sr_count = 0;
for (int id = 0; id < pucch_Config->schedulingRequestResourceToAddModList->list.count; id++) {
NR_SchedulingRequestResourceConfig_t *sr_Config = pucch_Config->schedulingRequestResourceToAddModList->list.array[id];
int SR_period; int SR_offset;
find_period_offset_SR(sr_Config, &SR_period, &SR_offset);
const int n_slots_frame = mac->frame_structure.numb_slots_frame;
int sfn_sf = frame * n_slots_frame + slot;
if ((sfn_sf - SR_offset) % SR_period == 0) {
LOG_D(MAC, "Scheduling Request active in frame %d slot %d \n", frame, slot);
if (!sr_Config->resource) {
LOG_E(MAC, "No resource associated with SR. SR not scheduled\n");
break;
}
NR_PUCCH_Resource_t *sr_pucch =
find_pucch_resource_from_list(pucch_Config->resourceToAddModList, *sr_Config->resource);
AssertFatal(sr_pucch != NULL, "Couldn't find PUCCH Resource ID for SR in PUCCH resource list\n");
pucch->pucch_resource = sr_pucch;
pucch->n_sr = 1;
/* sr_payload = 1 means that this is a positive SR, sr_payload = 0 means that it is a negative SR */
int ret = nr_ue_get_SR(mac, frame, slot, sr_Config->schedulingRequestID);
if (ret < 0) {
memset(pucch, 0, sizeof(*pucch));
return false;
}
pucch->sr_payload = ret;
sr_count++;
AssertFatal(sr_count < 2, "Cannot handle more than 1 SR per slot yet\n");
}
}
return sr_count > 0;
}
// section 5.2.5 of 38.214
int compute_csi_priority(NR_UE_MAC_INST_t *mac, NR_CSI_ReportConfig_t *csirep)
{
int y = 4 - csirep->reportConfigType.present;
int k = csirep->reportQuantity.present == NR_CSI_ReportConfig__reportQuantity_PR_cri_RSRP
|| csirep->reportQuantity.present == NR_CSI_ReportConfig__reportQuantity_PR_ssb_Index_RSRP
? 0
: 1;
int Ncells = 32; // maxNrofServingCells
int c = mac->servCellIndex;
int s = csirep->reportConfigId;
int Ms = 48; // maxNrofCSI-ReportConfigurations
return 2 * Ncells * Ms * y + Ncells * Ms * k + Ms * c + s;
}
int nr_get_csi_measurements(NR_UE_MAC_INST_t *mac,
frame_t frame,
int slot,
nfapi_nr_ue_csi_payload_t *csi_payload,
NR_PUCCH_Resource_t **csi_pucch,
bool csi_on_pusch)
{
NR_UE_UL_BWP_t *current_UL_BWP = mac->current_UL_BWP;
NR_PUCCH_Config_t *pucch_Config = current_UL_BWP ? current_UL_BWP->pucch_Config : NULL;
if (!mac->sc_info.csi_MeasConfig || !pucch_Config)
return 0;
int num_csi = 0;
NR_CSI_MeasConfig_t *csi_measconfig = mac->sc_info.csi_MeasConfig;
int csi_priority = INT_MAX;
for (int csi_report_id = 0; csi_report_id < csi_measconfig->csi_ReportConfigToAddModList->list.count; csi_report_id++) {
NR_CSI_ReportConfig_t *csirep = csi_measconfig->csi_ReportConfigToAddModList->list.array[csi_report_id];
AssertFatal(csirep->reportConfigType.present == NR_CSI_ReportConfig__reportConfigType_PR_aperiodic
|| csirep->reportConfigType.present == NR_CSI_ReportConfig__reportConfigType_PR_periodic,
"Not supported CSI report type\n");
// Aperiodic CSI measurements handled in nr_ue_aperiodic_csi_reporting function
if (csirep->reportConfigType.present != NR_CSI_ReportConfig__reportConfigType_PR_periodic)
continue;
int period, offset;
csi_period_offset(csirep, NULL, &period, &offset);
const int n_slots_frame = mac->frame_structure.numb_slots_frame;
// Check if current slot falls in the report period
if (((n_slots_frame * frame + slot - offset) % period) != 0)
continue;
int csi_res_id = -1;
for (int i = 0; i < csirep->reportConfigType.choice.periodic->pucch_CSI_ResourceList.list.count; i++) {
const NR_PUCCH_CSI_Resource_t *pucchcsires = csirep->reportConfigType.choice.periodic->pucch_CSI_ResourceList.list.array[i];
if (pucchcsires->uplinkBandwidthPartId == current_UL_BWP->bwp_id) {
csi_res_id = pucchcsires->pucch_Resource;
break;
}
}
if (csi_res_id < 0) {
// This CSI Report ID is not associated with current active BWP
continue;
}
*csi_pucch = find_pucch_resource_from_list(pucch_Config->resourceToAddModList, csi_res_id);
AssertFatal(*csi_pucch != NULL, "Couldn't find PUCCH Resource ID for CSI in PUCCH resource list\n");
LOG_D(NR_MAC, "Preparing CSI report in frame %d slot %d CSI report ID %d\n", frame, slot, csi_report_id);
int temp_priority = compute_csi_priority(mac, csirep);
if (num_csi > 0) {
// need to verify if we can multiplex multiple CSI report
if (pucch_Config->multi_CSI_PUCCH_ResourceList) {
AssertFatal(false, "Multiplexing multiple CSI report in a single PUCCH not supported yet\n");
} else if (temp_priority < csi_priority) {
// we discard previous report
csi_priority = temp_priority;
num_csi = 1;
*csi_payload = nr_get_csi_payload(mac, csi_report_id, csi_on_pusch ? ON_PUSCH : WIDEBAND_ON_PUCCH, csi_measconfig);
} else
continue;
} else {
num_csi = 1;
csi_priority = temp_priority;
*csi_payload = nr_get_csi_payload(mac, csi_report_id, csi_on_pusch ? ON_PUSCH : WIDEBAND_ON_PUCCH, csi_measconfig);
}
}
return num_csi;
}
// Referred - Table 10.1.16.1-1 in 38.133 V16.7.0.
static uint8_t get_sinr_index(float sinr)
{
int index = sinr * 2 + 47;
if (sinr >= 40)
index = 127;
if (sinr < -23)
index = 0;
return index;
}
// Reffered Table 10.1.16.1-2 in 38.133 V16.7.0. Differential value is reported.
static uint8_t get_sinr_diff_index(float best_sinr, float current_sinr)
{
int diff = best_sinr - current_sinr;
if (diff >= 15)
return 15;
else if (diff <= 0)
return 0;
else
return diff;
}
// Comparison function for sorting SSB SINR measurements in descending order
static int compare_ssb_sinr(const void *a, const void *b)
{
const NR_RSRP_meas_t *ma = (const NR_RSRP_meas_t *)a;
const NR_RSRP_meas_t *mb = (const NR_RSRP_meas_t *)b;
return mb->ssb_sinr_dB - ma->ssb_sinr_dB;
}
static nfapi_nr_ue_csi_payload_t get_ssb_sinr_payload(NR_UE_MAC_INST_t *mac,
const struct NR_CSI_ReportConfig *csi_reportconfig,
const NR_CSI_ResourceConfigId_t csi_ResourceConfigId,
const NR_CSI_MeasConfig_t *csi_MeasConfig)
{
int nb_ssb = 0; // nb of ssb in the resource
int nb_meas = 0; // nb of ssb to report measurements on
int bits = 0;
uint64_t temp_payload = 0;
for (int csi_resourceidx = 0; csi_resourceidx < csi_MeasConfig->csi_ResourceConfigToAddModList->list.count; csi_resourceidx++) {
struct NR_CSI_ResourceConfig *csi_resourceconfig = csi_MeasConfig->csi_ResourceConfigToAddModList->list.array[csi_resourceidx];
if (csi_resourceconfig->csi_ResourceConfigId == csi_ResourceConfigId) {
if (csi_reportconfig->groupBasedBeamReporting.present == NR_CSI_ReportConfig__groupBasedBeamReporting_PR_disabled) {
if (csi_reportconfig->groupBasedBeamReporting.choice.disabled->nrofReportedRS != NULL)
nb_meas = *(csi_reportconfig->groupBasedBeamReporting.choice.disabled->nrofReportedRS) + 1;
else
nb_meas = 1;
} else
nb_meas = 2;
const struct NR_CSI_SSB_ResourceSet__csi_SSB_ResourceList *SSB_resource = NULL;
for (int csi_ssb_idx = 0; csi_ssb_idx < csi_MeasConfig->csi_SSB_ResourceSetToAddModList->list.count; csi_ssb_idx++) {
if (csi_MeasConfig->csi_SSB_ResourceSetToAddModList->list.array[csi_ssb_idx]->csi_SSB_ResourceSetId
== *(csi_resourceconfig->csi_RS_ResourceSetList.choice.nzp_CSI_RS_SSB->csi_SSB_ResourceSetList->list.array[0])) {
SSB_resource = &csi_MeasConfig->csi_SSB_ResourceSetToAddModList->list.array[csi_ssb_idx]->csi_SSB_ResourceList;
/// only one SSB resource set from spec 38.331 IE CSI-ResourceConfig
nb_ssb = SSB_resource->list.count;
break;
}
}
AssertFatal(nb_ssb > 0, "No SSB found in the resource set\n");
AssertFatal(nb_meas <= 4,"Can't report more than 4 RSRPs\n");
int ssbri_bits = ceil(log2(nb_ssb));
// map SSB index to SSB resource table index, copy measurements, sort in descending order
NR_RSRP_meas_t sorted_sinr_measurements[nb_ssb];
int sorted_idx = 0;
for (int measured_ssb_idx = 0; measured_ssb_idx < MAX_NB_SSB; measured_ssb_idx++) {
// searching for the SSB index in the SSB resource table
for (int ssb_resource = 0; ssb_resource < nb_ssb; ssb_resource++) {
if (*SSB_resource->list.array[ssb_resource] == measured_ssb_idx) {
sorted_sinr_measurements[sorted_idx].ssb_index = ssb_resource;
sorted_sinr_measurements[sorted_idx].ssb_rsrp_dBm = mac->ssb_measurements[measured_ssb_idx].ssb_rsrp_dBm;
sorted_sinr_measurements[sorted_idx].ssb_sinr_dB = mac->ssb_measurements[measured_ssb_idx].ssb_sinr_dB;
sorted_idx++;
break;
}
}
}
qsort(sorted_sinr_measurements, nb_ssb, sizeof(NR_RSRP_meas_t), compare_ssb_sinr);
// TS38.212 v16.5.0: Table 6.3.1.1.2-8A
for (int i = 0; i < nb_meas; i++) {
if (ssbri_bits > 0) {
uint8_t ssbi = sorted_sinr_measurements[i].ssb_index;
temp_payload |= (reverse_bits(ssbi, ssbri_bits) << bits);
bits += ssbri_bits;
}
}
uint8_t sinr_idx = get_sinr_index(sorted_sinr_measurements[0].ssb_sinr_dB);
temp_payload |= (reverse_bits(sinr_idx, 7) << bits);
bits += 7; // 7 bits for highest SINR
for (int i = 1; i < nb_meas; i++) {
sinr_idx = get_sinr_diff_index(sorted_sinr_measurements[0].ssb_sinr_dB, sorted_sinr_measurements[i].ssb_sinr_dB);
temp_payload |= (reverse_bits(sinr_idx, 4) << bits);
bits += 4; // 4 bits for differential SINR
}
break; // resource found
}
}
int max_bits = sizeof(((nfapi_nr_ue_csi_payload_t *)0)->part1_payload) * 8;
AssertFatal(bits <= max_bits, "Not supporting CSI report with more than %d bits (payload: %d bits)\n", max_bits, bits);
nfapi_nr_ue_csi_payload_t csi = {.part1_payload = temp_payload, .part2_payload = 0, .p1_bits = bits, csi.p2_bits = 0};
return csi;
}
// Comparison function for sorting SSB RSRP measurements in descending order
static int compare_ssb_rsrp(const void *a, const void *b)
{
const NR_RSRP_meas_t *ma = (const NR_RSRP_meas_t *)a;
const NR_RSRP_meas_t *mb = (const NR_RSRP_meas_t *)b;
return mb->ssb_rsrp_dBm - ma->ssb_rsrp_dBm;
}
// returns index from differential RSRP
// according to Table 10.1.6.1-2 in 38.133
static uint8_t get_rsrp_diff_index(int best_rsrp, int current_rsrp)
{
int diff = best_rsrp-current_rsrp;
if (diff>30)
return 15;
else
return (diff>>1);
}
static nfapi_nr_ue_csi_payload_t get_ssb_rsrp_payload(NR_UE_MAC_INST_t *mac,
const struct NR_CSI_ReportConfig *csi_reportconfig,
const NR_CSI_ResourceConfigId_t csi_ResourceConfigId,
const NR_CSI_MeasConfig_t *csi_MeasConfig)
{
int nb_ssb = 0; // nb of ssb in the resource
int nb_meas = 0; // nb of ssb to report measurements on
int bits = 0;
uint64_t temp_payload = 0;
for (int csi_resourceidx = 0; csi_resourceidx < csi_MeasConfig->csi_ResourceConfigToAddModList->list.count; csi_resourceidx++) {
struct NR_CSI_ResourceConfig *csi_resourceconfig = csi_MeasConfig->csi_ResourceConfigToAddModList->list.array[csi_resourceidx];
if (csi_resourceconfig->csi_ResourceConfigId == csi_ResourceConfigId) {
if (csi_reportconfig->groupBasedBeamReporting.present == NR_CSI_ReportConfig__groupBasedBeamReporting_PR_disabled) {
if (csi_reportconfig->groupBasedBeamReporting.choice.disabled->nrofReportedRS != NULL)
nb_meas = *(csi_reportconfig->groupBasedBeamReporting.choice.disabled->nrofReportedRS) + 1;
else
nb_meas = 1;
} else
nb_meas = 2;
const struct NR_CSI_SSB_ResourceSet__csi_SSB_ResourceList *SSB_resource = NULL;
for (int csi_ssb_idx = 0; csi_ssb_idx < csi_MeasConfig->csi_SSB_ResourceSetToAddModList->list.count; csi_ssb_idx++) {
if (csi_MeasConfig->csi_SSB_ResourceSetToAddModList->list.array[csi_ssb_idx]->csi_SSB_ResourceSetId ==
*(csi_resourceconfig->csi_RS_ResourceSetList.choice.nzp_CSI_RS_SSB->csi_SSB_ResourceSetList->list.array[0])){
SSB_resource = &csi_MeasConfig->csi_SSB_ResourceSetToAddModList->list.array[csi_ssb_idx]->csi_SSB_ResourceList;
///only one SSB resource set from spec 38.331 IE CSI-ResourceConfig
nb_ssb = SSB_resource->list.count;
break;
}
}
AssertFatal(nb_ssb > 0,"No SSB found in the resource set\n");
AssertFatal(nb_meas <= 4,"Can't report more than 4 RSRPs\n");
int ssbri_bits = ceil(log2(nb_ssb));
// map SSB index to SSB resource table index, copy measurements, sort in descending order
NR_RSRP_meas_t sorted_rsrp_measurements[nb_ssb];
int sorted_idx = 0;
for (int measured_ssb_idx = 0; measured_ssb_idx < MAX_NB_SSB; measured_ssb_idx++) {
// searching for the SSB index in the SSB resource table
for (int ssb_resource = 0; ssb_resource < nb_ssb; ssb_resource++) {
if (*SSB_resource->list.array[ssb_resource] == measured_ssb_idx) {
sorted_rsrp_measurements[sorted_idx].ssb_index = ssb_resource;
sorted_rsrp_measurements[sorted_idx].ssb_rsrp_dBm = mac->ssb_measurements[measured_ssb_idx].ssb_rsrp_dBm;
sorted_rsrp_measurements[sorted_idx].ssb_sinr_dB = mac->ssb_measurements[measured_ssb_idx].ssb_sinr_dB;
sorted_idx++;
break;
}
}
}
qsort(sorted_rsrp_measurements, nb_ssb, sizeof(NR_RSRP_meas_t), compare_ssb_rsrp);
for (int i = 0; i < nb_meas; i++) {
if (ssbri_bits > 0) {
uint32_t ssbi = sorted_rsrp_measurements[i].ssb_index;
temp_payload |= (reverse_bits(ssbi, ssbri_bits) << bits);
bits += ssbri_bits;
}
}
uint8_t rsrp_idx = get_rsrp_index(sorted_rsrp_measurements[0].ssb_rsrp_dBm);
temp_payload |= (reverse_bits(rsrp_idx, 7) << bits);
bits += 7; // 7 bits for highest RSRP
// from the second SSB, differential report
for (int i = 1; i < nb_meas; i++) {
rsrp_idx = get_rsrp_diff_index(sorted_rsrp_measurements[0].ssb_rsrp_dBm,sorted_rsrp_measurements[i].ssb_rsrp_dBm);
temp_payload |= (reverse_bits(rsrp_idx, 4) << bits);
bits += 4; // 4 bits for subsequent RSRP
}
break; // resource found
}
}
int max_bits = sizeof(((nfapi_nr_ue_csi_payload_t *)0)->part1_payload) * 8;
AssertFatal(bits <= max_bits, "Not supporting CSI report with more than %d bits (payload: %d bits)\n", max_bits, bits);
nfapi_nr_ue_csi_payload_t csi = {.part1_payload = temp_payload, .part2_payload = 0, .p1_bits = bits, .p2_bits = 0};
return csi;
}
static nfapi_nr_ue_csi_payload_t get_csirs_RI_PMI_CQI_payload(NR_UE_MAC_INST_t *mac,
const struct NR_CSI_ReportConfig *csi_reportconfig,
const NR_CSI_ResourceConfigId_t csi_ResourceConfigId,
const NR_CSI_MeasConfig_t *csi_MeasConfig,
const CSI_mapping_t mapping_type)
{
int p1_bits = 0;
int p2_bits = 0;
uint64_t temp_payload_1 = 0;
uint64_t temp_payload_2 = 0;
AssertFatal(mapping_type != SUBBAND_ON_PUCCH, "CSI mapping for subband PMI and CQI not implemented\n");
for (int csi_resourceidx = 0; csi_resourceidx < csi_MeasConfig->csi_ResourceConfigToAddModList->list.count; csi_resourceidx++) {
struct NR_CSI_ResourceConfig *csi_resourceconfig = csi_MeasConfig->csi_ResourceConfigToAddModList->list.array[csi_resourceidx];
if (csi_resourceconfig->csi_ResourceConfigId == csi_ResourceConfigId) {
for (int csi_idx = 0; csi_idx < csi_MeasConfig->nzp_CSI_RS_ResourceSetToAddModList->list.count; csi_idx++) {
if (csi_MeasConfig->nzp_CSI_RS_ResourceSetToAddModList->list.array[csi_idx]->nzp_CSI_ResourceSetId ==
*(csi_resourceconfig->csi_RS_ResourceSetList.choice.nzp_CSI_RS_SSB->nzp_CSI_RS_ResourceSetList->list.array[0])) {
nr_csi_report_t *csi_report = NULL;
for (int i = 0; i < MAX_CSI_REPORTCONFIG; i++) {
if (mac->csi_report_template[i].reportConfigId == csi_reportconfig->reportConfigId) {
csi_report = &mac->csi_report_template[i];
break;
}
}
AssertFatal(csi_report, "Couldn't find CSI report with ID %ld\n", csi_reportconfig->reportConfigId);
int cri_bitlen = csi_report->csi_meas_bitlen.cri_bitlen;
int ri_bitlen = csi_report->csi_meas_bitlen.ri_bitlen;
int pmi_x1_bitlen = csi_report->csi_meas_bitlen.pmi_x1_bitlen[mac->csirs_measurements.ri];
int pmi_x2_bitlen = csi_report->csi_meas_bitlen.pmi_x2_bitlen[mac->csirs_measurements.ri];
int cqi_bitlen = csi_report->csi_meas_bitlen.cqi_bitlen[mac->csirs_measurements.ri];
int padding_bitlen = 0;
// TODO: Improvements will be needed to cri_bitlen>0 and pmi_x1_bitlen>0
if (mapping_type == ON_PUSCH) {
p1_bits = cri_bitlen + ri_bitlen + cqi_bitlen;
p2_bits = pmi_x1_bitlen + pmi_x2_bitlen;
temp_payload_1 = (0/*mac->csi_measurements.cri*/ << (cqi_bitlen + ri_bitlen)) |
(mac->csirs_measurements.ri << cqi_bitlen) |
(mac->csirs_measurements.cqi);
temp_payload_2 = (mac->csirs_measurements.i1 << pmi_x2_bitlen) |
mac->csirs_measurements.i2;
}
else {
p1_bits = nr_get_csi_bitlen(csi_report);
padding_bitlen = p1_bits - (cri_bitlen + ri_bitlen + pmi_x1_bitlen + pmi_x2_bitlen + cqi_bitlen);
temp_payload_1 = (0/*mac->csi_measurements.cri*/ << (cqi_bitlen + pmi_x2_bitlen + pmi_x1_bitlen + padding_bitlen + ri_bitlen)) |
(mac->csirs_measurements.ri << (cqi_bitlen + pmi_x2_bitlen + pmi_x1_bitlen + padding_bitlen)) |
(mac->csirs_measurements.i1 << (cqi_bitlen + pmi_x2_bitlen)) |
(mac->csirs_measurements.i2 << (cqi_bitlen)) |
(mac->csirs_measurements.cqi);
}
temp_payload_1 = reverse_bits(temp_payload_1, p1_bits);
temp_payload_2 = reverse_bits(temp_payload_2, p2_bits);
LOG_D(NR_MAC, "cri_bitlen = %d\n", cri_bitlen);
LOG_D(NR_MAC, "ri_bitlen = %d\n", ri_bitlen);
LOG_D(NR_MAC, "pmi_x1_bitlen = %d\n", pmi_x1_bitlen);
LOG_D(NR_MAC, "pmi_x2_bitlen = %d\n", pmi_x2_bitlen);
LOG_D(NR_MAC, "cqi_bitlen = %d\n", cqi_bitlen);
LOG_D(NR_MAC, "csi_part1_payload = 0x%lx\n", temp_payload_1);
LOG_D(NR_MAC, "csi_part2_payload = 0x%lx\n", temp_payload_2);
LOG_D(NR_MAC, "part1_bits = %d\n", p1_bits);
LOG_D(NR_MAC, "part2_bits = %d\n", p2_bits);
break;
}
}
}
}
AssertFatal(p1_bits <= 32 && p2_bits <= 32, "Not supporting CSI report with more than 32 bits\n");
nfapi_nr_ue_csi_payload_t csi = {.part1_payload = temp_payload_1, .part2_payload = temp_payload_2, .p1_bits = p1_bits, csi.p2_bits = p2_bits};
return csi;
}
static nfapi_nr_ue_csi_payload_t get_csirs_RSRP_payload(NR_UE_MAC_INST_t *mac,
const struct NR_CSI_ReportConfig *csi_reportconfig,
const NR_CSI_ResourceConfigId_t csi_ResourceConfigId,
const NR_CSI_MeasConfig_t *csi_MeasConfig)
{
int n_bits = 0;
uint64_t temp_payload = 0;
for (int csi_resourceidx = 0; csi_resourceidx < csi_MeasConfig->csi_ResourceConfigToAddModList->list.count; csi_resourceidx++) {
struct NR_CSI_ResourceConfig *csi_resourceconfig = csi_MeasConfig->csi_ResourceConfigToAddModList->list.array[csi_resourceidx];
if (csi_resourceconfig->csi_ResourceConfigId == csi_ResourceConfigId) {
for (int csi_idx = 0; csi_idx < csi_MeasConfig->nzp_CSI_RS_ResourceSetToAddModList->list.count; csi_idx++) {
if (csi_MeasConfig->nzp_CSI_RS_ResourceSetToAddModList->list.array[csi_idx]->nzp_CSI_ResourceSetId ==
*(csi_resourceconfig->csi_RS_ResourceSetList.choice.nzp_CSI_RS_SSB->nzp_CSI_RS_ResourceSetList->list.array[0])) {
nr_csi_report_t *csi_report = NULL;
for (int i = 0; i < MAX_CSI_REPORTCONFIG; i++) {
if (mac->csi_report_template[i].reportConfigId == csi_reportconfig->reportConfigId) {
csi_report = &mac->csi_report_template[i];
break;
}
}
AssertFatal(csi_report, "Couldn't find CSI report with ID %ld\n", csi_reportconfig->reportConfigId);
n_bits = nr_get_csi_bitlen(csi_report);
int cri_ssbri_bitlen = csi_report->CSI_report_bitlen.cri_ssbri_bitlen;
int rsrp_bitlen = csi_report->CSI_report_bitlen.rsrp_bitlen;
int diff_rsrp_bitlen = csi_report->CSI_report_bitlen.diff_rsrp_bitlen;
if (cri_ssbri_bitlen > 0) {
LOG_E(NR_MAC, "Implementation for cri_ssbri_bitlen>0 is not supported yet!\n");;
}
// TODO: Improvements will be needed to cri_ssbri_bitlen>0
temp_payload = get_rsrp_index(mac->csirs_measurements.rsrp_dBm);
temp_payload = reverse_bits(temp_payload, n_bits);
LOG_D(NR_MAC, "cri_ssbri_bitlen = %d\n", cri_ssbri_bitlen);
LOG_D(NR_MAC, "rsrp_bitlen = %d\n", rsrp_bitlen);
LOG_D(NR_MAC, "diff_rsrp_bitlen = %d\n", diff_rsrp_bitlen);
LOG_D(NR_MAC, "n_bits = %d\n", n_bits);
LOG_D(NR_MAC, "csi_part1_payload = 0x%lx\n", temp_payload);
break;
}
}
}
}
AssertFatal(n_bits <= 32, "Not supporting CSI report with more than 32 bits\n");
nfapi_nr_ue_csi_payload_t csi = {.part1_payload = temp_payload, .p1_bits = n_bits, csi.p2_bits = 0};
return csi;
}
nfapi_nr_ue_csi_payload_t nr_get_csi_payload(NR_UE_MAC_INST_t *mac,
int csi_report_id,
CSI_mapping_t mapping_type,
const NR_CSI_MeasConfig_t *csi_MeasConfig)
{
AssertFatal(csi_MeasConfig->csi_ReportConfigToAddModList->list.count > 0,"No CSI Report configuration available\n");
nfapi_nr_ue_csi_payload_t csi = {0};
struct NR_CSI_ReportConfig *csi_reportconfig = csi_MeasConfig->csi_ReportConfigToAddModList->list.array[csi_report_id];
NR_CSI_ResourceConfigId_t csi_ResourceConfigId = csi_reportconfig->resourcesForChannelMeasurement;
if (csi_reportconfig->ext2 && csi_reportconfig->ext2->reportQuantity_r16) {
switch (csi_reportconfig->ext2->reportQuantity_r16->present) {
case NR_CSI_ReportConfig__ext2__reportQuantity_r16_PR_ssb_Index_SINR_r16:
csi = get_ssb_sinr_payload(mac, csi_reportconfig, csi_ResourceConfigId, csi_MeasConfig);
break;
case NR_CSI_ReportConfig__ext2__reportQuantity_r16_PR_cri_SINR_r16:
LOG_E(NR_MAC, "CSI Reporting of CSI-RS based SINR not yet available\n");
break;
default:
AssertFatal(1 == 0, "Invalid CSI report quantity r16 type %d\n", csi_reportconfig->ext2->reportQuantity_r16->present);
}
} else {
switch (csi_reportconfig->reportQuantity.present) {
case NR_CSI_ReportConfig__reportQuantity_PR_none:
break;
case NR_CSI_ReportConfig__reportQuantity_PR_ssb_Index_RSRP:
csi = get_ssb_rsrp_payload(mac, csi_reportconfig, csi_ResourceConfigId, csi_MeasConfig);
break;
case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_PMI_CQI:
csi = get_csirs_RI_PMI_CQI_payload(mac, csi_reportconfig, csi_ResourceConfigId, csi_MeasConfig, mapping_type);
break;
case NR_CSI_ReportConfig__reportQuantity_PR_cri_RSRP:
csi = get_csirs_RSRP_payload(mac, csi_reportconfig, csi_ResourceConfigId, csi_MeasConfig);
break;
case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_i1:
case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_i1_CQI:
case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_CQI:
case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_LI_PMI_CQI:
LOG_E(NR_MAC, "Measurement report %d based on CSI-RS is not available\n", csi_reportconfig->reportQuantity.present);
break;
default:
AssertFatal(1 == 0, "Invalid CSI report quantity type %d\n", csi_reportconfig->reportQuantity.present);
}
}
return csi;
}
static void set_time_alignment(NR_UE_MAC_INST_t *mac, int ta, ta_type_t type, int frame, int slot)
{
NR_UL_TIME_ALIGNMENT_t *ul_time_alignment = &mac->ul_time_alignment;
ul_time_alignment->ta_command = ta;
ul_time_alignment->ta_apply = type;
const int ntn_ue_koffset = GET_NTN_UE_K_OFFSET(&mac->phy_config.config_req.ntn_config, mac->current_UL_BWP->scs);
const int n_slots_frame = mac->frame_structure.numb_slots_frame;
ul_time_alignment->frame = (frame + (slot + ntn_ue_koffset) / n_slots_frame) % MAX_FRAME_NUMBER;
ul_time_alignment->slot = (slot + ntn_ue_koffset) % n_slots_frame;
// start or restart the timeAlignmentTimer associated with the indicated TAG
nr_timer_start(&mac->time_alignment_timer);
}
static void handle_rar_reception(NR_UE_MAC_INST_t *mac, NR_MAC_RAR *rar, frame_t frame, int slot)
{
RAR_grant_t rar_grant;
RA_config_t *ra = &mac->ra;
#ifdef DEBUG_RAR
// CSI
unsigned char csi_req = (unsigned char)(rar->UL_GRANT_4 & 0x01);
#endif
// TPC
unsigned char tpc_command = (unsigned char)((rar->UL_GRANT_4 >> 1) & 0x07);
ra->Msg3_TPC = (tpc_command << 1) - 6;
// MCS
rar_grant.mcs = (unsigned char)(rar->UL_GRANT_4 >> 4);
// time alloc
rar_grant.Msg3_t_alloc = (unsigned char)(rar->UL_GRANT_3 & 0x0f);
// frequency alloc
rar_grant.Msg3_f_alloc = (uint16_t)((rar->UL_GRANT_3 >> 4) | (rar->UL_GRANT_2 << 4) | ((rar->UL_GRANT_1 & 0x03) << 12));
// frequency hopping
rar_grant.freq_hopping = (unsigned char)(rar->UL_GRANT_1 >> 2);
// Schedule Msg3
const NR_UE_UL_BWP_t *current_UL_BWP = mac->current_UL_BWP;
const NR_UE_DL_BWP_t *current_DL_BWP = mac->current_DL_BWP;
const NR_BWP_PDCCH_t *pdcch_config = &mac->config_BWP_PDCCH[current_DL_BWP->bwp_id];
const NR_SearchSpace_t *ra_SS = get_common_search_space(mac, pdcch_config->ra_SS_id);
NR_tda_info_t tda_info = get_ul_tda_info(current_UL_BWP,
*ra_SS->controlResourceSetId,
ra_SS->searchSpaceType->present,
TYPE_RA_RNTI_,
rar_grant.Msg3_t_alloc);
if (!tda_info.valid_tda || tda_info.nrOfSymbols == 0) {
LOG_E(MAC, "Cannot schedule Msg3. Something wrong in TDA information\n");
// resume RAR response window timer if MSG2 decoding failed
nr_timer_suspension(&mac->ra.response_window_timer);
return;
}
frame_t frame_tx = 0;
int slot_tx = 0;
const int ntn_ue_koffset = GET_NTN_UE_K_OFFSET(&mac->phy_config.config_req.ntn_config, mac->current_UL_BWP->scs);
int ret = nr_ue_pusch_scheduler(mac, 1, frame, slot, &frame_tx, &slot_tx, tda_info.k2 + ntn_ue_koffset);
// TA command
// if the timeAlignmentTimer associated with this TAG is not running
if (!nr_timer_is_active(&mac->time_alignment_timer)) {
const int ta = rar->TA2 + (rar->TA1 << 5);
set_time_alignment(mac, ta, rar_ta, frame_tx, slot_tx);
LOG_W(MAC, "received TA command %d\n", 31 + ta);
}
// else ignore the received Timing Advance Command
#ifdef DEBUG_RAR
LOG_I(NR_MAC, "rarh->E = 0x%x\n", rarh->E);
LOG_I(NR_MAC, "rarh->T = 0x%x\n", rarh->T);
LOG_I(NR_MAC, "rarh->RAPID = 0x%x (%i)\n", rarh->RAPID, rarh->RAPID);
LOG_I(NR_MAC, "rar->R = 0x%x\n", rar->R);
LOG_I(NR_MAC, "rar->TA1 = 0x%x\n", rar->TA1);
LOG_I(NR_MAC, "rar->TA2 = 0x%x\n", rar->TA2);
LOG_I(NR_MAC, "rar->UL_GRANT_1 = 0x%x\n", rar->UL_GRANT_1);
LOG_I(NR_MAC, "rar->UL_GRANT_2 = 0x%x\n", rar->UL_GRANT_2);
LOG_I(NR_MAC, "rar->UL_GRANT_3 = 0x%x\n", rar->UL_GRANT_3);
LOG_I(NR_MAC, "rar->UL_GRANT_4 = 0x%x\n", rar->UL_GRANT_4);
LOG_I(NR_MAC, "rar->TCRNTI_1 = 0x%x\n", rar->TCRNTI_1);
LOG_I(NR_MAC, "rar->TCRNTI_2 = 0x%x\n", rar->TCRNTI_2);
LOG_I(NR_MAC,
"[%d.%d]: [UE %d] Received RAR with t_alloc %d f_alloc %d ta_command %d mcs %d freq_hopping %d tpc_command %d\n",
frame,
slot,
mac->ue_id,
rar_grant.Msg3_t_alloc,
rar_grant.Msg3_f_alloc,
ta_command,
rar_grant.mcs,
rar_grant.freq_hopping,
tpc_command);
#endif
if (ret != -1) {
uint16_t rnti = mac->crnti;
// Upon successful reception, set the T-CRNTI to the RAR value
// if the RA preamble is selected among the contention-based RA Preambles
if (!ra->cfra) {
ra->t_crnti = rar->TCRNTI_2 + (rar->TCRNTI_1 << 8);
rnti = ra->t_crnti;
if (!mac->msg3_C_RNTI)
nr_mac_rrc_msg3_ind(mac->ue_id, rnti, false);
}
fapi_nr_ul_config_request_pdu_t *pdu = lockGet_ul_config(mac, frame_tx, slot_tx, FAPI_NR_UL_CONFIG_TYPE_PUSCH);
if (!pdu)
return;
// Config Msg3 PDU
int ret = nr_config_pusch_pdu(mac,
&tda_info,
&pdu->pusch_config_pdu,
NULL,
NULL,
&rar_grant,
rnti,
NR_SearchSpace__searchSpaceType_PR_common,
NR_DCI_NONE);
if (ret != 0)
remove_ul_config_last_item(pdu);
release_ul_config(pdu, false);
}
}
/////////////////////////////////////
// Random Access Response PDU //
// TS 38.213 ch 8.2 //
// TS 38.321 ch 6.2.3 //
/////////////////////////////////////
//| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |// bit-wise
//| E | T | R A P I D |//
//| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |//
//| R | T A |//
//| T A | UL grant |//
//| UL grant |//
//| UL grant |//
//| UL grant |//
//| T C - R N T I |//
//| T C - R N T I |//
/////////////////////////////////////
// UL grant (27 bits) //
/////////////////////////////////////
//| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |// bit-wise
//|-------------------|FHF|F_alloc|//
//| Freq allocation |//
//| F_alloc |Time allocation|//
//| MCS | TPC |CSI|//
/////////////////////////////////////
// TbD WIP Msg3 development ongoing
// - apply UL grant freq alloc & time alloc as per 8.2 TS 38.213
// - apply tpc command
// WIP fix:
// - time domain indication hardcoded to 0 for k2 offset
// - extend TS 38.213 ch 8.3 Msg3 PUSCH
// - b buffer
// - ulsch power offset
// - optimize: mu_pusch, j and table_6_1_2_1_1_2_time_dom_res_alloc_A are already defined in nr_ue_procedures
static void nr_ue_process_rar(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *dl_info, int pdu_id)
{
frame_t frame = dl_info->frame;
int slot = dl_info->slot;
if(dl_info->rx_ind->rx_indication_body[pdu_id].pdsch_pdu.ack_nack == 0) {
LOG_W(NR_MAC,"[UE %d][RAPROC][%d.%d] CRC check failed on RAR (NAK)\n", mac->ue_id, frame, slot);
return;
}
RA_config_t *ra = &mac->ra;
ra->t_crnti = 0;
uint8_t n_subPDUs = 0; // number of RAR payloads
uint8_t n_subheaders = 0; // number of MAC RAR subheaders
uint8_t *dlsch_buffer = dl_info->rx_ind->rx_indication_body[pdu_id].pdsch_pdu.pdu;
NR_RA_HEADER_RAPID *rarh = (NR_RA_HEADER_RAPID *) dlsch_buffer; // RAR subheader pointer
NR_MAC_RAR *rar = (NR_MAC_RAR *) (dlsch_buffer + 1); // RAR subPDU pointer
uint8_t preamble_index = ra->ra_PreambleIndex;
uint16_t rnti = mac->ra.ra_rnti;
T(T_NRUE_MAC_DL_RAR_PDU_WITH_DATA, T_INT(rnti), T_INT(frame), T_INT(slot),
T_BUFFER(dlsch_buffer, dl_info->rx_ind->rx_indication_body[pdu_id].pdsch_pdu.pdu_length));
ra->RA_backoff_limit = 0;
LOG_D(NR_MAC, "[%d.%d]: [UE %d][RAPROC] MAC received RAR (current preamble %d)\n", frame, slot, mac->ue_id, preamble_index);
while (1) {
n_subheaders++;
if (rarh->T == 1) {
n_subPDUs++;
LOG_I(NR_MAC, "[UE %d][RAPROC][RA-RNTI %04x] Got RAPID RAR subPDU\n", mac->ue_id, rnti);
} else {
int bi_ms = table_7_2_1[((NR_RA_HEADER_BI *)rarh)->BI] * ra->scaling_factor_bi;
int slots_per_ms = mac->frame_structure.numb_slots_frame / 10;
ra->RA_backoff_limit = bi_ms * slots_per_ms;
LOG_I(NR_MAC, "[UE %d][RAPROC][RA-RNTI %04x] Got BI RAR subPDU %d ms\n", mac->ue_id, rnti, bi_ms);
if (((NR_RA_HEADER_BI *)rarh)->E == 1) {
rarh += sizeof(NR_RA_HEADER_BI);
continue;
} else {
break;
}
}
if (rarh->RAPID == preamble_index) {
// The MAC entity may stop ra-ResponseWindow (and hence monitoring for Random Access Response(s)) after
// successful reception of a Random Access Response containing Random Access Preamble identifiers
// that matches the transmitted PREAMBLE_INDEX.
nr_timer_stop(&ra->response_window_timer);
LOG_A(NR_MAC, "[UE %d][RAPROC][%d.%d] Found RAR with the intended RAPID %d\n", mac->ue_id, frame, slot, rarh->RAPID);
rar = (NR_MAC_RAR *) (dlsch_buffer + n_subheaders + (n_subPDUs - 1) * sizeof(NR_MAC_RAR));
handle_rar_reception(mac, rar, frame, slot);
if (ra->cfra)
nr_ra_succeeded(mac, frame, slot);
break;
}
if (rarh->E == 0) {
LOG_W(NR_MAC,"[UE %d][RAPROC][%d.%d] Received RAR preamble (%d) doesn't match the intended RAPID (%d)\n",
mac->ue_id,
frame,
slot,
rarh->RAPID,
preamble_index);
// resume RAR response window timer if MSG2 decoding failed
nr_timer_suspension(&mac->ra.response_window_timer);
break;
} else {
rarh += sizeof(NR_MAC_RAR) + 1;
}
}
#ifdef DEBUG_RAR
LOG_D(NR_MAC,
"[DEBUG_RAR] (%d,%d) number of RAR subheader %d; number of RAR pyloads %d\n",
frame,
slot,
n_subheaders,
n_subPDUs);
LOG_D(NR_MAC,
"[DEBUG_RAR] Received RAR (%02x|%02x.%02x.%02x.%02x.%02x.%02x) for preamble %d/%d\n",
*(uint8_t *) rarh,
rar[0],
rar[1],
rar[2],
rar[3],
rar[4],
rar[5],
rarh->RAPID,
preamble_index);
#endif
return;
}
// #define EXTRACT_DCI_ITEM(val,size) val= readBits(dci_pdu, &pos, size);
#define EXTRACT_DCI_ITEM(val, size) \
val = readBits(dci_pdu, &pos, size); \
LOG_D(NR_MAC_DCI, " " #val ": %d\n", val);
// Fixme: Intel Endianess only procedure
static inline int readBits(const uint8_t *dci, int *start, int length)
{
uint32_t mask = (1U << length) - 1;
uint64_t *tmp = (uint64_t *)dci;
*start -= length;
return *tmp >> *start & mask;
}
static void extract_10_ra_rnti(dci_pdu_rel15_t *dci_pdu_rel15, const uint8_t *dci_pdu, int pos, const int N_RB)
{
LOG_D(NR_MAC_DCI, "Received dci 1_0 RA rnti\n");
// Freq domain assignment
EXTRACT_DCI_ITEM(dci_pdu_rel15->frequency_domain_assignment.val, (int)ceil(log2((N_RB * (N_RB + 1)) >> 1)));
// Time domain assignment
EXTRACT_DCI_ITEM(dci_pdu_rel15->time_domain_assignment.val, 4);
// VRB to PRB mapping
EXTRACT_DCI_ITEM(dci_pdu_rel15->vrb_to_prb_mapping.val, 1);
// MCS
EXTRACT_DCI_ITEM(dci_pdu_rel15->mcs, 5);
// TB scaling
EXTRACT_DCI_ITEM(dci_pdu_rel15->tb_scaling, 2);
}
static uint8_t extract_10_si_rnti(dci_pdu_rel15_t *dci_pdu_rel15, const uint8_t *dci_pdu, int pos, const int N_RB)
{
LOG_D(NR_MAC_DCI, "Received dci 1_0 SI rnti\n");
// Freq domain assignment 0-16 bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->frequency_domain_assignment.val, (int)ceil(log2((N_RB * (N_RB + 1)) >> 1)));
// Time domain assignment 4 bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->time_domain_assignment.val, 4);
// VRB to PRB mapping 1 bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->vrb_to_prb_mapping.val, 1);
// MCS 5bit //bit over 32
EXTRACT_DCI_ITEM(dci_pdu_rel15->mcs, 5);
// Redundancy version 2 bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->rv, 2);
// System information indicator 1 bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->system_info_indicator, 1);
return dci_pdu_rel15->system_info_indicator;
}
static bool extract_10_c_rnti(NR_UE_MAC_INST_t *mac,
dci_pdu_rel15_t *dci_pdu_rel15,
const uint8_t *dci_pdu,
int pos,
const int N_RB)
{
LOG_D(NR_MAC_DCI, "Received dci 1_0 C rnti\n");
// Freq domain assignment (275rb >> fsize = 16)
int fsize = (int)ceil(log2((N_RB * (N_RB + 1)) >> 1));
EXTRACT_DCI_ITEM(dci_pdu_rel15->frequency_domain_assignment.val, fsize);
bool pdcch_order = true;
for (int i = 0; i < fsize; i++) {
if (!((dci_pdu_rel15->frequency_domain_assignment.val >> i) & 1)) {
pdcch_order = false;
break;
}
}
if (pdcch_order) { // Frequency domain resource assignment field are all 1 38.212 section 7.3.1.2.1
// ra_preamble_index 6 bits
EXTRACT_DCI_ITEM(dci_pdu_rel15->ra_preamble_index, 6);
// UL/SUL indicator 1 bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->ul_sul_indicator.val, 1);
// SS/PBCH index 6 bits
EXTRACT_DCI_ITEM(dci_pdu_rel15->ss_pbch_index, 6);
// prach_mask_index 4 bits
EXTRACT_DCI_ITEM(dci_pdu_rel15->prach_mask_index, 4);
trigger_MAC_UE_RA(mac, dci_pdu_rel15);
return true;
} // end if
// Time domain assignment 4bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->time_domain_assignment.val, 4);
// VRB to PRB mapping 1bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->vrb_to_prb_mapping.val, 1);
// MCS 5bit //bit over 32, so dci_pdu ++
EXTRACT_DCI_ITEM(dci_pdu_rel15->mcs, 5);
// New data indicator 1bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->ndi, 1);
// Redundancy version 2bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->rv, 2);
// HARQ process number 4/5 bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->harq_pid.val, dci_pdu_rel15->harq_pid.nbits);
// Downlink assignment index 2bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->dai[0].val, 2);
// TPC command for scheduled PUCCH 2bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->tpc, 2);
// PUCCH resource indicator 3bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->pucch_resource_indicator, 3);
// PDSCH-to-HARQ_feedback timing indicator 3bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->pdsch_to_harq_feedback_timing_indicator.val, 3);
return false;
}
static void extract_00_c_rnti(dci_pdu_rel15_t *dci_pdu_rel15, const uint8_t *dci_pdu, int pos)
{
LOG_D(NR_MAC_DCI, "Received dci 0_0 C rnti\n");
// Frequency domain assignment
EXTRACT_DCI_ITEM(dci_pdu_rel15->frequency_domain_assignment.val, dci_pdu_rel15->frequency_domain_assignment.nbits);
// Time domain assignment 4bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->time_domain_assignment.val, 4);
// Frequency hopping flag  E1 bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->frequency_hopping_flag.val, 1);
// MCS 5 bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->mcs, 5);
// New data indicator 1bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->ndi, 1);
// Redundancy version 2bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->rv, 2);
// HARQ process number 4bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->harq_pid.val, 4);
// TPC command for scheduled PUSCH  E2 bits
EXTRACT_DCI_ITEM(dci_pdu_rel15->tpc, 2);
// UL/SUL indicator  E1 bit
/* commented for now (RK): need to get this from BWP descriptor
if (cfg->pucch_config.pucch_GroupHopping.value)
dci_pdu->= ((uint64_t)readBits(dci_pdu,>>(dci_size-pos)ul_sul_indicator&1)<<(dci_size-pos++);
*/
}
static void extract_10_tc_rnti(dci_pdu_rel15_t *dci_pdu_rel15, const uint8_t *dci_pdu, int pos, const int N_RB)
{
LOG_D(NR_MAC_DCI, "Received dci 1_0 TC rnti\n");
// Freq domain assignment 0-16 bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->frequency_domain_assignment.val, (int)ceil(log2((N_RB * (N_RB + 1)) >> 1)));
// Time domain assignment - 4 bits
EXTRACT_DCI_ITEM(dci_pdu_rel15->time_domain_assignment.val, 4);
// VRB to PRB mapping - 1 bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->vrb_to_prb_mapping.val, 1);
// MCS 5bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->mcs, 5);
// New data indicator - 1 bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->ndi, 1);
// Redundancy version - 2 bits
EXTRACT_DCI_ITEM(dci_pdu_rel15->rv, 2);
// HARQ process number - 4 bits
EXTRACT_DCI_ITEM(dci_pdu_rel15->harq_pid.val, 4);
// Downlink assignment index - 2 bits
EXTRACT_DCI_ITEM(dci_pdu_rel15->dai[0].val, 2);
// TPC command for scheduled PUCCH - 2 bits
EXTRACT_DCI_ITEM(dci_pdu_rel15->tpc, 2);
// PUCCH resource indicator - 3 bits
EXTRACT_DCI_ITEM(dci_pdu_rel15->pucch_resource_indicator, 3);
// PDSCH-to-HARQ_feedback timing indicator - 3 bits
EXTRACT_DCI_ITEM(dci_pdu_rel15->pdsch_to_harq_feedback_timing_indicator.val, 3);
}
static void extract_00_tc_rnti(dci_pdu_rel15_t *dci_pdu_rel15, const uint8_t *dci_pdu, int pos)
{
LOG_D(NR_MAC_DCI, "Received dci 0_0 TC rnti\n");
// Frequency domain assignment
EXTRACT_DCI_ITEM(dci_pdu_rel15->frequency_domain_assignment.val, dci_pdu_rel15->frequency_domain_assignment.nbits);
// Time domain assignment 4bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->time_domain_assignment.val, 4);
// Frequency hopping flag  E1 bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->frequency_hopping_flag.val, 1);
// MCS 5 bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->mcs, 5);
// New data indicator 1bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->ndi, 1);
// Redundancy version 2bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->rv, 2);
// HARQ process number 4bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->harq_pid.val, 4);
// TPC command for scheduled PUSCH  E2 bits
EXTRACT_DCI_ITEM(dci_pdu_rel15->tpc, 2);
}
static void extract_11_c_rnti(dci_pdu_rel15_t *dci_pdu_rel15, const uint8_t *dci_pdu, int pos)
{
LOG_D(NR_MAC_DCI, "Received dci 1_1 C rnti\n");
// Carrier indicator
EXTRACT_DCI_ITEM(dci_pdu_rel15->carrier_indicator.val, dci_pdu_rel15->carrier_indicator.nbits);
// BWP Indicator
EXTRACT_DCI_ITEM(dci_pdu_rel15->bwp_indicator.val, dci_pdu_rel15->bwp_indicator.nbits);
// Frequency domain resource assignment
EXTRACT_DCI_ITEM(dci_pdu_rel15->frequency_domain_assignment.val, dci_pdu_rel15->frequency_domain_assignment.nbits);
// Time domain resource assignment
EXTRACT_DCI_ITEM(dci_pdu_rel15->time_domain_assignment.val, dci_pdu_rel15->time_domain_assignment.nbits);
// VRB-to-PRB mapping
EXTRACT_DCI_ITEM(dci_pdu_rel15->vrb_to_prb_mapping.val, dci_pdu_rel15->vrb_to_prb_mapping.nbits);
// PRB bundling size indicator
EXTRACT_DCI_ITEM(dci_pdu_rel15->prb_bundling_size_indicator.val, dci_pdu_rel15->prb_bundling_size_indicator.nbits);
// Rate matching indicator
EXTRACT_DCI_ITEM(dci_pdu_rel15->rate_matching_indicator.val, dci_pdu_rel15->rate_matching_indicator.nbits);
// ZP CSI-RS trigger
EXTRACT_DCI_ITEM(dci_pdu_rel15->zp_csi_rs_trigger.val, dci_pdu_rel15->zp_csi_rs_trigger.nbits);
// TB1
// MCS 5bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->mcs, 5);
// New data indicator 1bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->ndi, 1);
// Redundancy version 2bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->rv, 2);
//TB2
// MCS 5bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->mcs2.val, dci_pdu_rel15->mcs2.nbits);
// New data indicator 1bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->ndi2.val, dci_pdu_rel15->ndi2.nbits);
// Redundancy version 2bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->rv2.val, dci_pdu_rel15->rv2.nbits);
// HARQ process number 4/5 bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->harq_pid.val, dci_pdu_rel15->harq_pid.nbits);
// Downlink assignment index
EXTRACT_DCI_ITEM(dci_pdu_rel15->dai[0].val, dci_pdu_rel15->dai[0].nbits);
// TPC command for scheduled PUCCH 2bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->tpc, 2);
// PUCCH resource indicator 3bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->pucch_resource_indicator, 3);
// PDSCH-to-HARQ_feedback timing indicator
EXTRACT_DCI_ITEM(dci_pdu_rel15->pdsch_to_harq_feedback_timing_indicator.val,
dci_pdu_rel15->pdsch_to_harq_feedback_timing_indicator.nbits);
// Antenna ports
EXTRACT_DCI_ITEM(dci_pdu_rel15->antenna_ports.val, dci_pdu_rel15->antenna_ports.nbits);
// TCI
EXTRACT_DCI_ITEM(dci_pdu_rel15->transmission_configuration_indication.val,
dci_pdu_rel15->transmission_configuration_indication.nbits);
// SRS request
EXTRACT_DCI_ITEM(dci_pdu_rel15->srs_request.val, dci_pdu_rel15->srs_request.nbits);
// CBG transmission information
EXTRACT_DCI_ITEM(dci_pdu_rel15->cbgti.val, dci_pdu_rel15->cbgti.nbits);
// CBG flushing out information
EXTRACT_DCI_ITEM(dci_pdu_rel15->cbgfi.val, dci_pdu_rel15->cbgfi.nbits);
// DMRS sequence init
EXTRACT_DCI_ITEM(dci_pdu_rel15->dmrs_sequence_initialization.val, 1);
}
static void extract_01_c_rnti(dci_pdu_rel15_t *dci_pdu_rel15, const uint8_t *dci_pdu, int pos, const int N_RB)
{
LOG_D(NR_MAC_DCI, "Received dci 0_1 C rnti\n");
// Carrier indicator
EXTRACT_DCI_ITEM(dci_pdu_rel15->carrier_indicator.val, dci_pdu_rel15->carrier_indicator.nbits);
// UL/SUL Indicator
EXTRACT_DCI_ITEM(dci_pdu_rel15->ul_sul_indicator.val, dci_pdu_rel15->ul_sul_indicator.nbits);
// BWP Indicator
EXTRACT_DCI_ITEM(dci_pdu_rel15->bwp_indicator.val, dci_pdu_rel15->bwp_indicator.nbits);
// Freq domain assignment max 16 bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->frequency_domain_assignment.val, (int)ceil(log2((N_RB * (N_RB + 1)) >> 1)));
// Time domain assignment
EXTRACT_DCI_ITEM(dci_pdu_rel15->time_domain_assignment.val, dci_pdu_rel15->time_domain_assignment.nbits);
// Not supported yet - skip for now
// Frequency hopping flag – 1 bit
// EXTRACT_DCI_ITEM(dci_pdu_rel15->frequency_hopping_flag.val, 1);
// MCS 5 bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->mcs, 5);
// New data indicator 1bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->ndi, 1);
// Redundancy version 2bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->rv, 2);
// HARQ process number 4/5 bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->harq_pid.val, dci_pdu_rel15->harq_pid.nbits);
// 1st Downlink assignment index
EXTRACT_DCI_ITEM(dci_pdu_rel15->dai[0].val, dci_pdu_rel15->dai[0].nbits);
// 2nd Downlink assignment index
EXTRACT_DCI_ITEM(dci_pdu_rel15->dai[1].val, dci_pdu_rel15->dai[1].nbits);
// TPC command for scheduled PUSCH – 2 bits
EXTRACT_DCI_ITEM(dci_pdu_rel15->tpc, 2);
// SRS resource indicator
EXTRACT_DCI_ITEM(dci_pdu_rel15->srs_resource_indicator.val, dci_pdu_rel15->srs_resource_indicator.nbits);
// Precoding info and n. of layers
EXTRACT_DCI_ITEM(dci_pdu_rel15->precoding_information.val, dci_pdu_rel15->precoding_information.nbits);
// Antenna ports
EXTRACT_DCI_ITEM(dci_pdu_rel15->antenna_ports.val, dci_pdu_rel15->antenna_ports.nbits);
// SRS request
EXTRACT_DCI_ITEM(dci_pdu_rel15->srs_request.val, dci_pdu_rel15->srs_request.nbits);
// CSI request
EXTRACT_DCI_ITEM(dci_pdu_rel15->csi_request.val, dci_pdu_rel15->csi_request.nbits);
// CBG transmission information
EXTRACT_DCI_ITEM(dci_pdu_rel15->cbgti.val, dci_pdu_rel15->cbgti.nbits);
// PTRS DMRS association
EXTRACT_DCI_ITEM(dci_pdu_rel15->ptrs_dmrs_association.val, dci_pdu_rel15->ptrs_dmrs_association.nbits);
// Beta offset indicator
EXTRACT_DCI_ITEM(dci_pdu_rel15->beta_offset_indicator.val, dci_pdu_rel15->beta_offset_indicator.nbits);
// DMRS sequence initialization
EXTRACT_DCI_ITEM(dci_pdu_rel15->dmrs_sequence_initialization.val, dci_pdu_rel15->dmrs_sequence_initialization.nbits);
// UL-SCH indicator
EXTRACT_DCI_ITEM(dci_pdu_rel15->ulsch_indicator, 1);
// UL/SUL indicator – 1 bit
/* commented for now (RK): need to get this from BWP descriptor
if (cfg->pucch_config.pucch_GroupHopping.value)
dci_pdu->= ((uint64_t)*dci_pdu>>(dci_size-pos)ul_sul_indicator&1)<<(dci_size-pos++);
*/
}
static int get_nrb_for_dci(NR_UE_MAC_INST_t *mac, nr_dci_format_t dci_format, int ss_type)
{
NR_UE_DL_BWP_t *current_DL_BWP = mac->current_DL_BWP;
NR_UE_UL_BWP_t *current_UL_BWP = mac->current_UL_BWP;
int N_RB;
if(current_DL_BWP)
N_RB = get_rb_bwp_dci(dci_format,
ss_type,
mac->type0_PDCCH_CSS_config.num_rbs,
current_UL_BWP->BWPSize,
current_DL_BWP->BWPSize,
mac->sc_info.initial_dl_BWPSize,
mac->sc_info.initial_dl_BWPSize);
else
N_RB = mac->type0_PDCCH_CSS_config.num_rbs;
if (N_RB == 0)
LOG_E(NR_MAC_DCI, "DCI configuration error! N_RB = 0\n");
return N_RB;
}
static nr_dci_format_t nr_extract_dci_00_10(NR_UE_MAC_INST_t *mac,
int pos,
const int rnti_type,
const uint8_t *dci_pdu,
const int slot,
const int ss_type)
{
nr_dci_format_t format = NR_DCI_NONE;
dci_pdu_rel15_t *dci_pdu_rel15 = NULL;
int format_indicator = -1;
int n_RB = 0;
switch (rnti_type) {
case TYPE_RA_RNTI_ :
format = NR_DL_DCI_FORMAT_1_0;
dci_pdu_rel15 = &mac->def_dci_pdu_rel15[slot][format];
n_RB = get_nrb_for_dci(mac, format, ss_type);
if (n_RB == 0)
return NR_DCI_NONE;
extract_10_ra_rnti(dci_pdu_rel15, dci_pdu, pos, n_RB);
break;
case TYPE_P_RNTI_ :
AssertFatal(false, "DCI for P-RNTI not handled yet\n");
break;
case TYPE_SI_RNTI_ :
format = NR_DL_DCI_FORMAT_1_0;
dci_pdu_rel15 = &mac->def_dci_pdu_rel15[slot][format];
n_RB = get_nrb_for_dci(mac, format, ss_type);
if (n_RB == 0)
return NR_DCI_NONE;
uint8_t sys_info = extract_10_si_rnti(dci_pdu_rel15, dci_pdu, pos, n_RB);
// sys info = 0 for SIB1 and 1 for other SIB
if (mac->get_sib1 == 0 && sys_info == 0)
return NR_DCI_NONE;
break;
case TYPE_C_RNTI_ :
// Identifier for DCI formats
EXTRACT_DCI_ITEM(format_indicator, 1);
if (format_indicator == 1) {
format = NR_DL_DCI_FORMAT_1_0;
dci_pdu_rel15 = &mac->def_dci_pdu_rel15[slot][format];
int n_RB = get_nrb_for_dci(mac, format, ss_type);
if (n_RB == 0)
return NR_DCI_NONE;
bool pdcch_order = extract_10_c_rnti(mac, dci_pdu_rel15, dci_pdu, pos, n_RB);
if (pdcch_order)
return NR_DCI_NONE;
}
else {
format = NR_UL_DCI_FORMAT_0_0;
dci_pdu_rel15 = &mac->def_dci_pdu_rel15[slot][format];
extract_00_c_rnti(dci_pdu_rel15, dci_pdu, pos);
}
dci_pdu_rel15->format_indicator = format_indicator;
break;
case TYPE_TC_RNTI_ :
case TYPE_MSGB_RNTI_:
// Identifier for DCI formats
EXTRACT_DCI_ITEM(format_indicator, 1);
if (format_indicator == 1) {
format = NR_DL_DCI_FORMAT_1_0;
dci_pdu_rel15 = &mac->def_dci_pdu_rel15[slot][format];
n_RB = get_nrb_for_dci(mac, format, ss_type);
if (n_RB == 0)
return NR_DCI_NONE;
extract_10_tc_rnti(dci_pdu_rel15, dci_pdu, pos, n_RB);
}
else {
format = NR_UL_DCI_FORMAT_0_0;
dci_pdu_rel15 = &mac->def_dci_pdu_rel15[slot][format];
extract_00_tc_rnti(dci_pdu_rel15, dci_pdu, pos);
}
dci_pdu_rel15->format_indicator = format_indicator;
break;
default :
AssertFatal(false, "Invalid RNTI type\n");
}
return format;
}
static nr_dci_format_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac,
const nfapi_nr_dci_formats_e dci_format,
const uint8_t dci_size,
const uint16_t rnti,
const int ss_type,
const uint8_t *dci_pdu,
const int slot)
{
LOG_D(NR_MAC_DCI,"nr_extract_dci_info : dci_pdu %lx, size %d, format %d\n", *(uint64_t *)dci_pdu, dci_size, dci_format);
int rnti_type = get_rnti_type(mac, rnti);
int pos = dci_size;
nr_dci_format_t format = NR_DCI_NONE;
switch(dci_format) {
case NFAPI_NR_FORMAT_0_0_AND_1_0 :
format = nr_extract_dci_00_10(mac, pos, rnti_type, dci_pdu, slot, ss_type);
break;
case NFAPI_NR_FORMAT_0_1_AND_1_1 :
if (rnti_type == TYPE_C_RNTI_) {
// Identifier for DCI formats
int format_indicator = 0;
EXTRACT_DCI_ITEM(format_indicator, 1);
if (format_indicator == 1) {
format = NR_DL_DCI_FORMAT_1_1;
dci_pdu_rel15_t *def_dci_pdu_rel15 = &mac->def_dci_pdu_rel15[slot][format];
def_dci_pdu_rel15->format_indicator = format_indicator;
extract_11_c_rnti(def_dci_pdu_rel15, dci_pdu, pos);
}
else {
format = NR_UL_DCI_FORMAT_0_1;
dci_pdu_rel15_t *def_dci_pdu_rel15 = &mac->def_dci_pdu_rel15[slot][format];
def_dci_pdu_rel15->format_indicator = format_indicator;
int n_RB = get_nrb_for_dci(mac, format, ss_type);
if (n_RB == 0)
return NR_DCI_NONE;
extract_01_c_rnti(def_dci_pdu_rel15, dci_pdu, pos, n_RB);
}
}
else {
LOG_E(NR_MAC_DCI, "RNTI type not supported for formats 01 or 11\n");
return NR_DCI_NONE;
}
break;
default :
LOG_E(NR_MAC_DCI, "DCI format not supported\n");
}
return format;
}
nr_dci_format_t nr_ue_process_dci_indication_pdu(NR_UE_MAC_INST_t *mac, frame_t frame, int slot, fapi_nr_dci_indication_pdu_t *dci)
{
LOG_D(NR_MAC,
"Received dci indication (rnti %x, dci format %d, n_CCE %d, payloadSize %d, payload %llx)\n",
dci->rnti,
dci->dci_format,
dci->n_CCE,
dci->payloadSize,
*(unsigned long long *)dci->payloadBits);
const nr_dci_format_t format =
nr_extract_dci_info(mac, dci->dci_format, dci->payloadSize, dci->rnti, dci->ss_type, dci->payloadBits, slot);
if (format == NR_DCI_NONE)
return NR_DCI_NONE;
int ret = nr_ue_process_dci(mac, frame, slot, mac->def_dci_pdu_rel15[slot] + format, dci, format);
if (ret < 0) {
mac->stats.bad_dci++;
return NR_DCI_NONE;
}
return format;
}
static bool check_ra_contention_resolution(const uint8_t *pdu, const uint8_t *cont_res)
{
if (IS_SOFTMODEM_IQPLAYER) // Control is bypassed when replaying IQs (BMC)
return true;
for (int i = 0; i < 6; i++) {
if (pdu[i] != cont_res[i]) {
return false;
}
}
return true;
}
static int nr_ue_validate_successrar(uint8_t *pduP, int32_t pdu_len, NR_UE_MAC_INST_t *mac, frame_t frameP, int slot)
{
// TS 38.321 - Figure 6.1.5a-1: BI MAC subheader
// TS 38.321 - Figure 6.1.5a-3: SuccessRAR MAC subheader
int n = 0;
uint8_t E = 1;
uint8_t cont_res_id[6];
RA_config_t *ra = &mac->ra;
ra->RA_backoff_limit = 0;
while (n < pdu_len && E) {
E = (pduP[n] >> 7) & 0x1;
uint8_t SUCESS_RAR_header_T1 = (pduP[n] >> 6) & 0x1;
if (SUCESS_RAR_header_T1 == 0) { // T2 exist
int SUCESS_RAR_header_T2 = (pduP[n] >> 5) & 0x1;
if (SUCESS_RAR_header_T2 == 0) { // BI
int bi_ms = table_7_2_1[(pduP[n] & 0x0F)] * ra->scaling_factor_bi;
int slots_per_ms = mac->frame_structure.numb_slots_frame / 10;
ra->RA_backoff_limit = bi_ms * slots_per_ms;
n++;
} else { // S
n++;
// TS 38.321 - Figure 6.2.3a-2: successRAR
for (int i = 0; i < 6; ++i)
cont_res_id[i] = pduP[n + i];
n += 6;
// Oct 7
ra->MsgB_R = 0;
ra->MsgB_CH_ACESS_CPEXT = (pduP[n] >> 5) & 0x3;
ra->MsgB_TPC = (pduP[n] >> 3) & 3;
ra->MsgB_HARQ_FTI = (int8_t)pduP[n] & 0x7;
// Oct 8
n++;
ra->PUCCH_RI = ((int8_t)pduP[n] >> 4) & 0x0F;
// Oct 8 and Oct 9
ra->timing_advance_command = ((uint16_t)(pduP[n] & 0xf) << 8) | pduP[n + 1];
n += 2;
// Oct 10 and Oct 11
ra->t_crnti = ((uint16_t)pduP[n] << 8) | pduP[n + 1];
n += 2;
LOG_D(NR_MAC,
"successRAR: Contention Resolution ID 0x%02x%02x%02x%02x%02x%02x R 0x%01x CH_ACESS_CPEXT 0x%02x TPC 0x%02x "
"HARQ_FTI 0x%03x PUCCH_RI 0x%04x TA 0x%012x CRNTI 0x%04x\n",
cont_res_id[0],
cont_res_id[1],
cont_res_id[2],
cont_res_id[3],
cont_res_id[4],
cont_res_id[5],
ra->MsgB_R,
ra->MsgB_CH_ACESS_CPEXT,
ra->MsgB_TPC,
ra->MsgB_HARQ_FTI,
ra->PUCCH_RI,
ra->timing_advance_command,
ra->t_crnti);
bool ra_success = check_ra_contention_resolution(cont_res_id, ra->cont_res_id);
if (ra->RA_active && ra_success) {
nr_timer_stop(&ra->response_window_timer);
nr_ra_succeeded(mac, frameP, slot);
} else if (!ra_success) {
nr_ra_backoff_setting(ra);
}
}
} else { // RAPID
int RAPID = pduP[n] & 0x3F;
n++;
LOG_D(MAC, "RAPID %d\n", RAPID);
AssertFatal(false, "FallbackRAR not implemented yet!\n");
}
}
const int ta = ((NR_MAC_CE_TA *)pduP)[1].TA_COMMAND;
set_time_alignment(mac, ta, adjustment_ta, frameP, slot);
return n;
}
#define MAX_NUM_DATA_IND 1024
///////////////////////////////////
// brief: nr_ue_process_mac_pdu
// function: parsing DL PDU header
///////////////////////////////////
// Header for DLSCH:
// Except:
// - DL-SCH: fixed-size MAC CE(known by LCID)
// - DL-SCH: padding
//
// |0|1|2|3|4|5|6|7| bit-wise
// |R|F| LCID |
// | L |
// |0|1|2|3|4|5|6|7| bit-wise
// |R|F| LCID |
// | L |
// | L |
////////////////////////////////
// Header for DLSCH:
// - DLSCH: fixed-size MAC CE(known by LCID)
// - DLSCH: padding, for single/multiple 1-oct padding CE(s)
//
// |0|1|2|3|4|5|6|7| bit-wise
// |R|R| LCID |
// LCID: The Logical Channel ID field identifies the logical channel instance of the corresponding MAC SDU or the type of the
// corresponding MAC CE or padding as described
// in Tables 6.2.1-1 and 6.2.1-2 for the DL-SCH and UL-SCH respectively. There is one LCID field per MAC subheader. The LCID
// field size is 6 bits;
// L: The Length field indicates the length of the corresponding MAC SDU or variable-sized MAC CE in bytes. There is one L field
// per MAC subheader except for subheaders
// corresponding to fixed-sized MAC CEs and padding. The size of the L field is indicated by the F field;
// F: lenght of L is 0:8 or 1:16 bits wide
// R: Reserved bit, set to zero.
////////////////////////////////
static void nr_ue_process_mac_pdu(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *dl_info, int pdu_id)
{
frame_t frameP = dl_info->frame;
int slot = dl_info->slot;
fapi_nr_pdsch_pdu_t *pdsch_pdu = &(dl_info->rx_ind->rx_indication_body + pdu_id)->pdsch_pdu;
uint8_t *pduP = pdsch_pdu->pdu;
int32_t pdu_len = (int32_t)pdsch_pdu->pdu_length;
uint8_t CC_id = dl_info->cc_id;
uint8_t done = 0;
RA_config_t *ra = &mac->ra;
if (!pduP) {
return;
}
T(T_NRUE_MAC_DL_PDU_WITH_DATA, T_INT(mac->crnti), T_INT(frameP), T_INT(slot), T_INT(pdsch_pdu->harq_pid), T_BUFFER(pduP, pdu_len));
LOG_D(MAC,
"[%d.%d]: processing PDU %d (with length %d) of %d total number of PDUs...\n",
frameP,
slot,
pdu_id,
pdu_len,
dl_info->rx_ind->number_pdus);
if (ra->ra_type == RA_2_STEP && ra->ra_state == nrRA_WAIT_MSGB) {
int n = nr_ue_validate_successrar(pduP, pdu_len, mac, frameP, slot);
pduP += n;
pdu_len -= n;
}
nr_rlc_data_ind_t data_ind[MAX_NUM_DATA_IND] = {0};
int num_data_ind = 0;
while (!done && pdu_len > 0){
uint16_t mac_len = 0x0000;
uint16_t mac_subheader_len = 0x0001; // default to fixed-length subheader = 1-oct
uint8_t rx_lcid = ((NR_MAC_SUBHEADER_FIXED *)pduP)->LCID;
LOG_D(MAC, "[UE] LCID %d, PDU length %d\n", rx_lcid, pdu_len);
bool ret;
switch (rx_lcid) {
// MAC CE
case DL_SCH_LCID_CCCH:
// MSG4 RRC Setup 38.331
// variable length
ret=get_mac_len(pduP, pdu_len, &mac_len, &mac_subheader_len);
AssertFatal(ret, "The mac_len (%d) has an invalid size. PDU len = %d! \n", mac_len, pdu_len);
// Check if it is a valid CCCH message, we get all 00's messages very often
int i = 0;
for(i=0; i<(mac_subheader_len+mac_len); i++) {
if(pduP[i] != 0) {
break;
}
}
if (i == (mac_subheader_len+mac_len)) {
LOG_E(NR_MAC, "Invalid CCCH message!, pdu_len: %d\n", pdu_len);
done = 1;
break;
}
if (mac_len > 0) {
LOG_DDUMP(NR_MAC, (void *)pduP, mac_subheader_len + mac_len, LOG_DUMP_CHAR, "DL_SCH_LCID_CCCH (e.g. RRCSetup) payload: ");
nr_rlc_data_ind_t ind = {.ch = rx_lcid, .buf = pduP + mac_subheader_len, .len = mac_len};
data_ind[num_data_ind++] = ind;
DevAssert(num_data_ind < MAX_NUM_DATA_IND);
}
break;
case DL_SCH_LCID_TCI_STATE_ACT_UE_SPEC_PDSCH:
case DL_SCH_LCID_APERIODIC_CSI_TRI_STATE_SUBSEL:
case DL_SCH_LCID_SP_CSI_RS_CSI_IM_RES_SET_ACT:
case DL_SCH_LCID_SP_SRS_ACTIVATION:
// 38.321 Ch6.1.3.14
// varialbe length
get_mac_len(pduP, pdu_len, &mac_len, &mac_subheader_len);
break;
case DL_SCH_LCID_RECOMMENDED_BITRATE:
// 38.321 Ch6.1.3.20
mac_len = 2;
break;
case DL_SCH_LCID_SP_ZP_CSI_RS_RES_SET_ACT:
// 38.321 Ch6.1.3.19
mac_len = 2;
break;
case DL_SCH_LCID_PUCCH_SPATIAL_RELATION_ACT:
// 38.321 Ch6.1.3.18
mac_len = 3;
break;
case DL_SCH_LCID_SP_CSI_REP_PUCCH_ACT:
// 38.321 Ch6.1.3.16
mac_len = 2;
break;
case DL_SCH_LCID_TCI_STATE_IND_UE_SPEC_PDCCH:
// 38.321 Ch6.1.3.15
mac_len = 2;
break;
case DL_SCH_LCID_DUPLICATION_ACT:
// 38.321 Ch6.1.3.11
mac_len = 1;
break;
case DL_SCH_LCID_SCell_ACT_4_OCT:
// 38.321 Ch6.1.3.10
mac_len = 4;
break;
case DL_SCH_LCID_SCell_ACT_1_OCT:
// 38.321 Ch6.1.3.10
mac_len = 1;
break;
case DL_SCH_LCID_L_DRX:
// 38.321 Ch6.1.3.6
// fixed length but not yet specify.
mac_len = 0;
break;
case DL_SCH_LCID_DRX:
// 38.321 Ch6.1.3.5
// fixed length but not yet specify.
mac_len = 0;
break;
case DL_SCH_LCID_TA_COMMAND:
// 38.321 Ch6.1.3.4
mac_len = 1;
const int ta = ((NR_MAC_CE_TA *)pduP)[1].TA_COMMAND;
const int tag = ((NR_MAC_CE_TA *)pduP)[1].TAGID;
if (tag != mac->tag_Id) {
LOG_E(NR_MAC, "MAC CE TAG %d does not correspond to the one configured at MAC %ld\n", tag, mac->tag_Id);
done = 1;
break;
}
set_time_alignment(mac, ta, adjustment_ta, frameP, slot);
if (ta == 31)
LOG_D(NR_MAC, "[%d.%d] Received TA_COMMAND %u TAGID %u CC_id %d \n", frameP, slot, ta, tag, CC_id);
else
LOG_I(NR_MAC, "[%d.%d] Received TA_COMMAND %u TAGID %u CC_id %d \n", frameP, slot, ta, tag, CC_id);
break;
case DL_SCH_LCID_CON_RES_ID:
// Clause 5.1.5 and 6.1.3.3 of 3GPP TS 38.321 version 16.2.1 Release 16
// MAC Header: 1 byte (R/R/LCID)
// MAC SDU: 6 bytes (UE Contention Resolution Identity)
mac_len = 6;
if (ra->ra_state == nrRA_WAIT_CONTENTION_RESOLUTION) {
LOG_D(NR_MAC,
"[UE %d]Frame %d Contention resolution identity: 0x%02x%02x%02x%02x%02x%02x Terminating RA procedure\n",
mac->ue_id,
frameP,
pduP[1],
pduP[2],
pduP[3],
pduP[4],
pduP[5],
pduP[6]);
nr_timer_stop(&ra->contention_resolution_timer);
bool ra_success = check_ra_contention_resolution(&pduP[1], ra->cont_res_id);
if (ra->RA_active && ra_success) {
nr_ra_succeeded(mac, frameP, slot);
} else if (!ra_success) {
// consider this Contention Resolution not successful and discard the successfully decoded MAC PDU
nr_ra_contention_resolution_failed(mac);
return;
}
}
break;
case DL_SCH_LCID_PADDING:
done = 1;
// end of MAC PDU, can ignore the rest.
break;
// MAC SDU
// From values 1 to 32 it equals to the identity of the logical channel
case 1 ... 32:
if (!get_mac_len(pduP, pdu_len, &mac_len, &mac_subheader_len)) {
LOG_E(MAC, "get_mac_len(): invalid pdu_len %d (mac_len %d mac_subheader_len %d)\n", pdu_len, mac_len, mac_subheader_len);
done = 1;
break;
}
// discard the received subPDU if RB is suspended
if (is_lcid_suspended(mac, rx_lcid)) {
LOG_W(NR_MAC, "Received PDU for a suspended RB, corresponding to LCID %d. Dropping it.\n", rx_lcid);
break;
}
LOG_D(NR_MAC, "%4d.%2d : DLSCH -> LCID %d %d bytes\n", frameP, slot, rx_lcid, mac_len);
nr_rlc_data_ind_t ind = {.ch = rx_lcid, .buf = pduP + mac_subheader_len, .len = mac_len};
data_ind[num_data_ind++] = ind;
DevAssert(num_data_ind < MAX_NUM_DATA_IND);
break;
default:
LOG_W(MAC, "unknown lcid %02x\n", rx_lcid);
break;
}
pduP += (mac_subheader_len + mac_len);
pdu_len -= (mac_subheader_len + mac_len);
if (pdu_len < 0) {
LOG_E(MAC, "[UE %d][%d.%d] nr_ue_process_mac_pdu, residual mac pdu length %d < 0!\n", mac->ue_id, frameP, slot, pdu_len);
done = 1;
}
}
nr_mac_rlc_data_ind(mac->ue_id, mac->ue_id, false, data_ind, num_data_ind);
}
/**
* Function: generating MAC CEs (MAC CE and subheader) for the ULSCH PDU
* Parameters:
* @mac_ce pointer to the MAC sub-PDUs including the MAC CEs
* Return: number of written bytes
*/
int nr_write_ce_ulsch_pdu(uint8_t *mac_ce, NR_SINGLE_ENTRY_PHR_MAC_CE *power_headroom, const type_bsr_t *bsr, uint8_t *mac_ce_end)
{
uint8_t *pdu = mac_ce;
if (power_headroom) {
// MAC CE fixed subheader
*(NR_MAC_SUBHEADER_FIXED *)mac_ce = (NR_MAC_SUBHEADER_FIXED){.R = 0, .LCID = UL_SCH_LCID_SINGLE_ENTRY_PHR};
mac_ce += sizeof(NR_MAC_SUBHEADER_FIXED);
*(NR_SINGLE_ENTRY_PHR_MAC_CE *)mac_ce = *power_headroom;
mac_ce += sizeof(NR_SINGLE_ENTRY_PHR_MAC_CE);
LOG_D(NR_MAC, "[UE] Generating ULSCH PDU : power_headroom pdu %p mac_ce %p b\n",
pdu, mac_ce);
}
switch (bsr->type_bsr) {
case b_short:
case b_short_trunc: {
*(NR_MAC_SUBHEADER_FIXED *)mac_ce =
(NR_MAC_SUBHEADER_FIXED){.R = 0, .LCID = bsr->type_bsr == b_short ? UL_SCH_LCID_S_BSR : UL_SCH_LCID_S_TRUNCATED_BSR};
mac_ce += sizeof(NR_MAC_SUBHEADER_FIXED);
*(NR_BSR_SHORT *)mac_ce = bsr->bsr.s;
mac_ce += sizeof(NR_BSR_SHORT);
LOG_D(NR_MAC, "[UE] Generating ULSCH PDU : bsr Buffer_size %d LcgID %d \n", bsr->bsr.s.Buffer_size, bsr->bsr.s.LcgID);
} break;
case b_long:
case b_long_trunc: {
// ch 6.1.3.1. TS 38.321
NR_MAC_SUBHEADER_SHORT *mac_pdu_subheader_ptr = (NR_MAC_SUBHEADER_SHORT *)mac_ce;
mac_ce += sizeof(NR_MAC_SUBHEADER_SHORT);
uint8_t *mark = mac_ce;
// Could move to nr_get_sdu()
NR_BSR_LONG *ceLong = (NR_BSR_LONG *)mac_ce;
*ceLong = (NR_BSR_LONG){0};
mac_ce += sizeof(NR_BSR_LONG);
// int NR_BSR_LONG_SIZE = 1;
if (bsr->bsr.lcg_bsr[0] && mac_ce < mac_ce_end) {
ceLong->LcgID0 = 1;
*mac_ce++ = bsr->bsr.lcg_bsr[0];
}
if (bsr->bsr.lcg_bsr[1] && mac_ce < mac_ce_end) {
ceLong->LcgID1 = 1;
*mac_ce++ = bsr->bsr.lcg_bsr[1];
}
if (bsr->bsr.lcg_bsr[2] && mac_ce < mac_ce_end) {
ceLong->LcgID2 = 1;
*mac_ce++ = bsr->bsr.lcg_bsr[2];
}
if (bsr->bsr.lcg_bsr[3] && mac_ce < mac_ce_end) {
ceLong->LcgID3 = 1;
*mac_ce++ = bsr->bsr.lcg_bsr[3];
}
if (bsr->bsr.lcg_bsr[4] && mac_ce < mac_ce_end) {
ceLong->LcgID4 = 1;
*mac_ce++ = bsr->bsr.lcg_bsr[4];
}
if (bsr->bsr.lcg_bsr[5] && mac_ce < mac_ce_end) {
ceLong->LcgID5 = 1;
*mac_ce++ = bsr->bsr.lcg_bsr[5];
}
if (bsr->bsr.lcg_bsr[6] && mac_ce < mac_ce_end) {
ceLong->LcgID6 = 1;
*mac_ce++ = bsr->bsr.lcg_bsr[6];
}
if (bsr->bsr.lcg_bsr[7] && mac_ce < mac_ce_end) {
ceLong->LcgID7 = 1;
*mac_ce++ = bsr->bsr.lcg_bsr[7];
}
*mac_pdu_subheader_ptr =
(NR_MAC_SUBHEADER_SHORT){.LCID = bsr->type_bsr == b_long ? UL_SCH_LCID_L_BSR : UL_SCH_LCID_L_TRUNCATED_BSR,
.L = mac_ce - mark};
LOG_D(NR_MAC,
"[UE] Generating ULSCH PDU : long_bsr size %d Lcgbit 0x%02x Buffer_size %d %d %d %d %d %d %d %d\n",
((NR_MAC_SUBHEADER_SHORT *)mac_pdu_subheader_ptr)->L,
*mac_ce,
bsr->bsr.lcg_bsr[0],
bsr->bsr.lcg_bsr[1],
bsr->bsr.lcg_bsr[2],
bsr->bsr.lcg_bsr[3],
bsr->bsr.lcg_bsr[4],
bsr->bsr.lcg_bsr[5],
bsr->bsr.lcg_bsr[6],
bsr->bsr.lcg_bsr[7]);
} break;
case b_none:
break;
default:
DevAssert(false);
}
return mac_ce - pdu;
}
void nr_ue_send_sdu(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *dl_info, int pdu_id)
{
LOG_D(NR_MAC,
"In [%d.%d] Handling DLSCH PDU type %d\n",
dl_info->frame,
dl_info->slot,
dl_info->rx_ind->rx_indication_body[pdu_id].pdu_type);
// Processing MAC PDU
// it parses MAC CEs subheaders, MAC CEs, SDU subheaderds and SDUs
switch (dl_info->rx_ind->rx_indication_body[pdu_id].pdu_type) {
case FAPI_NR_RX_PDU_TYPE_DLSCH :
// start or restart dataInactivityTimer if any MAC entity receives a MAC SDU for DTCH logical channel,
// DCCH logical channel, or CCCH logical channel
if (mac->data_inactivity_timer)
nr_timer_start(mac->data_inactivity_timer);
// DL data arrival during RRC_CONNECTED when UL synchronisation status is "non-synchronised"
if (!nr_timer_is_active(&mac->time_alignment_timer) && mac->state == UE_CONNECTED && !get_softmodem_params()->phy_test) {
trigger_MAC_UE_RA(mac, NULL);
break;
}
nr_ue_process_mac_pdu(mac, dl_info, pdu_id);
break;
case FAPI_NR_RX_PDU_TYPE_RAR :
nr_ue_process_rar(mac, dl_info, pdu_id);
break;
default :
AssertFatal(false, "Invalid DLSCH PDU type\n");
}
}