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.
This commit is contained in:
alexjiao
2025-09-26 11:14:12 +08:00
parent 8222fa27e5
commit ceda9c1141
2 changed files with 382 additions and 2 deletions
@@ -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.
+371
View File
@@ -49,6 +49,12 @@
#include "PHY/NR_REFSIG/ul_ref_seq_nr.h"
#include <openair2/UTIL/OPT/opt.h>
#include "PHY/log_tools.h"
#include "PHY/NR_UE_TRANSPORT/pucch_nr.h"
#include <math.h>
#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,