From a64038658f66693808f6f3dde1e8747047118d53 Mon Sep 17 00:00:00 2001 From: alexjiao Date: Fri, 13 Jun 2025 15:56:05 +0800 Subject: [PATCH 01/11] Modify nr_uci_encoding for UCI on PUSCH 38.212 section 5.3.3.1 and 5.3.3.2 describe how to encode 1-bit and 2-bit information. This is needed when typically HARQ-ACK will be transmitted on PUSCH. 38.212 section 5.4.3 also describes how to do rate matching for channel coding of small block lengths. --- .../CODING/nrSmallBlock/encodeSmallBlock.c | 3 + openair1/PHY/NR_UE_TRANSPORT/pucch_nr.c | 285 ++++++++++++------ openair1/PHY/NR_UE_TRANSPORT/pucch_nr.h | 2 + 3 files changed, 194 insertions(+), 96 deletions(-) diff --git a/openair1/PHY/CODING/nrSmallBlock/encodeSmallBlock.c b/openair1/PHY/CODING/nrSmallBlock/encodeSmallBlock.c index 3078243c8f..a3a5863542 100644 --- a/openair1/PHY/CODING/nrSmallBlock/encodeSmallBlock.c +++ b/openair1/PHY/CODING/nrSmallBlock/encodeSmallBlock.c @@ -31,11 +31,14 @@ */ #include "PHY/CODING/nrSmallBlock/nr_small_block_defs.h" +#include "common/utils/assertions.h" //input = [0 ... 0 c_K-1 ... c_2 c_1 c_0] //output = [d_31 d_30 ... d_2 d_1 d_0] uint32_t encodeSmallBlock(int in, int len) { + AssertFatal(len >= 3, "encodeSmallBlock only supports input lengths A >= 3, got A=%d", len); + uint32_t out = 0; for (int i = 0; i < len; i++) if ((in & (1 << i)) > 0) diff --git a/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.c b/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.c index 611572a0a3..b4c65cf28e 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.c +++ b/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.c @@ -520,101 +520,143 @@ static inline void nr_pucch2_3_4_scrambling(uint16_t M_bit, uint16_t rnti, uint1 printf("\t\t [nr_pucch2_3_4_scrambling] scrambling M_bit=%d bits\n", M_bit); #endif } -static void nr_uci_encoding(uint64_t payload, - uint8_t nr_bit, - int fmt, - uint8_t is_pi_over_2_bpsk_enabled, - uint8_t nrofSymbols, - uint8_t nrofPRB, - uint8_t n_SF_PUCCH_s, - uint8_t intraSlotFrequencyHopping, - uint8_t add_dmrs, - uint64_t *b, - uint16_t *M_bit) { + +void nr_uci_encoding(uint64_t payload, uint8_t nr_bit, uint8_t nrofPRB, bool uci_on_pusch, uint16_t E, uint8_t Qm, uint64_t *b) +{ /* - * Implementing TS 38.212 Subclause 6.3.1.2 + * Implementing TS 38.212 Subclause 6.3.1.2 and 6.3.2 * */ // A is the payload size, to be provided in function call uint8_t A = nr_bit; // L is the CRC size - //uint8_t L; + // uint8_t L; // E is the rate matching output sequence length as given in TS 38.212 subclause 6.3.1.4.1 - uint16_t E=0,E_init; - - if (fmt == 2) E = 16*nrofSymbols*nrofPRB; - - if (fmt == 3) { - E_init = (is_pi_over_2_bpsk_enabled == 0) ? 24:12; - - if (nrofSymbols == 4) { - E = (intraSlotFrequencyHopping == 0)?(E_init*(nrofSymbols-1)*nrofPRB):((E_init*(nrofSymbols-1)*nrofPRB)); + // int I_seg; #ifdef DEBUG_NR_PUCCH_TX - printf("format 3 nrofSymbols =4 and E_init=%d,E=%d\n",E_init,E); + printf("\t\t [nr_uci_encoding] start function with encoding A=%d bits into M_bit=%d (where nrofPRB=%d)\n", A, E, nrofPRB); #endif - } - if (nrofSymbols > 4) { - E = E_init*(nrofSymbols-2)*nrofPRB; -#ifdef DEBUG_NR_PUCCH_TX - printf("format 3 nrofSymbols >4 and E_init=%d,E = %d\n",E_init,E); -#endif - } + // For A=1 case (single bit UCI) + if (A == 1) { + uint8_t uci_bit_val = payload & 1; // Extract the single UCI bit + uint64_t pattern_word; + uint8_t *b_bytes = (uint8_t *)b; // Access b as bytes to set individual special values - if (nrofSymbols > 9) { - E = (add_dmrs == 0)?(E_init*(nrofSymbols-2)*nrofPRB):((E_init*(nrofSymbols-4)*nrofPRB)); -#ifdef DEBUG_NR_PUCCH_TX - printf("format 3 nrofSymbols >9 and E_init=%d,E = %d\n",E_init,E); -#endif + if (Qm == 1) { + // For BPSK, just repeat the input bit + pattern_word = uci_bit_val ? 0xFFFFFFFFFFFFFFFFULL : 0x0000000000000000ULL; + for (int i = 0; i < 8; i++) { + b[i] = pattern_word; + } + } else { + // For higher order modulation (QPSK, etc.), use placeholder values + memset(b, 0, 8 * sizeof(uint64_t)); + + // Fill the entire output with the pattern + for (int i = 0; i < E; i++) { + switch (i % Qm) { + case 0: + // First bit in each group is actual UCI bit + b_bytes[i] = uci_bit_val; + LOG_D(PHY, + "[UCI_ENCODING_A1_QAM_LOOP] i=%d, i%%Qm=%d (case 0), writing uci_bit_val %d to b_bytes[%d]\n", + i, + i % Qm, + uci_bit_val, + i); + break; + case 1: + b_bytes[i] = NR_PUSCH_y; + LOG_D(PHY, + "[UCI_ENCODING_A1_QAM_LOOP] i=%d, i%%Qm=%d (case 1), writing NR_PUSCH_y (0x%02X) to b_bytes[%d]\n", + i, + i % Qm, + NR_PUSCH_y, + i); + break; + default: + b_bytes[i] = NR_PUSCH_x; + break; + } + } } } + // For A=2 case (two bits UCI) + else if (A == 2) { + uint8_t bit0 = (payload >> 0) & 1; + uint8_t bit1 = (payload >> 1) & 1; + uint8_t c2 = bit0 ^ bit1; // Parity bit (XOR of the two bits) + uint8_t *b_bytes = (uint8_t *)b; - if (fmt == 4) { - E_init = (is_pi_over_2_bpsk_enabled == 0) ? 24:12; + if (Qm == 1) { + // For BPSK, output is [bit0, bit1, c2] + uint64_t pattern = (bit0) | (bit1 << 1) | (c2 << 2); + // Repeat this 3-bit pattern to fill the output + pattern |= pattern << 3; + pattern |= pattern << 6; + pattern |= pattern << 12; + pattern |= pattern << 24; + pattern |= pattern << 48; - if (nrofSymbols == 4) { - E = (intraSlotFrequencyHopping == 0)?(E_init*(nrofSymbols-1)/n_SF_PUCCH_s):((E_init*(nrofSymbols-1)/n_SF_PUCCH_s)); -#ifdef DEBUG_NR_PUCCH_TX - printf("format 4 nrofSymbols =4 and E_init=%d,E=%d\n",E_init,E); -#endif + for (int i = 0; i < 8; i++) { + b[i] = pattern; + } + } else { + // For higher order modulation (Qm>=2), using patterns from Table 5.3.3.2-1 + // Pattern: 3 groups of Qm bits each = 3*Qm total length + memset(b, 0, 8 * sizeof(uint64_t)); + + for (int i = 0; i < E; i++) { + int pos_in_pattern = i % (3 * Qm); + int group = pos_in_pattern / Qm; + int pos_in_group = pos_in_pattern % Qm; + uint8_t val_to_write; + + if (pos_in_group == 0) { + if (group == 0) + val_to_write = bit0; + else if (group == 1) + val_to_write = c2; + else + val_to_write = bit1; + } else if (pos_in_group == 1) { + if (group == 0) + val_to_write = bit1; + else if (group == 1) + val_to_write = bit0; + else + val_to_write = c2; + } else { + // Positions 2 and beyond are x placeholders + val_to_write = NR_PUSCH_x; + } + + b_bytes[i] = val_to_write; + } } - - if (nrofSymbols > 4) { - E = E_init*(nrofSymbols-2)/n_SF_PUCCH_s; -#ifdef DEBUG_NR_PUCCH_TX - printf("format 4 nrofSymbols >4 and E_init=%d,E = %d\n",E_init,E); -#endif - } - - if (nrofSymbols > 9) { - E = (add_dmrs == 0)?(E_init*(nrofSymbols-2)/n_SF_PUCCH_s):((E_init*(nrofSymbols-4)/n_SF_PUCCH_s)); -#ifdef DEBUG_NR_PUCCH_TX - printf("format 4 nrofSymbols >9 and E_init=%d,E = %d\n",E_init,E); -#endif - } - } - - *M_bit = E; - //int I_seg; -#ifdef DEBUG_NR_PUCCH_TX - printf("\t\t [nr_uci_encoding] start function with fmt=%d, encoding A=%d bits into M_bit=%d (where nrofSymbols=%d,nrofPRB=%d)\n",fmt,A,*M_bit,nrofSymbols,nrofPRB); -#endif - - if (A<=11) { + } else if (A <= 11) { // procedure in subclause 6.3.1.2.2 (UCI encoded by channel coding of small block lengths -> subclause 6.3.1.3.2) // CRC bits are not attached, and coding small block lengths (subclause 5.3.3) uint64_t b0 = encodeSmallBlock(payload, A); - // repetition for rate-matching up to 16 PRB - b[0] = b0 | (b0<<32); - b[1] = b[0]; - b[2] = b[0]; - b[3] = b[0]; - b[4] = b[0]; - b[5] = b[0]; - b[6] = b[0]; - b[7] = b[0]; - AssertFatal(nrofPRB<=16,"Number of PRB >16\n"); - } else if (A>=12) { + if (uci_on_pusch) { + b[0] = b0; + for (int i = 1; i < 8; i++) { + b[i] = 0; + } + } else { + // repetition for rate-matching up to 16 PRB + b[0] = b0 | (b0<<32); + b[1] = b[0]; + b[2] = b[0]; + b[3] = b[0]; + b[4] = b[0]; + b[5] = b[0]; + b[6] = b[0]; + b[7] = b[0]; + AssertFatal(nrofPRB<=16,"Number of PRB >16\n"); + } + } else if (A >= 12) { // Encoder reversal payload = reverse_bits(payload, A); @@ -623,7 +665,52 @@ static void nr_uci_encoding(uint64_t payload, A, nrofPRB); } - + + if (uci_on_pusch) { + // Rate matching for HARQ ACK following 38.212 section 5.4.3 + uint64_t output[8] = {0}; // Assuming max 512 bits (8 words of 64 bits) + uint16_t N; + if (nr_bit <= 2) { + // For A=1 (BPSK), N=1. For A=2 (QPSK), N=3 + N = (nr_bit == 1) ? 1 : 3; + } else if (nr_bit <= 11) { + // For 3 <= A <= 11, the small block encoder produces a 32-bit codeword + N = 32; + } else { + // For polar-coded UCI, output depends on nrofPRB + N = 16 * nrofPRB; + } + + if ((nr_bit == 1 || nr_bit == 2) && Qm > 1) { + LOG_D(PHY, + "[UCI_ENCODING_RM] Bypassing bit-wise rate matching for A=%d, Qm=%d. 'b' (length %d bytes) is assumed to be already " + "final.\n", + nr_bit, + Qm, + E); + } else { + if (N == 0) { + LOG_W(PHY, "HARQ-ACK rate matching with encoded_length=0 but E_uci_ack=%d\n", E); + return; + } + + // Rate matching with single loop for both repetition and puncturing + for (int i = 0; i < E; i++) { + int src_bit = i % N; // Modulo for cyclic repetition + int src_word = src_bit / 64; + int src_bit_pos = src_bit % 64; + int dst_word = i / 64; + int dst_bit_pos = i % 64; + + if ((b[src_word] >> src_bit_pos) & 1ULL) + output[dst_word] |= (1ULL << dst_bit_pos); + } + + for (int i = 0; i < (E + 63) / 64; i++) { + b[i] = output[i]; + } + } + } } //#if 0 void nr_generate_pucch2(const PHY_VARS_NR_UE *ue, @@ -639,13 +726,8 @@ void nr_generate_pucch2(const PHY_VARS_NR_UE *ue, // b is the block of bits transmitted on the physical channel after payload coding uint64_t b[16] = {0}; // limit to 1024-bit encoded length // M_bit is the number of bits of block b (payload after encoding) - uint16_t M_bit = 0; - nr_uci_encoding(pucch_pdu->payload, - pucch_pdu->n_bit, - 2,0, - pucch_pdu->nr_of_symbols, - pucch_pdu->prb_size, - 1,0,0,&b[0],&M_bit); + uint16_t M_bit = 16 * pucch_pdu->nr_of_symbols * pucch_pdu->prb_size; + nr_uci_encoding(pucch_pdu->payload, pucch_pdu->n_bit, pucch_pdu->prb_size, false, M_bit, 0, &b[0]); /* * Implementing TS 38.211 * Subclauses 6.3.2.5.1 Scrambling (PUCCH format 2) @@ -829,7 +911,7 @@ void nr_generate_pucch3_4(const PHY_VARS_NR_UE *ue, // b is the block of bits transmitted on the physical channel after payload coding uint64_t b[16]; // M_bit is the number of bits of block b (payload after encoding) - uint16_t M_bit; + uint16_t M_bit = 0; // parameter PUCCH-F4-preDFT-OCC-length set of {2,4} -> to use table -1 or -2 // in format 4, n_SF_PUCCH_s = {2,4}, provided by higher layer parameter PUCCH-F4-preDFT-OCC-length (in format 3 n_SF_PUCCH_s=1) uint8_t n_SF_PUCCH_s; @@ -856,17 +938,28 @@ void nr_generate_pucch3_4(const PHY_VARS_NR_UE *ue, uint16_t startingPRB = pucch_pdu->prb_start + pucch_pdu->bwp_start; uint8_t add_dmrs = pucch_pdu->add_dmrs_flag; - nr_uci_encoding(pucch_pdu->payload, - pucch_pdu->n_bit, - pucch_pdu->format_type, - is_pi_over_2_bpsk_enabled, - nrofSymbols, - nrofPRB, - n_SF_PUCCH_s, - intraSlotFrequencyHopping, - add_dmrs, - b, - &M_bit); + uint16_t E_init = (is_pi_over_2_bpsk_enabled == 0) ? 24 : 12; + + if (pucch_pdu->format_type == 3) { + if (nrofSymbols == 4) { + M_bit = (intraSlotFrequencyHopping == 0) ? (E_init * (nrofSymbols - 1) * nrofPRB) : (E_init * (nrofSymbols - 1) * nrofPRB); + } else if (nrofSymbols > 4) { + M_bit = E_init * (nrofSymbols - 2) * nrofPRB; + } else if (nrofSymbols > 9) { + M_bit = (add_dmrs == 0) ? (E_init * (nrofSymbols - 2) * nrofPRB) : (E_init * (nrofSymbols - 4) * nrofPRB); + } + } else if (pucch_pdu->format_type == 4) { + if (nrofSymbols == 4) { + M_bit = (intraSlotFrequencyHopping == 0) ? (E_init * (nrofSymbols - 1) / n_SF_PUCCH_s) + : (E_init * (nrofSymbols - 1) / n_SF_PUCCH_s); + } else if (nrofSymbols > 4) { + M_bit = E_init * (nrofSymbols - 2) / n_SF_PUCCH_s; + } else if (nrofSymbols > 9) { + M_bit = (add_dmrs == 0) ? (E_init * (nrofSymbols - 2) / n_SF_PUCCH_s) : (E_init * (nrofSymbols - 4) / n_SF_PUCCH_s); + } + } + + nr_uci_encoding(pucch_pdu->payload, pucch_pdu->n_bit, nrofPRB, false, M_bit, 0, b); /* * Implementing TS 38.211 * Subclauses 6.3.2.6.1 Scrambling (PUCCH formats 3 and 4) diff --git a/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.h b/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.h index 97be0e274f..1f24b589f4 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.h +++ b/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.h @@ -72,6 +72,8 @@ void nr_generate_pucch3_4(const PHY_VARS_NR_UE *ue, const int nr_slot_tx, const fapi_nr_ul_config_pucch_pdu *pucch_pdu); +void nr_uci_encoding(uint64_t payload, uint8_t nr_bit, uint8_t nrofPRB, bool uci_on_pusch, uint16_t E, uint8_t Qm, uint64_t *b); + static const uint8_t list_of_prime_numbers[46] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101,103,107,109,113, From 8222fa27e5a2e358e351cb522122cc8433dfc3c2 Mon Sep 17 00:00:00 2001 From: alexjiao Date: Fri, 13 Jun 2025 16:31:45 +0800 Subject: [PATCH 02/11] Add functions to calculate rate matching information for UCI on PUSCH Use betaOffset and alpha scaling etc. to get rate matching information of UCI on PUSCH (e.g. max bit capacity of encoded UCI and encoded ULSCH) according to TS 38.212 section 6.3.2.4 and 6.2.7. --- .../PHY/NR_UE_TRANSPORT/nr_transport_ue.h | 9 + openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c | 169 ++++++++++++++++++ 2 files changed, 178 insertions(+) diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_ue.h b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_ue.h index c63754b583..7252663d41 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_ue.h +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_ue.h @@ -135,4 +135,13 @@ typedef struct { // PTRS symbol index, to be updated every PTRS symbol within a slot. uint8_t ptrs_symbol_index; } NR_UE_DLSCH_t; + +typedef struct { + uint16_t Q_dash_ACK; // number of coded HARQ-ACK symbols + uint16_t E_uci_ACK; // number of coded HARQ-ACK bits + uint16_t Q_dash_ACK_rvd; // number of coded HARQ-ACK symbols reserved + uint16_t E_uci_ACK_rvd; // number of coded HARQ-ACK bits reserved + uint32_t G_ulsch; // bit capacity of ULSCH +} rate_match_info_uci_t; + #endif diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c index 435716919a..bd51b0641c 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c @@ -472,6 +472,175 @@ static void map_symbols(const nr_phy_pxsch_params_t p, } } +// Function to lookup beta offset value from Table 9.3-1 in TS 38.213 +static double get_beta_offset_harq_ack(uint8_t beta_offset_index) +{ + static const double beta_offset_values[21] = { + 1.000, // Index 0 + 2.000, // Index 1 + 2.500, // Index 2 + 3.125, // Index 3 + 4.000, // Index 4 + 5.000, // Index 5 + 6.250, // Index 6 + 8.000, // Index 7 + 10.000, // Index 8 + 12.625, // Index 9 + 15.875, // Index 10 + 20.000, // Index 11 + 31.000, // Index 12 + 50.000, // Index 13 + 80.000, // Index 14 + 126.000, // Index 15 + 0.6, // Index 16 + 0.4, // Index 17 + 0.2, // Index 18 + 0.1, // Index 19 + 0.05, // Index 20 + }; + + if (beta_offset_index > 20) { + LOG_E(PHY, "Invalid beta_offset_index %d, using default value\n", beta_offset_index); + return 20.000; // Default value using index 11 + } + + return beta_offset_values[beta_offset_index]; +} + +static double get_alpha_scaling_value(uint8_t alpha_scaling) +{ + switch (alpha_scaling) { + case 0: + return 0.5; + case 1: + return 0.65; + case 2: + return 0.8; + case 3: + return 1.0; + default: + AssertFatal(false, "Invalid alpha_scaling value %d, valid range is 0-3", alpha_scaling); + return 1.0; + } +} + +/* + * This function gets the CRC size of UCI + */ +static int get_crc_uci(const uint16_t ouci) +{ + int L = 0; + if (ouci > 19) { + L = 11; + } else if (ouci > 11) { + L = 6; + } else { + L = 0; // no ACK/NACK + } + + return L; +} + +static uint16_t get_Qd(const uint16_t oack, double beta, double alpha, const uint32_t sumKr, const uint32_t s1, const uint32_t s2) +{ + if (oack == 0) + return 0; + + uint16_t first_term = ceil(((double)oack + get_crc_uci(oack)) * (double)beta * s1 / sumKr); + uint16_t second_term = ceil(alpha * s2); + + return (first_term < second_term) ? first_term : second_term; +} + +/* + * This function calculates the rate matching information for UCI multiplexing with PUSCH + */ +static rate_match_info_uci_t calc_rate_match_info_uci(const nfapi_nr_ue_pusch_pdu_t *pusch_pdu, + const NR_UL_UE_HARQ_t *harq_process_ul_ue, + const uint8_t nlqm, + unsigned int *G) +{ + // get beta offset + uint8_t beta_offset_index = pusch_pdu->pusch_uci.beta_offset_harq_ack; + double beta = get_beta_offset_harq_ack(beta_offset_index); + + // get alpha scaling value + uint8_t alpha_scaling = pusch_pdu->pusch_uci.alpha_scaling; + double alpha = get_alpha_scaling_value(alpha_scaling); + + // Calculate sumKr (total bits in all code blocks) + uint32_t sumKr = 0; + if (harq_process_ul_ue->C == 0) { + sumKr = 0; + } else if (harq_process_ul_ue->C == 1) { + sumKr = harq_process_ul_ue->K; + } else { + sumKr = harq_process_ul_ue->K * harq_process_ul_ue->C; + } + + // Calculate s1: total number of non-DMRS REs in allocation + uint16_t nb_rb = pusch_pdu->rb_size; + uint8_t start_symbol = pusch_pdu->start_symbol_index; + uint8_t number_of_symbols = pusch_pdu->nr_of_symbols; + uint16_t ul_dmrs_symb_pos = pusch_pdu->ul_dmrs_symb_pos; + + uint32_t s1 = 0; + for (int l = start_symbol; l < start_symbol + number_of_symbols; l++) { + if (!((ul_dmrs_symb_pos >> l) & 0x01)) { + s1 += nb_rb * NR_NB_SC_PER_RB; + } + } + + // Calculate s2: number of non-DMRS REs after first DMRS symbol + int first_dmrs_symbol = -1; + for (int l = start_symbol; l < start_symbol + number_of_symbols; l++) { + if ((ul_dmrs_symb_pos >> l) & 0x01) { + first_dmrs_symbol = l; + break; + } + } + int l0 = -1; + if (first_dmrs_symbol >= 0 && first_dmrs_symbol < start_symbol + number_of_symbols - 1) { + l0 = first_dmrs_symbol + 1; + } + uint32_t s2 = 0; + for (int l = l0; l < start_symbol + number_of_symbols; l++) { + if (!((ul_dmrs_symb_pos >> l) & 0x01)) { + s2 += nb_rb * NR_NB_SC_PER_RB; + } + } + + uint16_t oack = pusch_pdu->pusch_uci.harq_ack_bit_length; + uint16_t oack_rvd = (oack <= 2) ? 2 : 0; // get the reserved bits when oACK <= 2 according to TS 38.212 section 6.2.7, step 1 + + rate_match_info_uci_t rminfo = {0}; + + // get the number of coded HARQ-ACK symbols and bits, TS 38.212 section 6.3.2.4.1.1 + rminfo.Q_dash_ACK = get_Qd(oack, beta, alpha, sumKr, s1, s2); + rminfo.E_uci_ACK = rminfo.Q_dash_ACK * nlqm; + + if (oack_rvd > 0) { + rminfo.Q_dash_ACK_rvd = get_Qd(oack_rvd, beta, alpha, sumKr, s1, s2); + rminfo.E_uci_ACK_rvd = rminfo.Q_dash_ACK_rvd * nlqm; + } + + if (oack_rvd == 0) { + rminfo.G_ulsch = *G - rminfo.E_uci_ACK; + } else { + rminfo.G_ulsch = *G; + } + + *G = rminfo.G_ulsch; + LOG_D(PHY, "[UCI_RATE_MATCH] sumKr=%u, s1=%u, s2=%u, Final G_ulsch (output G): %u\n", sumKr, s1, s2, *G); + LOG_D(PHY, + "[UCI_RATE_MATCH] rate matching info returned: E_uci_ACK=%u, E_uci_ACK_rvd=%u, G_ulsch=%u\n", + rminfo.E_uci_ACK, + rminfo.E_uci_ACK_rvd, + rminfo.G_ulsch); + + return rminfo; +} + void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, const uint32_t frame, const uint8_t slot, From ceda9c11412a465c47b4dc092086eca4dcac0555 Mon Sep 17 00:00:00 2001 From: alexjiao Date: Fri, 13 Jun 2025 19:58:41 +0800 Subject: [PATCH 03/11] Add functions to implement UCI and data multiplexing on PUSCH Data and control multiplexing procedure is defined in 38.212 section 6.2.7. Step 3 (CSI1 and CSI2 mapping) is not implemented, only HARQ-ACK. PUSCH frequency hopping is not supported. --- .../NR_UE_TRANSPORT/nr_transport_proto_ue.h | 13 +- openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c | 371 ++++++++++++++++++ 2 files changed, 382 insertions(+), 2 deletions(-) diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h index 44a3611bf0..8a08b97121 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h @@ -41,6 +41,15 @@ #define NR_PUSCH_x 2 // UCI placeholder bit TS 38.212 V15.4.0 subclause 5.3.3.1 #define NR_PUSCH_y 3 // UCI placeholder bit +typedef enum { + BIT_TYPE_ULSCH = 0, // Default: UL-SCH data + BIT_TYPE_ACK = 1, // HARQ-ACK bit + BIT_TYPE_ACK_RESERVED = 2, // Reserved for HARQ-ACK (punctured) + BIT_TYPE_ACK_ULSCH = 3, + BIT_TYPE_CSI1 = 4, // CSI Part 1 bit + BIT_TYPE_CSI2 = 5 // CSI Part 2 bit +} uci_on_pusch_bit_type_t; + // Specifies the data that should be copied to the scope during PDSCH RX typedef struct pdsch_scope_req_s { bool copy_chanest_to_scope; @@ -121,8 +130,8 @@ void nr_pusch_codeword_scrambling(uint8_t *in, uint32_t Nid, uint32_t n_RNTI, bool uci_on_pusch, - uint32_t* out); - + const uci_on_pusch_bit_type_t *template, + uint32_t *out); /** \brief Alternative entry point to UE uplink shared channels procedures. It handles all the HARQ processes in only one call. diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c index bd51b0641c..6091746ef2 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c @@ -49,6 +49,12 @@ #include "PHY/NR_REFSIG/ul_ref_seq_nr.h" #include #include "PHY/log_tools.h" +#include "PHY/NR_UE_TRANSPORT/pucch_nr.h" +#include + +#define MAX_RE_PER_SYMBOL_IN_ALLOC (275 * 12) +#define MAX_NLQM (4 * 8) +#define MAX_UCI_CODED_BITS 1024 //#define DEBUG_PUSCH_MAPPING //#define DEBUG_MAC_PDU @@ -641,6 +647,371 @@ static rate_match_info_uci_t calc_rate_match_info_uci(const nfapi_nr_ue_pusch_pd return rminfo; } +static int initialize_mapping_resources(const nfapi_nr_ue_pusch_pdu_t *pusch_pdu, + uint32_t *m_ulsch_initial, + uint32_t *m_ulsch_current, + uint32_t *m_uci_current, + bool *is_dmrs_symbol_flags, + uint8_t *dmrs_symbol_set_relative, + uint8_t *num_dmrs_symbols_in_set_relative) +{ + if (!pusch_pdu || !m_ulsch_initial || !m_ulsch_current || !m_uci_current || !is_dmrs_symbol_flags || !dmrs_symbol_set_relative + || !num_dmrs_symbols_in_set_relative) + return -1; + + uint8_t n_pusch_sym_all = pusch_pdu->nr_of_symbols; + uint16_t ul_dmrs_symb_pos = pusch_pdu->ul_dmrs_symb_pos; + uint8_t dmrs_type = pusch_pdu->dmrs_config_type; + uint8_t cdm_grps_no_data = pusch_pdu->num_dmrs_cdm_grps_no_data; + uint32_t res_per_symbol_non_dmrs = pusch_pdu->rb_size * 12; + + *num_dmrs_symbols_in_set_relative = 0; + + // Initialize resources per symbol for ULSCH and UCI + for (uint8_t i = 0; i < n_pusch_sym_all; i++) { + uint8_t absolute_symbol_idx = pusch_pdu->start_symbol_index + i; + + if ((ul_dmrs_symb_pos >> absolute_symbol_idx) & 0x01) { + is_dmrs_symbol_flags[i] = true; + if (*num_dmrs_symbols_in_set_relative < 14) { + dmrs_symbol_set_relative[(*num_dmrs_symbols_in_set_relative)++] = i; + } + + // Calculate available data REs on DMRS symbols based on DMRS configuration + uint32_t data_re_on_dmrs_sym_per_prb = 0; + + if (dmrs_type == 0) { // Type 1 + if (cdm_grps_no_data == 1) { + data_re_on_dmrs_sym_per_prb = 6; + } else { + data_re_on_dmrs_sym_per_prb = 0; + } + } else { // Type 2 + if (cdm_grps_no_data == 1) { + data_re_on_dmrs_sym_per_prb = 4; + } else if (cdm_grps_no_data == 2) { + data_re_on_dmrs_sym_per_prb = 2; + } else { + data_re_on_dmrs_sym_per_prb = 0; + } + } + + m_ulsch_initial[i] = pusch_pdu->rb_size * data_re_on_dmrs_sym_per_prb; + m_ulsch_current[i] = m_ulsch_initial[i]; + m_uci_current[i] = 0; // UCI is not mapped on DMRS symbols + + } else { // Not a DMRS symbol + is_dmrs_symbol_flags[i] = false; + + m_ulsch_initial[i] = res_per_symbol_non_dmrs; + m_ulsch_current[i] = res_per_symbol_non_dmrs; + m_uci_current[i] = res_per_symbol_non_dmrs; + } + } + + return 0; +} + +static uint8_t find_first_uci_symbol(uint8_t n_pusch_sym_all, + const bool *is_dmrs_symbol_flags, + uint8_t *dmrs_symbol_set_relative, + uint32_t num_dmrs_symbols_in_set_relative) +{ + uint8_t l1_c = 0; + + // Find first non-DMRS symbol + for (uint8_t i = 0; i < n_pusch_sym_all; i++) { + if (!is_dmrs_symbol_flags[i]) { + l1_c = i; + break; + } + } + + // If there are DMRS symbols, try to find a symbol after the first DMRS + if (num_dmrs_symbols_in_set_relative > 0) { + uint8_t first_dmrs_idx = dmrs_symbol_set_relative[0]; + if (l1_c <= first_dmrs_idx) { + for (uint8_t i = first_dmrs_idx + 1; i < n_pusch_sym_all; i++) { + if (!is_dmrs_symbol_flags[i]) { + l1_c = i; + break; + } + } + } + } + + return l1_c; +} + +/* + * This function builds the initial template by reserving positions for HARQ-ACK. + */ +static void build_template_reserve_ack(uci_on_pusch_bit_type_t *template, + const nfapi_nr_ue_pusch_pdu_t *pusch_pdu, + uint32_t G_ack_rvd, + uint8_t l1_c, + const uint32_t *m_uci_current, + const uint32_t *m_ulsch_initial, + uint32_t positions_by_sym[][MAX_UCI_CODED_BITS], + uint32_t *count_by_sym) +{ + const uint8_t n_symbols = pusch_pdu->nr_of_symbols; + const uint32_t nlqm = pusch_pdu->qam_mod_order * pusch_pdu->nrOfLayers; + + memset(count_by_sym, 0, n_symbols * sizeof(uint32_t)); + + uint32_t symbol_start_bit_idx[14] = {0}; + for (uint8_t s = 1; s < n_symbols; s++) { + symbol_start_bit_idx[s] = symbol_start_bit_idx[s - 1] + (m_ulsch_initial[s - 1] * nlqm); + } + + // Reserve Positions using RE-level D-Factor Distribution + uint32_t total_reserved = 0; + + for (uint8_t sym = l1_c; sym < n_symbols && total_reserved < G_ack_rvd; sym++) { + const uint32_t uci_re_on_sym = m_uci_current[sym]; + + if (uci_re_on_sym > 0) { + const uint32_t remaining_to_reserve = G_ack_rvd - total_reserved; + uint32_t d_factor_re; + const uint32_t num_re_to_select = ceil((double)remaining_to_reserve / nlqm); + if (num_re_to_select >= uci_re_on_sym) { + d_factor_re = 1; + } else { + d_factor_re = floor((double)uci_re_on_sym / num_re_to_select); + if (d_factor_re == 0) { + d_factor_re = 1; + } + } + + for (uint32_t re_offset = 0; re_offset < uci_re_on_sym && total_reserved < G_ack_rvd; re_offset += d_factor_re) { + for (uint32_t bit_in_re = 0; bit_in_re < nlqm; bit_in_re++) { + if (total_reserved >= G_ack_rvd) { + break; + } + + uint32_t bit_offset_in_sym = (re_offset * nlqm) + bit_in_re; + uint32_t cw_idx = symbol_start_bit_idx[sym] + bit_offset_in_sym; + template[cw_idx] = BIT_TYPE_ACK_RESERVED; + positions_by_sym[sym][count_by_sym[sym]] = cw_idx; + count_by_sym[sym]++; + + total_reserved++; + } + } + } + } +} + +/* + * This function maps the HARQ-ACK bits when O_ACK > 2 + */ +static void map_non_overlapped_ack(uci_on_pusch_bit_type_t *template, + const nfapi_nr_ue_pusch_pdu_t *pusch_pdu, + uint16_t G_ack, + uint8_t l1_c, + const uint32_t *m_uci_current, + const uint32_t *m_ulsch_initial) +{ + const uint8_t n_symbols = pusch_pdu->nr_of_symbols; + const uint32_t nlqm = pusch_pdu->qam_mod_order * pusch_pdu->nrOfLayers; + + uint32_t symbol_start_bit_idx[14] = {0}; + for (uint8_t s = 1; s < n_symbols; s++) { + symbol_start_bit_idx[s] = symbol_start_bit_idx[s - 1] + (m_ulsch_initial[s - 1] * nlqm); + } + + uint32_t total_placed = 0; + for (uint8_t sym = l1_c; sym < n_symbols && total_placed < G_ack; sym++) { + const uint32_t uci_re_on_sym = m_uci_current[sym]; + + if (uci_re_on_sym > 0) { + const uint32_t remaining_to_place = G_ack - total_placed; + uint32_t d_factor_re; + const uint32_t num_re_to_select = ceil((double)remaining_to_place / nlqm); + + if (num_re_to_select >= uci_re_on_sym) { + d_factor_re = 1; + } else { + d_factor_re = floor((double)uci_re_on_sym / num_re_to_select); + if (d_factor_re == 0) { + d_factor_re = 1; + } + } + + for (uint32_t re_offset = 0; re_offset < uci_re_on_sym && total_placed < G_ack; re_offset += d_factor_re) { + for (uint32_t bit_in_re = 0; bit_in_re < nlqm; bit_in_re++) { + if (total_placed >= G_ack) { + break; + } + + uint32_t bit_offset_in_sym = (re_offset * nlqm) + bit_in_re; + uint32_t cw_idx = symbol_start_bit_idx[sym] + bit_offset_in_sym; + template[cw_idx] = BIT_TYPE_ACK; + + total_placed++; + } + } + } + } +} + +/* + * This function maps the HARQ-ACK bits when O_ACK <= 2 + */ +static void map_overlapped_ack(uci_on_pusch_bit_type_t *template, + uint16_t G_ack, + uint8_t l1_c, + uint8_t n_symbols, + uint32_t positions_by_sym[][MAX_UCI_CODED_BITS], + const uint32_t *count_by_sym) +{ + uint32_t ack_bits_marked = 0; + + for (uint8_t sym_iter = l1_c; sym_iter < n_symbols && ack_bits_marked < G_ack; sym_iter++) { + const uint32_t num_reserved_bits_on_sym = count_by_sym[sym_iter]; + + if (num_reserved_bits_on_sym > 0) { + const uint32_t num_ack_remaining = G_ack - ack_bits_marked; + uint32_t d_factor; + + // This d-factor is calculated for stepping through the list of *reserved bits*. + if (num_ack_remaining >= num_reserved_bits_on_sym) { + d_factor = 1; + } else { + d_factor = floor((double)num_reserved_bits_on_sym / num_ack_remaining); + if (d_factor == 0) { + d_factor = 1; + } + } + + const uint32_t *reserved_indices_on_this_sym = positions_by_sym[sym_iter]; + + for (uint32_t i = 0; i < num_reserved_bits_on_sym && ack_bits_marked < G_ack; i += d_factor) { + uint32_t pos_to_mark = reserved_indices_on_this_sym[i]; + template[pos_to_mark] = BIT_TYPE_ACK_ULSCH; + + ack_bits_marked++; + } + } + } +} + +/* + * Applies the template to build the final codeword + */ +static void apply_template_to_codeword(uint8_t *codeword, + const uci_on_pusch_bit_type_t *template, + uint32_t codeword_len, + const uint8_t *ulsch_bits, + const uint64_t *cack, + uint16_t G_ack, + uint32_t G_ulsch) +{ + uint32_t ulsch_idx = 0; + uint32_t ack_idx = 0; + + for (uint32_t i = 0; i < codeword_len; i++) { + switch (template[i]) { + case BIT_TYPE_ACK: + if (G_ack > 0 && ack_idx < G_ack) { + uint32_t word_idx = ack_idx / 64; + uint32_t bit_in_word_idx = ack_idx % 64; + codeword[i] = (cack[word_idx] >> bit_in_word_idx) & 1; + ack_idx++; + } + break; + + case BIT_TYPE_ACK_ULSCH: + if (G_ack > 0 && ack_idx < G_ack) { + codeword[i] = ((const uint8_t *)cack)[ack_idx++]; + if (G_ulsch > 0 && ulsch_idx < G_ulsch) { + ulsch_idx++; + } + } + break; + + case BIT_TYPE_ACK_RESERVED: + case BIT_TYPE_ULSCH: + default: + if (G_ulsch > 0 && ulsch_idx < G_ulsch) { + uint32_t byte_idx = ulsch_idx / 8; + uint32_t bit_in_byte_idx = ulsch_idx % 8; + codeword[i] = (ulsch_bits[byte_idx] >> bit_in_byte_idx) & 1; + ulsch_idx++; + } + break; + } + } +} + +/* + * This function implements the UCI multiplexing on PUSCH according to TS 38.212 section 6.2.7. + */ +static uci_on_pusch_bit_type_t *nr_data_control_mapping(const nfapi_nr_ue_pusch_pdu_t *pusch_pdu, + uci_on_pusch_bit_type_t *template, + unsigned int G_ulsch, + uint16_t G_ack, + uint32_t G_ack_rvd, + uint8_t *codeword, + uint32_t codeword_len, + const uint8_t *ulsch_bits, + const uint64_t *cack) +{ + if (!pusch_pdu || !codeword || codeword_len == 0 || !template) + return NULL; + const uint8_t n_symbols = pusch_pdu->nr_of_symbols; + if (n_symbols == 0 || n_symbols > 14) + return NULL; + + uint32_t m_ulsch_initial[14] = {0}; + uint32_t m_ulsch_current[14] = {0}; + uint32_t m_uci_current[14] = {0}; // This holds RE counts, not bit counts + bool is_dmrs_symbol_flags[14] = {0}; + uint8_t dmrs_symbol_set_relative[14] = {0}; + uint8_t num_dmrs_symbols_in_set_relative = 0; + + if (initialize_mapping_resources(pusch_pdu, + m_ulsch_initial, + m_ulsch_current, + m_uci_current, + is_dmrs_symbol_flags, + dmrs_symbol_set_relative, + &num_dmrs_symbols_in_set_relative) + != 0) { + LOG_E(PHY, "Failed to initialize mapping resources\n"); + return NULL; + } + + uint8_t l1_c = find_first_uci_symbol(n_symbols, is_dmrs_symbol_flags, dmrs_symbol_set_relative, num_dmrs_symbols_in_set_relative); + + memset(template, 0, codeword_len * sizeof(uci_on_pusch_bit_type_t)); + + uint32_t positions_by_sym[14][MAX_UCI_CODED_BITS] = {0}; + uint32_t count_by_sym[14] = {0}; + + if (G_ack_rvd > 0) { + build_template_reserve_ack(template, + pusch_pdu, + G_ack_rvd, + l1_c, + m_uci_current, + m_ulsch_initial, + positions_by_sym, + count_by_sym); + } else if (G_ack > 0) { + map_non_overlapped_ack(template, pusch_pdu, G_ack, l1_c, m_uci_current, m_ulsch_initial); + } + + if (G_ack > 0 && G_ack_rvd > 0) { + map_overlapped_ack(template, G_ack, l1_c, n_symbols, positions_by_sym, count_by_sym); + } + + apply_template_to_codeword(codeword, template, codeword_len, ulsch_bits, cack, G_ack, G_ulsch); + + return template; +} + void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, const uint32_t frame, const uint8_t slot, From 5f3a19cb7d83dd7c83c3a801ae4952a1a8a0d7e0 Mon Sep 17 00:00:00 2001 From: Sakthivel Velumani Date: Fri, 15 Aug 2025 21:20:53 -0400 Subject: [PATCH 04/11] Separate segmentation from ULSCH encoding function Segmentation have to be called before UCI mapping to get number of segments and sumKr values. --- .../NR_UE_TRANSPORT/nr_transport_proto_ue.h | 7 + .../PHY/NR_UE_TRANSPORT/nr_transport_ue.h | 2 + .../PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c | 153 +++++++++--------- openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c | 4 +- openair1/SIMULATION/NR_PHY/ulschsim.c | 1 + 5 files changed, 92 insertions(+), 75 deletions(-) diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h index 8a08b97121..c257726501 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h @@ -95,6 +95,13 @@ void nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue, int nb_dlsch, uint8_t *DLSCH_ids); +int nr_ulsch_pre_encoding(PHY_VARS_NR_UE *ue, + const NR_UE_ULSCH_t *ulsch, + const uint32_t frame, + const uint8_t slot, + const unsigned int *G, + const int nb_ulsch, + const uint8_t *ULSCH_ids); /** \brief This is the alternative top-level entry point for ULSCH encoding in UE. It handles all the HARQ processes in only one call. The routine first computes the segmentation information, followed by LDPC encoding algorithm of the diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_ue.h b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_ue.h index 7252663d41..26f41b1fd3 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_ue.h +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_ue.h @@ -55,6 +55,8 @@ typedef struct { uint32_t C; /// Number of bits in code segments uint32_t K; + /// + uint32_t Kb; /// Number of "Filler" bits uint32_t F; /// Number of soft channel bits diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c index b044d06305..d68ae6e9ae 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c @@ -34,56 +34,27 @@ #include "common/utils/LOG/vcd_signal_dumper.h" #include "PHY/log_tools.h" -int nr_ulsch_encoding(PHY_VARS_NR_UE *ue, - NR_UE_ULSCH_t *ulsch, - const uint32_t frame, - const uint8_t slot, - unsigned int *G, - int nb_ulsch, - uint8_t *ULSCH_ids, - uint16_t number_dmrs_symbols) +int nr_ulsch_pre_encoding(PHY_VARS_NR_UE *ue, + const NR_UE_ULSCH_t *ulsch, + const uint32_t frame, + const uint8_t slot, + const unsigned int *G, + const int nb_ulsch, + const uint8_t *ULSCH_ids) { - start_meas_nr_ue_phy(ue, ULSCH_ENCODING_STATS); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_UE_ULSCH_ENCODING, VCD_FUNCTION_IN); - - nrLDPC_TB_encoding_parameters_t TBs[nb_ulsch]; - memset(TBs, 0, sizeof(TBs)); - nrLDPC_slot_encoding_parameters_t slot_parameters = { - .frame = frame, - .slot = slot, - .nb_TBs = nb_ulsch, - .threadPool = &get_nrUE_params()->Tpool, - .tinput = NULL, - .tprep = NULL, - .tparity = NULL, - .toutput = NULL, - .TBs = TBs - }; - - int max_num_segments = 0; - - for (uint8_t pusch_id = 0; pusch_id < nb_ulsch; pusch_id++) { - uint8_t ULSCH_id = ULSCH_ids[pusch_id]; - uint8_t harq_pid = ulsch[ULSCH_id].pusch_pdu.pusch_data.harq_process_id; - - nrLDPC_TB_encoding_parameters_t *TB_parameters = &TBs[pusch_id]; - /* Neither harq_pid nor ULSCH_id are unique in the instance - * but their combination is. - * Since ULSCH_id < 2 - * then 2 * harq_pid + ULSCH_id is unique. - */ - TB_parameters->harq_unique_pid = 2 * harq_pid + ULSCH_id; + for (uint_fast8_t pusch_id = 0; pusch_id < nb_ulsch; pusch_id++) { + const uint8_t ULSCH_id = ULSCH_ids[pusch_id]; + const uint8_t harq_pid = ulsch[ULSCH_id].pusch_pdu.pusch_data.harq_process_id; /////////////////////////parameters and variables initialization///////////////////////// - unsigned int crc = 1; NR_UL_UE_HARQ_t *harq_process = &ue->ul_harq_processes[harq_pid]; const nfapi_nr_ue_pusch_pdu_t *pusch_pdu = &ulsch->pusch_pdu; - uint16_t nb_rb = pusch_pdu->rb_size; - uint32_t A = pusch_pdu->pusch_data.tb_size << 3; - uint8_t Qm = pusch_pdu->qam_mod_order; + const uint16_t nb_rb = pusch_pdu->rb_size; + const uint32_t A = pusch_pdu->pusch_data.tb_size << 3; + const uint8_t Qm = pusch_pdu->qam_mod_order; // target_code_rate is in 0.1 units - float Coderate = (float)pusch_pdu->target_code_rate / 10240.0f; + const float Coderate = (float)pusch_pdu->target_code_rate / 10240.0f; LOG_D(NR_PHY, "ulsch coding nb_rb %d, Nl = %d\n", nb_rb, pusch_pdu->nrOfLayers); LOG_D(NR_PHY, "ulsch coding A %d G %d mod_order %d Coderate %f\n", A, G[pusch_id], Qm, Coderate); @@ -91,11 +62,11 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue, ///////////////////////// a---->| add CRC |---->b ///////////////////////// - int max_payload_bytes = MAX_NUM_NR_ULSCH_SEGMENTS_PER_LAYER * pusch_pdu->nrOfLayers * 1056; + const int max_payload_bytes = MAX_NUM_NR_ULSCH_SEGMENTS_PER_LAYER * pusch_pdu->nrOfLayers * 1056; int B; if (A > NR_MAX_PDSCH_TBS) { // Add 24-bit crc (polynomial A) to payload - crc = crc24a(harq_process->payload_AB, A) >> 8; + const unsigned int crc = crc24a(harq_process->payload_AB, A) >> 8; harq_process->payload_AB[A >> 3] = ((uint8_t *)&crc)[2]; harq_process->payload_AB[1 + (A >> 3)] = ((uint8_t *)&crc)[1]; harq_process->payload_AB[2 + (A >> 3)] = ((uint8_t *)&crc)[0]; @@ -103,7 +74,7 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue, AssertFatal((A / 8) + 4 <= max_payload_bytes, "A %d is too big (A/8+4 = %d > %d)\n", A, (A / 8) + 4, max_payload_bytes); } else { // Add 16-bit crc (polynomial A) to payload - crc = crc16(harq_process->payload_AB, A) >> 16; + const unsigned int crc = crc16(harq_process->payload_AB, A) >> 16; harq_process->payload_AB[A >> 3] = ((uint8_t *)&crc)[1]; harq_process->payload_AB[1 + (A >> 3)] = ((uint8_t *)&crc)[0]; B = A + 16; @@ -151,7 +122,7 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue, T_INT((int)ulsch->pusch_pdu.transform_precoding), // transformPrecoder_enabled = 0, transformPrecoder_disabled = 1 T_INT((int)ulsch->pusch_pdu.dmrs_config_type), // dmrs_resource_map_config: pusch_dmrs_type1 = 0, pusch_dmrs_type2 = 1 T_INT((int)ulsch->pusch_pdu.ul_dmrs_symb_pos), // used to derive the DMRS symbol positions - T_INT((int)number_dmrs_symbols), + T_INT((int)get_num_dmrs(ulsch->pusch_pdu.ul_dmrs_symb_pos)), // dmrs_start_ofdm_symbol // dmrs_duration_num_ofdm_symbols // dmrs_num_add_positions @@ -168,36 +139,55 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue, VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_SEGMENTATION, VCD_FUNCTION_IN); start_meas_nr_ue_phy(ue, ULSCH_SEGMENTATION_STATS); - TB_parameters->Kb = nr_segmentation(harq_process->payload_AB, - harq_process->c, - B, - &harq_process->C, - &harq_process->K, - &harq_process->Z, - &harq_process->F, - harq_process->BG); - TB_parameters->C = harq_process->C; - TB_parameters->K = harq_process->K; - TB_parameters->Z = harq_process->Z; - TB_parameters->F = harq_process->F; - TB_parameters->BG = harq_process->BG; - if (TB_parameters->C > MAX_NUM_NR_DLSCH_SEGMENTS_PER_LAYER * pusch_pdu->nrOfLayers) { - LOG_E(PHY, "nr_segmentation.c: too many segments %d, B %d\n", TB_parameters->C, B); + harq_process->Kb = nr_segmentation(harq_process->payload_AB, + harq_process->c, + B, + &harq_process->C, + &harq_process->K, + &harq_process->Z, + &harq_process->F, + harq_process->BG); + if (harq_process->C > MAX_NUM_NR_DLSCH_SEGMENTS_PER_LAYER * pusch_pdu->nrOfLayers) { + LOG_E(PHY, "nr_segmentation.c: too many segments %d, B %d\n", harq_process->C, B); return (-1); } stop_meas_nr_ue_phy(ue, ULSCH_SEGMENTATION_STATS); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_SEGMENTATION, VCD_FUNCTION_OUT); - max_num_segments = max(max_num_segments, TB_parameters->C); - - TB_parameters->nb_rb = nb_rb; - TB_parameters->Qm = Qm; - TB_parameters->mcs = pusch_pdu->mcs_index; - TB_parameters->nb_layers = pusch_pdu->nrOfLayers; - TB_parameters->rv_index = pusch_pdu->pusch_data.rv_index; - TB_parameters->G = G[pusch_id]; - TB_parameters->tbslbrm = pusch_pdu->tbslbrm; - TB_parameters->A = A; } // pusch_id + return 0; +} + +int nr_ulsch_encoding(PHY_VARS_NR_UE *ue, + NR_UE_ULSCH_t *ulsch, + const uint32_t frame, + const uint8_t slot, + unsigned int *G, + int nb_ulsch, + uint8_t *ULSCH_ids, + uint16_t number_dmrs_symbols) +{ + start_meas_nr_ue_phy(ue, ULSCH_ENCODING_STATS); + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_UE_ULSCH_ENCODING, VCD_FUNCTION_IN); + + nrLDPC_TB_encoding_parameters_t TBs[nb_ulsch]; + memset(TBs, 0, sizeof(TBs)); + nrLDPC_slot_encoding_parameters_t slot_parameters = {.frame = frame, + .slot = slot, + .nb_TBs = nb_ulsch, + .threadPool = &get_nrUE_params()->Tpool, + .tinput = NULL, + .tprep = NULL, + .tparity = NULL, + .toutput = NULL, + .TBs = TBs}; + + int max_num_segments = 0; + for (uint_fast8_t pusch_id = 0; pusch_id < nb_ulsch; pusch_id++) { + const uint8_t ULSCH_id = ULSCH_ids[pusch_id]; + const uint8_t harq_pid = ulsch[ULSCH_id].pusch_pdu.pusch_data.harq_process_id; + NR_UL_UE_HARQ_t *harq_process = &ue->ul_harq_processes[harq_pid]; + max_num_segments = max(max_num_segments, harq_process->C); + } nrLDPC_segment_encoding_parameters_t segments[nb_ulsch][max_num_segments]; memset(segments, 0, sizeof(segments)); @@ -208,10 +198,25 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue, nrLDPC_TB_encoding_parameters_t *TB_parameters = &TBs[pusch_id]; NR_UL_UE_HARQ_t *harq_process = &ue->ul_harq_processes[harq_pid]; + const nfapi_nr_ue_pusch_pdu_t *pusch_pdu = &ulsch[ULSCH_id].pusch_pdu; + const uint16_t nb_rb = pusch_pdu->rb_size; + TB_parameters->harq_unique_pid = 2 * harq_pid + ULSCH_id; + TB_parameters->C = harq_process->C; + TB_parameters->K = harq_process->K; + TB_parameters->Z = harq_process->Z; + TB_parameters->F = harq_process->F; + TB_parameters->BG = harq_process->BG; + TB_parameters->Kb = harq_process->Kb; + TB_parameters->nb_rb = nb_rb; + TB_parameters->Qm = pusch_pdu->qam_mod_order; + TB_parameters->mcs = pusch_pdu->mcs_index; + TB_parameters->nb_layers = pusch_pdu->nrOfLayers; + TB_parameters->rv_index = pusch_pdu->pusch_data.rv_index; + TB_parameters->G = G[pusch_id]; + TB_parameters->tbslbrm = pusch_pdu->tbslbrm; + TB_parameters->A = pusch_pdu->pusch_data.tb_size / 8; TB_parameters->segments = segments[pusch_id]; - const nfapi_nr_ue_pusch_pdu_t *pusch_pdu = &ulsch->pusch_pdu; - uint16_t nb_rb = pusch_pdu->rb_size; memset(harq_process->f, 0, 14 * nb_rb * 12 * 16); TB_parameters->output = harq_process->f; diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c index 6091746ef2..561d357363 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c @@ -1092,7 +1092,9 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, /////////////////////////ULSCH coding///////////////////////// - if (nr_ulsch_encoding(UE, &phy_data->ulsch, frame, slot, G, 1, ULSCH_ids, number_dmrs_symbols) == -1) { + int enc_ret = nr_ulsch_pre_encoding(UE, &phy_data->ulsch, frame, slot, G, 1, ULSCH_ids); + enc_ret += nr_ulsch_encoding(UE, &phy_data->ulsch, frame, slot, G, 1, ULSCH_ids, number_dmrs_symbols); + if (enc_ret != 0) { stop_meas_nr_ue_phy(UE, PUSCH_PROC_STATS); return; } diff --git a/openair1/SIMULATION/NR_PHY/ulschsim.c b/openair1/SIMULATION/NR_PHY/ulschsim.c index 058a59feb5..64c04b900e 100644 --- a/openair1/SIMULATION/NR_PHY/ulschsim.c +++ b/openair1/SIMULATION/NR_PHY/ulschsim.c @@ -513,6 +513,7 @@ int main(int argc, char **argv) if (input_fd == NULL) { uint8_t ULSCH_ids[] = {0}; + nr_ulsch_pre_encoding(UE, ulsch_ue, 0, 0, &G, 1, ULSCH_ids); nr_ulsch_encoding(UE, ulsch_ue, 0, 0, &G, 1, ULSCH_ids, 0); } From 1c5af6065c83bb25bdbe553e6fe456f41b9c4e29 Mon Sep 17 00:00:00 2001 From: alexjiao Date: Fri, 13 Jun 2025 20:39:08 +0800 Subject: [PATCH 05/11] Handle UCI on PUSCH procedures at PHY Get beta offset, alpha scaling and other parameters to calculate rate matching information for UCI on PUSCH, then use the rate matching info to encode UCI and re-encode PUSCH, call nr_uci_on_pusch to get final multiplexed codeword for scrambling. --- openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c | 71 ++++++++++++++++++++-- 1 file changed, 65 insertions(+), 6 deletions(-) diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c index 561d357363..41b935221b 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c @@ -1034,8 +1034,11 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, LOG_D(PHY, "nr_ue_ulsch_procedures_slot hard_id %d %d.%d prepare for coding\n", harq_pid, frame, slot); NR_UE_ULSCH_t *ulsch_ue = &phy_data->ulsch; + NR_UE_PUCCH *pucch_ue = &phy_data->pucch_vars; 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; + const fapi_nr_ul_config_pucch_pdu *pucch_pdu = &pucch_ue->pucch_pdu[0]; + uci_on_pusch_bit_type_t *uci_mapping_template = NULL; uint16_t number_dmrs_symbols = 0; @@ -1076,6 +1079,11 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, G[pusch_id] = nr_get_G(nb_rb, number_of_symbols, nb_dmrs_re_per_rb, number_dmrs_symbols, unav_res, mod_order, Nl); + // Capture the initial total PUSCH bits. This is the total_codeword_length for mapping. + unsigned int G_initial_total_pusch_bits = G[pusch_id]; + + uci_on_pusch_bit_type_t template_buffer[G_initial_total_pusch_bits]; + ws_trace_t tmp = {.nr = true, .direction = DIRECTION_UPLINK, .pdu_buffer = harq_process_ul_ue->payload_AB, @@ -1092,9 +1100,18 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, /////////////////////////ULSCH coding///////////////////////// - int enc_ret = nr_ulsch_pre_encoding(UE, &phy_data->ulsch, frame, slot, G, 1, ULSCH_ids); - enc_ret += nr_ulsch_encoding(UE, &phy_data->ulsch, frame, slot, G, 1, ULSCH_ids, number_dmrs_symbols); - if (enc_ret != 0) { + rate_match_info_uci_t rm_info = {0}; + const uint8_t nl_qm = Nl * mod_order; // product of number of layers and modulation order + if(nr_ulsch_pre_encoding(UE, &phy_data->ulsch, frame, slot, G, 1, ULSCH_ids) != 0) { + LOG_E(PHY, "Error pre-encoding\n"); + return; + } + + if (pusch_pdu->pusch_uci.harq_ack_bit_length != 0) { + rm_info = calc_rate_match_info_uci(pusch_pdu, harq_process_ul_ue, nl_qm, &G[pusch_id]); + } + + if (nr_ulsch_encoding(UE, &phy_data->ulsch, frame, slot, G, 1, ULSCH_ids, number_dmrs_symbols) == -1) { stop_meas_nr_ue_phy(UE, PUSCH_PROC_STATS); return; } @@ -1107,9 +1124,51 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, int N_PRB_oh = 0; // higher layer (RRC) parameter xOverhead in PUSCH-ServingCellConfig - 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"); + if (pusch_pdu->pusch_uci.harq_ack_bit_length != 0) { + LOG_D(PHY, "[UCI_ON_PUSCH] Original HARQ-ACK bit length: %u\n", pusch_pdu->pusch_uci.harq_ack_bit_length); + LOG_D(PHY, "[UCI_ON_PUSCH] Initial G: %u\n", G_initial_total_pusch_bits); + // b is the block of bits transmitted on the physical channel after payload coding + uint64_t b[16] = {0}; // limit to 1024-bit encoded length + + if (pucch_pdu == NULL) { + LOG_E(PHY, "nr_ue_ulsch_procedures: pucch_pdu is NULL but HARQ-ACK is present. Cannot proceed with UCI encoding.\n"); + stop_meas_nr_ue_phy(UE, PUSCH_PROC_STATS); + return; + } + + nr_uci_encoding(pusch_pdu->pusch_uci.harq_payload, + pusch_pdu->pusch_uci.harq_ack_bit_length, + pucch_pdu->prb_size, + true, + rm_info.E_uci_ACK, + mod_order, + &b[0]); + + LOG_D(PHY, + "[UCI_ON_PUSCH] G_ulsch=%u (updated G[pusch_id]), G_ack=%u (M_bit), G_ack_rvd=%u, total_len=%u " + "(G_initial_total_pusch_bits).\n", + G[pusch_id], + rm_info.E_uci_ACK, + rm_info.E_uci_ACK_rvd, + G_initial_total_pusch_bits); + + uint8_t temp_codeword[G_initial_total_pusch_bits]; + nr_data_control_mapping(pusch_pdu, + template_buffer, + G[pusch_id], + rm_info.E_uci_ACK, + rm_info.E_uci_ACK_rvd, + temp_codeword, + G_initial_total_pusch_bits, + harq_process_ul_ue->f, + b); + + memcpy(harq_process_ul_ue->f, temp_codeword, G_initial_total_pusch_bits); + uci_mapping_template = template_buffer; + } + + AssertFatal(pusch_pdu->pusch_uci.csi_part1_bit_length == 0 && pusch_pdu->pusch_uci.csi_part2_bit_length == 0, + "UCI (CSI) on PUSCH not supported at PHY\n"); uint16_t start_rb = pusch_pdu->rb_start; uint16_t start_sc = frame_parms->first_carrier_offset + (start_rb + pusch_pdu->bwp_start) * NR_NB_SC_PER_RB; From b69b65ba6676d9fa84f4aa564322033724b1fc46 Mon Sep 17 00:00:00 2001 From: alexjiao Date: Fri, 13 Jun 2025 20:46:00 +0800 Subject: [PATCH 06/11] Handle UCI on PUSCH scrambling When the codeword has UCI multiplexed with data, we need to handle UCI scrambling according to 38.211 section 6.3.1.1. Especially when O_ACK <= 2 there are x and y placeholder bits for UCI. --- openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c | 106 +++++++++++++++++---- 1 file changed, 85 insertions(+), 21 deletions(-) diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c index 41b935221b..08cc881180 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c @@ -62,32 +62,84 @@ //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) +static void nr_pusch_codeword_scrambling_uci(uint8_t *in, + uint32_t size, + uint32_t Nid, + uint32_t n_RNTI, + const uci_on_pusch_bit_type_t *template, + uint32_t *out) { uint32_t *seq = gold_cache((n_RNTI << 15) + Nid, (size + 31) / 32); - for (int i=0; i> 31; + uint32_t num_words = (size + 31) / 32; + + // Step 1: Initial general scrambling + // First convert unpacked input to bit-packed words + uint32_t in_words[num_words]; + memset(in_words, 0, num_words * sizeof(uint32_t)); + + for (uint32_t i = 0; i < size; i++) { + uint32_t word_idx = i / 32; + uint32_t bit_idx = i % 32; + if (in[i] & 1) { + in_words[word_idx] |= (1U << bit_idx); + } + } + + for (uint32_t i = 0; i < num_words; i++) { + out[i] = in_words[i] ^ seq[i]; + } + + for (uint32_t i = 0; i < size; i++) { + if (template[i] == BIT_TYPE_ACK) { + // Step 2: Overwrite/Correct positions for ACK-only bits when O_ACK > 2 + uint32_t pos = i; + uint32_t idx = pos / 32; + uint32_t b_idx = pos % 32; + + // Clear the bit that was set by the initial general scrambling + out[idx] &= ~(1U << b_idx); + + uint32_t ack_bit_value = in[pos] & 1; + uint32_t scrambling_bit_for_ack = (seq[idx] >> b_idx) & 1; + out[idx] |= ((ack_bit_value ^ scrambling_bit_for_ack) << b_idx); + } else if (template[i] == BIT_TYPE_ACK_ULSCH) { + // Step 3: Overwrite/Correct positions for UCI bits including placeholders X, Y when O_ACK <= 2 + uint32_t pos = i; + uint32_t idx = pos / 32; + uint32_t b_idx = pos % 32; + + out[idx] &= ~(1U << b_idx); + + if (in[pos] == NR_PUSCH_y) { + if (b_idx > 0) { + // Y depends on the final value of the previous bit in the same word. + // This previous bit could be an ACK (already corrected) or ULSCH (from initial scramble). + out[idx] |= ((out[idx] >> (b_idx - 1)) & 1) << b_idx; + } else if (idx > 0) { + // Y depends on the last bit of the previous word. + out[idx] |= ((out[idx - 1] >> 31) & 1); + } + } else if (in[pos] == NR_PUSCH_x) { + out[idx] |= (1U << b_idx); + } else { + uint32_t ack_bit_value = in[pos] & 1; + uint32_t scrambling_bit_for_ack = (seq[idx] >> b_idx) & 1; + out[idx] |= ((ack_bit_value ^ scrambling_bit_for_ack) << b_idx); } } - 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) +void nr_pusch_codeword_scrambling(uint8_t *in, + uint32_t size, + uint32_t Nid, + uint32_t n_RNTI, + bool uci_on_pusch, + const uci_on_pusch_bit_type_t *template, + uint32_t *out) { if (uci_on_pusch) - nr_pusch_codeword_scrambling_uci(in, size, Nid, n_RNTI, out); + nr_pusch_codeword_scrambling_uci(in, size, Nid, n_RNTI, template, out); else nr_codeword_scrambling(in, size, 0, Nid, n_RNTI, out); } @@ -1201,16 +1253,28 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, /////////////////////////ULSCH scrambling///////////////////////// - uint32_t available_bits = G[pusch_id]; - // +1 because size can be not modulo 4 - uint32_t scrambled_output[available_bits / (8 * sizeof(uint32_t)) + 1]; + uint32_t available_bits; + bool is_uci_on_pusch = (pusch_pdu->pusch_uci.harq_ack_bit_length != 0); + + if (is_uci_on_pusch) { + // UCI on PUSCH is present, so available bits are the total codeword length + available_bits = G_initial_total_pusch_bits; + } else { + // No UCI on PUSCH, so available bits are the initial G value + available_bits = G[pusch_id]; + } + + // +1 because size can be not modulo 4 for the uint32_t array + uint32_t scrambled_output_len_u32 = (available_bits + 31) / 32; // Round up to nearest uint32_t count + uint32_t scrambled_output[scrambled_output_len_u32]; 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, + is_uci_on_pusch, + uci_mapping_template, scrambled_output); #if T_TRACER if (T_ACTIVE(T_UE_PHY_UL_SCRAMBLED_TX_BITS)) { From ac76c6e27a533ff4289044a97843805e643a5f2c Mon Sep 17 00:00:00 2001 From: alexjiao Date: Sun, 27 Jul 2025 19:40:09 +0800 Subject: [PATCH 07/11] Add functions to calculate M_bit for PUCCH format 2/3/4 Add functions to calculate PUCCH format 2/3/4 rate matching output sequence length (M_bit), according to 3GPP TS 38.212 Table 6.3.1.4-1. --- openair1/PHY/NR_UE_TRANSPORT/pucch_nr.c | 67 +++++++++++++++++-------- 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.c b/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.c index b4c65cf28e..b6b20cefaf 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.c +++ b/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.c @@ -485,6 +485,45 @@ void nr_generate_pucch1(const PHY_VARS_NR_UE *ue, } } +// Calculate number of DMRS symbols for PUCCH formats 3 and 4 +static uint8_t nr_pucch_get_dmrs_symbols(uint8_t nrofSymbols, uint8_t add_dmrs) +{ + if (nrofSymbols == 4) { + return 1; + } else if (nrofSymbols > 4 && nrofSymbols <= 9) { + return 2; + } else if (nrofSymbols > 9) { + return (add_dmrs == 0) ? 2 : 4; + } + return 0; +} + +// Calculate PUCCH format 2/3/4 rate matching output sequence length according to TS 38.212 Table 6.3.1.4-1 +static uint16_t nr_pucch_output_sequence_length(uint8_t format_type, + uint8_t nrofSymbols, + uint16_t nrofPRB, + uint8_t n_SF_PUCCH_s, + uint8_t is_pi_over_2_bpsk_enabled, + uint8_t add_dmrs) +{ + uint16_t M_bit = 0; + + if (format_type == 2) { + M_bit = 16 * nrofSymbols * nrofPRB; + } else if (format_type == 3) { + uint16_t E_init = (is_pi_over_2_bpsk_enabled == 0) ? 24 : 12; + uint8_t num_dmrs_symbols = nr_pucch_get_dmrs_symbols(nrofSymbols, add_dmrs); + M_bit = E_init * (nrofSymbols - num_dmrs_symbols) * nrofPRB / n_SF_PUCCH_s; + } else if (format_type == 4) { + nrofPRB = 1; + uint16_t E_init = (is_pi_over_2_bpsk_enabled == 0) ? 24 : 12; + uint8_t num_dmrs_symbols = nr_pucch_get_dmrs_symbols(nrofSymbols, add_dmrs); + M_bit = E_init * (nrofSymbols - num_dmrs_symbols) * nrofPRB / n_SF_PUCCH_s; + } + + return M_bit; +} + static inline void nr_pucch2_3_4_scrambling(uint16_t M_bit, uint16_t rnti, uint16_t n_id, uint64_t *B64, uint8_t *btilde) { // c_init=nRNTI*2^15+n_id according to TS 38.211 Subclause 6.3.2.6.1 @@ -726,7 +765,7 @@ void nr_generate_pucch2(const PHY_VARS_NR_UE *ue, // b is the block of bits transmitted on the physical channel after payload coding uint64_t b[16] = {0}; // limit to 1024-bit encoded length // M_bit is the number of bits of block b (payload after encoding) - uint16_t M_bit = 16 * pucch_pdu->nr_of_symbols * pucch_pdu->prb_size; + uint16_t M_bit = nr_pucch_output_sequence_length(pucch_pdu->format_type, pucch_pdu->nr_of_symbols, pucch_pdu->prb_size, 0, 0, 0); nr_uci_encoding(pucch_pdu->payload, pucch_pdu->n_bit, pucch_pdu->prb_size, false, M_bit, 0, &b[0]); /* * Implementing TS 38.211 @@ -938,26 +977,12 @@ void nr_generate_pucch3_4(const PHY_VARS_NR_UE *ue, uint16_t startingPRB = pucch_pdu->prb_start + pucch_pdu->bwp_start; uint8_t add_dmrs = pucch_pdu->add_dmrs_flag; - uint16_t E_init = (is_pi_over_2_bpsk_enabled == 0) ? 24 : 12; - - if (pucch_pdu->format_type == 3) { - if (nrofSymbols == 4) { - M_bit = (intraSlotFrequencyHopping == 0) ? (E_init * (nrofSymbols - 1) * nrofPRB) : (E_init * (nrofSymbols - 1) * nrofPRB); - } else if (nrofSymbols > 4) { - M_bit = E_init * (nrofSymbols - 2) * nrofPRB; - } else if (nrofSymbols > 9) { - M_bit = (add_dmrs == 0) ? (E_init * (nrofSymbols - 2) * nrofPRB) : (E_init * (nrofSymbols - 4) * nrofPRB); - } - } else if (pucch_pdu->format_type == 4) { - if (nrofSymbols == 4) { - M_bit = (intraSlotFrequencyHopping == 0) ? (E_init * (nrofSymbols - 1) / n_SF_PUCCH_s) - : (E_init * (nrofSymbols - 1) / n_SF_PUCCH_s); - } else if (nrofSymbols > 4) { - M_bit = E_init * (nrofSymbols - 2) / n_SF_PUCCH_s; - } else if (nrofSymbols > 9) { - M_bit = (add_dmrs == 0) ? (E_init * (nrofSymbols - 2) / n_SF_PUCCH_s) : (E_init * (nrofSymbols - 4) / n_SF_PUCCH_s); - } - } + M_bit = nr_pucch_output_sequence_length(pucch_pdu->format_type, + nrofSymbols, + nrofPRB, + n_SF_PUCCH_s, + is_pi_over_2_bpsk_enabled, + add_dmrs); nr_uci_encoding(pucch_pdu->payload, pucch_pdu->n_bit, nrofPRB, false, M_bit, 0, b); /* From 8de13fab97e7c2132ddb22451e3d026377f40e7e Mon Sep 17 00:00:00 2001 From: Sakthivel Velumani Date: Fri, 15 Aug 2025 23:56:39 -0400 Subject: [PATCH 08/11] Simplify code --- openair1/PHY/NR_REFSIG/dmrs_nr.c | 4 + openair1/PHY/NR_REFSIG/dmrs_nr.h | 2 + openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c | 145 +++++++------------- openair1/SCHED_NR_UE/phy_procedures_nr_ue.c | 8 +- 4 files changed, 53 insertions(+), 106 deletions(-) diff --git a/openair1/PHY/NR_REFSIG/dmrs_nr.c b/openair1/PHY/NR_REFSIG/dmrs_nr.c index a5a55d685a..7dba224cdb 100644 --- a/openair1/PHY/NR_REFSIG/dmrs_nr.c +++ b/openair1/PHY/NR_REFSIG/dmrs_nr.c @@ -306,6 +306,10 @@ int8_t get_next_dmrs_symbol_in_slot(uint16_t ul_dmrs_symb_pos, uint8_t counter, return -1; } +int8_t get_num_dmrs_re_per_rb(const uint8_t dmrs_type, const uint8_t num_cdm_grp_no_data) +{ + return (dmrs_type == NFAPI_NR_DMRS_TYPE1 ? 6 * num_cdm_grp_no_data : 4 * num_cdm_grp_no_data); +} /* return the position of valid dmrs symbol in a slot for channel compensation */ int8_t get_valid_dmrs_idx_for_channel_est(uint16_t dmrs_symb_pos, uint8_t counter) diff --git a/openair1/PHY/NR_REFSIG/dmrs_nr.h b/openair1/PHY/NR_REFSIG/dmrs_nr.h index 97d61993ce..5f17478874 100644 --- a/openair1/PHY/NR_REFSIG/dmrs_nr.h +++ b/openair1/PHY/NR_REFSIG/dmrs_nr.h @@ -71,6 +71,8 @@ void nr_chest_time_domain_avg(NR_DL_FRAME_PARMS *frame_parms, uint16_t dmrs_bitmap, uint16_t num_rbs); +int8_t get_num_dmrs_re_per_rb(const uint8_t dmrs_type, const uint8_t num_cdm_grp_no_data); + static inline uint8_t is_dmrs_symbol(uint8_t l, uint16_t dmrsSymbMask) { DevAssert(l < 32); diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c index 08cc881180..d48109440d 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c @@ -89,28 +89,17 @@ static void nr_pusch_codeword_scrambling_uci(uint8_t *in, out[i] = in_words[i] ^ seq[i]; } + // According to 38.211 6.3.1.1 for (uint32_t i = 0; i < size; i++) { - if (template[i] == BIT_TYPE_ACK) { - // Step 2: Overwrite/Correct positions for ACK-only bits when O_ACK > 2 + if (template[i] == BIT_TYPE_ACK_ULSCH) { + // Step 2: Overwrite/Correct positions for UCI bits including placeholders X, Y when O_ACK <= 2 uint32_t pos = i; uint32_t idx = pos / 32; uint32_t b_idx = pos % 32; - // Clear the bit that was set by the initial general scrambling - out[idx] &= ~(1U << b_idx); - - uint32_t ack_bit_value = in[pos] & 1; - uint32_t scrambling_bit_for_ack = (seq[idx] >> b_idx) & 1; - out[idx] |= ((ack_bit_value ^ scrambling_bit_for_ack) << b_idx); - } else if (template[i] == BIT_TYPE_ACK_ULSCH) { - // Step 3: Overwrite/Correct positions for UCI bits including placeholders X, Y when O_ACK <= 2 - uint32_t pos = i; - uint32_t idx = pos / 32; - uint32_t b_idx = pos % 32; - - out[idx] &= ~(1U << b_idx); - if (in[pos] == NR_PUSCH_y) { + // Clear bit + out[idx] &= ~(1U << b_idx); if (b_idx > 0) { // Y depends on the final value of the previous bit in the same word. // This previous bit could be an ACK (already corrected) or ULSCH (from initial scramble). @@ -121,10 +110,6 @@ static void nr_pusch_codeword_scrambling_uci(uint8_t *in, } } else if (in[pos] == NR_PUSCH_x) { out[idx] |= (1U << b_idx); - } else { - uint32_t ack_bit_value = in[pos] & 1; - uint32_t scrambling_bit_for_ack = (seq[idx] >> b_idx) & 1; - out[idx] |= ((ack_bit_value ^ scrambling_bit_for_ack) << b_idx); } } } @@ -139,8 +124,10 @@ void nr_pusch_codeword_scrambling(uint8_t *in, uint32_t *out) { if (uci_on_pusch) + // in buffer is in byte-packed format nr_pusch_codeword_scrambling_uci(in, size, Nid, n_RNTI, template, out); else + // in buffer is in bit-packed format nr_codeword_scrambling(in, size, 0, Nid, n_RNTI, out); } @@ -701,62 +688,31 @@ static rate_match_info_uci_t calc_rate_match_info_uci(const nfapi_nr_ue_pusch_pd static int initialize_mapping_resources(const nfapi_nr_ue_pusch_pdu_t *pusch_pdu, uint32_t *m_ulsch_initial, - uint32_t *m_ulsch_current, - uint32_t *m_uci_current, - bool *is_dmrs_symbol_flags, - uint8_t *dmrs_symbol_set_relative, - uint8_t *num_dmrs_symbols_in_set_relative) + uint32_t *m_uci_current) { - if (!pusch_pdu || !m_ulsch_initial || !m_ulsch_current || !m_uci_current || !is_dmrs_symbol_flags || !dmrs_symbol_set_relative - || !num_dmrs_symbols_in_set_relative) + if (!pusch_pdu || !m_ulsch_initial || !m_uci_current) return -1; - uint8_t n_pusch_sym_all = pusch_pdu->nr_of_symbols; - uint16_t ul_dmrs_symb_pos = pusch_pdu->ul_dmrs_symb_pos; - uint8_t dmrs_type = pusch_pdu->dmrs_config_type; - uint8_t cdm_grps_no_data = pusch_pdu->num_dmrs_cdm_grps_no_data; - uint32_t res_per_symbol_non_dmrs = pusch_pdu->rb_size * 12; - - *num_dmrs_symbols_in_set_relative = 0; + const uint8_t n_pusch_sym_all = pusch_pdu->nr_of_symbols; + const uint16_t ul_dmrs_symb_pos = pusch_pdu->ul_dmrs_symb_pos; + const uint8_t dmrs_type = pusch_pdu->dmrs_config_type; + const uint8_t cdm_grps_no_data = pusch_pdu->num_dmrs_cdm_grps_no_data; + const uint32_t res_per_symbol_non_dmrs = pusch_pdu->rb_size * NR_NB_SC_PER_RB; + const uint32_t data_re_on_dmrs_sym_per_prb = NR_NB_SC_PER_RB - get_num_dmrs_re_per_rb(dmrs_type, cdm_grps_no_data); // Initialize resources per symbol for ULSCH and UCI for (uint8_t i = 0; i < n_pusch_sym_all; i++) { uint8_t absolute_symbol_idx = pusch_pdu->start_symbol_index + i; if ((ul_dmrs_symb_pos >> absolute_symbol_idx) & 0x01) { - is_dmrs_symbol_flags[i] = true; - if (*num_dmrs_symbols_in_set_relative < 14) { - dmrs_symbol_set_relative[(*num_dmrs_symbols_in_set_relative)++] = i; - } - // Calculate available data REs on DMRS symbols based on DMRS configuration - uint32_t data_re_on_dmrs_sym_per_prb = 0; - - if (dmrs_type == 0) { // Type 1 - if (cdm_grps_no_data == 1) { - data_re_on_dmrs_sym_per_prb = 6; - } else { - data_re_on_dmrs_sym_per_prb = 0; - } - } else { // Type 2 - if (cdm_grps_no_data == 1) { - data_re_on_dmrs_sym_per_prb = 4; - } else if (cdm_grps_no_data == 2) { - data_re_on_dmrs_sym_per_prb = 2; - } else { - data_re_on_dmrs_sym_per_prb = 0; - } - } m_ulsch_initial[i] = pusch_pdu->rb_size * data_re_on_dmrs_sym_per_prb; - m_ulsch_current[i] = m_ulsch_initial[i]; m_uci_current[i] = 0; // UCI is not mapped on DMRS symbols } else { // Not a DMRS symbol - is_dmrs_symbol_flags[i] = false; m_ulsch_initial[i] = res_per_symbol_non_dmrs; - m_ulsch_current[i] = res_per_symbol_non_dmrs; m_uci_current[i] = res_per_symbol_non_dmrs; } } @@ -764,35 +720,31 @@ static int initialize_mapping_resources(const nfapi_nr_ue_pusch_pdu_t *pusch_pdu return 0; } -static uint8_t find_first_uci_symbol(uint8_t n_pusch_sym_all, - const bool *is_dmrs_symbol_flags, - uint8_t *dmrs_symbol_set_relative, - uint32_t num_dmrs_symbols_in_set_relative) +static void get_first_uci_symbol(const uint8_t start_symbol, + const uint8_t num_symbols, + const uint16_t dmrs_map, + uint8_t *first_non_dmrs_sym, + uint8_t *dmrs_p1) { - uint8_t l1_c = 0; - - // Find first non-DMRS symbol - for (uint8_t i = 0; i < n_pusch_sym_all; i++) { - if (!is_dmrs_symbol_flags[i]) { - l1_c = i; + // First non-DMRS symbol + const uint16_t last_sym = start_symbol + num_symbols; + for (uint_fast8_t s = start_symbol; s < last_sym; s++) { + if (!is_dmrs_symbol(s, dmrs_map)) { + *first_non_dmrs_sym = s; break; } } - // If there are DMRS symbols, try to find a symbol after the first DMRS - if (num_dmrs_symbols_in_set_relative > 0) { - uint8_t first_dmrs_idx = dmrs_symbol_set_relative[0]; - if (l1_c <= first_dmrs_idx) { - for (uint8_t i = first_dmrs_idx + 1; i < n_pusch_sym_all; i++) { - if (!is_dmrs_symbol_flags[i]) { - l1_c = i; - break; - } - } - } + // Symbol after first consequtive DMRS symbol + const uint8_t first_dmrs_sym = get_next_dmrs_symbol_in_slot(dmrs_map, start_symbol, last_sym); + *dmrs_p1 = first_dmrs_sym + 1; + while (is_dmrs_symbol(*dmrs_p1, dmrs_map) && *dmrs_p1 < last_sym) { + (*dmrs_p1)++; } - return l1_c; + // Return relative symbol idx + *first_non_dmrs_sym -= start_symbol; + *dmrs_p1 -= start_symbol; } /* @@ -1013,34 +965,29 @@ static uci_on_pusch_bit_type_t *nr_data_control_mapping(const nfapi_nr_ue_pusch_ if (!pusch_pdu || !codeword || codeword_len == 0 || !template) return NULL; const uint8_t n_symbols = pusch_pdu->nr_of_symbols; - if (n_symbols == 0 || n_symbols > 14) + if (n_symbols == 0 || n_symbols > NR_NUMBER_OF_SYMBOLS_PER_SLOT) return NULL; - uint32_t m_ulsch_initial[14] = {0}; - uint32_t m_ulsch_current[14] = {0}; - uint32_t m_uci_current[14] = {0}; // This holds RE counts, not bit counts - bool is_dmrs_symbol_flags[14] = {0}; - uint8_t dmrs_symbol_set_relative[14] = {0}; - uint8_t num_dmrs_symbols_in_set_relative = 0; + uint32_t m_ulsch_initial[NR_NUMBER_OF_SYMBOLS_PER_SLOT] = {0}; + uint32_t m_uci_current[NR_NUMBER_OF_SYMBOLS_PER_SLOT] = {0}; // This holds RE counts, not bit counts - if (initialize_mapping_resources(pusch_pdu, - m_ulsch_initial, - m_ulsch_current, - m_uci_current, - is_dmrs_symbol_flags, - dmrs_symbol_set_relative, - &num_dmrs_symbols_in_set_relative) - != 0) { + if (initialize_mapping_resources(pusch_pdu, m_ulsch_initial, m_uci_current) != 0) { LOG_E(PHY, "Failed to initialize mapping resources\n"); return NULL; } - uint8_t l1_c = find_first_uci_symbol(n_symbols, is_dmrs_symbol_flags, dmrs_symbol_set_relative, num_dmrs_symbols_in_set_relative); + uint8_t first_non_dmrs_sym = 0; + uint8_t l1_c = 0; + get_first_uci_symbol(pusch_pdu->start_symbol_index, + pusch_pdu->nr_of_symbols, + pusch_pdu->ul_dmrs_symb_pos, + &first_non_dmrs_sym, + &l1_c); memset(template, 0, codeword_len * sizeof(uci_on_pusch_bit_type_t)); - uint32_t positions_by_sym[14][MAX_UCI_CODED_BITS] = {0}; - uint32_t count_by_sym[14] = {0}; + uint32_t positions_by_sym[NR_NUMBER_OF_SYMBOLS_PER_SLOT][MAX_UCI_CODED_BITS] = {0}; + uint32_t count_by_sym[NR_NUMBER_OF_SYMBOLS_PER_SLOT] = {0}; if (G_ack_rvd > 0) { build_template_reserve_ack(template, diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c index ce398d9a7a..75f73b0e8c 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c @@ -1214,13 +1214,7 @@ void pdsch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_ } VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SLOT_FEP_PDSCH, VCD_FUNCTION_OUT); - uint8_t nb_re_dmrs; - if (dlsch_config->dmrsConfigType == NFAPI_NR_DMRS_TYPE1) { - nb_re_dmrs = 6 * dlsch_config->n_dmrs_cdm_groups; - } - else { - nb_re_dmrs = 4 * dlsch_config->n_dmrs_cdm_groups; - } + const uint8_t nb_re_dmrs = get_num_dmrs_re_per_rb(dlsch_config->dmrsConfigType, dlsch_config->n_dmrs_cdm_groups); uint16_t dmrs_len = get_num_dmrs(dlsch_config->dlDmrsSymbPos); uint32_t unav_res = 0; if(dlsch_config->pduBitmap & 0x1) { From b2174cb8e127da301650f16b0e404852d0bf2c77 Mon Sep 17 00:00:00 2001 From: Sakthivel Velumani Date: Sat, 16 Aug 2025 12:46:11 -0400 Subject: [PATCH 09/11] Add processing stats for UCI mapping on PUSCH --- openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c | 3 ++- openair1/PHY/nr_phy_common/inc/nr_ue_phy_meas.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c index d48109440d..7fca035a27 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c @@ -1152,6 +1152,7 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, G_initial_total_pusch_bits); uint8_t temp_codeword[G_initial_total_pusch_bits]; + start_meas_nr_ue_phy(UE, UCI_ON_PUSCH_MAPPING); nr_data_control_mapping(pusch_pdu, template_buffer, G[pusch_id], @@ -1161,7 +1162,7 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, G_initial_total_pusch_bits, harq_process_ul_ue->f, b); - + stop_meas_nr_ue_phy(UE, UCI_ON_PUSCH_MAPPING); memcpy(harq_process_ul_ue->f, temp_codeword, G_initial_total_pusch_bits); uci_mapping_template = template_buffer; } diff --git a/openair1/PHY/nr_phy_common/inc/nr_ue_phy_meas.h b/openair1/PHY/nr_phy_common/inc/nr_ue_phy_meas.h index 9f91274bae..79b0b378e8 100644 --- a/openair1/PHY/nr_phy_common/inc/nr_ue_phy_meas.h +++ b/openair1/PHY/nr_phy_common/inc/nr_ue_phy_meas.h @@ -47,6 +47,7 @@ FN(DLSCH_PROCEDURES_STATS),\ FN(PHY_PROC_TX),\ FN(PUSCH_PROC_STATS),\ + FN(UCI_ON_PUSCH_MAPPING),\ FN(ULSCH_SEGMENTATION_STATS),\ FN(ULSCH_LDPC_ENCODING_STATS),\ FN(ULSCH_RATE_MATCHING_STATS),\ From 83bf140bc3bd7e0416f09706b9681abd67a5edca Mon Sep 17 00:00:00 2001 From: Sakthivel Velumani Date: Sat, 16 Aug 2025 19:20:44 -0400 Subject: [PATCH 10/11] Add flag for Tx phase pre compensation Used in ulsim to verify waveform in MATLAB --- .../PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h | 3 ++- openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c | 16 +++++++++------- openair1/PHY/defs_nr_UE.h | 3 +++ openair1/SCHED_NR_UE/phy_procedures_nr_ue.c | 3 ++- openair1/SCHED_NR_UE/phy_procedures_nr_ue_sl.c | 10 +++++++++- 5 files changed, 25 insertions(+), 10 deletions(-) diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h index c257726501..0c519451a5 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h @@ -170,7 +170,8 @@ uint8_t nr_ue_pusch_common_procedures(PHY_VARS_NR_UE *UE, c16_t **txdataF, c16_t **txdata, uint32_t linktype, - bool was_symbol_used[NR_NUMBER_OF_SYMBOLS_PER_SLOT]); + bool was_symbol_used[NR_NUMBER_OF_SYMBOLS_PER_SLOT], + bool no_phase_pre_comp); void clean_UE_harq(PHY_VARS_NR_UE *UE); diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c index 7fca035a27..ff2d67e793 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c @@ -1506,19 +1506,21 @@ uint8_t nr_ue_pusch_common_procedures(PHY_VARS_NR_UE *UE, c16_t **txdataF, c16_t **txdata, uint32_t linktype, - bool was_symbol_used[NR_NUMBER_OF_SYMBOLS_PER_SLOT]) + bool was_symbol_used[NR_NUMBER_OF_SYMBOLS_PER_SLOT], + bool no_phase_pre_comp) { int N_RB = (linktype == link_type_sl) ? frame_parms->N_RB_SL : frame_parms->N_RB_UL; - for (int i = 0; i < NR_NUMBER_OF_SYMBOLS_PER_SLOT; i++) { - if (was_symbol_used[i] == false) - continue; - 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, i, 1); + if (!no_phase_pre_comp) { + for (int i = 0; i < NR_NUMBER_OF_SYMBOLS_PER_SLOT; i++) { + if (was_symbol_used[i] == false) + continue; + 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, i, 1); + } } } - for (int ap = 0; ap < n_antenna_ports; ap++) { if (frame_parms->Ncp == 1) { // extended cyclic prefix for (int i = 0; i < NR_NUMBER_OF_SYMBOLS_PER_SLOT_EXTENDED_CP; i++) { diff --git a/openair1/PHY/defs_nr_UE.h b/openair1/PHY/defs_nr_UE.h index 6ae3ddc37a..d56355298b 100644 --- a/openair1/PHY/defs_nr_UE.h +++ b/openair1/PHY/defs_nr_UE.h @@ -499,6 +499,9 @@ typedef struct PHY_VARS_NR_UE_s { /// RF and Interface devices per CC openair0_device rfdevice; + /// Phase precompensation flag + bool no_phase_pre_comp; + void* scopeData; // Pointers to hold PDSCH data only for phy simulators void *phy_sim_rxdataF; diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c index 75f73b0e8c..89b8ed91a7 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c @@ -404,7 +404,8 @@ void phy_procedures_nrUE_TX(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, n (c16_t **)txdataF, txp, link_type_ul, - was_symbol_used); + was_symbol_used, + ue->no_phase_pre_comp); stop_meas_nr_ue_phy(ue, OFDM_MOD_STATS); } diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue_sl.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue_sl.c index bd2ac08b81..e03c0a1fae 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue_sl.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue_sl.c @@ -313,7 +313,15 @@ void phy_procedures_nrUE_SL_TX(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc was_symbol_used[i] = true; if (tx_action) { LOG_D(NR_PHY, "Sending Uplink data \n"); - nr_ue_pusch_common_procedures(ue, proc->nr_slot_tx, fp, fp->nb_antennas_tx, txdataF, txp, link_type_sl, was_symbol_used); + nr_ue_pusch_common_procedures(ue, + proc->nr_slot_tx, + fp, + fp->nb_antennas_tx, + txdataF, + txp, + link_type_sl, + was_symbol_used, + ue->no_phase_pre_comp); } LOG_D(NR_PHY, "****** end Sidelink TX-Chain for AbsSubframe %d.%d ******\n", frame_tx, slot_tx); From 59ff9ae04e8d8826cc91df0e2989bc7ffd24f46c Mon Sep 17 00:00:00 2001 From: Sakthivel Velumani Date: Sat, 16 Aug 2025 21:04:56 -0400 Subject: [PATCH 11/11] ulsim: test UCI on PUSCH with matlab Since there is no UCI on PUSCH implementation in gNB, it is good to test UE's implementation with matlab. This commit allows to test with matlab in two ways: 1. generate OFDM waveform from ulsim and decode it in matlab using uci_on_pusch_decode.m 2. generate codeword in matlab using uci_on_pusch_encode.m read it in ulsim and compare the bits. ulsim option -o is used --- openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c | 3 + openair1/PHY/defs_nr_UE.h | 1 + openair1/SIMULATION/NR_PHY/ulsim.c | 176 +++++++++++++++++- .../SIMULATION/TOOLS/uci_on_pusch_decode.m | 79 ++++++++ .../SIMULATION/TOOLS/uci_on_pusch_encode.m | 85 +++++++++ 5 files changed, 335 insertions(+), 9 deletions(-) create mode 100644 openair1/SIMULATION/TOOLS/uci_on_pusch_decode.m create mode 100644 openair1/SIMULATION/TOOLS/uci_on_pusch_encode.m diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c index ff2d67e793..310fa7dc5f 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c @@ -1224,6 +1224,9 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, is_uci_on_pusch, uci_mapping_template, scrambled_output); + if (UE->phy_sim_test_buf) { + memcpy(UE->phy_sim_test_buf, scrambled_output, (available_bits + 7) / 8); + } #if T_TRACER if (T_ACTIVE(T_UE_PHY_UL_SCRAMBLED_TX_BITS)) { // Get Time Stamp for T-tracer messages diff --git a/openair1/PHY/defs_nr_UE.h b/openair1/PHY/defs_nr_UE.h index d56355298b..935226b238 100644 --- a/openair1/PHY/defs_nr_UE.h +++ b/openair1/PHY/defs_nr_UE.h @@ -511,6 +511,7 @@ typedef struct PHY_VARS_NR_UE_s { void *phy_sim_pdsch_dl_ch_estimates; void *phy_sim_pdsch_dl_ch_estimates_ext; uint8_t *phy_sim_dlsch_b; + uint8_t *phy_sim_test_buf; dynamic_barrier_t process_slot_tx_barriers[NUM_PROCESS_SLOT_TX_BARRIERS]; diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index ec4a7190fa..edab935e15 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -160,6 +160,108 @@ openair0_config_t openair0_cfg[MAX_CARDS]; channel_desc_t *UE2gNB[MAX_MOBILES_PER_GNB][NUMBER_OF_gNB_MAX]; +static void copy_bytes_to_packed_bits(const uint8_t *in, const uint32_t num_bits, const bool is_ulsch, uint8_t *out) +{ + if (is_ulsch) { // MATLAB computes CRC for input of MSB first + for (uint_fast32_t b = 0; b < num_bits; b++) { + out[b / 8] |= ((in[b] & 1) << (7 - (b % 8))); + } + } else { + for (uint_fast32_t b = 0; b < num_bits; b++) { + out[b / 8] |= (in[b] << (b % 8)); + } + } +} + +static void prepare_ue_pusch_pdu_from_matlab_vector(const bool uci_on_pusch, + FILE *vect_file, + nfapi_nr_ue_pusch_pdu_t *pusch_config_pdu, + uint8_t *cw_buf) +{ + if (!uci_on_pusch) + return; + + if (vect_file == NULL) + return; + + struct vect_vars { + uint32_t A; + uint32_t oack; + uint32_t ocsi1; + uint32_t ocsi2; + uint32_t cwlen; + uint32_t cwlen_scr; + } __attribute__((packed)); + + struct vect_vars var = {0}; + if (1 != fread(&var, sizeof(var), 1, vect_file)) { + printf("Error reading from matlab vector file\n"); + exit(-1); + } + + const uint16_t buff_len = var.A + var.oack + var.ocsi1 + var.ocsi2; + uint8_t vec_bits[buff_len]; + memset(vec_bits, 0, sizeof(vect_file)); + + uint8_t *p_vec_bits = vec_bits; + if (var.A != fread(p_vec_bits, sizeof(uint8_t), var.A, vect_file)) { + printf("Error reading ULSCH bits from file\n"); + exit(-1); + } + p_vec_bits += var.A; + if (var.oack != fread(p_vec_bits, sizeof(uint8_t), var.oack, vect_file)) { + printf("Error reading ACK bits from file\n"); + exit(-1); + } + p_vec_bits += var.oack; + if (var.ocsi1 != fread(p_vec_bits, sizeof(uint8_t), var.ocsi1, vect_file)) { + printf("Error reading CSI1 bits from file\n"); + exit(-1); + } + p_vec_bits += var.ocsi1; + if (var.ocsi2 != fread(p_vec_bits, sizeof(uint8_t), var.ocsi2, vect_file)) { + printf("Error reading CSI2 bits from file\n"); + exit(-1); + } + + if (var.cwlen != fread(cw_buf, sizeof(uint8_t), var.cwlen, vect_file)) { + printf("Error reading cw bits from file\n"); + exit(-1); + } + + memset(cw_buf, 0, var.cwlen_scr); + if (var.cwlen_scr != fread(cw_buf, sizeof(uint8_t), var.cwlen_scr, vect_file)) { + printf("Error reading cw bits from file\n"); + exit(-1); + } + + uint16_t tb_buf_size = (var.A + 7) / 8; + pusch_config_pdu->pusch_data.tb_size = tb_buf_size; + pusch_config_pdu->tx_request_body.pdu_length = tb_buf_size; + uint8_t *pb = pusch_config_pdu->tx_request_body.fapiTxPdu; + memset(pb, 0, tb_buf_size); + p_vec_bits = vec_bits; + copy_bytes_to_packed_bits(p_vec_bits, var.A, true, pb); + + pusch_config_pdu->pusch_uci.harq_ack_bit_length = var.oack; + pb = (uint8_t *)&pusch_config_pdu->pusch_uci.harq_payload; + memset(pb, 0, sizeof(pusch_config_pdu->pusch_uci.harq_payload)); + p_vec_bits += var.A; + copy_bytes_to_packed_bits(p_vec_bits, var.oack, false, pb); + + pusch_config_pdu->pusch_uci.csi_part1_bit_length = var.ocsi1; + pb = (uint8_t *)&pusch_config_pdu->pusch_uci.csi_part1_payload; + memset(pb, 0, sizeof(pusch_config_pdu->pusch_uci.csi_part1_payload)); + p_vec_bits += var.oack; + copy_bytes_to_packed_bits(p_vec_bits, var.ocsi1, false, pb); + + pusch_config_pdu->pusch_uci.csi_part2_bit_length = var.ocsi2; + pb = (uint8_t *)&pusch_config_pdu->pusch_uci.csi_part2_payload; + memset(pb, 0, sizeof(pusch_config_pdu->pusch_uci.csi_part2_payload)); + p_vec_bits += var.ocsi1; + copy_bytes_to_packed_bits(p_vec_bits, var.ocsi2, false, pb); +} + configmodule_interface_t *uniqCfg = NULL; int main(int argc, char *argv[]) { @@ -204,6 +306,8 @@ int main(int argc, char *argv[]) int print_perf = 0; cpuf = get_cpu_freq_GHz(); int msg3_flag = 0; + bool uci_on_pusch = false; + bool no_phase_pre_comp = false; int rv_index = 0; float roundStats; double effRate; @@ -227,6 +331,7 @@ int main(int argc, char *argv[]) UE_nr_rxtx_proc_t UE_proc; FILE *scg_fd=NULL; + FILE *uci_ulsch_matlab_vec = NULL; int file_offset = 0; double DS_TDL = .03; @@ -252,7 +357,8 @@ int main(int argc, char *argv[]) int c; bool setAffinity=false; char gNBthreads[128]="n"; - while ((c = getopt(argc, argv, "--:O:a:b:c:d:ef:g:h:i:jk:m:n:p:q:r:s:t:u:v:w:y:z:A:C:F:G:H:I:M:N:PR:S:T:U:L:ZW:E:X:Y:")) != -1) { + while ((c = getopt(argc, argv, "--:O:a:b:c:d:ef:g:h:i:jk:m:n:o::p:q:r:s:t:u:v:w:y:z:A:C:F:G:H:I:M:N:PR:S:T:U:L:ZW:E:X:Y:")) + != -1) { /* ignore long options starting with '--', option '-O' and their arguments that are handled by configmodule */ /* with this opstring getopt returns 1 for non-option arguments, refer to 'man 3 getopt' */ if (c == 1 || c == '-' || c == 'O') @@ -360,6 +466,20 @@ int main(int argc, char *argv[]) Imcs = atoi(optarg); break; + case 'o': + uci_on_pusch = true; + // UCI on PUSCH is not implemented in OAI gNB yet. + // So this flag is needed to verify in MATLAB + no_phase_pre_comp = true; + if (optarg) { // -o with file input: use matlab vector + uci_ulsch_matlab_vec = fopen(optarg, "rb"); + if (uci_ulsch_matlab_vec == NULL) { + printf("Error opening %s\n", optarg); + exit(-1); + } + } + break; + case 'W': precod_nbr_layers = atoi(optarg); break; @@ -539,6 +659,7 @@ int main(int argc, char *argv[]) printf("-k 3/4 sampling\n"); printf("-m MCS value\n"); printf("-n Number of trials to simulate\n"); + printf("-o Enable UCI on PUSCH. Optionally accepts input file (without space). This feature is not yet available in gNB so only used to verify with MATLAB generated vector\n"); printf("-p Use extended prefix mode\n"); printf("-q MCS table\n"); printf("-r Number of allocated resource blocks for PUSCH\n"); @@ -786,7 +907,8 @@ int main(int argc, char *argv[]) UE->if_inst->phy_config_request = nr_ue_phy_config_request; UE->if_inst->dl_indication = nr_ue_dl_indication; UE->if_inst->ul_indication = nr_ue_ul_indication; - + UE->no_phase_pre_comp = no_phase_pre_comp; + UE_mac->if_module = nr_ue_if_module_init(0); initFloatingCoresTpool(threadCnt, &nrUE_params.Tpool, false, "UE-tpool"); @@ -924,6 +1046,9 @@ int main(int argc, char *argv[]) } unsigned int available_bits = nr_get_G(nb_rb, nb_symb_sch, nb_re_dmrs, number_dmrs_symbols, unav_res, mod_order, precod_nbr_layers); + uint8_t cw_buf[available_bits]; + memset(cw_buf, 0, available_bits); + UE->phy_sim_test_buf = calloc(1, (available_bits + 7) / 8); printf("[ULSIM]: VALUE OF G: %u, TBS: %u\n", available_bits, TBS); int frame_length_complex_samples = gNB->frame_parms.samples_per_subframe * NR_NUMBER_OF_SUBFRAMES_PER_FRAME; @@ -1222,6 +1347,20 @@ int main(int argc, char *argv[]) // pusch_config_pdu->pdu_bit_map |= PUSCH_PDU_BITMAP_DFTS_OFDM; pusch_config_pdu->num_dmrs_cdm_grps_no_data = num_dmrs_cdm_grps_no_data; } + if (uci_on_pusch) { + const nfapi_nr_ue_pusch_uci_t pusch_uci = { + .alpha_scaling = 3, + .beta_offset_csi1 = 13, + .beta_offset_csi2 = 13, + .beta_offset_harq_ack = 11, + .harq_ack_bit_length = 3, + .harq_payload = 3, + //.csi_part1_bit_length = 4, + //.csi_part1_payload = 15 + }; + pusch_config_pdu->pusch_uci = pusch_uci; + prepare_ue_pusch_pdu_from_matlab_vector(uci_on_pusch, uci_ulsch_matlab_vec, pusch_config_pdu, cw_buf); + } if (do_SRS == 1) { fapi_nr_ul_config_request_pdu_t *ul_config1 = &ul_config.ul_config_list[1]; @@ -1457,7 +1596,7 @@ int main(int argc, char *argv[]) } if ((ulsch_gNB->last_iteration_cnt >= ulsch_gNB->max_ldpc_iterations) || ul_proc_error == 1) { - error_flag = 1; + error_flag = uci_on_pusch ? 0 : 1; n_errors[round]++; crc_status = 1; } else @@ -1474,15 +1613,29 @@ int main(int argc, char *argv[]) available_bits, (ptrsSymbPerSlot * ptrsRePerSymb * mod_order * precod_nbr_layers)); } - for (i = 0; i < available_bits; i++) { - const uint8_t current_bit = (UE->ul_harq_processes[harq_pid].f[i / 8] >> (i & 7)) & 1; - if (((current_bit == 0) && (pusch_vars->llr[i] <= 0)) || ((current_bit == 1) && (pusch_vars->llr[i] >= 0))) { - /*if(errors_scrambling == 0) - printf("\x1B[34m" "[frame %d][trial %d]\t1st bit in error in unscrambling = %d\n" "\x1B[0m", frame, trial, i);*/ - errors_scrambling[round]++; + if (uci_on_pusch) { + for (i = 0; i < available_bits; i++) { + const uint8_t current_bit = (UE->phy_sim_test_buf[i / 8] >> (i & 7)) & 1; + const uint8_t test_vector_bit = cw_buf[i] & 1; + if (current_bit != test_vector_bit) + errors_scrambling[round]++; + } + } else { + for (i = 0; i < available_bits; i++) { + const uint8_t current_bit = (UE->ul_harq_processes[harq_pid].f[i / 8] >> (i & 7)) & 1; + if (((current_bit == 0) && (pusch_vars->llr[i] <= 0)) || ((current_bit == 1) && (pusch_vars->llr[i] >= 0))) { + errors_scrambling[round]++; + } } } round++; + if (uci_on_pusch && uci_ulsch_matlab_vec && (errors_scrambling[round] == 0)) { + ret = 0; + printf("*************\n"); + printf("UCI on PUSCH test OK against MATLAB generated codeword\n"); + printf("*************\n"); + break; + } } // round if (n_trials == 1 && errors_scrambling[0] > 0) { @@ -1655,5 +1808,10 @@ int main(int argc, char *argv[]) free(filename_csv); } + if (uci_ulsch_matlab_vec) + fclose(uci_ulsch_matlab_vec); + + free_and_zero(UE->phy_sim_test_buf); + return ret; } diff --git a/openair1/SIMULATION/TOOLS/uci_on_pusch_decode.m b/openair1/SIMULATION/TOOLS/uci_on_pusch_decode.m new file mode 100644 index 0000000000..122b4912fb --- /dev/null +++ b/openair1/SIMULATION/TOOLS/uci_on_pusch_decode.m @@ -0,0 +1,79 @@ +% ULSIM command line +% ./nr_ulsim -m 27 -u 1 -R 51 -r 51 -s 300 -L 4 -o + +carrier = nrCarrierConfig; +carrier.NSizeGrid = 51; +carrier.NSlot = 8; +carrier.SubcarrierSpacing = 30; +carrier.CyclicPrefix = "normal"; + +pusch = nrPUSCHConfig; +pusch.PRBSet = 0:carrier.NSizeGrid-1; +pusch.SymbolAllocation = [0,12]; +pusch.MappingType = "B"; +pusch.NID = 0; +pusch.RNTI = 4660; +pusch.NumLayers = 1; +pusch.TransformPrecoding = false; +pusch.TransmissionScheme = "nonCodebook"; +pusch.NumAntennaPorts = 1; +pusch.TPMI = 0; +pusch.Modulation = "64QAM"; + +pusch.DMRS.DMRSConfigurationType = 1; +pusch.DMRS.DMRSTypeAPosition = 2; +pusch.DMRS.NumCDMGroupsWithoutData = 1; +pusch.DMRS.DMRSAdditionalPosition = 0; +pusch.DMRS.NIDNSCID = 0; +pusch.DMRS.NRSID = 0; +pusch.DMRS.NSCID = 0; +pusch.DMRS.GroupHopping = 0; +pusch.DMRS.SequenceHopping = 0; + +pusch.BetaOffsetACK = 20; +pusch.BetaOffsetCSI1 = 6.25; +pusch.BetaOffsetCSI2 = 1; +pusch.UCIScaling = 1; + +[puschIndices,puschIndicesInfo] = nrPUSCHIndices(carrier,pusch); + +dmrsLayerSymbols = nrPUSCHDMRS(carrier,pusch); +dmrsLayerIndices = nrPUSCHDMRSIndices(carrier,pusch); + +run("../../../cmake_targets/ran_build/build/txsig0.m"); +rxGrid = nrOFDMDemodulate(carrier,txs0); +[estChannelGrid,noiseEst] = nrChannelEstimate(carrier,rxGrid,dmrsLayerIndices,dmrsLayerSymbols); +[puschRx,puschHest] = nrExtractResources(puschIndices,rxGrid,estChannelGrid); +[puschEq,csi] = nrEqualizeMMSE(puschRx,puschHest,noiseEst); + +decodeULSCH = nrULSCHDecoder; +decodeULSCH.MultipleHARQProcesses = false; +decodeULSCH.CBGTransmission = false; +decodeULSCH.TargetCodeRate = 910 / 1024; +decodeULSCH.LDPCDecodingAlgorithm = 'Normalized min-sum'; +decodeULSCH.MaximumLDPCIterationCount = 6; +decodeULSCH.TransportBlockLength = 37896; + +oack = 3; +ocsi1 = 0; +ocsi2 = 0; +[ulschLLRs,rxSymbols] = nrPUSCHDecode(carrier,pusch,decodeULSCH.TargetCodeRate,... + decodeULSCH.TransportBlockLength,oack,ocsi1,ocsi2,puschEq,noiseEst); +rmInfo = nrULSCHInfo(pusch,decodeULSCH.TargetCodeRate,... + decodeULSCH.TransportBlockLength,oack,ocsi1,ocsi2); +[culsch,cack,ccsi1,ccsi2] = nrULSCHDemultiplex(pusch,decodeULSCH.TargetCodeRate,... + decodeULSCH.TransportBlockLength,oack,ocsi1,ocsi2,ulschLLRs); + +plot(rxSymbols,'*'); + +decodeULSCH.reset(); +rv = 0; % Redundancy version for decoding +[decodedBits,blkerr] = decodeULSCH(culsch,pusch.Modulation,pusch.NumLayers,rv); +% Display the decoded bits +disp('Block error:'); +disp(blkerr); + +ucibits = nrUCIDecode(cack,oack); +% Display the decoded UCI bits +disp('Decoded UCI bits:'); +disp(ucibits); diff --git a/openair1/SIMULATION/TOOLS/uci_on_pusch_encode.m b/openair1/SIMULATION/TOOLS/uci_on_pusch_encode.m new file mode 100644 index 0000000000..0b83d211d2 --- /dev/null +++ b/openair1/SIMULATION/TOOLS/uci_on_pusch_encode.m @@ -0,0 +1,85 @@ +% ulsim command for same pusch config +% ./nr_ulsim -m 27 -u 1 -R 51 -r 51 -s 300 -o uci_on_pusch_7.bin + +carrier = nrCarrierConfig; +carrier.NSizeGrid = 51; +carrier.NSlot = 8; +carrier.SubcarrierSpacing = 30; +carrier.CyclicPrefix = "normal"; + +pusch = nrPUSCHConfig; +pusch.PRBSet = 0:carrier.NSizeGrid-1; +pusch.SymbolAllocation = [0,12]; +pusch.MappingType = "B"; +pusch.NID = 0; +pusch.RNTI = 4660; +pusch.NumLayers = 1; +pusch.TransformPrecoding = false; +pusch.TransmissionScheme = "nonCodebook"; +pusch.NumAntennaPorts = 1; +pusch.TPMI = 0; +pusch.Modulation = "64QAM"; + +pusch.DMRS.DMRSConfigurationType = 1; +pusch.DMRS.DMRSTypeAPosition = 2; +pusch.DMRS.NumCDMGroupsWithoutData = 1; +pusch.DMRS.DMRSAdditionalPosition = 0; +pusch.DMRS.NIDNSCID = 0; +pusch.DMRS.NRSID = 0; +pusch.DMRS.NSCID = 0; +pusch.DMRS.GroupHopping = 0; +pusch.DMRS.SequenceHopping = 0; + +pusch.BetaOffsetACK = 20; +pusch.BetaOffsetCSI1 = 6.25; +pusch.BetaOffsetCSI2 = 1; +pusch.UCIScaling = 1; + +A = 37896; +rate = 910 / 1024; +rv = 0; +modulation = pusch.Modulation; +nlayers = pusch.NumLayers; % Number of layers for decoding + +oack = 7; +ocsi1 = 0; +ocsi2 = 0; +cbsInfo = nrULSCHInfo(pusch, rate, A, oack, ocsi1, ocsi2); % Get ULSCH information + +ack = randi([0 1],oack,1); +csi1 = randi([0 1],ocsi1,1); +csi2 = randi([0 1],ocsi2,1); +cack = nrUCIEncode(ack,cbsInfo.GACK,pusch.Modulation); +ccsi1 = []; +ccsi2 = []; + +tb_data = randi([0 1],A,1); +% Transport block CRC attachment +tbIn = nrCRCEncode(tb_data,cbsInfo.CRC); + +% Code block segmentation and CRC attachment +cbsIn = nrCodeBlockSegmentLDPC(tbIn,cbsInfo.BGN); + +% LDPC encoding +enc = nrLDPCEncode(cbsIn,cbsInfo.BGN); + +% Rate matching and code block concatenation +chIn = nrRateMatchLDPC(enc,cbsInfo.GULSCH,rv,modulation,nlayers); +culsch = chIn; + + +% Get the codeword and locations of each type (data and UCI) +[cw,indInfo] = nrULSCHMultiplex(pusch,rate,A,culsch,cack,ccsi1,ccsi2); +cw_scr = nrPUSCHScramble(cw,pusch.NID,pusch.RNTI); + +% Write to bin file +fid = fopen('uci_on_pusch_7.bin', 'wb'); +to_write = [A,oack,ocsi1,ocsi2,length(cw),length(cw_scr)]; +fwrite(fid, to_write, 'uint32'); % Write the data to the binary file +fwrite(fid, tb_data, "uint8"); +fwrite(fid, ack, "uint8"); +fwrite(fid, csi1, "uint8"); +fwrite(fid, csi2, "uint8"); +fwrite(fid, cw, "uint8"); +fwrite(fid, cw_scr, "uint8"); +fclose(fid); % Close the file after writing