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.
This commit is contained in:
alexjiao
2025-09-26 11:14:08 +08:00
parent 5e5f79e993
commit a64038658f
3 changed files with 194 additions and 96 deletions
@@ -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)
+189 -96
View File
@@ -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)
+2
View File
@@ -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,