Commit 03c73866 authored by Robert Schmidt's avatar Robert Schmidt

Merge remote-tracking branch...

Merge remote-tracking branch 'origin/NR_gNB_MAC_improvements_for_analog_beam_management' into integration_2025_w02 (!3101)

NR gNB improvements for analog beam management

- Improvements at gNB MAC layer to properly handle analog beam
  management for all channels
- Simple beam switching mechanism based on UE RSRP report
parents 8fd6f1c6 cb6eaa1d
......@@ -192,10 +192,10 @@ In the `MACRLCs` section of the gNB/DU configuration file:
* `identity_precoding_matrix` (default 0=false): flag to enable to use only
the identity precoding matrix in DL precoding
* `set_analog_beamforming` (default 0=false): flag to enable analog
beamforming
* `beam_duration`: duration/number of consecutive slots for a given set of
beamforming (for more information [`analog_beamforming.md`](../analog_beamforming.md))
* `beam_duration` (default 1): duration/number of consecutive slots for a given set of
beams, depending on hardware switching performance
* `beams_per_period`: set of beams that can be simultaneously allocated in a
* `beams_per_period` (default 1): set of beams that can be simultaneously allocated in a
period (`beam_duration`)
In the `gNBs` section of the gNB/DU configuration file: some of the parameters
......
......@@ -84,6 +84,7 @@ Legacy unmaintained files:
- [L1 threads in NR-UE](./nr-ue-design.md)
- [Information on gNB MAC](./MAC/mac-usage.md)
- [Information on gNB RRC](./RRC/rrc-usage.md)
- [Information on analog beamforming implementation](./analog_beamforming.md)
Legacy unmaintained files:
- [`5Gnas.md`](./5Gnas.md)
......
......@@ -220,14 +220,10 @@ Calls nr_schedule_ulsch(): It is divided into the "preprocessor" and the
"postprocessor": the first makes the scheduling decisions, the second fills
nFAPI structures to indicate to the PHY what it is supposed to do. To signal
which users have how many resources, the preprocessor populates the
NR_sched_pusch_t (for values changing every TTI, e.g., frequency domain
allocation) and NR_sched_pusch_save_t (for values changing less frequently, at
least in FR1 [to my understanding], e.g., DMRS fields when the time domain
allocation stays between TTIs) structures. Furthermore, the preprocessor is an
NR_sched_pusch_t structures. Furthermore, the preprocessor is an
exchangeable module that schedules differently based on a particular
use-case/deployment type, e.g., one user for phytest [in
nr_ul_preprocessor_phytest()], multiple users in FR1
[nr_fr1_ulsch_preprocessor()], or maybe FR2 [does not exist yet]:
use-case/deployment type, e.g., one user for phytest in
[nr_ul_preprocessor_phytest()], multiple users in [nr_ulsch_preprocessor()]:
* calls preprocessor via pre_processor_ul(): the preprocessor is responsible
for allocating CCEs (using allocate_nr_CCEs()) and deciding on resource
allocation for the UEs including TB size. Note that we do not yet have
......@@ -258,8 +254,7 @@ NR_UE_sched_ctrl_t structure of affected users. In particular, the field rbSize
decides whether a user is to be allocated. Furthermore, the preprocessor is an
exchangeable module that schedules differently based on a particular
use-case/deployment type, e.g., one user for phytest [in
nr_preprocessor_phytest()], multiple users in FR1
[nr_fr1_dlsch_preprocessor()], or maybe FR2 [does not exist yet].
nr_preprocessor_phytest()], multiple users [nr_dlsch_preprocessor()].
* calls preprocessor via pre_processor_dl(): the preprocessor is responsible
for allocating CCEs and PUCCH (using allocate_nr_CCEs() and
nr_acknack_scheduling()) and deciding on the frequency/time domain
......
This document explains the implementation of analog beamforming in OAI codebase.
[[_TOC_]]
# Introduction to analog beamforming
Beamforming is a technique applied to antenna arrays to create a directional radiation pattern. This often consists in providing a different phase shift to each element of the array such that signals with a different angle of arrival/departure experience a change in radiation pattern because of constructive or destructive interference.
There are three main beamforming techinques: analog, digital and hybrid. The names refer to the phase shift application before or after the digital to analog conversion (or analog to digital in reception). When we speak about analog beamforming we generally refer to a techinique where the phase shifts that produce the beam stearing are applied by the radio unit (RU) choosing from a finite set of steering directions. The advantage of analog beamforming is a simplified analog circuitry and therefore reduced costs.
The presence of a limited number of predefined beams at RU poses constraints to the scheduler at gNB. As a matter of fact, the scheduler can serve only a limited number of beams, depending on the RU characteristics (possibly only 1), in a given time scale, that also depends on the RU characteristics (e.g. 1 slot or 1 symbol). This limitation doesn't exist for digital beamforming.
# Configuration file fields for analog beamforming
A set of parameters in configuration files controls the implementation of analog beamforming and instructs the scheduler on how to behave in such scenarios. Since most notably this technique in 5G is employed in FR2, the configuration file example currently available is a RFsim one for band 261. [Config file example](../ci-scripts/conf_files/gnb.sa.band261.u3.32prb.rfsim.conf)
In the `MACRLC` section of configuration files, there are three new parameters: `set_analog_beamforming`, `beam_duration` and `beams_per_period`. The explanation of these parameters is here provided:
- `set_analog_beamforming` can be set to 1 or 0 to activate or desactivate analog beamforming (default value is 0)
- `beam_duration` is the number of slots (currently minimum duration of a beam) the scheduler is tied to a beam (default value is 1)
- `beams_per_period` is the number of concurrent beams the RU can handle in the beam duration (default value is 1)
In the `gNBs` section of the configuration file, under the phyisical parameters, a vector field containing the set of beam indices to be provided by the OAI L1 to the RU is also required. This field is called `beam_weights`. In current implementation, the number of beam indices should be equal to the number of SSBs transmitted.
# Implementation in OAI scheduler
A new MAC structure `NR_beam_info_t` controls the behavior of the scheduler in presence of analog beamforming. Besides the already mentioned parameters `beam_duration` and `beams_per_period`, the structure also holds a matrix `beam_allocation[i][j]`, whose indices `i` and `j` stands respectively for the number of beams in the period and the slot index (the size of the latter depends on the frame characteristics).
This matrix contains the beams already allocated in a given slot, to flag the scheduler to use one of these to schedule a UE in one of these beams. If the matrix is full (all the beams in the given period, e.g. slot) are already allocated, the scheduler can't allocate a UE in a new beam.
To this goal, we extended the virtual resource block (VRB) map by one dimension to also contain information per allocated beam. As said, the scheduler can independently schedule users in a number of beams up to `beams_per_period` concurrently.
It is important to note that in current implementation, there are several periodical channels, e.g. PRACH or PUCCH for CSI et cetera, that have the precendence in being assigned a beam, that is because the scheduling is automatic, set in RRC configuration, and not up to the scheduler. For these instances, we assume the beam is available (if not there are assertions to stop the process). For data channels, the currently implemented PF scheduler is used. The only modification is that a UE can be served only if there is a free beam available or the one of the beams already in use correspond to that UE beam.
......@@ -564,8 +564,8 @@ typedef struct nr_csi_report {
NR_CSI_ReportConfig__reportQuantity_PR reportQuantity_type;
long periodicity;
uint16_t offset;
long ** SSB_Index_list;
long ** CSI_Index_list;
long **SSB_Index_list;
long **CSI_Index_list;
// uint8_t nb_of_nzp_csi_report;
uint8_t nb_of_csi_ssb_report;
L1_RSRP_bitlen_t CSI_report_bitlen;
......
......@@ -116,6 +116,7 @@ void clear_beam_information(NR_beam_info_t *beam_info, int frame, int slot, int
idx_to_clear = (idx_to_clear + beam_info->beam_allocation_size - 1) % beam_info->beam_allocation_size;
if (slot % beam_info->beam_duration == 0) {
// resetting previous period allocation
LOG_D(NR_MAC, "%d.%d Clear beam information for index %d\n", frame, slot, idx_to_clear);
for (int i = 0; i < beam_info->beams_per_period; i++)
beam_info->beam_allocation[i][idx_to_clear] = -1;
}
......
......@@ -558,23 +558,13 @@ int get_cce_index(const gNB_MAC_INST *nrmac,
const uint32_t Y = is_common ? 0 : get_Y(ss, slot, rnti);
uint8_t nr_of_candidates;
for (int i=0; i<5; i++) {
for (int i = 0; i < 5; i++) {
// for now taking the lowest value among the available aggregation levels
find_aggregation_candidates(aggregation_level,
&nr_of_candidates,
ss,
1<<i);
if(nr_of_candidates>0)
find_aggregation_candidates(aggregation_level, &nr_of_candidates, ss, 1 << i);
if(nr_of_candidates > 0)
break;
}
int CCEIndex = find_pdcch_candidate(nrmac,
CC_id,
*aggregation_level,
nr_of_candidates,
beam_idx,
sched_pdcch,
coreset,
Y);
int CCEIndex = find_pdcch_candidate(nrmac, CC_id, *aggregation_level, nr_of_candidates, beam_idx, sched_pdcch, coreset, Y);
return CCEIndex;
}
......@@ -3185,7 +3175,7 @@ void fapi_beam_index_allocation(NR_ServingCellConfigCommon_t *scc, gNB_MAC_INST
}
}
static inline int get_beam_index(const NR_beam_info_t *beam_info, int frame, int slot, int beam_index, int slots_per_frame)
static inline int get_beam_index(const NR_beam_info_t *beam_info, int frame, int slot, int slots_per_frame)
{
return ((frame * slots_per_frame + slot) / beam_info->beam_duration) % beam_info->beam_allocation_size;
}
......@@ -3196,7 +3186,7 @@ NR_beam_alloc_t beam_allocation_procedure(NR_beam_info_t *beam_info, int frame,
if (!beam_info->beam_allocation)
return (NR_beam_alloc_t) {.new_beam = false, .idx = 0};
const int index = get_beam_index(beam_info, frame, slot, beam_index, slots_per_frame);
const int index = get_beam_index(beam_info, frame, slot, slots_per_frame);
for (int i = 0; i < beam_info->beams_per_period; i++) {
NR_beam_alloc_t beam_struct = {.new_beam = false, .idx = i};
int *beam = &beam_info->beam_allocation[i][index];
......@@ -3204,8 +3194,10 @@ NR_beam_alloc_t beam_allocation_procedure(NR_beam_info_t *beam_info, int frame,
beam_struct.new_beam = true;
*beam = beam_index;
}
if (*beam == beam_index)
if (*beam == beam_index) {
LOG_D(NR_MAC, "%d.%d Using beam structure with index %d for beam %d (%s)\n", frame, slot, beam_struct.idx, beam_index, beam_struct.new_beam ? "new beam" : "old beam");
return beam_struct;
}
}
return (NR_beam_alloc_t) {.new_beam = false, .idx = -1};
......@@ -3215,13 +3207,25 @@ void reset_beam_status(NR_beam_info_t *beam_info, int frame, int slot, int beam_
{
if(!new_beam) // need to reset only if the beam was allocated specifically for this instance
return;
const int index = get_beam_index(beam_info, frame, slot, beam_index, slots_per_frame);
const int index = get_beam_index(beam_info, frame, slot, slots_per_frame);
for (int i = 0; i < beam_info->beams_per_period; i++) {
if (beam_info->beam_allocation[i][index] == beam_index)
beam_info->beam_allocation[i][index] = -1;
}
}
void beam_selection_procedures(gNB_MAC_INST *mac, NR_UE_info_t *UE)
{
RSRP_report_t *rsrp_report = &UE->UE_sched_ctrl.CSI_report.ssb_rsrp_report;
// simple beam switching algorithm -> we select beam with highest RSRP from CSI report
int new_bf_index = get_fapi_beamforming_index(mac, rsrp_report->resource_id[0]);
if (UE->UE_beam_index == new_bf_index)
return; // no beam change needed
LOG_I(NR_MAC, "[UE %x] Switching to beam with ID %d (SSB number %d)\n", UE->rnti, new_bf_index, rsrp_report->resource_id[0]);
UE->UE_beam_index = new_bf_index;
}
void send_initial_ul_rrc_message(int rnti, const uint8_t *sdu, sdu_size_t sdu_len, void *data)
{
gNB_MAC_INST *mac = RC.nrmac[0];
......
......@@ -411,6 +411,7 @@ void nr_srs_ri_computation(const nfapi_nr_srs_normalized_channel_iq_matrix_t *nr
}
static void nr_configure_srs(nfapi_nr_srs_pdu_t *srs_pdu,
int frame,
int slot,
int module_id,
int CC_id,
......@@ -469,9 +470,14 @@ static void nr_configure_srs(nfapi_nr_srs_pdu_t *srs_pdu,
srs_pdu->beamforming.num_prgs = m_SRS[srs_pdu->config_index];
srs_pdu->beamforming.prg_size = 1;
}
// TODO properly use beam allocation
uint16_t *vrb_map_UL = &RC.nrmac[module_id]->common_channels[CC_id].vrb_map_UL[0][buffer_index * MAX_BWP_SIZE];
srs_pdu->beamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx = UE->UE_beam_index;
NR_beam_alloc_t beam = beam_allocation_procedure(&RC.nrmac[module_id]->beam_info,
frame,
slot,
UE->UE_beam_index,
nr_slots_per_frame[current_BWP->scs]);
AssertFatal(beam.idx >= 0, "Cannot allocate SRS in any available beam\n");
uint16_t *vrb_map_UL = &RC.nrmac[module_id]->common_channels[CC_id].vrb_map_UL[beam.idx][buffer_index * MAX_BWP_SIZE];
uint64_t mask = SL_to_bitmap(srs_pdu->time_start_position, srs_pdu->num_symbols);
for (int i = 0; i < srs_pdu->bwp_size; ++i)
vrb_map_UL[i + srs_pdu->bwp_start] |= mask;
......@@ -497,7 +503,7 @@ static void nr_fill_nfapi_srs(int module_id,
memset(srs_pdu, 0, sizeof(nfapi_nr_srs_pdu_t));
future_ul_tti_req->n_pdus += 1;
index = ul_buffer_index(frame, slot, UE->current_UL_BWP.scs, RC.nrmac[module_id]->vrb_map_UL_size);
nr_configure_srs(srs_pdu, slot, module_id, CC_id, UE, srs_resource_set, srs_resource, index);
nr_configure_srs(srs_pdu, frame, slot, module_id, CC_id, UE, srs_resource_set, srs_resource, index);
}
/*******************************************************************
......
......@@ -82,8 +82,8 @@ void nr_schedule_ue_spec(module_id_t module_id,
nfapi_nr_dl_tti_request_t *DL_req,
nfapi_nr_tx_data_request_t *TX_req);
/* \brief default FR1 DL preprocessor init routine, returns preprocessor to call */
nr_pp_impl_dl nr_init_fr1_dlsch_preprocessor(int CC_id);
/* \brief default DL preprocessor init routine, returns preprocessor to call */
nr_pp_impl_dl nr_init_dlsch_preprocessor(int CC_id);
void schedule_nr_sib1(module_id_t module_idP,
frame_t frameP,
......@@ -108,8 +108,8 @@ void schedule_nr_mib(module_id_t module_idP, frame_t frameP, sub_frame_t slotP,
* messages, statistics, HARQ handling, ... */
void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot, nfapi_nr_ul_dci_request_t *ul_dci_req);
/* \brief default FR1 UL preprocessor init routine, returns preprocessor to call */
nr_pp_impl_ul nr_init_fr1_ulsch_preprocessor(int CC_id);
/* \brief default UL preprocessor init routine, returns preprocessor to call */
nr_pp_impl_ul nr_init_ulsch_preprocessor(int CC_id);
/////// Random Access MAC-PHY interface functions and primitives ///////
......@@ -447,7 +447,7 @@ void fapi_beam_index_allocation(NR_ServingCellConfigCommon_t *scc, gNB_MAC_INST
int get_fapi_beamforming_index(gNB_MAC_INST *mac, int ssb_idx);
NR_beam_alloc_t beam_allocation_procedure(NR_beam_info_t *beam_info, int frame, int slot, int beam_index, int slots_per_frame);
void reset_beam_status(NR_beam_info_t *beam_info, int frame, int slot, int beam_index, int slots_per_frame, bool new_beam);
void beam_selection_procedures(gNB_MAC_INST *mac, NR_UE_info_t *UE);
void nr_sr_reporting(gNB_MAC_INST *nrmac, frame_t frameP, sub_frame_t slotP);
size_t dump_mac_stats(gNB_MAC_INST *gNB, char *output, size_t strlen, bool reset_rsrp);
......
......@@ -294,8 +294,8 @@ void mac_top_init_gNB(ngran_node_t node_type,
RC.nrmac[i]->pre_processor_dl = nr_preprocessor_phytest;
RC.nrmac[i]->pre_processor_ul = nr_ul_preprocessor_phytest;
} else {
RC.nrmac[i]->pre_processor_dl = nr_init_fr1_dlsch_preprocessor(0);
RC.nrmac[i]->pre_processor_ul = nr_init_fr1_ulsch_preprocessor(0);
RC.nrmac[i]->pre_processor_dl = nr_init_dlsch_preprocessor(0);
RC.nrmac[i]->pre_processor_ul = nr_init_ulsch_preprocessor(0);
}
if (!IS_SOFTMODEM_NOSTATS)
threadCreate(&RC.nrmac[i]->stats_thread,
......
......@@ -523,16 +523,16 @@ struct CRI_RI_LI_PMI_CQI {
bool print_report;
};
typedef struct CRI_SSB_RSRP {
uint8_t nr_ssbri_cri;
uint8_t CRI_SSBRI[MAX_NR_OF_REPORTED_RS];
uint8_t RSRP;
uint8_t diff_RSRP[MAX_NR_OF_REPORTED_RS - 1];
} CRI_SSB_RSRP_t;
typedef struct RSRP_report {
uint8_t nr_reports;
uint8_t resource_id[MAX_NR_OF_REPORTED_RS];
int RSRP[MAX_NR_OF_REPORTED_RS];
} RSRP_report_t;
struct CSI_Report {
struct CRI_RI_LI_PMI_CQI cri_ri_li_pmi_cqi_report;
struct CRI_SSB_RSRP ssb_cri_report;
RSRP_report_t ssb_rsrp_report;
RSRP_report_t csirs_rsrp_report;
};
#define MAX_SR_BITLEN 8
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment