NR UE: fix PDCCH LLR indexing for different search space RB sizes After !3603 (merged), when multiple search spaces have different CORESET configurations (different number of RBs), the LLR buffer stride must be calculated based on the maximum RB size across all search spaces, not the current search space's RB size. The LLR buffer is allocated with size based on get_pdcch_max_rbs(), but the demapping function was using coreset_nbr_rb (current search space) for symbol stride calculation, causing incorrect LLR extraction when search spaces have different RB configurations. This MR tries to fix it. Closes #1021
666 lines
29 KiB
C
666 lines
29 KiB
C
/*
|
|
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership.
|
|
* The OpenAirInterface Software Alliance licenses this file to You under
|
|
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.openairinterface.org/?page_id=698
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*-------------------------------------------------------------------------------
|
|
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
|
* contact@openairinterface.org
|
|
*/
|
|
|
|
/*! \file dci_nr.c
|
|
* \brief Implements PDCCH physical channel TX/RX procedures (36.211) and DCI encoding/decoding (36.212/36.213). Current LTE
|
|
* compliance V8.6 2009-03. \author R. Knopp, A. Mico Pereperez \date 2018 \version 0.1 \company Eurecom \email: knopp@eurecom.fr
|
|
* \note
|
|
* \warning
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
|
|
#include "executables/softmodem-common.h"
|
|
#include "nr_transport_proto_ue.h"
|
|
#include "PHY/CODING/nrPolar_tools/nr_polar_dci_defs.h"
|
|
#include "PHY/phy_extern.h"
|
|
#include "PHY/CODING/coding_extern.h"
|
|
#include "PHY/nr_phy_common/inc/nr_phy_common.h"
|
|
#include "PHY/sse_intrin.h"
|
|
#include "common/utils/nr/nr_common.h"
|
|
#include <openair1/PHY/TOOLS/phy_scope_interface.h>
|
|
#include "openair1/PHY/NR_REFSIG/refsig_defs_ue.h"
|
|
#include "PHY/NR_UE_ESTIMATION/nr_estimation.h"
|
|
|
|
#include "assertions.h"
|
|
#include "T.h"
|
|
|
|
// #define NR_PDCCH_DCI_DEBUG // activates NR_PDCCH_DCI_DEBUG logs
|
|
#ifdef NR_PDCCH_DCI_DEBUG
|
|
#define LOG_DDD(a, ...) printf("<-NR_PDCCH_DCI_DEBUG (%s)-> " a, __func__, ##__VA_ARGS__ )
|
|
#define LOG_DSYMB(b) \
|
|
LOG_DDD("RB[c_rb %d] \t RE[re %d] => rxF_ext[%d]=(%d,%d)\t rxF[%d]=(%d,%d)\n" b, \
|
|
c_rb, \
|
|
i, \
|
|
j, \
|
|
rxF_ext[j].r, \
|
|
rxF_ext[j].i, \
|
|
i, \
|
|
rxF[i].r, \
|
|
rxF[i].i)
|
|
#else
|
|
#define LOG_DDD(a...)
|
|
#define LOG_DSYMB(a...)
|
|
#endif
|
|
|
|
#define NR_NBR_CORESET_ACT_BWP 3 // The number of CoreSets per BWP is limited to 3 (including initial CORESET: ControlResourceId 0)
|
|
#define NR_NBR_SEARCHSPACE_ACT_BWP \
|
|
10 // The number of SearSpaces per BWP is limited to 10 (including initial SEARCHSPACE: SearchSpaceId 0)
|
|
|
|
#define RE_PER_RB 12
|
|
// after removing the 3 DMRS RE, the RB contains 9 RE with PDCCH
|
|
#define RE_PER_RB_OUT_DMRS 9
|
|
|
|
static void nr_pdcch_demapping_deinterleaving(uint32_t coreset_nbr_rb,
|
|
c16_t *llr,
|
|
c16_t *e_rx,
|
|
uint8_t coreset_time_dur,
|
|
uint8_t reg_bundle_size_L_in,
|
|
uint8_t coreset_interleaver_size_R,
|
|
uint8_t n_shift,
|
|
uint8_t number_of_candidates,
|
|
uint16_t *CCE,
|
|
uint8_t *L,
|
|
int llr_stride_per_symbol)
|
|
{
|
|
/*
|
|
* This function will do demapping and deinterleaving from llr containing demodulated symbols
|
|
* Demapping will regroup in REG and bundles
|
|
* Deinterleaving will order the bundles
|
|
*
|
|
* In the following example we can see the process. The llr contains the demodulated IQs, but they are not ordered from
|
|
REG 0,1,2,..
|
|
* In e_rx (z) we will order the REG ids and group them into bundles.
|
|
* Then we will put the bundles in the correct order as indicated in subclause 7.3.2.2
|
|
*
|
|
llr --------------------------> e_rx (z) ----> e_rx (z)
|
|
| ...
|
|
| ...
|
|
| REG 26
|
|
symbol 2 | ...
|
|
| ...
|
|
| REG 5
|
|
| REG 2
|
|
|
|
| ...
|
|
| ...
|
|
| REG 25
|
|
symbol 1 | ...
|
|
| ...
|
|
| REG 4
|
|
| REG 1
|
|
|
|
| ...
|
|
| ... ... ...
|
|
| REG 24 (bundle 7) ... ...
|
|
symbol 0 | ... bundle 3 bundle 6
|
|
| ... bundle 2 bundle 1
|
|
| REG 3 bundle 1 bundle 7
|
|
| REG 0 (bundle 0) bundle 0 bundle 0
|
|
|
|
*/
|
|
const int N_regs = coreset_nbr_rb * coreset_time_dur;
|
|
/* interleaving will be done only if reg_bundle_size_L != 0 */
|
|
const int coreset_C = (reg_bundle_size_L_in != 0) ? (uint32_t)(N_regs / (coreset_interleaver_size_R * reg_bundle_size_L_in)) : 0;
|
|
const int coreset_interleaved = (reg_bundle_size_L_in != 0) ? 1 : 0;
|
|
const int reg_bundle_size_L = (reg_bundle_size_L_in != 0) ? reg_bundle_size_L_in : 6;
|
|
|
|
int B_rb = reg_bundle_size_L / coreset_time_dur; // nb of RBs occupied by each REG bundle
|
|
int num_bundles_per_cce = 6 / reg_bundle_size_L;
|
|
int n_cce = N_regs / 6;
|
|
int max_bundles = n_cce * num_bundles_per_cce;
|
|
int f_bundle_j_list[max_bundles];
|
|
// for each bundle
|
|
int c = 0, r = 0, f_bundle_j = 0;
|
|
for (int nb = 0; nb < max_bundles; nb++) {
|
|
if (coreset_interleaved == 0)
|
|
f_bundle_j = nb;
|
|
else {
|
|
if (r == coreset_interleaver_size_R) {
|
|
r = 0;
|
|
c++;
|
|
}
|
|
f_bundle_j = ((r * coreset_C) + c + n_shift) % (N_regs / reg_bundle_size_L);
|
|
r++;
|
|
}
|
|
f_bundle_j_list[nb] = f_bundle_j;
|
|
}
|
|
|
|
// Get cce_list indices by bundle index in ascending order
|
|
int f_bundle_j_list_ord[number_of_candidates][max_bundles];
|
|
for (int c_id = 0; c_id < number_of_candidates; c_id++) {
|
|
int start_bund_cand = CCE[c_id] * num_bundles_per_cce;
|
|
int max_bund_per_cand = L[c_id] * num_bundles_per_cce;
|
|
int f_bundle_j_list_id = 0;
|
|
for (int nb = 0; nb < max_bundles; nb++) {
|
|
for (int bund_cand = start_bund_cand; bund_cand < start_bund_cand + max_bund_per_cand; bund_cand++) {
|
|
if (f_bundle_j_list[bund_cand] == nb) {
|
|
f_bundle_j_list_ord[c_id][f_bundle_j_list_id] = nb;
|
|
f_bundle_j_list_id++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int rb_count = 0;
|
|
for (int c_id = 0; c_id < number_of_candidates; c_id++) {
|
|
for (int symbol_idx = 0; symbol_idx < coreset_time_dur; symbol_idx++) {
|
|
for (int cce_count = 0; cce_count < L[c_id]; cce_count++) {
|
|
for (int k = 0; k < NR_NB_REG_PER_CCE / reg_bundle_size_L; k++) { // loop over REG bundles
|
|
int f = f_bundle_j_list_ord[c_id][k + NR_NB_REG_PER_CCE * cce_count / reg_bundle_size_L];
|
|
c16_t *in = llr + f * B_rb * RE_PER_RB_OUT_DMRS + symbol_idx * llr_stride_per_symbol;
|
|
// loop over the RBs of the bundle
|
|
memcpy(e_rx + RE_PER_RB_OUT_DMRS * rb_count, in, B_rb * RE_PER_RB_OUT_DMRS * sizeof(*e_rx));
|
|
rb_count += B_rb;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void nr_pdcch_llr(uint32_t sz, c16_t *rxF, c16_t *llr)
|
|
{
|
|
for (int i = 0; i < sz; i++) {
|
|
// We clip the signal
|
|
c16_t res;
|
|
res.r = min(rxF->r, 31);
|
|
res.r = max(-32, res.r);
|
|
res.i = min(rxF->i, 31);
|
|
res.i = max(-32, res.i);
|
|
*llr++ = res;
|
|
LOG_DDD("llr logs: rb=%d i=%d rxF:%d,%d => pdcch_llr:%d,%d\n", i / 18, i, rxF->r, rxF->i, llr->r, llr->i);
|
|
rxF++;
|
|
}
|
|
}
|
|
|
|
// This function will extract the mapped DM-RS PDCCH REs as per 38.211 Section 7.4.1.3.2 (Mapping to physical resources)
|
|
static void nr_pdcch_extract_rbs_single(uint32_t rxdataF_sz,
|
|
c16_t rxdataF[][rxdataF_sz],
|
|
int32_t est_size,
|
|
c16_t dl_ch_estimates[][est_size],
|
|
int arraySz,
|
|
c16_t rxdataF_ext[][arraySz],
|
|
c16_t dl_ch_estimates_ext[][arraySz],
|
|
NR_DL_FRAME_PARMS *frame_parms,
|
|
uint8_t *coreset_freq_dom,
|
|
uint32_t coreset_nbr_rb,
|
|
uint32_t n_BWP_start)
|
|
{
|
|
/*
|
|
* This function is demapping DM-RS PDCCH RE
|
|
* Implementing 38.211 Section 7.4.1.3.2 Mapping to physical resources
|
|
* PDCCH DM-RS signals are mapped on RE a_k_l where:
|
|
* k = 12*n + 4*kprime + 1
|
|
* n=0,1,..
|
|
* kprime=0,1,2
|
|
* According to this equations, DM-RS PDCCH are mapped on k where k%12==1 || k%12==5 || k%12==9
|
|
*
|
|
*/
|
|
|
|
for (int aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) {
|
|
c16_t *dl_ch0 = dl_ch_estimates[aarx];
|
|
c16_t *rxFbase = rxdataF[aarx];
|
|
LOG_DDD("dl_ch0 = &dl_ch_estimates[aarx = (%d)][0]\n", aarx);
|
|
|
|
c16_t *dl_ch0_ext = dl_ch_estimates_ext[aarx];
|
|
c16_t *rxF_ext = rxdataF_ext[aarx];
|
|
|
|
/*
|
|
* The following for loop handles treatment of PDCCH contained in table rxdataF (in frequency domain)
|
|
* In NR the PDCCH IQ symbols are contained within RBs in the CORESET defined by higher layers which is located within the BWP
|
|
* Lets consider that the first RB to be considered as part of the CORESET and part of the PDCCH is n_BWP_start
|
|
* Several cases have to be handled differently as IQ symbols are situated in different parts of rxdataF:
|
|
* 1. Number of RBs in the system bandwidth is even
|
|
* 1.1 The RB is < than the N_RB_DL/2 -> IQ symbols are in the second half of the rxdataF (from first_carrier_offset)
|
|
* 1.2 The RB is >= than the N_RB_DL/2 -> IQ symbols are in the first half of the rxdataF (from element 0)
|
|
* 2. Number of RBs in the system bandwidth is odd
|
|
* (particular case when the RB with DC as it is treated differently: it is situated in symbol borders of rxdataF)
|
|
* 2.1 The RB is < than the N_RB_DL/2 -> IQ symbols are in the second half of the rxdataF (from first_carrier_offset)
|
|
* 2.2 The RB is > than the N_RB_DL/2 -> IQ symbols are in the first half of the rxdataF (from element 0 + 2nd half RB
|
|
* containing DC) 2.3 The RB is == N_RB_DL/2 -> IQ symbols are in the upper border of the rxdataF for first 6 IQ
|
|
* element and the lower border of the rxdataF for the last 6 IQ elements If the first RB containing PDCCH within the UE BWP
|
|
* and within the CORESET is higher than half of the system bandwidth (N_RB_DL), then the IQ symbol is going to be found at
|
|
* the position 0+c_rb-N_RB_DL/2 in rxdataF and we have to point the pointer at (1+c_rb-N_RB_DL/2) in rxdataF
|
|
*/
|
|
|
|
c16_t middle_prb_buffer[RE_PER_RB];
|
|
for (int rb_group = 0; rb_group < coreset_nbr_rb / 6; rb_group++) {
|
|
if ((coreset_freq_dom[rb_group / 8] & (1 << (7 - (rb_group & 7)))) == 0) {
|
|
continue;
|
|
}
|
|
for (int rb = 0; rb < 6; rb++) {
|
|
int c_rb = rb_group * 6 + rb;
|
|
c16_t *rxF = NULL;
|
|
if ((frame_parms->N_RB_DL & 1) == 0) {
|
|
if ((c_rb + n_BWP_start) < frame_parms->N_RB_DL / 2)
|
|
// if RB to be treated is lower than middle system bandwidth then rxdataF pointed
|
|
// at (offset + c_br + symbol * ofdm_symbol_size): even case
|
|
rxF = rxFbase + frame_parms->first_carrier_offset + RE_PER_RB * (c_rb + n_BWP_start);
|
|
else
|
|
// number of RBs is even and c_rb is higher than half system bandwidth (we don't skip DC)
|
|
// if these conditions are true the pointer has to be situated at the 1st part of the rxdataF
|
|
// we point at the 1st part of the rxdataF in symbol
|
|
rxF = rxFbase + RE_PER_RB * (c_rb + n_BWP_start - frame_parms->N_RB_DL / 2);
|
|
} else {
|
|
if ((c_rb + n_BWP_start) <= frame_parms->N_RB_DL / 2)
|
|
// if RB to be treated is lower than middle system bandwidth then rxdataF pointed
|
|
// at (offset + c_br + symbol * ofdm_symbol_size): odd case
|
|
// Reassemble the middle PRB
|
|
if (c_rb + n_BWP_start == frame_parms->N_RB_DL / 2) {
|
|
memcpy(middle_prb_buffer, rxFbase + frame_parms->ofdm_symbol_size - RE_PER_RB / 2, sizeof(c16_t) * RE_PER_RB / 2);
|
|
memcpy(middle_prb_buffer + RE_PER_RB / 2, rxFbase, sizeof(c16_t) * RE_PER_RB / 2);
|
|
rxF = middle_prb_buffer;
|
|
} else {
|
|
rxF = rxFbase + frame_parms->first_carrier_offset + RE_PER_RB * (c_rb + n_BWP_start);
|
|
}
|
|
|
|
else
|
|
// number of RBs is odd and c_rb is higher than half system bandwidth + 1
|
|
// if these conditions are true the pointer has to be situated at the 1st part of
|
|
// the rxdataF just after the first IQ symbols of the RB containing DC
|
|
// we point at the 1st part of the rxdataF in symbol
|
|
rxF = rxFbase + RE_PER_RB * (c_rb + n_BWP_start - frame_parms->N_RB_DL / 2) - 6;
|
|
}
|
|
|
|
const int valid_re[RE_PER_RB_OUT_DMRS] = {0, 2, 3, 4, 6, 7, 8, 10, 11};
|
|
for (int i = 0; i < sizeofArray(valid_re); i++) {
|
|
*rxF_ext++ = rxF[valid_re[i]];
|
|
*dl_ch0_ext++ = dl_ch0[valid_re[i]];
|
|
}
|
|
dl_ch0 += RE_PER_RB;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void nr_pdcch_channel_compensation(int arraySz,
|
|
int sz2,
|
|
c16_t rxdataF_ext[][sz2],
|
|
c16_t dl_ch_estimates_ext[][sz2],
|
|
c16_t rxdataF_comp[][arraySz],
|
|
int antRx,
|
|
uint8_t output_shift)
|
|
{
|
|
for (int aarx = 0; aarx < antRx; aarx++) {
|
|
// multiply by conjugated channel, this function require size in _m128i, else it doesn't process all samples
|
|
mult_cpx_conj_vector(dl_ch_estimates_ext[aarx], rxdataF_ext[aarx], rxdataF_comp[aarx], arraySz, output_shift);
|
|
}
|
|
}
|
|
|
|
static void nr_pdcch_detection_mrc(int nb_ant, int sz, c16_t rxdataF_comp[][sz])
|
|
{
|
|
c16_t *rx0 = rxdataF_comp[0];
|
|
// MRC on each re of rb
|
|
// input always aligned and accepting tail padding to process all actual samples
|
|
for (int a = 1; a < nb_ant; a++) {
|
|
c16_t *rx = rxdataF_comp[a];
|
|
for (int i = 0; i < sz; i += 4) {
|
|
*(simde__m128i *)(rx0 + i) = simde_mm_adds_epi16(simde_mm_srai_epi16(*(simde__m128i *)(rx0 + i), 1),
|
|
simde_mm_srai_epi16(*(simde__m128i *)(rx + i), 1));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Produce LLRs from received PDCCH signal */
|
|
static void nr_rx_pdcch_symbol(PHY_VARS_NR_UE *ue,
|
|
const UE_nr_rxtx_proc_t *proc,
|
|
int symbol,
|
|
int rel_symb_monOcc,
|
|
int ss_idx,
|
|
nr_phy_data_t *phy_data,
|
|
int llr_size_symbol,
|
|
c16_t rxdataF[ue->frame_parms.nb_antennas_rx][ue->frame_parms.ofdm_symbol_size],
|
|
c16_t llr[llr_size_symbol])
|
|
{
|
|
NR_DL_FRAME_PARMS *fp = &ue->frame_parms;
|
|
NR_UE_PDCCH_CONFIG *phy_pdcch_config = &phy_data->phy_pdcch_config;
|
|
fapi_nr_coreset_t *coreset = &phy_pdcch_config->pdcch_config[ss_idx].coreset;
|
|
int32_t pdcch_est_size = ceil_mod(fp->ofdm_symbol_size + LTE_CE_FILTER_LENGTH, 16);
|
|
__attribute__((aligned(16))) c16_t pdcch_dl_ch_estimates[fp->nb_antennas_rx][pdcch_est_size];
|
|
int n_rb;
|
|
int rb_offset;
|
|
get_coreset_rballoc(coreset->frequency_domain_resource, &n_rb, &rb_offset);
|
|
|
|
unsigned short scrambling_id = coreset->pdcch_dmrs_scrambling_id;
|
|
int dmrs_ref = 0;
|
|
if (coreset->CoreSetType == NFAPI_NR_CSET_CONFIG_PDCCH_CONFIG)
|
|
dmrs_ref = phy_pdcch_config->pdcch_config[ss_idx].BWPStart;
|
|
// generate pilot
|
|
c16_t pilot[(n_rb + dmrs_ref) * 3] __attribute__((aligned(16)));
|
|
// Note: pilot returned by the following function is already the complex conjugate of the transmitted DMRS
|
|
const uint32_t *gold =
|
|
nr_gold_pdcch(ue->frame_parms.N_RB_DL, ue->frame_parms.symbols_per_slot, scrambling_id, proc->nr_slot_rx, symbol);
|
|
nr_pdcch_dmrs_ref(gold, pilot, n_rb + dmrs_ref);
|
|
nr_pdcch_channel_estimation(ue,
|
|
n_rb,
|
|
rb_offset,
|
|
dmrs_ref,
|
|
fp->first_carrier_offset,
|
|
phy_pdcch_config->pdcch_config[ss_idx].BWPStart,
|
|
pdcch_est_size,
|
|
pdcch_dl_ch_estimates,
|
|
rxdataF,
|
|
pilot);
|
|
|
|
const int32_t rx_size = ceil_mod(fp->N_RB_DL * 12, 32);
|
|
__attribute__((aligned(32))) c16_t rxdataF_ext[fp->nb_antennas_rx][rx_size];
|
|
__attribute__((aligned(32))) c16_t pdcch_dl_ch_estimates_ext[fp->nb_antennas_rx][rx_size];
|
|
|
|
nr_pdcch_extract_rbs_single(ue->frame_parms.ofdm_symbol_size,
|
|
rxdataF,
|
|
pdcch_est_size,
|
|
pdcch_dl_ch_estimates,
|
|
rx_size,
|
|
rxdataF_ext,
|
|
pdcch_dl_ch_estimates_ext,
|
|
fp,
|
|
coreset->frequency_domain_resource,
|
|
n_rb,
|
|
phy_pdcch_config->pdcch_config[ss_idx].BWPStart);
|
|
|
|
LOG_D(NR_PHY_DCI, "in channel level function (dl_ch_estimates_ext -> dl_ch_estimates_ext)\n");
|
|
int avg[fp->nb_antennas_rx];
|
|
nr_channel_level(0, rx_size, pdcch_dl_ch_estimates_ext, fp->nb_antennas_rx, 1, avg, n_rb * RE_PER_RB_OUT_DMRS);
|
|
int avgs = avg[0];
|
|
for (int i = 1; i < fp->nb_antennas_rx; i++)
|
|
avgs = cmax(avgs, avg[i]);
|
|
const int log2_maxh = (log2_approx(avgs) / 2) + 5; //+frame_parms->nb_antennas_rx;
|
|
int rx_comp_sz = ceil_mod(llr_size_symbol, 4);
|
|
__attribute__((aligned(32))) c16_t rxdataF_comp[fp->nb_antennas_rx][rx_comp_sz];
|
|
memset(rxdataF_comp, 0, sizeof(rxdataF_comp));
|
|
nr_pdcch_channel_compensation(rx_comp_sz,
|
|
rx_size,
|
|
rxdataF_ext,
|
|
pdcch_dl_ch_estimates_ext,
|
|
rxdataF_comp,
|
|
fp->nb_antennas_rx,
|
|
log2_maxh); // log2_maxh+I0_shift
|
|
|
|
if (fp->nb_antennas_rx > 1) {
|
|
nr_pdcch_detection_mrc(fp->nb_antennas_rx, rx_comp_sz, rxdataF_comp);
|
|
}
|
|
UEscopeCopy(ue, pdcchRxdataF_comp, rxdataF_comp[0], sizeof(c16_t), 1, llr_size_symbol, 0);
|
|
nr_pdcch_llr(llr_size_symbol, rxdataF_comp[0], llr);
|
|
}
|
|
|
|
bool is_start_symbol_in_ss(const fapi_nr_dl_config_dci_dl_pdu_rel15_t *ss, const int symbol)
|
|
{
|
|
return ((ss->coreset.StartSymbolBitmap >> (NR_SYMBOLS_PER_SLOT - 1 - symbol)) & 1);
|
|
}
|
|
|
|
int get_pdcch_mon_occasions_slot(const fapi_nr_dl_config_dci_dl_pdu_rel15_t *ss, uint8_t start_symb[NR_SYMBOLS_PER_SLOT])
|
|
{
|
|
int sum = 0;
|
|
for (int s = 0; s < NR_SYMBOLS_PER_SLOT; s++) {
|
|
if (is_start_symbol_in_ss(ss, s)) {
|
|
if (start_symb != NULL)
|
|
start_symb[sum] = s;
|
|
sum++;
|
|
}
|
|
}
|
|
|
|
return sum;
|
|
}
|
|
|
|
int get_max_pdcch_monOcc(const NR_UE_PDCCH_CONFIG *phy_pdcch_config)
|
|
{
|
|
int monOcc = 0;
|
|
for (int ss = 0; ss < phy_pdcch_config->nb_search_space; ss++) {
|
|
monOcc = max(monOcc, get_pdcch_mon_occasions_slot(&phy_pdcch_config->pdcch_config[ss], NULL));
|
|
}
|
|
return monOcc;
|
|
}
|
|
|
|
static void nr_dci_decoding_procedure(PHY_VARS_NR_UE *ue,
|
|
const UE_nr_rxtx_proc_t *proc,
|
|
c16_t *pdcch_e_rx,
|
|
fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15,
|
|
fapi_nr_dci_indication_t *dci_ind);
|
|
|
|
void set_first_last_pdcch_symb(const NR_UE_PDCCH_CONFIG *phy_pdcch_config, int *first_symb, int *last_symb)
|
|
{
|
|
*first_symb = NR_SYMBOLS_PER_SLOT; // max first pdcch symbol
|
|
*last_symb = 0; // min last pdcch symbol
|
|
for (int ss = 0; ss < phy_pdcch_config->nb_search_space; ss++) {
|
|
for (int symb = 0; symb < NR_SYMBOLS_PER_SLOT; symb++) {
|
|
if (is_start_symbol_in_ss(&phy_pdcch_config->pdcch_config[ss], symb)) {
|
|
const int duration = phy_pdcch_config->pdcch_config[ss].coreset.duration;
|
|
*first_symb = min(*first_symb, symb);
|
|
*last_symb = max(*last_symb, symb + duration - 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Generates PDCCH LLRs from received symbol for each Search-Space */
|
|
void nr_pdcch_generate_llr(PHY_VARS_NR_UE *ue,
|
|
const UE_nr_rxtx_proc_t *proc,
|
|
int symbol,
|
|
nr_phy_data_t *phy_data,
|
|
int llr_size_symbol,
|
|
int num_monitoring_occ,
|
|
int max_symb,
|
|
c16_t rxdataF[ue->frame_parms.nb_antennas_rx][ue->frame_parms.ofdm_symbol_size],
|
|
c16_t pdcch_llr[phy_data->phy_pdcch_config.nb_search_space][num_monitoring_occ][max_symb * llr_size_symbol])
|
|
{
|
|
const NR_UE_PDCCH_CONFIG *phy_pdcch_config = &phy_data->phy_pdcch_config;
|
|
|
|
// Loop over search spaces
|
|
for (int ss_idx = 0; ss_idx < phy_pdcch_config->nb_search_space; ss_idx++) {
|
|
uint8_t start_symb[NR_SYMBOLS_PER_SLOT] = {0};
|
|
const int num_monOcc = get_pdcch_mon_occasions_slot(&phy_pdcch_config->pdcch_config[ss_idx], start_symb);
|
|
// Loop over monitoring occations within the slot in this ss
|
|
for (int occ = 0; occ < num_monOcc; occ++) {
|
|
const int first_symb = start_symb[occ];
|
|
const int last_symb = first_symb + phy_pdcch_config->pdcch_config[ss_idx].coreset.duration;
|
|
// Decode PDCCH and generate LLR for each ss in this OFDM symbol
|
|
if ((symbol >= first_symb) && (symbol < last_symb)) {
|
|
const int rel_symb_monOcc = symbol - first_symb;
|
|
nr_rx_pdcch_symbol(ue,
|
|
proc,
|
|
symbol,
|
|
rel_symb_monOcc,
|
|
ss_idx,
|
|
phy_data,
|
|
llr_size_symbol,
|
|
rxdataF,
|
|
&pdcch_llr[ss_idx][occ][rel_symb_monOcc * llr_size_symbol]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Decode DCI from LLRs for each Search-Space and send to MAC */
|
|
void nr_pdcch_dci_indication(const UE_nr_rxtx_proc_t *proc,
|
|
int llr_size,
|
|
int max_monOcc,
|
|
PHY_VARS_NR_UE *ue,
|
|
nr_phy_data_t *phy_data,
|
|
c16_t llr[phy_data->phy_pdcch_config.nb_search_space][max_monOcc][llr_size])
|
|
{
|
|
NR_UE_PDCCH_CONFIG *phy_pdcch_config = &phy_data->phy_pdcch_config;
|
|
|
|
nr_downlink_indication_t dl_indication;
|
|
fapi_nr_dci_indication_t dci_ind = {.SFN = proc->frame_rx, .slot = proc->nr_slot_rx};
|
|
|
|
for (int ss_idx = 0; ss_idx < phy_pdcch_config->nb_search_space; ss_idx++) {
|
|
fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15 = &phy_pdcch_config->pdcch_config[ss_idx];
|
|
uint8_t unused_start_symb[NR_SYMBOLS_PER_SLOT] = {0};
|
|
const int num_monitoring_occ = get_pdcch_mon_occasions_slot(rel15, unused_start_symb);
|
|
const int llr_stride = llr_size / rel15->coreset.duration;
|
|
|
|
int n_rb;
|
|
int rb_offset;
|
|
get_coreset_rballoc(rel15->coreset.frequency_domain_resource, &n_rb, &rb_offset);
|
|
|
|
for (int m = 0; m < num_monitoring_occ; m++) {
|
|
/// PDCCH/DCI e-sequence (input to rate matching).
|
|
c16_t pdcch_e_rx[NR_MAX_PDCCH_SIZE];
|
|
|
|
nr_pdcch_demapping_deinterleaving(n_rb,
|
|
llr[ss_idx][m],
|
|
pdcch_e_rx,
|
|
rel15->coreset.duration,
|
|
rel15->coreset.RegBundleSize,
|
|
rel15->coreset.InterleaverSize,
|
|
rel15->coreset.ShiftIndex,
|
|
rel15->number_of_candidates,
|
|
rel15->CCE,
|
|
rel15->L,
|
|
llr_stride);
|
|
|
|
nr_dci_decoding_procedure(ue, proc, pdcch_e_rx, rel15, &dci_ind);
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < dci_ind.number_of_dcis; i++) {
|
|
LOG_D(PHY,
|
|
"Frame.slot: %d.%d: DCI %i of %d total DCIs found --> rnti %x : format %d\n",
|
|
proc->frame_rx,
|
|
proc->nr_slot_rx,
|
|
i + 1,
|
|
dci_ind.number_of_dcis,
|
|
dci_ind.dci_list[i].rnti,
|
|
dci_ind.dci_list[i].dci_format);
|
|
}
|
|
|
|
/* Send to MAC */
|
|
nr_fill_dl_indication(&dl_indication, &dci_ind, NULL, proc, ue, phy_data);
|
|
ue->if_inst->dl_indication(&dl_indication);
|
|
phy_pdcch_config->nb_search_space = 0;
|
|
}
|
|
|
|
static void nr_pdcch_unscrambling(c16_t *e_rx,
|
|
uint16_t scrambling_RNTI,
|
|
uint32_t length,
|
|
uint16_t pdcch_DMRS_scrambling_id,
|
|
int16_t *z2)
|
|
{
|
|
uint32_t rnti = (uint32_t) scrambling_RNTI;
|
|
uint16_t n_id = pdcch_DMRS_scrambling_id;
|
|
uint32_t *seq = gold_cache(((rnti << 16) + n_id) % (1U << 31), length / 32); // this is c_init in 38.211 v15.1.0 Section 7.3.2.3
|
|
LOG_D(NR_PHY_DCI, "PDCCH Unscrambling: scrambling_RNTI %x\n", rnti);
|
|
int16_t *ptr = &e_rx[0].r;
|
|
for (int i = 0; i < length; i++) {
|
|
if (seq[i / 32] & (1UL << (i % 32)))
|
|
z2[i] = -ptr[i];
|
|
else
|
|
z2[i] = ptr[i];
|
|
}
|
|
}
|
|
|
|
static void nr_dci_decoding_procedure(PHY_VARS_NR_UE *ue,
|
|
const UE_nr_rxtx_proc_t *proc,
|
|
c16_t *pdcch_e_rx,
|
|
fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15,
|
|
fapi_nr_dci_indication_t *dci_ind)
|
|
{
|
|
int e_rx_cand_idx = 0;
|
|
// if DCI for SIB we don't break after finding 1st DCI with that RNTI
|
|
// there might be SIB1 and otherSIB in the same slot with the same length
|
|
bool is_SI = rel15->rnti == SI_RNTI;
|
|
|
|
for (int j = 0; j < rel15->number_of_candidates; j++) {
|
|
int CCEind = rel15->CCE[j];
|
|
int L = rel15->L[j];
|
|
|
|
// Loop over possible DCI lengths
|
|
|
|
for (int k = 0; k < rel15->num_dci_options; k++) {
|
|
// skip this candidate if we've already found one with the
|
|
// same rnti and size at a different aggregation level
|
|
int dci_length = rel15->dci_length_options[k];
|
|
int ind;
|
|
for (ind = 0; ind < dci_ind->number_of_dcis; ind++) {
|
|
if (!is_SI && rel15->rnti == dci_ind->dci_list[ind].rnti && dci_length == dci_ind->dci_list[ind].payloadSize) {
|
|
break;
|
|
}
|
|
}
|
|
if (ind < dci_ind->number_of_dcis)
|
|
continue;
|
|
|
|
uint64_t dci_estimation[2] = {0};
|
|
LOG_D(NR_PHY_DCI,
|
|
"(%i.%i) Trying DCI candidate %d of %d number of candidates, CCE %d (%d), L %d, length %d, format %d\n",
|
|
proc->frame_rx,
|
|
proc->nr_slot_rx,
|
|
j,
|
|
rel15->number_of_candidates,
|
|
CCEind,
|
|
e_rx_cand_idx,
|
|
L,
|
|
dci_length,
|
|
rel15->dci_format_options[k]);
|
|
|
|
int16_t tmp_e[16 * 108];
|
|
nr_pdcch_unscrambling(&pdcch_e_rx[e_rx_cand_idx],
|
|
rel15->coreset.scrambling_rnti,
|
|
L * 108,
|
|
rel15->coreset.pdcch_dmrs_scrambling_id,
|
|
tmp_e);
|
|
|
|
const uint32_t crc = polar_decoder_int16(tmp_e, dci_estimation, 1, NR_POLAR_DCI_MESSAGE_TYPE, dci_length, L);
|
|
|
|
rnti_t n_rnti = rel15->rnti;
|
|
if (crc == n_rnti) {
|
|
LOG_D(NR_PHY_DCI,
|
|
"(%i.%i) Received dci indication (rnti %x,dci format %d,n_CCE %d,payloadSize %d,payload %llx)\n",
|
|
proc->frame_rx,
|
|
proc->nr_slot_rx,
|
|
n_rnti,
|
|
rel15->dci_format_options[k],
|
|
CCEind,
|
|
dci_length,
|
|
*(unsigned long long *)dci_estimation);
|
|
AssertFatal(dci_ind->number_of_dcis < sizeofArray(dci_ind->dci_list), "Fix allocation\n");
|
|
fapi_nr_dci_indication_pdu_t *dci = dci_ind->dci_list + dci_ind->number_of_dcis;
|
|
*dci = (fapi_nr_dci_indication_pdu_t){
|
|
.rnti = n_rnti,
|
|
.n_CCE = CCEind,
|
|
.N_CCE = L,
|
|
.dci_format = rel15->dci_format_options[k],
|
|
.ss_type = rel15->ss_type_options[k],
|
|
.coreset_type = rel15->coreset.CoreSetType,
|
|
};
|
|
int n_rb, rb_offset;
|
|
get_coreset_rballoc(rel15->coreset.frequency_domain_resource, &n_rb, &rb_offset);
|
|
dci->cset_start = rel15->BWPStart + rb_offset;
|
|
dci->payloadSize = dci_length;
|
|
memcpy(dci->payloadBits, dci_estimation, (dci_length + 7) / 8);
|
|
dci_ind->number_of_dcis++;
|
|
break; // If DCI is found, no need to check for remaining DCI lengths
|
|
} else {
|
|
LOG_D(NR_PHY_DCI,
|
|
"(%i.%i) Decoded crc %x does not match rnti %x for DCI format %d\n",
|
|
proc->frame_rx,
|
|
proc->nr_slot_rx,
|
|
crc,
|
|
n_rnti,
|
|
rel15->dci_format_options[k]);
|
|
}
|
|
}
|
|
e_rx_cand_idx += RE_PER_RB_OUT_DMRS * L * 6; // e_rx index for next candidate (L CCEs, 6 REGs per CCE and 9 REs per REG )
|
|
}
|
|
}
|