Compare commits

...

24 Commits

Author SHA1 Message Date
Bartosz Podrygajlo
1d1bd426ed Add a debug print for ORAN DL interface
Added a debug print for the contents of non-zero subcarriers on antenna 0,
symbol 0, frame 0 and symbol 0.
This is intended to be used by developers of O-RU / O-DU to verify correct
compression, delivery and decopression of IQ samples in DL.
2025-09-30 08:56:56 +02:00
Bartosz Podrygajlo
915e3cc12c Fix decompression for O-RU. 2025-09-30 08:56:56 +02:00
Bartosz Podrygajlo
d841911743 Replace tx_rf with tx_rf_symbols in ORU
Fix the O-RU per symbol transmission.
2025-09-30 08:56:56 +02:00
Bartosz Podrygajlo
60fb95985b Add a new tx_rf_symbols function to allow per symbol transmission 2025-09-30 08:56:56 +02:00
Bartosz Podrygajlo
03d9d6540f Add symbol timing functions for NR_DL_FRAME_PARMS
Add two new functions for calculation symbol timestamp and symbol
duration for NR_DL_FRAME_PARMS. These functions are not using
the existing indirection mechanism via function pointers as this
prevents the compiler from properly optimizing the code.
2025-09-30 08:56:56 +02:00
Bartosz Podrygajlo
6c242760f6 O-RU: Reset nSecDesc on symbol reception
This is required as xran does not manage this value by itself.
Not resetting this will cause nSecDesc to go above the maximum
supported number of sections per symbol and sections to be discarded.
2025-09-30 08:56:56 +02:00
Bartosz Podrygajlo
ce97c90603 Fix lte-softmodem and oairu builds 2025-09-30 08:56:56 +02:00
Bartosz Podrygajlo
90c0ab600f Offset timestamp_tx in O-RU by intended symbol
Add sample offset from symbol to timestamp_tx based on symbol
indicated by oran api.
2025-09-30 08:56:56 +02:00
Bartosz Podrygajlo
edbf4b069c Workarond for a bug in callback mechanism in XRAN
Update symbol_callback to detect and correct an issue with the calculated
frame index. This is caused by an issue with the timer functionality
in libxran where if a timer is setup close to the GPS second start
the sfn offset `xran_sfn_at_sec_start` is not setup correctly causing
the frame in xran_sense_of_time struct to be returned with an offset
of 100 frames (1 second).
2025-09-30 08:56:49 +02:00
Bartosz Podrygajlo
4d02c64d6a Ensure ofdm_offset_divisor is set in O-RU.
Hardcode ofdm_offset_divisor to 8 in O-RU.
2025-09-30 08:54:20 +02:00
Bartosz Podrygajlo
0f687981b8 Simplify vrtsim timing job thread. 2025-09-30 08:54:19 +02:00
Bartosz Podrygajlo
8b20a349ad Clean exit for the ORU
- Handle SIGINT
 - Print DL FH processing meas
 - Stop & terminate xran and vrtsim
2025-09-30 08:54:19 +02:00
Bartosz Podrygajlo
c42e069bbb Use synchronized rfdevice to xran timestamps in ORU
Synchronize the southbound (time domain) interface to XRAN timestamps
in ORU. This currently only works with vrtsim.

Transmit the samples after processing in oru_downlink_processing.
2025-09-30 08:54:19 +02:00
Bartosz Podrygajlo
17e1de5667 Load vrtsim in O-RU and start reading samples
Added oru_south_read_thread that reads samples from vrtsim.
No UL FH processing is done as of this commit.
2025-09-30 08:54:19 +02:00
Bartosz Podrygajlo
a28cf72b19 Print xran counters to stdout in xran_fh_tx_read_slot 2025-09-30 08:54:19 +02:00
Bartosz Podrygajlo
d060f06cd4 Add get_timestamp to openair_device_t and vrtsim
This function can be used to convert a timespec struct to a
openair0_timestamp. It can be used to synchronize the realtime
clock to the device sample number.
2025-09-30 08:54:19 +02:00
Bartosz Podrygajlo
200388bef3 Add a periodic print out of the O-RU north read thread 2025-09-30 08:54:19 +02:00
Bartosz Podrygajlo
33fb6de0ce Add a function which loads the specified radio library 2025-09-30 08:54:19 +02:00
Bartosz Podrygajlo
ee4b12f1fd Add a config file for O-RU from broadbill 2025-09-30 08:54:19 +02:00
Bartosz Podrygajlo
5f838bab48 DL FH processing for O-RU
Add DFT and symbol rotation to O-RU
2025-09-30 08:54:19 +02:00
Bartosz Podrygajlo
babf0544e6 O-RU: Add north_read thread
Added north read thread that can utilize north_in function for timing
and providing IQ data from the XRAN library.
2025-09-30 08:54:19 +02:00
Bartosz Podrygajlo
41982f8fa0 Add north_in function to oran_fhlib_5g
The north_in function is supposed to be used by the ORU to read IQ data
sent from O-DU.
2025-09-30 08:54:19 +02:00
Mario Joa-Ng
99fb9f80bb Use no of fh_config in configuration file to determine the number of peers
Determine number of DU for RU config file or no of RU for DU config file
based on number of fh_config elements.
2025-09-30 08:54:19 +02:00
Raymond Knopp
b16a5d9f7c Initial implementation of O-RU for 7.2 emulator
Added new executable oainr_ru which acts as a 7.2 O-RU.

Co-authored-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
Co-authored-by: Mario Joa-Ng <mario.joa-ng@openairinterface.org>
2025-09-30 08:54:19 +02:00
29 changed files with 1271 additions and 76 deletions

View File

@@ -1829,6 +1829,30 @@ target_link_libraries(lte-uesoftmodem PRIVATE ${blas_LIBRARIES} ${cblas_LIBRARIE
target_link_libraries(lte-uesoftmodem PRIVATE
asn1_lte_rrc asn1_s1ap asn1_m2ap asn1_m3ap asn1_x2ap)
# nr RRU
add_executable(oainr_ru
${OPENAIR_DIR}/executables/nr-ru.c
${OPENAIR_DIR}/openair1/PHY/INIT/nr_parms.c
${OPENAIR_DIR}/openair1/SCHED_NR/phy_frame_config_nr.c
${OPENAIR_DIR}/openair1/PHY/INIT/nr_parms.c
${OPENAIR_DIR}/openair1/SCHED_NR/nr_prach_procedures.c
${OPENAIR_DIR}/openair1/SCHED/phy_procedures_lte_common.c
${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_prach.c
${OPENAIR_DIR}/common/utils/nr/nr_common.c
${OPENAIR_DIR}/radio/COMMON/common_lib.c
${OPENAIR_DIR}/radio/COMMON/record_player.c
${OPENAIR_DIR}/executables/softmodem-common.c
${OPENAIR_DIR}/executables/main_nr_ru.c
${OPENAIR_DIR}/executables/nr-oru.c
)
target_link_libraries(oainr_ru PRIVATE
-Wl,--start-group
UTIL SCHED_RU_LIB PHY_COMMON PHY_RU shlib_loader
-Wl,--end-group z dl)
target_link_libraries(oainr_ru PRIVATE pthread m CONFIG_LIB rt ${T_LIB} utils
barrier actor)
target_link_libraries(oainr_ru PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs nr_phy_common time_management)
# nr-softmodem
###################################################

View File

@@ -82,6 +82,8 @@ Options:
Makes the NR softmodem
--RU
Makes the OAI RRU
--nrRU
Makes the OAI nrRRU
--UE
Makes the UE specific parts (usim, nvram) from the given configuration file
--nrUE
@@ -227,6 +229,11 @@ function main() {
TARGET_LIST="$TARGET_LIST oairu"
echo_info "Will compile RRU"
shift;;
--nrRU)
nrRU=1
TARGET_LIST="$TARGET_LIST oainr_ru"
echo_info "Will compile nrRRU"
shift;;
--UE)
UE=1
TARGET_LIST="$TARGET_LIST lte-uesoftmodem conf2uedata usim nvram"
@@ -456,7 +463,7 @@ function main() {
if [[ $TARGET_LIST != "" ]] && [[ -f $OPENAIR_DIR/CMakeLists.txt ]]; then
# add some default libraries that should always be built
# for eNB, gNB, UEs, simulators
if [[ $gNB == 1 || $eNB == 1 || $UE == 1 || $nrUE == 1 || $SIMUS_PHY == 1 || $RU == 1 ]]; then
if [[ $gNB == 1 || $eNB == 1 || $UE == 1 || $nrUE == 1 || $SIMUS_PHY == 1 || $RU == 1 || $nrRU == 1 ]]; then
TARGET_LIST="$TARGET_LIST params_libconfig coding rfsimulator dfts params_yaml vrtsim rf_emulator"
fi

View File

@@ -65,7 +65,8 @@ static int DEFBANDS[] = {7};
static int DEFENBS[] = {0};
static int DEFBFW[] = {0x00007fff};
static int DEFRUTPCORES[] = {2,4,6,8};
static int DEFBW[] = {273};
static int DEFCARRIER[] = {3430560};
#include "ENB_APP/enb_paramdef.h"
#include "common/config/config_userapi.h"

263
executables/main_nr_ru.c Normal file
View File

@@ -0,0 +1,263 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
/*! \file oairu.c
* \brief Top-level threads for radio-unit
* \author R. Knopp
* \date 2020
* \version 0.1
* \company Eurecom
* \email: knopp@eurecom.fr
* \note
* \warning
*/
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <sched.h>
#include "assertions.h"
#include "PHY/types.h"
#include "PHY/defs_RU.h"
#include "common/oai_version.h"
#include "common/config/config_userapi.h"
#include "common/utils/load_module_shlib.h"
#include "common/ran_context.h"
#include "radio/COMMON/common_lib.h"
#include "radio/ETHERNET/if_defs.h"
#include "PHY/phy_vars.h"
#include "PHY/phy_extern.h"
#include "PHY/TOOLS/phy_scope_interface.h"
#include "common/utils/LOG/log.h"
#include "common/utils/LOG/vcd_signal_dumper.h"
// #include "PHY/INIT/phy_init.h"
#include "openair2/ENB_APP/enb_paramdef.h"
#include "system.h"
#include "nfapi/oai_integration/vendor_ext.h"
#include <executables/softmodem-common.h>
#include <executables/thread-common.h>
#include "nr-oru.h"
#include "openair1/PHY/INIT/nr_phy_init.h"
#include "openair1/SCHED_NR/sched_nr.h"
pthread_cond_t sync_cond;
pthread_mutex_t sync_mutex;
int sync_var = -1; //!< protected by mutex \ref sync_mutex.
int config_sync_var = -1;
int oai_exit = 0;
int sf_ahead = 4;
int emulate_rf = 0;
RAN_CONTEXT_t RC;
extern void kill_NR_RU_proc(int inst);
extern void set_function_spec_param(RU_t *ru);
extern void start_NR_RU(RU_t *ru);
extern void init_NR_RU(configmodule_interface_t *cfg, char *);
int32_t uplink_frequency_offset[MAX_NUM_CCs][4];
void nfapi_setmode(nfapi_mode_t nfapi_mode)
{
return;
}
void exit_function(const char *file, const char *function, const int line, const char *s, const int assert)
{
if (s != NULL) {
printf("%s:%d %s() Exiting OAI softmodem: %s\n", file, line, function, s);
}
close_log_mem();
oai_exit = 1;
if (assert)
abort();
}
static void get_options(configmodule_interface_t *cfg)
{
CONFIG_SETRTFLAG(CONFIG_NOEXITONHELP);
get_common_options(cfg);
CONFIG_CLEARRTFLAG(CONFIG_NOEXITONHELP);
// NRCConfig();
}
nfapi_mode_t nfapi_getmode(void)
{
return (NFAPI_MODE_PNF);
}
void oai_nfapi_rach_ind(nfapi_rach_indication_t *rach_ind)
{
AssertFatal(1 == 0, "This is bad ... please check why we get here\n");
}
void wait_eNBs(void)
{
return;
}
void wait_gNBs(void)
{
return;
}
struct timespec timespec_add(struct timespec, struct timespec)
{
struct timespec t = {0};
return t;
};
struct timespec timespec_sub(struct timespec, struct timespec)
{
struct timespec t = {0};
return t;
};
void perform_symbol_rotation(NR_DL_FRAME_PARMS *fp, double f0, c16_t *symbol_rotation)
{
return;
}
void init_timeshift_rotation(NR_DL_FRAME_PARMS *fp)
{
return;
};
int beam_index_allocation(bool das,
int fapi_beam_index,
nfapi_nr_analog_beamforming_ve_t *analog_bf,
NR_gNB_COMMON *common_vars,
int slot,
int symbols_per_slot,
int bitmap_symbols)
{
return 0;
}
void nr_fill_du(uint16_t N_ZC, const uint16_t *prach_root_sequence_map, uint16_t nr_du[NR_PRACH_SEQ_LEN_L - 1])
{
return;
};
uint16_t nr_du[838];
uint64_t downlink_frequency[MAX_NUM_CCs][4];
configmodule_interface_t *uniqCfg = NULL;
THREAD_STRUCT thread_struct;
extern void fill_rf_config(RU_t *ru, char *rf_config_file);
extern void fill_split7_2_config(split7_config_t *split7, const nfapi_nr_config_request_scf_t *config, const NR_DL_FRAME_PARMS *fp);
void stop_ru(int sig)
{
exit_function(__FILE__, __FUNCTION__, __LINE__, "interrupted", false);
}
int main(int argc, char **argv)
{
memset(&RC, 0, sizeof(RC));
if ((uniqCfg = load_configmodule(argc, argv, 0)) == NULL) {
exit_fun("[SOFTMODEM] Error, configuration module init failed\n");
}
logInit();
printf("Reading in command-line options\n");
get_options(uniqCfg);
if (CONFIG_ISFLAGSET(CONFIG_ABORT)) {
fprintf(stderr, "Getting configuration failed\n");
exit(-1);
}
#if T_TRACER
T_Config_Init();
#endif
printf("configuring for RRU\n");
// strdup to put the sring in the core file for post mortem identification
LOG_I(HW, "Version: %s\n", strdup(OAI_PACKAGE_VERSION));
/* Read configuration */
printf("About to Init RU threads\n");
lock_memory_to_ram();
load_dftslib();
RC.nb_RU = 1;
RC.ru = malloc(sizeof(RC.ru));
init_NR_RU(config_get_if(), NULL);
RU_t *ru = RC.ru[0];
ORU_t oru;
oru.ru = ru;
cpumeas(CPUMEAS_ENABLE);
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
nr_dump_frame_parms(fp);
fp->ofdm_offset_divisor = 8;
ru->if_south = LOCAL_RF;
nr_phy_init_RU(ru);
fill_rf_config(ru, ru->rf_config_file);
fill_split7_2_config(&ru->openair0_cfg.split7, &ru->config, fp);
ru->N_TA_offset = set_default_nta_offset(fp->freq_range, fp->samples_per_subframe);
int ret = openair0_transport_load(&ru->ifdevice, &ru->openair0_cfg, &ru->eth_params);
AssertFatal(ret == 0, "RU %u: openair0_transport_init() ret %d: cannot initialize transport protocol\n", ru->idx, ret);
ret = ru->nr_start_if(ru, NULL);
AssertFatal(ret == 0, "Could not start xran\n");
LOG_I(PHY, "starting vrtsim\n");
ret = openair0_load(&ru->rfdevice, "vrtsim", &ru->openair0_cfg, NULL);
AssertFatal(ret == 0, "RU %u: openair0_load() ret %d: cannot initialize vrtsim\n", ru->idx, ret);
ret = ru->rfdevice.trx_start_func(&ru->rfdevice);
AssertFatal(ret == 0, "RU %u: trx_start_func() ret %d: cannot start vrtsim\n", ru->idx, ret);
signal(SIGINT, stop_ru);
threadCreate(&oru.north_read_thread, oru_north_read_thread, (void *)&oru, "north_read_thread", -1, OAI_PRIORITY_RT_MAX);
threadCreate(&oru.south_read_thread, oru_south_read_thread, (void *)&oru, "north_read_thread", -1, OAI_PRIORITY_RT_MAX);
while (oai_exit == 0) {
sleep(1);
}
ret = pthread_join(oru.north_read_thread, NULL);
AssertFatal(ret == 0, "pthread_join failed %d\n", ret);
ret = pthread_join(oru.south_read_thread, NULL);
AssertFatal(ret == 0, "pthread_join failed %d\n", ret);
LOG_I(PHY, "Threads joined\n");
end_configmodule(uniqCfg);
if (ru->rfdevice.trx_stop_func) {
ru->rfdevice.trx_stop_func(&ru->rfdevice);
}
if (ru->rfdevice.trx_end_func) {
ru->rfdevice.trx_end_func(&ru->rfdevice);
}
if (ru->ifdevice.trx_stop_func) {
ru->ifdevice.trx_stop_func(&ru->ifdevice);
}
if (ru->ifdevice.trx_end_func) {
ru->ifdevice.trx_end_func(&ru->ifdevice);
}
print_meas(&ru->tx_fhaul, "TX FH processing", NULL, NULL);
logClean();
printf("Bye.\n");
return 0;
}

View File

@@ -65,6 +65,8 @@
static int DEFBANDS[] = {7};
static int DEFENBS[] = {0};
static int DEFBFW[] = {0x00007fff};
static int DEFBW[] = {273};
static int DEFCARRIER[] = {3430560};
static int DEFRUTPCORES[] = {2,4,6,8};
THREAD_STRUCT thread_struct;

173
executables/nr-oru.c Normal file
View File

@@ -0,0 +1,173 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#define _GNU_SOURCE
#include "nr-oru.h"
#include "openair1/PHY/defs_nr_common.h"
#include "openair1/PHY/INIT/nr_phy_init.h"
#include "openair1/SCHED_NR/sched_nr.h"
#include <sched.h>
typedef struct {
openair0_timestamp sample;
int slot;
int frame;
int symbol;
} initial_sync_t;
typedef struct {
int frame_unwrap;
int last_frame;
int64_t sync_offset;
initial_sync_t initial_sync;
} sync_params_t;
extern void tx_rf_symbols(RU_t *ru, int frame, int slot, uint64_t timestamp, int start_symbol, int num_symbols);
void perform_initial_sync(ORU_t *oru, sense_of_time_t *sense_of_time, sync_params_t *sync_params)
{
initial_sync_t *initial_sync = &sync_params->initial_sync;
initial_sync->frame = sense_of_time->frame;
initial_sync->slot = sense_of_time->slot;
initial_sync->symbol = sense_of_time->symbol;
initial_sync->sample = oru->ru->rfdevice.get_timestamp(&oru->ru->rfdevice, &sense_of_time->ts);
NR_DL_FRAME_PARMS *fp = oru->ru->nr_frame_parms;
sync_params->frame_unwrap = 0;
sync_params->last_frame = initial_sync->frame;
sync_params->sync_offset = initial_sync->sample;
sync_params->sync_offset -= (uint64_t)(sync_params->initial_sync.frame) * fp->samples_per_subframe * 10
+ fp->get_samples_slot_timestamp(sync_params->initial_sync.slot, fp, 0);
LOG_I(PHY,
"RU synchronized: frame, slot %d.%d, symbol %d, offset: %ld\n",
initial_sync->frame,
initial_sync->slot,
initial_sync->symbol,
sync_params->sync_offset);
}
openair0_timestamp get_timestamp(ORU_t *oru, sense_of_time_t *sense_of_time, sync_params_t *sync_params)
{
if (sync_params->last_frame > sense_of_time->frame) {
sync_params->frame_unwrap++;
}
sync_params->last_frame = sense_of_time->frame;
NR_DL_FRAME_PARMS *fp = oru->ru->nr_frame_parms;
int num_frames = sense_of_time->frame + sync_params->frame_unwrap * 1024;
uint64_t timestamp = (uint64_t)(num_frames)*fp->samples_per_subframe * 10
+ fp->get_samples_slot_timestamp(sense_of_time->slot, fp, 0)
+ get_samples_symbol_timestamp(fp, sense_of_time->slot, sense_of_time->symbol);
timestamp += sync_params->sync_offset;
return timestamp;
}
void oru_downlink_processing(RU_t *ru,
c16_t *txDataF_ptr[ru->nb_tx],
int frame,
int slot,
int start_symbol,
int num_symbols,
openair0_timestamp timestamp_tx)
{
start_meas(&ru->tx_fhaul);
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
for (int aatx = 0; aatx < ru->nb_tx; aatx++) {
apply_nr_rotation_TX(fp, txDataF_ptr[aatx], fp->symbol_rotation[0], slot, fp->N_RB_DL, start_symbol, num_symbols);
nr_feptx0(ru, slot, start_symbol, num_symbols, aatx);
}
tx_rf_symbols(ru, frame, slot, timestamp_tx, start_symbol, num_symbols);
stop_meas(&ru->tx_fhaul);
}
void *oru_north_read_thread(void *arg)
{
ORU_t *oru = (ORU_t *)arg;
RU_t *ru = (RU_t *)oru->ru;
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
char threadname[40];
sprintf(threadname, "oru_thread %u", ru->idx);
AssertFatal(ru->ifdevice.xran_api.north_in_func != NULL, "No fronthaul interface at north port");
__attribute__((aligned(32))) c16_t txDataF[ru->nb_tx][ceil_mod(fp->ofdm_symbol_size * 14, 32)];
c16_t *txDataF_ptr[ru->nb_tx];
for (int aatx = 0; aatx < ru->nb_tx; aatx++) {
txDataF_ptr[aatx] = txDataF[aatx];
}
sync_params_t sync_params;
while (!oai_exit) {
int num_symbols = 0;
sense_of_time_t sense_of_time;
ru->ifdevice.xran_api.north_in_func((uint32_t **)txDataF_ptr, ru->nb_tx, &sense_of_time, &num_symbols);
if (sense_of_time.symbol == 0) {
perform_initial_sync(oru, &sense_of_time, &sync_params);
break;
}
}
while (!oai_exit) {
int num_symbols = 0;
sense_of_time_t sense_of_time;
ru->ifdevice.xran_api.north_in_func((uint32_t **)txDataF_ptr, ru->nb_tx, &sense_of_time, &num_symbols);
openair0_timestamp timestamp_tx = get_timestamp(oru, &sense_of_time, &sync_params);
if ((sense_of_time.frame & 0xff) == 0 && sense_of_time.slot == 0) {
LOG_I(PHY,
"[RU_thread] read data: frame %d, slot %d, start_symbol %d, num_symbols %d\n",
sense_of_time.frame,
sense_of_time.slot,
sense_of_time.symbol,
num_symbols);
}
nfapi_nr_config_request_scf_t *cfg = &ru->config;
int slot_type = nr_slot_select(cfg, sense_of_time.frame, sense_of_time.slot % fp->slots_per_frame);
if (slot_type != NR_UPLINK_SLOT)
oru_downlink_processing(ru,
txDataF_ptr,
sense_of_time.frame,
sense_of_time.slot,
sense_of_time.symbol,
num_symbols,
timestamp_tx);
}
return NULL;
}
void *oru_south_read_thread(void *arg)
{
ORU_t *oru = arg;
RU_t *ru = oru->ru;
const int num_samples = 3000;
c16_t throwaway_samples[ru->nb_rx][num_samples];
void *rxp[ru->nb_rx];
for (int i = 0; i < ru->nb_rx; i++)
rxp[i] = throwaway_samples[i];
openair0_timestamp timestamp;
while (!oai_exit) {
ru->rfdevice.trx_read_func(&ru->rfdevice, &timestamp, rxp, num_samples, ru->nb_rx);
}
// Perform RX processing
return NULL;
}

36
executables/nr-oru.h Normal file
View File

@@ -0,0 +1,36 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#ifndef NR_ORU_H
#define NR_ORU_H
#include "nr-oru.h"
#include <executables/softmodem-common.h>
#include "openair1/PHY/defs_RU.h"
typedef struct {
pthread_t north_read_thread;
pthread_t south_read_thread;
RU_t *ru;
} ORU_t;
void *oru_north_read_thread(void *arg);
void *oru_south_read_thread(void *arg);
#endif

View File

@@ -56,6 +56,8 @@ static int DEFBANDS[] = {7};
static int DEFENBS[] = {0};
static int DEFBFW[] = {0x00007fff};
static int DEFRUTPCORES[] = {-1,-1,-1,-1};
static int DEFBW[] = {273};
static int DEFCARRIER[] = {3430560};
#include "ENB_APP/enb_paramdef.h"
#include "GNB_APP/gnb_paramdef.h"
@@ -714,7 +716,7 @@ static radio_tx_gpio_flag_t get_gpio_flags(RU_t *ru, int slot)
return flags_gpio;
}
void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
void tx_rf_symbols(RU_t *ru, int frame, int slot, uint64_t timestamp, int start_symbol, int num_symbols)
{
RU_proc_t *proc = &ru->proc;
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
@@ -727,7 +729,7 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
T_INT(0),
T_BUFFER(&ru->common.txdata[0][fp->get_samples_slot_timestamp(slot, fp, 0)], fp->get_samples_per_slot(slot, fp) * 4));
int sf_extension = 0;
int siglen=fp->get_samples_per_slot(slot,fp);
uint32_t siglen = 0;
radio_tx_burst_flag_t flags_burst = TX_BURST_INVALID;
radio_tx_gpio_flag_t flags_gpio = 0;
@@ -741,22 +743,23 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
txsymb++;
}
AssertFatal(txsymb>0,"illegal txsymb %d\n",txsymb);
AssertFatal(txsymb > 0, "illegal txsymb %d\n", txsymb);
if (fp->slots_per_subframe == 1) {
if (txsymb <= 7)
siglen = (fp->ofdm_symbol_size + fp->nb_prefix_samples0) + (txsymb - 1) * (fp->ofdm_symbol_size + fp->nb_prefix_samples);
else
siglen = 2 * (fp->ofdm_symbol_size + fp->nb_prefix_samples0) + (txsymb - 2) * (fp->ofdm_symbol_size + fp->nb_prefix_samples);
} else {
if(slot%(fp->slots_per_subframe/2))
siglen = txsymb * (fp->ofdm_symbol_size + fp->nb_prefix_samples);
else
siglen = (fp->ofdm_symbol_size + fp->nb_prefix_samples0) + (txsymb - 1) * (fp->ofdm_symbol_size + fp->nb_prefix_samples);
if (txsymb < start_symbol) {
// No DL symbols in this transmission
return;
}
//+ ru->end_of_burst_delay;
flags_burst = TX_BURST_END;
int end_symbol = start_symbol + num_symbols - 1;
if (end_symbol >= txsymb) {
flags_burst = TX_BURST_END;
} else {
flags_burst = TX_BURST_MIDDLE;
}
int num_symbols_this_transmission = min(txsymb, end_symbol) - start_symbol + 1;
siglen = get_samples_symbol_duration(fp, slot, start_symbol, num_symbols_this_transmission);
} else if (slot_type == NR_DOWNLINK_SLOT) {
int prevslot_type = nr_slot_select(cfg,frame,(slot+(fp->slots_per_frame-1))%fp->slots_per_frame);
int nextslot_type = nr_slot_select(cfg,frame,(slot+1)%fp->slots_per_frame);
@@ -768,9 +771,11 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
} else {
flags_burst = proc->first_tx == 1 ? TX_BURST_START : TX_BURST_MIDDLE;
}
siglen = get_samples_symbol_duration(fp, slot, start_symbol, num_symbols);
}
} else { // FDD
flags_burst = proc->first_tx == 1 ? TX_BURST_START : TX_BURST_MIDDLE;
siglen = get_samples_symbol_duration(fp, slot, start_symbol, num_symbols);
}
if (ru->openair0_cfg.gpio_controller != RU_GPIO_CONTROL_NONE)
@@ -785,8 +790,9 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
int nt = ru->nb_tx * ru->num_beams_period;
void *txp[nt];
uint32_t time_offset = fp->get_samples_slot_timestamp(slot, fp, 0) + get_samples_symbol_timestamp(fp, slot, start_symbol);
for (int i = 0; i < nt; i++)
txp[i] = (void *)&ru->common.txdata[i][fp->get_samples_slot_timestamp(slot, fp, 0)] - sf_extension * sizeof(int32_t);
txp[i] = (void *)&ru->common.txdata[i][time_offset] - sf_extension * sizeof(int32_t);
VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST, (timestamp + ru->ts_offset) & 0xffffffff);
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_WRITE, 1);
@@ -813,7 +819,12 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_WRITE, 0);
}
static void fill_rf_config(RU_t *ru, char *rf_config_file)
void tx_rf(RU_t *ru, int frame, int slot, uint64_t timestamp)
{
tx_rf_symbols(ru, frame, slot, timestamp, 0, 14);
}
void fill_rf_config(RU_t *ru, char *rf_config_file)
{
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
nfapi_nr_config_request_scf_t *config = &ru->config; //tmp index
@@ -878,7 +889,7 @@ static void fill_rf_config(RU_t *ru, char *rf_config_file)
}
}
static void fill_split7_2_config(split7_config_t *split7, const nfapi_nr_config_request_scf_t *config, const NR_DL_FRAME_PARMS *fp)
void fill_split7_2_config(split7_config_t *split7, const nfapi_nr_config_request_scf_t *config, const NR_DL_FRAME_PARMS *fp)
{
const nfapi_nr_prach_config_t *prach_config = &config->prach_config;
const nfapi_nr_tdd_table_t *tdd_table = &config->tdd_table;
@@ -1487,6 +1498,7 @@ void set_function_spec_param(RU_t *ru)
reset_meas(&ru->tx_fhaul);
reset_meas(&ru->compression);
reset_meas(&ru->transport);
LOG_I(NR_PHY,"Setting IF4p5 (7.2 split)\n");
} else if (ru->function == gNodeB_3GPP) {
ru->do_prach = 0; // no prach processing in RU
ru->feprx = nr_fep_tp; // this is frequency-shift + DFTs
@@ -1578,8 +1590,13 @@ void init_NR_RU(configmodule_interface_t *cfg, char *rf_config_file)
}
}
PHY_VARS_gNB *gNB_RC = RC.gNB[0];
PHY_VARS_gNB *gNB0 = ru->gNB_list[0];
PHY_VARS_gNB *gNB_RC = NULL;
PHY_VARS_gNB *gNB0 = NULL;
if (RC.nb_nr_L1_inst > 0) {
gNB_RC = RC.gNB[0];
gNB0 = ru->gNB_list[0];
}
LOG_D(PHY, "RU FUnction:%d ru->if_south:%d\n", ru->function, ru->if_south);
if (gNB0) {
@@ -1595,6 +1612,9 @@ void init_NR_RU(configmodule_interface_t *cfg, char *rf_config_file)
}
}
}
else {
nr_ru_init_frame_parms(ru);
}
set_function_spec_param(ru);
init_RU_proc(ru);
if (ru->if_south != REMOTE_IF4p5) {
@@ -1621,9 +1641,8 @@ void init_NR_RU(configmodule_interface_t *cfg, char *rf_config_file)
LOG_D(HW,"[nr-softmodem.c] RU threads created\n");
}
void start_NR_RU()
void start_NR_RU(RU_t *ru)
{
RU_t *ru = RC.ru[0];
start_RU_proc(ru);
}
@@ -1814,8 +1833,23 @@ static void NRRCconfig_RU(configmodule_interface_t *cfg)
ru->if_freq_offset = *param[RU_IF_FREQ_OFFSET].iptr;
ru->sl_ahead = *param[RU_SL_AHEAD].iptr;
ru->num_bands = param[RU_BAND_LIST_IDX].numelt;
for (int i = 0; i < ru->num_bands; i++)
for (int i = 0; i < ru->num_bands; i++) {
ru->band[i] = param[RU_BAND_LIST_IDX].iptr[i];
ru->bw_tx[i] = param[RU_TX_BW_LIST_IDX].iptr[i];
ru->bw_rx[i] = param[RU_RX_BW_LIST_IDX].iptr[i];
ru->carrier_freq_tx[i] = param[RU_TX_CARRIER_LIST_IDX].iptr[i];
ru->carrier_freq_rx[i] = param[RU_RX_CARRIER_LIST_IDX].iptr[i];
}
ru->frame_type = *param[RU_FRAME_TYPE_IDX].iptr;
ru->prach_config_index = *param[RU_PRACH_MSG1FREQ_IDX].iptr;
ru->prach_msg1_freq = *param[RU_PRACH_MSG1FREQ_IDX].iptr;
ru->numerology = *param[RU_NUMEROLOGY_IDX].iptr;
ru->tdd_period = *param[RU_TDD_PERIOD_IDX].iptr;
ru->num_DL_slots = *param[RU_NUM_DL_SLOTS_IDX].iptr;
ru->num_UL_slots = *param[RU_NUM_UL_SLOTS_IDX].iptr;
ru->num_DL_symbols = *param[RU_NUM_DL_SYMBOLS_IDX].iptr;
ru->num_UL_symbols = *param[RU_NUM_UL_SYMBOLS_IDX].iptr;
ru->openair0_cfg.nr_flag = *param[RU_NR_FLAG].iptr;
ru->openair0_cfg.nr_band = ru->band[0];
ru->openair0_cfg.nr_scs_for_raster = *param[RU_NR_SCS_FOR_RASTER].iptr;

View File

@@ -444,7 +444,7 @@ int start_L1L2(module_id_t gnb_id)
init_NR_RU(config_get_if(), NULL);
start_NR_RU();
start_NR_RU(RC.ru[0]);
wait_RUs();
init_eNB_afterRU();
LOG_I(GNB_APP, "Sending sync to all threads\n");
@@ -662,7 +662,7 @@ int main( int argc, char **argv ) {
wait_f1_setup_response();
if (RC.nb_RU > 0)
start_NR_RU();
start_NR_RU(RC.ru[0]);
#ifdef ENABLE_AERIAL
gNB_MAC_INST *nrmac = RC.nrmac[0];

View File

@@ -47,7 +47,7 @@ extern void stop_gNB(int);
// In nr-ru.c
extern void init_NR_RU(configmodule_interface_t *cfg, char *);
extern void init_RU_proc(RU_t *ru);
extern void start_NR_RU(void);
extern void start_NR_RU(RU_t *ru);
extern void stop_RU(int nb_ru);
extern void kill_NR_RU_proc(int inst);
extern void set_function_spec_param(RU_t *ru);

View File

@@ -35,20 +35,23 @@ void nr_phy_init_RU(RU_t *ru)
LOG_D(PHY, "Initializing RU signal buffers (if_south %s) nb_tx %d, nb_rx %d\n", ru_if_types[ru->if_south], ru->nb_tx, ru->nb_rx);
nfapi_nr_config_request_scf_t *cfg = &ru->config;
ru->nb_log_antennas = 0;
for (int n = 0; n < ru->num_gNB; n++) {
if (cfg->carrier_config.num_tx_ant.value > ru->nb_log_antennas)
ru->nb_log_antennas = cfg->carrier_config.num_tx_ant.value;
ru->num_beams_period = 1;
if (ru->num_gNB > 0) {
nfapi_nr_config_request_scf_t *cfg = &ru->config;
ru->nb_log_antennas = 0;
for (int n = 0; n < ru->num_gNB; n++) {
if (cfg->carrier_config.num_tx_ant.value > ru->nb_log_antennas)
ru->nb_log_antennas = cfg->carrier_config.num_tx_ant.value;
}
nfapi_nr_analog_beamforming_ve_t *analog_config = &cfg->analog_beamforming_ve;
ru->num_beams_period = analog_config->analog_bf_vendor_ext.value ? analog_config->num_beams_period_vendor_ext.value : 1;
}
else ru->nb_log_antennas = ru->nb_tx;
// copy configuration from gNB[0] in to RU, assume that all gNB instances sharing RU use the same configuration
// (at least the parts that are needed by the RU, numerology and PRACH)
AssertFatal(ru->nb_log_antennas > 0 && ru->nb_log_antennas < 13, "ru->nb_log_antennas %d ! \n",ru->nb_log_antennas);
nfapi_nr_analog_beamforming_ve_t *analog_config = &cfg->analog_beamforming_ve;
ru->num_beams_period = analog_config->analog_bf_vendor_ext.value ? analog_config->num_beams_period_vendor_ext.value : 1;
int nb_tx_streams = ru->nb_tx * ru->num_beams_period;
int nb_rx_streams = ru->nb_rx * ru->num_beams_period;
LOG_I(NR_PHY, "nb_tx_streams %d, nb_rx_streams %d, num_Beams_period %d\n", nb_tx_streams, nb_rx_streams, ru->num_beams_period);

View File

@@ -259,6 +259,55 @@ uint32_t get_samples_per_slot(int slot, const NR_DL_FRAME_PARMS *fp)
return samp_count;
}
uint32_t get_samples_symbol_duration(const NR_DL_FRAME_PARMS *fp, int slot, int start_symbol, int num_symbols)
{
int end_symbol = start_symbol + num_symbols - 1;
AssertFatal(start_symbol <= end_symbol && start_symbol >= 0 && end_symbol < fp->symbols_per_slot,
"Symbol range is invalid start_symbol %d, num_symbols %d symbols_per_slot %d\n",
start_symbol,
num_symbols,
fp->symbols_per_slot);
if (num_symbols == fp->symbols_per_slot) {
return get_samples_per_slot(slot, fp);
}
uint32_t num_samples = 0;
int num_symbols_added = 0;
// Handle symbols with different nb_prefix_samples0
if (fp->numerology_index == 0) {
// Add symbol 0
if (start_symbol == 0) {
num_samples += fp->nb_prefix_samples0 + fp->ofdm_symbol_size;
num_symbols_added++;
}
// Add symbol 7
if (start_symbol <= 7 && end_symbol >= 7) {
num_samples += fp->nb_prefix_samples0 + fp->ofdm_symbol_size;
num_symbols_added++;
}
} else {
// Add first symbol
if (start_symbol == 0) {
num_samples += (slot % (fp->slots_per_subframe / 2)) ? fp->nb_prefix_samples : fp->nb_prefix_samples0;
num_samples += fp->ofdm_symbol_size;
num_symbols_added++;
}
}
int num_symbols_left = max(0, num_symbols - num_symbols_added);
num_samples += num_symbols_left * (fp->nb_prefix_samples + fp->ofdm_symbol_size);
return num_samples;
}
uint32_t get_samples_symbol_timestamp(const NR_DL_FRAME_PARMS *fp, int slot, int symbol)
{
if (symbol == 0) {
return 0;
}
return get_samples_symbol_duration(fp, slot, 0, symbol);
}
uint32_t get_slot_from_timestamp(openair0_timestamp timestamp_rx, const NR_DL_FRAME_PARMS *fp)
{
uint32_t slot_idx = 0;
@@ -286,6 +335,93 @@ uint32_t get_samples_slot_timestamp(int slot, const NR_DL_FRAME_PARMS *fp, unsig
return samp_count;
}
void nr_ru_init_frame_parms(RU_t *ru)
{
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
fp->frame_type = ru->frame_type;
ru->config.cell_config.frame_duplex_type.value = ru->frame_type;
ru->config.cell_config.frame_duplex_type.tl.tag = 0x100D;
fp->N_RB_DL = ru->bw_tx[0];
ru->config.ssb_config.scs_common.value = ru->numerology;
ru->config.carrier_config.dl_grid_size[ru->config.ssb_config.scs_common.value].value = ru->bw_tx[0];
fp->N_RB_UL = ru->bw_rx[0];
ru->config.carrier_config.ul_grid_size[ru->config.ssb_config.scs_common.value].value = ru->bw_rx[0];
fp->numerology_index = ru->numerology;
fp->nr_band = ru->band[0];
LOG_I(NR_PHY,
"Set RU frame type to %s, N_RB_DL %d, N_RB_UL %d, mu %d\n",
ru->frame_type == TDD ? "TDD" : "FDD",
ru->bw_tx[0],
ru->bw_rx[0],
ru->numerology);
set_scs_parameters(fp, fp->numerology_index, ru->bw_tx[0]);
fp->slots_per_frame = 10 * fp->slots_per_subframe;
fp->nb_antennas_rx = ru->nb_rx;
fp->nb_antennas_tx = ru->nb_tx;
fp->symbols_per_slot = 14;
fp->samples_per_subframe_wCP = fp->ofdm_symbol_size * fp->symbols_per_slot * fp->slots_per_subframe;
fp->samples_per_frame_wCP = 10 * fp->samples_per_subframe_wCP;
fp->samples_per_slot_wCP = fp->symbols_per_slot * fp->ofdm_symbol_size;
fp->samples_per_slotN0 = (fp->nb_prefix_samples + fp->ofdm_symbol_size) * fp->symbols_per_slot;
fp->samples_per_slot0 =
fp->nb_prefix_samples0 + ((fp->symbols_per_slot - 1) * fp->nb_prefix_samples) + (fp->symbols_per_slot * fp->ofdm_symbol_size);
fp->samples_per_subframe = (fp->nb_prefix_samples0 + fp->ofdm_symbol_size) * 2
+ (fp->nb_prefix_samples + fp->ofdm_symbol_size) * (fp->symbols_per_slot * fp->slots_per_subframe - 2);
fp->get_samples_per_slot = &get_samples_per_slot;
fp->get_samples_slot_timestamp = &get_samples_slot_timestamp;
fp->get_slot_from_timestamp = &get_slot_from_timestamp;
fp->samples_per_frame = 10 * fp->samples_per_subframe;
fp->freq_range = (ru->carrier_freq_tx[0] < 6e6) ? FR1 : FR2;
fp->dl_CarrierFreq = (double)ru->carrier_freq_tx[0] * 1000;
fp->ul_CarrierFreq = (double)ru->carrier_freq_rx[0] * 1000;
fp->Ncp = NORMAL;
// Split 7.2 parameters
ru->config.prach_config.num_prach_fd_occasions.value = 1;
ru->config.prach_config.prach_ConfigurationIndex.value = ru->prach_config_index;
ru->config.prach_config.prach_ConfigurationIndex.tl.tag = 0x1029;
ru->config.prach_config.num_prach_fd_occasions_list = malloc(sizeof(*ru->config.prach_config.num_prach_fd_occasions_list));
ru->config.prach_config.num_prach_fd_occasions_list[0].k1.value = ru->prach_msg1_freq;
if (ru->config.cell_config.frame_duplex_type.value == 1 /* TDD */) {
ru->config.tdd_table.tdd_period.value = ru->tdd_period;
ru->config.tdd_table.tdd_period.tl.tag = 0x1026;
int numb_slots_frame = (1 << ru->numerology) * NR_NUMBER_OF_SUBFRAMES_PER_FRAME;
int numb_period_frame = get_nb_periods_per_frame(ru->tdd_period);
int numb_slots_period = numb_slots_frame / numb_period_frame;
ru->config.tdd_table.max_tdd_periodicity_list =
malloc(sizeof(*ru->config.tdd_table.max_tdd_periodicity_list) * (numb_slots_frame));
for (int n = 0; n < numb_slots_frame; n++) {
int s = 0;
int p = n % numb_slots_period;
if (p < ru->num_DL_slots) {
ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list =
malloc(sizeof(*ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list)
* NR_NUMBER_OF_SYMBOLS_PER_SLOT);
for (s = 0; s < 14; s++)
ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list[s].slot_config.value = 0;
} else if (p == ru->num_DL_slots) {
ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list =
malloc(sizeof(*ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list)
* NR_NUMBER_OF_SYMBOLS_PER_SLOT);
for (s = 0; s < ru->num_DL_symbols; s++)
ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list[s].slot_config.value = 0;
for (; s < NR_NUMBER_OF_SYMBOLS_PER_SLOT - ru->num_UL_symbols; s++)
ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list[s].slot_config.value = 2;
for (; s < NR_NUMBER_OF_SYMBOLS_PER_SLOT; s++)
ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list[s].slot_config.value = 1;
} else {
ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list =
malloc(sizeof(*ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list)
* NR_NUMBER_OF_SYMBOLS_PER_SLOT);
for (s = 0; s < NR_NUMBER_OF_SYMBOLS_PER_SLOT; s++)
ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list[s].slot_config.value = 1;
}
}
}
}
void nr_init_frame_parms(nfapi_nr_config_request_scf_t* cfg, NR_DL_FRAME_PARMS *fp)
{

View File

@@ -27,6 +27,7 @@
int nr_get_ssb_start_symbol(const NR_DL_FRAME_PARMS *fp, uint8_t i_ssb);
void nr_init_frame_parms(nfapi_nr_config_request_scf_t *config, NR_DL_FRAME_PARMS *frame_parms);
void nr_ru_init_frame_parms(RU_t *ru);
int nr_init_frame_parms_ue(NR_DL_FRAME_PARMS *frame_parms, fapi_nr_config_request_t *config, uint16_t nr_band);
void nr_init_frame_parms_ue_sa(NR_DL_FRAME_PARMS *frame_parms, uint64_t downlink_frequency, int32_t uplink_frequency_offset, uint8_t mu, uint16_t nr_band);
int nr_init_frame_parms_ue_sl(NR_DL_FRAME_PARMS *fp,

View File

@@ -474,6 +474,30 @@ typedef struct RU_t_s {
int num_bands;
/// band list
int band[MAX_BANDS_PER_RRU];
/// tx carrier
uint64_t carrier_freq_tx[MAX_BANDS_PER_RRU];
/// rx carrier
uint64_t carrier_freq_rx[MAX_BANDS_PER_RRU];
/// tx BW in PRBs
int bw_tx[MAX_BANDS_PER_RRU];
/// rx BW in PRBs
int bw_rx[MAX_BANDS_PER_RRU];
/// 3GPP FRAME Type FDD/TDD
int frame_type;
/// 3GPP PRACH configuration index
int prach_config_index;
/// 3GPP MSG1 Start frequency
int prach_msg1_freq;
/// 3GPP TDD periodicity (0.5 ms, 1 0.625ms, 2 1ms, 3 1.25ms, 4 2ms,5 2.5ms, 6 5ms, 7 10ms, 8 3ms, 9 4ms
int tdd_period;
/// number of DL slots
int num_DL_slots;
/// number of UL slots
int num_UL_slots;
/// number of DL symbols
int num_DL_symbols;
/// number of UL symbols
int num_UL_symbols;
/// number of RX paths on device
int nb_rx;
/// number of TX paths on device

View File

@@ -303,4 +303,9 @@ typedef struct {
#define KHz (1000UL)
#define MHz (1000*KHz)
// Get symbol duration within slot in samples
uint32_t get_samples_symbol_duration(const NR_DL_FRAME_PARMS *fp, int slot, int start_symbol, int num_symbols);
// Get timestamp of of symbol within slot in samples
uint32_t get_samples_symbol_timestamp(const NR_DL_FRAME_PARMS *fp, int slot, int symbol);
#endif

View File

@@ -113,6 +113,19 @@ typedef enum {
#define CONFIG_STRING_RU_HALF_SLOT_PARALLELIZATION "half_slot_parallelization"
#define CONFIG_STRING_RU_RU_THREAD_CORE "ru_thread_core"
#define CONFIG_STRING_RU_GPIO_CONTROL "gpio_controller"
#define CONFIG_STRING_RU_TX_BW_LIST "tx_bw"
#define CONFIG_STRING_RU_RX_BW_LIST "rx_bw"
#define CONFIG_STRING_RU_CARRIER_TX_LIST "carrier_tx"
#define CONFIG_STRING_RU_CARRIER_RX_LIST "carrier_rx"
#define CONFIG_STRING_RU_FRAME_TYPE "frame_type"
#define CONFIG_STRING_RU_PRACH_CONFIGID "prach_config_index"
#define CONFIG_STRING_RU_PRACH_MSG1FREQ "prach_msg1_start"
#define CONFIG_STRING_RU_NUMEROLOGY "mu"
#define CONFIG_STRING_RU_TDD_PERIOD "tdd_period"
#define CONFIG_STRING_RU_NUM_DL_SLOTS "num_dl_slots"
#define CONFIG_STRING_RU_NUM_UL_SLOTS "num_ul_slots"
#define CONFIG_STRING_RU_NUM_DL_SYMBOLS "num_dl_symbols"
#define CONFIG_STRING_RU_NUM_UL_SYMBOLS "num_ul_symbols"
#define HLP_RU_SF_AHEAD "LTE TX processing advance"
#define HLP_RU_SL_AHEAD "NR TX processing advance"
@@ -126,6 +139,20 @@ typedef enum {
#define HLP_RU_HALF_SLOT_PARALLELIZATION "run half slots in parallel in RU FEP"
#define HLP_RU_RU_THREAD_CORE "id of core to pin ru_thread, -1 is default"
#define HLP_RU_GPIO_CONTROL "set the GPIO control type for the RU"
#define HLP_RU_TX_BW "set the TX bandwidth list per component carrier"
#define HLP_RU_RX_BW "set the RX bandwidth list per component carrier"
#define HLP_RU_CARRIER_TX "set the TX carrier frequencies per component carrier"
#define HLP_RU_CARRIER_RX "set the RX carrier frequencies per component carrier"
#define HLP_RU_FRAMETYPE "set the Frame type TDD/FDD of all component carriers"
#define HLP_RU_PRACH_CONFIGID "set the PRACH configuration id of all component carriers"
#define HLP_RU_PRACH_MSG1FREQ "set the PRACH MSG1 frequency of all component carriers"
#define HLP_RU_NUMEROLOGY "set the numerology of the RU"
#define HLP_RU_TDD_PERIOD "set the 3GPP TDD periodificty 0-9"
#define HLP_RU_NUM_DL_SLOTS "set the number of DL Slots in TDD"
#define HLP_RU_NUM_UL_SLOTS "set the number of UL Slots in TDD"
#define HLP_RU_NUM_DL_SYMBOLS "set the number of DL symbols in the mixed slot"
#define HLP_RU_NUM_UL_SYMBOLS "set the number of UL symbols in the mixed slot"
#define RU_LOCAL_IF_NAME_IDX 0
#define RU_LOCAL_ADDRESS_IDX 1
@@ -170,6 +197,19 @@ typedef enum {
#define RU_HALF_SLOT_PARALLELIZATION 40
#define RU_RU_THREAD_CORE 41
#define RU_GPIO_CONTROL 42
#define RU_TX_BW_LIST_IDX 43
#define RU_RX_BW_LIST_IDX 44
#define RU_TX_CARRIER_LIST_IDX 45
#define RU_RX_CARRIER_LIST_IDX 46
#define RU_FRAME_TYPE_IDX 47
#define RU_PRACH_CONFIGID_IDX 48
#define RU_PRACH_MSG1FREQ_IDX 49
#define RU_NUMEROLOGY_IDX 50
#define RU_TDD_PERIOD_IDX 51
#define RU_NUM_DL_SLOTS_IDX 52
#define RU_NUM_UL_SLOTS_IDX 53
#define RU_NUM_DL_SYMBOLS_IDX 54
#define RU_NUM_UL_SYMBOLS_IDX 55
/*-----------------------------------------------------------------------------------------------------------------------------------------*/
/* RU configuration parameters */
/* optname helpstr paramflags XXXptr defXXXval type numelt */
@@ -219,6 +259,19 @@ typedef enum {
{CONFIG_STRING_RU_HALF_SLOT_PARALLELIZATION, HLP_RU_HALF_SLOT_PARALLELIZATION, 0, .uptr=NULL, .defintval=1, TYPE_UINT, 0}, \
{CONFIG_STRING_RU_RU_THREAD_CORE, HLP_RU_RU_THREAD_CORE, 0, .uptr=NULL, .defintval=-1, TYPE_UINT, 0}, \
{CONFIG_STRING_RU_GPIO_CONTROL, HLP_RU_GPIO_CONTROL, 0, .strptr=NULL, .defstrval="generic", TYPE_STRING, 0}, \
{CONFIG_STRING_RU_TX_BW_LIST, HLP_RU_TX_BW, 0, .iptr=NULL, .defintarrayval=DEFBW, TYPE_INTARRAY, 0}, \
{CONFIG_STRING_RU_RX_BW_LIST, HLP_RU_RX_BW, 0, .iptr=NULL, .defintarrayval=DEFBW, TYPE_INTARRAY, 0}, \
{CONFIG_STRING_RU_CARRIER_TX_LIST, HLP_RU_CARRIER_TX, 0, .iptr=NULL, .defintarrayval=DEFCARRIER, TYPE_INTARRAY, 0}, \
{CONFIG_STRING_RU_CARRIER_RX_LIST, HLP_RU_CARRIER_RX, 0, .iptr=NULL, .defintarrayval=DEFCARRIER, TYPE_INTARRAY, 0}, \
{CONFIG_STRING_RU_FRAME_TYPE, HLP_RU_FRAMETYPE, 0, .uptr=NULL, .defintval=1, TYPE_UINT, 0}, \
{CONFIG_STRING_RU_PRACH_CONFIGID, HLP_RU_PRACH_CONFIGID, 0, .uptr=NULL, .defintval=152, TYPE_UINT, 0}, \
{CONFIG_STRING_RU_PRACH_MSG1FREQ, HLP_RU_PRACH_MSG1FREQ, 0, .uptr=NULL, .defintval=0, TYPE_UINT, 0}, \
{CONFIG_STRING_RU_NUMEROLOGY, HLP_RU_NUMEROLOGY, 0, .uptr=NULL, .defintval=1, TYPE_UINT, 0}, \
{CONFIG_STRING_RU_TDD_PERIOD, HLP_RU_TDD_PERIOD, 0, .uptr=NULL, .defintval=5, TYPE_UINT, 0}, \
{CONFIG_STRING_RU_NUM_DL_SLOTS, HLP_RU_NUM_DL_SLOTS, 0, .uptr=NULL, .defintval=3, TYPE_UINT, 0}, \
{CONFIG_STRING_RU_NUM_UL_SLOTS, HLP_RU_NUM_UL_SLOTS, 0, .uptr=NULL, .defintval=1, TYPE_UINT, 0}, \
{CONFIG_STRING_RU_NUM_DL_SYMBOLS, HLP_RU_NUM_DL_SYMBOLS, 0, .uptr=NULL, .defintval=7, TYPE_UINT, 0}, \
{CONFIG_STRING_RU_NUM_UL_SYMBOLS, HLP_RU_NUM_UL_SYMBOLS, 0, .uptr=NULL, .defintval=3, TYPE_UINT, 0}, \
}
// clang-format on

View File

@@ -84,7 +84,8 @@ static int DEFBANDS[] = {7};
static int DEFENBS[] = {0};
static int DEFBFW[] = {0x00007fff};
static int DEFRUTPCORES[] = {-1,-1,-1,-1};
static int DEFBW[] = {273};
static int DEFCARRIER[] = {3430560};
/**
* @brief Helper define to allocate and initialize SetupRelease structures
*/

View File

@@ -175,6 +175,19 @@ int openair0_transport_load(openair0_device *device,
return rc;
}
int openair0_load(openair0_device *device, char *name, openair0_config_t *openair0_cfg, eth_params_t *eth_params)
{
loader_shlibfunc_t shlib_fdesc[1];
int ret = 0;
shlib_fdesc[0].fname = eth_params == NULL ? "device_init" : "transport_init";
ret = load_module_shlib(name, shlib_fdesc, 1, NULL);
AssertFatal((ret >= 0), "Library %s couldn't be loaded\n", name);
return ((devfunc_t)shlib_fdesc[0].fptr)(device, openair0_cfg, eth_params);
}
static void writerEnqueue(re_order_t *ctx, openair0_timestamp timestamp, void **txp, int nsamps, int nbAnt, int flags)
{
pthread_mutex_lock(&ctx->mutex_store);

View File

@@ -408,6 +408,14 @@ typedef struct {
} queue[WRITE_QUEUE_SZ];
} re_order_t;
/*! \brief Provides a way to map between a symbol and a timespec */
typedef struct {
int frame;
int slot;
int symbol;
struct timespec ts;
} sense_of_time_t;
/*!\brief structure holds the parameters to configure USRP devices */
struct openair0_device_t {
/*!tx write thread*/
@@ -555,6 +563,10 @@ struct openair0_device_t {
*/
int (*trx_stop_func)(openair0_device *device);
/*! \brief Get timestamp from timespec
*/
openair0_timestamp (*get_timestamp)(openair0_device *device, struct timespec *ts);
/* Functions API related to UE*/
/*! \brief Set RX feaquencies
@@ -610,6 +622,18 @@ struct openair0_device_t {
*/
time_stats_t tx_fhaul;
re_order_t reOrder;
// Function pointers used for oran
struct {
/*! \brief O-RU only: reads DL FD IQ. Data is put into the beginning of txdataF buffer regardless
* of the returned start_symbol. maximum number of symbols returned is 7
* \param txdataF An array of nb_tx buffers to write the samples to
* \param nb_tx number of TX antennas and number of buffer in txDataF_BF
* \param sense_of_time frame, slot and symbol with mapping to clock_gettime result
* \param num_symbols number of symbols
*/
void (*north_in_func)(uint32_t **txdataF, int nb_tx, sense_of_time_t* sense_of_time, int *num_symbols);
} xran_api;
};
/* type of device init function, implemented in shared lib */
@@ -659,7 +683,7 @@ const char *get_devname(int devtype);
int openair0_device_load(openair0_device *device, openair0_config_t *openair0_cfg);
/*! \brief Initialize transport protocol . It returns 0 if OK */
int openair0_transport_load(openair0_device *device, openair0_config_t *openair0_cfg, eth_params_t *eth_params);
int openair0_load(openair0_device *device, char *name, openair0_config_t *openair0_cfg, eth_params_t *eth_params);
/*! \brief Get current timestamp of USRP
* \param device the hardware to use

View File

@@ -33,7 +33,7 @@ endif()
add_compile_options(-Wno-packed-not-aligned)
set_target_properties(oran_fhlib_5g PROPERTIES COMPILE_FLAGS "-fvisibility=hidden -march=native")
target_link_libraries(oran_fhlib_5g PRIVATE xran::xran ${dpdk_LINK_LIBRARIES} ${T_LIB} pthread dl rt m numa)
target_link_libraries(oran_fhlib_5g PRIVATE xran::xran ${dpdk_LINK_LIBRARIES} ${T_LIB} pthread dl rt m numa MAC_NR_COMMON)
target_include_directories(oran_fhlib_5g PRIVATE ${dpdk_INCLUDE_DIRS})
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
find_package(armral REQUIRED)

View File

@@ -60,6 +60,75 @@ extern notifiedFIFO_t oran_sync_fifo;
volatile oran_sync_info_t oran_sync_info = {0};
#endif
extern notifiedFIFO_t ru_dl_sync_fifo;
int32_t symbol_callback(void *args, struct xran_sense_of_time *p_sense_of_time)
{
uint32_t frame = p_sense_of_time->nFrameIdx;
uint32_t slot = p_sense_of_time->nSlotIdx;
uint32_t subframe = p_sense_of_time->nSubframeIdx;
oran_symbol_callback_args_t *callback_args = args;
const struct xran_fh_config *fh_cfg = get_xran_fh_config(0);
const int slots_in_sf = 1 << fh_cfg->frame_conf.nNumerology;
uint32_t slot_in_frame = slot + subframe * slots_in_sf;
if (!first_call_set)
return 0;
struct timespec ts;
if (clock_gettime(CLOCK_REALTIME, &ts))
abort();
static int last_frame = 0;
// Workaround for a bug in XRAN
// In XRAN, time is kept by GPS second and slot within GPS second (tti_counter).
// This is translated to 5G time within xran via xran_sfn_at_sec_start. Due to the
// way timers are setup in xran, the value of xran_sfn_at_sec_start might be incorrect
// at the beginning of the frame, causing the nFrameIdx value returned from xran to be incorrect.
// This was observed only when tti_counter == 0
int frames_per_second = 100;
bool is_xran_bug_triggered = p_sense_of_time->tti_counter == 0 && (last_frame - frames_per_second + 1 + 1024) % 1024 == frame;
if (is_xran_bug_triggered) {
frame = (frame + frames_per_second) % 1024;
}
if (last_frame != frame) {
if (((last_frame + 1024 + 1) % 1024) != frame) {
LOG_W(HW, "Frames are not in order. XRAN core might be too slow frame %d last_frame %d\n", frame, last_frame);
}
last_frame = frame;
}
notifiedFIFO_elt_t *req = newNotifiedFIFO_elt(sizeof(ru_dl_sync_info_t), 0, NULL, NULL);
ru_dl_sync_info_t *info = NotifiedFifoData(req);
info->frame = frame;
info->slot = slot_in_frame;
info->symbol = callback_args->start_symbol;
float slot_duration_uS[] = {1000, 500, 250, 125};
float symbol_duration_uS = slot_duration_uS[fh_cfg->frame_conf.nNumerology] / 14;
// Offset current time to indicate symbol start time
int64_t symbol_offset_ns = symbol_duration_uS * RU_SYMBOLS_PER_CALLBACK * 1000;
// This happens T1a_min_up before the last symbol OTA.
int64_t T1a_offset_ns = fh_cfg->T1a_min_up * 1000;
const long one_second_ns = 1000000000L;
ts.tv_nsec += T1a_offset_ns - symbol_offset_ns;
if (ts.tv_nsec >= one_second_ns) {
ts.tv_nsec -= one_second_ns;
ts.tv_sec++;
}
if (ts.tv_nsec < 0) {
ts.tv_nsec += one_second_ns;
ts.tv_sec--;
}
info->ts = ts;
pushNotifiedFIFO(&ru_dl_sync_fifo, req);
return 0;
}
/** @details xran-specific callback, called when all packets for given CC and
* 1/4, 1/2, 3/4, all symbols of a slot arrived. Currently, only used to get
* timing information and unblock another thread in xran_fh_rx_read_slot()
@@ -467,6 +536,142 @@ int xran_fh_rx_read_slot(ru_info_t *ru, int *frame, int *slot)
return (0);
}
/** @details Read DL IQ data from xran buffers on the O-RU. If I/Q compression
* (bitwidth < 16 bits) is configured, deccompresses the data before writing.
* Prints ON TIME counters every 128 frames.
*
* Function is blocking and waits for next frame/slot combination. It is unblocked
* by oai_xran_fh_rx_callback().
*/
int xran_fh_tx_read_slot(uint32_t **txdataF, int nb_tx, int *frame, int *slot, int *symbol, struct timespec *ts)
{
// pull next even from oran_sync_fifo
notifiedFIFO_elt_t *res = pullNotifiedFIFO(&ru_dl_sync_fifo);
ru_dl_sync_info_t *info = NotifiedFifoData(res);
*slot = info->slot;
*frame = info->frame;
*symbol = info->symbol;
*ts = info->ts;
delNotifiedFIFO_elt(res);
struct xran_fh_config *fh_cfg = get_xran_fh_config(0);
int slots_per_frame = 10 << fh_cfg->frame_conf.nNumerology;
int tti = slots_per_frame * (*frame) + (*slot);
const struct xran_fh_init *fh_init = get_xran_fh_init();
int fftsize = 1 << fh_cfg->ru_conf.fftSize;
int nPRBs = fh_cfg->nDLRBs;
oran_buf_list_t *bufs = get_xran_buffers(0);
for (uint16_t cc_id = 0; cc_id < 1 /*nSectorNum*/; cc_id++) { // OAI does not support multiple CC yet.
for (uint8_t ant_id = 0; ant_id < nb_tx; ant_id++) {
AssertFatal(txdataF[ant_id] != NULL, "Buffer for ant_idx %d, nb_tx %d is NULL\n", ant_id, nb_tx);
const struct xran_frame_config *frame_conf = &get_xran_fh_config(0)->frame_conf;
static struct xran_prb_map *pPrbMap;
pPrbMap = (struct xran_prb_map *)bufs->dstcp[ant_id][tti % XRAN_N_FE_BUF_LEN].pBuffers->pData;
AssertFatal(pPrbMap != NULL, "Invalid prbMap pointer\n");
AssertFatal(pPrbMap->cc_id == cc_id && pPrbMap->ru_port_id == 0,
"Invalid PRB map cc_id %u ru_port_id %u\n",
pPrbMap->cc_id,
pPrbMap->ru_port_id);
int32_t symbol_buffer[nPRBs * N_SC_PER_PRB] __attribute__((aligned(64)));
for (int32_t sym_idx = *symbol; sym_idx < *symbol + RU_SYMBOLS_PER_CALLBACK; sym_idx++) {
memset(symbol_buffer, 0, sizeof(symbol_buffer));
for (int prb_elem_index = 0; prb_elem_index < pPrbMap->nPrbElm; prb_elem_index++) {
struct xran_prb_elm *prb_elm = &pPrbMap->prbMap[prb_elem_index];
if (is_tdd_ul_symbol(frame_conf, *slot, sym_idx))
continue;
if (sym_idx < prb_elm->nStartSymb || sym_idx > (prb_elm->nStartSymb + prb_elm->numSymb - 1)) {
continue;
}
for (int cplane_sec_idx = 0; cplane_sec_idx < prb_elm->nSecDesc[sym_idx]; cplane_sec_idx++) {
struct xran_section_desc *sec_desc = &prb_elm->sec_desc[sym_idx][cplane_sec_idx];
if (prb_elm->compMethod == XRAN_COMPMETHOD_NONE) {
int start_prb = sec_desc->start_prbu;
int num_prb = sec_desc->num_prbu;
uint16_t *src = (uint16_t *)sec_desc->pData;
uint16_t *dst = (uint16_t *)symbol_buffer;
for (int idx = start_prb * N_SC_PER_PRB * 2; idx < (start_prb + num_prb) * N_SC_PER_PRB * 2; idx++) {
dst[idx] = (int16_t)ntohs(src[idx]);
}
} else if (prb_elm->compMethod == XRAN_COMPMETHOD_BLKFLOAT) {
struct xranlib_decompress_request bfp_decom_req = {};
struct xranlib_decompress_response bfp_decom_rsp = {};
bfp_decom_req.data_in = (int8_t *)sec_desc->pData;
bfp_decom_req.numRBs = sec_desc->num_prbu;
bfp_decom_req.len = (3 * prb_elm->iqWidth + 1) * sec_desc->num_prbu;
bfp_decom_req.compMethod = prb_elm->compMethod;
bfp_decom_req.iqWidth = prb_elm->iqWidth;
bfp_decom_rsp.data_out = (int16_t *)&symbol_buffer[sec_desc->start_prbu * N_SC_PER_PRB];
bfp_decom_rsp.len = 0;
xranlib_decompress_avx512(&bfp_decom_req, &bfp_decom_rsp);
}
}
}
int first_carrier_offset = fftsize - (nPRBs * N_SC_PER_PRB / 2);
size_t first_copy_size = fftsize - first_carrier_offset;
uint32_t *txdataF_symb = &txdataF[ant_id][sym_idx * fftsize];
memcpy(&txdataF_symb[first_carrier_offset], &symbol_buffer[0], first_copy_size * sizeof(uint32_t));
memcpy(&txdataF_symb[0], &symbol_buffer[first_copy_size], ((nPRBs * N_SC_PER_PRB) - first_copy_size) * sizeof(uint32_t));
}
}
}
if (*symbol + RU_SYMBOLS_PER_CALLBACK - 1 == 13) {
for (uint16_t cc_id = 0; cc_id < 1 /* fh_config->nCC */; cc_id++) { // OAI does not support multiple CC yet.
for(uint32_t ant_id = 0; ant_id < fh_cfg->neAxc; ant_id++) {
struct xran_prb_map *pRbMap = (struct xran_prb_map *)bufs->dstcp[ant_id][tti % XRAN_N_FE_BUF_LEN].pBuffers->pData;
AssertFatal(pRbMap != NULL, "(%d:%d:%d)pRbMap == NULL. Aborting.\n", cc_id, tti % XRAN_N_FE_BUF_LEN, ant_id);
for (uint32_t sym_id = 0; sym_id < XRAN_NUM_OF_SYMBOL_PER_SLOT; sym_id++) {
for (uint32_t idxElm = 0; idxElm < pRbMap->nPrbElm; idxElm++ ) {
struct xran_prb_elm *pRbElm = &pRbMap->prbMap[idxElm];
pRbElm->nSecDesc[sym_id] = 0; // number of section descriptors per symbol;
}
}
}
}
}
static int64_t old_rx_counter[XRAN_PORTS_NUM] = {0};
static int64_t old_tx_counter[XRAN_PORTS_NUM] = {0};
struct xran_common_counters x_counters[XRAN_PORTS_NUM];
if ((*frame & 0x7f) == 0 && *slot == 0 && xran_get_common_counters(gxran_handle, &x_counters[0]) == XRAN_STATUS_SUCCESS) {
for (int o_xu_id = 0; o_xu_id < fh_init->xran_ports; o_xu_id++) {
LOG_I(HW,
"[o-ru %d][rx %7ld pps %7ld kbps %7ld][tx %7ld pps %7ld kbps %7ld] [on_time %ld early %ld late %ld corrupt %ld "
"pkt_dupl %ld Invalid_Ext1_packets %ld Total %ld]\n",
o_xu_id,
x_counters[o_xu_id].rx_counter,
x_counters[o_xu_id].rx_counter - old_rx_counter[o_xu_id],
x_counters[o_xu_id].rx_bytes_per_sec * 8 / 1000L,
x_counters[o_xu_id].tx_counter,
x_counters[o_xu_id].tx_counter - old_tx_counter[o_xu_id],
x_counters[o_xu_id].tx_bytes_per_sec * 8 / 1000L,
x_counters[o_xu_id].Rx_on_time,
x_counters[o_xu_id].Rx_early,
x_counters[o_xu_id].Rx_late,
x_counters[o_xu_id].Rx_corrupt,
x_counters[o_xu_id].Rx_pkt_dupl,
x_counters[o_xu_id].rx_invalid_ext1_packets,
x_counters[o_xu_id].Total_msgs_rcvd);
if (x_counters[o_xu_id].rx_counter > old_rx_counter[o_xu_id])
old_rx_counter[o_xu_id] = x_counters[o_xu_id].rx_counter;
if (x_counters[o_xu_id].tx_counter > old_tx_counter[o_xu_id])
old_tx_counter[o_xu_id] = x_counters[o_xu_id].tx_counter;
}
}
return 0;
}
/** @details Write PDSCH IQ-data from OAI txdataF_BF buffer to xran buffers. If
* I/Q compression (bitwidth < 16 bits) is configured, compresses the data
* before writing. */

View File

@@ -31,6 +31,13 @@ typedef struct {
uint32_t f;
} oran_sync_info_t;
typedef struct {
int frame;
int slot;
int symbol;
struct timespec ts;
} ru_dl_sync_info_t;
/** @brief xran callback for fronthaul RX, see xran_5g_fronthault_config(). */
void oai_xran_fh_rx_callback(void *pCallbackTag, xran_status_t status);
/** @brief xran callback for time alignment, see xran_reg_physide_cb(). */

View File

@@ -184,7 +184,8 @@ void print_fh_init(const struct xran_fh_init *fh_init)
fh_init->filePrefix,
fh_init->mtu,
fh_init->p_o_du_addr);
print_ether_addr(" p_o_ru_addr", fh_init->xran_ports * fh_init->io_cfg.num_vfs, (struct rte_ether_addr *)fh_init->p_o_ru_addr);
if (fh_init->p_o_ru_addr) print_ether_addr(" p_o_ru_addr", fh_init->xran_ports * fh_init->io_cfg.num_vfs, (struct rte_ether_addr *)fh_init->p_o_ru_addr);
else if (fh_init->p_o_du_addr) print_ether_addr(" p_o_du_addr", fh_init->xran_ports * fh_init->io_cfg.num_vfs, (struct rte_ether_addr *)fh_init->p_o_du_addr);
printf("\
totalBfWeights %d\n",
fh_init->totalBfWeights);
@@ -466,14 +467,14 @@ char bbdev_dev[32] = "";
char bbdev_vfio_vf_token[64] = "";
#endif
static bool set_fh_io_cfg(struct xran_io_cfg *io_cfg, const paramdef_t *fhip, int nump, const int num_rus)
static bool set_fh_io_cfg(struct xran_io_cfg *io_cfg, const paramdef_t *fhip, int nump, const int num_peer, const int is_du)
{
DevAssert(fhip != NULL);
int num_dev = gpd(fhip, nump, ORAN_CONFIG_DPDK_DEVICES)->numelt;
AssertFatal(num_dev > 0, "need to provide DPDK devices for O-RAN 7.2 Fronthaul\n");
AssertFatal(num_dev < 17, "too many DPDK devices for O-RAN 7.2 Fronthaul\n");
io_cfg->id = 0; // 0 -> xran as O-DU; 1 -> xran as O-RU
io_cfg->id = 1-is_du; // 0 -> xran as O-DU; 1 -> xran as O-RU
io_cfg->num_vfs = num_dev; // number of VFs for C-plane and U-plane (should be even); max = XRAN_VF_MAX
io_cfg->num_rxq = 1; // number of RX queues per VF
for (int i = 0; i < num_dev; ++i) {
@@ -535,7 +536,7 @@ static bool set_fh_io_cfg(struct xran_io_cfg *io_cfg, const paramdef_t *fhip, in
io_cfg->io_sleep = 0; // enable sleep on PMD cores; 0 -> no sleep
io_cfg->nEthLinePerPort = *gpd(fhip, nump, ORAN_CONFIG_NETHPERPORT)->uptr; // 1, 2, 3 total number of links per O-RU (Fronthaul Ethernet link)
io_cfg->nEthLineSpeed = *gpd(fhip, nump, ORAN_CONFIG_NETHSPEED)->uptr; // 10G,25G,40G,100G speed of Physical connection on O-RU
io_cfg->one_vf_cu_plane = (io_cfg->num_vfs == num_rus); // C-plane and U-plane use one VF
io_cfg->one_vf_cu_plane = (io_cfg->num_vfs == num_peer); // C-plane and U-plane use one VF
/* eCPRI One-Way Delay Measurements common settings for O-DU and O-RU;
use owdm to calculate T12 and T34 -> CUS specification, section 2.3.3.3;
@@ -546,7 +547,7 @@ static bool set_fh_io_cfg(struct xran_io_cfg *io_cfg, const paramdef_t *fhip, in
/* if RU does support, io_cfg->eowd_cmn[0] should only be filled as id = O_DU; io_cfg->eowd_cmn[1] only used if id = O_RU */
const uint16_t owdm_enable = *gpd(fhip, nump, ORAN_CONFIG_ECPRI_OWDM)->uptr;
if (owdm_enable) {
io_cfg->eowd_cmn[0].initiator_en = 1; // 1 -> initiator (always O-DU), 0 -> recipient (always O-RU)
io_cfg->eowd_cmn[0].initiator_en = is_du ? 1 : 0; // 1 -> initiator (always O-DU), 0 -> recipient (always O-RU)
io_cfg->eowd_cmn[0].numberOfSamples = 8; // total number of samples to be collected and averaged per port
io_cfg->eowd_cmn[0].filterType = 0; // 0 -> simple average based on number of measurements; not used in xran in both E and F releases
io_cfg->eowd_cmn[0].responseTo = 10000000; // response timeout in [ns]
@@ -684,6 +685,16 @@ static bool set_fh_init(void *mplane_api, struct xran_fh_init *fh_init, enum xra
const int nfh = sizeofArray(FHconfigs);
config_getlist(config_get_if(), &FH_ConfigList, FHconfigs, nfh, aprefix);
int num_peer = FH_ConfigList.numelt; // based on the number of fh_config sections -> number of RUs
int is_du=0;
int num_ru_addr = gpd(fhip, nump, ORAN_CONFIG_RU_ADDR)->numelt;
int num_du_addr = gpd(fhip, nump, ORAN_CONFIG_DU_ADDR)->numelt;
if (num_ru_addr > 0 && num_du_addr == 0) is_du = 1;
else if (num_du_addr > 0 && num_ru_addr == 0) is_du = 0;
else AssertFatal(1==0,"Illegal node configuration, num_du_addr %d, num_ru_addr %d\n",num_du_addr,num_ru_addr);
fh_init->xran_ports = num_peer;
#ifdef OAI_MPLANE
ru_session_list_t *ru_session_list = (ru_session_list_t *)mplane_api;
int num_rus = ru_session_list->num_rus;
@@ -710,9 +721,8 @@ static bool set_fh_init(void *mplane_api, struct xran_fh_init *fh_init, enum xra
}
}
#else
int num_rus = FH_ConfigList.numelt; // based on the number of fh_config sections -> number of RUs
fh_init->xran_ports = num_rus; // since we use xran as O-DU, xran_ports is set to the number of RUs
if (!set_fh_io_cfg(&fh_init->io_cfg, fhip, nump, num_rus))
if (!set_fh_io_cfg(&fh_init->io_cfg, fhip, nump, num_peer, is_du))
return false;
if (!set_fh_eaxcid_conf(&fh_init->eAxCId_conf, xran_cat))
return false;
@@ -720,16 +730,36 @@ static bool set_fh_init(void *mplane_api, struct xran_fh_init *fh_init, enum xra
communicated in a single xRAN network layer transaction. Supported 1500 bytes and 9600 bytes (Jumbo Frame);
xran only checks if (MTU <= 1500), therefore setting any value > 1500, xran assumes 9600 value is used */
fh_init->mtu = *gpd(fhip, nump, ORAN_CONFIG_MTU)->uptr;
int num_ru_addr = gpd(fhip, nump, ORAN_CONFIG_RU_ADDR)->numelt;
fh_init->p_o_ru_addr = calloc(num_ru_addr, sizeof(struct rte_ether_addr));
AssertFatal(fh_init->p_o_ru_addr != NULL, "out of memory\n");
char **ru_addrs = gpd(fhip, nump, ORAN_CONFIG_RU_ADDR)->strlistptr;
for (int i = 0; i < num_ru_addr; ++i) {
struct rte_ether_addr *ea = (struct rte_ether_addr *)fh_init->p_o_ru_addr;
if (get_ether_addr(ru_addrs[i], &ea[i]) == NULL) {
printf("could not read ethernet address '%s' for RU!\n", ru_addrs[i]);
return false;
}
fh_init->p_o_du_addr = NULL; // DPDK retreives DU MAC address within the xran library with rte_eth_macaddr_get() function
char **ru_addrs,**du_addrs;
if (is_du > 0) {
fh_init->p_o_ru_addr = calloc(num_ru_addr, sizeof(struct rte_ether_addr));
ru_addrs = gpd(fhip, nump, ORAN_CONFIG_RU_ADDR)->strlistptr;
AssertFatal(fh_init->p_o_ru_addr != NULL, "out of memory\n");
for (int i = 0; i < num_ru_addr; ++i) {
struct rte_ether_addr *ea = (struct rte_ether_addr *)fh_init->p_o_ru_addr;
if (get_ether_addr(ru_addrs[i], &ea[i]) == NULL) {
printf("could not read ethernet address '%s' for RU!\n", ru_addrs[i]);
return false;
}
}
fh_init->p_o_du_addr = NULL;
}
else {
fh_init->p_o_du_addr = calloc(num_du_addr, sizeof(struct rte_ether_addr));
du_addrs = gpd(fhip, nump, ORAN_CONFIG_DU_ADDR)->strlistptr;
AssertFatal(fh_init->p_o_du_addr != NULL, "out of memory\n");
for (int i = 0; i < num_du_addr; ++i) {
struct rte_ether_addr *ea = (struct rte_ether_addr *)fh_init->p_o_du_addr;
if (get_ether_addr(du_addrs[i], &ea[i]) == NULL) {
printf("could not read ethernet address '%s' for DU!\n", du_addrs[i]);
return false;
}
}
fh_init->p_o_ru_addr = NULL;
}
#endif
@@ -738,7 +768,6 @@ static bool set_fh_init(void *mplane_api, struct xran_fh_init *fh_init, enum xra
/* used to specify a unique prefix for shared memory, and files created by multiple DPDK processes;
it is necessary */
fh_init->filePrefix = strdup(*gpd(fhip, nump, ORAN_CONFIG_FILE_PREFIX)->strptr);
fh_init->p_o_du_addr = NULL; // DPDK retreives DU MAC address within the xran library with rte_eth_macaddr_get() function
fh_init->totalBfWeights = 0; // only used if id = O_RU (for emulation); C-plane extension types; section 5.4.6 of CUS spec
#ifdef F_RELEASE

View File

@@ -52,6 +52,8 @@ static struct xran_fh_init g_fh_init = {0};
static struct xran_fh_config g_fh_config[XRAN_PORTS_NUM] = {0};
static uint32_t g_prach_conf_duration[XRAN_PORTS_NUM] = {0};
int32_t symbol_callback(void *args, struct xran_sense_of_time* p_sense_of_time);
static uint32_t get_nSW_ToFpga_FTH_TxBufferLen(int mu, int sections)
{
uint32_t xran_max_sections_per_slot = RTE_MAX(sections, XRAN_MIN_SECTIONS_PER_SLOT);
@@ -298,6 +300,7 @@ static void oai_xran_fh_rx_prach_callback(void *pCallbackTag, xran_status_t stat
}
static void oran_allocate_buffers(void *handle,
bool is_du,
int xran_inst,
int num_sectors,
oran_port_instance_t *portInstances,
@@ -425,8 +428,32 @@ static void oran_allocate_buffers(void *handle,
}
}
xran_5g_fronthault_config(pi->instanceHandle, src, srccp, dst, dstcp, oai_xran_fh_rx_callback, &portInstances->pusch_tag);
xran_5g_prach_req(pi->instanceHandle, prach, prachdecomp, oai_xran_fh_rx_prach_callback, &portInstances->prach_tag);
if (is_du) {
xran_5g_fronthault_config(pi->instanceHandle, src, srccp, dst, dstcp, oai_xran_fh_rx_callback, &portInstances->pusch_tag);
xran_5g_prach_req(pi->instanceHandle, prach, prachdecomp, oai_xran_fh_rx_prach_callback, &portInstances->prach_tag);
}
else {
static struct xran_sense_of_time sym_cb_time_half_slot;
static struct xran_sense_of_time sym_cb_time_full_slot;
static oran_symbol_callback_args_t args_half_slot;
static oran_symbol_callback_args_t args_full_slot;
args_half_slot = (oran_symbol_callback_args_t){
.start_symbol = 0,
.num_symbols = RU_SYMBOLS_PER_CALLBACK,
};
args_full_slot = (oran_symbol_callback_args_t){
.start_symbol = 7,
.num_symbols = RU_SYMBOLS_PER_CALLBACK,
};
xran_reg_sym_cb(handle, symbol_callback, &args_half_slot, &sym_cb_time_half_slot, 6, XRAN_CB_SYM_TX_WIN_END);
xran_reg_sym_cb(handle, symbol_callback, &args_full_slot, &sym_cb_time_full_slot, 13, XRAN_CB_SYM_TX_WIN_END);
// Only setup UPlane/CPlane buffers for O-RU. O-RU does not need callback for PUSCH and PRACH as the timing will be reliant on
// the underlying RF device
xran_5g_fronthault_config(pi->instanceHandle, src, srccp, dst, dstcp, NULL, NULL);
// TODO: PRACH
// TODO: Beamforming weights
}
}
int *oai_oran_initialize(struct xran_fh_init *xran_fh_init, struct xran_fh_config *xran_fh_config)
@@ -440,6 +467,7 @@ int *oai_oran_initialize(struct xran_fh_init *xran_fh_init, struct xran_fh_confi
exit(-1);
}
bool is_du = xran_fh_init->io_cfg.id == 0;
/** process all the O-RU|O-DU for use case */
for (int32_t o_xu_id = 0; o_xu_id < xran_fh_init->xran_ports; o_xu_id++) {
print_fh_config(&xran_fh_config[o_xu_id]);
@@ -457,9 +485,9 @@ int *oai_oran_initialize(struct xran_fh_init *xran_fh_init, struct xran_fh_confi
pi->pusch_tag = tag;
#ifdef E_RELEASE
LOG_W(HW, "Please be aware that E release support will be removed in the future. Consider switching to F release.\n");
oran_allocate_buffers(gxran_handle, o_xu_id, 1, pi, &xran_fh_config[o_xu_id]);
oran_allocate_buffers(gxran_handle, is_du, o_xu_id, 1, pi, &xran_fh_config[o_xu_id]);
#elif defined F_RELEASE
oran_allocate_buffers(gxran_handle, o_xu_id, 1, pi, xran_fh_init->mtu, &xran_fh_config[o_xu_id]);
oran_allocate_buffers(gxran_handle, is_du, o_xu_id, 1, pi, xran_fh_init->mtu, &xran_fh_config[o_xu_id]);
#endif
if ((xret = xran_reg_physide_cb(gxran_handle, oai_physide_dl_tti_call_back, NULL, 10, XRAN_CB_TTI)) != XRAN_STATUS_SUCCESS) {

View File

@@ -22,6 +22,8 @@
#ifndef ORAN_INIT_H
#define ORAN_INIT_H
#define RU_SYMBOLS_PER_CALLBACK 7
typedef struct oran_bufs {
struct xran_flat_buffer tx[XRAN_MAX_ANTENNA_NR][XRAN_N_FE_BUF_LEN][XRAN_NUM_OF_SYMBOL_PER_SLOT];
struct xran_flat_buffer tx_prbmap[XRAN_MAX_ANTENNA_NR][XRAN_N_FE_BUF_LEN];
@@ -54,6 +56,12 @@ typedef struct oran_port_instance_t {
struct xran_cb_tag pusch_tag;
} oran_port_instance_t;
typedef struct {
int start_symbol;
int num_symbols;
int symbol_diff;
} oran_symbol_callback_args_t;
extern struct xran_fh_config gxran_fh_config[XRAN_PORTS_NUM];
extern void *gxran_handle;

View File

@@ -53,6 +53,7 @@ typedef struct {
} oran_eth_state_t;
notifiedFIFO_t oran_sync_fifo;
notifiedFIFO_t ru_dl_sync_fifo;
int trx_oran_start(openair0_device *device)
{
@@ -210,6 +211,49 @@ int trx_oran_ctlrecv(openair0_device *device, void *msg, ssize_t msg_len)
return 0;
}
void dump_nonzero_symbol(c16_t *txdataF, uint32_t ofdm_symbol_size, int frame, int slot, int symbol)
{
float signal_energy = signal_energy_nodc(txdataF, ofdm_symbol_size);
if (g_log->log_component[HW].level < OAILOG_DEBUG) {
return;
}
if (signal_energy > 1) {
// Prepare a buffer to hold the formatted string for the symbol
const int num_chars_per_sample = 4 + 6 * 2;
char symbol_buf[ofdm_symbol_size * num_chars_per_sample]; // Enough for "(r,i) " per sample
int offset = 0;
bool is_zero_block = true;
for (int i = 0; i < ofdm_symbol_size; i++) {
bool is_zero = txdataF[i].r == 0 && txdataF[i].i == 0;
if (is_zero_block && !is_zero) {
offset += snprintf(symbol_buf + offset, sizeof(symbol_buf) - offset, "[sc %d]: ", i);
is_zero_block = false;
}
if (!is_zero_block && is_zero) {
is_zero_block = true;
}
if (!is_zero) {
offset += snprintf(symbol_buf + offset, sizeof(symbol_buf) - offset, "(%d,%d) ", txdataF[i].r, txdataF[i].i);
}
}
symbol_buf[offset] = '\0';
LOG_D(HW, "Antenna 0 Frame.Slot.Symbol %d.%d.%d signal_energy %.3f samples: %s\n", frame, slot, symbol, 10 * log10(signal_energy), symbol_buf);
}
}
void oran_fh_if4p5_north_in(uint32_t **txdataF, int nb_tx, sense_of_time_t* sense_of_time, int *num_symbols) {
*num_symbols = RU_SYMBOLS_PER_CALLBACK;
int ret = xran_fh_tx_read_slot(txdataF, nb_tx, &sense_of_time->frame, &sense_of_time->slot, &sense_of_time->symbol, &sense_of_time->ts);
AssertFatal(ret == 0, "ORAN: Error reading slot");
int fftsize = 1 << get_xran_fh_config(0)->ru_conf.fftSize;
if (sense_of_time->frame == 0 && sense_of_time->slot == 0) {
for (int symbol = sense_of_time->symbol; symbol < sense_of_time->symbol + *num_symbols; symbol++) {
dump_nonzero_symbol((c16_t *)&txdataF[0][fftsize * symbol], fftsize, sense_of_time->frame, sense_of_time->slot, symbol);
}
}
}
void oran_fh_if4p5_south_in(RU_t *ru, int *frame, int *slot)
{
ru_info_t ru_info;
@@ -271,6 +315,13 @@ void oran_fh_if4p5_south_out(RU_t *ru, int frame, int slot, uint64_t timestamp)
ru_info.txdataF_BF = ru->common.txdataF_BF;
// printf("south_out:\tframe=%d\tslot=%d\ttimestamp=%ld\n",frame,slot,timestamp);
int fftsize = 1 << get_xran_fh_config(0)->ru_conf.fftSize;
if (frame == 0 && slot == 0) {
for (int symbol = 0; symbol < 14; symbol++) {
dump_nonzero_symbol((c16_t *)&ru_info.txdataF_BF[0][fftsize * symbol], fftsize, frame, slot, symbol);
}
}
int ret = xran_fh_tx_send_slot(&ru_info, frame, slot, timestamp);
if (ret != 0) {
printf("ORAN: ORAN_fh_if4p5_south_out ERROR in TX function \n");
@@ -390,6 +441,7 @@ __attribute__((__visibility__("default"))) int transport_init(openair0_device *d
device->get_internal_parameter = get_internal_parameter;
device->priv = eth;
device->openair0_cfg = &openair0_cfg[0];
device->xran_api.north_in_func = oran_fh_if4p5_north_in;
return 0;
}

View File

@@ -54,4 +54,8 @@ int xran_fh_rx_read_slot(ru_info_t *ru, int *frame, int *slot);
/** @brief Writes TX data (PDSCH) of given slot. */
int xran_fh_tx_send_slot(ru_info_t *ru, int frame, int slot, uint64_t timestamp);
/** @brief Read DL IQ data from xran buffers on the O-RU
*/
int xran_fh_tx_read_slot(uint32_t **txdataF, int nb_tx, int *frame, int *slot, int *symbol, struct timespec *ts);
#endif /* _ORAN_ISOLATE_H_ */

View File

@@ -123,6 +123,7 @@ typedef struct {
Actor_t *channel_modelling_actors;
char *taps_socket;
int client_num_rx_antennas;
struct timespec start_ts;
} vrtsim_state_t;
// Sample history for channel impulse response
@@ -206,30 +207,24 @@ static void vrtsim_readconfig(vrtsim_state_t *vrtsim_state)
static void *vrtsim_timing_job(void *arg)
{
vrtsim_state_t *vrtsim_state = arg;
struct timespec timestamp;
if (clock_gettime(CLOCK_REALTIME, &timestamp)) {
struct timespec start_ts;
if (clock_gettime(CLOCK_REALTIME, &start_ts)) {
LOG_E(UTIL, "clock_gettime failed\n");
exit(1);
}
double leftover_samples = 0;
int64_t last_sample_index = 0;
vrtsim_state->start_ts = start_ts;
while (vrtsim_state->run_timing_thread) {
struct timespec current_time;
if (clock_gettime(CLOCK_REALTIME, &current_time)) {
LOG_E(UTIL, "clock_gettime failed\n");
exit(1);
}
uint64_t diff = (current_time.tv_sec - timestamp.tv_sec) * 1000000000 + (current_time.tv_nsec - timestamp.tv_nsec);
timestamp = current_time;
double samples_to_produce = vrtsim_state->sample_rate * vrtsim_state->timescale * diff / 1e9;
// Attempt to correct compounding rounding error
leftover_samples += samples_to_produce - (uint64_t)samples_to_produce;
if (leftover_samples > 1.0f) {
samples_to_produce += 1;
leftover_samples -= 1;
}
AssertFatal(samples_to_produce >= 0, "Negative samples to produce: %f\n", samples_to_produce);
uint64_t diff = (current_time.tv_sec - start_ts.tv_sec) * 1000000000 + (current_time.tv_nsec - start_ts.tv_nsec);
double sample_index = vrtsim_state->sample_rate * vrtsim_state->timescale * diff / 1e9;
int64_t samples_to_produce = sample_index - last_sample_index;
shm_td_iq_channel_produce_samples(vrtsim_state->channel, samples_to_produce);
last_sample_index = sample_index;
usleep(1);
}
return 0;
@@ -628,6 +623,14 @@ static int vrtsim_set_freq(openair0_device *device, openair0_config_t *openair0_
return 0;
}
openair0_timestamp vrtsim_get_timestamp(openair0_device *device, struct timespec *ts)
{
vrtsim_state_t *vrtsim_state = (vrtsim_state_t *)device->priv;
uint64_t diff = (ts->tv_sec - vrtsim_state->start_ts.tv_sec) * 1000000000 + (ts->tv_nsec - vrtsim_state->start_ts.tv_nsec);
double diff_samples = vrtsim_state->sample_rate * vrtsim_state->timescale * diff / 1e9;
return diff_samples > 0 ? diff_samples : 0;
}
__attribute__((__visibility__("default"))) int device_init(openair0_device *device, openair0_config_t *openair0_cfg)
{
vrtsim_state_t *vrtsim_state = calloc_or_fail(1, sizeof(vrtsim_state_t));
@@ -643,6 +646,9 @@ __attribute__((__visibility__("default"))) int device_init(openair0_device *devi
device->trx_set_gains_func = vrtsim_stub2;
device->trx_write_func = vrtsim_write;
device->trx_read_func = vrtsim_read;
if (vrtsim_state->role == ROLE_SERVER) {
device->get_timestamp = vrtsim_get_timestamp;
}
device->type = RFSIMULATOR;
device->openair0_cfg = &openair0_cfg[0];

View File

@@ -0,0 +1,56 @@
# Asn1_verbosity, choice in: none, info, annoying
Asn1_verbosity = "none";
RUs = (
{
local_rf = "no";
nb_tx = 2;
nb_rx = 2;
att_tx = 0;
att_rx = 0;
bands = [77];
tx_bw = [106];
rx_bw = [106];
max_pdschReferenceSignalPower = -27;
max_rxgain = 75;
sf_extension = 0;
eNB_instances = [0];
ru_thread_core = -1;
sl_ahead = 5;
tr_preference = "raw_if4p5"; # important: activate FHI7.2
do_precoding = 0; # needs to match O-RU configuration
}
);
log_config :
{
global_log_level = "info";
hw_log_level = "info";
phy_log_level = "info";
mac_log_level = "info";
rlc_log_level = "info";
pdcp_log_level = "info";
rrc_log_level = "info";
ngap_log_level = "info";
f1ap_log_level = "info";
};
fhi_72 = {
dpdk_devices = ("0000:41:12.0", "0000:41:12.1"); # one VF can be used as well
system_core = 25;
io_core = 26;
worker_cores = (27);
du_addr = ("00:11:22:33:54:00", "00:11:22:33:54:01");
mtu = 9216;
fh_config = ({
T1a_cp_dl = (285, 470);
T1a_cp_ul = (285, 429);
T1a_up = (200, 350);
Ta4 = (110, 180);
ru_config = {
iq_width = 9;
iq_width_prach = 9;
};
});
};