From 6f290e71253830f5a3ff9ea34196b5cbbc66258c Mon Sep 17 00:00:00 2001 From: root Date: Fri, 22 Sep 2017 15:51:03 +0200 Subject: [PATCH] UE add RF board ZC706+AD9371 Conflicts: cmake_targets/build_oai openair1/PHY/LTE_TRANSPORT/prach.c openair1/SCHED/phy_procedures_lte_ue.c targets/ARCH/COMMON/common_lib.h targets/RT/USER/lte-softmodem.c --- cmake_targets/CMakeLists.txt | 46 +- cmake_targets/build_oai | 95 +- cmake_targets/tools/build_helper | 30 + oaienv | 3 + openair1/PHY/LTE_TRANSPORT/dci_tools.c | 3 + openair1/PHY/LTE_TRANSPORT/initial_sync.c | 6 +- openair1/PHY/LTE_TRANSPORT/prach.c | 4 +- openair1/PHY/LTE_TRANSPORT/print_stats.c | 6 +- openair1/SCHED/phy_procedures_lte_ue.c | 28 +- openair2/RRC/LITE/rrc_UE.c | 2 +- openair2/RRC/LITE/rrc_eNB.c | 2 +- targets/ARCH/ADRV9371_ZC706/Makefile | 63 + .../USERSPACE/PROFILES/ADRV9371-W_test.ini | 337 +++++ .../USERSPACE/PROFILES/profile1-osc.ini | 85 ++ .../PROFILES/profile_rx30_Tx60_syr.txt | 285 ++++ .../ue.band7.tm1.PRB100.adrv9371-zc706.ini | 89 ++ ...7.tm1.PRB100.adrv9371-zc706_HWgain15dB.ini | 89 ++ .../ue.band7.tm1.PRB25.adrv9371-zc706.ini | 85 ++ .../ue.band7.tm1.PRB50.adrv9371-zc706.ini | 85 ++ targets/ARCH/ADRV9371_ZC706/readme.txt | 17 + .../ADRV9371_ZC706/slib/libadrv9371_zc706.so | Bin 0 -> 260713 bytes targets/ARCH/COMMON/common_lib.c | 6 + targets/ARCH/COMMON/common_lib.h | 12 + targets/COMMON/openairinterface5g_limits.h | 2 +- targets/RT/USER/lte-hwlat.c | 944 ++++++++++++ targets/RT/USER/lte-hwlat2.c | 1282 +++++++++++++++++ targets/SIMU/USER/oaisim_functions.c | 2 +- targets/build_oai.bash | 4 + 28 files changed, 3582 insertions(+), 30 deletions(-) create mode 100755 targets/ARCH/ADRV9371_ZC706/Makefile create mode 100644 targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/ADRV9371-W_test.ini create mode 100644 targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/profile1-osc.ini create mode 100644 targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/profile_rx30_Tx60_syr.txt create mode 100644 targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/ue.band7.tm1.PRB100.adrv9371-zc706.ini create mode 100644 targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/ue.band7.tm1.PRB100.adrv9371-zc706_HWgain15dB.ini create mode 100644 targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/ue.band7.tm1.PRB25.adrv9371-zc706.ini create mode 100644 targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/ue.band7.tm1.PRB50.adrv9371-zc706.ini create mode 100644 targets/ARCH/ADRV9371_ZC706/readme.txt create mode 100755 targets/ARCH/ADRV9371_ZC706/slib/libadrv9371_zc706.so create mode 100755 targets/RT/USER/lte-hwlat.c create mode 100644 targets/RT/USER/lte-hwlat2.c diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt index 5182f597e3..c536d90ab6 100644 --- a/cmake_targets/CMakeLists.txt +++ b/cmake_targets/CMakeLists.txt @@ -484,7 +484,7 @@ include_directories ("${X2AP_DIR}") add_list1_option(NB_ANTENNAS_RX "2" "Number of antennas in reception" "1" "2" "4") add_list1_option(NB_ANTENNAS_TX "4" "Number of antennas in transmission" "1" "2" "4") -add_list2_option(RF_BOARD "EXMIMO" "RF head type" "None" "EXMIMO" "OAI_USRP" "OAI_BLADERF" "CPRIGW" "OAI_LMSSDR") +add_list2_option(RF_BOARD "EXMIMO" "RF head type" "None" "EXMIMO" "OAI_USRP" "OAI_BLADERF" "CPRIGW" "OAI_LMSSDR" "OAI_ADRV9371_ZC706") add_list2_option(TRANSP_PRO "None" "Transport protocol type" "None" "ETHERNET") #NOKIA config enhancement @@ -507,6 +507,11 @@ set (SHLIB_LOADER_SOURCES ) # include RF devices / transport protocols library modules ###################################################################### +if (HWLAT) + add_definitions(-DHWLAT ) +endif() + + include_directories("${OPENAIR_TARGETS}/ARCH/EXMIMO/USERSPACE/LIB/") include_directories ("${OPENAIR_TARGETS}/ARCH/EXMIMO/DEFS/") @@ -761,6 +766,9 @@ include_directories("${OPENAIR3_DIR}/UDP") include_directories("${OPENAIR3_DIR}/GTPV1-U") include_directories("${OPENAIR_DIR}/targets/COMMON") include_directories("${OPENAIR_DIR}/targets/ARCH/COMMON") +include_directories("${OPENAIR_DIR}/targets/ARCH/ADRV9371_ZC706/USERSPACE/LIB/") +include_directories("${OPENAIR_DIR}/targets/ARCH/ADRV9371_ZC706/USERSPACE/libini/") +include_directories("${OPENAIR_DIR}/targets/ARCH/ADRV9371_ZC706/DEFS/") include_directories("${OPENAIR_DIR}/targets/ARCH/EXMIMO/USERSPACE/LIB/") include_directories("${OPENAIR_DIR}/targets/ARCH/EXMIMO/DEFS") include_directories("${OPENAIR2_DIR}/ENB_APP") @@ -2063,6 +2071,38 @@ target_link_libraries (lte-softmodem-nos1 pthread m ${CONFIG_LIBRARIES} rt crypt target_link_libraries (lte-softmodem-nos1 ${LIB_LMS_LIBRARIES}) target_link_libraries (lte-softmodem-nos1 ${T_LIB}) + +# lte-hwlat +################################################### +add_executable(lte-hwlat + ${HW_SOURCE} + ${OPENAIR_TARGETS}/RT/USER/lte-hwlat.c + ${OPENAIR_TARGETS}/ARCH/COMMON/common_lib.c + ) +target_link_libraries (lte-hwlat + -Wl,--start-group + -Wl,--end-group ) + +target_link_libraries (lte-hwlat rt pthread m ) +target_link_libraries (lte-hwlat ${CMAKE_DL_LIBS} ) +target_link_libraries (lte-hwlat ${OPENAIR_TARGETS}/ARCH/ADRV9371_ZC706/slib/libadrv9371_zc706.so ) + + +# lte-hwlat-test +################################################### +add_executable(lte-hwlat-test + ${HW_SOURCE} + ${OPENAIR1_DIR}/PHY/TOOLS/time_meas.c + ${OPENAIR_TARGETS}/RT/USER/lte-hwlat2.c + ${OPENAIR_TARGETS}/ARCH/COMMON/common_lib.c + ) +target_link_libraries (lte-hwlat-test + -Wl,--start-group + -Wl,--end-group ) + +target_link_libraries (lte-hwlat-test rt pthread m ) +target_link_libraries (lte-hwlat-test ${CMAKE_DL_LIBS} ) + # lte-uesoftmodem is UE implementation ####################################### @@ -2379,10 +2419,10 @@ if (${T_TRACER}) #all "add_executable" definitions (except tests, rb_tool, updatefw) lte-softmodem lte-softmodem-nos1 oaisim oaisim_nos1 dlsim_tm4 dlsim dlsim_tm7 ulsim pbchsim scansim mbmssim - pdcchsim pucchsim prachsim syncsim + pdcchsim pucchsim prachsim syncsim lte-hwlat #all "add_library" definitions ITTI RRC_LIB S1AP_LIB S1AP_ENB X2AP_LIB - oai_exmimodevif oai_usrpdevif oai_bladerfdevif oai_lmssdrdevif + oai_adrv9371_zc706devif oai_exmimodevif oai_usrpdevif oai_bladerfdevif oai_lmssdrdevif oai_eth_transpro FLPT_MSG ASYNC_IF FLEXRAN_AGENT HASHTABLE MSC UTIL OMG_SUMO SECU_OSA SECU_CN SCHED_LIB PHY L2 default_sched remote_sched RAL CN_UTILS diff --git a/cmake_targets/build_oai b/cmake_targets/build_oai index 596b5aff72..d9cad35b9e 100755 --- a/cmake_targets/build_oai +++ b/cmake_targets/build_oai @@ -51,6 +51,7 @@ REL="Rel14" HW="None" TP="None" NOS1=0 +HW_LATENCY=0 EPC=0 VERBOSE_COMPILE=0 CFLAGS_PROCESSOR_USER="" @@ -104,12 +105,14 @@ Options Specify conf_nvram_path (default \"$conf_nvram_path\") --UE-gen-nvram [output path] Specify gen_nvram_path (default \"$gen_nvram_path\") +--HWLAT + Makes test program for haw latency tests -r | --3gpp-release default is Rel14, Rel8 limits the implementation to 3GPP Release 8 version Rel10 limits the implementation to 3GPP Release 10 version -w | --hardware - EXMIMO, USRP, BLADERF, ETHERNET, LMSSDR, None (Default) + EXMIMO, USRP, BLADERF, ETHERNET, LMSSDR, ADRV9371_ZC706, None (Default) Adds this RF board support (in external packages installation and in compilation) -t | --transport protocol ETHERNET , None @@ -234,13 +237,16 @@ function main() { shift 2;; -w | --hardware) HW="$2" #"${i#*=}" - # Use OAI_USRP as the key word USRP is used inside UHD driver - if [ "$HW" != "BLADERF" -a "$HW" != "USRP" -a "$HW" != "LMSSDR" -a "$HW" != "None" -a "$HW" != "EXMIMO" ] ; then + # Use OAI_USRP as the key word USRP is used inside UHD driver + if [ "$HW" != "BLADERF" -a "$HW" != "USRP" -a "$HW" != "LMSSDR" -a "$HW" != "None" -a "$HW" != "EXMIMO" -a "$HW" != "ADRV9371_ZC706" ] ; then echo_fatal "Unknown HW type $HW will exit..." else if [ "$HW" == "USRP" ] ; then HW="OAI_USRP" fi + if [ "$HW" == "ADRV9371_ZC706" ] ; then + HW="OAI_ADRV9371_ZC706" + fi if [ "$HW" == "BLADERF" ] ; then HW="OAI_BLADERF" fi @@ -297,6 +303,14 @@ function main() { NOS1=1 echo_info "Will compile without S1 interface" shift;; + --HWLAT) + HWLAT=1 + echo_info "Will compile hw latency test program" + shift;; + --HWLAT_TEST) + HWLAT_TEST=1 + echo_info "Will compile hw latency test program" + shift;; --verbose-compile) VERBOSE_COMPILE=1 echo_info "Will compile with verbose instructions" @@ -397,6 +411,12 @@ function main() { fi fi + if [ "$HWLAT" = "1" ] ; then + if [ "$HW" = "None" ] ; then + echo_info "No radio head has been selected (HW set to $HW)" + fi + fi + echo_info "RF HW set to $HW" #Now we set flags to enable deadline scheduler settings #By default: USRP: disable, @@ -409,6 +429,8 @@ function main() { DEADLINE_SCHEDULER_FLAG_USER="False" elif [ "$HW" = "OAI_USRP" ] ; then DEADLINE_SCHEDULER_FLAG_USER="False" + elif [ "$HW" = "OAI_ADRV9371_ZC706" ] ; then + DEADLINE_SCHEDULER_FLAG_USER="False" elif [ "$HW" = "OAI_BLADERF" ] ; then DEADLINE_SCHEDULER_FLAG_USER="False" elif [ "$HW" = "OAI_LMSSDR" ] ; then @@ -474,6 +496,10 @@ function main() { install_usrp_uhd_driver $UHD_IMAGES_DIR fi fi + if [ "$HW" == "OAI_ADRV9371_ZC706" ] ; then + echo_info "\nInstalling packages for ADRV9371_ZC706 support" + check_install_libiio_driver + fi if [ "$HW" == "OAI_BLADERF" ] ; then echo_info "installing packages for BLADERF support" check_install_bladerf_driver @@ -811,9 +837,65 @@ function main() { libtelnetsrv.so $dbin/libtelnetsrv.so fi + # HWLAT compilation + ##################### + if [ "$HWLAT" = "1" ] ; then + + hwlat_exec=lte-hwlat + hwlat_build_dir=lte-hwlat + + echo_info "Compiling $hwlat_exec ..." + + [ "$CLEAN" = "1" ] && rm -rf $DIR/lte-hwlat/build + mkdir -p $DIR/$hwlat_build_dir/build + cmake_file=$DIR/$hwlat_build_dir/CMakeLists.txt + echo "cmake_minimum_required(VERSION 2.8)" > $cmake_file + echo "set ( CMAKE_BUILD_TYPE $CMAKE_BUILD_TYPE )" >> $cmake_file + echo "set ( RF_BOARD \"${HW}\")" >> $cmake_file + echo 'set ( PACKAGE_NAME "\"lte-hwlat\"")' >> $cmake_file + echo "set ( DEADLINE_SCHEDULER \"${DEADLINE_SCHEDULER_FLAG_USER}\" )" >>$cmake_file + echo "set ( CPU_AFFINITY \"${CPU_AFFINITY_FLAG_USER}\" )" >>$cmake_file + echo "set ( HWLAT \"${HWLAT}\" )" >>$cmake_file + echo 'include(${CMAKE_CURRENT_SOURCE_DIR}/../CMakeLists.txt)' >> $cmake_file + cd $DIR/$hwlat_build_dir/build + cmake .. + compilations \ + lte-hwlat lte-hwlat \ + lte-hwlat $dbin/lte-hwlat + + fi + + # HWLAT_TEST compilation + ##################### + if [ "$HWLAT_TEST" = "1" ] ; then + + hwlat_test_exec=lte-hwlat-test + hwlat_test_build_dir=lte-hwlat-test + + echo_info "Compiling $hwlat_test_exec ..." + + [ "$CLEAN" = "1" ] && rm -rf $DIR/lte-hwlat-test/build + mkdir -p $DIR/$hwlat_test_build_dir/build + cmake_file=$DIR/$hwlat_test_build_dir/CMakeLists.txt + echo "cmake_minimum_required(VERSION 2.8)" > $cmake_file + echo "set ( CMAKE_BUILD_TYPE $CMAKE_BUILD_TYPE )" >> $cmake_file + echo "set ( RF_BOARD \"${HW}\")" >> $cmake_file + echo 'set ( PACKAGE_NAME "\"lte-hwlat-test\"")' >> $cmake_file + echo "set ( DEADLINE_SCHEDULER \"${DEADLINE_SCHEDULER_FLAG_USER}\" )" >>$cmake_file + echo "set ( CPU_AFFINITY \"${CPU_AFFINITY_FLAG_USER}\" )" >>$cmake_file + echo "set ( HWLAT \"${HWLAT}\" )" >>$cmake_file + echo 'include(${CMAKE_CURRENT_SOURCE_DIR}/../CMakeLists.txt)' >> $cmake_file + cd $DIR/$hwlat_test_build_dir/build + cmake .. + compilations \ + lte-hwlat-test lte-hwlat-test \ + lte-hwlat-test $dbin/lte-hwlat-test + + fi + # build RF device and transport protocol libraries ##################################### - if [ "$eNB" = "1" -o "$UE" = "1" -o "$gNB" = "1" -o "$nrUE" = "1" ] ; then + if [ "$eNB" = "1" -o "$UE" = "1" -o "$gNB" = "1" -o "$nrUE" = "1" - o "$HWLAT" = "1" ] ; then build_dir=$build_dir @@ -831,6 +913,11 @@ function main() { ln -sf liboai_exmimodevif.so liboai_device.so ln -sf $dbin/liboai_exmimodevif.so.$REL $dbin/liboai_device.so echo_info "liboai_device.so is linked to EXMIMO device library" + elif [ "$HW" == "OAI_ADRV9371_ZC706" ] ; then + + ln -sf $OPENAIR_DIR/targets/ARCH/ADRV9371_ZC706/slib/libadrv9371_zc706.so liboai_device.so + + echo_info "liboai_device.so is linked to ADRV9371_ZC706 device library" elif [ "$HW" == "OAI_USRP" ] ; then compilations \ $build_dir oai_usrpdevif \ diff --git a/cmake_targets/tools/build_helper b/cmake_targets/tools/build_helper index 2b8123c86b..4d95292f08 100755 --- a/cmake_targets/tools/build_helper +++ b/cmake_targets/tools/build_helper @@ -242,6 +242,36 @@ install_protobuf_c_from_source(){ ) >& $protobuf_c_install_log } +install_libiio_driver_from_source(){ + libiio_install_log=$OPENAIR_DIR/cmake_targets/log/libiio_install_log.txt + echo_info "\nInstalling LibIIO driver from sources. The log file for LibIIO driver installation is here: $libiio_install_log " + ( + cd /tmp + echo "Downloading LibIIO driver" + rm -rf /tmp/libiio + git clone https://github.com/analogdevicesinc/libiio.git + cd libiio + git checkout 2016_R2 + cmake ./ + make all + $SUDO make install + ) >& $libiio_install_log +} + +check_install_libiio_driver(){ + if [[ "$OS_DISTRO" == "ubuntu" ]]; then + $SUDO apt-get install -y --allow-unauthenticated libxml2 + $SUDO apt-get install -y --allow-unauthenticated libxml2-dev + $SUDO apt-get install -y --allow-unauthenticated bison + $SUDO apt-get install -y --allow-unauthenticated flex + $SUDO apt-get install -y --allow-unauthenticated libcdk5-dev + $SUDO apt-get install -y --allow-unauthenticated cmake + $SUDO apt-get install -y --allow-unauthenticated libaio-dev + $SUDO apt-get install -y --allow-unauthenticated libavahi-client-dev + install_libiio_driver_from_source + fi +} + install_usrp_uhd_driver_from_source(){ uhd_install_log=$OPENAIR_DIR/cmake_targets/log/uhd_install_log.txt echo_info "\nInstalling UHD driver from sources. The log file for UHD driver installation is here: $uhd_install_log " diff --git a/oaienv b/oaienv index f6b298debf..d9a540697b 100644 --- a/oaienv +++ b/oaienv @@ -17,3 +17,6 @@ alias oait='cd $OPENAIR_TARGETS' alias oailte='cd $OPENAIR_TARGETS/RT/USER' alias oais='cd $OPENAIR_TARGETS/SIMU/USER' alias oaiex='cd $OPENAIR_TARGETS/SIMU/EXAMPLES' + +export IIOD_REMOTE=192.168.121.32 + diff --git a/openair1/PHY/LTE_TRANSPORT/dci_tools.c b/openair1/PHY/LTE_TRANSPORT/dci_tools.c index 4b73de6fcc..8f995e12e3 100644 --- a/openair1/PHY/LTE_TRANSPORT/dci_tools.c +++ b/openair1/PHY/LTE_TRANSPORT/dci_tools.c @@ -8118,6 +8118,9 @@ int generate_ue_ulsch_params_from_dci(void *dci_pdu, // ulsch->n_DMRS2 = ((DCI0_5MHz_TDD_1_6_t *)dci_pdu)->cshift; +//printf("Format 0 DCI : ulsch (ue): AbsSubframe %d.%d nrb %d harq_pid %d round %d mcs %d\n",proc->frame_rx%1024,nr_tti_rx,ulsch->harq_processes[harq_pid]->nb_rb, + // harq_pid,ulsch->harq_processes[harq_pid]->round,ulsch->harq_processes[harq_pid]->mcs); + #ifdef UE_DEBUG_TRACE LOG_D(PHY,"Format 0 DCI : ulsch (ue): AbsSubframe %d.%d\n",proc->frame_rx%1024,subframe); diff --git a/openair1/PHY/LTE_TRANSPORT/initial_sync.c b/openair1/PHY/LTE_TRANSPORT/initial_sync.c index 918696968b..ab987c4a4f 100644 --- a/openair1/PHY/LTE_TRANSPORT/initial_sync.c +++ b/openair1/PHY/LTE_TRANSPORT/initial_sync.c @@ -554,7 +554,7 @@ int initial_sync(PHY_VARS_UE *ue, runmode_t mode) ue->frame_parms.nb_antenna_ports_eNB); #endif -#if defined(OAI_USRP) || defined(EXMIMO) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) +#if defined(OAI_USRP) || defined(EXMIMO) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) || defined(OAI_ADRV9371_ZC706) # if DISABLE_LOG_X printf("[UE %d] Frame %d Measured Carrier Frequency %.0f Hz (offset %d Hz)\n", ue->Mod_id, @@ -618,9 +618,11 @@ int initial_sync(PHY_VARS_UE *ue, runmode_t mode) #ifndef OAI_USRP #ifndef OAI_BLADERF #ifndef OAI_LMSSDR +#ifndef OAI_ADRV9371_ZC706 phy_adjust_gain(ue,ue->measurements.rx_power_avg_dB[0],0); #endif #endif +#endif #endif } @@ -629,9 +631,11 @@ int initial_sync(PHY_VARS_UE *ue, runmode_t mode) #ifndef OAI_USRP #ifndef OAI_BLADERF #ifndef OAI_LMSSDR +#ifndef OAI_ADRV9371_ZC706 phy_adjust_gain(ue,dB_fixed(ue->measurements.rssi),0); #endif #endif +#endif #endif } diff --git a/openair1/PHY/LTE_TRANSPORT/prach.c b/openair1/PHY/LTE_TRANSPORT/prach.c index b58aa91636..0c97a307af 100644 --- a/openair1/PHY/LTE_TRANSPORT/prach.c +++ b/openair1/PHY/LTE_TRANSPORT/prach.c @@ -636,7 +636,7 @@ int32_t generate_prach( PHY_VARS_UE *ue, uint8_t eNB_id, uint8_t subframe, uint1 int i, prach_len; uint16_t first_nonzero_root_idx=0; -#if defined(EXMIMO) || defined(OAI_USRP) +#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_ADRV9371_ZC706) prach_start = (ue->rx_offset+subframe*ue->frame_parms.samples_per_tti-ue->hw_timing_advance-ue->N_TA_offset); #ifdef PRACH_DEBUG LOG_I(PHY,"[UE %d] prach_start %d, rx_offset %d, hw_timing_advance %d, N_TA_offset %d\n", ue->Mod_id, @@ -1042,7 +1042,7 @@ int32_t generate_prach( PHY_VARS_UE *ue, uint8_t eNB_id, uint8_t subframe, uint1 AssertFatal(prach_fmt<4, "prach_fmt4 not fully implemented" ); -#if defined(EXMIMO) || defined(OAI_USRP) +#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_ADRV9371_ZC706) int j; int overflow = prach_start + prach_len - LTE_NUMBER_OF_SUBFRAMES_PER_FRAME*ue->frame_parms.samples_per_tti; LOG_I( PHY, "prach_start=%d, overflow=%d\n", prach_start, overflow ); diff --git a/openair1/PHY/LTE_TRANSPORT/print_stats.c b/openair1/PHY/LTE_TRANSPORT/print_stats.c index 9e328353a4..22b1adf3b9 100644 --- a/openair1/PHY/LTE_TRANSPORT/print_stats.c +++ b/openair1/PHY/LTE_TRANSPORT/print_stats.c @@ -42,7 +42,7 @@ #endif extern int mac_get_rrc_status(uint8_t Mod_id,uint8_t eNB_flag,uint8_t index); -#if defined(OAI_USRP) || defined(EXMIMO) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) +#if defined(OAI_USRP) || defined(EXMIMO) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) || defined(OAI_ADRV9371_ZC706) #include "common_lib.h" extern openair0_config_t openair0_cfg[]; #endif @@ -90,10 +90,10 @@ int dump_ue_stats(PHY_VARS_UE *ue, UE_rxtx_proc_t *proc,char* buffer, int length #ifdef EXMIMO len += sprintf(&buffer[len], "[UE PROC] RX Gain %d dB (LNA %d, vga %d dB)\n",ue->rx_total_gain_dB, openair0_cfg[0].rxg_mode[0],(int)openair0_cfg[0].rx_gain[0]); #endif -#if defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) +#if defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) || defined(OAI_ADRV9371_ZC706) len += sprintf(&buffer[len], "[UE PROC] RX Gain %d dB\n",ue->rx_total_gain_dB); #endif -#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) +#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) || defined(OAI_ADRV9371_ZC706) len += sprintf(&buffer[len], "[UE_PROC] Frequency offset %d Hz, estimated carrier frequency %f Hz\n",ue->common_vars.freq_offset,openair0_cfg[0].rx_freq[0]-ue->common_vars.freq_offset); #endif len += sprintf(&buffer[len], "[UE PROC] UE mode = %s (%d)\n",mode_string[ue->UE_mode[0]],ue->UE_mode[0]); diff --git a/openair1/SCHED/phy_procedures_lte_ue.c b/openair1/SCHED/phy_procedures_lte_ue.c index 2635649589..c73e14b288 100644 --- a/openair1/SCHED/phy_procedures_lte_ue.c +++ b/openair1/SCHED/phy_procedures_lte_ue.c @@ -76,7 +76,7 @@ extern double cpuf; void Msg1_transmitted(module_id_t module_idP,uint8_t CC_id,frame_t frameP, uint8_t eNB_id); void Msg3_transmitted(module_id_t module_idP,uint8_t CC_id,frame_t frameP, uint8_t eNB_id); -#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) +#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) || defined(OAI_ADRV9371_ZC706) extern uint32_t downlink_frequency[MAX_NUM_CCs][4]; #endif @@ -158,7 +158,7 @@ void dump_dlsch_SI(PHY_VARS_UE *ue,UE_rxtx_proc_t *proc,uint8_t eNB_id,uint8_t s exit(-1); } -#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) +#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) || defined(OAI_ADRV9371_ZC706) //unsigned int gain_table[31] = {100,112,126,141,158,178,200,224,251,282,316,359,398,447,501,562,631,708,794,891,1000,1122,1258,1412,1585,1778,1995,2239,2512,2818,3162}; /* unsigned int get_tx_amp_prach(int power_dBm, int power_max_dBm, int N_RB_UL) @@ -1202,7 +1202,7 @@ void ulsch_common_procedures(PHY_VARS_UE *ue, UE_rxtx_proc_t *proc, uint8_t empt int frame_tx = proc->frame_tx; int ulsch_start; int overflow=0; -#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) +#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) || defined(OAI_ADRV9371_ZC706) int k,l; int dummy_tx_buffer[frame_parms->samples_per_tti] __attribute__((aligned(16))); #endif @@ -1213,7 +1213,7 @@ void ulsch_common_procedures(PHY_VARS_UE *ue, UE_rxtx_proc_t *proc, uint8_t empt #endif nsymb = (frame_parms->Ncp == 0) ? 14 : 12; -#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR)//this is the EXPRESS MIMO case +#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) || defined(OAI_ADRV9371_ZC706)//this is the EXPRESS MIMO case ulsch_start = (ue->rx_offset+subframe_tx*frame_parms->samples_per_tti- ue->hw_timing_advance- ue->timing_advance- @@ -1231,7 +1231,7 @@ void ulsch_common_procedures(PHY_VARS_UE *ue, UE_rxtx_proc_t *proc, uint8_t empt ulsch_start = (frame_parms->samples_per_tti*subframe_tx)-ue->N_TA_offset; //-ue->timing_advance; #endif //else EXMIMO -//#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) +//#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) || defined(OAI_ADRV9371_ZC706) if (empty_subframe) { //#if 1 @@ -1279,7 +1279,7 @@ void ulsch_common_procedures(PHY_VARS_UE *ue, UE_rxtx_proc_t *proc, uint8_t empt for (aa=0; aanb_antennas_tx; aa++) { if (frame_parms->Ncp == 1) PHY_ofdm_mod(&ue->common_vars.txdataF[aa][subframe_tx*nsymb*frame_parms->ofdm_symbol_size], -#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) +#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) || defined(OAI_ADRV9371_ZC706) dummy_tx_buffer, #else &ue->common_vars.txdata[aa][ulsch_start], @@ -1290,7 +1290,7 @@ void ulsch_common_procedures(PHY_VARS_UE *ue, UE_rxtx_proc_t *proc, uint8_t empt CYCLIC_PREFIX); else { normal_prefix_mod(&ue->common_vars.txdataF[aa][subframe_tx*nsymb*frame_parms->ofdm_symbol_size], -#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) +#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) || defined(OAI_ADRV9371_ZC706) dummy_tx_buffer, #else &ue->common_vars.txdata[aa][ulsch_start], @@ -1308,7 +1308,7 @@ void ulsch_common_procedures(PHY_VARS_UE *ue, UE_rxtx_proc_t *proc, uint8_t empt &ue->frame_parms); } -#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) +#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) || defined(OAI_ADRV9371_ZC706) apply_7_5_kHz(ue,dummy_tx_buffer,0); apply_7_5_kHz(ue,dummy_tx_buffer,1); #else @@ -1317,7 +1317,7 @@ void ulsch_common_procedures(PHY_VARS_UE *ue, UE_rxtx_proc_t *proc, uint8_t empt #endif -#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) +#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) || defined(OAI_ADRV9371_ZC706) overflow = ulsch_start - 9*frame_parms->samples_per_tti; @@ -1822,7 +1822,7 @@ void ue_ulsch_uespec_procedures(PHY_VARS_UE *ue,UE_rxtx_proc_t *proc,uint8_t eNB } ue->tx_total_RE[subframe_tx] = nb_rb*12; -#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) +#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) || defined(OAI_ADRV9371_ZC706) tx_amp = get_tx_amp(ue->tx_power_dBm[subframe_tx], ue->tx_power_max_dBm, ue->frame_parms.N_RB_UL, @@ -1899,7 +1899,7 @@ void ue_srs_procedures(PHY_VARS_UE *ue,UE_rxtx_proc_t *proc,uint8_t eNB_id,uint8 Po_SRS = ue->tx_power_max_dBm; } -#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) +#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) || defined(OAI_ADRV9371_ZC706) if (ue->mac_enabled==1) { tx_amp = get_tx_amp(Po_SRS, @@ -2182,7 +2182,7 @@ void ue_pucch_procedures(PHY_VARS_UE *ue,UE_rxtx_proc_t *proc,uint8_t eNB_id,uin ue->tx_power_dBm[subframe_tx] = Po_PUCCH; ue->tx_total_RE[subframe_tx] = 12; -#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) +#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) || defined(OAI_ADRV9371_ZC706) tx_amp = get_tx_amp(Po_PUCCH, ue->tx_power_max_dBm, ue->frame_parms.N_RB_UL, @@ -2277,7 +2277,7 @@ void ue_pucch_procedures(PHY_VARS_UE *ue,UE_rxtx_proc_t *proc,uint8_t eNB_id,uin ue->tx_power_dBm[subframe_tx] = Po_PUCCH; ue->tx_total_RE[subframe_tx] = 12; -#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) +#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR) || defined(OAI_ADRV9371_ZC706) tx_amp = get_tx_amp(Po_PUCCH, ue->tx_power_max_dBm, ue->frame_parms.N_RB_UL, @@ -2538,9 +2538,11 @@ void ue_measurement_procedures( #ifndef OAI_USRP #ifndef OAI_BLADERF #ifndef OAI_LMSSDR +#ifndef OAI_ADRV9371_ZC706 phy_adjust_gain (ue,dB_fixed(ue->measurements.rssi),0); #endif #endif +#endif #endif VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_GAIN_CONTROL, VCD_FUNCTION_OUT); diff --git a/openair2/RRC/LITE/rrc_UE.c b/openair2/RRC/LITE/rrc_UE.c index 3edf230ac2..2a12def752 100644 --- a/openair2/RRC/LITE/rrc_UE.c +++ b/openair2/RRC/LITE/rrc_UE.c @@ -788,7 +788,7 @@ rrc_ue_establish_drb( RADIO_ACCESS_BEARER,Rlc_info_um); */ #ifdef PDCP_USE_NETLINK -# if !defined(OAI_NW_DRIVER_TYPE_ETHERNET) && !defined(EXMIMO) && !defined(OAI_USRP) && !defined(OAI_BLADERF) && !defined(ETHERNET) && !defined(LINK_ENB_PDCP_TO_GTPV1U) +# if !defined(OAI_NW_DRIVER_TYPE_ETHERNET) && !defined(EXMIMO) && !defined(OAI_USRP) && !defined(OAI_BLADERF) && !defined(ETHERNET) && !defined(LINK_ENB_PDCP_TO_GTPV1U) && !defined(OAI_ADRV9371_ZC706) ip_addr_offset3 = 0; ip_addr_offset4 = 1; LOG_I(OIP,"[UE %d] trying to bring up the OAI interface oai%d, IP 10.0.%d.%d\n", ue_mod_idP, ip_addr_offset3+ue_mod_idP, diff --git a/openair2/RRC/LITE/rrc_eNB.c b/openair2/RRC/LITE/rrc_eNB.c index 9faa686ef6..78d32697f3 100644 --- a/openair2/RRC/LITE/rrc_eNB.c +++ b/openair2/RRC/LITE/rrc_eNB.c @@ -5401,7 +5401,7 @@ rrc_eNB_process_RRCConnectionReconfigurationComplete( ctxt_pP->module_id, ctxt_pP->frame, (int)DRB_configList->list.array[i]->drb_Identity); #if defined(PDCP_USE_NETLINK) && !defined(LINK_ENB_PDCP_TO_GTPV1U) // can mean also IPV6 since ether -> ipv6 autoconf -# if !defined(OAI_NW_DRIVER_TYPE_ETHERNET) && !defined(EXMIMO) && !defined(OAI_USRP) && !defined(OAI_BLADERF) && !defined(ETHERNET) +# if !defined(OAI_NW_DRIVER_TYPE_ETHERNET) && !defined(EXMIMO) && !defined(OAI_USRP) && !defined(OAI_BLADERF) && !defined(ETHERNET) && !defined(OAI_ADRV9371_ZC706) LOG_I(OIP, "[eNB %d] trying to bring up the OAI interface oai%d\n", ctxt_pP->module_id, ctxt_pP->module_id); diff --git a/targets/ARCH/ADRV9371_ZC706/Makefile b/targets/ARCH/ADRV9371_ZC706/Makefile new file mode 100755 index 0000000000..5684c7b3bc --- /dev/null +++ b/targets/ARCH/ADRV9371_ZC706/Makefile @@ -0,0 +1,63 @@ +# Creates shared library + +CC= gcc +CFLAGS+= -DADRV9371_ZC706 -DHWLAT -O0 -g3 -Wall -fmessage-length=0 -fPIC + +DROOT= ./USERSPACE +DIR= $(DROOT)/LIB + +IFLAGS+= -I$(DIR)/ \ + -I$(DROOT)/libini/ \ + -I../COMMON/ \ + -I/usr/include/ + +LDFLAGS+= -L/usr/lib/x86/64-linux-gnu/ \ + -lm -liio -ldl -lriffa + +OUTDIR= ./slib +TARGET= libadrv9371_zc706.so + +SRC= $(DIR)/adrv9371_dump.c \ + $(DIR)/adrv9371_helper.c \ + $(DIR)/adrv9371_self_test.c \ + $(DIR)/adrv9371_zc706_lib.c \ + $(DIR)/adrv9371_zc706_riffa_lib.c \ + $(DIR)/adrv9371_zc706_stats.c \ + $(DIR)/hw_init.c \ + $(DROOT)/libini/libini.c + +OBJ= $(OUTDIR)/adrv9371_dump.o \ + $(OUTDIR)/adrv9371_helper.o \ + $(OUTDIR)/adrv9371_self_test.o \ + $(OUTDIR)/adrv9371_zc706_lib.o \ + $(OUTDIR)/adrv9371_zc706_riffa_lib.o \ + $(OUTDIR)/adrv9371_zc706_stats.o \ + $(OUTDIR)/hw_init.o \ + $(OUTDIR)/libini.o + +all: lib + +dir: + mkdir -p $(OUTDIR) + +env: + set PATH=/usr/lib/:${PATH} + +obj: dir env + $(CC) -c $(CFLAGS) $(IFLAGS) $(SRC) + mv *.o $(OUTDIR) + +lib: dir obj + $(CC) -shared $(OBJ) -o $(OUTDIR)/$(TARGET) $(LDFLAGS) + +# exec: env +# $(CC) $(CFLAGS) $(IFLAGS) main.c $(SRC) -o lib.exe $(LDFLAGS) -lpthread + +.PHONY: clean + +clean: + rm -f $(OUTDIR)/*.o $(OUTDIR)/*~ + +rm: clean + rm -f $(OUTDIR)/$(TARGET) + rmdir $(OUTDIR) \ No newline at end of file diff --git a/targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/ADRV9371-W_test.ini b/targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/ADRV9371-W_test.ini new file mode 100644 index 0000000000..c1861b2e8d --- /dev/null +++ b/targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/ADRV9371-W_test.ini @@ -0,0 +1,337 @@ +[IIO Oscilloscope] +plugin.DMM.detached=0 +plugin.Debug.detached=0 +plugin.AD9371 Advanced.detached=0 +plugin.AD9371.detached=0 +startup_version_check=0 +test=1 + +[IIO Oscilloscope - Capture Window1] +fru_connect = 1 +test.message = Please ensure:\n • 30.72 MHz -> REF_CLK_IN (0dBm)\n • Rx1 <-> Tx1\n • Rx2 <-> Tx2 +domain=fft +sample_count=400 +fft_size=16384 +fft_avg=8 +fft_pwr_offset=0.000000 +graph_type=Lines +show_grid=1 +enable_auto_scale=1 +x_axis_min=-67.583626 +x_axis_max=67.576126 +y_axis_min=-130 +y_axis_max=3 +show_capture_options = 1 + +axi-ad9371-rx-obs-hpc.expanded=1 +axi-ad9371-rx-obs-hpc.active=0 +axi-ad9371-rx-obs-hpc.trigger_enabled=0 +axi-ad9371-rx-obs-hpc.voltage0_i.enabled=0 +axi-ad9371-rx-obs-hpc.voltage0_q.enabled=0 + +axi-ad9371-rx-hpc.expanded=1 +axi-ad9371-rx-hpc.active=1 +axi-ad9371-rx-hpc.voltage0_i.enabled=1 +axi-ad9371-rx-hpc.voltage0_q.enabled=1 +axi-ad9371-rx-hpc.voltage1_i.enabled=0 +axi-ad9371-rx-hpc.voltage1_q.enabled=0 + +marker_type = Single Tone Markers +marker.0 = 9525 +marker.1 = 8192 +marker.2 = 10859 +marker.3 = 12193 +marker.4 = 13526 +marker.5 = 14859 +capture_started=0 + +[DMM] +device_list = ad7291 0 +device_list = ad9371-phy 0 +device_list = xadc 0 +running = No + +# temp between 20C and 55C (in 0.25C units) +test.ad7291.in_temp0_raw.int = 80 220 + +# See the production testing wiki docs [1] for how these values are calculated. +# [1]: (https://wiki.analog.com/resources/eval/user-guides/ad-fmcomms2-ebz/testing#voltage) + +# scale = 0.610351562 +# All nominal voltages +/- 2.5% + +# 0 INPUT_VOLTAGE 12V - V(Drop Diode) 56/10 +test.ad7291.in_voltage0_raw.int = 2710 2850 + +# 1 VOUT2_1V3_DIG 1 +test.ad7291.in_voltage1_raw.int = 2077 2183 + +# 2 VDD_IF 10/10 +test.ad7291.in_voltage2_raw.int = 1997 2099 + +# 3 VOUT3_3V3 10/10 +test.ad7291.in_voltage3_raw.int = 2636 2770 + +# 5 VOUT4_1V8 1 +test.ad7291.in_voltage5_raw.int = 2875 3023 + +# 7 VOUT1_1V3_ANLG 1 +test.ad7291.in_voltage7_raw.int = 2077 2183 + + +# Test AD9528 lock status - Requires 30.720 MHz reference clock! +test.ad9528-1.pll1_reference_clk_a_present.int = 1 1 +test.ad9528-1.pll1_locked.int = 1 1 +test.ad9528-1.pll2_locked.int = 1 1 + + +[AD9371] +ad9371-phy.in_voltage2_rf_port_select = INTERNALCALS +ad9371-phy.in_voltage2_temp_comp_gain = 0.00 dB +ad9371-phy.out_altvoltage1_TX_LO_frequency = 2500000000 +ad9371-phy.out_altvoltage2_RX_SN_LO_frequency = 2600000000 +ad9371-phy.out_voltage1_hardwaregain = -10.000000 dB +ad9371-phy.out_voltage1_lo_leakage_tracking_en = 1 +ad9371-phy.out_voltage1_quadrature_tracking_en = 1 +ad9371-phy.in_voltage0_gain_control_mode = automatic +ad9371-phy.in_voltage0_quadrature_tracking_en = 1 +ad9371-phy.in_voltage0_temp_comp_gain = 0.00 dB +ad9371-phy.in_voltage1_quadrature_tracking_en = 1 +ad9371-phy.in_voltage1_temp_comp_gain = 0.00 dB +ad9371-phy.in_voltage1_gain_control_mode = automatic +ad9371-phy.out_altvoltage0_RX_LO_frequency = 2600000000 +ad9371-phy.out_voltage0_lo_leakage_tracking_en = 1 +ad9371-phy.out_voltage0_hardwaregain = -10.000000 dB +ad9371-phy.out_voltage0_quadrature_tracking_en = 1 +ad9371-phy.ensm_mode = radio_on +axi-ad9371-tx-hpc.out_altvoltage0_TX1_I_F1_phase = 90000 +axi-ad9371-tx-hpc.out_altvoltage0_TX1_I_F1_scale = 0.251160 +axi-ad9371-tx-hpc.out_altvoltage0_TX1_I_F1_frequency = 10001402 +axi-ad9371-tx-hpc.out_altvoltage0_TX1_I_F1_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage5_TX2_I_F2_phase = 90000 +axi-ad9371-tx-hpc.out_altvoltage5_TX2_I_F2_scale = 0.000000 +axi-ad9371-tx-hpc.out_altvoltage5_TX2_I_F2_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage5_TX2_I_F2_frequency = 1001265 +axi-ad9371-tx-hpc.out_altvoltage4_TX2_I_F1_frequency = 10001402 +axi-ad9371-tx-hpc.out_altvoltage4_TX2_I_F1_phase = 90000 +axi-ad9371-tx-hpc.out_altvoltage4_TX2_I_F1_scale = 0.251160 +axi-ad9371-tx-hpc.out_altvoltage4_TX2_I_F1_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage6_TX2_Q_F1_frequency = 10001402 +axi-ad9371-tx-hpc.out_altvoltage6_TX2_Q_F1_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage6_TX2_Q_F1_phase = 0 +axi-ad9371-tx-hpc.out_altvoltage6_TX2_Q_F1_scale = 0.251160 +axi-ad9371-tx-hpc.out_altvoltage3_TX1_Q_F2_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage3_TX1_Q_F2_phase = 0 +axi-ad9371-tx-hpc.out_altvoltage3_TX1_Q_F2_scale = 0.000000 +axi-ad9371-tx-hpc.out_altvoltage3_TX1_Q_F2_frequency = 40001860 +axi-ad9371-tx-hpc.out_altvoltage7_TX2_Q_F2_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage7_TX2_Q_F2_phase = 0 +axi-ad9371-tx-hpc.out_altvoltage7_TX2_Q_F2_scale = 0.000000 +axi-ad9371-tx-hpc.out_altvoltage7_TX2_Q_F2_frequency = 1001265 +axi-ad9371-tx-hpc.out_altvoltage2_TX1_Q_F1_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage2_TX1_Q_F1_phase = 0 +axi-ad9371-tx-hpc.out_altvoltage2_TX1_Q_F1_scale = 0.251160 +axi-ad9371-tx-hpc.out_altvoltage2_TX1_Q_F1_frequency = 10001402 +axi-ad9371-tx-hpc.out_altvoltage1_TX1_I_F2_frequency = 40001860 +axi-ad9371-tx-hpc.out_altvoltage1_TX1_I_F2_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage1_TX1_I_F2_phase = 90000 +axi-ad9371-tx-hpc.out_altvoltage1_TX1_I_F2_scale = 0.000000 +dds_mode_tx1 = 0 +dds_mode_tx2 = 0 +SYNC_RELOAD = 1 + +# test receiver at 1GHz to 5 GHz +# SEQ FIRST INCREMENT LAST + i 1000000000 5000000000 5000000000 + +[IIO Oscilloscope - Capture Window1] +# wait for device to settle +marker_type = Peak Markers +capture_started = 0 +cycle = 1000 +axi-ad9371-rx-hpc.voltage0_i.enabled=1 +axi-ad9371-rx-hpc.voltage0_q.enabled=1 +axi-ad9371-rx-hpc.voltage1_i.enabled=0 +axi-ad9371-rx-hpc.voltage1_q.enabled=0 +cycle = 1000 + +[AD9371] +ad9371-phy.out_altvoltage1_TX_LO_frequency = +ad9371-phy.out_altvoltage2_RX_SN_LO_frequency = {{} + {100000000}} +ad9371-phy.out_altvoltage0_RX_LO_frequency = {{} + {100000000}} +dds_mode_tx1 = 0 +dds_mode_tx2 = 0 +SYNC_RELOAD = 1 + +[IIO Oscilloscope - Capture Window1] +# wait for device to settle +capture_started = 1 +cycle = 3000 + +[AD9371] +# channels should be off, so RSSI should be low +test.ad9371-phy.in_voltage0_rssi.int = 44 45 +test.ad9371-phy.in_voltage1_rssi.int = 44 45 + +# and gain is high +test.ad9371-phy.in_voltage0_hardwaregain.double = 30.0 31.0 +test.ad9371-phy.in_voltage0_hardwaregain.double = 30.0 31.0 + +# set Tx and Rx to be the same +ad9371-phy.out_altvoltage0_RX_LO_frequency = +dds_mode_tx1 = 1 +dds_mode_tx2 = 1 +SYNC_RELOAD = 1 + +[IIO Oscilloscope - Capture Window1] +# wait for device to settle +marker_type = Single Tone Markers +cycle = 3000 + +[AD9371] + +ad9371-phy.out_voltage0_hardwaregain = 0.000000 dB +ad9371-phy.out_voltage1_hardwaregain = 0.000000 dB + +# channels should be on, so RSSI should be high +test.ad9371-phy.in_voltage0_rssi.int = 3 12 +test.ad9371-phy.in_voltage1_rssi.int = 3 12 + +# and gain is low +test.ad9371-phy.in_voltage0_hardwaregain.double = 15.0 29.0 +test.ad9371-phy.in_voltage0_hardwaregain.double = 15.0 29.0 + + + +[IIO Oscilloscope - Capture Window1] +cycle = 1000 +capture_started = 1 +cycle = 3000 +save_png = ADRV9371_rx1_.png +save_markers = markers.log + +#look at the markers - Fundamental +test.marker.0 = -12.0 -4.0 +# DC +test.marker.1 = -100.0 -60.0 +# 2st Harmonic +test.marker.2 = -110.0 -75.0 +# 3nd Harmonic +test.marker.3 = -110.0 -75.0 +# 4th Harmonic +test.marker.4 = -110.0 -75.0 +# 4th Harmonic +test.marker.5 = -110.0 -65.0 + +capture_started = 0 +cycle = 1000 + +axi-ad9371-rx-hpc.voltage0_i.enabled=0 +axi-ad9371-rx-hpc.voltage0_q.enabled=0 +axi-ad9371-rx-hpc.voltage1_i.enabled=1 +axi-ad9371-rx-hpc.voltage1_q.enabled=1 + +cycle = 1000 +capture_started = 1 +cycle = 3000 +save_png = ADRV9371_rx2_.png +save_markers = markers.log + +#look at the markers - Fundamental +test.marker.0 = -12.0 -4.0 +# DC +test.marker.1 = -100.0 -60.0 +# 2st Harmonic +test.marker.2 = -110.0 -75.0 +# 3nd Harmonic +test.marker.3 = -110.0 -75.0 +# 4th Harmonic +test.marker.4 = -110.0 -75.0 +# 4th Harmonic +test.marker.5 = -110.0 -65.0 + + + +[IIO Oscilloscope - Capture Window1] +capture_started = 0 +test.message = Please ensure:\n • ORX1 <-> Tx1\n • ORX22 <-> Tx2 + +axi-ad9371-rx-hpc.expanded=1 +axi-ad9371-rx-hpc.active=0 +axi-ad9371-rx-hpc.voltage0_i.enabled=0 +axi-ad9371-rx-hpc.voltage0_q.enabled=0 +axi-ad9371-rx-hpc.voltage1_i.enabled=0 +axi-ad9371-rx-hpc.voltage1_q.enabled=0 + +axi-ad9371-rx-obs-hpc.expanded=1 +axi-ad9371-rx-obs-hpc.active=1 +axi-ad9371-rx-obs-hpc.voltage0_i.enabled=1 +axi-ad9371-rx-obs-hpc.voltage0_q.enabled=1 +cycle = 1000 + +# test observer path at 1GHz to 5 GHz +# SEQ FIRST INCREMENT LAST + i 1000000000 1000000000 5000000000 + +[AD9371] +# set Tx and Rx to be the same +ad9371-phy.out_altvoltage1_TX_LO_frequency = +ad9371-phy.out_altvoltage2_RX_SN_LO_frequency = {{} + {1000000}} +ad9371-phy.out_altvoltage0_RX_LO_frequency = +dds_mode_tx1 = 1 +dds_mode_tx2 = 1 +ad9371-phy.out_voltage0_hardwaregain = -10.000000 dB +ad9371-phy.out_voltage1_hardwaregain = -10.000000 dB +ad9371-phy.in_voltage2_rf_port_select = ORX1_TX_LO +ad9371-phy.in_voltage2_gain_control_mode = manual +ad9371-phy.in_voltage2_hardwaregain = 18.000000 dB + +[IIO Oscilloscope - Capture Window1] +capture_started = 1 +cycle = 3000 +save_png = ADRV9371_ob1_.png +save_markers = markers.log + +#look at the markers - Fundamental +test.marker.0 = -15.0 -5.0 +# DC +test.marker.1 = -100.0 -75.0 +# 2st Harmonic +test.marker.2 = -110.0 -75.0 +# 3nd Harmonic +test.marker.3 = -110.0 -75.0 +# 4th Harmonic +test.marker.4 = -110.0 -75.0 +# 4th Harmonic +test.marker.5 = -110.0 -65.0 + +[AD9371] + +ad9371-phy.in_voltage2_rf_port_select = ORX2_TX_LO + + +[IIO Oscilloscope - Capture Window1] +cycle = 1000 +capture_started = 1 +cycle = 3000 +save_png = ADRV9371_ob2_.png +save_markers = markers.log + +#look at the markers - Fundamental +test.marker.0 = -15.0 -4.0 +# DC +test.marker.1 = -100.0 -75.0 +# 2st Harmonic +test.marker.2 = -110.0 -75.0 +# 3nd Harmonic +test.marker.3 = -110.0 -75.0 +# 4th Harmonic +test.marker.4 = -110.0 -75.0 +# 4th Harmonic +test.marker.5 = -110.0 -65.0 + + + +[IIO Oscilloscope - Capture Window1] +test.message = All tests passed - Ship it +quit = 1 diff --git a/targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/profile1-osc.ini b/targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/profile1-osc.ini new file mode 100644 index 0000000000..4282cfffdd --- /dev/null +++ b/targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/profile1-osc.ini @@ -0,0 +1,85 @@ +[AD9371] +ad9371-phy.in_voltage2_rf_port_select = OFF +ad9371-phy.in_voltage2_hardwaregain = -156.000000 dB +ad9371-phy.in_voltage2_temp_comp_gain = 0.00 dB +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.out_voltage0_lo_leakage_tracking_en = 0 +ad9371-phy.out_voltage0_hardwaregain = -10.000000 dB +ad9371-phy.out_voltage0_quadrature_tracking_en = 1 +ad9371-phy.out_voltage1_hardwaregain = -10.000000 dB +ad9371-phy.out_voltage1_lo_leakage_tracking_en = 0 +ad9371-phy.out_voltage1_quadrature_tracking_en = 1 +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.out_altvoltage1_TX_LO_frequency = 2685000000 +ad9371-phy.out_altvoltage2_RX_SN_LO_frequency = 2565000000 +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.in_voltage0_gain_control_mode = automatic +ad9371-phy.in_voltage0_quadrature_tracking_en = 1 +ad9371-phy.in_voltage0_hardwaregain = 30.000000 dB +ad9371-phy.in_voltage0_temp_comp_gain = 0.00 dB +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.in_voltage1_quadrature_tracking_en = 1 +ad9371-phy.in_voltage1_hardwaregain = 30.000000 dB +ad9371-phy.in_voltage1_temp_comp_gain = 0.00 dB +ad9371-phy.in_voltage1_gain_control_mode = automatic +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.out_altvoltage0_RX_LO_frequency = 2565000000 +ad9371-phy.calibrate_rx_qec_en = 0 +ad9371-phy.calibrate_tx_lol_en = 0 +ad9371-phy.calibrate_vswr_en = 0 +ad9371-phy.calibrate_tx_qec_en = 0 +ad9371-phy.calibrate_clgc_en = 0 +ad9371-phy.ensm_mode = radio_on +ad9371-phy.calibrate_tx_lol_ext_en = 0 +ad9371-phy.calibrate_dpd_en = 0 +axi-ad9371-tx-hpc.out_altvoltage0_TX1_I_F1_phase = 90000 +axi-ad9371-tx-hpc.out_altvoltage0_TX1_I_F1_scale = 0.501160 +axi-ad9371-tx-hpc.out_altvoltage0_TX1_I_F1_frequency = 1999718 +axi-ad9371-tx-hpc.out_altvoltage0_TX1_I_F1_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage5_TX2_I_F2_phase = 90000 +axi-ad9371-tx-hpc.out_altvoltage5_TX2_I_F2_scale = 0.000000 +axi-ad9371-tx-hpc.out_altvoltage5_TX2_I_F2_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage5_TX2_I_F2_frequency = 1000327 +axi-ad9371-tx-hpc.out_altvoltage4_TX2_I_F1_frequency = 7999809 +axi-ad9371-tx-hpc.out_altvoltage4_TX2_I_F1_phase = 90000 +axi-ad9371-tx-hpc.out_altvoltage4_TX2_I_F1_scale = 0.251160 +axi-ad9371-tx-hpc.out_altvoltage4_TX2_I_F1_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage6_TX2_Q_F1_frequency = 7999809 +axi-ad9371-tx-hpc.out_altvoltage6_TX2_Q_F1_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage6_TX2_Q_F1_phase = 0 +axi-ad9371-tx-hpc.out_altvoltage6_TX2_Q_F1_scale = 0.251160 +axi-ad9371-tx-hpc.out_altvoltage3_TX1_Q_F2_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage3_TX1_Q_F2_phase = 0 +axi-ad9371-tx-hpc.out_altvoltage3_TX1_Q_F2_scale = 0.000000 +axi-ad9371-tx-hpc.out_altvoltage3_TX1_Q_F2_frequency = 19998117 +axi-ad9371-tx-hpc.out_altvoltage7_TX2_Q_F2_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage7_TX2_Q_F2_phase = 0 +axi-ad9371-tx-hpc.out_altvoltage7_TX2_Q_F2_scale = 0.000000 +axi-ad9371-tx-hpc.out_altvoltage7_TX2_Q_F2_frequency = 1000327 +axi-ad9371-tx-hpc.out_altvoltage2_TX1_Q_F1_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage2_TX1_Q_F1_phase = 0 +axi-ad9371-tx-hpc.out_altvoltage2_TX1_Q_F1_scale = 0.501160 +axi-ad9371-tx-hpc.out_altvoltage2_TX1_Q_F1_frequency = 1999718 +axi-ad9371-tx-hpc.out_altvoltage1_TX1_I_F2_frequency = 19998117 +axi-ad9371-tx-hpc.out_altvoltage1_TX1_I_F2_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage1_TX1_I_F2_phase = 90000 +axi-ad9371-tx-hpc.out_altvoltage1_TX1_I_F2_scale = 0.000000 +load_myk_profile_file = /targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/profile_rx30_Tx60_syr.txt +dds_mode_tx1 = 1 +dds_mode_tx2 = 1 +dac_buf_filename = /usr/local/lib/osc/waveforms/LTE20.mat +tx_channel_0 = 1 +tx_channel_1 = 1 +tx_channel_2 = 0 +tx_channel_3 = 0 +global_settings_show = 1 +tx_show = 1 +rx_show = 1 +obs_show = 1 +fpga_show = 1 + +[ADRV9371_ZC706] +debug_mode = 0 +interpolation_decimation_factor = 2 diff --git a/targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/profile_rx30_Tx60_syr.txt b/targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/profile_rx30_Tx60_syr.txt new file mode 100644 index 0000000000..ac20ab2663 --- /dev/null +++ b/targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/profile_rx30_Tx60_syr.txt @@ -0,0 +1,285 @@ + + + + + + + + + + + + + + + + + + + + 0 + 2 + 4 + 3 + -4 + -14 + -21 + -12 + 17 + 56 + 73 + 39 + -53 + -159 + -198 + -101 + 129 + 377 + 457 + 229 + -274 + -793 + -951 + -482 + 527 + 1564 + 1899 + 1011 + -978 + -3154 + -4109 + -2611 + 1669 + 7795 + 13807 + 17524 + 17524 + 13807 + 7795 + 1669 + -2611 + -4109 + -3154 + -978 + 1011 + 1899 + 1564 + 527 + -482 + -951 + -793 + -274 + 229 + 457 + 377 + 129 + -101 + -198 + -159 + -53 + 39 + 73 + 56 + 17 + -12 + -21 + -14 + -4 + 3 + 4 + 2 + 0 + + + + 599 + 357 + 201 + 98 + 1280 + 112 + 1505 + 53 + 1331 + 21 + 820 + 40 + 48 + 40 + 23 + 191 + + + + + + + + + + + + + + + 0 + -1 + 1 + 2 + -2 + -6 + 6 + 12 + -13 + -24 + 25 + 43 + -45 + -73 + 77 + 118 + -124 + -183 + 193 + 274 + -289 + -402 + 423 + 579 + -607 + -826 + 866 + 1187 + -1244 + -1759 + 1842 + 2818 + -2970 + -5815 + 4337 + 18436 + 18436 + 4337 + -5815 + -2970 + 2818 + 1842 + -1759 + -1244 + 1187 + 866 + -826 + -607 + 579 + 423 + -402 + -289 + 274 + 193 + -183 + -124 + 118 + 77 + -73 + -45 + 43 + 25 + -24 + -13 + 12 + 6 + -6 + -2 + 2 + 1 + -1 + 0 + + + + 596 + 358 + 201 + 98 + 1280 + 134 + 1509 + 64 + 1329 + 25 + 818 + 39 + 48 + 40 + 23 + 190 + + + + 599 + 357 + 201 + 98 + 1280 + 112 + 1505 + 53 + 1331 + 21 + 820 + 40 + 48 + 40 + 23 + 191 + + + + + + + + + + + + + + + + + -118 + -122 + 242 + 240 + -429 + -499 + 730 + 900 + -1154 + -1615 + 1742 + 2957 + -2322 + -5354 + 3885 + 17211 + 17211 + 3885 + -5354 + -2322 + 2957 + 1742 + -1615 + -1154 + 900 + 730 + -499 + -429 + 240 + 242 + -122 + -118 + + + diff --git a/targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/ue.band7.tm1.PRB100.adrv9371-zc706.ini b/targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/ue.band7.tm1.PRB100.adrv9371-zc706.ini new file mode 100644 index 0000000000..34a98d4a77 --- /dev/null +++ b/targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/ue.band7.tm1.PRB100.adrv9371-zc706.ini @@ -0,0 +1,89 @@ +[AD9371] +ad9371-phy.in_voltage2_rf_port_select = OFF +ad9371-phy.in_voltage2_hardwaregain = -156.000000 dB +ad9371-phy.in_voltage2_temp_comp_gain = 0.00 dB +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.out_voltage0_lo_leakage_tracking_en = 0 +ad9371-phy.out_voltage0_hardwaregain = 0.000000 dB +ad9371-phy.out_voltage0_quadrature_tracking_en = 1 +ad9371-phy.out_voltage1_hardwaregain = 0.000000 dB +ad9371-phy.out_voltage1_lo_leakage_tracking_en = 0 +ad9371-phy.out_voltage1_quadrature_tracking_en = 1 +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.out_altvoltage1_TX_LO_frequency = 2560000000 +ad9371-phy.out_altvoltage2_RX_SN_LO_frequency = 2680000000 +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.in_voltage0_gain_control_mode = manual +ad9371-phy.in_voltage0_quadrature_tracking_en = 1 +ad9371-phy.in_voltage0_hardwaregain = 25.000000 dB +ad9371-phy.in_voltage0_temp_comp_gain = 0.00 dB +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.in_voltage1_quadrature_tracking_en = 1 +ad9371-phy.in_voltage1_hardwaregain = 25.000000 dB +ad9371-phy.in_voltage1_temp_comp_gain = 0.00 dB +ad9371-phy.in_voltage1_gain_control_mode = manual +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.out_altvoltage0_RX_LO_frequency = 2680000000 +ad9371-phy.calibrate_rx_qec_en = 0 +ad9371-phy.calibrate_tx_lol_en = 0 +ad9371-phy.calibrate_vswr_en = 0 +ad9371-phy.calibrate_tx_qec_en = 0 +ad9371-phy.calibrate_clgc_en = 0 +ad9371-phy.ensm_mode = radio_on +ad9371-phy.calibrate_tx_lol_ext_en = 0 +ad9371-phy.calibrate_dpd_en = 0 +axi-ad9371-tx-hpc.out_altvoltage0_TX1_I_F1_phase = 90000 +axi-ad9371-tx-hpc.out_altvoltage0_TX1_I_F1_scale = 0.501160 +axi-ad9371-tx-hpc.out_altvoltage0_TX1_I_F1_frequency = 1999718 +axi-ad9371-tx-hpc.out_altvoltage0_TX1_I_F1_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage5_TX2_I_F2_phase = 90000 +axi-ad9371-tx-hpc.out_altvoltage5_TX2_I_F2_scale = 0.000000 +axi-ad9371-tx-hpc.out_altvoltage5_TX2_I_F2_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage5_TX2_I_F2_frequency = 1000327 +axi-ad9371-tx-hpc.out_altvoltage4_TX2_I_F1_frequency = 7999809 +axi-ad9371-tx-hpc.out_altvoltage4_TX2_I_F1_phase = 90000 +axi-ad9371-tx-hpc.out_altvoltage4_TX2_I_F1_scale = 0.251160 +axi-ad9371-tx-hpc.out_altvoltage4_TX2_I_F1_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage6_TX2_Q_F1_frequency = 7999809 +axi-ad9371-tx-hpc.out_altvoltage6_TX2_Q_F1_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage6_TX2_Q_F1_phase = 0 +axi-ad9371-tx-hpc.out_altvoltage6_TX2_Q_F1_scale = 0.251160 +axi-ad9371-tx-hpc.out_altvoltage3_TX1_Q_F2_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage3_TX1_Q_F2_phase = 0 +axi-ad9371-tx-hpc.out_altvoltage3_TX1_Q_F2_scale = 0.000000 +axi-ad9371-tx-hpc.out_altvoltage3_TX1_Q_F2_frequency = 19998117 +axi-ad9371-tx-hpc.out_altvoltage7_TX2_Q_F2_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage7_TX2_Q_F2_phase = 0 +axi-ad9371-tx-hpc.out_altvoltage7_TX2_Q_F2_scale = 0.000000 +axi-ad9371-tx-hpc.out_altvoltage7_TX2_Q_F2_frequency = 1000327 +axi-ad9371-tx-hpc.out_altvoltage2_TX1_Q_F1_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage2_TX1_Q_F1_phase = 0 +axi-ad9371-tx-hpc.out_altvoltage2_TX1_Q_F1_scale = 0.501160 +axi-ad9371-tx-hpc.out_altvoltage2_TX1_Q_F1_frequency = 1999718 +axi-ad9371-tx-hpc.out_altvoltage1_TX1_I_F2_frequency = 19998117 +axi-ad9371-tx-hpc.out_altvoltage1_TX1_I_F2_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage1_TX1_I_F2_phase = 90000 +axi-ad9371-tx-hpc.out_altvoltage1_TX1_I_F2_scale = 0.000000 +load_myk_profile_file = /targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/profile20MHz.txt +dds_mode_tx1 = 1 +dds_mode_tx2 = 1 +dac_buf_filename = /usr/local/lib/osc/waveforms/LTE20.mat +tx_channel_0 = 1 +tx_channel_1 = 1 +tx_channel_2 = 0 +tx_channel_3 = 0 +global_settings_show = 1 +tx_show = 1 +rx_show = 1 +obs_show = 1 +fpga_show = 1 + +[ADRV9371_ZC706] +# NO_DEBUG=0; DEBUG=1 +debug_mode = 0 +# 20MHz 40MHz 80MHz=1; 10MHz=2; 5MHz=4 +interpolation_decimation_factor = 1 +# is taken into account only if "ad9371-phy.in_voltage0_gain_control_mode = manual" +rx_gain_offset = 69 diff --git a/targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/ue.band7.tm1.PRB100.adrv9371-zc706_HWgain15dB.ini b/targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/ue.band7.tm1.PRB100.adrv9371-zc706_HWgain15dB.ini new file mode 100644 index 0000000000..e12de4edbe --- /dev/null +++ b/targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/ue.band7.tm1.PRB100.adrv9371-zc706_HWgain15dB.ini @@ -0,0 +1,89 @@ +[AD9371] +ad9371-phy.in_voltage2_rf_port_select = OFF +ad9371-phy.in_voltage2_hardwaregain = -156.000000 dB +ad9371-phy.in_voltage2_temp_comp_gain = 0.00 dB +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.out_voltage0_lo_leakage_tracking_en = 0 +ad9371-phy.out_voltage0_hardwaregain = 0.000000 dB +ad9371-phy.out_voltage0_quadrature_tracking_en = 1 +ad9371-phy.out_voltage1_hardwaregain = 0.000000 dB +ad9371-phy.out_voltage1_lo_leakage_tracking_en = 0 +ad9371-phy.out_voltage1_quadrature_tracking_en = 1 +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.out_altvoltage1_TX_LO_frequency = 2560000000 +ad9371-phy.out_altvoltage2_RX_SN_LO_frequency = 2680000000 +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.in_voltage0_gain_control_mode = manual +ad9371-phy.in_voltage0_quadrature_tracking_en = 1 +ad9371-phy.in_voltage0_hardwaregain = 15.000000 dB +ad9371-phy.in_voltage0_temp_comp_gain = 0.00 dB +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.in_voltage1_quadrature_tracking_en = 1 +ad9371-phy.in_voltage1_hardwaregain = 15.000000 dB +ad9371-phy.in_voltage1_temp_comp_gain = 0.00 dB +ad9371-phy.in_voltage1_gain_control_mode = manual +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.out_altvoltage0_RX_LO_frequency = 2680000000 +ad9371-phy.calibrate_rx_qec_en = 0 +ad9371-phy.calibrate_tx_lol_en = 0 +ad9371-phy.calibrate_vswr_en = 0 +ad9371-phy.calibrate_tx_qec_en = 0 +ad9371-phy.calibrate_clgc_en = 0 +ad9371-phy.ensm_mode = radio_on +ad9371-phy.calibrate_tx_lol_ext_en = 0 +ad9371-phy.calibrate_dpd_en = 0 +axi-ad9371-tx-hpc.out_altvoltage0_TX1_I_F1_phase = 90000 +axi-ad9371-tx-hpc.out_altvoltage0_TX1_I_F1_scale = 0.501160 +axi-ad9371-tx-hpc.out_altvoltage0_TX1_I_F1_frequency = 1999718 +axi-ad9371-tx-hpc.out_altvoltage0_TX1_I_F1_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage5_TX2_I_F2_phase = 90000 +axi-ad9371-tx-hpc.out_altvoltage5_TX2_I_F2_scale = 0.000000 +axi-ad9371-tx-hpc.out_altvoltage5_TX2_I_F2_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage5_TX2_I_F2_frequency = 1000327 +axi-ad9371-tx-hpc.out_altvoltage4_TX2_I_F1_frequency = 7999809 +axi-ad9371-tx-hpc.out_altvoltage4_TX2_I_F1_phase = 90000 +axi-ad9371-tx-hpc.out_altvoltage4_TX2_I_F1_scale = 0.251160 +axi-ad9371-tx-hpc.out_altvoltage4_TX2_I_F1_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage6_TX2_Q_F1_frequency = 7999809 +axi-ad9371-tx-hpc.out_altvoltage6_TX2_Q_F1_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage6_TX2_Q_F1_phase = 0 +axi-ad9371-tx-hpc.out_altvoltage6_TX2_Q_F1_scale = 0.251160 +axi-ad9371-tx-hpc.out_altvoltage3_TX1_Q_F2_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage3_TX1_Q_F2_phase = 0 +axi-ad9371-tx-hpc.out_altvoltage3_TX1_Q_F2_scale = 0.000000 +axi-ad9371-tx-hpc.out_altvoltage3_TX1_Q_F2_frequency = 19998117 +axi-ad9371-tx-hpc.out_altvoltage7_TX2_Q_F2_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage7_TX2_Q_F2_phase = 0 +axi-ad9371-tx-hpc.out_altvoltage7_TX2_Q_F2_scale = 0.000000 +axi-ad9371-tx-hpc.out_altvoltage7_TX2_Q_F2_frequency = 1000327 +axi-ad9371-tx-hpc.out_altvoltage2_TX1_Q_F1_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage2_TX1_Q_F1_phase = 0 +axi-ad9371-tx-hpc.out_altvoltage2_TX1_Q_F1_scale = 0.501160 +axi-ad9371-tx-hpc.out_altvoltage2_TX1_Q_F1_frequency = 1999718 +axi-ad9371-tx-hpc.out_altvoltage1_TX1_I_F2_frequency = 19998117 +axi-ad9371-tx-hpc.out_altvoltage1_TX1_I_F2_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage1_TX1_I_F2_phase = 90000 +axi-ad9371-tx-hpc.out_altvoltage1_TX1_I_F2_scale = 0.000000 +load_myk_profile_file = /targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/profile20MHz.txt +dds_mode_tx1 = 1 +dds_mode_tx2 = 1 +dac_buf_filename = /usr/local/lib/osc/waveforms/LTE20.mat +tx_channel_0 = 1 +tx_channel_1 = 1 +tx_channel_2 = 0 +tx_channel_3 = 0 +global_settings_show = 1 +tx_show = 1 +rx_show = 1 +obs_show = 1 +fpga_show = 1 + +[ADRV9371_ZC706] +# NO_DEBUG=0; DEBUG=1 +debug_mode = 0 +# 20MHz 40MHz 80MHz=1; 10MHz=2; 5MHz=4 +interpolation_decimation_factor = 1 +# is taken into account only if "ad9371-phy.in_voltage0_gain_control_mode = manual" +rx_gain_offset = 30 diff --git a/targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/ue.band7.tm1.PRB25.adrv9371-zc706.ini b/targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/ue.band7.tm1.PRB25.adrv9371-zc706.ini new file mode 100644 index 0000000000..bcdbf7716a --- /dev/null +++ b/targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/ue.band7.tm1.PRB25.adrv9371-zc706.ini @@ -0,0 +1,85 @@ +[AD9371] +ad9371-phy.in_voltage2_rf_port_select = OFF +ad9371-phy.in_voltage2_hardwaregain = -156.000000 dB +ad9371-phy.in_voltage2_temp_comp_gain = 0.00 dB +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.out_voltage0_lo_leakage_tracking_en = 0 +ad9371-phy.out_voltage0_hardwaregain = -10.000000 dB +ad9371-phy.out_voltage0_quadrature_tracking_en = 1 +ad9371-phy.out_voltage1_hardwaregain = -10.000000 dB +ad9371-phy.out_voltage1_lo_leakage_tracking_en = 0 +ad9371-phy.out_voltage1_quadrature_tracking_en = 1 +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.out_altvoltage1_TX_LO_frequency = 2535000000 +ad9371-phy.out_altvoltage2_RX_SN_LO_frequency = 2655000000 +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.in_voltage0_gain_control_mode = automatic +ad9371-phy.in_voltage0_quadrature_tracking_en = 1 +ad9371-phy.in_voltage0_hardwaregain = 30.000000 dB +ad9371-phy.in_voltage0_temp_comp_gain = 0.00 dB +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.in_voltage1_quadrature_tracking_en = 1 +ad9371-phy.in_voltage1_hardwaregain = 30.000000 dB +ad9371-phy.in_voltage1_temp_comp_gain = 0.00 dB +ad9371-phy.in_voltage1_gain_control_mode = automatic +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.out_altvoltage0_RX_LO_frequency = 2655000000 +ad9371-phy.calibrate_rx_qec_en = 0 +ad9371-phy.calibrate_tx_lol_en = 0 +ad9371-phy.calibrate_vswr_en = 0 +ad9371-phy.calibrate_tx_qec_en = 0 +ad9371-phy.calibrate_clgc_en = 0 +ad9371-phy.ensm_mode = radio_on +ad9371-phy.calibrate_tx_lol_ext_en = 0 +ad9371-phy.calibrate_dpd_en = 0 +axi-ad9371-tx-hpc.out_altvoltage0_TX1_I_F1_phase = 90000 +axi-ad9371-tx-hpc.out_altvoltage0_TX1_I_F1_scale = 0.501160 +axi-ad9371-tx-hpc.out_altvoltage0_TX1_I_F1_frequency = 1999718 +axi-ad9371-tx-hpc.out_altvoltage0_TX1_I_F1_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage5_TX2_I_F2_phase = 90000 +axi-ad9371-tx-hpc.out_altvoltage5_TX2_I_F2_scale = 0.000000 +axi-ad9371-tx-hpc.out_altvoltage5_TX2_I_F2_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage5_TX2_I_F2_frequency = 1000327 +axi-ad9371-tx-hpc.out_altvoltage4_TX2_I_F1_frequency = 7999809 +axi-ad9371-tx-hpc.out_altvoltage4_TX2_I_F1_phase = 90000 +axi-ad9371-tx-hpc.out_altvoltage4_TX2_I_F1_scale = 0.251160 +axi-ad9371-tx-hpc.out_altvoltage4_TX2_I_F1_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage6_TX2_Q_F1_frequency = 7999809 +axi-ad9371-tx-hpc.out_altvoltage6_TX2_Q_F1_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage6_TX2_Q_F1_phase = 0 +axi-ad9371-tx-hpc.out_altvoltage6_TX2_Q_F1_scale = 0.251160 +axi-ad9371-tx-hpc.out_altvoltage3_TX1_Q_F2_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage3_TX1_Q_F2_phase = 0 +axi-ad9371-tx-hpc.out_altvoltage3_TX1_Q_F2_scale = 0.000000 +axi-ad9371-tx-hpc.out_altvoltage3_TX1_Q_F2_frequency = 19998117 +axi-ad9371-tx-hpc.out_altvoltage7_TX2_Q_F2_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage7_TX2_Q_F2_phase = 0 +axi-ad9371-tx-hpc.out_altvoltage7_TX2_Q_F2_scale = 0.000000 +axi-ad9371-tx-hpc.out_altvoltage7_TX2_Q_F2_frequency = 1000327 +axi-ad9371-tx-hpc.out_altvoltage2_TX1_Q_F1_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage2_TX1_Q_F1_phase = 0 +axi-ad9371-tx-hpc.out_altvoltage2_TX1_Q_F1_scale = 0.501160 +axi-ad9371-tx-hpc.out_altvoltage2_TX1_Q_F1_frequency = 1999718 +axi-ad9371-tx-hpc.out_altvoltage1_TX1_I_F2_frequency = 19998117 +axi-ad9371-tx-hpc.out_altvoltage1_TX1_I_F2_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage1_TX1_I_F2_phase = 90000 +axi-ad9371-tx-hpc.out_altvoltage1_TX1_I_F2_scale = 0.000000 +load_myk_profile_file = /targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/profile_rx30_Tx60_syr.txt +dds_mode_tx1 = 1 +dds_mode_tx2 = 1 +dac_buf_filename = /usr/local/lib/osc/waveforms/LTE20.mat +tx_channel_0 = 1 +tx_channel_1 = 1 +tx_channel_2 = 0 +tx_channel_3 = 0 +global_settings_show = 1 +tx_show = 1 +rx_show = 1 +obs_show = 1 +fpga_show = 1 + +[ADRV9371_ZC706] +debug_mode = 0 +interpolation_decimation_factor = 4 diff --git a/targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/ue.band7.tm1.PRB50.adrv9371-zc706.ini b/targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/ue.band7.tm1.PRB50.adrv9371-zc706.ini new file mode 100644 index 0000000000..a23a765503 --- /dev/null +++ b/targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/ue.band7.tm1.PRB50.adrv9371-zc706.ini @@ -0,0 +1,85 @@ +[AD9371] +ad9371-phy.in_voltage2_rf_port_select = OFF +ad9371-phy.in_voltage2_hardwaregain = -156.000000 dB +ad9371-phy.in_voltage2_temp_comp_gain = 0.00 dB +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.out_voltage0_lo_leakage_tracking_en = 0 +ad9371-phy.out_voltage0_hardwaregain = -10.000000 dB +ad9371-phy.out_voltage0_quadrature_tracking_en = 1 +ad9371-phy.out_voltage1_hardwaregain = -10.000000 dB +ad9371-phy.out_voltage1_lo_leakage_tracking_en = 0 +ad9371-phy.out_voltage1_quadrature_tracking_en = 1 +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.out_altvoltage1_TX_LO_frequency = 2535000000 +ad9371-phy.out_altvoltage2_RX_SN_LO_frequency = 2655000000 +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.in_voltage0_gain_control_mode = automatic +ad9371-phy.in_voltage0_quadrature_tracking_en = 1 +ad9371-phy.in_voltage0_hardwaregain = 30.000000 dB +ad9371-phy.in_voltage0_temp_comp_gain = 0.00 dB +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.in_voltage1_quadrature_tracking_en = 1 +ad9371-phy.in_voltage1_hardwaregain = 30.000000 dB +ad9371-phy.in_voltage1_temp_comp_gain = 0.00 dB +ad9371-phy.in_voltage1_gain_control_mode = automatic +ad9371-phy.in_voltage_rf_port_select_available = OFF INTERNALCALS OBS_SNIFFER SN_A SN_B SN_C ORX1_TX_LO ORX2_TX_LO ORX1_SN_LO ORX2_SN_LO +ad9371-phy.out_altvoltage0_RX_LO_frequency = 2655000000 +ad9371-phy.calibrate_rx_qec_en = 0 +ad9371-phy.calibrate_tx_lol_en = 0 +ad9371-phy.calibrate_vswr_en = 0 +ad9371-phy.calibrate_tx_qec_en = 0 +ad9371-phy.calibrate_clgc_en = 0 +ad9371-phy.ensm_mode = radio_on +ad9371-phy.calibrate_tx_lol_ext_en = 0 +ad9371-phy.calibrate_dpd_en = 0 +axi-ad9371-tx-hpc.out_altvoltage0_TX1_I_F1_phase = 90000 +axi-ad9371-tx-hpc.out_altvoltage0_TX1_I_F1_scale = 0.501160 +axi-ad9371-tx-hpc.out_altvoltage0_TX1_I_F1_frequency = 1999718 +axi-ad9371-tx-hpc.out_altvoltage0_TX1_I_F1_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage5_TX2_I_F2_phase = 90000 +axi-ad9371-tx-hpc.out_altvoltage5_TX2_I_F2_scale = 0.000000 +axi-ad9371-tx-hpc.out_altvoltage5_TX2_I_F2_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage5_TX2_I_F2_frequency = 1000327 +axi-ad9371-tx-hpc.out_altvoltage4_TX2_I_F1_frequency = 7999809 +axi-ad9371-tx-hpc.out_altvoltage4_TX2_I_F1_phase = 90000 +axi-ad9371-tx-hpc.out_altvoltage4_TX2_I_F1_scale = 0.251160 +axi-ad9371-tx-hpc.out_altvoltage4_TX2_I_F1_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage6_TX2_Q_F1_frequency = 7999809 +axi-ad9371-tx-hpc.out_altvoltage6_TX2_Q_F1_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage6_TX2_Q_F1_phase = 0 +axi-ad9371-tx-hpc.out_altvoltage6_TX2_Q_F1_scale = 0.251160 +axi-ad9371-tx-hpc.out_altvoltage3_TX1_Q_F2_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage3_TX1_Q_F2_phase = 0 +axi-ad9371-tx-hpc.out_altvoltage3_TX1_Q_F2_scale = 0.000000 +axi-ad9371-tx-hpc.out_altvoltage3_TX1_Q_F2_frequency = 19998117 +axi-ad9371-tx-hpc.out_altvoltage7_TX2_Q_F2_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage7_TX2_Q_F2_phase = 0 +axi-ad9371-tx-hpc.out_altvoltage7_TX2_Q_F2_scale = 0.000000 +axi-ad9371-tx-hpc.out_altvoltage7_TX2_Q_F2_frequency = 1000327 +axi-ad9371-tx-hpc.out_altvoltage2_TX1_Q_F1_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage2_TX1_Q_F1_phase = 0 +axi-ad9371-tx-hpc.out_altvoltage2_TX1_Q_F1_scale = 0.501160 +axi-ad9371-tx-hpc.out_altvoltage2_TX1_Q_F1_frequency = 1999718 +axi-ad9371-tx-hpc.out_altvoltage1_TX1_I_F2_frequency = 19998117 +axi-ad9371-tx-hpc.out_altvoltage1_TX1_I_F2_raw = 1 +axi-ad9371-tx-hpc.out_altvoltage1_TX1_I_F2_phase = 90000 +axi-ad9371-tx-hpc.out_altvoltage1_TX1_I_F2_scale = 0.000000 +load_myk_profile_file = /targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/profile_rx30_Tx60_syr.txt +dds_mode_tx1 = 1 +dds_mode_tx2 = 1 +dac_buf_filename = /usr/local/lib/osc/waveforms/LTE20.mat +tx_channel_0 = 1 +tx_channel_1 = 1 +tx_channel_2 = 0 +tx_channel_3 = 0 +global_settings_show = 1 +tx_show = 1 +rx_show = 1 +obs_show = 1 +fpga_show = 1 + +[ADRV9371_ZC706] +debug_mode = 0 +interpolation_decimation_factor = 2 diff --git a/targets/ARCH/ADRV9371_ZC706/readme.txt b/targets/ARCH/ADRV9371_ZC706/readme.txt new file mode 100644 index 0000000000..05b3bc1b7d --- /dev/null +++ b/targets/ARCH/ADRV9371_ZC706/readme.txt @@ -0,0 +1,17 @@ +Common command line: +-------------------- +cd /openairinterface5g/ +source oaienv + +HWLAT application: +------------------ +./cmake_targets/build_oai -c -C -w ADRV9371_ZC706 --HWLAT +./cmake_targets/lte-hwlat/build/lte-hwlat + +LTE-SOFTMODEM application: +-------------------------- +./cmake_targets/build_oai -c --eNB --UE --noS1 -w ADRV9371_ZC706 +sudo su +source oaienv +source ./targets/bin/init_nas_nos1 UE +./cmake_targets/lte_noS1_build_oai/build/lte-softmodem-nos1 -U -C 2680000000 -r100 --ue-scan-carrier --ue-txgain 0 --ue-rxgain 5 -S -A 6 --ue-max-power -25 --phy-test -g 7 --rf-config-file ./targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/ue.band7.tm1.PRB100.adrv9371-zc706_HWgain15dB.ini diff --git a/targets/ARCH/ADRV9371_ZC706/slib/libadrv9371_zc706.so b/targets/ARCH/ADRV9371_ZC706/slib/libadrv9371_zc706.so new file mode 100755 index 0000000000000000000000000000000000000000..c060bfdc57da7c5213e7b6f4c6b576f28a9cff08 GIT binary patch literal 260713 zcmc${4O~>!);~T7DEZP*X;E2DHI`Zyq!gqSlY^eIp(wR5UjPx(L?sw}H?bMY(>O+D z6tTz|+9`M~A!)yAacfY0hwYM^_VY%M;-j6Tsau&YT zwytru#Iqhstk%J!(q(guOk8M-NytQA_scWm&YFVyzJ7ck!Iz&PzDw|Z4Bu*e zt=@E*>t=22 zPdg8%eK&s3foVfrdlnqrdHC#(33q&dsz*5Wt*_47_sqDYXO^w*^s(#X&tup8W%Zb? zhm7J8^M0AouhSKe=OpD++%xUGl$!@0+u^?DzpH=PvU|v+cREhXx^!gH?q9;IF8XTJ zhv(mNN!iA#%42I9JIy!he~O<_dgS~Uo~`>!^PvF;t3Nn&`JlO%#XkS)XR*VkZoT-K z+v7id{LZ?2^$R}gKeplX*e@Q)?YZ`wC+6++6eN!SK7N|*l8t{G@X8ZEZn<;z!I!UC zH0f90d6x~p_`l{o=Zx!?oEtxW-^`P9Q(qtU^Q~E*{*dPUJ?nw&PDS0GbYA!PRbO2@ z@RLbzp4i{%qSc|F&VK5{4(G!RQ9rS0`ReQ1X8+LHW;?enNResr`EWQR*#@!vEta_!pz_=^91;%e%BMPd)71o_)qf;WH+Rz5}Aj z`E?Y$6P<5+^*$d(p3kDR`-~`lcy<)}+oQD0uqb%;d+o`&Gm8D+h(d3RqSs4N>~lPd zoj;AzE;FOZlN_bpcSoTIdz%B_(?7pNp?^GzJnN(A^$8faXU|;%`k|^!c7k<*-Jmj4y@_Ze|o_Vm(IkpRI`9C{^s8%e$C+h8D zTh56$K6&zDOBMdv#a6mTA|8U>`MFT=?_$e0C)`03?-uRe#g_cAr29Mc)zBA}%)ZF33avTYhl}44PYf`iZ#c zVyk6f;sZ6s=Nlx>v7K*AHs!JCCz&IH2L3Y(r21>?k&k}E>mCIP>EPzm)EH#ebWjFUuPs8u<>vuY{+#&|Hr2KznRci)?RKTiw(U3hSKlKM2TlAS z6Ti)r$7+}3X1%tDBw@&;A7koO`MN|5HRZIM_Ng}Mmzr|kZOXIl&yxOHlg}Ybel7s< z;k=WdvqW6LPs|FZpgceKoAT7ol`z+g{Jdb|Gfg|3Ez!21$v^S5xQGLu{#I-Gvsv$N zmR`A%L5Yb!VDisdDDf|t{HNo3uC0r0Tbe|qfGq__HqE~Y%C`I7P_@a1Q$*SCREhY9=~u~SKi0$z1AHudb~oG6+MMvpY}-^}&-2f)=T&C=+7?K{ z9JCWZ51aV>c@o|uFSegdy)ws1#AT*DLrna(F%o}+Nq;u#r9G2hmk76sUu*I&HT|={ zi612JXRhDBvh4hnB>dc@x8>#CH+A~7ymDVbnJ+KTmRCG^+GJavXJmHX#G=|sVPDZKU*3I1K9J3rTv${NT*Wls8M)I6 zrlOj>(z40Ze8qW%lctSkkyn+MMcE7kdw>v$_opo1tagsDjX2w2{fZRb#LMWtk1`lUEK>njx=rI$EZv z%vSCzD=C^L@}{wA%Zmzqlc!H3ep=zwQnX?DG_@7MLzXX?T!zf4MNHM4B8 zugLNZ)HHe8eNkwOi%-vh`o781|7nTorBEA{7EPOBD;7X;c~Q|6Td}XGq=a5VUnwk^ zUM{`Ga(x=mH+gCinn?8`p@NjItYGGu+KDV$gEl>*nPfpYt+ZZAtLVO>ay-N@(vOOZODf7IwG~7< zjI6Y6j;Oz)yrifoswYLv+eSCq1%CZNTXu{)X)PhxeWlQhI68dU^b$n-eYUnaWwQ!P zrnG}6mx}UsGJI_dl$K3Lpfhw!%Zg?cmR6u6jrf5OMUESJQzuWGS}?2qJYPBOuex2U zBil^ZX2jbHTgQSzm`-#b#RU~5QbTlkZ5)l7&nPM@r(>}3N+wSzD=3>SETZDA?tt`Y z*ut{OrNWt%Fo5AiSE|4J zonAh976)sLfA>w};EX1-mCDX6Pdb#Xyl_&{MB(YEVLC?I*?9~kb+q*wubg1oc< z&1Rd5W|+d!qm|ajaXfEYX#@q+6%?~41zVsoAyC?h(}OY$DRLqK`Nddh$%8IJ3~6*0 zDq$;?V@_GoObj*XuQ4AkvlSOjM}L5xnvI0+1+`a9oq#D0Vi*B+ZTa+TlbJ4Kgb+$0 zRLIl~w!ET=1-=4EHK80cHjWXtVP4OzL-MXirj<$=7)iFKuD>=lf*o+}KskBufV;W; zw*Hg%clpa@Me8pXi2`o@6$tZ3(h?vytDNM8Wr@JG#Y$Q$N4?5ATlZT?Da&$UWG;4; zYioTSY%%}O|MA>Gysc2I^@QNI^CwU4Mr|rh+|tLN#)>lD_NWPW7x{6vr%brwqRCik z#oGScgnbth?qGWzFhABg1}xLWr`lZq&pPA~u!YYt@qJEP&sy-L!;&7taJ3!zlq&eH z5iIVnEBF}-zEZ)POG%NtNWq&+WPz_%@Us*?%M|=83VykQPf_r%D)_%B_>~I&zZHCq zf)6S9S_NOO;I}CF+ZFsa1#fQ2h}>NY-lxzvD)0xxO(I261UHO%d!;AK?HGRX>_F>8I&6g+*!`eZ72)@^+>1#kHx^KumYT#Fdj zTm=tDk9_hKJhoaQpGgWHfff0bD)_S_Sop1iH@84VZl!{^_Nth%NWq_@@Tpet)?N~` zmnr!36#C@~zPo~dRl%EEJ0f?bf)6V6H45I`$`ZM?3ciOzzeT}6qTshFcr{LUDfmSS zeWQZs83*fQDEJF3B(6;gK1snhEBFf)yzM84fap6HDfk2hZ|$iuJ5j;+ROpiwe6@mi zDEQ|Td>;k>l7dfG@Rum~GzEXDg3na&y%fBr;2jD+N5NmF;ByuH6a}BJ;LWW;kvmDj zU#`%XDtNzwpQYfJDfmhSU#Z|1DflZCe6@miD)?mzzPEy3uHdg!@UJTPs}%f71>Z-( z*C_bD3cgms_fzm&6#Q%jzfHkkt>AYl_-hn=qk^~gMwx9W_-hsVCIx?;f^Syv$qL?f z#33O1e~N-nQ1I6)_(TOiK*1*|cyn4Oavcg@SLpjF_*4a-tl+J^V`ir*_$L+mOa*_V zg4YzhIRz29ISM{qq0d$DH!1jh1wTl^Pg3xU6@00JAFSYKDfkQpU#Z~DTO}fQk%GTj zp|4i(<}C=3yG+5~qR=l_@R&7_}dhGl7jat zc!z?|R`7ik{2dBDS;0T9;L{X*j)Kor@OLVBO~K!#;ByrG2nC<3;72O>d<8#B!B0}~ zcPsc(1%Ho%pQYe)6?~*Qt%TLe6xZt zRPeT+9Ri~NPgL*;3cg6eCo1@21)rqgCn!C0G3 z^VcVAlteA5cOk2#_elKOdu1X;Sa1$Ew&2ryIN=yjhqnp%R>E9*glh$S6Jap5g;xsr zdctvpmkYQb;dsK;0=}Fumlokl0bfLzW(k)H_*}vXg!2X5nebVJa|GOhFqa77OaY&G zA8;4K$pSt?m`mBPL%;_JbLkLH6!1R6Trz}h0^UuSONDULDIj|9Ae>0JQNUXXpF?38aX7hwnCMgb2ed>P?w0=|_nr=sCn0pCRU3c@P|d_7?&;pGDEN4PiPY5`wPm{Y`X zrGPIYd==qR0iR2_58-?PcP8AIaE^dG5atvxoGIWF>j86$7fu%N5yG6>g&hJuK$ugy zaH4?s5$04bY!mQq!kof|n|>4RPnc7;aHD{?66TaGyiLFx2wzXQR={fr4a!Z`wdh%l#4;Y@Y5`wPm{X5%rGPIYJdALufX^kY5zZHIXTl!B zIRfrLcsSuq0iRe0m{WvsvVe~e=F}kU5by!QoDzf+1-y^&?SyRt-c6WOgK*Oc(f))v z^oJV-yp=GA{O~pbZyt>I(=mk}OI*dgHi33JE{Ckl81 z;qioR0=}1U9^t0rqWuZy6K)jnaKZ(Iw+Z-G!W{C#wF170a3SH90=}Lwhq&-^0rw-! zp)Fi3;L8bfNDEgA_#(ph5iS+*xr8|Yh4Tg6neb%7IRfrL_EjWs?3Pg}$yiR?|0Vbq8+I zVzz19n=3@&9)A^ucleHs)AUU(`&hm*BfQpOvsGNB`7<6vHmcP3`?_kuj5WX;-5{|s z2|wdD#n%E6b97@dKiArO1t2n~IvK_)6!TpP;jS`;yJ!^T-vKmuwQL&~z6T8Ky(&o` zz76Tt;?=HuTz7l)Q?602k)uXnGL@$3^PCP8|HhBafhDkrS3hQa2T#IP->AEt*BH%! zv`}go6q;VA?@pe`2Fwrsr-RS5PewZ?Q*0MVFX9Q{?4F%GiP z^usQcT}d{f)D_4^A-BHI66{8l%c}a;zT|VrNof+ZLi3z0DZgf=9CG=;k1^gQ#422$9V6TmKK-)x0toGkUelWE?HGAzS&+%v7Sh;-eTO)TK@=He?4q$*56>& ze?Zoc0;2YFWbMB%jMQ$R_BG@Q0fq~{6rq<(h0%IHf0?oPQXHg{`=4)fj8I^<)mhGsfR&s-^ z56}q* zIbb=+`Cyt=bqKDT1QWq^w{^g8FO2fAR+4CN<8oF^|J`U0Jcn$yN+`7nJ&QNg`$t@i zYT=4|U{7PeY<^8|Fm8fCTByfHPy|qVkJs)#V#Jd))U8%Q?lm|*((eV!Zg1d%NJt9Q zdPDI9y!vsJ{ijxS2t=!(gV&zbV1|ZQ_c;^1y3=Xg2J6!Cj!Frjy3s5hCi>5)itdw6}{%>Q1!VQ*tT!#$Na!^q?Ws_Gwq3J&|DD*wTJ^n4U zO%0(gAzHSM4m2!6;vXnYXaCy~hQ#kN>*%TWKo0A!`qt+Y(ZKp^C@-V2-e*R`yTF`L zu~+|2shIu%yL4A7_Ue1BXh=2d?vJ|t4Ty$A_Q2<;UjLC54zntZ1VUD8h4q%E`@A}R z)YA0GQBmPb7-~#S8__~Ke9G92(saM~e-gE!g#M$kk3LPUHroTU;jl8|{tc|DS+t6D zSw32BFv4$DQtVUSqHF3Lnf!MP)`c2(Xk+OQ{_*&NbFG&NbeZx3P8X5b?qB!yG&o z{;&+o-t6Ec^yyw_rlyZ{Y7@0!*WMfsG<~si5wcT0(5he?w=+#cv~e10V}kQRk;u0a zoFNGerz#@BIRpIVe#TzauO9tZdtf}%!LiOXZ)l=3R|}OmbF|aa@bf*o%D^x*7)hw_rZZl@fIAYs@A^bCh|-l>&&hAhJl|W`u!h`5nxbNTk(k& zdd^u1LDppACj|k$T??&nR$|QX!i-RT55jfYe4A}O?_bhiLhm^%$rF?qTa$$gA$j<% z7$m*=K&SMm4o2l+dX&#O%Nue#OT8hlbCNeS(wVOzxcks%4lfLwF~EA6 z*P^k%(AdEnJ~%qH8I0&+cQ}dtJ~-`x!Wc%30PBK!$$(c=QXtt^cv0@wO#vaXC^$>x zl4&Vq2DQ-_B#+vj;u_Yv9iow1C1S6r84O`v(WmqgQlhL*6c$;x7*kyd}PgL~Vd{ z<%&DCh4plGH7cz3K#c{E*sC9~s#^ndQRd8p?;tZ3;;f;)X_>=^T3W(MFrE<)8=W3c z`bq8oQb^6DJXMINX>uMWcrx;&9z%>6$e0>0tcH=7zS~F}C=)l6c zzGeyc`2QSVG0;SbuX{yxR(ySmm1ZQq9y?&g*Iy2r@udZaVQtZ4C&*xw=XnXvN(lFc zbBg#g^Owl{`Br|smG8R8qaRa-yj=<80G{Z4^0naleDt?%2E8pHz@P`)X^fgoj&^N_ zG;K4wTXa_ZFuh17MY>u^FEi7ILHn6A(buul>j^%?YN1a&K?QxLAz#6C>mI z#y<_Xc1W7U8(QqF1SbxO7$L`Egw!$Ki@5~HMT}%vH+uE4P6zsJFM8ZGj*H3AjNXVr z(uY;(SU3W8bV5fj)}OPu5Cf<0VFS$Xj1ttyxJ?9Xj*E!fO~yc!L)>0NiBWKcXeD!K zTqWcZqvHXi3kb8SeigKGbbJ6EeC2z>9-_(|d*IEkv|NU-0w$XCy=YA_FC0%imJi=i z3OyW5(foCB?$E<6r%s(Z_L=LlrYybAwQz4GKE8c!{gW(xt2Z>dL*3y-*TTk1SjQV$ z<*Y@EyY24JJ$CnpS+*Sv+Z@gRMXc60tcBbO9kdLNKQsGM%^Y;5C$O2L>DM^3_2WVz zF=rONE2gqPe8uQS&Zd@unr4v>OS=}hB&nij%muCGCJV|A|uZ!w-_iBRf!VBpq|SQ2*^BDNS^ zg}#^^c>Hr5m~9R|3;xVik?VaCNY@}=?E63pD{f{%6Z%vd`+|1kCV>4jOxw-4nE?nA zyn%~V{Zn9ZdYtHHoX{H)Ig$8aLbQm!T?v(!_R~%!&Rk4w-KZ^^|HHE=>(96&)vo`8 zO}qY&a(~SKL3pPB(}gmj^ImK$YKSyM9$IXH!{`~7Et*3J1hpVA;bx8sFWUAE&Sv|O?=(u_>DV=|1=j4>|Kj+axND>^qlimT#a;kAqF8L*WfuXC_a|U!hmxnFq>^I zclrQqmlW$cahozbV#{(fNAViu@xw(%riQ?rbkK=$$t~R3<=+%zYzJS6)fw|@v5m6X z=p}-0N&?pgL(qV(^i5^|6pM9o!O|97j5&MRvhZqeuoPQ9$BZzHM3eGSS}pjVkjij_ zN(5bYaAu-V@NW>NeFbw_ZAfbEI#WZheo`t~>^4<|wn8IJg$`aN+egvsxL}uhJ!Yw< zUc*G7O@a3xHl{$@cJ+F(QF<)&nu*c(m~oD&SD4Lg=~V(M;UNa;-?u|4V;R}6cDe7m zn~&1E)+XP;&K|=|!sg6`ynBb-2*Go^Zpu8{R$Q&ddo9W_hSFkDf!S}L&H0X;C#}KM z3f^$a$VE+9`q!hej^jp)GR}Q19J|H%Y|oitpMZNX?S;M4XCFo}b6-AMf4xE)VTl`K zX?t=09$2&&=K+-aLvcO^glEP%25;^bvB6#gfz^HA0`>I`B3fPgBP&3GDZ&@<)U4oR zgtDqW@bwl0G{@#@*p1g$LBOjG4$o^1IG)}~_QV~k|HWE$U$?YWN3&A^{?4}Si9WtP zJ1s@w_Utqk<^GVJ27vHC+UX$>$YoC`bv(2YR?;6@1+(ZIV5YaAb>%$lblIzp3DfQ| zdci;kZ!*X0iC7?ny4{OAeekpXa2`)^sgv`1vvYK<$F5e3h#yYYzYz`poRjMdY)$kO zyKHW~$zHV+u7Vv3u1u623e(TPy8yhgEN{!UJESAq#bA5zudSPWY)lOssVMgW8s8Iq znj(cKK@?N&66POA{%n!|l*ret{7u~33(vqtv+O5MZ}&gpPUrv9?jQWSrNxc;9M&wE zXxsMZ^#RDu_8%Cn{lTQ&_D8ut*8U)Dul*4sZq6Z$Cr|@-+R%!reV~=FZwJ#vXHg4W(``QT;+H^*gi{w+u16x)Vy7PB;UM z=`G{GLiP;E&y^O2!~@WMH^{3QV8k+1-NI5NyI>>NB<(5_c$ZFq_N;HY|uyPM2f48+hlpCC_k2k2V>>=0O_h76*s#4bulQ~gRrab2S|2vAX#5`Tfd*6j1P@26t2N+#@4WN z{MX?Z;H}lTpD*-4ZH+UQ7grx#YR2Q$H);zv*tR}9Nb~;~6TZ>X7oL9-u~-FmM$_lQ zPD>CW48{j`3y%;tD6fTIV~1uAvqGseadFlB8x^n4p;6!^OA%u5lB(YbhkD#;5@Kj@ z)$rYvtDo*n!ac?mlW?ab>>~;96@-sqh*^CJen@z$N%)#1Y?(yHsU1Ys14#HWr~Dwi z&?H_Dxn`3^J^HvL8!E5<^;e@LQDg6I>+43 zz(S=FlD`pysFvGszI%br66npwy(fj#Hv@HtNjXfSt_DgoQ3C}E5}l8VZz1=~O^Iat zt8(!L#8R$PpF*zP>iG{-zTV`}h+I_h(|yu11&AhVoA6y}>%GhhrB)+&T{ZoITYU{P z;12;IkG+bc5KLJk3Cm9>++&Q9y5xaqnIyVZ5cMLrO9i(*;yeRd?j{hGOQOCY5{(j} zGtRM0a2`ys0)Arjf&y#{tdmszc{<9}|6kDmtJcvB^LZSmxL9n!VxRlmO_f}dVNwbf zay+d$h4xf#TGqf5RM;wt^EW(5TvLhSl1q${3~@vkn9zE^@N|B1G!kX=`almS^|%LO za_YQ*EfC7+P23RTf@{g&xCfqNJS18x<3eU$DKh6yqBily7Ul%!mP%-iq#FYR8mD%O z98KmdrvnD(=1AxX3FS&CAfbE-%^(!Y*Z}7<)(9ShWX8v=IFzxT883(oA)(A{lH&iJ znE{!(Evp0W6j8`nsn4zm;)tM#zwHSJhMhDX!5%X%+znk+oAXMzU+gzxA{*%bACkb% z9{5x&Z|>`pjZ->-*8#Y&V=CIx9&m9!izWJ^`A950Zd(~=bLk)3A4vh2#Xd|E1Uoc+ zr#30@BHpf~nn6TAZ8kKWh;CU_;l!`@hQ^Mch?6wY^#hOsy{wiuite{tQ!z8#Pl zgv51mao%7o>YUi8BT`+M>YISnc}(@|jMT+UUELL_0Zd(cb{u#of^Eg$v|#_E)ec*B z;V+v0j*VkP@SR;$KUhU5_|$R$b^GGIg(p&Wc#h$Qm%zJ&Rm=#6`0J``jL$CIQnxo= z3k`lcWrw$LyXV+X-k2la^d0txaqrXS4n9vpvvA6e;5!6eb>GC-eTSkay@l9_9pE|E zybotA?;s?qzUAma3`);#56gqH~FgYSra-B9Y0 zA~KFlWKMvKBv^__*@R4E<~9=*eh^&2@~^;J47ZY=wS^00W+gH+|0f1Zz`sYbhKj6= z7s#z|gj+JOj27j5AMbV~X#Q^-McA~IHKW7XZymaD-Ni{E<6qDI$R4S#JI9QG80%4R4*f;RqgQT zpW6d9NC!uK^!`L}@Nej(k6KkXe^snjn7vA?+uITMC>&bfk9fDZm$8^*Wbl&E5{JzL zshc#ve+w)224CkDJ>WK881pG-5gjD#EgbbxwtluZ_-6qH-xe2rRLvcso)>u*Zn8B& z*7UFJ{&gs`Ija^$Y^Sr%PXERJNNNHJ585C82V&l<&tL9ZhXG(gw!SC)H5|OSIv8Az zvRcs38sM6138MO1ub#CCK3(@coc{}N;U~9+1~2vMqpIQCp1wQbQ_q7CEXOS~YPNHw zm$gg_W>>GFXj<^?SD6`8=k2?}6B@jjzG^%UGUNzVf?10+sfMNt%t9!ElBRCB-RlZv z!zPCyWOn*y`@`QNfnH8gQrIy&y~+M?C*)}h=hu*d{o&)N8D^yb5w+aGKQ9N&kjpOo z5Gt5C?%-imI*w^jtbR3;j9yUz3E%+58r}?qFE%u+O(LSRp~9vs@1a> zxzJyrDEPr@s0xM)z?jviTkRpZIV`r!8ywZfYMz*_THmcSx3Jn6K_+Yz%w7aDSN9%ZPYry^Ka#W2=p|(G13tPgvcPrVA|dn6}jgCftlNXnAjN{&IWZ(oXclirV3! z!J_vm&EoS+y?6lFgp?h$XGa&Mq{NLGeNZpG?_d`T<%+y24Ix%mH@O zkMK3X@aZ>TIk@fj#{PdseAN^4i+}zWoGE*qV%wLTfVTz5edG>K-Qu?Y{U8Lk2fhQY z>PvfoG35?sBXiuAtk453*@ZhjaDA8myG~h%!Vqua`R9KW6P&+Atg&wkb?+YPdW(I@ zUyy;_6no%%)Zy}>*PuQ&>r9)kHJ`C59~)d&3^)|&6q_Bt^Jft#d7*} z0As%%yYR;EmY8qC72r?j*%eJC+gYD zM7KC|D|;I`@&~i3wS{$J6x9U=Lqzxj?s1ysD{9@rx0j=ilpSrlI&@nu%qK-Z_^Y?D zPRu7ii%kB`6LrkTxPHJIp8Fo*2;+@8h9UYr-7I0%C&mX1 zb6Vk62%(Ioe#+&RLxh|cZ)3+SC&r$_pV&RiK3_{eGP7LMPl>TX95)$<=NTY^Ip?97 zIqd4@3}{>}r#ZlwGlcM!5KEkE5%U1dIUgNQlbEwkuP!DurcsTdP(Hl;SL`pHHs5gv zg~*)7Ku(H`u_C*08wBUvfP}(g_Dk3Q|0$C6k>)6wVoW;I+b1k`-rJ` z_&MZror+B3cs;P-))IbLU{hW9q7hmr?4C%eVp(bs@{Nrwh0(u7Y$l_kqod7=j3gES zX-k;Lq>=ScWXT!(@6btRT_ky(LY6Tz!YdBgHuH5nL?gDQ6O1Db+y~MdW}PMX5XOw# zXneg?_6& z@8hMg4dUduQFb=5L7Eem9#z(}*eHe0C=*}+v{5};?unLGd3O=Jnb@P~ZtrJLf}1I5Vm;0|gfi9@TLe6)`D z!+8a(7N$#;^ei*&4Xtoio3xcm{vtDNoC|xZj7emCys0gcoDB^&R4K{A@T1LCBRykMn| zv$^c=2Ar3GAW-X@JUGV7BYY_CfsqlsxIX8sz9PovOB~#R>BRjpI3L3tHrcD7T$76* z4UzvL>?ZD#a3L-qmq^7H=^1_5}_& z|INnv0C}Q-lr~?6C-gkW(3C?we{fq9wrx;+1B!ctLz?--h+u6!mTw>J}{Mu%ED@Xf{=2o|0X1!Qj)&zK5jd^-vxd)_xYkMLnDUp_9_I-e6O@1NLK zeBLiO9h(K1&wmDl=vR)~16z?sZ}IRp;zFr)U@XKF6L{kX)Q&;Gft}sYy@KHEY}7mo zXz|33ebKYBycmxfe!2&DLdHHxSHT^Qtuu4++rxmz&b)!$6_%i>vrvI@FD%Fk^xd+K z)YLoWP3-W?m=VbRnT)-NoVAyNqj7jW`lk2f_#Mjl6Ywct8oP=7tJPl#BZ+=cq&6C3 zkd1qB6X6E9-F~Ok;lJ*IYwERlWYB8RIEW$p;SEvx_YGjgjg~eY{O{`o_l|;l>M+T+ zO$Yxvf+y0!XWFc-9BUsr4GqMbe0`vw4cDIL)nQ(pyxId_VYP?`^XNa@1KW`nXC>cn z#Ad9~4}M~8`^P1KESS-2zQf$q2GnEkJO?h_)`&~$m$-O?@z04%w?pF6;}vna&?b#x36p9N5g7gc(CAc1vb?_2(-gAPOaW^`fNBSqTu% zW(szjv^Nee;wyl4;$~cCH;F_(k*NOJchIl9s)*9 zcTv~Ke&TCp?)5^u)bHBnE*80SvF&+cJ=W+p<2J?vo@FA!){V1GA7oDj7IOAvjzIgj z#^H{BPoqTiQLW@gmoq6Hg7Q|Aat;1SrEPr&u%^2aFs`z)0NB>Ynpp<_uG+WEk{k zoXHT9P{w#5alSXfc!C+0h#i5g+m~SfpRJ(5K5ti}9NDH`Zk%iDxxN!fTw;sSxQ9j2 zM6SC1iLRPnRR`~MEyU8X#kTs5;Fpu?zEA3cyVm%2b)R+EwQeHJq&MRk=3_?Psb07p z?5aB+@9O&#Zr0*_G_bC^6TS52x)bs4zB^p{&SPP>{!>bAmi_O0>cX-9y(qE`CxNr{ ze**Q3tFF0gR^KmNeVfp9r_eWH5$o=IOdsqNU4wBDjml@skn4d{(Ir}p^XZPE)HEQ( z>1lC*d5FU}B|YF8i%`>t^fAH`nb4Lv+1M`ezvZ-Hl4h(XUby}tWPwGd@i1MNozDx_DhcSsk?z#l%f8Gl3C8!YETA~sXdV}@G_ za{^qbBF`jgtsmJgJFvKI8H^RK~dU zbr3@TJ`w`-5kk*(Xd(IFcQr-d34T?zE<7!{=t9t9MB;;Pq15XjV(WN>Tl(>T$_DvJ zR3m9H!o+_Dz2>@^T7i^3cs@A*dWF!VX@Kli*GsPPWx%;OLFF`J7N*fSt%Cz42QL@n zO2ry*jla2l8i?}~dZSixjE`Y(7rdS(YO=D>d%Pp?m}sIBWc{dQJI{@%9|%Exeln{l5B<7cd7*SPzC1YKgvj`cRQxN(Bk6~0nx z58MW&*$ltHTwI*ks}4eIRR1W8{x^ymCny8y{gueFiflGsTnl_C<4F*1_Af#fKb!sB zu%hKQ`>P|VrAVP7;Fd*F&mo1S0dUJBsh5$0h`_ySri`AH9=G+sg%tFYYlOivo?_o_ zW1hc0O*gDb9-c=v9(@;`MVj#>2Z&I&k+%y2e+((bfF5}yVF1L(dWNg}mV>J!>lu61 zx4#R|iJyQf+VO+%1*g>gFnST+hBEgCriziWKUxYI?N#^Vm%ehn$_LXF)1Shhpq}FD z3LGFwjPIz@3~r@)oix^bCIw-@yB94+C#V?ifE4DBp^U!~FODf3eh2siZRA6~3nR+9 zpIPn`=5Iwlu;;N9kS)e)VpmCQKg@q5ua}7ZFNxhA!G?%kB(V!4*qOwZ0c&(aDwu{R zwbwP1_1VJByL0$_5^;)CPS$*C6}&Kfi)3^?0(RXmq`#Jo`jS!oDl*dajq9)@(_%EB zwSqVokh&NRsj!(!HrzKyRu~$2tg~N{mV+a;#-lVt8VhYiA+$g4muarm9}N&sX~mh3 zCgVsU)(?2-Hjz`WM6keq0{Q@)8^$BO7%jVFB7kI14C6dH_A{DV9{pO4c?_1fcr=%V zA`>^9dOSDAXj+2@teX?;Pi?TjyW!X_9u7^zQO)k6){1zzvbNFa0uw0r`&^??l}G=#nq#*p{Di}dfHSPqo46z|yi+CL9(I}m3$9^8R0F#(bqudjrN-W5r~kt^cR zsSEa%Q+UJH%t&=w6p1EKf4t}J2voBft|#XPfkZ>4x0vlg$YM)hW{Lov-u%pJQT z4!6-3-3lM)c{kV+Bd@(`AYfEljegSHxG;Pm4_^eld5#-;aYUtF_NpfIpwP`3V_&i# z$#wMJ@Wmi5t_B^)U`h9uq;r7oucYG`9X^Uw>;3@fIeJU_5=q|^^aZ3xW7xtQPp7AX zlKx6bp9K1gkxV%hei|2RJo8}uFb4k}s>x>JYb7?qsT0;Gg2Vec_%WvbLzuufo1>%y z16j(BhS_~;Tjgt5o4ci9?m2OZ4M%gg;ryjHF%ic&cOf-0(b4ctZX;4-6O$Xh%r%gj zn3&nHGq(w;lEj>b4|929)0dc!-+76pn*Ts$!;oBf%?bWB=!%ySLVPEnlJP=Qyevd((=Vw>baYgsOo+;dT zz|{Hv6*&GMysax&?)e~IEr^ew^tBa910Fs3*Ov*LCjY~0c`69goY@`Z3>t`irEnWJmfSv`An+@4Ht>RW4d$v z)U?ng+tE#UL)|_Wm-y?4M^;*R{RZBc<<%!T6I=^7mY~zd10`;7=KEXpp3c>o5PtnF z_~E&32Ook>!b^faWRrbsT!w1UtIP*mE&=wT{M z)EGG?adr)ew+ZAH6!`(&9!NiEQ-g7ZNjz!`h#Lj*E)c&jiMO^T{uR2BWZW(gHwj_` z#7{`#58D!dU=kM@F8^;isQDv%RX6s)3+owB%31?L2P<^5`bty znQuHRi!vyGyiJLbeE`}ig`mU(`o=_AiV<0(mRiw%sTN~^C?Fz|>Y-evTFwWc{fNX; z!1(pAR8K}^8Y1!*P$MD-iA(&pp=|<;F)2KuH6{mwH)2vmYTLj>>UJh$#(sF;(0BEFclN_WGl$77zhTJu_#UL(IXX2S*Lv4&Vy<#qg0YEZ3jW-W z@4~4sc)i~?o&Z4Qr41L4@4xpZl(~BJCEIbWcU_VD3(QcSXgne6IoJ=oZ{O7y`1>`B zdR928Zo|Rx@vpI%e{{EJc|9toKx2}Ov7*$6{dljge(#t48bwVni&3fJ!|@qDQhzzR z|3qF#BMeo z_%AfY(SBHTL9JlFCbP1YEOvB!k4`MsWpu`|bX@(p8(_a7$;N6?YG*&ZZvzJWrr$2J zvRanfIX-neslOTByN=f{b5Ehv+%&__QZOBsi>PaDzgn}dZ7g;5_)Gpw>OrHs1#zu+ z4M1bfJ*P0!@QPx7*;)lpnyt0M*{1;H{_))=veRzM&rvA@$G)toDG;)m#~1NDspf26JsXa%^Q4Kmd34L-kRq9e{#V+XY=0kCgTp$ z@x3hAKHg)b{Ko!Hys+p{$TXisC+i>Gst z`)(Ys`R+qJeD}L+B&JPR@bayw2yllMLZ21xfMK}RKAvE90_>cK*-uh-umV$v`WTOY zeggCmZ-c`OtfC7LiCFmZZ((a#rzP6(f>&IVuypeRmmR7|^7tQ2z>%!%gzVs`#O&aF z8((RIsUA8^mVc1t6Wb|Y0-Ps!J7l;!5qy$OKJ)SDqo`jkzb)p>`WLhQ;80Y1JIiZU z`DDIV2leB9bCLS1+bO?7)ISvUi}Lwac?=>bPa#^%^Tz+_`hO(KKZyE=vi@0C`FyTp zP(OCzBK0S>Q~n2*4?R^4m(%=nXu|Q31-yduaWfL_MLEC%nKa?Yg05{7oQ2hc7Mzpd z4c?lF=IfLle245j3mZ87b1nRhd(^Gs*aMuai8ik2BwE%tM`~pc{K%b2*|LFsNPB`= zkY*HwpxaUu2OqrcW4yE63}W~X4(@Qs!7FKoa0HjHq!H6~qJsv1m*6k=;H5_Bsei)0Bzx+; zxYpMVfJ)D>f*VjlG31{Ci#-VWakG|2?_-9%U+_mS3I53(ODX?e7v)#^qu+d-{LybN z6#UPF0ymQX`GS8o<`3`@vqczR4l2>dxPtuIt%AR1waCIV_@hNq$R90o8Lw=SUFb`~ z$Iv@reIVt(8~oASLIt!uTYTexk-xNmz8IMy|7BB9W1z^}lK$<^%c4M>d0!MhA_y-kDlYjNiV=^>fe*2k=KZkpH;L8?4i z%s!Et!kKO%XS!oJ(;dN??roUqCdOjW#H+R-6%s3)H8SzCbEQn+)iE-Gr}<@Kv2&SB zV21z+j9ygD3f=Hh=*9hNj6V7%G5Tow=fWZ|#>YHVxM^sASm2+=oaanOg%sWXk__9L zlYqUP04xRq6M%YbBMnKKaTewQLy|TP>E8jwQ@Bype=Z2-q*vUCbRBaOksC8}ARcQj zci7(@(mzlOY`LQ%**~ODj3r*{d>X}!nS5%xZpKHV42s8!62aU0z@|wzV~Yqjhpmz< zbKaIH9(oSl86)P+XsW{*uz=nD845g!BqpYNj3*F)Ui}J9pMtXvhg~hBa@$B?&htm; zJ24(7rf;3!4PBqu^R9#=iu*NP5e*5(y?-|C!yRl9$b1oQSN5eduR~pk-1~SnAO3!Q zy!G(+3rIok-jDN&hrjOx)tnB#jz~Ak$HQkLN8iOKz>AQwo&X<>G!C~{m9$_NcB6O* z{BEH7e#i!LBh~O>zoSA}s9VzCx<7?Mh11=y!+%2LM&>83yQ?tyaE)~7KaUz=Uot(0 zCq6p4L({k_ya2Ys%i8%iWntU3$i>obf2_;?{uUfuNc4oJ#Tg#(bEkh}4_uFIad!U+ zLH2u$Zw%hM+`-o$Pa+_wQn$eWQ6fPsUIoLH+O-%@agI;26Y*{m{~5%QQ*=-hr`6&j zbf2MLFzLz#ohjhybZ?T5qCJCJ%mTNnjQONuglnbb`g3SR45YmXbf%CIit&=7IW&Ts z^vzgwolY@WQv4Pg5etgXKoJqGRs0J~v8pDc2_rr)_N`NDw;%SM*E-+BV;$QMR~$dO zk=wBBzgpjajMp=GgMY6Sn!Udm7tA$7s8HMNc5*Q*5{XtKi3y|sGQ8VEoMkJA3?juH z*<+$%qv5Nrp4-lEss?fQ;SGQZP&@A*E2rs6n$(12JEPP|QjB)%eFVLW4Q& zYw#r4aLY9j)jtLjp&1^+63w#N-;ApG(e&4wcnQjkpud)ozY%H}II>{?!t-q`L?Z+n znY-C9PH2cMK>-Z}+n_j@X(XX)_ao({?nerJ=$t3R@U%HzaI(iJK^~9cY^s0Im(~m=;1Q_K(=ypj>cCgRda&avG@TW z1W(LJhrFrqNRAJ02H@?S)_6w2QVvGg7c!QkFzC0BFzNN}k7%WjR6i$-34QSzfNF7{ zI+T&a%1R~u3!qQgA?dWLFMa33Zfkf)VmTm6ejW6NV=vLf7Kcb-PrL#W<6ujRG(>I9)brPtn73!xY^`C%J znz&N3?hfMpH@3$5WQooMI-u~lOY-;zl*&2Y!6cR%fPM<-t$Wj#BPW~BAwSmzGV*E0 zhmgQjp*tRZmyg9`B}j{SHdk%Ig&#aHqbAUhMohaO0KZV-gAZ zPQiRaU=H?&Quw!Ia+&55w$#f%As~Q}c%40*dX|XqjLT4QN^O(9?vdw^i;g#7;LaG0 z%30ZFvAR$F?GA^n$v?Cd9YVJzq}{=SQvOMpMM06zcK`=7K1T*_K_r3=h9g(-j#55X zwy?PcG&6h1BRQTrqoZf%-rM!X^~*sg3}@U8v*9?My!mHe^eCd#xLlY8kAi^8yy3K& z+xBy+<>+t&h~8&6Vbri6!Eyv@W9!fOljv4PI!o7ABA1xWz;Hmh16EC`{Z*P1Z?>4{ zoV1!W5Z0H<;cpgBz;V-c4Yw>rr5GzY5VRQQF+5m8-5*g>4FXM1z`oNqN?yN!q9edA6cmRlQ!5eBw0y6Rqwr z<@xOldCJ@2^U!I0CP+TnVs=d0T%-DNl!uEZ%h6NB#yn`lUI31W ziH+Bp?*>74c}6XWGY*0tkCmV8){h(SLtuA$v%Tt0bSvzKmJ9?FSFmGN=sx_(nQvoW z#goSqpbEPCimq1w$~_=FcN6n!2YXd-k$K3c@m@SObVbJxv8y1qHIBLUmz@Eso*vfh zw|`Pw3gn*q+;8omb-WP`U8FZOEJk)dqfwn;2Ko0xUp&#{ z8{|d*m&8B5Ly5qxp4`lV*pq$$+&;~Y722iLX6xHi4vDR1qZ@d6{F`Ik>5$vLT2<&Mc9)=#mVOwG@Vix(D+ADZ^y4Xqq;&>8 z)<&a?!OyC_>PF5SIr0xf!jt~BX)gz?bJXGp+hvNX@E5M|o;$%5PZ@oSUP$tR)e{9D ztiv2k2+x7#@jRiFQIs*g#bQfA;bE4~&?qT~j3p7SaExzHnbQ4?M0Dw1Oras#N;fpZ z2M^fu3^9D7A`vfm&yHPe+L&%(wuWr%$9Qy%tH!i!eZLzcKzDDjM}y|T+k$>;%8psX zM)b&lNZFAceEwCy+*!$F7|O9x1fz8()s2^i9>c5Wv+*VhPBk>65l!JvZ?sqKVWS4S zO#<^KZ_Mgux<@c|G(d8$+9lwJ1)3aKNi61Nc+$A~PsiSKRx%&W%#Bq&Vys_5=0=E8bT z$(5Ck<^PA+gS^wmwZmusCbn|iC`Y$??IgQF{N1@x&Sse=jB-DC;>cKia)CGZi1`wcxPW{{C2D8&3tbtGsZ)LQ)mScXs~pxAlgqN-yZy4 zfqq%I>;~=F4lU*co+$E7=LmJ&98dAKix}Tn{C4nV<2TlKGk)WI*WowbhrST+ z!oYT?SwI&BOkiFs;43#teD_-kyvt0``LMpwVzl(*n!d$1wlcl5Z{zB^ef=tD$1xS>b5zcbXDZ%ze&y_rOm*~it(@J7sZJHI6<51fcZLLE_8pqu zjQa=a&6?iPz7*?hu|H;Q<-hL`f?}0@fT4$%^r%1bKx{Mo7e}=hV?PBW&;5STgfdFN#<*6;&FxLXPeU2!1B3jFh(N2j0b$qcUxbd>&nRwCKMvP3c>w9oz2Tt2+BS;RVNqvmY1E zeq1>FapCO8g|i=5oqe$=U^@GPR)BAdk*+<}N=&p8W3*5yg0czcn0(VKZ|>|HZsoWt zAEdJn?clqy@gWO42Iz{0PZ$klbRDs8WnEDWnvaL?|pN2ATPkq6K<+)9WCB z&?V3dc`(w2Qk-)DQ4}rYfn9cj_6#Hb2&6RP10Y!`NXlUp7iv33oj(R@@HqYr9edz$ zq`mqWHiM^w7cY<%?eKali&)U>1Y*kp?x~Bn7vARl$gw4fOjYP&c)-^=tZU^di#E=>HY8|ApC4|jJT^# zhosZ7?+o_1^A-pDnJ4;L`wqd*YtR+gj9PSWH5mPQqE66;_=gs-mxa3m#(BqES^~8d z7id8^FhbYQ{j0_u>bzM{^B)9XHMsD{n?O(hC6WW|&vIUIoz#)Z_8LhdkVl_c@2#D-UWws~eit%!?!S^-bWgDIt zVGK?Hg)xpHfu|l>4W=bo_IGn)qz11DPSoIEg2c)njr{+^-kX3|RbBnV=iGa9b8-`t zOSq5(2p)!DfDmpV%mN7sAyLLKh$1E-14Kg-k`Mx@L_x$V3W|z~6=!T6uvVmXD9-xU zDh^d_t){UKM@$&yPG-#4%*IsU8P zRg*tSD(fHWJoMi)C%l-naXT6dVmV9L_-hC}USIN`nL}SH8Tx#&iJ9apgd4<047;$T zO?N|n@x~9>R-rf;7MFC5#;ZNF)@tcvZh6Nb+_(G#~T)MKibcqOD{!W$)s;J?X>b8Gv=K+9|);Sr^XcZTxcX+!3$rG?grLw^^k!WW!rv))VLEQmhQ5~H zzP958SvqIox;kmclTw^{aZ~QK+{U}PVBC)B6H=!|P9RYqh@QVL~cL0K(a-Ig^2bqfB&8K$BTaJ9p zE}I6SVjtYBgem#tyC>3~Lz3b8g_2>kXH_Jugw%Dx+@&D?k3?*iiL>OU_RoBzh-iSdJzcQGbDY;Pc4 z?JJ5edOm3r7w=-uq8sq(X~9nwF^ew1r>|u)J~4~p6UUt#4#K>o9nVsVc@$qCA&(Iv zQVUJ|knDMdz#i!dv(S7FKL%8YClul!Mwmk2x1FRoBpgXQgu#5Sivn2Lvc6Lb#6wB@ zI+Hpd=MN3d=Qs@@_XGqNng=-*aKw`E5N89><#doLM(l+c!fli-; z%GFz)zq{e2G8v#Bo!^t}{|Dw*oWDPPlI?~cjbJJOXw)~f*v*yr6H3+f70c?XtKy8R z+Gc@_hSe>Nt6PlPdVY>Dh7B8L7fxSPQZU`lsIn*88O?EV8O_-N!?sb9-_p`lyKHq! zbu;pKKvDrzX7fgBcdcGc>Yjdo>2eNFB1)lC&GwGH(=IPlSAu%N2CwYIX_HGt$T)om@= z0gI z6;-tjWeqhohFvwyKy7OpR@YbA_GzUPGnxZHWM?!xMD<2ZLt}M)=D>{Rfm!yzrh!B4 z!a1|22joxn;V<~jnLB-Ueo1LrVM!?%M}o1jsiCH}uDVRs-B?*szq+E%Xlg54UQt_5 ztTubLil(YH6;0JNS^BvajjVdOjQ&`*rm422x~!>UP3FK(5d*UZIu%w8HgM=9 zX<>U!ZOaPQ%GImwJf=UCwE?Fd^$m7wMcrytMS8WSp~)WDZQ~9UE8Y5c+LRqxtz&vX zxk;8?S}5LP82>#+?LN@0>(ecJj`Ehsp8s)rkISg^vN3Bh$eJ1(>SR!&hbn7VDzK)a zvZbNPu`9wK;0NF1636;a1U<)17bohr!r@~pth=WhdmLlB5e}wq<#*FAj{F~J4>h*X zaHr*$&MuifquY^kj3v{Xn&5a%)tGAPm)mF%&e{Q^pkZ}gm1>V#OgVZA?yg9U9J``w z+^7jTn2pvLE!8U|;jYpR>7>nkxO)tysT zfeEI%xw^hZ71lLWo>N^#cvP8&JKN7_YHqGIx(;l2y@A=7DedMJQmJLt_Oj}hHPzKv zbLvpZ3e-;kEKN2%3$tL|TD!Jckn}^lbLG;KbfLXO_(lljQg{4!(fdD9EnR<%eoFiv z6PDzc7RsLmUAkmJ$*k#1?Sj=!XxA2YZT6_T8vBU{o3rd$So@+86>VJ*4QnG2ZvBSY z^IIyKTIes$)ewgTopW!KonI(EU}MQR77bDJ7ZwyupFiIopgj6}TKD z-nS5HZmDRgHd<;|LUsi!U_tGh0WDXT0gUmF0WC# zxNPQ}vYMvqRjV;t*BT4D%*uBokRSpg&F!*A-?Jf(0YY~S%@)M4iqQ8E`@ zP}GFl%Py}=!GZ-PWRb2}uvoj6T?waWqd-j8(LtQ}wv7?eQd?11yG}^$8f*!%796Lv zs-eD`Wkt4Yy%;Oz+Ovw+ITIO1nw(=~B5Q#iD+%ePt{LZ^X4JUZ#u*1A)m|E&WRDJy z8x;;O)q`Z1y<$yMTT5GZQya!9PbArO6&Si0#qdzEBE+V<|?Z6 zHf5sC33|@L1@?-HX1ltrv9_tY${sL4O_0^HG~@Vz!$56~%m>_`$`Q(zgFEmtZ4zn}Dqj4Cq zYpW&#!a~~8JP}pScB&1!E@O>Dv!$WIu4`ChXQHt&-Be8@5+{wJTxX9%zoK^e3NT=O z8Xe6+YuUcb(4yD|!tt*)cHlxk*Gv3m;#&C1hYCjW3`>Rytc*0p@(h);H`(zKFiCp&`*@IP%GW|uhQQ?;d-%1N}-+|E%&dbhbdMOM=H{B!+vr_DO8)aiVk zUX}06cWOOsY-n7~qY-Lf)3_XqczsPnSzT>2_XTR!tY5uy875@RU0f)&mo>u~)U*UT z!E>~fjGTw9FV??iOtj8f-^N?jd>nb5?TEEl@luqnrU0B4RPo6$q6lPKytKHg2drcrLK87)&+%kvkv{JPWn;Ap&J?%iV!Qc3@S8{$_Oi~4oYbP z(1Ek58q;gNn&~oXCjIFD$+0A(c_~j`tCz;bNgvqk8>}q0&aLF#V;nBmvPae6tP=p( zSpu?%zOpBFOV`nhtJb&3@+`}25|QU)?t@v*8^ z5k;ZLMb5F-u>=(88k@e*J(XKZS#*f*jgk_OxW3UXS!b!1HmX}P>KR4T=pNmhB_$M6 zY16m5Cz29H)U71xJ7@rja=d6UdO}csg?r`y!}D@}>vH&=d&CalV1%=h5zeSG)(pqZ z2=-)?Dsk7>f~(urBQmnX@Ovzv!|XY@xV4>gn;Z^G3P)fYMSC?Qa>bdQ*=s@`x2L!S zJfG={`RiD3Lnlk0t)7f;~sBy%g&Z=w8{l(v;t zSGG=*(H@wE@+z%`mS~^_&>CB!T-ok935NaOkbw=9m@A` zv4pKTwxE;smJ$1>d1dn#lupl|RkpZjdTH7Gk~5}bvBaaU%XNcC(&ZQC7nBv16wOh@ zQFJ)1>z>nVCN@{951sS85_mj1v=)7;y2;h6s=@8Knr_50cF=frw(l&j#8s2FyxVeg zjufM2-IcqS496+&JYRM4NBFt6qvZ$eMEUpHiSqBYv!(6(?QCiL0Xv(q1XPt_7lk{{ z+6H{xu(ph+o8$V2<45xK1l9}Tmjlo7c-VDnpXl71;8e%$HO6v9O$`s!Yi+lGc{$1{ z0*->sA1_hhq6$3S&yjz)7uE8)rA`xHHgL_qpvEkKSlw&v#X1y^^aZ5T2Ooa;MW<&`ex8AE%cJ|w!+ zupH{tK3i~Dm!eTx_URDnk*Q*e%2M{j`Rrs%DmUQ&s}?!YCrihX=P>Es)uXk3#d?KXDM;uVJ#$rwx9E z$tw=Wso|iGN0!w$8uJT^rx(i923efUyX`RQpN=vdZqy}AnX0@&SHzCrSSa6Ba|LxK zK|Lu(l<*5v`6AKz>ZXWaD&jr@UtqMX4Mb4NoeefM_!bhc302f#I$a50E}31TzKFza z2EKv7QLI^iS<3aex=Gy#bW88bIF>_yPqnb#^+~n@YKo(7HO7S5*E@;v>L_Q=I#Q9gBOgY(_kxa&a-@gxHve9v z+wh~hqs$}C^g}Ox=U9$3j9+fF%F(bhIIzdrwEl zb4ZUMJ&e@87xIyo-;ee~+s^$3+7Ic0htPgV5AKIv{6_u=qVYE(J@{+bhqU|=@FQ(U zdK9UB0CpvyU67U|ZA99J^Z?SGNXuVD`yt)^66$>d}i zx6xmTs3+3JNcn_RVwmen4YMt1_DzVj7@ux1@cr=5Duqnc-AD`;C8nN|6u-u5H>UQP zG;H+Xfn=SDe}x2=;P(;e17liZ@Cwhg#MH~Z(-ZBjzG;b>m&N2KhBy236UT3iElh0m z_Vg!?FGvjMCuUAdv_T1uX^B+9hw}L5;*s)>j#KGFo$^3^jJz3%2T$;R=m{i_hmI~1 zPeBukymu)NKD^|I<()!#N54lN+bb9CV=V9J=!?X8h@T0()dkP`Q~@6ZenS#6QlIH; zpYH3kE!lg0LbvrPW_{S!zl6NJR`?2F%Dc>ypP0JYTbO8X^vy`j^Hc;9ZD8`HU8l!4 zA&b6K*?bDYQ-y!uK+Yk^8ICl0>&*4K*IE>)N_ zcCW$jWdJ+RU;yU=-`3XA!FRF@n77g6ooxmZt^D|6`6MmrZUfx`%q{c_MdxV*!Hftp zp<@ih@tAu)>!f=XbcK<0wC6L>1=n|U3?ZF<4d8QyM%!%CGB4kGXIFQq|t*mhSzaFmbN8pnEq|4NE_G z1adMlm)^=got_XdW(ED8R%2F@w{@{UnB-|~3nqG68-pi!T2}-UJgw!^gI-VTT<;}T zfW#Q-Me(eM&xbkUCm3&l*}j|UPaC~6676{bry7pmnG3mwsQ18v&eEQ!S z#eo4mgN~uSt}uQ9NbA67;g^B;v;LU zpG?fme+HAp3z#K-2X}svVm)6cT2DUn!tY+BdbYz;klTn~>W*eVK&~DSVvpy}z>~1z z88E+)XqCjbM&wYzjlRv^%is>up9xr7R^Yd_9JA5wP64@OZI=#GM}%%wZETe=0H3t#{Jbgb{Wpj!dD5nA3x&+J6|%)}r^4fX9pdD{=j zp+7zay1k%dT-d07J7{`taC`2Qc<&7ul?6&Z^?w7t=XP~;+zc4)9rRq*+1?!Y{jf(^ zd}Bw)R+j6B{m#FWco6smlrgteP2F%S}*1PGHN4sBxymste_;WV#qpK1v;y(i3x~-$*B;xf$zw%-6wSi}l z2jy2-pR`{=$Htgb62sm;-e|wl{ItK2{MZxygE;X^w1?&|RQ!8?7wfsai)WU^Hy7DIubZPz`n~psNYs^5$Bx} z&O2z!js8;-w>@ck{wU3g0fZ{30YoXPpRzE{_h665-x)^8DW)9MQ_JzRsd~BsPKo!d zl8U+*uxH`lxsca}z2X~y>9-p_FbPW^2S9PW=Qic3EW})ce|LjE^FI9S+SQMFSI?iK zU*=D5t9gtzfl&lYfxi@(8>4A^;y zuLJ%N@YzISo?eD=&T+U2c>c|vS;R{_pb0T?m7<*X-G*}emmM9uu!oKC2N@sY51sqV z8Hq=dy;sF|JKknEAr3o zmTkiRotc37{9s2%24LqQz6$tu;4h45Z>Ri!)BkOO|80T)_gi4wc%@^mP92TTkW{m` z`V*uqTZl2u$0@JWc?@NFwjrvV`t$Lh35r;Bu2qtjNMZr15FI=w@u`*iw@ zPG8sQdpi9>r+x(E5&b$HqSLWDEz;>Co!02IRi~SEdW}x+(CI#%KBLpub^4x8ztE{4 zk(NZiPKW4ptWJw`x=5!rI&IbIW}RN6(>rv!Pp8l5^mU!Sr_(QV>L0E3>vV`t$Lh35 zr;Bu2qtjNMZr15FI=w@u`*iw@PG8sQdpi9>r~X{6U#CNKI##DeI$fmG8lAT4bhA#c z(divJ-KW!Mbo#nZ-_z+AI`t#`9-rPi9ir2*IxW)aBAwRgv{k2@b$X3X@6hQ!oj#+} z*LC`yPQTEpUoN4bU#CNKI##DeI$fmG8lAT4bhA#c(divJ-KW!Mbo#nZ-_z+AI`tz8 zq10ceLv%V;r$stlq|+Lmw(4}VPOs7F9Xj2o(`R)0x=u4cvqc}~(T*7f1rzPeh0E}^ z!)iM>dwh28h*7I0mveqjZgw~~d-PC+m*bV1q1A+aP%nFOMJ)oOKeCYZ7zsU{g(x^x zl{fT`rgL(N{_uQ+$7i$`z_(rxSgc=j<6kf zCq8342xkV^;jxCvOgQicGp1z52R2VV#T4a}OmUz$Gcl5x8<l)K3}lJ894L)^s3KV|TNXaVNu( zV{ayC#PK`H*jrd#D~@3yV((yWgXrZ#m>PVU%r)%v z9!63Y>foJuAqz*dFa;bcAH0RgnPT%Q!LmV&v>XPTEG>4}$C4RhA3C=K?2RCD%& z07JEgIhSm`YVl#t6YwL9G_&+=6!#9*RvG3Q)ZeSL2e3;BLhvK_Fh`lMxPnJhFr|$V z5%02mEWzHrhM_sklgZe-S3Rm=PNm1wALTf>6R8q%$ta=J4gbKMG@Uu;fUXTY8TUCRgF zijpf)GVpEy`v8auyS17$CB-l&48pP})xsibr2nehYR&JgmeghtxhF#Z!2J-O1;#;h zfq4=O_Q0orSq98tj;j~7xVu!E}Qj`Ym&QlWi6> z#%k*}gD4|Qm$Cm2LKs{@_u*9$7M=%uspourE@ppb+!MhS3whuIUb;S#jVOn$6>Rn- zn1qMQCM&jXlqs$aq6}Q=7e#P&Z(bJ;BjY38NdJPOOOzsS084O&(-bk75$;n3zU{j= z_+h$KPuw*lS66QgsGfDJXJpiAJjEW46w5!Y!G~h`{Q=v96>x;I@()G`?Wbvo|7(QM zF$%A2nC?8t-lSrXwM+m*Qd!gz4^D=xW zlr@1OBjXRcNoN50j$(9M?mU#Ap{v1i6Mo#;1az2#5v3>UQdJj}rjOAI$xA-B#K^c> zt6heoQq2f^(w$M)rdepC8jsg$VeXW${xe}U!!UCRf4fD#@P27Us7uQ5q;?bimn&1J8_>L$(A1e)}hyRE=2+C;88a7km3 z4J$5z{(;{?_)oz&s2!MI#01bYuK^=->ib&U5tN!kUZg6{seEfn=G2`8IH!_BHSgfb zfbbjy)*Sj2oK39bi&Pm?bs3v?FiPOkdYaoGVIiHo6l05X>IQH<9Kp3jaY-qb(?~Wl zl(OA5+t4Y>5Gvb#TxE(&&8g?20k4eU>fXFA97cwz?cx_( z48;6-Qn?wLnNv!dreUSmMF<_G@Ji^7x`09#MG0lqmCz?6gw{t0{V+o4#gRf2b*(A% z=TSo0GV{0)XXI!aTKc4>A%20zv-)pE3SAQ+lpEsC>bpYIZ;TMiatJgs9Itx}nnp!3 znqebjg{}h&=SCKCHqLl6qO`tiY5Ekcjl7-K$Si)($;w;aMA6MIqmk|mxPQ4Uw9wYn z#JlJxI1PqPx_EKD)mIgOzvGO0YQo5i^#H_v|%Le#?Ww9|=7fQjWw zcpSXTv*81m=cVKDysPmTBl4IRX255Zo+G-cYiG`Cr=ujWrp4fz!)|XAD+{QxSHFDW=~ z6unYA7t?=yrk5EG5-Bo<|Ah(y!oVS-u^UO7ELaW@jr|y`@dLd)(Q2% zpW{>Ckj0*csVo@V_6AT^dQY^)x(fjs)+l<&`s)d*Ycxw=qrDGQ93{v#THIUoZ{IXj zIS}@urm<519*X~bBO8G?YVqnu&3b{Y_4Vz*SQc;8;=dsrw0NTy|1IHEi^tRW?+Du# zJu<$7u)0x;H%w;Kjas}Z8Fix;?~#nUQH%FUM%}2z$4ExqsKxswqi)pVE%RYmsBYBa z1Lot%s2jET$Q!lz$Q!lz$Q!lz$Q!lz$Q!lzRP$L#Q#Wey{meIzQ8#MwkvD4b+2&T% zN8PB!kCcqMQHu{tM%}2z=SW7~sKt*m*-`37Ek5!_Eq<)|C+N{PYVp^{%8gq5ZdTIb zjavN81PzNfYVo(Qx|TJXuzRES6Xg6B`!IpsOS9b@HTI@pKX$P0jhgT8eNpxlN1M-r z(UvNE&I=qJOb0|(4 zdkauK2>JqW^x)_YnC5#G8{**jM^Tjg6&Av6!8r3J4EeytnD>M6a%X7-$~j09CF2XM zWTuCCMhw&jYM2R`n~_NfWMbGE``3-0Jnd^bh8o}JFqAg8C$wF zGcXFM;7KO_ls#N~9ZRy!8<80wn1%g#aExT~0>#ub)qEKx#esjKszJPhj@FtR_$>`B z6# ziYA}k3v!^Zf$bf79Quq<5FBQRFLZfAzkyNc?hyNQO|_z3&+Z8E1ymz+F&e=PZDkEY zUjhxdxBoU;Kgqvwfy~nx?~<)!mxNq3tmbXF1CtIec7X+felyiWEXk>jc0^% z@M(rFfTd`eN7$a%d;qH6?Pl=0%b>s#Z@PhF1g}N2{6;liN(kS?*0Zc@U{>&E;uEZI z*t)lJMGIQ1S@YXzLaH^I@SUWwtuU>*i|}A;7Q6cAgfp#cVMg#C!daG|wY!gS*qTZD z`)SU2E0*2-0O7^fbi(^~0dBK?OZZoWcUu3Xu1D?$yx)qYJ-_({-~-k?bW89F(j2t- zo^tSM!beh?sP|dIK{Mra5yhZ}?tc}k5Ep~PS{hRt7Dr$VCGw+*Aqux$}0lmV(4@xlvtY>NTKWL3%JwP)* z8Vb>tb%5|c2?wkpwD<^Vf>s&L`h@sYi?^`B&j|OiN?4IENMlP+rID<%0+Ex^&eT)wjZ*q!%>lX9d zm^8^!Vw(E_YI6FmTOFRSTpode(McT_2G=;80H5o)^f5@CUHs>O`zwCic-w! zr?VI({9})OI1eeKXbrG)frz~uE)?#O^eSNM5j0$nAVI^LhPLX_L(oZxa%1%9$vzJz z;rF0Mj}YNhi*J_p=tbDJF2(5XkwQ<(wD=RE9%(-ZoMk0ZP9Ju_2&nu8aJ~R+?$Ngf zYWkL9@i*n};sf$#vrL2DgV4kDZ?KG$O~VcS{-X+1Is zXIlS-nW@J~gu_-p_(hLQ!sD&MP}XB8;XJDde$itX;bJRDnZpUswU)pwdW;~v*eWC( zCR}bk4FB$tLwJRCHvF;2D8h|a2CF)TD`=awj__E*?N*31vJh{vUSxg86Ti*+8R3b9 zcUoMCdrac(`tFpuuW`DWi+saB$dl#Sm z%F#xLr+bX#ffs@#iC+gL59OVPk%XXWMsn7bfUP8=vk3;1ngE6grdlsRLh|Sv0ozs& z%%aJ;gfp!Q^j-28!eJ{HHYJZGJl?vRG~)>8S@TIVk#MnD#Pjz$BO4i_doJPIO@MDI7~%tPjyh z$)^&&%=(7B@@?QY>n&71c^2_IEZ)N=&nCRn8c1Dp2=B5uj*{hTz}?o@q?hjjZ?k?z z`cl&DvF@jQIjP=lJ&(>$Ud#*Kz1HQd;~Av+rF9K$Te=nSek-0b&mw%lN<@bzm+{{B zIg6L5$;(K8(0ZEoR}y~B`VVQU2p_U8qps!LHXXL|NV9_Q`__k)f6kkbd?Y2C^mT-7 zGlj9zk{dWs=9(#q?2Sfdg3D5V$||g4=HL_Qyvs>$VrCnDV3mFrxtfJ*6S99IUyJbl z4cVuuV9hU~@*sRCoo{$2w=sJNS)+&gDwht`Lw%LIl_1Q=NZv{C8#FMcHGqDLn?%2L z6otMl%2_u8HpJcq3foNcKyQ+KJKL8oXIOtm^P2r)fwruTpfS_c{LTf@?9Z{4YV^l% zdLfd$K3?0ICq9kO6Z!qISABmx21Pc%KlaxA6;cNnC-VDa{P6>1yvtdbf_kXDeG8Bt z@fe%mAA4rL0fcelL}>R;W~X?s2gks&vS#FI4ZwPUBvVQzYb6h%4Lo5{@`9^G^7hkk z6;C(?_A4b}Q8MT@RM*#s)$+bij=u0-UmuDyPHch!p4C+7?Sr0FCQDvPTTL=0ZM8@n zycPcI?IXt*@0no9xEQSl1TD(A1fq4}5UGG7%(xXT@^uhPj27pT#V`Y`=tMTv~>2Lt~hilCwX2X1I98W%P;-$Y%W&(#)+a^=@x(&UzKz zYFrlPA<>F(yN8RXre zO7}EpeS}^!uVK}^+xIqSEhO)?I=d$8A!c{#?E2w*$$K4VhI#Pt;jf}y%^TQ#oSug7 zBJnOt9)NxwzMgt-B=Kp;X47HKJQU)w;kV{5d14#61Za+wk-WkE8-eKn(xFJP^vojz zrcbi$o9M7?0D-VPOn@w(3JX2IZ=P@%yUDWuHnMC5dMN&lybvF&2&}=!J@^+c*VGSS z0nd?Z`5bcrF4#Re=HfW-PJQ>7W1jBYexS*p!*6uXhx}2j&nV4{30xlY!9O|%en};) z(-`=e7`PIEk^WvKF2Jjxv++^Khf0aPC6-R17}+?X zEZWj7k12BY#(4loar%^f5;8nc@-bgnO;L&Pn6ud*UI5z&90lB5Im_>eAyg}HBQJimJUVwP&?KU&6Rt1p0c6uWT=D-yK2?oGw1oXKgT96^=&`Mbg^&yA$;4J`-0}9 z)PnNDXx_PR(Ke0ivw+Q^+3L3C3zQI&2nBeyz&=&5L;6|N*5VbLgr zx(_sw42n4KMn}@9PZ#VL!6(;29VTAW6(v~zLFRx66LIE|#yw}DtKPC1I$S)4{GVrOw0txBE6DOZ*5 zX;ur&SS~PGXco1=jAQ9CW@UjH&jn^DvT7xp7`>8BieAZ1j$X+ollLFsmBp!|0D|$9 zbBF1>6RQ)0WPSw7?FtAWlcU_z~SMbZ?&MBLVQ>mtg zU&v#YXVDVMp8{zjzl8whUr+hVqvg93NvB*cPFppVyErXBn{v509i)_Nb$R!T)8BPz zIcwbXm}AkWjzvQ;(!`>f0KzfTmF>~XC8wFl#VJu!xr@{4B(@tDrv^$nNtZ`Y`QNiR zOwAF^ffY{hoxtV#&hx78Oh}OoRPa!({P#DCp3|ZSAzB@ksW(8N85X(Uo*TG ziI~1^zsAZp<}Dnj%zshR;bS!40(naF zzvR4z@u)QaJ3{j()Le<8abJJ0W@pa?ov-1fVtz##@+C16iPeMD+;!t5N9R}6c|8OV zzye#+eCf9p&5MH^J!)St*-`+4$PNOGi`CE{W0bx+&1-y^x?zw4_oCkXdnSYWj z*2yMUTw|gfIzy&jjT6AGeYy!5g^Zg0LEkq+ zj)H$X3Q|TvL560D-Wx;;<`(d`nhL%pk9meUAHz=efpMb&@RDK-0qz5r00@7kS)6^K ztY+$xVjd5+Vz*payzKhO>0CazBW#71DRkOUiBjk;_2uSGRLot;`BXDZ6GXe6 zad$bHcXdlODSzH&)|g%l!;y1RKn=l5#-s;xg!`iuMuPZD66jgWZZR9cUFmG{W?V+qCnjpIF z-#6qO4o15(?=oMfI1afZ5GNj+H5LGePZa<)d{$^J<>a}?96lHn96r~Rl*5NVqm<#( ze;kauQImHWK1UCzN}!!Me3~>BF5%=ce`Z!vFpXINF=EU%0yO410E+KC%@^nJ{oK64 z;q#2wd{YRJuN8orH#Td&-K@mj=1UIG-44(D4v+lwMGaNWbC5j0Ftaisl|%K62>>`$ z&m+L0%GcJz-)Qn5IaE(oHAr>h% zH#q?F56H@rw~SR$aQIwJ{EBvK*(`N>-zHt$+cEGNGi?a$q9>nDbFS9}(cbsmZxlT} zxfS44hYXG)fiYP!>DRa z-o^J9KdvgHeR!g#a(c=9t64_DG-e^hh%ws<(1)J~pnQM2=5u}j9rId;&x=kJKE9edqOj*C} zR@U)6`F)tsp*aq+bNYC8Q5?O}p9g?mIg9|kauEPEpqgd_!jrBN!yM>&8GQMyQh2j2 zJOW3+;gau3i~Aqe_#mf{Tu;m}t!QnYR&*l)DtaD(Qgq53KzLk56Fn2brxcx~3%e@X zsquMKbc*L}N6|-l;AD+N$;^Dnl}7zW)4LsXnr9z*SevUs6;7T@8*pWa(Zd?4oAiiA zRoenCMSV1BBXuwF{DX2h6u*QV8B(WC1HhqpEdbRpCuyGD@z*@6bF!C^IWPRu(8YzC%R-xVDWW!0~+sfbiX#{Kt*&M>VxO zzNUwAVok)b#^(8+z9S%k4)YZx zNFU8C2Eg{-2|zi_Le0Yv$oScLBYEgBCzJr7!;}%A!#oKfyg`$9ahSFP$~^5bWts{y z`y>U({8PW;&qpd&#ubv&UgtOQyGuWs=$R zh{oHFJ+sUg9md;fk5C2Yt74aGzUMQC>Sa>NV93){hiSk~p3E?;=3w|16yi%0<^Z5C ztpcD1!z|6iSGmkvJm-;zgW*wJfP*0)gEhQHlXn>mA3v(>(}UqOO$DEp$K36C z&@pB!#E3D^6QD6B-A5|0O!Lj9F*kcYaCjzxLJbB2Z(1=w>kRD~F^7s%6l? z@|eHyyiLLU&f#f_>8}am)S~Ikp4!z0=~B7Ldcb3k0xQSChh#0#h0)`n)SWoRqtHNg;>FAcnpvd_qs#5a5Jsdr2%o`mkH&D;$Em>!f_~?AyMDw;A%)eeP3O%=qw|0V_9(YMW5`w(pd?5Bw^RDZbX;Q;)B;o%=0* zGmxeEp+w01&gGCV zXDAb|7hI?*UIlOOF{-uBhsirM{(ay_3Lfw2gjXB6_v-@hRD9o~3gWzP=q6wv@RA9y z4B&pr$aOB2C}p)HWq&*xTT97=l&xAVTgu0!9uL1w`kBT{7t_{MT`2t@Ss2~_no9bA z?0=*GHJ|jq=Ih%33%IMmGZALb6B^vF4t`7_S@~Af!MV%edF#CHh&sQ&%sr?Dvc9~Q za$TDBJ**2^-vg0_QT4rSrc%%P9*w5@UVnEE%B*icTvJhX*ZTIGrDU_d`9jl8eVy|w z>$|c$qKNu7X@RWoPqb3Ef4AsD)^~4YVN`wpqp9fa&nhZ&oRK?cwlZ}CtoWN|))zw? zjNA%cdOb=5<5^i$#Cgv{d99JVQWL!cqJcsru_kh_)%Y)gpQ!QYV{*D%-uQF>AUeqFKLkMHsma8c(xc)_9t=N#n(= z%XNkg{)B|sOaan}){hY#`t9}fHfX$j(LIg70T1lqPami8_g;a! z2o{{4gr9R6mH0djLby3iyB$CpZ#mRYPXziavIC5QpZH0=1D{)g;1)5BKpNkkRxG48 z3f~Um-Q?T&^7J6mkUNbKzWS#0!jbs!%4_?4%R}i=DyH*IZOY_EKSKC=pp1eQ3DnQm zuPKHvRVZskfv7Ix@4`bL$(O#@vz#ALyBkRyUxuGC28V;*6S=QQ<2S`(oKdtJYI|KF zmxLUv3R}1intErlWsbP?0)2rjq*5<_^@GpC&mg`6W>^;HcTj?~9H=wGmnM;!odF#Ajs<=mqb`NgLBg7Q25qi46Hs|qK zy3#288~gvEvrx=^v0@QVphZT3jgOvg-~nz(g~TZ2aj$nOyM^0Ag)FK-hsET2*yG&w zX>1%+1cLH%0JnKchqOZx-?k2X?4d61@6uS6w6l;?))jpohw@K6#BjQna--;6WMf{D zH}biIlU6VaH;kezisHsglfI7j2)(G@#mB6i#$WBEVd$WHeh#xjS9(4M%n4iEPuV78 zgiULla#;*iy<>7KAkA(Rt%S*;pLxl~wZF4hb!{2fAXF*kT>c`;*(}P|BE|&>c|145 z8m@v$w=(N34B60AUe@E+EFw*sKwE)=>l^?!mFl@Ps$w=~gjWb*(Hai~sfYwzEYItG zaZh+iU(T^IhK$0m;2p_3NOd`pd~+p@@2eO^Y3!xl;s*D!NCtsGy3W4@_Egq6VtlYj z4FDlBCZl6~Mmw5M$252d1~8^U*FdN@CEzz9Dxjhhyz@s#`-}|p>D^cj6C|{OcN0M! z=2Jo&crPUep$(>@ZM^p}gP$IfNJacq@eF)zuL33U41DV@0jS~`_|{)T&l1`2CVmkqi75?q~3_tHc8J2XOI% zpQTAGP=A7nW0{Tx>JLgr#RBywn{_Bru|WO3&1uM}SfKtC8N@0Us6W-b2hvpJ0)IdA zQDju)0)K|N20SVjs6X4>fsBe=;2$X&6}iA4mW+yAfOVf_Dsq8;l*ukpkqi8}Vw;Ku z>K|)92|YS;fnP^1@b6|NC31oPW`Zhmf&Ug(S4A%HOF(DF0&N7&Z+!rw1a>6NKH)+D z#IiR9%LpEV?FlT~soRL4e(PQo>42c0fkg)d{{W&-B>=MzBRH6Sd*SmDy?Yy>1fekxaz4#K(F_#z`#_hE z#@w0CZ@1EU5Hp@s@Y&6$ENA&5)V2^sUZjT0JEp!)}6W0%DPiew= z=q6*3(;d4(W4koQK&LH~^` zjP#oGAYjl|-Guwly93aR^o(=GGuU*z_8WAECV3qsc|yXMt5L{qGX^=GME_XqN-zdG z?MnQ>3T=%zr&9xQ&cPeeCgzB3n&3mHR=b|Z4j?1Q!fZ}{*P)p*;+P%D?B9?bg4adE zxnSsj2FkwXgeW0ru4J~A*-Xvl=ddNnuBV_eoOv!lHnXC2xz0X>>`RCFvlxou|^K(qM(9-puw=Jn3FF>T~c-fopmOFs{yPB zkj28ZpCk7ya`;IOr>=Ai0X~gnoFN`Eb}4=WGDqX1-Ei-diW;9Ix$!iA!Wy@0TKRLF z$eX+PM`njeK;}!oMC&bs#FJ#%+R7E7_2p^aCm+da^3%bnZ4ZFOO*rE#be|G6v0v;QwPZ|5(@HXzRU!-^B|5Xz({i z@b^W(MAUxjartK|`(FtdgRYL?XIRe&{^H~EPl^})FU1@EejULd!8`A`yq$G7M)JsH zK1_E$qQ&e`O%8YFH_6JKAE7({$93n&=*|UaX?OliGrBH$peM$wa%Tgx0w32U7rQPg zL!nfcySy*Yae2?`%DdC$6?a~x%Ma6?pW@+J>AbKjUz^J}C`p&mo$m%mM0=f~+sha< zL=$nx_i{8*rAss+g6NBAqIQR<-^>W22vwIk1jb07PR&DX|81J~Gifkjccr6mRQtcl z_P-1buBMYevi%P^?Y~1ax^4KUtI#5Kt^RwukM{(8LjqveRjlL2qdy4(PL^iJ~Ti zTQvu_AXXN}gU1?S42sd}$-O6cq1wn zrL`MABZqgYv3qzZ%i*1B>|F#$jf5E5^#rFuQCv6?J*9400-s+FjNGyWBH}{^zTQiLa?29KnNhba0aG&SmL=ekjJjnB_#~rlSpqSVQMW7szhu-c zOTaRpfraXpB@i%QKt|oN1R~=@;{KKUQgzD`h>Q;zh>Q;zh>Q;zNHt%9GF z8FkANh>Q;z$ToLEw7O*pjFgPJWeJ2Oqi$IOIg(MgEP+vS?XPZG0+I0{17pqip-11c z1oSOSU^go%w=991394I`z%8t@u)Jj< zr@m#8D3S3`5hGC|<0VR@?}-%fojeWL#K+*6;}fQqU@#>y9!x^MfV+WCm`1-;3QkA4 zs2ykc$dWL92r#jy_kxiLr(OcsDg~S{i=bgmB0O8rFsKSR-)9)L33CX>p4}VXlW=A% zR!eISO*xAO8rCIgfXg^YeAX&TC@0>s@L3m2o3%-VTbMC8)#V zio}Z+VXd)B(Q=7fNMu;2pd}J7Asn=3z=Fg}3ES3%q`8c6*t&}Nt%UQehtWcbR}h|S zU4`yU+)lXMI*<4(2{&3DP?mTV;daYQ-W`OuSwl#3HR0V7(=1WmOx`Oo%@TLgsrE}u zv&8ENACQ=4iPsZ;PGXuR?jn3pVwxr1$f_I)F{WAKO~fB65ADQ;B5^k}hs#5ZX_k01 zGw+v&Zo#%G@m6Mzl!tx=Pe{C-b{s7ad0A2U;gwYpitmTa9+sq5gx)9F-80dAnH8aO z*5e*#!WE%gnYovl@fD#|*7iPT@+v|(Y|g#R6jy|vXMOKyW^RR%z@AzTn|rN?cm_FT zXpzJRUIn^me;D!H6NwK#%I-OtY!8vmuzp7y_S0_5s-&gAJ`HftdMzFBBNUe!>Pz@h z!i^P%Z!B&54gWBKj-txnkoJyL{xJL{Z9DIBGG9Dm>;#L{YsIRBjYi{(97n zqRPL+;YLxt9NpqZQQd)z8%1>|?02K6-iH!5iYouqfg44YnLHUH;O7_ce+tj zDZq`Q%8VODbsEUrD5@-Rqo`J(#Eqi59vL@^>ULzd7^#S&%1~8l5=Av_C_pnUDvBzb z3RK0DNF`BJ)1soN4pHTc*jQ;2MKw*LsHRC2)ijBsnkG?H(O`@o#MMY7)M$xWkcce)a)wGLwYDtqQs%cSCRG$D%A4E}2 z;$BBZQT?g79Xu0JR0+FLRR5{uUP1HHB#LSpd%{eMilW*FrG48GMfFj5)el5bW$XP= z6jhq=eEJMU}7dB4TZPN08NzeB}Y#fw3r^E!%Z9!UEjit0v`=qReR z#)+cJzsP3kD5`{=D5|7!qNoz@L{TN|L{TM;6GgQgcqfV~VK<6uBJ?7{wS3YB`D)reX&!_>8G<8CCh>6&v{50MQeb)00<|F zst-$CzfDmxA*imL25oG7ZYU@KXos1gh&OB7XtP88J_0XtDt2|H0#2|H0#2|H0#2|H0#2|H0# z2|H0#2|H0#2|H0#-vsPLQ6=m|QM~|->O@ifGw}akM^U{N_H-9Tl_SWBqDuIHj-q-Q z=>I!WRJm30W0IRrvmftcs$_x0Dz~)mBkdnUyH2y)Q>?{1zRmcY=rD zV+2uMfX@?DQB?UCQ~cI*GG}>lFw{|0&w>rH!BpgX7AL_fYa{lwJ^B3}p4dO9pr=E_ zxC65GfGCvJ7xg!+zknz-g6BJ201%(eJDEId5B9I2kvtC-TYa$`4TUMF(dtK<9KtO) z)In%y6qUt(gWX0rl=~Fr^K0_ZSb~Q2+EBpb1f5)6UuA^G6AoJ3RfQ(}6(p$=(Kj@a zzGz#gVe=lEL^u5B;OvX-%WGswH!ieb|f6TVG?5r;~s zc8>%j4xK{ykdg3TP`?JCthq3D2boU&J+L01Z$G5@X5pjP;2oeDFqd)x3`{b7=Mz2r z=R|*p&VGn!uQ4d%Z=Sve)L_P}_0)U}#tdT+UsCjjcOv8U<6tzHF>6(R@YATeH+(v% z2g~0)4YQuckVbT!?|_F&yDPl=Z$1Ij?;w?2fe!{gzH=941kz!Kp$Fi_+{ zU?m*Hl$fWq7$f@y>b;(74{G|cF1-Yc6ty={Ya#T6MemJNyA9c#Bbw=}ao`&DF?!#e zU~Yw;!9qBZgvRJewCcs+nMyJy3VF<3^PzmCD0qnq{@^NjnS4Xa@qRNMBV99M30I7- z`k-jcvnU!nRF@@<#}8-u1z_>hno9BJbme^`&*h!mm3L_;FY7*AmmhTWzSR>3kG}v# zCFR;ShsL$-W>ixhwC^Xx<}Td1r2OdB5q(dxy)LvqG0;qMve{ic4mjE|DK5{4_FicVxz? zu#h=5m`Q1lN%EtG6PBuGYG6tKh>S78afMAN8jg;HyYK~uoD!|(I&lU3q;~ToZmw^rK=y%DWIyKSJ5Y&i zDaXV=DecTEWXsTsDlFNaYGn6O>JgS6t#L-(R~!^4tw1)HGt$>A-HGf7hFV8JBd#p( zFyBf|Gvrn_uhuXo-KNd$qX*txlryT;V7W)W6eN?olAL_DVGfbvCsB;wd2%+M!HEV> z9=|2s&={|ms0X9IW_z%&43t2OzH`35CT<b!DS3ad2|kp{;XSZ%{Eq*q9;7ghVFHz` z!{K@I)5a;3wa4*$y? zf>N)Zx}4K8AJcE14rZy>JG5m;x!{fHdOaR3C9H|yaPpW~LL4bcb&!&;36vBx%tNk# zJT~vCy4USx6*w$~#sgK)D*JgE_R8HE>1{wJ$2l4KFvZgSxLp@}04onT&R z9)-?fEy&A2yh`XVVE#4iH_RQ9kHyq7;&qN{gjm7$plJ>A3?FNLEct7g=Xzouk^G1@ zk%r{yyqED&;Zo&uP{`#C!rU;?G#q@XL2L;+M$FaQ7}#ZM&PJX|U%I>Mo<{%=6)G z9H3)3k|tQ1jKlmY&~O!Q-L}C$YVf(nKsc|zbtf~%gejQz_>l0C?;#xfHO9L`$VvKK z4s~y99%c_QY?#(~n1-F?xjbDPwkz7Okr9TC0S!bY(lFXH(6KHMFpdzxNn}EGgoVq0 z49D0LF=ir6^`C3NOx9ZD;_qO0PELcX6se7UnmNLg(_dTtykoUYOnojs8k6Ik!R#mz;UB-F_XM#-t-y+4zJ!Dos35 z(cC9A&$eS(>Po_8>3%8x5XA*N1q7BW?shio1D^4NfJ?dnCZ+%w4FHd2Vk8%AEy3+BWJo*Ymmv?kA5~;Mwm;y1~`!uZ?J?7o(&t zjE9$;Z@5kv`~NWa9)ML9TifuSvroc4A)K7-1QG}()FhN7^cIRBy@&!D1Ze__6p@5p z1q&)FQBd?EU`Is(D}n_(SW!{2Uh5Uxy@+u2TCU&oteL&fJ|TF$@BjY){~hIIt(mpf ztXZ>W&CEWtXO>t?%`nF3c&TZo@rbV~GxR%UbKn_INq3vf!~mVWPpbk=Rk0Z;MjPKm z4A5%cr-tMjQL8EH0z;4}DlmqH=MLs=uQGBhGQ@t!?i9Kl(sbCq)MWZ$yQk5|Fjts- zUzG>cyPhgLsmh&(AW@YQ4S_ohwXe%gRSS_z>+*PIUE)KCROAJd?+;#^)EF>}g+He* z2dmVj(h&G*WhpnPn>;D8iy-BP1Sttaps4Yi$@lAUmmC;rhVkjrVwP{u(DOP0f}2dx zU~*^a>>&qTEtM-=26I`5tCJD?t!40d&|Dd%p35 zO&ul~lEfDF^5M5KqvQ5@1i(T=To-lx9MTZf>>t!f%(0LUS|F!dt$P`&exPdYsc;$) ztHm)6r0+_km}RIgMLIuf!*h)>)XP-iy%47gOHhrI0azi@(2NDu6=eqo�nwUPTBg}V|DW==6CsH_=v+?80m|_2rNYS`5 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

ep}Q*Pf`yC~A0>qH+VGmU^N*Qs2EE?Fik(Q**z8 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 SF*t1C}%&a{?L`#6;rmvr!3%U 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 literal 0 HcmV?d00001 diff --git a/targets/ARCH/COMMON/common_lib.c b/targets/ARCH/COMMON/common_lib.c index d74b1c6d4f..c0a3e8a720 100644 --- a/targets/ARCH/COMMON/common_lib.c +++ b/targets/ARCH/COMMON/common_lib.c @@ -60,6 +60,12 @@ case USRP_X300_DEV: case NONE_DEV: printf("[%s] has not loaded a HW device.\n",((device->host_type == RAU_HOST) ? "RAU": "RRU")); break; + case ADRV9371_ZC706_DEV: + printf("[%s] has loaded ADRV9371_ZC706 device.\n",((device->host_type == RAU_HOST) ? "RAU": "RRU")); + break; + case UEDv2_DEV: + printf("[%s] has loaded UEDv2 device.\n",((device->host_type == RAU_HOST) ? "RAU": "RRU")); + break; default: printf("[%s] invalid HW device.\n",((device->host_type == RAU_HOST) ? "RAU": "RRU")); return -1; diff --git a/targets/ARCH/COMMON/common_lib.h b/targets/ARCH/COMMON/common_lib.h index 433e29e97d..7460c4849b 100644 --- a/targets/ARCH/COMMON/common_lib.h +++ b/targets/ARCH/COMMON/common_lib.h @@ -97,6 +97,10 @@ typedef enum { LMSSDR_DEV, /*!\brief device is NONE*/ NONE_DEV, + /*!\brief device is ADRV9371_ZC706 */ + ADRV9371_ZC706_DEV, + /*!\brief device is UEDv2 */ + UEDv2_DEV, MAX_RF_DEV_TYPE } dev_type_t; @@ -216,6 +220,14 @@ typedef struct { unsigned int sf_write_delay; // write delay in replay mode unsigned int eth_mtu; // ethernet MTU #endif + + //! number of samples per tti + unsigned int samples_per_tti; + //! the sample rate for receive. + double rx_sample_rate; + //! the sample rate for transmit. + double tx_sample_rate; + } openair0_config_t; /*! \brief RF mapping */ diff --git a/targets/COMMON/openairinterface5g_limits.h b/targets/COMMON/openairinterface5g_limits.h index 98de4a024f..4ce85f4cad 100644 --- a/targets/COMMON/openairinterface5g_limits.h +++ b/targets/COMMON/openairinterface5g_limits.h @@ -1,7 +1,7 @@ #ifndef OPENAIRINTERFACE5G_LIMITS_H_ #define OPENAIRINTERFACE5G_LIMITS_H_ -#if defined(CBMIMO1) || defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_LMSSDR) +#if defined(CBMIMO1) || defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_LMSSDR) || defined(OAI_ADRV9371_ZC706) # define NUMBER_OF_eNB_MAX 1 # define NUMBER_OF_RU_MAX 2 # define NUMBER_OF_UE_MAX 16 diff --git a/targets/RT/USER/lte-hwlat.c b/targets/RT/USER/lte-hwlat.c new file mode 100755 index 0000000000..9f5a3306aa --- /dev/null +++ b/targets/RT/USER/lte-hwlat.c @@ -0,0 +1,944 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "assertions.h" +#include "PHY/types.h" + +#include "PHY/defs.h" + +#include +#define GET_TIME_INIT(num) struct timeval _timers[num] +#define GET_TIME_VAL(num) gettimeofday(&_timers[num], NULL) +#define TIME_VAL_TO_MS(num) (((double)_timers[num].tv_sec*1000.0) + ((double)_timers[num].tv_usec/1000.0)) + +#undef MALLOC //there are two conflicting definitions, so we better make sure we don't use it at all +//#undef FRAME_LENGTH_COMPLEX_SAMPLES //there are two conflicting definitions, so we better make sure we don't use it at all + +#include "../../ARCH/COMMON/common_lib.h" + +#include "PHY/extern.h" +#include "SCHED/extern.h" +#include "LAYER2/MAC/extern.h" +#include "LAYER2/MAC/proto.h" + +#define RIFFA_CHANNEL_TESTER 0 +#define RIFFA_CHANNEL_TESTER2 0 +#define RIFFA_CHANNEL_DATA1 1 + +volatile int oai_exit = 0; + +openair0_config_t openair0_cfg[MAX_CARDS]; + +#if 0 +#define NB_ANTENNAS_RX 4 + +#define DevAssert(cOND) _Assert_(cOND, _Assert_Exit_, "") +#define malloc16(x) memalign(16,x) + +#ifdef 0 +static inline void* malloc16_clear( size_t size ) +{ +#ifdef __AVX2__ + void* ptr = memalign(32, size); +#else + void* ptr = memalign(16, size); +#endif + if(ptr) + memset( ptr, 0, size ); + return ptr; +} +#endif + +#endif + +typedef struct latency_stat { + uint64_t counter; + + uint64_t stat250; + uint64_t stat500; + uint64_t stat600; + uint64_t stat700; + uint64_t stat800; + + uint64_t stat1300; + uint64_t stat1500; + uint64_t stat2000; + uint64_t stat2500; + uint64_t stat3000; + + uint64_t stat880; + uint64_t stat960; + uint64_t stat1040; + uint64_t stat1120; + uint64_t stat1200; +} latency_stat_t; + + +typedef struct timing_stats { + char *name; + double min; + double max; + double total; + unsigned int count; +} timing_stats_t; + + +//static struct timespec get_timespec_diff( +// struct timespec *start, +// struct timespec *stop ) +//{ +// struct timespec result; +// +// if ( ( stop->tv_nsec - start->tv_nsec ) < 0 ) { +// result.tv_sec = stop->tv_sec - start->tv_sec - 1; +// result.tv_nsec = stop->tv_nsec - start->tv_nsec + 1000000000; +// } +// else { +// result.tv_sec = stop->tv_sec - start->tv_sec; +// result.tv_nsec = stop->tv_nsec - start->tv_nsec; +// } +// +// return result; +//} + + +//static void measure_time ( +// openair0_device *rf_device, +// struct timespec *start, +// struct timespec *stop, +// timing_stats_t *stats, +// boolean_t START, +// uint8_t PRINT_INTERVAL ) +//{ +// if ( START ) { +// clock_gettime( CLOCK_MONOTONIC, start ); +// } +// else { +// clock_gettime( CLOCK_MONOTONIC, stop ); +// +// struct timespec diff; +// double current = 0; +//// boolean_t show_stats = false; +// +// diff = get_timespec_diff( start, stop ); +// current = (double)diff.tv_sec * 1000000 + (double)diff.tv_nsec / 1000; +// +// if ( current > stats->max ) { +// stats->max = current; +//// show_stats = true; +// } +// if ( stats->min == 0 || current < stats->min ) { +// stats->min = current; +//// show_stats = true; +// } +// stats->total += current; +// +//// if ( show_stats ) { +//// rf_device.trx_get_stats_func( &rf_device ); +//// } +// +//// if ( stats->count % PRINT_INTERVAL == 0 ) { +//// double avg = stats->total / ( stats->count + 1 ); +//// printf( "[%s][%d] Current : %.2lf µs, Min : %.2lf µs, Max : %.2lf µs, Avg : %.2lf µs\n", +//// stats->count, stats->name, current, stats->min, stats->max, avg ); +//// } +// +// stats->count++; +// } +//} +int32_t **rxdata; +int32_t **txdata; + +int setup_ue_buffers(PHY_VARS_UE **phy_vars_ue, openair0_config_t *openair0_cfg); + +static inline void saif_meas(int frame_rx, int subframe_rx) { + static latency_stat_t __thread latency_stat; + static struct timespec __thread last= {0}; + struct timespec now; + clock_gettime(CLOCK_MONOTONIC_RAW, &now); + if ( last.tv_sec ) { + uint64_t diffTime = ((uint64_t)now.tv_sec *1000 *1000 *1000 + now.tv_nsec) - + ((uint64_t)last.tv_sec *1000 *1000 *1000 + last.tv_nsec); + diffTime/=1000; + latency_stat.counter++; + + if ( diffTime <= 800 ) { + if (diffTime < 250 ) + latency_stat.stat250++; + else if (diffTime < 500 ) + latency_stat.stat500++; + else if (diffTime < 600 ) + latency_stat.stat600++; + else if (diffTime < 700 ) + latency_stat.stat700++; + else + latency_stat.stat800++; + } + else if ( diffTime > 1200 ) { + if (diffTime < 1500 ) + latency_stat.stat1300++; + else if ( diffTime < 2000 ) + latency_stat.stat1500++; + else if ( diffTime < 2500 ) + latency_stat.stat2000++; + else if ( diffTime < 3000 ) + latency_stat.stat2500++; + else + latency_stat.stat3000++; + } + else + if (diffTime <= 880 ) + latency_stat.stat880++; + else if (diffTime <= 960 ) + latency_stat.stat960++; + else if (diffTime <= 1040 ) + latency_stat.stat1040++; + else if (diffTime < 1120 ) + latency_stat.stat1120++; + else + latency_stat.stat1200++; + + + if ( (diffTime>=1500) || ( !(frame_rx%1024) && subframe_rx == 0 ) ) { + time_t current=time(NULL); + printf("\n"); + printf("%.2f Period stats cnt=%7.7ld 0.. 250=%7.7ld 250.. 500=%7.7ld 500.. 600=%7.7ld 600.. 700=%7.7ld 700.. 800=%7.7ld - (frame_rx=%d) - %s", + now.tv_sec+(double)now.tv_nsec/1e9, + latency_stat.counter, + latency_stat.stat250, latency_stat.stat500, + latency_stat.stat600, latency_stat.stat700, + latency_stat.stat800, + frame_rx, + ctime(¤t)); + printf("%.2f Period stats cnt=%7.7ld 800.. 880=%7.7ld 880.. 960=%7.7ld 960..1040=%7.7ld 1040..1120=%7.7ld 1120..1200=%7.7ld - (frame_rx=%d) - %s", + now.tv_sec+(double)now.tv_nsec/1e9, + latency_stat.counter, + latency_stat.stat880, latency_stat.stat960, + latency_stat.stat1040, latency_stat.stat1120, + latency_stat.stat1200, + frame_rx, + ctime(¤t)); + printf("%.2f Period stats cnt=%7.7ld 1200..1300=%7.7ld 1300..1500=%7.7ld 1500..2000=%7.7ld 2000..2500=%7.7ld >3000=%7.7ld - (frame_rx=%d) - %s", + now.tv_sec+(double)now.tv_nsec/1e9, + latency_stat.counter, + latency_stat.stat1300, latency_stat.stat1500, + latency_stat.stat2000, latency_stat.stat2500, + latency_stat.stat3000, + frame_rx, + ctime(¤t)); + fflush(stdout); + } + } + last=now; +} +/* End of Changed by SYRTEM */ + +void exit_fun(const char* s) +{ + + if (s != NULL) { + printf("%s %s() Exiting OAI softmodem: %s\n",__FILE__, __FUNCTION__, s); + } + + oai_exit = 1; + +} + + +void init_thread(int sched_runtime, int sched_deadline, int sched_fifo, char * name) { + +#ifdef DEADLINE_SCHEDULER + if (sched_runtime!=0) { + struct sched_attr attr= {0}; + attr.size = sizeof(attr); + // This creates a .5 ms fpga_recv_cnt reservation + attr.sched_policy = SCHED_DEADLINE; + attr.sched_runtime = sched_runtime; + attr.sched_deadline = sched_deadline; + attr.sched_period = 0; + AssertFatal(sched_setattr(0, &attr, 0) == 0, + "[SCHED] main eNB thread: sched_setattr failed %s \n",perror(errno)); + LOG_I(HW,"[SCHED][eNB] eNB main deadline thread %lu started on CPU %d\n", + (unsigned long)gettid(), sched_getcpu()); + } + +#else +#ifdef CPU_AFFINITY + if (get_nprocs() >2) { + for (j = 1; j < get_nprocs(); j++) + CPU_SET(j, &cpuset); + } + AssertFatal( 0 == pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset)==0,""); +#endif + struct sched_param sp; + sp.sched_priority = sched_fifo; + AssertFatal(pthread_setschedparam(pthread_self(),SCHED_FIFO,&sp)==0, + "Can't set thread priority, Are you root?\n"); +#endif + + // Lock memory from swapping. This is a process wide call (not constraint to this thread). + mlockall(MCL_CURRENT | MCL_FUTURE); + pthread_setname_np( pthread_self(), name ); + +} + + +int main(void) +{ + int ret; + uint64_t i; + openair0_device rf_device; + openair0_timestamp timestamp = 0; + + int sub_frame = 0; + unsigned int frame_rx = 0; + + unsigned int nb_antennas_tx = 1; + unsigned int nb_antennas_rx = 1; + + + openair0_cfg[0].mmapped_dma = 0; + openair0_cfg[0].configFilename = NULL; + openair0_cfg[0].duplex_mode = duplex_mode_FDD; + + uint32_t **sendbuff = NULL; + uint32_t **recvbuff = NULL; + uint32_t expected_value = 0; + uint32_t received_value = 0; + int nsamp = 0; // 1 ms + int antenna_id = 1; + int c = 0; + int numIter = 0; + int fpga_loop = 0; +#if RIFFA_CHANNEL_DATA1 + uint64_t first_ts = 0; + uint8_t is_first_ts = 0; + uint64_t trx_read_cnt = 0; + uint32_t j = 0; + uint32_t err_cnt = 0; +#else + int failure = 0; +#endif +#if 0 // 5MHz BW + unsigned int nb_sample_per_tti = 7680; + openair0_cfg[0].sample_rate = 7.68e6; + openair0_cfg[0].samples_per_frame = nb_sample_per_tti*10; + openair0_cfg[0].rx_bw = 2.5e6; + openair0_cfg[0].tx_bw = 2.5e6; + openair0_cfg[0].num_rb_dl = 25; +#endif + +#if 0 // 10MHz BW + unsigned int nb_sample_per_tti = 15360; + openair0_cfg[0].sample_rate = 15.36e6; + openair0_cfg[0].samples_per_frame = nb_sample_per_tti*10; + openair0_cfg[0].rx_bw = 5.0e6; + openair0_cfg[0].tx_bw = 5.0e6; + openair0_cfg[0].num_rb_dl = 50; +#endif + +#if 1 // 20MHz BW + unsigned int nb_sample_per_tti = 30720; + openair0_cfg[0].sample_rate = 30.72e6; + openair0_cfg[0].samples_per_frame = nb_sample_per_tti*10; + openair0_cfg[0].rx_bw = 10.0e6; + openair0_cfg[0].tx_bw = 10.0e6; + openair0_cfg[0].num_rb_dl = 100; +#endif + + const char *openair_dir = getenv("OPENAIR_DIR"); + const char *ini_file = "/targets/ARCH/ADRV9371_ZC706/USERSPACE/PROFILES/ue.band7.tm1.PRB100.adrv9371-zc706_HWgain15dB.ini"; + + int readBlockSize; + void* rxp[nb_antennas_rx]; + void* txp[nb_antennas_tx]; + +// int32_t rxdata[1][nb_sample_per_tti*10+2048]; +#if 0 + int32_t **rxdata; +#endif +// init_thread(100000, 500000, sched_get_priority_max(SCHED_FIFO),"main UE"); + + printf("LTE HARDWARE Latency debug utility \n"); + printf("INIT data buffers \n"); + +#if 0 + rxdata = (int32_t**)memalign(32, nb_antennas_rx*sizeof(int32_t*) ); + rxdata[0] = (int32_t*)memalign(32,(nb_sample_per_tti*10+2048)*sizeof(int32_t)); +#else + rxdata = (int32_t**)malloc16( nb_antennas_rx*sizeof(int32_t*) ); + txdata = (int32_t**)malloc16( nb_antennas_tx*sizeof(int32_t*) ); + rxdata[0] = (int32_t*)malloc16_clear( 307200*sizeof(int32_t) ); + txdata[0] = (int32_t*)malloc16_clear( 307200*sizeof(int32_t) ); +#endif + + printf("INIT done\n"); + + memset(&rf_device, 0, sizeof(openair0_device)); + + rf_device.host_type = BBU_HOST; + openair0_cfg[0].duplex_mode = duplex_mode_FDD; + openair0_cfg[0].rx_num_channels = 1; + openair0_cfg[0].tx_num_channels = 1; + + // configure channel 0 + for (i=0; i= 2-307200 -> check expected value + status (RxoVer TxUnder) + + rxp[0] = (void*)&rxdata[0][0]; + txp[0] = (void*)&txdata[0][0]; + timestamp = 0; + nsamp = 30720; // 30720 => 1ms buffer + trx_read_cnt = 100000; // 1 loop => 1ms (10sec=10000; 15min = 900000) + antenna_id = 1; + int diff = 0; + int compare_start = 0; + + uint32_t looploop=0; + uint64_t error_cnt = 0; + uint64_t error_ts_start; + + for (i = 0; i < trx_read_cnt; i++) + { + +// printf("\n\n"); + + +// ret = rf_device.trx_read_func( &rf_device, ×tamp, &(rxp[0][(i*30720)%307200]), nsamp, antenna_id ); +// ret = rf_device.trx_read_func( &rf_device, ×tamp, recvbuff[0][(i*30720)%307200], nsamp, antenna_id ); + rxp[0] = (void*)&rxdata[0][(i*30720)%307200]; + GET_TIME_VAL(0); + ret = rf_device.trx_read_func( &rf_device, ×tamp, rxp, nsamp, antenna_id ); + GET_TIME_VAL(1); + + if (ret != nsamp) + { + printf("Error: nsamp received (%d) != nsamp required (%d)\n", ret, nsamp); + fflush(stdout); + return (-1); + } + if (!is_first_ts) + { + first_ts = timestamp; + is_first_ts = 1; + } + +// printf("%d - trx_read_func ret=%d - ts = %d - @=0x%08lx\n", i, ret, timestamp, rxp[0] ); + + txp[0] = (void*)&(txdata[0][ ((i*30720)%307200+2*30720)%307200 ]); +// printf(" i=%d @txp[0][0] = 0x%016lx\n", i, &(((uint32_t *)txp[0])[0]) ); +// printf(" i=%d @txp[0][1] = 0x%016lx\n", i, &(((uint32_t *)txp[0])[1]) ); + + for ( c = 0; c < nsamp; c++ ) + { + (((uint32_t *)txp[0])[c]) = ( (((timestamp+c+2*30720)<<4)&0x0000FFF0) + (((timestamp+c+2*30720)<<8)&0xFFF00000) ); + } + +// printf(" txp[0][%d] = 0x%x\n",0,((uint32_t *)(txp[0]))[0]); +// printf(" txp[0][%d] = 0x%x\n",1,((uint32_t *)(txp[0]))[1]); +// printf(" ...\n"); +// printf(" txp[0][%d] = 0x%x\n",nsamp-2,((uint32_t *)(txp[0]))[nsamp-2]); +// printf(" txp[0][%d] = 0x%x\n",nsamp-1,((uint32_t *)(txp[0]))[nsamp-1]); + + + ret = rf_device.trx_write_func( &rf_device, (timestamp+2*30720), txp, nsamp, antenna_id, false ); + if (ret != nsamp) + { + printf("Error: nsamp sent (%d) != nsamp required (%d)\n", ret, nsamp); + fflush(stdout); + return (-1); + } + +// printf("%d - trx_write_func ret=%d - ts = %d - @=0x%08lx\n", i, ret, (timestamp+2*30720), txp[0] ); + + + if (timestamp >= (first_ts + 2 * 307200)) + { + + // check Rx Overflow + + // check Tx Underflow + + // check Expected Value + for ( c = 0; c < nsamp; c++ ) + { + +// LOOPBACK +#if 1 + expected_value = ((timestamp + c)&0xFFFFFF); + received_value = ((uint32_t *)(rxp[0]))[c]; + received_value = ((received_value)&0xFFF) + ((received_value>>4)&0xFFF000); + + if (compare_start == 0) + { + compare_start = 1; + diff = expected_value - received_value; + } + received_value = (received_value + diff)&0xFFFFFF; + +#if 1 + + if (expected_value != received_value) + { + if(!error_cnt) + error_ts_start = (timestamp + c); + else + { + if (looploop < 32) + printf("%d - %d != %d - ts %d - raw 0x%x - diff %d\n", + looploop, + expected_value, received_value, + (timestamp + c), ((uint32_t *)(rxp[0]))[c], + diff); + looploop++; + } + + error_cnt++; + + if(!(error_cnt%102400)) + printf(" -> error detected : cnt=%d - start=%ld - stop=... diff=%d\n", + error_cnt, + error_ts_start, + diff); + + } + else + { + if(error_cnt) + { + printf(" -> error detected : cnt=%d - start=%ld - stop=%ld\n\n", + error_cnt, + error_ts_start, + (timestamp + c) ); + looploop=0; + } + error_cnt=0; + } +#endif +#endif + +// DEBUG mode 0 +#if 0 + received_value = ((uint32_t *)(rxp[0]))[c]; + received_value = ((received_value)&0xFFF) + ((received_value>>4)&0xFFF000); + received_value = (received_value&0xFFFFFF); + if (compare_start == 0) + { + compare_start = 1; + expected_value = received_value; + } + else + { + expected_value++; + expected_value = (expected_value&0xFFFFFF); + } + + if (expected_value != received_value) + { + if(!error_cnt) + error_ts_start = (timestamp + c); + error_cnt++; + } + else + { + if(error_cnt) + { + printf(" -> error detected : cnt=%d - start=%ld - stop=%ld\n\n", + error_cnt, + error_ts_start, + (timestamp + c) ); + } + error_cnt=0; + } + + + +// if (expected_value != received_value) +// { +// printf("%d -> %d != %d (ts+c=%ld)(raw=0x%08x)\n", +// looploop, +// expected_value, received_value, +// (timestamp + c), +// ((uint32_t *)(rxp[0]))[c] ); +// +// if (received_value) +// expected_value = received_value; +// } +#endif + + } + } + } + + printf("HwLat Application returns !!!\n"); + fflush(stdout); + sleep(1); + + + printf("\n"); + rf_device.trx_end_func( &rf_device ); + + sleep(1); + free(sendbuff[0]); + free(sendbuff); + free(recvbuff[0]); + free(recvbuff); + + exit(0); + + + + return(0); + + + +// puts( "* Frequency modification test" ); +// rf_device.trx_set_freq_func( &rf_device, &openair0_cfg[0], 0 ); + +// puts( "* Gain modification test" ); +// rf_device.trx_set_gains_func( &rf_device, &openair0_cfg[0] ); // NOT working (cf. initialization) + +//#if LTE_UE +// sleep(1); + GET_TIME_VAL(0); + for (i = 0; i < trx_read_cnt; i++) + { + printf("\n"); + ret = rf_device.trx_read_func( &rf_device, ×tamp, rxp, nsamp, antenna_id ); + printf("* timestamp=%ld\n", timestamp); +#if 1 + for (j = 0; j < nsamp; j++) + { + if ( ((uint32_t *)rxp[0])[j] != ((expected_value + j + timestamp)%307200) ) + { + err_cnt++; + printf("rxp[%06d]=0x%08x (expected 0x%08lx)\n", j, ((uint32_t *)rxp[0])[j], ((expected_value + j + timestamp)%307200) ); + } + if (err_cnt >= 128) + { + printf("Error: more than 128 expected value failed !\n"); + i = (trx_read_cnt - 1); + j = nsamp; + break; + } + } + //expected_value = (expected_value + nsamp)%307200; + expected_value = (expected_value)%307200; +#endif + } + GET_TIME_VAL(1); +//#endif + +/* ********** ********** */ +/* RIFFA_CHANNEL_DATA1 */ +/* ********** ********** */ +#if RIFFA_CHANNEL_DATA1 + +// rf_device.trx_get_stats_func( &rf_device ); + + rf_device.trx_end_func( &rf_device ); + + printf("\n* rf_device.trx_read_func(%d) x %d: %.6lf s\n\n", nsamp, i, ((TIME_VAL_TO_MS(1) - TIME_VAL_TO_MS(0)))/1000.0 ); + +// rf_device.trx_get_stats_func( &rf_device ); + +#if 0 + for (i = 0; i < nsamp; i++) + { +// if ( ((uint32_t *)rxp[0])[i] != i) +// { + err_cnt++; + printf("rxp[%06d]=0x%08x (expected 0x%08x)\n", i, ((uint32_t *)rxp[0])[i], i); +// } +// if (err_cnt > 256) +// { +// i = 307200; +// break; +// } + } +#endif +// for (i = 1024-32; i < 1024+32; i++) +// { +//// if ( ((uint32_t *)rxp[0])[i] != i) +//// { +// err_cnt++; +// printf("rxp[%06d]=0x%08x (expected 0x%08x)\n", i, ((uint32_t *)rxp[0])[i], i); +//// } +// if (err_cnt > 256) +// { +// i = 307200; +// break; +// } +// } + printf("RIFFA CHANNEL DATA1 done !!!\n"); + sleep(1); + return(0); +#endif +/* ********** ********** */ +/* RIFFA_CHANNEL_DATA1 */ +/* ********** ********** */ + + + + timestamp = 0; + ret = rf_device.trx_write_func( &rf_device, timestamp, (void**)sendbuff, nsamp, antenna_id, false ); + printf("* rf_device.trx_write_func returns %d\n", ret); + + sleep(1); + + nsamp = 30720; + numIter = 100000; + for ( c = 0; c < numIter; c++ ) + { + fpga_loop ++; + if ( !(fpga_loop % 1000) ) + { + printf("\rtest loop %d / %d", fpga_loop, numIter); + fflush(stdout); + } + +// printf("* TEST : %08d\n", (c+1)); +// measure_time( &rf_device, &start, &stop, &tx_stats, true, 10 ); +// ret = rf_device.trx_write_func( &rf_device, timestamp, (void**)sendbuff, nsamp, antenna_id, false ); +// printf("* rf_device.trx_write_func returns %d\n", ret); +// measure_time( &rf_device, &start, &stop, &tx_stats, false, 10 ); + +// sleep(1); + +// measure_time( &rf_device, &start, &stop, &tx_stats, true, 10 ); + ret = rf_device.trx_read_func( &rf_device, ×tamp, (void**)recvbuff, nsamp, antenna_id ); +// measure_time( &rf_device, &start, &stop, &tx_stats, false, 10 ); + + // Check the data + if (ret > 0) + { +/* ********** ********** */ +/* RIFFA_CHANNEL_TESTER */ +/* ********** ********** */ +#if RIFFA_CHANNEL_TESTER + failure = 0; + for (i = 0; i < ret; i++) + { + if ( ((i%1024) == 0) || ((i%1024) == 1) || ((i%1024) == 2) || ((i%1024) == 3) ) + { + if ( (recvbuff[0])[i] != (1020 + ((i%1024)+1)) ) + { + printf("* ERROR (buff[0])[%d]: %d, expected %d\n", i, (uint32_t)(recvbuff[0])[i], ((i%1024)+1) ); + failure = 1; + } + } + else if ( (recvbuff[0])[i] != ((i%1024)+1) ) + { + printf("* ERROR (buff[0])[%d]: %d, expected %d\n", i, (uint32_t)(recvbuff[0])[i], ((i%1024)+1) ); + failure = 1; + } + else + { + printf("* DONE (buff[0])[%d]: %d, expected %d\n", i, (uint32_t)(recvbuff[0])[i], ((i%1024)+1) ); + } + if(failure) + break; + } + if (failure) + printf("* ERROR recv %08d checked FAILURE ret=%d\n", (c+1), ret); + else + { +// printf("* DONE recv %08d checked SUCCESSFULLY ret=%d\n", (c+1), ret); + } +#endif +/* ********** ********** */ +/* RIFFA_CHANNEL_TESTER2*/ +/* ********** ********** */ +#if RIFFA_CHANNEL_TESTER2 +// printf("* ret=%d timestamp=%ld (%ld)\n", ret, timestamp, (timestamp%307200)); + failure = 0; + for (i = 0; i < ret; i++) + { +// printf("* (recvbuff[0])[%d]: %d\n", i, (uint32_t)(recvbuff[0])[i]); +// printf("* timestamp+(i/1024+1)*1024 - 3 + i%1024: %d\n", (timestamp-nsamp+(i/1024+1)*1024 - 3 + i%1024)%307200 ); + expected_value = ((((i%1024)+1)/*>>4*/)&0x00000FFF); + if ( ((i%1024) == 0) || ((i%1024) == 1) || ((i%1024) == 2) || ((i%1024) == 3) ) + { + expected_value = ((((timestamp-nsamp+(i/1024+1)*1024 - 3 + i%1024))%307200)); + if ( (recvbuff[0])[i] != expected_value) + { + if ( (expected_value == 0) && ((recvbuff[0])[i] != 4915200) ) + { + printf("* ERROR (buff[0])[%d]: %d, expected %d\n", i, (uint32_t)(recvbuff[0])[i], expected_value ); + failure = 1; + } + if ( (expected_value == 0) && ((recvbuff[0])[i] == 4915200) ) + { +// printf("* DONE loop in circular buffer\n"); + } + } + } + else if ( (recvbuff[0])[i] != expected_value ) + { + printf("* ERROR (buff[0])[%d]: %d, expected %d\n", i, (uint32_t)(recvbuff[0])[i], ((i%1024)+1) ); + failure = 1; + } + else + { +// printf("* DONE (buff[0])[%d]: %d, expected %d\n", i, (uint32_t)(recvbuff[0])[i], ((i%1024)+1) ); + } +// if(failure) +// break; + } + if (failure) + printf("* ERROR recv %08d checked FAILURE ret=%d\n", (c+1), ret); + else + { +// printf("* DONE recv %08d checked SUCCESSFULLY ret=%d\n", (c+1), ret); + } +#endif +#if LOOPBACK + +#endif + } + else + { + printf("* ERROR rf_device.trx_read_func returns %d\n", ret); + } + + } + printf("\n"); + rf_device.trx_end_func( &rf_device ); + + sleep(1); + free(sendbuff[0]); + free(sendbuff); + free(recvbuff[0]); + free(recvbuff); + + exit(0); + +// END IS HERE ! + + rf_device.trx_set_freq_func(&rf_device,&openair0_cfg[0],0); + + if (rf_device.trx_start_func(&rf_device) != 0 ) { + printf("Could not start the device\n"); + oai_exit=1; + } + + while(!oai_exit){ + + rxp[0] = (void*)&rxdata[0][sub_frame*nb_sample_per_tti]; + + readBlockSize = rf_device.trx_read_func( &rf_device, + ×tamp, + rxp, + nb_sample_per_tti, + 0); + + if ( readBlockSize != nb_sample_per_tti ) + oai_exit = 1; + + sub_frame++; + sub_frame%=10; + + if( sub_frame == 0) + frame_rx++; + + saif_meas(frame_rx, sub_frame); + } + + return(0); +} diff --git a/targets/RT/USER/lte-hwlat2.c b/targets/RT/USER/lte-hwlat2.c new file mode 100644 index 0000000000..67761be9a7 --- /dev/null +++ b/targets/RT/USER/lte-hwlat2.c @@ -0,0 +1,1282 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + + +/* ************************************************************************************************* + + USER GUIDE + + 1 - CONFIGURE TEST SESSION + see TESTS PARAMETERS section below + + 2 - COMPILATION CMD LINE (same as openair compilation) + - NO AVX SUPPORT + /usr/bin/cc -msse4.1 -mssse3 -std=gnu99 -Wall -Wstrict-prototypes -fno-strict-aliasing -rdynamic -funroll-loops -Wno-packed-bitfield-compat -fPIC -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_FCNTL_H=1 -DHAVE_ARPA_INET_H=1 -DHAVE_SYS_TIME_H=1 -DHAVE_SYS_SOCKET_H=1 -DHAVE_STRERROR=1 -DHAVE_SOCKET=1 -DHAVE_MEMSET=1 -DHAVE_GETTIMEOFDAY=1 -DHAVE_STDLIB_H=1 -DHAVE_MALLOC=1 -DHAVE_LIBSCTP -g -DMALLOC_CHECK_=3 -O2 -o lte-hwlat-test lte-hwlat2.c -lrt -lpthread -lm -ldl + - AVX2 Support + /usr/bin/cc -mavx2 -msse4.1 -mssse3 -std=gnu99 -Wall -Wstrict-prototypes -fno-strict-aliasing -rdynamic -funroll-loops -Wno-packed-bitfield-compat -fPIC -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_FCNTL_H=1 -DHAVE_ARPA_INET_H=1 -DHAVE_SYS_TIME_H=1 -DHAVE_SYS_SOCKET_H=1 -DHAVE_STRERROR=1 -DHAVE_SOCKET=1 -DHAVE_MEMSET=1 -DHAVE_GETTIMEOFDAY=1 -DHAVE_STDLIB_H=1 -DHAVE_MALLOC=1 -DHAVE_LIBSCTP -g -DMALLOC_CHECK_=3 -O2 -o lte-hwlat-test lte-hwlat2.c -lrt -lpthread -lm -ldl + + + 3 - RUN + sudo cset shield --force --kthread on -c 1-3 // for 4 cores + sudo cset shield --force --kthread on -c 1-7 // for 8 cores + sudo cset shield ./lte-hwlat-test + + 4 - remove cset shield + sudo cset shield --reset + + ***************************************************************************************************/ + +/* ************************************************************************************************* + * TESTS PARAMETERS + */ +#define HWLAT_LOOP_CNT 1000000 /* measurment loop count for each thread*/ +#define HWLAT_TTI_SLEEP_US 250 /* usleep duration -> IQ capture simulation (in µ seconds) */ + +#define RX_NB_TH 6 + +#define CALIB_RT_INTRUMENTATION 0 + +/* Laurent Thpmas instrumentation -> see openair2/UTIL/LOG/log.h for full implementation + -> This is a copy and paste implementation in this file for a self contained source */ +#define INSTRUMENTATION_LT_RDTSC 1 +/* SYRTEM rdtsc instrumentation implementation (see below for more infaormation) */ +#define INSTRUMENTATION_SYR_RDTSC 2 +/* SYRTEM instrumentation using clock_gettime MONOTONIC */ +#define INSTRUMENTATION_SYR_CLOCK_MONO 3 +/* SYRTEM instrumentation using clock_gettime REALTIME */ +#define INSTRUMENTATION_SYR_CLOCK_REALTIME 4 + +#define HWLAT_INSTRUMENTATION INSTRUMENTATION_LT_RDTSC + + +/* Statistics histogram output */ +#define HISTOGRAM_MIN_VALUE 0 +#define HISTOGRAM_MAX_VALUE 2000 +#define HISTOGRAM_STEP 1 +#define HISTOGRAM_SIZE ( ( ( HISTOGRAM_MAX_VALUE - HISTOGRAM_MIN_VALUE ) / HISTOGRAM_STEP ) + 1 ) + + + +/* *************************************************************************************************/ + + +/* + * INCLUDES + */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* For SYS_xxx definitions */ +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif +#include + + +/* From rt_wrapper.h +****************************************************************************************/ +#define gettid() syscall(__NR_gettid) // for gettid + +/* From common/utils/itti/assertions.h +****************************************************************************************/ +# define display_backtrace() + +#define _Assert_Exit_ \ +{ \ + fprintf(stderr, "\nExiting execution\n"); \ + display_backtrace(); \ + fflush(stdout); \ + fflush(stderr); \ + exit(EXIT_FAILURE); \ +} + +#define _Assert_(cOND, aCTION, fORMAT, aRGS...) \ +do { \ + if (!(cOND)) { \ + fprintf(stderr, "\nAssertion ("#cOND") failed!\n" \ + "In %s() %s:%d\n" fORMAT, \ + __FUNCTION__, __FILE__, __LINE__, ##aRGS); \ + aCTION; \ + } \ +} while(0) + +#define AssertFatal(cOND, fORMAT, aRGS...) _Assert_(cOND, _Assert_Exit_, fORMAT, ##aRGS) + + +/* From "openair1/PHY/TOOLS/time_meas.h" +****************************************************************************************/ +double cpu_freq_GHz; + + +typedef struct { + + long long in; + 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; + int trials; + int meas_flag; +} time_stats_t; + +static inline unsigned long long rdtsc_oai(void) __attribute__((always_inline)); +static inline unsigned long long rdtsc_oai(void) +{ + unsigned long long a, d; + __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d)); + return (d<<32) | a; +} + +double get_cpu_freq_GHz(void); + +static inline void reset_meas(time_stats_t *ts) { + + ts->trials=0; + ts->diff=0; + ts->diff_now=0; + ts->p_time=0; + ts->diff_square=0; + ts->max=0; + ts->meas_flag=0; + +} + +double estimate_MHz_syr(void); +static __inline__ uint64_t pickCyclesStart(void); +static __inline__ uint64_t pickCyclesStop(void); + + +/* From "openair2/UTIL/LOG/log.h" +****************************************************************************************/ +extern double cpuf; +extern double cpu_mhz_syr; + + +static __inline__ uint64_t rdtsc(void) { + uint64_t a, d; + __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d)); + return (d<<32) | a; +} + +typedef struct m { + uint64_t iterations; + uint64_t sum; + uint64_t maxArray[11]; +} Meas; + +static inline void printMeas(char * txt, Meas *M, int period) { + if (M->iterations%period == 0 ) { + char txt2[512]; + sprintf(txt2,"%s avg=%" PRIu64 " iterations=%" PRIu64 " max=%" + PRIu64 ":%" PRIu64 ":%" PRIu64 ":%" PRIu64 ":%" PRIu64 ":%" PRIu64 ":%" PRIu64 ":%" PRIu64 ":%" PRIu64 ":%" PRIu64 "\n", + txt, + M->sum/M->iterations, + M->iterations, + M->maxArray[1],M->maxArray[2], M->maxArray[3],M->maxArray[4], M->maxArray[5], + M->maxArray[6],M->maxArray[7], M->maxArray[8],M->maxArray[9],M->maxArray[10]); +// SYRTEM : just use printf do not include all LOG_X for this test +//#if DISABLE_LOG_X + printf("%s",txt2); +//#else +// LOG_W(PHY, "%s",txt2); +//#endif + } +} + +static inline int cmpint(const void* a, const void* b) { + uint64_t* aa=(uint64_t*)a; + uint64_t* bb=(uint64_t*)b; + return (int)(*aa-*bb); +} + +static inline uint64_t updateTimes(uint64_t start, Meas *M, int period, char * txt) { + if (start!=0) { + uint64_t end=rdtsc(); + long long diff=(end-start)/(cpuf*1000); + M->maxArray[0]=diff; + M->sum+=diff; + M->iterations++; + qsort(M->maxArray, 11, sizeof(uint64_t), cmpint); +// printMeas(txt,M,period); // SYRTEM : Printed only a the end of the measurment loop + return diff; + } + return 0; +} + +static inline uint64_t updateTimes_syr(uint64_t start, Meas *M, int period, char * txt) { + if (start!=0) { +// uint64_t end=rdtsc(); + uint64_t end=pickCyclesStop(); + long long diff=(long long)((double)(end-start)/(cpu_mhz_syr)); +// long long diff=(end-start)/(cpuf*1000); + M->maxArray[0]=diff; + M->sum+=diff; + M->iterations++; + qsort(M->maxArray, 11, sizeof(uint64_t), cmpint); +// printMeas(txt,M,period); // SYRTEM : Printed only a the end of the measurment loop + return diff; + } + return 0; +} + + + + +#define initRefTimes(a) static __thread Meas a= {0} +#define pickTime(a) uint64_t a=rdtsc() +#define pickTime_syr(a) uint64_t a=pickCyclesStart() +#define readTime(a) a + + + + + + +/* + * DEFINES + */ + +#define TIMESPEC_TO_DOUBLE_US( t ) ( ( (double)t.tv_sec * 1000000 ) + ( (double)t.tv_nsec / 1000 ) ) + +typedef struct histo_time { + double max; + unsigned int count; +} histo_time_t; + +static void measure_time ( struct timespec *start, struct timespec *stop, uint8_t START, uint16_t PRINT_INTERVAL ); +static struct timespec get_timespec_diff( struct timespec *start, struct timespec *stop ); +void histogram_save_in_csv( histo_time_t *histo , char *file_prefix); +histo_time_t *histogram_init( histo_time_t *histo ); +void histogram_store_value( histo_time_t *histo, double value ); + + +#define FIFO_PRIORITY 40 + + + +#define true 1 +#define false 0 + + + + +/* + * STRUCTURES + */ + + +/* stub of UE_rxtx_proc full structure in openair1/PHY/defs.h +*/ +typedef struct UE_rxtx_proc { + int instance_cnt_rxtx; + pthread_t pthread_rxtx; + pthread_cond_t cond_rxtx; + pthread_mutex_t mutex_rxtx; + + + int sub_frame_start; + int sub_frame_step; + unsigned long long gotIQs; + + + unsigned long syr_rdtsc_rxtx_th_unlock_iteration; + uint64_t syr_rdtsc_ue_th_got_iq; + double syr_rdtsc_rxtx_th_unlock; + double syr_rdtsc_rxtx_th_unlock_max; + double syr_rdtsc_rxtx_th_unlock_mean; + double syr_rdtsc_rxtx_th_unlock_min; + histo_time_t *syr_rdtsc_rxtx_th_unlock_histogram; + +} UE_rxtx_proc_t; + + + +/* this structure is used to pass both UE phy vars and + * proc to the function UE_thread_rxn_txnp4 + */ +struct rx_tx_thread_data { + /* PHY_VARS_UE *UE; */ // UE phy vars not used for this test + UE_rxtx_proc_t *proc; // We use a stub of rxtx_proc see definition above +}; + + +// ODD / EVEN Scheduling +#if 0 +typedef struct threads_s { + int iq; + int odd; + int even; +} threads_t; +threads_t threads = { -1, -1, -1 }; // Core number for each thread (iq=3, even=2, odd=1) +#endif + +// SLOT 0 / SLOT 1 parallelization +typedef struct threads_s { + int iq; + int one; + int two; + int three; + int four; + int five; + int six; + int slot1_proc_one; + int slot1_proc_two; + int slot1_proc_three; +} threads_t; +threads_t threads= {7,6,5,4,3,2,1,-1,-1,-1}; + +/* + * FUNCTIONS DEFINITION + */ + +void *UE_thread(void *arg); +void init_UE(int nb_inst); + + + +/* + * GLOBALS VARIABLES + */ + +volatile int oai_exit = 0; +int th_count = 0; +double cpuf; +double cpu_mhz_syr; + + + + + + + +pthread_t pthread_ue; +pthread_attr_t attr_ue; + +UE_rxtx_proc_t **rxtx_proc; + +struct timespec even_start, even_stop; +struct timespec odd_start, odd_stop; + +histo_time_t *th_wake_histogram; + + +/* + * FUNCTIONS + */ + + + +void exit_fun(const char* s) { + if ( s != NULL ) { + printf("%s %s() Exiting OAI softmodem: %s\n",__FILE__, __FUNCTION__, s); + } + + oai_exit = 1; +} + + + + +void init_thread(int sched_runtime, int sched_deadline, int sched_fifo, cpu_set_t *cpuset, char * name) { + +#ifdef DEADLINE_SCHEDULER + if (sched_runtime!=0) { + struct sched_attr attr= {0}; + attr.size = sizeof(attr); + attr.sched_policy = SCHED_DEADLINE; + attr.sched_runtime = sched_runtime; + attr.sched_deadline = sched_deadline; + attr.sched_period = 0; + AssertFatal(sched_setattr(0, &attr, 0) == 0, + "[SCHED] %s thread: sched_setattr failed %s \n", name, strerror(errno)); + LOG_I(HW,"[SCHED][eNB] %s deadline thread %lu started on CPU %d\n", + name, (unsigned long)gettid(), sched_getcpu()); + } + +#else + if (CPU_COUNT(cpuset) > 0) + AssertFatal( 0 == pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), cpuset), ""); + struct sched_param sp; + sp.sched_priority = sched_fifo; + AssertFatal(pthread_setschedparam(pthread_self(),SCHED_FIFO,&sp)==0, + "Can't set thread priority, Are you root?\n"); + /* Check the actual affinity mask assigned to the thread */ + cpu_set_t *cset=CPU_ALLOC(CPU_SETSIZE); + if (0 == pthread_getaffinity_np(pthread_self(), CPU_ALLOC_SIZE(CPU_SETSIZE), cset)) { + char txt[512]={0}; + for (int j = 0; j < CPU_SETSIZE; j++) + if (CPU_ISSET(j, cset)) + sprintf(txt+strlen(txt), " %d ", j); + printf("CPU Affinity of thread %s is %s\n", name, txt); + } + CPU_FREE(cset); +#endif + + // Lock memory from swapping. This is a process wide call (not constraint to this thread). + mlockall(MCL_CURRENT | MCL_FUTURE); + pthread_setname_np( pthread_self(), name ); + +// SYRTEM : Synchronization thread is not simulated -> just ignore this part of code +// // LTS: this sync stuff should be wrong +// printf("waiting for sync (%s)\n",name); +// pthread_mutex_lock(&sync_mutex); +// printf("Locked sync_mutex, waiting (%s)\n",name); +// while (sync_var<0) +// pthread_cond_wait(&sync_cond, &sync_mutex); +// pthread_mutex_unlock(&sync_mutex); + printf("started %s as PID: %ld\n",name, gettid()); +} + +void init_UE(int nb_inst) +{ + int inst; + for (inst=0; inst < nb_inst; inst++) { + // UE->rfdevice.type = NONE_DEV; +// SYRTEM : we use a stub of phy_var_ue +// PHY_VARS_UE *UE = PHY_vars_UE_g[inst][0]; +// AssertFatal(0 == pthread_create(&UE->proc.pthread_ue, &UE->proc.attr_ue, UE_thread, (void*)UE), ""); + AssertFatal( 0 == pthread_create( &pthread_ue, &attr_ue, UE_thread, NULL ), "" ); + } + + printf("UE threads created by %ld\n", gettid()); +} + + +// SYRTEM - UE synchronization thread is not used in this latency test program +//static void *UE_thread_synch(void *arg) { +// ... +//} + + +// SYRTEM - Stub of UE_thread_rxn_txnp4 +// -> No DSP scheduled +// -> Only thread initialization and thread wake up are kept + +static void *UE_thread_rxn_txnp4(void *arg){ + static __thread int UE_thread_rxtx_retval; + struct rx_tx_thread_data *rtd = arg; + UE_rxtx_proc_t *proc = rtd->proc; + + proc->instance_cnt_rxtx=-1; + + char threadname[256]; + sprintf(threadname,"UE_%d_proc_%d", 0, proc->sub_frame_start); + + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + + if ( (proc->sub_frame_start)%RX_NB_TH == 0 && threads.one != -1 ) + CPU_SET(threads.one, &cpuset); + if ( (proc->sub_frame_start)%RX_NB_TH == 1 && threads.two != -1 ) + CPU_SET(threads.two, &cpuset); + if ( (proc->sub_frame_start)%RX_NB_TH == 2 && threads.three != -1 ) + CPU_SET(threads.three, &cpuset); + if ( (proc->sub_frame_start)%RX_NB_TH == 3 && threads.four != -1 ) + CPU_SET(threads.four, &cpuset); + if ( (proc->sub_frame_start)%RX_NB_TH == 4 && threads.five != -1 ) + CPU_SET(threads.five, &cpuset); + if ( (proc->sub_frame_start)%RX_NB_TH == 5 && threads.six != -1 ) + CPU_SET(threads.six, &cpuset); + //CPU_SET(threads.three, &cpuset); + init_thread(900000,1000000 , FIFO_PRIORITY-1, &cpuset, + threadname); + + + proc->syr_rdtsc_rxtx_th_unlock_iteration=0; + proc->syr_rdtsc_rxtx_th_unlock_max=0; + proc->syr_rdtsc_rxtx_th_unlock_mean=0; + proc->syr_rdtsc_rxtx_th_unlock_min=1000; + proc->syr_rdtsc_rxtx_th_unlock_histogram = NULL; + proc->syr_rdtsc_rxtx_th_unlock_histogram = histogram_init (proc->syr_rdtsc_rxtx_th_unlock_histogram); + + while (!oai_exit) { + if (pthread_mutex_lock(&proc->mutex_rxtx) != 0) { + printf("[SCHED][UE] error locking mutex for UE RXTX\n" ); + exit_fun("nothing to add"); + } + while (proc->instance_cnt_rxtx < 0) { + // most of the time, the thread is waiting here + pthread_cond_wait( &proc->cond_rxtx, &proc->mutex_rxtx ); + } + if (pthread_mutex_unlock(&proc->mutex_rxtx) != 0) { + printf("[SCHED][UE] error unlocking mutex for UE RXn_TXnp4\n" ); + exit_fun("nothing to add"); + } + + + proc->syr_rdtsc_rxtx_th_unlock = (double)(pickCyclesStop()-proc->syr_rdtsc_ue_th_got_iq )/cpu_mhz_syr; + + proc->syr_rdtsc_rxtx_th_unlock_iteration++; + + proc->syr_rdtsc_rxtx_th_unlock_mean += proc->syr_rdtsc_rxtx_th_unlock; + if ( proc->syr_rdtsc_rxtx_th_unlock_max < proc->syr_rdtsc_rxtx_th_unlock ) + proc->syr_rdtsc_rxtx_th_unlock_max = proc->syr_rdtsc_rxtx_th_unlock; + if ( proc->syr_rdtsc_rxtx_th_unlock_min > proc->syr_rdtsc_rxtx_th_unlock ) + proc->syr_rdtsc_rxtx_th_unlock_min = proc->syr_rdtsc_rxtx_th_unlock; + histogram_store_value( proc->syr_rdtsc_rxtx_th_unlock_histogram, proc->syr_rdtsc_rxtx_th_unlock ); + + + if (pthread_mutex_lock(&proc->mutex_rxtx) != 0) { + printf("[SCHED][UE] error locking mutex for UE RXTX\n" ); + exit_fun("noting to add"); + } + proc->instance_cnt_rxtx--; + if (pthread_mutex_unlock(&proc->mutex_rxtx) != 0) { + printf("[SCHED][UE] error unlocking mutex for UE RXTX\n" ); + exit_fun("noting to add"); + } + } + +// thread finished + free(arg); + return &UE_thread_rxtx_retval; +} + + + + +void *UE_thread(void *arg) { + + int i = 0; + int hw_loop_cnt = 0; + + int nb_threads = RX_NB_TH; + char threadname[128]; + cpu_set_t cpuset; + struct rx_tx_thread_data *rtd; + UE_rxtx_proc_t *proc; + + CPU_ZERO( &cpuset ); + + if ( threads.iq != -1 ) + CPU_SET( threads.iq, &cpuset ); + init_thread( 100000, 500000, FIFO_PRIORITY, &cpuset, "HDW Threads" ); + + sprintf( threadname, "Main UE %d", 0 ); + pthread_setname_np( pthread_self(), threadname ); + + +// init_UE_threads(UE) + pthread_attr_init( &attr_ue ); + pthread_attr_setstacksize( &attr_ue, 8192 );//5*PTHREAD_STACK_MIN); + + for ( i = 0; i < nb_threads; i++ ) { + + printf("\n"); + + rtd = calloc( 1, sizeof( struct rx_tx_thread_data ) ); + if ( rtd == NULL ) + abort(); + + rtd->proc = rxtx_proc[i]; // &UE->proc.proc_rxtx[i]; + + pthread_mutex_init( &rxtx_proc[i]->mutex_rxtx ,NULL ); // pthread_mutex_init( &UE->proc.proc_rxtx[i].mutex_rxtx,NULL ); + pthread_cond_init( &rxtx_proc[i]->cond_rxtx, NULL ); // pthread_cond_init(&UE->proc.proc_rxtx[i].cond_rxtx,NULL); + rtd->proc->sub_frame_start=i; + rtd->proc->sub_frame_step=nb_threads; + printf("Init_UE_threads rtd %d proc %d nb_threads %d i %d\n",rtd->proc->sub_frame_start, rxtx_proc[i]->sub_frame_start,nb_threads, i); + pthread_create( &rxtx_proc[i]->pthread_rxtx, NULL, UE_thread_rxn_txnp4, rtd ); // pthread_create( &UE->proc.proc_rxtx[i].pthread_rxtx, NULL, UE_thread_rxn_txnp4, rtd); + + usleep(1000); + + } +// init_UE_threads(UE) + + + + int sub_frame=-1; + while ( !oai_exit ) { + + sub_frame++; + sub_frame %= 10; + + proc = rxtx_proc[hw_loop_cnt%RX_NB_TH]; + + // Simulate IQ reception + usleep( HWLAT_TTI_SLEEP_US ); + + proc->syr_rdtsc_ue_th_got_iq = pickCyclesStart(); + + AssertFatal(pthread_mutex_lock(&proc->mutex_rxtx) ==0,""); + + proc->instance_cnt_rxtx++; + if ( proc->instance_cnt_rxtx == 0 ) { + if ( pthread_cond_signal( &proc->cond_rxtx) != 0 ) { + exit_fun( "nothing to add" ); + } + } else { + if ( proc->instance_cnt_rxtx > 2 ) + exit_fun( "instance_cnt_rxtx > 2" ); + } + + AssertFatal( pthread_cond_signal( &proc->cond_rxtx ) == 0 ,"" ); + AssertFatal(pthread_mutex_unlock(&proc->mutex_rxtx) ==0,""); + + + /* Do not go indefinitely */ + hw_loop_cnt++; + + if ( hw_loop_cnt%1000 == 0 ) + { + printf("\r%d/%d",hw_loop_cnt,HWLAT_LOOP_CNT*RX_NB_TH); + fflush(stdout); + } + + if (hw_loop_cnt >= HWLAT_LOOP_CNT*RX_NB_TH) + { + printf("\n\n"); + for (i=0 ; ipthread_rxtx,threadname,128 ); + printf("RxTX Thread unlock latency on thread %s (it. %ld) (us) : max=%8.3f - mean=%8.3f - min=%8.3f\n", + threadname, + proc->syr_rdtsc_rxtx_th_unlock_iteration, + proc->syr_rdtsc_rxtx_th_unlock_max, + proc->syr_rdtsc_rxtx_th_unlock_mean/proc->syr_rdtsc_rxtx_th_unlock_iteration, + proc->syr_rdtsc_rxtx_th_unlock_min); + + histogram_save_in_csv(proc->syr_rdtsc_rxtx_th_unlock_histogram, threadname ); + + } + printf("\n\n"); + oai_exit = 1; + } + + } // while !oai_exit + + oai_exit = 1; + + return NULL; +} + +#define CALIB_LOOP_CNT 1000000 //2000000000 +#define CALIB_LOOP_REPORT_PERIOD 1000 +#define CALIB_LOOP_UP_THRESHOLD 20 // 20 -> for NG Intel flat model +#define CALIB_USLEEP 250 + +int main( void ) +{ + + int i; + + +#if CALIB_RT_INTRUMENTATION + uint32_t calib_loop_count; + + + uint64_t lt_overhead_cur; + uint64_t lt_overhead_min; + uint64_t lt_overhead_max; + uint64_t lt_overhead_mean; + histo_time_t *lt_overhead_histogram = NULL; + + uint64_t lt_syr_overhead_cur; + uint64_t lt_syr_overhead_min; + uint64_t lt_syr_overhead_max; + uint64_t lt_syr_overhead_mean; + histo_time_t *lt_syr_overhead_histogram = NULL; + + double syr_rdtsc_overhead_cur; + double syr_rdtsc_overhead_min; + double syr_rdtsc_overhead_max; + double syr_rdtsc_overhead_mean; + histo_time_t *syr_rdtsc_overhead_histogram = NULL; + + double syr_clock_gettime_overhead_cur; + double syr_clock_gettime_overhead_min; + double syr_clock_gettime_overhead_max; + double syr_clock_gettime_overhead_mean; + histo_time_t *syr_clock_gettime_overhead_histogram = NULL; + + + uint64_t cycles_start; + uint64_t cycles_stop; + + uint64_t cycles_diff; + uint64_t cycles_diff_max = 0; + + + struct timespec start; + struct timespec stop; +#endif + + cpuf = get_cpu_freq_GHz(); + cpu_mhz_syr = estimate_MHz_syr(); + + printf("\n\ncpuf %f - cpu_mhz_syr %f\n\n", cpuf, cpu_mhz_syr); + +#if CALIB_RT_INTRUMENTATION + + /* ********************************************************************************************** */ + /* CALIBRATION */ + /* ********************************************************************************************** */ + + /* ************************************************************* + Laurent Thomas instrumentation overhead + */ + printf("\nCalibrating OAI Realtime Instrumentation (Laurent Thomas imp.)\n"); + calib_loop_count = CALIB_LOOP_CNT; + initRefTimes(lt_instru_calib); // Laurent Thomas realtime instrumentation calibration measurments + lt_overhead_min = 1000; + lt_overhead_max = 0; + lt_overhead_mean = 0; + lt_overhead_histogram = histogram_init( lt_overhead_histogram ); + while(calib_loop_count) + { + pickTime(lt_start); + +// asm volatile(""); // Nop instruction + usleep(CALIB_USLEEP); + + lt_overhead_cur = updateTimes( readTime(lt_start), + <_instru_calib, + CALIB_LOOP_CNT, + "Laurent Thomas realtime instrumentation calibration measurments"); + + lt_overhead_mean += lt_overhead_cur; + if ( lt_overhead_max < lt_overhead_cur ) + lt_overhead_max = lt_overhead_cur; + if ( lt_overhead_min > lt_overhead_cur ) + lt_overhead_min = lt_overhead_cur; + histogram_store_value( lt_overhead_histogram, (double)lt_overhead_cur ); + + if ( calib_loop_count%CALIB_LOOP_REPORT_PERIOD == 0 ) + { + printf("\r%d/%d",calib_loop_count,CALIB_LOOP_CNT); + fflush(stdout); + } + + calib_loop_count--; + } + lt_overhead_mean = lt_overhead_mean/CALIB_LOOP_CNT; + +// printMeas("\nLaurent Thomas realtime instrumentation calibration measurments",<_instru_calib,CALIB_LOOP_CNT); + + + /* ************************************************************* + Laurent Thomas instrumentation overhead + */ + printf("\nCalibrating OAI Realtime Instrumentation - SYRTEM revisited\n"); + calib_loop_count = CALIB_LOOP_CNT; + initRefTimes(lt_instru_calib_syr); // Laurent Thomas realtime instrumentation calibration measurments + lt_syr_overhead_min = 1000; + lt_syr_overhead_max = 0; + lt_syr_overhead_mean = 0; + lt_syr_overhead_histogram = histogram_init( lt_syr_overhead_histogram ); + while(calib_loop_count) + { + pickTime_syr(lt_start_syr); + +// asm volatile(""); // Nop instruction + usleep(CALIB_USLEEP); + + lt_syr_overhead_cur = updateTimes_syr(readTime(lt_start_syr), <_instru_calib_syr, CALIB_LOOP_CNT, "Laurent Thomas realtime instrumentation calibration measurments (SYRTEM Update)"); + + lt_syr_overhead_mean += lt_syr_overhead_cur; + if ( lt_syr_overhead_max < lt_syr_overhead_cur ) + lt_syr_overhead_max = lt_syr_overhead_cur; + if ( lt_syr_overhead_min > lt_syr_overhead_cur ) + lt_syr_overhead_min = lt_syr_overhead_cur; + histogram_store_value( lt_syr_overhead_histogram, (double)lt_syr_overhead_cur ); + + if ( calib_loop_count%CALIB_LOOP_REPORT_PERIOD == 0 ) + { + printf("\r%d/%d",calib_loop_count,CALIB_LOOP_CNT); + fflush(stdout); + } + + calib_loop_count--; + } + lt_syr_overhead_mean = lt_syr_overhead_mean / CALIB_LOOP_CNT; + +// printMeas("\nLaurent Thomas realtime instrumentation calibration measurments (SYRTEM Update)",<_instru_calib_syr,CALIB_LOOP_CNT); + + + + /* ************************************************************* + SYRTEM RDTSC instrumentation overhead + */ + printf("\nCalibrating SYRTEM RDTSC Realtime Instrumentation\n"); + calib_loop_count = CALIB_LOOP_CNT; + syr_rdtsc_overhead_min = 1000; + syr_rdtsc_overhead_max = 0; + syr_rdtsc_overhead_mean = 0; + syr_rdtsc_overhead_histogram = histogram_init( syr_rdtsc_overhead_histogram ); + while(calib_loop_count) + { + + cycles_start = pickCyclesStart(); + +// asm volatile(""); // Nop instruction + usleep(CALIB_USLEEP); + + cycles_stop = pickCyclesStop(); + + cycles_diff = cycles_stop - cycles_start; + if(cycles_diff_max < cycles_diff) + cycles_diff_max = cycles_diff; + + syr_rdtsc_overhead_cur = (double)cycles_diff/cpu_mhz_syr; + + syr_rdtsc_overhead_mean += syr_rdtsc_overhead_cur; + if ( syr_rdtsc_overhead_max < syr_rdtsc_overhead_cur ) + syr_rdtsc_overhead_max = syr_rdtsc_overhead_cur; + if ( syr_rdtsc_overhead_min > syr_rdtsc_overhead_cur ) + syr_rdtsc_overhead_min = syr_rdtsc_overhead_cur; + histogram_store_value( syr_rdtsc_overhead_histogram, (double)syr_rdtsc_overhead_cur ); + + if ( calib_loop_count%CALIB_LOOP_REPORT_PERIOD == 0 ) + { + printf("\r%d/%d",calib_loop_count,CALIB_LOOP_CNT); + fflush(stdout); + } + + calib_loop_count--; + } + syr_rdtsc_overhead_mean = syr_rdtsc_overhead_mean / CALIB_LOOP_CNT; + +// printf("\nSYRTEM RDTSV realtime instrumentation calibration : cycles_diff_max %ld - tsc_duration_max %f us\n ", cycles_diff_max, tsc_duration_max); + + + + /* ************************************************************* + SYRTEM Clock_gettime MONOTONIC instrumentation overhead + */ + printf("\nCalibrating SYRTEM clock_gettime Realtime Instrumentation\n"); + calib_loop_count = CALIB_LOOP_CNT; + syr_clock_gettime_overhead_min = 1000; + syr_clock_gettime_overhead_max = 0; + syr_clock_gettime_overhead_mean = 0; + syr_clock_gettime_overhead_histogram = histogram_init( syr_clock_gettime_overhead_histogram ); + while(calib_loop_count) + { + + clock_gettime( CLOCK_MONOTONIC, &start ); + +// asm volatile(""); // Nop instruction + usleep(CALIB_USLEEP); + + clock_gettime( CLOCK_MONOTONIC, &stop ); + syr_clock_gettime_overhead_cur = TIMESPEC_TO_DOUBLE_US( get_timespec_diff( &start, &stop ) ); + + syr_clock_gettime_overhead_mean += syr_clock_gettime_overhead_cur; + if ( syr_clock_gettime_overhead_max < syr_clock_gettime_overhead_cur ) + syr_clock_gettime_overhead_max = syr_clock_gettime_overhead_cur; + if ( syr_clock_gettime_overhead_min > syr_clock_gettime_overhead_cur ) + syr_clock_gettime_overhead_min = syr_clock_gettime_overhead_cur; + histogram_store_value( syr_clock_gettime_overhead_histogram, (double)syr_clock_gettime_overhead_cur ); + + if ( calib_loop_count%CALIB_LOOP_REPORT_PERIOD == 0 ) + { + printf("\r%d/%d",calib_loop_count,CALIB_LOOP_CNT); + fflush(stdout); + } + + calib_loop_count--; + } + syr_clock_gettime_overhead_mean = syr_clock_gettime_overhead_mean / CALIB_LOOP_CNT; + +// printf("\nSYRTEM clock_gettime MONOTONIC calibration : clock_gettime duration_max %f us\n ", max); + + + printf("\n"); + + printf("OAI LT RT profiling overhead (it. %d) (us) : max=%8ld - mean=%8ld - min=%8ld\n", + CALIB_LOOP_CNT, + lt_overhead_max, lt_overhead_mean, lt_overhead_min); + printf("OAI SYR RT profiling overhead (it. %d) (us) : max=%8ld - mean=%8ld - min=%8ld\n", + CALIB_LOOP_CNT, + lt_syr_overhead_max, lt_syr_overhead_mean, lt_syr_overhead_min); + printf("SYR RDTSC profiling overhead (it. %d) (us) : max=%8.3f - mean=%8.3f - min=%8.3f\n", + CALIB_LOOP_CNT, + syr_rdtsc_overhead_max, syr_rdtsc_overhead_mean, syr_rdtsc_overhead_min); + printf("SYR clock_gettime profiling overhead (it. %d) (us): max=%8.3f - mean=%8.3f - min=%8.3f\n", + CALIB_LOOP_CNT, + syr_clock_gettime_overhead_max, syr_clock_gettime_overhead_mean, syr_clock_gettime_overhead_min); + + return 0; +#endif + + rxtx_proc = calloc( RX_NB_TH, sizeof( UE_rxtx_proc_t* ) ); + for (i=0 ; i + * Small changes by David Sterba + * + */ + + struct timespec start; + struct timespec stop; + + unsigned long long int cycles[2]; /* must be 64 bit */ + double microseconds; /* total time taken */ + + + /* get this function in cached memory */ + cycles[0] = rdtsc (); + clock_gettime( CLOCK_REALTIME, &start ); + + /* we don't trust that this is any specific length of time */ + usleep (100000); + + cycles[1] = rdtsc (); + clock_gettime( CLOCK_REALTIME, &stop ); + microseconds = TIMESPEC_TO_DOUBLE_US( get_timespec_diff( &start, &stop ) ); + + unsigned long long int elapsed = 0; + if (cycles[1] < cycles[0]) + { + //printf("c0 = %llu c1 = %llu",cycles[0],cycles[1]); + elapsed = UINT32_MAX - cycles[0]; + elapsed = elapsed + cycles[1]; + //printf("c0 = %llu c1 = %llu max = %llu elapsed=%llu\n",cycles[0], cycles[1], UINT32_MAX,elapsed); + } + else + { + elapsed = cycles[1] - cycles[0]; + // printf("\nc0 = %llu c1 = %llu elapsed=%llu\n",cycles[0], cycles[1],elapsed); + } + + double mhz = elapsed / microseconds; + + + //printf("%llg MHz processor (estimate). diff cycles=%llu microseconds=%llu \n", mhz, elapsed, microseconds); + //printf("%g elapsed %llu microseconds %llu\n",mhz, elapsed, microseconds); + return (mhz); +} + + + +/** TSC Timestamp references + + - http://oliveryang.net/2015/09/pitfalls-of-TSC-usage/#312-tsc-sync-behaviors-on-smp-system + - https://www.intel.com/content/www/us/en/embedded/training/ia-32-ia-64-benchmark-code-execution-paper.html + - https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-vol-3b-part-2-manual.pdf + + - http://blog.tinola.com/?e=54 + - https://stackoverflow.com/questions/24392392/getting-tsc-frequency-from-proc-cpuinfo !! warning on turbo + - https://gist.github.com/nfarring/1624742 + - https://www.lmax.com/blog/staff-blogs/2015/10/25/time-stamp-counters/ + - https://github.com/cyring/CoreFreq + - https://patchwork.kernel.org/patch/9438133/ --> TSC_ADJUST - to be checked + -> https://software.intel.com/en-us/forums/software-tuning-performance-optimization-platform-monitoring/topic/388964 + - https://www.google.fr/url?sa=t&rct=j&q=&esrc=s&source=web&cd=11&ved=0ahUKEwjwpJbem5DVAhWBDpoKHXv0Dt04ChAWCCIwAA&url=https%3A%2F%2Fsigarra.up.pt%2Ffeup%2Fpt%2Fpub_geral.show_file%3Fpi_gdoc_id%3D809041&usg=AFQjCNFHTMjmjWMG6QSVnLDflEoU0RavKw&cad=rja + - https://unix4lyfe.org/benchmarking/ + - https://stackoverflow.com/questions/27693145/rdtscp-versus-rdtsc-cpuid + - https://github.com/dterei/tsc/blob/master/tsc.h + - https://software.intel.com/en-us/forums/intel-isa-extensions/topic/280440 + - https://stackoverflow.com/questions/19941588/wrong-clock-cycle-measurements-with-rdtsc + + + +*/ + + + + + + +static __inline__ uint64_t pickCyclesStart(void) { + unsigned cycles_low, cycles_high; + asm volatile( "CPUID\n\t" // serialize + "RDTSC\n\t" // read clock + "MOV %%edx, %0\n\t" + "MOV %%eax, %1\n\t" + : "=r" (cycles_high), "=r" (cycles_low) + :: "%rax", "%rbx", "%rcx", "%rdx" ); +return ((uint64_t) cycles_high << 32) | cycles_low; +} + +static __inline__ uint64_t pickCyclesStop(void) { + uint32_t eax, edx; + + __asm__ __volatile__("rdtscp" + : "=a" (eax), "=d" (edx) + : + : "%ecx", "memory"); + +return (((uint64_t)edx << 32) | eax); +} + + + + + + + + +/************************************************************************************** + EXTRACT from openair1/PHY/TOOLS/time_meas.c +***************************************************************************************/ +double get_cpu_freq_GHz(void) { + + time_stats_t ts = {0}; + reset_meas(&ts); + ts.trials++; + ts.in = rdtsc_oai(); + sleep(1); + 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; +} + + + +/************************************************************************************** + SYRTEM STATISTICS HELPER +***************************************************************************************/ + + + +histo_time_t *histogram_init(histo_time_t *histo ) +{ + int i = 0; + double max_val = HISTOGRAM_MIN_VALUE + HISTOGRAM_STEP; + + histo = calloc( HISTOGRAM_SIZE, sizeof( histo_time_t ) ); + if ( histo == NULL ) { + printf( "ERROR allocating histogram structure !\n" ); + return NULL; + } + + for ( i = 0; i < HISTOGRAM_SIZE; i++ ) { + histo[i].max = max_val; + max_val += HISTOGRAM_STEP; + } + + return histo; +} + + +void histogram_store_value( histo_time_t *histo, double value ) +{ + int i = 0; + + for ( i = 0; i < HISTOGRAM_SIZE; i++ ) { + if ( histo[i].max >= value || ( i + 1 ) == HISTOGRAM_SIZE ) { + histo[i].count++; + break; + } + } +} + + +void histogram_save_in_csv( histo_time_t *histo , char *file_sufix) +{ + char *csv_filename; + char month[3], day[3], hour[3], min[3], sec[3]; + time_t curr_time; + struct tm *datetime; + + curr_time = time( NULL ); + datetime = localtime( &curr_time ); + + csv_filename = calloc( sizeof( char ), 64 ); + if ( csv_filename == NULL ) { + return; + } + + memset( csv_filename, 0x00, 64 ); + memset( month, 0x00, 3 ); + memset( day, 0x00, 3 ); + memset( hour, 0x00, 3 ); + memset( min, 0x00, 3 ); + memset( sec, 0x00, 3 ); + +/* Month. */ + if ( datetime->tm_mon < 9 ) + snprintf( month, 3, "0%d", datetime->tm_mon + 1 ); + else + snprintf( month, 3, "%d", datetime->tm_mon + 1 ); + +/* Day. */ + if ( datetime->tm_mday < 10 ) + snprintf( day, 3, "0%d", datetime->tm_mday ); + else + snprintf( day, 3, "%d", datetime->tm_mday ); + +/* Hour. */ + if ( datetime->tm_hour < 10 ) + snprintf( hour, 3, "0%d", datetime->tm_hour ); + else + snprintf( hour, 3, "%d", datetime->tm_hour ); + +/* Minute. */ + if ( datetime->tm_min < 10 ) + snprintf( min, 3, "0%d", datetime->tm_min ); + else + snprintf( min, 3, "%d", datetime->tm_min ); + +/* Second. */ + if ( datetime->tm_sec < 10 ) + snprintf( sec, 3, "0%d", datetime->tm_sec ); + else + snprintf( sec, 3, "%d", datetime->tm_sec ); + + snprintf( csv_filename, 63, "ADRV9371_ZC706_HISTO_UEtoRXTX_%d%s%s%s%s%s_%s.csv", + datetime->tm_year + 1900, month, day, hour, min, sec, file_sufix ); + + FILE *fp; + int i = 0; + int min_val = 0; + + fp = fopen( csv_filename, "w+" ); + + fprintf( fp, "range;count\n" ); + + for ( i = 0; i < HISTOGRAM_SIZE; i++ ) { + if ( i + 1 == HISTOGRAM_SIZE ) + fprintf( fp, "%u+;%u\n", min_val, histo[i].count ); + else + fprintf( fp, "%u-%.0f;%u\n", min_val, histo[i].max, histo[i].count ); + min_val = histo[i].max; + } + + fclose( fp ); +} + + + +static struct timespec get_timespec_diff( + struct timespec *start, + struct timespec *stop ) +{ + struct timespec result; + + if ( ( stop->tv_nsec - start->tv_nsec ) < 0 ) { + result.tv_sec = stop->tv_sec - start->tv_sec - 1; + result.tv_nsec = stop->tv_nsec - start->tv_nsec + 1000000000; + } + else { + result.tv_sec = stop->tv_sec - start->tv_sec; + result.tv_nsec = stop->tv_nsec - start->tv_nsec; + } + + return result; +} + + +static void measure_time ( + struct timespec *start, + struct timespec *stop, + uint8_t START, + uint16_t PRINT_INTERVAL ) +{ + static double max = 0; + static double min = 0; + static double total = 0; + static int count = 0; + + if ( START ) { + clock_gettime( CLOCK_REALTIME, start ); + } + else { + clock_gettime( CLOCK_REALTIME, stop ); + + struct timespec diff; + double current = 0; + + diff = get_timespec_diff( start, stop ); +// current = ( (double)diff.tv_sec * 1000000 ) + ( (double)diff.tv_nsec / 1000 ); + current = TIMESPEC_TO_DOUBLE_US( diff ); + + if ( current > max ) { + max = current; + } + if ( min == 0 || current < min ) { + min = current; + } + total += current; + + histogram_store_value( th_wake_histogram, current ); + + if ( count % PRINT_INTERVAL == 0 && count != 0 ) { + double avg = total / ( count + 1 ); + printf( "[%d] Current : %.2lf µs, Min : %.2lf µs, Max : %.2lf µs, Moy : %.2lf µs\n", + count, current, min, max, avg ); + } + count++; + } +} + + + + + + + + + + + + + + + + diff --git a/targets/SIMU/USER/oaisim_functions.c b/targets/SIMU/USER/oaisim_functions.c index ceaae1958b..c6907e666b 100644 --- a/targets/SIMU/USER/oaisim_functions.c +++ b/targets/SIMU/USER/oaisim_functions.c @@ -1704,7 +1704,7 @@ void update_otg_eNB(module_id_t enb_module_idP, unsigned int ctime) } #else -#if 0 // defined(EXMIMO) || defined(OAI_USRP) +#if 0 // defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_ADRV9371_ZC706) if (otg_enabled==1) { ctime = frame * 100; diff --git a/targets/build_oai.bash b/targets/build_oai.bash index cfe5e35d26..05b14a541b 100755 --- a/targets/build_oai.bash +++ b/targets/build_oai.bash @@ -344,6 +344,10 @@ build_enb(){ SOFTMODEM_DIRECTIVES="$SOFTMODEM_DIRECTIVES USRP=1 " fi + if [ $HW = "ADRV9371_ZC706" ]; then + SOFTMODEM_DIRECTIVES="$SOFTMODEM_DIRECTIVES ADRV9371_ZC706=1 " + fi + if [ $HW = "EXMIMO" ]; then SOFTMODEM_DIRECTIVES="$SOFTMODEM_DIRECTIVES EXMIMO=1 " fi