Commit afed0cec authored by Raymond Knopp's avatar Raymond Knopp

testing with 20 and 50 MHz 2x2 on Jaguar. Adding simple documentation in ethernet.md file.

parent 745ef122
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include "common/utils/assertions.h" #include "common/utils/assertions.h"
#include "common/utils/system.h" #include "common/utils/system.h"
#include "common/ran_context.h" #include "common/ran_context.h"
#include "rt_profiling.h"
#include "../../ARCH/COMMON/common_lib.h" #include "../../ARCH/COMMON/common_lib.h"
#include "../../ARCH/ETHERNET/USERSPACE/LIB/ethernet_lib.h" #include "../../ARCH/ETHERNET/USERSPACE/LIB/ethernet_lib.h"
...@@ -1222,6 +1223,12 @@ void ru_tx_func(void *param) { ...@@ -1222,6 +1223,12 @@ void ru_tx_func(void *param) {
int print_frame = 8; int print_frame = 8;
char filename[40]; char filename[40];
// note that this will break for 60/120 kHz, to be handled
int absslot_tx = info->timestamp_tx/fp->get_samples_per_slot(slot_tx,fp);
int absslot_rx = absslot_tx-ru->sl_ahead;
int rt_prof_idx = absslot_rx % RT_PROF_DEPTH;
clock_gettime(CLOCK_MONOTONIC,&ru->rt_ru_profiling.start_RU_TX[rt_prof_idx]);
// do TX front-end processing if needed (precoding and/or IDFTs) // do TX front-end processing if needed (precoding and/or IDFTs)
if (ru->feptx_prec) ru->feptx_prec(ru,frame_tx,slot_tx); if (ru->feptx_prec) ru->feptx_prec(ru,frame_tx,slot_tx);
...@@ -1264,6 +1271,11 @@ void ru_tx_func(void *param) { ...@@ -1264,6 +1271,11 @@ void ru_tx_func(void *param) {
}//for (i=0; i<ru->nb_tx; i++) }//for (i=0; i<ru->nb_tx; i++)
}//if(frame_tx == print_frame) }//if(frame_tx == print_frame)
}//else emulate_rf }//else emulate_rf
clock_gettime(CLOCK_MONOTONIC,&ru->rt_ru_profiling.return_RU_TX[rt_prof_idx]);
struct timespec *t0=&ru->rt_ru_profiling.start_RU_TX[rt_prof_idx];
struct timespec *t1=&ru->rt_ru_profiling.return_RU_TX[rt_prof_idx];
LOG_D(PHY,"rt_prof_idx %d : RU_TX time %d\n",rt_prof_idx,(int)(1e9 * (t1->tv_sec - t0->tv_sec) + (t1->tv_nsec-t0->tv_nsec)));
} }
void *ru_thread( void *param ) { void *ru_thread( void *param ) {
...@@ -1424,6 +1436,9 @@ void *ru_thread( void *param ) { ...@@ -1424,6 +1436,9 @@ void *ru_thread( void *param ) {
proc->timestamp_tx += fp->get_samples_per_slot((sl+slidx)%fp->slots_per_frame,fp); proc->timestamp_tx += fp->get_samples_per_slot((sl+slidx)%fp->slots_per_frame,fp);
proc->frame_tx = (proc->tti_rx > (fp->slots_per_frame-1-(ru->sl_ahead))) ? (proc->frame_rx+1)&1023 : proc->frame_rx; proc->frame_tx = (proc->tti_rx > (fp->slots_per_frame-1-(ru->sl_ahead))) ? (proc->frame_rx+1)&1023 : proc->frame_rx;
proc->tti_tx = (proc->tti_rx + ru->sl_ahead)%fp->slots_per_frame; proc->tti_tx = (proc->tti_rx + ru->sl_ahead)%fp->slots_per_frame;
int absslot_rx = proc->timestamp_rx/fp->get_samples_per_slot(proc->tti_rx,fp);
int rt_prof_idx = absslot_rx % RT_PROF_DEPTH;
clock_gettime(CLOCK_MONOTONIC,&ru->rt_ru_profiling.return_RU_south_in[rt_prof_idx]);
LOG_D(PHY,"AFTER fh_south_in - SFN/SL:%d%d RU->proc[RX:%d.%d TX:%d.%d] RC.gNB[0]:[RX:%d%d TX(SFN):%d]\n", LOG_D(PHY,"AFTER fh_south_in - SFN/SL:%d%d RU->proc[RX:%d.%d TX:%d.%d] RC.gNB[0]:[RX:%d%d TX(SFN):%d]\n",
frame,slot, frame,slot,
proc->frame_rx,proc->tti_rx, proc->frame_rx,proc->tti_rx,
...@@ -1439,6 +1454,7 @@ void *ru_thread( void *param ) { ...@@ -1439,6 +1454,7 @@ void *ru_thread( void *param ) {
if (slot_type == NR_UPLINK_SLOT || slot_type == NR_MIXED_SLOT) { if (slot_type == NR_UPLINK_SLOT || slot_type == NR_MIXED_SLOT) {
if (ru->feprx) { if (ru->feprx) {
ru->feprx(ru,proc->tti_rx); ru->feprx(ru,proc->tti_rx);
clock_gettime(CLOCK_MONOTONIC,&ru->rt_ru_profiling.return_RU_feprx[rt_prof_idx]);
//LOG_M("rxdata.m","rxs",ru->common.rxdata[0],1228800,1,1); //LOG_M("rxdata.m","rxs",ru->common.rxdata[0],1228800,1,1);
LOG_D(PHY,"RU proc: frame_rx = %d, tti_rx = %d\n", proc->frame_rx, proc->tti_rx); LOG_D(PHY,"RU proc: frame_rx = %d, tti_rx = %d\n", proc->frame_rx, proc->tti_rx);
if (IS_SOFTMODEM_DOSCOPE && RC.gNB[0]->scopeData) if (IS_SOFTMODEM_DOSCOPE && RC.gNB[0]->scopeData)
...@@ -1464,14 +1480,18 @@ void *ru_thread( void *param ) { ...@@ -1464,14 +1480,18 @@ void *ru_thread( void *param ) {
prach_oc, prach_oc,
proc->frame_rx,proc->tti_rx); proc->frame_rx,proc->tti_rx);
} }
clock_gettime(CLOCK_MONOTONIC,&ru->rt_ru_profiling.return_RU_prachrx[rt_prof_idx]);
free_nr_ru_prach_entry(ru,prach_id); free_nr_ru_prach_entry(ru,prach_id);
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_RU_PRACH_RX, 0 ); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_RU_PRACH_RX, 0 );
} // end if (prach_id > 0)
} // end if (ru->feprx)
else {
memset(&ru->rt_ru_profiling.return_RU_feprx[rt_prof_idx],0,sizeof(struct timespec));
memset(&ru->rt_ru_profiling.return_RU_prachrx[rt_prof_idx],0,sizeof(struct timespec));
} }
} } // end if (slot_type == NR_UPLINK_SLOT || slot_type == NR_MIXED_SLOT) {
}
// At this point, all information for subframe has been received on FH interface // At this point, all RX information for slot has been received on FH interface and processed by RU
res = pullTpool(gNB->resp_L1, gNB->threadPool); res = pullTpool(gNB->resp_L1, gNB->threadPool);
syncMsg = (processingData_L1_t *)NotifiedFifoData(res); syncMsg = (processingData_L1_t *)NotifiedFifoData(res);
syncMsg->gNB = gNB; syncMsg->gNB = gNB;
...@@ -1483,7 +1503,7 @@ void *ru_thread( void *param ) { ...@@ -1483,7 +1503,7 @@ void *ru_thread( void *param ) {
res->key = proc->tti_rx; res->key = proc->tti_rx;
pushTpool(gNB->threadPool, res); pushTpool(gNB->threadPool, res);
} } //end while (!oai_exit)
printf( "Exiting ru_thread \n"); printf( "Exiting ru_thread \n");
......
/*
* 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 time_profiling.h
* \brief Definitions for proflling real-time scheduling
* \author
* \date 2022
* \version 0.1
* \company Eurecom
* \email:
* \note
* \warning
*/
#ifndef TIME_PROFILING_H
#define TIME_PROFILING_H
#ifdef __cplusplus
extern "C"
{
#endif
// depth of trace in slots
#define RT_PROF_DEPTH 100
typedef struct {
int absslot_rx[RT_PROF_DEPTH];
struct timespec return_RU_south_in[RT_PROF_DEPTH];
struct timespec return_RU_feprx[RT_PROF_DEPTH];
struct timespec return_RU_prachrx[RT_PROF_DEPTH];
struct timespec return_RU_pushL1[RT_PROF_DEPTH];
struct timespec start_RU_TX[RT_PROF_DEPTH];
struct timespec return_RU_TX[RT_PROF_DEPTH];
} rt_ru_profiling_t;
typedef struct {
int absslot_rx[RT_PROF_DEPTH];
struct timespec return_L1_RX[RT_PROF_DEPTH];
struct timespec return_L1_TX[RT_PROF_DEPTH];
struct timespec return_L1_prachrx[RT_PROF_DEPTH];
struct timespec return_L1_pushL1[RT_PROF_DEPTH];
} rt_l1_profiling_t;
#ifdef __cplusplus
}
#endif
#endif
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include "defs_common.h" #include "defs_common.h"
#include "nfapi_nr_interface_scf.h" #include "nfapi_nr_interface_scf.h"
#include <common/utils/threadPool/thread-pool.h> #include <common/utils/threadPool/thread-pool.h>
#include <executables/rt_profiling.h>
#define MAX_BANDS_PER_RRU 4 #define MAX_BANDS_PER_RRU 4
#define MAX_RRU_CONFIG_SIZE 1024 #define MAX_RRU_CONFIG_SIZE 1024
...@@ -678,6 +679,8 @@ typedef struct RU_t_s { ...@@ -678,6 +679,8 @@ typedef struct RU_t_s {
int tpcores[16]; int tpcores[16];
/// number of cores for RU ThreadPool /// number of cores for RU ThreadPool
int num_tpcores; int num_tpcores;
/// structure for analyzing high-level RT measurements
rt_ru_profiling_t rt_ru_profiling;
} RU_t; } RU_t;
......
...@@ -56,7 +56,7 @@ void nr_feptx0(RU_t *ru,int tti_tx,int first_symbol, int num_symbols, int aa) { ...@@ -56,7 +56,7 @@ void nr_feptx0(RU_t *ru,int tti_tx,int first_symbol, int num_symbols, int aa) {
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
unsigned int slot_offset,slot_offsetF,initial_slot_offset,num_samples;; unsigned int slot_offset,slot_offsetF;
int slot = tti_tx; int slot = tti_tx;
...@@ -72,7 +72,6 @@ void nr_feptx0(RU_t *ru,int tti_tx,int first_symbol, int num_symbols, int aa) { ...@@ -72,7 +72,6 @@ void nr_feptx0(RU_t *ru,int tti_tx,int first_symbol, int num_symbols, int aa) {
slot_offset += (idx_sym%(0x7<<fp->numerology_index)) ? fp->nb_prefix_samples : fp->nb_prefix_samples0; slot_offset += (idx_sym%(0x7<<fp->numerology_index)) ? fp->nb_prefix_samples : fp->nb_prefix_samples0;
slot_offset += fp->ofdm_symbol_size*first_symbol; slot_offset += fp->ofdm_symbol_size*first_symbol;
initial_slot_offset = slot_offset;
LOG_D(PHY,"SFN/SF:RU:TX:%d/%d aa %d Generating slot %d (first_symbol %d num_symbols %d) slot_offset %d, slot_offsetF %d\n",ru->proc.frame_tx, ru->proc.tti_tx,aa,slot,first_symbol,num_symbols,slot_offset,slot_offsetF); LOG_D(PHY,"SFN/SF:RU:TX:%d/%d aa %d Generating slot %d (first_symbol %d num_symbols %d) slot_offset %d, slot_offsetF %d\n",ru->proc.frame_tx, ru->proc.tti_tx,aa,slot,first_symbol,num_symbols,slot_offset,slot_offsetF);
...@@ -83,7 +82,6 @@ void nr_feptx0(RU_t *ru,int tti_tx,int first_symbol, int num_symbols, int aa) { ...@@ -83,7 +82,6 @@ void nr_feptx0(RU_t *ru,int tti_tx,int first_symbol, int num_symbols, int aa) {
num_symbols, num_symbols,
fp->nb_prefix_samples, fp->nb_prefix_samples,
CYCLIC_PREFIX); CYCLIC_PREFIX);
num_samples=num_symbols*(fp->nb_prefix_samples+fp->ofdm_symbol_size);
} else { } else {
if (fp->numerology_index != 0) { if (fp->numerology_index != 0) {
...@@ -101,8 +99,6 @@ void nr_feptx0(RU_t *ru,int tti_tx,int first_symbol, int num_symbols, int aa) { ...@@ -101,8 +99,6 @@ void nr_feptx0(RU_t *ru,int tti_tx,int first_symbol, int num_symbols, int aa) {
num_symbols-1, num_symbols-1,
fp->nb_prefix_samples, fp->nb_prefix_samples,
CYCLIC_PREFIX); CYCLIC_PREFIX);
num_samples=(num_symbols-1)*(fp->nb_prefix_samples+fp->ofdm_symbol_size)+
fp->nb_prefix_samples0+fp->ofdm_symbol_size;
} }
else { // all symbols in slot have shorter prefix else { // all symbols in slot have shorter prefix
PHY_ofdm_mod(&ru->common.txdataF_BF[aa][slot_offsetF], PHY_ofdm_mod(&ru->common.txdataF_BF[aa][slot_offsetF],
...@@ -111,7 +107,6 @@ void nr_feptx0(RU_t *ru,int tti_tx,int first_symbol, int num_symbols, int aa) { ...@@ -111,7 +107,6 @@ void nr_feptx0(RU_t *ru,int tti_tx,int first_symbol, int num_symbols, int aa) {
num_symbols, num_symbols,
fp->nb_prefix_samples, fp->nb_prefix_samples,
CYCLIC_PREFIX); CYCLIC_PREFIX);
num_samples=num_symbols*(fp->nb_prefix_samples+fp->ofdm_symbol_size);
} }
} // numerology_index!=0 } // numerology_index!=0
else { //numerology_index == 0 else { //numerology_index == 0
...@@ -137,7 +132,6 @@ void nr_feptx0(RU_t *ru,int tti_tx,int first_symbol, int num_symbols, int aa) { ...@@ -137,7 +132,6 @@ void nr_feptx0(RU_t *ru,int tti_tx,int first_symbol, int num_symbols, int aa) {
slot_offsetF += fp->ofdm_symbol_size; slot_offsetF += fp->ofdm_symbol_size;
} }
} // for(idx_symbol.. } // for(idx_symbol..
num_samples=slot_offset-initial_slot_offset;
} // numerology 0 } // numerology 0
} }
......
# Ethernet drivers
This directory contains ethernet-based drivers for fronthaul. The only functional versions today are ECPRI time-domain (split 8) over UDP for AW2S and IF4.5 (split 7) for OAI RU over UDP. The RAW ethernet implementation is currently not tested and quite possibly obsolete.
## license
Author:
Raymond Knopp, EURECOM
OAI License V1.1
# Top-level
The implement the OAI IF device interface which provides the transmit/receive (trx) functions for OAI. Some minimal control-plane socket handling for IF4p5 is also provided. It comprises the following top-level functions (ethernet_lib.c) which are mapped to the OAI device structure:
* trx_eth_start : create sockets and threads to handle I/O
* trx_eth_end : close sockets
* trx_eth_stop : stops 3rd party (AW2S) device
* trx_eth_set_freq : empty
* trx_eth_set_gains : empty
* trx_eth_get_stats : empty
* trx_eth_reset_stats : empty
* trx_eth_write_init : empty
* ethernet_tune : write certain performance-related parameters to ethernet device
* transport_init : entry routine to fill device data structure with function pointers
# IF4p5 U-plane implementation
It is contained in the eth_udp.c and eth_raw.c files. The two basic routines are
* trx_eth_read_udp_IF4p5() : implements a blocking read for three particular IF4p5 packets, IF4p5_PULFFT (for OAI RCC/DU), IF4p5_PRACH and IF4p5_PDLFFT (for OAI RU). The packets are parsed and mapped to the appropriate physical channels by the OAI physical layer
* trx_eth_write_udp_IF4p5 : implements a write for the three IF4p5 packets.
* trx_eth_ctlsend_udp : implements the sending component for the control socket
* trx_eth_ctlrecv_udp : implements the receive component for the control socket (blocking read)
# ECPRI U-plane implementation
It is contained in the eth_udp.c file and only implements ECPRI/UDP. The implementation has 2 top-level functions:
* trx_eth_write_udp : This sends a stream of packets containing 16-bit I/ 16-bit Q samples to the ECPRI RU. It uses the proprietary AW2S format (user-defined). Each packet carries 256 samples and some header information (timestamp, antenna index) and fits inslitly more than 1024 bytes. Each packet with Ethernet, IP and UDP headers fits into a regular Ethernet frame. The transmit function does not block and pushes the data to a worker thread (udp_write_thread) which runs in the background. The worker thread scales the IQ samples to fit in 16-bit units and forms the ECPRI packets for each antenna. The signals are split into 256-sample chunks and antennas are handled in sequence for each chunk. This ensures that all antennas receive their signals more or less at the same time.
* trx_eth_read_udp : This is a blocking read which waits for a worker thread (udp_read_thread) listening on the U-plane socket to have acquired enough samples satisfying the request (nsamps). Usually a slot or subframe worth of samples. The worker thread identifies the antenna index (aid) and timestamp of each packet and copies the received chunk into the destination memory location according to the timestamp modulo the length of the receive buffer. There is only one memcpy in the driver and the end OAI application can use the data in its normal local (RU.common.rxdata[aid]). The memcpy is needed since the destination address depends on the antenna index which is carried in the received packet itself. Kernel filtering and redirection (C/EBPF) doesn't seem to be possible with the AW2S packet format.
The two threads should be pinned to isolated CPUs to maximize performance. Their CPU id's can be passed to the driver from the OAI application. tx_fh_core and rx_fh_core in the RU section of the OAI .conf file.
# Obsolete code
Non ECPRI split 8 and IF4p5 with RAW sockets is not functional anymore (eth_raw.c). split-8 will be resusitated if an RU implementing ECPRI is integrated with OAI. IF4p5 with raw sockets is replaced with the FHI Split 7.2 interface.
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