From 8a92138086c642721e074cbd2271d8760bc42552 Mon Sep 17 00:00:00 2001
From: laurent <laurent.thomas@open-cells.com>
Date: Tue, 30 Apr 2019 10:17:08 +0200
Subject: [PATCH] rework threads in 5G

---
 cmake_targets/CMakeLists.txt                  |   25 +-
 common/utils/ocp_itti/intertask_interface.cpp |   17 +-
 common/utils/system.c                         |  136 +-
 common/utils/system.h                         |   24 +
 common/utils/threadPool/thread-pool.c         |   26 +-
 common/utils/threadPool/thread-pool.h         |    1 +
 executables/nr-gnb.c                          |  952 +++++++
 executables/nr-ru.c                           | 2312 +++++++++++++++++
 executables/nr-softmodem.c                    | 1243 +++++++++
 executables/nr-softmodem.h                    |  251 ++
 executables/nr-ue.c                           |   45 +-
 executables/nr-uesoftmodem.c                  |   14 +-
 executables/openairinterface5g_limits.h       |   89 +
 executables/stats.c                           |   50 +
 executables/stats.h                           |   46 +
 executables/threads_t.h                       |   21 +
 openair1/PHY/NR_TRANSPORT/nr_dci_tools.c      |    2 +-
 .../NR_UE_TRANSPORT/nr_dlsch_demodulation.c   |   27 +-
 openair1/SCHED_NR/nr_ru_procedures.c          |    2 +-
 openair1/SCHED_NR_UE/phy_procedures_nr_ue.c   |    1 -
 openair2/ENB_APP/RRC_config_tools.h           |    2 +-
 openair2/ENB_APP/flexran_agent.c              |   39 +-
 openair2/LAYER2/PDCP_v10.1.0/pdcp_thread.c    |   23 +-
 openair2/RRC/LTE/rrc_UE.c                     |   22 +-
 openair2/RRC/NR/L2_nr_interface.c             |    1 -
 openair2/RRC/NR/rrc_gNB.c                     |    5 +-
 openair2/UTIL/ASYNC_IF/link_manager.c         |   32 +-
 openair2/UTIL/CLI/cli_server.c                |    5 +-
 openair2/UTIL/OPT/probe.c                     |   11 +-
 openair2/UTIL/OTG/otg_rx_socket.c             |   12 +-
 openair3/NAS/UE/UEprocess.c                   |   18 +-
 targets/ARCH/USRP/USERSPACE/LIB/usrp_lib.cpp  |    3 +
 targets/RT/USER/lte-uesoftmodem.c             |    2 +-
 33 files changed, 5174 insertions(+), 285 deletions(-)
 create mode 100644 executables/nr-gnb.c
 create mode 100644 executables/nr-ru.c
 create mode 100644 executables/nr-softmodem.c
 create mode 100644 executables/nr-softmodem.h
 create mode 100644 executables/openairinterface5g_limits.h
 create mode 100644 executables/stats.c
 create mode 100644 executables/stats.h
 create mode 100644 executables/threads_t.h

diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt
index 87c51ccb62..cb9c181a94 100644
--- a/cmake_targets/CMakeLists.txt
+++ b/cmake_targets/CMakeLists.txt
@@ -754,6 +754,7 @@ include_directories("${OPENAIR_BIN_DIR}")
 
 # Legacy exact order
 
+include_directories("${OPENAIR_DIR}/executables")
 include_directories("${OPENAIR2_DIR}/COMMON")
 include_directories("${OPENAIR2_DIR}/UTIL")
 include_directories("${OPENAIR2_DIR}/UTIL/LOG")
@@ -792,10 +793,7 @@ include_directories("${OPENAIR3_DIR}/S1AP")
 include_directories("${OPENAIR2_DIR}/X2AP")
 include_directories("${OPENAIR3_DIR}/UDP")
 include_directories("${OPENAIR3_DIR}/GTPV1-U")
-include_directories("${OPENAIR_DIR}/targets/COMMON")
 include_directories("${OPENAIR_DIR}/targets/ARCH/COMMON")
-include_directories("${OPENAIR_DIR}/targets/ARCH/EXMIMO/USERSPACE/LIB/")
-include_directories("${OPENAIR_DIR}/targets/ARCH/EXMIMO/DEFS")
 include_directories("${OPENAIR2_DIR}/ENB_APP")
 include_directories("${OPENAIR2_DIR}/GNB_APP")
 include_directories("${OPENAIR2_DIR}/ENB_APP/CONTROL_MODULES/MAC")
@@ -2107,7 +2105,7 @@ if (${XFORMS})
     ${OPENAIR1_DIR}/PHY/TOOLS/nr_phy_scope.c
     )
   set(XFORMS_SOURCE_SOFTMODEM
-    ${OPENAIR_TARGETS}/RT/USER/stats.c
+	  ${OPENAIR_DIR}/executables/stats.c
     )
   set(XFORMS_LIBRARIES "forms")
 endif (${XFORMS})
@@ -2335,12 +2333,10 @@ add_executable(nr-softmodem
   ${nr_rrc_h}
   ${s1ap_h}
 #  ${OPENAIR_BIN_DIR}/messages_xml.h
-  ${OPENAIR_TARGETS}/RT/USER/rt_wrapper.c
-  ${OPENAIR_TARGETS}/RT/USER/nr-gnb.c
-  ${OPENAIR_TARGETS}/RT/USER/nr-ru.c
-  ${OPENAIR_TARGETS}/RT/USER/nr-softmodem.c
+  ${OPENAIR_DIR}/executables/nr-gnb.c
+  ${OPENAIR_DIR}/executables/nr-ru.c
+  ${OPENAIR_DIR}/executables/nr-softmodem.c
   ${OPENAIR1_DIR}/SIMULATION/TOOLS/taus.c
-  ${OPENAIR_TARGETS}/COMMON/create_nr_tasks.c
   ${OPENAIR_TARGETS}/ARCH/COMMON/common_lib.c
   ${OPENAIR1_DIR}/SIMULATION/ETH_TRANSPORT/netlink_init.c
   ${OPENAIR_DIR}/common/utils/utils.c
@@ -2373,13 +2369,10 @@ add_executable(nr-softmodem-nos1
   ${rrc_h}
   ${s1ap_h}
 #  ${OPENAIR_BIN_DIR}/messages_xml.h
-  ${OPENAIR_TARGETS}/RT/USER/rt_wrapper.c
-  ${OPENAIR_TARGETS}/RT/USER/nr-gnb.c
-  ${OPENAIR_TARGETS}/RT/USER/nr-ru.c
-  ${OPENAIR_TARGETS}/RT/USER/nr-softmodem.c
+  ${OPENAIR_DIR}/executables/nr-gnb.c
+  ${OPENAIR_DIR}/executables/nr-ru.c
+  ${OPENAIR_DIR}/executables/nr-softmodem.c
   ${OPENAIR1_DIR}/SIMULATION/TOOLS/taus.c
-  ${OPENAIR_TARGETS}/COMMON/create_tasks.c
-  ${OPENAIR_TARGETS}/COMMON/create_nr_tasks.c
   ${OPENAIR_TARGETS}/ARCH/COMMON/common_lib.c
   ${OPENAIR2_DIR}/RRC/NAS/nas_config.c
   ${OPENAIR2_DIR}/RRC/NAS/rb_config.c
@@ -2448,11 +2441,9 @@ add_executable(nr-uesoftmodem-nos1
   ${rrc_h}
   ${s1ap_h}
 #  ${OPENAIR_BIN_DIR}/messages_xml.h
-  ${OPENAIR_TARGETS}/RT/USER/rt_wrapper.c
   ${OPENAIR_DIR}/executables/nr-ue.c
   ${OPENAIR_DIR}/executables/nr-uesoftmodem.c
   ${OPENAIR1_DIR}/SIMULATION/TOOLS/taus.c
-  #${OPENAIR_TARGETS}/COMMON/create_tasks_ue.c
   ${OPENAIR_TARGETS}/ARCH/COMMON/common_lib.c
   ${OPENAIR1_DIR}/SIMULATION/ETH_TRANSPORT/netlink_init.c
   ${OPENAIR_DIR}/common/utils/utils.c
diff --git a/common/utils/ocp_itti/intertask_interface.cpp b/common/utils/ocp_itti/intertask_interface.cpp
index 5b8d1a430e..e896413b68 100644
--- a/common/utils/ocp_itti/intertask_interface.cpp
+++ b/common/utils/ocp_itti/intertask_interface.cpp
@@ -8,6 +8,7 @@
 
 
 #include <intertask_interface.h>
+#include <common/utils/system.h>
 
 typedef struct timer_elm_s {
   timer_type_t      type;     ///< Timer type
@@ -280,22 +281,8 @@ extern "C" {
 
   int itti_create_task(task_id_t task_id, void *(*start_routine)(void *), void *args_p) {
     task_list_t *t=&tasks[task_id];
-    AssertFatal ( pthread_create (&t->thread, NULL, start_routine, args_p) ==0,
-                  "Thread creation for task %d failed!\n", task_id);
-    pthread_setname_np( t->thread, itti_get_task_name(task_id) );
+    threadCreate (&t->thread, start_routine, args_p, itti_get_task_name(task_id),-1,OAI_PRIORITY_RT);
     LOG_I(TMR,"Created Posix thread %s\n",  itti_get_task_name(task_id) );
-#if 1 // BMC test RT prio
-    {
-      int policy;
-      struct sched_param sparam;
-      memset(&sparam, 0, sizeof(sparam));
-      sparam.sched_priority = sched_get_priority_max(SCHED_FIFO)-10;
-      policy = SCHED_FIFO ; 
-      if (pthread_setschedparam(t->thread, policy, &sparam) != 0) {
-	LOG_E(TMR,"task %s : Failed to set pthread priority\n",  itti_get_task_name(task_id) );
-      }
-    }
-#endif    
     return 0;
   }
 
diff --git a/common/utils/system.c b/common/utils/system.c
index dcec681e2d..90e5185a67 100644
--- a/common/utils/system.c
+++ b/common/utils/system.c
@@ -28,13 +28,23 @@
  * separate process solves this problem.
  */
 
+#define _GNU_SOURCE
 #include "system.h"
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <pthread.h>
 #include <string.h>
-
+#include <stdint.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <pthread.h>
+#include <common/utils/assertions.h>
+#include <common/utils/LOG/log.h>
 #define MAX_COMMAND 4096
 
 static int command_pipe_read;
@@ -50,37 +60,37 @@ static int module_initialized = 0;
 /* util functions                                                   */
 /********************************************************************/
 
-static void lock_system(void)
-{
+static void lock_system(void) {
   if (pthread_mutex_lock(&lock) != 0) {
     printf("pthread_mutex_lock fails\n");
     abort();
   }
 }
 
-static void unlock_system(void)
-{
+static void unlock_system(void) {
   if (pthread_mutex_unlock(&lock) != 0) {
     printf("pthread_mutex_unlock fails\n");
     abort();
   }
 }
 
-static void write_pipe(int p, char *b, int size)
-{
+static void write_pipe(int p, char *b, int size) {
   while (size) {
     int ret = write(p, b, size);
+
     if (ret <= 0) exit(0);
+
     b += ret;
     size -= ret;
   }
 }
 
-static void read_pipe(int p, char *b, int size)
-{
+static void read_pipe(int p, char *b, int size) {
   while (size) {
     int ret = read(p, b, size);
+
     if (ret <= 0) exit(0);
+
     b += ret;
     size -= ret;
   }
@@ -95,14 +105,13 @@ static void read_pipe(int p, char *b, int size)
  * when the main process exits, because then a "read" on the pipe
  * will return 0, in which case "read_pipe" exits.
  */
-static void background_system_process(void)
-{
+static void background_system_process(void) {
   int len;
   int ret;
   char command[MAX_COMMAND+1];
 
   while (1) {
-    read_pipe(command_pipe_read, (char*)&len, sizeof(int));
+    read_pipe(command_pipe_read, (char *)&len, sizeof(int));
     read_pipe(command_pipe_read, command, len);
     ret = system(command);
     write_pipe(result_pipe_write, (char *)&ret, sizeof(int));
@@ -114,8 +123,7 @@ static void background_system_process(void)
 /*     return -1 on error, 0 on success                             */
 /********************************************************************/
 
-int background_system(char *command)
-{
+int background_system(char *command) {
   int res;
   int len;
 
@@ -125,18 +133,22 @@ int background_system(char *command)
   }
 
   len = strlen(command)+1;
+
   if (len > MAX_COMMAND) {
     printf("FATAL: command too long. Increase MAX_COMMAND (%d).\n", MAX_COMMAND);
     printf("command was: '%s'\n", command);
     abort();
   }
+
   /* only one command can run at a time, so let's lock/unlock */
   lock_system();
-  write_pipe(command_pipe_write, (char*)&len, sizeof(int));
+  write_pipe(command_pipe_write, (char *)&len, sizeof(int));
   write_pipe(command_pipe_write, command, len);
-  read_pipe(result_pipe_read, (char*)&res, sizeof(int));
+  read_pipe(result_pipe_read, (char *)&res, sizeof(int));
   unlock_system();
+
   if (res == -1 || !WIFEXITED(res) || WEXITSTATUS(res) != 0) return -1;
+
   return 0;
 }
 
@@ -146,17 +158,16 @@ int background_system(char *command)
 /*     to be called very early by the main processing               */
 /********************************************************************/
 
-void start_background_system(void)
-{
+void start_background_system(void) {
   int p[2];
   pid_t son;
-
   module_initialized = 1;
 
   if (pipe(p) == -1) {
     perror("pipe");
     exit(1);
   }
+
   command_pipe_read  = p[0];
   command_pipe_write = p[1];
 
@@ -164,10 +175,11 @@ void start_background_system(void)
     perror("pipe");
     exit(1);
   }
+
   result_pipe_read  = p[0];
   result_pipe_write = p[1];
-
   son = fork();
+
   if (son == -1) {
     perror("fork");
     exit(1);
@@ -181,6 +193,88 @@ void start_background_system(void)
 
   close(result_pipe_read);
   close(command_pipe_write);
-
   background_system_process();
 }
+
+void thread_top_init(char *thread_name,
+                     int affinity,
+                     uint64_t runtime,
+                     uint64_t deadline,
+                     uint64_t period) {
+#ifdef DEADLINE_SCHEDULER
+  struct sched_attr attr;
+  unsigned int flags = 0;
+  attr.size = sizeof(attr);
+  attr.sched_flags = 0;
+  attr.sched_nice = 0;
+  attr.sched_priority = 0;
+  attr.sched_policy   = SCHED_DEADLINE;
+  attr.sched_runtime  = runtime;
+  attr.sched_deadline = deadline;
+  attr.sched_period   = period;
+  
+  AssertFatal(sched_setattr(0, &attr, flags) == 0, "[SCHED] eNB tx thread: sched_setattr failed\n");
+	      
+#else
+#ifdef CPU_AFFINITY
+  /* Set affinity mask to include CPUs 2 to MAX_CPUS */
+  /* CPU 0 is reserved for UHD threads */  /* CPU 1 is reserved for all RX_TX threads */
+  /* Enable CPU Affinity only if number of CPUs > 2 */
+  cpu_set_t cpuset;
+  CPU_ZERO(&cpuset);
+  if (affinity == 0) {
+    LOG_W(HW,"thread_top_init() called with affinity==0, but overruled by #ifdef CPU_AFFINITY\n");
+  } else if (get_nprocs() > 2) {
+    for (j = 2; j < get_nprocs(); j++)
+      CPU_SET(j, &cpuset);
+    AssertFatal( pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset) == 0, "Error setting processor affinity");
+  }
+#endif //CPU_AFFINITY
+  
+  struct sched_param sparam={0};
+  sparam.sched_priority = OAI_PRIORITY_RT;
+  AssertFatal(pthread_setschedparam(pthread_self(),SCHED_FIFO , &sparam) == 0,"Error setting thread priority");
+  pthread_setname_np(pthread_self(), thread_name);
+#endif 
+  mlockall(MCL_CURRENT | MCL_FUTURE);
+}
+
+void threadTopInit(const char* name, const int affinity, const int priority){
+struct sched_param sparam={0};
+  sparam.sched_priority = priority;
+  AssertFatal(pthread_setschedparam(pthread_self(),SCHED_FIFO , &sparam) == 0,"Error setting thread priority");
+  pthread_setname_np(pthread_self(), name);
+  if (affinity != -1 ) {
+    cpu_set_t cpuset;
+    CPU_ZERO(&cpuset);
+    CPU_SET(affinity, &cpuset);
+    AssertFatal( pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset) == 0, "Error setting processor affinity");
+  }
+}
+
+// Block CPU C-states deep sleep
+void configure_linux(void) {
+  int ret;
+  static int latency_target_fd=-1;
+  uint32_t latency_target_value=10; // in microseconds
+  if (latency_target_fd == -1) {
+    if ( (latency_target_fd = open("/dev/cpu_dma_latency", O_RDWR)) != -1 ) {
+      ret = write(latency_target_fd, &latency_target_value, sizeof(latency_target_value));
+      if (ret == 0) {
+	printf("# error setting cpu_dma_latency to %d!: %s\n", latency_target_value, strerror(errno));
+	close(latency_target_fd);
+	latency_target_fd=-1;
+	return;
+      }
+    }
+  }
+  if (latency_target_fd != -1) 
+    LOG_I(HW,"# /dev/cpu_dma_latency set to %dus\n", latency_target_value);
+  else
+    LOG_E(HW,"Can't set /dev/cpu_dma_latency to %dus\n", latency_target_value);
+
+  // 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");
+  
+}
diff --git a/common/utils/system.h b/common/utils/system.h
index 658c17adc1..b66d4031c9 100644
--- a/common/utils/system.h
+++ b/common/utils/system.h
@@ -21,6 +21,11 @@
 
 #ifndef _SYSTEM_H_OAI_
 #define _SYSTEM_H_OAI_
+#include <stdint.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 
 /****************************************************
  * send a command to the background process
@@ -36,4 +41,23 @@ int background_system(char *command);
 
 void start_background_system(void);
 
+void set_latency_target(void);
+void configure_linux(void);
+
+void threadCreate(pthread_t* t, void * (*func)(void*), void * param, char* name, int affinity, int priority);
+#define OAI_PRIORITY_RT_LOW sched_get_priority_min(SCHED_FIFO)
+#define OAI_PRIORITY_RT sched_get_priority_max(SCHED_FIFO)-10
+#define OAI_PRIORITY_RT_MAX sched_get_priority_max(SCHED_FIFO)
+
+void thread_top_init(char *thread_name,
+                     int affinity,
+                     uint64_t runtime,
+                     uint64_t deadline,
+                     uint64_t period);
+
+#ifdef __cplusplus
+}
+#endif
+
+
 #endif /* _SYSTEM_H_OAI_ */
diff --git a/common/utils/threadPool/thread-pool.c b/common/utils/threadPool/thread-pool.c
index 8eee0c2b8a..a17495c91f 100644
--- a/common/utils/threadPool/thread-pool.c
+++ b/common/utils/threadPool/thread-pool.c
@@ -49,23 +49,6 @@ void *one_thread(void *arg) {
   struct  one_thread *myThread=(struct  one_thread *) arg;
   struct  thread_pool *tp=myThread->pool;
 
-  // configure the thread core assignment
-  // TBD: reserve the core for us exclusively
-  if ( myThread->coreID >= 0 &&  myThread->coreID < get_nprocs_conf()) {
-    cpu_set_t cpuset;
-    CPU_ZERO(&cpuset);
-    CPU_SET(myThread->coreID, &cpuset);
-    pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
-  }
-
-  //Configure the thread scheduler policy for Linux
-  struct sched_param sparam= {0};
-  sparam.sched_priority = sched_get_priority_max(SCHED_RR);
-  pthread_setschedparam(pthread_self(), SCHED_RR, &sparam);
-  // set the thread name for debugging
-  sprintf(myThread->name,"Tpool_%d",myThread->coreID);
-  pthread_setname_np(pthread_self(), myThread->name );
-
   // Infinite loop to process requests
   do {
     notifiedFIFO_elt_t *elt=pullNotifiedFifoRemember(&tp->incomingFifo, myThread);
@@ -106,10 +89,6 @@ void initTpool(char *params,tpool_t *pool, bool performanceMeas) {
   } else
     pool->traceFd=-1;
 
-  //Configure the thread scheduler policy for Linux
-  struct sched_param sparam= {0};
-  sparam.sched_priority = sched_get_priority_max(SCHED_RR)-1;
-  pthread_setschedparam(pthread_self(), SCHED_RR, &sparam);
   pool->activated=true;
   initNotifiedFIFO(&pool->incomingFifo);
   char *saveptr, * curptr;
@@ -136,7 +115,10 @@ void initTpool(char *params,tpool_t *pool, bool performanceMeas) {
         pool->allthreads->coreID=atoi(curptr);
         pool->allthreads->id=pool->nbThreads;
         pool->allthreads->pool=pool;
-        pthread_create(&pool->allthreads->threadID, NULL, one_thread, (void *)pool->allthreads);
+        //Configure the thread scheduler policy for Linux
+        // set the thread name for debugging
+        sprintf(myThread->name,"Tpool_%d",myThread->coreID);
+        threadCreate(&pool->allthreads->threadID, one_thread, (void *)pool->allthreads, Tpool, myThread->name, myThread->coreID, OAI_PRIORITY_RT);
         pool->nbThreads++;
     }
 
diff --git a/common/utils/threadPool/thread-pool.h b/common/utils/threadPool/thread-pool.h
index 6f0230568a..2e1e504ed7 100644
--- a/common/utils/threadPool/thread-pool.h
+++ b/common/utils/threadPool/thread-pool.h
@@ -11,6 +11,7 @@
 #include <sys/syscall.h>
 #include <assertions.h>
 #include <LOG/log.h>
+#include <common/utils/system.h>
 
 #ifdef DEBUG
   #define THREADINIT   PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
diff --git a/executables/nr-gnb.c b/executables/nr-gnb.c
new file mode 100644
index 0000000000..8b4822ba56
--- /dev/null
+++ b/executables/nr-gnb.c
@@ -0,0 +1,952 @@
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.1  (the "License"); you may not use this file
+ * except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */
+
+/*! \file lte-enb.c
+ * \brief Top-level threads for gNodeB
+ * \author R. Knopp, F. Kaltenberger, Navid Nikaein
+ * \date 2012
+ * \version 0.1
+ * \company Eurecom
+ * \email: knopp@eurecom.fr,florian.kaltenberger@eurecom.fr, navid.nikaein@eurecom.fr
+ * \note
+ * \warning
+ */
+
+#define _GNU_SOURCE
+#include <pthread.h>
+
+#include "time_utils.h"
+
+#undef MALLOC //there are two conflicting definitions, so we better make sure we don't use it at all
+
+#include "assertions.h"
+
+#include "PHY/types.h"
+
+#include "PHY/INIT/phy_init.h"
+
+#include "PHY/defs_gNB.h"
+#include "SCHED/sched_eNB.h"
+#include "SCHED_NR/sched_nr.h"
+#include "SCHED_NR/fapi_nr_l1.h"
+#include "PHY/LTE_TRANSPORT/transport_proto.h"
+
+#undef MALLOC //there are two conflicting definitions, so we better make sure we don't use it at all
+//#undef FRAME_LENGTH_COMPLEX_SAMPLES //there are two conflicting definitions, so we better make sure we don't use it at all
+
+#include "../../ARCH/COMMON/common_lib.h"
+
+//#undef FRAME_LENGTH_COMPLEX_SAMPLES //there are two conflicting definitions, so we better make sure we don't use it at all
+
+#include "PHY/LTE_TRANSPORT/if4_tools.h"
+#include "PHY/LTE_TRANSPORT/if5_tools.h"
+
+#include "PHY/phy_extern.h"
+
+
+#include "LAYER2/MAC/mac.h"
+#include "LAYER2/MAC/mac_extern.h"
+#include "LAYER2/MAC/mac_proto.h"
+#include "RRC/LTE/rrc_extern.h"
+#include "PHY_INTERFACE/phy_interface.h"
+#include "common/utils/LOG/log_extern.h"
+#include "UTIL/OTG/otg_tx.h"
+#include "UTIL/OTG/otg_externs.h"
+#include "UTIL/MATH/oml.h"
+#include "common/utils/LOG/vcd_signal_dumper.h"
+#include "UTIL/OPT/opt.h"
+#include "enb_config.h"
+
+
+#ifndef OPENAIR2
+  #include "UTIL/OTG/otg_extern.h"
+#endif
+
+#if defined(ENABLE_ITTI)
+  #if defined(ENABLE_USE_MME)
+    #include "s1ap_eNB.h"
+    #ifdef PDCP_USE_NETLINK
+      #include "SIMULATION/ETH_TRANSPORT/proto.h"
+    #endif
+  #endif
+#endif
+
+#include "T.h"
+
+//#define DEBUG_THREADS 1
+
+//#define USRP_DEBUG 1
+// Fix per CC openair rf/if device update
+// extern openair0_device openair0;
+
+
+#if defined(ENABLE_ITTI)
+  extern volatile int             start_gNB;
+  extern volatile int             start_UE;
+#endif
+extern volatile int                    oai_exit;
+
+extern openair0_config_t openair0_cfg[MAX_CARDS];
+
+extern int transmission_mode;
+
+uint16_t sl_ahead=4;
+uint16_t sf_ahead=4;
+//pthread_t                       main_gNB_thread;
+
+time_stats_t softmodem_stats_mt; // main thread
+time_stats_t softmodem_stats_hw; //  hw acquisition
+time_stats_t softmodem_stats_rxtx_sf; // total tx time
+time_stats_t nfapi_meas; // total tx time
+time_stats_t softmodem_stats_rx_sf; // total rx time
+
+/* mutex, cond and variable to serialize phy proc TX calls
+ * (this mechanism may be relaxed in the future for better
+ * performances)
+ */
+static struct {
+  pthread_mutex_t  mutex_phy_proc_tx;
+  pthread_cond_t   cond_phy_proc_tx;
+  volatile uint8_t phy_proc_CC_id;
+} sync_phy_proc;
+
+extern double cpuf;
+
+void init_gNB(int,int);
+void stop_gNB(int nb_inst);
+
+int wakeup_txfh(gNB_L1_rxtx_proc_t *proc,PHY_VARS_gNB *gNB);
+int wakeup_tx(PHY_VARS_gNB *gNB);
+extern PARALLEL_CONF_t get_thread_parallel_conf(void);
+extern WORKER_CONF_t   get_thread_worker_conf(void);
+
+
+void wakeup_prach_gNB(PHY_VARS_gNB *gNB,RU_t *ru,int frame,int subframe);
+
+extern uint8_t nfapi_mode;
+extern void oai_subframe_ind(uint16_t sfn, uint16_t sf);
+extern void add_subframe(uint16_t *frameP, uint16_t *subframeP, int offset);
+
+//#define TICK_TO_US(ts) (ts.diff)
+#define TICK_TO_US(ts) (ts.trials==0?0:ts.diff/ts.trials)
+
+
+static inline int rxtx(PHY_VARS_gNB *gNB,gNB_L1_rxtx_proc_t *proc, char *thread_name) {
+  start_meas(&softmodem_stats_rxtx_sf);
+
+  // *******************************************************************
+
+  if (nfapi_mode == 1) {
+    // I am a PNF and I need to let nFAPI know that we have a (sub)frame tick
+    uint16_t frame = proc->frame_rx;
+    uint16_t slot  = proc->slot_rx;
+    //add_subframe(&frame, &subframe, 4);
+    //oai_subframe_ind(proc->frame_tx, proc->subframe_tx);
+    //LOG_D(PHY, "oai_subframe_ind(frame:%u, subframe:%d) - NOT CALLED ********\n", frame, subframe);
+    start_meas(&nfapi_meas);
+    oai_subframe_ind(frame, slot);
+    stop_meas(&nfapi_meas);
+
+    if (gNB->UL_INFO.rx_ind.rx_indication_body.number_of_pdus||
+        gNB->UL_INFO.harq_ind.harq_indication_body.number_of_harqs ||
+        gNB->UL_INFO.crc_ind.crc_indication_body.number_of_crcs ||
+        gNB->UL_INFO.rach_ind.rach_indication_body.number_of_preambles ||
+        gNB->UL_INFO.cqi_ind.number_of_cqis
+       ) {
+      LOG_D(PHY, "UL_info[rx_ind:%05d:%d harqs:%05d:%d crcs:%05d:%d preambles:%05d:%d cqis:%d] RX:%04d%d TX:%04d%d \n",
+            NFAPI_SFNSF2DEC(gNB->UL_INFO.rx_ind.sfn_sf),   gNB->UL_INFO.rx_ind.rx_indication_body.number_of_pdus,
+            NFAPI_SFNSF2DEC(gNB->UL_INFO.harq_ind.sfn_sf), gNB->UL_INFO.harq_ind.harq_indication_body.number_of_harqs,
+            NFAPI_SFNSF2DEC(gNB->UL_INFO.crc_ind.sfn_sf),  gNB->UL_INFO.crc_ind.crc_indication_body.number_of_crcs,
+            NFAPI_SFNSF2DEC(gNB->UL_INFO.rach_ind.sfn_sf), gNB->UL_INFO.rach_ind.rach_indication_body.number_of_preambles,
+            gNB->UL_INFO.cqi_ind.number_of_cqis,
+            proc->frame_rx, proc->slot_rx,
+            proc->frame_tx, proc->slot_tx);
+    }
+  }
+
+  /// NR disabling
+  // ****************************************
+  // Common RX procedures subframe n
+  T(T_GNB_PHY_DL_TICK, T_INT(gNB->Mod_id), T_INT(proc->frame_tx), T_INT(proc->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);
+    }
+
+    // UE-specific RX processing for subframe n
+    if (nfapi_mode == 0 || nfapi_mode == 1) {
+      phy_procedures_gNB_uespec_RX(gNB, proc, no_relay );
+    }
+  */
+  pthread_mutex_lock(&gNB->UL_INFO_mutex);
+  gNB->UL_INFO.frame     = proc->frame_rx;
+  gNB->UL_INFO.slot      = proc->slot_rx;
+  gNB->UL_INFO.module_id = gNB->Mod_id;
+  gNB->UL_INFO.CC_id     = gNB->CC_id;
+  gNB->if_inst->NR_UL_indication(&gNB->UL_INFO);
+  pthread_mutex_unlock(&gNB->UL_INFO_mutex);
+
+  /// end
+  // *****************************************
+  // TX processing for subframe n+sl_ahead
+  // run PHY TX procedures the one after the other for all CCs to avoid race conditions
+  // (may be relaxed in the future for performance reasons)
+  // *****************************************
+  //if (wait_CCs(proc)<0) return(-1);
+
+  if (oai_exit) return(-1);
+
+  if(get_thread_parallel_conf() == PARALLEL_SINGLE_THREAD) {
+    phy_procedures_gNB_TX(gNB, proc, 1);
+  }
+
+  stop_meas( &softmodem_stats_rxtx_sf );
+  LOG_D(PHY,"%s() Exit proc[rx:%d%d tx:%d%d]\n", __FUNCTION__, proc->frame_rx, proc->slot_rx, proc->frame_tx, proc->slot_tx);
+#if 0
+  LOG_D(PHY, "rxtx:%lld nfapi:%lld phy:%lld tx:%lld rx:%lld prach:%lld ofdm:%lld ",
+        softmodem_stats_rxtx_sf.diff_now, nfapi_meas.diff_now,
+        TICK_TO_US(gNB->phy_proc),
+        TICK_TO_US(gNB->phy_proc_tx),
+        TICK_TO_US(gNB->phy_proc_rx),
+        TICK_TO_US(gNB->rx_prach),
+        TICK_TO_US(gNB->ofdm_mod_stats),
+        softmodem_stats_rxtx_sf.diff_now, nfapi_meas.diff_now);
+  LOG_D(PHY,
+        "dlsch[enc:%lld mod:%lld scr:%lld rm:%lld t:%lld i:%lld] rx_dft:%lld ",
+        TICK_TO_US(gNB->dlsch_encoding_stats),
+        TICK_TO_US(gNB->dlsch_modulation_stats),
+        TICK_TO_US(gNB->dlsch_scrambling_stats),
+        TICK_TO_US(gNB->dlsch_rate_matching_stats),
+        TICK_TO_US(gNB->dlsch_turbo_encoding_stats),
+        TICK_TO_US(gNB->dlsch_interleaving_stats),
+        TICK_TO_US(gNB->rx_dft_stats));
+  LOG_D(PHY," ulsch[ch:%lld freq:%lld dec:%lld demod:%lld ru:%lld ",
+        TICK_TO_US(gNB->ulsch_channel_estimation_stats),
+        TICK_TO_US(gNB->ulsch_freq_offset_estimation_stats),
+        TICK_TO_US(gNB->ulsch_decoding_stats),
+        TICK_TO_US(gNB->ulsch_demodulation_stats),
+        TICK_TO_US(gNB->ulsch_rate_unmatching_stats));
+  LOG_D(PHY, "td:%lld dei:%lld dem:%lld llr:%lld tci:%lld ",
+        TICK_TO_US(gNB->ulsch_turbo_decoding_stats),
+        TICK_TO_US(gNB->ulsch_deinterleaving_stats),
+        TICK_TO_US(gNB->ulsch_demultiplexing_stats),
+        TICK_TO_US(gNB->ulsch_llr_stats),
+        TICK_TO_US(gNB->ulsch_tc_init_stats));
+  LOG_D(PHY, "tca:%lld tcb:%lld tcg:%lld tce:%lld l1:%lld l2:%lld]\n\n",
+        TICK_TO_US(gNB->ulsch_tc_alpha_stats),
+        TICK_TO_US(gNB->ulsch_tc_beta_stats),
+        TICK_TO_US(gNB->ulsch_tc_gamma_stats),
+        TICK_TO_US(gNB->ulsch_tc_ext_stats),
+        TICK_TO_US(gNB->ulsch_tc_intl1_stats),
+        TICK_TO_US(gNB->ulsch_tc_intl2_stats)
+       );
+#endif
+  return(0);
+}
+
+static void *gNB_L1_thread_tx(void *param) {
+  PHY_VARS_gNB *gNB        = (PHY_VARS_gNB *)param;
+  gNB_L1_proc_t *gNB_proc  = &gNB->proc;
+  gNB_L1_rxtx_proc_t *proc = &gNB_proc->L1_proc_tx;
+  //PHY_VARS_gNB *gNB = RC.gNB[0][proc->CC_id];
+  char thread_name[100];
+  sprintf(thread_name,"TXnp4_%d\n",&gNB->proc.L1_proc == proc ? 0 : 1);
+  //thread_top_init(thread_name,1,470000,500000,500000);
+
+  //wait_sync("tx_thread");
+
+  while (!oai_exit) {
+    VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_gNB_PROC_RXTX1, 0 );
+
+    if (wait_on_condition(&proc->mutex,&proc->cond,&proc->instance_cnt,thread_name)<0) break;
+
+    VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_gNB_PROC_RXTX1, 1 );
+
+    if (oai_exit) break;
+
+    // *****************************************
+    // TX processing for subframe n+4
+    // run PHY TX procedures the one after the other for all CCs to avoid race conditions
+    // (may be relaxed in the future for performance reasons)
+    // *****************************************
+    VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_SLOT_NUMBER_TX1_GNB,proc->slot_tx);
+    VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_SLOT_NUMBER_RX1_GNB,proc->slot_rx);
+    VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_TX1_GNB,proc->frame_tx);
+    VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_RX1_GNB,proc->frame_rx);
+    phy_procedures_gNB_TX(gNB, proc, 1);
+    pthread_mutex_lock( &proc->mutex );
+    proc->instance_cnt = -1;
+
+    // the thread can now be woken up
+    if (pthread_cond_signal(&proc->cond) != 0) {
+      LOG_E( PHY, "[gNB] ERROR pthread_cond_signal for gNB TXnp4 thread\n");
+      exit_fun( "ERROR pthread_cond_signal" );
+    }
+
+    pthread_mutex_unlock( &proc->mutex );
+    wakeup_txfh(proc,gNB);
+  }
+
+  return 0;
+}
+
+/*!
+ * \brief The RX UE-specific and TX thread of gNB.
+ * \param param is a \ref gNB_L1_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 *gNB_L1_thread( void *param ) {
+  static int gNB_thread_rxtx_status;
+  PHY_VARS_gNB *gNB        = (PHY_VARS_gNB *)param;
+  gNB_L1_proc_t *gNB_proc  = &gNB->proc;
+  gNB_L1_rxtx_proc_t *proc = &gNB_proc->L1_proc;
+  //PHY_VARS_gNB *gNB = RC.gNB[0][proc->CC_id];
+  char thread_name[100];
+  // set default return value
+  gNB_thread_rxtx_status = 0;
+  sprintf(thread_name,"RXn_TXnp4_%d",&gNB->proc.L1_proc == proc ? 0 : 1);
+  //thread_top_init(thread_name,1,850000L,1000000L,2000000L);
+
+  while (!oai_exit) {
+    VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_gNB_PROC_RXTX0, 0 );
+
+    if (wait_on_condition(&proc->mutex,&proc->cond,&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_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_SLOT_NUMBER_TX0_GNB,proc->slot_tx);
+    VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_SLOT_NUMBER_RX0_GNB,proc->slot_rx);
+    VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_TX0_GNB,proc->frame_tx);
+    VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_RX0_GNB,proc->frame_rx);
+
+    if (oai_exit) break;
+
+    if (gNB->CC_id==0) {
+      if (rxtx(gNB,proc,thread_name) < 0) break;
+    }
+
+    if(get_thread_parallel_conf() == PARALLEL_RU_L1_SPLIT) {
+      phy_procedures_gNB_TX(gNB, proc, 1);
+    }
+
+    if (release_thread(&proc->mutex,&proc->instance_cnt,thread_name)<0) break;
+
+    if(get_thread_parallel_conf() == PARALLEL_RU_L1_TRX_SPLIT) {
+      wakeup_tx(gNB);
+    } else if(get_thread_parallel_conf() == PARALLEL_RU_L1_SPLIT) {
+      wakeup_txfh(proc,gNB);
+    }
+  } // while !oai_exit
+
+  LOG_D(PHY, " *** Exiting gNB thread RXn_TXnp4\n");
+  gNB_thread_rxtx_status = 0;
+  return &gNB_thread_rxtx_status;
+}
+
+
+#if 0 //defined(ENABLE_ITTI) && defined(ENABLE_USE_MME)
+// Wait for gNB application initialization to be complete (gNB 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)) {
+    LOG_N(EMU, message, indicator[i]);
+    fflush(stdout);
+    i = (i + 1) % (sizeof(indicator) / sizeof(indicator[0]));
+    usleep(200000);
+  }
+
+  LOG_D(EMU,"\n");
+}
+#endif
+
+
+
+
+
+void gNB_top(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, char *string, struct RU_t_s *ru) {
+  gNB_L1_proc_t *proc           = &gNB->proc;
+  gNB_L1_rxtx_proc_t *L1_proc = &proc->L1_proc;
+  NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
+  RU_proc_t *ru_proc=&ru->proc;
+  proc->frame_rx    = frame_rx;
+  proc->slot_rx = slot_rx;
+
+  if (!oai_exit) {
+    T(T_ENB_MASTER_TICK, T_INT(0), T_INT(proc->frame_rx), T_INT(proc->slot_rx));
+    L1_proc->timestamp_tx = ru_proc->timestamp_rx + (sl_ahead*fp->samples_per_slot);
+    L1_proc->frame_rx     = ru_proc->frame_rx;
+    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;
+
+    if (rxtx(gNB,L1_proc,string) < 0) LOG_E(PHY,"gNB %d CC_id %d failed during execution\n",gNB->Mod_id,gNB->CC_id);
+
+    ru_proc->timestamp_tx = L1_proc->timestamp_tx;
+    ru_proc->tti_tx       = L1_proc->slot_tx;
+    ru_proc->frame_tx     = L1_proc->frame_tx;
+  }
+}
+
+int wakeup_txfh(gNB_L1_rxtx_proc_t *proc,PHY_VARS_gNB *gNB) {
+  RU_t *ru;
+  RU_proc_t *ru_proc;
+  struct timespec wait;
+  wait.tv_sec=0;
+  wait.tv_nsec=5000000L;
+  //printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~inside wakeup_txfh %d.%d IC_RU = %d\n", proc->frame_tx, proc->slot_tx, proc->instance_cnt_RUs);
+
+  if(wait_on_condition(&proc->mutex_RUs,&proc->cond_RUs,&proc->instance_cnt_RUs,"wakeup_txfh")<0) {
+    LOG_E(PHY,"Frame %d, subframe %d: TX FH not ready\n", proc->frame_tx, proc->slot_tx);
+    return(-1);
+  }
+
+  pthread_mutex_lock(&gNB->proc.mutex_RU_tx);
+  gNB->proc.RU_mask_tx = 0;
+  pthread_mutex_unlock(&gNB->proc.mutex_RU_tx);
+
+  if (release_thread(&proc->mutex_RUs,&proc->instance_cnt_RUs,"wakeup_txfh")<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) {
+      LOG_E(PHY,"Frame %d, subframe %d: TX FH thread busy, dropping Frame %d, subframe %d\n", ru_proc->frame_tx, ru_proc->tti_tx, proc->frame_rx, proc->slot_rx);
+      return(-1);
+    }
+
+    if (pthread_mutex_timedlock(&ru_proc->mutex_gNBs,&wait) != 0) {
+      LOG_E( PHY, "[eNB] ERROR pthread_mutex_lock for eNB TX1 thread %d (IC %d)\n", ru_proc->tti_rx&1,ru_proc->instance_cnt_gNBs );
+      exit_fun( "error locking mutex_gNB" );
+      return(-1);
+    }
+
+    ru_proc->instance_cnt_gNBs = 0;
+    ru_proc->timestamp_tx = proc->timestamp_tx;
+    ru_proc->tti_tx       = proc->slot_tx;
+    ru_proc->frame_tx     = proc->frame_tx;
+    LOG_I(PHY,"Signaling tx_thread_fh for %d.%d\n",ru_proc->frame_tx,ru_proc->tti_tx);
+
+    // the thread can now be woken up
+    if (pthread_cond_signal(&ru_proc->cond_gNBs) != 0) {
+      LOG_E( PHY, "[gNB] ERROR pthread_cond_signal for gNB TXnp4 thread\n");
+      exit_fun( "ERROR pthread_cond_signal" );
+      return(-1);
+    }
+
+    pthread_mutex_unlock( &ru_proc->mutex_gNBs );
+  }
+
+  return(0);
+}
+
+int wakeup_tx(PHY_VARS_gNB *gNB) {
+  gNB_L1_proc_t *proc=&gNB->proc;
+  gNB_L1_rxtx_proc_t *L1_proc_tx = &proc->L1_proc_tx;
+  gNB_L1_rxtx_proc_t *L1_proc    = &proc->L1_proc;
+  struct timespec wait;
+  wait.tv_sec=0;
+  wait.tv_nsec=5000000L;
+
+  if (pthread_mutex_timedlock(&L1_proc_tx->mutex,&wait) != 0) {
+    LOG_E(PHY, "[SCHED][eNB] ERROR locking mutex for eNB L1_thread_tx\n");
+    exit_fun("ERROR pthread_lock");
+    return(-1);
+  }
+
+  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   = L1_proc->slot_rx;
+  L1_proc_tx->frame_rx      = L1_proc->frame_rx;
+  L1_proc_tx->slot_tx   = L1_proc->slot_tx;
+  L1_proc_tx->frame_tx      = L1_proc->frame_tx;
+  L1_proc_tx->timestamp_tx  = L1_proc->timestamp_tx;
+
+  // the thread can now be woken up
+  if (pthread_cond_signal(&L1_proc_tx->cond) != 0) {
+    LOG_E( PHY, "[eNB] ERROR pthread_cond_signal for eNB TXnp4 thread\n");
+    exit_fun( "ERROR pthread_cond_signal" );
+    return(-1);
+  }
+
+  pthread_mutex_unlock( &L1_proc_tx->mutex);
+  return(0);
+}
+
+int wakeup_rxtx(PHY_VARS_gNB *gNB,RU_t *ru) {
+  gNB_L1_proc_t *proc=&gNB->proc;
+  gNB_L1_rxtx_proc_t *L1_proc=&proc->L1_proc;
+  NR_DL_FRAME_PARMS *fp = &gNB->frame_parms;
+  RU_proc_t *ru_proc=&ru->proc;
+  int i;
+  struct timespec wait;
+  pthread_mutex_lock(&proc->mutex_RU);
+
+  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);
+
+      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");
+    pthread_mutex_unlock(&proc->mutex_RU);
+    return(0);
+  } else { // all RUs have provided their information so continue on and wakeup gNB processing
+    proc->RU_mask = 0;
+    pthread_mutex_unlock(&proc->mutex_RU);
+  }
+
+  wait.tv_sec=0;
+  wait.tv_nsec=5000000L;
+
+  /* accept some delay in processing - up to 5ms */
+  for (i = 0; i < fp->slots_per_frame && L1_proc->instance_cnt == 0; i++) {
+    LOG_W( PHY,"[gNB] SFN.SL %d.%d, gNB RXn-TXnp4 thread busy!! (i %d, cnt %i)\n", L1_proc->frame_tx, L1_proc->slot_tx, i,L1_proc->instance_cnt);
+    usleep(100);
+  }
+
+  if (L1_proc->instance_cnt == 0) {
+    exit_fun( "TX thread busy" );
+    return(-1);
+  }
+
+  // wake up TX for subframe n+sl_ahead
+  // lock the TX mutex and make sure the thread is ready
+  if (pthread_mutex_timedlock(&L1_proc->mutex,&wait) != 0) {
+    LOG_E( PHY, "[gNB] ERROR pthread_mutex_lock for gNB RXTX thread %d (IC %d)\n", L1_proc->slot_rx&1,L1_proc->instance_cnt );
+    exit_fun( "error locking mutex" );
+    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
+  // transmitted timestamp of the next TX slot (first).
+  // 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);
+  L1_proc->frame_rx     = ru_proc->frame_rx;
+  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_I(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);
+
+  // the thread can now be woken up
+  if (pthread_cond_signal(&L1_proc->cond) != 0) {
+    LOG_E( PHY, "[gNB] ERROR pthread_cond_signal for gNB RXn-TXnp4 thread\n");
+    exit_fun( "ERROR pthread_cond_signal" );
+    return(-1);
+  }
+
+  pthread_mutex_unlock( &L1_proc->mutex );
+  return(0);
+}
+/*
+void wakeup_prach_gNB(PHY_VARS_gNB *gNB,RU_t *ru,int frame,int subframe) {
+
+  gNB_L1_proc_t *proc = &gNB->proc;
+  LTE_DL_FRAME_PARMS *fp=&gNB->frame_parms;
+  int i;
+
+  if (ru!=NULL) {
+    pthread_mutex_lock(&proc->mutex_RU_PRACH);
+    for (i=0;i<gNB->num_RU;i++) {
+      if (ru == gNB->RU_list[i]) {
+  LOG_D(PHY,"frame %d, subframe %d: RU %d for gNB %d signals PRACH (mask %x, num_RU %d)\n",frame,subframe,i,gNB->Mod_id,proc->RU_mask_prach,gNB->num_RU);
+  if ((proc->RU_mask_prach&(1<<i)) > 0)
+    LOG_E(PHY,"gNB %d frame %d, subframe %d : previous information (PRACH) from RU %d (num_RU %d, mask %x) has not been served yet!\n",
+    gNB->Mod_id,frame,subframe,ru->idx,gNB->num_RU,proc->RU_mask_prach);
+  proc->RU_mask_prach |= (1<<i);
+      }
+    }
+    if (proc->RU_mask_prach != (1<<gNB->num_RU)-1) {  // not all RUs have provided their information so return
+      pthread_mutex_unlock(&proc->mutex_RU_PRACH);
+      return;
+    }
+    else { // all RUs have provided their information so continue on and wakeup gNB processing
+      proc->RU_mask_prach = 0;
+      pthread_mutex_unlock(&proc->mutex_RU_PRACH);
+    }
+  }
+
+  // check if we have to detect PRACH first
+  if (is_prach_subframe(fp,frame,subframe)>0) {
+    LOG_D(PHY,"Triggering prach processing, frame %d, subframe %d\n",frame,subframe);
+    if (proc->instance_cnt_prach == 0) {
+      LOG_W(PHY,"[gNB] Frame %d Subframe %d, dropping PRACH\n", frame,subframe);
+      return;
+    }
+
+    // wake up thread for PRACH RX
+    if (pthread_mutex_lock(&proc->mutex_prach) != 0) {
+      LOG_E( PHY, "[gNB] ERROR pthread_mutex_lock for gNB PRACH thread %d (IC %d)\n", proc->thread_index, proc->instance_cnt_prach);
+      exit_fun( "error locking mutex_prach" );
+      return;
+    }
+
+    ++proc->instance_cnt_prach;
+    // set timing for prach thread
+    proc->frame_prach = frame;
+    proc->subframe_prach = subframe;
+
+    // the thread can now be woken up
+    if (pthread_cond_signal(&proc->cond_prach) != 0) {
+      LOG_E( PHY, "[gNB] ERROR pthread_cond_signal for gNB PRACH thread %d\n", proc->thread_index);
+      exit_fun( "ERROR pthread_cond_signal" );
+      return;
+    }
+
+    pthread_mutex_unlock( &proc->mutex_prach );
+  }
+
+}*/
+
+/*!
+ * \brief The prach receive thread of gNB.
+ * \param param is a \ref gNB_L1_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* gNB_thread_prach( void* param ) {
+ static int gNB_thread_prach_status;
+
+
+ PHY_VARS_gNB *gNB= (PHY_VARS_gNB *)param;
+ gNB_L1_proc_t *proc = &gNB->proc;
+
+ // set default return value
+ gNB_thread_prach_status = 0;
+
+ thread_top_init("gNB_thread_prach",1,500000L,1000000L,20000000L);
+
+
+ while (!oai_exit) {
+
+   if (oai_exit) break;
+
+
+   if (wait_on_condition(&proc->mutex_prach,&proc->cond_prach,&proc->instance_cnt_prach,"gNB_prach_thread") < 0) break;
+
+   LOG_D(PHY,"Running gNB prach procedures\n");
+   prach_procedures(gNB
+#if (RRC_VERSION >= MAKE_VERSION(14, 0, 0))
+      ,0
+#endif
+      );
+
+   if (release_thread(&proc->mutex_prach,&proc->instance_cnt_prach,"gNB_prach_thread") < 0) break;
+ }
+
+ LOG_I(PHY, "Exiting gNB thread PRACH\n");
+
+ gNB_thread_prach_status = 0;
+ return &gNB_thread_prach_status;
+}
+*/
+
+extern void init_td_thread(PHY_VARS_gNB *, pthread_attr_t *);
+extern void init_te_thread(PHY_VARS_gNB *, pthread_attr_t *);
+
+void init_gNB_proc(int inst) {
+  int i=0;
+  int CC_id;
+  PHY_VARS_gNB *gNB;
+  gNB_L1_proc_t *proc;
+  gNB_L1_rxtx_proc_t *L1_proc,*L1_proc_tx;
+  pthread_attr_t *attr0=NULL,*attr1=NULL;
+  //*attr_prach=NULL;
+  LOG_I(PHY,"%s(inst:%d) RC.nb_nr_CC[inst]:%d \n",__FUNCTION__,inst,RC.nb_nr_CC[inst]);
+
+  for (CC_id=0; CC_id<RC.nb_nr_CC[inst]; CC_id++) {
+    gNB = RC.gNB[inst][CC_id];
+#ifndef OCP_FRAMEWORK
+    LOG_I(PHY,"Initializing gNB processes instance:%d CC_id %d \n",inst,CC_id);
+#endif
+    proc = &gNB->proc;
+    L1_proc                        = &proc->L1_proc;
+    L1_proc_tx                     = &proc->L1_proc_tx;
+    L1_proc->instance_cnt          = -1;
+    L1_proc_tx->instance_cnt       = -1;
+    L1_proc->instance_cnt_RUs      = 0;
+    L1_proc_tx->instance_cnt_RUs   = 0;
+    proc->instance_cnt_prach       = -1;
+    proc->instance_cnt_asynch_rxtx = -1;
+    proc->CC_id                    = CC_id;
+    proc->first_rx                 =1;
+    proc->first_tx                 =1;
+    proc->RU_mask                  =0;
+    proc->RU_mask_tx               = (1<<gNB->num_RU)-1;
+    proc->RU_mask_prach            =0;
+    pthread_mutex_init( &gNB->UL_INFO_mutex, NULL);
+    pthread_mutex_init( &L1_proc->mutex, NULL);
+    pthread_mutex_init( &L1_proc_tx->mutex, NULL);
+    pthread_cond_init( &L1_proc->cond, NULL);
+    pthread_cond_init( &L1_proc_tx->cond, NULL);
+    pthread_mutex_init( &proc->mutex_prach, NULL);
+    pthread_mutex_init( &proc->mutex_asynch_rxtx, NULL);
+    pthread_mutex_init( &proc->mutex_RU,NULL);
+    pthread_mutex_init( &proc->mutex_RU_tx,NULL);
+    pthread_mutex_init( &proc->mutex_RU_PRACH,NULL);
+    pthread_cond_init( &proc->cond_prach, NULL);
+    pthread_cond_init( &proc->cond_asynch_rxtx, NULL);
+    pthread_attr_init( &proc->attr_prach);
+    pthread_attr_init( &proc->attr_asynch_rxtx);
+    //    pthread_attr_init( &proc->attr_td);
+    //    pthread_attr_init( &proc->attr_te);
+    pthread_attr_init( &L1_proc->attr);
+    pthread_attr_init( &L1_proc_tx->attr);
+    attr0       = &L1_proc->attr;
+    attr1       = &L1_proc_tx->attr;
+    LOG_I(PHY,"gNB->single_thread_flag:%d\n", gNB->single_thread_flag);
+
+    if (get_thread_parallel_conf() == PARALLEL_RU_L1_SPLIT || get_thread_parallel_conf() == PARALLEL_RU_L1_TRX_SPLIT) {
+      threadCreate( &L1_proc->pthread, gNB_L1_thread, gNB, "L1_proc", -1, OAI_PRIORITY_RT );
+      threadCreate( &L1_proc_tx->pthread, gNB_L1_thread_tx, gNB,"L1_proc_tx", -1, OAI_PRIORITY_RT);
+    }
+
+    //pthread_create( &proc->pthread_prach, attr_prach, gNB_thread_prach, gNB );
+    char name[16];
+
+    if (gNB->single_thread_flag==0) {
+      snprintf( name, sizeof(name), "L1 %d", i );
+      pthread_setname_np( L1_proc->pthread, name );
+      snprintf( name, sizeof(name), "L1TX %d", i );
+      pthread_setname_np( L1_proc_tx->pthread, name );
+    }
+
+    AssertFatal(proc->instance_cnt_prach == -1,"instance_cnt_prach = %d\n",proc->instance_cnt_prach);
+  }
+
+  /* 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);
+  sync_phy_proc.phy_proc_CC_id = 0;
+}
+
+
+
+/*!
+ * \brief Terminate gNB TX and RX threads.
+ */
+void kill_gNB_proc(int inst) {
+  int *status;
+  PHY_VARS_gNB *gNB;
+  gNB_L1_proc_t *proc;
+  gNB_L1_rxtx_proc_t *L1_proc, *L1_proc_tx;
+
+  for (int CC_id=0; CC_id<MAX_NUM_CCs; CC_id++) {
+    gNB=RC.gNB[inst][CC_id];
+    proc = &gNB->proc;
+    L1_proc     = &proc->L1_proc;
+    L1_proc_tx  = &proc->L1_proc_tx;
+    LOG_I(PHY, "Killing TX CC_id %d inst %d\n", CC_id, inst );
+
+    if (get_thread_parallel_conf() == PARALLEL_RU_L1_SPLIT || get_thread_parallel_conf() == PARALLEL_RU_L1_TRX_SPLIT) {
+      pthread_mutex_lock(&L1_proc->mutex);
+      L1_proc->instance_cnt = 0;
+      pthread_cond_signal(&L1_proc->cond);
+      pthread_mutex_unlock(&L1_proc->mutex);
+      pthread_mutex_lock(&L1_proc_tx->mutex);
+      L1_proc_tx->instance_cnt = 0;
+      pthread_cond_signal(&L1_proc_tx->cond);
+      pthread_mutex_unlock(&L1_proc_tx->mutex);
+    }
+
+    proc->instance_cnt_prach = 0;
+    pthread_cond_signal( &proc->cond_prach );
+    pthread_cond_signal( &proc->cond_asynch_rxtx );
+    pthread_cond_broadcast(&sync_phy_proc.cond_phy_proc_tx);
+    //    LOG_D(PHY, "joining pthread_prach\n");
+    //    pthread_join( proc->pthread_prach, (void**)&status );
+    LOG_I(PHY, "Destroying prach mutex/cond\n");
+    pthread_mutex_destroy( &proc->mutex_prach );
+    pthread_cond_destroy( &proc->cond_prach );
+    LOG_I(PHY, "Destroying UL_INFO mutex\n");
+    pthread_mutex_destroy(&gNB->UL_INFO_mutex);
+
+    if (get_thread_parallel_conf() == PARALLEL_RU_L1_SPLIT || get_thread_parallel_conf() == PARALLEL_RU_L1_TRX_SPLIT) {
+      LOG_I(PHY, "Joining L1_proc mutex/cond\n");
+      pthread_join( L1_proc->pthread, (void **)&status );
+      LOG_I(PHY, "Joining L1_proc_tx mutex/cond\n");
+      pthread_join( L1_proc_tx->pthread, (void **)&status );
+    }
+
+    LOG_I(PHY, "Destroying L1_proc mutex/cond\n");
+    pthread_mutex_destroy( &L1_proc->mutex );
+    pthread_cond_destroy( &L1_proc->cond );
+    LOG_I(PHY, "Destroying L1_proc_tx mutex/cond\n");
+    pthread_mutex_destroy( &L1_proc_tx->mutex );
+    pthread_cond_destroy( &L1_proc_tx->cond );
+    pthread_mutex_destroy( &proc->mutex_RU );
+    pthread_mutex_destroy( &proc->mutex_RU_tx );
+  }
+}
+
+
+
+
+void reset_opp_meas(void) {
+  int sfn;
+  reset_meas(&softmodem_stats_mt);
+  reset_meas(&softmodem_stats_hw);
+
+  for (sfn=0; sfn < 10; sfn++) {
+    reset_meas(&softmodem_stats_rxtx_sf);
+    reset_meas(&softmodem_stats_rx_sf);
+  }
+}
+
+
+void print_opp_meas(void) {
+  int sfn=0;
+  print_meas(&softmodem_stats_mt, "Main gNB Thread", NULL, NULL);
+  print_meas(&softmodem_stats_hw, "HW Acquisation", NULL, NULL);
+
+  for (sfn=0; sfn < 10; sfn++) {
+    print_meas(&softmodem_stats_rxtx_sf,"[gNB][total_phy_proc_rxtx]",NULL, NULL);
+    print_meas(&softmodem_stats_rx_sf,"[gNB][total_phy_proc_rx]",NULL,NULL);
+  }
+}
+
+
+/// eNB kept in function name for nffapi calls, TO FIX
+void init_eNB_afterRU(void) {
+  int inst,CC_id,ru_id,i,aa;
+  PHY_VARS_gNB *gNB;
+  LOG_I(PHY,"%s() RC.nb_nr_inst:%d\n", __FUNCTION__, RC.nb_nr_inst);
+
+  for (inst=0; inst<RC.nb_nr_inst; inst++) {
+    LOG_I(PHY,"RC.nb_nr_CC[inst]:%d\n", RC.nb_nr_CC[inst]);
+
+    for (CC_id=0; CC_id<RC.nb_nr_CC[inst]; CC_id++) {
+      LOG_I(PHY,"RC.nb_nr_CC[inst:%d][CC_id:%d]:%p\n", inst, CC_id, RC.gNB[inst][CC_id]);
+      gNB                                  =  RC.gNB[inst][CC_id];
+      phy_init_nr_gNB(gNB,0,0);
+
+      // map antennas and PRACH signals to gNB RX
+      if (0) AssertFatal(gNB->num_RU>0,"Number of RU attached to gNB %d is zero\n",gNB->Mod_id);
+
+      LOG_I(PHY,"Mapping RX ports from %d RUs to gNB %d\n",gNB->num_RU,gNB->Mod_id);
+      //LOG_I(PHY,"Overwriting gNB->prach_vars.rxsigF[0]:%p\n", gNB->prach_vars.rxsigF[0]);
+      gNB->prach_vars.rxsigF[0] = (int16_t **)malloc16(64*sizeof(int16_t *));
+      LOG_I(PHY,"gNB->num_RU:%d\n", gNB->num_RU);
+
+      for (ru_id=0,aa=0; ru_id<gNB->num_RU; ru_id++) {
+        AssertFatal(gNB->RU_list[ru_id]->common.rxdataF!=NULL,
+                    "RU %d : common.rxdataF is NULL\n",
+                    gNB->RU_list[ru_id]->idx);
+        AssertFatal(gNB->RU_list[ru_id]->prach_rxsigF!=NULL,
+                    "RU %d : prach_rxsigF is NULL\n",
+                    gNB->RU_list[ru_id]->idx);
+
+        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->common_vars.rxdataF[aa]     =  gNB->RU_list[ru_id]->common.rxdataF[i];
+        }
+      }
+
+      /* TODO: review this code, there is something wrong.
+       * In monolithic mode, we come here with nb_antennas_rx == 0
+       * (not tested in other modes).
+       */
+      //init_precoding_weights(RC.gNB[inst][CC_id]);
+    }
+
+    init_gNB_proc(inst);
+  }
+
+  for (ru_id=0; ru_id<RC.nb_RU; ru_id++) {
+    AssertFatal(RC.ru[ru_id]!=NULL,"ru_id %d is null\n",ru_id);
+    RC.ru[ru_id]->nr_wakeup_rxtx         = wakeup_rxtx;
+    //    RC.ru[ru_id]->wakeup_prach_eNB    = wakeup_prach_gNB;
+    RC.ru[ru_id]->gNB_top             = gNB_top;
+  }
+}
+
+void init_gNB(int single_thread_flag,int wait_for_sync) {
+  int CC_id;
+  int inst;
+  PHY_VARS_gNB *gNB;
+  LOG_I(PHY,"[nr-softmodem.c] gNB structure about to allocated RC.nb_nr_L1_inst:%d RC.nb_nr_L1_CC[0]:%d\n",RC.nb_nr_L1_inst,RC.nb_nr_L1_CC[0]);
+
+  if (RC.gNB == NULL) RC.gNB = (PHY_VARS_gNB ***) malloc(RC.nb_nr_L1_inst*sizeof(PHY_VARS_gNB **));
+
+  LOG_I(PHY,"[lte-softmodem.c] gNB structure RC.gNB allocated\n");
+
+  for (inst=0; inst<RC.nb_nr_L1_inst; inst++) {
+    if (RC.gNB[inst] == NULL) RC.gNB[inst] = (PHY_VARS_gNB **) malloc(RC.nb_nr_CC[inst]*sizeof(PHY_VARS_gNB *));
+
+    for (CC_id=0; CC_id<RC.nb_nr_L1_CC[inst]; CC_id++) {
+      if (RC.gNB[inst][CC_id] == NULL) RC.gNB[inst][CC_id] = (PHY_VARS_gNB *) malloc(sizeof(PHY_VARS_gNB));
+
+      gNB                     = RC.gNB[inst][CC_id];
+      gNB->abstraction_flag   = 0;
+      gNB->single_thread_flag = single_thread_flag;
+      /*nr_polar_init(&gNB->nrPolar_params,
+                NR_POLAR_PBCH_MESSAGE_TYPE,
+          NR_POLAR_PBCH_PAYLOAD_BITS,
+          NR_POLAR_PBCH_AGGREGATION_LEVEL);*/
+      LOG_I(PHY,"Initializing gNB %d CC_id %d single_thread_flag:%d\n",inst,CC_id,single_thread_flag);
+#ifndef OCP_FRAMEWORK
+      LOG_I(PHY,"Initializing gNB %d CC_id %d\n",inst,CC_id);
+#endif
+      LOG_I(PHY,"Registering with MAC interface module\n");
+      AssertFatal((gNB->if_inst         = NR_IF_Module_init(inst))!=NULL,"Cannot register interface");
+      gNB->if_inst->NR_Schedule_response   = nr_schedule_response;
+      gNB->if_inst->NR_PHY_config_req      = nr_phy_config_request;
+      memset((void *)&gNB->UL_INFO,0,sizeof(gNB->UL_INFO));
+      memset((void *)&gNB->Sched_INFO,0,sizeof(gNB->Sched_INFO));
+      LOG_I(PHY,"Setting indication lists\n");
+      gNB->UL_INFO.rx_ind.rx_indication_body.rx_pdu_list   = gNB->rx_pdu_list;
+      gNB->UL_INFO.crc_ind.crc_indication_body.crc_pdu_list = gNB->crc_pdu_list;
+      gNB->UL_INFO.sr_ind.sr_indication_body.sr_pdu_list = gNB->sr_pdu_list;
+      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;
+    }
+  }
+
+  LOG_I(PHY,"[nr-softmodem.c] gNB structure allocated\n");
+}
+
+
+void stop_gNB(int nb_inst) {
+  for (int inst=0; inst<nb_inst; inst++) {
+    LOG_I(PHY,"Killing gNB %d processing threads\n",inst);
+    kill_gNB_proc(inst);
+  }
+}
diff --git a/executables/nr-ru.c b/executables/nr-ru.c
new file mode 100644
index 0000000000..547f4a7d51
--- /dev/null
+++ b/executables/nr-ru.c
@@ -0,0 +1,2312 @@
+/*******************************************************************************
+    OpenAirInterface
+    Copyright(c) 1999 - 2014 Eurecom
+
+    OpenAirInterface is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenAirInterface is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenAirInterface.The full GNU General Public License is
+    included in this distribution in the file called "COPYING". If not,
+    see <http://www.gnu.org/licenses/>.
+
+   Contact Information
+   OpenAirInterface Admin: openair_admin@eurecom.fr
+   OpenAirInterface Tech : openair_tech@eurecom.fr
+   OpenAirInterface Dev  : openair4g-devel@lists.eurecom.fr
+
+   Address      : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE
+
+*******************************************************************************/
+
+/*! \file lte-enb.c
+ * \brief Top-level threads for eNodeB
+ * \author R. Knopp, F. Kaltenberger, Navid Nikaein
+ * \date 2012
+ * \version 0.1
+ * \company Eurecom
+ * \email: knopp@eurecom.fr,florian.kaltenberger@eurecom.fr, navid.nikaein@eurecom.fr
+ * \note
+ * \warning
+ */
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sched.h>
+#include <linux/sched.h>
+#include <signal.h>
+#include <execinfo.h>
+#include <getopt.h>
+#include <sys/sysinfo.h>
+
+#undef MALLOC //there are two conflicting definitions, so we better make sure we don't use it at all
+
+#include "common/utils/assertions.h"
+#include "common/utils/system.h"
+#include "msc.h"
+
+#include "../../ARCH/COMMON/common_lib.h"
+#include "../../ARCH/ETHERNET/USERSPACE/LIB/ethernet_lib.h"
+
+#include "PHY/LTE_TRANSPORT/if4_tools.h"
+#include "PHY/LTE_TRANSPORT/if5_tools.h"
+
+#include "PHY/types.h"
+#include "PHY/defs_nr_common.h"
+#include "PHY/phy_extern.h"
+#include "PHY/LTE_TRANSPORT/transport_proto.h"
+#include "PHY/INIT/phy_init.h"
+#include "SCHED/sched_eNB.h"
+#include "SCHED_NR/sched_nr.h"
+
+#include "LAYER2/MAC/mac.h"
+#include "LAYER2/MAC/mac_extern.h"
+#include "LAYER2/MAC/mac_proto.h"
+#include "RRC/LTE/rrc_extern.h"
+#include "PHY_INTERFACE/phy_interface.h"
+
+#include "common/utils/LOG/log.h"
+#include "common/utils/LOG/vcd_signal_dumper.h"
+
+#include "enb_config.h"
+
+#ifdef SMBV
+#include "PHY/TOOLS/smbv.h"
+unsigned short config_frames[4] = {2,9,11,13};
+#endif
+
+/* these variables have to be defined before including ENB_APP/enb_paramdef.h */
+static int DEFBANDS[] = {7};
+static int DEFENBS[] = {0};
+
+#include "ENB_APP/enb_paramdef.h"
+#include "common/config/config_userapi.h"
+
+#ifndef OPENAIR2
+  #include "UTIL/OTG/otg_extern.h"
+#endif
+
+#if defined(ENABLE_ITTI)
+  #if defined(ENABLE_USE_MME)
+    #include "s1ap_eNB.h"
+    #ifdef PDCP_USE_NETLINK
+      #include "SIMULATION/ETH_TRANSPORT/proto.h"
+    #endif
+  #endif
+#endif
+
+#include "T.h"
+#include "nfapi_interface.h"
+
+extern volatile int                    oai_exit;
+
+
+extern void  nr_phy_init_RU(RU_t *);
+extern void  nr_phy_free_RU(RU_t *);
+extern void  nr_phy_config_request(NR_PHY_Config_t *gNB);
+
+extern PARALLEL_CONF_t get_thread_parallel_conf(void);
+extern WORKER_CONF_t   get_thread_worker_conf(void);
+
+void init_RU(char *);
+void stop_RU(int nb_ru);
+void do_ru_sync(RU_t *ru);
+
+void configure_ru(int idx,
+                  void *arg);
+
+void configure_rru(int idx,
+                   void *arg);
+
+int attach_rru(RU_t *ru);
+
+int connect_rau(RU_t *ru);
+
+extern uint16_t sl_ahead;
+
+extern int emulate_rf;
+extern int numerology;
+
+/*************************************************************/
+/* Functions to attach and configure RRU                     */
+
+extern void wait_gNBs(void);
+
+int attach_rru(RU_t *ru) {
+  ssize_t      msg_len,len;
+  RRU_CONFIG_msg_t rru_config_msg;
+  int received_capabilities=0;
+  wait_gNBs();
+
+  // Wait for capabilities
+  while (received_capabilities==0) {
+    memset((void *)&rru_config_msg,0,sizeof(rru_config_msg));
+    rru_config_msg.type = RAU_tick;
+    rru_config_msg.len  = sizeof(RRU_CONFIG_msg_t)-MAX_RRU_CONFIG_SIZE;
+    LOG_I(PHY,"Sending RAU tick to RRU %d\n",ru->idx);
+    AssertFatal((ru->ifdevice.trx_ctlsend_func(&ru->ifdevice,&rru_config_msg,rru_config_msg.len)!=-1),
+                "RU %d cannot access remote radio\n",ru->idx);
+    msg_len  = sizeof(RRU_CONFIG_msg_t)-MAX_RRU_CONFIG_SIZE+sizeof(RRU_capabilities_t);
+
+    // wait for answer with timeout
+    if ((len = ru->ifdevice.trx_ctlrecv_func(&ru->ifdevice,
+               &rru_config_msg,
+               msg_len))<0) {
+      LOG_I(PHY,"Waiting for RRU %d\n",ru->idx);
+    } else if (rru_config_msg.type == RRU_capabilities) {
+      AssertFatal(rru_config_msg.len==msg_len,"Received capabilities with incorrect length (%d!=%d)\n",(int)rru_config_msg.len,(int)msg_len);
+      LOG_I(PHY,"Received capabilities from RRU %d (len %d/%d, num_bands %d,max_pdschReferenceSignalPower %d, max_rxgain %d, nb_tx %d, nb_rx %d)\n",ru->idx,
+            (int)rru_config_msg.len,(int)msg_len,
+            ((RRU_capabilities_t *)&rru_config_msg.msg[0])->num_bands,
+            ((RRU_capabilities_t *)&rru_config_msg.msg[0])->max_pdschReferenceSignalPower[0],
+            ((RRU_capabilities_t *)&rru_config_msg.msg[0])->max_rxgain[0],
+            ((RRU_capabilities_t *)&rru_config_msg.msg[0])->nb_tx[0],
+            ((RRU_capabilities_t *)&rru_config_msg.msg[0])->nb_rx[0]);
+      received_capabilities=1;
+    } else {
+      LOG_E(PHY,"Received incorrect message %d from RRU %d\n",rru_config_msg.type,ru->idx);
+    }
+  }
+
+  configure_ru(ru->idx,
+               (RRU_capabilities_t *)&rru_config_msg.msg[0]);
+  rru_config_msg.type = RRU_config;
+  rru_config_msg.len  = sizeof(RRU_CONFIG_msg_t)-MAX_RRU_CONFIG_SIZE+sizeof(RRU_config_t);
+  LOG_I(PHY,"Sending Configuration to RRU %d (num_bands %d,band0 %d,txfreq %u,rxfreq %u,att_tx %d,att_rx %d,N_RB_DL %d,N_RB_UL %d,3/4FS %d, prach_FO %d, prach_CI %d)\n",ru->idx,
+        ((RRU_config_t *)&rru_config_msg.msg[0])->num_bands,
+        ((RRU_config_t *)&rru_config_msg.msg[0])->band_list[0],
+        ((RRU_config_t *)&rru_config_msg.msg[0])->tx_freq[0],
+        ((RRU_config_t *)&rru_config_msg.msg[0])->rx_freq[0],
+        ((RRU_config_t *)&rru_config_msg.msg[0])->att_tx[0],
+        ((RRU_config_t *)&rru_config_msg.msg[0])->att_rx[0],
+        ((RRU_config_t *)&rru_config_msg.msg[0])->N_RB_DL[0],
+        ((RRU_config_t *)&rru_config_msg.msg[0])->N_RB_UL[0],
+        ((RRU_config_t *)&rru_config_msg.msg[0])->threequarter_fs[0],
+        ((RRU_config_t *)&rru_config_msg.msg[0])->prach_FreqOffset[0],
+        ((RRU_config_t *)&rru_config_msg.msg[0])->prach_ConfigIndex[0]);
+  AssertFatal((ru->ifdevice.trx_ctlsend_func(&ru->ifdevice,&rru_config_msg,rru_config_msg.len)!=-1),
+              "RU %d failed send configuration to remote radio\n",ru->idx);
+  return 0;
+}
+
+int connect_rau(RU_t *ru) {
+  RRU_CONFIG_msg_t   rru_config_msg;
+  ssize_t      msg_len;
+  int                tick_received          = 0;
+  int                configuration_received = 0;
+  RRU_capabilities_t *cap;
+  int                i;
+  int                len;
+
+  // wait for RAU_tick
+  while (tick_received == 0) {
+    msg_len  = sizeof(RRU_CONFIG_msg_t)-MAX_RRU_CONFIG_SIZE;
+
+    if ((len = ru->ifdevice.trx_ctlrecv_func(&ru->ifdevice,
+               &rru_config_msg,
+               msg_len))<0) {
+      LOG_I(PHY,"Waiting for RAU\n");
+    } else {
+      if (rru_config_msg.type == RAU_tick) {
+        LOG_I(PHY,"Tick received from RAU\n");
+        tick_received = 1;
+      } else LOG_E(PHY,"Received erroneous message (%d)from RAU, expected RAU_tick\n",rru_config_msg.type);
+    }
+  }
+
+  // send capabilities
+  rru_config_msg.type = RRU_capabilities;
+  rru_config_msg.len  = sizeof(RRU_CONFIG_msg_t)-MAX_RRU_CONFIG_SIZE+sizeof(RRU_capabilities_t);
+  cap                 = (RRU_capabilities_t *)&rru_config_msg.msg[0];
+  LOG_I(PHY,"Sending Capabilities (len %d, num_bands %d,max_pdschReferenceSignalPower %d, max_rxgain %d, nb_tx %d, nb_rx %d)\n",
+        (int)rru_config_msg.len,ru->num_bands,ru->max_pdschReferenceSignalPower,ru->max_rxgain,ru->nb_tx,ru->nb_rx);
+
+  switch (ru->function) {
+    case NGFI_RRU_IF4p5:
+      cap->FH_fmt                                   = OAI_IF4p5_only;
+      break;
+
+    case NGFI_RRU_IF5:
+      cap->FH_fmt                                   = OAI_IF5_only;
+      break;
+
+    case MBP_RRU_IF5:
+      cap->FH_fmt                                   = MBP_IF5;
+      break;
+
+    default:
+      AssertFatal(1==0,"RU_function is unknown %d\n",RC.ru[0]->function);
+      break;
+  }
+
+  cap->num_bands                                  = ru->num_bands;
+
+  for (i=0; i<ru->num_bands; i++) {
+    LOG_I(PHY,"Band %d: nb_rx %d nb_tx %d pdschReferenceSignalPower %d rxgain %d\n",
+          ru->band[i],ru->nb_rx,ru->nb_tx,ru->max_pdschReferenceSignalPower,ru->max_rxgain);
+    cap->band_list[i]                             = ru->band[i];
+    cap->nb_rx[i]                                 = ru->nb_rx;
+    cap->nb_tx[i]                                 = ru->nb_tx;
+    cap->max_pdschReferenceSignalPower[i]         = ru->max_pdschReferenceSignalPower;
+    cap->max_rxgain[i]                            = ru->max_rxgain;
+  }
+
+  AssertFatal((ru->ifdevice.trx_ctlsend_func(&ru->ifdevice,&rru_config_msg,rru_config_msg.len)!=-1),
+              "RU %d failed send capabilities to RAU\n",ru->idx);
+  // wait for configuration
+  rru_config_msg.len  = sizeof(RRU_CONFIG_msg_t)-MAX_RRU_CONFIG_SIZE+sizeof(RRU_config_t);
+
+  while (configuration_received == 0) {
+    if ((len = ru->ifdevice.trx_ctlrecv_func(&ru->ifdevice,
+               &rru_config_msg,
+               rru_config_msg.len))<0) {
+      LOG_I(PHY,"Waiting for configuration from RAU\n");
+    } else {
+      LOG_I(PHY,"Configuration received from RAU  (num_bands %d,band0 %d,txfreq %u,rxfreq %u,att_tx %d,att_rx %d,N_RB_DL %d,N_RB_UL %d,3/4FS %d, prach_FO %d, prach_CI %d)\n",
+            ((RRU_config_t *)&rru_config_msg.msg[0])->num_bands,
+            ((RRU_config_t *)&rru_config_msg.msg[0])->band_list[0],
+            ((RRU_config_t *)&rru_config_msg.msg[0])->tx_freq[0],
+            ((RRU_config_t *)&rru_config_msg.msg[0])->rx_freq[0],
+            ((RRU_config_t *)&rru_config_msg.msg[0])->att_tx[0],
+            ((RRU_config_t *)&rru_config_msg.msg[0])->att_rx[0],
+            ((RRU_config_t *)&rru_config_msg.msg[0])->N_RB_DL[0],
+            ((RRU_config_t *)&rru_config_msg.msg[0])->N_RB_UL[0],
+            ((RRU_config_t *)&rru_config_msg.msg[0])->threequarter_fs[0],
+            ((RRU_config_t *)&rru_config_msg.msg[0])->prach_FreqOffset[0],
+            ((RRU_config_t *)&rru_config_msg.msg[0])->prach_ConfigIndex[0]);
+      configure_rru(ru->idx,
+                    (void *)&rru_config_msg.msg[0]);
+      configuration_received = 1;
+    }
+  }
+
+  return 0;
+}
+/*************************************************************/
+/* Southbound Fronthaul functions, RCC/RAU                   */
+
+// southbound IF5 fronthaul for 16-bit OAI format
+static inline void fh_if5_south_out(RU_t *ru) {
+  if (ru == RC.ru[0]) VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST, ru->proc.timestamp_tx&0xffffffff );
+
+  send_IF5(ru, ru->proc.timestamp_tx, ru->proc.tti_tx, &ru->seqno, IF5_RRH_GW_DL);
+}
+
+// southbound IF5 fronthaul for Mobipass packet format
+static inline void fh_if5_mobipass_south_out(RU_t *ru) {
+  if (ru == RC.ru[0]) VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST, ru->proc.timestamp_tx&0xffffffff );
+
+  send_IF5(ru, ru->proc.timestamp_tx, ru->proc.tti_tx, &ru->seqno, IF5_MOBIPASS);
+}
+
+// southbound IF4p5 fronthaul
+static inline void fh_if4p5_south_out(RU_t *ru) {
+  if (ru == RC.ru[0]) VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST, ru->proc.timestamp_tx&0xffffffff );
+
+  LOG_D(PHY,"Sending IF4p5 for frame %d subframe %d\n",ru->proc.frame_tx,ru->proc.tti_tx);
+
+  if (nr_slot_select(&ru->gNB_list[0]->gNB_config,ru->proc.tti_tx)!=SF_UL)
+    send_IF4p5(ru,ru->proc.frame_tx, ru->proc.tti_tx, IF4p5_PDLFFT);
+}
+
+/*************************************************************/
+/* Input Fronthaul from south RCC/RAU                        */
+
+// Synchronous if5 from south
+void fh_if5_south_in(RU_t *ru,int *frame, int *tti) {
+  NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
+  RU_proc_t *proc = &ru->proc;
+  recv_IF5(ru, &proc->timestamp_rx, *tti, IF5_RRH_GW_UL);
+  proc->frame_rx    = (proc->timestamp_rx / (fp->samples_per_slot*20))&1023;
+  proc->tti_rx = (proc->timestamp_rx / fp->samples_per_slot)%20;
+
+  if (proc->first_rx == 0) {
+    if (proc->tti_rx != *tti) {
+      LOG_E(PHY,"Received Timestamp doesn't correspond to the time we think it is (proc->tti_rx %d, subframe %d)\n",proc->tti_rx,*tti);
+      exit_fun("Exiting");
+    }
+
+    if (proc->frame_rx != *frame) {
+      LOG_E(PHY,"Received Timestamp doesn't correspond to the time we think it is (proc->frame_rx %d frame %d)\n",proc->frame_rx,*frame);
+      exit_fun("Exiting");
+    }
+  } else {
+    proc->first_rx = 0;
+    *frame = proc->frame_rx;
+    *tti = proc->tti_rx;
+  }
+
+  VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TS, proc->timestamp_rx&0xffffffff );
+}
+
+// Synchronous if4p5 from south
+void fh_if4p5_south_in(RU_t *ru,int *frame,int *slot) {
+  NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
+  RU_proc_t *proc = &ru->proc;
+  int f,sl;
+  uint16_t packet_type;
+  uint32_t symbol_number=0;
+  uint32_t symbol_mask_full=0;
+
+  /*
+    if ((fp->frame_type == TDD) && (subframe_select(fp,*slot)==SF_S))
+      symbol_mask_full = (1<<fp->ul_symbols_in_S_subframe)-1;
+    else
+      symbol_mask_full = (1<<fp->symbols_per_slot)-1;
+
+    AssertFatal(proc->symbol_mask[*slot]==0,"rx_fh_if4p5: proc->symbol_mask[%d] = %x\n",*slot,proc->symbol_mask[*slot]);*/
+  do {   // Blocking, we need a timeout on this !!!!!!!!!!!!!!!!!!!!!!!
+    recv_IF4p5(ru, &f, &sl, &packet_type, &symbol_number);
+
+    if (packet_type == IF4p5_PULFFT) proc->symbol_mask[sl] = proc->symbol_mask[sl] | (1<<symbol_number);
+    else if (packet_type == IF4p5_PULTICK) {
+      if ((proc->first_rx==0) && (f!=*frame)) LOG_E(PHY,"rx_fh_if4p5: PULTICK received frame %d != expected %d\n",f,*frame);
+
+      if ((proc->first_rx==0) && (sl!=*slot)) LOG_E(PHY,"rx_fh_if4p5: PULTICK received subframe %d != expected %d (first_rx %d)\n",sl,*slot,proc->first_rx);
+
+      break;
+    } else if (packet_type == IF4p5_PRACH) {
+      // nothing in RU for RAU
+    }
+
+    LOG_D(PHY,"rx_fh_if4p5: subframe %d symbol mask %x\n",*slot,proc->symbol_mask[sl]);
+  } while(proc->symbol_mask[sl] != symbol_mask_full);
+
+  //caculate timestamp_rx, timestamp_tx based on frame and subframe
+  proc->tti_rx  = sl;
+  proc->frame_rx     = f;
+  proc->timestamp_rx = ((proc->frame_rx * fp->slots_per_frame)  + proc->tti_rx ) * fp->samples_per_slot ;
+  //  proc->timestamp_tx = proc->timestamp_rx +  (4*fp->samples_per_subframe);
+  proc->tti_tx  = (sl+sl_ahead)%fp->slots_per_frame;
+  proc->frame_tx     = (sl>(fp->slots_per_frame-sl_ahead)) ? (f+1)&1023 : f;
+
+  if (proc->first_rx == 0) {
+    if (proc->tti_rx != *slot) {
+      LOG_E(PHY,"Received Timestamp (IF4p5) doesn't correspond to the time we think it is (proc->tti_rx %d, subframe %d)\n",proc->tti_rx,*slot);
+      exit_fun("Exiting");
+    }
+
+    if (proc->frame_rx != *frame) {
+      LOG_E(PHY,"Received Timestamp (IF4p5) doesn't correspond to the time we think it is (proc->frame_rx %d frame %d)\n",proc->frame_rx,*frame);
+      exit_fun("Exiting");
+    }
+  } else {
+    proc->first_rx = 0;
+    *frame = proc->frame_rx;
+    *slot = proc->tti_rx;
+  }
+
+  if (ru == RC.ru[0]) {
+    VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_RX0_RU, f );
+    VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TTI_NUMBER_RX0_RU,  sl);
+    VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_TX0_RU, proc->frame_tx );
+    VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TTI_NUMBER_TX0_RU, proc->tti_tx );
+  }
+
+  proc->symbol_mask[proc->tti_rx] = 0;
+  VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TS, proc->timestamp_rx&0xffffffff );
+  LOG_D(PHY,"RU %d: fh_if4p5_south_in sleeping ...\n",ru->idx);
+}
+
+// asynchronous inbound if4p5 fronthaul from south
+void fh_if4p5_south_asynch_in(RU_t *ru,int *frame,int *slot) {
+  NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
+  RU_proc_t *proc       = &ru->proc;
+  uint16_t packet_type;
+  uint32_t symbol_number,symbol_mask,prach_rx;
+  //  uint32_t got_prach_info=0;
+  symbol_number = 0;
+  symbol_mask   = (1<<(fp->symbols_per_slot))-1;
+  prach_rx      = 0;
+
+  do {   // Blocking, we need a timeout on this !!!!!!!!!!!!!!!!!!!!!!!
+    recv_IF4p5(ru, &proc->frame_rx, &proc->tti_rx, &packet_type, &symbol_number);
+
+    // grab first prach information for this new subframe
+    /*if (got_prach_info==0) {
+      prach_rx       = is_prach_subframe(fp, proc->frame_rx, proc->tti_rx);
+      got_prach_info = 1;
+    }*/
+    if (proc->first_rx != 0) {
+      *frame = proc->frame_rx;
+      *slot = proc->tti_rx;
+      proc->first_rx = 0;
+    } else {
+      if (proc->frame_rx != *frame) {
+        LOG_E(PHY,"frame_rx %d is not what we expect %d\n",proc->frame_rx,*frame);
+        exit_fun("Exiting");
+      }
+
+      if (proc->tti_rx != *slot) {
+        LOG_E(PHY,"tti_rx %d is not what we expect %d\n",proc->tti_rx,*slot);
+        exit_fun("Exiting");
+      }
+    }
+
+    if      (packet_type == IF4p5_PULFFT)       symbol_mask &= (~(1<<symbol_number));
+    else if (packet_type == IF4p5_PRACH)        prach_rx    &= (~0x1);
+  } while( (symbol_mask > 0) || (prach_rx >0));   // haven't received all PUSCH symbols and PRACH information
+}
+
+
+
+
+
+/*************************************************************/
+/* Input Fronthaul from North RRU                            */
+
+// RRU IF4p5 TX fronthaul receiver. Assumes an if_device on input and if or rf device on output
+// receives one subframe's worth of IF4p5 OFDM symbols and OFDM modulates
+void fh_if4p5_north_in(RU_t *ru,int *frame,int *slot) {
+  uint32_t symbol_number=0;
+  uint32_t symbol_mask, symbol_mask_full;
+  uint16_t packet_type;
+  /// **** incoming IF4p5 from remote RCC/RAU **** ///
+  symbol_number = 0;
+  symbol_mask = 0;
+  symbol_mask_full = (1<<(ru->nr_frame_parms->symbols_per_slot))-1;
+
+  do {
+    recv_IF4p5(ru, frame, slot, &packet_type, &symbol_number);
+    symbol_mask = symbol_mask | (1<<symbol_number);
+  } while (symbol_mask != symbol_mask_full);
+
+  // dump VCD output for first RU in list
+  if (ru == RC.ru[0]) {
+    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 );
+  }
+}
+
+void fh_if5_north_asynch_in(RU_t *ru,int *frame,int *slot) {
+  NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
+  RU_proc_t *proc        = &ru->proc;
+  int tti_tx,frame_tx;
+  openair0_timestamp timestamp_tx;
+  recv_IF5(ru, &timestamp_tx, *slot, IF5_RRH_GW_DL);
+  //      printf("Received subframe %d (TS %llu) from RCC\n",tti_tx,timestamp_tx);
+  tti_tx = (timestamp_tx/fp->samples_per_slot)%fp->slots_per_frame;
+  frame_tx    = (timestamp_tx/(fp->samples_per_slot*fp->slots_per_frame))&1023;
+
+  if (proc->first_tx != 0) {
+    *slot = tti_tx;
+    *frame    = frame_tx;
+    proc->first_tx = 0;
+  } else {
+    AssertFatal(tti_tx == *slot,
+                "tti_tx %d is not what we expect %d\n",tti_tx,*slot);
+    AssertFatal(frame_tx == *frame,
+                "frame_tx %d is not what we expect %d\n",frame_tx,*frame);
+  }
+}
+
+void fh_if4p5_north_asynch_in(RU_t *ru,int *frame,int *slot) {
+  NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
+  nfapi_nr_config_request_t *cfg = &ru->gNB_list[0]->gNB_config;
+  RU_proc_t *proc        = &ru->proc;
+  uint16_t packet_type;
+  uint32_t symbol_number,symbol_mask,symbol_mask_full=0;
+  int slot_tx,frame_tx;
+  LOG_D(PHY, "%s(ru:%p frame, subframe)\n", __FUNCTION__, ru);
+  symbol_number = 0;
+  symbol_mask = 0;
+
+  //  symbol_mask_full = ((subframe_select(fp,*slot) == SF_S) ? (1<<fp->dl_symbols_in_S_subframe) : (1<<fp->symbols_per_slot))-1;
+  do {
+    recv_IF4p5(ru, &frame_tx, &slot_tx, &packet_type, &symbol_number);
+
+    if ((nr_slot_select(cfg,slot_tx) == SF_DL) && (symbol_number == 0)) start_meas(&ru->rx_fhaul);
+
+    LOG_D(PHY,"subframe %d (%d): frame %d, subframe %d, symbol %d\n",
+          *slot,nr_slot_select(cfg,*slot),frame_tx,slot_tx,symbol_number);
+
+    if (proc->first_tx != 0) {
+      *frame         = frame_tx;
+      *slot          = slot_tx;
+      proc->first_tx = 0;
+      //symbol_mask_full = ((subframe_select(fp,*slot) == SF_S) ? (1<<fp->dl_symbols_in_S_subframe) : (1<<fp->symbols_per_slot))-1;
+    } else {
+      AssertFatal(frame_tx == *frame,
+                  "frame_tx %d is not what we expect %d\n",frame_tx,*frame);
+      AssertFatal(slot_tx == *slot,
+                  "slot_tx %d is not what we expect %d\n",slot_tx,*slot);
+    }
+
+    if (packet_type == IF4p5_PDLFFT) {
+      symbol_mask = symbol_mask | (1<<symbol_number);
+    } else AssertFatal(1==0,"Illegal IF4p5 packet type (should only be IF4p5_PDLFFT%d\n",packet_type);
+  } while (symbol_mask != symbol_mask_full);
+
+  if (nr_slot_select(cfg,slot_tx) == SF_DL) stop_meas(&ru->rx_fhaul);
+
+  proc->tti_tx  = slot_tx;
+  proc->frame_tx     = frame_tx;
+
+  if ((frame_tx == 0)&&(slot_tx == 0)) proc->frame_tx_unwrap += 1024;
+
+  proc->timestamp_tx = ((((uint64_t)frame_tx + (uint64_t)proc->frame_tx_unwrap) * fp->slots_per_frame) + (uint64_t)slot_tx) * (uint64_t)fp->samples_per_slot;
+  LOG_D(PHY,"RU %d/%d TST %llu, frame %d, subframe %d\n",ru->idx,0,(long long unsigned int)proc->timestamp_tx,frame_tx,slot_tx);
+
+  // dump VCD output for first RU in list
+  if (ru == RC.ru[0]) {
+    VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_TX0_RU, frame_tx );
+    VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TTI_NUMBER_TX0_RU, slot_tx );
+  }
+
+  if (ru->feptx_ofdm) ru->feptx_ofdm(ru);
+
+  if (ru->fh_south_out) ru->fh_south_out(ru);
+}
+
+void fh_if5_north_out(RU_t *ru) {
+  RU_proc_t *proc=&ru->proc;
+  uint8_t seqno=0;
+  /// **** send_IF5 of rxdata to BBU **** ///
+  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_SEND_IF5, 1 );
+  send_IF5(ru, proc->timestamp_rx, proc->tti_rx, &seqno, IF5_RRH_GW_UL);
+  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_SEND_IF5, 0 );
+}
+
+// RRU IF4p5 northbound interface (RX)
+void fh_if4p5_north_out(RU_t *ru) {
+  RU_proc_t *proc=&ru->proc;
+
+  //NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
+  //const int subframe     = proc->tti_rx;
+  if (ru->idx==0) VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TTI_NUMBER_RX0_RU, proc->tti_rx );
+
+  /*
+    if ((fp->frame_type == TDD) && (subframe_select(fp,subframe)!=SF_UL)) {
+      /// **** in TDD during DL send_IF4 of ULTICK to RCC **** ///
+      send_IF4p5(ru, proc->frame_rx, proc->tti_rx, IF4p5_PULTICK);
+      return;
+    }*/
+  start_meas(&ru->tx_fhaul);
+  send_IF4p5(ru, proc->frame_rx, proc->tti_rx, IF4p5_PULFFT);
+  stop_meas(&ru->tx_fhaul);
+}
+
+static void *emulatedRF_thread(void *param) {
+  RU_proc_t *proc = (RU_proc_t *) param;
+  int microsec = 500; // length of time to sleep, in miliseconds
+  struct timespec req = {0};
+  req.tv_sec = 0;
+  req.tv_nsec = (numerology>0)? ((microsec * 1000L)/numerology):(microsec * 1000L)*2;
+  threadTopInit("emulatedRF",-1,OAI_PRIORITY_RT_LOW);
+  wait_sync("emulatedRF_thread");
+
+  while(!oai_exit) {
+    nanosleep(&req, (struct timespec *)NULL);
+    pthread_mutex_lock(&proc->mutex_emulateRF);
+    ++proc->instance_cnt_emulateRF;
+    pthread_mutex_unlock(&proc->mutex_emulateRF);
+    pthread_cond_signal(&proc->cond_emulateRF);
+  }
+
+  return 0;
+}
+
+void rx_rf(RU_t *ru,int *frame,int *slot) {
+  RU_proc_t *proc = &ru->proc;
+  NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
+  void *rxp[ru->nb_rx];
+  unsigned int rxs;
+  int i;
+  openair0_timestamp ts,old_ts;
+  AssertFatal(*slot<fp->slots_per_frame && *slot>=0, "slot %d is illegal (%d)\n",*slot,fp->slots_per_frame);
+
+  for (i=0; i<ru->nb_rx; i++)
+    rxp[i] = (void *)&ru->common.rxdata[i][*slot*fp->samples_per_slot];
+
+  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_READ, 1 );
+  old_ts = proc->timestamp_rx;
+  LOG_D(PHY,"Reading %d samples for slot %d (%p)\n",fp->samples_per_slot,*slot,rxp[0]);
+
+  if(emulate_rf) {
+    wait_on_condition(&proc->mutex_emulateRF,&proc->cond_emulateRF,&proc->instance_cnt_emulateRF,"emulatedRF_thread");
+    release_thread(&proc->mutex_emulateRF,&proc->instance_cnt_emulateRF,"emulatedRF_thread");
+    rxs = fp->samples_per_slot;
+    ts = old_ts + rxs;
+  } else {
+    rxs = ru->rfdevice.trx_read_func(&ru->rfdevice,
+                                     &ts,
+                                     rxp,
+                                     fp->samples_per_slot,
+                                     ru->nb_rx);
+  }
+
+  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_READ, 0 );
+  proc->timestamp_rx = ts-ru->ts_offset;
+
+  //AssertFatal(rxs == fp->samples_per_subframe,
+  //"rx_rf: Asked for %d samples, got %d from USRP\n",fp->samples_per_subframe,rxs);
+  if (rxs != fp->samples_per_slot) LOG_E(PHY, "rx_rf: Asked for %d samples, got %d from USRP\n",fp->samples_per_slot,rxs);
+
+  if (proc->first_rx == 1) {
+    ru->ts_offset = proc->timestamp_rx;
+    proc->timestamp_rx = 0;
+  } else {
+    if (proc->timestamp_rx - old_ts != fp->samples_per_slot) {
+      LOG_I(PHY,"rx_rf: rfdevice timing drift of %"PRId64" samples (ts_off %"PRId64")\n",proc->timestamp_rx - old_ts - fp->samples_per_slot,ru->ts_offset);
+      ru->ts_offset += (proc->timestamp_rx - old_ts - fp->samples_per_slot);
+      proc->timestamp_rx = ts-ru->ts_offset;
+    }
+  }
+
+  proc->frame_rx     = (proc->timestamp_rx / (fp->samples_per_slot*fp->slots_per_frame))&1023;
+  proc->tti_rx       = (proc->timestamp_rx / fp->samples_per_slot)%fp->slots_per_frame;
+  // synchronize first reception to frame 0 subframe 0
+  proc->timestamp_tx = proc->timestamp_rx+(sl_ahead*fp->samples_per_slot);
+  proc->tti_tx  = (proc->tti_rx+sl_ahead)%fp->slots_per_frame;
+  proc->frame_tx     = (proc->tti_rx>(fp->slots_per_frame-1-sl_ahead)) ? (proc->frame_rx+1)&1023 : proc->frame_rx;
+  LOG_I(PHY,"RU %d/%d TS %llu (off %d), frame %d, slot %d.%d / %d\n",
+        ru->idx,
+        0,
+        (unsigned long long int)proc->timestamp_rx,
+        (int)ru->ts_offset,proc->frame_rx,proc->tti_rx,proc->tti_tx,fp->slots_per_frame);
+
+  // dump VCD output for first RU in list
+  if (ru == RC.ru[0]) {
+    VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_RX0_RU, proc->frame_rx );
+    VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TTI_NUMBER_RX0_RU, proc->tti_rx );
+  }
+
+  if (proc->first_rx == 0) {
+    if (proc->tti_rx != *slot) {
+      LOG_E(PHY,"Received Timestamp (%llu) doesn't correspond to the time we think it is (proc->tti_rx %d, subframe %d)\n",(long long unsigned int)proc->timestamp_rx,proc->tti_rx,*slot);
+      exit_fun("Exiting");
+    }
+
+    if (proc->frame_rx != *frame) {
+      LOG_E(PHY,"Received Timestamp (%llu) doesn't correspond to the time we think it is (proc->frame_rx %d frame %d)\n",(long long unsigned int)proc->timestamp_rx,proc->frame_rx,*frame);
+      exit_fun("Exiting");
+    }
+  } else {
+    proc->first_rx = 0;
+    *frame = proc->frame_rx;
+    *slot  = proc->tti_rx;
+  }
+
+  //printf("timestamp_rx %lu, frame %d(%d), subframe %d(%d)\n",ru->timestamp_rx,proc->frame_rx,frame,proc->tti_rx,subframe);
+  VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TS, proc->timestamp_rx&0xffffffff );
+
+  if (rxs != fp->samples_per_slot) {
+    //exit_fun( "problem receiving samples" );
+    LOG_E(PHY, "problem receiving samples\n");
+  }
+}
+
+
+void tx_rf(RU_t *ru) {
+  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;
+  void *txp[ru->nb_tx];
+  unsigned int txs;
+  int i;
+  T(T_ENB_PHY_OUTPUT_SIGNAL, T_INT(0), T_INT(0), T_INT(proc->frame_tx), T_INT(proc->tti_tx),
+    T_INT(0), T_BUFFER(&ru->common.txdata[0][proc->tti_tx * fp->samples_per_slot], fp->samples_per_slot * 4));
+  nr_subframe_t SF_type     = nr_slot_select(cfg,proc->tti_tx%fp->slots_per_frame);
+  int sf_extension = 0;
+
+  if ((SF_type == SF_DL) ||
+      (SF_type == SF_S)) {
+    int siglen=fp->samples_per_slot,flags=1;
+    /*
+        if (SF_type == SF_S) {
+          siglen = fp->dl_symbols_in_S_subframe*(fp->ofdm_symbol_size+fp->nb_prefix_samples0);
+          flags=3; // end of burst
+        }
+        if ((fp->frame_type == TDD) &&
+      (SF_type == SF_DL)&&
+      (prevSF_type == SF_UL) &&
+      (nextSF_type == SF_DL)) {
+          flags = 2; // start of burst
+          sf_extension = ru->N_TA_offset<<1;
+        }
+
+        if ((cfg->subframe_config.duplex_mode == TDD) &&
+      (SF_type == SF_DL)&&
+      (prevSF_type == SF_UL) &&
+      (nextSF_type == SF_UL)) {
+          flags = 4; // start of burst and end of burst (only one DL SF between two UL)
+          sf_extension = ru->N_TA_offset<<1;
+        } */
+    VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_TX0_RU, proc->frame_tx );
+    VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TTI_NUMBER_TX0_RU, proc->tti_tx );
+
+    for (i=0; i<ru->nb_tx; i++)
+      txp[i] = (void *)&ru->common.txdata[i][(proc->tti_tx*fp->samples_per_slot)-sf_extension];
+
+    VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST, (proc->timestamp_tx-ru->openair0_cfg.tx_sample_advance)&0xffffffff );
+    VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_WRITE, 1 );
+    // prepare tx buffer pointers
+    txs = ru->rfdevice.trx_write_func(&ru->rfdevice,
+                                      proc->timestamp_tx+ru->ts_offset-ru->openair0_cfg.tx_sample_advance-sf_extension,
+                                      txp,
+                                      siglen+sf_extension,
+                                      ru->nb_tx,
+                                      flags);
+    LOG_D(PHY,"[TXPATH] RU %d tx_rf, writing to TS %llu, frame %d, unwrapped_frame %d, subframe %d\n",ru->idx,
+          (long long unsigned int)proc->timestamp_tx,proc->frame_tx,proc->frame_tx_unwrap,proc->tti_tx);
+    VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_WRITE, 0 );
+    AssertFatal(txs ==  siglen+sf_extension,"TX : Timeout (sent %d/%d)\n",txs, siglen);
+  }
+}
+
+
+/*!
+ * \brief The Asynchronous RX/TX FH thread of RAU/RCC/gNB/RRU.
+ * This handles the RX FH for an asynchronous RRU/UE
+ * \param param is a \ref gNB_L1_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 *ru_thread_asynch_rxtx( void *param ) {
+  static int ru_thread_asynch_rxtx_status;
+  RU_t *ru         = (RU_t *)param;
+  RU_proc_t *proc  = &ru->proc;
+  int subframe=0, frame=0;
+  thread_top_init("ru_thread_asynch_rxtx",1,870000L,1000000L,1000000L);
+  // wait for top-level synchronization and do one acquisition to get timestamp for setting frame/subframe
+  wait_sync("ru_thread_asynch_rxtx");
+  // wait for top-level synchronization and do one acquisition to get timestamp for setting frame/subframe
+  printf( "waiting for devices (ru_thread_asynch_rx)\n");
+  wait_on_condition(&proc->mutex_asynch_rxtx,&proc->cond_asynch_rxtx,&proc->instance_cnt_asynch_rxtx,"thread_asynch");
+  printf( "devices ok (ru_thread_asynch_rx)\n");
+
+  while (!oai_exit) {
+    if (oai_exit) break;
+
+    if (subframe==9) {
+      subframe=0;
+      frame++;
+      frame&=1023;
+    } else {
+      subframe++;
+    }
+
+    LOG_D(PHY,"ru_thread_asynch_rxtx: Waiting on incoming fronthaul\n");
+
+    // asynchronous receive from south (Mobipass)
+    if (ru->fh_south_asynch_in) ru->fh_south_asynch_in(ru,&frame,&subframe);
+    // asynchronous receive from north (RRU IF4/IF5)
+    else if (ru->fh_north_asynch_in) {
+      if (nr_slot_select(&ru->gNB_list[0]->gNB_config,subframe)!=SF_UL)
+        ru->fh_north_asynch_in(ru,&frame,&subframe);
+    } else AssertFatal(1==0,"Unknown function in ru_thread_asynch_rxtx\n");
+  }
+
+  ru_thread_asynch_rxtx_status=0;
+  return(&ru_thread_asynch_rxtx_status);
+}
+
+
+
+
+/*!
+ * \brief The prach receive thread of RU.
+ * \param param is a \ref RU_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 *ru_thread_prach( void *param ) {
+  static int ru_thread_prach_status;
+  RU_t *ru        = (RU_t *)param;
+  RU_proc_t *proc = (RU_proc_t *)&ru->proc;
+  // set default return value
+  ru_thread_prach_status = 0;
+  thread_top_init("ru_thread_prach",1,500000L,1000000L,20000000L);
+
+  while (RC.ru_mask>0) {
+    usleep(1e6);
+    LOG_I(PHY,"%s() RACH waiting for RU to be configured\n", __FUNCTION__);
+  }
+
+  LOG_I(PHY,"%s() RU configured - RACH processing thread running\n", __FUNCTION__);
+
+  while (!oai_exit) {
+    if (oai_exit) break;
+
+    if (wait_on_condition(&proc->mutex_prach,&proc->cond_prach,&proc->instance_cnt_prach,"ru_prach_thread") < 0) break;
+
+    VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_RU_PRACH_RX, 1 );
+
+    /*if (ru->gNB_list[0]){
+      prach_procedures(
+        ru->gNB_list[0]
+    #if (RRC_VERSION >= MAKE_VERSION(14, 0, 0))
+        ,0
+    #endif
+        );
+    }
+    else {
+       rx_prach(NULL,
+            ru,
+          NULL,
+                NULL,
+                NULL,
+                proc->frame_prach,
+                0
+    #if (RRC_VERSION >= MAKE_VERSION(14, 0, 0))
+          ,0
+    #endif
+          );
+    }
+    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;
+  }
+
+  LOG_I(PHY, "Exiting RU thread PRACH\n");
+  ru_thread_prach_status = 0;
+  return &ru_thread_prach_status;
+}
+
+
+int wakeup_synch(RU_t *ru) {
+  struct timespec wait;
+  wait.tv_sec=0;
+  wait.tv_nsec=5000000L;
+
+  // wake up synch thread
+  // lock the synch mutex and make sure the thread is ready
+  if (pthread_mutex_timedlock(&ru->proc.mutex_synch,&wait) != 0) {
+    LOG_E( PHY, "[RU] ERROR pthread_mutex_lock for RU synch thread (IC %d)\n", ru->proc.instance_cnt_synch );
+    exit_fun( "error locking mutex_synch" );
+    return(-1);
+  }
+
+  ++ru->proc.instance_cnt_synch;
+
+  // the thread can now be woken up
+  if (pthread_cond_signal(&ru->proc.cond_synch) != 0) {
+    LOG_E( PHY, "[RU] ERROR pthread_cond_signal for RU synch thread\n");
+    exit_fun( "ERROR pthread_cond_signal" );
+    return(-1);
+  }
+
+  pthread_mutex_unlock( &ru->proc.mutex_synch );
+  return(0);
+}
+
+void do_ru_synch(RU_t *ru) {
+  NR_DL_FRAME_PARMS *fp  = ru->nr_frame_parms;
+  RU_proc_t *proc         = &ru->proc;
+  int i;
+  void *rxp[2],*rxp2[2];
+  int32_t dummy_rx[ru->nb_rx][fp->samples_per_subframe] __attribute__((aligned(32)));
+  int rxs;
+  int ic;
+
+  // initialize the synchronization buffer to the common_vars.rxdata
+  for (int i=0; i<ru->nb_rx; i++)
+    rxp[i] = &ru->common.rxdata[i][0];
+
+  double temp_freq1 = ru->rfdevice.openair0_cfg->rx_freq[0];
+  double temp_freq2 = ru->rfdevice.openair0_cfg->tx_freq[0];
+
+  for (i=0; i<4; i++) {
+    ru->rfdevice.openair0_cfg->rx_freq[i] = ru->rfdevice.openair0_cfg->tx_freq[i];
+    ru->rfdevice.openair0_cfg->tx_freq[i] = temp_freq1;
+  }
+
+  ru->rfdevice.trx_set_freq_func(&ru->rfdevice,ru->rfdevice.openair0_cfg,0);
+
+  while ((ru->in_synch ==0)&&(!oai_exit)) {
+    // read in frame
+    rxs = ru->rfdevice.trx_read_func(&ru->rfdevice,
+                                     &(proc->timestamp_rx),
+                                     rxp,
+                                     fp->samples_per_subframe*10,
+                                     ru->nb_rx);
+
+    if (rxs != fp->samples_per_subframe*10) LOG_E(PHY,"requested %d samples, got %d\n",fp->samples_per_subframe*10,rxs);
+
+    // wakeup synchronization processing thread
+    wakeup_synch(ru);
+    ic=0;
+
+    while ((ic>=0)&&(!oai_exit)) {
+      // continuously read in frames, 1ms at a time,
+      // until we are done with the synchronization procedure
+      for (i=0; i<ru->nb_rx; i++)
+        rxp2[i] = (void *)&dummy_rx[i][0];
+
+      for (i=0; i<10; i++)
+        rxs = ru->rfdevice.trx_read_func(&ru->rfdevice,
+                                         &(proc->timestamp_rx),
+                                         rxp2,
+                                         fp->samples_per_subframe,
+                                         ru->nb_rx);
+
+      pthread_mutex_lock(&ru->proc.mutex_synch);
+      ic = ru->proc.instance_cnt_synch;
+      pthread_mutex_unlock(&ru->proc.mutex_synch);
+    } // ic>=0
+  } // in_synch==0
+
+  // read in rx_offset samples
+  LOG_I(PHY,"Resynchronizing by %d samples\n",ru->rx_offset);
+  rxs = ru->rfdevice.trx_read_func(&ru->rfdevice,
+                                   &(proc->timestamp_rx),
+                                   rxp,
+                                   ru->rx_offset,
+                                   ru->nb_rx);
+
+  for (i=0; i<4; i++) {
+    ru->rfdevice.openair0_cfg->rx_freq[i] = temp_freq1;
+    ru->rfdevice.openair0_cfg->tx_freq[i] = temp_freq2;
+  }
+
+  ru->rfdevice.trx_set_freq_func(&ru->rfdevice,ru->rfdevice.openair0_cfg,0);
+}
+
+
+
+void wakeup_gNB_L1s(RU_t *ru) {
+  int i;
+  PHY_VARS_gNB **gNB_list = ru->gNB_list;
+  LOG_D(PHY,"wakeup_gNB_L1s (num %d) for RU %d ru->gNB_top:%p\n",ru->num_gNB,ru->idx, ru->gNB_top);
+
+  if (ru->num_gNB==1 && ru->gNB_top!=0 && get_thread_parallel_conf() == PARALLEL_SINGLE_THREAD) {
+    // call gNB function directly
+    char string[20];
+    sprintf(string,"Incoming RU %d",ru->idx);
+    LOG_D(PHY,"RU %d Call gNB_top\n",ru->idx);
+    ru->gNB_top(gNB_list[0],ru->proc.frame_rx,ru->proc.tti_rx,string,ru);
+  } else {
+    LOG_D(PHY,"ru->num_gNB:%d\n", ru->num_gNB);
+
+    for (i=0; i<ru->num_gNB; i++) {
+      LOG_D(PHY,"ru->wakeup_rxtx:%p\n", ru->nr_wakeup_rxtx);
+
+      if (ru->nr_wakeup_rxtx!=0 && ru->nr_wakeup_rxtx(gNB_list[i],ru) < 0) {
+        LOG_E(PHY,"could not wakeup gNB rxtx process for subframe %d\n", ru->proc.tti_rx);
+      }
+    }
+  }
+}
+
+static inline int wakeup_prach_ru(RU_t *ru) {
+  struct timespec wait;
+  wait.tv_sec=0;
+  wait.tv_nsec=5000000L;
+
+  if (pthread_mutex_timedlock(&ru->proc.mutex_prach,&wait) !=0) {
+    LOG_E( PHY, "[RU] ERROR pthread_mutex_lock for RU prach thread (IC %d)\n", ru->proc.instance_cnt_prach);
+    exit_fun( "error locking mutex_rxtx" );
+    return(-1);
+  }
+
+  if (ru->proc.instance_cnt_prach==-1) {
+    ++ru->proc.instance_cnt_prach;
+    ru->proc.frame_prach    = ru->proc.frame_rx;
+    ru->proc.subframe_prach = ru->proc.tti_rx;
+
+    // DJP - think prach_procedures() is looking at gNB frame_prach
+    if (ru->gNB_list[0]) {
+      ru->gNB_list[0]->proc.frame_prach = ru->proc.frame_rx;
+      ru->gNB_list[0]->proc.slot_prach = ru->proc.tti_rx;
+    }
+
+    LOG_I(PHY,"RU %d: waking up PRACH thread\n",ru->idx);
+    // the thread can now be woken up
+    AssertFatal(pthread_cond_signal(&ru->proc.cond_prach) == 0, "ERROR pthread_cond_signal for RU prach thread\n");
+  } else LOG_W(PHY,"RU prach thread busy, skipping\n");
+
+  pthread_mutex_unlock( &ru->proc.mutex_prach );
+  return(0);
+}
+
+// this is for RU with local RF unit
+void fill_rf_config(RU_t *ru, char *rf_config_file) {
+  int i;
+  NR_DL_FRAME_PARMS *fp   = ru->nr_frame_parms;
+  nfapi_nr_config_request_t *gNB_config = &ru->gNB_list[0]->gNB_config; //tmp index
+  openair0_config_t *cfg   = &ru->openair0_cfg;
+  int N_RB = gNB_config->rf_config.dl_carrier_bandwidth.value;
+  int mu = gNB_config->subframe_config.numerology_index_mu.value;
+
+  if (mu == NR_MU_0) { //or if LTE
+    if(N_RB == 100) {
+      if (fp->threequarter_fs) {
+        cfg->sample_rate=23.04e6;
+        cfg->samples_per_frame = 230400;
+        cfg->tx_bw = 10e6;
+        cfg->rx_bw = 10e6;
+      } else {
+        cfg->sample_rate=30.72e6;
+        cfg->samples_per_frame = 307200;
+        cfg->tx_bw = 10e6;
+        cfg->rx_bw = 10e6;
+      }
+    } else if(N_RB == 50) {
+      cfg->sample_rate=15.36e6;
+      cfg->samples_per_frame = 153600;
+      cfg->tx_bw = 5e6;
+      cfg->rx_bw = 5e6;
+    } else if (N_RB == 25) {
+      cfg->sample_rate=7.68e6;
+      cfg->samples_per_frame = 76800;
+      cfg->tx_bw = 2.5e6;
+      cfg->rx_bw = 2.5e6;
+    } else if (N_RB == 6) {
+      cfg->sample_rate=1.92e6;
+      cfg->samples_per_frame = 19200;
+      cfg->tx_bw = 1.5e6;
+      cfg->rx_bw = 1.5e6;
+    } else AssertFatal(1==0,"Unknown N_RB %d\n",N_RB);
+  } else if (mu == NR_MU_1) {
+    if(N_RB == 217) {
+      if (fp->threequarter_fs) {
+        cfg->sample_rate=92.16e6;
+        cfg->samples_per_frame = 921600;
+        cfg->tx_bw = 40e6;
+        cfg->rx_bw = 40e6;
+      } else {
+        cfg->sample_rate=122.88e6;
+        cfg->samples_per_frame = 1228800;
+        cfg->tx_bw = 40e6;
+        cfg->rx_bw = 40e6;
+      }
+    } else if(N_RB == 106) {
+      cfg->sample_rate=61.44e6;
+      cfg->samples_per_frame = 614400;
+      cfg->tx_bw = 20e6;
+      cfg->rx_bw = 20e6;
+    } else {
+      AssertFatal(0==1,"N_RB %d not yet supported for numerology %d\n",N_RB,mu);
+    }
+  } else {
+    AssertFatal(0 == 1,"Numerology %d not supported for the moment\n",mu);
+  }
+
+  if (gNB_config->subframe_config.duplex_mode.value==TDD)
+    cfg->duplex_mode = duplex_mode_TDD;
+  else //FDD
+    cfg->duplex_mode = duplex_mode_FDD;
+
+  cfg->Mod_id = 0;
+  cfg->num_rb_dl=N_RB;
+  cfg->tx_num_channels=ru->nb_tx;
+  cfg->rx_num_channels=ru->nb_rx;
+
+  for (i=0; i<ru->nb_tx; i++) {
+    cfg->tx_freq[i] = (double)fp->dl_CarrierFreq;
+    cfg->rx_freq[i] = (double)fp->ul_CarrierFreq;
+    cfg->tx_gain[i] = ru->att_tx;
+    cfg->rx_gain[i] = ru->max_rxgain-ru->att_rx;
+    cfg->configFilename = rf_config_file;
+    printf("channel %d, Setting tx_gain offset %f, rx_gain offset %f, tx_freq %f, rx_freq %f\n",
+           i, cfg->tx_gain[i],
+           cfg->rx_gain[i],
+           cfg->tx_freq[i],
+           cfg->rx_freq[i]);
+  }
+}
+
+/* this function maps the RU tx and rx buffers to the available rf chains.
+   Each rf chain is is addressed by the card number and the chain on the card. The
+   rf_map specifies for each antenna port, on which rf chain the mapping should start. Multiple
+   antennas are mapped to successive RF chains on the same card. */
+int setup_RU_buffers(RU_t *ru) {
+  int i,j;
+  int card,ant;
+  //uint16_t N_TA_offset = 0;
+  NR_DL_FRAME_PARMS *frame_parms;
+  //nfapi_nr_config_request_t *gNB_config = ru->gNB_list[0]->gNB_config; //tmp index
+
+  if (ru) {
+    frame_parms = ru->nr_frame_parms;
+    printf("setup_RU_buffers: frame_parms = %p\n",frame_parms);
+  } else {
+    printf("RU[%d] not initialized\n", ru->idx);
+    return(-1);
+  }
+
+  /*  if (frame_parms->frame_type == TDD) {
+      if      (frame_parms->N_RB_DL == 100) ru->N_TA_offset = 624;
+      else if (frame_parms->N_RB_DL == 50)  ru->N_TA_offset = 624/2;
+      else if (frame_parms->N_RB_DL == 25)  ru->N_TA_offset = 624/4;
+    } */
+  if (ru->openair0_cfg.mmapped_dma == 1) {
+    // replace RX signal buffers with mmaped HW versions
+    for (i=0; i<ru->nb_rx; i++) {
+      card = i/4;
+      ant = i%4;
+      printf("Mapping RU id %d, rx_ant %d, on card %d, chain %d\n",ru->idx,i,ru->rf_map.card+card, ru->rf_map.chain+ant);
+      free(ru->common.rxdata[i]);
+      ru->common.rxdata[i] = ru->openair0_cfg.rxbase[ru->rf_map.chain+ant];
+      printf("rxdata[%d] @ %p\n",i,ru->common.rxdata[i]);
+
+      for (j=0; j<16; j++) {
+        printf("rxbuffer %d: %x\n",j,ru->common.rxdata[i][j]);
+        ru->common.rxdata[i][j] = 16-j;
+      }
+    }
+
+    for (i=0; i<ru->nb_tx; i++) {
+      card = i/4;
+      ant = i%4;
+      printf("Mapping RU id %d, tx_ant %d, on card %d, chain %d\n",ru->idx,i,ru->rf_map.card+card, ru->rf_map.chain+ant);
+      free(ru->common.txdata[i]);
+      ru->common.txdata[i] = ru->openair0_cfg.txbase[ru->rf_map.chain+ant];
+      printf("txdata[%d] @ %p\n",i,ru->common.txdata[i]);
+
+      for (j=0; j<16; j++) {
+        printf("txbuffer %d: %x\n",j,ru->common.txdata[i][j]);
+        ru->common.txdata[i][j] = 16-j;
+      }
+    }
+  } else { // not memory-mapped DMA
+    //nothing to do, everything already allocated in lte_init
+  }
+
+  return(0);
+}
+
+static void *ru_stats_thread(void *param) {
+  RU_t               *ru      = (RU_t *)param;
+  wait_sync("ru_stats_thread");
+
+  while (!oai_exit) {
+    sleep(1);
+
+    if (opp_enabled == 1) {
+      if (ru->feprx) print_meas(&ru->ofdm_demod_stats,"feprx",NULL,NULL);
+
+      if (ru->feptx_ofdm) print_meas(&ru->ofdm_mod_stats,"feptx_ofdm",NULL,NULL);
+
+      if (ru->fh_north_asynch_in) print_meas(&ru->rx_fhaul,"rx_fhaul",NULL,NULL);
+
+      if (ru->fh_north_out) {
+        print_meas(&ru->tx_fhaul,"tx_fhaul",NULL,NULL);
+        print_meas(&ru->compression,"compression",NULL,NULL);
+        print_meas(&ru->transport,"transport",NULL,NULL);
+      }
+    }
+  }
+
+  return(NULL);
+}
+
+static void *ru_thread_tx( void *param ) {
+  RU_t *ru              = (RU_t *)param;
+  RU_proc_t *proc       = &ru->proc;
+  PHY_VARS_gNB *gNB;
+  gNB_L1_proc_t *gNB_proc;
+  gNB_L1_rxtx_proc_t *L1_proc;
+  NR_DL_FRAME_PARMS *fp      = ru->nr_frame_parms;
+  char               filename[40];
+  int                print_frame = 8;
+  int                i = 0;
+  cpu_set_t cpuset;
+  CPU_ZERO(&cpuset);
+  thread_top_init("ru_thread_tx",1,400000,500000,500000);
+  //CPU_SET(5, &cpuset);
+  //pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
+  //wait_sync("ru_thread_tx");
+  wait_on_condition(&proc->mutex_FH1,&proc->cond_FH1,&proc->instance_cnt_FH1,"ru_thread_tx");
+  printf( "ru_thread_tx ready\n");
+
+  while (!oai_exit) {
+    if (oai_exit) break;
+
+    LOG_I(PHY,"ru_thread_tx: Waiting for TX processing\n");
+    // wait until eNBs are finished subframe RX n and TX n+4
+    wait_on_condition(&proc->mutex_gNBs,&proc->cond_gNBs,&proc->instance_cnt_gNBs,"ru_thread_tx");
+
+    if (oai_exit) break;
+
+    //printf("~~~~~~~~~~~~~~~~start process for ru_thread_tx %d.%d\n", proc->frame_tx, proc->tti_tx);
+    VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_TX0_RU, proc->frame_tx );
+    VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TTI_NUMBER_TX0_RU, proc->tti_tx );
+
+    // do TX front-end processing if needed (precoding and/or IDFTs)
+    if (ru->feptx_prec) ru->feptx_prec(ru);
+
+    // do OFDM if needed
+    if ((ru->fh_north_asynch_in == NULL) && (ru->feptx_ofdm)) ru->feptx_ofdm(ru);
+
+    if(!emulate_rf) {
+      // do outgoing fronthaul (south) if needed
+      if ((ru->fh_north_asynch_in == NULL) && (ru->fh_south_out)) ru->fh_south_out(ru);
+
+      if (ru->fh_north_out) ru->fh_north_out(ru);
+    } else {
+      if(proc->frame_tx == print_frame) {
+        for (i=0; i<ru->nb_tx; i++) {
+          sprintf(filename,"tx%ddataF_frame%d_sl%d.m", i, print_frame, proc->tti_tx);
+          LOG_M(filename,"txdataF_frame",&ru->common.txdataF_BF[i][0],fp->samples_per_subframe_wCP, 1, 1);
+
+          if(proc->tti_tx == 9) {
+            sprintf(filename,"tx%ddata_frame%d.m", i, print_frame);
+            LOG_M(filename,"txdata_frame",&ru->common.txdata[i][0],fp->samples_per_frame, 1, 1);
+            sprintf(filename,"tx%ddata_frame%d.dat", i, print_frame);
+            FILE *output_fd = fopen(filename,"w");
+
+            if (output_fd) {
+              fwrite(&ru->common.txdata[i][0],
+                     sizeof(int32_t),
+                     fp->samples_per_frame,
+                     output_fd);
+              fclose(output_fd);
+            } else {
+              LOG_E(PHY,"Cannot write to file %s\n",filename);
+            }
+          }//if(proc->tti_tx == 9)
+        }//for (i=0; i<ru->nb_tx; i++)
+      }//if(proc->frame_tx == print_frame)
+    }//else  emulate_rf
+
+    release_thread(&proc->mutex_gNBs,&proc->instance_cnt_gNBs,"ru_thread_tx");
+
+    for(i = 0; i<ru->num_gNB; i++) {
+      gNB       = ru->gNB_list[i];
+      gNB_proc  = &gNB->proc;
+      L1_proc   = (get_thread_parallel_conf() == PARALLEL_RU_L1_TRX_SPLIT)? &gNB_proc->L1_proc_tx : &gNB_proc->L1_proc;
+      pthread_mutex_lock(&gNB_proc->mutex_RU_tx);
+
+      for (int j=0; j<gNB->num_RU; j++) {
+        if (ru == gNB->RU_list[j]) {
+          if ((gNB_proc->RU_mask_tx&(1<<j)) > 0)
+            LOG_E(PHY,"eNB %d frame %d, subframe %d : previous information from RU tx %d (num_RU %d,mask %x) has not been served yet!\n",
+                  gNB->Mod_id,gNB_proc->frame_rx,gNB_proc->slot_rx,ru->idx,gNB->num_RU,gNB_proc->RU_mask_tx);
+
+          gNB_proc->RU_mask_tx |= (1<<j);
+        }
+      }
+
+      if (gNB_proc->RU_mask_tx != (1<<gNB->num_RU)-1) {  // not all RUs have provided their information so return
+        pthread_mutex_unlock(&gNB_proc->mutex_RU_tx);
+      } else { // all RUs TX are finished so send the ready signal to eNB processing
+        gNB_proc->RU_mask_tx = 0;
+        pthread_mutex_unlock(&gNB_proc->mutex_RU_tx);
+        pthread_mutex_lock( &L1_proc->mutex_RUs);
+        L1_proc->instance_cnt_RUs = 0;
+
+        // the thread can now be woken up
+        if (pthread_cond_signal(&L1_proc->cond_RUs) != 0) {
+          LOG_E( PHY, "[eNB] ERROR pthread_cond_signal for eNB TXnp4 thread\n");
+          exit_fun( "ERROR pthread_cond_signal" );
+        }
+
+        pthread_mutex_unlock( &L1_proc->mutex_RUs );
+      }
+    }
+  }
+
+  release_thread(&proc->mutex_FH1,&proc->instance_cnt_FH1,"ru_thread_tx");
+  return 0;
+}
+
+static void *ru_thread( void *param ) {
+  static int ru_thread_status;
+  RU_t               *ru      = (RU_t *)param;
+  RU_proc_t          *proc    = &ru->proc;
+  NR_DL_FRAME_PARMS *fp      = ru->nr_frame_parms;
+  int                ret;
+  int                slot = fp->slots_per_frame-1;
+  int                frame    =1023;
+  char               filename[40],threadname[40];
+  int                print_frame = 8;
+  int                i = 0;
+  // set default return value
+  ru_thread_status = 0;
+  // set default return value
+  sprintf(threadname,"ru_thread %d",ru->idx);
+  thread_top_init(threadname,0,870000,1000000,1000000);
+  LOG_I(PHY,"Starting RU %d (%s,%s),\n",ru->idx,NB_functions[ru->function],NB_timing[ru->if_timing]);
+
+  if(emulate_rf) {
+    fill_rf_config(ru,ru->rf_config_file);
+    nr_init_frame_parms(&ru->gNB_list[0]->gNB_config, fp);
+    nr_dump_frame_parms(fp);
+    nr_phy_init_RU(ru);
+
+    if (setup_RU_buffers(ru)!=0) {
+      printf("Exiting, cannot initialize RU Buffers\n");
+      exit(-1);
+    }
+  } else {
+    // Start IF device if any
+    if (ru->start_if) {
+      LOG_I(PHY,"Starting IF interface for RU %d\n",ru->idx);
+      AssertFatal(ru->start_if(ru,NULL) == 0, "Could not start the IF device\n");
+
+      if (ru->if_south == LOCAL_RF) ret = connect_rau(ru);
+      else ret = attach_rru(ru);
+
+      AssertFatal(ret==0,"Cannot connect to remote radio\n");
+    }
+
+    if (ru->if_south == LOCAL_RF) { // configure RF parameters only
+      fill_rf_config(ru,ru->rf_config_file);
+      nr_init_frame_parms(&ru->gNB_list[0]->gNB_config, fp);
+      nr_dump_frame_parms(fp);
+      nr_phy_init_RU(ru);
+      ret = openair0_device_load(&ru->rfdevice,&ru->openair0_cfg);
+      AssertFatal(ret==0,"Cannot connect to local radio\n");
+    }
+
+    if (setup_RU_buffers(ru)!=0) {
+      printf("Exiting, cannot initialize RU Buffers\n");
+      exit(-1);
+    }
+  }
+
+  LOG_I(PHY, "Signaling main thread that RU %d is ready\n",ru->idx);
+  pthread_mutex_lock(&RC.ru_mutex);
+  RC.ru_mask &= ~(1<<ru->idx);
+  pthread_cond_signal(&RC.ru_cond);
+  pthread_mutex_unlock(&RC.ru_mutex);
+  wait_sync("ru_thread");
+
+  if(!emulate_rf) {
+    // Start RF device if any
+    if (ru->start_rf) {
+      if (ru->start_rf(ru) != 0)
+        LOG_E(HW,"Could not start the RF device\n");
+      else LOG_I(PHY,"RU %d rf device ready\n",ru->idx);
+    } else LOG_I(PHY,"RU %d no rf device\n",ru->idx);
+
+    // if an asnych_rxtx thread exists
+    // wakeup the thread because the devices are ready at this point
+
+    if ((ru->fh_south_asynch_in)||(ru->fh_north_asynch_in)) {
+      pthread_mutex_lock(&proc->mutex_asynch_rxtx);
+      proc->instance_cnt_asynch_rxtx=0;
+      pthread_mutex_unlock(&proc->mutex_asynch_rxtx);
+      pthread_cond_signal(&proc->cond_asynch_rxtx);
+    } else LOG_I(PHY,"RU %d no asynch_south interface\n",ru->idx);
+
+    // if this is a slave RRU, try to synchronize on the DL frequency
+    if ((ru->is_slave) && (ru->if_south == LOCAL_RF)) do_ru_synch(ru);
+  }
+
+  pthread_mutex_lock(&proc->mutex_FH1);
+  proc->instance_cnt_FH1 = 0;
+  pthread_mutex_unlock(&proc->mutex_FH1);
+  pthread_cond_signal(&proc->cond_FH1);
+
+  // This is a forever while loop, it loops over subframes which are scheduled by incoming samples from HW devices
+  while (!oai_exit) {
+    // these are local subframe/frame counters to check that we are in synch with the fronthaul timing.
+    // They are set on the first rx/tx in the underly FH routines.
+    if (slot==(fp->slots_per_frame-1)) {
+      slot=0;
+      frame++;
+      frame&=1023;
+    } else {
+      slot++;
+    }
+
+    // synchronization on input FH interface, acquire signals/data and block
+    if (ru->fh_south_in) ru->fh_south_in(ru,&frame,&slot);
+    else AssertFatal(1==0, "No fronthaul interface at south port");
+
+    LOG_D(PHY,"AFTER fh_south_in - SFN/SL:%d%d RU->proc[RX:%d.%d TX:%d.%d] RC.gNB[0][0]:[RX:%d%d TX(SFN):%d]\n",
+          frame,slot,
+          proc->frame_rx,proc->tti_rx,
+          proc->frame_tx,proc->tti_tx,
+          RC.gNB[0][0]->proc.frame_rx,RC.gNB[0][0]->proc.slot_rx,
+          RC.gNB[0][0]->proc.frame_tx);
+    /*
+          LOG_D(PHY,"RU thread (do_prach %d, is_prach_subframe %d), received frame %d, subframe %d\n",
+              ru->do_prach,
+              is_prach_subframe(fp, proc->frame_rx, proc->tti_rx),
+              proc->frame_rx,proc->tti_rx);
+
+        if ((ru->do_prach>0) && (is_prach_subframe(fp, proc->frame_rx, proc->tti_rx)==1)) {
+          wakeup_prach_ru(ru);
+        }*/
+
+    // adjust for timing offset between RU
+    //printf("~~~~~~~~~~~~~~~~~~~~~~~~~~%d.%d in ru_thread is in process\n", proc->frame_rx, proc->tti_rx);
+    if (ru->idx!=0) proc->frame_tx = (proc->frame_tx+proc->frame_offset)&1023;
+
+    // do RX front-end processing (frequency-shift, dft) if needed
+    if (ru->feprx) ru->feprx(ru);
+
+    // At this point, all information for subframe has been received on FH interface
+
+    // wakeup all gNB processes waiting for this RU
+    if (ru->num_gNB>0) wakeup_gNB_L1s(ru);
+
+    if(get_thread_parallel_conf() == PARALLEL_SINGLE_THREAD && ru->num_eNB==0) {
+      // do TX front-end processing if needed (precoding and/or IDFTs)
+      if (ru->feptx_prec) ru->feptx_prec(ru);
+
+      // do OFDM if needed
+      if ((ru->fh_north_asynch_in == NULL) && (ru->feptx_ofdm)) ru->feptx_ofdm(ru);
+
+      if(!emulate_rf) {
+        // do outgoing fronthaul (south) if needed
+        if ((ru->fh_north_asynch_in == NULL) && (ru->fh_south_out)) ru->fh_south_out(ru);
+
+        if (ru->fh_north_out) ru->fh_north_out(ru);
+      } else {
+        if(proc->frame_tx == print_frame) {
+          for (i=0; i<ru->nb_tx; i++) {
+            sprintf(filename,"tx%ddataF_frame%d_sl%d.m", i, print_frame, proc->tti_tx);
+            LOG_M(filename,"txdataF_frame",&ru->common.txdataF_BF[i][0],fp->samples_per_slot_wCP, 1, 1);
+
+            if(proc->tti_tx == 9) {
+              sprintf(filename,"tx%ddata_frame%d.m", i, print_frame);
+              LOG_M(filename,"txdata_frame",&ru->common.txdata[i][0],fp->samples_per_frame, 1, 1);
+              sprintf(filename,"tx%ddata_frame%d.dat", i, print_frame);
+              FILE *output_fd = fopen(filename,"w");
+
+              if (output_fd) {
+                fwrite(&ru->common.txdata[i][0],
+                       sizeof(int32_t),
+                       fp->samples_per_frame,
+                       output_fd);
+                fclose(output_fd);
+              } else {
+                LOG_E(PHY,"Cannot write to file %s\n",filename);
+              }
+            }//if(proc->tti_tx == 9)
+          }//for (i=0; i<ru->nb_tx; i++)
+        }//if(proc->frame_tx == print_frame)
+      }//else  emulate_rf
+
+      proc->emulate_rf_busy = 0;
+    }//if(get_thread_parallel_conf() == PARALLEL_SINGLE_THREAD)
+  }
+
+  printf( "Exiting ru_thread \n");
+
+  if (ru->stop_rf != NULL) {
+    if (ru->stop_rf(ru) != 0)
+      LOG_E(HW,"Could not stop the RF device\n");
+    else LOG_I(PHY,"RU %d rf device stopped\n",ru->idx);
+  }
+
+  ru_thread_status = 0;
+  return &ru_thread_status;
+}
+/*
+// This thread run the initial synchronization like a UE
+void *ru_thread_synch(void *arg) {
+
+  RU_t *ru = (RU_t*)arg;
+  NR_DL_FRAME_PARMS *fp=ru->nr_frame_parms;
+  int32_t sync_pos,sync_pos2;
+  uint32_t peak_val;
+  uint32_t sync_corr[307200] __attribute__((aligned(32)));
+  static int ru_thread_synch_status;
+
+
+  thread_top_init("ru_thread_synch",0,5000000,10000000,10000000);
+
+  wait_sync("ru_thread_synch");
+
+  // initialize variables for PSS detection
+  lte_sync_time_init(ru->nr_frame_parms);
+
+  while (!oai_exit) {
+
+    // wait to be woken up
+    if (wait_on_condition(&ru->proc.mutex_synch,&ru->proc.cond_synch,&ru->proc.instance_cnt_synch,"ru_thread_synch")<0) break;
+
+    // if we're not in synch, then run initial synch
+    if (ru->in_synch == 0) {
+      // run intial synch like UE
+      LOG_I(PHY,"Running initial synchronization\n");
+
+      sync_pos = lte_sync_time_gNB(ru->common.rxdata,
+           fp,
+           fp->samples_per_subframe*5,
+           &peak_val,
+           sync_corr);
+      LOG_I(PHY,"RU synch: %d, val %d\n",sync_pos,peak_val);
+
+      if (sync_pos >= 0) {
+  if (sync_pos >= fp->nb_prefix_samples)
+    sync_pos2 = sync_pos - fp->nb_prefix_samples;
+  else
+    sync_pos2 = sync_pos + (fp->samples_per_subframe*10) - fp->nb_prefix_samples;
+
+  if (fp->frame_type == FDD) {
+
+    // PSS is hypothesized in last symbol of first slot in Frame
+    int sync_pos_slot = (fp->samples_per_subframe>>1) - fp->ofdm_symbol_size - fp->nb_prefix_samples;
+
+    if (sync_pos2 >= sync_pos_slot)
+      ru->rx_offset = sync_pos2 - sync_pos_slot;
+    else
+      ru->rx_offset = (fp->samples_per_subframe*10) + sync_pos2 - sync_pos_slot;
+  }
+  else {
+
+  }
+
+  LOG_I(PHY,"Estimated sync_pos %d, peak_val %d => timing offset %d\n",sync_pos,peak_val,ru->rx_offset);
+
+  if ((peak_val > 300000) && (sync_pos > 0)) {
+  //      if (sync_pos++ > 3) {
+  write_output("ru_sync.m","sync",(void*)&sync_corr[0],fp->samples_per_subframe*5,1,2);
+  write_output("ru_rx.m","rxs",(void*)ru->ru_time.rxdata[0][0],fp->samples_per_subframe*10,1,1);
+  exit(-1);
+  }
+
+  ru->in_synch=1;
+      }
+    }
+
+    if (release_thread(&ru->proc.mutex_synch,&ru->proc.instance_cnt_synch,"ru_synch_thread") < 0) break;
+  } // oai_exit
+
+  ru_thread_synch_status = 0;
+  return &ru_thread_synch_status;
+
+}
+*/
+
+int start_if(struct RU_t_s *ru,struct PHY_VARS_gNB_s *gNB) {
+  return(ru->ifdevice.trx_start_func(&ru->ifdevice));
+}
+
+int start_rf(RU_t *ru) {
+  return(ru->rfdevice.trx_start_func(&ru->rfdevice));
+}
+
+int stop_rf(RU_t *ru) {
+  ru->rfdevice.trx_end_func(&ru->rfdevice);
+  return 0;
+}
+
+extern void fep_full(RU_t *ru);
+extern void ru_fep_full_2thread(RU_t *ru);
+extern void nr_feptx_ofdm(RU_t *ru);
+extern void nr_feptx_ofdm_2thread(RU_t *ru);
+extern void feptx_prec(RU_t *ru);
+extern void init_fep_thread(RU_t *ru,pthread_attr_t *attr);
+extern void init_nr_feptx_thread(RU_t *ru,pthread_attr_t *attr);
+
+void init_RU_proc(RU_t *ru) {
+  int i=0;
+  RU_proc_t *proc;
+  pthread_attr_t *attr_FH=NULL, *attr_FH1=NULL,*attr_prach=NULL,*attr_asynch=NULL, *attr_emulateRF=NULL;// *attr_synch=NULL;
+  //pthread_attr_t *attr_fep=NULL;
+#if (RRC_VERSION >= MAKE_VERSION(14, 0, 0))
+  //pthread_attr_t *attr_prach_br=NULL;
+#endif
+  char name[100];
+#ifndef OCP_FRAMEWORK
+  LOG_I(PHY,"Initializing RU proc %d (%s,%s),\n",ru->idx,NB_functions[ru->function],NB_timing[ru->if_timing]);
+#endif
+  proc = &ru->proc;
+  memset((void *)proc,0,sizeof(RU_proc_t));
+  proc->ru = ru;
+  proc->instance_cnt_prach       = -1;
+  proc->instance_cnt_synch       = -1;     ;
+  proc->instance_cnt_FH          = -1;
+  proc->instance_cnt_FH1         = -1;
+  proc->instance_cnt_gNBs        = -1;
+  proc->instance_cnt_asynch_rxtx = -1;
+  proc->instance_cnt_emulateRF   = -1;
+  proc->first_rx                 = 1;
+  proc->first_tx                 = 1;
+  proc->frame_offset             = 0;
+  proc->num_slaves               = 0;
+  proc->frame_tx_unwrap          = 0;
+
+  for (i=0; i<10; i++) proc->symbol_mask[i]=0;
+
+  pthread_mutex_init( &proc->mutex_prach, NULL);
+  pthread_mutex_init( &proc->mutex_asynch_rxtx, NULL);
+  pthread_mutex_init( &proc->mutex_synch,NULL);
+  pthread_mutex_init( &proc->mutex_FH,NULL);
+  pthread_mutex_init( &proc->mutex_FH1,NULL);
+  pthread_mutex_init( &proc->mutex_emulateRF,NULL);
+  pthread_mutex_init( &proc->mutex_gNBs, NULL);
+  pthread_cond_init( &proc->cond_prach, NULL);
+  pthread_cond_init( &proc->cond_FH, NULL);
+  pthread_cond_init( &proc->cond_FH1, NULL);
+  pthread_cond_init( &proc->cond_emulateRF, NULL);
+  pthread_cond_init( &proc->cond_asynch_rxtx, NULL);
+  pthread_cond_init( &proc->cond_synch,NULL);
+  pthread_cond_init( &proc->cond_gNBs, NULL);
+  pthread_attr_init( &proc->attr_FH);
+  pthread_attr_init( &proc->attr_FH1);
+  pthread_attr_init( &proc->attr_emulateRF);
+  pthread_attr_init( &proc->attr_prach);
+  pthread_attr_init( &proc->attr_synch);
+  pthread_attr_init( &proc->attr_asynch_rxtx);
+  pthread_attr_init( &proc->attr_fep);
+#ifndef DEADLINE_SCHEDULER
+  attr_FH        = &proc->attr_FH;
+  attr_FH1       = &proc->attr_FH1;
+  attr_emulateRF = &proc->attr_emulateRF;
+  attr_prach     = &proc->attr_prach;
+  //attr_synch     = &proc->attr_synch;
+  attr_asynch    = &proc->attr_asynch_rxtx;
+#endif
+  threadCreate( &proc->pthread_FH, ru_thread, (void *)ru, "thread_FH", -1, OAI_PRIORITY_RT );
+
+  if (get_thread_parallel_conf() == PARALLEL_RU_L1_SPLIT || get_thread_parallel_conf() == PARALLEL_RU_L1_TRX_SPLIT)
+    threadCreate( &proc->pthread_FH1, ru_thread_tx, (void *)ru, "thread_FH1", -1, OAI_PRIORITY_RT );
+
+  if(emulate_rf)
+    threadCreate( &proc->pthread_emulateRF, emulatedRF_thread, (void *)proc, "emulateRF", -1, OAI_PRIORITY_RT );
+
+  if (ru->function == NGFI_RRU_IF4p5) {
+    threadCreate( &proc->pthread_prach, ru_thread_prach, (void *)ru, "RACH", -1, OAI_PRIORITY_RT );
+    ///tmp deactivation of synch thread
+    //    if (ru->is_slave == 1) pthread_create( &proc->pthread_synch, attr_synch, ru_thread_synch, (void*)ru);
+
+    if ((ru->if_timing == synch_to_other) ||
+        (ru->function == NGFI_RRU_IF5) ||
+        (ru->function == NGFI_RRU_IF4p5)) threadCreate( &proc->pthread_asynch_rxtx, ru_thread_asynch_rxtx, (void *)ru, "asynch_rxtx", -1, OAI_PRIORITY_RT );
+
+    snprintf( name, sizeof(name), "ru_thread_FH %d", ru->idx );
+    pthread_setname_np( proc->pthread_FH, name );
+  } else if (ru->function == gNodeB_3GPP && ru->if_south == LOCAL_RF) { // DJP - need something else to distinguish between monolithic and PNF
+    LOG_I(PHY,"%s() DJP - added creation of pthread_prach\n", __FUNCTION__);
+    threadCreate( &proc->pthread_prach, ru_thread_prach, (void *)ru,"RACH", -1, OAI_PRIORITY_RT );
+  }
+
+  if (get_nprocs()>=2) {
+    if (ru->feprx) init_fep_thread(ru,NULL);
+
+    if (ru->feptx_ofdm) nr_init_feptx_thread(ru,NULL);
+  }
+
+  if (opp_enabled == 1) threadCreate(&ru->ru_stats_thread,ru_stats_thread,(void *)ru, "emulateRF", -1, OAI_PRIORITY_RT_LOW);
+}
+
+void kill_RU_proc(int inst) {
+  RU_t *ru = RC.ru[inst];
+  RU_proc_t *proc = &ru->proc;
+  pthread_mutex_lock(&proc->mutex_FH);
+  proc->instance_cnt_FH = 0;
+  pthread_mutex_unlock(&proc->mutex_FH);
+  pthread_cond_signal(&proc->cond_FH);
+  pthread_mutex_lock(&proc->mutex_prach);
+  proc->instance_cnt_prach = 0;
+  pthread_mutex_unlock(&proc->mutex_prach);
+  pthread_cond_signal(&proc->cond_prach);
+  pthread_mutex_lock(&proc->mutex_synch);
+  proc->instance_cnt_synch = 0;
+  pthread_mutex_unlock(&proc->mutex_synch);
+  pthread_cond_signal(&proc->cond_synch);
+  pthread_mutex_lock(&proc->mutex_gNBs);
+  proc->instance_cnt_gNBs = 0;
+  pthread_mutex_unlock(&proc->mutex_gNBs);
+  pthread_cond_signal(&proc->cond_gNBs);
+  pthread_mutex_lock(&proc->mutex_asynch_rxtx);
+  proc->instance_cnt_asynch_rxtx = 0;
+  pthread_mutex_unlock(&proc->mutex_asynch_rxtx);
+  pthread_cond_signal(&proc->cond_asynch_rxtx);
+  LOG_D(PHY, "Joining pthread_FH\n");
+  pthread_join(proc->pthread_FH, NULL);
+
+  if (ru->function == NGFI_RRU_IF4p5) {
+    LOG_D(PHY, "Joining pthread_prach\n");
+    pthread_join(proc->pthread_prach, NULL);
+
+    if (ru->is_slave) {
+      LOG_D(PHY, "Joining pthread_\n");
+      pthread_join(proc->pthread_synch, NULL);
+    }
+
+    if ((ru->if_timing == synch_to_other) ||
+        (ru->function == NGFI_RRU_IF5) ||
+        (ru->function == NGFI_RRU_IF4p5)) {
+      LOG_D(PHY, "Joining pthread_asynch_rxtx\n");
+      pthread_join(proc->pthread_asynch_rxtx, NULL);
+    }
+  }
+
+  if (get_nprocs() >= 2) {
+    if (ru->feprx) {
+      pthread_mutex_lock(&proc->mutex_fep);
+      proc->instance_cnt_fep = 0;
+      pthread_mutex_unlock(&proc->mutex_fep);
+      pthread_cond_signal(&proc->cond_fep);
+      LOG_D(PHY, "Joining pthread_fep\n");
+      pthread_join(proc->pthread_fep, NULL);
+      pthread_mutex_destroy(&proc->mutex_fep);
+      pthread_cond_destroy(&proc->cond_fep);
+    }
+
+    if (ru->feptx_ofdm) {
+      pthread_mutex_lock(&proc->mutex_feptx);
+      proc->instance_cnt_feptx = 0;
+      pthread_mutex_unlock(&proc->mutex_feptx);
+      pthread_cond_signal(&proc->cond_feptx);
+      LOG_D(PHY, "Joining pthread_feptx\n");
+      pthread_join(proc->pthread_feptx, NULL);
+      pthread_mutex_destroy(&proc->mutex_feptx);
+      pthread_cond_destroy(&proc->cond_feptx);
+    }
+  }
+
+  if (opp_enabled) {
+    LOG_D(PHY, "Joining ru_stats_thread\n");
+    pthread_join(ru->ru_stats_thread, NULL);
+  }
+
+  pthread_mutex_destroy(&proc->mutex_prach);
+  pthread_mutex_destroy(&proc->mutex_asynch_rxtx);
+  pthread_mutex_destroy(&proc->mutex_synch);
+  pthread_mutex_destroy(&proc->mutex_FH);
+  pthread_mutex_destroy(&proc->mutex_gNBs);
+  pthread_cond_destroy(&proc->cond_prach);
+  pthread_cond_destroy(&proc->cond_FH);
+  pthread_cond_destroy(&proc->cond_asynch_rxtx);
+  pthread_cond_destroy(&proc->cond_synch);
+  pthread_cond_destroy(&proc->cond_gNBs);
+  pthread_attr_destroy(&proc->attr_FH);
+  pthread_attr_destroy(&proc->attr_prach);
+  pthread_attr_destroy(&proc->attr_synch);
+  pthread_attr_destroy(&proc->attr_asynch_rxtx);
+  pthread_attr_destroy(&proc->attr_fep);
+}
+
+int check_capabilities(RU_t *ru,RRU_capabilities_t *cap) {
+  FH_fmt_options_t fmt = cap->FH_fmt;
+  int i;
+  int found_band=0;
+  LOG_I(PHY,"RRU %d, num_bands %d, looking for band %d\n",ru->idx,cap->num_bands,ru->nr_frame_parms->eutra_band);
+
+  for (i=0; i<cap->num_bands; i++) {
+    LOG_I(PHY,"band %d on RRU %d\n",cap->band_list[i],ru->idx);
+
+    if (ru->nr_frame_parms->eutra_band == cap->band_list[i]) {
+      found_band=1;
+      break;
+    }
+  }
+
+  if (found_band == 0) {
+    LOG_I(PHY,"Couldn't find target EUTRA band %d on RRU %d\n",ru->nr_frame_parms->eutra_band,ru->idx);
+    return(-1);
+  }
+
+  switch (ru->if_south) {
+    case LOCAL_RF:
+      AssertFatal(1==0, "This RU should not have a local RF, exiting\n");
+      return(0);
+      break;
+
+    case REMOTE_IF5:
+      if (fmt == OAI_IF5_only || fmt == OAI_IF5_and_IF4p5) return(0);
+
+      break;
+
+    case REMOTE_IF4p5:
+      if (fmt == OAI_IF4p5_only || fmt == OAI_IF5_and_IF4p5) return(0);
+
+      break;
+
+    case REMOTE_MBP_IF5:
+      if (fmt == MBP_IF5) return(0);
+
+      break;
+
+    default:
+      LOG_I(PHY,"No compatible Fronthaul interface found for RRU %d\n", ru->idx);
+      return(-1);
+  }
+
+  return(-1);
+}
+
+
+char rru_format_options[4][20] = {"OAI_IF5_only","OAI_IF4p5_only","OAI_IF5_and_IF4p5","MBP_IF5"};
+
+char rru_formats[3][20] = {"OAI_IF5","MBP_IF5","OAI_IF4p5"};
+char ru_if_formats[4][20] = {"LOCAL_RF","REMOTE_OAI_IF5","REMOTE_MBP_IF5","REMOTE_OAI_IF4p5"};
+
+void configure_ru(int idx,
+                  void *arg) {
+  RU_t               *ru           = RC.ru[idx];
+  RRU_config_t       *config       = (RRU_config_t *)arg;
+  RRU_capabilities_t *capabilities = (RRU_capabilities_t *)arg;
+  nfapi_nr_config_request_t *gNB_config = &ru->gNB_list[0]->gNB_config;
+  int ret;
+  LOG_I(PHY, "Received capabilities from RRU %d\n",idx);
+
+  if (capabilities->FH_fmt < MAX_FH_FMTs) LOG_I(PHY, "RU FH options %s\n",rru_format_options[capabilities->FH_fmt]);
+
+  AssertFatal((ret=check_capabilities(ru,capabilities)) == 0,
+              "Cannot configure RRU %d, check_capabilities returned %d\n", idx,ret);
+  // take antenna capabilities of RRU
+  ru->nb_tx                      = capabilities->nb_tx[0];
+  ru->nb_rx                      = capabilities->nb_rx[0];
+  // Pass configuration to RRU
+  LOG_I(PHY, "Using %s fronthaul (%d), band %d \n",ru_if_formats[ru->if_south],ru->if_south,ru->nr_frame_parms->eutra_band);
+  // wait for configuration
+  config->FH_fmt                 = ru->if_south;
+  config->num_bands              = 1;
+  config->band_list[0]           = ru->nr_frame_parms->eutra_band;
+  config->tx_freq[0]             = ru->nr_frame_parms->dl_CarrierFreq;
+  config->rx_freq[0]             = ru->nr_frame_parms->ul_CarrierFreq;
+  //config->tdd_config[0]          = ru->nr_frame_parms->tdd_config;
+  //config->tdd_config_S[0]        = ru->nr_frame_parms->tdd_config_S;
+  config->att_tx[0]              = ru->att_tx;
+  config->att_rx[0]              = ru->att_rx;
+  config->N_RB_DL[0]             = gNB_config->rf_config.dl_carrier_bandwidth.value;
+  config->N_RB_UL[0]             = gNB_config->rf_config.ul_carrier_bandwidth.value;
+  config->threequarter_fs[0]     = ru->nr_frame_parms->threequarter_fs;
+  /*  if (ru->if_south==REMOTE_IF4p5) {
+      config->prach_FreqOffset[0]  = ru->nr_frame_parms->prach_config_common.prach_ConfigInfo.prach_FreqOffset;
+      config->prach_ConfigIndex[0] = ru->nr_frame_parms->prach_config_common.prach_ConfigInfo.prach_ConfigIndex;
+      LOG_I(PHY,"REMOTE_IF4p5: prach_FrequOffset %d, prach_ConfigIndex %d\n",
+      config->prach_FreqOffset[0],config->prach_ConfigIndex[0]);*/
+  nr_init_frame_parms(&ru->gNB_list[0]->gNB_config, ru->nr_frame_parms);
+  nr_phy_init_RU(ru);
+}
+
+void configure_rru(int idx,
+                   void *arg) {
+  RRU_config_t *config = (RRU_config_t *)arg;
+  RU_t         *ru         = RC.ru[idx];
+  nfapi_nr_config_request_t *gNB_config = &ru->gNB_list[0]->gNB_config;
+  ru->nr_frame_parms->eutra_band                                               = config->band_list[0];
+  ru->nr_frame_parms->dl_CarrierFreq                                           = config->tx_freq[0];
+  ru->nr_frame_parms->ul_CarrierFreq                                           = config->rx_freq[0];
+
+  if (ru->nr_frame_parms->dl_CarrierFreq == ru->nr_frame_parms->ul_CarrierFreq) {
+    gNB_config->subframe_config.duplex_mode.value                         = TDD;
+    //ru->nr_frame_parms->tdd_config                                            = config->tdd_config[0];
+    //ru->nr_frame_parms->tdd_config_S                                          = config->tdd_config_S[0];
+  } else
+    gNB_config->subframe_config.duplex_mode.value                            = FDD;
+
+  ru->att_tx                                                               = config->att_tx[0];
+  ru->att_rx                                                               = config->att_rx[0];
+  gNB_config->rf_config.dl_carrier_bandwidth.value                         = config->N_RB_DL[0];
+  gNB_config->rf_config.ul_carrier_bandwidth.value                         = config->N_RB_UL[0];
+  ru->nr_frame_parms->threequarter_fs                                       = config->threequarter_fs[0];
+
+  //ru->nr_frame_parms->pdsch_config_common.referenceSignalPower                 = ru->max_pdschReferenceSignalPower-config->att_tx[0];
+  if (ru->function==NGFI_RRU_IF4p5) {
+    ru->nr_frame_parms->att_rx = ru->att_rx;
+    ru->nr_frame_parms->att_tx = ru->att_tx;
+    /*
+        LOG_I(PHY,"Setting ru->function to NGFI_RRU_IF4p5, prach_FrequOffset %d, prach_ConfigIndex %d, att (%d,%d)\n",
+        config->prach_FreqOffset[0],config->prach_ConfigIndex[0],ru->att_tx,ru->att_rx);
+        ru->nr_frame_parms->prach_config_common.prach_ConfigInfo.prach_FreqOffset  = config->prach_FreqOffset[0];
+        ru->nr_frame_parms->prach_config_common.prach_ConfigInfo.prach_ConfigIndex = config->prach_ConfigIndex[0]; */
+  }
+
+  fill_rf_config(ru,ru->rf_config_file);
+  nr_init_frame_parms(&ru->gNB_list[0]->gNB_config, ru->nr_frame_parms);
+  nr_phy_init_RU(ru);
+}
+
+/*
+void init_precoding_weights(PHY_VARS_gNB *gNB) {
+
+  int layer,ru_id,aa,re,ue,tb;
+  LTE_DL_FRAME_PARMS *fp=&gNB->frame_parms;
+  RU_t *ru;
+  LTE_gNB_DLSCH_t *dlsch;
+
+  // init precoding weigths
+  for (ue=0;ue<NUMBER_OF_UE_MAX;ue++) {
+    for (tb=0;tb<2;tb++) {
+      dlsch = gNB->dlsch[ue][tb];
+      for (layer=0; layer<4; layer++) {
+  int nb_tx=0;
+  for (ru_id=0;ru_id<RC.nb_RU;ru_id++) {
+    ru = RC.ru[ru_id];
+    nb_tx+=ru->nb_tx;
+  }
+  dlsch->ue_spec_bf_weights[layer] = (int32_t**)malloc16(nb_tx*sizeof(int32_t*));
+
+  for (aa=0; aa<nb_tx; aa++) {
+    dlsch->ue_spec_bf_weights[layer][aa] = (int32_t *)malloc16(fp->ofdm_symbol_size*sizeof(int32_t));
+    for (re=0;re<fp->ofdm_symbol_size; re++) {
+      dlsch->ue_spec_bf_weights[layer][aa][re] = 0x00007fff;
+    }
+  }
+      }
+    }
+  }
+}*/
+
+void set_function_spec_param(RU_t *ru) {
+  int ret;
+
+  switch (ru->if_south) {
+    case LOCAL_RF:   // this is an RU with integrated RF (RRU, gNB)
+      if (ru->function ==  NGFI_RRU_IF5) {                 // IF5 RRU
+        ru->do_prach              = 0;                      // no prach processing in RU
+        ru->fh_north_in           = NULL;                   // no shynchronous incoming fronthaul from north
+        ru->fh_north_out          = fh_if5_north_out;       // need only to do send_IF5  reception
+        ru->fh_south_out          = tx_rf;                  // send output to RF
+        ru->fh_north_asynch_in    = fh_if5_north_asynch_in; // TX packets come asynchronously
+        ru->feprx                 = NULL;                   // nothing (this is a time-domain signal)
+        ru->feptx_ofdm            = NULL;                   // nothing (this is a time-domain signal)
+        ru->feptx_prec            = NULL;                   // nothing (this is a time-domain signal)
+        ru->start_if              = start_if;               // need to start the if interface for if5
+        ru->ifdevice.host_type    = RRU_HOST;
+        ru->rfdevice.host_type    = RRU_HOST;
+        ru->ifdevice.eth_params   = &ru->eth_params;
+        reset_meas(&ru->rx_fhaul);
+        reset_meas(&ru->tx_fhaul);
+        reset_meas(&ru->compression);
+        reset_meas(&ru->transport);
+        ret = openair0_transport_load(&ru->ifdevice,&ru->openair0_cfg,&ru->eth_params);
+        printf("openair0_transport_init returns %d for ru_id %d\n", ret, ru->idx);
+
+        if (ret<0) {
+          printf("Exiting, cannot initialize transport protocol\n");
+          exit(-1);
+        }
+      } else if (ru->function == NGFI_RRU_IF4p5) {
+        ru->do_prach              = 1;                        // do part of prach processing in RU
+        ru->fh_north_in           = NULL;                     // no synchronous incoming fronthaul from north
+        ru->fh_north_out          = fh_if4p5_north_out;       // send_IF4p5 on reception
+        ru->fh_south_out          = tx_rf;                    // send output to RF
+        ru->fh_north_asynch_in    = fh_if4p5_north_asynch_in; // TX packets come asynchronously
+        ru->feprx                 = (get_nprocs()<=2) ? fep_full :ru_fep_full_2thread;                 // RX DFTs
+        ru->feptx_ofdm            = (get_nprocs()<=2) ? nr_feptx_ofdm : nr_feptx_ofdm_2thread;               // this is fep with idft only (no precoding in RRU)
+        ru->feptx_prec            = NULL;
+        ru->start_if              = start_if;                 // need to start the if interface for if4p5
+        ru->ifdevice.host_type    = RRU_HOST;
+        ru->rfdevice.host_type    = RRU_HOST;
+        ru->ifdevice.eth_params   = &ru->eth_params;
+        reset_meas(&ru->rx_fhaul);
+        reset_meas(&ru->tx_fhaul);
+        reset_meas(&ru->compression);
+        reset_meas(&ru->transport);
+        ret = openair0_transport_load(&ru->ifdevice,&ru->openair0_cfg,&ru->eth_params);
+        printf("openair0_transport_init returns %d for ru_id %d\n", ret, ru->idx);
+
+        if (ret<0) {
+          printf("Exiting, cannot initialize transport protocol\n");
+          exit(-1);
+        }
+
+        malloc_IF4p5_buffer(ru);
+      } else if (ru->function == gNodeB_3GPP) {
+        ru->do_prach             = 0;                       // no prach processing in RU
+        ru->feprx                = (get_nprocs()<=2) ? fep_full : ru_fep_full_2thread;                // RX DFTs
+        ru->feptx_ofdm           = (get_nprocs()<=2) ? nr_feptx_ofdm : nr_feptx_ofdm_2thread;              // this is fep with idft and precoding
+        ru->feptx_prec           = feptx_prec;              // this is fep with idft and precoding
+        ru->fh_north_in          = NULL;                    // no incoming fronthaul from north
+        ru->fh_north_out         = NULL;                    // no outgoing fronthaul to north
+        ru->start_if             = NULL;                    // no if interface
+        ru->rfdevice.host_type   = RAU_HOST;
+      }
+
+      ru->fh_south_in            = rx_rf;                               // local synchronous RF RX
+      ru->fh_south_out           = tx_rf;                               // local synchronous RF TX
+      ru->start_rf               = start_rf;                            // need to start the local RF interface
+      ru->stop_rf                = stop_rf;
+      printf("configuring ru_id %d (start_rf %p)\n", ru->idx, start_rf);
+      /*
+          if (ru->function == gNodeB_3GPP) { // configure RF parameters only for 3GPP eNodeB, we need to get them from RAU otherwise
+            fill_rf_config(ru,rf_config_file);
+            init_frame_parms(&ru->frame_parms,1);
+            nr_phy_init_RU(ru);
+          }
+
+          ret = openair0_device_load(&ru->rfdevice,&ru->openair0_cfg);
+          if (setup_RU_buffers(ru)!=0) {
+            printf("Exiting, cannot initialize RU Buffers\n");
+            exit(-1);
+          }*/
+      break;
+
+    case REMOTE_IF5: // the remote unit is IF5 RRU
+      ru->do_prach               = 0;
+      ru->feprx                  = (get_nprocs()<=2) ? fep_full : fep_full;                   // this is frequency-shift + DFTs
+      ru->feptx_prec             = feptx_prec;                 // need to do transmit Precoding + IDFTs
+      ru->feptx_ofdm             = (get_nprocs()<=2) ? nr_feptx_ofdm : nr_feptx_ofdm_2thread;                 // need to do transmit Precoding + IDFTs
+      ru->fh_south_in          = fh_if5_south_in;     // synchronous IF5 reception
+      ru->fh_south_out         = fh_if5_south_out;    // synchronous IF5 transmission
+      ru->fh_south_asynch_in   = NULL;                // no asynchronous UL
+      ru->start_rf               = NULL;                 // no local RF
+      ru->stop_rf                = NULL;
+      ru->start_if               = start_if;             // need to start if interface for IF5
+      ru->ifdevice.host_type     = RAU_HOST;
+      ru->ifdevice.eth_params    = &ru->eth_params;
+      ru->ifdevice.configure_rru = configure_ru;
+      ret = openair0_transport_load(&ru->ifdevice,&ru->openair0_cfg,&ru->eth_params);
+      printf("openair0_transport_init returns %d for ru_id %d\n", ret, ru->idx);
+
+      if (ret<0) {
+        printf("Exiting, cannot initialize transport protocol\n");
+        exit(-1);
+      }
+
+      break;
+
+    case REMOTE_IF4p5:
+      ru->do_prach               = 0;
+      ru->feprx                  = NULL;                // DFTs
+      ru->feptx_prec             = feptx_prec;          // Precoding operation
+      ru->feptx_ofdm             = NULL;                // no OFDM mod
+      ru->fh_south_in            = fh_if4p5_south_in;   // synchronous IF4p5 reception
+      ru->fh_south_out           = fh_if4p5_south_out;  // synchronous IF4p5 transmission
+      ru->fh_south_asynch_in     = (ru->if_timing == synch_to_other) ? fh_if4p5_south_in : NULL;                // asynchronous UL if synch_to_other
+      ru->fh_north_out           = NULL;
+      ru->fh_north_asynch_in     = NULL;
+      ru->start_rf               = NULL;                // no local RF
+      ru->stop_rf                = NULL;
+      ru->start_if               = start_if;            // need to start if interface for IF4p5
+      ru->ifdevice.host_type     = RAU_HOST;
+      ru->ifdevice.eth_params    = &ru->eth_params;
+      ru->ifdevice.configure_rru = configure_ru;
+      ret = openair0_transport_load(&ru->ifdevice, &ru->openair0_cfg, &ru->eth_params);
+      printf("openair0_transport_init returns %d for ru_id %d\n", ret, ru->idx);
+
+      if (ret<0) {
+        printf("Exiting, cannot initialize transport protocol\n");
+        exit(-1);
+      }
+
+      malloc_IF4p5_buffer(ru);
+      break;
+
+    default:
+      LOG_E(PHY,"RU with invalid or unknown southbound interface type %d\n",ru->if_south);
+      break;
+  } // switch on interface type
+}
+
+extern void RCconfig_RU(void);
+
+void init_RU(char *rf_config_file) {
+  int ru_id;
+  RU_t *ru;
+  PHY_VARS_gNB *gNB0= (PHY_VARS_gNB *)NULL;
+  NR_DL_FRAME_PARMS *fp = (NR_DL_FRAME_PARMS *)NULL;
+  int i;
+  int CC_id;
+  // create status mask
+  RC.ru_mask = 0;
+  pthread_mutex_init(&RC.ru_mutex,NULL);
+  pthread_cond_init(&RC.ru_cond,NULL);
+  // read in configuration file)
+  printf("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_nr_L1_inst,RC.nb_RU,get_nprocs());
+
+  if (RC.nb_nr_CC != 0)
+    for (i=0; i<RC.nb_nr_L1_inst; i++)
+      for (CC_id=0; CC_id<RC.nb_nr_CC[i]; CC_id++) RC.gNB[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++) {
+    LOG_D(PHY,"Process RC.ru[%d]\n",ru_id);
+    ru               = RC.ru[ru_id];
+    ru->rf_config_file = rf_config_file;
+    ru->idx          = ru_id;
+    ru->ts_offset    = 0;
+    // use gNB_list[0] as a reference for RU frame parameters
+    // NOTE: multiple CC_id are not handled here yet!
+
+    if (ru->num_gNB > 0) {
+      LOG_D(PHY, "%s() RC.ru[%d].num_gNB:%d ru->gNB_list[0]:%p RC.gNB[0][0]:%p rf_config_file:%s\n", __FUNCTION__, ru_id, ru->num_gNB, ru->gNB_list[0], RC.gNB[0][0], ru->rf_config_file);
+
+      if (ru->gNB_list[0] == 0) {
+        LOG_E(PHY,"%s() DJP - ru->gNB_list ru->num_gNB are not initialized - so do it manually\n", __FUNCTION__);
+        ru->gNB_list[0] = RC.gNB[0][0];
+        ru->num_gNB=1;
+        //
+        // DJP - feptx_prec() / feptx_ofdm() parses the gNB_list (based on num_gNB) and copies the txdata_F to txdata in RU
+        //
+      } else {
+        LOG_E(PHY,"DJP - delete code above this %s:%d\n", __FILE__, __LINE__);
+      }
+    }
+
+    gNB0             = ru->gNB_list[0];
+    fp               = ru->nr_frame_parms;
+    LOG_D(PHY, "RU FUnction:%d ru->if_south:%d\n", ru->function, ru->if_south);
+
+    if (gNB0) {
+      if ((ru->function != NGFI_RRU_IF5) && (ru->function != NGFI_RRU_IF4p5))
+        AssertFatal(gNB0!=NULL,"gNB0 is null!\n");
+
+      if (gNB0) {
+        LOG_I(PHY,"Copying frame parms from gNB %d to ru %d\n",gNB0->Mod_id,ru->idx);
+        memcpy((void *)fp,(void *)&gNB0->frame_parms,sizeof(NR_DL_FRAME_PARMS));
+        memset((void *)ru->frame_parms, 0, sizeof(LTE_DL_FRAME_PARMS));
+        // attach all RU to all gNBs in its list/
+        LOG_D(PHY,"ru->num_gNB:%d gNB0->num_RU:%d\n", ru->num_gNB, gNB0->num_RU);
+
+        for (i=0; i<ru->num_gNB; i++) {
+          gNB0 = ru->gNB_list[i];
+          gNB0->RU_list[gNB0->num_RU++] = ru;
+        }
+      }
+    }
+
+    //    LOG_I(PHY,"Initializing RRU descriptor %d : (%s,%s,%d)\n",ru_id,ru_if_types[ru->if_south],gNB_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,"[nr-softmodem.c] RU threads created\n");
+}
+
+
+
+
+void stop_RU(int nb_ru) {
+  for (int inst = 0; inst < nb_ru; inst++) {
+    LOG_I(PHY, "Stopping RU %d processing threads\n", inst);
+    kill_RU_proc(inst);
+  }
+}
+
+
+/* --------------------------------------------------------*/
+/* from here function to use configuration module          */
+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) {
+    RC.ru = (RU_t **)malloc(RC.nb_RU*sizeof(RU_t *));
+    RC.ru_mask=(1<<NB_RU) - 1;
+    printf("Set RU mask to %lx\n",RC.ru_mask);
+
+    for (j = 0; j < RC.nb_RU; j++) {
+      RC.ru[j]                                    = (RU_t *)malloc(sizeof(RU_t));
+      memset((void *)RC.ru[j],0,sizeof(RU_t));
+      RC.ru[j]->idx                                 = j;
+      RC.ru[j]->nr_frame_parms                      = (NR_DL_FRAME_PARMS *)malloc(sizeof(NR_DL_FRAME_PARMS));
+      RC.ru[j]->frame_parms                      = (LTE_DL_FRAME_PARMS *)malloc(sizeof(LTE_DL_FRAME_PARMS));
+      printf("Creating RC.ru[%d]:%p\n", j, RC.ru[j]);
+      RC.ru[j]->if_timing                           = synch_to_ext_device;
+
+      if (RC.nb_nr_L1_inst >0)
+        RC.ru[j]->num_gNB                           = RUParamList.paramarray[j][RU_ENB_LIST_IDX].numelt;
+      else
+        RC.ru[j]->num_gNB                           = 0;
+
+      for (i=0; i<RC.ru[j]->num_gNB; i++) RC.ru[j]->gNB_list[i] = RC.gNB[RUParamList.paramarray[j][RU_ENB_LIST_IDX].iptr[i]][0];
+
+      if (config_isparamset(RUParamList.paramarray[j], RU_SDR_ADDRS)) {
+        RC.ru[j]->openair0_cfg.sdr_addrs = strdup(*(RUParamList.paramarray[j][RU_SDR_ADDRS].strptr));
+      }
+
+      if (config_isparamset(RUParamList.paramarray[j], RU_SDR_CLK_SRC)) {
+        if (strcmp(*(RUParamList.paramarray[j][RU_SDR_CLK_SRC].strptr), "internal") == 0) {
+          RC.ru[j]->openair0_cfg.clock_source = internal;
+          LOG_D(PHY, "RU clock source set as internal\n");
+        } else if (strcmp(*(RUParamList.paramarray[j][RU_SDR_CLK_SRC].strptr), "external") == 0) {
+          RC.ru[j]->openair0_cfg.clock_source = external;
+          LOG_D(PHY, "RU clock source set as external\n");
+        } else if (strcmp(*(RUParamList.paramarray[j][RU_SDR_CLK_SRC].strptr), "gpsdo") == 0) {
+          RC.ru[j]->openair0_cfg.clock_source = gpsdo;
+          LOG_D(PHY, "RU clock source set as gpsdo\n");
+        } else {
+          LOG_E(PHY, "Erroneous RU clock source in the provided configuration file: '%s'\n", *(RUParamList.paramarray[j][RU_SDR_CLK_SRC].strptr));
+        }
+      }
+
+      if (strcmp(*(RUParamList.paramarray[j][RU_LOCAL_RF_IDX].strptr), "yes") == 0) {
+        if ( !(config_isparamset(RUParamList.paramarray[j],RU_LOCAL_IF_NAME_IDX)) ) {
+          RC.ru[j]->if_south                        = LOCAL_RF;
+          RC.ru[j]->function                        = gNodeB_3GPP;
+          printf("Setting function for RU %d to gNodeB_3GPP\n",j);
+        } else {
+          RC.ru[j]->eth_params.local_if_name            = strdup(*(RUParamList.paramarray[j][RU_LOCAL_IF_NAME_IDX].strptr));
+          RC.ru[j]->eth_params.my_addr                  = strdup(*(RUParamList.paramarray[j][RU_LOCAL_ADDRESS_IDX].strptr));
+          RC.ru[j]->eth_params.remote_addr              = strdup(*(RUParamList.paramarray[j][RU_REMOTE_ADDRESS_IDX].strptr));
+          RC.ru[j]->eth_params.my_portc                 = *(RUParamList.paramarray[j][RU_LOCAL_PORTC_IDX].uptr);
+          RC.ru[j]->eth_params.remote_portc             = *(RUParamList.paramarray[j][RU_REMOTE_PORTC_IDX].uptr);
+          RC.ru[j]->eth_params.my_portd                 = *(RUParamList.paramarray[j][RU_LOCAL_PORTD_IDX].uptr);
+          RC.ru[j]->eth_params.remote_portd             = *(RUParamList.paramarray[j][RU_REMOTE_PORTD_IDX].uptr);
+
+          if (strcmp(*(RUParamList.paramarray[j][RU_TRANSPORT_PREFERENCE_IDX].strptr), "udp") == 0) {
+            RC.ru[j]->if_south                        = LOCAL_RF;
+            RC.ru[j]->function                        = NGFI_RRU_IF5;
+            RC.ru[j]->eth_params.transp_preference    = ETH_UDP_MODE;
+            printf("Setting function for RU %d to NGFI_RRU_IF5 (udp)\n",j);
+          } else if (strcmp(*(RUParamList.paramarray[j][RU_TRANSPORT_PREFERENCE_IDX].strptr), "raw") == 0) {
+            RC.ru[j]->if_south                        = LOCAL_RF;
+            RC.ru[j]->function                        = NGFI_RRU_IF5;
+            RC.ru[j]->eth_params.transp_preference    = ETH_RAW_MODE;
+            printf("Setting function for RU %d to NGFI_RRU_IF5 (raw)\n",j);
+          } else if (strcmp(*(RUParamList.paramarray[j][RU_TRANSPORT_PREFERENCE_IDX].strptr), "udp_if4p5") == 0) {
+            RC.ru[j]->if_south                        = LOCAL_RF;
+            RC.ru[j]->function                        = NGFI_RRU_IF4p5;
+            RC.ru[j]->eth_params.transp_preference    = ETH_UDP_IF4p5_MODE;
+            printf("Setting function for RU %d to NGFI_RRU_IF4p5 (udp)\n",j);
+          } else if (strcmp(*(RUParamList.paramarray[j][RU_TRANSPORT_PREFERENCE_IDX].strptr), "raw_if4p5") == 0) {
+            RC.ru[j]->if_south                        = LOCAL_RF;
+            RC.ru[j]->function                        = NGFI_RRU_IF4p5;
+            RC.ru[j]->eth_params.transp_preference    = ETH_RAW_IF4p5_MODE;
+            printf("Setting function for RU %d to NGFI_RRU_IF4p5 (raw)\n",j);
+          }
+        }
+
+        RC.ru[j]->max_pdschReferenceSignalPower     = *(RUParamList.paramarray[j][RU_MAX_RS_EPRE_IDX].uptr);;
+        RC.ru[j]->max_rxgain                        = *(RUParamList.paramarray[j][RU_MAX_RXGAIN_IDX].uptr);
+        RC.ru[j]->num_bands                         = RUParamList.paramarray[j][RU_BAND_LIST_IDX].numelt;
+
+        for (i=0; i<RC.ru[j]->num_bands; i++) RC.ru[j]->band[i] = RUParamList.paramarray[j][RU_BAND_LIST_IDX].iptr[i];
+      } //strcmp(local_rf, "yes") == 0
+      else {
+        printf("RU %d: Transport %s\n",j,*(RUParamList.paramarray[j][RU_TRANSPORT_PREFERENCE_IDX].strptr));
+        RC.ru[j]->eth_params.local_if_name        = strdup(*(RUParamList.paramarray[j][RU_LOCAL_IF_NAME_IDX].strptr));
+        RC.ru[j]->eth_params.my_addr          = strdup(*(RUParamList.paramarray[j][RU_LOCAL_ADDRESS_IDX].strptr));
+        RC.ru[j]->eth_params.remote_addr        = strdup(*(RUParamList.paramarray[j][RU_REMOTE_ADDRESS_IDX].strptr));
+        RC.ru[j]->eth_params.my_portc         = *(RUParamList.paramarray[j][RU_LOCAL_PORTC_IDX].uptr);
+        RC.ru[j]->eth_params.remote_portc       = *(RUParamList.paramarray[j][RU_REMOTE_PORTC_IDX].uptr);
+        RC.ru[j]->eth_params.my_portd         = *(RUParamList.paramarray[j][RU_LOCAL_PORTD_IDX].uptr);
+        RC.ru[j]->eth_params.remote_portd       = *(RUParamList.paramarray[j][RU_REMOTE_PORTD_IDX].uptr);
+
+        if (strcmp(*(RUParamList.paramarray[j][RU_TRANSPORT_PREFERENCE_IDX].strptr), "udp") == 0) {
+          RC.ru[j]->if_south                     = REMOTE_IF5;
+          RC.ru[j]->function                     = NGFI_RAU_IF5;
+          RC.ru[j]->eth_params.transp_preference = ETH_UDP_MODE;
+        } else if (strcmp(*(RUParamList.paramarray[j][RU_TRANSPORT_PREFERENCE_IDX].strptr), "raw") == 0) {
+          RC.ru[j]->if_south                     = REMOTE_IF5;
+          RC.ru[j]->function                     = NGFI_RAU_IF5;
+          RC.ru[j]->eth_params.transp_preference = ETH_RAW_MODE;
+        } else if (strcmp(*(RUParamList.paramarray[j][RU_TRANSPORT_PREFERENCE_IDX].strptr), "udp_if4p5") == 0) {
+          RC.ru[j]->if_south                     = REMOTE_IF4p5;
+          RC.ru[j]->function                     = NGFI_RAU_IF4p5;
+          RC.ru[j]->eth_params.transp_preference = ETH_UDP_IF4p5_MODE;
+        } else if (strcmp(*(RUParamList.paramarray[j][RU_TRANSPORT_PREFERENCE_IDX].strptr), "raw_if4p5") == 0) {
+          RC.ru[j]->if_south                     = REMOTE_IF4p5;
+          RC.ru[j]->function                     = NGFI_RAU_IF4p5;
+          RC.ru[j]->eth_params.transp_preference = ETH_RAW_IF4p5_MODE;
+        } else if (strcmp(*(RUParamList.paramarray[j][RU_TRANSPORT_PREFERENCE_IDX].strptr), "raw_if5_mobipass") == 0) {
+          RC.ru[j]->if_south                     = REMOTE_IF5;
+          RC.ru[j]->function                     = NGFI_RAU_IF5;
+          RC.ru[j]->if_timing                    = synch_to_other;
+          RC.ru[j]->eth_params.transp_preference = ETH_RAW_IF5_MOBIPASS;
+        }
+      }  /* strcmp(local_rf, "yes") != 0 */
+
+      RC.ru[j]->nb_tx                             = *(RUParamList.paramarray[j][RU_NB_TX_IDX].uptr);
+      RC.ru[j]->nb_rx                             = *(RUParamList.paramarray[j][RU_NB_RX_IDX].uptr);
+      RC.ru[j]->att_tx                            = *(RUParamList.paramarray[j][RU_ATT_TX_IDX].uptr);
+      RC.ru[j]->att_rx                            = *(RUParamList.paramarray[j][RU_ATT_RX_IDX].uptr);
+    }// j=0..num_rus
+  } else {
+    RC.nb_RU = 0;
+  } // setting != NULL
+
+  return;
+}
diff --git a/executables/nr-softmodem.c b/executables/nr-softmodem.c
new file mode 100644
index 0000000000..c724940fb2
--- /dev/null
+++ b/executables/nr-softmodem.c
@@ -0,0 +1,1243 @@
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.1  (the "License"); you may not use this file
+ * except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */
+
+
+#define _GNU_SOURCE             /* See feature_test_macros(7) */
+#include <sched.h>
+
+
+#include "T.h"
+
+#undef MALLOC //there are two conflicting definitions, so we better make sure we don't use it at all
+#include <common/utils/assertions.h>
+
+#include "msc.h"
+
+#include "PHY/types.h"
+#include "common/ran_context.h"
+
+#include "PHY/defs_gNB.h"
+#include "common/config/config_userapi.h"
+#include "common/utils/load_module_shlib.h"
+#undef MALLOC //there are two conflicting definitions, so we better make sure we don't use it at all
+//#undef FRAME_LENGTH_COMPLEX_SAMPLES //there are two conflicting definitions, so we better make sure we don't use it at all
+
+#include "../../ARCH/COMMON/common_lib.h"
+#include "../../ARCH/ETHERNET/USERSPACE/LIB/if_defs.h"
+
+//#undef FRAME_LENGTH_COMPLEX_SAMPLES //there are two conflicting definitions, so we better make sure we don't use it at all
+
+#include "PHY/phy_vars.h"
+#include "SCHED/sched_common_vars.h"
+#include "LAYER2/MAC/mac_vars.h"
+
+#include "LAYER2/MAC/mac.h"
+#include "LAYER2/MAC/mac_proto.h"
+#include "RRC/LTE/rrc_vars.h"
+#include "PHY_INTERFACE/phy_interface_vars.h"
+#include "gnb_config.h"
+
+#ifdef SMBV
+#include "PHY/TOOLS/smbv.h"
+unsigned short config_frames[4] = {2,9,11,13};
+#endif
+
+#include "common/utils/LOG/log.h"
+#include "common/utils/LOG/vcd_signal_dumper.h"
+
+#include "UTIL/OPT/opt.h"
+
+//#include "PHY/TOOLS/time_meas.h"
+
+#ifndef OPENAIR2
+  #include "UTIL/OTG/otg_vars.h"
+#endif
+
+#if defined(ENABLE_ITTI)
+  #include "intertask_interface.h"
+#endif
+
+#include "PHY/INIT/phy_init.h"
+
+#include "system.h"
+#include <openair2/GNB_APP/gnb_app.h>
+
+#ifdef XFORMS
+  #include "PHY/TOOLS/lte_phy_scope.h"
+  #include "stats.h"
+#endif
+#include "nr-softmodem.h"
+#include "NB_IoT_interface.h"
+
+#ifdef XFORMS
+  // current status is that every UE has a DL scope for a SINGLE eNB (gnb_id=0)
+  // at eNB 0, an UL scope for every UE
+
+  FD_lte_phy_scope_ue  *form_ue[NUMBER_OF_UE_MAX];
+  FD_lte_phy_scope_enb *form_enb[MAX_NUM_CCs][NUMBER_OF_UE_MAX];
+  FD_stats_form                  *form_stats=NULL,*form_stats_l2=NULL;
+  char title[255];
+  unsigned char                   scope_enb_num_ue = 2;
+  static pthread_t                forms_thread; //xforms
+
+#endif //XFORMS
+
+short nr_mod_table[NR_MOD_TABLE_SIZE_SHORT] = {0,0,16384,16384,-16384,-16384,16384,16384,16384,-16384,-16384,16384,-16384,-16384,7327,7327,7327,21981,21981,7327,21981,21981,7327,-7327,7327,-21981,21981,-7327,21981,-21981,-7327,7327,-7327,21981,-21981,7327,-21981,21981,-7327,-7327,-7327,-21981,-21981,-7327,-21981,-21981,10726,10726,10726,3576,3576,10726,3576,3576,10726,17876,10726,25027,3576,17876,3576,25027,17876,10726,17876,3576,25027,10726,25027,3576,17876,17876,17876,25027,25027,17876,25027,25027,10726,-10726,10726,-3576,3576,-10726,3576,-3576,10726,-17876,10726,-25027,3576,-17876,3576,-25027,17876,-10726,17876,-3576,25027,-10726,25027,-3576,17876,-17876,17876,-25027,25027,-17876,25027,-25027,-10726,10726,-10726,3576,-3576,10726,-3576,3576,-10726,17876,-10726,25027,-3576,17876,-3576,25027,-17876,10726,-17876,3576,-25027,10726,-25027,3576,-17876,17876,-17876,25027,-25027,17876,-25027,25027,-10726,-10726,-10726,-3576,-3576,-10726,-3576,-3576,-10726,-17876,-10726,-25027,-3576,-17876,-3576,-25027,-17876,-10726,-17876,-3576,-25027,-10726,-25027,-3576,-17876,-17876,-17876,-25027,-25027,-17876,-25027,-25027,8886,8886,8886,12439,12439,8886,12439,12439,8886,5332,8886,1778,12439,5332,12439,1778,5332,8886,5332,12439,1778,8886,1778,12439,5332,5332,5332,1778,1778,5332,1778,1778,8886,19547,8886,15993,12439,19547,12439,15993,8886,23101,8886,26655,12439,23101,12439,26655,5332,19547,5332,15993,1778,19547,1778,15993,5332,23101,5332,26655,1778,23101,1778,26655,19547,8886,19547,12439,15993,8886,15993,12439,19547,5332,19547,1778,15993,5332,15993,1778,23101,8886,23101,12439,26655,8886,26655,12439,23101,5332,23101,1778,26655,5332,26655,1778,19547,19547,19547,15993,15993,19547,15993,15993,19547,23101,19547,26655,15993,23101,15993,26655,23101,19547,23101,15993,26655,19547,26655,15993,23101,23101,23101,26655,26655,23101,26655,26655,8886,-8886,8886,-12439,12439,-8886,12439,-12439,8886,-5332,8886,-1778,12439,-5332,12439,-1778,5332,-8886,5332,-12439,1778,-8886,1778,-12439,5332,-5332,5332,-1778,1778,-5332,1778,-1778,8886,-19547,8886,-15993,12439,-19547,12439,-15993,8886,-23101,8886,-26655,12439,-23101,12439,-26655,5332,-19547,5332,-15993,1778,-19547,1778,-15993,5332,-23101,5332,-26655,1778,-23101,1778,-26655,19547,-8886,19547,-12439,15993,-8886,15993,-12439,19547,-5332,19547,-1778,15993,-5332,15993,-1778,23101,-8886,23101,-12439,26655,-8886,26655,-12439,23101,-5332,23101,-1778,26655,-5332,26655,-1778,19547,-19547,19547,-15993,15993,-19547,15993,-15993,19547,-23101,19547,-26655,15993,-23101,15993,-26655,23101,-19547,23101,-15993,26655,-19547,26655,-15993,23101,-23101,23101,-26655,26655,-23101,26655,-26655,-8886,8886,-8886,12439,-12439,8886,-12439,12439,-8886,5332,-8886,1778,-12439,5332,-12439,1778,-5332,8886,-5332,12439,-1778,8886,-1778,12439,-5332,5332,-5332,1778,-1778,5332,-1778,1778,-8886,19547,-8886,15993,-12439,19547,-12439,15993,-8886,23101,-8886,26655,-12439,23101,-12439,26655,-5332,19547,-5332,15993,-1778,19547,-1778,15993,-5332,23101,-5332,26655,-1778,23101,-1778,26655,-19547,8886,-19547,12439,-15993,8886,-15993,12439,-19547,5332,-19547,1778,-15993,5332,-15993,1778,-23101,8886,-23101,12439,-26655,8886,-26655,12439,-23101,5332,-23101,1778,-26655,5332,-26655,1778,-19547,19547,-19547,15993,-15993,19547,-15993,15993,-19547,23101,-19547,26655,-15993,23101,-15993,26655,-23101,19547,-23101,15993,-26655,19547,-26655,15993,-23101,23101,-23101,26655,-26655,23101,-26655,26655,-8886,-8886,-8886,-12439,-12439,-8886,-12439,-12439,-8886,-5332,-8886,-1778,-12439,-5332,-12439,-1778,-5332,-8886,-5332,-12439,-1778,-8886,-1778,-12439,-5332,-5332,-5332,-1778,-1778,-5332,-1778,-1778,-8886,-19547,-8886,-15993,-12439,-19547,-12439,-15993,-8886,-23101,-8886,-26655,-12439,-23101,-12439,-26655,-5332,-19547,-5332,-15993,-1778,-19547,-1778,-15993,-5332,-23101,-5332,-26655,-1778,-23101,-1778,-26655,-19547,-8886,-19547,-12439,-15993,-8886,-15993,-12439,-19547,-5332,-19547,-1778,-15993,-5332,-15993,-1778,-23101,-8886,-23101,-12439,-26655,-8886,-26655,-12439,-23101,-5332,-23101,-1778,-26655,-5332,-26655,-1778,-19547,-19547,-19547,-15993,-15993,-19547,-15993,-15993,-19547,-23101,-19547,-26655,-15993,-23101,-15993,-26655,-23101,-19547,-23101,-15993,-26655,-19547,-26655,-15993,-23101,-23101,-23101,-26655,-26655,-23101,-26655,-26655};
+
+
+pthread_cond_t nfapi_sync_cond;
+pthread_mutex_t nfapi_sync_mutex;
+int nfapi_sync_var=-1; //!< protected by mutex \ref nfapi_sync_mutex
+
+uint8_t nfapi_mode = 0; // Default to monolithic mode
+
+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;
+
+#if defined(ENABLE_ITTI)
+  volatile int             start_gNB = 0;
+#endif
+volatile int             oai_exit = 0;
+
+static clock_source_t clock_source = internal;
+static int wait_for_sync = 0;
+
+unsigned int                    mmapped_dma=0;
+int                             single_thread_flag=1;
+
+static int8_t                     threequarter_fs=0;
+
+uint32_t                 downlink_frequency[MAX_NUM_CCs][4];
+int32_t                  uplink_frequency_offset[MAX_NUM_CCs][4];
+
+//Temp fix for inexisting NR upper layer
+unsigned char NB_gNB_INST = 1;
+
+#if defined(ENABLE_ITTI)
+  static char                    *itti_dump_file = NULL;
+#endif
+
+int UE_scan = 1;
+int UE_scan_carrier = 0;
+runmode_t mode = normal_txrx;
+
+FILE *input_fd=NULL;
+
+
+#if MAX_NUM_CCs == 1
+rx_gain_t                rx_gain_mode[MAX_NUM_CCs][4] = {{max_gain,max_gain,max_gain,max_gain}};
+double tx_gain[MAX_NUM_CCs][4] = {{20,0,0,0}};
+double rx_gain[MAX_NUM_CCs][4] = {{110,0,0,0}};
+#else
+rx_gain_t                rx_gain_mode[MAX_NUM_CCs][4] = {{max_gain,max_gain,max_gain,max_gain},{max_gain,max_gain,max_gain,max_gain}};
+double tx_gain[MAX_NUM_CCs][4] = {{20,0,0,0},{20,0,0,0}};
+double rx_gain[MAX_NUM_CCs][4] = {{110,0,0,0},{20,0,0,0}};
+#endif
+
+double rx_gain_off = 0.0;
+
+double sample_rate=30.72e6;
+double bw = 10.0e6;
+
+static int                      tx_max_power[MAX_NUM_CCs]; /* =  {0,0}*/;
+
+char   rf_config_file[1024]="/usr/local/etc/syriq/ue.band7.tm1.PRB100.NR40.dat";
+
+int chain_offset=0;
+int phy_test = 0;
+uint8_t usim_test = 0;
+
+uint8_t dci_Format = 0;
+uint8_t agregation_Level =0xFF;
+
+uint8_t nb_antenna_tx = 1;
+uint8_t nb_antenna_rx = 1;
+
+char ref[128] = "internal";
+char channels[128] = "0";
+
+int                      rx_input_level_dBm;
+
+#ifdef XFORMS
+  extern int                      otg_enabled;
+  static char                     do_forms=0;
+#else
+  int                             otg_enabled;
+#endif
+//int                             number_of_cards =   1;
+
+
+//static NR_DL_FRAME_PARMS      *frame_parms[MAX_NUM_CCs];
+//static nfapi_nr_config_request_t *config[MAX_NUM_CCs];
+uint32_t target_dl_mcs = 28; //maximum allowed mcs
+uint32_t target_ul_mcs = 20;
+uint32_t timing_advance = 0;
+uint8_t exit_missed_slots=1;
+uint64_t num_missed_slots=0; // counter for the number of missed slots
+
+
+extern void reset_opp_meas(void);
+extern void print_opp_meas(void);
+
+extern void init_eNB_afterRU(void);
+
+int transmission_mode=1;
+int emulate_rf = 0;
+int numerology = 0;
+char *parallel_config = NULL;
+char *worker_config = NULL;
+
+static THREAD_STRUCT thread_struct;
+void set_parallel_conf(char *parallel_conf) {
+  if(strcmp(parallel_conf,"PARALLEL_SINGLE_THREAD")==0)           thread_struct.parallel_conf = PARALLEL_SINGLE_THREAD;
+  else if(strcmp(parallel_conf,"PARALLEL_RU_L1_SPLIT")==0)        thread_struct.parallel_conf = PARALLEL_RU_L1_SPLIT;
+  else if(strcmp(parallel_conf,"PARALLEL_RU_L1_TRX_SPLIT")==0)    thread_struct.parallel_conf = PARALLEL_RU_L1_TRX_SPLIT;
+
+  printf("[CONFIG] parallel conf is set to %d\n",thread_struct.parallel_conf);
+}
+void set_worker_conf(char *worker_conf) {
+  if(strcmp(worker_conf,"WORKER_DISABLE")==0)                     thread_struct.worker_conf = WORKER_DISABLE;
+  else if(strcmp(worker_conf,"WORKER_ENABLE")==0)                 thread_struct.worker_conf = WORKER_ENABLE;
+
+  printf("[CONFIG] worker conf is set to %d\n",thread_struct.worker_conf);
+}
+PARALLEL_CONF_t get_thread_parallel_conf(void) {
+  return thread_struct.parallel_conf;
+}
+WORKER_CONF_t get_thread_worker_conf(void) {
+  return thread_struct.worker_conf;
+}
+
+
+/* struct for ethernet specific parameters given in eNB conf file */
+eth_params_t *eth_params;
+
+openair0_config_t openair0_cfg[MAX_CARDS];
+
+double cpuf;
+
+extern char uecap_xer[1024];
+char uecap_xer_in=0;
+
+/* see file openair2/LAYER2/MAC/main.c for why abstraction_flag is needed
+ * this is very hackish - find a proper solution
+ */
+uint8_t abstraction_flag=0;
+
+/* forward declarations */
+void set_default_frame_parms(nfapi_nr_config_request_t *config[MAX_NUM_CCs], NR_DL_FRAME_PARMS *frame_parms[MAX_NUM_CCs]);
+
+/*---------------------BMC: timespec helpers -----------------------------*/
+
+struct timespec min_diff_time = { .tv_sec = 0, .tv_nsec = 0 };
+struct timespec max_diff_time = { .tv_sec = 0, .tv_nsec = 0 };
+
+struct timespec clock_difftime(struct timespec start, struct timespec end) {
+  struct timespec temp;
+
+  if ((end.tv_nsec-start.tv_nsec)<0) {
+    temp.tv_sec = end.tv_sec-start.tv_sec-1;
+    temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec;
+  } else {
+    temp.tv_sec = end.tv_sec-start.tv_sec;
+    temp.tv_nsec = end.tv_nsec-start.tv_nsec;
+  }
+
+  return temp;
+}
+
+void print_difftimes(void) {
+#ifdef DEBUG
+  printf("difftimes min = %lu ns ; max = %lu ns\n", min_diff_time.tv_nsec, max_diff_time.tv_nsec);
+#else
+  LOG_I(HW,"difftimes min = %lu ns ; max = %lu ns\n", min_diff_time.tv_nsec, max_diff_time.tv_nsec);
+#endif
+}
+
+void update_difftimes(struct timespec start, struct timespec end) {
+  struct timespec diff_time = { .tv_sec = 0, .tv_nsec = 0 };
+  int             changed = 0;
+  diff_time = clock_difftime(start, end);
+
+  if ((min_diff_time.tv_nsec == 0) || (diff_time.tv_nsec < min_diff_time.tv_nsec)) {
+    min_diff_time.tv_nsec = diff_time.tv_nsec;
+    changed = 1;
+  }
+
+  if ((max_diff_time.tv_nsec == 0) || (diff_time.tv_nsec > max_diff_time.tv_nsec)) {
+    max_diff_time.tv_nsec = diff_time.tv_nsec;
+    changed = 1;
+  }
+
+#if 1
+
+  if (changed) print_difftimes();
+
+#endif
+}
+
+/*------------------------------------------------------------------------*/
+
+unsigned int build_rflocal(int txi, int txq, int rxi, int rxq) {
+  return (txi + (txq<<6) + (rxi<<12) + (rxq<<18));
+}
+unsigned int build_rfdc(int dcoff_i_rxfe, int dcoff_q_rxfe) {
+  return (dcoff_i_rxfe + (dcoff_q_rxfe<<8));
+}
+
+#if !defined(ENABLE_ITTI)
+void signal_handler(int sig) {
+  void *array[10];
+  size_t size;
+
+  if (sig==SIGSEGV) {
+    // get void*'s for all entries on the stack
+    size = backtrace(array, 10);
+    // print out all the frames to stderr
+    fprintf(stderr, "Error: signal %d:\n", sig);
+    backtrace_symbols_fd(array, size, 2);
+    exit(-1);
+  } else {
+    printf("trying to exit gracefully...\n");
+    oai_exit = 1;
+  }
+}
+#endif
+#define KNRM  "\x1B[0m"
+#define KRED  "\x1B[31m"
+#define KGRN  "\x1B[32m"
+#define KBLU  "\x1B[34m"
+#define RESET "\033[0m"
+
+#if defined(ENABLE_ITTI)
+void signal_handler_itti(int sig) {
+  // Call exit function
+  char msg[256];
+  memset(msg, 0, 256);
+  sprintf(msg, "caught signal %s\n", strsignal(sig));
+  exit_function(__FILE__, __FUNCTION__, __LINE__, msg);
+}
+#endif
+
+void exit_function(const char *file, const char *function, const int line, const char *s) {
+  int ru_id;
+
+  if (s != NULL) {
+    printf("%s:%d %s() Exiting OAI softmodem: %s\n",file,line, function, s);
+  }
+
+  oai_exit = 1;
+
+  if (RC.ru == NULL)
+    exit(-1); // likely init not completed, prevent crash or hang, exit now...
+
+  for (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
+#if defined(ENABLE_ITTI)
+  itti_terminate_tasks (TASK_UNKNOWN);
+#endif
+  exit(1);
+}
+
+#ifdef XFORMS
+
+
+void reset_stats(FL_OBJECT *button, long arg) {
+  PHY_VARS_gNB *phy_vars_gNB = RC.gNB[0][0];
+
+  for (int i=0; i<NUMBER_OF_UE_MAX; i++) {
+    for (int k=0; k<8; k++) { //harq_processes
+      /*      for (j=0; j<phy_vars_gNB->dlsch[i][0]->Mlimit; j++) {
+        phy_vars_gNB->UE_stats[i].dlsch_NAK[k][j]=0;
+        phy_vars_gNB->UE_stats[i].dlsch_ACK[k][j]=0;
+        phy_vars_gNB->UE_stats[i].dlsch_trials[k][j]=0;
+            }*/
+      phy_vars_gNB->UE_stats[i].dlsch_l2_errors[k]=0;
+      phy_vars_gNB->UE_stats[i].ulsch_errors[k]=0;
+      phy_vars_gNB->UE_stats[i].ulsch_consecutive_errors=0;
+      phy_vars_gNB->UE_stats[i].dlsch_sliding_cnt=0;
+      phy_vars_gNB->UE_stats[i].dlsch_NAK_round0=0;
+      phy_vars_gNB->UE_stats[i].dlsch_mcs_offset=0;
+    }
+  }
+}
+
+static void *scope_thread(void *arg) {
+  threadTopInit("scope",-1,OAI_PRIORITY_RT_LOW);
+  int UE_id, CC_id;
+  int ue_cnt=0;
+# ifdef ENABLE_XFORMS_WRITE_STATS
+  FILE *gNB_stats = fopen("gNB_stats.txt", "w");
+#endif
+
+  while (!oai_exit) {
+    ue_cnt=0;
+
+    for(UE_id=0; UE_id<NUMBER_OF_UE_MAX; UE_id++) {
+      for(CC_id=0; CC_id<MAX_NUM_CCs; CC_id++) {
+        if ((ue_cnt<scope_enb_num_ue)) {
+          /*
+          //this function needs to be written
+          phy_scope_gNB(form_enb[CC_id][ue_cnt],
+                    RC.gNB[0][CC_id],
+                UE_id);
+          */
+          ue_cnt++;
+        }
+      }
+    }
+
+    sleep(1);
+  }
+
+  //  printf("%s",stats_buffer);
+# ifdef ENABLE_XFORMS_WRITE_STATS
+
+  if (eNB_stats) {
+    rewind (gNB_stats);
+    fwrite (stats_buffer, 1, len, gNB_stats);
+    fclose (gNB_stats);
+  }
+
+# endif
+  pthread_exit((void *)arg);
+}
+#endif
+
+
+
+
+#if defined(ENABLE_ITTI)
+void *l2l1_task(void *arg) {
+  MessageDef *message_p = NULL;
+  int         result;
+  itti_set_task_real_time(TASK_L2L1);
+  itti_mark_task_ready(TASK_L2L1);
+  /* Wait for the initialize message */
+  printf("Wait for the ITTI initialize message\n");
+
+  do {
+    if (message_p != NULL) {
+      result = itti_free (ITTI_MSG_ORIGIN_ID(message_p), message_p);
+      AssertFatal (result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
+    }
+
+    itti_receive_msg (TASK_L2L1, &message_p);
+
+    switch (ITTI_MSG_ID(message_p)) {
+      case INITIALIZE_MESSAGE:
+        /* Start eNB thread */
+        LOG_D(EMU, "L2L1 TASK received %s\n", ITTI_MSG_NAME(message_p));
+        start_gNB = 1;
+        break;
+
+      case TERMINATE_MESSAGE:
+        printf("received terminate message\n");
+        oai_exit=1;
+        start_gNB = 0;
+        itti_exit_task ();
+        break;
+
+      default:
+        LOG_E(EMU, "Received unexpected message %s\n", ITTI_MSG_NAME(message_p));
+        break;
+    }
+  } while (ITTI_MSG_ID(message_p) != INITIALIZE_MESSAGE);
+
+  result = itti_free (ITTI_MSG_ORIGIN_ID(message_p), message_p);
+  AssertFatal (result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
+  /* ???? no else but seems to be UE only ???
+    do {
+      // Wait for a message
+      itti_receive_msg (TASK_L2L1, &message_p);
+
+      switch (ITTI_MSG_ID(message_p)) {
+      case TERMINATE_MESSAGE:
+        oai_exit=1;
+        itti_exit_task ();
+        break;
+
+      case ACTIVATE_MESSAGE:
+        start_UE = 1;
+        break;
+
+      case DEACTIVATE_MESSAGE:
+        start_UE = 0;
+        break;
+
+      case MESSAGE_TEST:
+        LOG_I(EMU, "Received %s\n", ITTI_MSG_NAME(message_p));
+        break;
+
+      default:
+        LOG_E(EMU, "Received unexpected message %s\n", ITTI_MSG_NAME(message_p));
+        break;
+      }
+
+      result = itti_free (ITTI_MSG_ORIGIN_ID(message_p), message_p);
+      AssertFatal (result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
+    } while(!oai_exit);
+  */
+  return NULL;
+}
+#endif
+
+int create_gNB_tasks(uint32_t gnb_nb) {
+  LOG_D(GNB_APP, "%s(gnb_nb:%d\n", __FUNCTION__, gnb_nb);
+  itti_wait_ready(1);
+
+  if (itti_create_task (TASK_L2L1, l2l1_task, NULL) < 0) {
+    LOG_E(PDCP, "Create task for L2L1 failed\n");
+    return -1;
+  }
+
+  if (gnb_nb > 0) {
+    /* Last task to create, others task must be ready before its start */
+    if (itti_create_task (TASK_GNB_APP, gNB_app_task, NULL) < 0) {
+      LOG_E(GNB_APP, "Create task for gNB APP failed\n");
+      return -1;
+    }
+  }
+
+  /*
+  #   if defined(ENABLE_USE_MME)
+        if (gnb_nb > 0) {
+          if (itti_create_task (TASK_SCTP, sctp_eNB_task, NULL) < 0) {
+            LOG_E(SCTP, "Create task for SCTP failed\n");
+            return -1;
+          }
+
+          if (itti_create_task (TASK_S1AP, s1ap_eNB_task, NULL) < 0) {
+            LOG_E(S1AP, "Create task for S1AP failed\n");
+            return -1;
+          }
+          if(!emulate_rf){
+            if (itti_create_task (TASK_UDP, udp_eNB_task, NULL) < 0) {
+              LOG_E(UDP_, "Create task for UDP failed\n");
+              return -1;
+            }
+          }
+
+          if (itti_create_task (TASK_GTPV1_U, &gtpv1u_eNB_task, NULL) < 0) {
+            LOG_E(GTPU, "Create task for GTPV1U failed\n");
+            return -1;
+          }
+        }
+
+  #      endif
+  */
+
+  if (gnb_nb > 0) {
+    LOG_I(NR_RRC,"Creating NR RRC gNB Task\n");
+
+    if (itti_create_task (TASK_RRC_GNB, rrc_gnb_task, NULL) < 0) {
+      LOG_E(NR_RRC, "Create task for NR RRC gNB failed\n");
+      return -1;
+    }
+  }
+
+  return 0;
+}
+
+
+static void get_options(void) {
+  int tddflag, nonbiotflag;
+  uint32_t online_log_messages;
+  uint32_t glog_level, glog_verbosity;
+  uint32_t start_telnetsrv;
+  paramdef_t cmdline_params[] =CMDLINE_PARAMS_DESC ;
+  paramdef_t cmdline_logparams[] =CMDLINE_LOGPARAMS_DESC ;
+  config_process_cmdline( cmdline_params,sizeof(cmdline_params)/sizeof(paramdef_t),NULL);
+
+  if (strlen(in_path) > 0) {
+    opt_type = OPT_PCAP;
+    opt_enabled=1;
+    printf("Enabling OPT for PCAP  with the following file %s \n",in_path);
+  }
+
+  if (strlen(in_ip) > 0) {
+    opt_enabled=1;
+    opt_type = OPT_WIRESHARK;
+    printf("Enabling OPT for wireshark for local interface");
+  }
+
+  config_process_cmdline( cmdline_logparams,sizeof(cmdline_logparams)/sizeof(paramdef_t),NULL);
+
+  if(config_isparamset(cmdline_logparams,CMDLINE_ONLINELOG_IDX)) {
+    set_glog_onlinelog(online_log_messages);
+  }
+
+  if(config_isparamset(cmdline_logparams,CMDLINE_GLOGLEVEL_IDX)) {
+    set_glog(glog_level);
+  }
+
+  if (start_telnetsrv) {
+    load_module_shlib("telnetsrv",NULL,0,NULL);
+  }
+
+#if T_TRACER
+  paramdef_t cmdline_ttraceparams[] =CMDLINE_TTRACEPARAMS_DESC ;
+  config_process_cmdline( cmdline_ttraceparams,sizeof(cmdline_ttraceparams)/sizeof(paramdef_t),NULL);
+#endif
+
+  if ( !(CONFIG_ISFLAGSET(CONFIG_ABORT)) ) {
+    memset((void *)&RC,0,sizeof(RC));
+    /* Read RC configuration file */
+    NRRCConfig();
+    NB_gNB_INST = RC.nb_nr_inst;
+    NB_RU   = RC.nb_RU;
+    printf("Configuration: nb_rrc_inst %d, nb_nr_L1_inst %d, nb_ru %d\n",NB_gNB_INST,RC.nb_nr_L1_inst,NB_RU);
+  }
+
+  if(parallel_config != NULL) set_parallel_conf(parallel_config);
+
+  if(worker_config != NULL)   set_worker_conf(worker_config);
+}
+
+
+#if T_TRACER
+  int T_nowait = 0;     /* by default we wait for the tracer */
+  int T_port = 2021;    /* default port to listen to to wait for the tracer */
+  int T_dont_fork = 0;  /* default is to fork, see 'T_init' to understand */
+#endif
+
+
+
+void set_default_frame_parms(nfapi_nr_config_request_t *config[MAX_NUM_CCs], NR_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] = (NR_DL_FRAME_PARMS *) malloc(sizeof(NR_DL_FRAME_PARMS));
+    config[CC_id] = (nfapi_nr_config_request_t *) malloc(sizeof(nfapi_nr_config_request_t));
+    config[CC_id]->subframe_config.numerology_index_mu.value =1;
+    config[CC_id]->subframe_config.duplex_mode.value = 1; //FDD
+    config[CC_id]->subframe_config.dl_cyclic_prefix_type.value = 0; //NORMAL
+    config[CC_id]->rf_config.dl_carrier_bandwidth.value = 106;
+    config[CC_id]->rf_config.ul_carrier_bandwidth.value = 106;
+    config[CC_id]->sch_config.physical_cell_id.value = 0;
+    ///dl frequency to be filled in
+    /*  //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_openair0(void) {
+
+  int card;
+  int i;
+
+  for (card=0; card<MAX_CARDS; card++) {
+
+    openair0_cfg[card].mmapped_dma=mmapped_dma;
+    openair0_cfg[card].configFilename = NULL;
+
+    if(config[0]->rf_config.dl_carrier_bandwidth.value == 100) {
+      if (frame_parms[0]->threequarter_fs) {
+  openair0_cfg[card].sample_rate=23.04e6;
+  openair0_cfg[card].samples_per_frame = 230400;
+  openair0_cfg[card].tx_bw = 10e6;
+  openair0_cfg[card].rx_bw = 10e6;
+      } else {
+  openair0_cfg[card].sample_rate=30.72e6;
+  openair0_cfg[card].samples_per_frame = 307200;
+  openair0_cfg[card].tx_bw = 10e6;
+  openair0_cfg[card].rx_bw = 10e6;
+      }
+    } else if(config[0]->rf_config.dl_carrier_bandwidth.value == 50) {
+      openair0_cfg[card].sample_rate=15.36e6;
+      openair0_cfg[card].samples_per_frame = 153600;
+      openair0_cfg[card].tx_bw = 5e6;
+      openair0_cfg[card].rx_bw = 5e6;
+    } else if (config[0]->rf_config.dl_carrier_bandwidth.value == 25) {
+      openair0_cfg[card].sample_rate=7.68e6;
+      openair0_cfg[card].samples_per_frame = 76800;
+      openair0_cfg[card].tx_bw = 2.5e6;
+      openair0_cfg[card].rx_bw = 2.5e6;
+    } else if (config[0]->rf_config.dl_carrier_bandwidth.value == 6) {
+      openair0_cfg[card].sample_rate=1.92e6;
+      openair0_cfg[card].samples_per_frame = 19200;
+      openair0_cfg[card].tx_bw = 1.5e6;
+      openair0_cfg[card].rx_bw = 1.5e6;
+    }
+
+
+    if (config[0]->subframe_config.duplex_mode.value==TDD)
+      openair0_cfg[card].duplex_mode = duplex_mode_TDD;
+    else //FDD
+      openair0_cfg[card].duplex_mode = duplex_mode_FDD;
+
+    printf("HW: Configuring card %d, nb_antennas_tx/rx %d/%d\n",card,
+     RC.gNB[0][0]->gNB_config.rf_config.tx_antenna_ports.value,
+     RC.gNB[0][0]->gNB_config.rf_config.tx_antenna_ports.value );
+    openair0_cfg[card].Mod_id = 0;
+
+    openair0_cfg[card].num_rb_dl=config[0]->rf_config.dl_carrier_bandwidth.value;
+
+    openair0_cfg[card].clock_source = clock_source;
+
+
+    openair0_cfg[card].tx_num_channels=min(2,RC.gNB[0][0]->gNB_config.rf_config.tx_antenna_ports.value );
+    openair0_cfg[card].rx_num_channels=min(2,RC.gNB[0][0]->gNB_config.rf_config.tx_antenna_ports.value );
+
+    for (i=0; i<4; i++) {
+
+      if (i<openair0_cfg[card].tx_num_channels)
+  openair0_cfg[card].tx_freq[i] = downlink_frequency[0][i] ;
+      else
+  openair0_cfg[card].tx_freq[i]=0.0;
+
+      if (i<openair0_cfg[card].rx_num_channels)
+  openair0_cfg[card].rx_freq[i] =downlink_frequency[0][i] + uplink_frequency_offset[0][i] ;
+      else
+  openair0_cfg[card].rx_freq[i]=0.0;
+
+      openair0_cfg[card].autocal[i] = 1;
+      openair0_cfg[card].tx_gain[i] = tx_gain[0][i];
+      openair0_cfg[card].rx_gain[i] = RC.gNB[0][0]->rx_total_gain_dB;
+
+
+      openair0_cfg[card].configFilename = rf_config_file;
+      printf("Card %d, channel %d, Setting tx_gain %f, rx_gain %f, tx_freq %f, rx_freq %f\n",
+       card,i, openair0_cfg[card].tx_gain[i],
+       openair0_cfg[card].rx_gain[i],
+       openair0_cfg[card].tx_freq[i],
+       openair0_cfg[card].rx_freq[i]);
+    }
+  } // for loop on cards
+}
+*/
+
+void wait_RUs(void) {
+  LOG_I(PHY,"Waiting for RUs to be configured ... RC.ru_mask:%02lx\n", RC.ru_mask);
+  // wait for all RUs to be configured over fronthaul
+  pthread_mutex_lock(&RC.ru_mutex);
+
+  while (RC.ru_mask>0) {
+    pthread_cond_wait(&RC.ru_cond,&RC.ru_mutex);
+    printf("RC.ru_mask:%02lx\n", RC.ru_mask);
+  }
+
+  pthread_mutex_unlock(&RC.ru_mutex);
+  LOG_I(PHY,"RUs configured\n");
+}
+
+void wait_gNBs(void) {
+  int i,j;
+  int waiting=1;
+
+  while (waiting==1) {
+    printf("Waiting for gNB L1 instances to all get configured ... sleeping 50ms (nb_nr_sL1_inst %d)\n",RC.nb_nr_L1_inst);
+    usleep(50*1000);
+    waiting=0;
+
+    for (i=0; i<RC.nb_nr_L1_inst; i++) {
+      printf("RC.nb_nr_L1_CC[%d]:%d\n", i, RC.nb_nr_L1_CC[i]);
+
+      for (j=0; j<RC.nb_nr_L1_CC[i]; j++) {
+        if (RC.gNB[i][j]->configured==0) {
+          waiting=1;
+          break;
+        }
+      }
+    }
+  }
+
+  printf("gNB L1 are configured\n");
+}
+
+#if defined(ENABLE_ITTI)
+/*
+ * helper function to terminate a certain ITTI task
+ */
+void terminate_task(task_id_t task_id, module_id_t mod_id) {
+  LOG_I(ENB_APP, "sending TERMINATE_MESSAGE to task %s (%d)\n", itti_get_task_name(task_id), task_id);
+  MessageDef *msg;
+  msg = itti_alloc_new_message (ENB_APP, TERMINATE_MESSAGE);
+  itti_send_msg_to_task (task_id, ENB_MODULE_ID_TO_INSTANCE(mod_id), msg);
+}
+
+//extern void  free_transport(PHY_VARS_gNB *);
+extern void  nr_phy_free_RU(RU_t *);
+
+int stop_L1L2(module_id_t gnb_id) {
+  LOG_W(ENB_APP, "stopping nr-softmodem\n");
+  oai_exit = 1;
+
+  if (!RC.ru) {
+    LOG_F(ENB_APP, "no RU configured\n");
+    return -1;
+  }
+
+  /* stop trx devices, multiple carrier currently not supported by RU */
+  if (RC.ru[gnb_id]) {
+    if (RC.ru[gnb_id]->rfdevice.trx_stop_func) {
+      RC.ru[gnb_id]->rfdevice.trx_stop_func(&RC.ru[gnb_id]->rfdevice);
+      LOG_I(ENB_APP, "turned off RU rfdevice\n");
+    } else {
+      LOG_W(ENB_APP, "can not turn off rfdevice due to missing trx_stop_func callback, proceding anyway!\n");
+    }
+
+    if (RC.ru[gnb_id]->ifdevice.trx_stop_func) {
+      RC.ru[gnb_id]->ifdevice.trx_stop_func(&RC.ru[gnb_id]->ifdevice);
+      LOG_I(ENB_APP, "turned off RU ifdevice\n");
+    } else {
+      LOG_W(ENB_APP, "can not turn off ifdevice due to missing trx_stop_func callback, proceding anyway!\n");
+    }
+  } else {
+    LOG_W(ENB_APP, "no RU found for index %d\n", gnb_id);
+    return -1;
+  }
+
+  /* these tasks need to pick up new configuration */
+  terminate_task(TASK_RRC_ENB, gnb_id);
+  terminate_task(TASK_L2L1, gnb_id);
+  LOG_I(ENB_APP, "calling kill_gNB_proc() for instance %d\n", gnb_id);
+  kill_gNB_proc(gnb_id);
+  LOG_I(ENB_APP, "calling kill_RU_proc() for instance %d\n", gnb_id);
+  kill_RU_proc(gnb_id);
+  oai_exit = 0;
+
+  for (int cc_id = 0; cc_id < RC.nb_nr_CC[gnb_id]; cc_id++) {
+    //free_transport(RC.gNB[gnb_id][cc_id]);
+    phy_free_nr_gNB(RC.gNB[gnb_id][cc_id]);
+  }
+
+  nr_phy_free_RU(RC.ru[gnb_id]);
+  free_lte_top();
+  return 0;
+}
+
+/*
+ * Restart the nr-softmodem after it has been soft-stopped with stop_L1L2()
+ */
+int restart_L1L2(module_id_t gnb_id) {
+  RU_t *ru = RC.ru[gnb_id];
+  int cc_id;
+  MessageDef *msg_p = NULL;
+  LOG_W(ENB_APP, "restarting nr-softmodem\n");
+  /* block threads */
+  sync_var = -1;
+
+  for (cc_id = 0; cc_id < RC.nb_nr_L1_CC[gnb_id]; cc_id++) {
+    RC.gNB[gnb_id][cc_id]->configured = 0;
+  }
+
+  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 gNB */
+  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(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 gNB successfully\n");
+  }
+
+  if (itti_create_task (TASK_L2L1, l2l1_task, NULL) < 0) {
+    LOG_E(PDCP, "Create task for L2L1 failed\n");
+    return -1;
+  } else {
+    LOG_I(PDCP, "Re-created task for L2L1 successfully\n");
+  }
+
+  /* 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[gnb_id]->configuration;
+  itti_send_msg_to_task(TASK_RRC_ENB, ENB_MODULE_ID_TO_INSTANCE(gnb_id), msg_p);
+  /* TODO XForms might need to be restarted, but it is currently (09/02/18)
+   * broken, so we cannot test it */
+  wait_gNBs();
+  init_RU_proc(ru);
+  ru->rf_map.card = 0;
+  ru->rf_map.chain = 0; /* CC_id + chain_offset;*/
+  wait_RUs();
+  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;
+}
+#endif
+
+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);
+}
+
+int main( int argc, char **argv ) {
+  int i;
+#if defined (XFORMS)
+  //void *status;
+#endif
+  int CC_id;
+  int ru_id;
+#if defined (XFORMS)
+  int ret;
+#endif
+  start_background_system();
+
+  ///static configuration for NR at the moment
+  if ( load_configmodule(argc,argv) == NULL) {
+    exit_fun("[SOFTMODEM] Error, configuration module init failed\n");
+  }
+
+#ifdef DEBUG_CONSOLE
+  setvbuf(stdout, NULL, _IONBF, 0);
+  setvbuf(stderr, NULL, _IONBF, 0);
+#endif
+  mode = normal_txrx;
+  memset(&openair0_cfg[0],0,sizeof(openair0_config_t)*MAX_CARDS);
+  memset(tx_max_power,0,sizeof(int)*MAX_NUM_CCs);
+  configure_linux();
+  logInit();
+  printf("Reading in command-line options\n");
+  get_options ();
+
+  if (CONFIG_ISFLAGSET(CONFIG_ABORT) ) {
+    fprintf(stderr,"Getting configuration failed\n");
+    exit(-1);
+  }
+
+#if T_TRACER
+  T_Config_Init();
+#endif
+  //randominit (0);
+  set_taus_seed (0);
+  printf("configuring for RAU/RRU\n");
+
+  if (opp_enabled ==1) {
+    reset_opp_meas();
+  }
+
+  cpuf=get_cpu_freq_GHz();
+#if defined(ENABLE_ITTI)
+  itti_init(TASK_MAX, THREAD_MAX, MESSAGES_ID_MAX, tasks_info, messages_info);
+  // initialize mscgen log after ITTI
+  MSC_INIT(MSC_E_UTRAN, THREAD_MAX+TASK_MAX);
+#endif
+
+  if (opt_type != OPT_NONE) {
+    if (init_opt(in_path, in_ip) == -1)
+      LOG_E(OPT,"failed to run OPT \n");
+  }
+
+#ifdef PDCP_USE_NETLINK
+  netlink_init();
+#if defined(PDCP_USE_NETLINK_QUEUES)
+  pdcp_netlink_init();
+#endif
+#endif
+#if !defined(ENABLE_ITTI)
+  // to make a graceful exit when ctrl-c is pressed
+  signal(SIGSEGV, signal_handler);
+  signal(SIGINT, signal_handler);
+#endif
+#ifndef PACKAGE_VERSION
+#  define PACKAGE_VERSION "UNKNOWN-EXPERIMENTAL"
+#endif
+  LOG_I(HW, "Version: %s\n", PACKAGE_VERSION);
+#if defined(ENABLE_ITTI)
+
+  if (RC.nb_nr_inst > 0)  {
+    // don't create if node doesn't connect to RRC/S1/GTP
+    AssertFatal(create_gNB_tasks(1) == 0,"cannot create ITTI tasks\n");
+  } else {
+    printf("No ITTI, Initializing L1\n");
+    RCconfig_L1();
+  }
+
+#endif
+  /* Start the agent. If it is turned off in the configuration, it won't start */
+  RCconfig_nr_flexran();
+
+  for (i = 0; i < RC.nb_nr_L1_inst; i++) {
+    flexran_agent_start(i);
+  }
+
+  // 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)*NUMBER_OF_UE_MAX*MAX_NUM_CCs);
+  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");
+
+    for(UE_id=0; UE_id<scope_enb_num_ue; UE_id++) {
+      for(CC_id=0; CC_id<MAX_NUM_CCs; CC_id++) {
+        form_enb[CC_id][UE_id] = create_lte_phy_scope_enb();
+        sprintf (title, "LTE UL SCOPE eNB for CC_id %d, UE %d",CC_id,UE_id);
+        fl_show_form (form_enb[CC_id][UE_id]->lte_phy_scope_enb, FL_PLACE_HOTSPOT, FL_FULLBORDER, title);
+
+        if (otg_enabled) {
+          fl_set_button(form_enb[CC_id][UE_id]->button_0,1);
+          fl_set_object_label(form_enb[CC_id][UE_id]->button_0,"DL Traffic ON");
+        } else {
+          fl_set_button(form_enb[CC_id][UE_id]->button_0,0);
+          fl_set_object_label(form_enb[CC_id][UE_id]->button_0,"DL Traffic OFF");
+        }
+      } // CC_id
+    } // UE_id
+
+    ret = threadCreate(&forms_thread, scope_thread, NULL, "scope", -1, OAI_PRIORITY_RT_LOW);
+
+    if (ret == 0)
+      pthread_setname_np( forms_thread, "xforms" );
+
+    printf("Scope thread created, ret=%d\n",ret);
+  }
+
+#endif
+  usleep(10*1000);
+
+  if (nfapi_mode) {
+    printf("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);
+  }
+
+  const char *nfapi_mode_str = "<UNKNOWN>";
+
+  switch(nfapi_mode) {
+    case 0:
+      nfapi_mode_str = "MONOLITHIC";
+      break;
+
+    case 1:
+      nfapi_mode_str = "PNF";
+      break;
+
+    case 2:
+      nfapi_mode_str = "VNF";
+      break;
+
+    default:
+      nfapi_mode_str = "<UNKNOWN NFAPI MODE>";
+      break;
+  }
+
+  printf("NFAPI MODE:%s\n", nfapi_mode_str);
+
+  if (nfapi_mode==2) // VNF
+    wait_nfapi_init("main?");
+
+  printf("START MAIN THREADS\n");
+  // start the main threads
+  number_of_cards = 1;
+  printf("RC.nb_nr_L1_inst:%d\n", RC.nb_nr_L1_inst);
+
+  if (RC.nb_nr_L1_inst > 0) {
+    printf("Initializing gNB threads single_thread_flag:%d wait_for_sync:%d\n", single_thread_flag,wait_for_sync);
+    init_gNB(single_thread_flag,wait_for_sync);
+  }
+
+  printf("wait_gNBs()\n");
+  wait_gNBs();
+  printf("About to Init RU threads RC.nb_RU:%d\n", RC.nb_RU);
+
+  if (RC.nb_RU >0) {
+    printf("Initializing RU threads\n");
+    init_RU(rf_config_file);
+
+    for (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+chain_offset;
+    }
+  }
+
+  config_sync_var=0;
+
+  if (nfapi_mode==1) { // PNF
+    wait_nfapi_init("main?");
+  }
+
+  printf("wait RUs\n");
+  wait_RUs();
+  printf("ALL RUs READY!\n");
+  printf("RC.nb_RU:%d\n", RC.nb_RU);
+  // once all RUs are ready initialize the rest of the gNBs ((dependence on final RU parameters after configuration)
+  printf("ALL RUs ready - init gNBs\n");
+
+  if (nfapi_mode != 1 && nfapi_mode != 2) {
+    printf("Not NFAPI mode - call init_eNB_afterRU()\n");
+    init_eNB_afterRU();
+  } else {
+    printf("NFAPI mode - DO NOT call init_gNB_afterRU()\n");
+  }
+
+  printf("ALL RUs ready - ALL gNBs ready\n");
+  // connect the TX/RX buffers
+  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);
+  printf("About to call end_configmodule() from %s() %s:%d\n", __FUNCTION__, __FILE__, __LINE__);
+  end_configmodule();
+  printf("Called end_configmodule() from %s() %s:%d\n", __FUNCTION__, __FILE__, __LINE__);
+  // wait for end of program
+  printf("TYPE <CTRL-C> TO TERMINATE\n");
+  //getchar();
+#if defined(ENABLE_ITTI)
+  printf("Entering ITTI signals handler\n");
+  itti_wait_tasks_end();
+  printf("Returned from ITTI signal handler\n");
+  oai_exit=1;
+  printf("oai_exit=%d\n",oai_exit);
+#else
+
+  while (oai_exit==0)
+    sleep(1);
+
+  printf("Terminating application - oai_exit=%d\n",oai_exit);
+#endif
+  // stop threads
+#ifdef XFORMS
+  /*
+    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);
+
+        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]->lte_phy_scope_enb);
+      fl_free_form(form_enb[CC_id][UE_id]->lte_phy_scope_enb);
+    }
+        }
+    }
+  */
+#endif
+  printf("stopping MODEM threads\n");
+  // cleanup
+  stop_gNB(NB_gNB_INST);
+  stop_RU(NB_RU);
+
+  /* release memory used by the RU/gNB threads (incomplete), after all
+   * threads have been stopped (they partially use the same memory) */
+  for (int inst = 0; inst < NB_gNB_INST; inst++) {
+    for (int cc_id = 0; cc_id < RC.nb_nr_CC[inst]; cc_id++) {
+      //free_transport(RC.gNB[inst][cc_id]);
+      phy_free_nr_gNB(RC.gNB[inst][cc_id]);
+    }
+  }
+
+  for (int inst = 0; inst < NB_RU; inst++) {
+    nr_phy_free_RU(RC.ru[inst]);
+  }
+
+  free_lte_top();
+  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);
+
+  // *** Handle per CC_id openair0
+
+  for(ru_id=0; ru_id<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);
+
+    if (RC.ru[ru_id]->ifdevice.trx_end_func)
+      RC.ru[ru_id]->ifdevice.trx_end_func(&RC.ru[ru_id]->ifdevice);
+  }
+
+  if (opt_enabled == 1)
+    terminate_opt();
+
+  logClean();
+  printf("Bye.\n");
+  return 0;
+}
diff --git a/executables/nr-softmodem.h b/executables/nr-softmodem.h
new file mode 100644
index 0000000000..49e079ae13
--- /dev/null
+++ b/executables/nr-softmodem.h
@@ -0,0 +1,251 @@
+#ifndef NR_SOFTMODEM_H
+#define NR_SOFTMODEM_H
+
+#define _GNU_SOURCE
+#include <execinfo.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <linux/sched.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syscall.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/sysinfo.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <sys/sysinfo.h>
+#include "targets/ARCH/COMMON/common_lib.h"
+#undef MALLOC
+#include "assertions.h"
+#include "msc.h"
+#include "PHY/types.h"
+
+#include "flexran_agent.h"
+#include "PHY/defs_gNB.h"
+
+#if defined(ENABLE_ITTI)
+  #if defined(ENABLE_USE_MME)
+    #include "s1ap_eNB.h"
+    #ifdef PDCP_USE_NETLINK
+      #include "SIMULATION/ETH_TRANSPORT/proto.h"
+    #endif
+  #endif
+#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_ULMAXE        "set the eNodeB max ULSCH erros\n"
+#define CONFIG_HLP_CALUER        "set UE RX calibration\n"
+#define CONFIG_HLP_CALUERM       ""
+#define CONFIG_HLP_CALUERB       ""
+#define CONFIG_HLP_DBGUEPR       "UE run normal prach power ramping, but don't continue random-access\n"
+#define CONFIG_HLP_CALPRACH      "UE run normal prach with maximum power, but don't continue random-access\n"
+#define CONFIG_HLP_NOL2CN        "bypass L2 and upper layers\n"
+#define CONFIG_HLP_UERXG         "set UE RX gain\n"
+#define CONFIG_HLP_UERXGOFF      "external UE amplifier offset\n"
+#define CONFIG_HLP_UETXG         "set UE TX gain\n"
+#define CONFIG_HLP_UENANTR       "set UE number of rx antennas\n"
+#define CONFIG_HLP_UENANTT       "set UE number of tx antennas\n"
+#define CONFIG_HLP_UESCAN        "set UE to scan around carrier\n"
+#define CONFIG_HLP_DUMPFRAME     "dump UE received frame to rxsig_frame0.dat and exit\n"
+#define CONFIG_HLP_DLSHIFT       "dynamic shift for LLR compuation for TM3/4 (default 0)\n"
+#define CONFIG_HLP_UELOOP        "get softmodem (UE) to loop through memory instead of acquiring from HW\n"
+#define CONFIG_HLP_PHYTST        "test UE phy layer, mac disabled\n"
+#define CONFIG_HLP_DMAMAP        "sets flag for improved EXMIMO UE performance\n"
+#define CONFIG_HLP_EXCCLK        "tells hardware to use an external clock reference\n"
+#define CONFIG_HLP_USIM          "use XOR autentication algo in case of test usim mode\n"
+#define CONFIG_HLP_NOSNGLT       "Disables single-thread mode in lte-softmodem\n"
+#define CONFIG_HLP_TADV          "Set timing_advance\n"
+#define CONFIG_HLP_DLF           "Set the downlink frequency for all component carriers\n"
+#define CONFIG_HLP_CHOFF         "Channel id offset\n"
+#define CONFIG_HLP_SOFTS         "Enable soft scope and L1 and L2 stats (Xforms)\n"
+#define CONFIG_HLP_EXMCAL        "Calibrate the EXMIMO borad, available files: exmimo2_2arxg.lime exmimo2_2brxg.lime \n"
+#define CONFIG_HLP_ITTIL         "Generate ITTI analyzser logs (similar to wireshark logs but with more details)\n"
+#define CONFIG_HLP_DLMCS         "Set the maximum downlink MCS\n"
+#define CONFIG_HLP_STMON         "Enable processing timing measurement of lte softmodem on per subframe basis \n"
+#define CONFIG_HLP_PRB           "Set the PRB, valid values: 6, 25, 50, 100  \n"
+#define CONFIG_HLP_MSLOTS        "Skip the missed slots/subframes \n"
+#define CONFIG_HLP_ULMCS         "Set the maximum uplink MCS\n"
+#define CONFIG_HLP_TDD           "Set hardware to TDD mode (default: FDD). Used only with -U (otherwise set in config file).\n"
+#define CONFIG_HLP_UE            "Set the lte softmodem as a UE\n"
+#define CONFIG_HLP_L2MONW        "Enable L2 wireshark messages on localhost \n"
+#define CONFIG_HLP_L2MONP        "Enable L2 pcap  messages on localhost \n"
+#define CONFIG_HLP_VCD           "Enable VCD (generated file will is named openair_dump_eNB.vcd, read it with target/RT/USER/eNB.gtkw\n"
+#define CONFIG_HLP_TQFS          "Apply three-quarter of sampling frequency, 23.04 Msps to reduce the data rate on USB/PCIe transfers (only valid for 20 MHz)\n"
+#define CONFIG_HLP_TPORT         "tracer port\n"
+#define CONFIG_HLP_NOTWAIT       "don't wait for tracer, start immediately\n"
+#define CONFIG_HLP_TNOFORK       "to ease debugging with gdb\n"
+#define CONFIG_HLP_DISABLNBIOT   "disable nb-iot, even if defined in config\n"
+
+#define CONFIG_HLP_NUMEROLOGY    "adding numerology for 5G\n"
+#define CONFIG_HLP_EMULATE_RF    "Emulated RF enabled(disable by defult)\n"
+#define CONFIG_HLP_PARALLEL_CMD  "three config for level of parallelism 'PARALLEL_SINGLE_THREAD', 'PARALLEL_RU_L1_SPLIT', or 'PARALLEL_RU_L1_TRX_SPLIT'\n"
+#define CONFIG_HLP_WORKER_CMD    "two option for worker 'WORKER_DISABLE' or 'WORKER_ENABLE'\n"
+
+/***************************************************************************************************************************************/
+/* command line options definitions, CMDLINE_XXXX_DESC macros are used to initialize paramdef_t arrays which are then used as argument
+   when calling config_get or config_getlist functions                                                                                 */
+
+
+/*------------------------------------------------------------------------------------------------------------------------------------------*/
+/*                                            command line parameters defining UE running mode                                              */
+/*   optname                     helpstr                paramflags                      XXXptr        defXXXval         type       numelt   */
+/*------------------------------------------------------------------------------------------------------------------------------------------*/
+#define CMDLINE_UEMODEPARAMS_DESC {  \
+    {"calib-ue-rx",                 CONFIG_HLP_CALUER,     0,    iptr:&rx_input_level_dBm,   defintval:0,  TYPE_INT,   0},    \
+    {"calib-ue-rx-med",             CONFIG_HLP_CALUERM,    0,    iptr:&rx_input_level_dBm,   defintval:0,  TYPE_INT,   0},    \
+    {"calib-ue-rx-byp",             CONFIG_HLP_CALUERB,    0,    iptr:&rx_input_level_dBm,   defintval:0,  TYPE_INT,   0},    \
+    {"debug-ue-prach",              CONFIG_HLP_DBGUEPR,    PARAMFLAG_BOOL,   uptr:NULL,        defuintval:1,   TYPE_INT,   0},    \
+    {"no-L2-connect",               CONFIG_HLP_NOL2CN,     PARAMFLAG_BOOL,   uptr:NULL,        defuintval:1,   TYPE_INT,   0},    \
+    {"calib-prach-tx",              CONFIG_HLP_CALPRACH,   PARAMFLAG_BOOL,   uptr:NULL,        defuintval:1,   TYPE_INT,   0},    \
+    {"loop-memory",                 CONFIG_HLP_UELOOP,     0,    strptr:&loopfile,       defstrval:"iqs.in", TYPE_STRING,0},    \
+    {"ue-dump-frame",               CONFIG_HLP_DUMPFRAME,  PARAMFLAG_BOOL,   iptr:&dumpframe,      defintval:0,  TYPE_INT,   0},    \
+  }
+#define CMDLINE_CALIBUERX_IDX                   0
+#define CMDLINE_CALIBUERXMED_IDX                1
+#define CMDLINE_CALIBUERXBYP_IDX                2
+#define CMDLINE_DEBUGUEPRACH_IDX                3
+#define CMDLINE_NOL2CONNECT_IDX                 4
+#define CMDLINE_CALIBPRACHTX_IDX                5
+#define CMDLINE_MEMLOOP_IDX                     6
+#define CMDLINE_DUMPMEMORY_IDX                  7
+/*------------------------------------------------------------------------------------------------------------------------------------------*/
+
+
+/*--------------------------------------------------------------------------------------------------------------------------------------------------*/
+/*                                            command line parameters specific to UE                                                                */
+/*   optname                     helpstr             paramflags                      XXXptr                  defXXXval       type          numelt   */
+/*--------------------------------------------------------------------------------------------------------------------------------------------------*/
+#define CMDLINE_UEPARAMS_DESC {  \
+    {"ue-rxgain",                CONFIG_HLP_UERXG,      0,    dblptr:&(rx_gain[0][0]),      defdblval:0,    TYPE_DOUBLE,   0},     \
+    {"ue-rxgain-off",            CONFIG_HLP_UERXGOFF,   0,    dblptr:&rx_gain_off,        defdblval:0,    TYPE_DOUBLE,   0},     \
+    {"ue-txgain",                CONFIG_HLP_UETXG,      0,    dblptr:&(tx_gain[0][0]),      defdblval:0,    TYPE_DOUBLE,   0},     \
+    {"ue-nb-ant-rx",             CONFIG_HLP_UENANTR,    0,    u8ptr:&nb_antenna_rx,       defuintval:1,   TYPE_UINT8,    0},     \
+    {"ue-nb-ant-tx",             CONFIG_HLP_UENANTT,    0,    u8ptr:&nb_antenna_tx,       defuintval:1,   TYPE_UINT8,    0},     \
+    {"ue-scan-carrier",          CONFIG_HLP_UESCAN,     PARAMFLAG_BOOL, iptr:&UE_scan_carrier,        defintval:0,    TYPE_INT,    0},     \
+    {"ue-max-power",             NULL,          0,    iptr:&(tx_max_power[0]),      defintval:90,   TYPE_INT,    0},     \
+    {"r"  ,                        CONFIG_HLP_PRB,        0,                u8ptr:&(frame_parms[0]->N_RB_DL),   defintval:25,   TYPE_UINT8,    0},     \
+    {"dlsch-demod-shift",        CONFIG_HLP_DLSHIFT,  0,      iptr:(int32_t *)&dlsch_demod_shift, defintval:0,         TYPE_INT,    0},        \
+  }
+
+#define DEFAULT_DLF 2680000000
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
+/*                                            command line parameters common to eNodeB and UE                                                                                */
+/*   optname                     helpstr                paramflags                      XXXptr                  defXXXval                            type           numelt   */
+/*---------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
+#define CMDLINE_PARAMS_DESC {  \
+    {"rf-config-file",           CONFIG_HLP_RFCFGF, 0,      strptr:(char **)&rf_config_file,  defstrval:NULL,        TYPE_STRING,   sizeof(rf_config_file)}, \
+    {"ulsch-max-errors",         CONFIG_HLP_ULMAXE, 0,      uptr:&ULSCH_max_consecutive_errors, defuintval:0,        TYPE_UINT,   0},        \
+    {"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},        \
+    {"mmapped-dma",              CONFIG_HLP_DMAMAP, PARAMFLAG_BOOL,   uptr:&mmapped_dma,      defintval:0,         TYPE_INT,    0},        \
+    {"external-clock",           CONFIG_HLP_EXCCLK, PARAMFLAG_BOOL,   uptr:&clock_source,     defintval:0,         TYPE_INT,    0},        \
+    {"wait-for-sync",            NULL,      PARAMFLAG_BOOL,   iptr:&wait_for_sync,      defintval:0,         TYPE_INT,    0},        \
+    {"single-thread-disable",    CONFIG_HLP_NOSNGLT,  PARAMFLAG_BOOL,   iptr:&single_thread_flag,   defintval:1,         TYPE_INT,    0},        \
+    {"A" ,           CONFIG_HLP_TADV, 0,      uptr:&timing_advance,     defintval:0,         TYPE_UINT,   0},        \
+    {"C" ,           CONFIG_HLP_DLF,  0,      uptr:&(downlink_frequency[0][0]), defuintval:DEFAULT_DLF,      TYPE_UINT,   0},        \
+    {"a" ,           CONFIG_HLP_CHOFF,  0,      iptr:&chain_offset,     defintval:0,         TYPE_INT,    0},        \
+    {"d" ,           CONFIG_HLP_SOFTS,  PARAMFLAG_BOOL,   uptr:(uint32_t *)&do_forms,   defintval:0,         TYPE_INT8,   0},        \
+    {"E" ,           CONFIG_HLP_TQFS, PARAMFLAG_BOOL,   i8ptr:&threequarter_fs,   defintval:0,         TYPE_INT8,   0},        \
+    {"K" ,           CONFIG_HLP_ITTIL,  PARAMFLAG_NOFREE, strptr:&itti_dump_file,   defstrval:"/tmp/itti.dump",    TYPE_STRING,   0},        \
+    {"m" ,           CONFIG_HLP_DLMCS,  0,      uptr:&target_dl_mcs,      defintval:0,         TYPE_UINT,   0},        \
+    {"t" ,           CONFIG_HLP_ULMCS,  0,      uptr:&target_ul_mcs,      defintval:0,         TYPE_UINT,   0},        \
+    {"W" ,           CONFIG_HLP_L2MONW, 0,      strptr:(char **)&in_ip,   defstrval:"127.0.0.1",       TYPE_STRING,   sizeof(in_ip)},    \
+    {"P" ,           CONFIG_HLP_L2MONP, 0,      strptr:(char **)&in_path,   defstrval:"/tmp/oai_opt.pcap",     TYPE_STRING,   sizeof(in_path)},    \
+    {"q" ,           CONFIG_HLP_STMON,  PARAMFLAG_BOOL,   iptr:&opp_enabled,      defintval:0,         TYPE_INT,    0},        \
+    {"S" ,           CONFIG_HLP_MSLOTS, PARAMFLAG_BOOL,   u8ptr:&exit_missed_slots,   defintval:1,         TYPE_UINT8,    0},        \
+    {"T" ,           CONFIG_HLP_TDD,  PARAMFLAG_BOOL,   iptr:&tddflag,      defintval:0,         TYPE_INT,    0},        {"numerology" ,                  CONFIG_HLP_NUMEROLOGY,  PARAMFLAG_BOOL,         iptr:&numerology,                   defintval:0,                    TYPE_INT,       0},                     \
+    {"emulate-rf" ,                  CONFIG_HLP_EMULATE_RF,  PARAMFLAG_BOOL,         iptr:&emulate_rf,                   defintval:0,                    TYPE_INT,       0},                     \
+    {"parallel-config",              CONFIG_HLP_PARALLEL_CMD,0,                      strptr:(char **)&parallel_config,   defstrval:NULL,                 TYPE_STRING,    0},                     \
+    {"worker-config",                CONFIG_HLP_WORKER_CMD,  0,                      strptr:(char **)&worker_config,     defstrval:NULL,                 TYPE_STRING,    0},                     \
+    {"nbiot-disable",          CONFIG_HLP_DISABLNBIOT,PARAMFLAG_BOOL,   iptr:&nonbiotflag,      defintval:0,         TYPE_INT,    0}                       \
+  }
+
+#define CONFIG_HLP_FLOG          "Enable online log \n"
+#define CONFIG_HLP_LOGL          "Set the global log level, valide options: (9:trace, 8/7:debug, 6:info, 4:warn, 3:error)\n"
+#define CONFIG_HLP_LOGV          "Set the global log verbosity \n"
+#define CONFIG_HLP_TELN          "Start embedded telnet server \n"
+/*---------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
+/*                                            command line parameters for LOG utility                                                                                        */
+/*   optname                     helpstr                paramflags                      XXXptr                  defXXXval                            type           numelt   */
+/*---------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
+#define CMDLINE_LOGPARAMS_DESC {  \
+    {"R" ,           CONFIG_HLP_FLOG, 0,                uptr:&online_log_messages,    defintval:1,         TYPE_INT,    0},        \
+    {"g" ,           CONFIG_HLP_LOGL, 0,      uptr:&glog_level,     defintval:0,         TYPE_UINT,     0},        \
+    {"G" ,                           CONFIG_HLP_LOGV, 0,      uptr:&glog_verbosity,           defintval:0,         TYPE_UINT16,   0},        \
+    {"telnetsrv",        CONFIG_HLP_TELN, PARAMFLAG_BOOL,   uptr:&start_telnetsrv,    defintval:0,         TYPE_UINT,     0},        \
+  }
+#define CMDLINE_ONLINELOG_IDX     0
+#define CMDLINE_GLOGLEVEL_IDX     1
+#define CMDLINE_GLOGVERBO_IDX     2
+#define CMDLINE_STARTTELN_IDX     3
+
+
+extern int T_port;
+extern int T_nowait;
+extern int T_dont_fork;
+
+
+/***************************************************************************************************************************************/
+/*  */
+extern pthread_cond_t sync_cond;
+extern pthread_mutex_t sync_mutex;
+extern int sync_var;
+
+
+extern uint32_t          downlink_frequency[MAX_NUM_CCs][4];
+extern int32_t           uplink_frequency_offset[MAX_NUM_CCs][4];
+
+extern int rx_input_level_dBm;
+extern uint8_t exit_missed_slots;
+extern uint64_t num_missed_slots; // counter for the number of missed slots
+
+extern int oaisim_flag;
+extern volatile int  oai_exit;
+
+extern openair0_config_t openair0_cfg[MAX_CARDS];
+extern pthread_cond_t sync_cond;
+extern pthread_mutex_t sync_mutex;
+extern int sync_var;
+extern int transmission_mode;
+extern double cpuf;
+
+#if defined(ENABLE_ITTI)
+  extern volatile int             start_gNB;
+#endif
+
+#include "threads_t.h"
+extern threads_t threads;
+
+// In nr-gnb.c
+extern void init_gNB(int single_thread_flag,int wait_for_sync);
+extern void stop_gNB(int);
+extern void kill_gNB_proc(int inst);
+
+// In nr-ru.c
+extern void init_RU(const char *);
+extern void init_RU_proc(RU_t *ru);
+extern void stop_RU(int nb_ru);
+extern void kill_RU_proc(int inst);
+extern void set_function_spec_param(RU_t *ru);
+
+extern void reset_opp_meas(void);
+extern void print_opp_meas(void);
+
+extern void init_fep_thread(PHY_VARS_gNB *, pthread_attr_t *);
+
+void init_gNB_afterRU(void);
+
+extern int stop_L1L2(module_id_t gnb_id);
+extern int restart_L1L2(module_id_t gnb_id);
+
+#endif
+
diff --git a/executables/nr-ue.c b/executables/nr-ue.c
index 7c96f14159..21b1edd617 100644
--- a/executables/nr-ue.c
+++ b/executables/nr-ue.c
@@ -43,6 +43,7 @@
 #include "PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h"
 
 #include "common/utils/LOG/log.h"
+#include "common/utils/system.h"
 #include "common/utils/LOG/vcd_signal_dumper.h"
 
 #include "T.h"
@@ -123,35 +124,6 @@ extern double cpuf;
 
 #define FRAME_PERIOD    100000000ULL
 #define DAQ_PERIOD      66667ULL
-#define FIFO_PRIORITY   40
-
-
-void init_thread(int core, char *name) {
-  pthread_setname_np(pthread_self(),name);
-  cpu_set_t cpuset;
-  CPU_ZERO(&cpuset);
-
-  if (core >0)
-    CPU_SET(core, &cpuset);
-
-  AssertFatal( 0 == pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset), "");
-  struct sched_param sp;
-  sp.sched_priority = FIFO_PRIORITY;
-  AssertFatal(pthread_setschedparam(pthread_self(),SCHED_FIFO,&sp)==0,
-              "Can't set thread priority, Are you root?\n");
-  /* Check the actual affinity mask assigned to the thread */
-  cpu_set_t *cset=CPU_ALLOC(CPU_SETSIZE);
-
-  if (0 == pthread_getaffinity_np(pthread_self(), CPU_ALLOC_SIZE(CPU_SETSIZE), cset)) {
-    char txt[512]= {0};
-
-    for (int j = 0; j < CPU_SETSIZE; j++)
-      if (CPU_ISSET(j, cset))
-        sprintf(txt+strlen(txt), " %d ", j);
-
-    printf("CPU Affinity of thread %s is %s\n", name, txt);
-  }
-}
 
 typedef enum {
   pss=0,
@@ -403,7 +375,9 @@ void processSubframeRX( PHY_VARS_NR_UE *UE, UE_nr_rxtx_proc_t *proc) {
 #ifdef UE_SLOT_PARALLELISATION
     phy_procedures_slot_parallelization_nrUE_RX( UE, proc, 0, 0, 1, UE->mode, no_relay, NULL );
 #else
+    uint64_t a=rdtsc();
     phy_procedures_nrUE_RX( UE, proc, 0, 1, UE->mode);
+    printf("phy_procedures_nrUE_RX: slot:%d, time %lu\n", proc->nr_tti_rx, (rdtsc()-a)/3500);
     //            printf(">>> nr_ue_pdcch_procedures ended\n");
 #endif
   }
@@ -563,7 +537,8 @@ int computeSamplesShift(PHY_VARS_NR_UE *UE) {
 }
 
 void *UE_thread(void *arg) {
-  init_thread(1, "IQ samples");
+  //this thread should be over the processing thread to keep in real time
+  threadTopInit("UE_IQ",1,OAI_PRIORITY_RT_MAX);
   PHY_VARS_NR_UE *UE = (PHY_VARS_NR_UE *) arg;
   //  int tx_enabled = 0;
   openair0_timestamp timestamp;
@@ -770,10 +745,7 @@ void init_UE(int nb_inst) {
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
   pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
-  pthread_attr_setschedpolicy(&attr, SCHED_RR);
-  struct sched_param sched;
-  sched.sched_priority = sched_get_priority_max(SCHED_RR)-1;
-  pthread_attr_setschedparam(&attr, &sched);
+  pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
 
   for (inst=0; inst < nb_inst; inst++) {
     PHY_VARS_NR_UE *UE = PHY_vars_UE_g[inst][0];
@@ -794,10 +766,7 @@ void init_UE(int nb_inst) {
     mac_inst->initial_bwp_ul.N_RB = UE->frame_parms.N_RB_UL;
     mac_inst->initial_bwp_ul.cyclic_prefix = UE->frame_parms.Ncp;
     LOG_I(PHY,"Intializing UE Threads for instance %d (%p,%p)...\n",inst,PHY_vars_UE_g[inst],PHY_vars_UE_g[inst][0]);
-    AssertFatal(0 == pthread_create(&threads[inst],
-                                    &attr,
-                                    UE_thread,
-                                    (void *)UE), "");
+    threadCreate(&threads[inst], UE_thread, (void *)UE), "UEthread", -1, OAI_PRIORITY_RT);
   }
 
   printf("UE threads created by %ld\n", gettid());
diff --git a/executables/nr-uesoftmodem.c b/executables/nr-uesoftmodem.c
index 0a2f0ac2d5..babf169103 100644
--- a/executables/nr-uesoftmodem.c
+++ b/executables/nr-uesoftmodem.c
@@ -64,7 +64,6 @@ unsigned short config_frames[4] = {2,9,11,13};
 #endif
 
 #include "intertask_interface.h"
-#include "create_tasks.h"
 
 #include "PHY/INIT/phy_init.h"
 #include "system.h"
@@ -311,11 +310,6 @@ void reset_stats(FL_OBJECT *button, long arg) {
 }
 
 static void *scope_thread(void *arg) {
-  struct sched_param sched_param;
-  sched_param.sched_priority = sched_get_priority_min(SCHED_FIFO)+1;
-  sched_setscheduler(0, SCHED_FIFO,&sched_param);
-  printf("Scope thread has priority %d\n",sched_param.sched_priority);
-  //wait the modem is runnign
   sleep(5);
 
   while (!oai_exit) {
@@ -343,12 +337,7 @@ void init_scope(void) {
     fl_show_form (form_ue[UE_id]->lte_phy_scope_ue, FL_PLACE_HOTSPOT, FL_FULLBORDER, title);
     fl_set_button(form_ue[UE_id]->button_0,0);
     fl_set_object_label(form_ue[UE_id]->button_0, "IA Receiver OFF");
-    ret = pthread_create(&forms_thread, NULL, scope_thread, NULL);
-
-    if (ret == 0)
-      pthread_setname_np( forms_thread, "xforms" );
-
-    printf("Scope thread created, ret=%d\n",ret);
+    threadCreate(&forms_thread, scope_thread, NULL, "scope", -1, OAI_PRIORITY_RT_LOW);
   }
 
 #endif
@@ -763,6 +752,7 @@ int main( int argc, char **argv ) {
   // 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)*NUMBER_OF_UE_MAX*MAX_NUM_CCs);
+  configure_linux();
   mlockall(MCL_CURRENT | MCL_FUTURE);
   init_scope();
   number_of_cards = 1;
diff --git a/executables/openairinterface5g_limits.h b/executables/openairinterface5g_limits.h
new file mode 100644
index 0000000000..53ee79a61d
--- /dev/null
+++ b/executables/openairinterface5g_limits.h
@@ -0,0 +1,89 @@
+#ifndef OPENAIRINTERFACE5G_LIMITS_H_
+#define OPENAIRINTERFACE5G_LIMITS_H_
+
+#if 1 /*defined(CBMIMO1) || defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_LMSSDR) || defined(OAI_ADRV9371_ZC706)*/
+  #define NUMBER_OF_eNB_MAX 1
+  #define NUMBER_OF_gNB_MAX 1
+  #define NUMBER_OF_RU_MAX 2
+  #define NUMBER_OF_NR_RU_MAX 2
+  #ifndef PHYSIM
+    #ifndef UE_EXPANSION
+      #define NUMBER_OF_UE_MAX 16
+      #define NUMBER_OF_NR_UE_MAX 16
+      #define NUMBER_OF_CONNECTED_eNB_MAX 3
+      #define NUMBER_OF_CONNECTED_gNB_MAX 3
+    #else
+      #define NUMBER_OF_UE_MAX 256
+      #define NUMBER_OF_NR_UE_MAX 256
+      #define NUMBER_OF_CONNECTED_eNB_MAX 1
+      #define NUMBER_OF_CONNECTED_gNB_MAX 1
+    #endif
+  #else
+    #define NUMBER_OF_UE_MAX 1
+    #define NUMBER_OF_NR_UE_MAX 1
+    #define NUMBER_OF_CONNECTED_eNB_MAX 1
+    #define NUMBER_OF_CONNECTED_gNB_MAX 1
+  #endif
+#else
+  #define NUMBER_OF_eNB_MAX 7
+  #define NUMBER_OF_gNB_MAX 7
+  #define NUMBER_OF_RU_MAX 32
+  #define NUMBER_OF_NR_RU_MAX 32
+  #ifndef UE_EXPANSION
+    #define NUMBER_OF_UE_MAX 20
+    #define NUMBER_OF_NR_UE_MAX 20
+    #define NUMBER_OF_CONNECTED_eNB_MAX 3
+    #define NUMBER_OF_CONNECTED_gNB_MAX 3
+  #else
+    #define NUMBER_OF_UE_MAX 256
+    #define NUMBER_OF_NR_UE_MAX 256
+    #define NUMBER_OF_CONNECTED_eNB_MAX 1
+    #define NUMBER_OF_CONNECTED_gNB_MAX 1
+  #endif
+  #if defined(STANDALONE) && STANDALONE==1
+    #undef  NUMBER_OF_eNB_MAX
+    #undef  NUMBER_OF_gNB_MAX
+
+    #undef  NUMBER_OF_UE_MAX
+    #undef  NUMBER_OF_NR_UE_MAX
+
+    #undef  NUMBER_OF_RU_MAX
+    #undef  NUMBER_OF_NR_RU_MAX
+
+    #define NUMBER_OF_eNB_MAX 3
+    #define NUMBER_OF_gNB_MAX 3
+
+    #define NUMBER_OF_UE_MAX 3
+    #define NUMBER_OF_NR_UE_MAX 3
+
+    #define NUMBER_OF_RU_MAX 3
+    #define NUMBER_OF_NR_RU_MAX 3
+  #endif
+  #if defined(LARGE_SCALE) && LARGE_SCALE
+    #undef  NUMBER_OF_eNB_MAX
+    #undef  NUMBER_OF_gNB_MAX
+
+    #undef  NUMBER_OF_UE_MAX
+    #undef  NUMBER_OF_NR_UE_MAX
+
+    #undef  NUMBER_OF_CONNECTED_eNB_MAX
+    #undef  NUMBER_OF_CONNECTED_gNB_MAX
+
+    #undef  NUMBER_OF_RU_MAX
+    #undef  NUMBER_OF_NR_RU_MAX
+
+    #define NUMBER_OF_eNB_MAX 2
+    #define NUMBER_OF_gNB_MAX 2
+
+    #define NUMBER_OF_UE_MAX 120
+    #define NUMBER_OF_NR_UE_MAX 120
+
+    #define NUMBER_OF_RU_MAX 16
+    #define NUMBER_OF_NR_RU_MAX 16
+
+    #define NUMBER_OF_CONNECTED_eNB_MAX 1 // to save some memory
+    #define NUMBER_OF_CONNECTED_gNB_MAX 1
+  #endif
+#endif
+
+#endif /* OPENAIRINTERFACE5G_LIMITS_H_ */
diff --git a/executables/stats.c b/executables/stats.c
new file mode 100644
index 0000000000..63cadc8ae6
--- /dev/null
+++ b/executables/stats.c
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.1  (the "License"); you may not use this file
+ * except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */
+
+/* Form definition file generated by fdesign */
+
+#include <stdlib.h>
+#include "stats.h"
+
+
+/***************************************
+ ***************************************/
+
+FD_stats_form *
+create_form_stats_form( void ) {
+  FL_OBJECT *obj;
+  FD_stats_form *fdui = fl_malloc( sizeof *fdui );
+  fdui->vdata = fdui->cdata = NULL;
+  fdui->ldata = 0;
+  fdui->stats_form = fl_bgn_form( FL_NO_BOX, 1115, 900 );
+  obj = fl_add_box( FL_UP_BOX, 0, 0, 1115, 900, "" );
+  //fdui->stats_text = obj = fl_add_text( FL_NORMAL_TEXT, 60, 50, 1000, 810, "test" );
+  //fl_set_object_lsize( obj, FL_TINY_SIZE );
+  fdui->stats_text = obj = fl_add_browser( FL_NORMAL_BROWSER, 60, 50, 1000, 810, "test" );
+  fl_set_browser_fontsize(obj,FL_TINY_SIZE);
+  fdui->stats_button = obj = fl_add_button( FL_PUSH_BUTTON, 60, 10, 130, 30, "Reset Stats" );
+  fl_set_object_lalign( obj, FL_ALIGN_CENTER );
+  fl_set_object_color( obj, FL_GREEN, FL_GREEN);
+  fl_set_object_callback( obj, reset_stats, 0 );
+  fl_end_form( );
+  fdui->stats_form->fdui = fdui;
+  return fdui;
+}
diff --git a/executables/stats.h b/executables/stats.h
new file mode 100644
index 0000000000..6c847a2860
--- /dev/null
+++ b/executables/stats.h
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.1  (the "License"); you may not use this file
+ * except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */
+
+/* Header file generated by fdesign on Thu Aug 28 12:13:51 2014 */
+
+#ifndef FD_stats_form_h_
+#define FD_stats_form_h_
+
+#include <forms.h>
+
+/* Callbacks, globals and object handlers */
+
+extern void reset_stats( FL_OBJECT *, long );
+
+/* Forms and Objects */
+
+typedef struct {
+  FL_FORM    *stats_form;
+  void       *vdata;
+  char       *cdata;
+  long        ldata;
+  FL_OBJECT *stats_text;
+  FL_OBJECT *stats_button;
+} FD_stats_form;
+
+extern FD_stats_form *create_form_stats_form( void );
+
+#endif /* FD_stats_form_h_ */
diff --git a/executables/threads_t.h b/executables/threads_t.h
new file mode 100644
index 0000000000..0671f40988
--- /dev/null
+++ b/executables/threads_t.h
@@ -0,0 +1,21 @@
+#ifndef _THREADS_T_H_
+#define _THREADS_T_H_
+
+typedef struct threads_s {
+  int main;
+  int sync;
+  int one;
+  int two;
+  int three;
+  int slot1_proc_one;
+  int slot1_proc_two;
+  int slot1_proc_three;
+  //int dlsch_td_one;
+  //int dlsch_td_two;
+  //int dlsch_td_three;
+  //int dlsch_td1_one;
+  //int dlsch_td1_two;
+  //int dlsch_td1_three;
+} threads_t;
+
+#endif /* _THREADS_T_H_ */
diff --git a/openair1/PHY/NR_TRANSPORT/nr_dci_tools.c b/openair1/PHY/NR_TRANSPORT/nr_dci_tools.c
index e4bec93ddf..14d8958811 100644
--- a/openair1/PHY/NR_TRANSPORT/nr_dci_tools.c
+++ b/openair1/PHY/NR_TRANSPORT/nr_dci_tools.c
@@ -64,7 +64,7 @@ void nr_fill_cce_list(NR_gNB_DCI_ALLOC_t* dci_alloc, uint16_t n_shift, uint8_t m
   }
 
   if (pdcch_params->cr_mapping_type == NFAPI_NR_CCE_REG_MAPPING_INTERLEAVED) {
-    AssertFatal((N_reg%(bsize*R))==0, "CCE to REG interleaving: Invalid configuration leading to non integer C (N_reg %us, bsize %d R %d)\n",
+    AssertFatal((N_reg % (bsize*R))==0, "CCE to REG interleaving: Invalid configuration leading to non integer C (N_reg %us, bsize %d R %d)\n",
     N_reg, bsize, R);
     C = N_reg/(bsize*R);
   }
diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c
index c99d2ea61b..6c4fc3db10 100644
--- a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c
+++ b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c
@@ -84,6 +84,20 @@ unsigned char offset_mumimo_llr_drange[29][3]={{8,8,8},{7,7,7},{7,7,7},{7,7,7},{
 
 extern void print_shorts(char *s,int16_t *x);
 
+static void nr_dlsch_dual_stream_correlation_core(int **dl_ch_estimates_ext,
+                                        int **dl_ch_estimates_ext_i,
+                                        int **dl_ch_rho_ext,
+                                        unsigned char n_tx,
+                                        unsigned char n_rx,
+                                        unsigned char output_shift,
+                                        int length,
+                                        int start_point);
+
+static void nr_dlsch_layer_demapping(int16_t **llr_cw,
+                         uint8_t Nl,
+                                                 uint8_t mod_order,
+                         uint16_t length,
+                         int16_t **llr_layers);
 
 int nr_rx_pdsch(PHY_VARS_NR_UE *ue,
              PDSCH_t type,
@@ -112,7 +126,7 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue,
   uint8_t slot = 0;
 #endif
 
-  unsigned char aatx,aarx;
+  unsigned char aatx=0,aarx=0;
 
   unsigned short nb_rb = 0, nb_re =0, round;
   int avgs = 0;// rb;
@@ -1120,6 +1134,7 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue,
 	nr_dlsch_layer_demapping(pdsch_vars[eNB_id]->llr,
 		  	  	  	  	   dlsch[0]->harq_processes[harq_pid]->Nl,
 						   dlsch[0]->harq_processes[harq_pid]->G,
+						   -1,
                            pdsch_vars[eNB_id]->layer_llr);
  }
 
@@ -2179,7 +2194,7 @@ void nr_dlsch_channel_level_median(int **dl_ch_estimates_ext,
 
 }
 
-void nr_dlsch_dual_stream_correlation_core(int **dl_ch_estimates_ext,
+static void nr_dlsch_dual_stream_correlation_core(int **dl_ch_estimates_ext,
                                         int **dl_ch_estimates_ext_i,
                                         int **dl_ch_rho_ext,
                                         unsigned char n_tx,
@@ -2287,7 +2302,7 @@ void nr_dlsch_detection_mrc_core(int **rxdataF_comp,
   __m128i *rxdataF_comp128_0, *rxdataF_comp128_1, *rxdataF_comp128_2, *rxdataF_comp128_3;
   __m128i *dl_ch_mag128_0, *dl_ch_mag128_1, *dl_ch_mag128_2, *dl_ch_mag128_3;
   __m128i *dl_ch_mag128_0b, *dl_ch_mag128_1b,  *dl_ch_mag128_2b,  *dl_ch_mag128_3b;
-  __m128i *rho128_0, *rho128_1, *rho128_2, *rho128_3;
+  __m128i *rho128_0, *rho128_1, *rho128_2=NULL, *rho128_3=NULL;
   __m128i *rho128_i0, *rho128_i1, *rho128_i2, *rho128_i3;
   int length_mod4 = 0;
   int length2;
@@ -2592,9 +2607,9 @@ unsigned short nr_dlsch_extract_rbs_dual(int **rxdataF,
   //int prb_off,prb_off2;
   int skip_half=0,l;//sss_symb,pss_symb=0,nsymb
   int i,aarx;
-  int32_t *dl_ch0,*dl_ch0p,*dl_ch0_ext,*dl_ch1,*dl_ch1p,*dl_ch1_ext,*rxF,*rxF_ext;
+  int32_t *dl_ch0=NULL,*dl_ch0p=NULL,*dl_ch0_ext=NULL,*dl_ch1=NULL,*dl_ch1p=NULL,*dl_ch1_ext=NULL,*rxF=NULL,*rxF_ext=NULL;
   int symbol_mod,pilots=0,j=0;
-  unsigned char *pmi_loc;
+  unsigned char *pmi_loc=NULL;
 
   pilots = (symbol==2) ? 1 : 0; //to updated from config
   k = frame_parms->first_carrier_offset + 516; //0
@@ -2673,7 +2688,7 @@ unsigned short nr_dlsch_extract_rbs_dual(int **rxdataF,
   return(nb_rb/frame_parms->nb_antennas_rx);
 }
 
-void nr_dlsch_layer_demapping(int16_t **llr_cw,
+static void nr_dlsch_layer_demapping(int16_t **llr_cw,
                          uint8_t Nl,
 						 uint8_t mod_order,
                          uint16_t length,
diff --git a/openair1/SCHED_NR/nr_ru_procedures.c b/openair1/SCHED_NR/nr_ru_procedures.c
index 65dfb64734..8a474d3644 100644
--- a/openair1/SCHED_NR/nr_ru_procedures.c
+++ b/openair1/SCHED_NR/nr_ru_procedures.c
@@ -205,7 +205,7 @@ void nr_init_feptx_thread(RU_t *ru,pthread_attr_t *attr_feptx) {
   pthread_mutex_init( &proc->mutex_feptx, NULL);
   pthread_cond_init( &proc->cond_feptx, NULL);
 
-  pthread_create(&proc->pthread_feptx, attr_feptx, nr_feptx_thread, (void*)ru);
+  threadCreate(&proc->pthread_feptx, nr_feptx_thread, (void*)ru, "feptx", -1, OAI_PRIORITY_RT);
 
 
 }
diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c
index c83074d8a2..b5f19814f7 100644
--- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c
+++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c
@@ -2814,7 +2814,6 @@ void nr_ue_pbch_procedures(uint8_t eNB_id,PHY_VARS_NR_UE *ue,UE_nr_rxtx_proc_t *
   //uint8_t pbch_phase;
   int ret = 0;
   uint16_t frame_tx;
-  static uint8_t first_run = 1;
   uint8_t pbch_trials = 0;
 
   DevAssert(ue);
diff --git a/openair2/ENB_APP/RRC_config_tools.h b/openair2/ENB_APP/RRC_config_tools.h
index a44da1f90c..488441ff59 100644
--- a/openair2/ENB_APP/RRC_config_tools.h
+++ b/openair2/ENB_APP/RRC_config_tools.h
@@ -30,7 +30,7 @@
 #define RRC_CONFIG_TOOLS_H_
 
 #define KHz (1000UL)
-#define MHz (1000 * KHz)
+#define MHz (1000*KHz)
 
 typedef struct eutra_band_s {
   int16_t             band;
diff --git a/openair2/ENB_APP/flexran_agent.c b/openair2/ENB_APP/flexran_agent.c
index 9c0af82e90..2cf72c40ee 100644
--- a/openair2/ENB_APP/flexran_agent.c
+++ b/openair2/ENB_APP/flexran_agent.c
@@ -27,12 +27,12 @@
  */
 
 #include "flexran_agent.h"
+#include <common/utils/system.h>
 
 #include <arpa/inet.h>
 
 void *send_thread(void *args);
 void *receive_thread(void *args);
-pthread_t new_thread(void *(*f)(void *), void *b);
 Protocol__FlexranMessage *flexran_agent_timeout(void* args);
 
 
@@ -102,6 +102,7 @@ void *flexran_agent_task(void *args){
 }
 
 void *receive_thread(void *args) {
+  threadTopInit("flexran",-1,OAI_PRIORITY_RT_LOW);
 
   flexran_agent_info_t  *d = args;
   void                  *data;
@@ -145,38 +146,6 @@ error:
 }
 
 
-/* utility function to create a thread */
-pthread_t new_thread(void *(*f)(void *), void *b) {
-  pthread_t t;
-  pthread_attr_t att;
-
-  if (pthread_attr_init(&att)){ 
-    fprintf(stderr, "pthread_attr_init err\n"); 
-    exit(1); 
-  }
-
-  struct sched_param sched_param_recv_thread;
-
-  sched_param_recv_thread.sched_priority = sched_get_priority_max(SCHED_FIFO) - 1;
-  pthread_attr_setschedparam(&att, &sched_param_recv_thread);
-  pthread_attr_setschedpolicy(&att, SCHED_FIFO);
-
-  if (pthread_attr_setdetachstate(&att, PTHREAD_CREATE_DETACHED)) { 
-    fprintf(stderr, "pthread_attr_setdetachstate err\n"); 
-    exit(1); 
-  }
-  if (pthread_create(&t, &att, f, b)) { 
-    fprintf(stderr, "pthread_create err\n"); 
-    exit(1); 
-  }
-  if (pthread_attr_destroy(&att)) { 
-    fprintf(stderr, "pthread_attr_destroy err\n"); 
-    exit(1); 
-  }
-
-  return t;
-}
-
 int channel_container_init = 0;
 int flexran_agent_start(mid_t mod_id)
 {
@@ -227,8 +196,8 @@ int flexran_agent_start(mid_t mod_id)
 
   /*Initialize the continuous stats update mechanism*/
   flexran_agent_init_cont_stats_update(mod_id);
-  
-  new_thread(receive_thread, flexran);
+  pthread_t t; 
+ threadCreate(&t, receive_thread, flexran, "flexran", -1, OAI_PRIORITY_RT);
 
   /*Initialize and register the mac xface. Must be modified later
    *for more flexibility in agent management */
diff --git a/openair2/LAYER2/PDCP_v10.1.0/pdcp_thread.c b/openair2/LAYER2/PDCP_v10.1.0/pdcp_thread.c
index 0b6be93d59..c37d665fcc 100644
--- a/openair2/LAYER2/PDCP_v10.1.0/pdcp_thread.c
+++ b/openair2/LAYER2/PDCP_v10.1.0/pdcp_thread.c
@@ -101,33 +101,12 @@ static void *pdcp_thread_main(void* param)
 int init_pdcp_thread(void)
 {
 
-  int    error_code;
-  struct sched_param p;
-
-  pthread_attr_init (&pdcp_thread_attr);
-  pthread_attr_setstacksize(&pdcp_thread_attr,OPENAIR_THREAD_STACK_SIZE);
-  //attr_dlsch_threads.priority = 1;
-
-  p.sched_priority = OPENAIR_THREAD_PRIORITY;
-  pthread_attr_setschedparam  (&pdcp_thread_attr, &p);
-  pthread_attr_setschedpolicy (&pdcp_thread_attr, SCHED_FIFO);
   pthread_mutex_init(&pdcp_mutex,NULL);
   pthread_cond_init(&pdcp_cond,NULL);
 
   pdcp_instance_cnt = -1;
   LOG_I(PDCP,"Allocating PDCP thread\n");
-  error_code = pthread_create(&pdcp_thread,
-                              &pdcp_thread_attr,
-                              pdcp_thread_main,
-                              (void*)NULL);
-
-  if (error_code!= 0) {
-    LOG_I(PDCP,"Could not allocate PDCP thread, error %d\n",error_code);
-    return(error_code);
-  } else {
-    LOG_I(PDCP,"Allocate PDCP thread successful\n");
-    pthread_setname_np( pdcp_thread, "PDCP" );
-  }
+  threadCreate(&pdcp_thread, pdcp_thread_main, (void*)NULL, "PDCP", -1, OAI_PRIORITY_RT);
 
   return(0);
 }
diff --git a/openair2/RRC/LTE/rrc_UE.c b/openair2/RRC/LTE/rrc_UE.c
index 42d1e04caa..bef23a83a1 100644
--- a/openair2/RRC/LTE/rrc_UE.c
+++ b/openair2/RRC/LTE/rrc_UE.c
@@ -76,6 +76,7 @@
 #include "pdcp.h"
 #include "plmn_data.h"
 #include "msc.h"
+#include <common/utils/system.h>
 
 #if defined(ENABLE_ITTI)
 # include "intertask_interface.h"
@@ -5477,7 +5478,6 @@ void rrc_control_socket_init(){
 
    struct sockaddr_in rrc_ctrl_socket_addr;
    pthread_attr_t     attr;
-   struct sched_param sched_param;
    int optval; // flag value for setsockopt
    //int n; // message byte size
 
@@ -5505,27 +5505,11 @@ void rrc_control_socket_init(){
       LOG_E(RRC,"[rrc_control_socket_init] ERROR: Failed on binding the socket\n");
       exit(1);
    }
-   //create thread to listen to incoming packets
-   if (pthread_attr_init(&attr) != 0) {
-      LOG_E(RRC, "[rrc_control_socket_init]Failed to initialize pthread attribute for ProSe -> RRC communication (%d:%s)\n",
-            errno, strerror(errno));
-      exit(EXIT_FAILURE);
-   }
-
-   sched_param.sched_priority = 10;
-
-   pthread_attr_setschedpolicy(&attr, SCHED_RR);
-   pthread_attr_setschedparam(&attr, &sched_param);
 
+   threadTopInit("RRC Control Socket",-1,OAI_PRIORITY_RT);
    pthread_t rrc_control_socket_thread;
 
-   if (pthread_create(&rrc_control_socket_thread, &attr, rrc_control_socket_thread_fct, NULL) != 0) {
-      LOG_E(RRC, "[rrc_control_socket_init]Failed to create new thread for RRC/ProSeApp communication (%d:%s)\n",
-            errno, strerror(errno));
-      exit(EXIT_FAILURE);
-   }
-
-   pthread_setname_np( rrc_control_socket_thread, "RRC Control Socket" );
+   threadCreate(&rrc_control_socket_thread, rrc_control_socket_thread_fct, NULL, "RRC/ProSeApp", -1, OAI_PRIORITY_RT);
 
 }
 
diff --git a/openair2/RRC/NR/L2_nr_interface.c b/openair2/RRC/NR/L2_nr_interface.c
index 0dca3b80d2..c8cdfd336f 100644
--- a/openair2/RRC/NR/L2_nr_interface.c
+++ b/openair2/RRC/NR/L2_nr_interface.c
@@ -58,7 +58,6 @@ int8_t mac_rrc_nr_data_req(const module_id_t Mod_idP,
   uint8_t sfn_msb                     = (uint8_t)((frameP>>4)&0x3f);
   
 #ifdef DEBUG_RRC
-  int i;
   LOG_I(RRC,"[eNB %d] mac_rrc_data_req to SRB ID=%d\n",Mod_idP,Srb_id);
 #endif
 
diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c
index 801673b2ef..2cb5414ed0 100644
--- a/openair2/RRC/NR/rrc_gNB.c
+++ b/openair2/RRC/NR/rrc_gNB.c
@@ -116,10 +116,9 @@ mui_t                               rrc_gNB_mui = 0;
 
 void openair_nr_rrc_on(const protocol_ctxt_t* const ctxt_pP){
   
-  int            CC_id;
   LOG_I(NR_RRC, PROTOCOL_NR_RRC_CTXT_FMT" gNB:OPENAIR NR RRC IN....\n",PROTOCOL_NR_RRC_CTXT_ARGS(ctxt_pP));
 
-  for (CC_id = 0; CC_id < MAX_NUM_CCs; CC_id++) {
+  for (int CC_id = 0; CC_id < MAX_NUM_CCs; CC_id++) {
     rrc_config_nr_buffer (&RC.nrrrc[ctxt_pP->module_id]->carrier[CC_id].SI, BCCH, 1);
     RC.nrrrc[ctxt_pP->module_id]->carrier[CC_id].SI.Active = 1;
     rrc_config_nr_buffer (&RC.nrrrc[ctxt_pP->module_id]->carrier[CC_id].Srb0, CCCH, 1);
@@ -144,8 +143,6 @@ void rrc_gNB_generate_SgNBAdditionRequestAcknowledge(
      rrc_gNB_ue_context_t   *const ue_context_pP
      ){
   
-  uint8_t size;
-  uint8_t buffer[100];
   int     CC_id = ue_context_pP->ue_context.primaryCC_id;
   OCTET_STRING_t                                      *secondaryCellGroup;
   NR_CellGroupConfig_t                                *cellGroupconfig;
diff --git a/openair2/UTIL/ASYNC_IF/link_manager.c b/openair2/UTIL/ASYNC_IF/link_manager.c
index c1ce742d0b..c8b7d59f50 100644
--- a/openair2/UTIL/ASYNC_IF/link_manager.c
+++ b/openair2/UTIL/ASYNC_IF/link_manager.c
@@ -30,6 +30,7 @@
 
 #include "link_manager.h"
 #include "common/utils/LOG/log.h"
+#include <common/utils/system.h>
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -112,41 +113,14 @@ link_manager_t *create_link_manager(
   ret->socket_link = link;
   ret->run = 1;
 
-  if (pthread_attr_init(&attr))
-    goto error;
-
-  // Make the async interface threads real-time
-  //#ifndef LOWLATENCY
-  struct sched_param sched_param_recv_thread;
-
-  sched_param_recv_thread.sched_priority = sched_get_priority_max(SCHED_RR) - 1;
-  pthread_attr_setschedparam(&attr, &sched_param_recv_thread);
-  pthread_attr_setschedpolicy(&attr, SCHED_RR);
-  //#endif
-
-  if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED))
-    goto error;
-
-  if (pthread_create(&t, &attr, link_manager_sender_thread, ret))
-    goto error;
+  threadCreate(&t, link_manager_sender_thread, ret, "flexranSender", -1, OAI_PRIORITY_RT_LOW);
   ret->sender = t;
 
-  if (pthread_create(&t, &attr, link_manager_receiver_thread, ret))
-    /* we should destroy the other thread here */
-    goto error;
+  threadCreate(&t, link_manager_receiver_thread, ret, "flexranReceiver", -1, OAI_PRIORITY_RT_LOW);
   ret->receiver = t;
 
-  if (pthread_attr_destroy(&attr))
-    /* to be clean we should destroy the threads at this point,
-     * even if in practice we never reach it */
-    goto error;
-
   return ret;
 
-error:
-  LOG_E(MAC, "%s: an error occured\n", __FUNCTION__);
-  free(ret);
-  return NULL;
 }
 
 void destroy_link_manager(link_manager_t *manager)
diff --git a/openair2/UTIL/CLI/cli_server.c b/openair2/UTIL/CLI/cli_server.c
index d4f97f15ba..1994855b5d 100644
--- a/openair2/UTIL/CLI/cli_server.c
+++ b/openair2/UTIL/CLI/cli_server.c
@@ -90,10 +90,7 @@ int cli_server_init(cli_handler_t handler)
   }
 
   /* create telnet listener thread */
-  if (pthread_create(&cli_server_listener, NULL, cli_server_listen, NULL)) {
-    perror("thread");
-    return -1;
-  }
+  threadCreate(&cli_server_listener, cli_server_listen, NULL, "telnet", -1, OAI_PRIORITY_RT_LOW);
 
   return 0;
 }
diff --git a/openair2/UTIL/OPT/probe.c b/openair2/UTIL/OPT/probe.c
index a1e5367168..f2296017b8 100644
--- a/openair2/UTIL/OPT/probe.c
+++ b/openair2/UTIL/OPT/probe.c
@@ -90,6 +90,7 @@ what about the implementation
 
 #include <pthread.h>
 #include <stdint.h>
+#include <common/utils/system.h>
 
 #include "opt.h"
 
@@ -203,15 +204,7 @@ int opt_create_listener_socket(char *ip_address, uint16_t port)
     return -1;
   }
 
-  ret = pthread_create(&opt_listener.thread, NULL, opt_listener_thread, NULL);
-
-  if (ret != 0) {
-    LOG_E(OPT, "Failed to create thread for server socket: %s\n", strerror(errno));
-    opt_type = OPT_NONE;
-    close(opt_listener.sd);
-    opt_listener.sd = -1;
-    return -1;
-  }
+  threadCreate(&opt_listener.thread, opt_listener_thread, NULL, "flexran", -1, OAI_PRIORITY_RT_LOW);
 
   return 0;
 }
diff --git a/openair2/UTIL/OTG/otg_rx_socket.c b/openair2/UTIL/OTG/otg_rx_socket.c
index 88ecd3e904..9e9b6bf4a1 100644
--- a/openair2/UTIL/OTG/otg_rx_socket.c
+++ b/openair2/UTIL/OTG/otg_rx_socket.c
@@ -195,17 +195,7 @@ void server_socket_tcp_ip4()
 
 
               /* create  new thread for the new connection */
-              if (pthread_create(&id, NULL, (void *)recv_ip4_tcp, (void*)csock))
-                LOG_W(OTG,"SOCKET:: TCP-IP4 ::pthread_create OK!\n");
-
-              else
-                LOG_W(OTG,"SOCKET:: TCP-IP4 ::Error in pthread_create \n");
-
-              if (pthread_detach(id))
-                LOG_W(OTG,"SOCKET:: TCP-IP4 ::pthread_detach OK!\n");
-              else
-                LOG_W(OTG,"SOCKET:: TCP-IP4 ::Error in pthread_detach\n");
-
+              threadCreate(&id, (void *)recv_ip4_tcp, (void*)csock), "OTG", -1, OAI_PRIORITY_RT_LOW);
               LOG_I(OTG,"SOCKET:: TCP-IP4 :: Client n=%d finish transmission\n", cmpt_cl);
               cmpt_cl+=1;
             }
diff --git a/openair3/NAS/UE/UEprocess.c b/openair3/NAS/UE/UEprocess.c
index 6a70911e21..2478eaee29 100644
--- a/openair3/NAS/UE/UEprocess.c
+++ b/openair3/NAS/UE/UEprocess.c
@@ -160,27 +160,15 @@ int main(int argc, const char *argv[])
    */
   pthread_t user_mngr;
 
-  if (pthread_create (&user_mngr, &attr, _nas_user_mngr, &user_fd) != 0) {
-    LOG_TRACE (ERROR, "UE-MAIN   - "
-               "Failed to create the user management thread");
-    user_api_close (user_api_id);
-    network_api_close (network_fd);
-    exit (EXIT_FAILURE);
-  }
+  threadCreate (&user_mngr, , _nas_user_mngr, &user_fd, "UE-nas", -1, OAI_PRIORITY_RT_LOW) ;
 
   /*
    * Start thread use to manage the network connection endpoint
    */
   pthread_t network_mngr;
 
-  if (pthread_create (&network_mngr, &attr, _nas_network_mngr,
-                      &network_fd) != 0) {
-    LOG_TRACE (ERROR, "UE-MAIN   - "
-               "Failed to create the network management thread");
-    user_api_close (user_api_id);
-    network_api_close (network_fd);
-    exit (EXIT_FAILURE);
-  }
+  threadCreate (&network_mngr,  _nas_network_mngr,
+                      &network_fd, "UE-nas-mgr", -1, OAI_PRIORITY_RT_LOW) ;
 
   pthread_attr_destroy (&attr);
 
diff --git a/targets/ARCH/USRP/USERSPACE/LIB/usrp_lib.cpp b/targets/ARCH/USRP/USERSPACE/LIB/usrp_lib.cpp
index 58a8697f79..ab23d52b3a 100644
--- a/targets/ARCH/USRP/USERSPACE/LIB/usrp_lib.cpp
+++ b/targets/ARCH/USRP/USERSPACE/LIB/usrp_lib.cpp
@@ -1102,6 +1102,9 @@ extern "C" {
         device->type=USRP_X300_DEV;
         usrp_master_clock = 184.32e6;
         args += boost::str(boost::format(",master_clock_rate=%f") % usrp_master_clock);
+	// USRP recommended: https://files.ettus.com/manual/page_usrp_x3x0_config.html
+	if ( 0 != system("sysctl -w net.core.rmem_max=33554432 net.core.wmem_max=33554432") )
+		LOG_W(HW,"Can't set kernel paramters for X3xx\n");
       }
 
       s->usrp = uhd::usrp::multi_usrp::make(args);
diff --git a/targets/RT/USER/lte-uesoftmodem.c b/targets/RT/USER/lte-uesoftmodem.c
index 9b8ea00123..e14abe4f57 100644
--- a/targets/RT/USER/lte-uesoftmodem.c
+++ b/targets/RT/USER/lte-uesoftmodem.c
@@ -1005,7 +1005,7 @@ printf("~~~~~~~~~~~~~~~~~~~~successfully get the parallel config[%d], worker con
   
   
   
-#ifndef DEADLINE_SCHEDULER
+#if 0 // #ifndef DEADLINE_SCHEDULER
   
   printf("NO deadline scheduler\n");
   /* Currently we set affinity for UHD to CPU 0 for eNB/UE and only if number of CPUS >2 */
-- 
2.26.2