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

4094 lines
173 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.
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
/* \file ue_procedures.c
* \brief procedures related to UE
* \author R. Knopp, K.H. HSU, G. Casati
* \date 2018
* \version 0.1
* \company Eurecom / NTUST
* \email: knopp@eurecom.fr, kai-hsiang.hsu@eurecom.fr, guido.casati@iis.fraunhofer.de
* \note
* \warning
*/
#include <stdio.h>
#include <math.h>
/* exe */
#include "executables/nr-softmodem.h"
/* RRC*/
#include "RRC/NR_UE/rrc_proto.h"
/* MAC */
#include "NR_MAC_COMMON/nr_mac.h"
#include "NR_MAC_UE/mac_proto.h"
#include "NR_MAC_UE/mac_extern.h"
#include "NR_MAC_COMMON/nr_mac_extern.h"
#include "common/utils/nr/nr_common.h"
#include "openair2/NR_UE_PHY_INTERFACE/NR_Packet_Drop.h"
/* PHY */
#include "executables/softmodem-common.h"
#include "openair1/PHY/defs_nr_UE.h"
/* utils */
#include "assertions.h"
#include "oai_asn1.h"
#include "common/utils/LOG/log.h"
#include "common/utils/LOG/vcd_signal_dumper.h"
// #define DEBUG_MIB
// #define ENABLE_MAC_PAYLOAD_DEBUG 1
// #define DEBUG_RAR
extern uint32_t N_RB_DL;
/* 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 };
/* \brief Function called by PHY to process the received RAR and check that the preamble matches what was sent by the gNB. It
provides the timing advance and t-CRNTI.
@param Mod_id Index of UE instance
@param CC_id Index to a component carrier
@param frame Frame index
@param ra_rnti RA_RNTI value
@param dlsch_buffer Pointer to dlsch_buffer containing RAR PDU
@param t_crnti Pointer to PHY variable containing the T_CRNTI
@param preamble_index Preamble Index used by PHY to transmit the PRACH. This should match the received RAR to trigger the rest of
random-access procedure
@param selected_rar_buffer the output buffer for storing the selected RAR header and RAR payload
@returns timing advance or 0xffff if preamble doesn't match
*/
static void nr_ue_process_rar(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *dl_info, int pdu_id);
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 } },
};
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);
static int get_NTN_UE_Koffset(NR_NTN_Config_r17_t *ntn_Config_r17, int scs)
{
if (ntn_Config_r17 && ntn_Config_r17->cellSpecificKoffset_r17)
return *ntn_Config_r17->cellSpecificKoffset_r17 << scs;
return 0;
}
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->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 == FR2) {
for (int i = 0; i < 3; i++)
mac->mib_ssb += (((extra_bits >> (7 - i)) & 0x01) << (3 + i));
} else{
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;
if (mac->first_sync_frame == -1)
mac->first_sync_frame = frame;
if(get_softmodem_params()->phy_test)
mac->state = UE_CONNECTED;
else if(mac->state == UE_NOT_SYNC)
mac->state = UE_SYNC;
}
static void configure_ratematching_csi(fapi_nr_dl_config_dlsch_pdu_rel15_t *dlsch_pdu,
fapi_nr_dl_config_request_t *dl_config,
int rnti_type,
int frame,
int slot,
int mu,
NR_PDSCH_Config_t *pdsch_config)
{
// 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) {
for (int i = 0; i < pdsch_config->zp_CSI_RS_ResourceToAddModList->list.count; i++) {
NR_ZP_CSI_RS_Resource_t *zp_res = pdsch_config->zp_CSI_RS_ResourceToAddModList->list.array[i];
NR_ZP_CSI_RS_ResourceId_t id = zp_res->zp_CSI_RS_ResourceId;
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");
bool found = false;
for (int j = 0; j < zp_set->choice.setup->zp_CSI_RS_ResourceIdList.list.count; j++) {
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 * nr_slots_per_frame[mu] + 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++;
}
}
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
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++;
}
}
}
int8_t nr_ue_decode_BCCH_DL_SCH(NR_UE_MAC_INST_t *mac,
int cc_id,
unsigned int gNB_index,
uint8_t ack_nack,
uint8_t *pduP,
uint32_t pdu_len)
{
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, cc_id, gNB_index, 0, 0, 0, mac->physCellId, 0, NR_BCCH_DL_SCH, (uint8_t *) pduP, pdu_len);
mac->get_sib1 = false;
mac->get_otherSI = false;
}
else
LOG_E(NR_MAC, "Got NACK on NR-BCCH-DL-SCH-Message (%s)\n", mac->get_sib1 ? "SIB1" : "other SI");
return 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
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);
dlsch_config_pdu->number_rbs = count_bits(dlsch_config_pdu->rb_bitmap, sizeofArray(dlsch_config_pdu->rb_bitmap));
// Temporary code to process type0 as type1 when the RB allocation is contiguous
int state = 0;
for (int i = 0; i < sizeof(dlsch_config_pdu->rb_bitmap) * 8; i++) {
int allocated = dlsch_config_pdu->rb_bitmap[i / 8] & (1 << (i % 8));
if (allocated) {
if (state == 0) {
dlsch_config_pdu->start_rb = i;
state = 1;
} else
AssertFatal(state == 1, "non-contiguous RB allocation in RB allocation type 0 not implemented");
} else {
if (state == 1) {
state = 2;
}
}
}
}
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;
}
LOG_D(MAC,"ULSCH riv = %i\n", riv);
LOG_D(MAC,"ULSCH n_RB_DLBWP = %i\n", n_RB_ULBWP);
LOG_D(MAC,"ULSCH number_rbs = %i\n", pusch_config_pdu->rb_size);
LOG_D(MAC,"ULSCH start_rb = %i\n", pusch_config_pdu->rb_start);
}
return 0;
}
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_Koffset(mac->sc_info.ntn_Config_r17, 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;
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_Koffset(mac->sc_info.ntn_Config_r17, 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);
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 (mac->ra.RA_window_cnt >= 0 && rnti_type == TYPE_RA_RNTI_) {
dl_conf_req->pdu_type = FAPI_NR_DL_CONFIG_TYPE_RA_DLSCH;
} else {
dl_conf_req->pdu_type = FAPI_NR_DL_CONFIG_TYPE_DLSCH;
}
}
dlsch_pdu->numCsiRsForRateMatching = 0;
configure_ratematching_csi(dlsch_pdu, dl_config, rnti_type, frame, slot, dlsch_pdu->SubcarrierSpacing, pdsch_config);
/* 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->rb_offset = dlsch_pdu->start_rb + dlsch_pdu->BWPStart;
if (mac->get_sib1)
dlsch_pdu->rb_offset -= dlsch_pdu->BWPStart;
/* 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;
struct NR_DMRS_DownlinkConfig *dl_dmrs_config = NULL;
if (pdsch_config)
dl_dmrs_config = (tda_info.mapping_type == typeA) ? pdsch_config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup
: 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_HARQ_STATUS_t *current_harq = &mac->dl_harq_info[dci->harq_pid];
/* 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;
/* 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_Koffset(mac->sc_info.ntn_Config_r17, dlsch_pdu->SubcarrierSpacing);
const uint16_t feedback_ti = 1 + dci->pdsch_to_harq_feedback_timing_indicator.val + ntn_ue_koffset;
if (rnti_type != TYPE_RA_RNTI_ && rnti_type != TYPE_SI_RNTI_) {
AssertFatal(feedback_ti > DURATION_RX_TO_TX,
"PDSCH to HARQ feedback time (%d) needs to be higher than DURATION_RX_TO_TX (%d).\n",
feedback_ti,
DURATION_RX_TO_TX);
// 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,
tpc[dci->tpc],
feedback_ti,
dci->dai[0].val,
dci_ind->n_CCE,
dci_ind->N_CCE,
frame,
slot);
}
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 inline uint16_t packBits(const uint8_t *toPack, const int nb)
{
int res = 0;
for (int i = 0; i < nb; i++)
res += (*toPack++) << i;
return res;
}
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;
configure_ratematching_csi(dlsch_pdu, dl_config, rnti_type, frame, slot, current_DL_BWP->scs, pdsch_Config);
/* 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->rb_offset = dlsch_pdu->start_rb + dlsch_pdu->BWPStart;
/* 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;
struct NR_DMRS_DownlinkConfig *dl_dmrs_config = (tda_info.mapping_type == typeA)
? pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup
: 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_HARQ_STATUS_t *current_harq = &mac->dl_harq_info[dci->harq_pid];
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;
/* 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
const int ant = dci->antenna_ports.val;
if (dmrs_type == NULL) {
if (max_length == NULL) {
// Table 7.3.1.2.2-1: Antenna port(s) (1000 + DMRS port), dmrs-Type=1, maxLength=1
dlsch_pdu->n_dmrs_cdm_groups = table_7_3_2_3_3_1[ant][0];
dlsch_pdu->dmrs_ports = packBits(&table_7_3_2_3_3_1[ant][1], 4);
} else {
// Table 7.3.1.2.2-2: Antenna port(s) (1000 + DMRS port), dmrs-Type=1, maxLength=2
if (n_codewords == 1) {
dlsch_pdu->n_dmrs_cdm_groups = table_7_3_2_3_3_2_oneCodeword[ant][0];
dlsch_pdu->dmrs_ports = packBits(&table_7_3_2_3_3_2_oneCodeword[ant][1], 8);
dlsch_pdu->n_front_load_symb = table_7_3_2_3_3_2_oneCodeword[ant][9];
}
if (n_codewords == 2) {
dlsch_pdu->n_dmrs_cdm_groups = table_7_3_2_3_3_2_twoCodeword[ant][0];
dlsch_pdu->dmrs_ports = packBits(&table_7_3_2_3_3_2_twoCodeword[ant][1], 8);
dlsch_pdu->n_front_load_symb = table_7_3_2_3_3_2_twoCodeword[ant][9];
}
}
} else {
if (max_length == NULL) {
// Table 7.3.1.2.2-3: Antenna port(s) (1000 + DMRS port), dmrs-Type=2, maxLength=1
if (n_codewords == 1) {
dlsch_pdu->n_dmrs_cdm_groups = table_7_3_2_3_3_3_oneCodeword[ant][0];
dlsch_pdu->dmrs_ports = packBits(&table_7_3_2_3_3_3_oneCodeword[ant][1], 6);
}
if (n_codewords == 2) {
dlsch_pdu->n_dmrs_cdm_groups = table_7_3_2_3_3_3_twoCodeword[ant][0];
dlsch_pdu->dmrs_ports = packBits(&table_7_3_2_3_3_3_twoCodeword[ant][1], 6);
}
} else {
// Table 7.3.1.2.2-4: Antenna port(s) (1000 + DMRS port), dmrs-Type=2, maxLength=2
if (n_codewords == 1) {
dlsch_pdu->n_dmrs_cdm_groups = table_7_3_2_3_3_4_oneCodeword[ant][0];
dlsch_pdu->dmrs_ports = packBits(&table_7_3_2_3_3_4_oneCodeword[ant][1], 12);
dlsch_pdu->n_front_load_symb = table_7_3_2_3_3_4_oneCodeword[ant][13];
}
if (n_codewords == 2) {
dlsch_pdu->n_dmrs_cdm_groups = table_7_3_2_3_3_4_twoCodeword[ant][0];
dlsch_pdu->dmrs_ports = packBits(&table_7_3_2_3_3_4_twoCodeword[ant][1], 12);
dlsch_pdu->n_front_load_symb = table_7_3_2_3_3_4_twoCodeword[ant][13];
}
}
}
/* 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_Koffset(mac->sc_info.ntn_Config_r17, dlsch_pdu->SubcarrierSpacing);
const uint16_t feedback_ti = pucch_Config->dl_DataToUL_ACK->list.array[dci->pdsch_to_harq_feedback_timing_indicator.val][0] + ntn_ue_koffset;
AssertFatal(feedback_ti > DURATION_RX_TO_TX,
"PDSCH to HARQ feedback time (%d) needs to be higher than DURATION_RX_TO_TX (%d). Min feedback time set in config "
"file (min_rxtxtime).\n",
feedback_ti,
DURATION_RX_TO_TX);
// 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,
tpc[dci->tpc],
feedback_ti,
dci->dai[0].val,
dci_ind->n_CCE,
dci_ind->N_CCE,
frame,
slot);
// 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 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,
dlsch_pdu->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 (pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS != NULL) {
bool valid_ptrs_setup =
set_dl_ptrs_values(pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup,
dlsch_pdu->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;
}
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;
dci_pdu_rel15_t *def_dci_pdu_rel15 = &mac->def_dci_pdu_rel15[slot][format];
int ret = nr_ue_process_dci(mac, frame, slot, def_dci_pdu_rel15, dci, format);
if (ret < 0)
return NR_DCI_NONE;
return format;
}
int8_t nr_ue_process_csirs_measurements(NR_UE_MAC_INST_t *mac,
frame_t frame,
int slot,
fapi_nr_csirs_measurements_t *csirs_measurements) {
LOG_D(NR_MAC,"(%d.%d) Received CSI-RS measurements\n", frame, slot);
memcpy(&mac->csirs_measurements, csirs_measurements, sizeof(*csirs_measurements));
return 0;
}
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_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 scs = mac->current_DL_BWP ? mac->current_DL_BWP->scs : get_softmodem_params()->numerology;
int slots_per_frame = nr_slots_per_frame[scs];
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;
for (int i = 0; i < NR_MAX_HARQ_PROCESSES; i++) {
// looking for other active HARQ processes with feedback in the same frame/slot
if (i == harq_id)
continue;
NR_UE_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);
}
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 / (nr_slots_per_frame[scs]/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 int idx = *current_UL_BWP->pucch_ConfigCommon->pucch_ResourceCommon;
const initial_pucch_resource_t pucch_resourcecommon = initial_pucch_resource[idx];
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->initial_pucch_id == 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->n_csi;
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,
n_uci,
pucch_Config->format2->choice.setup->maxCodeRate,
2,
pucchres->format.choice.format2->nrofSymbols,
8);
pucch_pdu->payload = (pucch->csi_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,
n_uci,
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_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_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].n_csi > 0) {
AssertFatal(res[i].n_csi == 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].n_csi = res[i + 1].n_csi;
res[i].csi_part1_payload = res[i + 1].csi_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].n_csi > 0) {
AssertFatal(res[i + 1].n_csi == 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].n_csi = res[i].n_csi;
res[i + 1].csi_part1_payload = res[i].csi_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)
{
/* 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;
}
/*******************************************************************
*
* 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;
}
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 < NR_MAX_HARQ_PROCESSES; dl_harq_pid++) {
NR_UE_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 (get_softmodem_params()->emulate_l1) {
mac->nr_ue_emul_l1.harq[dl_harq_pid].active = true;
mac->nr_ue_emul_l1.harq[dl_harq_pid].active_dl_harq_sfn = frame;
mac->nr_ue_emul_l1.harq[dl_harq_pid].active_dl_harq_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_D(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);
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);
}
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 = nr_slots_per_frame[current_UL_BWP->scs];
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 ? true : false;
}
int8_t 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\n",
mac->ue_id,
frame,
slot,
sr_info->pending ? "pending" : "not pending",
sr_id); // todo
// TODO check if the PUCCH resource for the SR transmission occasion does not overlap with a UL-SCH resource
if (!sr_info->pending || is_nr_timer_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;
}
// 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, PUCCH_sched_t *pucch)
{
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;
int num_csi = 0;
if (mac->sc_info.csi_MeasConfig) {
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];
if(csirep->reportConfigType.present == NR_CSI_ReportConfig__reportConfigType_PR_periodic) {
int period, offset;
csi_period_offset(csirep, NULL, &period, &offset);
const int n_slots_frame = nr_slots_per_frame[current_UL_BWP->scs];
if (((n_slots_frame*frame + slot - offset)%period) == 0 && pucch_Config) {
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;
}
NR_PUCCH_Resource_t *csi_pucch = find_pucch_resource_from_list(pucch_Config->resourceToAddModList, csi_res_id);
AssertFatal(csi_pucch != NULL, "Couldn't find PUCCH Resource ID for SR 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_t csi = nr_get_csi_payload(mac, csi_report_id, WIDEBAND_ON_PUCCH, csi_measconfig);
pucch->n_csi = csi.p1_bits;
pucch->csi_part1_payload = csi.part1_payload;
pucch->pucch_resource = csi_pucch;
} else
continue;
} else {
num_csi = 1;
csi_priority = temp_priority;
csi_payload_t csi = nr_get_csi_payload(mac, csi_report_id, WIDEBAND_ON_PUCCH, csi_measconfig);
pucch->n_csi = csi.p1_bits;
pucch->csi_part1_payload = csi.part1_payload;
pucch->pucch_resource = csi_pucch;
}
}
}
else
AssertFatal(csirep->reportConfigType.present == NR_CSI_ReportConfig__reportConfigType_PR_aperiodic,
"Not supported CSI report type\n");
}
}
return num_csi;
}
csi_payload_t nr_get_csi_payload(NR_UE_MAC_INST_t *mac,
int csi_report_id,
CSI_mapping_t mapping_type,
NR_CSI_MeasConfig_t *csi_MeasConfig)
{
AssertFatal(csi_MeasConfig->csi_ReportConfigToAddModList->list.count > 0,"No CSI Report configuration available\n");
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;
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;
}
csi_payload_t get_ssb_rsrp_payload(NR_UE_MAC_INST_t *mac,
struct NR_CSI_ReportConfig *csi_reportconfig,
NR_CSI_ResourceConfigId_t csi_ResourceConfigId,
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;
uint32_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;
struct NR_CSI_SSB_ResourceSet__csi_SSB_ResourceList SSB_resource;
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==1,"PHY currently reports only the strongest SSB to MAC. Can't report more than 1 RSRP\n");
int ssbri_bits = ceil(log2(nb_ssb));
int ssb_rsrp[2][nb_meas]; // the array contains index and RSRP of each SSB to be reported (nb_meas highest RSRPs)
memset(ssb_rsrp, 0, sizeof(ssb_rsrp));
//TODO replace the following 2 lines with a function to order the nb_meas highest SSB RSRPs
for (int i=0; i<nb_ssb; i++) {
if(*SSB_resource.list.array[i] == mac->mib_ssb) {
ssb_rsrp[0][0] = i;
break;
}
}
AssertFatal(*SSB_resource.list.array[ssb_rsrp[0][0]] == mac->mib_ssb, "Couldn't find corresponding SSB in csi_SSB_ResourceList\n");
ssb_rsrp[1][0] = mac->ssb_measurements.ssb_rsrp_dBm;
uint8_t ssbi;
if (ssbri_bits > 0) {
ssbi = ssb_rsrp[0][0];
temp_payload = reverse_bits(ssbi, ssbri_bits);
bits += ssbri_bits;
}
uint8_t rsrp_idx = get_rsrp_index(ssb_rsrp[1][0]);
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++){
ssbi = ssb_rsrp[0][i];
temp_payload = reverse_bits(ssbi, ssbri_bits);
bits += ssbri_bits;
rsrp_idx = get_rsrp_diff_index(ssb_rsrp[1][0],ssb_rsrp[1][i]);
temp_payload |= (reverse_bits(rsrp_idx, 4) << bits);
bits += 4; // 7 bits for highest RSRP
}
break; // resorce found
}
}
AssertFatal(bits <= 32, "Not supporting CSI report with more than 32 bits\n");
csi_payload_t csi = {.part1_payload = temp_payload, .p1_bits = bits, csi.p2_bits = 0};
return csi;
}
csi_payload_t get_csirs_RI_PMI_CQI_payload(NR_UE_MAC_INST_t *mac,
struct NR_CSI_ReportConfig *csi_reportconfig,
NR_CSI_ResourceConfigId_t csi_ResourceConfigId,
NR_CSI_MeasConfig_t *csi_MeasConfig,
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.rank_indicator];
int pmi_x2_bitlen = csi_report->csi_meas_bitlen.pmi_x2_bitlen[mac->csirs_measurements.rank_indicator];
int cqi_bitlen = csi_report->csi_meas_bitlen.cqi_bitlen[mac->csirs_measurements.rank_indicator];
if (get_softmodem_params()->emulate_l1) {
static const uint8_t mcs_to_cqi[] = {0, 1, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,
10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15};
CHECK_INDEX(nr_bler_data, NR_NUM_MCS - 1);
int mcs = get_mcs_from_sinr(nr_bler_data, (mac->nr_ue_emul_l1.cqi - 640) * 0.1);
CHECK_INDEX(mcs_to_cqi, mcs);
mac->csirs_measurements.rank_indicator = mac->nr_ue_emul_l1.ri;
mac->csirs_measurements.i1 = mac->nr_ue_emul_l1.pmi;
mac->csirs_measurements.cqi = mcs_to_cqi[mcs];
}
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.rank_indicator << 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.rank_indicator << (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");
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;
}
csi_payload_t get_csirs_RSRP_payload(NR_UE_MAC_INST_t *mac,
struct NR_CSI_ReportConfig *csi_reportconfig,
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
// TS 38.133 - Table 10.1.6.1-1
int rsrp_dBm = mac->csirs_measurements.rsrp_dBm;
if (rsrp_dBm < -140) {
temp_payload = 16;
} else if (rsrp_dBm > -44) {
temp_payload = 113;
} else {
temp_payload = mac->csirs_measurements.rsrp_dBm + 157;
}
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");
csi_payload_t csi = {.part1_payload = temp_payload, .p1_bits = n_bits, csi.p2_bits = 0};
return csi;
}
// returns index from RSRP
// according to Table 10.1.6.1-1 in 38.133
uint8_t get_rsrp_index(int rsrp) {
int index = rsrp + 157;
if (rsrp>-44)
index = 113;
if (rsrp<-140)
index = 16;
return index;
}
// returns index from differential RSRP
// according to Table 10.1.6.1-2 in 38.133
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);
}
void nr_ue_send_sdu(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *dl_info, int pdu_id)
{
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SEND_SDU, VCD_FUNCTION_IN);
LOG_D(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:
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:
break;
}
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SEND_SDU, VCD_FUNCTION_OUT);
}
// #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)
{
const int mask[] = {0, 1, 3, 7, 0xf, 0x1f, 0x3f, 0x7f, 0xff, 0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff, 0x7fff, 0xffff};
uint64_t *tmp = (uint64_t *)dci;
*start -= length;
return *tmp >> *start & mask[length];
}
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 void 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);
}
static void extract_10_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 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);
} // end if
else {
// 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 4bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->harq_pid, 4);
// 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);
} // end else
}
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, 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, 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 1_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, 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 4bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->harq_pid, 4);
// 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 4bit
EXTRACT_DCI_ITEM(dci_pdu_rel15->harq_pid, 4);
// 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;
extract_10_si_rnti(dci_pdu_rel15, dci_pdu, pos, n_RB);
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;
extract_10_c_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_c_rnti(dci_pdu_rel15, dci_pdu, pos);
}
dci_pdu_rel15->format_indicator = format_indicator;
break;
case TYPE_TC_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;
}
///////////////////////////////////
// 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.
////////////////////////////////
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;
uint8_t *pduP = (dl_info->rx_ind->rx_indication_body + pdu_id)->pdsch_pdu.pdu;
int32_t pdu_len = (int32_t)(dl_info->rx_ind->rx_indication_body + pdu_id)->pdsch_pdu.pdu_length;
uint8_t gNB_index = dl_info->gNB_index;
uint8_t CC_id = dl_info->cc_id;
uint8_t done = 0;
RA_config_t *ra = &mac->ra;
if (!pduP){
return;
}
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);
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: ");
mac_rlc_data_ind(mac->ue_id,
mac->ue_id,
gNB_index,
frameP,
ENB_FLAG_NO,
MBMS_FLAG_NO,
0,
(char *)(pduP + mac_subheader_len),
mac_len,
1,
NULL);
}
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;
/*uint8_t ta_command = ((NR_MAC_CE_TA *)pduP)[1].TA_COMMAND;
uint8_t tag_id = ((NR_MAC_CE_TA *)pduP)[1].TAGID;*/
const int ta = ((NR_MAC_CE_TA *)pduP)[1].TA_COMMAND;
const int tag = ((NR_MAC_CE_TA *)pduP)[1].TAGID;
NR_UL_TIME_ALIGNMENT_t *ul_time_alignment = &mac->ul_time_alignment;
ul_time_alignment->tag_id = tag;
ul_time_alignment->ta_command = ta;
ul_time_alignment->ta_apply = adjustment_ta;
const int ntn_ue_koffset = get_NTN_UE_Koffset(mac->sc_info.ntn_Config_r17, mac->current_UL_BWP->scs);
const int n_slots_frame = nr_slots_per_frame[mac->current_UL_BWP->scs];
ul_time_alignment->frame = (frameP + (slot + ntn_ue_koffset) / n_slots_frame) % MAX_FRAME_NUMBER;
ul_time_alignment->slot = (slot + ntn_ue_koffset) % n_slots_frame;
/*
#ifdef DEBUG_HEADER_PARSING
LOG_D(MAC, "[UE] CE %d : UE Timing Advance : %d\n", i, pduP[1]);
#endif
*/
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_I(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]);
bool ra_success = true;
if (!IS_SOFTMODEM_IQPLAYER) { // Control is bypassed when replaying IQs (BMC)
for(int i = 0; i < mac_len; i++) {
if(ra->cont_res_id[i] != pduP[i + 1]) {
ra_success = false;
break;
}
}
}
if (ra->RA_active && ra_success) {
nr_ra_succeeded(mac, gNB_index, frameP, slot);
} else if (!ra_success) {
// TODO: Handle failure of RA procedure @ MAC layer
// nr_ra_failed(module_idP, CC_id, prach_resources, frameP, slot); // prach_resources is a PHY structure
ra->ra_state = nrRA_UE_IDLE;
ra->RA_active = false;
}
}
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))
return;
LOG_D(NR_MAC, "%4d.%2d : DLSCH -> LCID %d %d bytes\n", frameP, slot, rx_lcid, mac_len);
mac_rlc_data_ind(mac->ue_id,
mac->ue_id,
gNB_index,
frameP,
ENB_FLAG_NO,
MBMS_FLAG_NO,
rx_lcid,
(char *)(pduP + mac_subheader_len),
mac_len,
1,
NULL);
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);
}
}
/**
* Function: generating MAC CEs (MAC CE and subheader) for the ULSCH PDU
* Notes: TODO: PHR and BSR reporting
* Parameters:
* @mac_ce pointer to the MAC sub-PDUs including the MAC CEs
* @mac pointer to the MAC instance
* Return: number of written bytes
*/
int nr_write_ce_ulsch_pdu(uint8_t *mac_ce,
NR_UE_MAC_INST_t *mac,
uint8_t power_headroom, // todo: NR_POWER_HEADROOM_CMD *power_headroom,
uint16_t *crnti,
NR_BSR_SHORT *truncated_bsr,
NR_BSR_SHORT *short_bsr,
NR_BSR_LONG *long_bsr)
{
int mac_ce_len = 0;
uint8_t mac_ce_size = 0;
uint8_t *pdu = mac_ce;
if (power_headroom) {
// MAC CE fixed subheader
((NR_MAC_SUBHEADER_FIXED *) mac_ce)->R = 0;
((NR_MAC_SUBHEADER_FIXED *) mac_ce)->LCID = UL_SCH_LCID_SINGLE_ENTRY_PHR;
mac_ce++;
// PHR MAC CE (1 octet)
((NR_SINGLE_ENTRY_PHR_MAC_CE *) mac_ce)->PH = power_headroom;
((NR_SINGLE_ENTRY_PHR_MAC_CE *) mac_ce)->R1 = 0;
((NR_SINGLE_ENTRY_PHR_MAC_CE *) mac_ce)->PCMAX = 0; // todo
((NR_SINGLE_ENTRY_PHR_MAC_CE *) mac_ce)->R2 = 0;
// update pointer and length
mac_ce_size = sizeof(NR_SINGLE_ENTRY_PHR_MAC_CE);
mac_ce += mac_ce_size;
mac_ce_len += mac_ce_size + sizeof(NR_MAC_SUBHEADER_FIXED);
LOG_D(NR_MAC, "[UE] Generating ULSCH PDU : power_headroom pdu %p mac_ce %p b\n",
pdu, mac_ce);
}
if (crnti && (!get_softmodem_params()->sa && get_softmodem_params()->do_ra && mac->ra.ra_state != nrRA_SUCCEEDED)) {
LOG_D(NR_MAC, "In %s: generating C-RNTI MAC CE with C-RNTI %x\n", __FUNCTION__, (*crnti));
// MAC CE fixed subheader
((NR_MAC_SUBHEADER_FIXED *) mac_ce)->R = 0;
((NR_MAC_SUBHEADER_FIXED *) mac_ce)->LCID = UL_SCH_LCID_C_RNTI;
mac_ce++;
// C-RNTI MAC CE (2 octets)
memcpy(mac_ce, crnti, sizeof(*crnti));
// update pointer and length
mac_ce_size = sizeof(uint16_t);
mac_ce += mac_ce_size;
mac_ce_len += mac_ce_size + sizeof(NR_MAC_SUBHEADER_FIXED);
}
if (truncated_bsr) {
// MAC CE fixed subheader
((NR_MAC_SUBHEADER_FIXED *) mac_ce)->R = 0;
((NR_MAC_SUBHEADER_FIXED *) mac_ce)->LCID = UL_SCH_LCID_S_TRUNCATED_BSR;
mac_ce++;
// Short truncated BSR MAC CE (1 octet)
((NR_BSR_SHORT_TRUNCATED *) mac_ce)-> Buffer_size = truncated_bsr->Buffer_size;
((NR_BSR_SHORT_TRUNCATED *) mac_ce)-> LcgID = truncated_bsr->LcgID;;
// update pointer and length
mac_ce_size = sizeof(NR_BSR_SHORT_TRUNCATED);
mac_ce += mac_ce_size;
mac_ce_len += mac_ce_size + sizeof(NR_MAC_SUBHEADER_FIXED);
LOG_D(NR_MAC, "[UE] Generating ULSCH PDU : truncated_bsr Buffer_size %d LcgID %d pdu %p mac_ce %p\n",
truncated_bsr->Buffer_size, truncated_bsr->LcgID, pdu, mac_ce);
} else if (short_bsr) {
// MAC CE fixed subheader
((NR_MAC_SUBHEADER_FIXED *) mac_ce)->R = 0;
((NR_MAC_SUBHEADER_FIXED *) mac_ce)->LCID = UL_SCH_LCID_S_BSR;
mac_ce++;
// Short truncated BSR MAC CE (1 octet)
((NR_BSR_SHORT *) mac_ce)->Buffer_size = short_bsr->Buffer_size;
((NR_BSR_SHORT *) mac_ce)->LcgID = short_bsr->LcgID;
// update pointer and length
mac_ce_size = sizeof(NR_BSR_SHORT);
mac_ce += mac_ce_size;
mac_ce_len += mac_ce_size + sizeof(NR_MAC_SUBHEADER_FIXED);
LOG_D(NR_MAC, "[UE] Generating ULSCH PDU : short_bsr Buffer_size %d LcgID %d pdu %p mac_ce %p\n",
short_bsr->Buffer_size, short_bsr->LcgID, pdu, mac_ce);
} else if (long_bsr) {
// MAC CE variable subheader
// ch 6.1.3.1. TS 38.321
((NR_MAC_SUBHEADER_SHORT *) mac_ce)->R = 0;
((NR_MAC_SUBHEADER_SHORT *) mac_ce)->F = 0;
((NR_MAC_SUBHEADER_SHORT *) mac_ce)->LCID = UL_SCH_LCID_L_BSR;
NR_MAC_SUBHEADER_SHORT *mac_pdu_subheader_ptr = (NR_MAC_SUBHEADER_SHORT *) mac_ce;
mac_ce += 2;
// Could move to nr_get_sdu()
uint8_t *Buffer_size_ptr= (uint8_t*) mac_ce + 1;
//int NR_BSR_LONG_SIZE = 1;
if (long_bsr->Buffer_size0 == 0) {
((NR_BSR_LONG *) mac_ce)->LcgID0 = 0;
} else {
((NR_BSR_LONG *) mac_ce)->LcgID0 = 1;
*Buffer_size_ptr++ = long_bsr->Buffer_size0;
}
if (long_bsr->Buffer_size1 == 0) {
((NR_BSR_LONG *) mac_ce)->LcgID1 = 0;
} else {
((NR_BSR_LONG *) mac_ce)->LcgID1 = 1;
*Buffer_size_ptr++ = long_bsr->Buffer_size1;
}
if (long_bsr->Buffer_size2 == 0) {
((NR_BSR_LONG *) mac_ce)->LcgID2 = 0;
} else {
((NR_BSR_LONG *) mac_ce)->LcgID2 = 1;
*Buffer_size_ptr++ = long_bsr->Buffer_size2;
}
if (long_bsr->Buffer_size3 == 0) {
((NR_BSR_LONG *) mac_ce)->LcgID3 = 0;
} else {
((NR_BSR_LONG *) mac_ce)->LcgID3 = 1;
*Buffer_size_ptr++ = long_bsr->Buffer_size3;
}
if (long_bsr->Buffer_size4 == 0) {
((NR_BSR_LONG *) mac_ce)->LcgID4 = 0;
} else {
((NR_BSR_LONG *) mac_ce)->LcgID4 = 1;
*Buffer_size_ptr++ = long_bsr->Buffer_size4;
}
if (long_bsr->Buffer_size5 == 0) {
((NR_BSR_LONG *) mac_ce)->LcgID5 = 0;
} else {
((NR_BSR_LONG *) mac_ce)->LcgID5 = 1;
*Buffer_size_ptr++ = long_bsr->Buffer_size5;
}
if (long_bsr->Buffer_size6 == 0) {
((NR_BSR_LONG *) mac_ce)->LcgID6 = 0;
} else {
((NR_BSR_LONG *) mac_ce)->LcgID6 = 1;
*Buffer_size_ptr++ = long_bsr->Buffer_size6;
}
if (long_bsr->Buffer_size7 == 0) {
((NR_BSR_LONG *) mac_ce)->LcgID7 = 0;
} else {
((NR_BSR_LONG *) mac_ce)->LcgID7 = 1;
*Buffer_size_ptr++ = long_bsr->Buffer_size7;
}
((NR_MAC_SUBHEADER_SHORT *) mac_pdu_subheader_ptr)->L = mac_ce_size = (uint8_t*) Buffer_size_ptr - (uint8_t*) mac_ce;
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", mac_ce_size, *((uint8_t*) mac_ce),
((NR_BSR_LONG *) mac_ce)->Buffer_size0, ((NR_BSR_LONG *) mac_ce)->Buffer_size1, ((NR_BSR_LONG *) mac_ce)->Buffer_size2, ((NR_BSR_LONG *) mac_ce)->Buffer_size3,
((NR_BSR_LONG *) mac_ce)->Buffer_size4, ((NR_BSR_LONG *) mac_ce)->Buffer_size5, ((NR_BSR_LONG *) mac_ce)->Buffer_size6, ((NR_BSR_LONG *) mac_ce)->Buffer_size7);
mac_ce_len += mac_ce_size + sizeof(NR_MAC_SUBHEADER_SHORT);
}
return mac_ce_len;
}
/////////////////////////////////////
// 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;
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;
uint8_t is_Msg3 = 1;
frame_t frame_tx = 0;
int slot_tx = 0;
int ret = 0;
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;
LOG_D(NR_MAC, "[%d.%d]: [UE %d][RAPROC] invoking MAC for 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 {
ra->RA_backoff_indicator = table_7_2_1[((NR_RA_HEADER_BI *)rarh)->BI];
ra->RA_BI_found = 1;
LOG_I(NR_MAC, "[UE %d][RAPROC][RA-RNTI %04x] Got BI RAR subPDU %d ms\n", mac->ue_id, ra->RA_backoff_indicator, rnti);
if ( ((NR_RA_HEADER_BI *)rarh)->E == 1) {
rarh += sizeof(NR_RA_HEADER_BI);
continue;
} else {
break;
}
}
if (rarh->RAPID == preamble_index) {
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));
ra->RA_RAPID_found = 1;
if (get_softmodem_params()->emulate_l1) {
/* When we are emulating L1 with multiple UEs, the rx_indication will have
multiple RAR PDUs. The code would previously handle each of these PDUs,
but it should only be handling the single RAR that matches the current
UE. */
LOG_I(NR_MAC, "RAR PDU found for our UE with PDU index %d\n", pdu_id);
dl_info->rx_ind->number_pdus = 1;
if (pdu_id != 0) {
memcpy(&dl_info->rx_ind->rx_indication_body[0],
&dl_info->rx_ind->rx_indication_body[pdu_id],
sizeof(fapi_nr_rx_indication_body_t));
}
mac->nr_ue_emul_l1.expected_rar = false;
memset(mac->nr_ue_emul_l1.index_has_rar, 0, sizeof(mac->nr_ue_emul_l1.index_has_rar));
}
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);
break;
} else {
rarh += sizeof(NR_MAC_RAR) + 1;
}
}
#ifdef DEBUG_RAR
LOG_D(MAC, "[DEBUG_RAR] (%d,%d) number of RAR subheader %d; number of RAR pyloads %d\n",
frame,
slot,
n_subheaders,
n_subPDUs);
LOG_D(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
if (ra->RA_RAPID_found) {
RAR_grant_t rar_grant;
unsigned char tpc_command;
#ifdef DEBUG_RAR
unsigned char csi_req;
#endif
// TA command
NR_UL_TIME_ALIGNMENT_t *ul_time_alignment = &mac->ul_time_alignment;
const int ta = rar->TA2 + (rar->TA1 << 5);
ul_time_alignment->ta_command = ta;
ul_time_alignment->ta_apply = rar_ta;
LOG_W(MAC, "received TA command %d\n", 31 + ta);
#ifdef DEBUG_RAR
// CSI
csi_req = (unsigned char) (rar->UL_GRANT_4 & 0x01);
#endif
// TPC
tpc_command = (unsigned char) ((rar->UL_GRANT_4 >> 1) & 0x07);
switch (tpc_command){
case 0:
ra->Msg3_TPC = -6;
break;
case 1:
ra->Msg3_TPC = -4;
break;
case 2:
ra->Msg3_TPC = -2;
break;
case 3:
ra->Msg3_TPC = 0;
break;
case 4:
ra->Msg3_TPC = 2;
break;
case 5:
ra->Msg3_TPC = 4;
break;
case 6:
ra->Msg3_TPC = 6;
break;
case 7:
ra->Msg3_TPC = 8;
break;
default:
LOG_E(NR_PHY, "RAR impossible msg3 TPC\n");
}
// 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);
#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
// 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];
NR_tda_info_t tda_info = get_ul_tda_info(current_UL_BWP,
*pdcch_config->ra_SS->controlResourceSetId,
pdcch_config->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");
return;
}
const int ntn_ue_koffset = get_NTN_UE_Koffset(mac->sc_info.ntn_Config_r17, mac->current_UL_BWP->scs);
ret = nr_ue_pusch_scheduler(mac, is_Msg3, frame, slot, &frame_tx, &slot_tx, tda_info.k2 + ntn_ue_koffset);
const int n_slots_frame = nr_slots_per_frame[current_UL_BWP->scs];
ul_time_alignment->frame = (frame_tx + (slot_tx + ntn_ue_koffset) / n_slots_frame) % MAX_FRAME_NUMBER;
ul_time_alignment->slot = (slot_tx + ntn_ue_koffset) % n_slots_frame;
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 (!ra->msg3_C_RNTI)
nr_mac_rrc_msg3_ind(mac->ue_id, rnti, dl_info->gNB_index);
}
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);
}
} else {
ra->t_crnti = 0;
}
return;
}