From d7781f7a315e2d3558329f095597bd8d94b2f13e Mon Sep 17 00:00:00 2001 From: "S. Sandeep Kumar" <ee13b1025@iith.ac.in> Date: Fri, 17 Jun 2016 01:47:42 +0530 Subject: [PATCH] asthetic fixes for lte-enb.c --- targets/RT/USER/lte-enb.c | 634 +++++++++++++++++--------------------- 1 file changed, 286 insertions(+), 348 deletions(-) diff --git a/targets/RT/USER/lte-enb.c b/targets/RT/USER/lte-enb.c index 3afdb15ef1c..918c1f82ee9 100644 --- a/targets/RT/USER/lte-enb.c +++ b/targets/RT/USER/lte-enb.c @@ -108,10 +108,6 @@ unsigned short config_frames[4] = {2,9,11,13}; # endif #endif - - - - //#define DEBUG_THREADS 1 //#define USRP_DEBUG 1 @@ -158,15 +154,14 @@ static struct { volatile uint8_t phy_proc_CC_id; } sync_phy_proc; - void exit_fun(const char* s); void init_eNB(eNB_func_t node_function); void stop_eNB(void); -void do_OFDM_mod_rt(int subframe,PHY_VARS_eNB *phy_vars_eNB) -{ +void do_OFDM_mod_rt(int subframe,PHY_VARS_eNB *phy_vars_eNB) { + unsigned int aa,slot_offset, slot_offset_F; int dummy_tx_b[7680*4] __attribute__((aligned(32))); int i,j, tx_offset; @@ -183,7 +178,6 @@ void do_OFDM_mod_rt(int subframe,PHY_VARS_eNB *phy_vars_eNB) ((subframe_select(&phy_vars_eNB->frame_parms,subframe)==SF_S))) { // LOG_D(HW,"Frame %d: Generating slot %d\n",frame,next_slot); - for (aa=0; aa<phy_vars_eNB->frame_parms.nb_antennas_tx; aa++) { if (phy_vars_eNB->frame_parms.Ncp == EXTENDED) { PHY_ofdm_mod(&phy_vars_eNB->common_vars.txdataF[0][aa][slot_offset_F], @@ -204,7 +198,7 @@ void do_OFDM_mod_rt(int subframe,PHY_VARS_eNB *phy_vars_eNB) 7, &(phy_vars_eNB->frame_parms)); // if S-subframe generate first slot only - if (subframe_select(&phy_vars_eNB->frame_parms,subframe) == SF_DL) + if (subframe_select(&phy_vars_eNB->frame_parms,subframe) == SF_DL) normal_prefix_mod(&phy_vars_eNB->common_vars.txdataF[0][aa][slot_offset_F+slot_sizeF], dummy_tx_b+(phy_vars_eNB->frame_parms.samples_per_tti>>1), 7, @@ -237,9 +231,8 @@ void do_OFDM_mod_rt(int subframe,PHY_VARS_eNB *phy_vars_eNB) txdata[j++] = ((int16_t*)dummy_tx_b)[i]<<openair0_cfg[0].iq_txshift; } } - } + } else if ((slot_offset+time_offset[aa]+len)>(LTE_NUMBER_OF_SUBFRAMES_PER_FRAME*phy_vars_eNB->frame_parms.samples_per_tti)) { - tx_offset = (int)slot_offset+time_offset[aa]; txdata = (int16_t*)&phy_vars_eNB->common_vars.txdata[0][aa][tx_offset]; len2 = -tx_offset+LTE_NUMBER_OF_SUBFRAMES_PER_FRAME*phy_vars_eNB->frame_parms.samples_per_tti; @@ -260,7 +253,6 @@ void do_OFDM_mod_rt(int subframe,PHY_VARS_eNB *phy_vars_eNB) } } - // if S-subframe switch to RX in second subframe /* if (subframe_select(&phy_vars_eNB->frame_parms,subframe) == SF_S) { @@ -270,34 +262,35 @@ void do_OFDM_mod_rt(int subframe,PHY_VARS_eNB *phy_vars_eNB) } */ if ((((phy_vars_eNB->frame_parms.tdd_config==0) || - (phy_vars_eNB->frame_parms.tdd_config==1) || - (phy_vars_eNB->frame_parms.tdd_config==2) || - (phy_vars_eNB->frame_parms.tdd_config==6)) && - (subframe==0)) || (subframe==5)) { + (phy_vars_eNB->frame_parms.tdd_config==1) || + (phy_vars_eNB->frame_parms.tdd_config==2) || + (phy_vars_eNB->frame_parms.tdd_config==6)) && + (subframe==0)) || (subframe==5)) { // turn on tx switch N_TA_offset before //LOG_D(HW,"subframe %d, time to switch to tx (N_TA_offset %d, slot_offset %d) \n",subframe,phy_vars_eNB->N_TA_offset,slot_offset); for (i=0; i<phy_vars_eNB->N_TA_offset; i++) { - tx_offset = (int)slot_offset+time_offset[aa]+i-phy_vars_eNB->N_TA_offset/2; - if (tx_offset<0) - tx_offset += LTE_NUMBER_OF_SUBFRAMES_PER_FRAME*phy_vars_eNB->frame_parms.samples_per_tti; + tx_offset = (int)slot_offset+time_offset[aa]+i-phy_vars_eNB->N_TA_offset/2; + if (tx_offset<0) + tx_offset += LTE_NUMBER_OF_SUBFRAMES_PER_FRAME*phy_vars_eNB->frame_parms.samples_per_tti; - if (tx_offset>=(LTE_NUMBER_OF_SUBFRAMES_PER_FRAME*phy_vars_eNB->frame_parms.samples_per_tti)) - tx_offset -= LTE_NUMBER_OF_SUBFRAMES_PER_FRAME*phy_vars_eNB->frame_parms.samples_per_tti; + if (tx_offset>=(LTE_NUMBER_OF_SUBFRAMES_PER_FRAME*phy_vars_eNB->frame_parms.samples_per_tti)) + tx_offset -= LTE_NUMBER_OF_SUBFRAMES_PER_FRAME*phy_vars_eNB->frame_parms.samples_per_tti; - phy_vars_eNB->common_vars.txdata[0][aa][tx_offset] = 0x00000000; + phy_vars_eNB->common_vars.txdata[0][aa][tx_offset] = 0x00000000; } } } } } + /*! * \brief The RX UE-specific and TX thread of eNB. * \param param is a \ref eNB_proc_t structure which contains the info what to process. * \returns a pointer to an int. The storage is not on the heap and must not be freed. */ -static void* eNB_thread_rxtx( void* param ) -{ +static void* eNB_thread_rxtx( void* param ) { + static int eNB_thread_rxtx_status; eNB_rxtx_proc_t *proc = (eNB_rxtx_proc_t*)param; @@ -365,56 +358,47 @@ static void* eNB_thread_rxtx( void* param ) #endif //CPU_AFFINITY /* Check the actual affinity mask assigned to the thread */ - s = pthread_getaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); - if (s != 0) - { + if (s != 0) { perror( "pthread_getaffinity_np"); exit_fun("Error getting processor affinity "); } memset(cpu_affinity,0,sizeof(cpu_affinity)); for (j = 0; j < CPU_SETSIZE; j++) - if (CPU_ISSET(j, &cpuset)) - { - char temp[1024]; - sprintf (temp, " CPU_%d", j); - strcat(cpu_affinity, temp); - } + if (CPU_ISSET(j, &cpuset)) { + char temp[1024]; + sprintf (temp, " CPU_%d", j); + strcat(cpu_affinity, temp); + } - memset(&sparam, 0 , sizeof (sparam)); + memset(&sparam, 0, sizeof(sparam)); sparam.sched_priority = sched_get_priority_max(SCHED_FIFO)-1; policy = SCHED_FIFO ; s = pthread_setschedparam(pthread_self(), policy, &sparam); - if (s != 0) - { - perror("pthread_setschedparam : "); - exit_fun("Error setting thread priority"); - } + if (s != 0) { + perror("pthread_setschedparam : "); + exit_fun("Error setting thread priority"); + } + s = pthread_getschedparam(pthread_self(), &policy, &sparam); - if (s != 0) - { - perror("pthread_getschedparam : "); - exit_fun("Error getting thread priority"); - - } + if (s != 0) { + perror("pthread_getschedparam : "); + exit_fun("Error getting thread priority"); + } - LOG_I( HW, "[SCHED][eNB] TX thread started on CPU %d TID %ld, sched_policy = %s , priority = %d, CPU Affinity=%s \n",sched_getcpu(),gettid(), + LOG_I(HW, "[SCHED][eNB] TX thread started on CPU %d TID %ld, sched_policy = %s , priority = %d, CPU Affinity=%s \n",sched_getcpu(),gettid(), (policy == SCHED_FIFO) ? "SCHED_FIFO" : (policy == SCHED_RR) ? "SCHED_RR" : (policy == SCHED_OTHER) ? "SCHED_OTHER" : "???", sparam.sched_priority, cpu_affinity ); - #endif //LOW_LATENCY - mlockall(MCL_CURRENT | MCL_FUTURE); - while (!oai_exit) { - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_PROC_RXTX0+(proc->subframe_rx&1), 0 ); if (pthread_mutex_lock(&proc->mutex_rxtx) != 0) { @@ -462,6 +446,7 @@ static void* eNB_thread_rxtx( void* param ) exit_fun("nothing to add"); break; } + // wait for our turn or oai_exit while (sync_phy_proc.phy_proc_CC_id != proc->CC_id && !oai_exit) { pthread_cond_wait(&sync_phy_proc.cond_phy_proc_tx, @@ -473,28 +458,27 @@ static void* eNB_thread_rxtx( void* param ) exit_fun("nothing to add"); } - - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_TX_ENB, proc->frame_tx ); VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_SUBFRAME_NUMBER_TX_ENB, proc->subframe_tx ); - if (oai_exit) - break; + + if (oai_exit) break; + if (PHY_vars_eNB_g[0][proc->CC_id]->node_function != NGFI_RRU_IF4) { - phy_procedures_eNB_TX(PHY_vars_eNB_g[0][proc->CC_id], proc, 0, no_relay, NULL ); - - /* we're done, let the next one proceed */ - if (pthread_mutex_lock(&sync_phy_proc.mutex_phy_proc_tx) != 0) { - LOG_E(PHY, "[SCHED][eNB] error locking PHY proc mutex for eNB TX proc\n"); - exit_fun("nothing to add"); - break; - } - sync_phy_proc.phy_proc_CC_id++; - sync_phy_proc.phy_proc_CC_id %= MAX_NUM_CCs; - pthread_cond_broadcast(&sync_phy_proc.cond_phy_proc_tx); - if (pthread_mutex_unlock(&sync_phy_proc.mutex_phy_proc_tx) != 0) { - LOG_E(PHY, "[SCHED][eNB] error unlocking PHY proc mutex for eNB TX proc\n"); - exit_fun("nothing to add"); - break; + phy_procedures_eNB_TX(PHY_vars_eNB_g[0][proc->CC_id], proc, 0, no_relay, NULL ); + + /* we're done, let the next one proceed */ + if (pthread_mutex_lock(&sync_phy_proc.mutex_phy_proc_tx) != 0) { + LOG_E(PHY, "[SCHED][eNB] error locking PHY proc mutex for eNB TX proc\n"); + exit_fun("nothing to add"); + break; + } + sync_phy_proc.phy_proc_CC_id++; + sync_phy_proc.phy_proc_CC_id %= MAX_NUM_CCs; + pthread_cond_broadcast(&sync_phy_proc.cond_phy_proc_tx); + if (pthread_mutex_unlock(&sync_phy_proc.mutex_phy_proc_tx) != 0) { + LOG_E(PHY, "[SCHED][eNB] error unlocking PHY proc mutex for eNB TX proc\n"); + exit_fun("nothing to add"); + break; } } else { @@ -504,53 +488,51 @@ static void* eNB_thread_rxtx( void* param ) } } - // eNodeB_3GPP and RRU create txdata and write to RF device - if (PHY_vars_eNB_g[0][proc->CC_id]->node_function != NGFI_RCC_IF4) { - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_SFGEN , 1 ); - do_OFDM_mod_rt( proc->subframe_tx, PHY_vars_eNB_g[0][proc->CC_id] ); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_SFGEN , 0 ); - /* - short *txdata = (short*)&PHY_vars_eNB_g[0][proc->CC_id]->common_vars.txdata[0][0][proc->subframe_tx*PHY_vars_eNB_g[0][proc->CC_id]->frame_parms.samples_per_tti]; - int i; - for (i=0;i<PHY_vars_eNB_g[0][proc->CC_id]->frame_parms.samples_per_tti*2;i+=8) { - txdata[i] = 2047; - txdata[i+1] = 0; - txdata[i+2] = 0; - txdata[i+3] = 2047; - txdata[i+4] = -2047; - txdata[i+5] = 0; - txdata[i+6] = 0; - txdata[i+7] = -2047; } - */ - - - // Transmit TX buffer based on timestamp from RX + // eNodeB_3GPP and RRU create txdata and write to RF device + if (PHY_vars_eNB_g[0][proc->CC_id]->node_function != NGFI_RCC_IF4) { + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_SFGEN , 1 ); + do_OFDM_mod_rt( proc->subframe_tx, PHY_vars_eNB_g[0][proc->CC_id] ); + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_SFGEN , 0 ); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_WRITE, 1 ); - // prepare tx buffer pointers - int i; - for (i=0; i<PHY_vars_eNB_g[0][0]->frame_parms.nb_antennas_tx; i++) - txp[i] = (void*)&PHY_vars_eNB_g[0][0]->common_vars.txdata[0][i][proc->subframe_tx*PHY_vars_eNB_g[0][0]->frame_parms.samples_per_tti]; - // if symb_written < spp ==> error - PHY_vars_eNB_g[0][proc->CC_id]->rfdevice.trx_write_func(&PHY_vars_eNB_g[0][proc->CC_id]->rfdevice, - (proc->timestamp_tx-openair0_cfg[0].tx_sample_advance), - txp, - PHY_vars_eNB_g[0][0]->frame_parms.samples_per_tti, - PHY_vars_eNB_g[0][0]->frame_parms.nb_antennas_tx, - 1); - - - - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_WRITE, 0 ); + /* + short *txdata = (short*)&PHY_vars_eNB_g[0][proc->CC_id]->common_vars.txdata[0][0][proc->subframe_tx*PHY_vars_eNB_g[0][proc->CC_id]->frame_parms.samples_per_tti]; + int i; + for (i=0;i<PHY_vars_eNB_g[0][proc->CC_id]->frame_parms.samples_per_tti*2;i+=8) { + txdata[i] = 2047; + txdata[i+1] = 0; + txdata[i+2] = 0; + txdata[i+3] = 2047; + txdata[i+4] = -2047; + txdata[i+5] = 0; + txdata[i+6] = 0; + txdata[i+7] = -2047; } + */ - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST, (proc->timestamp_tx-openair0_cfg[0].tx_sample_advance)&0xffffffff ); + // Transmit TX buffer based on timestamp from RX + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_WRITE, 1 ); + // prepare tx buffer pointers + int i; + for (i=0; i<PHY_vars_eNB_g[0][0]->frame_parms.nb_antennas_tx; i++) + txp[i] = (void*)&PHY_vars_eNB_g[0][0]->common_vars.txdata[0][i][proc->subframe_tx*PHY_vars_eNB_g[0][0]->frame_parms.samples_per_tti]; + + // if symb_written < spp ==> error + PHY_vars_eNB_g[0][proc->CC_id]->rfdevice.trx_write_func(&PHY_vars_eNB_g[0][proc->CC_id]->rfdevice, + (proc->timestamp_tx-openair0_cfg[0].tx_sample_advance), + txp, + PHY_vars_eNB_g[0][0]->frame_parms.samples_per_tti, + PHY_vars_eNB_g[0][0]->frame_parms.nb_antennas_tx, + 1); + + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_WRITE, 0 ); + + VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST, (proc->timestamp_tx-openair0_cfg[0].tx_sample_advance)&0xffffffff ); - } else { // RCC sends the txdataF using send_IF4 function + } else { + + /// **** send_IF4 of txdataF to RRU **** /// + //send_IF4(PHY_vars_eNB_g[0][proc->CC_id], proc); - /// **** send_IF4 of txdataF to RRU **** /// - //send_IF4(PHY_vars_eNB_g[0][proc->CC_id], proc); - - } + } if (pthread_mutex_lock(&proc->mutex_rxtx) != 0) { LOG_E( PHY, "[SCHED][eNB] error locking mutex for eNB TX proc\n"); @@ -567,19 +549,18 @@ static void* eNB_thread_rxtx( void* param ) } stop_meas( &softmodem_stats_rxtx_sf ); + #ifdef LOWLATENCY - if (opp_enabled){ - if(softmodem_stats_rxtx_sf.diff_now/(cpuf) > attr.sched_runtime){ - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_RUNTIME_TX_ENB, (softmodem_stats_rxtx_sf.diff_now/cpuf - attr.sched_runtime)/1000000.0); + if (opp_enabled) { + if(softmodem_stats_rxtx_sf.diff_now/(cpuf) > attr.sched_runtime) { + VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_RUNTIME_TX_ENB, (softmodem_stats_rxtx_sf.diff_now/cpuf - attr.sched_runtime)/1000000.0); } } #endif - print_meas_now(&softmodem_stats_rxtx_sf,"eNB_TX_SF",tx_time_file); + print_meas_now(&softmodem_stats_rxtx_sf,"eNB_TX_SF",tx_time_file); } - - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_PROC_RXTX0+(proc->subframe_rx&1), 0 ); #ifdef DEBUG_THREADS @@ -591,13 +572,11 @@ static void* eNB_thread_rxtx( void* param ) } #if defined(ENABLE_ITTI) -static void wait_system_ready (char *message, volatile int *start_flag) - - /* Wait for eNB application initialization to be complete (eNB registration to MME) */ -{ +/* Wait for eNB application initialization to be complete (eNB registration to MME) */ +static void wait_system_ready (char *message, volatile int *start_flag) { + static char *indicator[] = {". ", ".. ", "... ", ".... ", ".....", - " ....", " ...", " ..", " .", " " - }; + " ....", " ...", " ..", " .", " "}; int i = 0; while ((!oai_exit) && (*start_flag == 0)) { @@ -609,16 +588,16 @@ static void wait_system_ready (char *message, volatile int *start_flag) LOG_D(EMU,"\n"); } - #endif + /*! * \brief The RX common thread of eNB. * \param param is a \ref eNB_proc_t structure which contains the info what to process. * \returns a pointer to an int. The storage is not on the heap and must not be freed. */ -static void* eNB_thread_rx_common( void* param ) -{ +static void* eNB_thread_rx_common( void* param ) { + static int eNB_thread_rx_status; eNB_proc_t *proc = (eNB_proc_t*)param; @@ -629,7 +608,7 @@ static void* eNB_thread_rx_common( void* param ) char rx_time_name[101]; //int i; - if (opp_enabled == 1){ + if (opp_enabled == 1) { snprintf(rx_time_name, 100,"/tmp/%s_rx_time_thread_sf", "eNB"); rx_time_file = fopen(rx_time_name,"w"); } @@ -674,14 +653,12 @@ static void* eNB_thread_rx_common( void* param ) /* Set CPU Affinity only if number of CPUs >2 */ CPU_ZERO(&cpuset); #ifdef CPU_AFFINITY - if (get_nprocs() >2) - { + if (get_nprocs() >2) { for (j = 1; j < get_nprocs(); j++) - CPU_SET(j, &cpuset); + CPU_SET(j, &cpuset); s = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); - if (s != 0) - { + if (s != 0) { perror( "pthread_setaffinity_np"); exit_fun (" Error setting processor affinity :"); } @@ -690,66 +667,58 @@ static void* eNB_thread_rx_common( void* param ) /* Check the actual affinity mask assigned to the thread */ s = pthread_getaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); - if (s != 0) - { - perror ("pthread_getaffinity_np"); - exit_fun (" Error getting processor affinity :"); + if (s != 0) { + perror ("pthread_getaffinity_np"); + exit_fun (" Error getting processor affinity :"); } memset(cpu_affinity,0, sizeof(cpu_affinity)); for (j = 0; j < CPU_SETSIZE; j++) - if (CPU_ISSET(j, &cpuset)) - { - char temp[1024]; - sprintf (temp, " CPU_%d", j); - strcat(cpu_affinity, temp); - } - + if (CPU_ISSET(j, &cpuset)) { + char temp[1024]; + sprintf (temp, " CPU_%d", j); + strcat(cpu_affinity, temp); + } memset(&sparam, 0 , sizeof (sparam)); sparam.sched_priority = sched_get_priority_max(SCHED_FIFO); policy = SCHED_FIFO ; s = pthread_setschedparam(pthread_self(), policy, &sparam); - if (s != 0) - { - perror("pthread_setschedparam : "); - exit_fun("Error setting thread priority"); - } + if (s != 0) { + perror("pthread_setschedparam : "); + exit_fun("Error setting thread priority"); + } memset(&sparam, 0 , sizeof (sparam)); s = pthread_getschedparam(pthread_self(), &policy, &sparam); - if (s != 0) - { - perror("pthread_getschedparam"); - exit_fun("Error getting thread priority"); - } - + if (s != 0) { + perror("pthread_getschedparam"); + exit_fun("Error getting thread priority"); + } - LOG_I( HW, "[SCHED][eNB] RX thread started on CPU %d TID %ld, sched_policy = %s, priority = %d, CPU Affinity = %s\n", sched_getcpu(),gettid(), + LOG_I(HW, "[SCHED][eNB] RX thread started on CPU %d TID %ld, sched_policy = %s, priority = %d, CPU Affinity = %s\n", sched_getcpu(),gettid(), (policy == SCHED_FIFO) ? "SCHED_FIFO" : (policy == SCHED_RR) ? "SCHED_RR" : (policy == SCHED_OTHER) ? "SCHED_OTHER" : "???", sparam.sched_priority, cpu_affinity); - - + #endif // LOWLATENCY - mlockall(MCL_CURRENT | MCL_FUTURE); - + mlockall(MCL_CURRENT | MCL_FUTURE); - // wait for top-level synchronization and do one acquisition to get timestamp for setting frame/subframe of TX and RX threads - printf( "waiting for sync (eNB_thread_rx_common)\n"); - pthread_mutex_lock( &sync_mutex ); + // wait for top-level synchronization and do one acquisition to get timestamp for setting frame/subframe of TX and RX threads + printf( "waiting for sync (eNB_thread_rx_common)\n"); + pthread_mutex_lock( &sync_mutex ); - while (sync_var<0) - pthread_cond_wait( &sync_cond, &sync_mutex ); + while (sync_var<0) + pthread_cond_wait( &sync_cond, &sync_mutex ); - pthread_mutex_unlock(&sync_mutex); + pthread_mutex_unlock(&sync_mutex); - printf( "got sync (eNB_thread)\n" ); + printf( "got sync (eNB_thread)\n" ); #if defined(ENABLE_ITTI) wait_system_ready ("Waiting for eNB application to be ready %s\r", &start_eNB); @@ -767,96 +736,90 @@ static void* eNB_thread_rx_common( void* param ) LOG_E(HW,"Could not start the IF device\n"); } - // This is a forever while loop, it loops over subframes which are scheduled by incoming samples from HW devices - while (!oai_exit) { - - - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_PROC_RX, 0 ); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_ENB_RX_COMMON, 0 ); - start_meas( &softmodem_stats_rx_sf ); + // This is a forever while loop, it loops over subframes which are scheduled by incoming samples from HW devices + while (!oai_exit) { + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_PROC_RX, 0 ); + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_ENB_RX_COMMON, 0 ); + start_meas( &softmodem_stats_rx_sf ); - if (oai_exit) break; + if (oai_exit) break; - if ((((fp->frame_type == TDD )&&(subframe_select(fp,proc->subframe_rx)==SF_UL)) || - (fp->frame_type == FDD))) { - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_ENB_RX_COMMON, 1 ); - // this spawns the prach inside and updates the frame and subframe counters - phy_procedures_eNB_common_RX(eNB, 0); - - - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_ENB_RX_COMMON, 0 ); - } - - // choose even or odd thread for RXn-TXnp4 processing - eNB_rxtx_proc_t *proc_rxtx = &proc->proc_rxtx[proc->subframe_rx&1]; - - // wake up TX for subframe n+4 - // lock the TX mutex and make sure the thread is ready - if (pthread_mutex_lock(&proc_rxtx->mutex_rxtx) != 0) { - LOG_E( PHY, "[eNB] ERROR pthread_mutex_lock for eNB TX thread %d (IC %d)\n", proc_rxtx->instance_cnt_rxtx ); - exit_fun( "error locking mutex_rxtx" ); - break; - } - int cnt_rxtx = ++proc_rxtx->instance_cnt_rxtx; - // 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_pexr_frame) was n*samples_per_tti, - // we want to generate subframe (n+3), so TS_tx = TX_rx+3*samples_per_tti, - // and proc->subframe_tx = proc->subframe_rx+3 - proc_rxtx->timestamp_tx = proc->timestamp_rx + (4*fp->samples_per_tti); - proc_rxtx->frame_rx = proc->frame_rx; - proc_rxtx->subframe_rx = proc->subframe_rx; - proc_rxtx->frame_tx = (proc_rxtx->subframe_rx > 5) ? (proc_rxtx->frame_rx+1)&1023 : proc_rxtx->frame_rx; - proc_rxtx->subframe_tx = (proc_rxtx->subframe_rx + 4)%10; + if ((((fp->frame_type == TDD )&&(subframe_select(fp,proc->subframe_rx)==SF_UL)) || + (fp->frame_type == FDD))) { + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_ENB_RX_COMMON, 1 ); + // this spawns the prach inside and updates the frame and subframe counters + phy_procedures_eNB_common_RX(eNB, 0); + + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_ENB_RX_COMMON, 0 ); + } + + // choose even or odd thread for RXn-TXnp4 processing + eNB_rxtx_proc_t *proc_rxtx = &proc->proc_rxtx[proc->subframe_rx&1]; + + // wake up TX for subframe n+4 + // lock the TX mutex and make sure the thread is ready + if (pthread_mutex_lock(&proc_rxtx->mutex_rxtx) != 0) { + LOG_E( PHY, "[eNB] ERROR pthread_mutex_lock for eNB TX thread %d (IC %d)\n", proc_rxtx->instance_cnt_rxtx ); + exit_fun( "error locking mutex_rxtx" ); + break; + } + int cnt_rxtx = ++proc_rxtx->instance_cnt_rxtx; + // 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_pexr_frame) was n*samples_per_tti, + // we want to generate subframe (n+3), so TS_tx = TX_rx+3*samples_per_tti, + // and proc->subframe_tx = proc->subframe_rx+3 + proc_rxtx->timestamp_tx = proc->timestamp_rx + (4*fp->samples_per_tti); + proc_rxtx->frame_rx = proc->frame_rx; + proc_rxtx->subframe_rx = proc->subframe_rx; + proc_rxtx->frame_tx = (proc_rxtx->subframe_rx > 5) ? (proc_rxtx->frame_rx+1)&1023 : proc_rxtx->frame_rx; + proc_rxtx->subframe_tx = (proc_rxtx->subframe_rx + 4)%10; - pthread_mutex_unlock( &proc_rxtx->mutex_rxtx ); + pthread_mutex_unlock( &proc_rxtx->mutex_rxtx ); - if (cnt_rxtx == 0){ - // the thread was presumably waiting where it should and can now be woken up - if (pthread_cond_signal(&proc_rxtx->cond_rxtx) != 0) { - LOG_E( PHY, "[eNB] ERROR pthread_cond_signal for eNB RXn-TXnp4 thread\n"); - exit_fun( "ERROR pthread_cond_signal" ); - break; - } - } else { - LOG_W( PHY,"[eNB] Frame %d, eNB RXn-TXnp4 thread busy!! (cnt_rxtx %i)\n", proc_rxtx->frame_tx, cnt_rxtx ); - exit_fun( "TX thread busy" ); - break; - } + if (cnt_rxtx == 0) { + // the thread was presumably waiting where it should and can now be woken up + if (pthread_cond_signal(&proc_rxtx->cond_rxtx) != 0) { + LOG_E( PHY, "[eNB] ERROR pthread_cond_signal for eNB RXn-TXnp4 thread\n"); + exit_fun( "ERROR pthread_cond_signal" ); + break; + } + } else { + LOG_W( PHY,"[eNB] Frame %d, eNB RXn-TXnp4 thread busy!! (cnt_rxtx %i)\n", proc_rxtx->frame_tx, cnt_rxtx ); + exit_fun( "TX thread busy" ); + break; + } - stop_meas( &softmodem_stats_rxtx_sf ); + stop_meas( &softmodem_stats_rxtx_sf ); + #ifdef LOWLATENCY - if (opp_enabled){ - if(softmodem_stats_rxtx_sf.diff_now/(cpuf) > attr.sched_runtime){ - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_RUNTIME_RXTX_ENB, (softmodem_stats_rxtx_sf.diff_now/cpuf - attr.sched_runtime)/1000000.0); - } - } + if (opp_enabled){ + if(softmodem_stats_rxtx_sf.diff_now/(cpuf) > attr.sched_runtime){ + VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_RUNTIME_RXTX_ENB, (softmodem_stats_rxtx_sf.diff_now/cpuf - attr.sched_runtime)/1000000.0); + } + } #endif // LOWLATENCY - print_meas_now(&softmodem_stats_rx_sf,"eNB_RX_SF", rx_time_file); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_PROC_RXTX0+(proc->subframe_rx&1), 0 ); - - } - + print_meas_now(&softmodem_stats_rx_sf,"eNB_RX_SF", rx_time_file); + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_PROC_RXTX0+(proc->subframe_rx&1), 0 ); + } #ifdef DEBUG_THREADS - printf( "Exiting eNB thread RXn-TXnp4\n"); + printf( "Exiting eNB thread RXn-TXnp4\n"); #endif - eNB_thread_rx_status = 0; - return &eNB_thread_rx_status; + eNB_thread_rx_status = 0; + return &eNB_thread_rx_status; } - /*! * \brief The prach receive thread of eNB. * \param param is a \ref eNB_proc_t structure which contains the info what to process. * \returns a pointer to an int. The storage is not on the heap and must not be freed. */ -static void* eNB_thread_prach( void* param ) -{ +static void* eNB_thread_prach( void* param ) { static int eNB_thread_prach_status; eNB_proc_t *proc = (eNB_proc_t*)param; @@ -865,7 +828,6 @@ static void* eNB_thread_prach( void* param ) eNB_thread_prach_status = 0; MSC_START_USE(); - #ifdef LOWLATENCY struct sched_attr attr; @@ -903,113 +865,102 @@ static void* eNB_thread_prach( void* param ) /* Set CPU Affinity only if number of CPUs >2 */ CPU_ZERO(&cpuset); #ifdef CPU_AFFINITY - if (get_nprocs() >2) - { + if (get_nprocs() >2) { for (j = 1; j < get_nprocs(); j++) - CPU_SET(j, &cpuset); + CPU_SET(j, &cpuset); s = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); - if (s != 0) - { + if (s != 0) { perror( "pthread_setaffinity_np"); exit_fun (" Error setting processor affinity :"); } } #endif //CPU_AFFINITY - /* Check the actual affinity mask assigned to the thread */ + /* Check the actual affinity mask assigned to the thread */ s = pthread_getaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); - if (s != 0) - { - perror ("pthread_getaffinity_np"); - exit_fun (" Error getting processor affinity :"); + if (s != 0) { + perror ("pthread_getaffinity_np"); + exit_fun (" Error getting processor affinity :"); } memset(cpu_affinity,0, sizeof(cpu_affinity)); for (j = 0; j < CPU_SETSIZE; j++) - if (CPU_ISSET(j, &cpuset)) - { - char temp[1024]; - sprintf (temp, " CPU_%d", j); - strcat(cpu_affinity, temp); - } - + if (CPU_ISSET(j, &cpuset)) { + char temp[1024]; + sprintf (temp, " CPU_%d", j); + strcat(cpu_affinity, temp); + } memset(&sparam, 0 , sizeof (sparam)); sparam.sched_priority = sched_get_priority_max(SCHED_FIFO)-2; policy = SCHED_FIFO ; s = pthread_setschedparam(pthread_self(), policy, &sparam); - if (s != 0) - { - perror("pthread_setschedparam : "); - exit_fun("Error setting thread priority"); - } + if (s != 0) { + perror("pthread_setschedparam : "); + exit_fun("Error setting thread priority"); + } memset(&sparam, 0 , sizeof (sparam)); s = pthread_getschedparam(pthread_self(), &policy, &sparam); - if (s != 0) - { - perror("pthread_getschedparam"); - exit_fun("Error getting thread priority"); - } - + if (s != 0) { + perror("pthread_getschedparam"); + exit_fun("Error getting thread priority"); + } - LOG_I( HW, "[SCHED][eNB] PRACH thread started on CPU %d TID %ld, sched_policy = %s, priority = %d, CPU Affinity = %s\n", sched_getcpu(),gettid(), + LOG_I(HW, "[SCHED][eNB] PRACH thread started on CPU %d TID %ld, sched_policy = %s, priority = %d, CPU Affinity = %s\n", sched_getcpu(),gettid(), (policy == SCHED_FIFO) ? "SCHED_FIFO" : (policy == SCHED_RR) ? "SCHED_RR" : (policy == SCHED_OTHER) ? "SCHED_OTHER" : "???", sparam.sched_priority, cpu_affinity); - - + #endif // LOWLATENCY - mlockall(MCL_CURRENT | MCL_FUTURE); - + mlockall(MCL_CURRENT | MCL_FUTURE); - while (!oai_exit) { - - - if (oai_exit) break; + while (!oai_exit) { + + if (oai_exit) break; - if (pthread_mutex_lock(&proc->mutex_prach) != 0) { - LOG_E( PHY, "[SCHED][eNB] error locking mutex for eNB PRACH\n"); - exit_fun( "error locking mutex" ); - break; - } - - while (proc->instance_cnt_prach < 0) { - // most of the time the thread is waiting here - // proc->instance_cnt_prach is -1 - pthread_cond_wait( &proc->cond_prach, &proc->mutex_prach ); // this unlocks mutex_rxtx while waiting and then locks it again - } - - if (pthread_mutex_unlock(&proc->mutex_prach) != 0) { - LOG_E( PHY, "[SCHED][eNB] error unlocking mutex for eNB PRACH\n"); - exit_fun( "error unlocking mutex" ); - break; - } + if (pthread_mutex_lock(&proc->mutex_prach) != 0) { + LOG_E( PHY, "[SCHED][eNB] error locking mutex for eNB PRACH\n"); + exit_fun( "error locking mutex" ); + break; + } + + while (proc->instance_cnt_prach < 0) { + // most of the time the thread is waiting here + // proc->instance_cnt_prach is -1 + pthread_cond_wait( &proc->cond_prach, &proc->mutex_prach ); // this unlocks mutex_rxtx while waiting and then locks it again + } + + if (pthread_mutex_unlock(&proc->mutex_prach) != 0) { + LOG_E( PHY, "[SCHED][eNB] error unlocking mutex for eNB PRACH\n"); + exit_fun( "error unlocking mutex" ); + break; + } - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_PRACH_RX,1); - prach_procedures(eNB,0); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_PRACH_RX,0); + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_PRACH_RX,1); + prach_procedures(eNB,0); + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_PRACH_RX,0); - if (pthread_mutex_lock(&proc->mutex_prach) != 0) { - LOG_E( PHY, "[SCHED][eNB] error locking mutex for eNB PRACH proc %d\n"); - exit_fun( "error locking mutex" ); - break; - } - - proc->instance_cnt_prach--; + if (pthread_mutex_lock(&proc->mutex_prach) != 0) { + LOG_E( PHY, "[SCHED][eNB] error locking mutex for eNB PRACH proc %d\n"); + exit_fun( "error locking mutex" ); + break; + } - if (pthread_mutex_unlock(&proc->mutex_prach) != 0) { - LOG_E( PHY, "[SCHED][eNB] error unlocking mutex for eNB RX proc %d\n"); - exit_fun( "error unlocking mutex" ); - break; - } - } + proc->instance_cnt_prach--; + + if (pthread_mutex_unlock(&proc->mutex_prach) != 0) { + LOG_E( PHY, "[SCHED][eNB] error unlocking mutex for eNB RX proc %d\n"); + exit_fun( "error unlocking mutex" ); + break; + } + } #ifdef DEBUG_THREADS printf( "Exiting eNB thread PRACH\n"); @@ -1020,10 +971,8 @@ static void* eNB_thread_prach( void* param ) } - - -void init_eNB_proc(void) -{ +void init_eNB_proc(void) { + int i; int CC_id; PHY_VARS_eNB *eNB; @@ -1033,7 +982,6 @@ void init_eNB_proc(void) for (CC_id=0; CC_id<MAX_NUM_CCs; CC_id++) { eNB = PHY_vars_eNB_g[0][CC_id]; - proc = &eNB->proc; proc_rxtx = proc->proc_rxtx; #ifndef LOWLATENCY @@ -1097,7 +1045,6 @@ void init_eNB_proc(void) pthread_setname_np( proc->pthread_rx, name ); } - /* setup PHY proc TX sync mechanism */ pthread_mutex_init(&sync_phy_proc.mutex_phy_proc_tx, NULL); pthread_cond_init(&sync_phy_proc.cond_phy_proc_tx, NULL); @@ -1108,8 +1055,8 @@ void init_eNB_proc(void) /*! * \brief Terminate eNB TX and RX threads. */ -void kill_eNB_proc(void) -{ +void kill_eNB_proc(void) { + int *status; PHY_VARS_eNB *eNB; eNB_proc_t *proc; @@ -1133,6 +1080,7 @@ void kill_eNB_proc(void) #ifdef DEBUG_THREADS printf( "Joining eNB TX CC_id %d thread\n", CC_id); #endif + int result,i; for (i=0;i<1;i++) { pthread_join( proc_rxtx[i].pthread_rxtx, (void**)&status ); @@ -1148,16 +1096,14 @@ void kill_eNB_proc(void) printf( "The thread was killed. No status available.\n" ); } } - #else UNUSED(result); #endif pthread_mutex_destroy( &proc_rxtx[i].mutex_rxtx ); pthread_cond_destroy( &proc_rxtx[i].cond_rxtx ); - - } + #ifdef DEBUG_THREADS printf( "Killing RX CC_id %d thread\n", CC_id); #endif @@ -1165,10 +1111,10 @@ void kill_eNB_proc(void) #ifdef DEBUG_THREADS printf( "Joining eNB RX CC_id %d thread ...\n", CC_id); #endif + result = pthread_join( proc->pthread_rx, (void**)&status ); -#ifdef DEBUG_THREADS - +#ifdef DEBUG_THREADS if (result != 0) { printf( "Error joining thread.\n" ); } else { @@ -1177,8 +1123,7 @@ void kill_eNB_proc(void) } else { printf( "The thread was killed. No status available.\n" ); } - } - + } #else UNUSED(result); #endif @@ -1193,17 +1138,17 @@ void kill_eNB_proc(void) Each rf chain is is addressed by the card number and the chain on the card. The rf_map specifies for each CC, on which rf chain the mapping should start. Multiple antennas are mapped to successive RF chains on the same card. */ -int setup_eNB_buffers(PHY_VARS_eNB **phy_vars_eNB, openair0_config_t *openair0_cfg, openair0_rf_map rf_map[MAX_NUM_CCs]) -{ +int setup_eNB_buffers(PHY_VARS_eNB **phy_vars_eNB, openair0_config_t *openair0_cfg, openair0_rf_map rf_map[MAX_NUM_CCs]) { int i, CC_id; + #ifndef EXMIMO uint16_t N_TA_offset = 0; #else int j; #endif - LTE_DL_FRAME_PARMS *frame_parms; + LTE_DL_FRAME_PARMS *frame_parms; for (CC_id=0; CC_id<MAX_NUM_CCs; CC_id++) { if (phy_vars_eNB[CC_id]) { @@ -1213,18 +1158,15 @@ int setup_eNB_buffers(PHY_VARS_eNB **phy_vars_eNB, openair0_config_t *openair0_c printf("phy_vars_eNB[%d] not initialized\n", CC_id); return(-1); } - #ifndef EXMIMO - - if (frame_parms->frame_type == TDD) { - if (frame_parms->N_RB_DL == 100) - N_TA_offset = 624; - else if (frame_parms->N_RB_DL == 50) - N_TA_offset = 624/2; - else if (frame_parms->N_RB_DL == 25) - N_TA_offset = 624/4; - } - + if (frame_parms->frame_type == TDD) { + if (frame_parms->N_RB_DL == 100) + N_TA_offset = 624; + else if (frame_parms->N_RB_DL == 50) + N_TA_offset = 624/2; + else if (frame_parms->N_RB_DL == 25) + N_TA_offset = 624/4; + } #endif // replace RX signal buffers with mmaped HW versions @@ -1285,8 +1227,7 @@ int setup_eNB_buffers(PHY_VARS_eNB **phy_vars_eNB, openair0_config_t *openair0_c rxdata[i] = (int32_t*)(32 + malloc16(32+openair0_cfg[rf_map[CC_id].card].samples_per_frame*sizeof(int32_t))); // FIXME broken memory allocation phy_vars_eNB[CC_id]->common_vars.rxdata[0][i] = rxdata[i]-N_TA_offset; // N_TA offset for TDD FIXME! N_TA_offset > 16 => access of unallocated memory memset(rxdata[i], 0, openair0_cfg[rf_map[CC_id].card].samples_per_frame*sizeof(int32_t)); - printf("rxdata[%d] @ %p (%p) (N_TA_OFFSET %d)\n", i, phy_vars_eNB[CC_id]->common_vars.rxdata[0][i],rxdata[i],N_TA_offset); - + printf("rxdata[%d] @ %p (%p) (N_TA_OFFSET %d)\n", i, phy_vars_eNB[CC_id]->common_vars.rxdata[0][i],rxdata[i],N_TA_offset); } for (i=0; i<frame_parms->nb_antennas_tx; i++) { @@ -1295,19 +1236,17 @@ int setup_eNB_buffers(PHY_VARS_eNB **phy_vars_eNB, openair0_config_t *openair0_c phy_vars_eNB[CC_id]->common_vars.txdata[0][i] = txdata[i]; memset(txdata[i],0, openair0_cfg[rf_map[CC_id].card].samples_per_frame*sizeof(int32_t)); printf("txdata[%d] @ %p\n", i, phy_vars_eNB[CC_id]->common_vars.txdata[0][i]); - } - #endif + } return(0); } - - void reset_opp_meas(void) { + int sfn; reset_meas(&softmodem_stats_mt); reset_meas(&softmodem_stats_hw); @@ -1318,6 +1257,7 @@ void reset_opp_meas(void) { } } + void print_opp_meas(void) { int sfn=0; @@ -1333,7 +1273,6 @@ void print_opp_meas(void) { void init_eNB(eNB_func_t node_function) { - int CC_id; for (CC_id=0;CC_id<MAX_NUM_CCs;CC_id++) @@ -1363,11 +1302,11 @@ void stop_eNB() { #ifdef DEBUG_THREADS printf("Joining eNB_thread ..."); #endif - int *eNB_thread_status_p; - int result = pthread_join( main_eNB_thread, (void**)&eNB_thread_status_p ); + + int *eNB_thread_status_p; + int result = pthread_join( main_eNB_thread, (void**)&eNB_thread_status_p ); #ifdef DEBUG_THREADS - if (result != 0) { printf( "\nError joining main_eNB_thread.\n" ); } else { @@ -1377,7 +1316,6 @@ void stop_eNB() { printf( "The thread was killed. No status available.\n"); } } - #else UNUSED(result); #endif // DEBUG_THREADS -- GitLab