From a64038658f66693808f6f3dde1e8747047118d53 Mon Sep 17 00:00:00 2001 From: alexjiao Date: Fri, 13 Jun 2025 15:56:05 +0800 Subject: [PATCH] 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,