diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt index 0bc19f419507d41d54fd13d59c45535290c4a92b..e061da3ef287eb4934e45fcb535eb4532bc0bc5e 100644 --- a/cmake_targets/CMakeLists.txt +++ b/cmake_targets/CMakeLists.txt @@ -362,14 +362,14 @@ elseif (${RF_BOARD} STREQUAL "OAI_BLADERF") ${OPENAIR_TARGETS}/ARCH/BLADERF/USERSPACE/LIB/bladerf_lib.c ) LINK_DIRECTORIES("/usr/lib/x86_64-linux-gnu/") - set(option_HW_lib "bladeRF") - + elseif (${RF_BOARD} STREQUAL "ETHERNET") include_directories ("${OPENAIR_TARGETS}/ARCH/ETHERNET/USERSPACE/LIB") set(HW_SOURCE ${HW_SOURCE} - ${OPENAIR_TARGETS}/ARCH/ETHERNET/USERSPACE/LIB/ethernet_lib.c - ) - + ${OPENAIR_TARGETS}/ARCH/ETHERNET/USERSPACE/LIB/ethernet_lib.c + ) + set(LOWLATENCY True) + elseif (${RF_BOARD} STREQUAL "CPRIGW") set(HW_SOURCE ${HW_SOURCE} ${OPENAIR_TARGETS}/ARCH/CPRIGW/USERSPACE/LIB/cprigw_lib.c @@ -393,7 +393,7 @@ add_boolean_option(OAI_NW_DRIVER_TYPE_ETHERNET False "????") add_boolean_option(DISABLE_USE_NAS False "???") add_boolean_option(ENABLE_STANDALONE_EPC True "Compile MME, SGW and PGW in a single executable") add_boolean_option(EPC_BUILD False "???") -add_boolean_option(LOWLATENCY True "Use the Linux scheduler SCHED_DEADLINE: kernel >= 3.14") +add_boolean_option(LOWLATENCY True "Use the Linux scheduler SCHED_DEADLINE: kernel >= 3.14") add_boolean_option(NAS_ADDRESS_FIX False "specific to oaisim: for nasmesh driver") add_boolean_option(NAS_NETLINK False "???? Must be True to compile nasmesh driver without rtai") add_boolean_option(OAISIM False "specific to oaisim") @@ -1567,6 +1567,8 @@ add_executable(lte-softmodem-nos1 ${OPENAIR1_DIR}/SIMULATION/TOOLS/taus.c ${OPENAIR_TARGETS}/SIMU/USER/init_lte.c ${OPENAIR_TARGETS}/COMMON/create_tasks.c + #${OPENAIR2_DIR}/RRC/NAS/nas_config.c # enable if you want rrc to mount ip interface + #${OPENAIR2_DIR}/RRC/NAS/rb_config.c ${OPENAIR1_DIR}/SIMULATION/ETH_TRANSPORT/netlink_init.c ${HW_SOURCE} ${RTAI_SOURCE} @@ -1711,6 +1713,36 @@ target_link_libraries (oai_sgw ) endif(MESSAGE_CHART_GENERATOR) + +# rrh +################################ +set(DRIVER2013) + +#Note: only on RF type is currently supported for RRH +add_executable(rrh_gw + ${OPENAIR_TARGETS}/RT/USER/rrh_gw.c + ${OPENAIR_TARGETS}/RT/USER/eNB_transport_IQ.c + ${OPENAIR_TARGETS}/RT/USER/UE_transport_IQ.c + ${OPENAIR_TARGETS}/RT/USER/rt_wrapper.c + ${OPENAIR_TARGETS}/ARCH/ETHERNET/USERSPACE/LIB/ethernet_lib.c + ${HW_SOURCE} + ) +# assert and common_lib.h +target_include_directories(rrh_gw PRIVATE ${OPENAIR_DIR}/common/utils/itti ${OPENAIR_TARGETS}/ARCH/COMMON ${OPENAIR_TARGETS}/ARCH/ETHERNET/USERSPACE/LIB/ ) + +target_link_libraries(rrh_gw + -Wl,--start-group + UTIL LFDS + -Wl,--end-group ) +target_link_libraries (rrh_gw rt pthread m ) +target_link_libraries (rrh_gw ${option_HW_lib} ${LIBBOOST_LIBRARIES} ) + +#Message("-- default_HW_lib=ETHERNET") # only in case of RRH +Message("-- option_HW_lib=${option_HW_lib}") +Message("-- HW_SOURCE=${HW_SOURCE}") + + + # USIM process ################# #add_executable(usim diff --git a/cmake_targets/autotests/run_compilation_autotests.bash b/cmake_targets/autotests/run_compilation_autotests.bash index 50ec7ee21c44c4e717c1265665a7387bd24b238f..228331ea8b30974163bad1b6a7d5ead75ded8f0a 100755 --- a/cmake_targets/autotests/run_compilation_autotests.bash +++ b/cmake_targets/autotests/run_compilation_autotests.bash @@ -34,7 +34,7 @@ mkdir -p $tdir/bin $tdir/log updated=$(svn st -q $OPENAIR_DIR) if [ "$updated" != "" ] ; then - echo_warning "some files are not in svn: $updated" + echo_warning "some files are not in svn:\n $updated" fi cd $tdir @@ -83,5 +83,9 @@ test_compile \ test.0120 nasmesh \ CMakeFiles/nasmesh/nasmesh.ko $tdir/bin/nasmesh.ko +test_compile \ + test.0130 rrh_gw \ + rrh_gw $tdir/bin/rrh_gw + # write the test results into a file xUnit_write "$tdir/log/compilation_autotests.xml" diff --git a/cmake_targets/autotests/test.0130/CMakeLists.txt b/cmake_targets/autotests/test.0130/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..45c99b01ab5c684a16661316a6fe9c69c1ec480a --- /dev/null +++ b/cmake_targets/autotests/test.0130/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 2.8) +set(ENABLE_VCD_FIFO False ) +set(ENABLE_ITTI False ) +set(RF_BOARD "ETHERNET") +set(PACKAGE_NAME "\"rrh_gw\"") +include(${CMAKE_CURRENT_SOURCE_DIR}/../../CMakeLists.txt) diff --git a/cmake_targets/build_oai b/cmake_targets/build_oai index ae5de617b1549becb2ebe82c6c806f1a0e8e8dd2..73924cee2a3a7a2796fcb9f7a43642bb891ef9bf 100755 --- a/cmake_targets/build_oai +++ b/cmake_targets/build_oai @@ -75,11 +75,13 @@ Options Makes the UE specific parts (ue_ip, usim, nvram) --EPC Makes the EPC (MME-SPGW, HSS) +--RRH + Makes the RRH -r | --3gpp-release default is Rel10, Rel8 limits the implementation to 3GPP Release 8 version -w | --hardware - EXMIMO (Default), USRP, BLADERF, None + EXMIMO (Default), USRP, BLADERF, ETHERNET, None Adds this RF board support (in external packages installation and in compilation) --oaisim Makes the oaisim simulator. Hardware will be defaulted to "NONE". @@ -147,6 +149,10 @@ function main() { EPC=1 echo_info "Will compile EPC" shift;; + --RRH) + RRH=1 + echo_info "Will compile RRH" + shift;; -r | --3gpp-release) REL=$2 echo_info "setting release to: $REL" @@ -326,7 +332,7 @@ function main() { # compilations \ # at_commands at_nas_ue \ # at_nas_ue $dbin/at_nas_ue - + [ "$CLEAN" = "1" ] && rm -rf $DIR/nas_sim_tools/build mkdir -p $DIR/nas_sim_tools/build cd $DIR/nas_sim_tools/build @@ -497,6 +503,32 @@ function main() { # oaisim_mme $dbin/oaisim_mme.$REL fi + # RRH compilation + ################## + if [ "$RRH" = "1" ] ; then + echo_info "Compiling RRH" + if [ $HW == "ETHERNET" ] ; then + echo_info "RF frontend for RRH is not defined. This mode is used for testing (loopback)." + elif [ $HW != "EXMIMO" -a $HW != "OAI_USRP" -a $HW != "OAI_BLADERF" ] ; then + echo_fatal "Hardware not defined ($HW)" + fi + cmake_file=$DIR/rrh_gw/CMakeLists.txt + echo "cmake_minimum_required(VERSION 2.8)" > $cmake_file + echo "set(ENABLE_VCD_FIFO $VCD_TIMING )" >> $cmake_file + echo "set(ENABLE_ITTI False )" >> $cmake_file + echo "set(RF_BOARD \"${HW}\")" >> $cmake_file + echo 'set(PACKAGE_NAME "\"rrh_gw\"")' >> $cmake_file + echo 'include(${CMAKE_CURRENT_SOURCE_DIR}/../CMakeLists.txt)' >> $cmake_file + + [ "$CLEAN" = "1" ] && rm -rf $DIR/rrh_gw/build + mkdir -p $DIR/rrh_gw/build + cd $DIR/rrh_gw/build + cmake .. + compilations \ + rrh_gw rrh_gw \ + rrh_gw $dbin/rrh_gw + fi + # EPC compilation ################## if [ "$EPC" = "1" ] ; then diff --git a/cmake_targets/rrh_gw/CMakeLists.txt b/cmake_targets/rrh_gw/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..9ff1437f0c58db559a3a9386489a18cc1e2a596e --- /dev/null +++ b/cmake_targets/rrh_gw/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 2.8) +set(ENABLE_VCD_FIFO False ) +set(ENABLE_ITTI False ) +set(RF_BOARD "ETHERNET") +set(PACKAGE_NAME "\"rrh_gw\"") +include(${CMAKE_CURRENT_SOURCE_DIR}/../CMakeLists.txt) diff --git a/openair1/PHY/TOOLS/time_meas.c b/openair1/PHY/TOOLS/time_meas.c index c734db09af8f17d36cdfa811ba6b51cc898e5b85..675de01d31e7b1de429d5984995856cc032a1af2 100644 --- a/openair1/PHY/TOOLS/time_meas.c +++ b/openair1/PHY/TOOLS/time_meas.c @@ -39,10 +39,12 @@ double get_cpu_freq_GHz(void) { time_stats_t ts = {0}; reset_meas(&ts); - start_meas(&ts); + ts.trials++; + ts.in = rdtsc_oai(); sleep(1); - stop_meas(&ts); + ts.diff = (rdtsc_oai()-ts.in); cpu_freq_GHz = (double)ts.diff/1000000000; + printf("CPU Freq is %f \n", cpu_freq_GHz); return cpu_freq_GHz; } diff --git a/openair1/PHY/TOOLS/time_meas.h b/openair1/PHY/TOOLS/time_meas.h index 2bd6dc489deb07e6924debcc4c3ce7aebc6115fc..1bb5212b55f32263f2d12431c50349b492e923a4 100644 --- a/openair1/PHY/TOOLS/time_meas.h +++ b/openair1/PHY/TOOLS/time_meas.h @@ -43,8 +43,8 @@ double cpu_freq_GHz; typedef struct { long long in; - long long diff_now; long long diff; + long long diff_now; long long p_time; /*!< \brief absolute process duration */ long long diff_square; /*!< \brief process duration square */ long long max; @@ -113,38 +113,31 @@ static inline void stop_meas(time_stats_t *ts) if (opp_enabled) { long long out = rdtsc_oai(); - - ts->diff_now = (out-ts->in); - - ts->diff += (out-ts->in); - /// process duration is the difference between two clock points - ts->p_time = (out-ts->in); - ts->diff_square += (out-ts->in)*(out-ts->in); - - if ((out-ts->in) > ts->max) - ts->max = out-ts->in; - + + ts->diff_now = (out-ts->in); + + ts->diff_now = (out-ts->in); + ts->diff += (out-ts->in); + /// process duration is the difference between two clock points + ts->p_time = (out-ts->in); + ts->diff_square += (out-ts->in)*(out-ts->in); + + if ((out-ts->in) > ts->max) + ts->max = out-ts->in; + } } -static inline void reset_meas(time_stats_t *ts) -{ +static inline void reset_meas(time_stats_t *ts) { + static int cpu_freq_set=0; - if (opp_enabled) { - ts->trials=0; - ts->diff_now=0; - ts->diff=0; - ts->p_time=0; - ts->diff_square=0; - ts->max=0; - - if (cpu_freq_set == 0) { - cpu_freq_set = 1; - get_cpu_freq_GHz(); - printf("CPU Freq is %f \n", cpu_freq_GHz); - } - } + ts->trials=0; + ts->diff=0; + ts->diff_now=0; + ts->p_time=0; + ts->diff_square=0; + ts->max=0; } diff --git a/openair2/UTIL/LOG/log.c b/openair2/UTIL/LOG/log.c index 1ae3b720f2691cd9ce4b34cffe76373b7e8552d4..283a906b5885ec72a5750f054cb0ba882765ed5e 100755 --- a/openair2/UTIL/LOG/log.c +++ b/openair2/UTIL/LOG/log.c @@ -398,7 +398,15 @@ int logInit (void) g_log->log_component[SCTP].fd = 0; g_log->log_component[SCTP].filelog = 0; g_log->log_component[SCTP].filelog_name = ""; - + + g_log->log_component[RRH].name = "RRH"; + g_log->log_component[RRH].level = LOG_EMERG; + g_log->log_component[RRH].flag = LOG_MED; + g_log->log_component[RRH].interval = 1; + g_log->log_component[RRH].fd = 0; + g_log->log_component[RRH].filelog = 0; + g_log->log_component[RRH].filelog_name = ""; + g_log->level2string[LOG_EMERG] = "G"; //EMERG g_log->level2string[LOG_ALERT] = "A"; // ALERT g_log->level2string[LOG_CRIT] = "C"; // CRITIC diff --git a/openair2/UTIL/LOG/log.h b/openair2/UTIL/LOG/log.h index 9de693fa92fbaf70414f4b41750e628284534b43..724504656f9a6570c5138cd65573f6c977e8937e 100755 --- a/openair2/UTIL/LOG/log.h +++ b/openair2/UTIL/LOG/log.h @@ -267,6 +267,7 @@ typedef enum { TMR, USIM, LOCALIZE, + RRH, MAX_LOG_COMPONENTS, } comp_name_t; diff --git a/openair2/UTIL/LOG/vcd_signal_dumper.c b/openair2/UTIL/LOG/vcd_signal_dumper.c index 3775b7e1dc06f939c018ac7e582cec0e2bfc8ce1..62b380fddbd41f0b3db1b062940d6db6ec76f9bf 100644 --- a/openair2/UTIL/LOG/vcd_signal_dumper.c +++ b/openair2/UTIL/LOG/vcd_signal_dumper.c @@ -77,6 +77,8 @@ struct vcd_module_s { const char* eurecomVariablesNames[] = { "frame_number_TX_eNB", "frame_number_RX_eNB", + "runtime_TX_eNB", + "runtime_RX_eNB", "frame_number_TX_UE", "frame_number_RX_UE", "slot_number_TX_UE", @@ -94,6 +96,13 @@ const char* eurecomVariablesNames[] = { "rxcnt", "trx_ts", "trx_tst", + "tx_ts", + "rx_ts", + "hw_cnt_rx", + "lhw_cnt_rx", + "hw_cnt_tx", + "lhw_cnt_tx", + "pck_rx", "dummy_dump", "itti_send_msg", "itti_poll_msg", @@ -133,6 +142,15 @@ const char* eurecomFunctionsNames[] = { "ue_thread_tx", "ue_thread_rx", + /* RRH signals */ + "eNB_tx", + "eNB_rx", + "eNB_trx", + "eNB_tm", + "eNB_rx_sleep", + "eNB_tx_sleep", + "eNB_proc_sleep", + /* PHY signals */ "ue_synch", "ue_slot_fep", diff --git a/openair2/UTIL/LOG/vcd_signal_dumper.h b/openair2/UTIL/LOG/vcd_signal_dumper.h index 2d8560d615e642bd494cb20cb815daf691c141a5..825b05d53576f30a03bfa9353e84a5c453a037f6 100644 --- a/openair2/UTIL/LOG/vcd_signal_dumper.h +++ b/openair2/UTIL/LOG/vcd_signal_dumper.h @@ -49,6 +49,8 @@ typedef enum { VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_TX_ENB = 0, VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_RX_ENB, + VCD_SIGNAL_DUMPER_VARIABLES_RUNTIME_TX_ENB, + VCD_SIGNAL_DUMPER_VARIABLES_RUNTIME_RX_ENB, VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_TX_UE, VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_RX_UE, VCD_SIGNAL_DUMPER_VARIABLES_SLOT_NUMBER_TX_UE, @@ -66,6 +68,13 @@ typedef enum { VCD_SIGNAL_DUMPER_VARIABLES_RXCNT, VCD_SIGNAL_DUMPER_VARIABLES_TRX_TS, VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST, + VCD_SIGNAL_DUMPER_VARIABLES_TX_TS, + VCD_SIGNAL_DUMPER_VARIABLES_RX_TS, + VCD_SIGNAL_DUMPER_VARIABLES_RX_HWCNT, + VCD_SIGNAL_DUMPER_VARIABLES_RX_LHWCNT, + VCD_SIGNAL_DUMPER_VARIABLES_TX_HWCNT, + VCD_SIGNAL_DUMPER_VARIABLES_TX_LHWCNT, + VCD_SIGNAL_DUMPER_VARIABLES_RX_PCK, VCD_SIGNAL_DUMPER_VARIABLES_DUMMY_DUMP, VCD_SIGNAL_DUMPER_VARIABLE_ITTI_SEND_MSG, VCD_SIGNAL_DUMPER_VARIABLE_ITTI_POLL_MSG, @@ -107,6 +116,16 @@ typedef enum { VCD_SIGNAL_DUMPER_FUNCTIONS_UE_THREAD_TX, VCD_SIGNAL_DUMPER_FUNCTIONS_UE_THREAD_RX, + /* RRH signals */ + + VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_TX, + VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_RX, + VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_TRX, + VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_TM, + VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_RX_SLEEP, + VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_TX_SLEEP, + VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_PROC_SLEEP, + /* PHY signals */ VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SYNCH, VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SLOT_FEP, diff --git a/targets/ARCH/BLADERF/USERSPACE/LIB/bladerf_lib.c b/targets/ARCH/BLADERF/USERSPACE/LIB/bladerf_lib.c index bbb8463714c7696124114636feb1bf098e5a537b..72cc76984b8212cbf5bcf2ffc581bc48cb715144 100644 --- a/targets/ARCH/BLADERF/USERSPACE/LIB/bladerf_lib.c +++ b/targets/ARCH/BLADERF/USERSPACE/LIB/bladerf_lib.c @@ -132,7 +132,7 @@ int trx_brf_end(openair0_device *device) { return 0; } -//int openair0_device_brf_init(openair0_device *device, openair0_config_t *openair0_cfg) { +//int openair0_dev_init_bladerf(openair0_device *device, openair0_config_t *openair0_cfg) { int openair0_device_init(openair0_device *device, openair0_config_t *openair0_cfg) { int status; @@ -263,15 +263,27 @@ int openair0_device_init(openair0_device *device, openair0_config_t *openair0_cf return 0; } -void brf_error(int status) { +int brf_error(int status) { exit(-1); + //return 1; // or status error code } int openair0_stop(int card) { return(0); +} + +int openair0_print_stats(openair0_device* device) { + + return(0); + +} +int openair0_reset_stats(openair0_device* device) { + + return(0); + } int openair0_set_frequencies(openair0_device* device, openair0_config_t *openair0_cfg,int dummy) { return 0; diff --git a/targets/ARCH/BLADERF/USERSPACE/LIB/bladerf_lib.h b/targets/ARCH/BLADERF/USERSPACE/LIB/bladerf_lib.h index 7a73352e71a1f1a8ba278d52692e05cffd60fa5b..52b5ca84537cc6b0689b387211b5963df53bc3a5 100644 --- a/targets/ARCH/BLADERF/USERSPACE/LIB/bladerf_lib.h +++ b/targets/ARCH/BLADERF/USERSPACE/LIB/bladerf_lib.h @@ -78,4 +78,4 @@ typedef struct { * func prototypes */ -void brf_error(int status); +int brf_error(int status); diff --git a/targets/ARCH/COMMON/common_lib.h b/targets/ARCH/COMMON/common_lib.h index dc8e508c18befe023c25e1dae7c8d9b385059b69..9560117b2ea5fc4faa10ca7ec5ae29da4a163ad3 100644 --- a/targets/ARCH/COMMON/common_lib.h +++ b/targets/ARCH/COMMON/common_lib.h @@ -26,16 +26,23 @@ Address : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE *******************************************************************************/ +/*! \file common_lib.h + * \brief common APIs for different RF frontend device + * \author HongliangXU, Navid Nikaein + * \date 2015 + * \version 0.2 + * \company Eurecom + * \maintainer: navid.nikaein@eurecom.fr + * \note + * \warning + */ -/** common_lib.h -* -* Author: HongliangXU : hong-liang-xu@agilent.com -*/ #ifndef COMMON_LIB_H #define COMMON_LIB_H #include <stdint.h> typedef int64_t openair0_timestamp; +typedef volatile int64_t openair0_vtimestamp; typedef struct openair0_device_t openair0_device; /* structrue holds the parameters to configure USRP devices */ @@ -56,10 +63,18 @@ typedef struct { int Mod_id; // device log level int log_level; + //! number of downlink resource blocks + int num_rb_dl; + //! number of samples per frame + unsigned int samples_per_frame; //! the sample rate for both transmit and receive. double sample_rate; //! number of samples per RX/TX packet (USRP + Ethernet) int samples_per_packet; + // delay in sending samples (write) due to hardware access, softmodem processing and fronthaul delay if exist + int tx_delay; + //! adjust the position of the samples after delay when sending + unsigned int tx_forward_nsamps; //! number of RX channels (=RX antennas) int rx_num_channels; //! number of TX channels (=TX antennas) @@ -84,9 +99,14 @@ typedef struct { //! Auto calibration flag int autocal[4]; //! RRH IP addr for Ethernet interface - char *rrh_ip; + char *remote_ip; //! RRH port number for Ethernet interface - int rrh_port; + int remote_port; + //! my IP addr for Ethernet interface (eNB/BBU, UE) + char *my_ip; + //! my port number for Ethernet interface (eNB/BBU, UE) + int my_port; + } openair0_config_t; typedef struct { @@ -98,10 +118,42 @@ typedef struct { +/*!\brief device type */ +typedef enum { + MIN_DEV_TYPE = 0, + /*!\brief device is ETH */ + ETH_IF, + /*!\brief device is ExpressMIMO */ + EXMIMO_IF, + /*!\brief device is USRP*/ + USRP_IF, + /*!\brief device is BLADE RF*/ + BLADERF_IF, + /*!\brief device is NONE*/ + NONE_IF, + MAX_DEV_TYPE + +} dev_type_t; + +/*!\brief type */ +typedef enum { + MIN_FUNC_TYPE = 0, + BBU_FUNC, + RRH_FUNC, + MAX_FUNC_TYPE + +}func_type_t; + struct openair0_device_t { /* Module ID of this device */ int Mod_id; + /* Type of this device */ + func_type_t func_type; + + /* Type of this device */ + dev_type_t type; + /* RF frontend parameters set by application */ openair0_config_t openair0_cfg; @@ -113,11 +165,20 @@ struct openair0_device_t { /* Called to start the transceiver. Return 0 if OK, < 0 if error */ int (*trx_start_func)(openair0_device *device); + /* Called to initiate transceiver threads */ + void (*trx_thread_func)(openair0_device *device, unsigned int rt_period, uint8_t RT_flag,uint8_t NRT_flag); + + /* Called to request connection from the transceiver/RRH. Return 0 if OK, < 0 if error */ + int (*trx_request_func)(openair0_device *device); + + /* Called to reply back to connection state to eNB/BBU. Return 0 if OK, < 0 if error */ + int (*trx_reply_func)(openair0_device *openair0); + /* Write 'nsamps' samples on each channel from buffers. buff[0] is the array for * the first channel. timestamp if the time (in samples) at which the first sample * MUST be sent * use flags = 1 to send as timestamp specfied*/ - void (*trx_write_func)(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int cc, int flags); + int (*trx_write_func)(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps,int antenna_id, int flags); /*! \brief Receive samples from hardware. * Read \ref nsamps samples from each channel to buffers. buff[0] is the array for @@ -130,7 +191,19 @@ struct openair0_device_t { * \param cc Number of channels. If cc == 1, only buff[0] is filled with samples. * \returns the number of sample read */ - int (*trx_read_func)(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps,int cc); + int (*trx_read_func)(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps,int antenna_id); + + /*! \brief print the device statistics + * \param device the hardware to use + * \returns 0 on success + */ + int (*trx_get_stats_func)(openair0_device *device); + + /*! \brief Reset device statistics + * \param device the hardware to use + * \returns 0 in success + */ + int (*trx_reset_stats_func)(openair0_device *device); /* Terminate operation of the transceiver -- free all associated resources */ void (*trx_end_func)(openair0_device *device); @@ -142,15 +215,33 @@ extern "C" { #endif -/* return 0 if OK, < 0 if error */ - int openair0_device_init(openair0_device* device, openair0_config_t *openair0_cfg); - openair0_timestamp get_usrp_time(openair0_device *device); - int openair0_set_frequencies(openair0_device* device, openair0_config_t *openair0_cfg,int exmimo_dump_config); - int openair0_set_rx_frequencies(openair0_device* device, openair0_config_t *openair0_cfg); - - int openair0_set_gains(openair0_device* device, openair0_config_t *openair0_cfg); - - int openair0_stop(int card); +/* return 0 if OK */ + +int openair0_device_init(openair0_device* device, openair0_config_t *openair0_cfg); +openair0_timestamp get_usrp_time(openair0_device *device); + +//EXMIMO +//int openair0_dev_init_exmimo(openair0_device *device, openair0_config_t *openair0_cfg); +//USPRP +//int openair0_dev_init_usrp(openair0_device* device, openair0_config_t *openair0_cfg); +//BLADERF +//int openair0_dev_init_bladerf(openair0_device* device, openair0_config_t *openair0_cfg); + +//ETHERNET +int openair0_dev_init_eth(openair0_device *device, openair0_config_t *openair0_cfg); + +int openair0_set_frequencies(openair0_device* device, openair0_config_t *openair0_cfg,int exmimo_dump_config); + +int openair0_set_rx_frequencies(openair0_device* device, openair0_config_t *openair0_cfg); + +int openair0_set_gains(openair0_device* device, openair0_config_t *openair0_cfg); + +int openair0_print_stats(openair0_device* device); + +int openair0_reset_stats(openair0_device* device); + +int openair0_stop(int card); + #ifdef __cplusplus } #endif diff --git a/targets/ARCH/ETHERNET/USERSPACE/LIB/ethernet_lib.c b/targets/ARCH/ETHERNET/USERSPACE/LIB/ethernet_lib.c index df8b150502b16d4298c0362a607f6ac7ca5d4d0b..ae5c540dc4efe2f9c4f0c0cce97d883d3a67511b 100644 --- a/targets/ARCH/ETHERNET/USERSPACE/LIB/ethernet_lib.c +++ b/targets/ARCH/ETHERNET/USERSPACE/LIB/ethernet_lib.c @@ -26,16 +26,16 @@ Address : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE *******************************************************************************/ - -/** ethernet_lib : API to stream I/Q samples over standard ethernet -* -* Authors: Pedro Dinis <pedrodinis20@gmail.com> -* Lucio Ferreira <lucio.ferreira@inov.pt> -* Raymond Knopp <raymond.knopp@eurecom.fr> -* -* Changelog: -* 06.10.2014: Initial version -*/ +/*! \fileethernet_lib.c + * \brief API to stream I/Q samples over standard ethernet + * \author Katerina Trilyraki, Navid Nikaein, Pedro Dinis, Lucio Ferreira, Raymond Knopp + * \date 2015 + * \version 0.2 + * \company Eurecom + * \maintainer: navid.nikaein@eurecom.fr + * \note + * \warning + */ #include <arpa/inet.h> #include <linux/if_packet.h> @@ -50,203 +50,361 @@ #include <errno.h> #include "common_lib.h" +#include "ethernet_lib.h" #define DEFAULT_IF "eth0" -#define BUF_SIZ 8960 /*Jumbo frame size*/ +#define BUF_SIZ 8960 /*Jumbo frame size*/ #define MAX_INST 4 -int sockfd[MAX_INST]; -struct sockaddr_in dest_addr[MAX_INST]; +//int sockfd[MAX_INST]; +int num_devices = 0; int dest_addr_len[MAX_INST]; - -int i; -int tx_len = 0; char sendbuf[MAX_INST][BUF_SIZ]; /*TODO*/ -/**PDF: Initialization of UDP Socket to communicate with one DEST */ -int ethernet_socket_init(int Mod_id, char *dest_ip,int dest_port) -{ +/* Initialization of UDP Socket to communicate with one destination */ +int ethernet_socket_init(openair0_device *device) { - /**PDF: To be passed by input argument*/ - // DEST_port = 32000; - struct sockaddr_in *dest = &dest_addr[Mod_id]; + int i = 0; + eth_state_t *eth = (eth_state_t*)device->priv; + int Mod_id = device->Mod_id; + +// struct sockaddr_in *dest = &dest_addr[Mod_id]; char str[INET_ADDRSTRLEN]; - + const char *dest_ip; + int dest_port; + + if (device->func_type == RRH_FUNC ){ + dest_ip = device->openair0_cfg.my_ip; + dest_port = device->openair0_cfg.my_port; + printf("[RRH] ip addr %s port %d\n",dest_ip, dest_port); + } else { + dest_ip = device->openair0_cfg.remote_ip; + dest_port = device->openair0_cfg.remote_port; + printf("[BBU] ip addr %s port %d\n",dest_ip, dest_port); + } + /* Open RAW socket to send on */ - - if ((sockfd[Mod_id] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { + if ((eth->sockfd[Mod_id] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { perror("ETHERNET: Error opening socket"); exit(0); } - bzero((void *)dest,sizeof(struct sockaddr)); - dest->sin_family = AF_INET; - dest->sin_addr.s_addr=inet_addr(dest_ip); - dest->sin_port=htons(dest_port); - dest_addr_len[Mod_id] = sizeof(struct sockaddr); + /* initialize destination address */ + for (i=0; i< MAX_INST; i++) + bzero((void *)&(eth->dest_addr[i]), sizeof(eth->dest_addr[i])); + + // bzero((void *)dest,sizeof(struct sockaddr_in)); + eth->dest_addr[Mod_id].sin_family = AF_INET; + inet_pton(AF_INET,dest_ip,&(eth->dest_addr[Mod_id].sin_addr.s_addr)); + eth->dest_addr[Mod_id].sin_port=htons(dest_port); + dest_addr_len[Mod_id] = sizeof(struct sockaddr_in); + inet_ntop(AF_INET, &(eth->dest_addr[Mod_id].sin_addr), str, INET_ADDRSTRLEN); + + /* if RRH, then I am the server, so bind */ + if (device->func_type == RRH_FUNC ){ + if (bind(eth->sockfd[Mod_id],(struct sockaddr *)ð->dest_addr[Mod_id], dest_addr_len[Mod_id])<0) { + perror("ETHERNET: Cannot bind to socket"); + exit(0); + }else { + printf("[RRH] binding mod_%d to %s:%d\n",Mod_id,str,ntohs(eth->dest_addr[Mod_id].sin_port)); + } + + }else { + printf("[BBU] Connecting to %s:%d\n",str,ntohs(eth->dest_addr[Mod_id].sin_port)); + } + + return 0; +} - inet_ntop(AF_INET, &(dest->sin_addr), str, INET_ADDRSTRLEN); - printf("Connecting to %s:%d\n",str,ntohs(dest->sin_port)); +void ethernet_socket_opt (openair0_device *device){ + + eth_state_t *eth = (eth_state_t*)device->priv; + int Mod_id=device->Mod_id; + + int sndbuf_size=0, rcvbuf_size=0; + socklen_t optlen; + /* chang the MTU of the eth interface */ + struct ifreq ifr; + + optlen = sizeof(int); + + if (getsockopt(eth->sockfd[Mod_id], + SOL_SOCKET, + SO_SNDBUF, + &sndbuf_size,&optlen)) + printf("error:getsockopt()\n"); + + if (getsockopt(eth->sockfd[Mod_id], + SOL_SOCKET, + SO_RCVBUF, + &rcvbuf_size,&optlen)) + printf("error:getsockopt()\n"); + + printf( "sndbuf_size= %d bytes rcvbuf_size= %d bytes\n", sndbuf_size, + rcvbuf_size); + + ifr.ifr_addr.sa_family = AF_INET; + //iap->ifa_name is bond1:xx + strncpy(ifr.ifr_name, DEFAULT_IF, sizeof(ifr.ifr_name)); + ifr.ifr_mtu = device->openair0_cfg.samples_per_packet*5; + if (ioctl(eth->sockfd[Mod_id], SIOCSIFMTU, (caddr_t)&ifr) < 0 ) + perror ("Can't set the MTU"); + else + printf("[ETHERNET] %s MTU size has changed to %d\n",DEFAULT_IF,ifr.ifr_mtu); + +} +int trx_eth_request(openair0_device *device) { + + int Mod_id = device->Mod_id; + eth_state_t *eth = (eth_state_t*)device->priv; + void *msg; + ssize_t msg_len; + + + /* send to RRH (server) required configuration parameters: + -number of downlink RBs (so that frame/packet size can be set accordingly) + -rx_num_0channels + -tx_num_channels + -rx_freq + -tx_freq + -rxg_mode[4] + -rx_gain + -tx_gain + -rx_bw + -tx_bw + -autocal */ + + msg=malloc(sizeof(openair0_config_t)); + msg_len=sizeof(openair0_config_t); + memcpy(msg,(void*)&device->openair0_cfg,msg_len); + + if (sendto(eth->sockfd[Mod_id],msg,msg_len,0,(struct sockaddr *)ð->dest_addr[Mod_id],dest_addr_len[Mod_id])==-1){ + perror("ETHERNET: "); + exit(0); + } + + return 0; } -int ethernet_write_data(int Mod_id, openair0_timestamp timestamp, const void **buff, int antenna_id, int nsamps) -{ +int trx_eth_reply(openair0_device *device) { - void *buff2 = (void*)buff[antenna_id]-sizeof(openair0_timestamp)-(sizeof(int16_t)*2); - int32_t temp0 = *(int32_t *)buff2; - openair0_timestamp temp1 = *(openair0_timestamp *)(buff2+(sizeof(int16_t)*2)); + eth_state_t *eth = (eth_state_t*)device->priv; + int Mod_id = device->Mod_id; + char str[INET_ADDRSTRLEN]; + void *msg; + ssize_t msg_len; + + msg=malloc(sizeof(openair0_config_t)); + msg_len=sizeof(openair0_config_t); - int n_written; + /* receive from client (lte-softmodem) */ + if (recvfrom(eth->sockfd[Mod_id], + msg, + msg_len, + 0, + (struct sockaddr *)ð->dest_addr[Mod_id], + (socklen_t *)&dest_addr_len[Mod_id])==-1){ + perror("ETHERNET: "); + exit(0); + } + + memcpy((void*)&device->openair0_cfg,msg,msg_len); + inet_ntop(AF_INET, &(eth->dest_addr[Mod_id].sin_addr), str, INET_ADDRSTRLEN); + device->openair0_cfg.remote_port =ntohs(eth->dest_addr[Mod_id].sin_port); + device->openair0_cfg.remote_ip=str; + //ethernet_socket_opt (device); + printf("[RRH] write mod_%d %d to %s:%d\n",Mod_id,eth->sockfd[Mod_id],str,ntohs(eth->dest_addr[Mod_id].sin_port)); + + return 1; +} - n_written = 0; - ((int16_t *)buff2)[0] = 1+(antenna_id<<1); - ((int16_t *)buff2)[1] = nsamps; - *((openair0_timestamp *)(buff2+(sizeof(int16_t)*2))) = timestamp; - // printf("Timestamp TX sent : %d\n",timestamp); - // printf("buffer head : %d %d %d %d \n",((int16_t *)buff2)[0],((int16_t *)buff2)[1],((int16_t *)buff2)[2],((int16_t *)buff2)[3]); - while(n_written < nsamps) { +int ethernet_write_data(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps,int antenna_id) { + + int n_written; + uint16_t header_size=sizeof(int32_t) + sizeof(openair0_timestamp); + eth_state_t *eth = (eth_state_t*)device->priv; + int Mod_id = device->Mod_id; + int sendto_flag =0; + sendto_flag|=MSG_DONTWAIT; + + /* buff[antenna_id] points to the position in tx buffer where the payload to be sent is + buff2 points to the position in tx buffer where the packet header will be placed */ + void *buff2 = (void*)(buff[antenna_id]-header_size); // (void*)((unsigned char *)buff[antenna_id]-header_size); + + /* we don't want to ovewrite with the header info the previous tx buffer data so we store it*/ + int32_t temp0 = *(int32_t *)buff2; + openair0_timestamp temp1 = *(openair0_timestamp *)(buff2 + sizeof(int32_t)); + //printf("temp0=%d temp=%d\n",temp0,temp1); + //char str[INET_ADDRSTRLEN]; + + n_written = 0; + + *(int16_t *)(buff2 + sizeof(int16_t))=1+(antenna_id<<1); + *(openair0_timestamp *)(buff2 + sizeof(int32_t)) = timestamp; + + //inet_ntop(AF_INET, &(eth->dest_addr[Mod_id].sin_addr), str, INET_ADDRSTRLEN); + //printf("[RRH]write mod_%d %d to %s:%d, len %d, buff %p antenna %d\n", + //Mod_id,eth->sockfd[Mod_id],str,ntohs(eth->dest_addr[Mod_id].sin_port),(nsamps<<2)+header_size, buff2, antenna_id); + +while(n_written < nsamps) { /* Send packet */ - if ((n_written += sendto(sockfd[Mod_id], - buff2, - (nsamps<<2)+sizeof(openair0_timestamp)+(2*sizeof(int16_t)), - 0, - (struct sockaddr*)&dest_addr[Mod_id], - dest_addr_len[Mod_id])) < 0) { - printf("Send failed for Mod_id %d\n",Mod_id); - perror("ETHERNET:"); - exit(-1); + if ((n_written += sendto(eth->sockfd[Mod_id], + buff2, + (nsamps<<2)+header_size, + 0, + (struct sockaddr*)ð->dest_addr[Mod_id], + dest_addr_len[Mod_id])) < 0) { + perror("ETHERNET WRITE"); + exit(-1); } } +/* printf("Buffer head TX: nu=%d an_id=%d ts%d byte_send=%d \n", *(int16_t *)buff2, + *(int16_t *)(buff2 + sizeof(int16_t)), + *(openair0_timestamp *)(buff2 + sizeof(int32_t)), + n_written>>2);*/ + + /* tx buffer values restored */ *(int32_t *)buff2 = temp0; - *(openair0_timestamp *)(buff2+2*sizeof(int16_t)) = temp1; + *(openair0_timestamp *)(buff2 + sizeof(int32_t)) = temp1; + //printf("-----------------temp0=%d temp=%d\n",*(int32_t *)buff2,*(openair0_timestamp *)(buff2 + sizeof(int32_t))); return n_written; } -int ethernet_read_data(int Mod_id,openair0_timestamp *timestamp,void **buff, int antenna_id, int nsamps) -{ +int ethernet_read_data(openair0_device *device,openair0_timestamp *timestamp,void **buff, int nsamps,int antenna_id) { - void *buff2 = (void*)buff[antenna_id]-sizeof(openair0_timestamp); int bytes_received; int block_cnt; int ret; - openair0_timestamp temp = *(openair0_timestamp*)(buff2); - int16_t mesg[2]; char str[INET_ADDRSTRLEN]; - - //mesg[0] = 0+(antenna_id<<1); - //mesg[1] = nsamps; - - inet_ntop(AF_INET, &(dest_addr[Mod_id].sin_addr), str, INET_ADDRSTRLEN); + uint16_t header_size=sizeof(int32_t) + sizeof(openair0_timestamp); + + + /* buff[antenna_id] points to the position in rx buffer where the payload to be received will be placed + buff2 points to the position in rx buffer where the packet header will be placed */ + void *buff2 = (void*)(buff[antenna_id]-header_size); + + /* we don't want to ovewrite with the header info the previous rx buffer data so we store it*/ + int32_t temp0 = *(int32_t *)buff2; + openair0_timestamp temp1 = *(openair0_timestamp *)(buff2 + sizeof(int32_t)); + + eth_state_t *eth = (eth_state_t*)device->priv; + int Mod_id = device->Mod_id; + + inet_ntop(AF_INET, &(eth->dest_addr[Mod_id].sin_addr), str, INET_ADDRSTRLEN); // send command RX for nsamps samples - // printf("requesting %d samples from (%s:%d)\n",nsamps,str,ntohs(dest_addr[Mod_id].sin_port)); - - //sendto(sockfd[Mod_id],mesg,4,0,(struct sockaddr *)&dest_addr[Mod_id],dest_addr_len[Mod_id]); + // printf("requesting %d samples from (%s:%d)\n",nsamps,str,ntohs(eth->dest_addr[Mod_id].sin_port)); bytes_received=0; block_cnt=0; while(bytes_received < (int)((nsamps<<2))) { //printf("requesting %d bytes\n",(nsamps<<2)); - ret=recvfrom(sockfd[Mod_id], + ret=recvfrom(eth->sockfd[Mod_id], buff2+bytes_received, - (nsamps<<2)+sizeof(openair0_timestamp)-bytes_received, + (nsamps<<2)+header_size-bytes_received, 0,//MSG_DONTWAIT, - (struct sockaddr *)&dest_addr[Mod_id], - &dest_addr_len[Mod_id]); + (struct sockaddr *)ð->dest_addr[Mod_id], + (socklen_t *)&dest_addr_len[Mod_id]); //printf("bytes_received %d (ret %d)\n",bytes_received+ret,ret); if (ret==-1) { if (errno == EAGAIN) { - perror("ETHERNET: "); - return((nsamps<<2)+sizeof(openair0_timestamp)); + perror("ETHERNET READ: "); + return((nsamps<<2) + header_size); } else if (errno == EWOULDBLOCK) { block_cnt++; usleep(10); - + if (block_cnt == 100) return(-1); } } else { bytes_received+=ret; } } - - - //printf("buffer head : %x %x %x %x \n",((int32_t *)buff2)[0],((int32_t *)buff2)[1],((int32_t *)buff2)[2],((int32_t *)buff2)[3]); - *timestamp = *(openair0_timestamp *)(buff2); - // printf("Received %d samples, timestamp = %d\n",bytes_received>>2,*(int32_t*)timestamp); - *(openair0_timestamp *)(buff2) = temp; + /* + printf("Buffer head RX: nu=%d an_id=%d ts%d byte_recv=%d\n", *(int16_t *)buff2, + *(int16_t *)(buff2 + sizeof(int16_t)), + *(openair0_timestamp *)(buff2 + sizeof(int32_t)), + ret>>2);*/ + + /* store the timestamp value from packet's header */ + *timestamp = *(openair0_timestamp *)(buff2 + sizeof(int32_t)); + + /* tx buffer values restored */ + *(int32_t *)buff2 = temp0; + *(openair0_timestamp *)(buff2 + sizeof(int32_t)) = temp1; + // printf("Received %d samples, timestamp = %d\n",bytes_received>>2,*(int32_t*)timestamp); + return nsamps; - + } -int trx_eth_start(openair0_device *openair0) -{ - - int16_t mesg[2]; - int Mod_id; - - Mod_id = openair0->openair0_cfg.Mod_id; - ethernet_socket_init(openair0->openair0_cfg.Mod_id, openair0->openair0_cfg.rrh_ip,openair0->openair0_cfg.rrh_port); - - mesg[0] = 1 + 0; //antenna index is 0, to be replaced by the number of active antennas - mesg[1] = openair0->openair0_cfg.samples_per_packet; - - sendto(sockfd[Mod_id],mesg,4,0,(struct sockaddr *)&dest_addr[Mod_id],dest_addr_len[Mod_id]); - - - return(0); +int trx_eth_write(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int antenna_id, int flags) { + + return ethernet_write_data(device,timestamp,buff,nsamps,antenna_id); } -void trx_eth_write(openair0_device *device, openair0_timestamp timestamp, const void **buff, int nsamps, int cc, int flags) -{ - int i; - - for (i=0; i<cc; i++) - ethernet_write_data(device->Mod_id,timestamp,buff,i,nsamps); +int trx_eth_read(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps, int antenna_id) { + + return(ethernet_read_data(device,ptimestamp,buff,nsamps,antenna_id)); + } -int trx_eth_read(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps,int cc) -{ - int i; +int openair0_stop(int card) { - for (i=0; i<cc; i++) - return(ethernet_read_data(device->Mod_id,ptimestamp,buff,i,nsamps)); + return(0); } -void trx_eth_end(openair0_device *device) -{ +int openair0_print_stats(openair0_device* device) { + return(0); +} +int openair0_reset_stats(openair0_device* device) { + return(0); } +void trx_eth_end(openair0_device *device) { + // close all the sockets -int openair0_stop(int dummy) { - - return 0; + } -int num_devices = 0; -int openair0_device_init(openair0_device *device, openair0_config_t *openair0_cfg) -{ +int openair0_dev_init_eth(openair0_device *device, openair0_config_t *openair0_cfg){ + + eth_state_t *eth = (eth_state_t*)malloc(sizeof(eth_state_t)); + int card = 0; + memset(eth, 0, sizeof(eth_state_t)); + eth->buffer_size = (unsigned int)openair0_cfg[card].samples_per_packet*sizeof(int32_t); // buffer size = 4096 for sample_len of 1024 + eth->sample_rate = (unsigned int)openair0_cfg[card].sample_rate; + device->priv = eth; - printf("ETHERNET: Initializing openair0_device\n"); - device->Mod_id = num_devices++; - device->trx_start_func = trx_eth_start; - device->trx_end_func = trx_eth_end; - device->trx_read_func = trx_eth_read; - device->trx_write_func = trx_eth_write; + printf("ETHERNET: Initializing openair0_device for %s ...\n", ((device->func_type == BBU_FUNC) ? "BBU": "RRH")); + device->Mod_id = num_devices++; + device->trx_request_func = trx_eth_request; + device->trx_reply_func = trx_eth_reply; + device->trx_end_func = trx_eth_end; + device->trx_read_func = trx_eth_read; + device->trx_write_func = trx_eth_write; + memcpy((void*)&device->openair0_cfg,(void*)openair0_cfg,sizeof(openair0_config_t)); + + if (ethernet_socket_init(device)!=0){ + return -1; + } + + return 0; } diff --git a/targets/ARCH/ETHERNET/USERSPACE/LIB/ethernet_lib.h b/targets/ARCH/ETHERNET/USERSPACE/LIB/ethernet_lib.h index d00e6ad94a6eff1e3d296675f8c5624cd2ff7b98..727f06738af6a5c37f3a94d17fb3b735b66f7474 100644 --- a/targets/ARCH/ETHERNET/USERSPACE/LIB/ethernet_lib.h +++ b/targets/ARCH/ETHERNET/USERSPACE/LIB/ethernet_lib.h @@ -1,3 +1,42 @@ +/******************************************************************************* + OpenAirInterface + Copyright(c) 1999 - 2014 Eurecom + + OpenAirInterface is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + + OpenAirInterface is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenAirInterface.The full GNU General Public License is + included in this distribution in the file called "COPYING". If not, + see <http://www.gnu.org/licenses/>. + + Contact Information + OpenAirInterface Admin: openair_admin@eurecom.fr + OpenAirInterface Tech : openair_tech@eurecom.fr + OpenAirInterface Dev : openair4g-devel@eurecom.fr + + Address : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE + + *******************************************************************************/ +/*! \file ethernet_lib.h + * \brief API to stream I/Q samples over standard ethernet + * \author Katerina Trilyraki, Navid Nikaein + * \date 2015 + * \version 0.2 + * \company Eurecom + * \maintainer: navid.nikaein@eurecom.fr + * \note + * \warning + */ + #include <arpa/inet.h> #include <linux/if_packet.h> #include <stdio.h> @@ -8,8 +47,75 @@ #include <net/if.h> #include <netinet/ether.h> -int ethernet_socket_init(int Mod_id, char *dest_ip,int dest_port); -int ethernet_write_data(int Mod_id, const void *buff, int nsamps,int cc); +typedef struct { + + // opaque eth data struct + //struct eth_if *dev; + // An empty ("") or NULL device identifier will result in the first encountered device being opened (using the first discovered backend) + + int sockfd[4]; + struct sockaddr_in dest_addr[4]; + + + unsigned int buffer_size; + unsigned int timeout_ns; + + //struct eth_metadata meta_rx; + //struct eth_metadata meta_tx; + + unsigned int sample_rate; + // time offset between transmiter timestamp and receiver timestamp; + double tdiff; + // use brf_time_offset to get this value + int tx_forward_nsamps; //166 for 20Mhz + + + // -------------------------------- + // Debug and output control + // -------------------------------- + int num_underflows; + int num_overflows; + int num_seq_errors; + int num_rx_errors; + int num_tx_errors; + + uint64_t tx_actual_nsamps; // actual number of samples transmitted + uint64_t rx_actual_nsamps; + uint64_t tx_nsamps; // number of planned samples + uint64_t rx_nsamps; + uint64_t tx_count; // number pf packets + uint64_t rx_count; + //openair0_timestamp rx_timestamp; + +} eth_state_t; + +#define ETH_META_STATUS_OVERRUN (1 << 0) +#define ETH_META_STATUS_UNDERRUN (1 << 1) + +struct eth_meta_data{ + uint64_t timestamp; + uint32_t flags; + uint32_t status; + unsigned int actual_count; +}; + + +typedef struct { + /* packet's timestamp */ + openair0_timestamp timestamp; + /* variable declared for alignment purposes (sample size=32 bit) */ + int16_t not_used; + /* antenna port used to resynchronize*/ + int16_t antenna_id; +} header_t; + + + +int ethernet_socket_init(openair0_device *device); + +int ethernet_write_data(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps,int antenna_id); + +int ethernet_read_data(openair0_device *device,openair0_timestamp *timestamp,void **buff, int nsamps,int antenna_id); -int ethernet_read_data(int Mod_id,void *buff, int nsamps,int cc); +void ethernet_socket_opt (openair0_device *device); diff --git a/targets/ARCH/EXMIMO/USERSPACE/LIB/openair0_lib.c b/targets/ARCH/EXMIMO/USERSPACE/LIB/openair0_lib.c index 5e11a4c2f4eea97a0276d3528fc99eb3b180d320..6c8f4f16f92df91b812cdae3399a2f0b17aa7153 100644 --- a/targets/ARCH/EXMIMO/USERSPACE/LIB/openair0_lib.c +++ b/targets/ARCH/EXMIMO/USERSPACE/LIB/openair0_lib.c @@ -247,6 +247,7 @@ int openair0_stop_without_reset(int card) #define MY_RF_MODE (RXEN + TXEN + TXLPFNORM + TXLPFEN + TXLPF25 + RXLPFNORM + RXLPFEN + RXLPF25 + LNA1ON +LNAMax + RFBBNORM + DMAMODE_RX + DMAMODE_TX) #define RF_MODE_BASE (LNA1ON + RFBBNORM) +//int openair0_dev_init_exmimo(openair0_device *device, openair0_config_t *openair0_cfg) { int openair0_device_init(openair0_device *device, openair0_config_t *openair0_cfg) { // Initialize card diff --git a/targets/ARCH/USRP/USERSPACE/LIB/usrp_lib.cpp b/targets/ARCH/USRP/USERSPACE/LIB/usrp_lib.cpp index 66e2b9ed9ece20af98dbcef4cf0eabc8f0faab61..cf15321c8d94f7399db2adcdea6ecf874c527472 100644 --- a/targets/ARCH/USRP/USERSPACE/LIB/usrp_lib.cpp +++ b/targets/ARCH/USRP/USERSPACE/LIB/usrp_lib.cpp @@ -44,8 +44,10 @@ #include <complex> #include <fstream> #include <cmath> + #include "common_lib.h" + typedef struct { @@ -121,7 +123,7 @@ static void trx_usrp_end(openair0_device *device) s->tx_stream->send("", 0, s->tx_md); s->tx_md.end_of_burst = false; } -static void trx_usrp_write(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int cc, int flags) +static int trx_usrp_write(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int cc, int flags) { usrp_state_t *s = (usrp_state_t*)device->priv; s->tx_md.time_spec = uhd::time_spec_t::from_ticks(timestamp, s->sample_rate); @@ -138,6 +140,8 @@ static void trx_usrp_write(openair0_device *device, openair0_timestamp timestamp else s->tx_stream->send(buff[0], nsamps, s->tx_md); s->tx_md.start_of_burst = false; + + return 0; } static int trx_usrp_read(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps, int cc) @@ -195,7 +199,7 @@ static bool is_equal(double a, double b) return std::fabs(a-b) < std::numeric_limits<double>::epsilon(); } -int openair0_set_frequencies(openair0_device* device, openair0_config_t *openair0_cfg,int dummy) { +int openair0_set_frequencies(openair0_device* device, openair0_config_t *openair0_cfg, int dummy) { usrp_state_t *s = (usrp_state_t*)device->priv; @@ -237,7 +241,17 @@ int openair0_stop(int card) { return(0); } +int openair0_print_stats(openair0_device* device) { + return(0); + +} +int openair0_reset_stats(openair0_device* device) { + + return(0); + +} +//int openair0_dev_init_usrp(openair0_device* device, openair0_config_t *openair0_cfg) int openair0_device_init(openair0_device* device, openair0_config_t *openair0_cfg) { uhd::set_thread_priority_safe(1.0); @@ -376,7 +390,7 @@ int openair0_device_init(openair0_device* device, openair0_config_t *openair0_cf device->trx_end_func = trx_usrp_end; device->trx_read_func = trx_usrp_read; device->trx_write_func = trx_usrp_write; - + s->sample_rate = openair0_cfg[0].sample_rate; // TODO: // init tx_forward_nsamps based usrp_time_offset ex diff --git a/targets/RT/USER/UE_transport_IQ.c b/targets/RT/USER/UE_transport_IQ.c new file mode 100644 index 0000000000000000000000000000000000000000..74623e1ba81b88ed442909264c44477f36f055e4 --- /dev/null +++ b/targets/RT/USER/UE_transport_IQ.c @@ -0,0 +1,562 @@ +/******************************************************************************* + OpenAirInterface + Copyright(c) 1999 - 2014 Eurecom + + OpenAirInterface is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + + OpenAirInterface is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenAirInterface.The full GNU General Public License is + included in this distribution in the file called "COPYING". If not, + see <http://www.gnu.org/licenses/>. + + Contact Information + OpenAirInterface Admin: openair_admin@eurecom.fr + OpenAirInterface Tech : openair_tech@eurecom.fr + OpenAirInterface Dev : openair4g-devel@eurecom.fr + + Address : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE + + *******************************************************************************/ +/*! \file UE_transport_IQ.c + * \brief UE transport IQ sampels + * \author Katerina Trilyraki, Navid Nikaein, Raymond Knopp + * \date 2015 + * \version 0.1 + * \company Eurecom + * \maintainer: navid.nikaein@eurecom.fr + * \note + * \warning very experimental + */ + +#include <unistd.h> +#include <time.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> + +#include "common_lib.h" +#include "PHY/defs.h" +#include "rrh_gw.h" +#include "rrh_gw_externs.h" + +#define START_CMD 1 +#define RRH_UE_PORT 51000 +#define RRH_UE_DEST_IP "127.0.0.1" +#define PRINTF_PERIOD 3750 + +/****************************************************************************** + ** FUNCTION PROTOTYPES ** + ******************************************************************************/ +void *rrh_proc_UE_thread(void *); +void *rrh_UE_thread(void *); +void *rrh_UE_rx_thread(void *); +void *rrh_UE_tx_thread(void *); + + + +openair0_timestamp timestamp_UE_tx[4]= {0,0,0,0},timestamp_UE_rx[4]= {0,0,0,0}; +openair0_timestamp nrt_UE_counter[4]= {0,0,0,0}; + +pthread_t main_rrh_UE_thread,main_rrh_proc_UE_thread; +pthread_attr_t attr, attr_proc; +struct sched_param sched_param_rrh, sched_param_rrh_proc; +pthread_cond_t sync_UE_cond[4]; +pthread_mutex_t sync_UE_mutex[4]; + +int32_t overflow_rx_buffer_UE[4]= {0,0,0,0}; +int32_t nsamps_UE[4]= {0,0,0,0}; +int32_t UE_tx_started=0,UE_rx_started=0; +int32_t RT_flag_UE=0, NRT_flag_UE=1; +int32_t counter_UE_rx[4]= {0,0,0,0}; +int32_t counter_UE_tx[4]= {0,0,0,0}; +int32_t **tx_buffer_UE, **rx_buffer_UE; + +int sync_UE_rx[4]= {-1,-1,-1,-1}; +void *rrh_UE_thread_status; + + +void *rx_ue[2]; // FIXME hard coded array size; indexed by lte_frame_parms.nb_antennas_rx +void *tx_ue[2]; // FIXME hard coded array size; indexed by lte_frame_parms.nb_antennas_tx + +/*! \fn void create_UE_trx_threads( openair0_device *dev_ue, uint8_t RT_flag,uint8_t NRT_flag) +* \brief this function +* \param[in] +* \param[out] +* \return +* \note +* @ingroup _oai +*/ +void create_UE_trx_threads( rrh_module_t *dev_ue, uint8_t RT_flag,uint8_t NRT_flag) { + + int i; + int error_code_UE, error_code_proc_UE; + void *tmp; + + RT_flag_UE=RT_flag; + NRT_flag_UE=NRT_flag; + + pthread_attr_init(&attr); + sched_param_rrh.sched_priority = sched_get_priority_max(SCHED_FIFO); + pthread_attr_init(&attr_proc); + sched_param_rrh_proc.sched_priority = sched_get_priority_max(SCHED_FIFO-1); + + pthread_attr_setschedparam(&attr,&sched_param_rrh); + pthread_attr_setschedpolicy(&attr,SCHED_FIFO); + pthread_attr_setschedparam(&attr_proc,&sched_param_rrh_proc); + pthread_attr_setschedpolicy(&attr_proc,SCHED_FIFO-1); + + + for (i=0; i<4; i++) { + pthread_mutex_init(&sync_UE_mutex[i],NULL); + pthread_cond_init(&sync_UE_cond[i],NULL); + } + + error_code_UE = pthread_create(&main_rrh_UE_thread, &attr, rrh_UE_thread, (void *)dev_ue); + error_code_proc_UE = pthread_create(&main_rrh_proc_UE_thread, &attr_proc, rrh_proc_UE_thread, (void *)dev_ue); + + if (error_code_UE) { + printf("Error while creating UE thread\n"); + exit(-1); + } + + if (error_code_proc_UE) { + printf("Error while creating UE proc thread\n"); + exit(-1); + } + +} + + +/*! \fn void *rrh_proc_UE_thread((void *)dev_ue) +* \brief this function +* \param[in] +* \param[out] +* \return +* \note +* @ingroup _oai +*/ +void *rrh_proc_UE_thread(void * arg) { + + int antenna_index,i; + openair0_timestamp truncated_timestamp, truncated_timestamp_final, last_hw_counter=0; + struct timespec time_req, time_rem; + int16_t *txp,*rxp; + unsigned int samples_per_frame=0; + rrh_module_t *dev=(rrh_module_t *)arg; + + samples_per_frame= dev->eth_dev.openair0_cfg.samples_per_frame; + AssertFatal(samples_per_frame <=0, "invalide samples_per_frame !%u\n",samples_per_frame); + + time_req.tv_sec = 0; + time_req.tv_nsec = 1000; + + while (rrh_exit==0) { + //wait until some data has been copied + for (antenna_index=0; antenna_index<4; antenna_index++) { + if (sync_UE_rx[antenna_index]==0) { + if (!UE_tx_started) { + UE_tx_started=1; //Set this flag to 1 to indicate that a UE started retrieving data + if (RT_flag_UE==1) { + last_hw_counter=hw_counter; + } + } else { + if (RT_flag_UE==1) { + if (hw_counter > last_hw_counter+1) { + printf("L1"); + // goto end_copy_UE; + } else { + while (hw_counter < last_hw_counter+1) + nanosleep(&time_req,&time_rem); + } + } + } + + truncated_timestamp = timestamp_UE_tx[antenna_index]%(samples_per_frame); + truncated_timestamp_final = (timestamp_UE_tx[antenna_index]+nsamps_UE[antenna_index])%samples_per_frame; + + if ((truncated_timestamp + nsamps_UE[antenna_index]) > samples_per_frame) { + if ((timestamp_eNB_rx[antenna_index]%samples_per_frame < nsamps_UE[antenna_index]) && (eNB_rx_started==1)) { + overflow_rx_buffer_eNB[antenna_index]++; + printf("eNB Overflow[%d] : %d, timestamp : %d\n",antenna_index,overflow_rx_buffer_eNB[antenna_index],(int)truncated_timestamp); + + if (NRT_flag_UE==1) { + while ((timestamp_eNB_rx[antenna_index]%samples_per_frame) < nsamps_UE[antenna_index]) + nanosleep(&time_req,&time_rem); + } + } + + rxp = (int16_t*)&rx_buffer_eNB[antenna_index][truncated_timestamp]; + txp = (int16_t*)&tx_buffer_UE[antenna_index][truncated_timestamp]; + + for (i=0; i<(samples_per_frame<<1)-(truncated_timestamp<<1); i++) { + rxp[i] = txp[i]>>6; + } + + rxp = (int16_t*)&rx_buffer_eNB[antenna_index][0]; + txp = (int16_t*)&tx_buffer_UE[antenna_index][0]; + + for (i=0; i<nsamps_eNB[antenna_index]-(samples_per_frame)+(truncated_timestamp); i++) { + rxp[i] = txp[i]>>6; + } + + } else { + if (((truncated_timestamp < (timestamp_eNB_rx[antenna_index]%samples_per_frame)) && + (truncated_timestamp_final > (timestamp_eNB_rx[antenna_index]%samples_per_frame))) && + (eNB_rx_started==1)) { + overflow_rx_buffer_eNB[antenna_index]++; + printf("eNB Overflow[%d] : %d, timestamp : %d\n",antenna_index,overflow_rx_buffer_eNB[antenna_index],(int)truncated_timestamp); + + if (NRT_flag_UE==1) { + while (truncated_timestamp_final > timestamp_eNB_rx[antenna_index]%samples_per_frame) + nanosleep(&time_req,&time_rem); + } + } + + rxp = (int16_t*)&rx_buffer_eNB[antenna_index][truncated_timestamp]; + txp = (int16_t*)&tx_buffer_UE[antenna_index][truncated_timestamp]; + + for (i=0; i<(nsamps_eNB[antenna_index]); i++) { + rxp[i] =txp[i]>>6; + } + + } + //end_copy_UE : + last_hw_counter=hw_counter; + pthread_mutex_lock(&sync_UE_mutex[antenna_index]); + sync_UE_rx[antenna_index]--; + pthread_mutex_unlock(&sync_UE_mutex[antenna_index]); + + } + } + + } + + return(0); +} + +/*! \fn void *rrh_UE_thread(void *arg) +* \brief this function +* \param[in] +* \param[out] +* \return +* \note +* @ingroup _oai +*/ +void *rrh_UE_thread(void *arg) { + + rrh_module_t *dev=(rrh_module_t *)arg; + + struct sched_param sched_param_UE_rx, sched_param_UE_tx; + int16_t i,cmd; //,nsamps,antenna_index; + ssize_t bytes_received; + struct timespec time_req_1us, time_rem_1us; + pthread_t UE_rx_thread, UE_tx_thread; + pthread_attr_t attr_UE_rx, attr_UE_tx; + int error_code_UE_rx, error_code_UE_tx; + void *tmp; + unsigned int samples_per_frame=0; + + samples_per_frame= dev->eth_dev.openair0_cfg.samples_per_frame; + time_req_1us.tv_sec = 0; + time_req_1us.tv_nsec = 1000; + + while (rrh_exit==0) { + + cmd=dev->eth_dev.trx_reply_func(&dev->eth_dev); + + /* allocate memory for TX/RX buffers */ + rx_buffer_UE = (int32_t**)malloc16(dev->eth_dev.openair0_cfg.rx_num_channels*sizeof(int32_t*)); + tx_buffer_UE = (int32_t**)malloc16(dev->eth_dev.openair0_cfg.tx_num_channels*sizeof(int32_t*)); + + for (i=0; i<dev->eth_dev.openair0_cfg.rx_num_channels; i++) { + tmp=(void *)malloc(sizeof(int32_t)*(samples_per_frame+4)); + memset(tmp,0,sizeof(int32_t)*(samples_per_frame+4)); + rx_buffer_UE[i]=(tmp+4*sizeof(int32_t)); + } + + for (i=0; i<dev->eth_dev.openair0_cfg.tx_num_channels; i++) { + tmp=(void *)malloc(sizeof(int32_t)*(samples_per_frame+4)); + memset(tmp,0,sizeof(int32_t)*(samples_per_frame+4)); + tx_buffer_UE[i]=(tmp+4*sizeof(int32_t)); + } + + printf("Client %s:%d is connected (DL_RB=%d) rt=%d|%d. \n" , dev->eth_dev.openair0_cfg.remote_ip, + dev->eth_dev.openair0_cfg.remote_port, + dev->eth_dev.openair0_cfg.num_rb_dl, + dev->eth_dev.openair0_cfg.rx_num_channels, + dev->eth_dev.openair0_cfg.tx_num_channels); + + if (cmd==START_CMD) { + + pthread_attr_init(&attr_UE_rx); + pthread_attr_init(&attr_UE_tx); + sched_param_UE_rx.sched_priority = sched_get_priority_max(SCHED_FIFO); + sched_param_UE_tx.sched_priority = sched_get_priority_max(SCHED_FIFO); + pthread_attr_setschedparam(&attr_UE_rx,&sched_param_UE_rx); + pthread_attr_setschedparam(&attr_UE_tx,&sched_param_UE_tx); + pthread_attr_setschedpolicy(&attr_UE_rx,SCHED_FIFO); + pthread_attr_setschedpolicy(&attr_UE_tx,SCHED_FIFO); + + error_code_UE_rx = pthread_create(&UE_rx_thread, &attr_UE_rx, rrh_UE_rx_thread, (void *)&dev); + error_code_UE_tx = pthread_create(&UE_tx_thread, &attr_UE_tx, rrh_UE_tx_thread, (void *)&dev); + + if (error_code_UE_rx) { + printf("Error while creating UE RX thread\n"); + exit(-1); + } + + if (error_code_UE_tx) { + printf("Error while creating UE TX thread\n"); + exit(-1); + } + + while (rrh_exit==0) + sleep(1); + } + } + + + rrh_UE_thread_status = 0; + pthread_exit(&rrh_UE_thread_status); + + return(0); +} + + +/*! \fn void *rrh_UE_rx_thread(void *arg) +* \brief this function +* \param[in] +* \param[out] +* \return +* \note +* @ingroup _oai +*/ +void *rrh_UE_rx_thread(void *arg) { + + rrh_module_t *dev = (rrh_module_t *)arg; + struct timespec time0,time1,time2; + struct timespec time_req_1us, time_rem_1us; + ssize_t bytes_sent; + int antenna_index, nsamps; + int trace_cnt=0; + unsigned long long max_rx_time=0, min_rx_time=133333, total_rx_time=0, average_rx_time=133333, s_period=0, trial=0; + unsigned int samples_per_frame=0; + openair0_timestamp temp, last_hw_counter=0; + + antenna_index = 0; + nsamps = dev->eth_dev.openair0_cfg.samples_per_packet; + samples_per_frame = dev->eth_dev.openair0_cfg.samples_per_frame; + + while (rrh_exit == 0) { + if (!UE_rx_started) { + UE_rx_started=1; //Set this flag to 1 to indicate that a UE started retrieving data + + if (RT_flag_UE==1) { + last_hw_counter=hw_counter; + } + } else { + if (RT_flag_UE==1) { + if (hw_counter > last_hw_counter+1) { + printf("L1"); + //goto end_copy_UE; + } else { + while (hw_counter < last_hw_counter+1) + nanosleep(&time_req_1us,&time_rem_1us); + } + } + } + + clock_gettime(CLOCK_MONOTONIC,&time1); + + // send return + + if ((timestamp_UE_rx[antenna_index]%(samples_per_frame)+nsamps) > samples_per_frame) { // Wrap around if nsamps exceeds the buffer limit + if (((timestamp_eNB_tx[antenna_index]%(samples_per_frame)) < ((timestamp_UE_rx[antenna_index]+nsamps)%(samples_per_frame))) && (eNB_tx_started==1)) { + printf("UE underflow wraparound timestamp_UE_rx : %d, timestamp_eNB_tx : %d\n", + (int)(timestamp_UE_rx[antenna_index]%(samples_per_frame)), + (int)(timestamp_eNB_tx[antenna_index]%samples_per_frame)); + + if (NRT_flag_UE==1) { + while ((timestamp_eNB_tx[antenna_index]%samples_per_frame) < ((timestamp_UE_rx[antenna_index]+nsamps)%(samples_per_frame))) + nanosleep(&time_req_1us,&time_rem_1us); + } + } + + rx_ue[antenna_index]=(void*)&rx_buffer_UE[antenna_index][timestamp_UE_rx[antenna_index]%(samples_per_frame)]; + if ((bytes_sent =dev->eth_dev.trx_write_func (dev, + timestamp_UE_rx[antenna_index], + rx_ue, + ( samples_per_frame- (timestamp_eNB_rx[antenna_index]%(samples_per_frame)) ), + antenna_index, + 0))<0) + perror("RRH UE : sendto for RX"); + + rx_ue[antenna_index]=(void*)&rx_buffer_UE[antenna_index][3]; + if ((bytes_sent =dev->eth_dev.trx_write_func (dev, + timestamp_UE_rx[antenna_index], + rx_ue, + (nsamps - samples_per_frame + (timestamp_eNB_rx[antenna_index]%(samples_per_frame))), + antenna_index, + 0))<0) + perror("RRH UE : sendto for RX"); + + } else { + if (((timestamp_UE_rx[antenna_index]%samples_per_frame)< timestamp_eNB_tx[antenna_index]%samples_per_frame) && + (((timestamp_UE_rx[antenna_index]+nsamps)%samples_per_frame) > (timestamp_eNB_tx[antenna_index]%samples_per_frame)) && + (eNB_tx_started==1) ) { + printf("UE underflow timestamp_UE_rx : %d, timestamp_eNB_tx : %d\n", + (int)(timestamp_UE_rx[antenna_index]%samples_per_frame), + (int)(timestamp_eNB_tx[antenna_index]%samples_per_frame)); + + if (NRT_flag_UE==1) { + while (((timestamp_UE_rx[antenna_index]+nsamps)%samples_per_frame) > (timestamp_eNB_tx[antenna_index]%samples_per_frame)) { + nanosleep(&time_req_1us,&time_rem_1us); + } + } + } + + rx_ue[antenna_index]=(void*)&rx_buffer_UE[antenna_index][timestamp_UE_rx[antenna_index]%(samples_per_frame)]; + if ((bytes_sent = dev->eth_dev.trx_write_func (dev, + timestamp_UE_rx[antenna_index], + rx_ue, + nsamps, + antenna_index, + 0))<0) + perror("RRH UE thread: sendto for RX"); + } + + timestamp_UE_rx[antenna_index]+=nsamps; + last_hw_counter=hw_counter; + + clock_gettime(CLOCK_MONOTONIC,&time2); + + if (trace_cnt++ > 10) { + total_rx_time = (unsigned int)(time2.tv_nsec - time0.tv_nsec); + + if (total_rx_time < 0) total_rx_time=1000000000-total_rx_time; + + if ((total_rx_time > 0) && (total_rx_time < 1000000000)) { + trial++; + + if (total_rx_time < min_rx_time) + min_rx_time = total_rx_time; + + if (total_rx_time > max_rx_time) + max_rx_time = total_rx_time; + + average_rx_time = (long long unsigned int)((average_rx_time*trial)+total_rx_time)/(trial+1); + } + + if (s_period++ == PRINTF_PERIOD) { + s_period=0; + printf("Average UE RX time : %lu\tMax RX time : %lu\tMin RX time : %lu\n",average_rx_time,max_rx_time,min_rx_time); + + } + + //printf("RX: t1 %llu (time from last send), t2 %llu (sendto of %lu bytes) total time : %llu\n",(long long unsigned int)(time1.tv_nsec - time0.tv_nsec), (long long unsigned int)(time2.tv_nsec - time1.tv_nsec), + // (nsamps<<2)+sizeof(openair0_timestamp),(long long unsigned int)(time2.tv_nsec - time0.tv_nsec)); + + } + + memcpy(&time0,&time2,sizeof(struct timespec)); + + } //while (UE_exit==0) + + return(0); + +} + + +/*! \fn void *rrh_UE_tx_thread(void *arg) +* \brief this function +* \param[in] +* \param[out] +* \return +* \note +* @ingroup _oai +*/ +void *rrh_UE_tx_thread(void *arg) { + + struct timespec time0a,time0,time1,time2; + struct timespec time_req_1us, time_rem_1us; + + + rrh_module_t *dev = (rrh_module_t *)arg; + ssize_t bytes_received; + int antenna_index, nsamps; + int trace_cnt=0; + unsigned int samples_per_frame=0; + + antenna_index = 0; + nsamps = dev->eth_dev.openair0_cfg.samples_per_packet; + samples_per_frame = dev->eth_dev.openair0_cfg.samples_per_frame; + + + while (rrh_exit == 0) { + + clock_gettime(CLOCK_MONOTONIC,&time0a); + + bytes_received = dev->eth_dev.trx_read_func(dev, + ×tamp_UE_tx[antenna_index], + tx_ue, + nsamps, + antenna_index); + + clock_gettime(CLOCK_MONOTONIC,&time1); + + if (NRT_flag_UE==1) { + nrt_UE_counter[antenna_index]++; + } + printf(" first part size: %d second part size: %d idx=%d \n", + (int32_t)(((samples_per_frame)<<2)-((timestamp_UE_tx[antenna_index]%(samples_per_frame))<<2)), + (nsamps<<2)-((samples_per_frame)<<2)+((timestamp_UE_tx[antenna_index]%(samples_per_frame))<<2), + timestamp_UE_tx[antenna_index]%(samples_per_frame)); + if ((timestamp_UE_tx[antenna_index]%(samples_per_frame)+nsamps) > samples_per_frame) { // Wrap around if nsamps exceeds the buffer limit + memcpy(&tx_buffer_UE[antenna_index][timestamp_UE_tx[antenna_index]%(samples_per_frame)],(void*)(tx_ue[antenna_index]), + (samples_per_frame<<2)-((timestamp_UE_tx[antenna_index]%(samples_per_frame))<<2)); + + memcpy(&tx_buffer_UE[antenna_index][0], (void*)(tx_ue[antenna_index]+(samples_per_frame*4)-((timestamp_UE_tx[antenna_index]%(samples_per_frame))<<2)), + (nsamps<<2)-((samples_per_frame-(timestamp_UE_tx[antenna_index]%(samples_per_frame)))<<2)); + //printf("Received UE TX samples for antenna %d, nsamps %d (%d)\n",antenna_index,nsamps,(int)(bytes_received>>2)); + } else { + memcpy(&tx_buffer_UE[antenna_index][timestamp_UE_tx[antenna_index]%(samples_per_frame)], (void*)(tx_ue[antenna_index]),(nsamps<<2)); + } + + while (sync_UE_rx[antenna_index]==0) + nanosleep(&time_req_1us,&time_rem_1us); + + pthread_mutex_lock(&sync_UE_mutex[antenna_index]); + sync_UE_rx[antenna_index]++; + + if (!sync_UE_rx[antenna_index]) { + counter_UE_tx[antenna_index]=(counter_UE_tx[antenna_index]+nsamps)%samples_per_frame; + nsamps_UE[antenna_index]=nsamps; + } else { + printf("rrh_eNB_proc thread is busy, will exit\n"); + exit(-1); + } + + pthread_mutex_unlock(&sync_UE_mutex[antenna_index]); + + clock_gettime(CLOCK_MONOTONIC,&time2); + + memcpy(&time0,&time2,sizeof(struct timespec)); + + } + + return(0); + +} + + diff --git a/targets/RT/USER/eNB_transport_IQ.c b/targets/RT/USER/eNB_transport_IQ.c new file mode 100644 index 0000000000000000000000000000000000000000..19b03f76928aa6b5183a235f1372d53727c6c4d2 --- /dev/null +++ b/targets/RT/USER/eNB_transport_IQ.c @@ -0,0 +1,588 @@ +/******************************************************************************* + OpenAirInterface + Copyright(c) 1999 - 2014 Eurecom + + OpenAirInterface is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + + OpenAirInterface is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenAirInterface.The full GNU General Public License is + included in this distribution in the file called "COPYING". If not, + see <http://www.gnu.org/licenses/>. + + Contact Information + OpenAirInterface Admin: openair_admin@eurecom.fr + OpenAirInterface Tech : openair_tech@eurecom.fr + OpenAirInterface Dev : openair4g-devel@eurecom.fr + + Address : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE + + *******************************************************************************/ + +/*! \file eNB_transport_IQ.c + * \brief eNB transport IQ sampels + * \author Katerina Trilyraki, Navid Nikaein, Raymond Knopp + * \date 2015 + * \version 0.1 + * \company Eurecom + * \maintainer: navid.nikaein@eurecom.fr + * \note + * \warning very experimental + */ +#include <unistd.h> +#include <time.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> + +#include "common_lib.h" +#include "PHY/defs.h" +#include "rrh_gw.h" +#include "rrh_gw_externs.h" +#include "rt_wrapper.h" + +#define RRH_eNB_PORT 50000 +#define RRH_eNB_DEST_IP "192.168.12.31" +#define PRINTF_PERIOD 3750 +#define HEADER_SIZE ((sizeof(int32_t) + sizeof(openair0_timestamp))>>2) +//#undef LOWLATENCY +/****************************************************************************** + ** FUNCTION PROTOTYPES ** + ******************************************************************************/ +void *rrh_eNB_rx_thread(void *); +void *rrh_eNB_tx_thread(void *); +void *rrh_proc_eNB_thread(void *); +void *rrh_eNB_thread(void *); +void set_rt_period( openair0_config_t openair0_cfg); +void print_dev_config(openair0_device device); + +openair0_timestamp timestamp_eNB_tx[4]= {0,0,0,0},timestamp_eNB_rx[4]= {0,0,0,0}; +openair0_timestamp nrt_eNB_counter[4]= {0,0,0,0}; + +pthread_t main_rrh_eNB_thread; +pthread_attr_t attr, attr_proc; +struct sched_param sched_param_rrh; +pthread_cond_t sync_eNB_cond[4]; +pthread_mutex_t sync_eNB_mutex[4]; + +int32_t overflow_rx_buffer_eNB[4]= {0,0,0,0}; +int32_t nsamps_eNB[4]= {0,0,0,0}; +int32_t eNB_tx_started=0,eNB_rx_started=0; +int32_t counter_eNB_rx[4]= {0,0,0,0}; +int32_t counter_eNB_tx[4]= {0,0,0,0}; +uint8_t RT_flag_eNB,NRT_flag_eNB; +int32_t **tx_buffer_eNB, **rx_buffer_eNB; + +void *rrh_eNB_thread_status; +void *rx_eNB[2]; //to be fixed +void *tx_eNB[2]; //to be fixed +int sync_eNB_rx[4]= {-1,-1,-1,-1}; + +unsigned int rx_pos=0, next_rx_pos=0; +unsigned int tx_pos=0, prev_tx_pos=0; + + +/*! \fn void create_eNB_trx_threads( rrh_module_t *dev_enb, uint8_t RT_flag,uint8_t NRT_flag) + * \brief this function + * \param[in] + * \param[out] + * \return + * \note + * @ingroup _oai + */ +void create_eNB_trx_threads( rrh_module_t *dev_enb, uint8_t RT_flag, uint8_t NRT_flag){ + + //int i; + int error_code_eNB;//, error_code_proc_eNB; + + RT_flag_eNB=RT_flag; + NRT_flag_eNB=NRT_flag; + + pthread_attr_init(&attr); + sched_param_rrh.sched_priority = sched_get_priority_max(SCHED_FIFO); + pthread_attr_setschedparam(&attr,&sched_param_rrh); + pthread_attr_setschedpolicy(&attr,SCHED_FIFO); +/* + for (i=0; i<4; i++) { + pthread_mutex_init(&sync_eNB_mutex[i],NULL); + pthread_cond_init(&sync_eNB_cond[i],NULL); + }*/ + + error_code_eNB = pthread_create(&main_rrh_eNB_thread, &attr, rrh_eNB_thread, (void *)dev_enb); + if (error_code_eNB) { + printf("Error while creating eNB thread\n"); + exit(-1); + } + +} + +/*! \fn void *rrh_eNB_thread(void *arg) + * \brief this function + * \param[in] + * \param[out] + * \return + * \note + * @ingroup _oai + */ +void *rrh_eNB_thread(void *arg) +{ + rrh_module_t *dev=(rrh_module_t *)arg; + pthread_t eNB_rx_thread, eNB_tx_thread; + int error_code_eNB_rx, error_code_eNB_tx; + int16_t i,j; + void *tmp; + unsigned int samples_per_frame=0; + + + while (rrh_exit==0) { + + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_TRX, 1 ); + + /* handshake with client to exchange parameters */ + dev->eth_dev.trx_reply_func(&dev->eth_dev);//change port make it plus_id + /* prints received configuration parameters */ + print_dev_config(dev->eth_dev); + set_rt_period(dev->eth_dev.openair0_cfg); + samples_per_frame = dev->eth_dev.openair0_cfg.samples_per_frame; + + /* allocate memory for TX/RX buffers + each antenna port has a TX and a RX buffer + each TX and RX buffer is of (samples_per_frame + HEADER_SIZE) samples(int32_t) */ + rx_buffer_eNB = (int32_t**)malloc16(dev->eth_dev.openair0_cfg.rx_num_channels*sizeof(int32_t*)); + tx_buffer_eNB = (int32_t**)malloc16(dev->eth_dev.openair0_cfg.tx_num_channels*sizeof(int32_t*)); + + LOG_D(RRH,"rx ch %d %p and tx ch %d %p\n", + dev->eth_dev.openair0_cfg.rx_num_channels, + rx_buffer_eNB, + dev->eth_dev.openair0_cfg.tx_num_channels, + tx_buffer_eNB); + /* rx_buffer_eNB points to the beginning of data */ + for (i=0; i<dev->eth_dev.openair0_cfg.rx_num_channels; i++) { + tmp=(void *)malloc(sizeof(int32_t)*(samples_per_frame + HEADER_SIZE)); + memset(tmp,0,sizeof(int32_t)*(samples_per_frame + HEADER_SIZE)); + rx_buffer_eNB[i]=( tmp + (HEADER_SIZE*sizeof(int32_t)) ); + LOG_D(RRH," rx ch %d %p |%p header=%d size=%d sample=%d spf=%d\n", + i, + rx_buffer_eNB[i],tmp,HEADER_SIZE,(sizeof(int32_t)*(samples_per_frame + HEADER_SIZE)),sizeof(int32_t),samples_per_frame); + } + /* tx_buffer_eNB points to the beginning of data */ + for (i=0; i<dev->eth_dev.openair0_cfg.tx_num_channels; i++) { + tmp=(void *)malloc(sizeof(int32_t)*(samples_per_frame + HEADER_SIZE)); + memset(tmp,0,sizeof(int32_t)*(samples_per_frame + HEADER_SIZE)); + tx_buffer_eNB[i]=( tmp + (HEADER_SIZE*sizeof(int32_t)) ); + LOG_D(RRH," tx ch %d %p| %p \n", i,tx_buffer_eNB[i],tmp); + } + /* dummy initialization for TX/RX buffers */ + for (i=0; i<dev->eth_dev.openair0_cfg.tx_num_channels; i++) { + for (j=0; j<samples_per_frame; j++) { + rx_buffer_eNB[i][j]=32+i; + tx_buffer_eNB[i][j]=13+i; + } + } + + +#ifdef LOWLATENCY + error_code_eNB_rx = pthread_create(&eNB_rx_thread, NULL, rrh_eNB_rx_thread, (void *)dev); + error_code_eNB_tx = pthread_create(&eNB_tx_thread, NULL, rrh_eNB_tx_thread, (void *)dev); + LOG_D(RRH,"[eNB][SCHED] deadline scheduling applied to TX/RX threads\n"); +#else + pthread_attr_t attr_eNB_rx, attr_eNB_tx; + struct sched_param sched_param_eNB_rx, sched_param_eNB_tx; + pthread_attr_init(&attr_eNB_rx); + pthread_attr_init(&attr_eNB_tx); + sched_param_eNB_rx.sched_priority = sched_get_priority_max(SCHED_FIFO); + sched_param_eNB_tx.sched_priority = sched_get_priority_max(SCHED_FIFO); + pthread_attr_setschedparam(&attr_eNB_rx,&sched_param_eNB_rx); + pthread_attr_setschedparam(&attr_eNB_tx,&sched_param_eNB_tx); + pthread_attr_setschedpolicy(&attr_eNB_rx,SCHED_FIFO); + pthread_attr_setschedpolicy(&attr_eNB_tx,SCHED_FIFO); + + error_code_eNB_rx = pthread_create(&eNB_rx_thread, &attr_eNB_rx, rrh_eNB_rx_thread, (void *)dev); + error_code_eNB_tx = pthread_create(&eNB_tx_thread, &attr_eNB_tx, rrh_eNB_tx_thread, (void *)dev); + LOG_D(RRH,"[eNB][SCHED] FIFO scheduling applied to TX/RX threads\n"); +#endif + + if (error_code_eNB_rx) { + LOG_E(RRH,"[eNB] Error while creating eNB RX thread\n"); + exit(-1); + } + if (error_code_eNB_tx) { + LOG_E(RRH,"[eNB] Error while creating eNB TX thread\n"); + exit(-1); + } + + while (rrh_exit==0) + sleep(1); + + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_TRX,0 ); + } //while (eNB_exit==0) + + rrh_eNB_thread_status = 0; + pthread_exit(&rrh_eNB_thread_status); + return(0); +} + + + +/*! \fn void *rrh_eNB_rx_thread(void *arg) + * \brief this function + * \param[in] + * \param[out] + * \return + * \note + * @ingroup _oai + */ +void *rrh_eNB_rx_thread(void *arg){ + + /* measuremnt related vars */ + struct timespec time0,time1,time2; + unsigned long long max_rx_time=0, min_rx_time=rt_period, total_rx_time=0, average_rx_time=rt_period, s_period=0, trial=0; + int trace_cnt=0; + + struct timespec time_req_1us, time_rem_1us; + rrh_module_t *dev = (rrh_module_t *)arg; + ssize_t bytes_sent; + int i, spp ,pck_rx=0; + openair0_vtimestamp last_hw_counter=0; //volatile int64_t last_hw_counter=0;---> read + unsigned int samples_per_frame=0; + uint8_t loopback=0,measurements=0; + + //RTIME sleep_ns=1000; + time_req_1us.tv_sec = 0; + time_req_1us.tv_nsec =1000; //time_req_1us.tv_nsec = (int)rt_period/2;--->granularity issue + spp = dev->eth_dev.openair0_cfg.samples_per_packet; + samples_per_frame = dev->eth_dev.openair0_cfg.samples_per_frame; + loopback = dev->loopback; + measurements = dev->measurements; + next_rx_pos = spp; + + LOG_I(RRH,"rt period is set to %d\n",rt_period); +#ifdef LOWLATENCY + struct sched_attr attr; + unsigned int flags = 0; + + attr.size = sizeof(attr); + attr.sched_flags = 0; + attr.sched_nice = 0; + attr.sched_priority = 0; + + attr.sched_policy = SCHED_DEADLINE; + attr.sched_runtime = (0.1 * 100) * 10000; // + attr.sched_deadline = rt_period;// 0.1 * 1000000; // + attr.sched_period = rt_period; //0.1 * 1000000; // each TX/RX thread has + + if (sched_setattr(0, &attr, flags) < 0 ) { + perror("[SCHED] eNB RX thread: sched_setattr failed (run with sudo)\n"); + exit(-1); + } +#endif + + while (rrh_exit == 0) { + + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_RX, 1 ); + VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_RX_HWCNT, hw_counter ); + VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_RX_LHWCNT, last_hw_counter ); + VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_RX_PCK, pck_rx ); + + for (i=0; i<dev->eth_dev.openair0_cfg.rx_num_channels; i++){ + if (!eNB_rx_started) { + eNB_rx_started=1; // set this flag to 1 to indicate that eNB started + if (RT_flag_eNB==1) { + last_hw_counter=hw_counter; + } + } else { + if (RT_flag_eNB==1) { + if (hw_counter > last_hw_counter+1) { + printf("LR"); + } else { + while (hw_counter < last_hw_counter+1){ + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_RX_SLEEP, 1 ); + nanosleep(&time_req_1us,&time_rem_1us); + //rt_sleep_ns(sleep_ns); + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_RX_SLEEP, 0 ); + } + } + } + } + + if (measurements == 1 ) + clock_gettime(CLOCK_MONOTONIC,&time1); + + /* LOG_I(RRH,"send for%d at %d with %d |%d|%d| \n",i,rx_pos,timestamp_eNB_rx[i],((timestamp_eNB_rx[i]+spp)%samples_per_frame),next_rx_pos ); + + if ((timestamp_UE_tx[i]%samples_per_frame < next_rx_pos) && (UE_tx_started==1)) { + printf("eNB underflow\n"); + if (NRT_flag_eNB==1) { + while ((timestamp_UE_tx[i]%samples_per_frame) < spp) + nanosleep(&time_req_1us,&time_rem_1us); + } + } + if (((rx_pos)< timestamp_UE_tx[i]%samples_per_frame) && (next_rx_pos > (timestamp_UE_tx[i]%samples_per_frame)) && (UE_tx_started==1)) { + printf("eNB underflow\n"); + if (NRT_flag_eNB==1) { + while (next_rx_pos > (timestamp_UE_tx[i]%samples_per_frame)) + nanosleep(&time_req_1us,&time_rem_1us); + } + }*/ + + if (loopback == 1 ) { + if (sync_eNB_rx[i]==0) { + rx_eNB[i] = (void*)&tx_buffer_eNB[i][tx_pos]; + LOG_I(RRH,"tx_buffer_eNB[i][tx_pos]=%d ,tx_pos=%d\n",tx_buffer_eNB[i][tx_pos],tx_pos); + } + else{ + rx_eNB[i] = (void*)&rx_buffer_eNB[i][rx_pos]; + LOG_I(RRH,"rx_buffer_eNB[i][rx_pos]=%d ,rx_pos=%d\n",rx_buffer_eNB[i][rx_pos],rx_pos); + } + } + + rx_eNB[i] = (void*)&rx_buffer_eNB[i][rx_pos]; + + VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_RXCNT, rx_pos ); + VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_RX_TS, timestamp_eNB_rx[i]&0xffffffff ); + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_WRITE, 1 ); + + //LOG_D(RRH,"rx_buffer_eNB[i][rx_pos]=%p ,rx_pos=%d, i=%d ts=%d\n",&rx_buffer_eNB[i][rx_pos],rx_pos,i,timestamp_eNB_rx[i]); + if ((bytes_sent = dev->eth_dev.trx_write_func (&dev->eth_dev, + timestamp_eNB_rx[i], + rx_eNB, + spp, + i, + 0))<0){ + perror("RRH eNB : sendto for RX"); + } + + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_WRITE, 0 ); + + timestamp_eNB_rx[i]+=spp; + last_hw_counter=hw_counter; + + if (measurements == 1 ) { + + clock_gettime(CLOCK_MONOTONIC,&time2); + + if (trace_cnt++ > 10) { + total_rx_time = (unsigned int)(time2.tv_nsec - time0.tv_nsec); + if (total_rx_time < 0) + total_rx_time=1000000000-total_rx_time; + + if ((total_rx_time > 0) && (total_rx_time < 1000000000)) { + trial++; + if (total_rx_time < min_rx_time) + min_rx_time = total_rx_time; + if (total_rx_time > max_rx_time){ + max_rx_time = total_rx_time; + LOG_D(RRH,"Max value %d update at rx_position %d \n",timestamp_eNB_rx[i], rx_pos); + } + average_rx_time = (long long unsigned int)((average_rx_time*trial)+total_rx_time)/(trial+1); + } + if (s_period++ == PRINTF_PERIOD) { + s_period=0; + LOG_I(RRH,"Average eNB RX time : %lu\tMax RX time : %lu\tMin RX time : %lu\n",average_rx_time,max_rx_time,min_rx_time); + } + } + + memcpy(&time0,&time2,sizeof(struct timespec)); + } + + if (loopback == 1 ){ + pthread_mutex_lock(&sync_eNB_mutex[i]); + sync_eNB_rx[i]--; + pthread_mutex_unlock(&sync_eNB_mutex[i]); + } + + }//for each antenna + + rx_pos += spp; + pck_rx++; + next_rx_pos=(rx_pos+spp); + + if (next_rx_pos >= samples_per_frame) + next_rx_pos -= samples_per_frame; + if (rx_pos >= samples_per_frame) + rx_pos -= samples_per_frame; + + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_RX, 0 ); + } //while (eNB_exit==0) + return(0); +} + + +/*! \fn void *rrh_eNB_tx_thread(void *arg) + * \brief this function + * \param[in] + * \param[out] + * \return + * \note + * @ingroup _oai + */ +void *rrh_eNB_tx_thread(void *arg){ + + struct timespec time0a,time0,time1,time2; + + rrh_module_t *dev = (rrh_module_t *)arg; + struct timespec time_req_1us, time_rem_1us; + ssize_t bytes_received; + int spp,i; + openair0_timestamp last_hw_counter=0; + unsigned int samples_per_frame=0; + uint8_t loopback=0,measurements=0; + +#ifdef LOWLATENCY + struct sched_attr attr; + unsigned int flags = 0; + + attr.size = sizeof(attr); + attr.sched_flags = 0; + attr.sched_nice = 0; + attr.sched_priority = 0; + + attr.sched_policy = SCHED_DEADLINE; + attr.sched_runtime = (0.1 * 100) * 10000; // + attr.sched_deadline = rt_period;//0.1 * 1000000; // + attr.sched_period = rt_period;//0.1 * 1000000; // each TX/RX thread has + + if (sched_setattr(0, &attr, flags) < 0 ) { + perror("[SCHED] eNB TX thread: sched_setattr failed\n"); + exit(-1); + } +#endif + + tx_pos=0; + time_req_1us.tv_sec = 0; + time_req_1us.tv_nsec = 1000; + spp = dev->eth_dev.openair0_cfg.samples_per_packet; + samples_per_frame = dev->eth_dev.openair0_cfg.samples_per_frame; + loopback = dev->loopback; + measurements = dev->measurements; + + while (rrh_exit == 0) { + + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_TX, 1 ); + VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TX_HWCNT, hw_counter ); + VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TX_LHWCNT, last_hw_counter ); + + if (measurements == 1 ) + clock_gettime(CLOCK_MONOTONIC,&time0a); + + for (i=0; i<dev->eth_dev.openair0_cfg.tx_num_channels; i++){ + if (!eNB_tx_started) { + eNB_tx_started=1; // set this flag to 1 to indicate that eNB started + if (RT_flag_eNB==1) { + last_hw_counter=hw_counter; + } + } else { + if (RT_flag_eNB==1) { + if (hw_counter > last_hw_counter+1) { + printf("LT"); + } else { + while (hw_counter < last_hw_counter+1){ + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_TX_SLEEP, 1 ); + nanosleep(&time_req_1us,&time_rem_1us); + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_TX_SLEEP, 0 ); + } + } + } + } + + if (measurements == 1 ) + clock_gettime(CLOCK_MONOTONIC,&time1); + + tx_eNB[i] = (void*)&tx_buffer_eNB[i][tx_pos]; + VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TXCNT, tx_pos ); + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_READ, 1 ); + bytes_received = dev->eth_dev.trx_read_func(&dev->eth_dev, + ×tamp_eNB_tx[i], + tx_eNB, + spp, + i); + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_READ, 0 ); + VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TX_TS, timestamp_eNB_tx[i]&0xffffffff ); + if (NRT_flag_eNB==1) { + nrt_eNB_counter[i]++; + } + prev_tx_pos=tx_pos; + tx_pos += spp; + + if (tx_pos >= samples_per_frame) + tx_pos -= samples_per_frame; + + last_hw_counter=hw_counter; + + if (loopback ==1 ) { + while (sync_eNB_rx[i]==0) + nanosleep(&time_req_1us,&time_rem_1us); + + pthread_mutex_lock(&sync_eNB_mutex[i]); + sync_eNB_rx[i]++; + pthread_mutex_unlock(&sync_eNB_mutex[i]); + } + + } + if (measurements == 1 ) { + clock_gettime(CLOCK_MONOTONIC,&time2); + memcpy(&time0,&time2,sizeof(struct timespec)); + } + + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_TX, 0 ); + } + return(0); +} + +//needs to be fixed +void set_rt_period( openair0_config_t openair0_cfg){ + + AssertFatal(openair0_cfg.samples_per_frame > 0, "invalide samples per frame !%u\n",openair0_cfg.samples_per_frame); + AssertFatal(openair0_cfg.samples_per_packet > 0, "invalide samples per packets !%u\n",openair0_cfg.samples_per_packet); + rt_period= (double)(openair0_cfg.samples_per_packet/(openair0_cfg.samples_per_frame/10.0)*1000000); + AssertFatal(rt_period > 0, "invalide rt period !%u\n", rt_period); + LOG_I(RRH,"Real time period is set to %d ns.\n",rt_period); + // set the calibration parameters for ETH later: check with RK + openair0_cfg.tx_delay=0; + openair0_cfg.tx_forward_nsamps=0; + + //only in case of NRT with emulated UE + create_timer_thread(); + // usleep(rt_period*1000); + +} + + +void print_dev_config(openair0_device device){ + + printf("\tMod_id=%d\n \tlog level=%d\n \tDL_RB=%d\n \tspf=%d\n \tsr=%f\n \tspp=%d\n \ttx_delay=%d\n \ttx_forwd_samps=%d\n \trx_ch=%d\n \ttx_ch=%d\n \trx_freq=%f\n \ttx_freq=%f\n \trxg_mode=%d\n \trx_gain=%f\n \ttx_gain=%f\n \trx_bw=%f\n \ttx_bw=%f\n \tautocal=%d\n \trem_addr %s:%d\n \tmy_addr %s:%d\n", + device.openair0_cfg.Mod_id, + device.openair0_cfg.log_level, + device.openair0_cfg.num_rb_dl, + device.openair0_cfg.samples_per_frame, + device.openair0_cfg.sample_rate, + device.openair0_cfg.samples_per_packet, + device.openair0_cfg.tx_delay, + device.openair0_cfg.tx_forward_nsamps, + device.openair0_cfg.rx_num_channels, + device.openair0_cfg.tx_num_channels, + device.openair0_cfg.rx_freq[0], + device.openair0_cfg.tx_freq[0], + device.openair0_cfg.rxg_mode[0], + device.openair0_cfg.rx_gain[0], + device.openair0_cfg.tx_gain[0], + device.openair0_cfg.rx_bw, + device.openair0_cfg.tx_bw, + device.openair0_cfg.autocal[0], + device.openair0_cfg.remote_ip, + device.openair0_cfg.remote_port, + device.openair0_cfg.my_ip, + device.openair0_cfg.my_port + ); + +} diff --git a/targets/RT/USER/lte-softmodem.c b/targets/RT/USER/lte-softmodem.c index 84ff023e69ec5618b11193068247d61c08e431ea..44e806c82d1f9efedcd5be8008d7beb486b633c7 100644 --- a/targets/RT/USER/lte-softmodem.c +++ b/targets/RT/USER/lte-softmodem.c @@ -45,15 +45,11 @@ #include <sys/ioctl.h> #include <sys/types.h> #include <sys/mman.h> -#include <sys/stat.h> -#include <fcntl.h> #include <sched.h> #include <linux/sched.h> #include <signal.h> #include <execinfo.h> #include <getopt.h> -#include <syscall.h> -#include <pthread.h> // for gettid #include "rt_wrapper.h" #undef MALLOC //there are two conflicting definitions, so we better make sure we don't use it at all @@ -156,6 +152,10 @@ int32_t **txdata; int setup_ue_buffers(PHY_VARS_UE **phy_vars_ue, openair0_config_t *openair0_cfg, openair0_rf_map rf_map[MAX_NUM_CCs]); int setup_eNB_buffers(PHY_VARS_eNB **phy_vars_eNB, openair0_config_t *openair0_cfg, openair0_rf_map rf_map[MAX_NUM_CCs]); +uint16_t runtime_phy_rx[29][6]; // SISO [MCS 0-28][RBs 0-5 : 6, 15, 25, 50, 75, 100] +uint16_t runtime_phy_tx[29][6]; // SISO [MCS 0-28][RBs 0-5 : 6, 15, 25, 50, 75, 100] +double cpuf; + void fill_ue_band_info(void); #ifdef XFORMS // current status is that every UE has a DL scope for a SINGLE eNB (eNB_id=0) @@ -293,9 +293,9 @@ static int tx_max_power[MAX_NUM_CCs]; /* = {0,0}*/; char ref[128] = "internal"; char channels[128] = "0"; -unsigned int samples_per_frame = 307200; -unsigned int tx_forward_nsamps=0; -int tx_delay; +//unsigned int samples_per_frame = 307200; +//unsigned int tx_forward_nsamps=0; +//int tx_delay; #endif @@ -380,20 +380,6 @@ unsigned int build_rfdc(int dcoff_i_rxfe, int dcoff_q_rxfe) return (dcoff_i_rxfe + (dcoff_q_rxfe<<8)); } -#ifdef LOWLATENCY -int sched_setattr(pid_t pid, const struct sched_attr *attr, unsigned int flags) -{ - - return syscall(__NR_sched_setattr, pid, attr, flags); -} - - -int sched_getattr(pid_t pid,struct sched_attr *attr,unsigned int size, unsigned int flags) -{ - - return syscall(__NR_sched_getattr, pid, attr, size, flags); -} -#endif #if !defined(ENABLE_ITTI) void signal_handler(int sig) { @@ -474,40 +460,6 @@ void exit_fun(const char* s) //exit (-1); } -static int latency_target_fd = -1; -static int32_t latency_target_value = 0; -/* Latency trick - taken from cyclictest.c - * if the file /dev/cpu_dma_latency exists, - * open it and write a zero into it. This will tell - * the power management system not to transition to - * a high cstate (in fact, the system acts like idle=poll) - * When the fd to /dev/cpu_dma_latency is closed, the behavior - * goes back to the system default. - * - * Documentation/power/pm_qos_interface.txt - */ -static void set_latency_target(void) -{ - struct stat s; - int ret; - - if (stat("/dev/cpu_dma_latency", &s) == 0) { - latency_target_fd = open("/dev/cpu_dma_latency", O_RDWR); - - if (latency_target_fd == -1) - return; - - ret = write(latency_target_fd, &latency_target_value, 4); - - if (ret == 0) { - printf("# error setting cpu_dma_latency to %d!: %s\n", latency_target_value, strerror(errno)); - close(latency_target_fd); - return; - } - - printf("# /dev/cpu_dma_latency set to %dus\n", latency_target_value); - } -} #ifdef XFORMS @@ -1082,7 +1034,8 @@ static void* eNB_thread_tx( void* param ) // This creates a 1ms reservation every 10ms period attr.sched_policy = SCHED_DEADLINE; - attr.sched_runtime = 0.9 * 1000000; // each tx thread requires 1ms to finish its job + //attr.sched_runtime = 0.9 * 1000000; // each tx thread requires 1ms to finish its job + attr.sched_runtime = (uint64_t) (get_runtime_tx(proc->subframe, runtime_phy_tx, target_dl_mcs,frame_parms[0]->N_RB_DL,cpuf,PHY_vars_eNB_g[0][0]->lte_frame_parms.nb_antennas_tx) * 1000000); // each tx thread requires 1ms to finish its job attr.sched_deadline = 1 * 1000000; // each tx thread will finish within 1ms attr.sched_period = 1 * 10000000; // each tx thread has a period of 10ms from the starting point @@ -1196,13 +1149,20 @@ static void* eNB_thread_tx( void* param ) if (proc->frame_tx==1024) proc->frame_tx=0; - stop_meas( &softmodem_stats_tx_sf[proc->subframe] ); +#ifdef LOWLATENCY + if (opp_enabled){ + if(softmodem_stats_tx_sf[proc->subframe].diff_now/(cpuf) > attr.sched_runtime){ + VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_RUNTIME_TX_ENB, (softmodem_stats_tx_sf[proc->subframe].diff_now/cpuf - attr.sched_runtime)/1000000.0); + } + } +#endif print_meas_now(&softmodem_stats_tx_sf[proc->subframe],"eNB_TX_SF",proc->subframe, tx_time_file); } - stop_meas( &softmodem_stats_tx_sf[proc->subframe] ); + + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_PROC_TX0+(2*proc->subframe), 0 ); #ifdef HARD_RT @@ -1276,7 +1236,7 @@ static void* eNB_thread_rx( void* param ) /* This creates a 2ms reservation every 10ms period*/ attr.sched_policy = SCHED_DEADLINE; - attr.sched_runtime = 1 * 1000000; // each rx thread must finish its job in the worst case in 2ms + attr.sched_runtime = (uint64_t)(get_runtime_rx(proc->subframe, runtime_phy_rx, target_ul_mcs,frame_parms[0]->N_RB_DL,cpuf,PHY_vars_eNB_g[0][0]->lte_frame_parms.nb_antennas_rx) * 1000000); // each tx thread requires 1ms to finish its job attr.sched_deadline = 1 * 1000000; // each rx thread will finish within 2ms attr.sched_period = 1 * 10000000; // each rx thread has a period of 10ms from the starting point @@ -1355,11 +1315,17 @@ static void* eNB_thread_rx( void* param ) proc->frame_rx=0; stop_meas( &softmodem_stats_rx_sf[proc->subframe] ); +#ifdef LOWLATENCY + if (opp_enabled){ + if(softmodem_stats_rx_sf[proc->subframe].diff_now/(cpuf) > attr.sched_runtime){ + VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_RUNTIME_RX_ENB, (softmodem_stats_rx_sf[proc->subframe].diff_now/cpuf - attr.sched_runtime)/1000000.0); + } + } +#endif print_meas_now(&softmodem_stats_rx_sf[proc->subframe],"eNB_RX_SF",proc->subframe, rx_time_file); - } - stop_meas( &softmodem_stats_rx_sf[proc->subframe] ); + //stop_meas( &softmodem_stats_rx_sf[proc->subframe] ); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_PROC_RX0+(2*proc->subframe), 0 ); #ifdef HARD_RT @@ -1409,7 +1375,6 @@ void init_eNB_proc(void) pthread_attr_setschedpolicy (&attr_eNB_proc_rx[CC_id][i], SCHED_FIFO); printf("Setting OS scheduler to SCHED_FIFO for eNB [cc%d][thread%d] \n",CC_id, i); #endif - PHY_vars_eNB_g[0][CC_id]->proc[i].instance_cnt_tx = -1; PHY_vars_eNB_g[0][CC_id]->proc[i].instance_cnt_rx = -1; PHY_vars_eNB_g[0][CC_id]->proc[i].subframe = i; @@ -1418,8 +1383,13 @@ void init_eNB_proc(void) pthread_mutex_init( &PHY_vars_eNB_g[0][CC_id]->proc[i].mutex_rx, NULL); pthread_cond_init( &PHY_vars_eNB_g[0][CC_id]->proc[i].cond_tx, NULL); pthread_cond_init( &PHY_vars_eNB_g[0][CC_id]->proc[i].cond_rx, NULL); +#ifndef LOWLATENCY + pthread_create( &PHY_vars_eNB_g[0][CC_id]->proc[i].pthread_tx, &attr_eNB_proc_tx[CC_id][i], eNB_thread_tx, &PHY_vars_eNB_g[0][CC_id]->proc[i] ); + pthread_create( &PHY_vars_eNB_g[0][CC_id]->proc[i].pthread_rx, &attr_eNB_proc_rx[CC_id][i], eNB_thread_rx, &PHY_vars_eNB_g[0][CC_id]->proc[i] ); +#else pthread_create( &PHY_vars_eNB_g[0][CC_id]->proc[i].pthread_tx, NULL, eNB_thread_tx, &PHY_vars_eNB_g[0][CC_id]->proc[i] ); pthread_create( &PHY_vars_eNB_g[0][CC_id]->proc[i].pthread_rx, NULL, eNB_thread_rx, &PHY_vars_eNB_g[0][CC_id]->proc[i] ); +#endif char name[16]; snprintf( name, sizeof(name), "TX %d", i ); pthread_setname_np( PHY_vars_eNB_g[0][CC_id]->proc[i].pthread_tx, name ); @@ -1574,7 +1544,7 @@ static void* eNB_thread( void* arg ) int hw_subframe = 0; // 0..NUM_ENB_THREADS-1 => 0..9 spp = openair0_cfg[0].samples_per_packet; unsigned int rx_pos = 0; - unsigned int tx_pos = spp*tx_delay; + unsigned int tx_pos = 0; //spp*tx_delay; #endif int CC_id=0; struct timespec trx_time0, trx_time1, trx_time2; @@ -1591,11 +1561,11 @@ static void* eNB_thread( void* arg ) attr.sched_nice = 0; attr.sched_priority = 0; - /* This creates a .5 ms reservation */ + /* This creates a .2 ms reservation */ attr.sched_policy = SCHED_DEADLINE; - attr.sched_runtime = 0.2 * 1000000; - attr.sched_deadline = 0.9 * 1000000; - attr.sched_period = 1.0 * 1000000; + attr.sched_runtime = (0.2 * 100) * 10000; + attr.sched_deadline = (0.9 * 100) * 10000; + attr.sched_period = 1 * 1000000; /* pin the eNB main thread to CPU0*/ @@ -1674,7 +1644,7 @@ static void* eNB_thread( void* arg ) first_run=0; if (diff<0) - diff = diff +150; + diff = diff + 150; LOG_I(HW,"eNB Frame %d, time %llu: slot %d, hw_slot %d, diff %d\n",frame, rt_get_time_ns(), slot, hw_slot, diff); } @@ -1774,18 +1744,21 @@ static void* eNB_thread( void* arg ) clock_gettime( CLOCK_MONOTONIC, &trx_time0 ); - // prepare rx buffer pointers - for (int i=0; i<PHY_vars_eNB_g[0][0]->lte_frame_parms.nb_antennas_rx; i++) - rxp[i] = (void*)&rxdata[i][rx_pos]; - - start_meas( &softmodem_stats_hw ); + start_meas( &softmodem_stats_hw ); openair0_timestamp timestamp; - rxs = openair0.trx_read_func(&openair0, - ×tamp, - rxp, - spp, - PHY_vars_eNB_g[0][0]->lte_frame_parms.nb_antennas_rx); + int i=0; + // prepare rx buffer pointers + for (i=0; i<PHY_vars_eNB_g[0][0]->lte_frame_parms.nb_antennas_rx; i++){ + rxp[i] = (void*)&rxdata[i][rx_pos]; + // check if nsymb_read == spp + // map antenna port i to the cc_id. Now use the 1:1 mapping + rxs = openair0.trx_read_func(&openair0, + ×tamp, + rxp, + spp, + i); + } stop_meas( &softmodem_stats_hw ); clock_gettime( CLOCK_MONOTONIC, &trx_time1 ); @@ -1796,23 +1769,26 @@ static void* eNB_thread( void* arg ) VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_READ, 0 ); // Transmit TX buffer based on timestamp from RX - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_WRITE, 1 ); - // prepare tx buffer pointers - for (int i=0; i<PHY_vars_eNB_g[0][0]->lte_frame_parms.nb_antennas_tx; i++) - txp[i] = (void*)&txdata[i][tx_pos]; - if (frame > 50) { - openair0.trx_write_func(&openair0, - (timestamp+(tx_delay*spp)-tx_forward_nsamps), +if (frame > 50) { + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_WRITE, 1 ); + // prepare tx buffer pointers + for (i=0; i<PHY_vars_eNB_g[0][0]->lte_frame_parms.nb_antennas_tx; i++){ + txp[i] = (void*)&rxdata[i][tx_pos]; + //printf("tx_pos %d ts %d, ts_offset %d txp[i] %p, ap %d\n", tx_pos, timestamp, (timestamp+(tx_delay*spp)-tx_forward_nsamps),txp[i], i); + // if symb_written < spp ==> error + openair0.trx_write_func(&openair0, + (timestamp+(openair0_cfg[card].tx_delay*spp)-openair0_cfg[card].tx_forward_nsamps), txp, spp, - PHY_vars_eNB_g[0][0]->lte_frame_parms.nb_antennas_tx, + i, 1); - } + } + } VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TS, timestamp&0xffffffff ); - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST, (timestamp+(tx_delay*spp)-tx_forward_nsamps)&0xffffffff ); + VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST, (timestamp+(openair0_cfg[card].tx_delay*spp)-openair0_cfg[card].tx_forward_nsamps)&0xffffffff ); stop_meas( &softmodem_stats_mt ); clock_gettime( CLOCK_MONOTONIC, &trx_time2 ); @@ -1856,12 +1832,12 @@ static void* eNB_thread( void* arg ) rx_pos += spp; tx_pos += spp; - if (tx_pos >= samples_per_frame) - tx_pos -= samples_per_frame; + if (tx_pos >= openair0_cfg[card].samples_per_frame) + tx_pos -= openair0_cfg[card].samples_per_frame; } - if (rx_pos >= samples_per_frame) - rx_pos -= samples_per_frame; + if (rx_pos >= openair0_cfg[card].samples_per_frame) + rx_pos -= openair0_cfg[card].samples_per_frame; #endif // USRP @@ -2573,8 +2549,10 @@ int main( int argc, char **argv ) VCD_SIGNAL_DUMPER_INIT("/tmp/openair_dump_eNB.vcd"); } - if (opp_enabled ==1) + if (opp_enabled ==1){ reset_opp_meas(); + } + cpuf=get_cpu_freq_GHz(); #if defined(ENABLE_ITTI) @@ -2585,10 +2563,11 @@ int main( int argc, char **argv ) } itti_init(TASK_MAX, THREAD_MAX, MESSAGES_ID_MAX, tasks_info, messages_info, messages_definition_xml, itti_dump_file); -#endif + // initialize mscgen log after ITTI MSC_INIT(MSC_E_UTRAN, THREAD_MAX+TASK_MAX); - +#endif + #ifdef OPENAIR2 if (opt_type != OPT_NONE) { @@ -2834,7 +2813,10 @@ int main( int argc, char **argv ) openair_daq_vars.target_ue_ul_mcs=target_ul_mcs; } - +#ifndef RTAI + fill_modeled_runtime_table(runtime_phy_rx,runtime_phy_tx); + cpuf=get_cpu_freq_GHz(); +#endif dump_frame_parms(frame_parms[0]); @@ -2842,56 +2824,64 @@ int main( int argc, char **argv ) sample_rate = 30.72e6; bw = 10.0e6; #ifndef EXMIMO - openair0_cfg[0].samples_per_packet = 2048; - samples_per_frame = 307200; + openair0_cfg[card].sample_rate=30.72e6; + openair0_cfg[card].samples_per_packet = 2048; + openair0_cfg[card].samples_per_frame = 307200; openair0_cfg[card].tx_bw = 10e6; openair0_cfg[card].rx_bw = 10e6; // from usrp_time_offset - tx_forward_nsamps = 175; - tx_delay = 8; + openair0_cfg[card].tx_forward_nsamps = 175; + openair0_cfg[card].tx_delay = 8; #endif } else if(frame_parms[0]->N_RB_DL == 50) { sample_rate = 15.36e6; bw = 5.0e6; #ifndef EXMIMO - openair0_cfg[0].samples_per_packet = 2048; - samples_per_frame = 153600; + openair0_cfg[card].sample_rate=15.36e6; + openair0_cfg[card].samples_per_packet = 2048; + openair0_cfg[card].samples_per_frame = 153600; openair0_cfg[card].tx_bw = 5e6; openair0_cfg[card].rx_bw = 5e6; - tx_forward_nsamps = 95; - tx_delay = 5; + openair0_cfg[card].tx_forward_nsamps = 95; + openair0_cfg[card].tx_delay = 5; #endif } else if (frame_parms[0]->N_RB_DL == 25) { sample_rate = 7.68e6; bw = 2.5e6; #ifndef EXMIMO - samples_per_frame = 76800; + openair0_cfg[card].sample_rate=7.68e6; + openair0_cfg[card].samples_per_frame = 76800; openair0_cfg[card].tx_bw = 2.5e6; openair0_cfg[card].rx_bw = 2.5e6; - openair0_cfg[0].samples_per_packet = 1024; + openair0_cfg[card].samples_per_packet = 1024; #ifdef OAI_USRP - tx_forward_nsamps = 70; - tx_delay = 6; + openair0_cfg[card].tx_forward_nsamps = 70; + openair0_cfg[card].tx_delay = 6; #elif OAI_BLADERF - tx_forward_nsamps = 70; - tx_delay = 6; + openair0_cfg[card].tx_forward_nsamps = 70; + openair0_cfg[card].tx_delay = 6; #endif #endif } else if (frame_parms[0]->N_RB_DL == 6) { sample_rate = 1.92e6; bw = 0.96e6; #ifndef EXMIMO - openair0_cfg[0].samples_per_packet = 256; - samples_per_frame = 19200; + openair0_cfg[card].sample_rate=1.92e6; + openair0_cfg[card].samples_per_packet = 256; + openair0_cfg[card].samples_per_frame = 19200; openair0_cfg[card].tx_bw = 1.5e6; openair0_cfg[card].rx_bw = 1.5e6; - tx_forward_nsamps = 40; - tx_delay = 8; + openair0_cfg[card].tx_forward_nsamps = 40; + openair0_cfg[card].tx_delay = 8; #endif } #ifdef ETHERNET +//calib needed + openair0_cfg[card].tx_delay = 0; + openair0_cfg[card].tx_forward_nsamps = 0; + if (frame_parms[0]->N_RB_DL == 6) openair0_cfg[0].samples_per_packet = 256; else openair0_cfg[0].samples_per_packet = 1024; @@ -2911,14 +2901,14 @@ int main( int argc, char **argv ) if (UE_flag) { printf("ETHERNET: Configuring UE ETH for %s:%d\n",rrh_UE_ip,rrh_UE_port); - openair0_cfg[card].rrh_ip = &rrh_UE_ip[0]; - openair0_cfg[card].rrh_port = rrh_UE_port; + openair0_cfg[card].remote_ip = &rrh_UE_ip[0]; + openair0_cfg[card].remote_port = rrh_UE_port; } else { printf("ETHERNET: Configuring eNB ETH for %s:%d\n",rrh_eNB_ip,rrh_eNB_port); - openair0_cfg[card].rrh_ip = &rrh_eNB_ip[0]; - openair0_cfg[card].rrh_port = rrh_eNB_port; + openair0_cfg[card].remote_ip = &rrh_eNB_ip[0]; + openair0_cfg[card].remote_port = rrh_eNB_port; } - +openair0_cfg[card].num_rb_dl=frame_parms[0]->N_RB_DL; #endif openair0_cfg[card].sample_rate = sample_rate; openair0_cfg[card].tx_bw = bw; @@ -2967,7 +2957,22 @@ int main( int argc, char **argv ) #endif } - printf("Initializing openair0 ..."); + +#ifdef ETHERNET + openair0.type=ETH_IF; // not used for the moment + openair0_dev_init_eth(&openair0, &openair0_cfg[0]); +#else +#ifdef EXMIMO + openair0.type=EXMIMO_IF; + printf("Setting the HW to EXMIMO and initializing openair0 ...\n"); +#elif OAI_USRP + openair0.type=USRP_IF; + printf("Setting the HW to USRP and initializing openair0 ...\n"); +#elif OAI_BLADERF + openair0.type=BLADERF_IF; + printf("Setting the HW to BLADERF and initializing openair0 ...\n"); +#endif + openair0.func_type = BBU_FUNC; openair0_cfg[0].log_level = glog_level; @@ -2979,6 +2984,7 @@ int main( int argc, char **argv ) else if (mode==loop_through_memory) { } +#endif printf("Done\n"); @@ -3296,8 +3302,12 @@ int main( int argc, char **argv ) #ifndef EXMIMO #ifndef USRP_DEBUG +<<<<<<< .mine + openair0.trx_request_func(&openair0); +======= if (mode!=loop_through_memory) openair0.trx_start_func(&openair0); +>>>>>>> .r7708 // printf("returning from usrp start streaming: %llu\n",get_usrp_time(&openair0)); #endif #endif @@ -3543,17 +3553,18 @@ int setup_eNB_buffers(PHY_VARS_eNB **phy_vars_eNB, openair0_config_t *openair0_c for (i=0; i<frame_parms->nb_antennas_rx; i++) { free(phy_vars_eNB[CC_id]->lte_eNB_common_vars.rxdata[0][i]); - rxdata[i] = (int32_t*)(16 + malloc16(16+samples_per_frame*sizeof(int32_t))); // FIXME broken memory allocation + rxdata[i] = (int32_t*)(16 + malloc16(16+openair0_cfg[rf_map[CC_id].card].samples_per_frame*sizeof(int32_t))); // FIXME broken memory allocation phy_vars_eNB[CC_id]->lte_eNB_common_vars.rxdata[0][i] = rxdata[i]-N_TA_offset; // N_TA offset for TDD FIXME! N_TA_offset > 16 => access of unallocated memory - memset(rxdata[i], 0, samples_per_frame*sizeof(int32_t)); + memset(rxdata[i], 0, openair0_cfg[rf_map[CC_id].card].samples_per_frame*sizeof(int32_t)); printf("rxdata[%d] @ %p (%p) (N_TA_OFFSET %d)\n", i, phy_vars_eNB[CC_id]->lte_eNB_common_vars.rxdata[0][i],rxdata[i],N_TA_offset); + } for (i=0; i<frame_parms->nb_antennas_tx; i++) { free(phy_vars_eNB[CC_id]->lte_eNB_common_vars.txdata[0][i]); - txdata[i] = (int32_t*)(16 + malloc16(16 + samples_per_frame*sizeof(int32_t))); // FIXME broken memory allocation + txdata[i] = (int32_t*)(16 + malloc16(16 + openair0_cfg[rf_map[CC_id].card].samples_per_frame*sizeof(int32_t))); // FIXME broken memory allocation phy_vars_eNB[CC_id]->lte_eNB_common_vars.txdata[0][i] = txdata[i]; - memset(txdata[i], 0, samples_per_frame*sizeof(int32_t)); + memset(txdata[i],0, openair0_cfg[rf_map[CC_id].card].samples_per_frame*sizeof(int32_t)); printf("txdata[%d] @ %p\n", i, phy_vars_eNB[CC_id]->lte_eNB_common_vars.txdata[0][i]); } @@ -3564,26 +3575,27 @@ int setup_eNB_buffers(PHY_VARS_eNB **phy_vars_eNB, openair0_config_t *openair0_c return(0); } -void reset_opp_meas(void) -{ +void reset_opp_meas(void) { int sfn; reset_meas(&softmodem_stats_mt); reset_meas(&softmodem_stats_hw); - + for (sfn=0; sfn < 10; sfn++) { reset_meas(&softmodem_stats_tx_sf[sfn]); reset_meas(&softmodem_stats_rx_sf[sfn]); } } -void print_opp_meas(void) -{ +void print_opp_meas(void) { + int sfn=0; print_meas(&softmodem_stats_mt, "Main ENB Thread", NULL, NULL); print_meas(&softmodem_stats_hw, "HW Acquisation", NULL, NULL); - + for (sfn=0; sfn < 10; sfn++) { print_meas(&softmodem_stats_tx_sf[sfn],"[eNB][total_phy_proc_tx]",NULL, NULL); print_meas(&softmodem_stats_rx_sf[sfn],"[eNB][total_phy_proc_rx]",NULL,NULL); } } + + diff --git a/targets/RT/USER/lte-ue.c b/targets/RT/USER/lte-ue.c index 5c9bf13504f8a81d718528c2a169b4f2af76d71a..dc624b6050a7e6cabad086c04f5481d0a3005e38 100644 --- a/targets/RT/USER/lte-ue.c +++ b/targets/RT/USER/lte-ue.c @@ -119,8 +119,8 @@ extern int oai_exit; extern int32_t **rxdata; extern int32_t **txdata; -extern unsigned int tx_forward_nsamps; -extern int tx_delay; +//extern unsigned int tx_forward_nsamps; +//extern int tx_delay; extern int rx_input_level_dBm; extern uint8_t exit_missed_slots; @@ -583,9 +583,9 @@ static void *UE_thread_tx(void *arg) /* This creates a 1ms reservation every 10ms period*/ attr.sched_policy = SCHED_DEADLINE; - attr.sched_runtime = 1 * 900000; // each tx thread requires .5ms to finish its job - attr.sched_deadline =1 * 1000000; // each tx thread will finish within 1ms - attr.sched_period = 1 * 1000000; // each tx thread has a period of 1ms from the starting point + attr.sched_runtime = (0.9 * 100) * 10000; // each tx thread requires .5ms to finish its job + attr.sched_deadline = (1 * 100) * 10000; // each tx thread will finish within 1ms + attr.sched_period = (1 * 100) * 10000; // each tx thread has a period of 1ms from the starting point if (sched_setattr(0, &attr, flags) < 0 ) { @@ -732,9 +732,9 @@ static void *UE_thread_rx(void *arg) // This creates a .5ms reservation every 1ms period attr.sched_policy = SCHED_DEADLINE; - attr.sched_runtime = 1 * 900000; // each rx thread requires 1ms to finish its job - attr.sched_deadline =1 * 1000000; // each rx thread will finish within 1ms - attr.sched_period = 1 * 1000000; // each rx thread has a period of 1ms from the starting point + attr.sched_runtime = (0.9 * 100) * 10000;//900000; // each rx thread requires 1ms to finish its job + attr.sched_deadline = (1 * 100) * 10000; // each rx thread will finish within 1ms + attr.sched_period = (1 * 100) * 10000; // each rx thread has a period of 1ms from the starting point if (sched_setattr(0, &attr, flags) < 0 ) { perror("[SCHED] UE_thread_rx : sched_setattr failed\n"); @@ -938,7 +938,7 @@ void *UE_thread(void *arg) static int UE_thread_retval; PHY_VARS_UE *UE = PHY_vars_UE_g[0][0]; int spp = openair0_cfg[0].samples_per_packet; - int slot=1, frame=0, hw_subframe=0, rxpos=0, txpos=spp*tx_delay; + int slot=1, frame=0, hw_subframe=0, rxpos=0, txpos=spp*openair0_cfg[0].tx_delay; int dummy[2][spp]; int dummy_dump = 0; int tx_enabled = 0; @@ -972,9 +972,9 @@ void *UE_thread(void *arg) // This creates a .5 ms reservation attr.sched_policy = SCHED_DEADLINE; - attr.sched_runtime = 0.25 * 1000000; - attr.sched_deadline = 0.25 * 1000000; - attr.sched_period = 0.5 * 1000000; + attr.sched_runtime = (0.25 * 100) * 10000; + attr.sched_deadline = (0.25 * 100) * 10000; + attr.sched_period = (0.5 * 100) * 10000; if (sched_setattr(0, &attr, flags) < 0 ) { perror("[SCHED] main eNB thread: sched_setattr failed\n"); @@ -1056,7 +1056,7 @@ void *UE_thread(void *arg) txp[i] = (void*)&txdata[i][txpos]; openair0.trx_write_func(&openair0, - (timestamp+spp*tx_delay-tx_forward_nsamps), + (timestamp+spp*openair0_cfg[0].tx_delay-openair0_cfg[0].tx_forward_nsamps), txp, spp - ((first_rx==1) ? rx_off_diff : 0), UE->lte_frame_parms.nb_antennas_tx, @@ -1353,9 +1353,9 @@ void *UE_thread(void *arg) // This creates a .25 ms reservation attr.sched_policy = SCHED_DEADLINE; - attr.sched_runtime = 0.1 * 1000000; - attr.sched_deadline = 0.25 * 1000000; - attr.sched_period = 0.5 * 1000000; + attr.sched_runtime = (0.1 * 100) * 10000; + attr.sched_deadline = (0.25 * 100) * 10000; + attr.sched_period = (0.5 * 100) * 10000; // pin the UE main thread to CPU0 // if (pthread_setaffinity_np(pthread_self(), sizeof(mask),&mask) <0) { diff --git a/targets/RT/USER/rrh.gtkw b/targets/RT/USER/rrh.gtkw new file mode 100644 index 0000000000000000000000000000000000000000..36ebadf9ca5fbe847b1a1d2914469f54eab9c46c --- /dev/null +++ b/targets/RT/USER/rrh.gtkw @@ -0,0 +1,111 @@ +[*] +[*] GTKWave Analyzer v3.3.58 (w)1999-2014 BSI +[*] Thu Jul 9 09:53:25 2015 +[*] +[dumpfile] "/tmp/openair_dump_eNB.vcd" +[dumpfile_mtime] "Thu Jul 9 09:52:29 2015" +[dumpfile_size] 170586112 +[savefile] "/home/sud/openair4G/targets/RT/USER/eNB2.gtkw" +[timestart] 16177999000 +[size] 1535 876 +[pos] -1 -1 +*-17.749426 16178576148 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +[sst_width] 224 +[signals_width] 230 +[sst_expanded] 1 +[sst_vpaned_height] 230 +@28 +[color] 1 +functions.eNB_rx +functions.eNB_rx_sleep +[color] 7 +functions.trx_write +@c00024 +variables.rxcnt[63:0] +@28 +(0)variables.rxcnt[63:0] +(1)variables.rxcnt[63:0] +(2)variables.rxcnt[63:0] +(3)variables.rxcnt[63:0] +(4)variables.rxcnt[63:0] +(5)variables.rxcnt[63:0] +(6)variables.rxcnt[63:0] +(7)variables.rxcnt[63:0] +(8)variables.rxcnt[63:0] +(9)variables.rxcnt[63:0] +(10)variables.rxcnt[63:0] +(11)variables.rxcnt[63:0] +(12)variables.rxcnt[63:0] +(13)variables.rxcnt[63:0] +(14)variables.rxcnt[63:0] +(15)variables.rxcnt[63:0] +(16)variables.rxcnt[63:0] +(17)variables.rxcnt[63:0] +(18)variables.rxcnt[63:0] +(19)variables.rxcnt[63:0] +(20)variables.rxcnt[63:0] +(21)variables.rxcnt[63:0] +(22)variables.rxcnt[63:0] +(23)variables.rxcnt[63:0] +(24)variables.rxcnt[63:0] +(25)variables.rxcnt[63:0] +(26)variables.rxcnt[63:0] +(27)variables.rxcnt[63:0] +(28)variables.rxcnt[63:0] +(29)variables.rxcnt[63:0] +(30)variables.rxcnt[63:0] +(31)variables.rxcnt[63:0] +(32)variables.rxcnt[63:0] +(33)variables.rxcnt[63:0] +(34)variables.rxcnt[63:0] +(35)variables.rxcnt[63:0] +(36)variables.rxcnt[63:0] +(37)variables.rxcnt[63:0] +(38)variables.rxcnt[63:0] +(39)variables.rxcnt[63:0] +(40)variables.rxcnt[63:0] +(41)variables.rxcnt[63:0] +(42)variables.rxcnt[63:0] +(43)variables.rxcnt[63:0] +(44)variables.rxcnt[63:0] +(45)variables.rxcnt[63:0] +(46)variables.rxcnt[63:0] +(47)variables.rxcnt[63:0] +(48)variables.rxcnt[63:0] +(49)variables.rxcnt[63:0] +(50)variables.rxcnt[63:0] +(51)variables.rxcnt[63:0] +(52)variables.rxcnt[63:0] +(53)variables.rxcnt[63:0] +(54)variables.rxcnt[63:0] +(55)variables.rxcnt[63:0] +(56)variables.rxcnt[63:0] +(57)variables.rxcnt[63:0] +(58)variables.rxcnt[63:0] +(59)variables.rxcnt[63:0] +(60)variables.rxcnt[63:0] +(61)variables.rxcnt[63:0] +(62)variables.rxcnt[63:0] +(63)variables.rxcnt[63:0] +@1401200 +-group_end +@28 +[color] 1 +functions.eNB_tx +functions.eNB_tx_sleep +[color] 7 +functions.trx_read +@24 +variables.txcnt[63:0] +variables.rx_ts[63:0] +variables.tx_ts[63:0] +variables.pck_rx[63:0] +variables.hw_cnt_rx[63:0] +variables.lhw_cnt_rx[63:0] +variables.hw_frame[63:0] +@28 +functions.eNB_tm +@29 +functions.eNB_trx +[pattern_trace] 1 +[pattern_trace] 0 diff --git a/targets/RT/USER/rrh_gw.c b/targets/RT/USER/rrh_gw.c new file mode 100644 index 0000000000000000000000000000000000000000..8042be78858da09c2660efd8afe4f5a075cffde2 --- /dev/null +++ b/targets/RT/USER/rrh_gw.c @@ -0,0 +1,540 @@ + +/******************************************************************************* + OpenAirInterface + Copyright(c) 1999 - 2014 Eurecom + + OpenAirInterface is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + + OpenAirInterface is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenAirInterface.The full GNU General Public License is + included in this distribution in the file called "COPYING". If not, + see <http://www.gnu.org/licenses/>. + + Contact Information + OpenAirInterface Admin: openair_admin@eurecom.fr + OpenAirInterface Tech : openair_tech@eurecom.fr + OpenAirInterface Dev : openair4g-devel@eurecom.fr + + Address : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE + + *******************************************************************************/ + +/*! \file rrh_gw.h + * \brief top-level for the remote radio head gateway (RRH_gw) module reusing the ethernet library + * \author Navid Nikaein, Katerina Trilyraki, Raymond Knopp + * \date 2015 + * \version 0.1 + * \company Eurecom + * \maintainer: navid.nikaein@eurecom.fr + * \note + * \warning very experimental + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> +#include <execinfo.h> +#include <unistd.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <net/if.h> +#include <time.h> + +#include "common_lib.h" +#include "rrh_gw.h" // change to rrh_new.h, put externs in rrh_extern.h +#include "rt_wrapper.h" + + +#include "log_if.h" +#include "log_extern.h" +#include "vcd_signal_dumper.h" + +//#undef LOWLATENCY + +/****************************************************************************** + ** FUNCTION PROTOTYPES ** + ******************************************************************************/ +static void debug_init(void); +static void get_options(int argc, char *argv[]); +static void print_help(void); +static void get_RFinterfaces(void); +static rrh_module_t new_module(unsigned int id, dev_type_t type); + + +char rrh_ip[20] = "192.168.12.31"; // there is code to detect the my ip address +int rrh_port = 50000; // has to be an option + +/* log */ +int16_t glog_level = LOG_DEBUG; +int16_t glog_verbosity = LOG_MED; +int16_t rrh_log_level = LOG_INFO; +int16_t rrh_log_verbosity = LOG_MED; +int16_t enb_log_level = LOG_INFO; +int16_t enb_log_verbosity = LOG_MED; +int16_t ue_log_level = LOG_INFO; +int16_t ue_log_verbosity = LOG_MED; + + +/* flags definitions */ +uint8_t eNB_flag=0; +uint8_t UE_flag=0; +uint8_t EXMIMO_flag=0; +uint8_t USRP_flag=0; +uint8_t RT_flag=0, NRT_flag=1; +uint8_t rrh_exit=0; + +/* Default operation as RRH: + - there are no eNB nor UE modules + - no RF device is asscociated with RRH */ +uint8_t num_eNB_mod=0; +uint8_t num_UE_mod=0; +uint8_t num_EXMIMO_mod=0; +uint8_t num_USRP_mod=0; +uint8_t loopback_flag=0; +uint8_t measurements_flag=0; +uint8_t hardware_target=NONE_IF; + +unsigned int RT_period=0; +rrh_module_t *enb_array,*ue_array; +pthread_mutex_t timer_mutex; +openair0_vtimestamp hw_counter=0; + +unsigned int rt_period; + +struct itimerspec timerspec; + + +int main(int argc, char **argv) { + + unsigned int i; + + /* parse input arguments */ + get_options(argc, argv); + /* initialize logger and signal analyzer */ + debug_init(); + + set_latency_target(); + /*make a graceful exit when ctrl-c is pressed */ + signal(SIGSEGV, signal_handler); + signal(SIGINT, signal_handler); + //probe RF front end devices interfaced to RRH + //get_RFinterfaces(); + + /* create modules based on input arguments */ + if (eNB_flag==1){ + + enb_array=(rrh_module_t*)malloc(num_eNB_mod*sizeof(rrh_module_t)); + // create num of modules + for(i=0;i<num_eNB_mod;i++){ + /* + enb_array[i]=new_module(i, get_RF_interfaces(&hardware_target)); + */ + enb_array[i]=new_module(i, NONE_IF); + create_eNB_trx_threads(&enb_array[i],RT_flag,NRT_flag); + LOG_I(RRH,"[eNB %d] module(s) created (%u) \n",i,num_eNB_mod); + } + + } + if (UE_flag==1){ + + ue_array=(rrh_module_t*)malloc(num_UE_mod*sizeof(rrh_module_t)); + // create num of modules + for(i=0;i<num_UE_mod;i++){ + ue_array[i]=new_module(i, NONE_IF); + create_UE_trx_threads(&ue_array[i],RT_flag,NRT_flag); + LOG_I(RRH,"[UE %d] module(s) created (%u)\n",i, num_UE_mod); + } + } + + printf("TYPE <CTRL-C> TO TERMINATE\n"); + + while (rrh_exit==0) + sleep(1); + + //close sockets + + return EXIT_SUCCESS; +} + + + + +/*!\fn openair0_device new_module (unsigned int id, dev_type_t type) +* \brief this function +* \param[in] +* \param[out] +* \return +* \note +* @ingroup _oai +*/ +static rrh_module_t new_module (unsigned int id, dev_type_t type) { + + rrh_module_t rrh_mod; + openair0_config_t openair0_cfg; + + rrh_mod.id=id; + rrh_mod.loopback=loopback_flag; + rrh_mod.measurements=measurements_flag; + rrh_mod.eth_dev.func_type=RRH_FUNC; + /* each module is associated with an ethernet device */ + rrh_mod.eth_dev.type=ETH_IF; + openair0_cfg.my_ip=&rrh_ip[0]; + openair0_cfg.my_port=rrh_port; + + //printf("rrh ip %s\n", rrh_mod.eth_dev.openair0_cfg.my_ip); + /* ethernet device initialization*/ + if (openair0_dev_init_eth(&rrh_mod.eth_dev, &openair0_cfg)<0){ + LOG_E(RRH,"Exiting, cannot initialize ethernet interface.\n"); + exit(-1); + } + + /* initialize another HW device */ + switch(type){ + case ETH_IF: + LOG_E(RRH,"Error: one module is associated with one ETHERNET iterface!\n"); + break; + case EXMIMO_IF: + rrh_mod.devs=malloc(sizeof(openair0_device)); + rrh_mod.devs->type=EXMIMO_IF; + LOG_I(RRH,"Setting RF interface to EXMIMO_IF and initializing device ...\n"); + /*if (openair0_device_init(rrh_mod.devs, &openair0_cfg)<0){ + printf("Exiting, cannot initialize device.\n"); + exit(-1); + }*/ + break; + case USRP_IF: + rrh_mod.devs=malloc(sizeof(openair0_device)); + rrh_mod.devs->type=USRP_IF; + LOG_I(RRH,"Setting RF interface to USRP_IF and initializing device ...\n"); + /*if (openair0_device_init(rrh_mod.devs, &openair0_cfg)<0){ + printf("Exiting, cannot initialize device.\n"); + exit(-1); + } */ + break; + case BLADERF_IF: + rrh_mod.devs=malloc(sizeof(openair0_device)); + rrh_mod.devs->type=BLADERF_IF; + LOG_I(RRH,"Setting RF interface to BLADERF_IF and initializing device ...\n"); + /* if (openair0_device_init(rrh_mod.devs, &openair0_cfg)<0){ + printf("Exiting, cannot initialize device.\n"); + exit(-1); + } */ + break; + case NONE_IF: + rrh_mod.devs=NULL; + LOG_I(RRH,"Setting RF interface to NONE_IF... \n"); + break; + default: + break; + } + return rrh_mod; +} + + + +/*! \fn static void get_RFinterfaces(void) +* \brief this function +* \param[in] +* \param[out] +* \return +* \note +* @ingroup _oai +*/ +static void get_RFinterfaces(void) { + + //tbd + EXMIMO_flag=1; + USRP_flag=1; + num_EXMIMO_mod=1; + num_USRP_mod=1; + +} + + +/*!\fn void create_timer_thread(void) +* \brief this function +* \param[in] +* \param[out] +* \return +* \note +* @ingroup _oai +*/ +void create_timer_thread() { + + int error_code_timer; + pthread_t main_timer_proc_thread; + + LOG_I(RRH,"Creating timer thread with rt period %d ns.\n",rt_period); + + /* setup the timer to generate an interrupt: + -for the first time in (sample_per_packet/sample_rate) ns + -and then every (sample_per_packet/sample_rate) ns */ + timerspec.it_value.tv_sec = rt_period/1000000000; + timerspec.it_value.tv_nsec = rt_period%1000000000; + timerspec.it_interval.tv_sec = rt_period/1000000000; + timerspec.it_interval.tv_nsec = rt_period%1000000000; + + pthread_mutex_init(&timer_mutex,NULL); + +#ifndef LOWLATENCY + pthread_attr_t attr_timer; + struct sched_param sched_param_timer; + + pthread_attr_init(&attr_timer); + sched_param_timer.sched_priority = sched_get_priority_max(SCHED_FIFO-1); + pthread_attr_setschedparam(&attr_timer,&sched_param_timer); + pthread_attr_setschedpolicy(&attr_timer,SCHED_FIFO-1); + error_code_timer = pthread_create(&main_timer_proc_thread, &attr_timer, timer_proc, (void *)&timerspec); + LOG_D(RRH,"[SCHED] FIFO scheduling \n"); +#else + error_code_timer = pthread_create(&main_timer_proc_thread, NULL, timer_proc, (void *)&timerspec); + LOG_D(RRH,"[SCHED] LOWLATENCY scheduling \n"); +#endif + + if (error_code_timer) { + LOG_E(RRH,"Error while creating timer proc thread\n"); + exit(-1); + } +} + + +/*! \fn void *timer_proc(void *arg) +* \brief this function +* \param[in] +* \param[out] +* \return +* \note +* @ingroup _oai +*/ +void *timer_proc(void *arg) { + + timer_t timerid; // timer ID for timer + struct itimerspec *timer = (struct itimerspec*)arg; // the timer data structure + struct itimerspec *old_value; + + LOG_I(RRH,"timer thread created\n"); + + +#ifdef LOWLATENCY + struct sched_attr attr; + unsigned int flags = 0; + + attr.size = sizeof(attr); + attr.sched_flags = 0; + attr.sched_nice = 0; + attr.sched_priority = 0; + + attr.sched_policy = SCHED_DEADLINE; + attr.sched_runtime = (0.1 * 100) * 10000; // + attr.sched_deadline = rt_period;//(0.1 * 100) * 10000; // + attr.sched_period = rt_period;//(0.1 * 100) * 10000; // each TX/RX thread has, as a function of RT PERIOD ?? + + if (sched_setattr(0, &attr, flags) < 0 ) { + perror("[SCHED] timer thread: sched_setattr failed\n"); + exit(-1); + } +#endif + //printf("Starting the timer\n"); + if (timer_create (CLOCK_REALTIME, NULL, &timerid) == -1) { + fprintf (stderr, "couldn't create a timer\n"); + perror (NULL); + exit (EXIT_FAILURE); + } + + signal(SIGALRM, timer_signal_handler); + // and start it! + timer_settime (timerid, 0, timer, old_value); + //timer_settime (timerid, TIMER_ABSTIME, timer, old_value); + + while (!rrh_exit) { + //VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_TM, 1 ); + sleep(1); + //VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_TM, 0 ); + } + + timer_delete(timerid); + + return (0); +} + + +/*! \fn void timer_signal_handler(int sig) +* \brief this function +* \param[in] +* \param[out] +* \return +* \note +* @ingroup _oai +*/ +void timer_signal_handler(int sig) { + + if (sig == SIGALRM) { + pthread_mutex_lock(&timer_mutex); + hw_counter ++; + pthread_mutex_unlock(&timer_mutex); + VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_HW_FRAME, hw_counter); + } +} + + +/*! \fn void signal_handler(int sig) +* \brief this function +* \param[in] +* \param[out] +* \return +* \note +* @ingroup _oai +*/ +void signal_handler(int sig) { + + void *array[10]; + size_t size; + + if (sig==SIGSEGV) { + // get void*'s for all entries on the stack + size = backtrace(array, 10); + + // print out all the frames to stderr + fprintf(stderr, "Error: signal %d:\n", sig); + backtrace_symbols_fd(array, size, 2); + exit(-1); + } else { + printf("trying to exit gracefully...\n"); + rrh_exit = 1; + } +} + + +/*! \fn void debug_init(void) + * \brief this function + * \param[in] + * \param[out] + * \return + * \note + * @ingroup _oai + */ +static void debug_init(void) { + + // log initialization + logInit(); + set_glog(glog_level, glog_verbosity); + + set_comp_log(RRH, rrh_log_level, rrh_log_verbosity, 1); + //set_comp_log(ENB_LOG, enb_log_level, enb_log_verbosity, 1); + //set_comp_log(UE_LOG, ue_log_level, ue_log_verbosity, 1); + + // vcd initialization + if (ouput_vcd) { + + if (eNB_flag==1) + vcd_signal_dumper_init("/tmp/openair_dump_eNB.vcd"); + if (UE_flag==1) + vcd_signal_dumper_init("/tmp/openair_dump_UE.vcd"); + + } + +} + + +/*!\fn void get_options(int argc, char *argv[]) +* \brief this function +* \param[in] +* \param[out] +* \return +* \note +* @ingroup _oai +*/ +static void get_options(int argc, char *argv[]) { + + int opt; + + while ((opt = getopt(argc, argv, "xvhlte:n:u:g:r:w:")) != -1) { + + switch (opt) { + case 'n': + eNB_flag=1; + num_eNB_mod=atoi(optarg); + break; + case 'u': + UE_flag=1; + num_UE_mod=atoi(optarg); + break; + case 'g': + glog_level=atoi(optarg); + break; + case 'r': + //rrh_log_level=atoi(optarg); + break; + case 'e': + //enb_log_level=atoi(optarg); + break; + case 'x': + rt_period = DEFAULT_PERIOD_NS; + RT_flag=1; + NRT_flag=0; + break; + case 'v': + /* extern from vcd */ + ouput_vcd=1; + break; + case 'l': + /*In loopback mode rrh sends back to bbu what it receives*/ + loopback_flag=1; + break; + case 't': + /*When measurements are enabled statistics related to TX/RX time are printed*/ + measurements_flag=1; + break; + case 'w': + /* force to use this target*/ + //hardware_target= + break; + case 'h': + print_help(); + exit(-1); + default: /* '?' */ + //fprintf(stderr, "Usage: \n", argv[0]); + exit(-1); + } + } + +} + + +/*!\fn void print_help(void) +* \brief this function +* \param[in] +* \param[out] +* \return +* \note +* @ingroup _oai +*/ +static void print_help(void) { + + puts("Usage: \n"); + puts(" sudo -E chrt 99 ./rrh -n1 -g6 -v -t"); + puts("Options:\n"); + puts("\t -n create eNB module\n"); + puts("\t -u create UE module\n"); + puts("\t -g define global log level\n"); + puts("\t -r define rrh log level\n"); + puts("\t -e define eNB log level\n"); + puts("\t -x enable real time bahaviour\n"); + puts("\t -v enable vcd dump\n"); + puts("\t -l enable loopback mode\n"); + puts("\t -t enable measurements\n"); + puts("\t -w force to use specified HW\n"); + puts("\t -h display info\n"); + +} + diff --git a/targets/RT/USER/rrh_gw.h b/targets/RT/USER/rrh_gw.h new file mode 100644 index 0000000000000000000000000000000000000000..de941f1b375a6ff5580bf43b9dfdcf41c4a6f120 --- /dev/null +++ b/targets/RT/USER/rrh_gw.h @@ -0,0 +1,85 @@ +/******************************************************************************* + OpenAirInterface + Copyright(c) 1999 - 2014 Eurecom + + OpenAirInterface is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + + OpenAirInterface is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenAirInterface.The full GNU General Public License is + included in this distribution in the file called "COPYING". If not, + see <http://www.gnu.org/licenses/>. + + Contact Information + OpenAirInterface Admin: openair_admin@eurecom.fr + OpenAirInterface Tech : openair_tech@eurecom.fr + OpenAirInterface Dev : openair4g-devel@eurecom.fr + + Address : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE + + *******************************************************************************/ + +/*! \file rrh_gw.h + * \brief header file for remote radio head gateway (RRH_gw) module + * \author Navid Nikaein, Katerina Trilyraki, Raymond Knopp + * \date 2015 + * \version 0.1 + * \company Eurecom + * \maintainer: navid.nikaein@eurecom.fr + * \note + * \warning very experimental + */ + +#ifndef RRH_GW_H_ +#define RRH_GW_H_ + +#include "ethernet_lib.h" +#include "vcd_signal_dumper.h" +#include "assertions.h" + +#define DEFAULT_PERIOD_NS 200000 +#define RRH_UE_PORT 51000 +#define RRH_UE_DEST_IP "127.0.0.1" + +/*! \brief RRH supports two types of modules: eNB and UE + each module is associated a device of type ETH_IF + and optionally with another device (USRP/BLADERF/EXMIMO) */ +typedef struct { + //! module id + uint8_t id; + //! loopback flag + uint8_t loopback; + //! measurement flag + uint8_t measurements; + //! module's ethernet device + openair0_device eth_dev; + //! pointer to RF module's device (pointer->since its optional) + openair0_device *devs; + +}rrh_module_t; + + +/****************************************************************************** + ** FUNCTION PROTOTYPES ** + ******************************************************************************/ +void signal_handler(int sig); +void timer_signal_handler(int); +void *timer_proc(void *); +void create_timer_thread(void); + + +/****************************************************************************** + ** FUNCTION PROTOTYPES ** + ******************************************************************************/ +void create_UE_trx_threads( rrh_module_t *dev_ue, uint8_t RT_flag, uint8_t NRT_flag); +void create_eNB_trx_threads( rrh_module_t *dev_enb, uint8_t RT_flag, uint8_t NRT_flag); + +#endif diff --git a/targets/RT/USER/rrh_gw_externs.h b/targets/RT/USER/rrh_gw_externs.h new file mode 100644 index 0000000000000000000000000000000000000000..317af1bf4868600a1d2bab76ed8be436fc93cb98 --- /dev/null +++ b/targets/RT/USER/rrh_gw_externs.h @@ -0,0 +1,53 @@ +/******************************************************************************* + OpenAirInterface + Copyright(c) 1999 - 2014 Eurecom + + OpenAirInterface is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + + OpenAirInterface is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenAirInterface.The full GNU General Public License is + included in this distribution in the file called "COPYING". If not, + see <http://www.gnu.org/licenses/>. + + Contact Information + OpenAirInterface Admin: openair_admin@eurecom.fr + OpenAirInterface Tech : openair_tech@eurecom.fr + OpenAirInterface Dev : openair4g-devel@eurecom.fr + + Address : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE + + *******************************************************************************/ +/*! \file rrh_gw_extern.h + * \brief rrh gatewy external vars + * \author Navid Nikaein, Katerina Trilyraki, Raymond Knopp + * \date 2015 + * \version 0.1 + * \company Eurecom + * \maintainer: navid.nikaein@eurecom.fr + * \note + * \warning very experimental + */ + +#ifndef RRH_GW_EXTERNS_H_ +#define RRH_GW_EXTERNS_H_ + +extern openair0_timestamp timestamp_UE_tx[4] ,timestamp_UE_rx[4] ,timestamp_eNB_rx[4],timestamp_eNB_tx[4]; +extern openair0_vtimestamp hw_counter; +extern int32_t UE_tx_started,UE_rx_started,eNB_rx_started ,eNB_tx_started; +extern int32_t nsamps_UE[4],nsamps_eNB[4]; +extern int32_t overflow_rx_buffer_eNB[4],overflow_rx_buffer_UE[4]; +extern uint8_t rrh_exit; +extern int32_t **rx_buffer_eNB, **rx_buffer_UE; + +extern unsigned int rt_period; + +#endif diff --git a/targets/RT/USER/rt_wrapper.c b/targets/RT/USER/rt_wrapper.c index f6221400b7e44d341a7d54ccb24376ed9b3950a4..62329d0f1b098ee464ed1e16c1183c7ad7ec6fb2 100644 --- a/targets/RT/USER/rt_wrapper.c +++ b/targets/RT/USER/rt_wrapper.c @@ -28,17 +28,51 @@ *******************************************************************************/ /*! \file rt_wrapper.h -* \brief provides a wrapper for the timing function for real-time opeartions depending on weather RTAI is used or not -* \author F. Kaltenberger +* \brief provides a wrapper for the timing function, runtime calculations for real-time opeartions depending on weather RTAI or LOWLATENCY kernels are used or not +* \author F. Kaltenberger and Navid Nikaein * \date 2013 * \version 0.1 * \company Eurecom -* \email: florian.kaltenberger@eurecom.fr +* \email: florian.kaltenberger@eurecom.fr, navid.nikaein@eurecom.fr * \note * \warning */ #include "rt_wrapper.h" +static int latency_target_fd = -1; +static int32_t latency_target_value = 0; +/* Latency trick - taken from cyclictest.c + * if the file /dev/cpu_dma_latency exists, + * open it and write a zero into it. This will tell + * the power management system not to transition to + * a high cstate (in fact, the system acts like idle=poll) + * When the fd to /dev/cpu_dma_latency is closed, the behavior + * goes back to the system default. + * + * Documentation/power/pm_qos_interface.txt + */ +void set_latency_target(void) { + struct stat s; + int ret; + + if (stat("/dev/cpu_dma_latency", &s) == 0) { + latency_target_fd = open("/dev/cpu_dma_latency", O_RDWR); + + if (latency_target_fd == -1) + return; + + ret = write(latency_target_fd, &latency_target_value, 4); + + if (ret == 0) { + printf("# error setting cpu_dma_latency to %d!: %s\n", latency_target_value, strerror(errno)); + close(latency_target_fd); + return; + } + + printf("# /dev/cpu_dma_latency set to %dus\n", latency_target_value); + } +} + #ifndef RTAI @@ -89,6 +123,135 @@ void check_clock(void) } } +uint16_t cell_processing_dl[6]={10,15,24,42,80,112}; +uint16_t platform_processing_dl=20; // upperbound for GPP, LXC, DOCKER and KVM +uint16_t user_processing_dl_a[6]={2,4,5,7,10,12}; +uint16_t user_processing_dl_b[6]={10, 15, 25, 70, 110, 150}; +uint16_t user_processing_dl_err[6]={20, 40, 60, 90, 120, 160}; +uint16_t protocol_processing_dl[6]={150, 250, 350, 450, 650, 800}; // assumption: max MCS 27 --> gives an upper bound for the transport block size : to be measured + +uint16_t cell_processing_ul[6]={10,15,24,42,80,112}; +uint16_t platform_processing_ul=30; // upperbound for GPP, LXC, DOCKER and KVM +uint16_t user_processing_ul_a[6]={5, 9, 12, 24, 33, 42}; +uint16_t user_processing_ul_b[6]={20, 30, 40, 76, 140, 200}; +uint16_t user_processing_ul_err[6]={15, 25, 32, 60, 80, 95}; +uint16_t protocol_processing_ul[6]={100, 200, 300, 400, 550, 700}; // assumption: max MCS 16 --> gives an upper bound for the transport block size + +int fill_modeled_runtime_table(uint16_t runtime_phy_rx[29][6], + uint16_t runtime_phy_tx[29][6]){ + //double cpu_freq; + //cpu_freq = get_cpu_freq_GHz(); + // target_dl_mcs + // target_ul_mcs + // frame_parms[0]->N_RB_DL + int i,j; + memset(runtime_phy_rx,0,sizeof(uint16_t)*29*6); + memset(runtime_phy_tx,0,sizeof(uint16_t)*29*6); + /* only the BBU/PHY procesing time */ + for (i=0;i<29;i++){ + for (j=0;j<6;j++){ + runtime_phy_rx[i][j] = cell_processing_ul[j] + platform_processing_ul + user_processing_ul_err[j] + user_processing_ul_a[j]*i+ user_processing_ul_b[j]; + runtime_phy_tx[i][j] = cell_processing_dl[j] + platform_processing_dl + user_processing_dl_err[j] + user_processing_dl_a[j]*i+ user_processing_dl_b[j]; + } + } +} + +// int runtime_upper_layers[6]; // values for different RBs +// int runtime_phy_rx[29][6]; // SISO [MCS 0-28][RBs 0-5 : 6, 15, 25, 50, 75, 100] +// int runtime_phy_tx[29][6]; // SISO [MCS 0-28][RBs 0-5 : 6, 15, 25, 50, 75, 100] + +// target_dl_mcs + // target_ul_mcs + // frame_parms[0]->N_RB_DL + //runtime_upper_layers[6]; // values for different RBs + // int runtime_phy_rx[29][6]; // SISO [MCS 0-28][RBs 0-5 : 6, 15, 25, 50, 75, 100] + // int runtime_phy_tx[29][6] + +double get_runtime_tx(int tx_subframe, uint16_t runtime_phy_tx[29][6], uint32_t mcs, int N_RB_DL,double cpuf,int nb_tx_antenna){ + int i; + double runtime; + //printf("cpuf =%lf \n",cpuf); + switch(N_RB_DL){ + case 6: + i = 0; + break; + case 15: + i = 1; + break; + case 25: + i = 2; + break; + case 50: + i = 3; + break; + case 75: + i = 4; + break; + case 100: + i = 5; + break; + default: + i = 3; + break; + } + + runtime = ( (3.2/cpuf)*(double)runtime_phy_tx[mcs][i] + (3.2/cpuf)*(double)protocol_processing_dl[i])/1000 ; + printf("Setting tx %d runtime value (ms) = %lf\n",tx_subframe,runtime); + + return runtime; + } + +double get_runtime_rx(int rx_subframe, uint16_t runtime_phy_rx[29][6], uint32_t mcs, int N_RB_DL,double cpuf,int nb_rx_antenna){ + int i; + double runtime; + + //printf("N_RB_DL=%d cpuf =%lf \n",N_RB_DL, cpuf); + switch(N_RB_DL){ + case 6: + i = 0; + break; + case 15: + i = 1; + break; + case 25: + i = 2; + break; + case 50: + i = 3; + break; + case 75: + i = 4; + break; + case 100: + i = 5; + break; + default: + i = 3; + break; + } + + runtime = ((3.2/cpuf)*(double)runtime_phy_rx[mcs][i] + (3.2/cpuf)*(double)protocol_processing_ul[i])/1000 ; + printf("Setting rx %d runtime value (ms) = %lf \n",rx_subframe, runtime); + + return runtime; +} + +#ifdef LOWLATENCY +int sched_setattr(pid_t pid, const struct sched_attr *attr, unsigned int flags) +{ + + return syscall(__NR_sched_setattr, pid, attr, flags); +} + + +int sched_getattr(pid_t pid,struct sched_attr *attr,unsigned int size, unsigned int flags) +{ + + return syscall(__NR_sched_getattr, pid, attr, size, flags); +} + +#endif + #else int rt_sleep_ns(RTIME x) diff --git a/targets/RT/USER/rt_wrapper.h b/targets/RT/USER/rt_wrapper.h index e917ceaca63353f7f93a33e6027d19d776bf1ac1..abe6e79a9116c89c773f658710bef115ac698382 100644 --- a/targets/RT/USER/rt_wrapper.h +++ b/targets/RT/USER/rt_wrapper.h @@ -38,13 +38,20 @@ * \warning This code will be removed when a legacy libc API becomes available. */ + +void set_latency_target(void); + #ifndef RTAI #include <time.h> #include <errno.h> -#include <stdio.h> +#include <stdint.h> +#include <sys/stat.h> +#include <fcntl.h> #include <pthread.h> #include <linux/kernel.h> #include <linux/types.h> +#include <syscall.h> +#include <math.h> #define RTIME long long int @@ -56,6 +63,11 @@ int rt_sleep_ns (RTIME x); void check_clock(void); +int fill_modeled_runtime_table(uint16_t runtime_phy_rx[29][6],uint16_t runtime_phy_tx[29][6]); + +double get_runtime_tx(int tx_subframe, uint16_t runtime_phy_tx[29][6],uint32_t mcs, int N_RB_DL,double cpuf,int nb_tx_antenna); + +double get_runtime_rx(int rx_subframe, uint16_t runtime_phy_rx[29][6], uint32_t mcs, int N_RB_DL,double cpuf,int nb_rx_antenna); /** * see https://www.kernel.org/doc/Documentation/scheduler/sched-deadline.txt or * http://www.blaess.fr/christophe/2014/04/05/utiliser-un-appel-systeme-inconnu-de-la-libc/