Files
openairinterface5g/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c
T

782 lines
31 KiB
C

/*
* 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 gNB_scheduler_ulsch.c
* \brief gNB procedures for the ULSCH transport channel
* \author Navid Nikaein and Raymond Knopp, Guido Casati
* \date 2019
* \email: guido.casati@iis.fraunhofer.de
* \version 1.0
* @ingroup _mac
*/
#include "LAYER2/NR_MAC_gNB/mac_proto.h"
#include "executables/softmodem-common.h"
#include "common/utils/nr/nr_common.h"
void nr_process_mac_pdu(
module_id_t module_idP,
rnti_t rnti,
uint8_t CC_id,
frame_t frameP,
uint8_t *pduP,
uint16_t mac_pdu_len)
{
// This function is adapting code from the old
// parse_header(...) and ue_send_sdu(...) functions of OAI LTE
uint8_t *pdu_ptr = pduP, rx_lcid, done = 0;
int pdu_len = mac_pdu_len;
uint16_t mac_ce_len, mac_subheader_len, mac_sdu_len;
// For both DL/UL-SCH
// Except:
// - UL/DL-SCH: fixed-size MAC CE(known by LCID)
// - UL/DL-SCH: padding
// - UL-SCH: MSG3 48-bits
// |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 |
// For both DL/UL-SCH
// For:
// - UL/DL-SCH: fixed-size MAC CE(known by LCID)
// - UL/DL-SCH: padding, for single/multiple 1-oct padding CE(s)
// - UL-SCH: MSG3 48-bits
// |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.
while (!done && pdu_len > 0){
mac_ce_len = 0;
mac_subheader_len = 1; // default to fixed-length subheader = 1-oct
mac_sdu_len = 0;
rx_lcid = ((NR_MAC_SUBHEADER_FIXED *)pdu_ptr)->LCID;
LOG_D(MAC, "LCID received at gNB side: %d \n", rx_lcid);
switch(rx_lcid){
// MAC CE
/*#ifdef DEBUG_HEADER_PARSING
LOG_D(MAC, "[UE] LCID %d, PDU length %d\n", ((NR_MAC_SUBHEADER_FIXED *)pdu_ptr)->LCID, pdu_len);
#endif*/
case UL_SCH_LCID_RECOMMENDED_BITRATE_QUERY:
// 38.321 Ch6.1.3.20
mac_ce_len = 2;
break;
case UL_SCH_LCID_CONFIGURED_GRANT_CONFIRMATION:
// 38.321 Ch6.1.3.7
break;
case UL_SCH_LCID_S_BSR:
//38.321 section 6.1.3.1
//fixed length
mac_ce_len =1;
/* Extract short BSR value */
break;
case UL_SCH_LCID_S_TRUNCATED_BSR:
//38.321 section 6.1.3.1
//fixed length
mac_ce_len =1;
/* Extract short truncated BSR value */
break;
case UL_SCH_LCID_L_BSR:
//38.321 section 6.1.3.1
//variable length
mac_ce_len |= (uint16_t)((NR_MAC_SUBHEADER_SHORT *)pdu_ptr)->L;
mac_subheader_len = 2;
if(((NR_MAC_SUBHEADER_SHORT *)pdu_ptr)->F){
mac_ce_len |= (uint16_t)(((NR_MAC_SUBHEADER_LONG *)pdu_ptr)->L2)<<8;
mac_subheader_len = 3;
}
/* Extract long BSR value */
break;
case UL_SCH_LCID_L_TRUNCATED_BSR:
//38.321 section 6.1.3.1
//variable length
mac_ce_len |= (uint16_t)((NR_MAC_SUBHEADER_SHORT *)pdu_ptr)->L;
mac_subheader_len = 2;
if(((NR_MAC_SUBHEADER_SHORT *)pdu_ptr)->F){
mac_ce_len |= (uint16_t)(((NR_MAC_SUBHEADER_LONG *)pdu_ptr)->L2)<<8;
mac_subheader_len = 3;
}
/* Extract long truncated BSR value */
break;
case UL_SCH_LCID_C_RNTI:
//38.321 section 6.1.3.2
//fixed length
mac_ce_len = 2;
/* Extract CRNTI value */
break;
case UL_SCH_LCID_SINGLE_ENTRY_PHR:
//38.321 section 6.1.3.8
//fixed length
mac_ce_len = 2;
/* Extract SINGLE ENTRY PHR elements for PHR calculation */
break;
case UL_SCH_LCID_MULTI_ENTRY_PHR_1_OCT:
//38.321 section 6.1.3.9
// varialbe length
mac_ce_len |= (uint16_t)((NR_MAC_SUBHEADER_SHORT *)pdu_ptr)->L;
mac_subheader_len = 2;
if(((NR_MAC_SUBHEADER_SHORT *)pdu_ptr)->F){
mac_ce_len |= (uint16_t)(((NR_MAC_SUBHEADER_LONG *)pdu_ptr)->L2)<<8;
mac_subheader_len = 3;
}
/* Extract MULTI ENTRY PHR elements from single octet bitmap for PHR calculation */
break;
case UL_SCH_LCID_MULTI_ENTRY_PHR_4_OCT:
//38.321 section 6.1.3.9
// varialbe length
mac_ce_len |= (uint16_t)((NR_MAC_SUBHEADER_SHORT *)pdu_ptr)->L;
mac_subheader_len = 2;
if(((NR_MAC_SUBHEADER_SHORT *)pdu_ptr)->F){
mac_ce_len |= (uint16_t)(((NR_MAC_SUBHEADER_LONG *)pdu_ptr)->L2)<<8;
mac_subheader_len = 3;
}
/* Extract MULTI ENTRY PHR elements from four octets bitmap for PHR calculation */
break;
case UL_SCH_LCID_PADDING:
done = 1;
// end of MAC PDU, can ignore the rest.
break;
// MAC SDUs
case UL_SCH_LCID_SRB1:
// todo
break;
case UL_SCH_LCID_SRB2:
// todo
break;
case UL_SCH_LCID_SRB3:
// todo
break;
case UL_SCH_LCID_CCCH_MSG3:
// todo
break;
case UL_SCH_LCID_CCCH:
// todo
mac_subheader_len = 2;
break;
case UL_SCH_LCID_DTCH:
// check if LCID is valid at current time.
if(((NR_MAC_SUBHEADER_SHORT *)pdu_ptr)->F){
//mac_sdu_len |= (uint16_t)(((NR_MAC_SUBHEADER_LONG *)pdu_ptr)->L2)<<8;
mac_subheader_len = 3;
mac_sdu_len = ((uint16_t)(((NR_MAC_SUBHEADER_LONG *) pdu_ptr)->L1 & 0x7f) << 8)
| ((uint16_t)((NR_MAC_SUBHEADER_LONG *) pdu_ptr)->L2 & 0xff);
} else {
mac_sdu_len = (uint16_t)((NR_MAC_SUBHEADER_SHORT *)pdu_ptr)->L;
mac_subheader_len = 2;
}
LOG_D(MAC, "[UE %d] Frame %d : ULSCH -> UL-DTCH %d (gNB %d, %d bytes)\n", module_idP, frameP, rx_lcid, module_idP, mac_sdu_len);
int UE_id = find_nr_UE_id(module_idP, rnti);
RC.nrmac[module_idP]->UE_info.mac_stats[UE_id].lc_bytes_rx[rx_lcid] += mac_sdu_len;
#if defined(ENABLE_MAC_PAYLOAD_DEBUG)
log_dump(MAC, pdu_ptr + mac_subheader_len, 32, LOG_DUMP_CHAR, "\n");
#endif
if(IS_SOFTMODEM_NOS1){
mac_rlc_data_ind(module_idP,
0x1234,
module_idP,
frameP,
ENB_FLAG_YES,
MBMS_FLAG_NO,
rx_lcid,
(char *) (pdu_ptr + mac_subheader_len),
mac_sdu_len,
1,
NULL);
}
else{
mac_rlc_data_ind(module_idP,
rnti,
module_idP,
frameP,
ENB_FLAG_YES,
MBMS_FLAG_NO,
rx_lcid,
(char *) (pdu_ptr + mac_subheader_len),
mac_sdu_len,
1,
NULL);
}
break;
default:
return;
break;
}
pdu_ptr += ( mac_subheader_len + mac_ce_len + mac_sdu_len );
pdu_len -= ( mac_subheader_len + mac_ce_len + mac_sdu_len );
if (pdu_len < 0) {
LOG_E(MAC, "%s() residual mac pdu length < 0!, pdu_len: %d\n", __func__, pdu_len);
return;
}
}
}
void handle_nr_ul_harq(uint16_t slot, NR_UE_sched_ctrl_t *sched_ctrl, NR_mac_stats_t *stats, nfapi_nr_crc_t crc_pdu) {
int max_harq_rounds = 4; // TODO define macro
uint8_t hrq_id = crc_pdu.harq_id;
NR_UE_ul_harq_t *cur_harq = &sched_ctrl->ul_harq_processes[hrq_id];
if (cur_harq->state==ACTIVE_SCHED) {
if (!crc_pdu.tb_crc_status) {
cur_harq->ndi ^= 1;
cur_harq->round = 0;
cur_harq->state = INACTIVE; // passed -> make inactive. can be used by scheduder for next grant
#ifdef UL_HARQ_PRINT
printf("[HARQ HANDLER] Ulharq id %d crc passed, freeing it for scheduler\n",hrq_id);
#endif
} else {
cur_harq->round++;
cur_harq->state = ACTIVE_NOT_SCHED;
#ifdef UL_HARQ_PRINT
printf("[HARQ HANDLER] Ulharq id %d crc failed, requesting retransmission\n",hrq_id);
#endif
}
if (!(cur_harq->round<max_harq_rounds)) {
cur_harq->ndi ^= 1;
cur_harq->state = INACTIVE; // failed after 4 rounds -> make inactive
cur_harq->round = 0;
LOG_D(MAC,"[HARQ HANDLER] RNTI %x: Ulharq id %d crc failed in all round, freeing it for scheduler\n",crc_pdu.rnti,hrq_id);
stats->ulsch_errors++;
}
return;
} else
LOG_W(MAC,"Incorrect ULSCH HARQ process %d or invalid state %d (ignore this warning for RA)\n",hrq_id,cur_harq->state);
}
/*
* When data are received on PHY and transmitted to MAC
*/
void nr_rx_sdu(const module_id_t gnb_mod_idP,
const int CC_idP,
const frame_t frameP,
const sub_frame_t slotP,
const rnti_t rntiP,
uint8_t *sduP,
const uint16_t sdu_lenP,
const uint16_t timing_advance,
const uint8_t ul_cqi,
const uint16_t rssi){
int current_rnti = 0, UE_id = -1, harq_pid = 0;
gNB_MAC_INST *gNB_mac = NULL;
NR_UE_info_t *UE_info = NULL;
NR_UE_sched_ctrl_t *UE_scheduling_control = NULL;
current_rnti = rntiP;
UE_id = find_nr_UE_id(gnb_mod_idP, current_rnti);
gNB_mac = RC.nrmac[gnb_mod_idP];
UE_info = &gNB_mac->UE_info;
int target_snrx10 = gNB_mac->pusch_target_snrx10;
if (sduP != NULL) {
T(T_GNB_MAC_UL_PDU_WITH_DATA, T_INT(gnb_mod_idP), T_INT(CC_idP),
T_INT(rntiP), T_INT(frameP), T_INT(slotP), T_INT(-1) /* harq_pid */,
T_BUFFER(sduP, sdu_lenP));
}
if (UE_id != -1) {
UE_scheduling_control = &(UE_info->UE_sched_ctrl[UE_id]);
UE_info->mac_stats[UE_id].ulsch_total_bytes_rx += sdu_lenP;
LOG_D(MAC, "[gNB %d][PUSCH %d] CC_id %d %d.%d Received ULSCH sdu from PHY (rnti %x, UE_id %d) ul_cqi %d\n",
gnb_mod_idP,
harq_pid,
CC_idP,
frameP,
slotP,
current_rnti,
UE_id,
ul_cqi);
// if not missed detection (10dB threshold for now)
if (UE_scheduling_control->ul_rssi < (100+rssi)) {
UE_scheduling_control->tpc0 = nr_get_tpc(target_snrx10,ul_cqi,30);
if (timing_advance != 0xffff)
UE_scheduling_control->ta_update = timing_advance;
UE_scheduling_control->ul_rssi = rssi;
LOG_D(MAC, "[UE %d] PUSCH TPC %d and TA %d\n",UE_id,UE_scheduling_control->tpc0,UE_scheduling_control->ta_update);
}
else{
UE_scheduling_control->tpc0 = 1;
}
#if defined(ENABLE_MAC_PAYLOAD_DEBUG)
LOG_I(MAC, "Printing received UL MAC payload at gNB side: %d \n");
for (int i = 0; i < sdu_lenP ; i++) {
//harq_process_ul_ue->a[i] = (unsigned char) rand();
//printf("a[%d]=0x%02x\n",i,harq_process_ul_ue->a[i]);
printf("%02x ",(unsigned char)sduP[i]);
}
printf("\n");
#endif
if (sduP != NULL){
LOG_D(MAC, "Received PDU at MAC gNB \n");
nr_process_mac_pdu(gnb_mod_idP, current_rnti, CC_idP, frameP, sduP, sdu_lenP);
}
else {
}
} else {
if (!sduP) // check that CRC passed
return;
/* we don't know this UE (yet). Check whether there is a ongoing RA (Msg 3)
* and check the corresponding UE's RNTI match, in which case we activate
* it. */
for (int i = 0; i < NR_NB_RA_PROC_MAX; ++i) {
NR_RA_t *ra = &gNB_mac->common_channels[CC_idP].ra[i];
if (ra->state != WAIT_Msg3)
continue;
// random access pusch with TC-RNTI
if (ra->rnti != current_rnti) {
LOG_W(MAC,
"expected TC-RNTI %04x to match current RNTI %04x\n",
ra->rnti,
current_rnti);
continue;
}
const int UE_id = add_new_nr_ue(gnb_mod_idP, ra->rnti);
UE_info->secondaryCellGroup[UE_id] = ra->secondaryCellGroup;
compute_csi_bitlen(ra->secondaryCellGroup, UE_info, UE_id);
UE_info->UE_beam_index[UE_id] = ra->beam_id;
struct NR_ServingCellConfig__downlinkBWP_ToAddModList *bwpList = ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList;
AssertFatal(bwpList->list.count == 1,
"downlinkBWP_ToAddModList has %d BWP!\n",
bwpList->list.count);
const int bwp_id = 1;
UE_info->UE_sched_ctrl[UE_id].active_bwp = bwpList->list.array[bwp_id - 1];
struct NR_UplinkConfig__uplinkBWP_ToAddModList *ubwpList = ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList;
AssertFatal(ubwpList->list.count == 1,
"uplinkBWP_ToAddModList has %d BWP!\n",
ubwpList->list.count);
UE_info->UE_sched_ctrl[UE_id].active_ubwp = ubwpList->list.array[bwp_id - 1];
LOG_I(MAC,
"[gNB %d][RAPROC] PUSCH with TC_RNTI %x received correctly, "
"adding UE MAC Context UE_id %d/RNTI %04x\n",
gnb_mod_idP,
current_rnti,
UE_id,
ra->rnti);
// re-initialize ta update variables afrer RA procedure completion
UE_info->UE_sched_ctrl[UE_id].ta_frame = frameP;
free(ra->preambles.preamble_list);
ra->state = RA_IDLE;
LOG_I(MAC,
"reset RA state information for RA-RNTI %04x/index %d\n",
ra->rnti,
i);
return;
}
}
}
long get_K2(NR_BWP_Uplink_t *ubwp, int time_domain_assignment, int mu) {
DevAssert(ubwp);
const NR_PUSCH_TimeDomainResourceAllocation_t *tda_list = ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.array[time_domain_assignment];
if (tda_list->k2)
return *tda_list->k2;
else if (mu < 2)
return 1;
else if (mu == 2)
return 2;
else
return 3;
}
int8_t select_ul_harq_pid(NR_UE_sched_ctrl_t *sched_ctrl) {
const uint8_t max_ul_harq_pids = 3; // temp: for testing
// schedule active harq processes
for (uint8_t hrq_id = 0; hrq_id < max_ul_harq_pids; hrq_id++) {
NR_UE_ul_harq_t *cur_harq = &sched_ctrl->ul_harq_processes[hrq_id];
if (cur_harq->state == ACTIVE_NOT_SCHED) {
LOG_D(MAC, "Found ulharq id %d, scheduling it for retransmission\n", hrq_id);
return hrq_id;
}
}
// schedule new harq processes
for (uint8_t hrq_id=0; hrq_id < max_ul_harq_pids; hrq_id++) {
NR_UE_ul_harq_t *cur_harq = &sched_ctrl->ul_harq_processes[hrq_id];
if (cur_harq->state == INACTIVE) {
LOG_D(MAC, "Found new ulharq id %d, scheduling it\n", hrq_id);
return hrq_id;
}
}
LOG_E(MAC, "All UL HARQ processes are busy. Cannot schedule ULSCH\n");
return -1;
}
void nr_simple_ulsch_preprocessor(module_id_t module_id,
frame_t frame,
sub_frame_t slot,
int num_slots_per_tdd,
uint64_t ulsch_in_slot_bitmap) {
gNB_MAC_INST *nr_mac = RC.nrmac[module_id];
NR_COMMON_channels_t *cc = nr_mac->common_channels;
NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon;
const int mu = scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.subcarrierSpacing;
NR_UE_info_t *UE_info = &nr_mac->UE_info;
AssertFatal(UE_info->num_UEs <= 1,
"%s() cannot handle more than one UE, but found %d\n",
__func__,
UE_info->num_UEs);
if (UE_info->num_UEs == 0)
return;
const int UE_id = 0;
const int CC_id = 0;
NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id];
const int tda = 1;
const struct NR_PUSCH_TimeDomainResourceAllocationList *tdaList =
sched_ctrl->active_ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList;
AssertFatal(tda < tdaList->list.count,
"time domain assignment %d >= %d\n",
tda,
tdaList->list.count);
int K2 = get_K2(sched_ctrl->active_ubwp, tda, mu);
const int sched_frame = frame + (slot + K2 >= num_slots_per_tdd);
const int sched_slot = (slot + K2) % num_slots_per_tdd;
if (!is_xlsch_in_slot(ulsch_in_slot_bitmap, sched_slot))
return;
/* get first, largest unallocated region */
uint16_t *vrb_map_UL =
&RC.nrmac[module_id]->common_channels[CC_id].vrb_map_UL[sched_slot * 275];
uint16_t rbStart = 0;
while (vrb_map_UL[rbStart]) rbStart++;
const uint16_t bwpSize = NRRIV2BW(sched_ctrl->active_ubwp->bwp_Common->genericParameters.locationAndBandwidth,275);
uint16_t rbSize = 1;
while (rbStart + rbSize < bwpSize && !vrb_map_UL[rbStart+rbSize])
rbSize++;
sched_ctrl->sched_pusch.slot = sched_slot;
sched_ctrl->sched_pusch.frame = sched_frame;
const int target_ss = NR_SearchSpace__searchSpaceType_PR_ue_Specific;
sched_ctrl->search_space = get_searchspace(sched_ctrl->active_bwp, target_ss);
uint8_t nr_of_candidates;
find_aggregation_candidates(&sched_ctrl->aggregation_level,
&nr_of_candidates,
sched_ctrl->search_space);
sched_ctrl->coreset = get_coreset(
sched_ctrl->active_bwp, sched_ctrl->search_space, 1 /* dedicated */);
const int cid = sched_ctrl->coreset->controlResourceSetId;
const uint16_t Y = UE_info->Y[UE_id][cid][slot];
const int m = UE_info->num_pdcch_cand[UE_id][cid];
sched_ctrl->cce_index = allocate_nr_CCEs(RC.nrmac[module_id],
sched_ctrl->active_bwp,
sched_ctrl->coreset,
sched_ctrl->aggregation_level,
Y,
m,
nr_of_candidates);
if (sched_ctrl->cce_index < 0) {
LOG_E(MAC, "%s(): CCE list not empty, couldn't schedule PUSCH\n", __func__);
return;
}
UE_info->num_pdcch_cand[UE_id][cid]++;
sched_ctrl->sched_pusch.time_domain_allocation = tda;
const long f = sched_ctrl->search_space->searchSpaceType->choice.ue_Specific->dci_Formats;
const int dci_format = f ? NR_UL_DCI_FORMAT_0_1 : NR_UL_DCI_FORMAT_0_0;
const uint8_t num_dmrs_cdm_grps_no_data = 1;
/* we want to avoid a lengthy deduction of DMRS and other parameters in
* every TTI if we can save it, so check whether dci_format, TDA, or
* num_dmrs_cdm_grps_no_data has changed and only then recompute */
NR_sched_pusch_save_t *ps = &sched_ctrl->pusch_save;
if (ps->time_domain_allocation != tda
|| ps->dci_format != dci_format
|| ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data)
nr_save_pusch_fields(scc,
sched_ctrl->active_ubwp,
dci_format,
tda,
num_dmrs_cdm_grps_no_data,
ps);
const int mcs = 9;
NR_sched_pusch_t *sched_pusch = &sched_ctrl->sched_pusch;
sched_pusch->mcs = mcs;
sched_pusch->rbStart = rbStart;
sched_pusch->rbSize = rbSize;
/* Calculate TBS from MCS */
sched_pusch->R = nr_get_code_rate_ul(mcs, ps->mcs_table);
sched_pusch->Qm = nr_get_Qm_ul(mcs, ps->mcs_table);
if (ps->pusch_Config->tp_pi2BPSK
&& ((ps->mcs_table == 3 && mcs < 2) || (ps->mcs_table == 4 && mcs < 6))) {
sched_pusch->R >>= 1;
sched_pusch->Qm <<= 1;
}
sched_pusch->tb_size = nr_compute_tbs(sched_pusch->Qm,
sched_pusch->R,
sched_pusch->rbSize,
ps->nrOfSymbols,
ps->N_PRB_DMRS * ps->num_dmrs_symb,
0, // nb_rb_oh
0,
1 /* NrOfLayers */)
>> 3;
/* mark the corresponding RBs as used */
for (int rb = 0; rb < sched_ctrl->sched_pusch.rbSize; rb++)
vrb_map_UL[rb + sched_ctrl->sched_pusch.rbStart] = 1;
}
void nr_schedule_ulsch(module_id_t module_id,
frame_t frame,
sub_frame_t slot,
int num_slots_per_tdd,
int ul_slots,
uint64_t ulsch_in_slot_bitmap) {
RC.nrmac[module_id]->pre_processor_ul(
module_id, frame, slot, num_slots_per_tdd, ulsch_in_slot_bitmap);
NR_ServingCellConfigCommon_t *scc = RC.nrmac[module_id]->common_channels[0].ServingCellConfigCommon;
NR_UE_info_t *UE_info = &RC.nrmac[module_id]->UE_info;
const NR_UE_list_t *UE_list = &UE_info->list;
for (int UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id]) {
NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id];
/* dynamic PUSCH values (RB alloc, MCS, hence R, Qm, TBS) that change in
* every TTI are pre-populated by the preprocessor and used below */
NR_sched_pusch_t *sched_pusch = &sched_ctrl->sched_pusch;
if (sched_pusch->rbSize <= 0)
continue;
uint16_t rnti = UE_info->rnti[UE_id];
int8_t harq_id = select_ul_harq_pid(sched_ctrl);
if (harq_id < 0) return;
NR_UE_ul_harq_t *cur_harq = &sched_ctrl->ul_harq_processes[harq_id];
cur_harq->state = ACTIVE_SCHED;
cur_harq->last_tx_slot = sched_pusch->slot;
int rnti_types[2] = { NR_RNTI_C, 0 };
/* pre-computed PUSCH values that only change if time domain allocation,
* DCI format, or DMRS parameters change. Updated in the preprocessor
* through nr_save_pusch_fields() */
NR_sched_pusch_save_t *ps = &sched_ctrl->pusch_save;
/* Statistics */
UE_info->mac_stats[UE_id].ulsch_rounds[cur_harq->round]++;
if (cur_harq->round == 0)
UE_info->mac_stats[UE_id].ulsch_total_bytes_scheduled += sched_pusch->tb_size;
/* PUSCH in a later slot, but corresponding DCI now! */
nfapi_nr_ul_tti_request_t *future_ul_tti_req = &RC.nrmac[module_id]->UL_tti_req_ahead[0][sched_pusch->slot];
AssertFatal(future_ul_tti_req->SFN == sched_pusch->frame
&& future_ul_tti_req->Slot == sched_pusch->slot,
"%d.%d future UL_tti_req's frame.slot %d.%d does not match PUSCH %d.%d\n",
frame, slot,
future_ul_tti_req->SFN,
future_ul_tti_req->Slot,
sched_pusch->frame,
sched_pusch->slot);
future_ul_tti_req->pdus_list[future_ul_tti_req->n_pdus].pdu_type = NFAPI_NR_UL_CONFIG_PUSCH_PDU_TYPE;
future_ul_tti_req->pdus_list[future_ul_tti_req->n_pdus].pdu_size = sizeof(nfapi_nr_pusch_pdu_t);
nfapi_nr_pusch_pdu_t *pusch_pdu = &future_ul_tti_req->pdus_list[future_ul_tti_req->n_pdus].pusch_pdu;
memset(pusch_pdu, 0, sizeof(nfapi_nr_pusch_pdu_t));
future_ul_tti_req->n_pdus += 1;
LOG_D(MAC, "%4d.%2d Scheduling UE specific PUSCH\n", frame, slot);
pusch_pdu->pdu_bit_map = PUSCH_PDU_BITMAP_PUSCH_DATA;
pusch_pdu->rnti = rnti;
pusch_pdu->handle = 0; //not yet used
/* FAPI: BWP */
pusch_pdu->bwp_size = NRRIV2BW(sched_ctrl->active_ubwp->bwp_Common->genericParameters.locationAndBandwidth,275);
pusch_pdu->bwp_start = NRRIV2PRBOFFSET(sched_ctrl->active_ubwp->bwp_Common->genericParameters.locationAndBandwidth,275);
pusch_pdu->subcarrier_spacing = sched_ctrl->active_ubwp->bwp_Common->genericParameters.subcarrierSpacing;
pusch_pdu->cyclic_prefix = 0;
/* FAPI: PUSCH information always included */
pusch_pdu->target_code_rate = sched_pusch->R;
pusch_pdu->qam_mod_order = sched_pusch->Qm;
pusch_pdu->mcs_index = sched_pusch->mcs;
pusch_pdu->mcs_table = ps->mcs_table;
pusch_pdu->transform_precoding = ps->transform_precoding;
if (ps->pusch_Config->dataScramblingIdentityPUSCH)
pusch_pdu->data_scrambling_id = *ps->pusch_Config->dataScramblingIdentityPUSCH;
else
pusch_pdu->data_scrambling_id = *scc->physCellId;
pusch_pdu->nrOfLayers = 1;
/* FAPI: DMRS */
pusch_pdu->ul_dmrs_symb_pos = ps->ul_dmrs_symb_pos;
pusch_pdu->dmrs_config_type = ps->dmrs_config_type;
if (pusch_pdu->transform_precoding) { // transform precoding disabled
long *scramblingid;
if (pusch_pdu->scid == 0)
scramblingid = ps->NR_DMRS_UplinkConfig->transformPrecodingDisabled->scramblingID0;
else
scramblingid = ps->NR_DMRS_UplinkConfig->transformPrecodingDisabled->scramblingID1;
if (scramblingid == NULL)
pusch_pdu->ul_dmrs_scrambling_id = *scc->physCellId;
else
pusch_pdu->ul_dmrs_scrambling_id = *scramblingid;
}
else {
pusch_pdu->ul_dmrs_scrambling_id = *scc->physCellId;
if (ps->NR_DMRS_UplinkConfig->transformPrecodingEnabled->nPUSCH_Identity != NULL)
pusch_pdu->pusch_identity = *ps->NR_DMRS_UplinkConfig->transformPrecodingEnabled->nPUSCH_Identity;
else
pusch_pdu->pusch_identity = *scc->physCellId;
}
pusch_pdu->scid = 0; // DMRS sequence initialization [TS38.211, sec 6.4.1.1.1]
pusch_pdu->num_dmrs_cdm_grps_no_data = ps->num_dmrs_cdm_grps_no_data;
pusch_pdu->dmrs_ports = 1;
/* FAPI: Pusch Allocation in frequency domain */
AssertFatal(ps->pusch_Config->resourceAllocation == NR_PUSCH_Config__resourceAllocation_resourceAllocationType1,
"Only frequency resource allocation type 1 is currently supported\n");
pusch_pdu->resource_alloc = 1; //type 1
pusch_pdu->rb_start = sched_pusch->rbStart;
pusch_pdu->rb_size = sched_pusch->rbSize;
pusch_pdu->vrb_to_prb_mapping = 0;
if (ps->pusch_Config->frequencyHopping==NULL)
pusch_pdu->frequency_hopping = 0;
else
pusch_pdu->frequency_hopping = 1;
/* FAPI: Resource Allocation in time domain */
pusch_pdu->start_symbol_index = ps->startSymbolIndex;
pusch_pdu->nr_of_symbols = ps->nrOfSymbols;
/* PUSCH PDU */
pusch_pdu->pusch_data.rv_index = nr_rv_round_map[cur_harq->round];
pusch_pdu->pusch_data.harq_process_id = harq_id;
pusch_pdu->pusch_data.new_data_indicator = cur_harq->ndi;
pusch_pdu->pusch_data.tb_size = sched_pusch->tb_size;
pusch_pdu->pusch_data.num_cb = 0; //CBG not supported
/* PUSCH PTRS */
if (ps->NR_DMRS_UplinkConfig->phaseTrackingRS != NULL) {
// TODO to be fixed from RRC config
uint8_t ptrs_mcs1 = 2; // higher layer parameter in PTRS-UplinkConfig
uint8_t ptrs_mcs2 = 4; // higher layer parameter in PTRS-UplinkConfig
uint8_t ptrs_mcs3 = 10; // higher layer parameter in PTRS-UplinkConfig
uint16_t n_rb0 = 25; // higher layer parameter in PTRS-UplinkConfig
uint16_t n_rb1 = 75; // higher layer parameter in PTRS-UplinkConfig
pusch_pdu->pusch_ptrs.ptrs_time_density = get_L_ptrs(ptrs_mcs1, ptrs_mcs2, ptrs_mcs3, pusch_pdu->mcs_index, pusch_pdu->mcs_table);
pusch_pdu->pusch_ptrs.ptrs_freq_density = get_K_ptrs(n_rb0, n_rb1, pusch_pdu->rb_size);
pusch_pdu->pusch_ptrs.ptrs_ports_list = (nfapi_nr_ptrs_ports_t *) malloc(2*sizeof(nfapi_nr_ptrs_ports_t));
pusch_pdu->pusch_ptrs.ptrs_ports_list[0].ptrs_re_offset = 0;
pusch_pdu->pdu_bit_map |= PUSCH_PDU_BITMAP_PUSCH_PTRS; // enable PUSCH PTRS
}
else{
pusch_pdu->pdu_bit_map &= ~PUSCH_PDU_BITMAP_PUSCH_PTRS; // disable PUSCH PTRS
}
nfapi_nr_ul_dci_request_t *ul_dci_req = &RC.nrmac[module_id]->UL_dci_req[0];
ul_dci_req->SFN = frame;
ul_dci_req->Slot = slot;
nfapi_nr_ul_dci_request_pdus_t *ul_dci_request_pdu = &ul_dci_req->ul_dci_pdu_list[ul_dci_req->numPdus];
memset(ul_dci_request_pdu, 0, sizeof(nfapi_nr_ul_dci_request_pdus_t));
ul_dci_request_pdu->PDUType = NFAPI_NR_DL_TTI_PDCCH_PDU_TYPE;
ul_dci_request_pdu->PDUSize = (uint8_t)(2+sizeof(nfapi_nr_dl_tti_pdcch_pdu));
nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = &ul_dci_request_pdu->pdcch_pdu.pdcch_pdu_rel15;
ul_dci_req->numPdus += 1;
LOG_D(MAC,"Configuring ULDCI/PDCCH in %d.%d\n", frame,slot);
nr_configure_pdcch(RC.nrmac[0],
pdcch_pdu_rel15,
rnti,
sched_ctrl->search_space,
sched_ctrl->coreset,
scc,
sched_ctrl->active_bwp,
sched_ctrl->aggregation_level,
sched_ctrl->cce_index);
dci_pdu_rel15_t dci_pdu_rel15[MAX_DCI_CORESET];
memset(dci_pdu_rel15, 0, sizeof(dci_pdu_rel15));
NR_CellGroupConfig_t *secondaryCellGroup = UE_info->secondaryCellGroup[UE_id];
const int n_ubwp = secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.count;
// NOTE: below functions assume that dci_formats is an array corresponding
// to all UL DCIs in the PDCCH, but for us it is a simple int. So before
// having multiple UEs, the below need to be changed (IMO the functions
// should fill for one DCI only and not handle all of them).
config_uldci(sched_ctrl->active_ubwp,
pusch_pdu,
pdcch_pdu_rel15,
&dci_pdu_rel15[0],
&ps->dci_format,
ps->time_domain_allocation,
UE_info->UE_sched_ctrl[UE_id].tpc0,
n_ubwp,
sched_ctrl->active_bwp->bwp_Id);
fill_dci_pdu_rel15(scc,
secondaryCellGroup,
pdcch_pdu_rel15,
dci_pdu_rel15,
&ps->dci_format,
rnti_types,
pusch_pdu->bwp_size,
sched_ctrl->active_bwp->bwp_Id);
memset(sched_pusch, 0, sizeof(*sched_pusch));
}
}