mirror of
https://gitlab.eurecom.fr/oai/openairinterface5g.git
synced 2026-07-13 04:30:28 +00:00
map_overlapped_ack() divides num_reserved_bits_on_sym by Qm to get the number of reserved REs. Add assertion to catch cases where the reserved bit count is not a multiple of Qm, which would indicate a bug in the upstream UCI-on-PUSCH mapping. Signed-off-by: Alex Jiao <alex.jiao@keysight.com>
1567 lines
61 KiB
C
1567 lines
61 KiB
C
/*
|
||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||
*/
|
||
|
||
/*!
|
||
* \brief Top-level routines for transmission of the PUSCH TS 38.211 v 15.4.0
|
||
*/
|
||
#include <stdint.h>
|
||
#include "PHY/NR_REFSIG/dmrs_nr.h"
|
||
#include "PHY/NR_REFSIG/ptrs_nr.h"
|
||
#include "PHY/NR_REFSIG/nr_refsig.h"
|
||
#include "PHY/NR_UE_TRANSPORT/nr_transport_ue.h"
|
||
#include "PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h"
|
||
#include "PHY/MODULATION/nr_modulation.h"
|
||
#include "PHY/MODULATION/modulation_common.h"
|
||
#include "common/utils/assertions.h"
|
||
#include "common/utils/nr/nr_common.h"
|
||
#include "PHY/NR_TRANSPORT/nr_transport_common_proto.h"
|
||
#include "PHY/NR_TRANSPORT/nr_sch_dmrs.h"
|
||
#include "PHY/defs_nr_common.h"
|
||
#include "PHY/TOOLS/tools_defs.h"
|
||
#include "executables/nr-softmodem.h"
|
||
#include "executables/softmodem-common.h"
|
||
#include "T_messages_creator.h"
|
||
#include "PHY/NR_REFSIG/ul_ref_seq_nr.h"
|
||
#include <openair2/UTIL/OPT/opt.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
|
||
//#define DEBUG_DFT_IDFT
|
||
|
||
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 data (punctured)
|
||
BIT_TYPE_ACK_PLACEHOLDER = 3, // Reserved for HARQ-ACK placeholders (not scrambled)
|
||
BIT_TYPE_CSI1 = 4, // CSI Part 1 bit
|
||
BIT_TYPE_CSI2 = 5, // CSI Part 2 bit
|
||
BIT_TYPE_ACK_RESERVED_CSI2,
|
||
BIT_TYPE_ACK_PLACEHOLDER_CSI2
|
||
} uci_on_pusch_bit_type_t;
|
||
|
||
static void nr_pusch_codeword_scrambling(uint8_t *in,
|
||
uint32_t size,
|
||
uint32_t Nid,
|
||
uint32_t A,
|
||
uint32_t n_RNTI,
|
||
const uci_on_pusch_bit_type_t *template,
|
||
uint32_t *out)
|
||
{
|
||
// no UCI on PUSCH -> optimized scrambling
|
||
if (template == NULL) {
|
||
nr_codeword_scrambling(in, size, 0, Nid, n_RNTI, out);
|
||
return;
|
||
}
|
||
uint32_t *seq = gold_cache((n_RNTI << 15) + Nid, (size + 31) / 32);
|
||
uint32_t num_words = (size + 31) / 32;
|
||
memset(out, 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;
|
||
uint32_t bit = (in[i / 8] >> (i % 8)) & 1;
|
||
if (template[i] != BIT_TYPE_ACK_PLACEHOLDER && template[i] != BIT_TYPE_ACK_PLACEHOLDER_CSI2)
|
||
bit ^= (seq[word_idx] >> bit_idx) & 1;
|
||
else if (A == 1 && (template[i - 1] == BIT_TYPE_ACK_RESERVED || template[i - 1] == BIT_TYPE_ACK_RESERVED_CSI2)) {
|
||
uint32_t last_word_idx = (i - 1) / 32;
|
||
uint32_t last_bit_idx = (i - 1) % 32;
|
||
bit ^= (seq[last_word_idx] >> last_bit_idx) & 1;
|
||
}
|
||
out[word_idx] |= (bit << bit_idx);
|
||
}
|
||
}
|
||
|
||
/*
|
||
The function pointers are set once before calling the mapping funcion for
|
||
all symbols based on different parameters. Then the mapping is done for
|
||
each symbol by calling the function pointers.
|
||
*/
|
||
typedef void (*map_dmrs_func_t)(const unsigned int, const c16_t *, c16_t *);
|
||
typedef void (*map_data_dmrs_func_t)(const unsigned int, const c16_t *, c16_t *);
|
||
|
||
/*
|
||
The following set of functions map dmrs and/or data REs in one RB based on
|
||
configuration of DMRS type, number of CDM groups with no data and delta.
|
||
For all other combinations of the parameters not present below is not
|
||
applicable.
|
||
*/
|
||
|
||
/*
|
||
DMRS mapping in a RB for Type 1.
|
||
Mapping as in TS 38.211 6.4.1.1.3 k = 4n + 2k^prime + delta
|
||
*/
|
||
static void map_dmrs_type1_cdm1_rb(const unsigned int delta, const c16_t *dmrs, c16_t *out)
|
||
{
|
||
*(out + delta) = *dmrs++;
|
||
*(out + delta + 2) = *dmrs++;
|
||
*(out + delta + 4) = *dmrs++;
|
||
*(out + delta + 6) = *dmrs++;
|
||
*(out + delta + 8) = *dmrs++;
|
||
*(out + delta + 10) = *dmrs++;
|
||
}
|
||
|
||
/*
|
||
Data in DMRS symbol for Type 1, NumCDMGroupNoData = 1 and delta 0 (antenna port 0 and 1).
|
||
There is no data in DMRS symbol for other scenarios in type 1.
|
||
*/
|
||
static void map_data_dmrs_type1_cdm1_rb(const unsigned int num_cdm_no_data, const c16_t *data, c16_t *out)
|
||
{
|
||
UNUSED(num_cdm_no_data);
|
||
*(out + 1) = *data++;
|
||
*(out + 3) = *data++;
|
||
*(out + 5) = *data++;
|
||
*(out + 7) = *data++;
|
||
*(out + 9) = *data++;
|
||
*(out + 11) = *data++;
|
||
}
|
||
|
||
#define NR_DMRS_TYPE2_CDM_GRP_SIZE 2
|
||
#define NR_DMRS_TYPE2_NUM_CDM_GRP 3
|
||
|
||
/*
|
||
Map DMRS for type 2
|
||
Mapping as in TS 38.211 6.4.1.1.3 k = 6n + k^prime + delta
|
||
*/
|
||
static void map_dmrs_type2_rb(const unsigned int delta, const c16_t *dmrs, c16_t *out)
|
||
{
|
||
memcpy(out + delta, dmrs, sizeof(c16_t) * NR_DMRS_TYPE2_CDM_GRP_SIZE);
|
||
out += (NR_DMRS_TYPE2_CDM_GRP_SIZE * NR_DMRS_TYPE2_NUM_CDM_GRP);
|
||
dmrs += NR_DMRS_TYPE2_CDM_GRP_SIZE;
|
||
memcpy(out + delta, dmrs, sizeof(c16_t) * NR_DMRS_TYPE2_CDM_GRP_SIZE);
|
||
}
|
||
|
||
/*
|
||
Map data for type 2 DMRS
|
||
*/
|
||
static void map_data_dmrs_type2_rb(const unsigned int num_cdm_no_data, const c16_t *data, c16_t *out)
|
||
{
|
||
unsigned int offset = num_cdm_no_data * NR_DMRS_TYPE2_CDM_GRP_SIZE;
|
||
const unsigned int size = (NR_DMRS_TYPE2_NUM_CDM_GRP - num_cdm_no_data) * NR_DMRS_TYPE2_CDM_GRP_SIZE;
|
||
memcpy(out + offset, data, sizeof(c16_t) * size);
|
||
offset += NR_DMRS_TYPE2_CDM_GRP_SIZE * NR_DMRS_TYPE2_NUM_CDM_GRP;
|
||
data += size;
|
||
memcpy(out + offset, data, sizeof(c16_t) * size);
|
||
}
|
||
|
||
/*
|
||
Map data and PTRS in RB
|
||
*/
|
||
static void map_data_ptrs(const unsigned int ptrsIdx, const c16_t *data, const c16_t *ptrs, c16_t *out)
|
||
{
|
||
memcpy(out, data, sizeof(c16_t) * ptrsIdx);
|
||
data += ptrsIdx;
|
||
*(out + ptrsIdx) = *ptrs;
|
||
memcpy(out + ptrsIdx + 1, data, sizeof(c16_t) * NR_NB_SC_PER_RB - ptrsIdx - 1);
|
||
}
|
||
|
||
/*
|
||
Map data only in RB
|
||
*/
|
||
static void map_data_rb(const c16_t *data, c16_t *out)
|
||
{
|
||
memcpy(out, data, sizeof(c16_t) * NR_NB_SC_PER_RB);
|
||
}
|
||
|
||
/*
|
||
This function is used when a PRB is on both sides of DC.
|
||
The destination buffer in this case in not contiguous so REs are mapped on to a temporary buffer
|
||
so that we can reuse the existing functions. Then it is copied to the destination buffer.
|
||
*/
|
||
static void map_over_dc(const unsigned int right_dc,
|
||
const unsigned int num_cdm_no_data,
|
||
const unsigned int fft_size,
|
||
const unsigned int dmrs_per_rb,
|
||
const unsigned int data_per_rb,
|
||
const unsigned int delta,
|
||
const unsigned int ptrsIdx,
|
||
const c16_t **ptrs,
|
||
const c16_t **dmrs,
|
||
const c16_t **data,
|
||
c16_t **out,
|
||
map_dmrs_func_t map_data_dmrs_ptr,
|
||
map_dmrs_func_t map_dmrs_ptr)
|
||
{
|
||
// if first RE is DC no need to map in this function
|
||
if (right_dc == 0)
|
||
return;
|
||
|
||
c16_t *out_tmp = *out;
|
||
c16_t tmp_out_buf[NR_NB_SC_PER_RB];
|
||
const unsigned int left_dc = NR_NB_SC_PER_RB - right_dc;
|
||
/* copy out to temp buffer. incase we want to preserve the REs in the out buffer
|
||
as we call mapping of data in DMRS symbol after mapping DMRS REs
|
||
*/
|
||
memcpy(tmp_out_buf, out_tmp, sizeof(c16_t) * left_dc);
|
||
out_tmp -= (fft_size - left_dc);
|
||
memcpy(tmp_out_buf + left_dc, out_tmp, sizeof(c16_t) * right_dc);
|
||
|
||
/* map on to temp buffer */
|
||
if (dmrs && data) {
|
||
map_data_dmrs_ptr(num_cdm_no_data, *data, tmp_out_buf);
|
||
*data += data_per_rb;
|
||
} else if (dmrs) {
|
||
map_dmrs_ptr(delta, *dmrs, tmp_out_buf);
|
||
*dmrs += dmrs_per_rb;
|
||
} else if (ptrs) {
|
||
map_data_ptrs(ptrsIdx, *data, *ptrs, tmp_out_buf);
|
||
*data += (NR_NB_SC_PER_RB - 1);
|
||
*ptrs += 1;
|
||
} else if (data) {
|
||
map_data_rb(*data, tmp_out_buf);
|
||
*data += NR_NB_SC_PER_RB;
|
||
} else {
|
||
DevAssert(false);
|
||
}
|
||
|
||
/* copy back to out buffer */
|
||
out_tmp = *out;
|
||
memcpy(out_tmp, tmp_out_buf, sizeof(c16_t) * left_dc);
|
||
out_tmp -= (fft_size - left_dc);
|
||
memcpy(out_tmp, tmp_out_buf + left_dc, sizeof(c16_t) * right_dc);
|
||
out_tmp += right_dc;
|
||
*out = out_tmp;
|
||
}
|
||
|
||
/*
|
||
Holds params needed for PUSCH resoruce mapping
|
||
*/
|
||
typedef struct {
|
||
rnti_t rnti;
|
||
unsigned int K_ptrs;
|
||
unsigned int k_RE_ref;
|
||
unsigned int first_sc_offset;
|
||
unsigned int fft_size;
|
||
unsigned int num_rb_max;
|
||
unsigned int symbols_per_slot;
|
||
unsigned int slot;
|
||
unsigned int dmrs_scrambling_id;
|
||
unsigned int scid;
|
||
unsigned int dmrs_port;
|
||
int *Wt;
|
||
int *Wf;
|
||
unsigned int dmrs_symb_pos;
|
||
unsigned int ptrs_symb_pos;
|
||
unsigned int pdu_bit_map;
|
||
transformPrecoder_t transform_precoding;
|
||
unsigned int bwp_start;
|
||
unsigned int start_rb;
|
||
unsigned int nb_rb;
|
||
unsigned int start_symbol;
|
||
unsigned int num_symbols;
|
||
pusch_dmrs_type_t dmrs_type;
|
||
unsigned int delta;
|
||
unsigned int num_cdm_no_data;
|
||
} nr_phy_pxsch_params_t;
|
||
|
||
/*
|
||
Map all REs in one OFDM symbol
|
||
This function operation is as follows:
|
||
mapping is done on RB basis. if RB contains DC and if DC is in middle
|
||
of the RB, then the mapping is done via map_over_dc().
|
||
*/
|
||
static void map_current_symbol(const nr_phy_pxsch_params_t p,
|
||
const bool dmrs_symbol,
|
||
const bool ptrs_symbol,
|
||
const c16_t *dmrs_seq,
|
||
const c16_t *ptrs_seq,
|
||
const c16_t **data,
|
||
c16_t *out,
|
||
map_dmrs_func_t map_dmrs_ptr,
|
||
map_data_dmrs_func_t map_data_dmrs_ptr)
|
||
{
|
||
const unsigned int abs_start_rb = p.bwp_start + p.start_rb;
|
||
const unsigned int start_sc = (p.first_sc_offset + abs_start_rb * NR_NB_SC_PER_RB) % p.fft_size;
|
||
const unsigned int dc_rb = (p.fft_size - start_sc) / NR_NB_SC_PER_RB;
|
||
const unsigned int rb_over_dc = (p.fft_size - start_sc) % NR_NB_SC_PER_RB;
|
||
const unsigned int n_cdm = p.num_cdm_no_data;
|
||
const c16_t *data_tmp = *data;
|
||
/* If current symbol is DMRS symbol */
|
||
if (dmrs_symbol) {
|
||
const unsigned int dmrs_per_rb = (p.dmrs_type == pusch_dmrs_type1) ? 6 : 4;
|
||
const unsigned int data_per_rb = NR_NB_SC_PER_RB - dmrs_per_rb;
|
||
|
||
const c16_t *p_mod_dmrs = dmrs_seq + abs_start_rb * dmrs_per_rb;
|
||
c16_t *out_tmp = out + start_sc;
|
||
for (unsigned int rb = 0; rb < p.nb_rb; rb++) {
|
||
if (rb == dc_rb) {
|
||
// map RB at DC
|
||
if (rb_over_dc) {
|
||
// if DC is in middle of RB, the following function handles it.
|
||
map_over_dc(rb_over_dc, n_cdm, p.fft_size, dmrs_per_rb, data_per_rb, p.delta, 0, NULL, &p_mod_dmrs, NULL, &out_tmp, map_data_dmrs_ptr, map_dmrs_ptr);
|
||
continue;
|
||
} else {
|
||
// else just move the pointer and following function will map the rb
|
||
out_tmp -= p.fft_size;
|
||
}
|
||
}
|
||
map_dmrs_ptr(p.delta, p_mod_dmrs, out_tmp);
|
||
p_mod_dmrs += dmrs_per_rb;
|
||
out_tmp += NR_NB_SC_PER_RB;
|
||
}
|
||
|
||
/* if there is data in current DMRS symbol, we map it here. */
|
||
if (map_data_dmrs_ptr) {
|
||
c16_t *out_tmp = out + start_sc;
|
||
for (unsigned int rb = 0; rb < p.nb_rb; rb++) {
|
||
if (rb == dc_rb) {
|
||
if (rb_over_dc) {
|
||
map_over_dc(rb_over_dc, n_cdm, p.fft_size, dmrs_per_rb, data_per_rb, p.delta, 0, NULL, &p_mod_dmrs, &data_tmp, &out_tmp, map_data_dmrs_ptr, map_dmrs_ptr);
|
||
continue;
|
||
} else {
|
||
out_tmp -= p.fft_size;
|
||
}
|
||
}
|
||
map_data_dmrs_ptr(n_cdm, data_tmp, out_tmp);
|
||
data_tmp += data_per_rb;
|
||
out_tmp += NR_NB_SC_PER_RB;
|
||
}
|
||
}
|
||
/* If current symbol is a PTRS symbol */
|
||
} else if (ptrs_symbol) {
|
||
const unsigned int first_ptrs_re = get_first_ptrs_re(p.rnti, p.K_ptrs, p.nb_rb, p.k_RE_ref) + start_sc;
|
||
const unsigned int ptrs_idx_re = (start_sc - first_ptrs_re) % NR_NB_SC_PER_RB; // PTRS RE index within RB
|
||
unsigned int non_ptrs_rb = (start_sc - first_ptrs_re) / NR_NB_SC_PER_RB; // number of RBs before the first PTRS RB
|
||
int ptrs_idx_rb = -non_ptrs_rb; // RB count to check for PTRS RB
|
||
c16_t *out_tmp = out + start_sc;
|
||
const c16_t *p_mod_ptrs = ptrs_seq;
|
||
/* map data to RBs before the first PTRS RB or if current RB has no PTRS */
|
||
for (unsigned int rb = 0; rb < p.nb_rb; rb++) {
|
||
if (rb < non_ptrs_rb || ptrs_idx_rb % p.K_ptrs) {
|
||
if (rb == dc_rb) {
|
||
if (rb_over_dc) {
|
||
map_over_dc(rb_over_dc, n_cdm, p.fft_size, 0, 0, p.delta, 0, NULL, NULL, &data_tmp, &out_tmp, map_data_dmrs_ptr, map_dmrs_ptr);
|
||
continue;
|
||
} else {
|
||
out_tmp -= p.fft_size;
|
||
}
|
||
}
|
||
map_data_rb(data_tmp, out_tmp);
|
||
data_tmp += NR_NB_SC_PER_RB;
|
||
out_tmp += NR_NB_SC_PER_RB;
|
||
} else {
|
||
if (rb == dc_rb) {
|
||
if (rb_over_dc) {
|
||
map_over_dc(rb_over_dc, n_cdm, p.fft_size, 0, 0, p.delta, ptrs_idx_re, &p_mod_ptrs, NULL, &data_tmp, &out_tmp, map_data_dmrs_ptr, map_dmrs_ptr);
|
||
continue;
|
||
} else {
|
||
out_tmp -= p.fft_size;
|
||
}
|
||
}
|
||
map_data_ptrs(ptrs_idx_re, data_tmp, p_mod_ptrs, out_tmp);
|
||
p_mod_ptrs++; // increament once as only one PTRS RE per RB
|
||
data_tmp += (NR_NB_SC_PER_RB - 1);
|
||
out_tmp += NR_NB_SC_PER_RB;
|
||
}
|
||
ptrs_idx_rb++;
|
||
}
|
||
} else {
|
||
/* only data in this symbol */
|
||
c16_t *out_tmp = out + start_sc;
|
||
for (unsigned int rb = 0; rb < p.nb_rb; rb++) {
|
||
if (rb == dc_rb) {
|
||
if (rb_over_dc) {
|
||
map_over_dc(rb_over_dc, n_cdm, p.fft_size, 0, 0, p.delta, 0, NULL, NULL, &data_tmp, &out_tmp, map_data_dmrs_ptr, map_dmrs_ptr);
|
||
continue;
|
||
} else {
|
||
out_tmp -= p.fft_size;
|
||
}
|
||
}
|
||
map_data_rb(data_tmp, out_tmp);
|
||
data_tmp += NR_NB_SC_PER_RB;
|
||
out_tmp += NR_NB_SC_PER_RB;
|
||
}
|
||
}
|
||
*data = data_tmp;
|
||
}
|
||
|
||
/*
|
||
TS 38.211 table 6.4.1.1.3-1 and 2
|
||
*/
|
||
static void dmrs_amp_mult(const int Wt,
|
||
const int Wf[2],
|
||
const c16_t *mod_dmrs,
|
||
c16_t *mod_dmrs_out,
|
||
const uint32_t n_dmrs,
|
||
const pusch_dmrs_type_t dmrs_type,
|
||
const unsigned int num_cdm_groups_no_data)
|
||
{
|
||
float beta_dmrs_pusch = get_beta_dmrs(num_cdm_groups_no_data, dmrs_type == pusch_dmrs_type2);
|
||
/* short array that hold amplitude for k_prime = 0 and k_prime = 1 */
|
||
int32_t alpha_dmrs[2] __attribute((aligned(16)));
|
||
for (int_fast8_t i = 0; i < sizeofArray(alpha_dmrs); i++) {
|
||
const int32_t a = Wf[i] * Wt * AMP;
|
||
alpha_dmrs[i] = a * beta_dmrs_pusch;
|
||
}
|
||
|
||
/* multiply amplitude with complex DMRS vector */
|
||
for (int_fast16_t i = 0; i < n_dmrs; i++) {
|
||
mod_dmrs_out[i] = c16mulRealShift(mod_dmrs[i], alpha_dmrs[i % 2], 15);
|
||
}
|
||
}
|
||
|
||
/*
|
||
Map ULSCH data and DMRS in all of the scheduled symbols and PRBs
|
||
*/
|
||
static void map_symbols(const nr_phy_pxsch_params_t p,
|
||
const unsigned int slot,
|
||
const c16_t *dmrs_seq,
|
||
const c16_t *data,
|
||
c16_t *out)
|
||
{
|
||
// asign the function pointers
|
||
map_dmrs_func_t map_dmrs_ptr = NULL;
|
||
map_data_dmrs_func_t map_data_dmrs_ptr = NULL;
|
||
if (p.dmrs_type == pusch_dmrs_type1) {
|
||
map_dmrs_ptr = map_dmrs_type1_cdm1_rb;
|
||
map_data_dmrs_ptr = (p.num_cdm_no_data == 1) ? map_data_dmrs_type1_cdm1_rb : NULL;
|
||
} else {
|
||
map_dmrs_ptr = map_dmrs_type2_rb;
|
||
map_data_dmrs_ptr = (p.num_cdm_no_data < 3) ? map_data_dmrs_type2_rb : NULL;
|
||
}
|
||
// for all symbols
|
||
const unsigned int n_dmrs = (p.bwp_start + p.start_rb + p.nb_rb) * ((p.dmrs_type == pusch_dmrs_type1) ? 6 : 4);
|
||
const c16_t *cur_data = data;
|
||
uint8_t dmrs_symb_idx = 0;
|
||
for (int l = p.start_symbol; l < p.start_symbol + p.num_symbols; l++) {
|
||
const bool dmrs_symbol = is_dmrs_symbol(l, p.dmrs_symb_pos);
|
||
const bool ptrs_symbol = is_ptrs_symbol(l, p.ptrs_symb_pos);
|
||
c16_t mod_dmrs_amp[ALNARS_16_4(n_dmrs)] __attribute((aligned(16)));
|
||
c16_t mod_ptrs_amp[ALNARS_16_4(p.nb_rb)] __attribute((aligned(16)));
|
||
const uint32_t *gold = NULL;
|
||
if (dmrs_symbol || ptrs_symbol) {
|
||
gold = nr_gold_pusch(p.num_rb_max, p.symbols_per_slot, p.dmrs_scrambling_id, p.scid, slot, l);
|
||
}
|
||
if (dmrs_symbol) {
|
||
c16_t mod_dmrs[ALNARS_16_4(n_dmrs)] __attribute((aligned(16)));
|
||
if (p.transform_precoding == transformPrecoder_disabled) {
|
||
nr_modulation(gold, n_dmrs * 2, DMRS_MOD_ORDER, (int16_t *)mod_dmrs);
|
||
dmrs_amp_mult(p.Wt[dmrs_symb_idx % 2], p.Wf, mod_dmrs, mod_dmrs_amp, n_dmrs, p.dmrs_type, p.num_cdm_no_data);
|
||
} else {
|
||
dmrs_amp_mult(p.Wt[dmrs_symb_idx % 2], p.Wf, dmrs_seq, mod_dmrs_amp, n_dmrs, p.dmrs_type, p.num_cdm_no_data);
|
||
}
|
||
dmrs_symb_idx++;
|
||
} else if ((p.pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) && ptrs_symbol) {
|
||
AssertFatal(p.transform_precoding == transformPrecoder_disabled, "PTRS NOT SUPPORTED IF TRANSFORM PRECODING IS ENABLED\n");
|
||
c16_t mod_ptrs[ALNARS_16_4(p.nb_rb)] __attribute((aligned(16)));
|
||
nr_modulation(gold, p.nb_rb, DMRS_MOD_ORDER, (int16_t *)mod_ptrs);
|
||
const unsigned int beta_ptrs = 1; // temp value until power control is implemented
|
||
mult_complex_vector_real_scalar(mod_ptrs, beta_ptrs * AMP, mod_ptrs_amp, p.nb_rb);
|
||
}
|
||
map_current_symbol(p,
|
||
dmrs_symbol,
|
||
ptrs_symbol,
|
||
mod_dmrs_amp,
|
||
mod_ptrs_amp,
|
||
&cur_data, // increments every symbol
|
||
out + l * p.fft_size,
|
||
map_dmrs_ptr,
|
||
map_data_dmrs_ptr);
|
||
}
|
||
}
|
||
|
||
// Function to lookup beta offset value from Table 9.3-2 in TS 38.213
|
||
static double get_beta_offset_csi(const uint8_t beta_offset_idx)
|
||
{
|
||
static const double beta_offset_values[19] = {1.125,
|
||
1.250,
|
||
1.375,
|
||
1.625,
|
||
1.750,
|
||
2.000,
|
||
2.250,
|
||
2.500,
|
||
2.875,
|
||
3.125,
|
||
3.500,
|
||
4.000,
|
||
5.000,
|
||
6.250,
|
||
8.000,
|
||
10.000,
|
||
12.625,
|
||
15.875,
|
||
20.000};
|
||
|
||
if (beta_offset_idx >= sizeofArray(beta_offset_values)) {
|
||
LOG_E(PHY, "Invalid beta_offset_index %d, using default value\n", beta_offset_idx);
|
||
return beta_offset_values[9];
|
||
}
|
||
|
||
return beta_offset_values[beta_offset_idx];
|
||
}
|
||
|
||
static uint32_t get_d_factor_re(const uint32_t a, const uint32_t b)
|
||
{
|
||
uint32_t d_factor_re;
|
||
if (a >= b) {
|
||
d_factor_re = 1;
|
||
} else {
|
||
d_factor_re = floor((double)b / a);
|
||
if (d_factor_re == 0) {
|
||
d_factor_re = 1;
|
||
}
|
||
}
|
||
return d_factor_re;
|
||
}
|
||
|
||
// 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 according to 6.3.1.2.1 of 38.212
|
||
*/
|
||
static int get_crc_uci(const uint32_t ouci)
|
||
{
|
||
int L = 0;
|
||
if (ouci > 19) {
|
||
L = 11;
|
||
} else if (ouci > 11) {
|
||
L = 6;
|
||
} else {
|
||
L = 0;
|
||
}
|
||
return L;
|
||
}
|
||
|
||
static uint32_t get_Qd(const uint32_t ouci,
|
||
double beta,
|
||
double alpha,
|
||
const uint32_t eff_bits,
|
||
const uint32_t s1,
|
||
const uint32_t s2,
|
||
const uint32_t sub)
|
||
{
|
||
// as described in section 6.3.2.4.1 of 38.212
|
||
if (ouci == 0)
|
||
return 0;
|
||
uint32_t first_term = ceil(((double)ouci + get_crc_uci(ouci)) * (double)beta * s1 / eff_bits);
|
||
uint32_t second_term = ceil(alpha * s2) - sub;
|
||
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 NR_UE_ULSCH_t *ulsch_ue,
|
||
const NR_UL_UE_HARQ_t *harq_process_ul_ue,
|
||
unsigned int *G)
|
||
{
|
||
const nfapi_nr_ue_pusch_pdu_t *pusch_pdu = &ulsch_ue->pusch_pdu;
|
||
// 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 = harq_process_ul_ue->K * harq_process_ul_ue->C;
|
||
|
||
uint16_t ul_dmrs_symb_pos = pusch_pdu->ul_dmrs_symb_pos;
|
||
// Calculate s1: total number of non-DMRS REs in allocation
|
||
int s1 = pusch_pdu->rb_size * NR_NB_SC_PER_RB * (pusch_pdu->nr_of_symbols - get_num_dmrs(ul_dmrs_symb_pos));
|
||
|
||
// Calculate s2: number of non-DMRS REs after first DMRS symbol
|
||
// __builtin_ctz returns the index of the first set bit
|
||
int first_dmrs_symbol = __builtin_ctz(ul_dmrs_symb_pos);
|
||
// mask with everything from (first_dmrs_symbol + 1) to the end
|
||
uint32_t range_mask = ((1U << pusch_pdu->nr_of_symbols) - 1) << pusch_pdu->start_symbol_index;
|
||
uint32_t post_dmrs_mask = range_mask & ~((1U << (first_dmrs_symbol + 1)) - 1);
|
||
// number of non-DMRS REs bits in that post-DMRS range
|
||
uint32_t non_dmrs_bits = post_dmrs_mask & ~ul_dmrs_symb_pos;
|
||
int num_non_dmrs_symbols = __builtin_popcount(non_dmrs_bits);
|
||
int s2 = num_non_dmrs_symbols * pusch_pdu->rb_size * NR_NB_SC_PER_RB;
|
||
|
||
if (ulsch_ue->ptrs_symbols) {
|
||
// for any OFDM symbol that does not carry DMRS of the PUSCH, M_UCI = M_PUSCH − M_PTRS
|
||
uint32_t non_dmrs_ptrs_mask = ulsch_ue->ptrs_symbols & ~ul_dmrs_symb_pos;
|
||
int ptrs_symb_in_alloc = __builtin_popcount(non_dmrs_ptrs_mask);
|
||
s1 -= (ptrs_symb_in_alloc * ulsch_ue->n_ptrs);
|
||
uint32_t ptrs_in_post_window = ulsch_ue->ptrs_symbols & post_dmrs_mask;
|
||
int num_ptrs_symbols_s2 = __builtin_popcount(ptrs_in_post_window);
|
||
s2 -= (num_ptrs_symbols_s2 * ulsch_ue->n_ptrs);
|
||
}
|
||
|
||
|
||
rate_match_info_uci_t rminfo = {0};
|
||
// if the number of HARQ-ACK information bits to be transmitted on PUSCH is 0, 1 or 2 bits
|
||
// the number of reserved resource elements for potential HARQ-ACK transmission is calculated using oack = 2
|
||
// according to TS 38.212 section 6.2.7, step 1
|
||
rminfo.O_ack = (pusch_pdu->pusch_uci.harq_ack_bit_length <= 2) ? 2 : pusch_pdu->pusch_uci.harq_ack_bit_length;
|
||
const int nlqm = pusch_pdu->nrOfLayers * pusch_pdu->qam_mod_order; // product of number of layers and modulation order
|
||
|
||
// get the number of coded HARQ-ACK symbols and bits, TS 38.212 section 6.3.2.4.1.1 (considering reservetion)
|
||
rminfo.Q_dash_ACK = get_Qd(rminfo.O_ack, beta, alpha, sumKr, s1, s2, 0);
|
||
rminfo.E_uci_ACK = rminfo.Q_dash_ACK * nlqm;
|
||
// actual number of coded HARQ-ACK bits to place
|
||
if (pusch_pdu->pusch_uci.harq_ack_bit_length <= 2) {
|
||
uint16_t Q_dash_ACK_actual = get_Qd(pusch_pdu->pusch_uci.harq_ack_bit_length, beta, alpha, sumKr, s1, s2, 0);
|
||
rminfo.E_uci_ACK_actual = Q_dash_ACK_actual * nlqm;
|
||
}
|
||
|
||
// get beta offset for csi
|
||
const double beta_csi1 = get_beta_offset_csi(pusch_pdu->pusch_uci.beta_offset_csi1);
|
||
|
||
// get the number of coded CSI part 1 symbols and bits, TS 38.212 section 6.3.2.4.1.2
|
||
const uint16_t ocsi1 = pusch_pdu->pusch_uci.csi_payload.p1_bits;
|
||
rminfo.Q_dash_CSI1 = get_Qd(ocsi1, beta_csi1, alpha, sumKr, s1, s1, rminfo.Q_dash_ACK);
|
||
rminfo.E_uci_CSI1 = rminfo.Q_dash_CSI1 * nlqm;
|
||
|
||
// get the number of coded CSI part 2 symbols and bits, TS 38.212 section 6.3.2.4.1.3
|
||
const double beta_csi2 = get_beta_offset_csi(pusch_pdu->pusch_uci.beta_offset_csi2);
|
||
const uint16_t ocsi2 = pusch_pdu->pusch_uci.csi_payload.p2_bits;
|
||
rminfo.Q_dash_CSI2 = get_Qd(ocsi2, beta_csi2, alpha, sumKr, s1, s1, rminfo.Q_dash_ACK + rminfo.Q_dash_CSI1);
|
||
rminfo.E_uci_CSI2 = rminfo.Q_dash_CSI2 * nlqm;
|
||
|
||
rminfo.G_ulsch = *G - (rminfo.E_uci_CSI1 + rminfo.E_uci_CSI2);
|
||
if (rminfo.O_ack > 2) {
|
||
rminfo.G_ulsch -= rminfo.E_uci_ACK;
|
||
}
|
||
|
||
*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_CSI1=%u, E_uci_CSI2=%u, G_ulsch=%u\n",
|
||
rminfo.E_uci_ACK,
|
||
rminfo.E_uci_CSI1,
|
||
rminfo.E_uci_CSI2,
|
||
rminfo.G_ulsch);
|
||
|
||
return rminfo;
|
||
}
|
||
|
||
static int initialize_mapping_resources(const NR_UE_ULSCH_t *ulsch_ue,
|
||
uint32_t *m_ulsch_initial,
|
||
uint32_t *m_uci_current)
|
||
{
|
||
if (!m_ulsch_initial || !m_uci_current)
|
||
return -1;
|
||
const nfapi_nr_ue_pusch_pdu_t *pusch_pdu = &ulsch_ue->pusch_pdu;
|
||
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;
|
||
bool is_ptrs = (ulsch_ue->ptrs_symbols >> absolute_symbol_idx) & 0x01;
|
||
int ptrs_overhead = is_ptrs ? ulsch_ue->n_ptrs : 0;
|
||
if ((ul_dmrs_symb_pos >> absolute_symbol_idx) & 0x01) {
|
||
// Calculate available data REs on DMRS symbols based on DMRS configuration
|
||
m_ulsch_initial[i] = pusch_pdu->rb_size * data_re_on_dmrs_sym_per_prb - ptrs_overhead;
|
||
m_uci_current[i] = 0; // UCI is not mapped on DMRS symbols
|
||
} else { // Not a DMRS symbol
|
||
m_ulsch_initial[i] = res_per_symbol_non_dmrs - ptrs_overhead;
|
||
m_uci_current[i] = m_ulsch_initial[i];
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
// to compute the first non dmrs symbol and the first symbol after the first set of consecutive DMRS symbols
|
||
static void get_first_uci_symbol(const uint8_t start_symbol,
|
||
const uint8_t num_symbols,
|
||
const uint16_t dmrs_map,
|
||
int *first_non_dmrs_sym,
|
||
int *after_dmrs_symb)
|
||
{
|
||
// 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;
|
||
}
|
||
}
|
||
|
||
// Symbol after first consequtive DMRS symbol
|
||
const int first_dmrs_sym = get_next_dmrs_symbol_in_slot(dmrs_map, start_symbol, last_sym);
|
||
*after_dmrs_symb = first_dmrs_sym + 1;
|
||
while (is_dmrs_symbol(*after_dmrs_symb, dmrs_map) && *after_dmrs_symb < last_sym) {
|
||
(*after_dmrs_symb)++;
|
||
}
|
||
|
||
// Return relative symbol idx
|
||
*first_non_dmrs_sym -= start_symbol;
|
||
*after_dmrs_symb -= start_symbol;
|
||
}
|
||
|
||
static inline bool skip_mapping_current_uci(const uci_on_pusch_bit_type_t template, const uci_on_pusch_bit_type_t uci_type_to_map)
|
||
{
|
||
bool ret = false;
|
||
switch (uci_type_to_map) {
|
||
case BIT_TYPE_ACK_RESERVED:
|
||
case BIT_TYPE_ACK:
|
||
// ACK bits get the highest priority. Don't skip
|
||
ret = false;
|
||
break;
|
||
|
||
case BIT_TYPE_CSI1:
|
||
// Skip if already occupied by ACK and reserved ACK. Template is initialized with ULSCH
|
||
ret = (template != BIT_TYPE_ULSCH);
|
||
break;
|
||
|
||
case BIT_TYPE_CSI2:
|
||
// Skip if already occupied by ACK but not reserved ACK
|
||
ret = (template != BIT_TYPE_ACK_RESERVED && template != BIT_TYPE_ULSCH);
|
||
break;
|
||
|
||
default:
|
||
AssertFatal(0, "Mapping called with incorrect UCI type\n");
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
struct map_uci_common_arg {
|
||
uci_on_pusch_bit_type_t *template;
|
||
uci_on_pusch_bit_type_t uci_type_to_map;
|
||
uint32_t n_symbols;
|
||
uint32_t nlqm;
|
||
uint16_t G_uci;
|
||
uint8_t l1_c;
|
||
uint32_t *m_uci_current;
|
||
uint32_t *m_ulsch_initial;
|
||
uint32_t (*resv_ack_pos_symb)[MAX_UCI_CODED_BITS];
|
||
uint32_t *resv_ack_count_symb;
|
||
};
|
||
|
||
static void map_uci_common(struct map_uci_common_arg p)
|
||
{
|
||
uint32_t symbol_start_bit_idx[NR_SYMBOLS_PER_SLOT] = {0};
|
||
for (uint8_t s = 1; s < p.n_symbols; s++) {
|
||
symbol_start_bit_idx[s] = symbol_start_bit_idx[s - 1] + (p.m_ulsch_initial[s - 1] * p.nlqm);
|
||
}
|
||
|
||
if (p.uci_type_to_map == BIT_TYPE_ACK_RESERVED)
|
||
memset(p.resv_ack_count_symb, 0, sizeof(*p.resv_ack_count_symb) * NR_SYMBOLS_PER_SLOT);
|
||
|
||
uint32_t total_placed = 0;
|
||
for (uint8_t sym = p.l1_c; sym < p.n_symbols && total_placed < p.G_uci; sym++) {
|
||
uint32_t uci_re_on_sym = p.m_uci_current[sym];
|
||
if (p.uci_type_to_map == BIT_TYPE_CSI1 && p.resv_ack_count_symb) // need to remove reserved res
|
||
uci_re_on_sym -= p.resv_ack_count_symb[sym] / p.nlqm;
|
||
if (uci_re_on_sym <= 0) {
|
||
continue;
|
||
}
|
||
|
||
const uint32_t remaining_to_place = p.G_uci - total_placed;
|
||
const uint32_t num_re_to_select = ceil((double)remaining_to_place / p.nlqm);
|
||
uint32_t d_factor_re = get_d_factor_re(num_re_to_select, uci_re_on_sym);
|
||
uint32_t re_offset = 0;
|
||
uint32_t *cur_sym_resv_ack_pos = p.resv_ack_pos_symb[sym];
|
||
while (re_offset < uci_re_on_sym && total_placed < p.G_uci) {
|
||
uci_on_pusch_bit_type_t cur_template = p.template[symbol_start_bit_idx[sym] + (re_offset * p.nlqm)];
|
||
if (skip_mapping_current_uci(cur_template, p.uci_type_to_map)) {
|
||
re_offset++;
|
||
continue; // if RE already allocated to UCI or reserved for ACK
|
||
}
|
||
for (uint32_t bit_in_re = 0; bit_in_re < p.nlqm; bit_in_re++) {
|
||
if (total_placed >= p.G_uci) {
|
||
break;
|
||
}
|
||
uint32_t bit_offset_in_sym = (re_offset * p.nlqm) + bit_in_re;
|
||
uint32_t cw_idx = symbol_start_bit_idx[sym] + bit_offset_in_sym;
|
||
p.template[cw_idx] = p.uci_type_to_map;
|
||
if (p.uci_type_to_map == BIT_TYPE_ACK_RESERVED) {
|
||
cur_sym_resv_ack_pos[p.resv_ack_count_symb[sym]++] = cw_idx;
|
||
}
|
||
total_placed++;
|
||
}
|
||
if (p.uci_type_to_map == BIT_TYPE_CSI1 && p.resv_ack_pos_symb) {
|
||
uint32_t prev_re_offset = re_offset;
|
||
re_offset += d_factor_re;
|
||
for (uint32_t i = 0; i < p.resv_ack_count_symb[sym] / p.nlqm; i++) {
|
||
uint32_t resv_re = (p.resv_ack_pos_symb[sym][i * p.nlqm] - symbol_start_bit_idx[sym]) / p.nlqm;
|
||
if (resv_re > prev_re_offset && resv_re <= re_offset)
|
||
re_offset++;
|
||
}
|
||
} else {
|
||
re_offset += d_factor_re;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Maps HARQ-ACK bits when O_ACK <= 2 (overlapped ACK/ULSCH case).
|
||
*
|
||
* The template already has BIT_TYPE_ACK_RESERVED positions marked by map_uci_common,
|
||
* some of which may have been overwritten by CSI2 mapping.
|
||
*
|
||
* This function:
|
||
* 1. Resets all non-CSI2 reserved positions back to BIT_TYPE_ULSCH
|
||
* 2. Selects a subset of reserved positions for actual ACK placement,
|
||
* marking them as BIT_TYPE_ACK_RESERVED (real ACK bit) or
|
||
* BIT_TYPE_ACK_PLACEHOLDER (resolved x/y bit, not scrambled), based
|
||
* on their position within the Qm-bit modulation group:
|
||
* A=1: pos 0 is real ACK, pos 1+ are placeholders (y at pos 1, x at pos 2+)
|
||
* A=2: pos 0,1 are real ACK, pos 2+ are placeholders (x only)
|
||
*
|
||
*/
|
||
static void map_overlapped_ack(uci_on_pusch_bit_type_t *template,
|
||
uint16_t G_ack,
|
||
uint8_t l1_c,
|
||
const nfapi_nr_ue_pusch_pdu_t *pusch_pdu,
|
||
uint32_t positions_by_sym[][MAX_UCI_CODED_BITS],
|
||
const uint32_t *count_by_sym)
|
||
{
|
||
const int placeholder_start = (pusch_pdu->pusch_uci.harq_ack_bit_length == 1) ? 1 : 2;
|
||
const int Qm = pusch_pdu->qam_mod_order;
|
||
uint32_t ack_bits_marked = 0;
|
||
for (uint8_t sym_iter = l1_c; sym_iter < pusch_pdu->nr_of_symbols; sym_iter++) {
|
||
const uint32_t num_reserved_bits_on_sym = count_by_sym[sym_iter];
|
||
if (num_reserved_bits_on_sym == 0)
|
||
continue;
|
||
const uint32_t *reserved_indices_on_this_sym = positions_by_sym[sym_iter];
|
||
// pass 1: reset all non-CSI2 reserved positions to ULSCH
|
||
for (uint32_t i = 0; i < num_reserved_bits_on_sym; i++) {
|
||
uint32_t pos = reserved_indices_on_this_sym[i];
|
||
if (template[pos] != BIT_TYPE_CSI2)
|
||
template[pos] = BIT_TYPE_ULSCH;
|
||
}
|
||
// pass 2: mark selected positions as ACK_RESERVED or PLACEHOLDER
|
||
const int32_t num_ack_remaining = G_ack - ack_bits_marked;
|
||
if (num_ack_remaining <= 0)
|
||
continue;
|
||
AssertFatal(num_reserved_bits_on_sym % Qm == 0,
|
||
"reserved bits on symbol (%u) not a multiple of Qm (%d)\n",
|
||
num_reserved_bits_on_sym,
|
||
Qm);
|
||
const uint32_t num_reserved_re = num_reserved_bits_on_sym / Qm;
|
||
const uint32_t num_ack_re_remaining = num_ack_remaining / Qm;
|
||
const uint32_t d_factor_re = get_d_factor_re(num_ack_re_remaining, num_reserved_re);
|
||
for (uint32_t re = 0; re < num_reserved_re && ack_bits_marked < G_ack; re += d_factor_re) {
|
||
for (int b = 0; b < Qm; b++) {
|
||
uint32_t pos = reserved_indices_on_this_sym[re * Qm + b];
|
||
int bit_in_group = pos % Qm;
|
||
if (template[pos] == BIT_TYPE_ULSCH) // puncturing ULSCH
|
||
template[pos] = (bit_in_group >= placeholder_start) ? BIT_TYPE_ACK_PLACEHOLDER : BIT_TYPE_ACK_RESERVED;
|
||
else // puncturing CSIp2
|
||
template[pos] = (bit_in_group >= placeholder_start) ? BIT_TYPE_ACK_PLACEHOLDER_CSI2 : BIT_TYPE_ACK_RESERVED_CSI2;
|
||
ack_bits_marked++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
* Applies the template to build the final codeword
|
||
*/
|
||
#define WRITE_BIT(cw, i, bit) do { if (bit) (cw)[(i) / 8] |= (1 << ((i) % 8)); } while(0)
|
||
#define READ_PACKED(arr, idx) (((arr)[(idx) / 64] >> ((idx) % 64)) & 1ULL)
|
||
|
||
static void apply_template_to_codeword(uint8_t *codeword,
|
||
const uci_on_pusch_bit_type_t *template,
|
||
rate_match_info_uci_t *rm_info,
|
||
uint32_t codeword_len,
|
||
const uint8_t *ulsch_bits,
|
||
const uint64_t *cack,
|
||
const uint64_t *csi1,
|
||
const uint64_t *csi2,
|
||
uint32_t G_ulsch)
|
||
{
|
||
uint32_t ulsch_idx = 0;
|
||
uint32_t ack_idx = 0;
|
||
uint32_t csi1_idx = 0;
|
||
uint32_t csi2_idx = 0;
|
||
memset(codeword, 0, (codeword_len + 7) / 8);
|
||
|
||
for (uint32_t i = 0; i < codeword_len; i++) {
|
||
switch (template[i]) {
|
||
case BIT_TYPE_ACK:
|
||
if (rm_info->E_uci_ACK > 0 && ack_idx < rm_info->E_uci_ACK) {
|
||
WRITE_BIT(codeword, i, READ_PACKED(cack, ack_idx));
|
||
ack_idx++;
|
||
}
|
||
break;
|
||
case BIT_TYPE_ACK_RESERVED:
|
||
case BIT_TYPE_ACK_PLACEHOLDER:
|
||
if (rm_info->E_uci_ACK > 0 && ack_idx < rm_info->E_uci_ACK) {
|
||
WRITE_BIT(codeword, i, READ_PACKED(cack, ack_idx));
|
||
ack_idx++;
|
||
if (G_ulsch > 0 && ulsch_idx < G_ulsch)
|
||
ulsch_idx++;
|
||
}
|
||
break;
|
||
case BIT_TYPE_ACK_RESERVED_CSI2:
|
||
case BIT_TYPE_ACK_PLACEHOLDER_CSI2:
|
||
if (rm_info->E_uci_ACK > 0 && ack_idx < rm_info->E_uci_ACK) {
|
||
WRITE_BIT(codeword, i, READ_PACKED(cack, ack_idx));
|
||
ack_idx++;
|
||
}
|
||
// advance csi2_idx for punctured CSI2 bits
|
||
if (rm_info->E_uci_CSI2 > 0 && csi2_idx < rm_info->E_uci_CSI2)
|
||
csi2_idx++;
|
||
break;
|
||
case BIT_TYPE_CSI1:
|
||
if (rm_info->E_uci_CSI1 > 0 && csi1_idx < rm_info->E_uci_CSI1) {
|
||
WRITE_BIT(codeword, i, READ_PACKED(csi1, csi1_idx));
|
||
csi1_idx++;
|
||
}
|
||
break;
|
||
case BIT_TYPE_CSI2:
|
||
if (rm_info->E_uci_CSI2 > 0 && csi2_idx < rm_info->E_uci_CSI2) {
|
||
WRITE_BIT(codeword, i, READ_PACKED(csi2, csi2_idx));
|
||
csi2_idx++;
|
||
}
|
||
break;
|
||
case BIT_TYPE_ULSCH:
|
||
default:
|
||
if (G_ulsch > 0 && ulsch_idx < G_ulsch) {
|
||
WRITE_BIT(codeword, i, (ulsch_bits[ulsch_idx / 8] >> (ulsch_idx % 8)) & 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 NR_UE_ULSCH_t *ulsch_ue,
|
||
uci_on_pusch_bit_type_t *template,
|
||
unsigned int G_ulsch,
|
||
rate_match_info_uci_t *rm_info,
|
||
uint8_t *codeword,
|
||
uint32_t codeword_len,
|
||
const uint8_t *ulsch_bits,
|
||
const uint64_t *cack,
|
||
const uint64_t *csi1,
|
||
const uint64_t *csi2)
|
||
{
|
||
if (!codeword || codeword_len == 0 || !template)
|
||
return NULL;
|
||
const nfapi_nr_ue_pusch_pdu_t *pusch_pdu = &ulsch_ue->pusch_pdu;
|
||
const uint8_t n_symbols = pusch_pdu->nr_of_symbols;
|
||
if (n_symbols == 0 || n_symbols > NR_SYMBOLS_PER_SLOT)
|
||
return NULL;
|
||
|
||
uint32_t m_ulsch_initial[NR_SYMBOLS_PER_SLOT] = {0};
|
||
uint32_t m_uci_current[NR_SYMBOLS_PER_SLOT] = {0}; // This holds RE counts, not bit counts
|
||
|
||
if (initialize_mapping_resources(ulsch_ue, m_ulsch_initial, m_uci_current) != 0) {
|
||
LOG_E(PHY, "Failed to initialize mapping resources\n");
|
||
return NULL;
|
||
}
|
||
|
||
int first_non_dmrs_sym = 0;
|
||
int first_symb_after_dmrs = 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,
|
||
&first_symb_after_dmrs);
|
||
|
||
memset(template, 0, codeword_len * sizeof(uci_on_pusch_bit_type_t));
|
||
|
||
uint32_t positions_by_sym[NR_SYMBOLS_PER_SLOT][MAX_UCI_CODED_BITS] = {0};
|
||
uint32_t count_by_sym[NR_SYMBOLS_PER_SLOT] = {0};
|
||
|
||
struct map_uci_common_arg map_arg = {.template = template,
|
||
.n_symbols = pusch_pdu->nr_of_symbols,
|
||
.nlqm = pusch_pdu->qam_mod_order * pusch_pdu->nrOfLayers,
|
||
.l1_c = first_symb_after_dmrs,
|
||
.m_uci_current = m_uci_current,
|
||
.m_ulsch_initial = m_ulsch_initial};
|
||
|
||
int G_ack = rm_info->E_uci_ACK;
|
||
if (rm_info->O_ack == 2) {
|
||
map_arg.uci_type_to_map = BIT_TYPE_ACK_RESERVED;
|
||
map_arg.G_uci = G_ack;
|
||
map_arg.resv_ack_pos_symb = positions_by_sym;
|
||
map_arg.resv_ack_count_symb = count_by_sym;
|
||
map_uci_common(map_arg);
|
||
} else if (G_ack > 0) {
|
||
map_arg.uci_type_to_map = BIT_TYPE_ACK;
|
||
map_arg.G_uci = G_ack;
|
||
map_uci_common(map_arg);
|
||
}
|
||
|
||
// CSI part 1
|
||
map_arg.uci_type_to_map = BIT_TYPE_CSI1;
|
||
map_arg.G_uci = rm_info->E_uci_CSI1;
|
||
map_arg.resv_ack_pos_symb = positions_by_sym;
|
||
map_arg.resv_ack_count_symb = count_by_sym;
|
||
map_arg.l1_c = first_non_dmrs_sym;
|
||
map_uci_common(map_arg);
|
||
// CSI part 2
|
||
map_arg.uci_type_to_map = BIT_TYPE_CSI2;
|
||
map_arg.G_uci = rm_info->E_uci_CSI2;
|
||
map_uci_common(map_arg);
|
||
|
||
if (rm_info->O_ack == 2) {
|
||
map_overlapped_ack(template, rm_info->E_uci_ACK_actual, first_symb_after_dmrs, pusch_pdu, positions_by_sym, count_by_sym);
|
||
}
|
||
|
||
apply_template_to_codeword(codeword, template, rm_info, codeword_len, ulsch_bits, cack, csi1, csi2, G_ulsch);
|
||
|
||
return template;
|
||
}
|
||
|
||
void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE,
|
||
const uint32_t frame,
|
||
const uint8_t slot,
|
||
nr_phy_data_tx_t *phy_data,
|
||
c16_t **txdataF,
|
||
bool was_symbol_used[NR_SYMBOLS_PER_SLOT])
|
||
{
|
||
|
||
int harq_pid = phy_data->ulsch.pusch_pdu.pusch_data.harq_process_id;
|
||
|
||
if (phy_data->ulsch.status != NR_ACTIVE)
|
||
return;
|
||
|
||
start_meas_nr_ue_phy(UE, PUSCH_PROC_STATS);
|
||
|
||
uint8_t ULSCH_ids[1];
|
||
unsigned int G[1];
|
||
uint8_t pusch_id = 0;
|
||
ULSCH_ids[pusch_id] = 0;
|
||
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;
|
||
|
||
uint16_t nb_rb = pusch_pdu->rb_size;
|
||
uint8_t number_of_symbols = pusch_pdu->nr_of_symbols;
|
||
uint8_t dmrs_type = pusch_pdu->dmrs_config_type;
|
||
uint8_t cdm_grps_no_data = pusch_pdu->num_dmrs_cdm_grps_no_data;
|
||
uint8_t nb_dmrs_re_per_rb = ((dmrs_type == pusch_dmrs_type1) ? 6 : 4) * cdm_grps_no_data;
|
||
int start_symbol = pusch_pdu->start_symbol_index;
|
||
uint16_t ul_dmrs_symb_pos = pusch_pdu->ul_dmrs_symb_pos;
|
||
uint8_t mod_order = pusch_pdu->qam_mod_order;
|
||
uint8_t Nl = pusch_pdu->nrOfLayers;
|
||
uint32_t tb_size = pusch_pdu->pusch_data.tb_size;
|
||
uint16_t rnti = pusch_pdu->rnti;
|
||
|
||
for (int i = start_symbol; i < start_symbol + number_of_symbols; i++) {
|
||
was_symbol_used[i] = true;
|
||
if ((ul_dmrs_symb_pos >> i) & 0x01)
|
||
number_dmrs_symbols += 1;
|
||
}
|
||
|
||
///////////////////////PTRS parameters' initialization///////////////////
|
||
|
||
unsigned int K_ptrs = 0, k_RE_ref = 0;
|
||
uint32_t unav_res = 0;
|
||
ulsch_ue->ptrs_symbols = 0;
|
||
if (pusch_pdu->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) {
|
||
K_ptrs = pusch_pdu->pusch_ptrs.ptrs_freq_density;
|
||
k_RE_ref = pusch_pdu->pusch_ptrs.ptrs_ports_list[0].ptrs_re_offset;
|
||
uint8_t L_ptrs = 1 << pusch_pdu->pusch_ptrs.ptrs_time_density;
|
||
set_ptrs_symb_idx(&ulsch_ue->ptrs_symbols, number_of_symbols, start_symbol, L_ptrs, ul_dmrs_symb_pos);
|
||
ulsch_ue->n_ptrs = (nb_rb + K_ptrs - 1) / K_ptrs;
|
||
int ptrsSymbPerSlot = get_ptrs_symbols_in_slot(ulsch_ue->ptrs_symbols, start_symbol, number_of_symbols);
|
||
unav_res = ulsch_ue->n_ptrs * ptrsSymbPerSlot;
|
||
}
|
||
|
||
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,
|
||
.type = UE->frame_parms.frame_type == FDD ? FDD_RADIO : TDD_RADIO,
|
||
.pdu_buffer = harq_process_ul_ue->payload_AB,
|
||
.pdu_buffer_size = tb_size,
|
||
.ueid = 0,
|
||
.rntiType = WS_C_RNTI,
|
||
.rnti = rnti,
|
||
.sysFrame = frame,
|
||
.subframe = slot,
|
||
.harq_pid = harq_pid,
|
||
.oob_event = 0,
|
||
.oob_event_value = 0};
|
||
trace_pdu(&tmp);
|
||
|
||
/////////////////////////ULSCH coding/////////////////////////
|
||
|
||
rate_match_info_uci_t rm_info = {0};
|
||
if(nr_ulsch_pre_encoding(UE, ulsch_ue, frame, slot, G, 1, ULSCH_ids) != 0) {
|
||
LOG_E(PHY, "Error pre-encoding\n");
|
||
return;
|
||
}
|
||
|
||
bool uci_present = (pusch_pdu->pusch_uci.harq_ack_bit_length != 0) || (pusch_pdu->pusch_uci.csi_payload.p1_bits != 0);
|
||
if (uci_present) {
|
||
rm_info = calc_rate_match_info_uci(ulsch_ue, harq_process_ul_ue, &G[pusch_id]);
|
||
}
|
||
|
||
if (nr_ulsch_encoding(UE, ulsch_ue, frame, slot, G, 1, ULSCH_ids) == -1) {
|
||
stop_meas_nr_ue_phy(UE, PUSCH_PROC_STATS);
|
||
return;
|
||
}
|
||
|
||
LOG_D(PHY, "nr_ue_ulsch_procedures_slot hard_id %d %d.%d\n", harq_pid, frame, slot);
|
||
|
||
NR_DL_FRAME_PARMS *frame_parms = &UE->frame_parms;
|
||
|
||
int N_PRB_oh = 0; // higher layer (RRC) parameter xOverhead in PUSCH-ServingCellConfig
|
||
|
||
// b is the block of bits transmitted on the physical channel after payload coding
|
||
uint64_t b_ack[16] = {0}; // limit to 1024-bit encoded length
|
||
|
||
if (pusch_pdu->pusch_uci.harq_ack_bit_length != 0) {
|
||
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,
|
||
rm_info.E_uci_ACK,
|
||
mod_order,
|
||
&b_ack[0]);
|
||
|
||
LOG_D(PHY,
|
||
"[UCI_ON_PUSCH] G_ulsch=%u (updated G[pusch_id]), G_ack=%u (M_bit), total_len=%u "
|
||
"(G_initial_total_pusch_bits).\n",
|
||
G[pusch_id],
|
||
rm_info.E_uci_ACK,
|
||
G_initial_total_pusch_bits);
|
||
}
|
||
|
||
uint64_t b_csi1[16] = {0}; // limit to 1024-bit encoded length
|
||
uint64_t b_csi2[16] = {0}; // limit to 1024-bit encoded length
|
||
if (pusch_pdu->pusch_uci.csi_payload.p1_bits != 0) {
|
||
nr_uci_encoding(pusch_pdu->pusch_uci.csi_payload.part1_payload,
|
||
pusch_pdu->pusch_uci.csi_payload.p1_bits,
|
||
pucch_pdu->prb_size,
|
||
rm_info.E_uci_CSI1,
|
||
mod_order,
|
||
&b_csi1[0]);
|
||
|
||
// Process CSI Part 2 if any
|
||
if (pusch_pdu->pusch_uci.csi_payload.p2_bits > 0)
|
||
nr_uci_encoding(pusch_pdu->pusch_uci.csi_payload.part2_payload,
|
||
pusch_pdu->pusch_uci.csi_payload.p2_bits,
|
||
pucch_pdu->prb_size,
|
||
rm_info.E_uci_CSI2,
|
||
mod_order,
|
||
&b_csi2[0]);
|
||
}
|
||
|
||
if (uci_present) {
|
||
uint8_t temp_codeword[(G_initial_total_pusch_bits + 7) / 8];
|
||
start_meas_nr_ue_phy(UE, UCI_ON_PUSCH_MAPPING);
|
||
nr_data_control_mapping(ulsch_ue,
|
||
template_buffer,
|
||
G[pusch_id],
|
||
&rm_info,
|
||
temp_codeword,
|
||
G_initial_total_pusch_bits,
|
||
harq_process_ul_ue->f,
|
||
b_ack,
|
||
b_csi1,
|
||
b_csi2);
|
||
stop_meas_nr_ue_phy(UE, UCI_ON_PUSCH_MAPPING);
|
||
memcpy(harq_process_ul_ue->f, temp_codeword, (G_initial_total_pusch_bits + 7) / 8);
|
||
uci_mapping_template = template_buffer;
|
||
}
|
||
|
||
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;
|
||
|
||
if (start_sc >= frame_parms->ofdm_symbol_size)
|
||
start_sc -= frame_parms->ofdm_symbol_size;
|
||
|
||
ulsch_ue->Nid_cell = frame_parms->Nid_cell;
|
||
|
||
LOG_D(PHY,
|
||
"ulsch TX %x : start_rb %d nb_rb %d mod_order %d Nl %d Tpmi %d bwp_start %d start_sc %d start_symbol %d num_symbols %d "
|
||
"cdmgrpsnodata %d "
|
||
"num_dmrs %d dmrs_re_per_rb %d\n",
|
||
rnti,
|
||
start_rb,
|
||
nb_rb,
|
||
mod_order,
|
||
Nl,
|
||
pusch_pdu->Tpmi,
|
||
pusch_pdu->bwp_start,
|
||
start_sc,
|
||
start_symbol,
|
||
number_of_symbols,
|
||
cdm_grps_no_data,
|
||
number_dmrs_symbols,
|
||
nb_dmrs_re_per_rb);
|
||
// TbD num_of_mod_symbols is set but never used
|
||
const uint32_t N_RE_prime = NR_NB_SC_PER_RB * number_of_symbols - nb_dmrs_re_per_rb * number_dmrs_symbols - N_PRB_oh;
|
||
harq_process_ul_ue->num_of_mod_symbols = N_RE_prime * nb_rb;
|
||
|
||
/////////////////////////ULSCH scrambling/////////////////////////
|
||
|
||
uint32_t available_bits;
|
||
|
||
if (uci_present) {
|
||
// 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,
|
||
pusch_pdu->pusch_uci.harq_ack_bit_length,
|
||
rnti,
|
||
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
|
||
{
|
||
// capture scrambled Tx bits via T-Tracer
|
||
log_ul_scrambled_tx_bits(frame, slot, frame_parms, pusch_pdu,
|
||
number_dmrs_symbols,
|
||
get_dmrs_port(0, pusch_pdu->dmrs_ports),
|
||
(const uint8_t *)scrambled_output,
|
||
available_bits);
|
||
}
|
||
#endif
|
||
/////////////////////////ULSCH modulation/////////////////////////
|
||
|
||
int max_num_re = Nl * number_of_symbols * nb_rb * NR_NB_SC_PER_RB;
|
||
c16_t d_mod[max_num_re] __attribute__((aligned(16)));
|
||
|
||
nr_modulation(scrambled_output, // assume one codeword for the moment
|
||
available_bits,
|
||
mod_order,
|
||
(int16_t *)d_mod);
|
||
|
||
/////////////////////////ULSCH layer mapping/////////////////////////
|
||
|
||
const int sz = available_bits / mod_order / Nl;
|
||
c16_t ulsch_mod[Nl][sz];
|
||
|
||
nr_ue_layer_mapping(d_mod, Nl, sz, ulsch_mod);
|
||
|
||
//////////////////////// ULSCH transform precoding ////////////////////////
|
||
|
||
uint8_t u = 0, v = 0;
|
||
c16_t *dmrs_seq = NULL;
|
||
/// Transform-coded "y"-sequences (for definition see 38-211 V15.3.0 2018-09, subsection 6.3.1.4)
|
||
c16_t ulsch_mod_tp[max_num_re] __attribute__((aligned(16)));
|
||
memset(ulsch_mod_tp, 0, sizeof(ulsch_mod_tp));
|
||
|
||
if (pusch_pdu->transform_precoding == transformPrecoder_enabled) {
|
||
uint32_t nb_re_pusch = nb_rb * NR_NB_SC_PER_RB;
|
||
uint32_t y_offset = 0;
|
||
uint16_t num_dmrs_res_per_symbol = nb_rb * (NR_NB_SC_PER_RB / 2);
|
||
|
||
// Calculate index to dmrs seq array based on number of DMRS Subcarriers on this symbol
|
||
int index = get_index_for_dmrs_lowpapr_seq(num_dmrs_res_per_symbol);
|
||
u = pusch_pdu->dfts_ofdm.low_papr_group_number;
|
||
v = pusch_pdu->dfts_ofdm.low_papr_sequence_number;
|
||
dmrs_seq = dmrs_lowpaprtype1_ul_ref_sig[u][v][index];
|
||
|
||
AssertFatal(index >= 0,
|
||
"Num RBs not configured according to 3GPP 38.211 section 6.3.1.4. For PUSCH with transform precoding, num RBs "
|
||
"cannot be multiple "
|
||
"of any other primenumber other than 2,3,5\n");
|
||
AssertFatal(dmrs_seq != NULL, "DMRS low PAPR seq not found, check if DMRS sequences are generated");
|
||
|
||
LOG_D(PHY, "Transform Precoding params. u: %d, v: %d, index for dmrsseq: %d\n", u, v, index);
|
||
|
||
for (int l = start_symbol; l < start_symbol + number_of_symbols; l++) {
|
||
if ((ul_dmrs_symb_pos >> l) & 0x01)
|
||
/* In the symbol with DMRS no data would be transmitted CDM groups is 2*/
|
||
continue;
|
||
|
||
nr_dft(&ulsch_mod_tp[y_offset], &ulsch_mod[0][y_offset], nb_re_pusch);
|
||
|
||
y_offset = y_offset + nb_re_pusch;
|
||
|
||
LOG_D(PHY, "Transform precoding being done on data- symbol: %d, nb_re_pusch: %d, y_offset: %d\n", l, nb_re_pusch, y_offset);
|
||
|
||
#ifdef DEBUG_PUSCH_MAPPING
|
||
printf("NR_ULSCH_UE: y_offset %u\t nb_re_pusch %u \t Symbol %d \t nb_rb %d \n", y_offset, nb_re_pusch, l, nb_rb);
|
||
#endif
|
||
}
|
||
|
||
#ifdef DEBUG_DFT_IDFT
|
||
int32_t debug_symbols[MAX_NUM_NR_RE] __attribute__((aligned(16)));
|
||
int offset = 0;
|
||
printf("NR_ULSCH_UE: available_bits: %u, mod_order: %d", available_bits, mod_order);
|
||
|
||
for (int ll = 0; ll < (available_bits / mod_order); ll++) {
|
||
debug_symbols[ll] = ulsch_ue->ulsch_mod_tp[ll];
|
||
}
|
||
|
||
printf("NR_ULSCH_UE: numSym: %d, num_dmrs_sym: %d", number_of_symbols, number_dmrs_symbols);
|
||
for (int ll = 0; ll < (number_of_symbols - number_dmrs_symbols); ll++) {
|
||
nr_idft(&debug_symbols[offset], nb_re_pusch);
|
||
offset = offset + nb_re_pusch;
|
||
}
|
||
LOG_M("preDFT_all_symbols.m", "UE_preDFT", ulsch_mod[0], number_of_symbols * nb_re_pusch, 1, 1);
|
||
LOG_M("postDFT_all_symbols.m", "UE_postDFT", ulsch_mod_tp, number_of_symbols * nb_re_pusch, 1, 1);
|
||
LOG_M("DEBUG_IDFT_SYMBOLS.m", "UE_Debug_IDFT", debug_symbols, number_of_symbols * nb_re_pusch, 1, 1);
|
||
LOG_M("UE_DMRS_SEQ.m", "UE_DMRS_SEQ", dmrs_seq, nb_re_pusch, 1, 1);
|
||
#endif
|
||
}
|
||
|
||
/////////////////////////ULSCH RE mapping/////////////////////////
|
||
|
||
const int slot_sz = frame_parms->ofdm_symbol_size * frame_parms->symbols_per_slot;
|
||
c16_t tx_precoding[Nl][slot_sz];
|
||
memset(tx_precoding, 0, sizeof(tx_precoding));
|
||
|
||
for (int nl = 0; nl < Nl; nl++) {
|
||
#ifdef DEBUG_PUSCH_MAPPING
|
||
printf("NR_ULSCH_UE: Value of CELL ID %d /t, u %d \n", frame_parms->Nid_cell, u);
|
||
#endif
|
||
|
||
const uint8_t dmrs_port = get_dmrs_port(nl, pusch_pdu->dmrs_ports);
|
||
const uint8_t delta = get_delta(dmrs_port, dmrs_type);
|
||
int Wt[2];
|
||
int Wf[2];
|
||
get_Wt(Wt, dmrs_port, dmrs_type);
|
||
get_Wf(Wf, dmrs_port, dmrs_type);
|
||
|
||
c16_t *data = (pusch_pdu->transform_precoding == transformPrecoder_enabled) ? ulsch_mod_tp : ulsch_mod[nl];
|
||
|
||
nr_phy_pxsch_params_t params = {.rnti = rnti,
|
||
.K_ptrs = K_ptrs,
|
||
.k_RE_ref = k_RE_ref,
|
||
.first_sc_offset = frame_parms->first_carrier_offset,
|
||
.fft_size = frame_parms->ofdm_symbol_size,
|
||
.num_rb_max = frame_parms->N_RB_UL,
|
||
.symbols_per_slot = frame_parms->symbols_per_slot,
|
||
.dmrs_scrambling_id = pusch_pdu->ul_dmrs_scrambling_id,
|
||
.scid = pusch_pdu->scid,
|
||
.dmrs_port = dmrs_port,
|
||
.Wt = Wt,
|
||
.Wf = Wf,
|
||
.dmrs_symb_pos = ul_dmrs_symb_pos,
|
||
.ptrs_symb_pos = ulsch_ue->ptrs_symbols,
|
||
.pdu_bit_map = pusch_pdu->pdu_bit_map,
|
||
.transform_precoding = pusch_pdu->transform_precoding,
|
||
.bwp_start = pusch_pdu->bwp_start,
|
||
.start_rb = start_rb,
|
||
.nb_rb = nb_rb,
|
||
.start_symbol = start_symbol,
|
||
.num_symbols = number_of_symbols,
|
||
.dmrs_type = dmrs_type,
|
||
.delta = delta,
|
||
.num_cdm_no_data = cdm_grps_no_data};
|
||
|
||
map_symbols(params, slot, dmrs_seq, data, tx_precoding[nl]);
|
||
|
||
} // for (nl=0; nl < Nl; nl++)
|
||
|
||
/////////////////////////ULSCH precoding/////////////////////////
|
||
|
||
/// Layer Precoding and Antenna port mapping
|
||
// ulsch_mod 0-3 are mapped on antenna ports
|
||
// The precoding info is supported by nfapi such as num_prgs, prg_size, prgs_list and pm_idx
|
||
// The same precoding matrix is applied on prg_size RBs, Thus
|
||
// pmi = prgs_list[rbidx/prg_size].pm_idx, rbidx =0,...,rbSize-1
|
||
|
||
// The Precoding matrix:
|
||
for (int ap = 0; ap < frame_parms->nb_antennas_tx; ap++) {
|
||
for (int l = start_symbol; l < start_symbol + number_of_symbols; l++) {
|
||
uint16_t k = start_sc;
|
||
|
||
for (int rb = 0; rb < nb_rb; rb++) {
|
||
// get pmi info
|
||
uint8_t pmi = pusch_pdu->Tpmi;
|
||
|
||
if (pmi == 0) { // unitary Precoding
|
||
if (k + NR_NB_SC_PER_RB <= frame_parms->ofdm_symbol_size) { // RB does not cross DC
|
||
if (ap < pusch_pdu->nrOfLayers)
|
||
memcpy(&txdataF[ap][l * frame_parms->ofdm_symbol_size + k],
|
||
&tx_precoding[ap][l * frame_parms->ofdm_symbol_size + k],
|
||
NR_NB_SC_PER_RB * sizeof(c16_t));
|
||
else
|
||
memset(&txdataF[ap][l * frame_parms->ofdm_symbol_size + k], 0, NR_NB_SC_PER_RB * sizeof(int32_t));
|
||
} else { // RB does cross DC
|
||
int neg_length = frame_parms->ofdm_symbol_size - k;
|
||
int pos_length = NR_NB_SC_PER_RB - neg_length;
|
||
if (ap < pusch_pdu->nrOfLayers) {
|
||
memcpy(&txdataF[ap][l * frame_parms->ofdm_symbol_size + k],
|
||
&tx_precoding[ap][l * frame_parms->ofdm_symbol_size + k],
|
||
neg_length * sizeof(c16_t));
|
||
memcpy(&txdataF[ap][l * frame_parms->ofdm_symbol_size],
|
||
&tx_precoding[ap][l * frame_parms->ofdm_symbol_size],
|
||
pos_length * sizeof(int32_t));
|
||
} else {
|
||
memset(&txdataF[ap][l * frame_parms->ofdm_symbol_size + k], 0, neg_length * sizeof(int32_t));
|
||
memset(&txdataF[ap][l * frame_parms->ofdm_symbol_size], 0, pos_length * sizeof(int32_t));
|
||
}
|
||
}
|
||
k += NR_NB_SC_PER_RB;
|
||
if (k >= frame_parms->ofdm_symbol_size) {
|
||
k -= frame_parms->ofdm_symbol_size;
|
||
}
|
||
} else {
|
||
// get the precoding matrix weights:
|
||
const char *W_prec;
|
||
switch (frame_parms->nb_antennas_tx) {
|
||
case 1: // 1 antenna port
|
||
W_prec = nr_W_1l_2p[pmi][ap];
|
||
break;
|
||
case 2: // 2 antenna ports
|
||
if (pusch_pdu->nrOfLayers == 1) // 1 layer
|
||
W_prec = nr_W_1l_2p[pmi][ap];
|
||
else // 2 layers
|
||
W_prec = nr_W_2l_2p[pmi][ap];
|
||
break;
|
||
case 4: // 4 antenna ports
|
||
if (pusch_pdu->nrOfLayers == 1) // 1 layer
|
||
W_prec = nr_W_1l_4p[pmi][ap];
|
||
else if (pusch_pdu->nrOfLayers == 2) // 2 layers
|
||
W_prec = nr_W_2l_4p[pmi][ap];
|
||
else if (pusch_pdu->nrOfLayers == 3) // 3 layers
|
||
W_prec = nr_W_3l_4p[pmi][ap];
|
||
else // 4 layers
|
||
W_prec = nr_W_4l_4p[pmi][ap];
|
||
break;
|
||
default:
|
||
LOG_D(PHY, "Precoding 1,2, or 4 antenna ports are currently supported\n");
|
||
W_prec = nr_W_1l_2p[pmi][ap];
|
||
break;
|
||
}
|
||
|
||
for (int i = 0; i < NR_NB_SC_PER_RB; i++) {
|
||
int32_t re_offset = l * frame_parms->ofdm_symbol_size + k;
|
||
txdataF[ap][re_offset] = nr_layer_precoder(slot_sz, tx_precoding, W_prec, pusch_pdu->nrOfLayers, re_offset);
|
||
if (++k >= frame_parms->ofdm_symbol_size) {
|
||
k -= frame_parms->ofdm_symbol_size;
|
||
}
|
||
}
|
||
}
|
||
} // RB loop
|
||
} // symbol loop
|
||
} // port loop
|
||
|
||
stop_meas_nr_ue_phy(UE, PUSCH_PROC_STATS);
|
||
}
|
||
|
||
void nr_tx_rotation_and_ofdm_mod(const uint8_t slot,
|
||
const NR_DL_FRAME_PARMS *frame_parms,
|
||
const uint8_t n_antenna_ports,
|
||
c16_t **txdataF,
|
||
c16_t **txdata,
|
||
uint32_t linktype,
|
||
bool was_symbol_used[NR_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;
|
||
|
||
if (!no_phase_pre_comp) {
|
||
for (int i = 0; i < frame_parms->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],
|
||
false,
|
||
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 < frame_parms->symbols_per_slot; i++) {
|
||
if (was_symbol_used[i] == false) {
|
||
memset(&txdata[ap][(frame_parms->ofdm_symbol_size + frame_parms->nb_prefix_samples) * i],
|
||
0,
|
||
(frame_parms->nb_prefix_samples + frame_parms->ofdm_symbol_size) * sizeof(int32_t));
|
||
continue;
|
||
}
|
||
PHY_ofdm_mod((int *)&txdataF[ap][frame_parms->ofdm_symbol_size * i],
|
||
(int *)&txdata[ap][frame_parms->ofdm_symbol_size * i],
|
||
frame_parms->ofdm_symbol_size,
|
||
1,
|
||
frame_parms->nb_prefix_samples,
|
||
CYCLIC_PREFIX);
|
||
}
|
||
} else { // normal cyclic prefix
|
||
nr_normal_prefix_mod(txdataF[ap], txdata[ap], frame_parms->symbols_per_slot, frame_parms, slot, was_symbol_used);
|
||
}
|
||
}
|
||
}
|