Files
openairinterface5g/openair2/LAYER2/NR_MAC_gNB/config.c
T
Bartosz Podrygajlo 48d68f7d90 Fix a bug where if BWP size is not exactly maximum BWP size for a bandwidth power control code asserts.
This assert and assumption is incorrect, according to 38.101-1 UE channel is defined only by the BWP size
and BWP size can be any value supported by the RRC parameters defining the BWP.
To determine the UE channel the smallest bandwidth supporting the BWP is selected. This is an assumption based
on references in the same document to UE channel bandwidth being equal to one of the values in set
 {5, 10, 15, 20, 25, 30, 35, 40, 50, 60, 70, 80, 90, 100}.
Also:
 - calculated P_CMIN during BWP setup since the parameter doesn't depend on DCI.
 - initiialize uniqCfg structure in test_nr_ue_power_procedures main function which enables LOG prints during tests
2024-07-26 11:15:49 +02:00

782 lines
35 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 config.c
* \brief gNB configuration performed by RRC or as a consequence of RRC procedures
* \author Navid Nikaein and Raymond Knopp, WEI-TAI CHEN
* \date 2010 - 2014, 2018
* \version 0.1
* \company Eurecom, NTUST
* \email: navid.nikaein@eurecom.fr, kroempa@gmail.com
* @ingroup _mac
*/
#include "common/platform_types.h"
#include "common/platform_constants.h"
#include "common/ran_context.h"
#include "common/utils/nr/nr_common.h"
#include "common/utils/LOG/log.h"
#include "common/utils/LOG/vcd_signal_dumper.h"
#include "NR_BCCH-BCH-Message.h"
#include "NR_ServingCellConfigCommon.h"
#include "uper_encoder.h"
#include "LAYER2/NR_MAC_gNB/mac_proto.h"
#include "SCHED_NR/phy_frame_config_nr.h"
#include "openair1/PHY/defs_gNB.h"
#include "NR_MIB.h"
#include "LAYER2/NR_MAC_COMMON/nr_mac_common.h"
#include "../../../../nfapi/oai_integration/vendor_ext.h"
/* Softmodem params */
#include "executables/softmodem-common.h"
#include <complex.h>
extern RAN_CONTEXT_t RC;
//extern int l2_init_gNB(void);
extern uint8_t nfapi_mode;
c16_t convert_precoder_weight(double complex c_in)
{
double cr = creal(c_in) * 32768 + 0.5;
if (cr < 0)
cr -= 1;
double ci = cimag(c_in) * 32768 + 0.5;
if (ci < 0)
ci -= 1;
return (c16_t) {.r = (short)cr, .i = (short)ci};
}
void get_K1_K2(int N1, int N2, int *K1, int *K2, int layers)
{
// num of allowed k1 and k2 according to 5.2.2.2.1-3 and -4 in 38.214
switch (layers) {
case 1:
*K1 = 1;
*K2 = 1;
break;
case 2:
*K2 = N2 == 1 ? 1 : 2;
if(N2 == N1 || N1 == 2)
*K1 = 2;
else if (N2 == 1)
*K1 = 4;
else
*K1 = 3;
break;
case 3:
case 4:
*K2 = N2 == 1 ? 1 : 2;
if (N1 == 6)
*K1 = 5;
else
*K1 = N1;
break;
default:
AssertFatal(false, "Number of layers %d not supported\n", layers);
}
}
int get_NTN_Koffset(const NR_ServingCellConfigCommon_t *scc)
{
if (scc->ext2 && scc->ext2->ntn_Config_r17 && scc->ext2->ntn_Config_r17->cellSpecificKoffset_r17)
return *scc->ext2->ntn_Config_r17->cellSpecificKoffset_r17 << *scc->ssbSubcarrierSpacing;
return 0;
}
int precoding_weigths_generation(nfapi_nr_pm_list_t *mat,
int pmiq,
int L,
int N1,
int N2,
int O1,
int O2,
int num_antenna_ports,
double complex theta_n[4],
double complex v_lm[N1 * O1 + 4 * O1][N2 * O2 + O2][N2 * N1])
{
// Table 5.2.2.2.1-X:
// Codebook for L-layer CSI reporting using antenna ports 3000 to 2999+PCSI-RS
// pmi=1,...,pmi_size are computed as follows
int K1 = 0, K2 = 0;
get_K1_K2(N1, N2, &K1, &K2, L);
int I2 = L == 1 ? 4 : 2;
for (int k2 = 0; k2 < K2; k2++) {
for (int k1 = 0; k1 < K1; k1++) {
for (int mm = 0; mm < N2 * O2; mm++) { // i_1_2
for (int ll = 0; ll < N1 * O1; ll++) { // i_1_1
for (int nn = 0; nn < I2; nn++) { // i_2
mat->pmi_pdu[pmiq].pm_idx = pmiq + 1; // index 0 is the identity matrix
mat->pmi_pdu[pmiq].numLayers = L;
mat->pmi_pdu[pmiq].num_ant_ports = num_antenna_ports;
LOG_D(PHY, "layer %d Codebook pmiq = %d\n", L, pmiq);
for (int j_col = 0; j_col < L; j_col++) {
int llc = ll + (k1 * O1 * (j_col & 1));
int mmc = mm + (k2 * O2 * (j_col & 1));
double complex phase_sign = j_col <= ((L - 1) / 2) ? 1 : -1;
double complex res_code;
for (int i_rows = 0; i_rows < N1 * N2; i_rows++) {
nfapi_nr_pm_weights_t *weights = &mat->pmi_pdu[pmiq].weights[j_col][i_rows];
res_code = sqrt(1 / (double)(L * num_antenna_ports)) * v_lm[llc][mmc][i_rows];
c16_t precoder_weight = convert_precoder_weight(res_code);
weights->precoder_weight_Re = precoder_weight.r;
weights->precoder_weight_Im = precoder_weight.i;
LOG_D(PHY,
"%d Layer Precoding Matrix[pmi %d][antPort %d][layerIdx %d]= %f+j %f -> Fixed Point %d+j %d \n",
L,
pmiq,
i_rows,
j_col,
creal(res_code),
cimag(res_code),
weights->precoder_weight_Re,
weights->precoder_weight_Im);
}
for (int i_rows = N1 * N2; i_rows < 2 * N1 * N2; i_rows++) {
nfapi_nr_pm_weights_t *weights = &mat->pmi_pdu[pmiq].weights[j_col][i_rows];
res_code = sqrt(1 / (double)(L * num_antenna_ports)) * (phase_sign)*theta_n[nn] * v_lm[llc][mmc][i_rows - N1 * N2];
c16_t precoder_weight = convert_precoder_weight(res_code);
weights->precoder_weight_Re = precoder_weight.r;
weights->precoder_weight_Im = precoder_weight.i;
LOG_D(PHY,
"%d Layer Precoding Matrix[pmi %d][antPort %d][layerIdx %d]= %f+j %f -> Fixed Point %d+j %d \n",
L,
pmiq,
i_rows,
j_col,
creal(res_code),
cimag(res_code),
weights->precoder_weight_Re,
weights->precoder_weight_Im);
}
}
pmiq++;
}
}
}
}
}
return pmiq;
}
nfapi_nr_pm_list_t init_DL_MIMO_codebook(gNB_MAC_INST *gNB, nr_pdsch_AntennaPorts_t antenna_ports)
{
int num_antenna_ports = antenna_ports.N1 * antenna_ports.N2 * antenna_ports.XP;
if (num_antenna_ports < 2)
return (nfapi_nr_pm_list_t) {0};
//NR Codebook Generation for codebook type1 SinglePanel
int N1 = antenna_ports.N1;
int N2 = antenna_ports.N2;
//Uniform Planner Array: UPA
// X X X X ... X
// X X X X ... X
// N2 . . . . ... .
// X X X X ... X
// |<-----N1---->|
//Get the uniform planar array parameters
// To be confirmed
int O2 = N2 > 1 ? 4 : 1; //Vertical beam oversampling (1 or 4)
int O1 = num_antenna_ports > 2 ? 4 : 1; //Horizontal beam oversampling (1 or 4)
int max_mimo_layers = (num_antenna_ports < NR_MAX_NB_LAYERS) ? num_antenna_ports : NR_MAX_NB_LAYERS;
AssertFatal(max_mimo_layers <= 4, "Max number of layers supported is 4\n");
AssertFatal(num_antenna_ports < 16, "Max number of antenna ports supported is currently 16\n");
int K1 = 0, K2 = 0;
nfapi_nr_pm_list_t mat = {.num_pm_idx = 0};
for (int i = 0; i < max_mimo_layers; i++) {
get_K1_K2(N1, N2, &K1, &K2, i + 1);
int i2_size = i == 0 ? 4 : 2;
gNB->precoding_matrix_size[i] = i2_size * N1 * O1 * N2 * O2 * K1 * K2;
mat.num_pm_idx += gNB->precoding_matrix_size[i];
}
mat.pmi_pdu = malloc16(mat.num_pm_idx * sizeof(*mat.pmi_pdu));
AssertFatal(mat.pmi_pdu != NULL, "out of memory\n");
// Generation of codebook Type1 with codebookMode 1 (num_antenna_ports < 16)
// Generate DFT vertical beams
// ll: index of a vertical beams vector (represented by i1_1 in TS 38.214)
const int max_l = N1 * O1 + 4 * O1; // max k1 is 4*O1
double complex v[max_l][N1];
for (int ll = 0; ll < max_l; ll++) { // i1_1
for (int nn = 0; nn < N1; nn++) {
v[ll][nn] = cexp(I * (2 * M_PI * nn * ll) / (N1 * O1));
LOG_D(PHY, "v[%d][%d] = %f +j %f\n", ll, nn, creal(v[ll][nn]), cimag(v[ll][nn]));
}
}
// Generate DFT Horizontal beams
// mm: index of a Horizontal beams vector (represented by i1_2 in TS 38.214)
const int max_m = N2 * O2 + O2; // max k2 is O2
double complex u[max_m][N2];
for (int mm = 0; mm < max_m; mm++) { // i1_2
for (int nn = 0; nn < N2; nn++) {
u[mm][nn] = cexp(I * (2 * M_PI * nn * mm) / (N2 * O2));
LOG_D(PHY, "u[%d][%d] = %f +j %f\n", mm, nn, creal(u[mm][nn]), cimag(u[mm][nn]));
}
}
// Generate co-phasing angles
// i_2: index of a co-phasing vector
// i1_1, i1_2, and i_2 are reported from UEs
double complex theta_n[4];
for (int nn = 0; nn < 4; nn++) {
theta_n[nn] = cexp(I * M_PI * nn / 2);
LOG_D(PHY, "theta_n[%d] = %f +j %f\n", nn, creal(theta_n[nn]), cimag(theta_n[nn]));
}
// Kronecker product v_lm
double complex v_lm[max_l][max_m][N2 * N1];
// v_ll_mm_codebook denotes the elements of a precoding matrix W_i1,1_i_1,2
for (int ll = 0; ll < max_l; ll++) { // i_1_1
for (int mm = 0; mm < max_m; mm++) { // i_1_2
for (int nn1 = 0; nn1 < N1; nn1++) {
for (int nn2 = 0; nn2 < N2; nn2++) {
v_lm[ll][mm][nn1 * N2 + nn2] = v[ll][nn1] * u[mm][nn2];
LOG_D(PHY,
"v_lm[%d][%d][%d] = %f +j %f\n",
ll,
mm,
nn1 * N2 + nn2,
creal(v_lm[ll][mm][nn1 * N2 + nn2]),
cimag(v_lm[ll][mm][nn1 * N2 + nn2]));
}
}
}
}
int pmiq = 0;
for (int layers = 1; layers <= max_mimo_layers; layers++)
pmiq = precoding_weigths_generation(&mat, pmiq, layers, N1, N2, O1, O2, num_antenna_ports, theta_n, v_lm);
return mat;
}
static void config_common(gNB_MAC_INST *nrmac, nr_pdsch_AntennaPorts_t pdsch_AntennaPorts, int pusch_AntennaPorts, NR_ServingCellConfigCommon_t *scc)
{
nfapi_nr_config_request_scf_t *cfg = &nrmac->config[0];
nrmac->common_channels[0].ServingCellConfigCommon = scc;
// Carrier configuration
struct NR_FrequencyInfoDL *frequencyInfoDL = scc->downlinkConfigCommon->frequencyInfoDL;
frequency_range_t frequency_range = *frequencyInfoDL->frequencyBandList.list.array[0] > 256 ? FR2 : FR1;
int bw_index = get_supported_band_index(frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing,
frequency_range,
frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth);
cfg->carrier_config.dl_bandwidth.value = get_supported_bw_mhz(frequency_range, bw_index);
cfg->carrier_config.dl_bandwidth.tl.tag = NFAPI_NR_CONFIG_DL_BANDWIDTH_TAG; // temporary
cfg->num_tlv++;
LOG_I(NR_MAC, "DL_Bandwidth:%d\n", cfg->carrier_config.dl_bandwidth.value);
cfg->carrier_config.dl_frequency.value = from_nrarfcn(*frequencyInfoDL->frequencyBandList.list.array[0],
*scc->ssbSubcarrierSpacing,
frequencyInfoDL->absoluteFrequencyPointA)
/ 1000; // freq in kHz
cfg->carrier_config.dl_frequency.tl.tag = NFAPI_NR_CONFIG_DL_FREQUENCY_TAG;
cfg->num_tlv++;
for (int i = 0; i < 5; i++) {
if (i == frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing) {
cfg->carrier_config.dl_grid_size[i].value = frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth;
cfg->carrier_config.dl_k0[i].value = frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->offsetToCarrier;
cfg->carrier_config.dl_grid_size[i].tl.tag = NFAPI_NR_CONFIG_DL_GRID_SIZE_TAG;
cfg->carrier_config.dl_k0[i].tl.tag = NFAPI_NR_CONFIG_DL_K0_TAG;
cfg->num_tlv++;
cfg->num_tlv++;
} else {
cfg->carrier_config.dl_grid_size[i].value = 0;
cfg->carrier_config.dl_k0[i].value = 0;
}
}
struct NR_FrequencyInfoUL *frequencyInfoUL = scc->uplinkConfigCommon->frequencyInfoUL;
frequency_range = *frequencyInfoUL->frequencyBandList->list.array[0] > 256 ? FR2 : FR1;
bw_index = get_supported_band_index(frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing,
frequency_range,
frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth);
cfg->carrier_config.uplink_bandwidth.value = get_supported_bw_mhz(frequency_range, bw_index);
cfg->carrier_config.uplink_bandwidth.tl.tag = NFAPI_NR_CONFIG_UPLINK_BANDWIDTH_TAG; // temporary
cfg->num_tlv++;
LOG_I(NR_MAC, "DL_Bandwidth:%d\n", cfg->carrier_config.uplink_bandwidth.value);
int UL_pointA;
if (frequencyInfoUL->absoluteFrequencyPointA == NULL)
UL_pointA = frequencyInfoDL->absoluteFrequencyPointA;
else
UL_pointA = *frequencyInfoUL->absoluteFrequencyPointA;
cfg->carrier_config.uplink_frequency.value = from_nrarfcn(*frequencyInfoUL->frequencyBandList->list.array[0],
*scc->ssbSubcarrierSpacing,
UL_pointA)
/ 1000; // freq in kHz
cfg->carrier_config.uplink_frequency.tl.tag = NFAPI_NR_CONFIG_UPLINK_FREQUENCY_TAG;
cfg->num_tlv++;
for (int i = 0; i < 5; i++) {
if (i == frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing) {
cfg->carrier_config.ul_grid_size[i].value = frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth;
cfg->carrier_config.ul_k0[i].value = frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->offsetToCarrier;
cfg->carrier_config.ul_grid_size[i].tl.tag = NFAPI_NR_CONFIG_UL_GRID_SIZE_TAG;
cfg->carrier_config.ul_k0[i].tl.tag = NFAPI_NR_CONFIG_UL_K0_TAG;
cfg->num_tlv++;
cfg->num_tlv++;
} else {
cfg->carrier_config.ul_grid_size[i].value = 0;
cfg->carrier_config.ul_k0[i].value = 0;
}
}
uint32_t band = *frequencyInfoDL->frequencyBandList.list.array[0];
frequency_range = band < 100 ? FR1 : FR2;
frame_type_t frame_type = get_frame_type(*frequencyInfoDL->frequencyBandList.list.array[0], *scc->ssbSubcarrierSpacing);
nrmac->common_channels[0].frame_type = frame_type;
// Cell configuration
cfg->cell_config.phy_cell_id.value = *scc->physCellId;
cfg->cell_config.phy_cell_id.tl.tag = NFAPI_NR_CONFIG_PHY_CELL_ID_TAG;
cfg->num_tlv++;
cfg->cell_config.frame_duplex_type.value = frame_type;
cfg->cell_config.frame_duplex_type.tl.tag = NFAPI_NR_CONFIG_FRAME_DUPLEX_TYPE_TAG;
cfg->num_tlv++;
// SSB configuration
cfg->ssb_config.ss_pbch_power.value = scc->ss_PBCH_BlockPower;
cfg->ssb_config.ss_pbch_power.tl.tag = NFAPI_NR_CONFIG_SS_PBCH_POWER_TAG;
cfg->num_tlv++;
cfg->ssb_config.bch_payload.value = 1;
cfg->ssb_config.bch_payload.tl.tag = NFAPI_NR_CONFIG_BCH_PAYLOAD_TAG;
cfg->num_tlv++;
cfg->ssb_config.scs_common.value = *scc->ssbSubcarrierSpacing;
cfg->ssb_config.scs_common.tl.tag = NFAPI_NR_CONFIG_SCS_COMMON_TAG;
cfg->num_tlv++;
// PRACH configuration
uint8_t nb_preambles = 64;
NR_RACH_ConfigCommon_t *rach_ConfigCommon = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup;
if (rach_ConfigCommon->totalNumberOfRA_Preambles != NULL)
nb_preambles = *rach_ConfigCommon->totalNumberOfRA_Preambles;
cfg->prach_config.prach_sequence_length.value = rach_ConfigCommon->prach_RootSequenceIndex.present - 1;
cfg->prach_config.prach_sequence_length.tl.tag = NFAPI_NR_CONFIG_PRACH_SEQUENCE_LENGTH_TAG;
cfg->num_tlv++;
if (rach_ConfigCommon->msg1_SubcarrierSpacing)
cfg->prach_config.prach_sub_c_spacing.value = *rach_ConfigCommon->msg1_SubcarrierSpacing;
else
cfg->prach_config.prach_sub_c_spacing.value = frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing;
cfg->prach_config.prach_sub_c_spacing.tl.tag = NFAPI_NR_CONFIG_PRACH_SUB_C_SPACING_TAG;
cfg->num_tlv++;
cfg->prach_config.restricted_set_config.value = rach_ConfigCommon->restrictedSetConfig;
cfg->prach_config.restricted_set_config.tl.tag = NFAPI_NR_CONFIG_RESTRICTED_SET_CONFIG_TAG;
cfg->num_tlv++;
cfg->prach_config.prach_ConfigurationIndex.value = rach_ConfigCommon->rach_ConfigGeneric.prach_ConfigurationIndex;
cfg->prach_config.prach_ConfigurationIndex.tl.tag = NFAPI_NR_CONFIG_PRACH_CONFIG_INDEX_TAG;
cfg->num_tlv++;
switch (rach_ConfigCommon->rach_ConfigGeneric.msg1_FDM) {
case 0:
cfg->prach_config.num_prach_fd_occasions.value = 1;
break;
case 1:
cfg->prach_config.num_prach_fd_occasions.value = 2;
break;
case 2:
cfg->prach_config.num_prach_fd_occasions.value = 4;
break;
case 3:
cfg->prach_config.num_prach_fd_occasions.value = 8;
break;
default:
AssertFatal(1 == 0, "msg1 FDM identifier %ld undefined (0,1,2,3) \n", rach_ConfigCommon->rach_ConfigGeneric.msg1_FDM);
}
cfg->prach_config.num_prach_fd_occasions.tl.tag = NFAPI_NR_CONFIG_NUM_PRACH_FD_OCCASIONS_TAG;
cfg->num_tlv++;
cfg->prach_config.prach_ConfigurationIndex.value = rach_ConfigCommon->rach_ConfigGeneric.prach_ConfigurationIndex;
cfg->prach_config.prach_ConfigurationIndex.tl.tag = NFAPI_NR_CONFIG_PRACH_CONFIG_INDEX_TAG;
cfg->num_tlv++;
cfg->prach_config.num_prach_fd_occasions_list = (nfapi_nr_num_prach_fd_occasions_t *)malloc(
cfg->prach_config.num_prach_fd_occasions.value * sizeof(nfapi_nr_num_prach_fd_occasions_t));
for (int i = 0; i < cfg->prach_config.num_prach_fd_occasions.value; i++) {
nfapi_nr_num_prach_fd_occasions_t *prach_fd_occasion = &cfg->prach_config.num_prach_fd_occasions_list[i];
// prach_fd_occasion->num_prach_fd_occasions = i;
if (cfg->prach_config.prach_sequence_length.value)
prach_fd_occasion->prach_root_sequence_index.value = rach_ConfigCommon->prach_RootSequenceIndex.choice.l139;
else
prach_fd_occasion->prach_root_sequence_index.value = rach_ConfigCommon->prach_RootSequenceIndex.choice.l839;
prach_fd_occasion->prach_root_sequence_index.tl.tag = NFAPI_NR_CONFIG_PRACH_ROOT_SEQUENCE_INDEX_TAG;
cfg->num_tlv++;
prach_fd_occasion->k1.value =
NRRIV2PRBOFFSET(scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE)
+ rach_ConfigCommon->rach_ConfigGeneric.msg1_FrequencyStart
+ (get_N_RA_RB(cfg->prach_config.prach_sub_c_spacing.value,
frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing)
* i);
if (get_softmodem_params()->sa) {
prach_fd_occasion->k1.value =
NRRIV2PRBOFFSET(scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE)
+ rach_ConfigCommon->rach_ConfigGeneric.msg1_FrequencyStart
+ (get_N_RA_RB(cfg->prach_config.prach_sub_c_spacing.value,
frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing)
* i);
} else {
prach_fd_occasion->k1.value = rach_ConfigCommon->rach_ConfigGeneric.msg1_FrequencyStart
+ (get_N_RA_RB(cfg->prach_config.prach_sub_c_spacing.value,
frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing)
* i);
}
prach_fd_occasion->k1.tl.tag = NFAPI_NR_CONFIG_K1_TAG;
cfg->num_tlv++;
prach_fd_occasion->prach_zero_corr_conf.value = rach_ConfigCommon->rach_ConfigGeneric.zeroCorrelationZoneConfig;
prach_fd_occasion->prach_zero_corr_conf.tl.tag = NFAPI_NR_CONFIG_PRACH_ZERO_CORR_CONF_TAG;
cfg->num_tlv++;
prach_fd_occasion->num_root_sequences.value = compute_nr_root_seq(rach_ConfigCommon, nb_preambles, frame_type, frequency_range);
prach_fd_occasion->num_root_sequences.tl.tag = NFAPI_NR_CONFIG_NUM_ROOT_SEQUENCES_TAG;
cfg->num_tlv++;
prach_fd_occasion->num_unused_root_sequences.tl.tag = NFAPI_NR_CONFIG_NUM_UNUSED_ROOT_SEQUENCES_TAG;
prach_fd_occasion->num_unused_root_sequences.value = 0;
cfg->num_tlv++;
}
cfg->prach_config.ssb_per_rach.value = rach_ConfigCommon->ssb_perRACH_OccasionAndCB_PreamblesPerSSB->present - 1;
cfg->prach_config.ssb_per_rach.tl.tag = NFAPI_NR_CONFIG_SSB_PER_RACH_TAG;
cfg->num_tlv++;
// SSB Table Configuration
cfg->ssb_table.ssb_offset_point_a.value =
get_ssb_offset_to_pointA(*scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencySSB,
scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencyPointA,
*scc->ssbSubcarrierSpacing,
frequency_range);
cfg->ssb_table.ssb_offset_point_a.tl.tag = NFAPI_NR_CONFIG_SSB_OFFSET_POINT_A_TAG;
cfg->num_tlv++;
cfg->ssb_table.ssb_period.value = *scc->ssb_periodicityServingCell;
cfg->ssb_table.ssb_period.tl.tag = NFAPI_NR_CONFIG_SSB_PERIOD_TAG;
cfg->num_tlv++;
cfg->ssb_table.ssb_subcarrier_offset.value =
get_ssb_subcarrier_offset(*scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencySSB,
scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencyPointA,
*scc->ssbSubcarrierSpacing);
cfg->ssb_table.ssb_subcarrier_offset.tl.tag = NFAPI_NR_CONFIG_SSB_SUBCARRIER_OFFSET_TAG;
cfg->num_tlv++;
uint8_t *mib_payload = nrmac->common_channels[0].MIB_pdu;
uint32_t mib = (mib_payload[2] << 16) | (mib_payload[1] << 8) | mib_payload[0];
cfg->ssb_table.MIB.tl.tag = NFAPI_NR_CONFIG_MIB_TAG;
cfg->ssb_table.MIB.value = mib;
cfg->num_tlv++;
nrmac->ssb_SubcarrierOffset = cfg->ssb_table.ssb_subcarrier_offset.value;
nrmac->ssb_OffsetPointA = cfg->ssb_table.ssb_offset_point_a.value;
LOG_I(NR_MAC,
"ssb_OffsetPointA %d, ssb_SubcarrierOffset %d\n",
cfg->ssb_table.ssb_offset_point_a.value,
cfg->ssb_table.ssb_subcarrier_offset.value);
switch (scc->ssb_PositionsInBurst->present) {
case 1:
cfg->ssb_table.ssb_mask_list[0].ssb_mask.value = scc->ssb_PositionsInBurst->choice.shortBitmap.buf[0] << 24;
cfg->ssb_table.ssb_mask_list[1].ssb_mask.value = 0;
break;
case 2:
cfg->ssb_table.ssb_mask_list[0].ssb_mask.value = ((uint32_t)scc->ssb_PositionsInBurst->choice.mediumBitmap.buf[0]) << 24;
cfg->ssb_table.ssb_mask_list[1].ssb_mask.value = 0;
break;
case 3:
cfg->ssb_table.ssb_mask_list[0].ssb_mask.value = 0;
cfg->ssb_table.ssb_mask_list[1].ssb_mask.value = 0;
for (int i = 0; i < 4; i++) {
cfg->ssb_table.ssb_mask_list[0].ssb_mask.value += (uint32_t)scc->ssb_PositionsInBurst->choice.longBitmap.buf[3 - i]
<< i * 8;
cfg->ssb_table.ssb_mask_list[1].ssb_mask.value += (uint32_t)scc->ssb_PositionsInBurst->choice.longBitmap.buf[7 - i]
<< i * 8;
}
break;
default:
AssertFatal(1 == 0, "SSB bitmap size value %d undefined (allowed values 1,2,3) \n", scc->ssb_PositionsInBurst->present);
}
cfg->ssb_table.ssb_mask_list[0].ssb_mask.tl.tag = NFAPI_NR_CONFIG_SSB_MASK_TAG;
cfg->ssb_table.ssb_mask_list[1].ssb_mask.tl.tag = NFAPI_NR_CONFIG_SSB_MASK_TAG;
cfg->num_tlv += 2;
// logical antenna ports
int num_pdsch_antenna_ports = pdsch_AntennaPorts.N1 * pdsch_AntennaPorts.N2 * pdsch_AntennaPorts.XP;
cfg->carrier_config.num_tx_ant.value = num_pdsch_antenna_ports;
AssertFatal(num_pdsch_antenna_ports > 0 && num_pdsch_antenna_ports < 33, "pdsch_AntennaPorts in 1...32\n");
cfg->carrier_config.num_tx_ant.tl.tag = NFAPI_NR_CONFIG_NUM_TX_ANT_TAG;
int num_ssb = 0;
for (int i = 0; i < 32; i++) {
cfg->ssb_table.ssb_beam_id_list[i].beam_id.tl.tag = NFAPI_NR_CONFIG_BEAM_ID_TAG;
if ((cfg->ssb_table.ssb_mask_list[0].ssb_mask.value >> (31 - i)) & 1) {
cfg->ssb_table.ssb_beam_id_list[i].beam_id.value = num_ssb;
num_ssb++;
}
cfg->num_tlv++;
}
for (int i = 0; i < 32; i++) {
cfg->ssb_table.ssb_beam_id_list[32 + i].beam_id.tl.tag = NFAPI_NR_CONFIG_BEAM_ID_TAG;
if ((cfg->ssb_table.ssb_mask_list[1].ssb_mask.value >> (31 - i)) & 1) {
cfg->ssb_table.ssb_beam_id_list[32 + i].beam_id.value = num_ssb;
num_ssb++;
}
cfg->num_tlv++;
}
cfg->carrier_config.num_rx_ant.value = pusch_AntennaPorts;
AssertFatal(pusch_AntennaPorts > 0 && pusch_AntennaPorts < 13, "pusch_AntennaPorts in 1...12\n");
cfg->carrier_config.num_rx_ant.tl.tag = NFAPI_NR_CONFIG_NUM_RX_ANT_TAG;
LOG_I(NR_MAC,
"Set RX antenna number to %d, Set TX antenna number to %d (num ssb %d: %x,%x)\n",
cfg->carrier_config.num_tx_ant.value,
cfg->carrier_config.num_rx_ant.value,
num_ssb,
cfg->ssb_table.ssb_mask_list[0].ssb_mask.value,
cfg->ssb_table.ssb_mask_list[1].ssb_mask.value);
AssertFatal(cfg->carrier_config.num_tx_ant.value > 0,
"carrier_config.num_tx_ant.value %d !\n",
cfg->carrier_config.num_tx_ant.value);
cfg->num_tlv++;
cfg->num_tlv++;
// TDD Table Configuration
if (cfg->cell_config.frame_duplex_type.value == TDD) {
cfg->tdd_table.tdd_period.tl.tag = NFAPI_NR_CONFIG_TDD_PERIOD_TAG;
cfg->num_tlv++;
if (scc->tdd_UL_DL_ConfigurationCommon->pattern1.ext1 == NULL) {
cfg->tdd_table.tdd_period.value = scc->tdd_UL_DL_ConfigurationCommon->pattern1.dl_UL_TransmissionPeriodicity;
} else {
AssertFatal(scc->tdd_UL_DL_ConfigurationCommon->pattern1.ext1->dl_UL_TransmissionPeriodicity_v1530 != NULL,
"In %s: scc->tdd_UL_DL_ConfigurationCommon->pattern1.ext1->dl_UL_TransmissionPeriodicity_v1530 is null\n",
__FUNCTION__);
cfg->tdd_table.tdd_period.value = *scc->tdd_UL_DL_ConfigurationCommon->pattern1.ext1->dl_UL_TransmissionPeriodicity_v1530;
}
LOG_I(NR_MAC, "Setting TDD configuration period to %d\n", cfg->tdd_table.tdd_period.value);
int periods_per_frame = set_tdd_config_nr(cfg,
frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing,
scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofDownlinkSlots,
scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofDownlinkSymbols,
scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofUplinkSlots,
scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofUplinkSymbols);
AssertFatal(periods_per_frame > 0, "TDD configuration cannot be configured\n");
if (frequency_range == FR2) {
LOG_I(NR_MAC, "Configuring TDD beam association to default\n");
nrmac->tdd_beam_association = malloc16(periods_per_frame * sizeof(int16_t));
for (int i = 0; i < periods_per_frame; ++i)
nrmac->tdd_beam_association[i] = -1; /* default: beams not configured */
} else {
nrmac->tdd_beam_association = NULL; /* default: no beams */
}
}
// precoding matrix configuration (to be improved)
cfg->pmi_list = init_DL_MIMO_codebook(nrmac, pdsch_AntennaPorts);
}
void nr_mac_config_scc(gNB_MAC_INST *nrmac, NR_ServingCellConfigCommon_t *scc, const nr_mac_config_t *config)
{
DevAssert(nrmac != NULL);
DevAssert(scc != NULL);
DevAssert(config != NULL);
AssertFatal(scc->ssb_PositionsInBurst->present > 0 && scc->ssb_PositionsInBurst->present < 4,
"SSB Bitmap type %d is not valid\n",
scc->ssb_PositionsInBurst->present);
const int NTN_gNB_Koffset = get_NTN_Koffset(scc);
const int n = nr_slots_per_frame[*scc->ssbSubcarrierSpacing];
const int size = n << (int)ceil(log2((NTN_gNB_Koffset + 13) / n + 1)); // 13 is upper limit for max_fb_time
nrmac->vrb_map_UL_size = size;
nrmac->common_channels[0].vrb_map_UL = calloc(size * MAX_BWP_SIZE, sizeof(uint16_t));
AssertFatal(nrmac->common_channels[0].vrb_map_UL,
"could not allocate memory for RC.nrmac[]->common_channels[0].vrb_map_UL\n");
nrmac->UL_tti_req_ahead_size = size;
nrmac->UL_tti_req_ahead[0] = calloc(size, sizeof(nfapi_nr_ul_tti_request_t));
AssertFatal(nrmac->UL_tti_req_ahead[0], "could not allocate memory for nrmac->UL_tti_req_ahead[0]\n");
LOG_I(NR_MAC, "Configuring common parameters from NR ServingCellConfig\n");
config_common(nrmac, config->pdsch_AntennaPorts, config->pusch_AntennaPorts, scc);
if (NFAPI_MODE == NFAPI_MONOLITHIC) {
// nothing to be sent in the other cases
NR_PHY_Config_t phycfg = {.Mod_id = 0, .CC_id = 0, .cfg = &nrmac->config[0]};
DevAssert(nrmac->if_inst->NR_PHY_config_req);
nrmac->if_inst->NR_PHY_config_req(&phycfg);
}
find_SSB_and_RO_available(nrmac);
const NR_TDD_UL_DL_Pattern_t *tdd = scc->tdd_UL_DL_ConfigurationCommon ? &scc->tdd_UL_DL_ConfigurationCommon->pattern1 : NULL;
int nr_slots_period = n;
int nr_dl_slots = n;
int nr_ulstart_slot = 0;
if (tdd) {
nr_dl_slots = tdd->nrofDownlinkSlots + (tdd->nrofDownlinkSymbols != 0);
nr_ulstart_slot = get_first_ul_slot(tdd->nrofDownlinkSlots, tdd->nrofDownlinkSymbols, tdd->nrofUplinkSymbols);
nr_slots_period /= get_nb_periods_per_frame(tdd->dl_UL_TransmissionPeriodicity);
} else {
// if TDD configuration is not present and the band is not FDD, it means it is a dynamic TDD configuration
AssertFatal(nrmac->common_channels[0].frame_type == FDD,"Dynamic TDD not handled yet\n");
}
for (int slot = 0; slot < n; ++slot) {
nrmac->dlsch_slot_bitmap[slot / 64] |= (uint64_t)((slot % nr_slots_period) < nr_dl_slots) << (slot % 64);
nrmac->ulsch_slot_bitmap[slot / 64] |= (uint64_t)((slot % nr_slots_period) >= nr_ulstart_slot) << (slot % 64);
LOG_D(NR_MAC,
"slot %d DL %d UL %d\n",
slot,
(nrmac->dlsch_slot_bitmap[slot / 64] & ((uint64_t)1 << (slot % 64))) != 0,
(nrmac->ulsch_slot_bitmap[slot / 64] & ((uint64_t)1 << (slot % 64))) != 0);
}
if (get_softmodem_params()->phy_test) {
nrmac->pre_processor_dl = nr_preprocessor_phytest;
nrmac->pre_processor_ul = nr_ul_preprocessor_phytest;
} else {
nrmac->pre_processor_dl = nr_init_fr1_dlsch_preprocessor(0);
nrmac->pre_processor_ul = nr_init_fr1_ulsch_preprocessor(0);
}
NR_COMMON_channels_t *cc = &nrmac->common_channels[0];
NR_SCHED_LOCK(&nrmac->sched_lock);
for (int n = 0; n < NR_NB_RA_PROC_MAX; n++) {
NR_RA_t *ra = &cc->ra[n];
nr_clear_ra_proc(ra);
}
NR_SCHED_UNLOCK(&nrmac->sched_lock);
}
void nr_mac_configure_sib1(gNB_MAC_INST *nrmac, const f1ap_plmn_t *plmn, uint64_t cellID, int tac)
{
AssertFatal(get_softmodem_params()->sa > 0, "error: SIB1 only applicable for SA\n");
NR_COMMON_channels_t *cc = &nrmac->common_channels[0];
NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon;
NR_BCCH_DL_SCH_Message_t *sib1 = get_SIB1_NR(scc, plmn, cellID, tac, &nrmac->radio_config.timer_config);
cc->sib1 = sib1;
cc->sib1_bcch_length = encode_SIB1_NR(sib1, cc->sib1_bcch_pdu, sizeof(cc->sib1_bcch_pdu));
AssertFatal(cc->sib1_bcch_length > 0, "could not encode SIB1\n");
}
bool nr_mac_add_test_ue(gNB_MAC_INST *nrmac, uint32_t rnti, NR_CellGroupConfig_t *CellGroup)
{
/* ideally, instead of this function, "users" of this function should call
* the ue context setup request function in mac_rrc_dl_handler.c */
DevAssert(nrmac != NULL);
DevAssert(CellGroup != NULL);
DevAssert(get_softmodem_params()->phy_test);
NR_SCHED_LOCK(&nrmac->sched_lock);
NR_UE_info_t *UE = add_new_nr_ue(nrmac, rnti, CellGroup);
if (!UE) {
LOG_E(NR_MAC, "Error adding UE %04x\n", rnti);
NR_SCHED_UNLOCK(&nrmac->sched_lock);
return false;
}
if (CellGroup->spCellConfig && CellGroup->spCellConfig->reconfigurationWithSync
&& CellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated
&& CellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra) {
nr_mac_prepare_ra_ue(RC.nrmac[0], UE->rnti, CellGroup);
}
process_addmod_bearers_cellGroupConfig(&UE->UE_sched_ctrl, CellGroup->rlc_BearerToAddModList);
AssertFatal(CellGroup->rlc_BearerToReleaseList == NULL, "cannot release bearers while adding new UEs\n");
NR_SCHED_UNLOCK(&nrmac->sched_lock);
LOG_I(NR_MAC, "Added new UE %x with initial CellGroup\n", rnti);
return true;
}
bool nr_mac_prepare_ra_ue(gNB_MAC_INST *nrmac, uint32_t rnti, NR_CellGroupConfig_t *CellGroup)
{
DevAssert(nrmac != NULL);
DevAssert(CellGroup != NULL);
NR_SCHED_ENSURE_LOCKED(&nrmac->sched_lock);
// NSA case: need to pre-configure CFRA
const int CC_id = 0;
NR_COMMON_channels_t *cc = &nrmac->common_channels[CC_id];
uint8_t ra_index = 0;
/* checking for free RA process */
for(; ra_index < NR_NB_RA_PROC_MAX; ra_index++) {
if ((cc->ra[ra_index].ra_state == nrRA_gNB_IDLE) && (!cc->ra[ra_index].cfra))
break;
}
if (ra_index == NR_NB_RA_PROC_MAX) {
LOG_E(NR_MAC, "RA processes are not available for CFRA RNTI %04x\n", rnti);
return false;
}
NR_RA_t *ra = &cc->ra[ra_index];
ra->cfra = true;
ra->rnti = rnti;
ra->CellGroup = CellGroup;
struct NR_CFRA *cfra = CellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra;
uint8_t num_preamble = cfra->resources.choice.ssb->ssb_ResourceList.list.count;
ra->preambles.num_preambles = num_preamble;
for (int i = 0; i < cc->num_active_ssb; i++) {
for (int j = 0; j < num_preamble; j++) {
if (cc->ssb_index[i] == cfra->resources.choice.ssb->ssb_ResourceList.list.array[j]->ssb) {
// one dedicated preamble for each beam
ra->preambles.preamble_list[i] = cfra->resources.choice.ssb->ssb_ResourceList.list.array[j]->ra_PreambleIndex;
break;
}
}
}
LOG_I(NR_MAC, "Added new %s process for UE RNTI %04x with initial CellGroup\n", ra->cfra ? "CFRA" : "CBRA", rnti);
return true;
}
/* Prepare a new CellGroupConfig to be applied for this UE. We cannot
* immediatly apply it, as we have to wait for the reconfiguration through RRC.
* This function sets up everything to apply the reconfiguration. Later, we
* will trigger the timer with nr_mac_enable_ue_rrc_processing_timer(); upon
* expiry nr_mac_apply_cellgroup() will apply the CellGroupConfig (radio config
* etc). */
bool nr_mac_prepare_cellgroup_update(gNB_MAC_INST *nrmac, NR_UE_info_t *UE, NR_CellGroupConfig_t *CellGroup)
{
DevAssert(nrmac != NULL);
DevAssert(UE != NULL);
DevAssert(CellGroup != NULL);
/* we assume that this function is mutex-protected from outside */
NR_SCHED_ENSURE_LOCKED(&nrmac->sched_lock);
UE->reconfigCellGroup = CellGroup;
UE->expect_reconfiguration = true;
return true;
}