feat(nr): implement rank-8 PDSCH

This commit is contained in:
2026-09-15 00:36:01 +09:00
parent 04ee4ea8e5
commit c50fe1af53
42 changed files with 3244 additions and 648 deletions
@@ -3034,7 +3034,10 @@ uint16_t nr_dci_size(const NR_UE_DL_BWP_t *DL_BWP,
// TB2
long *maxCWperDCI = pdsch_Config ? pdsch_Config->maxNrofCodeWordsScheduledByDCI : NULL;
if (maxCWperDCI && (*maxCWperDCI == NR_PDSCH_Config__maxNrofCodeWordsScheduledByDCI_n2)) {
size += 8;
dci_pdu->mcs2.nbits = 5;
dci_pdu->ndi2.nbits = 1;
dci_pdu->rv2.nbits = 2;
size += dci_pdu->mcs2.nbits + dci_pdu->ndi2.nbits + dci_pdu->rv2.nbits;
}
// HARQ process number – 5 bits if higher layer parameter harq-ProcessNumberSizeDCI-1-1 is configured;
// otherwise 4 bits. Spec 38.212 Section 7.3.1.2.2
+7 -4
View File
@@ -207,13 +207,16 @@ nfapi_nr_pm_list_t init_DL_MIMO_codebook(gNB_MAC_INST *gNB, nr_pdsch_AntennaPort
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");
const int max_mimo_layers = min(num_antenna_ports, NR_MAX_NB_LAYERS);
/* Type-I codebook generation below currently covers ranks 1--4. Rank
* 5--8 is an explicit identity-PMI path and therefore needs no generated
* codebook entries. */
const int codebook_layers = min(max_mimo_layers, 4);
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++) {
for (int i = 0; i < codebook_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;
@@ -274,7 +277,7 @@ nfapi_nr_pm_list_t init_DL_MIMO_codebook(gNB_MAC_INST *gNB, nr_pdsch_AntennaPort
}
int pmiq = 0;
for (int layers = 1; layers <= max_mimo_layers; layers++)
for (int layers = 1; layers <= codebook_layers; layers++)
pmiq = precoding_weigths_generation(&mat, pmiq, layers, N1, N2, O1, O2, num_antenna_ports, theta_n, v_lm);
return mat;
+210 -116
View File
@@ -404,6 +404,37 @@ bwp_info_t get_pdsch_bwp_start_size(gNB_MAC_INST *nr_mac, NR_UE_info_t *UE)
return bwp_info;
}
static uint32_t compute_sched_pdsch_tbs(const NR_sched_pdsch_t *sched, uint16_t rb_size, uint8_t codeword)
{
const int cw_layers = nr_pdsch_layers_per_codeword(sched->nrOfLayers, codeword);
return nr_compute_tbs(sched->Qm,
sched->R,
rb_size,
sched->tda_info.nrOfSymbols,
sched->dmrs_parms.N_PRB_DMRS * sched->dmrs_parms.N_DMRS_SLOT,
0,
0,
cw_layers)
>> 3;
}
void update_sched_pdsch_codeword_tbs(NR_sched_pdsch_t *sched)
{
sched->tb_size = compute_sched_pdsch_tbs(sched, sched->rbSize, 0);
sched->tb_size2 = nr_pdsch_num_codewords(sched->nrOfLayers) == 2 ? compute_sched_pdsch_tbs(sched, sched->rbSize, 1) : 0;
}
static void fit_dual_codeword_tbs(NR_sched_pdsch_t *sched, uint32_t requested_bytes, uint16_t max_rb_size)
{
if (nr_pdsch_num_codewords(sched->nrOfLayers) == 1)
return;
update_sched_pdsch_codeword_tbs(sched);
while ((uint64_t)sched->tb_size + sched->tb_size2 < requested_bytes && sched->rbSize < max_rb_size) {
sched->rbSize++;
update_sched_pdsch_codeword_tbs(sched);
}
}
static bool allocate_dl_retransmission(gNB_MAC_INST *nr_mac,
post_process_pdsch_t *pp_pdsch,
int *n_rb_sched,
@@ -420,7 +451,7 @@ static bool allocate_dl_retransmission(gNB_MAC_INST *nr_mac,
NR_UE_DL_BWP_t *dl_bwp = &UE->current_DL_BWP;
NR_UE_UL_BWP_t *ul_bwp = &UE->current_UL_BWP;
NR_sched_pdsch_t new_sched = sched_ctrl->harq_processes[current_harq_pid].sched_pdsch;
int layers = get_dl_nrOfLayers(sched_ctrl, dl_bwp->dci_format);
int layers = get_dl_nrOfLayers(nr_mac, sched_ctrl, dl_bwp->dci_format);
int pm_index = get_pm_index(nr_mac, UE, dl_bwp->dci_format, layers, nr_mac->radio_config.pdsch_AntennaPorts.XP);
// If the RI changed between current rtx and a previous transmission
@@ -473,26 +504,43 @@ static bool allocate_dl_retransmission(gNB_MAC_INST *nr_mac,
* that we have enough resources */
NR_pdsch_dmrs_t temp_dmrs = get_dl_dmrs_params(scc, dl_bwp, &temp_tda, layers);
uint32_t new_tbs;
uint16_t new_rbSize;
bool success = nr_find_nb_rb(new_sched.Qm,
new_sched.R,
1, // no transform precoding for DL
layers,
temp_tda.nrOfSymbols,
temp_dmrs.N_PRB_DMRS * temp_dmrs.N_DMRS_SLOT,
new_sched.tb_size,
1, /* minimum of 1RB: need to find exact TBS, don't preclude any number */
bwp_size,
&new_tbs,
&new_rbSize);
uint16_t new_rbSize = 0;
if (nr_pdsch_num_codewords(layers) == 1) {
uint32_t new_tbs = 0;
bool success = nr_find_nb_rb(new_sched.Qm,
new_sched.R,
1, // no transform precoding for DL
layers,
temp_tda.nrOfSymbols,
temp_dmrs.N_PRB_DMRS * temp_dmrs.N_DMRS_SLOT,
new_sched.tb_size,
1, /* find an exact retransmission TBS */
bwp_size,
&new_tbs,
&new_rbSize);
if (!success || new_tbs != new_sched.tb_size)
new_rbSize = 0;
} else {
NR_sched_pdsch_t candidate = new_sched;
candidate.nrOfLayers = layers;
candidate.dmrs_parms = temp_dmrs;
candidate.tda_info = temp_tda;
for (uint16_t rb = 1; rb <= bwp_size; rb++) {
candidate.rbSize = rb;
update_sched_pdsch_codeword_tbs(&candidate);
if (candidate.tb_size == new_sched.tb_size && candidate.tb_size2 == new_sched.tb_size2) {
new_rbSize = rb;
break;
}
}
}
if (!success || new_tbs != new_sched.tb_size) {
if (new_rbSize == 0) {
LOG_D(NR_MAC, "[UE %04x][%4d.%2d] allocation of DL retransmission failed: new TBS %d of new TDA does not match old TBS %d\n",
UE->rnti,
frame,
slot,
new_tbs,
0,
new_sched.tb_size);
return false; /* the maximum TBsize we might have is smaller than what we need */
}
@@ -505,13 +553,13 @@ static bool allocate_dl_retransmission(gNB_MAC_INST *nr_mac,
/* we can allocate it. Overwrite the time_domain_allocation, the number
* of RBs, and the new TB size. The rest is done below */
new_sched.tb_size = new_tbs;
new_sched.rbSize = new_rbSize;
new_sched.time_domain_allocation = tda;
new_sched.nrOfLayers = layers;
new_sched.pm_index = pm_index;
new_sched.dmrs_parms = temp_dmrs;
new_sched.tda_info = temp_tda;
update_sched_pdsch_codeword_tbs(&new_sched);
}
/* Find a free CCE */
@@ -538,6 +586,11 @@ static bool allocate_dl_retransmission(gNB_MAC_INST *nr_mac,
if (!get_FeedbackDisabled(UE->sc_info.downlinkHARQ_FeedbackDisabled_r17, current_harq_pid)) {
int r_pucch = nr_get_pucch_resource(sched_ctrl->coreset, ul_bwp->pucch_Config, CCEIndex);
alloc = nr_acknack_scheduling(nr_mac, UE, frame, slot, UE->UE_beam_index, r_pucch, 0);
if (alloc >= 0 && nr_pdsch_num_codewords(new_sched.nrOfLayers) == 2) {
const int second_alloc = nr_acknack_scheduling(nr_mac, UE, frame, slot, UE->UE_beam_index, r_pucch, 0);
if (second_alloc != alloc)
alloc = -1;
}
if (alloc < 0) {
LOG_D(NR_MAC, "[UE %04x][%4d.%2d] could not find PUCCH for DL DCI retransmission\n", UE->rnti, frame, slot);
return false;
@@ -565,7 +618,6 @@ static bool allocate_dl_retransmission(gNB_MAC_INST *nr_mac,
static void ack_reconfig(gNB_MAC_INST *mac, NR_UE_info_t *UE)
{
if (!UE->reconfigCellGroup) {
LOG_W(NR_MAC, "Received ACK for RRCReconfiguration, but nothing to apply!\n");
return;
}
ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->CellGroup);
@@ -700,7 +752,7 @@ static void pf_dl(gNB_MAC_INST *mac,
sched_ctrl->dl_bler_stats.mcs = selected_mcs;
} else
selected_mcs = get_mcs_from_bler(bo, stats, &sched_ctrl->dl_bler_stats, max_mcs, frame);
int l = get_dl_nrOfLayers(sched_ctrl, current_BWP->dci_format);
int l = get_dl_nrOfLayers(mac, sched_ctrl, current_BWP->dci_format);
const uint8_t Qm = nr_get_Qm_dl(selected_mcs, current_BWP->mcsTableIdx);
const uint16_t R = nr_get_code_rate_dl(selected_mcs, current_BWP->mcsTableIdx);
uint32_t tbs = nr_compute_tbs(Qm,
@@ -822,10 +874,17 @@ static void pf_dl(gNB_MAC_INST *mac,
/* Find PUCCH occasion: if it fails, undo CCE allocation (undoing PUCCH
* allocation after CCE alloc fail would be more complex) */
const int l = get_dl_nrOfLayers(mac, sched_ctrl, dl_bwp->dci_format);
int alloc = -1;
if (!get_FeedbackDisabled(iterator->UE->sc_info.downlinkHARQ_FeedbackDisabled_r17, sched_ctrl->available_dl_harq.head)) {
int r_pucch = nr_get_pucch_resource(sched_ctrl->coreset, ul_bwp->pucch_Config, CCEIndex);
alloc = nr_acknack_scheduling(mac, iterator->UE, frame, slot, iterator->UE->UE_beam_index, r_pucch, 0);
if (alloc >= 0 && nr_pdsch_num_codewords(l) == 2) {
const int second_alloc =
nr_acknack_scheduling(mac, iterator->UE, frame, slot, iterator->UE->UE_beam_index, r_pucch, 0);
if (second_alloc != alloc)
alloc = -1;
}
if (alloc < 0) {
LOG_D(NR_MAC, "[UE %04x][%4d.%2d] could not find PUCCH for DL DCI\n", rnti, frame, slot);
reset_beam_status(&mac->beam_info, frame, slot, iterator->UE->UE_beam_index, slots_per_frame, beam.new_beam);
@@ -837,7 +896,6 @@ static void pf_dl(gNB_MAC_INST *mac,
sched_ctrl->cce_index = CCEIndex;
fill_pdcch_vrb_map(mac, CC_id, &sched_ctrl->sched_pdcch, CCEIndex, sched_ctrl->aggregation_level, beam.idx);
int l = get_dl_nrOfLayers(sched_ctrl, dl_bwp->dci_format);
NR_sched_pdsch_t sched_pdsch = {
// rbSize below
.rbStart = rbStart,
@@ -877,6 +935,7 @@ static void pf_dl(gNB_MAC_INST *mac,
max_rbSize,
&sched_pdsch.tb_size,
&sched_pdsch.rbSize);
fit_dual_codeword_tbs(&sched_pdsch, sched_ctrl->num_total_bytes + oh, max_rbSize);
post_process_dlsch(mac, pp_pdsch, iterator->UE, &sched_pdsch);
@@ -943,13 +1002,15 @@ nfapi_nr_dl_tti_pdsch_pdu_rel15_t *prepare_pdsch_pdu(nfapi_nr_dl_tti_request_pdu
pdsch_pdu->SubcarrierSpacing = dl_bwp ? dl_bwp->scs : *scc->ssbSubcarrierSpacing;
pdsch_pdu->CyclicPrefix = dl_bwp && dl_bwp->cyclicprefix ? *dl_bwp->cyclicprefix : 0;
// Codeword information
pdsch_pdu->NrOfCodewords = 1;
pdsch_pdu->targetCodeRate[0] = sched_pdsch->R;
pdsch_pdu->qamModOrder[0] = sched_pdsch->Qm;
pdsch_pdu->mcsIndex[0] = sched_pdsch->mcs;
pdsch_pdu->mcsTable[0] = dl_bwp ? dl_bwp->mcsTableIdx : 0;
pdsch_pdu->rvIndex[0] = nr_get_rv(harq_round % 4);
pdsch_pdu->TBSize[0] = sched_pdsch->tb_size;
pdsch_pdu->NrOfCodewords = nr_pdsch_num_codewords(sched_pdsch->nrOfLayers);
for (int q = 0; q < pdsch_pdu->NrOfCodewords; q++) {
pdsch_pdu->targetCodeRate[q] = sched_pdsch->R;
pdsch_pdu->qamModOrder[q] = sched_pdsch->Qm;
pdsch_pdu->mcsIndex[q] = sched_pdsch->mcs;
pdsch_pdu->mcsTable[q] = dl_bwp ? dl_bwp->mcsTableIdx : 0;
pdsch_pdu->rvIndex[q] = nr_get_rv(harq_round % 4);
pdsch_pdu->TBSize[q] = q == 0 ? sched_pdsch->tb_size : sched_pdsch->tb_size2;
}
pdsch_pdu->dataScramblingId = pdsch_Config && pdsch_Config->dataScramblingIdentityPDSCH ? *pdsch_Config->dataScramblingIdentityPDSCH : *scc->physCellId;
pdsch_pdu->nrOfLayers = sched_pdsch->nrOfLayers;
pdsch_pdu->transmissionScheme = 0;
@@ -961,7 +1022,7 @@ nfapi_nr_dl_tti_pdsch_pdu_rel15_t *prepare_pdsch_pdu(nfapi_nr_dl_tti_request_pdu
pdsch_pdu->SCID = dmrs_parms->n_scid;
pdsch_pdu->dlDmrsScramblingId = dmrs_parms->scrambling_id;
pdsch_pdu->numDmrsCdmGrpsNoData = dmrs_parms->numDmrsCdmGrpsNoData;
pdsch_pdu->dmrsPorts = (1 << sched_pdsch->nrOfLayers) - 1; // FIXME with a better implementation
pdsch_pdu->dmrsPorts = dmrs_parms->dmrs_ports;
// Pdsch Allocation in frequency domain
pdsch_pdu->resourceAlloc = 1;
pdsch_pdu->rbStart = sched_pdsch->rbStart;
@@ -998,6 +1059,98 @@ nfapi_nr_dl_tti_pdsch_pdu_rel15_t *prepare_pdsch_pdu(nfapi_nr_dl_tti_request_pdu
return pdsch_pdu;
}
typedef struct {
int sdus;
int sdu_bytes;
} dlsch_fill_result_t;
static dlsch_fill_result_t fill_dlsch_transport_block(gNB_MAC_INST *mac,
NR_UE_info_t *UE,
uint8_t *tb,
uint32_t tbs,
frame_t frame,
slot_t slot,
bool include_ces)
{
const int module_id = mac->Mod_id;
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
dlsch_fill_result_t result = {0};
uint8_t *buf = tb;
if (include_ces)
buf += nr_write_ce_dlsch_pdu(module_id, sched_ctrl, buf, 255, NULL);
DevAssert(buf < tb + tbs);
uint8_t *buf_end = tb + tbs;
start_meas(&mac->rlc_data_req);
if (sched_ctrl->num_total_bytes > 0) {
for (int i = 0; i < seq_arr_size(&sched_ctrl->lc_config); ++i) {
const nr_lc_config_t *c = seq_arr_at(&sched_ctrl->lc_config, i);
const int lcid = c->lcid;
if (sched_ctrl->rlc_status[lcid].bytes_in_buffer == 0)
continue;
tb_size_t pdu_siz[MAX_NUM_DATA_REQ];
int num = nr_mac_rlc_multi_data_req(module_id,
UE->rnti,
true,
lcid,
buf_end - buf,
(char *)buf,
pdu_siz,
sizeofArray(pdu_siz));
DevAssert(num <= sizeofArray(pdu_siz));
result.sdus += num;
int lcid_bytes = 0;
for (int j = 0; j < num; ++j) {
NR_MAC_SUBHEADER_LONG *header = (NR_MAC_SUBHEADER_LONG *)buf;
header->R = 0;
header->F = 1;
header->LCID = lcid;
header->L = htons(pdu_siz[j]);
buf += pdu_siz[j] + sizeof(NR_MAC_SUBHEADER_LONG);
result.sdu_bytes += pdu_siz[j];
lcid_bytes += pdu_siz[j];
}
T(T_GNB_MAC_LCID_DL,
T_INT(UE->rnti),
T_INT(frame),
T_INT(slot),
T_INT(lcid),
T_INT(lcid_bytes),
T_INT(nr_rlc_tx_list_occupancy(UE->rnti, lcid)));
UE->mac_stats.dl.lc_bytes[lcid] += lcid_bytes;
}
} else if (get_softmodem_params()->phy_test || get_softmodem_params()->do_ra) {
LOG_D(NR_MAC, "Configuring DL_TX in %d.%d: TBS %d of random data\n", frame, slot, tbs);
if (buf_end - buf > sizeof(NR_MAC_SUBHEADER_LONG)) {
NR_MAC_SUBHEADER_LONG *header = (NR_MAC_SUBHEADER_LONG *)buf;
header->R = 0;
header->F = 1;
header->LCID = DL_SCH_LCID_PADDING;
buf += sizeof(NR_MAC_SUBHEADER_LONG);
header->L = htons(buf_end - buf);
for (; ((intptr_t)buf) % 4; buf++)
*buf = lrand48() & 0xff;
for (; buf < buf_end - 3; buf += 4)
*(uint32_t *)buf = lrand48();
for (; buf < buf_end; buf++)
*buf = lrand48() & 0xff;
result.sdus++;
}
}
stop_meas(&mac->rlc_data_req);
if (buf_end - buf > 0) {
NR_MAC_SUBHEADER_FIXED *padding = (NR_MAC_SUBHEADER_FIXED *)buf;
padding->R = 0;
padding->LCID = DL_SCH_LCID_PADDING;
buf++;
memset(buf, 0, buf_end - buf);
}
return result;
}
void post_process_dlsch(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pdsch, NR_UE_info_t *UE, NR_sched_pdsch_t *sched_pdsch)
{
int CC_id = 0;
@@ -1014,6 +1167,7 @@ void post_process_dlsch(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pdsch, NR_UE
/* POST processing */
const uint8_t nrOfLayers = sched_pdsch->nrOfLayers;
const uint32_t TBS = sched_pdsch->tb_size;
const uint32_t TBS2 = sched_pdsch->tb_size2;
int8_t current_harq_pid = sched_pdsch->dl_harq_pid;
if (current_harq_pid < 0) {
@@ -1208,11 +1362,15 @@ void post_process_dlsch(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pdsch, NR_UE
harq->round,
harq->ndi);
if (!get_softmodem_params()->phy_test)
AssertFatal(harq->sched_pdsch.tb_size == TBS,
"UE %04x mismatch between scheduled TBS and buffered TB for HARQ PID %d\n",
AssertFatal(harq->sched_pdsch.tb_size == TBS && harq->sched_pdsch.tb_size2 == TBS2,
"UE %04x mismatch between scheduled TBS (%u,%u) and buffered TB (%u,%u) for HARQ PID %d\n",
UE->rnti,
TBS,
TBS2,
harq->sched_pdsch.tb_size,
harq->sched_pdsch.tb_size2,
current_harq_pid);
else if (harq->sched_pdsch.tb_size != TBS)
else if (harq->sched_pdsch.tb_size != TBS || harq->sched_pdsch.tb_size2 != TBS2)
LOG_E(NR_MAC,
"Mismatch between scheduled TBS and buffered TB for HARQ PID %d. No RTX control in phy-test mode. "
"Possible causes: presence of CSI-RS or DLSCH scheduled in the mixed slot.\n",
@@ -1225,94 +1383,24 @@ void post_process_dlsch(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pdsch, NR_UE
LOG_D(NR_MAC, "Initial HARQ transmission in %d.%d\n", frame, slot);
// Flag HARQ process to start TCI timer at ACK
harq->start_tci_timer = sched_ctrl->UE_mac_ce_ctrl.tci_state_ind.is_scheduled;
uint8_t *buf = allocate_transportBlock_buffer(&harq->transportBlock, TBS);
/* first, write all CEs that might be there */
int written = nr_write_ce_dlsch_pdu(module_id,
sched_ctrl,
(unsigned char *)buf,
255, // no drx
NULL); // contention res id
buf += written;
uint8_t *bufEnd = buf + TBS - written;
DevAssert(TBS > written);
int dlsch_total_bytes = 0;
/* next, get RLC data */
start_meas(&nr_mac->rlc_data_req);
int sdus = 0;
if (sched_ctrl->num_total_bytes > 0) {
/* loop over all activated logical channels */
for (int i = 0; i < seq_arr_size(&sched_ctrl->lc_config); ++i) {
const nr_lc_config_t *c = seq_arr_at(&sched_ctrl->lc_config, i);
const int lcid = c->lcid;
if (sched_ctrl->rlc_status[lcid].bytes_in_buffer == 0)
continue; // no data for this LC tbs_size_t len = 0;
tb_size_t pdu_siz[MAX_NUM_DATA_REQ];
int num = nr_mac_rlc_multi_data_req(module_id, rnti, true, lcid, bufEnd - buf, (char *)buf, pdu_siz, sizeofArray(pdu_siz));
DevAssert(num <= sizeofArray(pdu_siz));
sdus += num;
int lcid_bytes = 0;
for (int j = 0; j < num; ++j) {
NR_MAC_SUBHEADER_LONG *header = (NR_MAC_SUBHEADER_LONG *) buf;
header->R = 0;
header->F = 1;
header->LCID = lcid;
header->L = htons(pdu_siz[j]);
buf += pdu_siz[j] + sizeof(NR_MAC_SUBHEADER_LONG);
dlsch_total_bytes += pdu_siz[j];
lcid_bytes += pdu_siz[j];
}
T(T_GNB_MAC_LCID_DL, T_INT(rnti), T_INT(frame), T_INT(slot), T_INT(lcid), T_INT(lcid_bytes), T_INT(nr_rlc_tx_list_occupancy(rnti, lcid)));
UE->mac_stats.dl.lc_bytes[lcid] += lcid_bytes;
}
} else if (get_softmodem_params()->phy_test || get_softmodem_params()->do_ra) {
/* we will need the large header, phy-test typically allocates all
* resources and fills to the last byte below */
LOG_D(NR_MAC, "Configuring DL_TX in %d.%d: TBS %d of random data\n", frame, slot, TBS);
if (bufEnd-buf > sizeof(NR_MAC_SUBHEADER_LONG) ) {
NR_MAC_SUBHEADER_LONG *header = (NR_MAC_SUBHEADER_LONG *) buf;
// fill dlsch_buffer with random data
header->R = 0;
header->F = 1;
header->LCID = DL_SCH_LCID_PADDING;
buf += sizeof(NR_MAC_SUBHEADER_LONG);
header->L = htons(bufEnd-buf);
for (; ((intptr_t)buf) % 4; buf++)
*buf = lrand48() & 0xff;
for (; buf < bufEnd - 3; buf += 4) {
uint32_t *buf32 = (uint32_t *)buf;
*buf32 = lrand48();
}
for (; buf < bufEnd; buf++)
*buf = lrand48() & 0xff;
sdus +=1;
}
for (int q = 0; q < nr_pdsch_num_codewords(nrOfLayers); q++) {
const uint32_t cw_tbs = q == 0 ? TBS : sched_pdsch->tb_size2;
byte_array_t *transport_block = q == 0 ? &harq->transportBlock : &harq->transportBlock2;
uint8_t *buf = allocate_transportBlock_buffer(transport_block, cw_tbs);
dlsch_fill_result_t fill = fill_dlsch_transport_block(nr_mac, UE, buf, cw_tbs, frame, slot, q == 0);
sdus += fill.sdus;
dlsch_total_bytes += fill.sdu_bytes;
}
stop_meas(&nr_mac->rlc_data_req);
// Add padding header and zero rest out if there is space left
if (bufEnd-buf > 0) {
NR_MAC_SUBHEADER_FIXED *padding = (NR_MAC_SUBHEADER_FIXED *) buf;
padding->R = 0;
padding->LCID = DL_SCH_LCID_PADDING;
buf += 1;
memset(buf,0,bufEnd-buf);
buf=bufEnd;
}
UE->mac_stats.dl.total_bytes += TBS;
UE->mac_stats.dl.current_bytes = TBS;
const uint32_t total_tbs = TBS + sched_pdsch->tb_size2;
UE->mac_stats.dl.total_bytes += total_tbs;
UE->mac_stats.dl.current_bytes = total_tbs;
UE->mac_stats.dl.total_rbs += sched_pdsch->rbSize;
UE->mac_stats.dl.num_mac_sdu += sdus;
UE->mac_stats.dl.current_rbs = sched_pdsch->rbSize;
UE->mac_stats.dl.last_layers = sched_pdsch->nrOfLayers;
UE->mac_stats.dl.total_sdu_bytes += dlsch_total_bytes;
nr_mac->mac_stats.dl.used_prb_aggregate += sched_pdsch->rbSize;
@@ -1349,10 +1437,16 @@ void post_process_dlsch(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pdsch, NR_UE
const int ntx_req = pdsch->TX_req->Number_of_PDUs;
nfapi_nr_pdu_t *tx_req = &pdsch->TX_req->pdu_list[ntx_req];
tx_req->PDU_index = pduindex;
tx_req->num_TLV = 1;
tx_req->TLVs[0].length = TBS;
tx_req->PDU_length = compute_PDU_length(tx_req->num_TLV, tx_req->TLVs[0].length);
memcpy(tx_req->TLVs[0].value.direct, harq->transportBlock.buf, TBS);
tx_req->num_TLV = nr_pdsch_num_codewords(nrOfLayers);
uint32_t total_tlv_length = 0;
for (int q = 0; q < tx_req->num_TLV; q++) {
const uint32_t cw_tbs = q == 0 ? TBS : TBS2;
const byte_array_t *transport_block = q == 0 ? &harq->transportBlock : &harq->transportBlock2;
tx_req->TLVs[q].length = cw_tbs;
memcpy(tx_req->TLVs[q].value.direct, transport_block->buf, cw_tbs);
total_tlv_length += cw_tbs;
}
tx_req->PDU_length = compute_PDU_length(tx_req->num_TLV, total_tlv_length);
pdsch->TX_req->Number_of_PDUs++;
pdsch->TX_req->SFN = frame;
pdsch->TX_req->Slot = slot;
@@ -166,15 +166,7 @@ void nr_preprocessor_phytest(gNB_MAC_INST *mac, post_process_pdsch_t *pp_pdsch)
.tda_info = tda_info,
};
sched_ctrl->dl_bler_stats.mcs = target_dl_mcs; /* for logging output */
sched_pdsch.tb_size = nr_compute_tbs(sched_pdsch.Qm,
sched_pdsch.R,
sched_pdsch.rbSize,
tda_info.nrOfSymbols,
sched_pdsch.dmrs_parms.N_PRB_DMRS * sched_pdsch.dmrs_parms.N_DMRS_SLOT,
0 /* N_PRB_oh, 0 for initialBWP */,
0 /* tb_scaling */,
target_dl_Nl)
>> 3;
update_sched_pdsch_codeword_tbs(&sched_pdsch);
post_process_dlsch(mac, pp_pdsch, UE, &sched_pdsch);
@@ -101,14 +101,13 @@ int get_ssbidx_from_beam(gNB_MAC_INST *mac, int beam_idx)
return 0;
}
uint8_t get_dl_nrOfLayers(const NR_UE_sched_ctrl_t *sched_ctrl, const nr_dci_format_t dci_format)
uint8_t get_dl_nrOfLayers(const gNB_MAC_INST *mac, const NR_UE_sched_ctrl_t *sched_ctrl, const nr_dci_format_t dci_format)
{
// TODO check this but it should be enough for now
// if there is not csi report activated RI is 0 from initialization
if(dci_format == NR_DL_DCI_FORMAT_1_0)
return 1;
else
return sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.ri + 1;
int layers = (dci_format == NR_DL_DCI_FORMAT_1_0) ? 1
: sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.ri + 1;
if (mac && mac->antenna_state)
return nr_get_effective_mimo_layers(mac->antenna_state, layers);
return layers;
}
int get_ul_nrOfLayers(const NR_UE_sched_ctrl_t *sched_ctrl, const nr_dci_format_t dci_format)
@@ -165,63 +164,88 @@ uint16_t get_pm_index(const gNB_MAC_INST *nrmac,
int layers,
int xp_pdsch_antenna_ports)
{
if (dci_format == NR_DL_DCI_FORMAT_1_0 || nrmac->identity_pm || xp_pdsch_antenna_ports == 1)
return 0; //identity matrix (basic 5G configuration handled by PMI report is with XP antennas)
const NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
const int report_id = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.csi_report_id;
const nr_csi_report_t *csi_report = &UE->csi_report_template[report_id];
const int N1 = csi_report->N1;
const int N2 = csi_report->N2;
const int antenna_ports = (N1 * N2) << 1;
if (antenna_ports < 2)
return 0; // single antenna port
if (dci_format == NR_DL_DCI_FORMAT_1_0 || nrmac->identity_pm || xp_pdsch_antenna_ports == 1 || layers > 4)
return 0; //identity matrix (basic 5G configuration handled by PMI report is with XP antennas)
const NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
int x1 = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.pmi_x1;
const int x2 = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.pmi_x2;
LOG_D(NR_MAC,"PMI report: x1 %d x2 %d layers: %d\n", x1, x2, layers);
int prev_layers_size = 0;
for (int i = 1; i < layers; i++)
prev_layers_size += nrmac->precoding_matrix_size[i - 1];
// need to return PM index to matrix initialized in init_DL_MIMO_codebook
// index 0 is for identity matrix
// order of matrices depends on layers to be transmitted
// elements from 1 to n for 1 layer
// elements from n+1 to m for 2 layers etc.
if (antenna_ports == 2)
return 1 + prev_layers_size + x2; // 0 for identity matrix
else {
int idx = layers - 1;
// the order of i1x in X1 report needs to be verified
// the standard is not very clear (Table 6.3.1.1.2-7 in 38.212)
// it says: PMI wideband information fields X1 , from left to right
int bitlen = csi_report->csi_meas_bitlen.pmi_i13_bitlen[idx];
if (layers == 1 && bitlen != 0) {
LOG_E(NR_MAC, "Invalid i13 bit length %d for single layer! It should be 0\n", bitlen);
return 0;
int reported_ri_layers = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.ri + 1;
if (layers != reported_ri_layers) {
LOG_D(NR_MAC, "PMI mismatch: using %d layers but RI report is for %d layers, using identity matrix\n",
layers, reported_ri_layers);
return 0;
}
const int i13 = x1 & ((1 << bitlen) - 1);
x1 >>= bitlen;
bitlen = csi_report->csi_meas_bitlen.pmi_i12_bitlen[idx];
const int i12 = x1 & ((1 << bitlen) - 1);
x1 >>= bitlen;
bitlen = csi_report->csi_meas_bitlen.pmi_i11_bitlen[idx];
const int i11 = x1 & ((1 << bitlen) - 1);
const int i2 = x2;
int k1, k2;
get_k1_k2_indices(layers, N1, N2, i13, &k1, &k2); // get indices k1 and k2 for PHY matrix (not actual k1 and k2 values)
const int O2 = N2 == 1 ? 1 : 4;
int O1 = 4; //Horizontal beam oversampling = 4 for more than 2 antenna ports
int max_i2 = 0;
int lay_index = 0;
max_i2 = layers == 1 ? 4 : 2;
int K1, K2;
get_K1_K2(N1, N2, &K1, &K2, layers);
// computing precoding matrix index according to rule set in allocation function init_codebook_gNB
lay_index = i2 + (i11 * max_i2) + (i12 * max_i2 * N1 * O1) + (k1 * max_i2 * N1 * O1 * N2 * O2) + (k2 * max_i2 * N1 * O1 * N2 * O2 * K1);
return 1 + prev_layers_size + lay_index;
}
nr_antenna_state_t* ant_state = nrmac->antenna_state;
if (ant_state) {
if (nr_is_in_recovery_mode(ant_state)) {
LOG_D(NR_MAC, "[ANTENNA_MUTE] Recovery mode active, using identity matrix\n");
return 0;
}
// Don't fall back to identity matrix for muted antennas.
// Antenna muting is handled at RU level (nr_feptx zeros muted antenna buffers).
// Using proper precoding ensures data reaches all active antennas.
}
const int report_id = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.csi_report_id;
const nr_csi_report_t *csi_report = &UE->csi_report_template[report_id];
const int N1 = csi_report->N1;
const int N2 = csi_report->N2;
const int antenna_ports = (N1 * N2) << 1;
if (antenna_ports < 2) return 0; // single antenna port
int x1 = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.pmi_x1;
const int x2 = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.pmi_x2;
LOG_D(NR_MAC,"PMI report: x1 %d x2 %d layers: %d\n", x1, x2, layers);
int prev_layers_size = 0;
for (int i = 1; i < layers; i++)
prev_layers_size += nrmac->precoding_matrix_size[i - 1];
// need to return PM index to matrix initialized in init_DL_MIMO_codebook
// index 0 is for identity matrix
// order of matrices depends on layers to be transmitted
// elements from 1 to n for 1 layer
// elements from n+1 to m for 2 layers etc.
int pm_index;
if (antenna_ports == 2)
pm_index = 1 + prev_layers_size + x2; // 0 for identity matrix
else {
int idx = layers - 1;
// the order of i1x in X1 report needs to be verified
// the standard is not very clear (Table 6.3.1.1.2-7 in 38.212)
// it says: PMI wideband information fields X1 , from left to right
int bitlen = csi_report->csi_meas_bitlen.pmi_i13_bitlen[idx];
if (layers == 1 && bitlen != 0) {
LOG_E(NR_MAC, "Invalid i13 bit length %d for single layer! It should be 0\n", bitlen);
return 0;
}
const int i13 = x1 & ((1 << bitlen) - 1);
x1 >>= bitlen;
bitlen = csi_report->csi_meas_bitlen.pmi_i12_bitlen[idx];
const int i12 = x1 & ((1 << bitlen) - 1);
x1 >>= bitlen;
bitlen = csi_report->csi_meas_bitlen.pmi_i11_bitlen[idx];
const int i11 = x1 & ((1 << bitlen) - 1);
const int i2 = x2;
int k1, k2;
get_k1_k2_indices(layers, N1, N2, i13, &k1, &k2); // get indices k1 and k2 for PHY matrix (not actual k1 and k2 values)
const int O2 = N2 == 1 ? 1 : 4;
int O1 = 4; //Horizontal beam oversampling = 4 for more than 2 antenna ports
int max_i2 = 0;
int lay_index = 0;
max_i2 = layers == 1 ? 4 : 2;
int K1, K2;
get_K1_K2(N1, N2, &K1, &K2, layers);
// computing precoding matrix index according to rule set in allocation function init_codebook_gNB
lay_index = i2 + (i11 * max_i2) + (i12 * max_i2 * N1 * O1) + (k1 * max_i2 * N1 * O1 * N2 * O2) + (k2 * max_i2 * N1 * O1 * N2 * O2 * K1);
pm_index = 1 + prev_layers_size + lay_index;
}
int max_valid_pm = prev_layers_size + nrmac->precoding_matrix_size[layers - 1];
if (pm_index < 1 || pm_index > max_valid_pm) {
LOG_W(NR_MAC, "PM index %d out of range [1, %d] for %d layers, using identity matrix\n",
pm_index, max_valid_pm, layers);
return 0;
}
return pm_index;
}
// look-up table for AMC. Based on BLER vs SNR curves from the nr_dlsim simulation
@@ -304,6 +328,11 @@ NR_pdsch_dmrs_t get_dl_dmrs_params(const NR_ServingCellConfigCommon_t *scc,
{
NR_pdsch_dmrs_t dmrs = {0};
int frontloaded_symb = 1; // default value
NR_PDSCH_Config_t *pdsch_Config = dl_bwp ? dl_bwp->pdsch_Config : NULL;
NR_DMRS_DownlinkConfig_t *dmrs_Config = NULL;
if (pdsch_Config)
dmrs_Config = tda_info->mapping_type == typeB ? pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeB->choice.setup
: pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup;
nr_dci_format_t dci_format = dl_bwp ? dl_bwp->dci_format : NR_DL_DCI_FORMAT_1_0;
if (dci_format == NR_DL_DCI_FORMAT_1_0) {
dmrs.numDmrsCdmGrpsNoData = tda_info->nrOfSymbols <= 2 ? 1 : 2;
@@ -338,19 +367,29 @@ NR_pdsch_dmrs_t get_dl_dmrs_params(const NR_ServingCellConfigCommon_t *scc,
dmrs.numDmrsCdmGrpsNoData = 2;
frontloaded_symb = 1;
break;
case 5:
case 6:
case 7:
case 8:
AssertFatal(dmrs_Config && dmrs_Config->dmrs_Type == NULL && dmrs_Config->maxLength,
"Rank %d PDSCH requires DMRS type 1 with maxLength len2\n",
Layers);
/* TS 38.212 Table 7.3.1.2.2-2, two-codeword rows 0--3. */
dmrs.dmrs_ports_id = Layers - 5;
dmrs.numDmrsCdmGrpsNoData = 2;
frontloaded_symb = 2;
break;
default:
AssertFatal(1==0,"Number of layers %d\n not supported or not valid\n",Layers);
}
}
dmrs.n_scid = 0;
static const uint16_t two_cw_dmrs_ports[] = {0x1f, 0x5f, 0x7f, 0xff};
dmrs.dmrs_ports = Layers > 4 ? two_cw_dmrs_ports[Layers - 5] : (1U << Layers) - 1;
long *scramblingID = NULL;
NR_PDSCH_Config_t *pdsch_Config = dl_bwp ? dl_bwp->pdsch_Config : NULL;
if (pdsch_Config) {
NR_DMRS_DownlinkConfig_t *dmrs_Config = tda_info->mapping_type == typeB ?
pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeB->choice.setup :
pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup;
dmrs.phaseTrackingRS = dmrs_Config->phaseTrackingRS ? dmrs_Config->phaseTrackingRS->choice.setup : NULL;
dmrs.phaseTrackingRS = Layers > 4 ? NULL : (dmrs_Config->phaseTrackingRS ? dmrs_Config->phaseTrackingRS->choice.setup : NULL);
dmrs.dmrsConfigType = dmrs_Config->dmrs_Type != NULL;
scramblingID = dmrs.n_scid ? dmrs_Config->scramblingID1 : dmrs_Config->scramblingID0;
} else {
@@ -930,6 +969,11 @@ dci_pdu_rel15_t prepare_dci_dl_payload(const gNB_MAC_INST *gNB_mac,
AssertFatal(harq, "HARQ process should be available for DCI with RNTI %s\n", rnti_types(rnti_type));
dci_payload.harq_pid.val = harq_pid;
dci_payload.ndi = harq->ndi;
if (pdsch_pdu->NrOfCodewords == 2) {
dci_payload.mcs2.val = sched_pdsch->mcs;
dci_payload.ndi2.val = harq->ndi;
dci_payload.rv2.val = pdsch_pdu->rvIndex[1];
}
dci_payload.pucch_resource_indicator = pucch ? pucch->resource_indicator : 0;
dci_payload.pdsch_to_harq_feedback_timing_indicator.val = pucch ? pucch->timing_indicator : 0; // PDSCH to HARQ TI
dci_payload.dai[0].val = pucch ? (pucch->dai_c - 1) & 3 : 0;
@@ -2518,8 +2562,10 @@ void delete_nr_ue_data(NR_UE_info_t *UE, uid_allocator_t *uia)
destroy_nr_list(&sched_ctrl->available_ul_harq);
destroy_nr_list(&sched_ctrl->feedback_ul_harq);
destroy_nr_list(&sched_ctrl->retrans_ul_harq);
for (int i = 0; i < NR_MAX_HARQ_PROCESSES; ++i)
for (int i = 0; i < NR_MAX_HARQ_PROCESSES; ++i) {
free_transportBlock_buffer(&sched_ctrl->harq_processes[i].transportBlock);
free_transportBlock_buffer(&sched_ctrl->harq_processes[i].transportBlock2);
}
free_sched_pucch_list(sched_ctrl);
uid_linear_allocator_free(uia, UE->uid);
LOG_I(NR_MAC, "Remove NR rnti 0x%04x\n", UE->rnti);
@@ -3614,9 +3660,16 @@ void nr_mac_update_timers(module_id_t module_id)
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
if (nr_mac_check_release(sched_ctrl)) {
// trigger release first as nr_mac_release_ue() invalidates UE ptr
nr_mac_trigger_release_complete(mac, UE->rnti);
nr_mac_release_ue(mac, UE->rnti);
// nakaolab UE release race fix:
// Save rnti before nr_mac_trigger_release_complete — that call drops
// the scheduler lock briefly (to send F1AP UE Context Release Complete),
// and another thread (e.g., RRC DL handler) can free this UE during
// that window, leaving UE->rnti pointing at freed memory.
rnti_t rnti = UE->rnti;
nr_mac_trigger_release_complete(mac, rnti);
// Re-validate after lock re-acquired: skip release if already gone.
if (find_nr_UE(&mac->UE_info, rnti) != NULL)
nr_mac_release_ue(mac, rnti);
// go back to examine the next UE, which is at the position the
// current UE was
UE--;
+69 -13
View File
@@ -497,7 +497,9 @@ static void evaluate_sinr_report(NR_UE_info_t *UE,
stats->num_sinr_meas++;
const int mcs_table = UE->current_DL_BWP.mcsTableIdx;
const int nrOfLayers = get_dl_nrOfLayers(sched_ctrl, UE->current_DL_BWP.dci_format);
/* NULL mac arg: SINR-based MCS estimation skips antenna-state rank cap
(caller doesn't have nrmac in scope; this is only for max-MCS hint). */
const int nrOfLayers = get_dl_nrOfLayers(NULL, sched_ctrl, UE->current_DL_BWP.dci_format);
sched_ctrl->dl_max_mcs = get_mcs_from_SINRx10(mcs_table, sinr_report->r[0].SINRx10, nrOfLayers);
LOG_D(MAC, "Reported SSB-SINR = %01f, dl_max_mcs %d\n", sinr_report->r[0].SINRx10 / 10.0, sched_ctrl->dl_max_mcs);
@@ -716,6 +718,7 @@ static void extract_pucch_csi_report(NR_CSI_MeasConfig_t *csi_MeasConfig,
int cumul_bits = 0;
int r_index = -1;
int new_bf_index = -1;
bool link_csi_decoded = false;
for (int csi_report_id = 0; csi_report_id < csi_MeasConfig->csi_ReportConfigToAddModList->list.count; csi_report_id++) {
nr_csi_report_t *csi_report = &UE->csi_report_template[csi_report_id];
csi_report->nb_of_csi_ssb_report = 0;
@@ -761,12 +764,25 @@ static void extract_pucch_csi_report(NR_CSI_MeasConfig_t *csi_MeasConfig,
evaluate_cri_report(payload, cri_bitlen, cumul_bits, sched_ctrl);
cumul_bits += cri_bitlen;
ri_bitlen = csi_report->csi_meas_bitlen.ri_bitlen;
if (ri_bitlen)
if (ri_bitlen) {
r_index = evaluate_ri_report(payload, ri_bitlen, csi_report->csi_meas_bitlen.ri_restriction, cumul_bits, sched_ctrl);
} else {
/* nakaolab: ri_bitlen=0 (single RI allowed via ri_restriction).
* Pick that RI directly — without this, r_index stays -1 and trips
* the DevAssert(ri>=0) in evaluate_cqi_report. */
for (int i = 0; i < 8; i++) {
if ((csi_report->csi_meas_bitlen.ri_restriction >> i) & 0x01) {
r_index = i;
sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.ri = i;
break;
}
}
}
cumul_bits += ri_bitlen;
if (r_index != -1)
skip_zero_padding(&cumul_bits, csi_report, r_index, bitlen);
evaluate_cqi_report(payload, csi_report, cumul_bits, r_index, UE, cqi_table);
link_csi_decoded |= r_index >= 0;
break;
case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_PMI_CQI:
sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.print_report = true;
@@ -775,8 +791,20 @@ static void extract_pucch_csi_report(NR_CSI_MeasConfig_t *csi_MeasConfig,
evaluate_cri_report(payload, cri_bitlen, cumul_bits, sched_ctrl);
cumul_bits += cri_bitlen;
ri_bitlen = csi_report->csi_meas_bitlen.ri_bitlen;
if (ri_bitlen)
if (ri_bitlen) {
r_index = evaluate_ri_report(payload, ri_bitlen, csi_report->csi_meas_bitlen.ri_restriction, cumul_bits, sched_ctrl);
} else {
/* nakaolab: ri_bitlen=0 (single RI allowed via ri_restriction).
* Pick that RI directly — without this, r_index stays -1 and trips
* the DevAssert(ri>=0) in evaluate_cqi_report. */
for (int i = 0; i < 8; i++) {
if ((csi_report->csi_meas_bitlen.ri_restriction >> i) & 0x01) {
r_index = i;
sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.ri = i;
break;
}
}
}
cumul_bits += ri_bitlen;
if (r_index != -1)
skip_zero_padding(&cumul_bits, csi_report, r_index, bitlen);
@@ -784,6 +812,7 @@ static void extract_pucch_csi_report(NR_CSI_MeasConfig_t *csi_MeasConfig,
sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.csi_report_id = csi_report_id;
cumul_bits += pmi_bitlen;
evaluate_cqi_report(payload, csi_report, cumul_bits, r_index, UE, cqi_table);
link_csi_decoded |= r_index >= 0;
break;
case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_LI_PMI_CQI:
sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.print_report = true;
@@ -792,8 +821,20 @@ static void extract_pucch_csi_report(NR_CSI_MeasConfig_t *csi_MeasConfig,
evaluate_cri_report(payload, cri_bitlen, cumul_bits, sched_ctrl);
cumul_bits += cri_bitlen;
ri_bitlen = csi_report->csi_meas_bitlen.ri_bitlen;
if (ri_bitlen)
if (ri_bitlen) {
r_index = evaluate_ri_report(payload, ri_bitlen, csi_report->csi_meas_bitlen.ri_restriction, cumul_bits, sched_ctrl);
} else {
/* nakaolab: ri_bitlen=0 (single RI allowed via ri_restriction).
* Pick that RI directly — without this, r_index stays -1 and trips
* the DevAssert(ri>=0) in evaluate_cqi_report. */
for (int i = 0; i < 8; i++) {
if ((csi_report->csi_meas_bitlen.ri_restriction >> i) & 0x01) {
r_index = i;
sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.ri = i;
break;
}
}
}
cumul_bits += ri_bitlen;
li_bitlen = evaluate_li_report(payload, csi_report, cumul_bits, r_index, sched_ctrl);
cumul_bits += li_bitlen;
@@ -803,6 +844,7 @@ static void extract_pucch_csi_report(NR_CSI_MeasConfig_t *csi_MeasConfig,
sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.csi_report_id = csi_report_id;
cumul_bits += pmi_bitlen;
evaluate_cqi_report(payload, csi_report, cumul_bits, r_index, UE, cqi_table);
link_csi_decoded |= r_index >= 0;
break;
default:
AssertFatal(1 == 0, "Invalid or not supported CSI measurement report\n");
@@ -810,6 +852,10 @@ static void extract_pucch_csi_report(NR_CSI_MeasConfig_t *csi_MeasConfig,
}
}
}
if (link_csi_decoded) {
sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.report_frame = frame;
sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.report_slot = slot;
}
if ((new_bf_index !=-1) && !nrmac->radio_config.do_TCI)
// Trigger RRCReconfiguration. Need to be out of the for loop as it may modify csi_MeasConfig
beam_switching_procedure(nrmac, UE, new_bf_index);
@@ -882,8 +928,7 @@ void handle_nr_uci_pucch_0_1(module_id_t mod_id, frame_t frame, slot_t slot, con
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
if (((uci_01->pduBitmap >> 1) & 0x01)) {
// iterate over received harq bits
for (int harq_bit = 0; harq_bit < uci_01->harq.num_harq; harq_bit++) {
const uint8_t harq_value = uci_01->harq.harq_list[harq_bit].harq_value;
for (int harq_bit = 0; harq_bit < uci_01->harq.num_harq;) {
const uint8_t harq_confidence = uci_01->harq.harq_confidence_level;
NR_UE_harq_t *harq = find_harq(frame, slot, UE, nrmac->dl_bler.harq_round_max);
if (!harq) {
@@ -891,11 +936,16 @@ void handle_nr_uci_pucch_0_1(module_id_t mod_id, frame_t frame, slot_t slot, con
break;
}
DevAssert(harq->is_waiting);
const int n_ack_bits = harq->sched_pdsch.nrOfLayers > 4 ? 2 : 1;
const int8_t pid = sched_ctrl->feedback_dl_harq.head;
remove_front_nr_list(&sched_ctrl->feedback_dl_harq);
LOG_D(NR_MAC,"%4d.%2d bit %d pid %d ack/nack %d\n",frame, slot, harq_bit,pid,harq_value);
bool success = harq_confidence == 0 && harq_bit + n_ack_bits <= uci_01->harq.num_harq;
for (int q = 0; q < n_ack_bits && harq_bit + q < uci_01->harq.num_harq; q++) {
const uint8_t harq_value = uci_01->harq.harq_list[harq_bit + q].harq_value;
LOG_D(NR_MAC, "%4d.%2d bit %d pid %d cw %d ack/nack %d\n", frame, slot, harq_bit + q, pid, q, harq_value);
success &= harq_value == 0;
}
nr_mac_update_pdcch_closed_loop_adjust(sched_ctrl, harq_confidence != 0);
bool success = harq_value == 0 && harq_confidence == 0;
// TCI state switch occurs at the first slot that is after slot n_+ T_HARQ + 3N_sf_slot (8.10.3 of 38.133)
if (success && harq->start_tci_timer) {
int slots = 3 * nrmac->frame_structure.numb_slots_frame / 10;
@@ -919,6 +969,7 @@ void handle_nr_uci_pucch_0_1(module_id_t mod_id, frame_t frame, slot_t slot, con
// DTX for each _bit_, but maybe once for PUCCH is enough? not sure
nr_mac_signal_dtx(&sched_ctrl->pucch_pc);
}
harq_bit += n_ack_bits;
}
// tpc (power control) only if we received AckNack
@@ -984,19 +1035,24 @@ void handle_nr_uci_pucch_2_3_4(module_id_t mod_id, frame_t frame, slot_t slot, c
if ((uci_234->pduBitmap >> 1) & 0x01) {
// iterate over received harq bits
for (int harq_bit = 0; harq_bit < uci_234->harq.harq_bit_len; harq_bit++) {
const int acknack = ((uci_234->harq.harq_payload[harq_bit >> 3]) >> harq_bit) & 0x01;
for (int harq_bit = 0; harq_bit < uci_234->harq.harq_bit_len;) {
NR_UE_harq_t *harq = find_harq(frame, slot, UE, RC.nrmac[mod_id]->dl_bler.harq_round_max);
if (!harq) {
LOG_E(NR_MAC, "UE %04x: Could not find a HARQ process at %4d.%2d!\n", UE->rnti, frame, slot);
break;
}
DevAssert(harq->is_waiting);
const int n_ack_bits = harq->sched_pdsch.nrOfLayers > 4 ? 2 : 1;
const int8_t pid = sched_ctrl->feedback_dl_harq.head;
remove_front_nr_list(&sched_ctrl->feedback_dl_harq);
LOG_D(NR_MAC,"%4d.%2d bit %d pid %d ack/nack %d\n",frame, slot, harq_bit, pid, acknack);
bool success = uci_234->harq.harq_crc != 1 && harq_bit + n_ack_bits <= uci_234->harq.harq_bit_len;
for (int q = 0; q < n_ack_bits && harq_bit + q < uci_234->harq.harq_bit_len; q++) {
const int bit = harq_bit + q;
const int acknack = (uci_234->harq.harq_payload[bit >> 3] >> (bit & 7)) & 0x01;
LOG_D(NR_MAC, "%4d.%2d bit %d pid %d cw %d ack/nack %d\n", frame, slot, bit, pid, q, acknack);
success &= acknack;
}
// TCI state switch occurs at the first slot that is after slot n_+ T_HARQ + 3N_sf_slot (8.10.3 of 38.133)
bool success = uci_234->harq.harq_crc != 1 && acknack;
if (success && harq->start_tci_timer) {
int slots = 3 * nrmac->frame_structure.numb_slots_frame / 10;
nr_timer_setup(&sched_ctrl->tci_beam_switch, slots, 1);
@@ -1004,6 +1060,7 @@ void handle_nr_uci_pucch_2_3_4(module_id_t mod_id, frame_t frame, slot_t slot, c
harq->start_tci_timer = false;
}
handle_dl_harq(nrmac, UE, pid, success, nrmac->dl_bler.harq_round_max);
harq_bit += n_ack_bits;
}
free(uci_234->harq.harq_payload);
}
@@ -1353,4 +1410,3 @@ void nr_sr_reporting(gNB_MAC_INST *nrmac, frame_t SFN, slot_t slot)
}
}
}
+2 -1
View File
@@ -373,7 +373,7 @@ uint16_t get_pm_index(const gNB_MAC_INST *nrmac,
int get_mcs_from_SINRx10(int mcs_table, int SINRx10, int Nl);
uint8_t get_mcs_from_cqi(int mcs_table, int cqi_table, int cqi_idx);
uint8_t get_dl_nrOfLayers(const NR_UE_sched_ctrl_t *sched_ctrl, const nr_dci_format_t dci_format);
uint8_t get_dl_nrOfLayers(const gNB_MAC_INST *mac, const NR_UE_sched_ctrl_t *sched_ctrl, const nr_dci_format_t dci_format);
int get_ul_nrOfLayers(const NR_UE_sched_ctrl_t *sched_ctrl, const nr_dci_format_t dci_format);
void free_sched_pucch_list(NR_UE_sched_ctrl_t *sched_ctrl);
@@ -464,6 +464,7 @@ bool prepare_initial_ul_rrc_message(gNB_MAC_INST *mac, NR_UE_info_t *UE);
void send_initial_ul_rrc_message(int rnti, const uint8_t *sdu, sdu_size_t sdu_len, void *data);
void finish_nr_dl_harq(NR_UE_sched_ctrl_t *sched_ctrl, int harq_pid);
void update_sched_pdsch_codeword_tbs(NR_sched_pdsch_t *sched);
void abort_nr_dl_harq(NR_UE_info_t* UE, int8_t harq_pid);
void nr_mac_trigger_release_timer(NR_UE_sched_ctrl_t *sched_ctrl, NR_SubcarrierSpacing_t subcarrier_spacing);
+14
View File
@@ -65,6 +65,7 @@
#include "LAYER2/NR_MAC_COMMON/nr_mac_common.h"
#include "LAYER2/NR_MAC_gNB/mac_config.h"
#include "NR_TAG.h"
#include "PHY/NR_TRANSPORT/nr_antenna_state.h"
/* Defs */
#define MAX_NUM_BWP 5
@@ -459,6 +460,7 @@ typedef struct NR_sched_pusch {
typedef struct NR_pdsch_dmrs {
uint8_t dmrs_ports_id;
uint16_t dmrs_ports;
uint8_t N_PRB_DMRS;
uint8_t N_DMRS_SLOT;
uint16_t dl_dmrs_symb_pos;
@@ -484,6 +486,8 @@ typedef struct NR_sched_pdsch {
uint16_t R;
uint8_t Qm;
uint32_t tb_size;
/// Second transport block for rank 5--8 (same MCS/R/Qm for bundled HARQ)
uint32_t tb_size2;
/// DL HARQ PID to use for this UE, or -1 for "any new"
int8_t dl_harq_pid;
@@ -510,6 +514,7 @@ typedef struct NR_UE_harq {
/* Transport block to be sent using this HARQ process */
byte_array_t transportBlock;
byte_array_t transportBlock2;
uint32_t tb_size; // size of currently stored TB
bool start_tci_timer;
/// sched_pdsch keeps information on MCS etc used for the initial transmission
@@ -546,6 +551,8 @@ struct CRI_RI_LI_PMI_CQI {
uint8_t wb_cqi_2tb;
uint8_t cqi_table;
uint8_t csi_report_id;
uint16_t report_frame;
uint16_t report_slot;
bool print_report;
};
@@ -714,6 +721,9 @@ typedef struct NR_mac_dir_stats {
uint32_t total_rbs_retx;
uint32_t num_mac_sdu;
uint32_t current_rbs;
// Most recently scheduled spatial layer count. Unlike current_rbs this is
// intentionally persistent so asynchronous E2 sampling does not miss it.
uint8_t last_layers;
} NR_mac_dir_stats_t;
typedef struct NR_mac_stats {
@@ -965,6 +975,10 @@ typedef struct gNB_MAC_INST_s {
nr_mac_config_t radio_config;
nr_rlc_configuration_t rlc_config;
/// Pointer to PHY antenna state (port mute mask) — set in nr-gnb init,
/// used by get_dl_nrOfLayers to cap MIMO rank to active TX port count.
nr_antenna_state_t *antenna_state;
NR_sched_ctrl_sib1_t *sched_ctrlSIB1;
NR_sched_pdcch_t *sched_pdcch_otherSI;
uint16_t cset0_bwp_start;
+37 -13
View File
@@ -32,7 +32,7 @@
#include "utils.h"
#include "xer_encoder.h"
#define NR_MAX_SUPPORTED_DL_LAYERS 4
#define NR_MAX_SUPPORTED_DL_LAYERS NR_MAX_NB_LAYERS
/* Default values for measurement gap configuration */
#define DEFAULT_MGRP NR_GapConfig__mgrp_ms160
@@ -655,10 +655,18 @@ static void set_dl_maxmimolayers(NR_PDSCH_ServingCellConfig_t *pdsch_servingcell
pdsch_servingcellconfig->ext1->maxMIMO_Layers = calloc(1, sizeof(*pdsch_servingcellconfig->ext1->maxMIMO_Layers));
long l = ue_supported_dl_layers(scc, uecap);
long ue_supported_layers = l > 1 ? l : 2; // min UE supported layers = 2
if (maxMIMO_layers == -1)
// nakaolab Patch 6: when called with NULL uecap (initial config before
// capability exchange), ue_supported_dl_layers returns -1. Fall back to
// maxMIMO_layers from config directly instead of clamping to 2.
long ue_supported_layers;
if (l < 1) {
ue_supported_layers = (maxMIMO_layers > 0) ? maxMIMO_layers : NR_MAX_SUPPORTED_DL_LAYERS;
} else {
ue_supported_layers = l;
}
if (maxMIMO_layers == -1)
*pdsch_servingcellconfig->ext1->maxMIMO_Layers = min(NR_MAX_SUPPORTED_DL_LAYERS, ue_supported_layers);
else
else
*pdsch_servingcellconfig->ext1->maxMIMO_Layers = min(maxMIMO_layers, ue_supported_layers);
}
@@ -1282,7 +1290,9 @@ static void config_pucch_resset1(const NR_ServingCellConfigCommon_t *scc,
pucchfmt2->interslotFrequencyHopping = NULL;
pucchfmt2->additionalDMRS = NULL;
pucchfmt2->maxCodeRate = calloc(1,sizeof(*pucchfmt2->maxCodeRate));
*pucchfmt2->maxCodeRate = NR_PUCCH_MaxCodeRate_zeroDot15;
// nakaolab Patch 5: bump from zeroDot15 (19.2 bit capacity) to zeroDot25
// (32 bit capacity) so 4-port CSI report (20 bits) fits in PUCCH.
*pucchfmt2->maxCodeRate = NR_PUCCH_MaxCodeRate_zeroDot25;
pucchfmt2->nrofSlots = NULL;
pucchfmt2->pi2BPSK = NULL;
@@ -1691,8 +1701,14 @@ static NR_PTRS_DownlinkConfig_t *config_dlptrs(const nr_ptrs_config_t *ptrs)
return ptrs_config;
}
static NR_SetupRelease_PDSCH_Config_t *config_pdsch(uint64_t ssb_bitmap, int bwp_Id, bool do_TCI, nr_ptrs_config_t *ptrs)
static NR_SetupRelease_PDSCH_Config_t *config_pdsch(uint64_t ssb_bitmap,
int bwp_Id,
const nr_mac_config_t *configuration)
{
const int antenna_ports = configuration->pdsch_AntennaPorts.N1 * configuration->pdsch_AntennaPorts.N2
* configuration->pdsch_AntennaPorts.XP;
const int configured_layers = configuration->maxMIMO_layers > 0 ? configuration->maxMIMO_layers : antenna_ports;
const bool two_codewords = min(configured_layers, NR_MAX_SUPPORTED_DL_LAYERS) > 4;
NR_SetupRelease_PDSCH_Config_t *setup_pdsch_Config = calloc(1,sizeof(*setup_pdsch_Config));
setup_pdsch_Config->present = NR_SetupRelease_PDSCH_Config_PR_setup;
NR_PDSCH_Config_t *pdsch_Config = calloc(1, sizeof(*pdsch_Config));
@@ -1701,15 +1717,20 @@ static NR_SetupRelease_PDSCH_Config_t *config_pdsch(uint64_t ssb_bitmap, int bwp
pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA->present = NR_SetupRelease_DMRS_DownlinkConfig_PR_setup;
pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup = calloc(1, sizeof(*pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup));
NR_DMRS_DownlinkConfig_t *dmrs_DownlinkForPDSCH_MappingTypeA = pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup;
if (ptrs) {
NR_PTRS_DownlinkConfig_t *ptrs_config = config_dlptrs(ptrs);
if (configuration->ptrs && !two_codewords) {
NR_PTRS_DownlinkConfig_t *ptrs_config = config_dlptrs(configuration->ptrs);
NR_SetupRelease_PTRS_DownlinkConfig_t *phaseTrackingRS = calloc(1, sizeof(*phaseTrackingRS));
phaseTrackingRS->present = NR_SetupRelease_PTRS_DownlinkConfig_PR_setup;
phaseTrackingRS->choice.setup = ptrs_config;
dmrs_DownlinkForPDSCH_MappingTypeA->phaseTrackingRS = phaseTrackingRS;
}
if (configuration->ptrs && two_codewords)
LOG_W(NR_MAC, "PDSCH PTRS is disabled for rank 5--8 until two-codeword PTRS is supported\n");
dmrs_DownlinkForPDSCH_MappingTypeA->dmrs_Type = NULL;
dmrs_DownlinkForPDSCH_MappingTypeA->maxLength = NULL;
if (two_codewords)
asn1cCallocOne(dmrs_DownlinkForPDSCH_MappingTypeA->maxLength, NR_DMRS_DownlinkConfig__maxLength_len2);
else
dmrs_DownlinkForPDSCH_MappingTypeA->maxLength = NULL;
dmrs_DownlinkForPDSCH_MappingTypeA->scramblingID0 = NULL;
dmrs_DownlinkForPDSCH_MappingTypeA->scramblingID1 = NULL;
dmrs_DownlinkForPDSCH_MappingTypeA->dmrs_AdditionalPosition = calloc(1, sizeof(*dmrs_DownlinkForPDSCH_MappingTypeA->dmrs_AdditionalPosition));
@@ -1718,13 +1739,16 @@ static NR_SetupRelease_PDSCH_Config_t *config_pdsch(uint64_t ssb_bitmap, int bwp
pdsch_Config->dataScramblingIdentityPDSCH = NULL;
pdsch_Config->resourceAllocation = NR_PDSCH_Config__resourceAllocation_resourceAllocationType1;
if (two_codewords)
asn1cCallocOne(pdsch_Config->maxNrofCodeWordsScheduledByDCI,
NR_PDSCH_Config__maxNrofCodeWordsScheduledByDCI_n2);
pdsch_Config->prb_BundlingType.present = NR_PDSCH_Config__prb_BundlingType_PR_staticBundling;
pdsch_Config->prb_BundlingType.choice.staticBundling = calloc(1, sizeof(*pdsch_Config->prb_BundlingType.choice.staticBundling));
pdsch_Config->prb_BundlingType.choice.staticBundling->bundleSize = calloc(1, sizeof(*pdsch_Config->prb_BundlingType.choice.staticBundling->bundleSize));
*pdsch_Config->prb_BundlingType.choice.staticBundling->bundleSize = NR_PDSCH_Config__prb_BundlingType__staticBundling__bundleSize_wideband;
if (do_TCI) {
if (configuration->do_TCI) {
int n_ssb = 0;
if (!pdsch_Config->tci_StatesToAddModList)
pdsch_Config->tci_StatesToAddModList=calloc(1,sizeof(*pdsch_Config->tci_StatesToAddModList));
@@ -1840,7 +1864,7 @@ static NR_BWP_Downlink_t *config_downlinkBWP(const NR_ServingCellConfigCommon_t
asn1cSeqAdd(&bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList->list, ss3);
bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToReleaseList = NULL;
bwp->bwp_Dedicated->pdsch_Config = config_pdsch(ssb_bitmap, bwp->bwp_Id, configuration->do_TCI, configuration->ptrs);
bwp->bwp_Dedicated->pdsch_Config = config_pdsch(ssb_bitmap, bwp->bwp_Id, configuration);
set_dl_mcs_table(bwp->bwp_Common->genericParameters.subcarrierSpacing,
force_256qam_off ? NULL : uecap,
@@ -3389,7 +3413,7 @@ static NR_BWP_DownlinkDedicated_t *configure_initial_dl_bwp(const NR_ServingCell
asn1cSeqAdd(&pdcch_Config->searchSpacesToAddModList->list, ss2);
bwp_Dedicated->pdcch_Config->choice.setup = pdcch_Config;
bwp_Dedicated->pdsch_Config = config_pdsch(bitmap, 0, configuration->do_TCI, configuration->ptrs);
bwp_Dedicated->pdsch_Config = config_pdsch(bitmap, 0, configuration);
// we might call configuration of initial BWP for BWP switch when we already have UE capabilities
set_dl_mcs_table(scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.subcarrierSpacing,
configuration->force_256qam_off ? NULL : uecap,
@@ -4062,7 +4086,7 @@ NR_CellGroupConfig_t *get_default_secondaryCellGroup(const NR_ServingCellConfigC
configDedicated->initialDownlinkBWP = calloc(1, sizeof(*configDedicated->initialDownlinkBWP));
configDedicated->initialDownlinkBWP->pdcch_Config = NULL;
configDedicated->initialDownlinkBWP->pdsch_Config = config_pdsch(bitmap, 0, configuration->do_TCI, configuration->ptrs);
configDedicated->initialDownlinkBWP->pdsch_Config = config_pdsch(bitmap, 0, configuration);
configDedicated->initialDownlinkBWP->sps_Config = NULL; // calloc(1,sizeof(struct NR_SetupRelease_SPS_Config));
configDedicated->initialDownlinkBWP->radioLinkMonitoringConfig = NULL;