/* * 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 nr_ulsch_ue.c * \brief Top-level routines for transmission of the PUSCH TS 38.211 v 15.4.0 * \author Khalid Ahmed * \date 2019 * \version 0.1 * \company Fraunhofer IIS * \email: khalid.ahmed@iis.fraunhofer.de * \note * \warning */ #include #include "PHY/NR_REFSIG/dmrs_nr.h" #include "PHY/NR_REFSIG/ptrs_nr.h" #include "PHY/NR_REFSIG/refsig_defs_ue.h" #include "PHY/NR_UE_TRANSPORT/nr_transport_ue.h" #include "PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h" #include "PHY/MODULATION/nr_modulation.h" #include "PHY/MODULATION/modulation_common.h" #include "common/utils/assertions.h" #include "common/utils/nr/nr_common.h" #include "common/utils/LOG/vcd_signal_dumper.h" #include "PHY/NR_TRANSPORT/nr_transport_common_proto.h" #include "PHY/NR_TRANSPORT/nr_sch_dmrs.h" #include "PHY/defs_nr_common.h" #include "PHY/TOOLS/tools_defs.h" #include "executables/nr-softmodem.h" #include "executables/softmodem-common.h" #include "PHY/NR_REFSIG/ul_ref_seq_nr.h" #include //#define DEBUG_PUSCH_MAPPING //#define DEBUG_MAC_PDU //#define DEBUG_DFT_IDFT //extern int32_t uplink_counter; void nr_pusch_codeword_scrambling_uci(uint8_t *in, uint32_t size, uint32_t Nid, uint32_t n_RNTI, uint32_t* out) { uint32_t *seq = gold_cache((n_RNTI << 15) + Nid, (size + 31) / 32); for (int i=0; i> 31; } } else out[idx] ^= (((in[i]) & 1) ^ ((seq[idx] >> b_idx) & 1)) << b_idx; //printf("i %d b_idx %d in %d s 0x%08x out 0x%08x\n", i, b_idx, in[i], s, *out); } } void nr_pusch_codeword_scrambling(uint8_t *in, uint32_t size, uint32_t Nid, uint32_t n_RNTI, bool uci_on_pusch, uint32_t* out) { if (uci_on_pusch) nr_pusch_codeword_scrambling_uci(in, size, Nid, n_RNTI, out); else nr_codeword_scrambling(in, size, 0, Nid, n_RNTI, out); } void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, const unsigned char harq_pid, const uint32_t frame, const uint8_t slot, const int gNB_id, nr_phy_data_tx_t *phy_data, c16_t **txdataF) { LOG_D(PHY,"nr_ue_ulsch_procedures hard_id %d %d.%d\n",harq_pid,frame,slot); int Wf[2], Wt[2]; int l_prime[2], delta; uint8_t nb_dmrs_re_per_rb; int i; int sample_offsetF, N_RE_prime; NR_DL_FRAME_PARMS *frame_parms = &UE->frame_parms; int N_PRB_oh = 0; // higher layer (RRC) parameter xOverhead in PUSCH-ServingCellConfig uint16_t number_dmrs_symbols = 0; NR_UE_ULSCH_t *ulsch_ue = &phy_data->ulsch; NR_UL_UE_HARQ_t *harq_process_ul_ue = &UE->ul_harq_processes[harq_pid]; const nfapi_nr_ue_pusch_pdu_t *pusch_pdu = &ulsch_ue->pusch_pdu; uint32_t tb_size = pusch_pdu->pusch_data.tb_size; AssertFatal(pusch_pdu->pusch_uci.harq_ack_bit_length == 0 && pusch_pdu->pusch_uci.csi_part1_bit_length == 0 && pusch_pdu->pusch_uci.csi_part2_bit_length == 0, "UCI on PUSCH not supported at PHY\n"); int start_symbol = pusch_pdu->start_symbol_index; uint16_t ul_dmrs_symb_pos = pusch_pdu->ul_dmrs_symb_pos; uint8_t number_of_symbols = pusch_pdu->nr_of_symbols; uint8_t dmrs_type = pusch_pdu->dmrs_config_type; uint16_t start_rb = pusch_pdu->rb_start; uint16_t nb_rb = pusch_pdu->rb_size; uint8_t Nl = pusch_pdu->nrOfLayers; uint8_t mod_order = pusch_pdu->qam_mod_order; uint16_t rnti = pusch_pdu->rnti; uint8_t cdm_grps_no_data = pusch_pdu->num_dmrs_cdm_grps_no_data; uint16_t start_sc = frame_parms->first_carrier_offset + (start_rb+pusch_pdu->bwp_start)*NR_NB_SC_PER_RB; if (start_sc >= frame_parms->ofdm_symbol_size) start_sc -= frame_parms->ofdm_symbol_size; ulsch_ue->Nid_cell = frame_parms->Nid_cell; for (int i = start_symbol; i < start_symbol + number_of_symbols; i++) { if((ul_dmrs_symb_pos >> i) & 0x01) number_dmrs_symbols += 1; } nb_dmrs_re_per_rb = ((dmrs_type == pusch_dmrs_type1) ? 6:4)*cdm_grps_no_data; LOG_D(PHY,"ulsch TX %x : start_rb %d nb_rb %d mod_order %d Nl %d Tpmi %d bwp_start %d start_sc %d start_symbol %d num_symbols %d cdmgrpsnodata %d num_dmrs %d dmrs_re_per_rb %d\n", rnti,start_rb,nb_rb,mod_order,Nl,pusch_pdu->Tpmi,pusch_pdu->bwp_start,start_sc,start_symbol,number_of_symbols,cdm_grps_no_data,number_dmrs_symbols,nb_dmrs_re_per_rb); // TbD num_of_mod_symbols is set but never used N_RE_prime = NR_NB_SC_PER_RB*number_of_symbols - nb_dmrs_re_per_rb*number_dmrs_symbols - N_PRB_oh; harq_process_ul_ue->num_of_mod_symbols = N_RE_prime*nb_rb; /////////////////////////PTRS parameters' initialization///////////////////////// /////////// uint8_t L_ptrs, K_ptrs = 0; uint32_t unav_res = 0; if (pusch_pdu->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) { K_ptrs = pusch_pdu->pusch_ptrs.ptrs_freq_density; L_ptrs = 1 << pusch_pdu->pusch_ptrs.ptrs_time_density; ulsch_ue->ptrs_symbols = 0; set_ptrs_symb_idx(&ulsch_ue->ptrs_symbols, number_of_symbols, start_symbol, L_ptrs, ul_dmrs_symb_pos); int n_ptrs = (nb_rb + K_ptrs - 1) / K_ptrs; int ptrsSymbPerSlot = get_ptrs_symbols_in_slot(ulsch_ue->ptrs_symbols, start_symbol, number_of_symbols); unav_res = n_ptrs * ptrsSymbPerSlot; } /////////// //////////////////////////////////////////////////////////////////// /////////////////////////ULSCH coding///////////////////////// /////////// unsigned int G = nr_get_G(nb_rb, number_of_symbols, nb_dmrs_re_per_rb, number_dmrs_symbols, unav_res, mod_order, Nl); trace_NRpdu(DIRECTION_UPLINK, harq_process_ul_ue->payload_AB, tb_size, WS_C_RNTI, rnti, frame, slot, 0, 0); if (nr_ulsch_encoding(UE, ulsch_ue, frame_parms, harq_pid, tb_size, G) == -1) return; /////////// //////////////////////////////////////////////////////////////////// /////////////////////////ULSCH scrambling///////////////////////// /////////// uint32_t available_bits = G; // +1 because size can be not modulo 4 uint32_t scrambled_output[available_bits / (8 * sizeof(uint32_t)) + 1]; memset(scrambled_output, 0, sizeof(scrambled_output)); nr_pusch_codeword_scrambling(harq_process_ul_ue->f, available_bits, pusch_pdu->data_scrambling_id, rnti, false, scrambled_output); ///////////// ////////////////////////////////////////////////////////////////////////// /////////////////////////ULSCH modulation///////////////////////// /////////// int max_num_re = Nl*number_of_symbols*nb_rb*NR_NB_SC_PER_RB; c16_t d_mod[max_num_re] __attribute__((aligned(16))); nr_modulation(scrambled_output, // assume one codeword for the moment available_bits, mod_order, (int16_t *)d_mod); /////////// //////////////////////////////////////////////////////////////////////// /////////////////////////DMRS Modulation///////////////////////// /////////// uint16_t n_dmrs = (pusch_pdu->bwp_start + start_rb + nb_rb)*((dmrs_type == pusch_dmrs_type1) ? 6:4); c16_t mod_dmrs[n_dmrs] __attribute((aligned(16))); /////////// //////////////////////////////////////////////////////////////////////// /////////////////////////ULSCH layer mapping///////////////////////// /////////// const int sz = available_bits / mod_order; c16_t tx_layers[Nl][sz]; memset(tx_layers, 0, sizeof(tx_layers)); nr_ue_layer_mapping(d_mod, Nl, available_bits / mod_order, sz, tx_layers); /////////// //////////////////////////////////////////////////////////////////////// //////////////////////// ULSCH transform precoding //////////////////////// /////////// l_prime[0] = 0; // single symbol ap 0 uint8_t u = 0, v = 0; c16_t *dmrs_seq = NULL; /// Transform-coded "y"-sequences (for definition see 38-211 V15.3.0 2018-09, subsection 6.3.1.4) c16_t y[max_num_re] __attribute__((aligned(16))); memset(y, 0, sizeof(y)); if (pusch_pdu->transform_precoding == transformPrecoder_enabled) { uint32_t nb_re_pusch=nb_rb * NR_NB_SC_PER_RB; uint32_t y_offset = 0; uint16_t num_dmrs_res_per_symbol = nb_rb*(NR_NB_SC_PER_RB/2); // Calculate index to dmrs seq array based on number of DMRS Subcarriers on this symbol int index = get_index_for_dmrs_lowpapr_seq(num_dmrs_res_per_symbol); u = pusch_pdu->dfts_ofdm.low_papr_group_number; v = pusch_pdu->dfts_ofdm.low_papr_sequence_number; dmrs_seq = dmrs_lowpaprtype1_ul_ref_sig[u][v][index]; AssertFatal(index >= 0, "Num RBs not configured according to 3GPP 38.211 section 6.3.1.4. For PUSCH with transform precoding, num RBs cannot be multiple of any other primenumber other than 2,3,5\n"); AssertFatal(dmrs_seq != NULL, "DMRS low PAPR seq not found, check if DMRS sequences are generated"); LOG_D(PHY,"Transform Precoding params. u: %d, v: %d, index for dmrsseq: %d\n", u, v, index); for (int l = start_symbol; l < start_symbol + number_of_symbols; l++) { if((ul_dmrs_symb_pos >> l) & 0x01) /* In the symbol with DMRS no data would be transmitted CDM groups is 2*/ continue; nr_dft(&y[y_offset], &tx_layers[0][y_offset], nb_re_pusch); y_offset = y_offset + nb_re_pusch; LOG_D(PHY,"Transform precoding being done on data- symbol: %d, nb_re_pusch: %d, y_offset: %d\n", l, nb_re_pusch, y_offset); #ifdef DEBUG_PUSCH_MAPPING printf("NR_ULSCH_UE: y_offset %u\t nb_re_pusch %u \t Symbol %d \t nb_rb %d \n", y_offset, nb_re_pusch, l, nb_rb); #endif } #ifdef DEBUG_DFT_IDFT int32_t debug_symbols[MAX_NUM_NR_RE] __attribute__ ((aligned(16))); int offset = 0; printf("NR_ULSCH_UE: available_bits: %u, mod_order: %d", available_bits,mod_order); for (int ll = 0; ll < (available_bits/mod_order); ll++) { debug_symbols[ll] = ulsch_ue->y[ll]; } printf("NR_ULSCH_UE: numSym: %d, num_dmrs_sym: %d", number_of_symbols,number_dmrs_symbols); for (int ll = 0; ll < (number_of_symbols-number_dmrs_symbols); ll++) { nr_idft(&debug_symbols[offset], nb_re_pusch); offset = offset + nb_re_pusch; } LOG_M("preDFT_all_symbols.m","UE_preDFT", tx_layers[0],number_of_symbols*nb_re_pusch,1,1); LOG_M("postDFT_all_symbols.m","UE_postDFT", y,number_of_symbols*nb_re_pusch,1,1); LOG_M("DEBUG_IDFT_SYMBOLS.m","UE_Debug_IDFT", debug_symbols,number_of_symbols*nb_re_pusch,1,1); LOG_M("UE_DMRS_SEQ.m","UE_DMRS_SEQ", dmrs_seq,nb_re_pusch,1,1); #endif } /////////// //////////////////////////////////////////////////////////////////////// /////////////////////////ULSCH RE mapping///////////////////////// /////////// const int encoded_length = frame_parms->N_RB_UL * 14 * NR_NB_SC_PER_RB * mod_order * Nl; c16_t tx_precoding[Nl][encoded_length]; memset(tx_precoding, 0, sizeof(tx_precoding)); for (int nl=0; nl < Nl; nl++) { uint8_t k_prime = 0; uint16_t m = 0; #ifdef DEBUG_PUSCH_MAPPING printf("NR_ULSCH_UE: Value of CELL ID %d /t, u %d \n", frame_parms->Nid_cell, u); #endif int dmrs_port = get_dmrs_port(nl,pusch_pdu->dmrs_ports); // DMRS params for this dmrs port get_Wt(Wt, dmrs_port, dmrs_type); get_Wf(Wf, dmrs_port, dmrs_type); delta = get_delta(dmrs_port, dmrs_type); for (int l=start_symbol; l> l) & 0x01) { is_dmrs_sym = 1; if (pusch_pdu->transform_precoding == transformPrecoder_disabled){ if (dmrs_type == pusch_dmrs_type1) dmrs_idx = (pusch_pdu->bwp_start + start_rb)*6; else dmrs_idx = (pusch_pdu->bwp_start + start_rb)*4; // TODO: performance improvement, we can skip the modulation of DMRS symbols outside the bandwidth part // Perform this on gold sequence, not required when SC FDMA operation is done, LOG_D(PHY,"DMRS in symbol %d\n",l); const uint32_t *gold = nr_gold_pusch(frame_parms->N_RB_UL, frame_parms->symbols_per_slot, pusch_pdu->ul_dmrs_scrambling_id, pusch_pdu->scid, slot, l); nr_modulation(gold, n_dmrs * 2, DMRS_MOD_ORDER, (int16_t *)mod_dmrs); // currently only codeword 0 is modulated. Qm = 2 as DMRS is QPSK modulated } else { dmrs_idx = 0; } } else if (pusch_pdu->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) { AssertFatal(pusch_pdu->transform_precoding == transformPrecoder_disabled, "PTRS NOT SUPPORTED IF TRANSFORM PRECODING IS ENABLED\n"); if(is_ptrs_symbol(l, ulsch_ue->ptrs_symbols)) { is_ptrs_sym = 1; const uint32_t *gold = nr_gold_pusch(frame_parms->N_RB_UL, frame_parms->symbols_per_slot, pusch_pdu->ul_dmrs_scrambling_id, pusch_pdu->scid, slot, l); nr_modulation(gold, nb_rb, DMRS_MOD_ORDER, (int16_t *)mod_ptrs); } } for (i=0; i< nb_rb*NR_NB_SC_PER_RB; i++) { uint8_t is_dmrs = 0; uint8_t is_ptrs = 0; sample_offsetF = l*frame_parms->ofdm_symbol_size + k; if (is_dmrs_sym) { if (k == ((start_sc+get_dmrs_freq_idx_ul(n, k_prime, delta, dmrs_type))%frame_parms->ofdm_symbol_size)) is_dmrs = 1; } else if (is_ptrs_sym) { is_ptrs = is_ptrs_subcarrier(k, rnti, K_ptrs, nb_rb, pusch_pdu->pusch_ptrs.ptrs_ports_list[0].ptrs_re_offset, start_sc, frame_parms->ofdm_symbol_size); } if (is_dmrs == 1) { // if transform precoding is enabled const int tmp = Wt[l_prime[0]] * Wf[k_prime] * AMP; if (pusch_pdu->transform_precoding == transformPrecoder_enabled) tx_precoding[nl][sample_offsetF] = c16mulRealShift(dmrs_seq[dmrs_idx], tmp, 15); else tx_precoding[nl][sample_offsetF] = c16mulRealShift(mod_dmrs[dmrs_idx], tmp, 15); #ifdef DEBUG_PUSCH_MAPPING printf("DMRS: Layer: %d\t, dmrs_idx %d\t l %d \t k %d \t k_prime %d \t n %d \t dmrs: %d %d\n", nl, dmrs_idx, l, k, k_prime, n, tx_precoding[nl][sample_offsetF].r, tx_precoding[nl][sample_offsetF].i); #endif dmrs_idx++; k_prime++; k_prime &= 1; n += (k_prime) ? 0 : 1; } else if (is_ptrs == 1) { uint16_t beta_ptrs = 1; // temp value until power control is implemented tx_precoding[nl][sample_offsetF] = c16mulRealShift(mod_ptrs[ptrs_idx], beta_ptrs * AMP, 15); ptrs_idx++; } else if (!is_dmrs_sym || allowed_xlsch_re_in_dmrs_symbol(k, start_sc, frame_parms->ofdm_symbol_size, cdm_grps_no_data, dmrs_type)) { if (pusch_pdu->transform_precoding == transformPrecoder_disabled) tx_precoding[nl][sample_offsetF] = tx_layers[nl][m]; else tx_precoding[nl][sample_offsetF] = y[m]; #ifdef DEBUG_PUSCH_MAPPING printf("DATA: layer %d\t m %d\t l %d \t k %d \t tx_precoding: %d %d\n", nl, m, l, k, tx_precoding[nl][sample_offsetF].r, tx_precoding[nl][sample_offsetF].i); #endif m++; } else { tx_precoding[nl][sample_offsetF] = (c16_t){0}; } if (++k >= frame_parms->ofdm_symbol_size) k -= frame_parms->ofdm_symbol_size; } // for (i=0; i< nb_rb*NR_NB_SC_PER_RB; i++) } // for (l=start_symbol; lnb_antennas_tx; ap++) { for (int l = start_symbol; l < start_symbol + number_of_symbols; l++) { uint16_t k = start_sc; for (int rb = 0; rb < nb_rb; rb++) { // get pmi info uint8_t pmi = pusch_pdu->Tpmi; if (pmi == 0) { // unitary Precoding if (k + NR_NB_SC_PER_RB <= frame_parms->ofdm_symbol_size) { // RB does not cross DC if (ap < pusch_pdu->nrOfLayers) memcpy(&txdataF[ap][l * frame_parms->ofdm_symbol_size + k], &tx_precoding[ap][l * frame_parms->ofdm_symbol_size + k], NR_NB_SC_PER_RB * sizeof(c16_t)); else memset(&txdataF[ap][l * frame_parms->ofdm_symbol_size + k], 0, NR_NB_SC_PER_RB * sizeof(int32_t)); } else { // RB does cross DC int neg_length = frame_parms->ofdm_symbol_size - k; int pos_length = NR_NB_SC_PER_RB - neg_length; if (ap < pusch_pdu->nrOfLayers) { memcpy(&txdataF[ap][l * frame_parms->ofdm_symbol_size + k], &tx_precoding[ap][l * frame_parms->ofdm_symbol_size + k], neg_length * sizeof(c16_t)); memcpy(&txdataF[ap][l * frame_parms->ofdm_symbol_size], &tx_precoding[ap][l * frame_parms->ofdm_symbol_size], pos_length * sizeof(int32_t)); } else { memset(&txdataF[ap][l * frame_parms->ofdm_symbol_size + k], 0, neg_length * sizeof(int32_t)); memset(&txdataF[ap][l * frame_parms->ofdm_symbol_size], 0, pos_length * sizeof(int32_t)); } } k += NR_NB_SC_PER_RB; if (k >= frame_parms->ofdm_symbol_size) { k -= frame_parms->ofdm_symbol_size; } } else { // get the precoding matrix weights: const char *W_prec; switch (frame_parms->nb_antennas_tx) { case 1: // 1 antenna port W_prec = nr_W_1l_2p[pmi][ap]; break; case 2: // 2 antenna ports if (pusch_pdu->nrOfLayers == 1) // 1 layer W_prec = nr_W_1l_2p[pmi][ap]; else // 2 layers W_prec = nr_W_2l_2p[pmi][ap]; break; case 4: // 4 antenna ports if (pusch_pdu->nrOfLayers == 1) // 1 layer W_prec = nr_W_1l_4p[pmi][ap]; else if (pusch_pdu->nrOfLayers == 2) // 2 layers W_prec = nr_W_2l_4p[pmi][ap]; else if (pusch_pdu->nrOfLayers == 3) // 3 layers W_prec = nr_W_3l_4p[pmi][ap]; else // 4 layers W_prec = nr_W_4l_4p[pmi][ap]; break; default: LOG_D(PHY, "Precoding 1,2, or 4 antenna ports are currently supported\n"); W_prec = nr_W_1l_2p[pmi][ap]; break; } for (int i = 0; i < NR_NB_SC_PER_RB; i++) { int32_t re_offset = l * frame_parms->ofdm_symbol_size + k; txdataF[ap][re_offset] = nr_layer_precoder(encoded_length, tx_precoding, W_prec, pusch_pdu->nrOfLayers, re_offset); if (++k >= frame_parms->ofdm_symbol_size) { k -= frame_parms->ofdm_symbol_size; } } } } // RB loop } // symbol loop } // port loop NR_UL_UE_HARQ_t *harq_process_ulsch = NULL; harq_process_ulsch = &UE->ul_harq_processes[harq_pid]; harq_process_ulsch->ULstatus = SCH_IDLE; /////////// //////////////////////////////////////////////////////////////////////// } uint8_t nr_ue_pusch_common_procedures(PHY_VARS_NR_UE *UE, const uint8_t slot, const NR_DL_FRAME_PARMS *frame_parms, const uint8_t n_antenna_ports, c16_t **txdataF, uint32_t linktype) { const int tx_offset = frame_parms->get_samples_slot_timestamp(slot, frame_parms, 0); int N_RB = (linktype == link_type_sl) ? frame_parms->N_RB_SL : frame_parms->N_RB_UL; c16_t **txdata = UE->common_vars.txData; for(int ap = 0; ap < n_antenna_ports; ap++) { apply_nr_rotation_TX(frame_parms, txdataF[ap], frame_parms->symbol_rotation[linktype], slot, N_RB, 0, NR_NUMBER_OF_SYMBOLS_PER_SLOT); } for (int ap = 0; ap < n_antenna_ports; ap++) { if (frame_parms->Ncp == 1) { // extended cyclic prefix PHY_ofdm_mod((int *)txdataF[ap], (int *)&txdata[ap][tx_offset], frame_parms->ofdm_symbol_size, 12, frame_parms->nb_prefix_samples, CYCLIC_PREFIX); } else { // normal cyclic prefix nr_normal_prefix_mod(txdataF[ap], &txdata[ap][tx_offset], 14, frame_parms, slot); } } /////////// //////////////////////////////////////////////////// return 0; }