update
This commit is contained in:
@@ -17,6 +17,11 @@ bool read_mac_sm(void* data)
|
||||
//fill_mac_ind_data(mac);
|
||||
|
||||
mac->msg.tstamp = time_now_us();
|
||||
// Export the physical denominator beside the per-UE TBS/PRB counters. This
|
||||
// avoids estimating PRB opportunity from a rounded, differently-windowed
|
||||
// KPM percentage in the rApp.
|
||||
mac->msg.dl_prb_opportunities = RC.nrmac[mod_id]->mac_stats.dl.total_prb_aggregate;
|
||||
mac->msg.dl_used_prbs = RC.nrmac[mod_id]->mac_stats.dl.used_prb_aggregate;
|
||||
|
||||
NR_UEs_t *UE_info = &RC.nrmac[mod_id]->UE_info;
|
||||
size_t num_ues = 0;
|
||||
@@ -33,6 +38,10 @@ bool read_mac_sm(void* data)
|
||||
|
||||
size_t i = 0; //TODO
|
||||
UE_iterator(UE_info->connected_ue_list, UE) {
|
||||
// Bound the write against the calloc'd buffer size: a UE attach between
|
||||
// the count loop above and this iterator can grow connected_ue_list and
|
||||
// overflow mac->msg.ue_stats (corrupts the heap → later free() abort).
|
||||
if (i >= num_ues) break;
|
||||
const NR_UE_sched_ctrl_t* sched_ctrl = &UE->UE_sched_ctrl;
|
||||
mac_ue_stats_impl_t* rd = &mac->msg.ue_stats[i];
|
||||
|
||||
@@ -58,8 +67,11 @@ bool read_mac_sm(void* data)
|
||||
rd->dl_aggr_retx_prb = UE->mac_stats.dl.total_rbs_retx;
|
||||
rd->ul_aggr_retx_prb = UE->mac_stats.ul.total_rbs_retx;
|
||||
|
||||
rd->dl_aggr_bytes_sdus = UE->mac_stats.dl.lc_bytes[3];
|
||||
rd->ul_aggr_bytes_sdus = UE->mac_stats.ul.lc_bytes[3];
|
||||
// The service-model field is aggregate SDU bytes. Using LCID 3 silently
|
||||
// drops payload whenever the DRB is assigned another logical channel and
|
||||
// corrupts the MAC payload/TBS fill ratio used by capacity estimation.
|
||||
rd->dl_aggr_bytes_sdus = UE->mac_stats.dl.total_sdu_bytes;
|
||||
rd->ul_aggr_bytes_sdus = UE->mac_stats.ul.total_sdu_bytes;
|
||||
|
||||
rd->dl_aggr_sdus = UE->mac_stats.dl.num_mac_sdu;
|
||||
rd->ul_aggr_sdus = UE->mac_stats.ul.num_mac_sdu;
|
||||
@@ -68,6 +80,16 @@ bool read_mac_sm(void* data)
|
||||
rd->pucch_snr = sched_ctrl->pucch_pc.avg_snr; //: float = -64;
|
||||
|
||||
rd->wb_cqi = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.wb_cqi_1tb;
|
||||
rd->csi_valid = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.print_report;
|
||||
rd->dl_ri = rd->csi_valid ? sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.ri + 1 : 0;
|
||||
rd->dl_layers = UE->mac_stats.dl.last_layers;
|
||||
rd->pmi_x1 = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.pmi_x1;
|
||||
rd->pmi_x2 = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.pmi_x2;
|
||||
rd->csi_frame = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.report_frame;
|
||||
rd->csi_slot = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.report_slot;
|
||||
rd->active_tx_chains = RC.nrmac[mod_id]->antenna_state != NULL
|
||||
? nr_get_active_tx_antenna_count(RC.nrmac[mod_id]->antenna_state)
|
||||
: 0;
|
||||
rd->dl_mcs1 = sched_ctrl->dl_bler_stats.mcs;
|
||||
rd->dl_bler = sched_ctrl->dl_bler_stats.bler;
|
||||
rd->ul_mcs1 = sched_ctrl->ul_bler_stats.mcs;
|
||||
@@ -110,4 +132,3 @@ sm_ag_if_ans_t write_ctrl_mac_sm(void const* data)
|
||||
sm_ag_if_ans_t ans = {0};
|
||||
return ans;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#endif
|
||||
|
||||
#if defined (NGRAN_GNB_CUUP)
|
||||
void fill_pdcp_stats_for_all_rbs(pdcp_radio_bearer_stats_t* rb, const size_t num_ues, const uint64_t* ue_id_list)
|
||||
void fill_pdcp_stats_for_all_rbs(pdcp_radio_bearer_stats_t* rb, const size_t num_ues, const uint64_t* ue_id_list, const size_t rb_capacity)
|
||||
{
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < num_ues; i++) {
|
||||
@@ -37,6 +37,10 @@ void fill_pdcp_stats_for_all_rbs(pdcp_radio_bearer_stats_t* rb, const size_t num
|
||||
|
||||
if (!rc) continue;
|
||||
|
||||
// Bound the write against the original capacity: an RB activating
|
||||
// between the count loop in read_pdcp_sm and this fill can grow the
|
||||
// observed count and overflow rb[] (corrupts the heap).
|
||||
if (j >= rb_capacity) return;
|
||||
pdcp_radio_bearer_stats_t *rd = &rb[j++];
|
||||
|
||||
rd->txpdu_pkts = rb_pdcp.txpdu_pkts; /* aggregated number of tx packets */
|
||||
@@ -114,7 +118,7 @@ bool read_pdcp_sm(void* data)
|
||||
|
||||
// Filling Radio Bearer Statistics
|
||||
#if defined (NGRAN_GNB_CUUP)
|
||||
fill_pdcp_stats_for_all_rbs(pdcp->msg.rb, num_ues, ue_id_list);
|
||||
fill_pdcp_stats_for_all_rbs(pdcp->msg.rb, num_ues, ue_id_list, pdcp->msg.len);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -83,6 +83,11 @@ bool read_rlc_sm(void* data)
|
||||
const int srb_flag = 0;
|
||||
const bool rc = nr_rlc_get_statistics(rnti, srb_flag, rb_id, &rb_rlc);
|
||||
if(!rc) continue;
|
||||
// Bound the write against the originally-calloc'd buffer size. A UE
|
||||
// attach between num_act_rb() above and this iterator can grow the
|
||||
// connected-UE list; without this check we'd overflow rlc->msg.rb
|
||||
// and corrupt the heap (crash later in free_rlc_ind_msg()).
|
||||
if (i >= act_rb) goto done_iter;
|
||||
rlc_radio_bearer_stats_t* sm_rb = &rlc->msg.rb[i];
|
||||
|
||||
/* TX */
|
||||
@@ -143,6 +148,7 @@ bool read_rlc_sm(void* data)
|
||||
++i;
|
||||
}
|
||||
}
|
||||
done_iter:
|
||||
|
||||
return act_rb > 0;
|
||||
}
|
||||
|
||||
@@ -86,8 +86,42 @@ static meas_record_lst_t fill_DRB_PdcpSduVolumeUL(__attribute__((unused)) const
|
||||
}
|
||||
|
||||
#if defined (NGRAN_GNB_DU)
|
||||
static uldlcounter_t last_rlc_pdu_total_bytes[MAX_MOBILES_PER_GNB] = {0};
|
||||
static uldlcounter_t last_total_prbs[MAX_MOBILES_PER_GNB] = {0};
|
||||
typedef struct ue_uldlcounter {
|
||||
const NR_UE_info_t *ue;
|
||||
rnti_t rnti;
|
||||
uldlcounter_t value;
|
||||
bool dl_valid;
|
||||
bool ul_valid;
|
||||
} ue_uldlcounter_t;
|
||||
|
||||
static ue_uldlcounter_t last_rlc_pdu_total_bytes[MAX_MOBILES_PER_GNB] = {0};
|
||||
static ue_uldlcounter_t last_total_prbs[MAX_MOBILES_PER_GNB] = {0};
|
||||
|
||||
/* The connected UE list is compacted when a UE leaves, so its iteration index
|
||||
* is not a stable identity. Keep baselines by the MAC-assigned UID instead and
|
||||
* reset them when that UID is reused for another UE. */
|
||||
static ue_uldlcounter_t *get_ue_counter(ue_uldlcounter_t counters[MAX_MOBILES_PER_GNB],
|
||||
const cudu_ue_info_pair_t ue_info)
|
||||
{
|
||||
AssertFatal(ue_info.ue != NULL, "KPM UE measurement requires MAC UE information\n");
|
||||
const uid_t uid = ue_info.ue->uid;
|
||||
AssertFatal(uid < MAX_MOBILES_PER_GNB, "KPM UE UID %u exceeds maximum %u\n", uid, MAX_MOBILES_PER_GNB);
|
||||
|
||||
ue_uldlcounter_t *counter = &counters[uid];
|
||||
if (counter->ue != ue_info.ue || counter->rnti != ue_info.ue->rnti)
|
||||
*counter = (ue_uldlcounter_t){.ue = ue_info.ue, .rnti = ue_info.ue->rnti};
|
||||
return counter;
|
||||
}
|
||||
|
||||
/* A new/reused UE counter and a reset/wrap have no valid preceding sample.
|
||||
* Establish a fresh baseline and report zero for that single period. */
|
||||
static uint32_t counter_delta(uint32_t current, uint32_t *previous, bool *valid)
|
||||
{
|
||||
const uint32_t delta = *valid && current >= *previous ? current - *previous : 0;
|
||||
*previous = current;
|
||||
*valid = true;
|
||||
return delta;
|
||||
}
|
||||
|
||||
static nr_rlc_statistics_t get_rlc_stats_per_drb(const rnti_t rnti, const int rb_id)
|
||||
{
|
||||
@@ -120,8 +154,16 @@ static meas_record_lst_t fill_DRB_RlcSduDelayDl(__attribute__((unused)) const la
|
||||
|
||||
meas_record.value = REAL_MEAS_VALUE;
|
||||
|
||||
// Get the value of sojourn time at the RLC buffer
|
||||
meas_record.real_val = rlc.txsdu_avg_time_to_tx; // [μs]
|
||||
// Report the windowed-average RLC SDU sojourn (mean per-packet wait over the
|
||||
// last 100 ms), not the instantaneous tx_list HOL snapshot. The snapshot
|
||||
// (txsdu_wt_us) reads 0 whenever the tx_list is momentarily drained at the
|
||||
// single sampling instant -- common with bursty DL even at high throughput --
|
||||
// which systematically under-reports delay (era/load-dependent). The averaging
|
||||
// accumulator is now activated once (reset guarded to off->on in
|
||||
// nr_rlc_activate_avg_time_to_tx), so it accumulates continuously instead of
|
||||
// being wiped on every read; it returns 0 only when no SDU was transmitted in
|
||||
// the window (genuine idle/starvation).
|
||||
meas_record.real_val = (double)rlc.txsdu_avg_time_to_tx; // [us] avg RLC SDU sojourn
|
||||
|
||||
return meas_record;
|
||||
}
|
||||
@@ -132,7 +174,7 @@ static meas_record_lst_t fill_DRB_RlcSduDelayDl(__attribute__((unused)) const la
|
||||
static meas_record_lst_t fill_DRB_UEThpDl(__attribute__((unused)) const label_info_lst_t label,
|
||||
uint32_t gran_period_ms,
|
||||
cudu_ue_info_pair_t ue_info,
|
||||
const size_t ue_idx,
|
||||
__attribute__((unused)) const size_t ue_idx,
|
||||
__attribute__((unused))e2_node_level_stats_t* node_stats)
|
||||
{
|
||||
meas_record_lst_t meas_record = {0};
|
||||
@@ -142,8 +184,9 @@ static meas_record_lst_t fill_DRB_UEThpDl(__attribute__((unused)) const label_in
|
||||
meas_record.value = REAL_MEAS_VALUE;
|
||||
|
||||
// Calculate DL Thp
|
||||
meas_record.real_val = (double)(rlc.txpdu_bytes - last_rlc_pdu_total_bytes[ue_idx].dl)*8/gran_period_ms; // [kbps]
|
||||
last_rlc_pdu_total_bytes[ue_idx].dl = rlc.txpdu_bytes;
|
||||
ue_uldlcounter_t *counter = get_ue_counter(last_rlc_pdu_total_bytes, ue_info);
|
||||
const uint32_t bytes = counter_delta(rlc.txpdu_bytes, &counter->value.dl, &counter->dl_valid);
|
||||
meas_record.real_val = gran_period_ms == 0 ? 0 : (double)bytes * 8 / gran_period_ms; // [kbps]
|
||||
|
||||
return meas_record;
|
||||
}
|
||||
@@ -154,7 +197,7 @@ static meas_record_lst_t fill_DRB_UEThpDl(__attribute__((unused)) const label_in
|
||||
static meas_record_lst_t fill_DRB_UEThpUl(__attribute__((unused)) const label_info_lst_t label,
|
||||
uint32_t gran_period_ms,
|
||||
cudu_ue_info_pair_t ue_info,
|
||||
const size_t ue_idx,
|
||||
__attribute__((unused)) const size_t ue_idx,
|
||||
__attribute__((unused))e2_node_level_stats_t* node_stats)
|
||||
{
|
||||
meas_record_lst_t meas_record = {0};
|
||||
@@ -166,8 +209,9 @@ static meas_record_lst_t fill_DRB_UEThpUl(__attribute__((unused)) const label_in
|
||||
meas_record.value = REAL_MEAS_VALUE;
|
||||
|
||||
// Calculate UL Thp
|
||||
meas_record.real_val = (double)(rlc.rxpdu_bytes - last_rlc_pdu_total_bytes[ue_idx].ul)*8/gran_period_ms; // [kbps]
|
||||
last_rlc_pdu_total_bytes[ue_idx].ul = rlc.rxpdu_bytes;
|
||||
ue_uldlcounter_t *counter = get_ue_counter(last_rlc_pdu_total_bytes, ue_info);
|
||||
const uint32_t bytes = counter_delta(rlc.rxpdu_bytes, &counter->value.ul, &counter->ul_valid);
|
||||
meas_record.real_val = gran_period_ms == 0 ? 0 : (double)bytes * 8 / gran_period_ms; // [kbps]
|
||||
|
||||
return meas_record;
|
||||
}
|
||||
@@ -177,7 +221,7 @@ static meas_record_lst_t fill_DRB_UEThpUl(__attribute__((unused)) const label_in
|
||||
static meas_record_lst_t fill_RRU_PrbTotDl(__attribute__((unused)) const label_info_lst_t label,
|
||||
__attribute__((unused)) uint32_t gran_period_ms,
|
||||
cudu_ue_info_pair_t ue_info,
|
||||
const size_t ue_idx,
|
||||
__attribute__((unused)) const size_t ue_idx,
|
||||
e2_node_level_stats_t* node_stats)
|
||||
{
|
||||
meas_record_lst_t meas_record = {0};
|
||||
@@ -185,8 +229,13 @@ static meas_record_lst_t fill_RRU_PrbTotDl(__attribute__((unused)) const label_i
|
||||
meas_record.value = INTEGER_MEAS_VALUE;
|
||||
|
||||
// Get the number of DL PRBs
|
||||
meas_record.int_val = (ue_info.ue->mac_stats.dl.total_rbs - last_total_prbs[ue_idx].dl) * 100 / (node_stats[1].mac_stats.dl.total_prb_aggregate - node_stats[0].mac_stats.dl.total_prb_aggregate); // [%]
|
||||
last_total_prbs[ue_idx].dl = ue_info.ue->mac_stats.dl.total_rbs;
|
||||
ue_uldlcounter_t *counter = get_ue_counter(last_total_prbs, ue_info);
|
||||
const uint32_t ue_delta = counter_delta(ue_info.ue->mac_stats.dl.total_rbs, &counter->value.dl, &counter->dl_valid);
|
||||
const uint64_t current = node_stats[1].mac_stats.dl.total_prb_aggregate;
|
||||
const uint64_t previous = node_stats[0].mac_stats.dl.total_prb_aggregate;
|
||||
const uint64_t total_delta = current >= previous ? current - previous : 0;
|
||||
const uint64_t percentage = total_delta == 0 ? 0 : (uint64_t)ue_delta * 100 / total_delta;
|
||||
meas_record.int_val = percentage > 100 ? 100 : percentage; // [%]
|
||||
|
||||
return meas_record;
|
||||
}
|
||||
@@ -196,7 +245,7 @@ static meas_record_lst_t fill_RRU_PrbTotDl(__attribute__((unused)) const label_i
|
||||
static meas_record_lst_t fill_RRU_PrbTotUl(__attribute__((unused)) const label_info_lst_t label,
|
||||
__attribute__((unused)) uint32_t gran_period_ms,
|
||||
cudu_ue_info_pair_t ue_info,
|
||||
const size_t ue_idx,
|
||||
__attribute__((unused)) const size_t ue_idx,
|
||||
e2_node_level_stats_t* node_stats)
|
||||
{
|
||||
meas_record_lst_t meas_record = {0};
|
||||
@@ -204,8 +253,13 @@ static meas_record_lst_t fill_RRU_PrbTotUl(__attribute__((unused)) const label_i
|
||||
meas_record.value = INTEGER_MEAS_VALUE;
|
||||
|
||||
// Get the number of UL PRBs
|
||||
meas_record.int_val = (ue_info.ue->mac_stats.ul.total_rbs - last_total_prbs[ue_idx].ul) * 100 / (node_stats[1].mac_stats.ul.total_prb_aggregate - node_stats[0].mac_stats.ul.total_prb_aggregate); // [%]
|
||||
last_total_prbs[ue_idx].ul = ue_info.ue->mac_stats.ul.total_rbs;
|
||||
ue_uldlcounter_t *counter = get_ue_counter(last_total_prbs, ue_info);
|
||||
const uint32_t ue_delta = counter_delta(ue_info.ue->mac_stats.ul.total_rbs, &counter->value.ul, &counter->ul_valid);
|
||||
const uint64_t current = node_stats[1].mac_stats.ul.total_prb_aggregate;
|
||||
const uint64_t previous = node_stats[0].mac_stats.ul.total_prb_aggregate;
|
||||
const uint64_t total_delta = current >= previous ? current - previous : 0;
|
||||
const uint64_t percentage = total_delta == 0 ? 0 : (uint64_t)ue_delta * 100 / total_delta;
|
||||
meas_record.int_val = percentage > 100 ? 100 : percentage; // [%]
|
||||
|
||||
return meas_record;
|
||||
}
|
||||
|
||||
+1
-1
Submodule openair2/E2AP/flexric updated: 340c36bc83...25353bb973
@@ -645,6 +645,9 @@ void RCconfig_verify(configmodule_interface_t *cfg, ngran_node_t node_type)
|
||||
verify_gnb_param_notset(gnbp, GNB_DO_SRS_IDX, GNB_CONFIG_STRING_DOSRS);
|
||||
verify_gnb_param_notset(gnbp, GNB_FORCE256QAMOFF_IDX, GNB_CONFIG_STRING_FORCE256QAMOFF);
|
||||
verify_gnb_param_notset(gnbp, GNB_MAXMIMOLAYERS_IDX, GNB_CONFIG_STRING_MAXMIMOLAYERS);
|
||||
verify_gnb_param_notset(gnbp,
|
||||
GNB_PDSCH_DMRS_ADDITIONAL_POSITION_IDX,
|
||||
GNB_CONFIG_STRING_PDSCH_DMRS_ADDITIONAL_POSITION);
|
||||
verify_gnb_param_notset(gnbp, GNB_DISABLE_HARQ_IDX, GNB_CONFIG_STRING_DISABLE_HARQ);
|
||||
verify_gnb_param_notset(gnbp, GNB_NUM_DL_HARQ_IDX, GNB_CONFIG_STRING_NUM_DL_HARQPROCESSES);
|
||||
verify_gnb_param_notset(gnbp, GNB_NUM_UL_HARQ_IDX, GNB_CONFIG_STRING_NUM_UL_HARQPROCESSES);
|
||||
@@ -1553,6 +1556,12 @@ void RCconfig_nr_macrlc(configmodule_interface_t *cfg)
|
||||
config.force_UL256qam_off = *GNBParamList.paramarray[0][GNB_FORCEUL256QAMOFF_IDX].iptr;
|
||||
config.use_deltaMCS = *GNBParamList.paramarray[0][GNB_USE_DELTA_MCS_IDX].iptr != 0;
|
||||
config.maxMIMO_layers = *GNBParamList.paramarray[0][GNB_MAXMIMOLAYERS_IDX].iptr;
|
||||
config.pdsch_dmrs_additional_position =
|
||||
*GNBParamList.paramarray[0][GNB_PDSCH_DMRS_ADDITIONAL_POSITION_IDX].iptr;
|
||||
AssertFatal(config.pdsch_dmrs_additional_position == 0 || config.pdsch_dmrs_additional_position == 1,
|
||||
"%s must be 0 (pos0) or 1 (pos1), got %d\n",
|
||||
GNB_CONFIG_STRING_PDSCH_DMRS_ADDITIONAL_POSITION,
|
||||
config.pdsch_dmrs_additional_position);
|
||||
config.disable_harq = *GNBParamList.paramarray[0][GNB_DISABLE_HARQ_IDX].iptr;
|
||||
config.num_dlharq = *GNBParamList.paramarray[0][GNB_NUM_DL_HARQ_IDX].iptr;
|
||||
config.num_ulharq = *GNBParamList.paramarray[0][GNB_NUM_UL_HARQ_IDX].iptr;
|
||||
@@ -1572,6 +1581,9 @@ void RCconfig_nr_macrlc(configmodule_interface_t *cfg)
|
||||
config.num_ulharq);
|
||||
int tot_ant = config.pdsch_AntennaPorts.N1 * config.pdsch_AntennaPorts.N2 * config.pdsch_AntennaPorts.XP;
|
||||
AssertFatal(config.maxMIMO_layers != 0 && config.maxMIMO_layers <= tot_ant, "Invalid maxMIMO_layers %d\n", config.maxMIMO_layers);
|
||||
LOG_I(GNB_APP,
|
||||
"PDSCH DMRS additional position pos%d\n",
|
||||
config.pdsch_dmrs_additional_position);
|
||||
|
||||
config.redcap = get_redcap_config(0);
|
||||
config.ptrs = get_ptrs_config();
|
||||
|
||||
@@ -101,6 +101,7 @@ typedef enum {
|
||||
#define GNB_CONFIG_STRING_CONFIG_REP "CSI_report_type"
|
||||
#define GNB_CONFIG_STRING_1ST_ACTIVE_BWP "first_active_bwp"
|
||||
#define GNB_CONFIG_STRING_LIMIT_RSRP_REPORT "max_num_RSRP_reported"
|
||||
#define GNB_CONFIG_STRING_PDSCH_DMRS_ADDITIONAL_POSITION "pdsch_dmrs_additional_position"
|
||||
|
||||
#define GNB_CONFIG_HLP_STRING_ENABLE_SDAP "enable the SDAP layer\n"
|
||||
#define GNB_CONFIG_HLP_FORCE256QAMOFF "suppress activation of 256 QAM despite UE support"
|
||||
@@ -114,6 +115,7 @@ typedef enum {
|
||||
#define GNB_CONFIG_HLP_CU_SIBS "List of CU generated SIBs to be transmitted"
|
||||
#define GNB_CONFIG_HLP_DU_SIBS "List of DU generated SIBs to be transmitted"
|
||||
#define GNB_CONFIG_HLP_CONFIG_REP "Define quantity for CSI report (options: ssb_rsrp, ssb_sinr and cri_rsrp)"
|
||||
#define GNB_CONFIG_HLP_PDSCH_DMRS_ADDITIONAL_POSITION "PDSCH DMRS additional position: 0=pos0, 1=pos1"
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------------------------------------------------------------------*/
|
||||
@@ -163,6 +165,7 @@ typedef enum {
|
||||
{GNB_CONFIG_STRING_CONFIG_REP, GNB_CONFIG_HLP_CONFIG_REP, 0, .strptr=NULL, .defstrval="ssb_rsrp", TYPE_STRING, 0}, \
|
||||
{GNB_CONFIG_STRING_1ST_ACTIVE_BWP, NULL, 0, .iptr=NULL, .defintval=0, TYPE_INT, 0}, \
|
||||
{GNB_CONFIG_STRING_LIMIT_RSRP_REPORT, NULL, 0, .iptr=NULL, .defintval=0, TYPE_INT, 0}, \
|
||||
{GNB_CONFIG_STRING_PDSCH_DMRS_ADDITIONAL_POSITION, GNB_CONFIG_HLP_PDSCH_DMRS_ADDITIONAL_POSITION, 0, .iptr=NULL, .defintval=1, TYPE_INT, 0}, \
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
@@ -207,6 +210,7 @@ typedef enum {
|
||||
#define GNB_CONFIG_REP_IDX 37
|
||||
#define GNB_1ST_ACTIVE_BWP_IDX 38
|
||||
#define GNB_LIMIT_RSRP_REPORT_IDX 39
|
||||
#define GNB_PDSCH_DMRS_ADDITIONAL_POSITION_IDX 40
|
||||
|
||||
#define TRACKING_AREA_CODE_OKRANGE {0x0001,0xFFFD}
|
||||
#define NUM_DL_HARQ_OKVALUES {2,4,6,8,10,12,16,32}
|
||||
@@ -256,6 +260,7 @@ typedef enum {
|
||||
3 } }, \
|
||||
{ .s5 = { NULL } }, \
|
||||
{ .s5 = { NULL } }, \
|
||||
{ .s5 = { NULL } }, \
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
@@ -4133,12 +4133,19 @@ uint16_t compute_pucch_prb_size(uint8_t format,
|
||||
nr_prbs);
|
||||
|
||||
if (format==2){
|
||||
/* nr_decode_pucch2() requires at least two PRB-symbols. Preserve the
|
||||
* one-symbol format-2 resource at symbol 13 (which does not overlap the
|
||||
* usual PUSCH ending at symbol 12) and use frequency width to meet that
|
||||
* decoder invariant. This common helper keeps UE TX and gNB RX aligned. */
|
||||
const int min_prbs = n_symb == 1 ? 2 : 1;
|
||||
AssertFatal(nr_prbs >= min_prbs,
|
||||
"PUCCH format 2 needs at least %d PRBs for %d symbol(s)", min_prbs, n_symb);
|
||||
// TODO fix this for multiple CSI reports
|
||||
for (int i = nr_prbs; i > 0; i--) {
|
||||
for (int i = nr_prbs; i >= min_prbs; i--) {
|
||||
// compute code rate factor for next prb value
|
||||
int next_prb_factor = (i - 1) * n_symb * Qm * n_re_ctrl * r;
|
||||
// if it does not sa
|
||||
if (O_tot > next_prb_factor)
|
||||
if (i == min_prbs || O_tot > next_prb_factor)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -4552,8 +4559,10 @@ static void set_bitlen_size_singlepanel(CSI_Meas_bitlen_t *csi_bitlen, int n1, i
|
||||
case 7:
|
||||
case 8:
|
||||
if(n1 == 4 && n2 == 1) {
|
||||
csi_bitlen->pmi_i11_bitlen[i] = ceil(log2(n1 * o1));
|
||||
csi_bitlen->pmi_i12_bitlen[i] = ceil(log2(n2 * o2 / 2));
|
||||
// TS 38.212 Table 6.3.1.1.2-1: only half of the horizontal
|
||||
// oversampled beams are valid for rank 7/8 in the 4x1 geometry.
|
||||
csi_bitlen->pmi_i11_bitlen[i] = ceil(log2(n1 * o1 / 2));
|
||||
csi_bitlen->pmi_i12_bitlen[i] = 0;
|
||||
csi_bitlen->pmi_i13_bitlen[i] = 0;
|
||||
csi_bitlen->pmi_x2_bitlen[i] = 1;
|
||||
}
|
||||
|
||||
@@ -2371,7 +2371,8 @@ bool get_downlink_ack(NR_UE_MAC_INST_t *mac, frame_t frame, int slot, PUCCH_sche
|
||||
if (current_DL_BWP
|
||||
&& current_DL_BWP->pdsch_Config
|
||||
&& current_DL_BWP->pdsch_Config->maxNrofCodeWordsScheduledByDCI
|
||||
&& current_DL_BWP->pdsch_Config->maxNrofCodeWordsScheduledByDCI[0] == 2) {
|
||||
&& *current_DL_BWP->pdsch_Config->maxNrofCodeWordsScheduledByDCI
|
||||
== NR_PDSCH_Config__maxNrofCodeWordsScheduledByDCI_n2) {
|
||||
two_transport_blocks = true;
|
||||
number_of_code_word = 2;
|
||||
}
|
||||
@@ -2964,6 +2965,11 @@ static nfapi_nr_ue_csi_payload_t get_csirs_RI_PMI_CQI_payload(NR_UE_MAC_INST_t *
|
||||
int pmi_x1_bitlen = csi_report->csi_meas_bitlen.pmi_x1_bitlen[mac->csirs_measurements.ri];
|
||||
int pmi_x2_bitlen = csi_report->csi_meas_bitlen.pmi_x2_bitlen[mac->csirs_measurements.ri];
|
||||
int cqi_bitlen = csi_report->csi_meas_bitlen.cqi_bitlen[mac->csirs_measurements.ri];
|
||||
/* For ranks with two transport blocks, the report carries one
|
||||
* wideband CQI per codeword. Until separate per-codeword estimates
|
||||
* are available, report the measured wideband CQI for both. */
|
||||
const uint16_t cqi = mac->csirs_measurements.cqi & 0x0f;
|
||||
const uint16_t cqi_payload = cqi_bitlen > 4 ? (cqi << 4) | cqi : cqi;
|
||||
int padding_bitlen = 0;
|
||||
// TODO: Improvements will be needed to cri_bitlen>0 and pmi_x1_bitlen>0
|
||||
if (mapping_type == ON_PUSCH) {
|
||||
@@ -2971,7 +2977,7 @@ static nfapi_nr_ue_csi_payload_t get_csirs_RI_PMI_CQI_payload(NR_UE_MAC_INST_t *
|
||||
p2_bits = pmi_x1_bitlen + pmi_x2_bitlen;
|
||||
temp_payload_1 = (0/*mac->csi_measurements.cri*/ << (cqi_bitlen + ri_bitlen)) |
|
||||
(mac->csirs_measurements.ri << cqi_bitlen) |
|
||||
(mac->csirs_measurements.cqi);
|
||||
cqi_payload;
|
||||
temp_payload_2 = (mac->csirs_measurements.i1 << pmi_x2_bitlen) |
|
||||
mac->csirs_measurements.i2;
|
||||
}
|
||||
@@ -2982,7 +2988,7 @@ static nfapi_nr_ue_csi_payload_t get_csirs_RI_PMI_CQI_payload(NR_UE_MAC_INST_t *
|
||||
(mac->csirs_measurements.ri << (cqi_bitlen + pmi_x2_bitlen + pmi_x1_bitlen + padding_bitlen)) |
|
||||
(mac->csirs_measurements.i1 << (cqi_bitlen + pmi_x2_bitlen)) |
|
||||
(mac->csirs_measurements.i2 << (cqi_bitlen)) |
|
||||
(mac->csirs_measurements.cqi);
|
||||
cqi_payload;
|
||||
}
|
||||
|
||||
temp_payload_1 = reverse_bits(temp_payload_1, p1_bits);
|
||||
|
||||
@@ -1154,15 +1154,36 @@ static NR_CSI_ResourceConfigId_t find_CSI_resourceconfig(NR_CSI_MeasConfig_t *cs
|
||||
return -1; // not found any CSI-resource in current DL BWP associated with this CSI-RS ID
|
||||
}
|
||||
|
||||
static uint8_t set_csirs_measurement_bitmap(NR_CSI_MeasConfig_t *csi_measconfig, NR_CSI_ResourceConfigId_t csi_res_id)
|
||||
static uint8_t set_csirs_measurement_bitmap(const NR_UE_MAC_INST_t *mac,
|
||||
NR_CSI_MeasConfig_t *csi_measconfig,
|
||||
NR_CSI_ResourceConfigId_t csi_res_id,
|
||||
fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_config_pdu)
|
||||
{
|
||||
uint8_t meas_bitmap = 0;
|
||||
csirs_config_pdu->codebook_n1 = 0;
|
||||
csirs_config_pdu->codebook_n2 = 0;
|
||||
csirs_config_pdu->codebook_mode = 0;
|
||||
if (csi_res_id > NR_maxNrofCSI_ResourceConfigurations)
|
||||
return meas_bitmap; // CSI-RS for tracking
|
||||
for(int i = 0; i < csi_measconfig->csi_ReportConfigToAddModList->list.count; i++) {
|
||||
NR_CSI_ReportConfig_t *report_config = csi_measconfig->csi_ReportConfigToAddModList->list.array[i];
|
||||
if(report_config->resourcesForChannelMeasurement != csi_res_id)
|
||||
continue;
|
||||
const nr_csi_report_t *report = &mac->csi_report_template[i];
|
||||
if (report->reportConfigId == report_config->reportConfigId && report->N1 > 0 && report->N2 > 0) {
|
||||
if (csirs_config_pdu->codebook_n1 != 0
|
||||
&& (csirs_config_pdu->codebook_n1 != report->N1
|
||||
|| csirs_config_pdu->codebook_n2 != report->N2
|
||||
|| csirs_config_pdu->codebook_mode != report->codebook_mode)) {
|
||||
LOG_W(NR_MAC,
|
||||
"CSI-RS resource %ld is associated with conflicting PMI codebooks; retaining the first one\n",
|
||||
csi_res_id);
|
||||
} else {
|
||||
csirs_config_pdu->codebook_n1 = report->N1;
|
||||
csirs_config_pdu->codebook_n2 = report->N2;
|
||||
csirs_config_pdu->codebook_mode = report->codebook_mode;
|
||||
}
|
||||
}
|
||||
// bit 0 RSRP bit 1 RI bit 2 LI bit 3 PMI bit 4 CQI bit 5 i1
|
||||
switch (report_config->reportQuantity.present) {
|
||||
case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_PMI_CQI:
|
||||
@@ -1220,7 +1241,8 @@ static void nr_schedule_csirs_reception(NR_UE_MAC_INST_t *mac, int frame, int sl
|
||||
continue;
|
||||
LOG_D(MAC,"Scheduling reception of CSI-RS in frame %d slot %d\n", frame, slot);
|
||||
fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_config_pdu = &dl_config->dl_config_list[dl_config->number_pdus].csirs_config_pdu.csirs_config_rel15;
|
||||
csirs_config_pdu->measurement_bitmap = set_csirs_measurement_bitmap(csi_measconfig, csi_res_id);
|
||||
csirs_config_pdu->measurement_bitmap =
|
||||
set_csirs_measurement_bitmap(mac, csi_measconfig, csi_res_id, csirs_config_pdu);
|
||||
csirs_config_pdu->subcarrier_spacing = mu;
|
||||
csirs_config_pdu->cyclic_prefix = current_DL_BWP->cyclicprefix ? *current_DL_BWP->cyclicprefix : 0;
|
||||
|
||||
|
||||
@@ -186,6 +186,28 @@ int precoding_weigths_generation(nfapi_nr_pm_list_t *mat,
|
||||
return pmiq;
|
||||
}
|
||||
|
||||
static int precoding_weights_generation_4x1_high_rank(nfapi_nr_pm_list_t *mat,
|
||||
int pmiq,
|
||||
int layers)
|
||||
{
|
||||
const uint8_t i11_count = nr_type1_sp_4x1_i11_count(layers);
|
||||
for (uint8_t i11 = 0; i11 < i11_count; i11++) {
|
||||
for (uint8_t i2 = 0; i2 < 2; i2++) {
|
||||
nfapi_nr_pm_pdu_t *pdu = &mat->pmi_pdu[pmiq];
|
||||
pdu->pm_idx = pmiq + 1; // index zero is the identity matrix
|
||||
pdu->numLayers = layers;
|
||||
pdu->num_ant_ports = 8;
|
||||
AssertFatal(nr_type1_sp_4x1_weights(layers, i11, 0, i2, pdu->weights),
|
||||
"Invalid rank-%d Type-I single-panel PMI i11=%u i2=%u\n",
|
||||
layers,
|
||||
i11,
|
||||
i2);
|
||||
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;
|
||||
@@ -208,20 +230,32 @@ nfapi_nr_pm_list_t init_DL_MIMO_codebook(gNB_MAC_INST *gNB, nr_pdsch_AntennaPort
|
||||
int O1 = num_antenna_ports > 2 ? 4 : 1; //Horizontal beam oversampling (1 or 4)
|
||||
|
||||
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);
|
||||
const int low_codebook_layers = min(max_mimo_layers, 4);
|
||||
const bool high_rank_4x1 = num_antenna_ports == 8 && antenna_ports.XP == 2 && N1 == 4 && N2 == 1;
|
||||
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 < codebook_layers; i++) {
|
||||
memset(gNB->precoding_matrix_size, 0, sizeof(gNB->precoding_matrix_size));
|
||||
for (int i = 0; i < low_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;
|
||||
mat.num_pm_idx += gNB->precoding_matrix_size[i];
|
||||
}
|
||||
if (max_mimo_layers > 4 && !high_rank_4x1) {
|
||||
LOG_W(NR_MAC,
|
||||
"Rank-5--8 PMI is only implemented for the eight-port Type-I single-panel 4x1 geometry; "
|
||||
"unsupported geometry XP=%d N1=%d N2=%d will use identity precoding\n",
|
||||
antenna_ports.XP,
|
||||
N1,
|
||||
N2);
|
||||
} else if (high_rank_4x1) {
|
||||
for (int layers = 5; layers <= max_mimo_layers; layers++) {
|
||||
gNB->precoding_matrix_size[layers - 1] = 2 * nr_type1_sp_4x1_i11_count(layers);
|
||||
mat.num_pm_idx += gNB->precoding_matrix_size[layers - 1];
|
||||
}
|
||||
}
|
||||
|
||||
mat.pmi_pdu = malloc16(mat.num_pm_idx * sizeof(*mat.pmi_pdu));
|
||||
AssertFatal(mat.pmi_pdu != NULL, "out of memory\n");
|
||||
@@ -277,8 +311,16 @@ nfapi_nr_pm_list_t init_DL_MIMO_codebook(gNB_MAC_INST *gNB, nr_pdsch_AntennaPort
|
||||
}
|
||||
|
||||
int pmiq = 0;
|
||||
for (int layers = 1; layers <= codebook_layers; layers++)
|
||||
for (int layers = 1; layers <= low_codebook_layers; layers++)
|
||||
pmiq = precoding_weigths_generation(&mat, pmiq, layers, N1, N2, O1, O2, num_antenna_ports, theta_n, v_lm);
|
||||
if (high_rank_4x1)
|
||||
for (int layers = 5; layers <= max_mimo_layers; layers++)
|
||||
pmiq = precoding_weights_generation_4x1_high_rank(&mat, pmiq, layers);
|
||||
|
||||
AssertFatal(pmiq == mat.num_pm_idx,
|
||||
"Generated %d precoding matrices but allocated %d\n",
|
||||
pmiq,
|
||||
mat.num_pm_idx);
|
||||
|
||||
return mat;
|
||||
}
|
||||
|
||||
@@ -115,6 +115,51 @@ static void copy_ul_tti_req(nfapi_nr_ul_tti_request_t *to, nfapi_nr_ul_tti_reque
|
||||
to->groups_list[i] = from->groups_list[i];
|
||||
}
|
||||
|
||||
static bool has_incompatible_dl_harq(gNB_MAC_INST *gNB, int target_layers)
|
||||
{
|
||||
UE_iterator(gNB->UE_info.connected_ue_list, UE) {
|
||||
const NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
|
||||
for (int pid = 0; pid < NR_MAX_HARQ_PROCESSES; ++pid) {
|
||||
const NR_UE_harq_t *harq = &sched_ctrl->harq_processes[pid];
|
||||
const bool in_flight = harq->is_waiting || harq->round > 0;
|
||||
if (in_flight && harq->sched_pdsch.nrOfLayers > target_layers) {
|
||||
LOG_D(NR_MAC,
|
||||
"[UE %04x] defer TX-mask downshift: HARQ PID %d still uses %d layers (target %d)\n",
|
||||
UE->rnti,
|
||||
pid,
|
||||
harq->sched_pdsch.nrOfLayers,
|
||||
target_layers);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* A physical downshift cannot occur while a buffered retransmission needs
|
||||
* more layers (and possibly a second codeword) than the target supports.
|
||||
* The control thread has already made the target visible to new scheduling,
|
||||
* so the old high-rank HARQ set can only drain from this point onward. */
|
||||
static void apply_pending_tx_mask_if_harq_safe(gNB_MAC_INST *gNB)
|
||||
{
|
||||
nr_antenna_state_t *state = gNB->antenna_state;
|
||||
uint8_t target_mask = 0;
|
||||
if (!nr_get_pending_tx_antenna_bitmask(state, &target_mask))
|
||||
return;
|
||||
|
||||
int target_layers = 0;
|
||||
for (uint8_t mask = target_mask; mask != 0; mask >>= 1)
|
||||
target_layers += mask & 1u;
|
||||
if (has_incompatible_dl_harq(gNB, target_layers))
|
||||
return;
|
||||
|
||||
if (nr_apply_requested_tx_antenna_bitmask(state, target_mask))
|
||||
LOG_I(NR_MAC,
|
||||
"Applied HARQ-safe TX-mask downshift to 0x%02X (%d layers)\n",
|
||||
target_mask,
|
||||
target_layers);
|
||||
}
|
||||
|
||||
void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, frame_t frame, slot_t slot, NR_Sched_Rsp_t *sched_info)
|
||||
{
|
||||
protocol_ctxt_t ctxt = {0};
|
||||
@@ -125,6 +170,7 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, frame_t frame, slot_t slo
|
||||
NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon;
|
||||
|
||||
NR_SCHED_LOCK(&gNB->sched_lock);
|
||||
apply_pending_tx_mask_if_harq_safe(gNB);
|
||||
int slots_frame = gNB->frame_structure.numb_slots_frame;
|
||||
clear_beam_information(&gNB->beam_info, frame, slot, slots_frame);
|
||||
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
#include "LAYER2/NR_MAC_gNB/mac_proto.h"
|
||||
#include "openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/*TAG*/
|
||||
#include "NR_TAG-Id.h"
|
||||
|
||||
@@ -348,6 +351,9 @@ void finish_nr_dl_harq(NR_UE_sched_ctrl_t *sched_ctrl, int harq_pid)
|
||||
NR_UE_harq_t *harq = &sched_ctrl->harq_processes[harq_pid];
|
||||
|
||||
harq->ndi ^= 1;
|
||||
if (harq->sched_pdsch.nrOfLayers > 4)
|
||||
harq->ndi2 ^= 1;
|
||||
harq->pending_cw_mask = 0;
|
||||
harq->round = 0;
|
||||
|
||||
add_tail_nr_list(&sched_ctrl->available_dl_harq, harq_pid);
|
||||
@@ -451,14 +457,23 @@ 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(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);
|
||||
const int layers = new_sched.nrOfLayers;
|
||||
|
||||
// If the RI changed between current rtx and a previous transmission
|
||||
// we need to verify if it is not decreased
|
||||
// othwise it wouldn't be possible to transmit the same TBS
|
||||
layers = (layers < new_sched.nrOfLayers) ? layers : new_sched.nrOfLayers;
|
||||
pm_index = (layers < new_sched.nrOfLayers) ? pm_index : new_sched.pm_index;
|
||||
/* HARQ retransmission must retain the original layer mapping, codeword
|
||||
* count, and TBS. Recomputing it from the current RI/TX target can turn a
|
||||
* rank-8 two-codeword HARQ process into a one-codeword transmission and
|
||||
* silently discard TB2. Normally a staged mask downshift keeps enough
|
||||
* physical chains active until this process drains; if that mechanism was
|
||||
* bypassed, defer instead of emitting an invalid retransmission. */
|
||||
if (nr_mac->antenna_state != NULL) {
|
||||
const int active_tx = nr_get_active_tx_antenna_count(nr_mac->antenna_state);
|
||||
if (active_tx < layers) {
|
||||
LOG_W(NR_MAC,
|
||||
"[UE %04x][%4d.%2d] defer DL HARQ PID %d: original rank %d exceeds %d active TX chains\n",
|
||||
UE->rnti, frame, slot, current_harq_pid, layers, active_tx);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const int coresetid = sched_ctrl->coreset->controlResourceSetId;
|
||||
const int tda = get_dl_tda(nr_mac, slot);
|
||||
@@ -556,7 +571,6 @@ static bool allocate_dl_retransmission(gNB_MAC_INST *nr_mac,
|
||||
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);
|
||||
@@ -586,11 +600,8 @@ 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;
|
||||
}
|
||||
/* DAI counts PDSCH assignments, not transport blocks. A two-codeword
|
||||
* PDSCH uses one PUCCH allocation carrying two HARQ-ACK bits. */
|
||||
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;
|
||||
@@ -659,6 +670,24 @@ static int comparator(const void *p, const void *q)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Keep latency bounded under multi-UE load by preventing the first PF-selected
|
||||
* UE from consuming the complete contiguous PRB region. The ordinary OAI PF
|
||||
* loop already supports several frequency-disjoint PDSCHs per slot, but a UE
|
||||
* with an accumulated RLC burst can otherwise take every PRB and make the
|
||||
* remaining UEs wait for later DL slots. This switch only divides resources
|
||||
* when at least two eligible UEs remain; single-UE peak throughput is unchanged. */
|
||||
static bool dl_fair_rb_cap_enabled(void)
|
||||
{
|
||||
static int enabled = -1;
|
||||
if (enabled < 0) {
|
||||
const char *setting = getenv("OAI_DL_FAIR_RB_CAP");
|
||||
enabled = setting != NULL && atoi(setting) != 0;
|
||||
if (enabled)
|
||||
LOG_I(NR_MAC, "DL fair-RB cap enabled for multi-UE low-latency scheduling\n");
|
||||
}
|
||||
return enabled;
|
||||
}
|
||||
|
||||
static void pf_dl(gNB_MAC_INST *mac,
|
||||
post_process_pdsch_t *pp_pdsch,
|
||||
NR_UE_info_t **UE_list,
|
||||
@@ -744,7 +773,13 @@ static void pf_dl(gNB_MAC_INST *mac,
|
||||
/* Calculate coeff */
|
||||
const NR_bler_options_t *bo = &mac->dl_bler;
|
||||
const int max_mcs_table = current_BWP->mcsTableIdx == 1 ? 27 : 28;
|
||||
const int max_mcs = min(sched_ctrl->dl_max_mcs, max_mcs_table);
|
||||
/* The experimental identity-precoded rank-8 path can make a Type-I
|
||||
* codebook CQI pessimistic for the actual PDSCH precoder. Keep CQI as
|
||||
* the default cap, but allow a bounded HARQ/BLER probe when explicitly
|
||||
* requested. The configured min/max MCS still apply in either mode. */
|
||||
const char *mcs_policy = getenv("OAI_DL_MCS_POLICY");
|
||||
const bool bler_only = mcs_policy != NULL && strcmp(mcs_policy, "bler_only") == 0;
|
||||
const int max_mcs = min(bler_only ? bo->max_mcs : sched_ctrl->dl_max_mcs, max_mcs_table);
|
||||
int selected_mcs;
|
||||
if (bo->harq_round_max == 1) {
|
||||
int new_mcs = min(bo->max_mcs, max_mcs);
|
||||
@@ -816,6 +851,16 @@ static void pf_dl(gNB_MAC_INST *mac,
|
||||
continue;
|
||||
}
|
||||
|
||||
int eligible_ues_left = 0;
|
||||
if (dl_fair_rb_cap_enabled()) {
|
||||
for (UEsched_t *candidate = iterator; candidate->UE != NULL; candidate++) {
|
||||
NR_UE_sched_ctrl_t *candidate_ctrl = &candidate->UE->UE_sched_ctrl;
|
||||
if (nr_mac_ue_is_active(candidate->UE)
|
||||
&& candidate_ctrl->available_dl_harq.head >= 0)
|
||||
eligible_ues_left++;
|
||||
}
|
||||
}
|
||||
|
||||
/* MCS has been set above */
|
||||
int tda = get_dl_tda(mac, slot);
|
||||
AssertFatal(tda >= 0,"Unable to find PDSCH time domain allocation in list\n");
|
||||
@@ -852,6 +897,15 @@ static void pf_dl(gNB_MAC_INST *mac,
|
||||
iterator++;
|
||||
continue;
|
||||
}
|
||||
if (eligible_ues_left > 1) {
|
||||
const int fair_ues = min(remainUEs[beam.idx], eligible_ues_left);
|
||||
if (fair_ues > 1) {
|
||||
/* Recompute after every allocation: unused shares from a lightly
|
||||
* loaded UE remain available to later UEs in this same slot. */
|
||||
const int fair_rb_cap = (n_rb_sched[beam.idx] + fair_ues - 1) / fair_ues;
|
||||
max_rbSize = min(max_rbSize, max(min_rbSize, fair_rb_cap));
|
||||
}
|
||||
}
|
||||
|
||||
int CCEIndex = get_cce_index(mac,
|
||||
CC_id,
|
||||
@@ -879,12 +933,8 @@ static void pf_dl(gNB_MAC_INST *mac,
|
||||
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;
|
||||
}
|
||||
/* One PDSCH assignment has one DAI increment even when it carries two
|
||||
* codewords; the UE multiplexes both ACK bits on this allocation. */
|
||||
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);
|
||||
@@ -1151,6 +1201,13 @@ static dlsch_fill_result_t fill_dlsch_transport_block(gNB_MAC_INST *mac,
|
||||
return result;
|
||||
}
|
||||
|
||||
static void fill_padding_only_transport_block(uint8_t *tb, uint32_t tbs)
|
||||
{
|
||||
DevAssert(tbs >= sizeof(NR_MAC_SUBHEADER_FIXED));
|
||||
memset(tb, 0, tbs);
|
||||
((NR_MAC_SUBHEADER_FIXED *)tb)->LCID = DL_SCH_LCID_PADDING;
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -1191,6 +1248,19 @@ void post_process_dlsch(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pdsch, NR_UE
|
||||
NR_tda_info_t *tda_info = &sched_pdsch->tda_info;
|
||||
NR_pdsch_dmrs_t *dmrs_parms = &sched_pdsch->dmrs_parms;
|
||||
NR_UE_harq_t *harq = &sched_ctrl->harq_processes[current_harq_pid];
|
||||
const int active_codewords = nr_pdsch_num_codewords(nrOfLayers);
|
||||
if (harq->round == 0) {
|
||||
harq->pending_cw_mask = (1U << active_codewords) - 1;
|
||||
} else if (active_codewords == 2) {
|
||||
DevAssert(harq->pending_cw_mask > 0 && harq->pending_cw_mask < 4);
|
||||
/* A codeword already ACKed must not be sent again with its old NDI:
|
||||
* the UE would otherwise deliver its MAC SDUs twice. Give its padding
|
||||
* replacement a fresh NDI and RV0 while the peer TB is retransmitted. */
|
||||
if (!(harq->pending_cw_mask & 1))
|
||||
harq->ndi ^= 1;
|
||||
if (!(harq->pending_cw_mask & 2))
|
||||
harq->ndi2 ^= 1;
|
||||
}
|
||||
NR_sched_pucch_t *pucch = NULL;
|
||||
DevAssert(!harq->is_waiting);
|
||||
if (sched_pdsch->pucch_allocation >= 0) {
|
||||
@@ -1202,6 +1272,28 @@ void post_process_dlsch(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pdsch, NR_UE
|
||||
}
|
||||
UE->mac_stats.dl.rounds[harq->round]++;
|
||||
int tpc = nr_mac_get_tpc(&sched_ctrl->pucch_pc);
|
||||
if (nrOfLayers > 4 && sched_pdsch->rbSize >= 100) {
|
||||
static bool logged_full_high_rank_shape[32] = {false};
|
||||
if (!__atomic_exchange_n(&logged_full_high_rank_shape[sched_pdsch->mcs], true, __ATOMIC_RELAXED))
|
||||
LOG_I(NR_MAC,
|
||||
"[HIGH_RANK_SCHEDULE] layers=%u RB=%u symbols=%u start=%u "
|
||||
"MCS=%u Qm=%u R=%u TBS=%u/%u total=%u DMRS_mask=0x%x "
|
||||
"DMRS_RE_per_RB=%u DMRS_symbols=%u CDM_groups=%u\n",
|
||||
nrOfLayers,
|
||||
sched_pdsch->rbSize,
|
||||
tda_info->nrOfSymbols,
|
||||
tda_info->startSymbolIndex,
|
||||
sched_pdsch->mcs,
|
||||
sched_pdsch->Qm,
|
||||
sched_pdsch->R,
|
||||
TBS,
|
||||
TBS2,
|
||||
TBS + TBS2,
|
||||
dmrs_parms->dl_dmrs_symb_pos,
|
||||
dmrs_parms->N_PRB_DMRS,
|
||||
dmrs_parms->N_DMRS_SLOT,
|
||||
dmrs_parms->numDmrsCdmGrpsNoData);
|
||||
}
|
||||
LOG_D(NR_MAC,
|
||||
"%4d.%2d [DLSCH/PDSCH/PUCCH] RNTI %04x DCI L %d start %3d RBs %3d startSymbol %2d nb_symbol %2d dmrspos %x MCS %2d nrOfLayers %d TBS %4d HARQ PID %2d round %d RV %d NDI %d dl_data_to_ULACK %d (%d.%d) PUCCH allocation %d TPC %d\n",
|
||||
frame,
|
||||
@@ -1279,6 +1371,10 @@ void post_process_dlsch(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pdsch, NR_UE
|
||||
fapi_beam,
|
||||
nl_tbslbrm,
|
||||
pduindex);
|
||||
if (harq->round > 0 && active_codewords == 2)
|
||||
for (int q = 0; q < active_codewords; q++)
|
||||
if (!(harq->pending_cw_mask & (1U << q)))
|
||||
pdsch_pdu->rvIndex[q] = 0;
|
||||
|
||||
|
||||
LOG_D(NR_MAC,"Configuring DCI/PDCCH in %d.%d at CCE %d, rnti %x\n", frame,slot,sched_ctrl->cce_index,rnti);
|
||||
@@ -1375,6 +1471,15 @@ void post_process_dlsch(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pdsch, NR_UE
|
||||
"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",
|
||||
current_harq_pid);
|
||||
if (active_codewords == 2)
|
||||
for (int q = 0; q < active_codewords; q++) {
|
||||
if (harq->pending_cw_mask & (1U << q))
|
||||
continue;
|
||||
byte_array_t *transport_block = q == 0 ? &harq->transportBlock : &harq->transportBlock2;
|
||||
const uint32_t cw_tbs = q == 0 ? TBS : TBS2;
|
||||
uint8_t *buf = allocate_transportBlock_buffer(transport_block, cw_tbs);
|
||||
fill_padding_only_transport_block(buf, cw_tbs);
|
||||
}
|
||||
T(T_GNB_MAC_RETRANSMISSION_DL_PDU_WITH_DATA, T_INT(module_id), T_INT(CC_id), T_INT(rnti),
|
||||
T_INT(frame), T_INT(slot), T_INT(current_harq_pid), T_INT(harq->round), T_BUFFER(harq->transportBlock.buf, TBS));
|
||||
UE->mac_stats.dl.total_rbs_retx += sched_pdsch->rbSize;
|
||||
|
||||
@@ -164,8 +164,10 @@ 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 || layers > 4)
|
||||
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)
|
||||
if (layers < 1 || layers > NR_MAX_NB_LAYERS || nrmac->precoding_matrix_size[layers - 1] == 0)
|
||||
return 0;
|
||||
const NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
|
||||
|
||||
int reported_ri_layers = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.ri + 1;
|
||||
@@ -180,9 +182,17 @@ uint16_t get_pm_index(const gNB_MAC_INST *nrmac,
|
||||
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 active_tx = nr_get_active_tx_antenna_count(ant_state);
|
||||
if (active_tx < ant_state->num_tx_antennas) {
|
||||
// A full-panel codebook spreads every layer over ports that the RU
|
||||
// subsequently blanks. Identity mapping instead places the
|
||||
// reduced layer count one-to-one on the remaining active ports.
|
||||
LOG_D(NR_MAC,
|
||||
"[ANTENNA_MUTE] %d/%u TX ports active, using identity matrix\n",
|
||||
active_tx,
|
||||
ant_state->num_tx_antennas);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const int report_id = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.csi_report_id;
|
||||
@@ -191,6 +201,13 @@ uint16_t get_pm_index(const gNB_MAC_INST *nrmac,
|
||||
const int N2 = csi_report->N2;
|
||||
const int antenna_ports = (N1 * N2) << 1;
|
||||
if (antenna_ports < 2) return 0; // single antenna port
|
||||
if (antenna_ports == 8 && (layers == 3 || layers == 4)) {
|
||||
LOG_D(NR_MAC,
|
||||
"Using identity matrix for rank-%d over eight ports: the legacy UE rank-3/4 detector "
|
||||
"does not support non-identity eight-port precoding\n",
|
||||
layers);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -226,17 +243,38 @@ uint16_t get_pm_index(const gNB_MAC_INST *nrmac,
|
||||
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);
|
||||
if (layers > 4) {
|
||||
const int i11_count = nr_type1_sp_4x1_i11_count(layers);
|
||||
if (antenna_ports != 8 || N1 != 4 || N2 != 1 || i11_count == 0
|
||||
|| i11 >= i11_count || i12 != 0 || i13 != 0 || i2 < 0 || i2 >= 2) {
|
||||
LOG_W(NR_MAC,
|
||||
"Unsupported rank-%d high-rank PMI for N1=%d N2=%d: i11=%d i12=%d i13=%d i2=%d; "
|
||||
"using identity matrix\n",
|
||||
layers,
|
||||
N1,
|
||||
N2,
|
||||
i11,
|
||||
i12,
|
||||
i13,
|
||||
i2);
|
||||
return 0;
|
||||
}
|
||||
// High-rank matrices are generated in i11-major, i2-minor order.
|
||||
lay_index = i11 * 2 + i2;
|
||||
} else {
|
||||
int k1, k2;
|
||||
get_k1_k2_indices(layers, N1, N2, i13, &k1, &k2); // indices for the generated PHY matrix
|
||||
const int O2 = N2 == 1 ? 1 : 4;
|
||||
const int O1 = 4; // horizontal beam oversampling for more than two antenna ports
|
||||
const int max_i2 = layers == 1 ? 4 : 2;
|
||||
int K1, K2;
|
||||
get_K1_K2(N1, N2, &K1, &K2, layers);
|
||||
// computing precoding matrix index according to init_DL_MIMO_codebook ordering
|
||||
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];
|
||||
@@ -860,7 +898,12 @@ int get_mcs_from_bler(const NR_bler_options_t *bler_options,
|
||||
if (diff < 0) // wrap around
|
||||
diff += 1024;
|
||||
|
||||
max_mcs = min(max_mcs, bler_options->max_mcs);
|
||||
/* Keep the configured interval valid even when CSI supplies a lower cap.
|
||||
* In particular, min_mcs == max_mcs is an explicit fixed-MCS request and
|
||||
* must not alternate between the CQI fallback and the configured value at
|
||||
* each BLER update boundary. */
|
||||
max_mcs = max((int)bler_options->min_mcs,
|
||||
min(max_mcs, (int)bler_options->max_mcs));
|
||||
const uint8_t old_mcs = min(bler_stats->mcs, max_mcs);
|
||||
if (diff < BLER_UPDATE_FRAME)
|
||||
return old_mcs; // no update
|
||||
@@ -971,8 +1014,17 @@ dci_pdu_rel15_t prepare_dci_dl_payload(const gNB_MAC_INST *gNB_mac,
|
||||
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.ndi2.val = harq->ndi2;
|
||||
dci_payload.rv2.val = pdsch_pdu->rvIndex[1];
|
||||
} else {
|
||||
/* maxNrofCodeWordsScheduledByDCI=n2 keeps the second-TB fields in DCI
|
||||
* format 1_1 even for a one-codeword (rank <= 4) PDSCH. TS 38.214
|
||||
* disables that transport block with I_MCS=26 and RV=1. Leaving the
|
||||
* zero-initialized values here makes the UE treat CW1 as active and
|
||||
* interpret the antenna-port row through the two-codeword DMRS table. */
|
||||
dci_payload.mcs2.val = 26;
|
||||
dci_payload.ndi2.val = 0;
|
||||
dci_payload.rv2.val = 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
|
||||
|
||||
@@ -15,6 +15,24 @@
|
||||
#include "common/ran_context.h"
|
||||
#include "common/utils/nr/nr_common.h"
|
||||
#include "nfapi/oai_integration/vendor_ext.h"
|
||||
|
||||
/* DAI counts PDSCH assignments, while O_ACK counts HARQ-ACK bits. With
|
||||
* maxNrofCodeWordsScheduledByDCI=n2, the type-2 codebook reserves two ACK
|
||||
* bits per assignment even when DCI disables the second transport block.
|
||||
* The reserved bit still occupies the UCI payload, but it must not turn a
|
||||
* successfully decoded one-codeword PDSCH into a HARQ failure. */
|
||||
static uint8_t harq_ack_bits_per_assignment(const NR_UE_info_t *UE, bool is_common)
|
||||
{
|
||||
if (is_common)
|
||||
return 1;
|
||||
const NR_PDSCH_Config_t *pdsch_config = UE->current_DL_BWP.pdsch_Config;
|
||||
return pdsch_config != NULL && pdsch_config->maxNrofCodeWordsScheduledByDCI != NULL
|
||||
&& *pdsch_config->maxNrofCodeWordsScheduledByDCI
|
||||
== NR_PDSCH_Config__maxNrofCodeWordsScheduledByDCI_n2
|
||||
? 2
|
||||
: 1;
|
||||
}
|
||||
|
||||
static void nr_fill_nfapi_pucch(gNB_MAC_INST *nrmac, frame_t frame, slot_t slot, const NR_sched_pucch_t *pucch, NR_UE_info_t* UE)
|
||||
{
|
||||
|
||||
@@ -48,14 +66,15 @@ static void nr_fill_nfapi_pucch(gNB_MAC_INST *nrmac, frame_t frame, slot_t slot,
|
||||
future_ul_tti_req->n_pdus += 1;
|
||||
|
||||
LOG_D(NR_MAC,
|
||||
"%s %4d.%2d Scheduling pucch reception in %4d.%2d: bits SR %d, DAI %d, CSI %d on res %d\n",
|
||||
pucch->dai_c>0 ? "pucch_acknack" : "",
|
||||
"%s %4d.%2d Scheduling pucch reception in %4d.%2d: bits SR %d, DAI %d, HARQ-ACK %d, CSI %d on res %d\n",
|
||||
pucch->harq_ack_bits > 0 ? "pucch_acknack" : "",
|
||||
frame,
|
||||
slot,
|
||||
pucch->frame,
|
||||
pucch->ul_slot,
|
||||
pucch->sr_flag,
|
||||
pucch->dai_c,
|
||||
pucch->harq_ack_bits,
|
||||
pucch->csi_bits,
|
||||
pucch->resource_indicator);
|
||||
NR_COMMON_channels_t * common_ch=nrmac->common_channels;
|
||||
@@ -75,7 +94,7 @@ static void nr_fill_nfapi_pucch(gNB_MAC_INST *nrmac, frame_t frame, slot_t slot,
|
||||
UE,
|
||||
pucch->resource_indicator,
|
||||
pucch->csi_bits,
|
||||
pucch->dai_c,
|
||||
pucch->harq_ack_bits,
|
||||
pucch->sr_flag,
|
||||
pucch->r_pucch,
|
||||
nrmac->beam_info.beam_mode);
|
||||
@@ -151,7 +170,7 @@ static void schedule_pucch_core(gNB_MAC_INST *nrmac, NR_UE_info_t *UE, frame_t f
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t O_ack = curr_pucch->dai_c;
|
||||
const uint16_t O_ack = curr_pucch->harq_ack_bits;
|
||||
const uint16_t O_csi = curr_pucch->csi_bits;
|
||||
const uint8_t O_sr = curr_pucch->sr_flag;
|
||||
LOG_D(NR_MAC,
|
||||
@@ -359,10 +378,15 @@ int get_pucch_resourceid(NR_PUCCH_Config_t *pucch_Config, int O_uci, int pucch_r
|
||||
return *resource_id;
|
||||
}
|
||||
|
||||
static void handle_dl_harq(gNB_MAC_INST *mac, NR_UE_info_t * UE, int8_t harq_pid, bool success, int harq_round_max)
|
||||
static void handle_dl_harq(gNB_MAC_INST *mac, NR_UE_info_t * UE, int8_t harq_pid, uint8_t ack_mask, int harq_round_max)
|
||||
{
|
||||
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
|
||||
NR_UE_harq_t *harq = &sched_ctrl->harq_processes[harq_pid];
|
||||
const uint8_t active_mask = harq->sched_pdsch.nrOfLayers > 4 ? 3 : 1;
|
||||
harq->pending_cw_mask = nr_pdsch_pending_after_ack(harq->pending_cw_mask, active_mask, ack_mask);
|
||||
LOG_D(NR_MAC, "DL HARQ UE %04x PID %d ACK mask 0x%x pending CW mask 0x%x round %u\n",
|
||||
UE->rnti, harq_pid, ack_mask, harq->pending_cw_mask, harq->round);
|
||||
const bool success = harq->pending_cw_mask == 0;
|
||||
harq->feedback_slot = -1;
|
||||
harq->is_waiting = false;
|
||||
if (success) {
|
||||
@@ -633,7 +657,7 @@ static void evaluate_cqi_report(uint8_t *payload,
|
||||
sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.wb_cqi_1tb = temp_cqi;
|
||||
LOG_D(MAC,"Wide-band CQI for the first TB %d\n", temp_cqi);
|
||||
if (cqi_bitlen > 4) {
|
||||
temp_cqi = pickandreverse_bits(payload, 4, cumul_bits);
|
||||
temp_cqi = pickandreverse_bits(payload, 4, cumul_bits + 4);
|
||||
sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.wb_cqi_2tb = temp_cqi;
|
||||
LOG_D(MAC,"Wide-band CQI for the second TB %d\n", temp_cqi);
|
||||
}
|
||||
@@ -886,7 +910,7 @@ static NR_UE_harq_t *find_harq(frame_t frame, slot_t slot, NR_UE_info_t * UE, in
|
||||
frame,
|
||||
slot);
|
||||
remove_front_nr_list(&sched_ctrl->feedback_dl_harq);
|
||||
handle_dl_harq(NULL, UE, pid, false, harq_round_max);
|
||||
handle_dl_harq(NULL, UE, pid, 0, harq_round_max);
|
||||
pid = sched_ctrl->feedback_dl_harq.head;
|
||||
if (pid < 0)
|
||||
return NULL;
|
||||
@@ -936,15 +960,28 @@ 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 int n_ack_bits = harq_ack_bits_per_assignment(UE, is_ra);
|
||||
/* Msg4/common HARQ entries can carry the legacy zero layer sentinel.
|
||||
* Preserve the scheduler's established 0--4 => one-codeword rule. */
|
||||
const int active_codewords = 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);
|
||||
bool success = harq_confidence == 0 && harq_bit + n_ack_bits <= uci_01->harq.num_harq;
|
||||
const bool feedback_valid = harq_confidence == 0 && harq_bit + n_ack_bits <= uci_01->harq.num_harq;
|
||||
bool success = feedback_valid;
|
||||
uint8_t ack_mask = 0;
|
||||
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;
|
||||
LOG_D(NR_MAC, "%4d.%2d bit %d pid %d cw %d active %d ack/nack %d\n",
|
||||
frame, slot, harq_bit + q, pid, q, q < active_codewords, harq_value);
|
||||
if (q < active_codewords) {
|
||||
const bool ack = harq_value == 0;
|
||||
success &= ack;
|
||||
if (feedback_valid && ack)
|
||||
ack_mask |= 1U << q;
|
||||
}
|
||||
}
|
||||
if (active_codewords == 2)
|
||||
success = feedback_valid && (harq->pending_cw_mask & ~ack_mask) == 0;
|
||||
nr_mac_update_pdcch_closed_loop_adjust(sched_ctrl, 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) {
|
||||
@@ -953,7 +990,7 @@ void handle_nr_uci_pucch_0_1(module_id_t mod_id, frame_t frame, slot_t slot, con
|
||||
nr_timer_start(&sched_ctrl->tci_beam_switch);
|
||||
harq->start_tci_timer = false;
|
||||
}
|
||||
handle_dl_harq(nrmac, UE, pid, success, nrmac->dl_bler.harq_round_max);
|
||||
handle_dl_harq(nrmac, UE, pid, ack_mask, nrmac->dl_bler.harq_round_max);
|
||||
if (is_ra) {
|
||||
bool ue_rejected = nr_check_Msg4_MsgB_Ack(mod_id, frame, slot, UE, success);
|
||||
if (ue_rejected) {
|
||||
@@ -1042,16 +1079,34 @@ void handle_nr_uci_pucch_2_3_4(module_id_t mod_id, frame_t frame, slot_t slot, c
|
||||
break;
|
||||
}
|
||||
DevAssert(harq->is_waiting);
|
||||
const int n_ack_bits = harq->sched_pdsch.nrOfLayers > 4 ? 2 : 1;
|
||||
const int n_ack_bits = harq_ack_bits_per_assignment(UE, false);
|
||||
/* Msg4/common HARQ entries can carry the legacy zero layer sentinel.
|
||||
* Preserve the scheduler's established 0--4 => one-codeword rule. */
|
||||
const int active_codewords = 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);
|
||||
bool success = uci_234->harq.harq_crc != 1 && harq_bit + n_ack_bits <= uci_234->harq.harq_bit_len;
|
||||
const bool feedback_valid = uci_234->harq.harq_crc != 1 && harq_bit + n_ack_bits <= uci_234->harq.harq_bit_len;
|
||||
bool success = feedback_valid;
|
||||
uint8_t ack_mask = 0;
|
||||
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;
|
||||
const int logical_bit = harq_bit + q;
|
||||
/* get_downlink_ack() reverses the Type-2 codebook before PUCCH
|
||||
* encoding. PUCCH formats 2/3/4 expose that wire order directly,
|
||||
* unlike the format-0/1 harq_list. Convert it back before assigning
|
||||
* bits to DAI/codeword positions. This matters when CW1 is disabled:
|
||||
* the reserved NACK and the active CW0 ACK otherwise exchange roles. */
|
||||
const int payload_bit = uci_234->harq.harq_bit_len - 1 - logical_bit;
|
||||
const int acknack = (uci_234->harq.harq_payload[payload_bit >> 3] >> (payload_bit & 7)) & 0x01;
|
||||
LOG_D(NR_MAC, "%4d.%2d logical bit %d payload bit %d pid %d cw %d active %d ack/nack %d\n",
|
||||
frame, slot, logical_bit, payload_bit, pid, q, q < active_codewords, acknack);
|
||||
if (q < active_codewords) {
|
||||
success &= acknack;
|
||||
if (feedback_valid && acknack)
|
||||
ack_mask |= 1U << q;
|
||||
}
|
||||
}
|
||||
if (active_codewords == 2)
|
||||
success = feedback_valid && (harq->pending_cw_mask & ~ack_mask) == 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;
|
||||
@@ -1059,7 +1114,7 @@ void handle_nr_uci_pucch_2_3_4(module_id_t mod_id, frame_t frame, slot_t slot, c
|
||||
nr_timer_start(&sched_ctrl->tci_beam_switch);
|
||||
harq->start_tci_timer = false;
|
||||
}
|
||||
handle_dl_harq(nrmac, UE, pid, success, nrmac->dl_bler.harq_round_max);
|
||||
handle_dl_harq(nrmac, UE, pid, ack_mask, nrmac->dl_bler.harq_round_max);
|
||||
harq_bit += n_ack_bits;
|
||||
}
|
||||
free(uci_234->harq.harq_payload);
|
||||
@@ -1137,6 +1192,15 @@ static void set_pucch0_vrb_occupation(const NR_sched_pucch_t *pucch, uint16_t *v
|
||||
}
|
||||
}
|
||||
|
||||
static void clear_pucch0_vrb_occupation(const NR_sched_pucch_t *pucch, uint16_t *vrb_map_UL, const int bwp_start)
|
||||
{
|
||||
for (int l = 0; l < pucch->nr_of_symb; l++) {
|
||||
const uint16_t symb = SL_to_bitmap(pucch->start_symb + l, 1);
|
||||
const int prb = l == 1 && pucch->second_hop_prb != 0 ? pucch->second_hop_prb : pucch->prb_start;
|
||||
vrb_map_UL[bwp_start + prb] &= ~symb;
|
||||
}
|
||||
}
|
||||
|
||||
bool check_bits_vs_coderate_limit(NR_PUCCH_Config_t *pucch_Config, int O_uci, int pucch_resource)
|
||||
{
|
||||
int resource_id = get_pucch_resourceid(pucch_Config, O_uci, pucch_resource);
|
||||
@@ -1182,6 +1246,84 @@ bool check_bits_vs_coderate_limit(NR_PUCCH_Config_t *pucch_Config, int O_uci, in
|
||||
AssertFatal(false, "No PUCCH resource found\n");
|
||||
}
|
||||
|
||||
/* PUCCH resource set 0 is limited to two UCI bits. A rank-8 PDSCH consumes
|
||||
* two HARQ-ACK bits by itself, so multiplexing a second assignment changes
|
||||
* the selected resource to set 1 (format 2). Keep the scheduler's VRB map in
|
||||
* sync with the resource that nr_configure_pucch() and the UE will select from
|
||||
* the final payload size. Without this migration every feedback occasion
|
||||
* accepts only one rank-8 assignment and halves the usable DL slots. */
|
||||
static bool migrate_pucch_to_large_payload_resource(gNB_MAC_INST *mac,
|
||||
NR_UE_info_t *UE,
|
||||
NR_sched_pucch_t *pucch,
|
||||
int old_uci_bits,
|
||||
int new_uci_bits,
|
||||
int ue_beam)
|
||||
{
|
||||
if (old_uci_bits > 2 || new_uci_bits <= 2 || pucch->r_pucch >= 0)
|
||||
return true;
|
||||
|
||||
NR_PUCCH_Config_t *pucch_config = UE->current_UL_BWP.pucch_Config;
|
||||
const int resource_id = get_pucch_resourceid(pucch_config, new_uci_bits, pucch->resource_indicator);
|
||||
const NR_PUCCH_Resource_t *resource = NULL;
|
||||
for (int i = 0; i < pucch_config->resourceToAddModList->list.count; i++) {
|
||||
const NR_PUCCH_Resource_t *candidate = pucch_config->resourceToAddModList->list.array[i];
|
||||
if (candidate->pucch_ResourceId == resource_id) {
|
||||
resource = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (resource == NULL || resource->format.present != NR_PUCCH_Resource__format_PR_format2
|
||||
|| resource->intraSlotFrequencyHopping != NULL || resource->secondHopPRB != NULL) {
|
||||
LOG_W(NR_MAC,
|
||||
"UE %04x cannot migrate %d-bit HARQ feedback to the configured non-hopping PUCCH format-2 resource\n",
|
||||
UE->rnti,
|
||||
new_uci_bits);
|
||||
return false;
|
||||
}
|
||||
|
||||
const int n_slots_frame = mac->frame_structure.numb_slots_frame;
|
||||
NR_beam_alloc_t beam =
|
||||
beam_allocation_procedure(&mac->beam_info, pucch->frame, pucch->ul_slot, ue_beam, n_slots_frame);
|
||||
if (beam.idx < 0)
|
||||
return false;
|
||||
const int index = ul_buffer_index(pucch->frame, pucch->ul_slot, n_slots_frame, mac->vrb_map_UL_size);
|
||||
const int bwp_start = UE->current_UL_BWP.BWPStart;
|
||||
uint16_t *vrb_map_UL = &mac->common_channels[0].vrb_map_UL[beam.idx][index * MAX_BWP_SIZE];
|
||||
|
||||
/* Release the format-0 reservation before testing format 2 so overlapping
|
||||
* resource layouts do not conflict with themselves. Restore it if the
|
||||
* larger footprint is occupied by another UL allocation. */
|
||||
clear_pucch0_vrb_occupation(pucch, vrb_map_UL, bwp_start);
|
||||
const int start_prb = resource->startingPRB;
|
||||
const int n_prbs = resource->format.choice.format2->nrofPRBs;
|
||||
const int start_symbol = resource->format.choice.format2->startingSymbolIndex;
|
||||
const int n_symbols = resource->format.choice.format2->nrofSymbols;
|
||||
const uint16_t symbol_mask = SL_to_bitmap(start_symbol, n_symbols);
|
||||
bool available = true;
|
||||
for (int prb = start_prb; prb < start_prb + n_prbs; prb++) {
|
||||
if ((vrb_map_UL[bwp_start + prb] & symbol_mask) != 0) {
|
||||
available = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!available) {
|
||||
set_pucch0_vrb_occupation(pucch, vrb_map_UL, bwp_start);
|
||||
return false;
|
||||
}
|
||||
for (int prb = start_prb; prb < start_prb + n_prbs; prb++)
|
||||
vrb_map_UL[bwp_start + prb] |= symbol_mask;
|
||||
|
||||
pucch->prb_start = start_prb;
|
||||
pucch->second_hop_prb = 0;
|
||||
pucch->nr_of_symb = n_symbols;
|
||||
pucch->start_symb = start_symbol;
|
||||
LOG_D(NR_MAC,
|
||||
"UE %04x migrated HARQ feedback from PUCCH format 0 to format 2 for %d UCI bits\n",
|
||||
UE->rnti,
|
||||
new_uci_bits);
|
||||
return true;
|
||||
}
|
||||
|
||||
// this function returns an index to NR_sched_pucch structure
|
||||
// if the function returns -1 it was not possible to schedule acknack
|
||||
int nr_acknack_scheduling(gNB_MAC_INST *mac,
|
||||
@@ -1203,6 +1345,7 @@ int nr_acknack_scheduling(gNB_MAC_INST *mac,
|
||||
const NR_UE_UL_BWP_t *ul_bwp = &UE->current_UL_BWP;
|
||||
const frame_structure_t *fs = &mac->frame_structure;
|
||||
const int n_slots_frame = fs->numb_slots_frame;
|
||||
const uint8_t n_ack_bits = harq_ack_bits_per_assignment(UE, is_common);
|
||||
|
||||
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
|
||||
NR_PUCCH_Config_t *pucch_Config = ul_bwp->pucch_Config;
|
||||
@@ -1238,11 +1381,28 @@ int nr_acknack_scheduling(gNB_MAC_INST *mac,
|
||||
if (curr_pucch->active &&
|
||||
curr_pucch->frame == pucch_frame &&
|
||||
curr_pucch->ul_slot == pucch_slot) { // if there is already a PUCCH in given frame and slot
|
||||
LOG_D(NR_MAC, "pucch_acknack DL %4d.%2d, UL_ACK %4d.%2d Bits already in current PUCCH: DAI_C %d CSI %d\n",
|
||||
frame, slot, pucch_frame, pucch_slot, curr_pucch->dai_c, curr_pucch->csi_bits);
|
||||
// we can't schedule if short pucch is already full
|
||||
if (curr_pucch->csi_bits == 0 &&
|
||||
curr_pucch->dai_c == 2)
|
||||
LOG_D(NR_MAC, "pucch_acknack DL %4d.%2d, UL_ACK %4d.%2d Bits already in current PUCCH: DAI_C %d ACK %d CSI %d\n",
|
||||
frame, slot, pucch_frame, pucch_slot, curr_pucch->dai_c, curr_pucch->harq_ack_bits, curr_pucch->csi_bits);
|
||||
const int old_uci_bits = curr_pucch->csi_bits + curr_pucch->harq_ack_bits + curr_pucch->sr_flag;
|
||||
const int new_uci_bits = old_uci_bits + n_ack_bits;
|
||||
/* Preserve the proven two-PDSCH processing budget per feedback
|
||||
* occasion. Rank-8 needs format 2 because those two assignments
|
||||
* produce four HARQ-ACK bits, but accepting more assignments here
|
||||
* can leave the UE insufficient time to decode every transport block
|
||||
* before their common feedback deadline. The scheduler will try the
|
||||
* next configured feedback occasion for additional assignments. */
|
||||
const int max_pdsch_assignments_per_feedback = 2;
|
||||
if (curr_pucch->dai_c >= max_pdsch_assignments_per_feedback)
|
||||
continue;
|
||||
if (new_uci_bits > 2
|
||||
&& !check_bits_vs_coderate_limit(pucch_Config, new_uci_bits, curr_pucch->resource_indicator))
|
||||
continue;
|
||||
if (!migrate_pucch_to_large_payload_resource(mac,
|
||||
UE,
|
||||
curr_pucch,
|
||||
old_uci_bits,
|
||||
new_uci_bits,
|
||||
ue_beam))
|
||||
continue;
|
||||
// if there is CSI but simultaneous HARQ+CSI is disable we can't schedule
|
||||
if (curr_pucch->csi_bits > 0 && !curr_pucch->simultaneous_harqcsi)
|
||||
@@ -1252,21 +1412,17 @@ int nr_acknack_scheduling(gNB_MAC_INST *mac,
|
||||
// the number of bits in the check need to include possible SR (1 bit)
|
||||
// and the ack/nack bit to be scheduled (1 bit)
|
||||
// so the number of bits already scheduled in current pucch + 2
|
||||
if (curr_pucch->csi_bits > 0
|
||||
&& !check_bits_vs_coderate_limit(pucch_Config,
|
||||
curr_pucch->csi_bits + curr_pucch->dai_c + 2,
|
||||
curr_pucch->resource_indicator))
|
||||
continue;
|
||||
// TODO temporarily limit ack/nak to 3 bits because of performances of polar for PUCCH (required for > 11 bits)
|
||||
if (curr_pucch->csi_bits > 0 && curr_pucch->dai_c >= 3)
|
||||
if (curr_pucch->csi_bits > 0 && curr_pucch->harq_ack_bits + n_ack_bits > 3)
|
||||
continue;
|
||||
|
||||
// otherwise we can schedule in this active PUCCH
|
||||
// no need to check VRB occupation because already done when PUCCH has been activated
|
||||
curr_pucch->timing_indicator = f;
|
||||
curr_pucch->dai_c++;
|
||||
LOG_D(NR_MAC, "DL %4d.%2d, UL_ACK %4d.%2d Scheduling ACK/NACK in PUCCH %d with timing indicator %d DAI %d CSI %d\n",
|
||||
frame,slot,curr_pucch->frame,curr_pucch->ul_slot,pucch_index,f,curr_pucch->dai_c,curr_pucch->csi_bits);
|
||||
curr_pucch->harq_ack_bits += n_ack_bits;
|
||||
LOG_D(NR_MAC, "DL %4d.%2d, UL_ACK %4d.%2d Scheduling ACK/NACK in PUCCH %d with timing indicator %d DAI %d ACK %d CSI %d\n",
|
||||
frame,slot,curr_pucch->frame,curr_pucch->ul_slot,pucch_index,f,curr_pucch->dai_c,curr_pucch->harq_ack_bits,curr_pucch->csi_bits);
|
||||
return pucch_index; // index of current PUCCH structure
|
||||
}
|
||||
else if (curr_pucch->active) {
|
||||
@@ -1310,11 +1466,12 @@ int nr_acknack_scheduling(gNB_MAC_INST *mac,
|
||||
curr_pucch->ul_slot = pucch_slot;
|
||||
curr_pucch->timing_indicator = f; // index in the list of timing indicators
|
||||
curr_pucch->dai_c++;
|
||||
curr_pucch->harq_ack_bits += n_ack_bits;
|
||||
curr_pucch->resource_indicator = 0; // each UE has dedicated PUCCH resources
|
||||
curr_pucch->r_pucch=r_pucch;
|
||||
|
||||
LOG_D(NR_MAC, "DL %4d.%2d, UL_ACK %4d.%2d Scheduling ACK/NACK in PUCCH %d with timing indicator %d DAI %d\n",
|
||||
frame, slot, curr_pucch->frame, curr_pucch->ul_slot, pucch_index, f, curr_pucch->dai_c);
|
||||
LOG_D(NR_MAC, "DL %4d.%2d, UL_ACK %4d.%2d Scheduling ACK/NACK in PUCCH %d with timing indicator %d DAI %d ACK %d\n",
|
||||
frame, slot, curr_pucch->frame, curr_pucch->ul_slot, pucch_index, f, curr_pucch->dai_c, curr_pucch->harq_ack_bits);
|
||||
|
||||
// blocking resources for current PUCCH in VRB map
|
||||
set_pucch0_vrb_occupation(curr_pucch, vrb_map_UL, bwp_start);
|
||||
|
||||
@@ -188,6 +188,8 @@ typedef struct nr_mac_config_s {
|
||||
bool force_UL256qam_off;
|
||||
bool use_deltaMCS;
|
||||
int maxMIMO_layers;
|
||||
/// PDSCH dmrs-AdditionalPosition: 0 for pos0, 1 for pos1.
|
||||
int pdsch_dmrs_additional_position;
|
||||
bool disable_harq;
|
||||
nr_power_config_t pusch;
|
||||
/// SNR threshold needed to put or not a PRB in the black list
|
||||
@@ -403,7 +405,9 @@ typedef struct NR_sched_pucch {
|
||||
bool sr_flag;
|
||||
int csi_bits;
|
||||
bool simultaneous_harqcsi;
|
||||
/// Number of PDSCH assignments represented by the DAI field.
|
||||
uint8_t dai_c;
|
||||
uint8_t harq_ack_bits; ///< Actual O_ACK payload width (one or two bits per assignment).
|
||||
uint8_t timing_indicator;
|
||||
uint8_t resource_indicator;
|
||||
int r_pucch;
|
||||
@@ -508,6 +512,8 @@ typedef struct NR_sched_pdsch {
|
||||
typedef struct NR_UE_harq {
|
||||
bool is_waiting;
|
||||
uint8_t ndi;
|
||||
uint8_t ndi2; /* independent NDI for the second transport block */
|
||||
uint8_t pending_cw_mask; /* original TBs awaiting ACK; padding CWs are not pending */
|
||||
uint8_t round;
|
||||
uint16_t feedback_frame;
|
||||
uint16_t feedback_slot;
|
||||
|
||||
@@ -1734,8 +1734,13 @@ static NR_SetupRelease_PDSCH_Config_t *config_pdsch(uint64_t ssb_bitmap,
|
||||
dmrs_DownlinkForPDSCH_MappingTypeA->scramblingID0 = NULL;
|
||||
dmrs_DownlinkForPDSCH_MappingTypeA->scramblingID1 = NULL;
|
||||
dmrs_DownlinkForPDSCH_MappingTypeA->dmrs_AdditionalPosition = calloc(1, sizeof(*dmrs_DownlinkForPDSCH_MappingTypeA->dmrs_AdditionalPosition));
|
||||
// TODO possible improvement is to select based on some input additional position
|
||||
*dmrs_DownlinkForPDSCH_MappingTypeA->dmrs_AdditionalPosition = NR_DMRS_DownlinkConfig__dmrs_AdditionalPosition_pos1;
|
||||
AssertFatal(configuration->pdsch_dmrs_additional_position == 0 || configuration->pdsch_dmrs_additional_position == 1,
|
||||
"Invalid PDSCH DMRS additional position %d\n",
|
||||
configuration->pdsch_dmrs_additional_position);
|
||||
*dmrs_DownlinkForPDSCH_MappingTypeA->dmrs_AdditionalPosition =
|
||||
configuration->pdsch_dmrs_additional_position == 0
|
||||
? NR_DMRS_DownlinkConfig__dmrs_AdditionalPosition_pos0
|
||||
: NR_DMRS_DownlinkConfig__dmrs_AdditionalPosition_pos1;
|
||||
|
||||
pdsch_Config->dataScramblingIdentityPDSCH = NULL;
|
||||
pdsch_Config->resourceAllocation = NR_PDSCH_Config__resourceAllocation_resourceAllocationType1;
|
||||
|
||||
@@ -123,7 +123,7 @@ nr_rlc_entity_t *new_nr_rlc_entity_am(int rx_maxsize,
|
||||
/* let's take average over the last 100 milliseconds
|
||||
* initial_size of 1024 (packets) is arbitrary
|
||||
*/
|
||||
ret->common.txsdu_avg_time_to_tx = time_average_new(100 * 1000, 1024);
|
||||
ret->common.txsdu_avg_time_to_tx = time_average_new(1000 * 1000, 1024);
|
||||
|
||||
ret->rx = nr_rlc_new_rx_manager(1 << (sn_field_length - 1));
|
||||
|
||||
@@ -176,7 +176,7 @@ nr_rlc_entity_t *new_nr_rlc_entity_um(int rx_maxsize,
|
||||
/* let's take average over the last 100 milliseconds
|
||||
* initial_size of 1024 is arbitrary
|
||||
*/
|
||||
ret->common.txsdu_avg_time_to_tx = time_average_new(100 * 1000, 1024);
|
||||
ret->common.txsdu_avg_time_to_tx = time_average_new(1000 * 1000, 1024);
|
||||
|
||||
return (nr_rlc_entity_t *)ret;
|
||||
}
|
||||
@@ -215,7 +215,7 @@ nr_rlc_entity_t *new_nr_rlc_entity_tm(int tx_maxsize,
|
||||
/* let's take average over the last 100 milliseconds
|
||||
* initial_size of 1024 is arbitrary
|
||||
*/
|
||||
ret->common.txsdu_avg_time_to_tx = time_average_new(100 * 1000, 1024);
|
||||
ret->common.txsdu_avg_time_to_tx = time_average_new(1000 * 1000, 1024);
|
||||
|
||||
return (nr_rlc_entity_t *)ret;
|
||||
}
|
||||
|
||||
@@ -1023,8 +1023,15 @@ void nr_rlc_activate_avg_time_to_tx(const int ue_id,
|
||||
}
|
||||
|
||||
if (rb != NULL) {
|
||||
// Reset the accumulator only on an off->on transition, NOT on every call.
|
||||
// KPM measurement fills call this once per report period (after reading the
|
||||
// stats); resetting on every call wiped the sliding-window average before it
|
||||
// could accumulate, forcing a fallback to the noisy instantaneous HOL
|
||||
// snapshot. The window self-expires old samples, so no periodic reset is
|
||||
// needed -- activate once and let it accumulate continuously.
|
||||
if (is_on && !rb->avg_time_is_on)
|
||||
time_average_reset(rb->txsdu_avg_time_to_tx);
|
||||
rb->avg_time_is_on = is_on;
|
||||
time_average_reset(rb->txsdu_avg_time_to_tx);
|
||||
} else {
|
||||
LOG_E(RLC, "Radio Bearer (channel ID %d) is NULL for UE %d\n", channel_id, ue_id);
|
||||
}
|
||||
|
||||
@@ -84,6 +84,10 @@
|
||||
|
||||
#ifdef E2_AGENT
|
||||
#include "openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_extern.h"
|
||||
/* PHY simulators link RRC with E2 enabled, but do not link the RC service.
|
||||
* Keep RC's real mutex when present and avoid a hard simulator dependency. */
|
||||
extern void rc_teardown_lock(void) __attribute__((weak));
|
||||
extern void rc_teardown_unlock(void) __attribute__((weak));
|
||||
#define E2_AGENT_SIGNAL_DL_DCCH_RRC_MSG(BUF, LEN, ID) \
|
||||
do { \
|
||||
byte_array_t buffer_ba = {.len = LEN}; \
|
||||
@@ -2735,11 +2739,14 @@ void rrc_remove_ue(gNB_RRC_INST *rrc, rrc_gNB_ue_context_t *ue_context_p)
|
||||
LOG_I(NR_RRC, "removed UE CU UE ID %u/RNTI %04x \n", UE->rrc_ue_id, UE->rnti);
|
||||
rrc_delete_ue_data(UE);
|
||||
#ifdef E2_AGENT
|
||||
rc_teardown_lock(); /* block in-flight RC indications during teardown */
|
||||
const bool rc_lock_available = rc_teardown_lock != NULL && rc_teardown_unlock != NULL;
|
||||
if (rc_lock_available)
|
||||
rc_teardown_lock(); /* block in-flight RC indications during teardown */
|
||||
#endif
|
||||
rrc_gNB_remove_ue_context(rrc, ue_context_p);
|
||||
#ifdef E2_AGENT
|
||||
rc_teardown_unlock();
|
||||
if (rc_lock_available)
|
||||
rc_teardown_unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user