From 53db39b8a02dfdcf2564a2fce11d4650f494a775 Mon Sep 17 00:00:00 2001 From: laurent <laurent.thomas@open-cells.com> Date: Mon, 2 Mar 2020 15:41:21 +0100 Subject: [PATCH] split 7.3 ready for merge --- SystemX-tutorial-design.odt | Bin 55501 -> 0 bytes cmake_targets/CMakeLists.txt | 8 - common/utils/system.c | 6 +- .../SystemX-tutorial-design.md | 21 +- enb.fs6.example.conf | 290 ------- executables/main-fs6.c | 126 +-- executables/main-ocp.c | 793 +++++++++++++----- executables/nr-gnb.c | 137 ++- executables/nr-ru.c | 40 +- executables/nr-softmodem.c | 134 +-- executables/nr-ue.c | 132 +-- executables/nr-uesoftmodem.c | 56 +- executables/softmodem-common.c | 52 +- executables/softmodem-common.h | 9 + executables/split_headers.h | 5 +- openair1/PHY/LTE_TRANSPORT/dlsch_coding.c | 8 +- openair1/PHY/LTE_TRANSPORT/dlsch_modulation.c | 24 +- openair1/PHY/LTE_TRANSPORT/dlsch_scrambling.c | 2 +- openair1/PHY/LTE_TRANSPORT/transport_eNB.h | 6 +- openair1/PHY/LTE_TRANSPORT/ulsch_decoding.c | 54 +- .../PHY/LTE_UE_TRANSPORT/dlsch_decoding.c | 2 +- openair1/PHY/NR_UE_TRANSPORT/dci_nr.c | 2 +- openair1/SCHED/phy_procedures_lte_eNb.c | 23 +- openair1/SIMULATION/LTE_PHY/dlsim.c | 9 +- openair1/SIMULATION/LTE_PHY/ulsim.c | 9 +- openair2/PHY_INTERFACE/IF_Module.h | 6 +- targets/ARCH/rfsimulator/simulator.c | 6 +- targets/COMMON/create_tasks.c | 15 +- targets/RT/USER/lte-softmodem.c | 31 +- 29 files changed, 1042 insertions(+), 964 deletions(-) delete mode 100644 SystemX-tutorial-design.odt rename SystemX-tutorial-design.md => doc/SystemX-tutorial-design.md (93%) delete mode 100644 enb.fs6.example.conf diff --git a/SystemX-tutorial-design.odt b/SystemX-tutorial-design.odt deleted file mode 100644 index 42e195ac112b370ff7d48cfbb5e083d9cfaecf6e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55501 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 U6A%AvGbQ$Q5gQ0mG<??k56pR0d;kCd diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt index 774de380abd..15b0c8bd548 100644 --- a/cmake_targets/CMakeLists.txt +++ b/cmake_targets/CMakeLists.txt @@ -1181,9 +1181,6 @@ set(SCHED_SRC add_library(SCHED_LIB ${SCHED_SRC}) add_dependencies(SCHED_LIB rrc_flag) -add_library(SCHED_LIB_FS6 ${SCHED_SRC}) -target_compile_definitions(SCHED_LIB_FS6 PRIVATE FS6=1) -add_dependencies(SCHED_LIB_FS6 rrc_flag) set(SCHED_NR_SRC ${OPENAIR1_DIR}/SCHED_NR/fapi_nr_l1.c ${OPENAIR1_DIR}/SCHED_NR/phy_procedures_nr_common.c @@ -1563,8 +1560,6 @@ endif () add_library(PHY_COMMON ${PHY_SRC_COMMON}) add_dependencies(PHY_COMMON rrc_flag) add_library(PHY ${PHY_SRC}) -add_library(PHY_FS6 ${PHY_SRC}) -target_compile_definitions(PHY_FS6 PRIVATE FS6=1) add_dependencies(PHY rrc_flag) add_library(PHY_UE ${PHY_SRC_UE}) @@ -2485,13 +2480,11 @@ target_link_libraries (lte-softmodem ${T_LIB}) add_executable(ocp-softmodem - ${OPENAIR_TARGETS}/RT/USER/rt_wrapper.c ${OPENAIR_DIR}/executables/main-ocp.c ${OPENAIR_DIR}/common/utils/threadPool/thread-pool.c ${OPENAIR_DIR}/executables/softmodem-common.c ${OPENAIR_DIR}/executables/main-fs6.c ${OPENAIR_DIR}/executables/transport_split.c - ${OPENAIR_TARGETS}/RT/USER/lte-softmodem.c ${OPENAIR2_DIR}/ENB_APP/NB_IoT_interface.c ${OPENAIR1_DIR}/SIMULATION/TOOLS/taus.c ${OPENAIR_TARGETS}/COMMON/create_tasks.c @@ -2511,7 +2504,6 @@ add_executable(ocp-softmodem ${CONFIG_SOURCES} ${SHLIB_LOADER_SOURCES} ) -target_compile_definitions(ocp-softmodem PRIVATE FS6=1) add_dependencies(ocp-softmodem rrc_flag s1ap_flag x2_flag) target_link_libraries (ocp-softmodem diff --git a/common/utils/system.c b/common/utils/system.c index 3a8fe7be3d8..1bd5c7d39bc 100644 --- a/common/utils/system.c +++ b/common/utils/system.c @@ -243,6 +243,8 @@ void configure_linux(void) { // Set CPU frequency to it's maximum if ( 0 != system("for d in /sys/devices/system/cpu/cpu[0-9]*; do cat $d/cpufreq/cpuinfo_max_freq > $d/cpufreq/scaling_min_freq; done")) - LOG_W(HW,"Can't set cpu frequency\n"); - + LOG_E(HW,"Can't set cpu frequency\n"); + + mlockall(MCL_CURRENT | MCL_FUTURE); + } diff --git a/SystemX-tutorial-design.md b/doc/SystemX-tutorial-design.md similarity index 93% rename from SystemX-tutorial-design.md rename to doc/SystemX-tutorial-design.md index c5d1f77704a..371cb33a751 100644 --- a/SystemX-tutorial-design.md +++ b/doc/SystemX-tutorial-design.md @@ -258,26 +258,22 @@ lte-softmodem. The functionality and parameters is the same, enhanced with FS6 mode. -The environment variable “fs6†enables the fs6 mode and decided to be cu -or du from the fs6 variable content. - -A example configuration file is: $OPENAIR_DIR/enb.fs6.example.conf -The IP addresses in this file should be updated for each network. +The end line option “--split73†enables the fs6 (also called split 7.3) mode and decided to be cu or du. Example: -`fs6=cu ./ocp-softmodem -O $OPENAIR_DIR/enb.fs6.example.conf --rfsim --log_config.phy_log_level debug +./ocp-softmodem -O $OPENAIR_DIR/enb.fs6.example.conf --rfsim --log_config.phy_log_level debug --split73 cu:127.0.0.1 ` +Run the CU init of the split 6 eNB, that will call du on 127.0.0.1 address -Run the CU init of the split 6 eNB. +./ocp-softmodem -O $OPENAIR_DIR/enb.fs6.example.conf --rfsim --log_config.phy_log_level debug --split73 du:127.0.0.1 -`fs6=du ./ocp-softmodem -O $OPENAIR_DIR/enb.fs6.example.conf --rfsim --log_config.phy_log_level debug +will run the du, calling the cu on 127.0.0.1 ` If the CU and the DU are not on the same machine, the remote address of each side need to be specified as per this example -`fs6=du FS6_REMOTE_IP=192.168.2.211 ./ocp-softmodem -O $OPENAIR_DIR/enb.fs6.example.conf --rfsim --log_config.phy_log_level debug +./ocp-softmodem -O $OPENAIR_DIR/enb.fs6.example.conf --rfsim --log_config.phy_log_level debug --split73 du:192.168.1.55 ` - runs the functional split 6 DU `./lte-uesoftmodem -C 2685000000 -r 50 --rfsim --rfsimulator.serveraddr 192.168.1.1 -d @@ -285,10 +281,7 @@ runs the functional split 6 DU Runs the UE (to have the UE signal scope, compile it with make uescope) -CU and DU IP address and port are configurable in the eNB configuration -file (as X2, GTP, … interfaces). - -CU+DU+UE can run with option --noS1 to avoid to use a EPC. +CU+DU+UE can run with option --noS1 to avoid to use a EPC and/or with --rfsim to simulate RF board ##5G and F1 diff --git a/enb.fs6.example.conf b/enb.fs6.example.conf deleted file mode 100644 index 6102f917564..00000000000 --- a/enb.fs6.example.conf +++ /dev/null @@ -1,290 +0,0 @@ -Active_eNBs = ( "eNB-Eurecom-LTEBox"); -# Asn1_verbosity, choice in: none, info, annoying -Asn1_verbosity = "none"; - -eNBs = -( - { - ////////// Identification parameters: - eNB_ID = 0xe00; - - cell_type = "CELL_MACRO_ENB"; - - eNB_name = "eNB-Eurecom-LTEBox"; - - // Tracking area code, 0x0000 and 0xfffe are reserved values - tracking_area_code = 1; - plmn_list = ( { mcc = 208; mnc = 92; mnc_length = 2; } ); - - tr_s_preference = "local_mac" - - ////////// Physical parameters: - - component_carriers = ( - { - node_function = "3GPP_eNODEB"; - node_timing = "synch_to_ext_device"; - node_synch_ref = 0; - frame_type = "FDD"; - tdd_config = 3; - tdd_config_s = 0; - prefix_type = "NORMAL"; - eutra_band = 7; - downlink_frequency = 2685000000L; - uplink_frequency_offset = -120000000; - Nid_cell = 0; - N_RB_DL = 50; - Nid_cell_mbsfn = 0; - nb_antenna_ports = 1; - nb_antennas_tx = 1; - nb_antennas_rx = 1; - tx_gain = 90; - rx_gain = 125; - pbch_repetition = "FALSE"; - prach_root = 0; - prach_config_index = 0; - prach_high_speed = "DISABLE"; - prach_zero_correlation = 1; - prach_freq_offset = 2; - pucch_delta_shift = 1; - pucch_nRB_CQI = 0; - pucch_nCS_AN = 0; - pucch_n1_AN = 0; - pdsch_referenceSignalPower = -27; - pdsch_p_b = 0; - pusch_n_SB = 1; - pusch_enable64QAM = "DISABLE"; - pusch_hoppingMode = "interSubFrame"; - pusch_hoppingOffset = 0; - pusch_groupHoppingEnabled = "ENABLE"; - pusch_groupAssignment = 0; - pusch_sequenceHoppingEnabled = "DISABLE"; - pusch_nDMRS1 = 1; - phich_duration = "NORMAL"; - phich_resource = "ONESIXTH"; - srs_enable = "DISABLE"; - /* srs_BandwidthConfig =; - srs_SubframeConfig =; - srs_ackNackST =; - srs_MaxUpPts =;*/ - - pusch_p0_Nominal = -96; - pusch_alpha = "AL1"; - pucch_p0_Nominal = -104; - msg3_delta_Preamble = 6; - pucch_deltaF_Format1 = "deltaF2"; - pucch_deltaF_Format1b = "deltaF3"; - pucch_deltaF_Format2 = "deltaF0"; - pucch_deltaF_Format2a = "deltaF0"; - pucch_deltaF_Format2b = "deltaF0"; - - rach_numberOfRA_Preambles = 64; - rach_preamblesGroupAConfig = "DISABLE"; - /* - rach_sizeOfRA_PreamblesGroupA = ; - rach_messageSizeGroupA = ; - rach_messagePowerOffsetGroupB = ; - */ - rach_powerRampingStep = 4; - rach_preambleInitialReceivedTargetPower = -108; - rach_preambleTransMax = 10; - rach_raResponseWindowSize = 10; - rach_macContentionResolutionTimer = 48; - rach_maxHARQ_Msg3Tx = 4; - - pcch_default_PagingCycle = 128; - pcch_nB = "oneT"; - drx_Config_present = "prSetup"; //"prSetup" "prRelease" - drx_onDurationTimer = "psf1"; // "psfX": X=1,2,3,4,5,6,8,10,20,30,40,50,60,80,100 - drx_InactivityTimer = "psf1"; // "psfX": X=1,2,3,4,5,6,8,10,20,30,40,50,60,80,100,200,300,500,750,1280,1920,2560 - drx_RetransmissionTimer = "psf1"; // "psfX": X=1,2,4,6,8,16,24,33 - drx_longDrx_CycleStartOffset_present = "prSf128"; // "psfX": X=10,20,32,40,64,80,128,160,256,320,512,640,1024,1280,2048,2560 - drx_longDrx_CycleStartOffset = 0; // X >= 0 && X < drx_longDrx_CycleStartOffset_present - drx_shortDrx_Cycle = "sf16"; // "sfX": X=2,5,8,10,16,20,32,40,64,80,128,160,256,320,512,640 - drx_shortDrx_ShortCycleTimer = 3; // 1..16 integer. Total duration in short cycle = drx_shortDrx_Cycle*drx_shortDrx_ShortCycleTimer [subframe] - bcch_modificationPeriodCoeff = 2; - ue_TimersAndConstants_t300 = 1000; - ue_TimersAndConstants_t301 = 1000; - ue_TimersAndConstants_t310 = 1000; - ue_TimersAndConstants_t311 = 10000; - ue_TimersAndConstants_n310 = 20; - ue_TimersAndConstants_n311 = 1; - ue_TransmissionMode = 1; - - //Parameters for SIB18 - rxPool_sc_CP_Len = "normal"; - rxPool_sc_Period = "sf40"; - rxPool_data_CP_Len = "normal"; - rxPool_ResourceConfig_prb_Num = 20; - rxPool_ResourceConfig_prb_Start = 5; - rxPool_ResourceConfig_prb_End = 44; - rxPool_ResourceConfig_offsetIndicator_present = "prSmall"; - rxPool_ResourceConfig_offsetIndicator_choice = 0; - rxPool_ResourceConfig_subframeBitmap_present = "prBs40"; - rxPool_ResourceConfig_subframeBitmap_choice_bs_buf = "00000000000000000000"; - rxPool_ResourceConfig_subframeBitmap_choice_bs_size = 5; - rxPool_ResourceConfig_subframeBitmap_choice_bs_bits_unused = 0; -/* rxPool_dataHoppingConfig_hoppingParameter = 0; - rxPool_dataHoppingConfig_numSubbands = "ns1"; - rxPool_dataHoppingConfig_rbOffset = 0; - rxPool_commTxResourceUC-ReqAllowed = "TRUE"; -*/ - // Parameters for SIB19 - discRxPool_cp_Len = "normal" - discRxPool_discPeriod = "rf32" - discRxPool_numRetx = 1; - discRxPool_numRepetition = 2; - discRxPool_ResourceConfig_prb_Num = 5; - discRxPool_ResourceConfig_prb_Start = 3; - discRxPool_ResourceConfig_prb_End = 21; - discRxPool_ResourceConfig_offsetIndicator_present = "prSmall"; - discRxPool_ResourceConfig_offsetIndicator_choice = 0; - discRxPool_ResourceConfig_subframeBitmap_present = "prBs40"; - discRxPool_ResourceConfig_subframeBitmap_choice_bs_buf = "f0ffffffff"; - discRxPool_ResourceConfig_subframeBitmap_choice_bs_size = 5; - discRxPool_ResourceConfig_subframeBitmap_choice_bs_bits_unused = 0; - - } - ); - - - srb1_parameters : - { - # timer_poll_retransmit = (ms) [5, 10, 15, 20,... 250, 300, 350, ... 500] - timer_poll_retransmit = 80; - - # timer_reordering = (ms) [0,5, ... 100, 110, 120, ... ,200] - timer_reordering = 35; - - # timer_reordering = (ms) [0,5, ... 250, 300, 350, ... ,500] - timer_status_prohibit = 0; - - # poll_pdu = [4, 8, 16, 32 , 64, 128, 256, infinity(>10000)] - poll_pdu = 4; - - # poll_byte = (kB) [25,50,75,100,125,250,375,500,750,1000,1250,1500,2000,3000,infinity(>10000)] - poll_byte = 99999; - - # max_retx_threshold = [1, 2, 3, 4 , 6, 8, 16, 32] - max_retx_threshold = 4; - } - - # ------- SCTP definitions - SCTP : - { - # Number of streams to use in input/output - SCTP_INSTREAMS = 2; - SCTP_OUTSTREAMS = 2; - }; - - - ////////// MME parameters: - mme_ip_address = ( { ipv4 = "127.0.0.20"; - ipv6 = "192:168:30::17"; - active = "yes"; - preference = "ipv4"; - } - ); - - enable_measurement_reports = "no"; - ///X2 - enable_x2 = "no"; - t_reloc_prep = 1000; /* unit: millisecond */ - tx2_reloc_overall = 2000; /* unit: millisecond */ - - NETWORK_INTERFACES : - { - - ENB_INTERFACE_NAME_FOR_S1_MME = "lo"; - ENB_IPV4_ADDRESS_FOR_S1_MME = "127.0.0.10"; - ENB_INTERFACE_NAME_FOR_S1U = "lo"; - ENB_IPV4_ADDRESS_FOR_S1U = "127.0.0.10"; - ENB_PORT_FOR_S1U = 2152; # Spec 2152 - - ENB_IPV4_ADDRESS_FOR_X2C = "127.0.0.1"; - ENB_PORT_FOR_X2C = 36422; # Spec 36422 - }; - } -); - -MACRLCs = ( - { - num_cc = 1; - tr_s_preference = "local_L1"; - tr_n_preference = "local_RRC"; - //scheduler_mode = "fairRR"; - phy_test_mode = 0; - puSch10xSnr = 160; - puCch10xSnr = 160; - } -); - -L1s = ( - { - num_cc = 1; - tr_n_preference = "local_mac"; - } -); - -RUs = ( - { - local_rf = "yes" - nb_tx = 1 - nb_rx = 1 - att_tx = 0 - att_rx = 0; - bands = [7]; - max_pdschReferenceSignalPower = -27; - max_rxgain = 108; - eNB_instances = [0]; - - } -); - -NETWORK_CONTROLLER : -{ - FLEXRAN_ENABLED = "no"; - FLEXRAN_INTERFACE_NAME = "lo"; - FLEXRAN_IPV4_ADDRESS = "127.0.0.1"; - FLEXRAN_PORT = 2210; - FLEXRAN_CACHE = "/mnt/oai_agent_cache"; - FLEXRAN_AWAIT_RECONF = "no"; -}; - -THREAD_STRUCT = ( - { - #three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT" - parallel_config = "PARALLEL_SINGLE_THREAD"; - #two option for worker "WORKER_DISABLE" or "WORKER_ENABLE" - worker_config = "WORKER_DISABLE"; - } -); - -#example config for rfsimulator -rfsimulator : -{ - serveraddr = "enb"; - serverport = "4043"; - options = (); #("saviq"); - modelname = "AWGN"; - IQfile = "/tmp/rfsimulator.iqs"; -}; - - log_config : - { - global_log_level ="info"; - global_log_verbosity ="medium"; - hw_log_level ="info"; - hw_log_verbosity ="medium"; - phy_log_level ="info"; - phy_log_verbosity ="medium"; - mac_log_level ="info"; - mac_log_verbosity ="high"; - rlc_log_level ="info"; - rlc_log_verbosity ="medium"; - pdcp_log_level ="info"; - pdcp_log_verbosity ="medium"; - rrc_log_level ="info"; - rrc_log_verbosity ="medium"; - }; - diff --git a/executables/main-fs6.c b/executables/main-fs6.c index 727f80a72b3..c20777a5d67 100644 --- a/executables/main-fs6.c +++ b/executables/main-fs6.c @@ -165,16 +165,16 @@ void prach_eNB_tosplit(uint8_t *bufferZone, int bufSize, PHY_VARS_eNB *eNB, L1_r } ocp_rx_prach(eNB, - proc, - eNB->RU_list[0], - header->max_preamble, - header->max_preamble_energy, - header->max_preamble_delay, - header->avg_preamble_energy, - proc->frame_prach, - 0, - false - ); + proc, + eNB->RU_list[0], + header->max_preamble, + header->max_preamble_energy, + header->max_preamble_delay, + header->avg_preamble_energy, + proc->frame_prach, + 0, + false + ); // run PRACH detection for CE-level 0 only for now when br_flag is set /* fixme: seems not operational and may overwrite regular LTE prach detection * OAI code can call is sequence @@ -513,15 +513,12 @@ void fill_rx_indication_from_split(uint8_t *bufferZone, PHY_VARS_eNB *eNB,int UE nfapi_rx_indication_pdu_t *pdu; int timing_advance_update; uint32_t harq_pid; -#if (LTE_RRC_VERSION >= MAKE_VERSION(14, 0, 0)) - if (eNB->ulsch[UE_id]->ue_type > 0) harq_pid = 0; + if (eNB->ulsch[UE_id]->ue_type > 0) + harq_pid = 0; else -#endif - { harq_pid = subframe2harq_pid (&eNB->frame_parms, frame, subframe); - } pthread_mutex_lock(&eNB->UL_INFO_mutex); eNB->UL_INFO.rx_ind.sfn_sf = frame<<4| subframe; @@ -533,7 +530,7 @@ void fill_rx_indication_from_split(uint8_t *bufferZone, PHY_VARS_eNB *eNB,int UE pdu->rx_indication_rel8.tl.tag = NFAPI_RX_INDICATION_REL8_TAG; pdu->rx_indication_rel8.length = eNB->ulsch[UE_id]->harq_processes[harq_pid]->TBS>>3; pdu->rx_indication_rel8.offset = 1; // DJP - I dont understand - but broken unless 1 ???? 0; // filled in at the end of the UL_INFO formation - pdu->data = eNB->ulsch[UE_id]->harq_processes[harq_pid]->b; + pdu->data = eNB->ulsch[UE_id]->harq_processes[harq_pid]->decodedBytes; // estimate timing advance for MAC timing_advance_update = ul_propa[UE_id].ta; @@ -662,7 +659,7 @@ void pusch_procedures_fromsplit(uint8_t *bufferZone, int bufSize, PHY_VARS_eNB * } // for (i=0; i<NUMBER_OF_UE_MAX; i++) while (proc->nbDecode > 0) { - notifiedFIFO_elt_t *req=pullTpool(&proc->respDecode, &proc->threadPool); + notifiedFIFO_elt_t *req=pullTpool(proc->respDecode, proc->threadPool); postDecode(proc, req); delNotifiedFIFO_elt(req); } @@ -678,7 +675,7 @@ void recvFs6Ul(uint8_t *bufferZone, int nbBlocks, PHY_VARS_eNB *eNB, ul_propagat if ( type == fs6ULsch) { LTE_eNB_ULSCH_t *ulsch =eNB->ulsch[hULUE(bufPtr)->UE_id]; LTE_UL_eNB_HARQ_t *ulsch_harq=ulsch->harq_processes[hULUE(bufPtr)->harq_id]; - memcpy(ulsch_harq->e+hULUE(bufPtr)->r_offset, + memcpy(ulsch_harq->eUL+hULUE(bufPtr)->r_offset, hULUE(bufPtr)+1, hULUE(bufPtr)->segLen); memcpy(eNB->pusch_vars[hULUE(bufPtr)->UE_id]->ulsch_power, @@ -691,7 +688,7 @@ void recvFs6Ul(uint8_t *bufferZone, int nbBlocks, PHY_VARS_eNB *eNB, ul_propagat sizeof(ulsch_harq->o_ACK)); memcpy(ulsch_harq->o,hULUE(bufPtr)->o, sizeof(ulsch_harq->o)); ul_propa[hULUE(bufPtr)->UE_id].ta=hULUE(bufPtr)->ta; - LOG_D(PHY,"Received ulsch data for: rnti:%x, cqi_crc_status %d O_ACK: %di, segment: %di, seglen: %d \n", + LOG_D(PHY,"Received ulsch data for: rnti:%x, cqi_crc_status %d O_ACK: %d, segment: %d, seglen: %d \n", ulsch->rnti, ulsch_harq->cqi_crc_status, ulsch_harq->O_ACK,hULUE(bufPtr)->segment, hULUE(bufPtr)->segLen); } else if ( type == fs6ULcch ) { int nb_uci=hULUEuci(bufPtr)->nb_active_ue; @@ -771,10 +768,10 @@ void rcvFs6DL(uint8_t *bufferZone, int nbBlocks, PHY_VARS_eNB *eNB, int frame, i dlsch0->i0=hDLUE(bufPtr)->i0; dlsch0->sib1_br_flag=hDLUE(bufPtr)->sib1_br_flag; #endif - fs6Dlunpack(dlsch_harq->e, + fs6Dlunpack(dlsch_harq->eDL, hDLUE(bufPtr)+1, hDLUE(bufPtr)->dataLen); LOG_D(PHY,"received %d bits, in harq id: %di fsf: %d.%d, sum %d\n", - hDLUE(bufPtr)->dataLen, hDLUE(bufPtr)->harq_pid, frame, subframe, sum(dlsch_harq->e, hDLUE(bufPtr)->dataLen)); + hDLUE(bufPtr)->dataLen, hDLUE(bufPtr)->harq_pid, frame, subframe, sum(dlsch_harq->eDL, hDLUE(bufPtr)->dataLen)); } else if (type == fs6UlConfig) { int nbUE=(((commonUDP_t *)bufPtr)->contentBytes - sizeof(fs6_dl_t)) / sizeof( fs6_dl_ulsched_t ) ; #define cpyVal(a) memcpy(&ulsch_harq->a,&hTxULUE(bufPtr)->a, sizeof(ulsch_harq->a)) @@ -1103,9 +1100,9 @@ void appendFs6DLUE(uint8_t *bufferZone, LTE_DL_FRAME_PARMS *fp, int UE_id, int8_ hDLUE(newUDPheader)->sib1_br_flag=dlsch0->sib1_br_flag; #endif hDLUE(newUDPheader)->dataLen=UEdataLen; - fs6Dlpack(hDLUE(newUDPheader)+1, harqData->e, UEdataLen); + fs6Dlpack(hDLUE(newUDPheader)+1, harqData->eDL, UEdataLen); LOG_D(PHY,"sending %d bits, in harq id: %di fsf: %d.%d, sum %d\n", - UEdataLen, harq_pid, frame, subframe, sum(harqData->e, UEdataLen)); + UEdataLen, harq_pid, frame, subframe, sum(harqData->eDL, UEdataLen)); //for (int i=0; i < UEdataLen; i++) //LOG_D(PHY,"buffer ei[%d]:%hhx\n", i, ( (uint8_t *)(hDLUE(newUDPheader)+1) )[i]); } @@ -1322,7 +1319,11 @@ void phy_procedures_eNB_TX_tosplit(uint8_t *bufferZone, PHY_VARS_eNB *eNB, L1_rx void *DL_du_fs6(void *arg) { RU_t *ru=(RU_t *)arg; static uint64_t lastTS; - L1_rxtx_proc_t L1_proc= {0}; + L1_rxtx_proc_t L1proc= {0}; + // We pick the global thread pool from the legacy code global vars + L1proc.threadPool=RC.eNB[0][0]->proc.L1_proc.threadPool; + L1proc.respEncode=RC.eNB[0][0]->proc.L1_proc.respEncode; + L1proc.respDecode=RC.eNB[0][0]->proc.L1_proc.respDecode; initStaticTime(begingWait); initStaticTime(begingProcessing); initRefTimes(fullLoop); @@ -1338,27 +1339,31 @@ void *DL_du_fs6(void *arg) { updateTimesReset(begingWait, &fullLoop, 1000, false, "DU wait CU"); if (nb_blocks > 0) { - if ( lastTS+ru->eNB_list[i]->frame_parms.samples_per_tti != hUDP(bufferZone)->timestamp) { + if ( lastTS+ru->eNB_list[i]->frame_parms.samples_per_tti < hUDP(bufferZone)->timestamp) { LOG_E(HW,"Missed a subframe: expecting: %lu, received %lu\n", lastTS+ru->eNB_list[i]->frame_parms.samples_per_tti, hUDP(bufferZone)->timestamp); + } else if ( lastTS+ru->eNB_list[i]->frame_parms.samples_per_tti > hUDP(bufferZone)->timestamp) { + LOG_E(HW,"Received a subframe in past time from CU (dropping it): expecting: %lu, received %lu\n", + lastTS+ru->eNB_list[i]->frame_parms.samples_per_tti, + hUDP(bufferZone)->timestamp); } pickStaticTime(begingProcessing); lastTS=hUDP(bufferZone)->timestamp; - setAllfromTS(hUDP(bufferZone)->timestamp - sf_ahead*ru->eNB_list[i]->frame_parms.samples_per_tti, &L1_proc); + setAllfromTS(hUDP(bufferZone)->timestamp - sf_ahead*ru->eNB_list[i]->frame_parms.samples_per_tti, &L1proc); measTransportTime(hDL(bufferZone)->DuClock, hDL(bufferZone)->CuSpentMicroSec, &transportTime, 1000, false, "Transport time, to CU + from CU for one subframe"); - phy_procedures_eNB_TX_fromsplit( bufferZone, nb_blocks, ru->eNB_list[i], &L1_proc, 1); + phy_procedures_eNB_TX_fromsplit( bufferZone, nb_blocks, ru->eNB_list[i], &L1proc, 1); updateTimesReset(begingProcessing, &DuHigh, 1000, false, "DU high layer1 processing for DL"); } else LOG_E(PHY,"DL not received for subframe\n"); } pickStaticTime(begingProcessing); - feptx_prec(ru, L1_proc.frame_tx,L1_proc.subframe_tx ); - feptx_ofdm(ru, L1_proc.frame_tx,L1_proc.subframe_tx ); - ocp_tx_rf(ru, &L1_proc); + feptx_prec(ru, L1proc.frame_tx,L1proc.subframe_tx ); + feptx_ofdm(ru, L1proc.frame_tx,L1proc.subframe_tx ); + ocp_tx_rf(ru, &L1proc); updateTimesReset(begingProcessing, &DuLow, 1000, false, "DU low layer1 processing for DL"); if ( IS_SOFTMODEM_RFSIM ) @@ -1374,7 +1379,6 @@ void UL_du_fs6(RU_t *ru, L1_rxtx_proc_t *proc) { pickStaticTime(begingWait); rx_rf(ru, proc); updateTimesReset(begingWait, &fullLoop, 1000, false, "DU wait USRP"); - setAllfromTS(proc->timestamp_rx, proc); // front end processing: convert from time domain to frequency domain // fills rxdataF buffer fep_full(ru, proc->subframe_rx); @@ -1480,23 +1484,22 @@ void *cu_fs6(void *arg) { init_frame_parms(ru->frame_parms,1); phy_init_RU(ru); wait_sync("ru_thread"); - char *remoteIP; - - if ( getenv("FS6_REMOTE_IP") ) - remoteIP=getenv("FS6_REMOTE_IP"); - else - remoteIP=DU_IP; - - AssertFatal(createUDPsock(NULL, CU_PORT, remoteIP, DU_PORT, &sockFS6), ""); + char remoteIP[1024]; + strncpy(remoteIP,get_softmodem_params()->split73+3, 1023); //three first char should be cu: or du: + char port_def[256]=DU_PORT; + for (int i=0; i <1000; i++) + if (remoteIP[i]==':') { + strncpy(port_def,remoteIP+i+1,255); + remoteIP[i]=0; + break; + } + + AssertFatal(createUDPsock(NULL, CU_PORT, remoteIP, port_def, &sockFS6), ""); L1_rxtx_proc_t L1proc= {0}; - - if ( strlen(get_softmodem_params()->threadPoolConfig) > 0 ) - initTpool(get_softmodem_params()->threadPoolConfig, &L1proc.threadPool, true); - else - initTpool("n", &L1proc.threadPool, true); - - initNotifiedFIFO(&L1proc.respEncode); - initNotifiedFIFO(&L1proc.respDecode); + // We pick the global thread pool from the legacy code global vars + L1proc.threadPool=RC.eNB[0][0]->proc.L1_proc.threadPool; + L1proc.respEncode=RC.eNB[0][0]->proc.L1_proc.respEncode; + L1proc.respDecode=RC.eNB[0][0]->proc.L1_proc.respDecode; uint64_t timeStamp=0; initStaticTime(begingWait); initStaticTime(begingWait2); @@ -1529,14 +1532,16 @@ void *du_fs6(void *arg) { phy_init_RU(ru); init_rf(ru); wait_sync("ru_thread"); - char *remoteIP; - - if ( getenv("FS6_REMOTE_IP") ) - remoteIP=getenv("FS6_REMOTE_IP"); - else - remoteIP=CU_IP; - - AssertFatal(createUDPsock(NULL, DU_PORT, remoteIP, CU_PORT, &sockFS6), ""); + char remoteIP[1024]; + strncpy(remoteIP,get_softmodem_params()->split73+3,1023); //three first char should be cu: or du: + char port_def[256]=CU_PORT; + for (int i=0; i <1000; i++) + if (remoteIP[i]==':') { + strncpy(port_def,remoteIP+i+1,255); + remoteIP[i]=0; + break; + } + AssertFatal(createUDPsock(NULL, DU_PORT, remoteIP, port_def, &sockFS6), ""); if (ru->rfdevice.trx_start_func(&ru->rfdevice) != 0) LOG_E(HW,"Could not start the RF device\n"); @@ -1551,8 +1556,13 @@ void *du_fs6(void *arg) { if ( !IS_SOFTMODEM_RFSIM ) threadCreate(&t, DL_du_fs6, (void *)ru, "MainDuTx", -1, OAI_PRIORITY_RT_MAX); - while(oai_exit) { - L1_rxtx_proc_t L1proc; + L1_rxtx_proc_t L1proc= {0}; + // We pick the global thread pool from the legacy code global vars + L1proc.threadPool=RC.eNB[0][0]->proc.L1_proc.threadPool; + L1proc.respEncode=RC.eNB[0][0]->proc.L1_proc.respEncode; + L1proc.respDecode=RC.eNB[0][0]->proc.L1_proc.respDecode; + + while(!oai_exit) { updateTimesReset(begingWait, &fullLoop, 1000, true,"DU for full SubFrame (must be less 1ms)"); pickStaticTime(begingWait); UL_du_fs6(ru, &L1proc); @@ -1562,7 +1572,7 @@ void *du_fs6(void *arg) { updateTimesReset(begingWait, &waitRxAndProcessingUL, 1000, true,"DU Time in wait Rx + Ul processing"); } - + ru->rfdevice.trx_end_func(&ru->rfdevice); LOG_I(PHY,"RU %d rf device stopped\n",ru->idx); return NULL; diff --git a/executables/main-ocp.c b/executables/main-ocp.c index 97939846fdb..50cd1f27144 100644 --- a/executables/main-ocp.c +++ b/executables/main-ocp.c @@ -5,8 +5,11 @@ /* * This file replaces + * targets/RT/USER/lte-softmodem.c + * targets/RT/USER/rt_wrapper.c * targets/RT/USER/lte-ru.c * targets/RT/USER/lte-enb.c + * targets/RT/USER/ru_control.c * openair1/SCHED/prach_procedures.c * The merger of OpenAir central code to this branch * should check if these 3 files are modified and analyze if code code has to be copied in here @@ -16,6 +19,7 @@ #include <common/utils/LOG/log.h> #include <common/utils/system.h> +#include <common/utils/assertions.h> static int DEFBANDS[] = {7}; static int DEFENBS[] = {0}; #include <common/config/config_userapi.h> @@ -30,8 +34,33 @@ static int DEFENBS[] = {0}; #include <nfapi/oai_integration/nfapi_pnf.h> #include <executables/split_headers.h> #include <common/utils/threadPool/thread-pool.h> - -extern uint16_t sf_ahead; +#include <openair2/ENB_APP/NB_IoT_interface.h> +#include <common/utils/load_module_shlib.h> +#include <targets/COMMON/create_tasks.h> +#include <openair1/PHY/TOOLS/phy_scope_interface.h> +#include <openair2/UTIL/OPT/opt.h> +#include <openair1/SIMULATION/TOOLS/sim.h> +#include <openair1/PHY/phy_vars.h> +#include <openair1/SCHED/sched_common_vars.h> +#include <openair2/LAYER2/MAC/mac_vars.h> +#include <openair2/RRC/LTE/rrc_vars.h> + +pthread_cond_t nfapi_sync_cond; +pthread_mutex_t nfapi_sync_mutex; +int nfapi_sync_var=-1; //!< protected by mutex \ref nfapi_sync_mutex +pthread_cond_t sync_cond; +pthread_mutex_t sync_mutex; +int sync_var=-1; //!< protected by mutex \ref sync_mutex. +int config_sync_var=-1; +volatile int oai_exit = 0; +double cpuf; +uint16_t sf_ahead=4; +int otg_enabled; +uint32_t downlink_frequency[MAX_NUM_CCs][4]; +int32_t uplink_frequency_offset[MAX_NUM_CCs][4]; +int split73; +char * split73_config; +int split73; static void *ru_thread( void *param ); void kill_RU_proc(RU_t *ru) { @@ -42,6 +71,34 @@ void free_transport(PHY_VARS_eNB *eNB) { } void reset_opp_meas(void) { } +extern void phy_free_RU(RU_t *); + +void exit_function(const char *file, const char *function, const int line, const char *s) { + if (s != NULL) { + printf("%s:%d %s() Exiting OAI softmodem: %s\n",file,line, function, s); + } + + close_log_mem(); + oai_exit = 1; + + if (RC.ru == NULL) + exit(-1); // likely init not completed, prevent crash or hang, exit now... + + for (int ru_id=0; ru_id<RC.nb_RU; ru_id++) { + if (RC.ru[ru_id] && RC.ru[ru_id]->rfdevice.trx_end_func) { + RC.ru[ru_id]->rfdevice.trx_end_func(&RC.ru[ru_id]->rfdevice); + RC.ru[ru_id]->rfdevice.trx_end_func = NULL; + } + + if (RC.ru[ru_id] && RC.ru[ru_id]->ifdevice.trx_end_func) { + RC.ru[ru_id]->ifdevice.trx_end_func(&RC.ru[ru_id]->ifdevice); + RC.ru[ru_id]->ifdevice.trx_end_func = NULL; + } + } + + sleep(1); //allow lte-softmodem threads to exit first + exit(1); +} // Fixme: there are many mistakes in the datamodel and in redondant variables // TDD is also mode complex @@ -67,92 +124,47 @@ void setAllfromTS(uint64_t TS, L1_rxtx_proc_t *proc) { return; } - -void init_eNB_proc(int inst) { - /*int i=0;*/ - int CC_id; - PHY_VARS_eNB *eNB; - - for (CC_id=0; CC_id<RC.nb_CC[inst]; CC_id++) { - eNB = RC.eNB[inst][CC_id]; - pthread_mutex_init( &eNB->UL_INFO_mutex, NULL); - } - - //for multiple CCs: setup master and slaves - /* - for (CC_id=0; CC_id<MAX_NUM_CCs; CC_id++) { - eNB = PHY_vars_eNB_g[inst][CC_id]; - - if (eNB->node_timing == synch_to_ext_device) { //master - proc->num_slaves = MAX_NUM_CCs-1; - proc->slave_proc = (L1_proc_t**)malloc(proc->num_slaves*sizeof(L1_proc_t*)); - - for (i=0; i< eNB->proc.num_slaves; i++) { - if (i < CC_id) eNB->proc.slave_proc[i] = &(PHY_vars_eNB_g[inst][i]->proc); - if (i >= CC_id) eNB->proc.slave_proc[i] = &(PHY_vars_eNB_g[inst][i+1]->proc); - } - } - } - */ -} - void init_RU_proc(RU_t *ru) { pthread_t t; - char *fs6=getenv("fs6"); - - if (fs6) { - if ( strncasecmp(fs6,"cu", 2) == 0 ) - threadCreate(&t, cu_fs6, (void *)ru, "MainCu", -1, OAI_PRIORITY_RT_MAX); - else if ( strncasecmp(fs6,"du", 2) == 0 ) { - threadCreate(&t, du_fs6, (void *)ru, "MainDuRx", -1, OAI_PRIORITY_RT_MAX); - } else - AssertFatal(false, "environement variable fs6 is not cu or du"); - } else + + switch(split73) { + case SPLIT73_CU: + threadCreate(&t, cu_fs6, (void *)ru, "MainCu", -1, OAI_PRIORITY_RT_MAX); + break; + case SPLIT73_DU: + threadCreate(&t, du_fs6, (void *)ru, "MainDuRx", -1, OAI_PRIORITY_RT_MAX); + break; + default: threadCreate(&t, ru_thread, (void *)ru, "MainRu", -1, OAI_PRIORITY_RT_MAX); + } } +// Create per UE structures void init_transport(PHY_VARS_eNB *eNB) { - int i; - int j; LTE_DL_FRAME_PARMS *fp = &eNB->frame_parms; LOG_I(PHY, "Initialise transport\n"); - for (i=0; i<NUMBER_OF_UE_MAX; i++) { + for (int i=0; i<NUMBER_OF_UE_MAX; i++) { LOG_D(PHY,"Allocating Transport Channel Buffers for DLSCH, UE %d\n",i); - for (j=0; j<2; j++) { - eNB->dlsch[i][j] = new_eNB_dlsch(1,8,NSOFT,fp->N_RB_DL,0,fp); - - if (!eNB->dlsch[i][j]) { - LOG_E(PHY,"Can't get eNB dlsch structures for UE %d \n", i); - exit(-1); - } else { - eNB->dlsch[i][j]->rnti=0; - LOG_D(PHY,"dlsch[%d][%d] => %p rnti:%d\n",i,j,eNB->dlsch[i][j], eNB->dlsch[i][j]->rnti); - } + for (int j=0; j<2; j++) { + AssertFatal( (eNB->dlsch[i][j] = new_eNB_dlsch(1,8,NSOFT,fp->N_RB_DL,0,fp)) != NULL, + "Can't get eNB dlsch structures for UE %d \n", i); + eNB->dlsch[i][j]->rnti=0; + LOG_D(PHY,"dlsch[%d][%d] => %p rnti:%d\n",i,j,eNB->dlsch[i][j], eNB->dlsch[i][j]->rnti); } LOG_D(PHY,"Allocating Transport Channel Buffer for ULSCH, UE %d\n",i); - eNB->ulsch[1+i] = new_eNB_ulsch(MAX_TURBO_ITERATIONS,fp->N_RB_UL, 0); - - if (!eNB->ulsch[1+i]) { - LOG_E(PHY,"Can't get eNB ulsch structures\n"); - exit(-1); - } - + AssertFatal((eNB->ulsch[1+i] = new_eNB_ulsch(MAX_TURBO_ITERATIONS,fp->N_RB_UL, 0)) != NULL, + "Can't get eNB ulsch structures\n"); // this is the transmission mode for the signalling channels // this will be overwritten with the real transmission mode by the RRC once the UE is connected eNB->transmission_mode[i] = fp->nb_antenna_ports_eNB==1 ? 1 : 2; } // ULSCH for RA - eNB->ulsch[0] = new_eNB_ulsch(MAX_TURBO_ITERATIONS, fp->N_RB_UL, 0); - - if (!eNB->ulsch[0]) { - LOG_E(PHY,"Can't get eNB ulsch structures\n"); - exit(-1); - } - + AssertFatal( (eNB->ulsch[0] = new_eNB_ulsch(MAX_TURBO_ITERATIONS, fp->N_RB_UL, 0)) !=NULL, + "Can't get eNB ulsch structures\n"); eNB->dlsch_SI = new_eNB_dlsch(1,8,NSOFT,fp->N_RB_DL, 0, fp); LOG_D(PHY,"eNB %d.%d : SI %p\n",eNB->Mod_id,eNB->CC_id,eNB->dlsch_SI); eNB->dlsch_ra = new_eNB_dlsch(1,8,NSOFT,fp->N_RB_DL, 0, fp); @@ -161,7 +173,7 @@ void init_transport(PHY_VARS_eNB *eNB) { LOG_D(PHY,"eNB %d.%d : MCH %p\n",eNB->Mod_id,eNB->CC_id,eNB->dlsch_MCH); eNB->rx_total_gain_dB=130; - for(i=0; i<NUMBER_OF_UE_MAX; i++) + for(int i=0; i<NUMBER_OF_UE_MAX; i++) eNB->mu_mimo_mode[i].dl_pow_off = 2; eNB->check_for_total_transmissions = 0; @@ -172,23 +184,19 @@ void init_transport(PHY_VARS_eNB *eNB) { } void init_eNB_afterRU(void) { - int inst,CC_id,ru_id,i,aa; - PHY_VARS_eNB *eNB; - - for (inst=0; inst<RC.nb_inst; inst++) { - for (CC_id=0; CC_id<RC.nb_CC[inst]; CC_id++) { - eNB = RC.eNB[inst][CC_id]; + for (int inst=0; inst<RC.nb_inst; inst++) { + for (int CC_id=0; CC_id<RC.nb_CC[inst]; CC_id++) { + PHY_VARS_eNB *eNB = RC.eNB[inst][CC_id]; phy_init_lte_eNB(eNB,0,0); eNB->frame_parms.nb_antennas_rx = 0; eNB->frame_parms.nb_antennas_tx = 0; eNB->prach_vars.rxsigF[0] = (int16_t **)malloc16(64*sizeof(int16_t *)); for (int ce_level=0; ce_level<4; ce_level++) { - LOG_I(PHY,"Overwriting eNB->prach_vars_br.rxsigF.rxsigF[0]:%p\n", eNB->prach_vars_br.rxsigF[ce_level]); eNB->prach_vars_br.rxsigF[ce_level] = (int16_t **)malloc16(64*sizeof(int16_t *)); } - for (ru_id=0,aa=0; ru_id<eNB->num_RU; ru_id++) { + for (int ru_id=0,aa=0; ru_id<eNB->num_RU; ru_id++) { eNB->frame_parms.nb_antennas_rx += eNB->RU_list[ru_id]->nb_rx; eNB->frame_parms.nb_antennas_tx += eNB->RU_list[ru_id]->nb_tx; AssertFatal(eNB->RU_list[ru_id]->common.rxdataF!=NULL, @@ -198,7 +206,7 @@ void init_eNB_afterRU(void) { "RU %d : prach_rxsigF is NULL\n", eNB->RU_list[ru_id]->idx); - for (i=0; i<eNB->RU_list[ru_id]->nb_rx; aa++,i++) { + for (int i=0; i<eNB->RU_list[ru_id]->nb_rx; aa++,i++) { LOG_I(PHY,"Attaching RU %d antenna %d to eNB antenna %d\n",eNB->RU_list[ru_id]->idx,i,aa); eNB->prach_vars.rxsigF[0][aa] = eNB->RU_list[ru_id]->prach_rxsigF[i]; @@ -209,35 +217,24 @@ void init_eNB_afterRU(void) { } } - /* TODO: review this code, there is something wrong. - * In monolithic mode, we come here with nb_antennas_rx == 0 - * (not tested in other modes). - */ AssertFatal( eNB->frame_parms.nb_antennas_rx > 0 && eNB->frame_parms.nb_antennas_rx < 4, ""); AssertFatal( eNB->frame_parms.nb_antennas_tx > 0 && eNB->frame_parms.nb_antennas_rx < 4, ""); LOG_I(PHY,"inst %d, CC_id %d : nb_antennas_rx %d\n",inst,CC_id,eNB->frame_parms.nb_antennas_rx); init_transport(eNB); //init_precoding_weights(RC.eNB[inst][CC_id]); } - - init_eNB_proc(inst); } } void init_eNB(int single_thread_flag,int wait_for_sync) { - int CC_id; - int inst; - PHY_VARS_eNB *eNB; + AssertFatal(RC.eNB != NULL,"RC.eNB must have been allocated\n"); - if (RC.eNB == NULL) RC.eNB = (PHY_VARS_eNB ** *) malloc(RC.nb_L1_inst*sizeof(PHY_VARS_eNB **)); + for (int inst=0; inst<RC.nb_L1_inst; inst++) { + AssertFatal(RC.eNB[inst] != NULL,"RC.eNB[%d] must have been allocated\n", inst); - for (inst=0; inst<RC.nb_L1_inst; inst++) { - if (RC.eNB[inst] == NULL) RC.eNB[inst] = (PHY_VARS_eNB **) malloc(RC.nb_CC[inst]*sizeof(PHY_VARS_eNB *)); - - for (CC_id=0; CC_id<RC.nb_L1_CC[inst]; CC_id++) { - if (RC.eNB[inst][CC_id] == NULL) RC.eNB[inst][CC_id] = (PHY_VARS_eNB *) malloc(sizeof(PHY_VARS_eNB)); - - eNB = RC.eNB[inst][CC_id]; + for (int CC_id=0; CC_id<RC.nb_L1_CC[inst]; CC_id++) { + AssertFatal(RC.eNB[inst][CC_id] != NULL,"RC.eNB[%d][%d] must have been allocated\n", inst, CC_id); + PHY_VARS_eNB *eNB = RC.eNB[inst][CC_id]; eNB->abstraction_flag = 0; eNB->single_thread_flag = single_thread_flag; AssertFatal((eNB->if_inst = IF_Module_init(inst))!=NULL,"Cannot register interface"); @@ -245,6 +242,7 @@ void init_eNB(int single_thread_flag,int wait_for_sync) { eNB->if_inst->PHY_config_req = phy_config_request; memset((void *)&eNB->UL_INFO,0,sizeof(eNB->UL_INFO)); memset((void *)&eNB->Sched_INFO,0,sizeof(eNB->Sched_INFO)); + pthread_mutex_init( &eNB->UL_INFO_mutex, NULL); LOG_I(PHY,"Setting indication lists\n"); eNB->UL_INFO.rx_ind.rx_indication_body.rx_pdu_list = eNB->rx_pdu_list; eNB->UL_INFO.crc_ind.crc_indication_body.crc_pdu_list = eNB->crc_pdu_list; @@ -381,7 +379,6 @@ int setup_RU_buffers(RU_t *ru) { return(0); } - void init_precoding_weights(PHY_VARS_eNB *eNB) { int layer,ru_id,aa,re,ue,tb; LTE_DL_FRAME_PARMS *fp=&eNB->frame_parms; @@ -416,21 +413,21 @@ void init_precoding_weights(PHY_VARS_eNB *eNB) { } void ocp_rx_prach(PHY_VARS_eNB *eNB, - L1_rxtx_proc_t *proc, - RU_t *ru, - uint16_t *max_preamble, - uint16_t *max_preamble_energy, - uint16_t *max_preamble_delay, - uint16_t *avg_preamble_energy, - uint16_t Nf, - uint8_t tdd_mapindex, - uint8_t br_flag) { + L1_rxtx_proc_t *proc, + RU_t *ru, + uint16_t *max_preamble, + uint16_t *max_preamble_energy, + uint16_t *max_preamble_delay, + uint16_t *avg_preamble_energy, + uint16_t Nf, + uint8_t tdd_mapindex, + uint8_t br_flag) { int i; int prach_mask=0; - + if (br_flag == 0) { rx_prach0(eNB,ru,proc->frame_prach, proc->subframe_prach, - max_preamble,max_preamble_energy,max_preamble_delay,avg_preamble_energy,Nf,tdd_mapindex,0,0); + max_preamble,max_preamble_energy,max_preamble_delay,avg_preamble_energy,Nf,tdd_mapindex,0,0); } else { // This is procedure for eMTC, basically handling the repetitions prach_mask = is_prach_subframe(&eNB->frame_parms,proc->frame_prach_br,proc->subframe_prach_br); @@ -445,7 +442,7 @@ void ocp_rx_prach(PHY_VARS_eNB *eNB, eNB->prach_vars_br.repetition_number[i]++; // do basic PRACH reception rx_prach0(eNB,ru,proc->frame_prach, proc->subframe_prach_br, - max_preamble,max_preamble_energy,max_preamble_delay,avg_preamble_energy,Nf,tdd_mapindex,1,i); + max_preamble,max_preamble_energy,max_preamble_delay,avg_preamble_energy,Nf,tdd_mapindex,1,i); // if last repetition, clear counter if (eNB->prach_vars_br.repetition_number[i] == eNB->frame_parms.prach_emtc_config_common.prach_ConfigInfo.prach_numRepetitionPerPreambleAttempt[i]) { @@ -477,16 +474,16 @@ void prach_procedures_ocp(PHY_VARS_eNB *eNB, L1_rxtx_proc_t *proc, int br_flag) // run PRACH detection for CE-level 0 only for now when br_flag is set ocp_rx_prach(eNB, - proc, - eNB->RU_list[0], - &max_preamble[0], - &max_preamble_energy[0], - &max_preamble_delay[0], - &avg_preamble_energy[0], - proc->frame_prach, - 0 - ,br_flag - ); + proc, + eNB->RU_list[0], + &max_preamble[0], + &max_preamble_energy[0], + &max_preamble_delay[0], + &avg_preamble_energy[0], + proc->frame_prach, + 0 + ,br_flag + ); LOG_D(PHY,"RACH detection index 0: max preamble: %u, energy: %u, delay: %u, avg energy: %u\n", max_preamble[0], max_preamble_energy[0], @@ -531,7 +528,7 @@ void prach_procedures_ocp(PHY_VARS_eNB *eNB, L1_rxtx_proc_t *proc, int br_flag) /* ind++; } - } */// ce_level + } */// ce_level } else if ((eNB->prach_energy_counter == 100) && (max_preamble_energy[0] > eNB->measurements.prach_I0+eNB->prach_DTX_threshold)) { LOG_I(PHY,"[eNB %d/%d][RAPROC] Frame %d, subframe %d Initiating RA procedure with preamble %d, energy %d.%d dB, delay %d\n", @@ -616,13 +613,6 @@ static inline int rxtx(PHY_VARS_eNB *eNB,L1_rxtx_proc_t *proc, char *thread_name return(0); } -void eNB_top(PHY_VARS_eNB *eNB, L1_rxtx_proc_t *proc, int dummy1, int dummy2, char *string,RU_t *ru) { - if (!oai_exit) { - if (rxtx(eNB,proc,string) < 0) - LOG_E(PHY,"eNB %d CC_id %d failed during execution\n",eNB->Mod_id,eNB->CC_id); - } -} - void rx_rf(RU_t *ru, L1_rxtx_proc_t *proc) { LTE_DL_FRAME_PARMS *fp = ru->frame_parms; void *rxp[ru->nb_rx]; @@ -728,42 +718,36 @@ void ocp_tx_rf(RU_t *ru, L1_rxtx_proc_t *proc) { static void *ru_thread( void *param ) { setbuf(stdout, NULL); setbuf(stderr, NULL); - RU_t *ru = (RU_t *)param; + RU_t *ru = (RU_t *)param; L1_rxtx_proc_t L1proc= {0}; - L1_rxtx_proc_t *proc=&L1proc; - - if ( strlen(get_softmodem_params()->threadPoolConfig) > 0 ) - initTpool(get_softmodem_params()->threadPoolConfig, &L1proc.threadPool, true); - else - initTpool("n", &L1proc.threadPool, true); - - initNotifiedFIFO(&L1proc.respEncode); - initNotifiedFIFO(&L1proc.respDecode); - + // We pick the global thread pool from the legacy code global vars + L1proc.threadPool=RC.eNB[0][0]->proc.L1_proc.threadPool; + L1proc.respEncode=RC.eNB[0][0]->proc.L1_proc.respEncode; + L1proc.respDecode=RC.eNB[0][0]->proc.L1_proc.respDecode; + if (ru->if_south == LOCAL_RF) { // configure RF parameters only fill_rf_config(ru,ru->rf_config_file); init_frame_parms(ru->frame_parms,1); phy_init_RU(ru); init_rf(ru); } - + AssertFatal(setup_RU_buffers(ru)==0, "Exiting, cannot initialize RU Buffers\n"); LOG_I(PHY, "Signaling main thread that RU %d is ready\n",ru->idx); wait_sync("ru_thread"); - + // Start RF device if any if (ru->rfdevice.trx_start_func(&ru->rfdevice) != 0) LOG_E(HW,"Could not start the RF device\n"); else LOG_I(PHY,"RU %d rf device ready\n",ru->idx); - + // This is a forever while loop, it loops over subframes which are scheduled by incoming samples from HW devices while (!oai_exit) { // synchronization on input FH interface, acquire signals/data and block - rx_rf(ru, proc); - + rx_rf(ru, &L1proc); // do RX front-end processing (frequency-shift, dft) if needed - fep_full(ru, proc->subframe_rx); - + fep_full(ru, L1proc.subframe_rx); + // At this point, all information for subframe has been received on FH interface // If this proc is to provide synchronization, do so // Fixme: not used @@ -771,24 +755,23 @@ static void *ru_thread( void *param ) { for (int i=0; i<ru->num_eNB; i++) { char string[20]; sprintf(string,"Incoming RU %d",ru->idx); - eNB_top(ru->eNB_list[i],proc, proc->frame_rx,proc->subframe_rx,string,ru); + + if (rxtx(ru->eNB_list[i],&L1proc,string) < 0) + LOG_E(PHY,"eNB %d CC_id %d failed during execution\n", + ru->eNB_list[i]->Mod_id,ru->eNB_list[i]->CC_id); } - + // do TX front-end processing if needed (precoding and/or IDFTs) - feptx_prec(ru, proc->frame_tx, proc->subframe_tx); - + feptx_prec(ru, L1proc.frame_tx, L1proc.subframe_tx); // do OFDM if needed - feptx_ofdm(ru, proc->frame_tx, proc->subframe_tx); - + feptx_ofdm(ru, L1proc.frame_tx, L1proc.subframe_tx); // do outgoing fronthaul (south) if needed - ocp_tx_rf(ru, proc); + ocp_tx_rf(ru, &L1proc); } - + LOG_W(PHY,"Exiting ru_thread \n"); - ru->rfdevice.trx_end_func(&ru->rfdevice); LOG_I(PHY,"RU %d rf device stopped\n",ru->idx); - return NULL; } @@ -801,34 +784,24 @@ int init_rf(RU_t *ru) { return ret; } - -void set_function_spec_param(RU_t *ru) { -} - -//extern void RCconfig_RU(void); - void init_RU(char *rf_config_file, clock_source_t clock_source,clock_source_t time_source,int send_dmrssync) { - int ru_id; RU_t *ru; PHY_VARS_eNB *eNB0= (PHY_VARS_eNB *)NULL; int i; int CC_id; - // create status mask - pthread_mutex_init(&RC.ru_mutex,NULL); - pthread_cond_init(&RC.ru_cond,NULL); // read in configuration file) LOG_I(PHY,"configuring RU from file\n"); - RCconfig_RU(); LOG_I(PHY,"number of L1 instances %d, number of RU %d, number of CPU cores %d\n", RC.nb_L1_inst,RC.nb_RU,get_nprocs()); if (RC.nb_CC != 0) for (i=0; i<RC.nb_L1_inst; i++) - for (CC_id=0; CC_id<RC.nb_CC[i]; CC_id++) RC.eNB[i][CC_id]->num_RU=0; + for (CC_id=0; CC_id<RC.nb_CC[i]; CC_id++) + RC.eNB[i][CC_id]->num_RU=0; LOG_D(PHY,"Process RUs RC.nb_RU:%d\n",RC.nb_RU); - for (ru_id=0; ru_id<RC.nb_RU; ru_id++) { + for (int ru_id=0; ru_id<RC.nb_RU; ru_id++) { LOG_D(PHY,"Process RC.ru[%d]\n",ru_id); ru = RC.ru[ru_id]; ru->rf_config_file = rf_config_file; @@ -862,43 +835,23 @@ void init_RU(char *rf_config_file, clock_source_t clock_source,clock_source_t ti if (ru->num_eNB > 0) { LOG_D(PHY, "%s() RC.ru[%d].num_eNB:%d ru->eNB_list[0]:%p RC.eNB[0][0]:%p rf_config_file:%s\n", __FUNCTION__, ru_id, ru->num_eNB, ru->eNB_list[0], RC.eNB[0][0], ru->rf_config_file); - - if (ru->eNB_list[0] == 0) { - LOG_E(PHY,"%s() DJP - ru->eNB_list ru->num_eNB are not initialized - so do it manually\n", - __FUNCTION__); - ru->eNB_list[0] = RC.eNB[0][0]; - ru->num_eNB=1; - // - // DJP - feptx_prec() / feptx_ofdm() parses the eNB_list (based on num_eNB) and copies the txdata_F to txdata in RU - // - } else { - LOG_E(PHY,"DJP - delete code above this %s:%d\n", __FILE__, __LINE__); - } + AssertFatal(ru->eNB_list[0], "ru->eNB_list is not initialized\n"); + } else { + LOG_E(PHY,"Wrong data model, assigning eNB 0, carrier 0 to RU 0\n"); + ru->eNB_list[0] = RC.eNB[0][0]; + ru->num_eNB=1; } eNB0 = ru->eNB_list[0]; - LOG_D(PHY, "RU FUnction:%d ru->if_south:%d\n", ru->function, ru->if_south); - LOG_D(PHY, "eNB0:%p\n", eNB0); - - if (eNB0) { - LOG_I(PHY,"Copying frame parms from eNB %d to ru %d\n",eNB0->Mod_id,ru->idx); - ru->frame_parms=&eNB0->frame_parms; + // datamodel error in regular OAI: a RU uses one single eNB carrier parameters! + ru->frame_parms = &eNB0->frame_parms; - for (i=0; i<ru->num_eNB; i++) { - eNB0 = ru->eNB_list[i]; - eNB0->RU_list[eNB0->num_RU++] = ru; - } + for (i=0; i<ru->num_eNB; i++) { + eNB0 = ru->eNB_list[i]; + int ruIndex=eNB0->num_RU++; + eNB0->RU_list[ruIndex] = ru; } - - LOG_I(PHY,"Initializing RRU descriptor %d : (%s,%s,%d)\n", - ru_id,ru_if_types[ru->if_south],NB_timing[ru->if_timing],ru->function); - set_function_spec_param(ru); - LOG_I(PHY,"Starting ru_thread %d\n",ru_id); - init_RU_proc(ru); } // for ru_id - - // sleep(1); - LOG_D(HW,"[lte-softmodem.c] RU threads created\n"); } void stop_RU(int nb_ru) { @@ -912,20 +865,25 @@ void stop_RU(int nb_ru) { /* from here function to use configuration module */ static int DEFBFW[] = {0x00007fff}; void RCconfig_RU(void) { - int j = 0; - int i = 0; paramdef_t RUParams[] = RUPARAMS_DESC; paramlist_def_t RUParamList = {CONFIG_STRING_RU_LIST,NULL,0}; config_getlist( &RUParamList,RUParams,sizeof(RUParams)/sizeof(paramdef_t), NULL); - if ( RUParamList.numelt == 0 ) { + if ( RUParamList.numelt == 0 ) { + LOG_W(PHY, "Calling RCconfig_RU while no ru\n"); RC.nb_RU = 0; return; } // setting != NULL + if ( RC.ru != NULL ) { + LOG_W(PHY, "Calling RCconfig_RU twice (nb ru=%d), ignoring the second call data structure is %p\n", + RUParamList.numelt,RC.ru); + return; + } + RC.ru = (RU_t **)malloc(RC.nb_RU*sizeof(RU_t *)); - for (j = 0; j < RC.nb_RU; j++) { + for (int j = 0; j < RC.nb_RU; j++) { RC.ru[j] = (RU_t *)calloc(sizeof(RU_t), 1); RC.ru[j]->idx = j; LOG_I(PHY,"Creating RC.ru[%d]:%p\n", j, RC.ru[j]); @@ -937,7 +895,7 @@ void RCconfig_RU(void) { else RC.ru[j]->num_eNB = 0; - for (i=0; i<RC.ru[j]->num_eNB; i++) + for (int i=0; i<RC.ru[j]->num_eNB; i++) RC.ru[j]->eNB_list[i] = RC.eNB[vals[RU_ENB_LIST_IDX].iptr[i]][0]; if (config_isparamset(vals, RU_SDR_ADDRS)) { @@ -981,7 +939,7 @@ void RCconfig_RU(void) { RC.ru[j]->sf_extension = *(vals[RU_SF_EXTENSION_IDX].uptr); RC.ru[j]->end_of_burst_delay = *(vals[RU_END_OF_BURST_DELAY_IDX].uptr); - for (i=0; i<RC.ru[j]->num_bands; i++) RC.ru[j]->band[i] = vals[RU_BAND_LIST_IDX].iptr[i]; + for (int i=0; i<RC.ru[j]->num_bands; i++) RC.ru[j]->band[i] = vals[RU_BAND_LIST_IDX].iptr[i]; } else { LOG_I(PHY,"RU %d: Transport %s\n",j,*(vals[RU_TRANSPORT_PREFERENCE_IDX].strptr)); RC.ru[j]->eth_params.local_if_name = strdup(*(vals[RU_LOCAL_IF_NAME_IDX].strptr)); @@ -1001,3 +959,430 @@ void RCconfig_RU(void) { return; } + + +static void get_options(void) { + CONFIG_SETRTFLAG(CONFIG_NOEXITONHELP); + get_common_options(); + CONFIG_CLEARRTFLAG(CONFIG_NOEXITONHELP); + + if ( !(CONFIG_ISFLAGSET(CONFIG_ABORT)) ) { + memset((void *)&RC,0,sizeof(RC)); + /* Read RC configuration file */ + RCConfig(); + NB_eNB_INST = RC.nb_inst; + printf("Configuration: nb_rrc_inst %d, nb_L1_inst %d, nb_ru %d\n",NB_eNB_INST,RC.nb_L1_inst,RC.nb_RU); + + if (!IS_SOFTMODEM_NONBIOT) { + load_NB_IoT(); + printf(" nb_nbiot_rrc_inst %d, nb_nbiot_L1_inst %d, nb_nbiot_macrlc_inst %d\n", + RC.nb_nb_iot_rrc_inst, RC.nb_nb_iot_L1_inst, RC.nb_nb_iot_macrlc_inst); + } else { + printf("All Nb-IoT instances disabled\n"); + RC.nb_nb_iot_rrc_inst=RC.nb_nb_iot_L1_inst=RC.nb_nb_iot_macrlc_inst=0; + } + } +} + +void set_default_frame_parms(LTE_DL_FRAME_PARMS *frame_parms[MAX_NUM_CCs]) { + int CC_id; + + for (CC_id=0; CC_id<MAX_NUM_CCs; CC_id++) { + frame_parms[CC_id] = (LTE_DL_FRAME_PARMS *) malloc(sizeof(LTE_DL_FRAME_PARMS)); + /* Set some default values that may be overwritten while reading options */ + frame_parms[CC_id]->frame_type = FDD; + frame_parms[CC_id]->tdd_config = 3; + frame_parms[CC_id]->tdd_config_S = 0; + frame_parms[CC_id]->N_RB_DL = 100; + frame_parms[CC_id]->N_RB_UL = 100; + frame_parms[CC_id]->Ncp = NORMAL; + frame_parms[CC_id]->Ncp_UL = NORMAL; + frame_parms[CC_id]->Nid_cell = 0; + frame_parms[CC_id]->num_MBSFN_config = 0; + frame_parms[CC_id]->nb_antenna_ports_eNB = 1; + frame_parms[CC_id]->nb_antennas_tx = 1; + frame_parms[CC_id]->nb_antennas_rx = 1; + frame_parms[CC_id]->nushift = 0; + frame_parms[CC_id]->phich_config_common.phich_resource = oneSixth; + frame_parms[CC_id]->phich_config_common.phich_duration = normal; + // UL RS Config + frame_parms[CC_id]->pusch_config_common.ul_ReferenceSignalsPUSCH.cyclicShift = 0;//n_DMRS1 set to 0 + frame_parms[CC_id]->pusch_config_common.ul_ReferenceSignalsPUSCH.groupHoppingEnabled = 0; + frame_parms[CC_id]->pusch_config_common.ul_ReferenceSignalsPUSCH.sequenceHoppingEnabled = 0; + frame_parms[CC_id]->pusch_config_common.ul_ReferenceSignalsPUSCH.groupAssignmentPUSCH = 0; + frame_parms[CC_id]->prach_config_common.rootSequenceIndex=22; + frame_parms[CC_id]->prach_config_common.prach_ConfigInfo.zeroCorrelationZoneConfig=1; + frame_parms[CC_id]->prach_config_common.prach_ConfigInfo.prach_ConfigIndex=0; + frame_parms[CC_id]->prach_config_common.prach_ConfigInfo.highSpeedFlag=0; + frame_parms[CC_id]->prach_config_common.prach_ConfigInfo.prach_FreqOffset=0; + // downlink_frequency[CC_id][0] = 2680000000; // Use float to avoid issue with frequency over 2^31. + // downlink_frequency[CC_id][1] = downlink_frequency[CC_id][0]; + // downlink_frequency[CC_id][2] = downlink_frequency[CC_id][0]; + // downlink_frequency[CC_id][3] = downlink_frequency[CC_id][0]; + //printf("Downlink for CC_id %d frequency set to %u\n", CC_id, downlink_frequency[CC_id][0]); + frame_parms[CC_id]->dl_CarrierFreq=downlink_frequency[CC_id][0]; + } +} + +void init_pdcp(void) { + if (!NODE_IS_DU(RC.rrc[0]->node_type)) { + pdcp_layer_init(); + uint32_t pdcp_initmask = (IS_SOFTMODEM_NOS1) ? + (PDCP_USE_NETLINK_BIT | LINK_ENB_PDCP_TO_IP_DRIVER_BIT) : LINK_ENB_PDCP_TO_GTPV1U_BIT; + + if (IS_SOFTMODEM_NOS1) + pdcp_initmask = pdcp_initmask | ENB_NAS_USE_TUN_BIT | SOFTMODEM_NOKRNMOD_BIT ; + + pdcp_initmask = pdcp_initmask | ENB_NAS_USE_TUN_W_MBMS_BIT; + + if ( split73!=SPLIT73_DU) + pdcp_module_init(pdcp_initmask); + + if (NODE_IS_CU(RC.rrc[0]->node_type)) { + pdcp_set_rlc_data_req_func((send_rlc_data_req_func_t)proto_agent_send_rlc_data_req); + } else { + pdcp_set_rlc_data_req_func((send_rlc_data_req_func_t) rlc_data_req); + pdcp_set_pdcp_data_ind_func((pdcp_data_ind_func_t) pdcp_data_ind); + } + } else { + pdcp_set_pdcp_data_ind_func((pdcp_data_ind_func_t) proto_agent_send_pdcp_data_ind); + } +} + +static void wait_nfapi_init(char *thread_name) { + printf( "waiting for NFAPI PNF connection and population of global structure (%s)\n",thread_name); + pthread_mutex_lock( &nfapi_sync_mutex ); + + while (nfapi_sync_var<0) + pthread_cond_wait( &nfapi_sync_cond, &nfapi_sync_mutex ); + + pthread_mutex_unlock(&nfapi_sync_mutex); + printf( "NFAPI: got sync (%s)\n", thread_name); +} + +void terminate_task(module_id_t mod_id, task_id_t from, task_id_t to) { + LOG_I(ENB_APP, "sending TERMINATE_MESSAGE from task %s (%d) to task %s (%d)\n", + itti_get_task_name(from), from, itti_get_task_name(to), to); + MessageDef *msg; + msg = itti_alloc_new_message (from, TERMINATE_MESSAGE); + itti_send_msg_to_task (to, ENB_MODULE_ID_TO_INSTANCE(mod_id), msg); +} + +int stop_L1L2(module_id_t enb_id) { + LOG_W(ENB_APP, "stopping lte-softmodem\n"); + + if (!RC.ru) { + LOG_UI(ENB_APP, "no RU configured\n"); + return -1; + } + + /* these tasks need to pick up new configuration */ + terminate_task(enb_id, TASK_ENB_APP, TASK_RRC_ENB); + oai_exit = 1; + LOG_I(ENB_APP, "calling kill_RU_proc() for instance %d\n", enb_id); + kill_RU_proc(RC.ru[enb_id]); + LOG_I(ENB_APP, "calling kill_eNB_proc() for instance %d\n", enb_id); + kill_eNB_proc(enb_id); + oai_exit = 0; + + for (int cc_id = 0; cc_id < RC.nb_CC[enb_id]; cc_id++) { + free_transport(RC.eNB[enb_id][cc_id]); + phy_free_lte_eNB(RC.eNB[enb_id][cc_id]); + } + + phy_free_RU(RC.ru[enb_id]); + free_lte_top(); + return 0; +} + +/* + * Restart the lte-softmodem after it has been soft-stopped with stop_L1L2() + */ +int restart_L1L2(module_id_t enb_id) { + RU_t *ru = RC.ru[enb_id]; + MessageDef *msg_p = NULL; + LOG_W(ENB_APP, "restarting lte-softmodem\n"); + /* block threads */ + pthread_mutex_lock(&sync_mutex); + sync_var = -1; + pthread_mutex_unlock(&sync_mutex); + RC.ru_mask |= (1 << ru->idx); + /* copy the changed frame parameters to the RU */ + /* TODO this should be done for all RUs associated to this eNB */ + memcpy(&ru->frame_parms, &RC.eNB[enb_id][0]->frame_parms, sizeof(LTE_DL_FRAME_PARMS)); + /* reset the list of connected UEs in the MAC, since in this process with + * loose all UEs (have to reconnect) */ + init_UE_list(&RC.mac[enb_id]->UE_list); + LOG_I(ENB_APP, "attempting to create ITTI tasks\n"); + // No more rrc thread, as many race conditions are hidden behind + rrc_enb_init(); + itti_mark_task_ready(TASK_RRC_ENB); + /* pass a reconfiguration request which will configure everything down to + * RC.eNB[i][j]->frame_parms, too */ + msg_p = itti_alloc_new_message(TASK_ENB_APP, RRC_CONFIGURATION_REQ); + RRC_CONFIGURATION_REQ(msg_p) = RC.rrc[enb_id]->configuration; + itti_send_msg_to_task(TASK_RRC_ENB, ENB_MODULE_ID_TO_INSTANCE(enb_id), msg_p); + /* TODO XForms might need to be restarted, but it is currently (09/02/18) + * broken, so we cannot test it */ + init_RU_proc(ru); + ru->rf_map.card = 0; + ru->rf_map.chain = 0; /* CC_id + chain_offset;*/ + init_eNB_afterRU(); + printf("Sending sync to all threads\n"); + pthread_mutex_lock(&sync_mutex); + sync_var=0; + pthread_cond_broadcast(&sync_cond); + pthread_mutex_unlock(&sync_mutex); + return 0; +} + +int main ( int argc, char **argv ) { + int i; + int CC_id = 0; + int node_type = ngran_eNB; + AssertFatal(load_configmodule(argc,argv,0), "[SOFTMODEM] Error, configuration module init failed\n"); + logInit(); + printf("Reading in command-line options\n"); + get_options (); + AssertFatal(!CONFIG_ISFLAGSET(CONFIG_ABORT),"Getting configuration failed\n"); + EPC_MODE_ENABLED = !IS_SOFTMODEM_NOS1; +#if T_TRACER + T_Config_Init(); +#endif + configure_linux(); + cpuf=get_cpu_freq_GHz(); + set_taus_seed (0); + + if (opp_enabled ==1) + reset_opp_meas(); + + itti_init(TASK_MAX, THREAD_MAX, MESSAGES_ID_MAX, tasks_info, messages_info); + init_opt(); +#ifndef PACKAGE_VERSION +# define PACKAGE_VERSION "UNKNOWN-EXPERIMENTAL" +#endif + LOG_I(HW, "Version: %s\n", PACKAGE_VERSION); + + /* Read configuration */ + if (RC.nb_inst > 0) { + // Allocate memory from RC variable + read_config_and_init(); + } else { + printf("RC.nb_inst = 0, Initializing L1\n"); + RCconfig_L1(); + } + + /* We need to read RU configuration before FlexRAN starts so it knows what + * splits to report. Actual RU start comes later. */ + if (RC.nb_RU > 0 && NFAPI_MODE != NFAPI_MODE_VNF) { + RCconfig_RU(); + LOG_I(PHY, + "number of L1 instances %d, number of RU %d, number of CPU cores %d\n", + RC.nb_L1_inst, RC.nb_RU, get_nprocs()); + } + + if ( strlen(get_softmodem_params()->split73) > 0 ) { + char tmp[1024]={0}; + strncpy(tmp,get_softmodem_params()->split73, 1023); + tmp[2]=0; + if ( strncasecmp(tmp,"cu", 2)==0 ) + split73=SPLIT73_CU; + else if ( strncasecmp(tmp,"du", 2)==0 ) + split73=SPLIT73_DU; + else + AssertFatal(false,"split73 syntax: <cu|du>:<remote ip addr>[:<ip port>] (string found: %s) \n",get_softmodem_params()->split73); + } + + if (RC.nb_inst > 0) { + /* Start the agent. If it is turned off in the configuration, it won't start */ + for (i = 0; i < RC.nb_inst; i++) { + flexran_agent_start(i); + } + + /* initializes PDCP and sets correct RLC Request/PDCP Indication callbacks + * for monolithic/F1 modes */ + init_pdcp(); + AssertFatal(create_tasks(1)==0,"cannot create ITTI tasks\n"); + + for (int enb_id = 0; enb_id < RC.nb_inst; enb_id++) { + MessageDef *msg_p = itti_alloc_new_message (TASK_ENB_APP, RRC_CONFIGURATION_REQ); + RRC_CONFIGURATION_REQ(msg_p) = RC.rrc[enb_id]->configuration; + itti_send_msg_to_task (TASK_RRC_ENB, ENB_MODULE_ID_TO_INSTANCE(enb_id), msg_p); + } + + node_type = RC.rrc[0]->node_type; + } + + if (RC.nb_inst > 0 && NODE_IS_CU(node_type)) { + protocol_ctxt_t ctxt; + ctxt.module_id = 0 ; + ctxt.instance = 0; + ctxt.rnti = 0; + ctxt.enb_flag = 1; + ctxt.frame = 0; + ctxt.subframe = 0; + pdcp_run(&ctxt); + } + + /* start threads if only L1 or not a CU */ + if (RC.nb_inst == 0 || !NODE_IS_CU(node_type) || NFAPI_MODE == NFAPI_MODE_PNF || NFAPI_MODE == NFAPI_MODE_VNF) { + // init UE_PF_PO and mutex lock + pthread_mutex_init(&ue_pf_po_mutex, NULL); + memset (&UE_PF_PO[0][0], 0, sizeof(UE_PF_PO_t)*MAX_MOBILES_PER_ENB*MAX_NUM_CCs); + pthread_cond_init(&sync_cond,NULL); + pthread_mutex_init(&sync_mutex, NULL); + + if (NFAPI_MODE!=NFAPI_MONOLITHIC) { + LOG_I(ENB_APP,"NFAPI*** - mutex and cond created - will block shortly for completion of PNF connection\n"); + pthread_cond_init(&sync_cond,NULL); + pthread_mutex_init(&sync_mutex, NULL); + } + + if (NFAPI_MODE==NFAPI_MODE_VNF) {// VNF +#if defined(PRE_SCD_THREAD) + init_ru_vnf(); // ru pointer is necessary for pre_scd. +#endif + wait_nfapi_init("main?"); + } + + LOG_I(ENB_APP,"START MAIN THREADS\n"); + // start the main threads + number_of_cards = 1; + printf("RC.nb_L1_inst:%d\n", RC.nb_L1_inst); + + if (RC.nb_L1_inst > 0) { + printf("Initializing eNB threads single_thread_flag:%d wait_for_sync:%d\n", + get_softmodem_params()->single_thread_flag, + get_softmodem_params()->wait_for_sync); + init_eNB(get_softmodem_params()->single_thread_flag, + get_softmodem_params()->wait_for_sync); + } + + for (int x=0; x < RC.nb_L1_inst; x++) + for (int CC_id=0; CC_id<RC.nb_L1_CC[x]; CC_id++) { + L1_rxtx_proc_t *L1proc= &RC.eNB[x][CC_id]->proc.L1_proc; + L1proc->threadPool=(tpool_t *)malloc(sizeof(tpool_t)); + L1proc->respEncode=(notifiedFIFO_t *) malloc(sizeof(notifiedFIFO_t)); + L1proc->respDecode=(notifiedFIFO_t *) malloc(sizeof(notifiedFIFO_t)); + + if ( strlen(get_softmodem_params()->threadPoolConfig) > 0 ) + initTpool(get_softmodem_params()->threadPoolConfig, L1proc->threadPool, true); + else + initTpool("n", L1proc->threadPool, true); + + initNotifiedFIFO(L1proc->respEncode); + initNotifiedFIFO(L1proc->respDecode); + } + } + + printf("About to Init RU threads RC.nb_RU:%d\n", RC.nb_RU); + + // RU thread and some L1 procedure aren't necessary in VNF or L2 FAPI simulator. + // but RU thread deals with pre_scd and this is necessary in VNF and simulator. + // some initialization is necessary and init_ru_vnf do this. + if (RC.nb_RU >0 && NFAPI_MODE!=NFAPI_MODE_VNF) { + printf("Initializing RU threads\n"); + init_RU(get_softmodem_params()->rf_config_file, + get_softmodem_params()->clock_source, + get_softmodem_params()->timing_source, + get_softmodem_params()->send_dmrs_sync); + + for (int ru_id=0; ru_id<RC.nb_RU; ru_id++) { + RC.ru[ru_id]->rf_map.card=0; + RC.ru[ru_id]->rf_map.chain=CC_id+(get_softmodem_params()->chain_offset); + LOG_I(PHY,"Starting ru_thread %d\n",ru_id); + init_RU_proc(RC.ru[ru_id]); + } + + config_sync_var=0; + + if (NFAPI_MODE==NFAPI_MODE_PNF) { // PNF + wait_nfapi_init("main?"); + } + + LOG_I(ENB_APP,"RC.nb_RU:%d\n", RC.nb_RU); + // once all RUs are ready intiailize the rest of the eNBs ((dependence on final RU parameters after configuration) + printf("ALL RUs ready - init eNBs\n"); + + if (NFAPI_MODE!=NFAPI_MODE_PNF && NFAPI_MODE!=NFAPI_MODE_VNF) { + LOG_I(ENB_APP,"Not NFAPI mode - call init_eNB_afterRU()\n"); + init_eNB_afterRU(); + } else { + LOG_I(ENB_APP,"NFAPI mode - DO NOT call init_eNB_afterRU()\n"); + } + + LOG_UI(ENB_APP,"ALL RUs ready - ALL eNBs ready\n"); + // connect the TX/RX buffers + sleep(1); /* wait for thread activation */ + LOG_I(ENB_APP,"Sending sync to all threads\n"); + pthread_mutex_lock(&sync_mutex); + sync_var=0; + pthread_cond_broadcast(&sync_cond); + pthread_mutex_unlock(&sync_mutex); + config_check_unknown_cmdlineopt(CONFIG_CHECKALLSECTIONS); + } + + create_tasks_mbms(1); + // wait for end of program + LOG_UI(ENB_APP,"TYPE <CTRL-C> TO TERMINATE\n"); + // CI -- Flushing the std outputs for the previous marker to show on the eNB / DU / CU log file + fflush(stdout); + fflush(stderr); + + // end of CI modifications + //getchar(); + if(IS_SOFTMODEM_DOFORMS) + load_softscope("enb"); + + itti_wait_tasks_end(); + oai_exit=1; + LOG_I(ENB_APP,"oai_exit=%d\n",oai_exit); + // stop threads + + if (RC.nb_inst == 0 || !NODE_IS_CU(node_type)) { + if(IS_SOFTMODEM_DOFORMS) + end_forms(); + + LOG_I(ENB_APP,"stopping MODEM threads\n"); + stop_eNB(NB_eNB_INST); + stop_RU(RC.nb_RU); + + /* release memory used by the RU/eNB threads (incomplete), after all + * threads have been stopped (they partially use the same memory) */ + for (int inst = 0; inst < NB_eNB_INST; inst++) { + for (int cc_id = 0; cc_id < RC.nb_CC[inst]; cc_id++) { + free_transport(RC.eNB[inst][cc_id]); + phy_free_lte_eNB(RC.eNB[inst][cc_id]); + } + } + + for (int inst = 0; inst < RC.nb_RU; inst++) { + phy_free_RU(RC.ru[inst]); + } + + free_lte_top(); + end_configmodule(); + pthread_cond_destroy(&sync_cond); + pthread_mutex_destroy(&sync_mutex); + pthread_cond_destroy(&nfapi_sync_cond); + pthread_mutex_destroy(&nfapi_sync_mutex); + pthread_mutex_destroy(&ue_pf_po_mutex); + + for(int ru_id=0; ru_id<RC.nb_RU; ru_id++) { + if (RC.ru[ru_id]->rfdevice.trx_end_func) { + RC.ru[ru_id]->rfdevice.trx_end_func(&RC.ru[ru_id]->rfdevice); + RC.ru[ru_id]->rfdevice.trx_end_func = NULL; + } + + if (RC.ru[ru_id]->ifdevice.trx_end_func) { + RC.ru[ru_id]->ifdevice.trx_end_func(&RC.ru[ru_id]->ifdevice); + RC.ru[ru_id]->ifdevice.trx_end_func = NULL; + } + } + } + + terminate_opt(); + logClean(); + printf("Bye.\n"); + return 0; +} diff --git a/executables/nr-gnb.c b/executables/nr-gnb.c index 46689b137ac..43407de19bf 100644 --- a/executables/nr-gnb.c +++ b/executables/nr-gnb.c @@ -180,14 +180,14 @@ static inline int rxtx(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int frame_t // **************************************** // Common RX procedures subframe n T(T_GNB_PHY_DL_TICK, T_INT(gNB->Mod_id), T_INT(frame_tx), T_INT(slot_tx)); -/* - // if this is IF5 or 3GPP_gNB - if (gNB && gNB->RU_list && gNB->RU_list[0] && gNB->RU_list[0]->function < NGFI_RAU_IF4p5) { - wakeup_prach_gNB(gNB,NULL,proc->frame_rx,proc->slot_rx); - } + /* + // if this is IF5 or 3GPP_gNB + if (gNB && gNB->RU_list && gNB->RU_list[0] && gNB->RU_list[0]->function < NGFI_RAU_IF4p5) { + wakeup_prach_gNB(gNB,NULL,proc->frame_rx,proc->slot_rx); + } - // UE-specific RX processing for subframe n - if (nfapi_mode == 0 || nfapi_mode == 1) */ + // UE-specific RX processing for subframe n + if (nfapi_mode == 0 || nfapi_mode == 1) */ pthread_mutex_lock(&gNB->UL_INFO_mutex); gNB->UL_INFO.frame = frame_rx; @@ -207,8 +207,8 @@ static inline int rxtx(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int frame_t if (oai_exit) return(-1); - //if (slot_rx == NR_UPLINK_SLOT || gNB->frame_parms.frame_type == FDD) - phy_procedures_gNB_uespec_RX(gNB, frame_rx, slot_rx); + //if (slot_rx == NR_UPLINK_SLOT || gNB->frame_parms.frame_type == FDD) + phy_procedures_gNB_uespec_RX(gNB, frame_rx, slot_rx); if(get_thread_parallel_conf() != PARALLEL_RU_L1_TRX_SPLIT) { phy_procedures_gNB_TX(gNB, frame_tx,slot_tx, 1); @@ -318,7 +318,7 @@ static void *gNB_L1_thread( void *param ) { gNB_L1_rxtx_proc_t *L1_proc = &gNB_proc->L1_proc; //PHY_VARS_gNB *gNB = RC.gNB[0][proc->CC_id]; char thread_name[100]; - + // set default return value // set default return value gNB_thread_rxtx_status = 0; @@ -327,16 +327,15 @@ static void *gNB_L1_thread( void *param ) { while (!oai_exit) { VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_gNB_PROC_RXTX0, 0 ); + if (wait_on_condition(&L1_proc->mutex,&L1_proc->cond,&L1_proc->instance_cnt,thread_name)<0) break; - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_gNB_PROC_RXTX0, 1 ); + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_gNB_PROC_RXTX0, 1 ); int frame_rx = L1_proc->frame_rx; int slot_rx = L1_proc->slot_rx; int frame_tx = L1_proc->frame_tx; int slot_tx = L1_proc->slot_tx; uint64_t timestamp_tx = L1_proc->timestamp_tx; - - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_SLOT_NUMBER_TX0_GNB,slot_tx); VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_SLOT_NUMBER_RX0_GNB,slot_rx); VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_TX0_GNB,frame_tx); @@ -408,17 +407,15 @@ void gNB_top(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, char *string, struct } int wakeup_txfh(PHY_VARS_gNB *gNB,gNB_L1_rxtx_proc_t *proc,int frame_tx,int slot_tx,uint64_t timestamp_tx) { - RU_t *ru; RU_proc_t *ru_proc; - int waitret = 0, ret = 0, time_ns = 1000*1000; struct timespec now, abstime; - // note this should depend on the numerology used by the TX L1 thread, set here for 500us slot time VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_GAIN_CONTROL,1); time_ns = time_ns/gNB->frame_parms.slots_per_subframe; AssertFatal((ret = pthread_mutex_lock(&proc->mutex_RUs_tx))==0,"mutex_lock returns %d\n",ret); + while (proc->instance_cnt_RUs < 0) { clock_gettime(CLOCK_REALTIME, &now); abstime.tv_sec = now.tv_sec; @@ -428,35 +425,32 @@ int wakeup_txfh(PHY_VARS_gNB *gNB,gNB_L1_rxtx_proc_t *proc,int frame_tx,int slot abstime.tv_nsec -= 1000*1000*1000; abstime.tv_sec += 1; } + if((waitret = pthread_cond_timedwait(&proc->cond_RUs,&proc->mutex_RUs_tx,&abstime)) == 0) break; // this unlocks mutex_rxtx while waiting and then locks it again } + proc->instance_cnt_RUs = -1; VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_RX0_UE,proc->instance_cnt_RUs); AssertFatal((ret = pthread_mutex_unlock(&proc->mutex_RUs_tx))==0,"mutex_unlock returns %d\n",ret); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_GAIN_CONTROL,0); if (waitret == ETIMEDOUT) { - LOG_W(PHY,"Dropping TX slot (%d.%d) because FH is blocked more than 1 slot times (500us)\n",frame_tx,slot_tx); - - AssertFatal((ret=pthread_mutex_lock(&gNB->proc.mutex_RU_tx))==0,"mutex_lock returns %d\n",ret); - gNB->proc.RU_mask_tx = 0; - AssertFatal((ret=pthread_mutex_unlock(&gNB->proc.mutex_RU_tx))==0,"mutex_unlock returns %d\n",ret); - AssertFatal((ret=pthread_mutex_lock(&proc->mutex_RUs_tx))==0,"mutex_lock returns %d\n",ret); - proc->instance_cnt_RUs = 0; - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_RX0_UE,proc->instance_cnt_RUs); - AssertFatal((ret=pthread_mutex_unlock(&proc->mutex_RUs_tx))==0,"mutex_unlock returns %d\n",ret); - - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_TX0_UE,1); - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_TX0_UE,0); - - return(-1); - } - - for(int i=0; i<gNB->num_RU; i++) - { + LOG_W(PHY,"Dropping TX slot (%d.%d) because FH is blocked more than 1 slot times (500us)\n",frame_tx,slot_tx); + AssertFatal((ret=pthread_mutex_lock(&gNB->proc.mutex_RU_tx))==0,"mutex_lock returns %d\n",ret); + gNB->proc.RU_mask_tx = 0; + AssertFatal((ret=pthread_mutex_unlock(&gNB->proc.mutex_RU_tx))==0,"mutex_unlock returns %d\n",ret); + AssertFatal((ret=pthread_mutex_lock(&proc->mutex_RUs_tx))==0,"mutex_lock returns %d\n",ret); + proc->instance_cnt_RUs = 0; + VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_RX0_UE,proc->instance_cnt_RUs); + AssertFatal((ret=pthread_mutex_unlock(&proc->mutex_RUs_tx))==0,"mutex_unlock returns %d\n",ret); + VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_TX0_UE,1); + VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_TX0_UE,0); + return(-1); + } + + for(int i=0; i<gNB->num_RU; i++) { ru = gNB->RU_list[i]; ru_proc = &ru->proc; - if (ru_proc->instance_cnt_gNBs == 0) { VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST_UE, 1); @@ -464,62 +458,46 @@ int wakeup_txfh(PHY_VARS_gNB *gNB,gNB_L1_rxtx_proc_t *proc,int frame_tx,int slot AssertFatal((ret=pthread_mutex_lock(&gNB->proc.mutex_RU_tx))==0,"mutex_lock returns %d\n",ret); gNB->proc.RU_mask_tx = 0; AssertFatal((ret=pthread_mutex_unlock(&gNB->proc.mutex_RU_tx))==0,"mutex_unlock returns %d\n",ret); - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST_UE, 0); return(-1); } - AssertFatal((ret = pthread_mutex_lock(&ru_proc->mutex_gNBs))==0,"ERROR pthread_mutex_lock failed on mutex_gNBs L1_thread_tx with ret=%d\n",ret); + AssertFatal((ret = pthread_mutex_lock(&ru_proc->mutex_gNBs))==0,"ERROR pthread_mutex_lock failed on mutex_gNBs L1_thread_tx with ret=%d\n",ret); ru_proc->instance_cnt_gNBs = 0; ru_proc->timestamp_tx = timestamp_tx; ru_proc->tti_tx = slot_tx; ru_proc->frame_tx = frame_tx; - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_RX1_UE, ru_proc->instance_cnt_gNBs); - - LOG_D(PHY,"Signaling tx_thread_fh for %d.%d\n",frame_tx,slot_tx); + LOG_D(PHY,"Signaling tx_thread_fh for %d.%d\n",frame_tx,slot_tx); // the thread can now be woken up AssertFatal(pthread_cond_signal(&ru_proc->cond_gNBs) == 0, - "[gNB] ERROR pthread_cond_signal for gNB TXnp4 thread\n"); + "[gNB] ERROR pthread_cond_signal for gNB TXnp4 thread\n"); AssertFatal((ret=pthread_mutex_unlock(&ru_proc->mutex_gNBs))==0,"mutex_unlock returned %d\n",ret); - } + return(0); } int wakeup_tx(PHY_VARS_gNB *gNB,int frame_rx,int slot_rx,int frame_tx,int slot_tx,uint64_t timestamp_tx) { - - gNB_L1_rxtx_proc_t *L1_proc_tx = &gNB->proc.L1_proc_tx; - int ret; - - AssertFatal((ret = pthread_mutex_lock(&L1_proc_tx->mutex))==0,"mutex_lock returns %d\n",ret); - - while(L1_proc_tx->instance_cnt == 0){ + while(L1_proc_tx->instance_cnt == 0) { pthread_cond_wait(&L1_proc_tx->cond,&L1_proc_tx->mutex); } L1_proc_tx->instance_cnt = 0; - - L1_proc_tx->slot_rx = slot_rx; L1_proc_tx->frame_rx = frame_rx; L1_proc_tx->slot_tx = slot_tx; L1_proc_tx->frame_tx = frame_tx; L1_proc_tx->timestamp_tx = timestamp_tx; - - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_TX1_UE,1); VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_TX1_UE,0); - // the thread can now be woken up AssertFatal(pthread_cond_signal(&L1_proc_tx->cond) == 0, "ERROR pthread_cond_signal for gNB L1 thread\n"); - AssertFatal((ret=pthread_mutex_unlock(&L1_proc_tx->mutex))==0,"mutex_unlock returns %d\n",ret); - return(0); } @@ -532,22 +510,23 @@ int wakeup_rxtx(PHY_VARS_gNB *gNB,RU_t *ru) { int i; struct timespec abstime; int time_ns = 50000; - AssertFatal((ret=pthread_mutex_lock(&proc->mutex_RU))==0,"mutex_lock returns %d\n",ret); - for (i=0;i<gNB->num_RU;i++) { + + for (i=0; i<gNB->num_RU; i++) { if (ru == gNB->RU_list[i]) { if ((proc->RU_mask&(1<<i)) > 0) - LOG_E(PHY,"gNB %d frame %d, subframe %d : previous information from RU %d (num_RU %d,mask %x) has not been served yet!\n", - gNB->Mod_id,proc->frame_rx,proc->slot_rx,ru->idx,gNB->num_RU,proc->RU_mask); + LOG_E(PHY,"gNB %d frame %d, subframe %d : previous information from RU %d (num_RU %d,mask %x) has not been served yet!\n", + gNB->Mod_id,proc->frame_rx,proc->slot_rx,ru->idx,gNB->num_RU,proc->RU_mask); + proc->RU_mask |= (1<<i); } } + if (proc->RU_mask != (1<<gNB->num_RU)-1) { // not all RUs have provided their information so return LOG_E(PHY,"Not all RUs have provided their info\n"); AssertFatal((ret=pthread_mutex_unlock(&proc->mutex_RU))==0,"mutex_unlock returns %d\n",ret); return(0); - } - else { // all RUs have provided their information so continue on and wakeup gNB processing + } else { // all RUs have provided their information so continue on and wakeup gNB processing proc->RU_mask = 0; AssertFatal((ret=pthread_mutex_unlock(&proc->mutex_RU))==0,"muex_unlock returns %d\n",ret); } @@ -565,17 +544,16 @@ int wakeup_rxtx(PHY_VARS_gNB *gNB,RU_t *ru) { AssertFatal((ret=pthread_mutex_timedlock(&L1_proc->mutex, &abstime)) == 0,"mutex_lock returns %d\n", ret); if (L1_proc->instance_cnt == 0) { // L1_thread is busy so abort the subframe - AssertFatal((ret=pthread_mutex_unlock( &L1_proc->mutex))==0,"muex_unlock return %d\n",ret); - LOG_W(PHY,"L1_thread isn't ready in %d.%d, aborting RX processing\n",ru_proc->frame_rx,ru_proc->tti_rx); - return(-1); + AssertFatal((ret=pthread_mutex_unlock( &L1_proc->mutex))==0,"muex_unlock return %d\n",ret); + LOG_W(PHY,"L1_thread isn't ready in %d.%d, aborting RX processing\n",ru_proc->frame_rx,ru_proc->tti_rx); + return(-1); } - + ++L1_proc->instance_cnt; - - // We have just received and processed the common part of a subframe, say n. - // TS_rx is the last received timestamp (start of 1st slot), TS_tx is the desired + // We have just received and processed the common part of a subframe, say n. + // TS_rx is the last received timestamp (start of 1st slot), TS_tx is the desired // transmitted timestamp of the next TX slot (first). - // The last (TS_rx mod samples_per_frame) was n*samples_per_tti, + // The last (TS_rx mod samples_per_frame) was n*samples_per_tti, // we want to generate subframe (n+sl_ahead), so TS_tx = TX_rx+sl_ahead*samples_per_tti, // and proc->slot_tx = proc->slot_rx+sl_ahead L1_proc->timestamp_tx = ru_proc->timestamp_rx + (sl_ahead*fp->samples_per_slot); @@ -583,9 +561,7 @@ int wakeup_rxtx(PHY_VARS_gNB *gNB,RU_t *ru) { L1_proc->slot_rx = ru_proc->tti_rx; L1_proc->frame_tx = (L1_proc->slot_rx > (fp->slots_per_frame-1-sl_ahead)) ? (L1_proc->frame_rx+1)&1023 : L1_proc->frame_rx; L1_proc->slot_tx = (L1_proc->slot_rx + sl_ahead)%fp->slots_per_frame; - LOG_D(PHY,"wakeupL1: passing parameter IC = %d, RX: %d.%d, TX: %d.%d to L1 sl_ahead = %d\n", L1_proc->instance_cnt, L1_proc->frame_rx, L1_proc->slot_rx, L1_proc->frame_tx, L1_proc->slot_tx, sl_ahead); - pthread_mutex_unlock( &L1_proc->mutex ); // the thread can now be woken up @@ -594,7 +570,7 @@ int wakeup_rxtx(PHY_VARS_gNB *gNB,RU_t *ru) { exit_fun( "ERROR pthread_cond_signal" ); return(-1); } - + return(0); } /* @@ -698,23 +674,20 @@ static void* gNB_thread_prach( void* param ) { extern void init_td_thread(PHY_VARS_gNB *); extern void init_te_thread(PHY_VARS_gNB *); -static void* process_stats_thread(void* param) { - - PHY_VARS_gNB *gNB = (PHY_VARS_gNB*)param; - +static void *process_stats_thread(void *param) { + PHY_VARS_gNB *gNB = (PHY_VARS_gNB *)param; reset_meas(&gNB->dlsch_encoding_stats); reset_meas(&gNB->dlsch_scrambling_stats); reset_meas(&gNB->dlsch_modulation_stats); - wait_sync("process_stats_thread"); - while(!oai_exit) - { + while(!oai_exit) { sleep(1); print_meas(&gNB->dlsch_encoding_stats, "pdsch_encoding", NULL, NULL); print_meas(&gNB->dlsch_scrambling_stats, "pdsch_scrambling", NULL, NULL); print_meas(&gNB->dlsch_modulation_stats, "pdsch_modulation", NULL, NULL); } + return(NULL); } @@ -767,6 +740,7 @@ void init_gNB_proc(int inst) { } if(opp_enabled == 1) threadCreate(&proc->L1_stats_thread, process_stats_thread,(void *)gNB, "time_meas", -1, OAI_PRIORITY_RT_LOW); + //pthread_create( &proc->pthread_prach, attr_prach, gNB_thread_prach, gNB ); char name[16]; @@ -904,7 +878,7 @@ void init_eNB_afterRU(void) { for (i=0; i<gNB->RU_list[ru_id]->nb_rx; aa++,i++) { LOG_I(PHY,"Attaching RU %d antenna %d to gNB antenna %d\n",gNB->RU_list[ru_id]->idx,i,aa); - // gNB->prach_vars.rxsigF[0][aa] = gNB->RU_list[ru_id]->prach_rxsigF[i]; + // gNB->prach_vars.rxsigF[0][aa] = gNB->RU_list[ru_id]->prach_rxsigF[i]; gNB->common_vars.rxdataF[aa] = gNB->RU_list[ru_id]->common.rxdataF[i]; } } @@ -968,7 +942,6 @@ void init_gNB(int single_thread_flag,int wait_for_sync) { gNB->UL_INFO.harq_ind.harq_indication_body.harq_pdu_list = gNB->harq_pdu_list; gNB->UL_INFO.cqi_ind.cqi_pdu_list = gNB->cqi_pdu_list; gNB->UL_INFO.cqi_ind.cqi_raw_pdu_list = gNB->cqi_raw_pdu_list; - gNB->prach_energy_counter = 0; } } diff --git a/executables/nr-ru.c b/executables/nr-ru.c index 0003cf770de..05ae216d005 100644 --- a/executables/nr-ru.c +++ b/executables/nr-ru.c @@ -92,7 +92,7 @@ unsigned short config_frames[4] = {2,9,11,13}; static int DEFBANDS[] = {7}; static int DEFENBS[] = {0}; static int DEFBFW[] = {0x00007fff}; - + //static int DEFNRBANDS[] = {7}; //static int DEFGNBS[] = {0}; @@ -712,7 +712,7 @@ void rx_rf(RU_t *ru,int *frame,int *slot) { } -void tx_rf(RU_t *ru,int frame,int slot, uint64_t timestamp) { +void tx_rf(RU_t *ru,int frame,int slot, uint64_t timestamp) { RU_proc_t *proc = &ru->proc; NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; //nfapi_nr_config_request_t *cfg = &ru->gNB_list[0]->gNB_config; @@ -758,7 +758,7 @@ void tx_rf(RU_t *ru,int frame,int slot, uint64_t timestamp) { VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_TX0_RU, frame ); VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TTI_NUMBER_TX0_RU, slot ); - for (i=0; i<ru->nb_tx; i++){ + for (i=0; i<ru->nb_tx; i++) { txp[i] = (void *)&ru->common.txdata[i][(slot*fp->samples_per_slot)-sf_extension]; } @@ -870,7 +870,7 @@ static void *ru_thread_prach( void *param ) { 0,0 ); } - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_RU_PRACH_RX, 0 );*/ + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_RU_PRACH_RX, 0 );*/ if (release_thread(&proc->mutex_prach,&proc->instance_cnt_prach,"ru_prach_thread") < 0) break; } @@ -1097,16 +1097,16 @@ void fill_rf_config(RU_t *ru, char *rf_config_file) { } } else if(N_RB == 106) { if (fp->threequarter_fs) { - cfg->sample_rate=46.08e6; - cfg->samples_per_frame = 460800; - cfg->tx_bw = 40e6; - cfg->rx_bw = 40e6; + cfg->sample_rate=46.08e6; + cfg->samples_per_frame = 460800; + cfg->tx_bw = 40e6; + cfg->rx_bw = 40e6; } else { - cfg->sample_rate=61.44e6; - cfg->samples_per_frame = 614400; - cfg->tx_bw = 40e6; - cfg->rx_bw = 40e6; + cfg->sample_rate=61.44e6; + cfg->samples_per_frame = 614400; + cfg->tx_bw = 40e6; + cfg->rx_bw = 40e6; } } else { AssertFatal(0==1,"N_RB %d not yet supported for numerology %d\n",N_RB,mu); @@ -1210,7 +1210,7 @@ static void *ru_stats_thread(void *param) { if (ru->feprx) print_meas(&ru->ofdm_demod_stats,"feprx",NULL,NULL); - if (ru->feptx_ofdm){ + if (ru->feptx_ofdm) { print_meas(&ru->precoding_stats,"feptx_prec",NULL,NULL); print_meas(&ru->txdataF_copy_stats,"txdataF_copy",NULL,NULL); print_meas(&ru->ofdm_mod_stats,"feptx_ofdm",NULL,NULL); @@ -1219,7 +1219,7 @@ static void *ru_stats_thread(void *param) { if (ru->fh_north_asynch_in) print_meas(&ru->rx_fhaul,"rx_fhaul",NULL,NULL); - print_meas(&ru->tx_fhaul,"tx_fhaul",NULL,NULL); + print_meas(&ru->tx_fhaul,"tx_fhaul",NULL,NULL); if (ru->fh_north_out) { print_meas(&ru->compression,"compression",NULL,NULL); print_meas(&ru->transport,"transport",NULL,NULL); @@ -1346,7 +1346,7 @@ static void *ru_thread_tx( void *param ) { if (L1_proc->instance_cnt_RUs == -1) { L1_proc->instance_cnt_RUs = 0; AssertFatal(pthread_cond_signal(&L1_proc->cond_RUs) == 0, - "ERROR pthread_cond_signal for gNB_L1_thread\n"); + "ERROR pthread_cond_signal for gNB_L1_thread\n"); } //else AssertFatal(1==0,"gNB TX thread is not ready\n"); ret = pthread_mutex_unlock(&L1_proc->mutex_RUs_tx); AssertFatal(ret == 0,"mutex_unlock returns %d\n",ret); @@ -1371,7 +1371,7 @@ static void *ru_thread( void *param ) { int print_frame = 8; int i = 0; int aa; - + // set default return value // set default return value ru_thread_status = 0; // set default return value @@ -1499,9 +1499,9 @@ static void *ru_thread( void *param ) { LOG_D(PHY,"RU proc: frame_rx = %d, tti_rx = %d\n", proc->frame_rx, proc->tti_rx); LOG_D(PHY,"Copying rxdataF from RU to gNB\n"); - for (aa=0;aa<ru->nb_rx;aa++) - memcpy((void*)RC.gNB[0][0]->common_vars.rxdataF[aa], - (void*)ru->common.rxdataF[aa], fp->symbols_per_slot*fp->ofdm_symbol_size*sizeof(int32_t)); + for (aa=0; aa<ru->nb_rx; aa++) + memcpy((void *)RC.gNB[0][0]->common_vars.rxdataF[aa], + (void *)ru->common.rxdataF[aa], fp->symbols_per_slot*fp->ofdm_symbol_size*sizeof(int32_t)); // At this point, all information for subframe has been received on FH interface @@ -2260,7 +2260,7 @@ void RCconfig_RU(void) } } else { - RC.ru[j]->openair0_cfg.clock_source = unset; + RC.ru[j]->openair0_cfg.clock_source = unset; } if (strcmp(*(RUParamList.paramarray[j][RU_LOCAL_RF_IDX].strptr), "yes") == 0) { diff --git a/executables/nr-softmodem.c b/executables/nr-softmodem.c index 6b725620332..60931f76740 100644 --- a/executables/nr-softmodem.c +++ b/executables/nr-softmodem.c @@ -176,6 +176,10 @@ uint32_t target_ul_mcs = 20; uint32_t timing_advance = 0; uint64_t num_missed_slots=0; // counter for the number of missed slots +int split73=0; +void sendFs6Ul(PHY_VARS_eNB *eNB, int UE_id, int harq_pid, int segmentID, int16_t *data, int dataLen, int r_offset) { + AssertFatal(false, "Must not be called in this context\n"); +} extern void reset_opp_meas(void); extern void print_opp_meas(void); @@ -721,13 +725,9 @@ int restart_L1L2(module_id_t gnb_id) { memcpy(&ru->nr_frame_parms, &RC.gNB[gnb_id][0]->frame_parms, sizeof(NR_DL_FRAME_PARMS)); set_function_spec_param(RC.ru[gnb_id]); LOG_I(GNB_APP, "attempting to create ITTI tasks\n"); - - if (itti_create_task (TASK_RRC_ENB, rrc_enb_task, NULL) < 0) { - LOG_E(RRC, "Create task for RRC eNB failed\n"); - return -1; - } else { - LOG_I(RRC, "Re-created task for RRC gNB successfully\n"); - } + // No more rrc thread, as many race conditions are hidden behind + rrc_enb_init(); + itti_mark_task_ready(TASK_RRC_ENB); if (itti_create_task (TASK_L2L1, l2l1_task, NULL) < 0) { LOG_E(PDCP, "Create task for L2L1 failed\n"); @@ -771,23 +771,23 @@ static void wait_nfapi_init(char *thread_name) { void init_pdcp(void) { //if (!NODE_IS_DU(RC.rrc[0]->node_type)) { - pdcp_layer_init(); - uint32_t pdcp_initmask = (IS_SOFTMODEM_NOS1) ? - (PDCP_USE_NETLINK_BIT | LINK_ENB_PDCP_TO_IP_DRIVER_BIT) : LINK_ENB_PDCP_TO_GTPV1U_BIT; + pdcp_layer_init(); + uint32_t pdcp_initmask = (IS_SOFTMODEM_NOS1) ? + (PDCP_USE_NETLINK_BIT | LINK_ENB_PDCP_TO_IP_DRIVER_BIT) : LINK_ENB_PDCP_TO_GTPV1U_BIT; - if (IS_SOFTMODEM_NOS1){ - printf("IS_SOFTMODEM_NOS1 option enabled \n"); - pdcp_initmask = pdcp_initmask | ENB_NAS_USE_TUN_BIT | SOFTMODEM_NOKRNMOD_BIT ; - } + if (IS_SOFTMODEM_NOS1) { + printf("IS_SOFTMODEM_NOS1 option enabled \n"); + pdcp_initmask = pdcp_initmask | ENB_NAS_USE_TUN_BIT | SOFTMODEM_NOKRNMOD_BIT ; + } - pdcp_module_init(pdcp_initmask); + pdcp_module_init(pdcp_initmask); - /*if (NODE_IS_CU(RC.rrc[0]->node_type)) { - pdcp_set_rlc_data_req_func((send_rlc_data_req_func_t)proto_agent_send_rlc_data_req); - } else {*/ - pdcp_set_rlc_data_req_func((send_rlc_data_req_func_t) rlc_data_req); - pdcp_set_pdcp_data_ind_func((pdcp_data_ind_func_t) pdcp_data_ind); - //} + /*if (NODE_IS_CU(RC.rrc[0]->node_type)) { + pdcp_set_rlc_data_req_func((send_rlc_data_req_func_t)proto_agent_send_rlc_data_req); + } else {*/ + pdcp_set_rlc_data_req_func((send_rlc_data_req_func_t) rlc_data_req); + pdcp_set_pdcp_data_ind_func((pdcp_data_ind_func_t) pdcp_data_ind); + //} /*} else { pdcp_set_pdcp_data_ind_func((pdcp_data_ind_func_t) proto_agent_send_pdcp_data_ind); }*/ @@ -841,7 +841,7 @@ int main( int argc, char **argv ) MSC_INIT(MSC_E_UTRAN, THREAD_MAX+TASK_MAX); #endif -init_opt(); + init_opt(); #ifdef PDCP_USE_NETLINK @@ -857,7 +857,7 @@ init_opt(); LOG_I(HW, "Version: %s\n", PACKAGE_VERSION); if(IS_SOFTMODEM_NOS1) - init_pdcp(); + init_pdcp(); #if defined(ENABLE_ITTI) @@ -883,37 +883,37 @@ init_opt(); mlockall(MCL_CURRENT | MCL_FUTURE); pthread_cond_init(&sync_cond,NULL); pthread_mutex_init(&sync_mutex, NULL); -/*#ifdef XFORMS - int UE_id; - - if (do_forms==1) { - fl_initialize (&argc, argv, NULL, 0, 0); - form_stats_l2 = create_form_stats_form(); - fl_show_form (form_stats_l2->stats_form, FL_PLACE_HOTSPOT, FL_FULLBORDER, "l2 stats"); - form_stats = create_form_stats_form(); - fl_show_form (form_stats->stats_form, FL_PLACE_HOTSPOT, FL_FULLBORDER, "stats"); + /*#ifdef XFORMS + int UE_id; - for(UE_id=0; UE_id<scope_enb_num_ue; UE_id++) { - for(CC_id=0; CC_id<MAX_NUM_CCs; CC_id++) { - form_gnb[CC_id][UE_id] = create_phy_scope_gnb(); - sprintf (title, "LTE UL SCOPE eNB for CC_id %d, UE %d",CC_id,UE_id); - fl_show_form (form_gnb[CC_id][UE_id]->phy_scope_gnb, FL_PLACE_HOTSPOT, FL_FULLBORDER, title); - - if (otg_enabled) { - fl_set_button(form_gnb[CC_id][UE_id]->button_0,1); - fl_set_object_label(form_gnb[CC_id][UE_id]->button_0,"DL Traffic ON"); - } else { - fl_set_button(form_gnb[CC_id][UE_id]->button_0,0); - fl_set_object_label(form_gnb[CC_id][UE_id]->button_0,"DL Traffic OFF"); - } - } // CC_id - } // UE_id + if (do_forms==1) { + fl_initialize (&argc, argv, NULL, 0, 0); + form_stats_l2 = create_form_stats_form(); + fl_show_form (form_stats_l2->stats_form, FL_PLACE_HOTSPOT, FL_FULLBORDER, "l2 stats"); + form_stats = create_form_stats_form(); + fl_show_form (form_stats->stats_form, FL_PLACE_HOTSPOT, FL_FULLBORDER, "stats"); + + for(UE_id=0; UE_id<scope_enb_num_ue; UE_id++) { + for(CC_id=0; CC_id<MAX_NUM_CCs; CC_id++) { + form_gnb[CC_id][UE_id] = create_phy_scope_gnb(); + sprintf (title, "LTE UL SCOPE eNB for CC_id %d, UE %d",CC_id,UE_id); + fl_show_form (form_gnb[CC_id][UE_id]->phy_scope_gnb, FL_PLACE_HOTSPOT, FL_FULLBORDER, title); + + if (otg_enabled) { + fl_set_button(form_gnb[CC_id][UE_id]->button_0,1); + fl_set_object_label(form_gnb[CC_id][UE_id]->button_0,"DL Traffic ON"); + } else { + fl_set_button(form_gnb[CC_id][UE_id]->button_0,0); + fl_set_object_label(form_gnb[CC_id][UE_id]->button_0,"DL Traffic OFF"); + } + } // CC_id + } // UE_id - threadCreate(&forms_thread, scope_thread, NULL, "scope", -1, OAI_PRIORITY_RT_LOW); - printf("Scope thread created, ret=%d\n",ret); - } + threadCreate(&forms_thread, scope_thread, NULL, "scope", -1, OAI_PRIORITY_RT_LOW); + printf("Scope thread created, ret=%d\n",ret); + } -#endif*/ + #endif*/ usleep(10*1000); if (nfapi_mode) { @@ -1018,27 +1018,27 @@ init_opt(); printf("Terminating application - oai_exit=%d\n",oai_exit); #endif // stop threads -/*#ifdef XFORMS + /*#ifdef XFORMS - printf("waiting for XFORMS thread\n"); + printf("waiting for XFORMS thread\n"); - if (do_forms==1) { - pthread_join(forms_thread,&status); - fl_hide_form(form_stats->stats_form); - fl_free_form(form_stats->stats_form); + if (do_forms==1) { + pthread_join(forms_thread,&status); + fl_hide_form(form_stats->stats_form); + fl_free_form(form_stats->stats_form); - fl_hide_form(form_stats_l2->stats_form); - fl_free_form(form_stats_l2->stats_form); + fl_hide_form(form_stats_l2->stats_form); + fl_free_form(form_stats_l2->stats_form); - for(UE_id=0; UE_id<scope_enb_num_ue; UE_id++) { - for(CC_id=0; CC_id<MAX_NUM_CCs; CC_id++) { - fl_hide_form(form_enb[CC_id][UE_id]->phy_scope_gNB); - fl_free_form(form_enb[CC_id][UE_id]->phy_scope_gNB); - } - } - } + for(UE_id=0; UE_id<scope_enb_num_ue; UE_id++) { + for(CC_id=0; CC_id<MAX_NUM_CCs; CC_id++) { + fl_hide_form(form_enb[CC_id][UE_id]->phy_scope_gNB); + fl_free_form(form_enb[CC_id][UE_id]->phy_scope_gNB); + } + } + } -#endif*/ + #endif*/ printf("stopping MODEM threads\n"); // cleanup stop_gNB(NB_gNB_INST); diff --git a/executables/nr-ue.c b/executables/nr-ue.c index c6e3873f186..f390e40cb5a 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -148,7 +148,7 @@ PHY_VARS_NR_UE *init_nr_ue_vars(NR_DL_FRAME_PARMS *frame_parms, ue->Mod_id = UE_id; ue->mac_enabled = 1; - + // initialize all signal buffers // initialize all signal buffers init_nr_ue_signal(ue,1,abstraction_flag); // intialize transport @@ -380,7 +380,7 @@ void processSlotRX( PHY_VARS_NR_UE *UE, UE_nr_rxtx_proc_t *proc) { scheduled_response.CC_id = 0; scheduled_response.frame = proc->frame_rx; scheduled_response.slot = proc->nr_tti_rx; - + //--------------------------Temporary configuration-----------------------------// //--------------------------Temporary configuration-----------------------------// n_rnti = 0x1234; nb_rb = 50; @@ -391,7 +391,7 @@ void processSlotRX( PHY_VARS_NR_UE *UE, UE_nr_rxtx_proc_t *proc) { mcs = 9; harq_pid = 0; rvidx = 0; - //------------------------------------------------------------------------------// + //------------------------------------------------------------------------------// scheduled_response.ul_config->sfn_slot = NR_UPLINK_SLOT; scheduled_response.ul_config->number_pdus = 1; @@ -416,18 +416,18 @@ void processSlotRX( PHY_VARS_NR_UE *UE, UE_nr_rxtx_proc_t *proc) { LOG_D(PHY,"phy_procedures_nrUE_RX: slot:%d, time %lu\n", proc->nr_tti_rx, (rdtsc()-a)/3500); //printf(">>> nr_ue_pdcch_procedures ended\n"); #endif - if(IS_SOFTMODEM_NOS1){ //&& proc->nr_tti_rx==1 - //Hardcoded rnti value - protocol_ctxt_t ctxt; - PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, UE->Mod_id, ENB_FLAG_NO, - 0x1234, proc->frame_rx, - proc->nr_tti_rx, 0); - pdcp_run(&ctxt); - pdcp_fifo_flush_sdus(&ctxt); - } + if(IS_SOFTMODEM_NOS1) { //&& proc->nr_tti_rx==1 + //Hardcoded rnti value + protocol_ctxt_t ctxt; + PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, UE->Mod_id, ENB_FLAG_NO, + 0x1234, proc->frame_rx, + proc->nr_tti_rx, 0); + pdcp_run(&ctxt); + pdcp_fifo_flush_sdus(&ctxt); + } } - + // no UL for now // no UL for now /* if (UE->mac_enabled==1) { @@ -465,7 +465,7 @@ void UE_processing(void *arg) { PHY_VARS_NR_UE *UE = rxtxD->UE; processSlotRX(UE, proc); uint8_t gNB_id = 0; - + // params for UL time alignment procedure // params for UL time alignment procedure NR_UL_TIME_ALIGNMENT_t *ul_time_alignment = &UE->ul_time_alignment[gNB_id]; uint8_t numerology = UE->frame_parms.numerology_index; @@ -475,24 +475,24 @@ void UE_processing(void *arg) { //printf(">>> mac ended\n"); // Prepare the future Tx data -/* -#ifndef NO_RAT_NR + /* + #ifndef NO_RAT_NR - if (slot_select_nr(&UE->frame_parms, proc->frame_tx, proc->nr_tti_tx) & NR_UPLINK_SLOT) -#else - if ((subframe_select( &UE->frame_parms, proc->subframe_tx) == SF_UL) || - (UE->frame_parms.frame_type == FDD) ) -#endif -*/ + if (slot_select_nr(&UE->frame_parms, proc->frame_tx, proc->nr_tti_tx) & NR_UPLINK_SLOT) + #else + if ((subframe_select( &UE->frame_parms, proc->subframe_tx) == SF_UL) || + (UE->frame_parms.frame_type == FDD) ) + #endif + */ /* UL time alignment // If the current tx frame and slot match the TA configuration in ul_time_alignment // then timing advance is processed and set to be applied in the next UL transmission */ if (UE->mac_enabled == 1) { - if (frame_tx == ul_time_alignment->ta_frame && slot_tx == ul_time_alignment->ta_slot){ + if (frame_tx == ul_time_alignment->ta_frame && slot_tx == ul_time_alignment->ta_slot) { LOG_D(PHY,"Applying timing advance -- frame %d -- slot %d\n", frame_tx, slot_tx); - + //if (nfapi_mode!=3){ //if (nfapi_mode!=3){ nr_process_timing_advance(UE->Mod_id, UE->CC_id, ul_time_alignment->ta_command, numerology, bwp_ul_NB_RB); ul_time_alignment->ta_frame = -1; @@ -501,7 +501,7 @@ void UE_processing(void *arg) { } } - if (proc->nr_tti_tx == NR_UPLINK_SLOT || UE->frame_parms.frame_type == FDD){ + if (proc->nr_tti_tx == NR_UPLINK_SLOT || UE->frame_parms.frame_type == FDD) { thread_id = PHY_vars_UE_g[UE->Mod_id][0]->current_thread_id[proc->nr_tti_tx]; @@ -566,7 +566,7 @@ void trashFrame(PHY_VARS_NR_UE *UE, openair0_timestamp *timestamp) { UE->frame_parms.samples_per_subframe, UE->frame_parms.nb_antennas_rx); if (IS_SOFTMODEM_RFSIM ) { - usleep(1000); // slow down, as would do actual rf to let cpu for the synchro thread + usleep(1000); // slow down, as would do actual rf to let cpu for the synchro thread } } @@ -579,24 +579,24 @@ void trashFrame(PHY_VARS_NR_UE *UE, openair0_timestamp *timestamp) { void syncInFrame(PHY_VARS_NR_UE *UE, openair0_timestamp *timestamp) { - LOG_I(PHY,"Resynchronizing RX by %d samples (mode = %d)\n",UE->rx_offset,UE->mode); - void *dummy_tx[UE->frame_parms.nb_antennas_tx]; + LOG_I(PHY,"Resynchronizing RX by %d samples (mode = %d)\n",UE->rx_offset,UE->mode); + void *dummy_tx[UE->frame_parms.nb_antennas_tx]; - for (int i=0; i<UE->frame_parms.nb_antennas_tx; i++) - dummy_tx[i]=malloc16_clear(UE->frame_parms.samples_per_subframe*4); - - for ( int size=UE->rx_offset ; size > 0 ; size -= UE->frame_parms.samples_per_subframe ) { - int unitTransfer=size>UE->frame_parms.samples_per_subframe ? UE->frame_parms.samples_per_subframe : size ; - AssertFatal(unitTransfer == - UE->rfdevice.trx_read_func(&UE->rfdevice, - timestamp, - (void **)UE->common_vars.rxdata, - unitTransfer, - UE->frame_parms.nb_antennas_rx),""); - } + for (int i=0; i<UE->frame_parms.nb_antennas_tx; i++) + dummy_tx[i]=malloc16_clear(UE->frame_parms.samples_per_subframe*4); - for (int i=0; i<UE->frame_parms.nb_antennas_tx; i++) - free(dummy_tx[i]); + for ( int size=UE->rx_offset ; size > 0 ; size -= UE->frame_parms.samples_per_subframe ) { + int unitTransfer=size>UE->frame_parms.samples_per_subframe ? UE->frame_parms.samples_per_subframe : size ; + AssertFatal(unitTransfer == + UE->rfdevice.trx_read_func(&UE->rfdevice, + timestamp, + (void **)UE->common_vars.rxdata, + unitTransfer, + UE->frame_parms.nb_antennas_rx),""); + } + + for (int i=0; i<UE->frame_parms.nb_antennas_tx; i++) + free(dummy_tx[i]); } @@ -727,7 +727,7 @@ void *UE_thread(void *arg) { #ifdef OAI_ADRV9371_ZC706 /*uint32_t total_gain_dB_prev = 0; if (total_gain_dB_prev != UE->rx_total_gain_dB) { - total_gain_dB_prev = UE->rx_total_gain_dB; + total_gain_dB_prev = UE->rx_total_gain_dB; openair0_cfg[0].rx_gain[0] = UE->rx_total_gain_dB; UE->rfdevice.trx_set_gains_func(&UE->rfdevice,&openair0_cfg[0]); }*/ @@ -763,29 +763,29 @@ void *UE_thread(void *arg) { readBlockSize, UE->frame_parms.nb_antennas_rx),""); -if (slot_nr == (20+NR_UPLINK_SLOT-DURATION_RX_TO_TX - 1)%20) - AssertFatal( writeBlockSize == - UE->rfdevice.trx_write_func(&UE->rfdevice, - timestamp+ - (DURATION_RX_TO_TX*UE->frame_parms.samples_per_slot) - - UE->frame_parms.ofdm_symbol_size-UE->frame_parms.nb_prefix_samples0 - - openair0_cfg[0].tx_sample_advance, - txp, - writeBlockSize, - UE->frame_parms.nb_antennas_tx, - 2),""); - -if (slot_nr == (20+NR_UPLINK_SLOT-DURATION_RX_TO_TX)%20) - AssertFatal( writeBlockSize == - UE->rfdevice.trx_write_func(&UE->rfdevice, - timestamp+ - (DURATION_RX_TO_TX*UE->frame_parms.samples_per_slot) - - UE->frame_parms.ofdm_symbol_size-UE->frame_parms.nb_prefix_samples0 - - openair0_cfg[0].tx_sample_advance, - txp, - writeBlockSize, - UE->frame_parms.nb_antennas_tx, - 3),""); + if (slot_nr == (20+NR_UPLINK_SLOT-DURATION_RX_TO_TX - 1)%20) + AssertFatal( writeBlockSize == + UE->rfdevice.trx_write_func(&UE->rfdevice, + timestamp+ + (DURATION_RX_TO_TX*UE->frame_parms.samples_per_slot) - + UE->frame_parms.ofdm_symbol_size-UE->frame_parms.nb_prefix_samples0 - + openair0_cfg[0].tx_sample_advance, + txp, + writeBlockSize, + UE->frame_parms.nb_antennas_tx, + 2),""); + + if (slot_nr == (20+NR_UPLINK_SLOT-DURATION_RX_TO_TX)%20) + AssertFatal( writeBlockSize == + UE->rfdevice.trx_write_func(&UE->rfdevice, + timestamp+ + (DURATION_RX_TO_TX*UE->frame_parms.samples_per_slot) - + UE->frame_parms.ofdm_symbol_size-UE->frame_parms.nb_prefix_samples0 - + openair0_cfg[0].tx_sample_advance, + txp, + writeBlockSize, + UE->frame_parms.nb_antennas_tx, + 3),""); if( slot_nr==(nb_slot_frame-1)) { // read in first symbol of next frame and adjust for timing drift diff --git a/executables/nr-uesoftmodem.c b/executables/nr-uesoftmodem.c index 6bcbec3790e..eb57d9a5de5 100644 --- a/executables/nr-uesoftmodem.c +++ b/executables/nr-uesoftmodem.c @@ -214,7 +214,7 @@ int emulate_rf = 0; tpool_t *Tpool; #ifdef UE_DLSCH_PARALLELISATION -tpool_t *Tpool_dl; + tpool_t *Tpool_dl; #endif @@ -308,8 +308,8 @@ static void *scope_thread(void *arg) { while (!oai_exit) { phy_scope_nrUE(form_nrue[0], - PHY_vars_UE_g[0][0], - 0,0,1); + PHY_vars_UE_g[0][0], + 0,0,1); usleep(100*1000); } @@ -382,9 +382,9 @@ static void get_options(void) { int tddflag=0, nonbiotflag, vcdflag=0; char *loopfile=NULL; int dumpframe=0; - //uint32_t noS1; //uint32_t nokrnmod; + //uint32_t nokrnmod; paramdef_t cmdline_params[] =CMDLINE_PARAMS_DESC_UE ; config_process_cmdline( cmdline_params,sizeof(cmdline_params)/sizeof(paramdef_t),NULL); @@ -511,12 +511,12 @@ void init_openair0(void) { else { openair0_cfg[card].sample_rate=122.88e6; openair0_cfg[card].samples_per_frame = 1228800; - } + } } else { LOG_E(PHY,"Unsupported numerology!\n"); exit(-1); } - }else if(frame_parms[0]->N_RB_DL == 273) { + } else if(frame_parms[0]->N_RB_DL == 273) { if (numerology==1) { if (frame_parms[0]->threequarter_fs) { AssertFatal(0 == 1,"three quarter sampling not supported for N_RB 273\n"); @@ -524,12 +524,12 @@ void init_openair0(void) { else { openair0_cfg[card].sample_rate=122.88e6; openair0_cfg[card].samples_per_frame = 1228800; - } + } } else { LOG_E(PHY,"Unsupported numerology!\n"); exit(-1); } - }else if(frame_parms[0]->N_RB_DL == 106) { + } else if(frame_parms[0]->N_RB_DL == 106) { if (numerology==0) { if (frame_parms[0]->threequarter_fs) { openair0_cfg[card].sample_rate=23.04e6; @@ -538,15 +538,15 @@ void init_openair0(void) { openair0_cfg[card].sample_rate=30.72e6; openair0_cfg[card].samples_per_frame = 307200; } - } else if (numerology==1) { + } else if (numerology==1) { if (frame_parms[0]->threequarter_fs) { - openair0_cfg[card].sample_rate=46.08e6; - openair0_cfg[card].samples_per_frame = 460800; + openair0_cfg[card].sample_rate=46.08e6; + openair0_cfg[card].samples_per_frame = 460800; } else { - openair0_cfg[card].sample_rate=61.44e6; - openair0_cfg[card].samples_per_frame = 614400; - } + openair0_cfg[card].sample_rate=61.44e6; + openair0_cfg[card].samples_per_frame = 614400; + } } else if (numerology==2) { openair0_cfg[card].sample_rate=122.88e6; openair0_cfg[card].samples_per_frame = 1228800; @@ -621,7 +621,7 @@ void init_pdcp(void) { pdcp_initmask = pdcp_initmask | UE_NAS_USE_TUN_BIT; /*if (rlc_module_init() != 0) { - LOG_I(RLC, "Problem at RLC initiation \n"); + LOG_I(RLC, "Problem at RLC initiation \n"); } pdcp_layer_init(); nr_ip_over_LTE_DRB_preconfiguration();*/ @@ -633,7 +633,7 @@ void init_pdcp(void) { // Stupid function addition because UE itti messages queues definition is common with eNB void *rrc_enb_process_itti_msg(void *notUsed) { - return NULL; + return NULL; } @@ -663,12 +663,12 @@ int main( int argc, char **argv ) { set_taus_seed (0); tpool_t pool; Tpool = &pool; - char params[]="-1,-1"; + char params[]="-1,-1"; initTpool(params, Tpool, false); #ifdef UE_DLSCH_PARALLELISATION tpool_t pool_dl; Tpool_dl = &pool_dl; - char params_dl[]="-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1"; + char params_dl[]="-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1"; initTpool(params_dl, Tpool_dl, false); #endif cpuf=get_cpu_freq_GHz(); @@ -676,21 +676,21 @@ int main( int argc, char **argv ) { init_opt() ; if(IS_SOFTMODEM_NOS1) - init_pdcp(); + init_pdcp(); if (ouput_vcd) { vcd_signal_dumper_init("/tmp/openair_dump_nrUE.vcd"); } -/* -#ifdef PDCP_USE_NETLINK - netlink_init(); -#if defined(PDCP_USE_NETLINK_QUEUES) - pdcp_netlink_init(); -#endif -#endif -*/ - #ifndef PACKAGE_VERSION + /* + #ifdef PDCP_USE_NETLINK + netlink_init(); + #if defined(PDCP_USE_NETLINK_QUEUES) + pdcp_netlink_init(); + #endif + #endif + */ +#ifndef PACKAGE_VERSION # define PACKAGE_VERSION "UNKNOWN-EXPERIMENTAL" #endif LOG_I(HW, "Version: %s\n", PACKAGE_VERSION); diff --git a/executables/softmodem-common.c b/executables/softmodem-common.c index 78d0d01ef95..de5009eccca 100644 --- a/executables/softmodem-common.c +++ b/executables/softmodem-common.c @@ -125,27 +125,27 @@ void get_common_options(void) { } void softmodem_printresources(int sig, telnet_printfunc_t pf) { - struct rusage usage; - struct timespec stop; - - clock_gettime(CLOCK_BOOTTIME, &stop); - - uint64_t elapse = (stop.tv_sec - start.tv_sec) ; // in seconds - - - int st = getrusage(RUSAGE_SELF,&usage); - if (!st) { - pf("\nRun time: %lluh %llus\n",(unsigned long long)elapse/3600,(unsigned long long)(elapse - (elapse/3600))); - pf("\tTime executing user inst.: %lds %ldus\n",(long)usage.ru_utime.tv_sec,(long)usage.ru_utime.tv_usec); - pf("\tTime executing system inst.: %lds %ldus\n",(long)usage.ru_stime.tv_sec,(long)usage.ru_stime.tv_usec); - pf("\tMax. Phy. memory usage: %ldkB\n",(long)usage.ru_maxrss); - pf("\tPage fault number (no io): %ld\n",(long)usage.ru_minflt); - pf("\tPage fault number (requiring io): %ld\n",(long)usage.ru_majflt); - pf("\tNumber of file system read: %ld\n",(long)usage.ru_inblock); - pf("\tNumber of filesystem write: %ld\n",(long)usage.ru_oublock); - pf("\tNumber of context switch (process origin, io...): %ld\n",(long)usage.ru_nvcsw); - pf("\tNumber of context switch (os origin, priority...): %ld\n",(long)usage.ru_nivcsw); - } + struct rusage usage; + struct timespec stop; + + clock_gettime(CLOCK_BOOTTIME, &stop); + + uint64_t elapse = (stop.tv_sec - start.tv_sec) ; // in seconds + + + int st = getrusage(RUSAGE_SELF,&usage); + if (!st) { + pf("\nRun time: %lluh %llus\n",(unsigned long long)elapse/3600,(unsigned long long)(elapse - (elapse/3600))); + pf("\tTime executing user inst.: %lds %ldus\n",(long)usage.ru_utime.tv_sec,(long)usage.ru_utime.tv_usec); + pf("\tTime executing system inst.: %lds %ldus\n",(long)usage.ru_stime.tv_sec,(long)usage.ru_stime.tv_usec); + pf("\tMax. Phy. memory usage: %ldkB\n",(long)usage.ru_maxrss); + pf("\tPage fault number (no io): %ld\n",(long)usage.ru_minflt); + pf("\tPage fault number (requiring io): %ld\n",(long)usage.ru_majflt); + pf("\tNumber of file system read: %ld\n",(long)usage.ru_inblock); + pf("\tNumber of filesystem write: %ld\n",(long)usage.ru_oublock); + pf("\tNumber of context switch (process origin, io...): %ld\n",(long)usage.ru_nvcsw); + pf("\tNumber of context switch (os origin, priority...): %ld\n",(long)usage.ru_nivcsw); + } } void signal_handler(int sig) { @@ -160,9 +160,9 @@ void signal_handler(int sig) { backtrace_symbols_fd(array, size, 2); exit(-1); } else { - if(sig==SIGINT ||sig==SOFTMODEM_RTSIGNAL) - softmodem_printresources(sig,(telnet_printfunc_t)printf); - if (sig != SOFTMODEM_RTSIGNAL) { + if(sig==SIGINT ||sig==SOFTMODEM_RTSIGNAL) + softmodem_printresources(sig,(telnet_printfunc_t)printf); + if (sig != SOFTMODEM_RTSIGNAL) { printf("Linux signal %s...\n",strsignal(sig)); exit_function(__FILE__, __FUNCTION__, __LINE__,"softmodem starting exit procedure\n"); } @@ -172,7 +172,7 @@ void signal_handler(int sig) { void set_softmodem_sighandler(void) { - struct sigaction act,oldact; + struct sigaction act,oldact; clock_gettime(CLOCK_BOOTTIME, &start); memset(&act,0,sizeof(act)); act.sa_handler=signal_handler; @@ -181,5 +181,5 @@ void set_softmodem_sighandler(void) { signal(SIGSEGV, signal_handler); signal(SIGINT, signal_handler); signal(SIGTERM, signal_handler); - signal(SIGABRT, signal_handler); + signal(SIGABRT, signal_handler); } diff --git a/executables/softmodem-common.h b/executables/softmodem-common.h index 8fedb749493..a356ec50a4f 100644 --- a/executables/softmodem-common.h +++ b/executables/softmodem-common.h @@ -37,6 +37,11 @@ extern "C" #endif /* help strings definition for command line options, used in CMDLINE_XXX_DESC macros and printed when -h option is used */ #define CONFIG_HLP_RFCFGF "Configuration file for front-end (e.g. LMS7002M)\n" +#define CONFIG_HLP_SPLIT73 "Split 7.3 (below rate matching) option: <cu|du>:<remote ip address>:<remote port>" +#define CONFIG_HLP_TPOOL "Thread pool configuration: \n\ + default no pool (runs in calling thread),\n\ + list of cores, comma separated (negative value is no core affinity)\n\ + example: -1,3 launches two working threads one floating, the second set on core 3" #define CONFIG_HLP_ULMAXE "set the eNodeB max ULSCH erros\n" #define CONFIG_HLP_CALUER "set UE RX calibration\n" #define CONFIG_HLP_CALUERM "" @@ -87,6 +92,7 @@ extern "C" /* optname helpstr paramflags XXXptr defXXXval type numelt */ /*-----------------------------------------------------------------------------------------------------------------------------------------------------*/ #define RF_CONFIG_FILE softmodem_params.rf_config_file +#define SPLIT73 softmodem_params.split73 #define TP_CONFIG softmodem_params.threadPoolConfig #define PHY_TEST softmodem_params.phy_test #define WAIT_FOR_SYNC softmodem_params.wait_for_sync @@ -103,6 +109,8 @@ extern "C" #define CMDLINE_PARAMS_DESC { \ {"rf-config-file", CONFIG_HLP_RFCFGF, 0, strptr:(char **)&RF_CONFIG_FILE, defstrval:NULL, TYPE_STRING, sizeof(RF_CONFIG_FILE)},\ + {"split73", CONFIG_HLP_SPLIT73, 0, strptr:(char **)&SPLIT73, defstrval:NULL, TYPE_STRING, sizeof(SPLIT73)},\ + {"thread-pool", CONFIG_HLP_TPOOL, 0, strptr:(char **)&TP_CONFIG, defstrval:"n", TYPE_STRING, sizeof(TP_CONFIG)}, \ {"phy-test", CONFIG_HLP_PHYTST, PARAMFLAG_BOOL, iptr:&PHY_TEST, defintval:0, TYPE_INT, 0}, \ {"usim-test", CONFIG_HLP_USIM, PARAMFLAG_BOOL, u8ptr:&USIM_TEST, defintval:0, TYPE_UINT8, 0}, \ {"clock", CONFIG_HLP_CLK, 0, uptr:&CLOCK_SOURCE, defintval:0, TYPE_UINT, 0}, \ @@ -179,6 +187,7 @@ typedef struct { uint64_t optmask; THREAD_STRUCT thread_struct; char rf_config_file[1024]; + char split73[1024]; char threadPoolConfig[1024]; int phy_test; uint8_t usim_test; diff --git a/executables/split_headers.h b/executables/split_headers.h index 3e8fe23b14e..54b9930250b 100644 --- a/executables/split_headers.h +++ b/executables/split_headers.h @@ -5,10 +5,11 @@ #include <stdbool.h> #include <openair1/PHY/defs_eNB.h> -#define CU_IP "127.0.0.1" #define CU_PORT "7878" -#define DU_IP "127.0.0.1" #define DU_PORT "8787" +#define SPLIT73_CU 1 +#define SPLIT73_DU 2 +extern int split73; #define MTU 65536 #define UDP_TIMEOUT 900000L // in micro second (struct timeval, NOT struct timespec) diff --git a/openair1/PHY/LTE_TRANSPORT/dlsch_coding.c b/openair1/PHY/LTE_TRANSPORT/dlsch_coding.c index 75d572c2fc7..15eb5206a19 100644 --- a/openair1/PHY/LTE_TRANSPORT/dlsch_coding.c +++ b/openair1/PHY/LTE_TRANSPORT/dlsch_coding.c @@ -310,7 +310,7 @@ static void TPencode(void * arg) { lte_rate_matching_turbo(hadlsch->RTC[rdata->r], rdata->G, //G hadlsch->w[rdata->r], - hadlsch->e+rdata->r_offset, + hadlsch->eDL+rdata->r_offset, hadlsch->C, // C rdata->dlsch->Nsoft, // Nsoft, rdata->dlsch->Mdlharq, @@ -389,7 +389,7 @@ int dlsch_encoding(PHY_VARS_eNB *eNB, for (int r=0, r_offset=0; r<hadlsch->C; r++) { union turboReqUnion id= {.s={dlsch->rnti,frame,subframe,r,0}}; - notifiedFIFO_elt_t *req=newNotifiedFIFO_elt(sizeof(turboEncode_t), id.p, &proc->respEncode, TPencode); + notifiedFIFO_elt_t *req=newNotifiedFIFO_elt(sizeof(turboEncode_t), id.p, proc->respEncode, TPencode); turboEncode_t * rdata=(turboEncode_t *) NotifiedFifoData(req); rdata->input=hadlsch->c[r]; rdata->Kr_bytes= ( r<hadlsch->Cminus ? hadlsch->Kminus : hadlsch->Kplus) >>3; @@ -404,8 +404,8 @@ int dlsch_encoding(PHY_VARS_eNB *eNB, rdata->r_offset=r_offset; rdata->G=G; - if ( proc->threadPool.activated) { - pushTpool(&proc->threadPool,req); + if ( proc->threadPool->activated ) { + pushTpool(proc->threadPool,req); proc->nbEncode++; } else { TPencode(rdata); diff --git a/openair1/PHY/LTE_TRANSPORT/dlsch_modulation.c b/openair1/PHY/LTE_TRANSPORT/dlsch_modulation.c index 04a49d062a3..475c8bd521d 100644 --- a/openair1/PHY/LTE_TRANSPORT/dlsch_modulation.c +++ b/openair1/PHY/LTE_TRANSPORT/dlsch_modulation.c @@ -158,7 +158,7 @@ int allocate_REs_in_RB_no_pilots_QPSK_siso(PHY_VARS_eNB* phy_vars_eNB, { LTE_DL_FRAME_PARMS *frame_parms = &phy_vars_eNB->frame_parms; - uint8_t *x0 = dlsch0_harq->e; + uint8_t *x0 = dlsch0_harq->eDL; uint32_t qpsk_table_offset_re = 0; uint32_t qpsk_table_offset_im = 0; @@ -248,7 +248,7 @@ int allocate_REs_in_RB_pilots_QPSK_siso(PHY_VARS_eNB* phy_vars_eNB, LTE_DL_FRAME_PARMS *frame_parms=&phy_vars_eNB->frame_parms; - uint8_t *x0 = dlsch0_harq->e; + uint8_t *x0 = dlsch0_harq->eDL; uint32_t qpsk_table_offset_re = 0; uint32_t qpsk_table_offset_im = 0; @@ -343,7 +343,7 @@ int allocate_REs_in_RB_no_pilots_16QAM_siso(PHY_VARS_eNB* phy_vars_eNB, { LTE_DL_FRAME_PARMS *frame_parms = &phy_vars_eNB->frame_parms; - uint8_t *x0 = dlsch0_harq->e; + uint8_t *x0 = dlsch0_harq->eDL; uint32_t qam16_table_offset_re = 0; uint32_t qam16_table_offset_im = 0; @@ -439,7 +439,7 @@ int allocate_REs_in_RB_pilots_16QAM_siso(PHY_VARS_eNB* phy_vars_eNB, LTE_DL_FRAME_PARMS *frame_parms=&phy_vars_eNB->frame_parms; - uint8_t *x0 = dlsch0_harq->e; + uint8_t *x0 = dlsch0_harq->eDL; uint32_t qam16_table_offset_re = 0; uint32_t qam16_table_offset_im = 0; @@ -542,7 +542,7 @@ int allocate_REs_in_RB_no_pilots_64QAM_siso(PHY_VARS_eNB* phy_vars_eNB, LTE_DL_FRAME_PARMS *frame_parms = &phy_vars_eNB->frame_parms; - uint8_t *x0 = dlsch0_harq->e; + uint8_t *x0 = dlsch0_harq->eDL; uint32_t qam64_table_offset_re = 0; uint32_t qam64_table_offset_im = 0; @@ -699,7 +699,7 @@ int allocate_REs_in_RB_pilots_64QAM_siso(PHY_VARS_eNB* phy_vars_eNB, LTE_DL_FRAME_PARMS *frame_parms=&phy_vars_eNB->frame_parms; - uint8_t *x0 = dlsch0_harq->e; + uint8_t *x0 = dlsch0_harq->eDL; uint32_t qam64_table_offset_re = 0; uint32_t qam64_table_offset_im = 0; @@ -866,12 +866,12 @@ int allocate_REs_in_RB(PHY_VARS_eNB* phy_vars_eNB, if ((dlsch0_harq != NULL) && (dlsch1_harq != NULL)) { //this is for TM3, TM4 - x0 = dlsch0_harq->e; + x0 = dlsch0_harq->eDL; mimo_mode = dlsch0_harq->mimo_mode; first_layer0 = dlsch0_harq->first_layer; Nlayers0 = dlsch0_harq->Nlayers; mod_order0 = dlsch0_harq->Qm; - x1 = dlsch1_harq->e; + x1 = dlsch1_harq->eDL; // Fill these in later for TM8-10 // Nlayers1 = dlsch1_harq->Nlayers; // first_layer1 = dlsch1_harq->first_layer; @@ -879,7 +879,7 @@ int allocate_REs_in_RB(PHY_VARS_eNB* phy_vars_eNB, } else if ((dlsch0_harq != NULL) && (dlsch1_harq == NULL)){ //This is for SIS0 TM1, TM6, etc - x0 = dlsch0_harq->e; + x0 = dlsch0_harq->eDL; mimo_mode = dlsch0_harq->mimo_mode; first_layer0 = dlsch0_harq->first_layer; Nlayers0 = dlsch0_harq->Nlayers; @@ -887,7 +887,7 @@ int allocate_REs_in_RB(PHY_VARS_eNB* phy_vars_eNB, } else if ((dlsch0_harq == NULL) && (dlsch1_harq != NULL)){ // This is for TM4 retransmission - x0 = dlsch1_harq->e; + x0 = dlsch1_harq->eDL; mimo_mode = dlsch1_harq->mimo_mode; first_layer0 = dlsch1_harq->first_layer; Nlayers0 = dlsch1_harq->Nlayers; @@ -2692,7 +2692,7 @@ int dlsch_modulation_SIC(int32_t **sic_buffer, LTE_DL_eNB_HARQ_t *dlsch0_harq = dlsch0->harq_processes[harq_pid]; uint32_t i,jj,re_allocated=0; uint8_t mod_order0 = dlsch0_harq->Qm; - uint8_t *x0 = dlsch0_harq->e; + uint8_t *x0 = dlsch0_harq->eDL; uint8_t qam64_table_offset_re = 0; uint8_t qam64_table_offset_im = 0; uint8_t qam16_table_offset_re = 0; @@ -2883,7 +2883,7 @@ int mch_modulation(int32_t **txdataF, &jj, re_offset, symbol_offset, - dlsch->harq_processes[0]->e, + dlsch->harq_processes[0]->eDL, l, mod_order, amp, diff --git a/openair1/PHY/LTE_TRANSPORT/dlsch_scrambling.c b/openair1/PHY/LTE_TRANSPORT/dlsch_scrambling.c index 2e8ddde8488..d48164e07d6 100644 --- a/openair1/PHY/LTE_TRANSPORT/dlsch_scrambling.c +++ b/openair1/PHY/LTE_TRANSPORT/dlsch_scrambling.c @@ -52,7 +52,7 @@ void dlsch_scrambling(LTE_DL_FRAME_PARMS *frame_parms, int n; // uint8_t reset; uint32_t x1, x2, s=0; - uint8_t *dlsch_e=dlsch->harq_processes[harq_pid]->e; + uint8_t *dlsch_e=dlsch->harq_processes[harq_pid]->eDL; uint8_t *e=dlsch_e; VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_ENB_DLSCH_SCRAMBLING, VCD_FUNCTION_IN); // Rule for accumulation of subframes for BL/CE UEs diff --git a/openair1/PHY/LTE_TRANSPORT/transport_eNB.h b/openair1/PHY/LTE_TRANSPORT/transport_eNB.h index 7dd4e46da4c..5e446db5fdd 100644 --- a/openair1/PHY/LTE_TRANSPORT/transport_eNB.h +++ b/openair1/PHY/LTE_TRANSPORT/transport_eNB.h @@ -100,7 +100,7 @@ typedef struct { /// start symbold of pdsch uint8_t pdsch_start; /// Concatenated "e"-sequences (for definition see 36-212 V8.6 2009-03, p.17-18) - uint8_t e[MAX_NUM_CHANNEL_BITS] __attribute__((aligned(32))); + uint8_t eDL[MAX_NUM_CHANNEL_BITS] __attribute__((aligned(32))); /// Turbo-code outputs (36-212 V8.6 2009-03, p.12 uint8_t *d[MAX_NUM_DLSCH_SEGMENTS];//[(96+3+(3*6144))]; /// Sub-block interleaver outputs (36-212 V8.6 2009-03, p.16-17) @@ -261,11 +261,11 @@ typedef struct { /// coded RI bits int16_t q_RI[MAX_RI_PAYLOAD]; /// Concatenated "e"-sequences (for definition see 36-212 V8.6 2009-03, p.17-18) - int16_t e[MAX_NUM_CHANNEL_BITS] __attribute__((aligned(32))); + int16_t eUL[MAX_NUM_CHANNEL_BITS] __attribute__((aligned(32))); /// Temporary h sequence to flag PUSCH_x/PUSCH_y symbols which are not scrambled uint8_t h[MAX_NUM_CHANNEL_BITS]; /// Pointer to the payload - uint8_t *b; + uint8_t *decodedBytes; /// Pointers to transport block segments //TBD uint8_t *c[MAX_NUM_ULSCH_SEGMENTS]; diff --git a/openair1/PHY/LTE_TRANSPORT/ulsch_decoding.c b/openair1/PHY/LTE_TRANSPORT/ulsch_decoding.c index 581f50494f4..36a3cd9a5c4 100644 --- a/openair1/PHY/LTE_TRANSPORT/ulsch_decoding.c +++ b/openair1/PHY/LTE_TRANSPORT/ulsch_decoding.c @@ -54,9 +54,9 @@ void free_eNB_ulsch(LTE_eNB_ULSCH_t *ulsch) { if (ulsch) { for (i=0; i<8; i++) { if (ulsch->harq_processes[i]) { - if (ulsch->harq_processes[i]->b) { - free16(ulsch->harq_processes[i]->b,MAX_ULSCH_PAYLOAD_BYTES); - ulsch->harq_processes[i]->b = NULL; + if (ulsch->harq_processes[i]->decodedBytes) { + free16(ulsch->harq_processes[i]->decodedBytes,MAX_ULSCH_PAYLOAD_BYTES); + ulsch->harq_processes[i]->decodedBytes = NULL; } for (r=0; r<MAX_NUM_ULSCH_SEGMENTS; r++) { @@ -115,10 +115,10 @@ LTE_eNB_ULSCH_t *new_eNB_ulsch(uint8_t max_turbo_iterations,uint8_t N_RB_UL, uin if (ulsch->harq_processes[i]) { memset(ulsch->harq_processes[i],0,sizeof(LTE_UL_eNB_HARQ_t)); - ulsch->harq_processes[i]->b = (uint8_t *)malloc16(MAX_ULSCH_PAYLOAD_BYTES/bw_scaling); + ulsch->harq_processes[i]->decodedBytes = (uint8_t *)malloc16(MAX_ULSCH_PAYLOAD_BYTES/bw_scaling); - if (ulsch->harq_processes[i]->b) - memset(ulsch->harq_processes[i]->b,0,MAX_ULSCH_PAYLOAD_BYTES/bw_scaling); + if (ulsch->harq_processes[i]->decodedBytes) + memset(ulsch->harq_processes[i]->decodedBytes,0,MAX_ULSCH_PAYLOAD_BYTES/bw_scaling); else exit_flag=3; @@ -229,7 +229,7 @@ void processULSegment(void * arg) { G, ulsch_harq->w[r], (uint8_t *) &dummy_w[0], - ulsch_harq->e+rdata->r_offset, + ulsch_harq->eUL+rdata->r_offset, ulsch_harq->C, NSOFT, 0, //Uplink @@ -327,19 +327,15 @@ int ulsch_decoding_data(PHY_VARS_eNB *eNB, L1_rxtx_proc_t *proc, E = ulsch_harq->Qm * (Gp/ulsch_harq->C); else E = ulsch_harq->Qm * ((GpmodC==0?0:1) + (Gp/ulsch_harq->C)); -#ifdef FS6 - if ( getenv("fs6") != NULL && strncasecmp( getenv("fs6"), "du", 2) == 0 ) { - - sendFs6Ul(eNB, UE_id, harq_pid, r, ulsch_harq->e+r_offset, E*sizeof(int16_t), r_offset); - + + if ( split73 == SPLIT73_DU ) { + sendFs6Ul(eNB, UE_id, harq_pid, r, ulsch_harq->eUL+r_offset, E*sizeof(int16_t), r_offset); r_offset += E; continue; } -#endif - union turboReqUnion id= {.s={ulsch->rnti,proc->frame_rx,proc->subframe_rx,0,0}}; - notifiedFIFO_elt_t *req=newNotifiedFIFO_elt(sizeof(turboDecode_t), id.p, &proc->respDecode, processULSegment); + notifiedFIFO_elt_t *req=newNotifiedFIFO_elt(sizeof(turboDecode_t), id.p, proc->respDecode, processULSegment); turboDecode_t * rdata=(turboDecode_t *) NotifiedFifoData(req); rdata->eNB=eNB; @@ -359,7 +355,7 @@ int ulsch_decoding_data(PHY_VARS_eNB *eNB, L1_rxtx_proc_t *proc, rdata->function=td; int Fbytes=(r==0) ? rdata->Fbits>>3 : 0; int sz=Kr_bytes - Fbytes - ((ulsch_harq->C>1)?3:0); - pushTpool(&proc->threadPool,req); + pushTpool(proc->threadPool,req); proc->nbDecode++; LOG_D(PHY,"Added a block to decode, in pipe: %d\n",proc->nbDecode); r_offset+=E; @@ -892,8 +888,8 @@ unsigned int ulsch_decoding(PHY_VARS_eNB *eNB, j2+=2; } - ulsch_harq->e[iprime++] = y[j2++]; - ulsch_harq->e[iprime++] = y[j2++]; + ulsch_harq->eUL[iprime++] = y[j2++]; + ulsch_harq->eUL[iprime++] = y[j2++]; } break; @@ -905,10 +901,10 @@ unsigned int ulsch_decoding(PHY_VARS_eNB *eNB, j2+=4; } - ulsch_harq->e[iprime++] = y[j2++]; - ulsch_harq->e[iprime++] = y[j2++]; - ulsch_harq->e[iprime++] = y[j2++]; - ulsch_harq->e[iprime++] = y[j2++]; + ulsch_harq->eUL[iprime++] = y[j2++]; + ulsch_harq->eUL[iprime++] = y[j2++]; + ulsch_harq->eUL[iprime++] = y[j2++]; + ulsch_harq->eUL[iprime++] = y[j2++]; } break; @@ -920,12 +916,12 @@ unsigned int ulsch_decoding(PHY_VARS_eNB *eNB, j2+=6; } - ulsch_harq->e[iprime++] = y[j2++]; - ulsch_harq->e[iprime++] = y[j2++]; - ulsch_harq->e[iprime++] = y[j2++]; - ulsch_harq->e[iprime++] = y[j2++]; - ulsch_harq->e[iprime++] = y[j2++]; - ulsch_harq->e[iprime++] = y[j2++]; + ulsch_harq->eUL[iprime++] = y[j2++]; + ulsch_harq->eUL[iprime++] = y[j2++]; + ulsch_harq->eUL[iprime++] = y[j2++]; + ulsch_harq->eUL[iprime++] = y[j2++]; + ulsch_harq->eUL[iprime++] = y[j2++]; + ulsch_harq->eUL[iprime++] = y[j2++]; } break; @@ -963,7 +959,7 @@ unsigned int ulsch_decoding(PHY_VARS_eNB *eNB, */ int16_t *yp,*ep; - for (iprime=0,yp=&y[j2],ep=&ulsch_harq->e[0]; + for (iprime=0,yp=&y[j2],ep=&ulsch_harq->eUL[0]; iprime<G; iprime+=8,j2+=8,ep+=8,yp+=8) { ep[0] = yp[0]; diff --git a/openair1/PHY/LTE_UE_TRANSPORT/dlsch_decoding.c b/openair1/PHY/LTE_UE_TRANSPORT/dlsch_decoding.c index 4ca9a7c7625..7442a0ae7e0 100644 --- a/openair1/PHY/LTE_UE_TRANSPORT/dlsch_decoding.c +++ b/openair1/PHY/LTE_UE_TRANSPORT/dlsch_decoding.c @@ -793,7 +793,7 @@ int dlsch_encoding_SIC(PHY_VARS_UE *ue, r_offset += lte_rate_matching_turbo(dlsch->harq_processes[harq_pid]->RTC[r], G, //G dlsch->harq_processes[harq_pid]->w[r], - dlsch->harq_processes[harq_pid]->e+r_offset, + dlsch->harq_processes[harq_pid]->eDL+r_offset, dlsch->harq_processes[harq_pid]->C, // C dlsch->Nsoft, // Nsoft, dlsch->Mdlharq, diff --git a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c index 0fabf8c6390..e59c651713d 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c +++ b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c @@ -2032,7 +2032,7 @@ uint16_t nr_dci_format_size (PHY_VARS_NR_UE *ue, ul_res_alloc_type_1 = 1; } - uint8_t n_bits_freq_dom_res_assign_ul=0,n_ul_RGB_tmp; + uint8_t n_bits_freq_dom_res_assign_ul=0,n_ul_RGB_tmp=0; if (ul_res_alloc_type_0 == 1) { // implementation of Table 6.1.2.2.1-1 TC 38.214 subclause 6.1.2.2.1 // config1: PUSCH-Config IE contains rbg-Size ENUMERATED {config1 config2} diff --git a/openair1/SCHED/phy_procedures_lte_eNb.c b/openair1/SCHED/phy_procedures_lte_eNb.c index 6e34a71e0ce..1ada5947254 100644 --- a/openair1/SCHED/phy_procedures_lte_eNb.c +++ b/openair1/SCHED/phy_procedures_lte_eNb.c @@ -320,10 +320,10 @@ bool dlsch_procedures(PHY_VARS_eNB *eNB, &eNB->dlsch_interleaving_stats); stop_meas(&eNB->dlsch_encoding_stats); - if ( proc->threadPool.activated ) { + if ( proc->threadPool->activated ) { // Wait all other threads finish to process while (proc->nbEncode) { - delNotifiedFIFO_elt(pullTpool(&proc->respEncode, &proc->threadPool)); + delNotifiedFIFO_elt(pullTpool(proc->respEncode, proc->threadPool)); proc->nbEncode--; } } @@ -604,12 +604,11 @@ void srs_procedures(PHY_VARS_eNB *eNB,L1_rxtx_proc_t *proc) { } void fill_sr_indication(int UEid, PHY_VARS_eNB *eNB,uint16_t rnti,int frame,int subframe,uint32_t stat) { - #ifdef FS6 - if ( getenv("fs6") != NULL && strncasecmp( getenv("fs6"), "du", 2) == 0 ) { + if ( split73 == SPLIT73_DU ) { sendFs6Ulharq(fs6ULindicationSr, UEid, eNB, NULL, frame, subframe, NULL,0,0, rnti, stat); return; } - #endif + pthread_mutex_lock(&eNB->UL_INFO_mutex); nfapi_sr_indication_t *sr_ind = &eNB->UL_INFO.sr_ind; nfapi_sr_indication_body_t *sr_ind_body = &sr_ind->sr_indication_body; @@ -1175,13 +1174,13 @@ void postDecode(L1_rxtx_proc_t *proc, notifiedFIFO_elt_t *req) { if (decodeSucess) { int Fbytes=(rdata->segment_r==0) ? rdata->Fbits>>3 : 0; int sz=(rdata->Kr>>3) - Fbytes - ((ulsch_harq->C>1)?3:0); - memcpy(ulsch_harq->b+rdata->offset, + memcpy(ulsch_harq->decodedBytes+rdata->offset, rdata->decoded_bytes+Fbytes, sz); } else { if ( rdata->nbSegments != ulsch_harq->processedSegments ) { - int nb=abortTpool(&proc->threadPool, req->key); - nb+=abortNotifiedFIFO(&proc->respDecode, req->key); + int nb=abortTpool(proc->threadPool, req->key); + nb+=abortNotifiedFIFO(proc->respDecode, req->key); proc->nbDecode-=nb; LOG_I(PHY,"uplink segment error %d/%d, aborted %d segments\n",rdata->segment_r,rdata->nbSegments, nb); AssertFatal(ulsch_harq->processedSegments+nb == rdata->nbSegments,"processed: %d, aborted: %d, total %d\n", @@ -1329,7 +1328,7 @@ void pusch_procedures(PHY_VARS_eNB *eNB,L1_rxtx_proc_t *proc) { } // for (i=0; i<NUMBER_OF_UE_MAX; i++) while (proc->nbDecode > 0) { - notifiedFIFO_elt_t *req=pullTpool(&proc->respDecode, &proc->threadPool); + notifiedFIFO_elt_t *req=pullTpool(proc->respDecode, proc->threadPool); postDecode(proc, req); delNotifiedFIFO_elt(req); } @@ -1415,7 +1414,7 @@ void fill_rx_indication(PHY_VARS_eNB *eNB, pdu->rx_indication_rel8.tl.tag = NFAPI_RX_INDICATION_REL8_TAG; pdu->rx_indication_rel8.length = eNB->ulsch[UE_id]->harq_processes[harq_pid]->TBS>>3; pdu->rx_indication_rel8.offset = 1; // DJP - I dont understand - but broken unless 1 ???? 0; // filled in at the end of the UL_INFO formation - pdu->data = eNB->ulsch[UE_id]->harq_processes[harq_pid]->b; + pdu->data = eNB->ulsch[UE_id]->harq_processes[harq_pid]->decodedBytes; // estimate timing advance for MAC sync_pos = lte_est_timing_advance_pusch(eNB,UE_id); timing_advance_update = sync_pos; // - eNB->frame_parms.nb_prefix_samples/4; //to check @@ -1749,12 +1748,10 @@ void fill_ulsch_harq_indication (PHY_VARS_eNB *eNB, LTE_UL_eNB_HARQ_t *ulsch_har } void fill_uci_harq_indication (int UEid, PHY_VARS_eNB *eNB, LTE_eNB_UCI *uci, int frame, int subframe, uint8_t *harq_ack, uint8_t tdd_mapping_mode, uint16_t tdd_multiplexing_mask) { - #ifdef FS6 - if ( getenv("fs6") != NULL && strncasecmp( getenv("fs6"), "du", 2) == 0 ) { + if ( split73 == SPLIT73_DU ) { sendFs6Ulharq(fs6ULindicationHarq, UEid, eNB, uci, frame, subframe, harq_ack, tdd_mapping_mode, tdd_multiplexing_mask, 0, 0); return; } - #endif int UE_id=find_dlsch(uci->rnti,eNB,SEARCH_EXIST); diff --git a/openair1/SIMULATION/LTE_PHY/dlsim.c b/openair1/SIMULATION/LTE_PHY/dlsim.c index d8ac505d141..6a12831e2d0 100644 --- a/openair1/SIMULATION/LTE_PHY/dlsim.c +++ b/openair1/SIMULATION/LTE_PHY/dlsim.c @@ -1266,9 +1266,12 @@ int main(int argc, char **argv) { } L1_rxtx_proc_t *proc_eNB = &eNB->proc.L1_proc; - initTpool("n", &proc_eNB->threadPool, true); - initNotifiedFIFO(&proc_eNB->respEncode); - initNotifiedFIFO(&proc_eNB->respDecode); + proc_eNB->threadPool=(tpool_t*)malloc(sizeof(tpool_t)); + proc_eNB->respEncode=(notifiedFIFO_t*) malloc(sizeof(notifiedFIFO_t)); + proc_eNB->respDecode=(notifiedFIFO_t*) malloc(sizeof(notifiedFIFO_t)); + initTpool("n", proc_eNB->threadPool, true); + initNotifiedFIFO(proc_eNB->respEncode); + initNotifiedFIFO(proc_eNB->respDecode); proc_eNB->frame_tx=0; diff --git a/openair1/SIMULATION/LTE_PHY/ulsim.c b/openair1/SIMULATION/LTE_PHY/ulsim.c index 34237771aa7..6c04d6edf72 100644 --- a/openair1/SIMULATION/LTE_PHY/ulsim.c +++ b/openair1/SIMULATION/LTE_PHY/ulsim.c @@ -793,9 +793,12 @@ int main(int argc, char **argv) { proc_rxtx_ue->frame_rx = (subframe<4)?(proc_rxtx->frame_tx-1):(proc_rxtx->frame_tx); proc_rxtx_ue->subframe_tx = proc_rxtx->subframe_rx; proc_rxtx_ue->subframe_rx = (proc_rxtx->subframe_tx+6)%10; - initTpool("n", &proc_rxtx->threadPool, true); - initNotifiedFIFO(&proc_rxtx->respEncode); - initNotifiedFIFO(&proc_rxtx->respDecode); + proc_rxtx->threadPool=(tpool_t*)malloc(sizeof(tpool_t)); + proc_rxtx->respEncode=(notifiedFIFO_t*) malloc(sizeof(notifiedFIFO_t)); + proc_rxtx->respDecode=(notifiedFIFO_t*) malloc(sizeof(notifiedFIFO_t)); + initTpool("n",proc_rxtx->threadPool, true); + initNotifiedFIFO(proc_rxtx->respEncode); + initNotifiedFIFO(proc_rxtx->respDecode); printf("Init UL hopping UE\n"); init_ul_hopping(&UE->frame_parms); diff --git a/openair2/PHY_INTERFACE/IF_Module.h b/openair2/PHY_INTERFACE/IF_Module.h index 5bcbc2420dd..42a44597323 100644 --- a/openair2/PHY_INTERFACE/IF_Module.h +++ b/openair2/PHY_INTERFACE/IF_Module.h @@ -176,11 +176,11 @@ typedef struct { pthread_cond_t cond_RUs; /// mutex for RXn-TXnp4 processing thread pthread_mutex_t mutex_RUs; - tpool_t threadPool; + tpool_t *threadPool; int nbEncode; int nbDecode; - notifiedFIFO_t respEncode; - notifiedFIFO_t respDecode; + notifiedFIFO_t *respEncode; + notifiedFIFO_t *respDecode; pthread_mutex_t mutex_emulateRF; int instance_cnt_emulateRF; pthread_t pthread_emulateRF; diff --git a/targets/ARCH/rfsimulator/simulator.c b/targets/ARCH/rfsimulator/simulator.c index 94f8f2e938e..01a78213ca6 100644 --- a/targets/ARCH/rfsimulator/simulator.c +++ b/targets/ARCH/rfsimulator/simulator.c @@ -346,7 +346,7 @@ static int rfsimulator_write_internal(rfsimulator_state_t *t, openair0_timestamp if ( t->lastWroteTS != 0 && abs((double)t->lastWroteTS-timestamp) > (double)CirSize) LOG_E(HW,"Discontinuous TX gap too large Tx:%lu, %lu\n", t->lastWroteTS, timestamp); - if (t->lastWroteTS >= timestamp+1) + if (t->lastWroteTS > timestamp+nsamps) LOG_E(HW,"Not supported to send Tx out of order (same in USRP) %lu, %lu\n", t->lastWroteTS, timestamp); t->lastWroteTS=timestamp+nsamps; @@ -468,8 +468,8 @@ static bool flushInput(rfsimulator_state_t *t, int timeout, int nsamps_for_initi } else if ( b->lastReceivedTS == b->th.timestamp ) { // normal case } else { - abort(); - AssertFatal(false, "received data in past: current is %lu, new reception: %lu!\n", b->lastReceivedTS, b->th.timestamp); + LOG_E(HW, "received data in past: current is %lu, new reception: %lu!\n", b->lastReceivedTS, b->th.timestamp); + b->trashingPacket=true; } pthread_mutex_lock(&Sockmutex); diff --git a/targets/COMMON/create_tasks.c b/targets/COMMON/create_tasks.c index 1f2e8fa8f19..10691d2db8c 100644 --- a/targets/COMMON/create_tasks.c +++ b/targets/COMMON/create_tasks.c @@ -41,6 +41,7 @@ # include "f1ap_du_task.h" # include "enb_app.h" # include "openair2/LAYER2/MAC/mac_proto.h" +#include <executables/split_headers.h> extern RAN_CONTEXT_t RC; @@ -50,25 +51,21 @@ int create_tasks(uint32_t enb_nb) { int rc; if (enb_nb == 0) return 0; - bool fs6Du=false; - if ( getenv("fs6") != NULL && strncasecmp( getenv("fs6"), "du", 2) == 0 ) - fs6Du=true; LOG_I(ENB_APP, "Creating ENB_APP eNB Task\n"); rc = itti_create_task (TASK_ENB_APP, eNB_app_task, NULL); AssertFatal(rc >= 0, "Create task for eNB APP failed\n"); - LOG_I(RRC,"Creating RRC eNB Task\n"); - rc = itti_create_task (TASK_RRC_ENB, rrc_enb_task, NULL); - AssertFatal(rc >= 0, "Create task for RRC eNB failed\n"); + // No more rrc thread, as many race conditions are hidden behind + rrc_enb_init(); + itti_mark_task_ready(TASK_RRC_ENB); - if (EPC_MODE_ENABLED && !fs6Du ) { + if (EPC_MODE_ENABLED && ! ( split73==SPLIT73_DU ) ) { rc = itti_create_task(TASK_SCTP, sctp_eNB_task, NULL); AssertFatal(rc >= 0, "Create task for SCTP failed\n"); } - - if (EPC_MODE_ENABLED && !NODE_IS_DU(type) && !fs6Du ) { + if (EPC_MODE_ENABLED && !NODE_IS_DU(type) && ! ( split73==SPLIT73_DU ) ) { rc = itti_create_task(TASK_S1AP, s1ap_eNB_task, NULL); AssertFatal(rc >= 0, "Create task for S1AP failed\n"); if (!(get_softmodem_params()->emulate_rf)){ diff --git a/targets/RT/USER/lte-softmodem.c b/targets/RT/USER/lte-softmodem.c index 8e85ae11c6e..67f9c622561 100644 --- a/targets/RT/USER/lte-softmodem.c +++ b/targets/RT/USER/lte-softmodem.c @@ -93,6 +93,7 @@ unsigned short config_frames[4] = {2,9,11,13}; #include "lte-softmodem.h" #include "NB_IoT_interface.h" +#include <executables/split_headers.h> pthread_cond_t nfapi_sync_cond; @@ -152,6 +153,13 @@ int otg_enabled; uint64_t num_missed_slots=0; // counter for the number of missed slots +int split73=0; +void sendFs6Ul(PHY_VARS_eNB *eNB, int UE_id, int harq_pid, int segmentID, int16_t *data, int dataLen, int r_offset) { + AssertFatal(false, "Must not be called in this context\n"); +} +void sendFs6Ulharq(enum pckType type, int UEid, PHY_VARS_eNB *eNB, LTE_eNB_UCI *uci, int frame, int subframe, uint8_t *harq_ack, uint8_t tdd_mapping_mode, uint16_t tdd_multiplexing_mask, uint16_t rnti, int32_t stat) { + AssertFatal(false, "Must not be called in this context\n"); +} extern void reset_opp_meas(void); extern void print_opp_meas(void); @@ -438,12 +446,9 @@ int restart_L1L2(module_id_t enb_id) { init_UE_list(&RC.mac[enb_id]->UE_list); LOG_I(ENB_APP, "attempting to create ITTI tasks\n"); - if (itti_create_task (TASK_RRC_ENB, rrc_enb_task, NULL) < 0) { - LOG_E(RRC, "Create task for RRC eNB failed\n"); - return -1; - } else { - LOG_I(RRC, "Re-created task for RRC eNB successfully\n"); - } + // No more rrc thread, as many race conditions are hidden behind + rrc_enb_init(); + itti_mark_task_ready(TASK_RRC_ENB); /* pass a reconfiguration request which will configure everything down to * RC.eNB[i][j]->frame_parms, too */ @@ -477,8 +482,7 @@ void init_pdcp(void) { pdcp_initmask = pdcp_initmask | ENB_NAS_USE_TUN_W_MBMS_BIT; - if ( getenv("fs6") != NULL && strncasecmp( getenv("fs6"), "du", 2) == 0 ) - pdcp_module_init(pdcp_initmask); + pdcp_module_init(pdcp_initmask); if (NODE_IS_CU(RC.rrc[0]->node_type)) { pdcp_set_rlc_data_req_func((send_rlc_data_req_func_t)proto_agent_send_rlc_data_req); @@ -643,12 +647,15 @@ int main ( int argc, char **argv ) for (int x=0; x < RC.nb_L1_inst; x++) for (int CC_id=0; CC_id<RC.nb_L1_CC[x]; CC_id++) { L1_rxtx_proc_t *L1proc= &RC.eNB[x][CC_id]->proc.L1_proc; + L1proc->threadPool=(tpool_t*)malloc(sizeof(tpool_t)); + L1proc->respEncode=(notifiedFIFO_t*) malloc(sizeof(notifiedFIFO_t)); + L1proc->respDecode=(notifiedFIFO_t*) malloc(sizeof(notifiedFIFO_t)); if ( strlen(get_softmodem_params()->threadPoolConfig) > 0 ) - initTpool(get_softmodem_params()->threadPoolConfig, &L1proc->threadPool, true); + initTpool(get_softmodem_params()->threadPoolConfig, L1proc->threadPool, true); else - initTpool("n", &L1proc->threadPool, true); - initNotifiedFIFO(&L1proc->respEncode); - initNotifiedFIFO(&L1proc->respDecode); + initTpool("n", L1proc->threadPool, true); + initNotifiedFIFO(L1proc->respEncode); + initNotifiedFIFO(L1proc->respDecode); } -- GitLab