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:
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user