/* * 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 */ /********************************************************************** * * FILENAME : harq_nr.c * * MODULE : HARQ * * DESCRIPTION : functions related to HARQ feature (Hybrid Automatic Repeat Request Acknowledgment) * This feature allows to acknowledge downlink and uplink transport blocks * TS 38.214 5.1 UE procedure for transmitting the physical downlink shared channel * TS 38.214 6.1 UE procedure for transmitting the physical uplink shared channel * TS 38.214 6.1.2.1 Resource allocation in time domain * TS 38.212 7.3 Downlink control information * TS 38.213 9.2.3 UE procedure for reporting HARQ-ACK * TS 38.321 5.4.1 UL Grant reception * TS 38.321 5.4.2.1 HARQ Entity * * Downlink HARQ mechanism * ----------------------- * A downlink DCI is received in a PDCCH. * Then received parameters are communicated to HARQ entity (including NDI new data indicator and K which is the number of slots * between current reception and transmission of this downlink acknowledgment. * * Reception on slot n transmission of acknowledgment * slot k * ---+---------------+--- * | | * Frame | PUCCH / PUSCH | * Subframe | | * Slot n ---+------------+--+--- * ---+-------------+--- / | * | PDCCH | / | * | DCI | / | * | downlink | +---------------+/ | * | NDI--->------------->----------| downlink HARQ | | * | k | | entity | | * ---+-----|-------+--- +---------------+ | * | | | * v |/__________________________________________\| * | \ slot between reception and transmission /| * |________________________^ * * Uplink HARQ mechanism * --------------------- * An uplink DCI is received in a PDCCH. * Then received parameters are communicated to HARQ entity (including NDI new data indicator and K which is the number of slots * between current reception and related PUSCH transmission). * Uplink HARQ entity decides to transmit a new block or to retransmit current one. * transmission/retransmission parameters should be determined based on received parameters. * * Reception on slot n Transmission on slot k * slot k * ---+---------------+--- * | PUSCH | * Frame | Transmission | * Subframe | Retransmission| * Slot n ---+------------+--+--- * ---+-------------+--- / | * | PDCCH | / | * | DCI | / | * | uplink | +-------------+/ | * | NDI--->------------->----------->| uplink HARQ | | * | k | | entity | | * ---+-----|-------+--- +-------------+ | * | | | * v |/__________________________________________\| * | \ slot between reception and transmission /| * |________________________^ ************************************************************************/ #include "PHY/defs_nr_UE.h" #include "PHY/NR_UE_TRANSPORT/nr_transport_ue.h" #include "SCHED_NR_UE/harq_nr.h" /********************* define **************************************/ #define DL_DCI (1) #define UL_DCI (0) /******************************************************************* * * NAME : get_dci_info_for_harq * * PARAMETERS : pointer to ue context * id of current gNB * number of uplink processes * maximum number of uplink retransmissions * RETURN : none * * DESCRIPTION : update HARQ entity with information from DCI * TS 38.212 7.3.1.2 DCI formats for scheduling PDSCH * *********************************************************************/ void get_dci_info_for_harq(PHY_VARS_NR_UE *ue, NR_DCI_INFO_EXTRACTED_t *nr_dci_info_extracted, NR_UE_DLSCH_t **dlsch, NR_UE_ULSCH_t *ulsch, uint8_t slot, uint8_t tx_offset) { if (nr_dci_info_extracted->identifier_dci_formats == DL_DCI) { dlsch[0]->current_harq_pid = nr_dci_info_extracted->harq_process_number; NR_DL_UE_HARQ_t *dl_harq = dlsch[0]->harq_processes[dlsch[0]->current_harq_pid]; dl_harq->harq_ack.vDAI_DL = nr_dci_info_extracted->dai+1; dl_harq->harq_ack.pucch_resource_indicator = nr_dci_info_extracted->pucch_resource_ind; dl_harq->harq_ack.slot_for_feedback_ack = (slot + nr_dci_info_extracted->pdsch_to_harq_feedback_time_ind)%ue->frame_parms.slots_per_subframe; dl_harq->harq_ack.harq_id = nr_dci_info_extracted->harq_process_number; dl_harq->harq_ack.rx_status = downlink_harq_process(dl_harq, dlsch[0]->current_harq_pid, nr_dci_info_extracted->ndi, dlsch[0]->rnti_type); } else if (nr_dci_info_extracted->identifier_dci_formats == UL_DCI) { /* store harq id for which pusch should be transmitted at rx_slot + tx_offset */ set_tx_harq_id(ulsch, nr_dci_info_extracted->harq_process_number, (slot + tx_offset)%ue->frame_parms.slots_per_subframe); ulsch->harq_processes[nr_dci_info_extracted->harq_process_number]->tx_status = uplink_harq_process(ulsch, nr_dci_info_extracted->harq_process_number, nr_dci_info_extracted->ndi, ulsch->rnti_type); } } /******************************************************************* * * NAME : config_uplink_harq_process * * PARAMETERS : pointer to ue context * id of current gNB * number of uplink processes * maximum number of uplink retransmissions * RETURN : none * * DESCRIPTION : configuration of uplink HARQ entity * *********************************************************************/ void config_uplink_harq_process(PHY_VARS_NR_UE *ue, int gNB_id, int thread_id, int code_word_idx, uint8_t number_harq_processes_pusch) { NR_UE_ULSCH_t *ulsch; ulsch = (NR_UE_ULSCH_t *)malloc16(sizeof(NR_UE_ULSCH_t)); if (ulsch != NULL) { memset(ulsch,0,sizeof(NR_UE_ULSCH_t)); ue->ulsch[thread_id][gNB_id][code_word_idx] = ulsch; } else { LOG_E(PHY, "Fatal memory allocation problem at line %d in function %s of file %s \n", __LINE__ , __func__, __FILE__); assert(0); } ulsch->number_harq_processes_for_pusch = number_harq_processes_pusch; /* allocation of HARQ process context */ for (int harq_pid = 0; harq_pid < number_harq_processes_pusch; harq_pid++) { ulsch->harq_processes[harq_pid] = (NR_UL_UE_HARQ_t *)malloc16(sizeof(NR_UL_UE_HARQ_t)); if (ulsch->harq_processes[harq_pid] == NULL) { LOG_E(PHY, "Fatal memory allocation problem at line %d in function %s of file %s \n", __LINE__ , __func__, __FILE__); assert(0); } ulsch->harq_processes[harq_pid]->subframe_scheduling_flag = 0; ulsch->harq_processes[harq_pid]->first_tx = 1; ulsch->harq_processes[harq_pid]->round = 0; } for (int slot_tx = 0; slot_tx < NR_MAX_SLOTS_PER_FRAME; slot_tx++) { ue->ulsch[thread_id][gNB_id][code_word_idx]->harq_process_id[slot_tx] = NR_MAX_HARQ_PROCESSES; } } /******************************************************************* * * NAME : release_uplink_harq_process * * PARAMETERS : pointer to ue context * id of current gNB * * RETURN : none * * DESCRIPTION : release of HARQ uplink entity * *********************************************************************/ void release_uplink_harq_process(PHY_VARS_NR_UE *ue, int gNB_id, int thread_id, int code_word_idx) { NR_UE_ULSCH_t *ulsch = ue->ulsch[thread_id][gNB_id][code_word_idx]; for (int process_id = 0; process_id < ulsch->number_harq_processes_for_pusch; process_id++) { free16(ulsch->harq_processes[process_id],sizeof(NR_UL_UE_HARQ_t)); ulsch->harq_processes[process_id] = NULL; } free16(ulsch, sizeof(NR_UE_ULSCH_t)); ue->ulsch[thread_id][gNB_id][code_word_idx] = NULL; } /******************************************************************* * * NAME : set_tx_harq_id * * PARAMETERS : ue context * slot_tx slot for transmission * gNB_id identifier * * RETURN : none * * DESCRIPTION : store tx harq process identifier for given transmission slot * *********************************************************************/ void set_tx_harq_id(NR_UE_ULSCH_t *ulsch, int harq_pid, int slot_tx) { ulsch->harq_process_id[slot_tx] = harq_pid; } /******************************************************************* * * NAME : get_tx_harq_id * * PARAMETERS : ue context * slot_tx slot for transmission * gNB_id identifier * * RETURN : harq process identifier * * DESCRIPTION : return tx harq process identifier for given slot transmission * *********************************************************************/ int get_tx_harq_id(NR_UE_ULSCH_t *ulsch, int slot_tx) { return (ulsch->harq_process_id[slot_tx]); } /******************************************************************* * * NAME : uplink_harq_process * * PARAMETERS : ue context * slot_tx slot for transmission * gNB_id identifier * ndi from DCI * rnti_type from DCI * * RETURN : true it a new transmission * false it is a retransmission * * DESCRIPTION : manage uplink grant information for transmissions/retransmissions * TS 38.321 5.4.1 UL Grant reception * TS 38.321 5.4.2.1 HARQ Entity * *********************************************************************/ harq_result_t uplink_harq_process(NR_UE_ULSCH_t *ulsch, int harq_pid, int ndi, uint8_t rnti_type) { harq_result_t result_harq = RETRANSMISSION_HARQ; if (rnti_type == _CS_RNTI_) { LOG_E(PHY, "Fatal error in HARQ entity due to not supported CS_RNTI at line %d in function %s of file %s \n", __LINE__ , __func__, __FILE__); return(NEW_TRANSMISSION_HARQ); } else if ((rnti_type != _C_RNTI_) && (rnti_type != _TC_RNTI_)) { /* harq mechanism is not relevant for other rnti */ return(NEW_TRANSMISSION_HARQ); } else if (harq_pid > ulsch->number_harq_processes_for_pusch) { LOG_E(PHY, "Fatal error in HARQ entity due to unknown process identity %d at line %d in function %s of file %s \n", harq_pid, __LINE__ , __func__, __FILE__); assert(0); } /* 38.321 5.4.2.1 2> if the uplink grant was received on PDCCH for the C-RNTI and the HARQ buffer of the identified process is empty */ if ((ulsch->harq_processes[harq_pid]->first_tx == 1) && (rnti_type == _C_RNTI_)) { /* no transmission yet on this process so consider its harq buffer as empty */ ulsch->harq_processes[harq_pid]->first_tx = 0; ulsch->harq_processes[harq_pid]->pusch_pdu.pusch_data.new_data_indicator = ndi; /* store first value of ndi */ ulsch->harq_processes[harq_pid]->round = 0; ulsch->harq_processes[harq_pid]->subframe_scheduling_flag = 1; result_harq = NEW_TRANSMISSION_HARQ; LOG_D(PHY, "[HARQ-UL-PUSCH harqId : %d] first new transmission \n", harq_pid); } /* 38.321 5.4.2.1 2> if the received grant was not addressed to a Temporary C-RNTI on PDCCH, and the NDI provided in the associated HARQ */ /* information has been toggled compared to the value in the previous transmission of this TB of this HARQ process */ else if ((ulsch->harq_processes[harq_pid]->pusch_pdu.pusch_data.new_data_indicator != ndi) && (rnti_type != _TC_RNTI_)) { /* is ndi toogled so this is a new grant ? */ ulsch->harq_processes[harq_pid]->pusch_pdu.pusch_data.new_data_indicator = ndi; /* store first value of ndi */ ulsch->harq_processes[harq_pid]->round = 0; ulsch->harq_processes[harq_pid]->subframe_scheduling_flag = 1; result_harq = NEW_TRANSMISSION_HARQ; LOG_D(PHY, "[HARQ-UL-PUSCH harqId : %d] new transmission due to toogle of ndi \n", harq_pid); } /* 38.321 5.4.2.1 2> else (i.e. retransmission): */ else { ulsch->harq_processes[harq_pid]->pusch_pdu.pusch_data.new_data_indicator = ndi; /* ndi has not toggled si this is a retransmission */ ulsch->harq_processes[harq_pid]->round++; /* increment number of retransmission */ result_harq = RETRANSMISSION_HARQ; LOG_D(PHY, "[HARQ-UL-PUSCH harqId : %d] retransmission \n", harq_pid); } return (result_harq); } /******************************************************************* * * NAME : init_downlink_harq_status * * PARAMETERS : pointer to dl harq status * * RETURN : none * * DESCRIPTION : initialisation of downlink HARQ status * *********************************************************************/ void init_downlink_harq_status(NR_DL_UE_HARQ_t *dl_harq) { dl_harq->status = SCH_IDLE; dl_harq->first_tx = 1; dl_harq->round = 0; dl_harq->harq_ack.ack = DL_ACKNACK_NO_SET; dl_harq->harq_ack.send_harq_status = 0; dl_harq->harq_ack.vDAI_UL = UL_DAI_NO_SET; dl_harq->harq_ack.vDAI_DL = DL_DAI_NO_SET; dl_harq->harq_ack.slot_for_feedback_ack = NR_MAX_SLOTS_PER_FRAME; dl_harq->harq_ack.pucch_resource_indicator = MAX_PUCCH_RESOURCE_INDICATOR; dl_harq->harq_ack.n_CCE = 0; dl_harq->harq_ack.N_CCE = 0;; } /******************************************************************* * * NAME : config_downlink_harq_process * * PARAMETERS : pointer to ue context * id of current gNB * number of downlink processes * * RETURN : none * * DESCRIPTION : configuration of downlink HARQ entity * *********************************************************************/ void config_downlink_harq_process(PHY_VARS_NR_UE *ue, int gNB_id, int TB_id, int execution_thread_number, uint8_t number_harq_processes_for_pdsch) { NR_UE_DLSCH_t *dlsch; dlsch = (NR_UE_DLSCH_t *)malloc16(sizeof(NR_UE_DLSCH_t)); if (dlsch != NULL) { memset(dlsch,0,sizeof(NR_UE_DLSCH_t)); ue->dlsch[execution_thread_number][gNB_id][TB_id] = dlsch; } else { LOG_E(PHY, "Fatal memory allocation problem at line %d in function %s of file %s \n", __LINE__ , __func__, __FILE__); assert(0); } dlsch->Mdlharq = number_harq_processes_for_pdsch; /* an additional HARQ is reserved for PBCCH */ dlsch->number_harq_processes_for_pdsch = number_harq_processes_for_pdsch; /* allocation of HARQ process context */ for (int harq_pid = 0; harq_pid < number_harq_processes_for_pdsch; harq_pid++) { //dlsch->harq_processes[harq_pid] = (NR_DL_UE_HARQ_t *)malloc16(sizeof(NR_DL_UE_HARQ_t)); /*if (dlsch->harq_processes[harq_pid] == NULL) { LOG_E(PHY, "Fatal memory allocation problem at line %d in function %s of file %s \n", __LINE__ , __func__, __FILE__); assert(0); }*/ memset(&dlsch->harq_processes[harq_pid],0,sizeof(NR_DL_UE_HARQ_t)); NR_DL_UE_HARQ_t *dl_harq = dlsch->harq_processes[harq_pid]; init_downlink_harq_status(dl_harq); } } /******************************************************************* * * NAME : release_downlink_harq_process * * PARAMETERS : pointer to ue context * id of current gNB * TB_id transport block identity 0 or 1 * execution_thread_number thread number for current downlink processing * RETURN : none * * DESCRIPTION : release of HARQ downlink entity * *********************************************************************/ void release_downlink_harq_process(PHY_VARS_NR_UE *ue, int gNB_id, int TB_id, int execution_thread_number) { NR_UE_DLSCH_t *dlsch = ue->dlsch[execution_thread_number][gNB_id][TB_id]; /*for (int process_id = 0; process_id < dlsch->Mdlharq; process_id++) { free16(dlsch->harq_processes[process_id],sizeof(NR_DL_UE_HARQ_t)); dlsch->harq_processes[process_id] = NULL; }*/ free16(dlsch,sizeof(NR_UE_DLSCH_t)); ue->dlsch[execution_thread_number][gNB_id][TB_id] = NULL; } /******************************************************************* * * NAME : downlink_harq_process * * PARAMETERS : downlink harq context * harq identifier * ndi (new data indicator) from DCI * rnti_type from DCI * * RETURN : none * * DESCRIPTION : manage downlink information from DCI for downlink transmissions/retransmissions * TS 38.321 5.3.1 DL Assignment reception * TS 38.321 5.3.2 HARQ operation * *********************************************************************/ harq_result_t downlink_harq_process(NR_DL_UE_HARQ_t *dl_harq, int harq_pid, int ndi, uint8_t rnti_type) { harq_result_t result_harq = RETRANSMISSION_HARQ; if (rnti_type == _CS_RNTI_) { LOG_E(PHY, "Fatal error in HARQ entity due to not supported CS_RNTI at line %d in function %s of file %s \n", __LINE__ , __func__, __FILE__); return(NEW_TRANSMISSION_HARQ); } else if ((rnti_type != _C_RNTI_) && (rnti_type != _TC_RNTI_)) { /* harq mechanism is not relevant for other rnti */ return(NEW_TRANSMISSION_HARQ); } if (dl_harq->first_tx == 1) { dl_harq->round = 0; dl_harq->status = ACTIVE; dl_harq->DCINdi = ndi; dl_harq->first_tx = 0; result_harq = NEW_TRANSMISSION_HARQ; LOG_D(PHY, "[HARQ-DL-PDSCH harqId : %d] first new reception \n", harq_pid); } else if (dl_harq->DCINdi != ndi) { dl_harq->round = 0; dl_harq->status = ACTIVE; dl_harq->DCINdi = ndi; result_harq = NEW_TRANSMISSION_HARQ; LOG_D(PHY, "[HARQ-DL-PDSCH harqId : %d] new reception due to toogle of ndi \n", harq_pid); } else { dl_harq->round++; if (dl_harq->harq_ack.ack) dl_harq->status = SCH_IDLE; result_harq = RETRANSMISSION_HARQ; LOG_D(PHY, "[HARQ-DL-PDSCH harqId : %d] reception of a retransmission \n", harq_pid); } return (result_harq); }