diff --git a/CMakeLists.txt b/CMakeLists.txt index bbb17b279..d79ab6714 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,46 +62,47 @@ configure_file( ######################################################################## # Options ######################################################################## -option(ENABLE_SRSUE "Build srsUE application" ON) -option(ENABLE_SRSENB "Build srsENB application" ON) -option(ENABLE_SRSEPC "Build srsEPC application" ON) -option(DISABLE_SIMD "Disable SIMD instructions" OFF) -option(AUTO_DETECT_ISA "Autodetect supported ISA extensions" ON) +option(ENABLE_SRSUE "Build srsUE application" ON) +option(ENABLE_SRSENB "Build srsENB application" ON) +option(ENABLE_SRSEPC "Build srsEPC application" ON) +option(DISABLE_SIMD "Disable SIMD instructions" OFF) +option(AUTO_DETECT_ISA "Autodetect supported ISA extensions" ON) + +option(ENABLE_GUI "Enable GUI (using srsGUI)" ON) +option(ENABLE_UHD "Enable UHD" ON) +option(ENABLE_BLADERF "Enable BladeRF" ON) +option(ENABLE_SOAPYSDR "Enable SoapySDR" ON) +option(ENABLE_ZEROMQ "Enable ZeroMQ" ON) +option(ENABLE_HARDSIM "Enable support for SIM cards" ON) + +option(ENABLE_TTCN3 "Enable TTCN3 test binaries" OFF) +option(ENABLE_ZMQ_TEST "Enable ZMQ based E2E tests" OFF) + +option(BUILD_STATIC "Attempt to statically link external deps" OFF) +option(RPATH "Enable RPATH" OFF) +option(ENABLE_ASAN "Enable gcc/clang address sanitizer" OFF) +option(ENABLE_GCOV "Enable gcc/clang address sanitizer" OFF) +option(ENABLE_MSAN "Enable clang memory sanitizer" OFF) +option(ENABLE_TSAN "Enable clang thread sanitizer" OFF) +option(ENABLE_TIDY "Enable clang tidy" OFF) + +option(USE_LTE_RATES "Use standard LTE sampling rates" OFF) +option(USE_MKL "Use MKL instead of fftw" OFF) + +option(ENABLE_TIMEPROF "Enable time profiling" ON) + +option(FORCE_32BIT "Add flags to force 32 bit compilation" OFF) -option(ENABLE_GUI "Enable GUI (using srsGUI)" ON) -option(ENABLE_UHD "Enable UHD" ON) -option(ENABLE_BLADERF "Enable BladeRF" ON) -option(ENABLE_SOAPYSDR "Enable SoapySDR" ON) -option(ENABLE_ZEROMQ "Enable ZeroMQ" ON) -option(ENABLE_HARDSIM "Enable support for SIM cards" ON) - -option(ENABLE_TTCN3 "Enable TTCN3 test binaries" OFF) -option(ENABLE_ZMQ_TEST "Enable ZMQ based E2E tests" OFF) - -option(BUILD_STATIC "Attempt to statically link external deps" OFF) -option(RPATH "Enable RPATH" OFF) -option(ENABLE_ASAN "Enable gcc/clang address sanitizer" OFF) -option(ENABLE_GCOV "Enable gcc/clang address sanitizer" OFF) -option(ENABLE_MSAN "Enable clang memory sanitizer" OFF) -option(ENABLE_TIDY "Enable clang tidy" OFF) - -option(USE_LTE_RATES "Use standard LTE sampling rates" OFF) -option(USE_MKL "Use MKL instead of fftw" OFF) - -option(ENABLE_TIMEPROF "Enable time profiling" ON) - -option(FORCE_32BIT "Add flags to force 32 bit compilation" OFF) - -option(ENABLE_SRSLOG_TRACING "Enable event tracing using srslog" OFF) -option(ASSERTS_ENABLED "Enable srsRAN asserts" ON) -option(STOP_ON_WARNING "Interrupt application on warning" OFF) +option(ENABLE_SRSLOG_TRACING "Enable event tracing using srslog" OFF) +option(ASSERTS_ENABLED "Enable srsRAN asserts" ON) +option(STOP_ON_WARNING "Interrupt application on warning" OFF) # Users that want to try this feature need to make sure the lto plugin is # loaded by bintools (ar, nm, ...). Older versions of bintools will not do # it automatically so it is necessary to use the gcc wrappers of the compiler # (gcc-ar, gcc-nm, ...). -option(BUILD_WITH_LTO "Enable LTO (experimental)" OFF) - +option(BUILD_WITH_LTO "Enable LTO (experimental)" OFF) + if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64") set(GCC_ARCH armv8-a CACHE STRING "GCC compile for specific architecture.") message(STATUS "Detected aarch64 processor") @@ -479,15 +480,20 @@ if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") endif(NOT WIN32) endif(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug") - if (ENABLE_ASAN AND ENABLE_MSAN) - message(FATAL_ERROR "ASAN and MSAN cannot be enabled at the same time.") - endif (ENABLE_ASAN AND ENABLE_MSAN) + if ((ENABLE_ASAN AND ENABLE_MSAN) OR (ENABLE_ASAN AND ENABLE_TSAN) OR (ENABLE_MSAN AND ENABLE_TSAN)) + message(FATAL_ERROR "ASAN, MSAN and TSAN cannot be enabled at the same time.") + endif () if (ENABLE_ASAN) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") endif (ENABLE_ASAN) + if (ENABLE_TSAN) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=thread") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread") + endif (ENABLE_TSAN) + if (ENABLE_MSAN AND CMAKE_C_COMPILER_ID MATCHES "Clang") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=memory -fno-omit-frame-pointer -fPIE -pie") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=memory -fno-omit-frame-pointer -fPIE -pie") diff --git a/lib/include/srsran/adt/circular_buffer.h b/lib/include/srsran/adt/circular_buffer.h index b3a384957..cb17e3c2b 100644 --- a/lib/include/srsran/adt/circular_buffer.h +++ b/lib/include/srsran/adt/circular_buffer.h @@ -24,6 +24,7 @@ #include "srsran/adt/detail/type_storage.h" #include "srsran/adt/expected.h" +#include "srsran/adt/pool/pool_utils.h" #include "srsran/common/srsran_assert.h" #include @@ -240,14 +241,6 @@ protected: size_t count = 0; }; -struct noop_operator { - template - void operator()(const T&) - { - // noop - } -}; - /** * Base common class for definition of blocking queue data structures with the following features: * - it stores pushed/popped samples in an internal circular buffer @@ -267,22 +260,29 @@ public: base_blocking_queue(PushingFunc push_func_, PoppingFunc pop_func_, Args&&... args) : circ_buffer(std::forward(args)...), push_func(push_func_), pop_func(pop_func_) {} + base_blocking_queue(const base_blocking_queue&) = delete; + base_blocking_queue(base_blocking_queue&&) = delete; + base_blocking_queue& operator=(const base_blocking_queue&) = delete; + base_blocking_queue& operator=(base_blocking_queue&&) = delete; void stop() { std::unique_lock lock(mutex); if (active) { active = false; - if (nof_waiting == 0) { - return; + if (nof_waiting > 0) { + // Stop pending pushing/popping threads + do { + lock.unlock(); + cvar_empty.notify_all(); + cvar_full.notify_all(); + std::this_thread::yield(); + lock.lock(); + } while (nof_waiting > 0); } - do { - lock.unlock(); - cvar_empty.notify_all(); - cvar_full.notify_all(); - std::this_thread::yield(); - lock.lock(); - } while (nof_waiting > 0); + + // Empty queue + circ_buffer.clear(); } } @@ -300,8 +300,7 @@ public: bool pop_wait_until(T& obj, const std::chrono::system_clock::time_point& until) { return pop_(obj, true, &until); } void clear() { - std::lock_guard lock(mutex); - T obj; + T obj; while (pop_(obj, false)) { } } diff --git a/lib/include/srsran/adt/circular_map.h b/lib/include/srsran/adt/circular_map.h index 25d5b5cf8..1c03e9b43 100644 --- a/lib/include/srsran/adt/circular_map.h +++ b/lib/include/srsran/adt/circular_map.h @@ -179,6 +179,16 @@ public: return iterator(this, idx); } + template + void overwrite(K id, U&& obj) + { + size_t idx = id % N; + if (present[idx]) { + erase(buffer[idx].get().first); + } + insert(id, std::forward(obj)); + } + bool erase(K id) { if (not contains(id)) { @@ -193,20 +203,24 @@ public: iterator erase(iterator it) { - srsran_assert(it.idx < N, "Iterator out-of-bounds (%zd >= %zd)", it.idx, N); + srsran_assert(it.idx < N and it.ptr == this, "Iterator out-of-bounds (%zd >= %zd)", it.idx, N); iterator next = it; ++next; - it->~obj_t(); - present[it->first] = false; + present[it.idx] = false; + get_obj_(it.idx).~obj_t(); --count; return next; } void clear() { - for (auto it = begin(); it != end();) { - it = erase(it); + for (size_t i = 0; i < N; ++i) { + if (present[i]) { + present[i] = false; + get_obj_(i).~obj_t(); + } } + count = 0; } T& operator[](K id) diff --git a/lib/include/srsran/adt/detail/type_storage.h b/lib/include/srsran/adt/detail/type_storage.h index 35a9a44c0..4dbf7fa7a 100644 --- a/lib/include/srsran/adt/detail/type_storage.h +++ b/lib/include/srsran/adt/detail/type_storage.h @@ -22,6 +22,7 @@ #ifndef SRSRAN_TYPE_STORAGE_H #define SRSRAN_TYPE_STORAGE_H +#include #include #include #include @@ -40,8 +41,9 @@ union max_alignment_t { long double d2; uint32_t* ptr; }; +const static size_t max_alignment = alignof(max_alignment_t); -template +template struct type_storage { using value_type = T; @@ -51,19 +53,29 @@ struct type_storage { new (&buffer) T(std::forward(args)...); } void destroy() { get().~T(); } - void copy_ctor(const type_storage& other) { emplace(other.get()); } - void move_ctor(type_storage&& other) { emplace(std::move(other.get())); } - void copy_assign(const type_storage& other) { get() = other.get(); } - void move_assign(type_storage&& other) { get() = std::move(other.get()); } + void copy_ctor(const type_storage& other) { emplace(other.get()); } + void move_ctor(type_storage&& other) { emplace(std::move(other.get())); } + void copy_assign(const type_storage& other) { get() = other.get(); } + void move_assign(type_storage&& other) { get() = std::move(other.get()); } T& get() { return reinterpret_cast(buffer); } const T& get() const { return reinterpret_cast(buffer); } - typename std::aligned_storage::type buffer; + void* addr() { return static_cast(&buffer); } + const void* addr() const { return static_cast(&buffer); } + explicit operator void*() { return addr(); } + + const static size_t obj_size = sizeof(T) > MinSize ? sizeof(T) : MinSize; + const static size_t align_size = alignof(T) > AlignSize ? alignof(T) : AlignSize; + + typename std::aligned_storage::type buffer; }; -template -void copy_if_present_helper(type_storage& lhs, const type_storage& rhs, bool lhs_present, bool rhs_present) +template +void copy_if_present_helper(type_storage& lhs, + const type_storage& rhs, + bool lhs_present, + bool rhs_present) { if (lhs_present and rhs_present) { lhs.get() = rhs.get(); @@ -76,8 +88,11 @@ void copy_if_present_helper(type_storage& lhs, const type_storage& rhs, bo } } -template -void move_if_present_helper(type_storage& lhs, type_storage& rhs, bool lhs_present, bool rhs_present) +template +void move_if_present_helper(type_storage& lhs, + type_storage& rhs, + bool lhs_present, + bool rhs_present) { if (lhs_present and rhs_present) { lhs.move_assign(std::move(rhs)); diff --git a/lib/include/srsran/adt/move_callback.h b/lib/include/srsran/adt/move_callback.h index bbf7b5d20..f4e80263a 100644 --- a/lib/include/srsran/adt/move_callback.h +++ b/lib/include/srsran/adt/move_callback.h @@ -42,9 +42,9 @@ namespace srsran { //! Size of the buffer used by "move_callback" to store functors without calling "new" -constexpr size_t default_buffer_size = 32; +constexpr size_t default_move_callback_buffer_size = 32; -template +template class move_callback; namespace task_details { @@ -54,11 +54,11 @@ template class oper_table_t { public: - constexpr oper_table_t() = default; - virtual R call(void* src, Args&&... args) const = 0; - virtual void move(void* src, void* dest) const = 0; - virtual void dtor(void* src) const = 0; - virtual bool is_in_small_buffer() const = 0; + constexpr oper_table_t() = default; + virtual R call(void* src, Args... args) const = 0; + virtual void move(void* src, void* dest) const = 0; + virtual void dtor(void* src) const = 0; + virtual bool is_in_small_buffer() const = 0; }; //! specialization of move/call/destroy operations for when the "move_callback" is empty @@ -67,7 +67,7 @@ class empty_table_t : public oper_table_t { public: constexpr empty_table_t() = default; - R call(void* src, Args&&... args) const final + R call(void* src, Args... args) const final { srsran_terminate("ERROR: bad function call (cause: function ptr is empty)"); } @@ -82,7 +82,7 @@ class smallbuffer_table_t : public oper_table_t { public: constexpr smallbuffer_table_t() = default; - R call(void* src, Args&&... args) const final { return (*static_cast(src))(std::forward(args)...); } + R call(void* src, Args... args) const final { return (*static_cast(src))(std::forward(args)...); } void move(void* src, void* dest) const final { ::new (dest) FunT(std::move(*static_cast(src))); @@ -98,7 +98,7 @@ class heap_table_t : public oper_table_t { public: constexpr heap_table_t() = default; - R call(void* src, Args&&... args) const final { return (**static_cast(src))(std::forward(args)...); } + R call(void* src, Args... args) const final { return (**static_cast(src))(std::forward(args)...); } void move(void* src, void* dest) const final { *static_cast(dest) = *static_cast(src); @@ -124,8 +124,8 @@ using enable_if_big_capture = } // namespace task_details -template -class move_callback +template +class move_callback { static constexpr size_t capacity = Capacity >= sizeof(void*) ? Capacity : sizeof(void*); ///< size of buffer using storage_t = typename std::aligned_storage::type; @@ -149,6 +149,9 @@ public: template = true> move_callback(T&& function) { + static_assert( + not ForbidAlloc, + "Failed to store provided callback in std::move_callback specialization that forbids heap allocations."); using FunT = typename std::decay::type; static const task_details::heap_table_t heap_oper_table{}; oper_ptr = &heap_oper_table; @@ -172,7 +175,7 @@ public: return *this; } - R operator()(Args&&... args) const noexcept { return oper_ptr->call(&buffer, std::forward(args)...); } + R operator()(Args... args) const noexcept { return oper_ptr->call(&buffer, std::forward(args)...); } bool is_empty() const { return oper_ptr == &empty_table; } bool is_in_small_buffer() const { return oper_ptr->is_in_small_buffer(); } @@ -185,8 +188,8 @@ private: const oper_table_t* oper_ptr; }; -template -constexpr task_details::empty_table_t move_callback::empty_table; +template +constexpr task_details::empty_table_t move_callback::empty_table; //! Generic move task using move_task_t = move_callback; diff --git a/lib/include/srsran/adt/pool/batch_mem_pool.h b/lib/include/srsran/adt/pool/batch_mem_pool.h new file mode 100644 index 000000000..1829fca26 --- /dev/null +++ b/lib/include/srsran/adt/pool/batch_mem_pool.h @@ -0,0 +1,184 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSRAN_BATCH_MEM_POOL_H +#define SRSRAN_BATCH_MEM_POOL_H + +#include "memblock_cache.h" +#include "pool_utils.h" +#include "srsran/common/srsran_assert.h" +#include "srsran/common/thread_pool.h" +#include +#include + +namespace srsran { + +/** + * Non-thread-safe, node-based memory pool that allocates nodes in batches of "objs_per_batch" > 1, and caches + * allocated blocks on deallocation + */ +class growing_batch_mem_pool +{ +public: + explicit growing_batch_mem_pool(size_t objs_per_batch_, + size_t node_size_, + size_t node_alignment_, + int init_size = -1) : + objs_per_batch(objs_per_batch_), + memblock_size(std::max(node_size_, free_memblock_list::min_memblock_size())), + allocated(objs_per_batch * memblock_size, std::max(node_alignment_, free_memblock_list::min_memblock_align())) + { + size_t N = init_size < 0 ? objs_per_batch_ : init_size; + while (N > cache_size()) { + allocate_batch(); + } + } + ~growing_batch_mem_pool() + { + srsran_assert(cache_size() == size(), "Not all nodes have been deallocated yet (%zd < %zd)", cache_size(), size()); + } + + size_t get_node_max_size() const { return memblock_size; } + + void clear() + { + free_list.clear(); + allocated.clear(); + } + + size_t cache_size() const { return free_list.size(); } + size_t size() const { return allocated.size() * objs_per_batch; } + + void allocate_batch() + { + uint8_t* batch_payload = static_cast(allocated.allocate_block()); + for (size_t i = 0; i < objs_per_batch; ++i) { + void* cache_node = batch_payload + i * memblock_size; + free_list.push(cache_node); + } + } + + void* allocate_node() + { + if (free_list.empty()) { + allocate_batch(); + } + return free_list.pop(); + } + + void deallocate_node(void* ptr) { free_list.push(ptr); } + +private: + const size_t objs_per_batch; + const size_t memblock_size; + + memblock_stack allocated; + free_memblock_list free_list; +}; + +/** + * Thread-safe object pool specialized in allocating batches of objects in a preemptive way in a background thread + * to minimize latency. + * Note: The dispatched allocation jobs may outlive the pool. To handle this, the pool state is passed to jobs via a + * shared ptr. + */ +class background_mem_pool +{ +public: + const size_t batch_threshold; + + explicit background_mem_pool(size_t nodes_per_batch_, size_t node_size_, size_t thres_, int initial_size = -1) : + batch_threshold(thres_), + state(std::make_shared(this)), + grow_pool(nodes_per_batch_, node_size_, detail::max_alignment, initial_size) + { + srsran_assert(batch_threshold > 1, "Invalid arguments for background memory pool"); + } + ~background_mem_pool() + { + std::lock_guard lock(state->mutex); + state->pool = nullptr; + grow_pool.clear(); + } + + /// alloc new object space. If no memory is pre-reserved in the pool, malloc is called to allocate new batch. + void* allocate_node(size_t sz) + { + srsran_assert(sz <= grow_pool.get_node_max_size(), + "Mismatch of allocated node size=%zd and object size=%zd", + sz, + grow_pool.get_node_max_size()); + std::lock_guard lock(state->mutex); + void* node = grow_pool.allocate_node(); + + if (grow_pool.size() < batch_threshold) { + allocate_batch_in_background_unlocked(); + } + return node; + } + + void deallocate_node(void* p) + { + std::lock_guard lock(state->mutex); + grow_pool.deallocate_node(p); + } + + void allocate_batch() + { + std::lock_guard lock(state->mutex); + grow_pool.allocate_batch(); + } + + size_t get_node_max_size() const { return grow_pool.get_node_max_size(); } + size_t cache_size() const + { + std::lock_guard lock(state->mutex); + return grow_pool.cache_size(); + } + +private: + void allocate_batch_in_background_unlocked() + { + if (state->dispatched) { + // new batch allocation already ongoing + return; + } + state->dispatched = true; + std::shared_ptr state_sptr = state; + get_background_workers().push_task([state_sptr]() { + std::lock_guard lock(state_sptr->mutex); + // check if pool has not been destroyed + if (state_sptr->pool != nullptr) { + auto* pool = state_sptr->pool; + do { + pool->grow_pool.allocate_batch(); + } while (pool->grow_pool.cache_size() < pool->batch_threshold); + } + state_sptr->dispatched = false; + }); + } + + // State is stored in a shared_ptr that may outlive the pool. + struct detached_pool_state { + std::mutex mutex; + background_mem_pool* pool; + bool dispatched = false; + explicit detached_pool_state(background_mem_pool* pool_) : pool(pool_) {} + }; + std::shared_ptr state; + + growing_batch_mem_pool grow_pool; +}; + +} // namespace srsran + +#endif // SRSRAN_BATCH_MEM_POOL_H diff --git a/lib/include/srsran/adt/pool/circular_stack_pool.h b/lib/include/srsran/adt/pool/circular_stack_pool.h new file mode 100644 index 000000000..ac9a3e82f --- /dev/null +++ b/lib/include/srsran/adt/pool/circular_stack_pool.h @@ -0,0 +1,112 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSRAN_CIRCULAR_MAP_STACK_POOL_H +#define SRSRAN_CIRCULAR_MAP_STACK_POOL_H + +#include "batch_mem_pool.h" +#include "linear_allocator.h" +#include "srsran/adt/circular_array.h" +#include + +namespace srsran { + +template +class circular_stack_pool +{ + struct mem_block_elem_t { + std::mutex mutex; + size_t key = std::numeric_limits::max(); + size_t count = 0; + linear_allocator alloc; + + void clear() + { + key = std::numeric_limits::max(); + count = 0; + alloc.clear(); + } + }; + +public: + circular_stack_pool(size_t nof_objs_per_batch, size_t stack_size, size_t batch_thres, int initial_size = -1) : + central_cache(std::min(NofStacks, nof_objs_per_batch), stack_size, batch_thres, initial_size), + logger(srslog::fetch_basic_logger("POOL")) + {} + circular_stack_pool(circular_stack_pool&&) = delete; + circular_stack_pool(const circular_stack_pool&) = delete; + circular_stack_pool& operator=(circular_stack_pool&&) = delete; + circular_stack_pool& operator=(const circular_stack_pool&) = delete; + ~circular_stack_pool() + { + for (mem_block_elem_t& elem : pools) { + std::unique_lock lock(elem.mutex); + srsran_assert(elem.count == 0, "There are missing deallocations for stack id=%zd", elem.key); + if (elem.alloc.is_init()) { + void* ptr = elem.alloc.memblock_ptr(); + elem.alloc.clear(); + central_cache.deallocate_node(ptr); + } + } + } + + void* allocate(size_t key, size_t size, size_t alignment) noexcept + { + size_t idx = key % NofStacks; + mem_block_elem_t& elem = pools[idx]; + std::unique_lock lock(elem.mutex); + if (not elem.alloc.is_init()) { + void* block = central_cache.allocate_node(central_cache.get_node_max_size()); + if (block == nullptr) { + logger.warning("Failed to allocate memory block from central cache"); + return nullptr; + } + elem.key = key; + elem.alloc = linear_allocator(block, central_cache.get_node_max_size()); + } + void* ptr = elem.alloc.allocate(size, alignment); + if (ptr == nullptr) { + logger.warning("No space left in memory block with key=%zd of circular stack pool", key); + } else { + elem.count++; + } + return ptr; + } + + void deallocate(size_t key, void* p) + { + size_t idx = key % NofStacks; + mem_block_elem_t& elem = pools[idx]; + std::lock_guard lock(elem.mutex); + elem.alloc.deallocate(p); + elem.count--; + if (elem.count == 0) { + // return back to central cache + void* ptr = elem.alloc.memblock_ptr(); + elem.clear(); + central_cache.deallocate_node(ptr); + } + } + + void allocate_batch() { central_cache.allocate_batch(); } + + size_t cache_size() const { return central_cache.cache_size(); } + +private: + srsran::circular_array pools; + srsran::background_mem_pool central_cache; + srslog::basic_logger& logger; +}; + +} // namespace srsran + +#endif // SRSRAN_CIRCULAR_MAP_STACK_POOL_H diff --git a/lib/include/srsran/adt/pool/fixed_size_pool.h b/lib/include/srsran/adt/pool/fixed_size_pool.h index 25164923a..74229222e 100644 --- a/lib/include/srsran/adt/pool/fixed_size_pool.h +++ b/lib/include/srsran/adt/pool/fixed_size_pool.h @@ -167,13 +167,13 @@ public: private: struct worker_ctxt { - std::thread::id id; - memblock_cache cache; + std::thread::id id; + free_memblock_list cache; worker_ctxt() : id(std::this_thread::get_id()) {} ~worker_ctxt() { - mutexed_memblock_cache& central_cache = pool_type::get_instance()->central_mem_cache; + concurrent_free_memblock_list& central_cache = pool_type::get_instance()->central_mem_cache; central_cache.steal_blocks(cache, cache.size()); } }; @@ -198,7 +198,7 @@ private: size_t local_growth_thres = 0; srslog::basic_logger* logger = nullptr; - mutexed_memblock_cache central_mem_cache; + concurrent_free_memblock_list central_mem_cache; std::mutex mutex; std::vector > allocated_blocks; }; diff --git a/lib/include/srsran/adt/pool/linear_allocator.h b/lib/include/srsran/adt/pool/linear_allocator.h new file mode 100644 index 000000000..bcfc05044 --- /dev/null +++ b/lib/include/srsran/adt/pool/linear_allocator.h @@ -0,0 +1,79 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSRAN_LINEAR_ALLOCATOR_H +#define SRSRAN_LINEAR_ALLOCATOR_H + +#include "pool_utils.h" +#include "srsran/common/srsran_assert.h" + +namespace srsran { + +class linear_allocator +{ +public: + linear_allocator() = default; + linear_allocator(void* start_, void* end_) : + start(static_cast(start_)), end(static_cast(end_)), cur(start) + {} + linear_allocator(void* start_, size_t sz) : start(static_cast(start_)), end(start + sz), cur(start) {} + linear_allocator(const linear_allocator& other) = delete; + linear_allocator(linear_allocator&& other) noexcept : start(other.start), end(other.end), cur(other.cur) + { + other.clear(); + } + linear_allocator& operator=(const linear_allocator& other) = delete; + linear_allocator& operator =(linear_allocator&& other) noexcept + { + start = other.start; + end = other.end; + cur = other.cur; + other.clear(); + return *this; + } + + void* allocate(size_t sz, size_t alignment) + { + void* alloc_start = align_to(cur, alignment); + uint8_t* new_cur = static_cast(alloc_start) + sz; + if (new_cur > end) { + // Cannot fit allocation in memory block + return nullptr; + } + cur = new_cur; + return alloc_start; + } + + void deallocate(void* p) { srsran_assert(p >= start and p < end, "pointer does not belong to pool"); } + + size_t nof_bytes_allocated() const { return cur - start; } + size_t nof_bytes_left() const { return end - cur; } + size_t size() const { return end - start; } + bool is_init() const { return start != end; } + void* memblock_ptr() { return static_cast(start); } + + void clear() + { + start = nullptr; + cur = nullptr; + end = nullptr; + } + +protected: + uint8_t* start = nullptr; + uint8_t* end = nullptr; + uint8_t* cur = nullptr; +}; + +} // namespace srsran + +#endif // SRSRAN_LINEAR_ALLOCATOR_H diff --git a/lib/include/srsran/adt/pool/mem_pool.h b/lib/include/srsran/adt/pool/mem_pool.h index 995e8c650..21c352ab0 100644 --- a/lib/include/srsran/adt/pool/mem_pool.h +++ b/lib/include/srsran/adt/pool/mem_pool.h @@ -44,7 +44,7 @@ template class big_obj_pool { // memory stack type derivation (thread safe or not) - using stack_type = typename std::conditional::type; + using stack_type = typename std::conditional::type; // memory stack to cache allocate memory chunks stack_type stack; @@ -56,8 +56,8 @@ public: void* allocate_node(size_t sz) { assert(sz == sizeof(T)); - static const size_t blocksize = std::max(sizeof(T), memblock_cache::min_memblock_size()); - uint8_t* block = stack.try_pop(); + static const size_t blocksize = std::max(sizeof(T), free_memblock_list::min_memblock_size()); + void* block = stack.try_pop(); if (block == nullptr) { block = new uint8_t[blocksize]; } @@ -67,16 +67,16 @@ public: void deallocate_node(void* p) { if (p != nullptr) { - stack.push(static_cast(p)); + stack.push(p); } } /// Pre-reserve N memory chunks for future object allocations void reserve(size_t N) { - static const size_t blocksize = std::max(sizeof(T), memblock_cache::min_memblock_size()); + static const size_t blocksize = std::max(sizeof(T), free_memblock_list::min_memblock_size()); for (size_t i = 0; i < N; ++i) { - stack.push(new uint8_t[blocksize]); + stack.push(static_cast(new uint8_t[blocksize])); } } @@ -84,10 +84,10 @@ public: void clear() { - uint8_t* block = stack.try_pop(); + uint8_t* block = static_cast(stack.try_pop()); while (block != nullptr) { delete[] block; - block = stack.try_pop(); + block = static_cast(stack.try_pop()); } } }; @@ -128,7 +128,7 @@ public: { assert(sz == sizeof(T)); std::lock_guard lock(mutex); - uint8_t* block = obj_cache.try_pop(); + void* block = obj_cache.try_pop(); if (block != nullptr) { // allocation successful @@ -179,7 +179,7 @@ private: // memory stack to cache allocate memory chunks std::mutex mutex; - memblock_cache obj_cache; + free_memblock_list obj_cache; std::vector > batches; }; diff --git a/lib/include/srsran/adt/pool/memblock_cache.h b/lib/include/srsran/adt/pool/memblock_cache.h index c50364680..9a7adbd63 100644 --- a/lib/include/srsran/adt/pool/memblock_cache.h +++ b/lib/include/srsran/adt/pool/memblock_cache.h @@ -22,90 +22,196 @@ #ifndef SRSRAN_MEMBLOCK_CACHE_H #define SRSRAN_MEMBLOCK_CACHE_H +#include "pool_utils.h" #include namespace srsran { -/// Stores provided mem blocks in a stack in an non-owning manner. Not thread-safe -class memblock_cache +namespace detail { + +class intrusive_memblock_list { - struct node { - node* prev; - explicit node(node* prev_) : prev(prev_) {} - }; - public: + struct node { + node* next; + explicit node(node* prev_) : next(prev_) {} + }; + node* head = nullptr; + size_t count = 0; + constexpr static size_t min_memblock_size() { return sizeof(node); } - - memblock_cache() = default; - - memblock_cache(const memblock_cache&) = delete; - - memblock_cache(memblock_cache&& other) noexcept : head(other.head) { other.head = nullptr; } - - memblock_cache& operator=(const memblock_cache&) = delete; - - memblock_cache& operator=(memblock_cache&& other) noexcept - { - head = other.head; - other.head = nullptr; - return *this; - } + constexpr static size_t min_memblock_align() { return alignof(node); } void push(void* block) noexcept { - // printf("head: %ld\n", (long)head); - node* next = ::new (block) node(head); - head = next; + srsran_assert(is_aligned(block, min_memblock_align()), "The provided memory block is not aligned"); + node* ptr = ::new (block) node(head); + head = ptr; count++; } - uint8_t* try_pop() noexcept + void* pop() noexcept { - if (is_empty()) { - return nullptr; - } + srsran_assert(not empty(), "pop() called on empty list"); node* last_head = head; - head = head->prev; + head = head->next; + last_head->~node(); count--; - return (uint8_t*)last_head; + return static_cast(last_head); } - bool is_empty() const { return head == nullptr; } + void* try_pop() noexcept { return empty() ? nullptr : pop(); } + + bool empty() const noexcept { return head == nullptr; } size_t size() const { return count; } - void clear() { head = nullptr; } + void clear() noexcept + { + head = nullptr; + count = 0; + } +}; + +} // namespace detail + +/** + * List of memory blocks. It overwrites bytes of blocks passed via push(void*). Thus, it is not safe to use in any + * pool of initialized objects + */ +class free_memblock_list : public detail::intrusive_memblock_list +{ +private: + using base_t = detail::intrusive_memblock_list; + using base_t::count; + using base_t::head; +}; + +/** + * List of memory blocks, each memory block containing a node. Memory Structure: + * memory block 1 memory block + * [ next | node ] [ next | node ] + * '--------------^ '-----------> nullptr + */ +class memblock_node_list : public detail::intrusive_memblock_list +{ + using base_t = detail::intrusive_memblock_list; + using base_t::count; + using base_t::head; + using base_t::try_pop; + +public: + const size_t memblock_alignment; + const size_t header_size; + const size_t payload_size; + const size_t memblock_size; + + explicit memblock_node_list(size_t node_size_, size_t node_alignment_ = detail::max_alignment) : + memblock_alignment(std::max(free_memblock_list::min_memblock_align(), node_alignment_)), + header_size(align_next(base_t::min_memblock_size(), memblock_alignment)), + payload_size(align_next(node_size_, memblock_alignment)), + memblock_size(header_size + payload_size) + { + srsran_assert(node_size_ > 0 and is_valid_alignment(node_alignment_), + "Invalid arguments node size=%zd,alignment=%zd", + node_size_, + node_alignment_); + } + + void* get_node_header(void* payload_addr) + { + srsran_assert(is_aligned(payload_addr, memblock_alignment), "Provided address is not valid"); + return static_cast(static_cast(payload_addr) - header_size); + } + + /// returns address of memblock payload (skips memblock header) + void* top() noexcept { return static_cast(reinterpret_cast(this->head) + header_size); } + + void steal_top(intrusive_memblock_list& other) noexcept + { + srsran_assert(not other.empty(), "Trying to steal from empty memblock list"); + node* other_head = other.head; + other.head = other.head->next; + other_head->next = head; + head = other_head; + other.count--; + count++; + } +}; + +/// Similar to node_memblock_list, but manages the allocation/deallocation of memory blocks +class memblock_stack +{ +public: + explicit memblock_stack(size_t node_size_, size_t node_alignment_ = detail::max_alignment) : + node_list(node_size_, node_alignment_) + {} + memblock_stack(const memblock_stack&) = delete; + memblock_stack(memblock_stack&& other) noexcept = delete; + memblock_stack& operator=(const memblock_stack&) = delete; + memblock_stack& operator=(memblock_stack&&) = delete; + ~memblock_stack() { clear(); } + + void clear() + { + while (not empty()) { + deallocate_block(); + } + } + + size_t get_memblock_size() const { return node_list.memblock_size; } + size_t get_node_max_size() const { return node_list.payload_size; } + + void* allocate_block() + { + node_list.push(new uint8_t[node_list.memblock_size]); + return current_node(); + } + + void deallocate_block() noexcept + { + uint8_t* block = static_cast(node_list.pop()); + delete[] block; + } + + bool empty() const noexcept { return node_list.empty(); } + size_t size() const noexcept { return node_list.size(); } + void* current_node() noexcept { return node_list.top(); } + void steal_top(memblock_stack& other) noexcept { return node_list.steal_top(other.node_list); } private: - node* head = nullptr; - size_t count = 0; + static size_t get_memblock_start_offset(size_t node_alignment) + { + return align_next(detail::intrusive_memblock_list::min_memblock_size(), node_alignment); + } + static size_t get_memblock_size(size_t node_size, size_t node_alignment) + { + return align_next(get_memblock_start_offset(node_alignment) + node_size, detail::max_alignment); + } + + memblock_node_list node_list; }; /// memblock stack that mutexes pushing/popping -class mutexed_memblock_cache +class concurrent_free_memblock_list { public: - mutexed_memblock_cache() = default; - - mutexed_memblock_cache(const mutexed_memblock_cache&) = delete; - - mutexed_memblock_cache(mutexed_memblock_cache&& other) noexcept + concurrent_free_memblock_list() = default; + concurrent_free_memblock_list(const concurrent_free_memblock_list&) = delete; + concurrent_free_memblock_list(concurrent_free_memblock_list&& other) noexcept { std::unique_lock lk1(other.mutex, std::defer_lock); std::unique_lock lk2(mutex, std::defer_lock); std::lock(lk1, lk2); - stack = std::move(other.stack); + stack = other.stack; } - - mutexed_memblock_cache& operator=(const mutexed_memblock_cache&) = delete; - - mutexed_memblock_cache& operator=(mutexed_memblock_cache&& other) noexcept + concurrent_free_memblock_list& operator=(const concurrent_free_memblock_list&) = delete; + concurrent_free_memblock_list& operator=(concurrent_free_memblock_list&& other) noexcept { std::unique_lock lk1(other.mutex, std::defer_lock); std::unique_lock lk2(mutex, std::defer_lock); std::lock(lk1, lk2); - stack = std::move(other.stack); + stack = other.stack; return *this; } @@ -115,18 +221,18 @@ public: stack.push(block); } - void steal_blocks(memblock_cache& other, size_t max_n) noexcept + void steal_blocks(free_memblock_list& other, size_t max_n) noexcept { std::lock_guard lock(mutex); - for (size_t i = 0; i < max_n and not other.is_empty(); ++i) { + for (size_t i = 0; i < max_n and not other.empty(); ++i) { stack.push(other.try_pop()); } } - uint8_t* try_pop() noexcept + void* try_pop() noexcept { std::lock_guard lock(mutex); - uint8_t* block = stack.try_pop(); + void* block = stack.try_pop(); return block; } @@ -144,7 +250,7 @@ public: return i; } - bool is_empty() const noexcept { return stack.is_empty(); } + bool empty() const noexcept { return stack.empty(); } size_t size() const noexcept { @@ -159,10 +265,39 @@ public: } private: - memblock_cache stack; + free_memblock_list stack; mutable std::mutex mutex; }; +/** + * Manages the allocation, caching and deallocation of memory blocks. + * On alloc, a memory block is stolen from cache. If cache is empty, malloc/new is called. + * Only the last allocated memory block can be deallocated. + */ +class cached_memblock_stack +{ +public: + explicit cached_memblock_stack(size_t block_size_) : used(block_size_), cache(block_size_) {} + + void* allocate_block() + { + if (cache.empty()) { + used.allocate_block(); + } else { + used.steal_top(cache); + } + return used.current_node(); + } + + void* current_node() noexcept { return used.current_node(); } + void deallocate_block() noexcept { cache.steal_top(used); } + size_t cache_size() const noexcept { return cache.size(); } + +private: + memblock_stack used; + memblock_stack cache; +}; + } // namespace srsran #endif // SRSRAN_MEMBLOCK_CACHE_H diff --git a/lib/include/srsran/adt/pool/obj_pool.h b/lib/include/srsran/adt/pool/obj_pool.h new file mode 100644 index 000000000..769479365 --- /dev/null +++ b/lib/include/srsran/adt/pool/obj_pool.h @@ -0,0 +1,219 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSRAN_OBJ_POOL_H +#define SRSRAN_OBJ_POOL_H + +#include "batch_mem_pool.h" +#include "memblock_cache.h" +#include "pool_interface.h" + +namespace srsran { + +template +class background_obj_pool; + +template +class growing_batch_obj_pool final : public obj_pool_itf +{ + static size_t memblock_size() + { + /// Node Structure [ node header | (pad to node alignment) | node size | (pad to node header alignment) ] + return align_next(align_next(free_memblock_list::min_memblock_size(), alignof(T)) + sizeof(T), + free_memblock_list::min_memblock_align()); + } + static size_t batch_size(size_t nof_objs_per_batch) + { + /// Batch Structure: [allocated stack header | (pad max alignment) | [memblock] x objs_per_batch ] + return align_next(detail::max_alignment + (memblock_size() * nof_objs_per_batch), detail::max_alignment); + } + +public: + using init_mem_oper_t = srsran::move_callback; + using recycle_oper_t = srsran::move_callback; + + explicit growing_batch_obj_pool(size_t objs_per_batch_, + int init_size = -1, + init_mem_oper_t init_oper_ = detail::inplace_default_ctor_operator{}, + recycle_oper_t recycle_oper_ = detail::noop_operator{}) : + objs_per_batch(objs_per_batch_), + init_oper(std::move(init_oper_)), + recycle_oper(std::move(recycle_oper_)), + allocated(batch_size(objs_per_batch_), detail::max_alignment), + cache(sizeof(T), alignof(T)) + { + size_t N = init_size < 0 ? objs_per_batch_ : init_size; + while (N > cache.size()) { + allocate_batch(); + } + } + ~growing_batch_obj_pool() { clear(); } + + void clear() + { + if (not allocated.empty()) { + srsran_assert(allocated.size() * objs_per_batch == cache_size(), + "Not all objects have been deallocated (%zd < %zd)", + cache_size(), + allocated.size() * objs_per_batch); + while (not cache.empty()) { + void* node_payload = cache.top(); + static_cast(node_payload)->~T(); + cache.pop(); + } + allocated.clear(); + } + } + + unique_pool_ptr make() final + { + return unique_pool_ptr(do_allocate(), [this](T* ptr) { + // dtor is not called, as object is going to be recycled + do_deallocate(ptr); + }); + } + + void allocate_batch() + { + uint8_t* batch_payload = static_cast(allocated.allocate_block()); + for (size_t i = 0; i < objs_per_batch; ++i) { + void* cache_node = batch_payload + (i * cache.memblock_size); + cache.push(cache_node); + init_oper(cache.top()); + } + } + + size_t cache_size() const { return cache.size(); } + +private: + friend class background_obj_pool; + + T* do_allocate() + { + if (cache.empty()) { + allocate_batch(); + } + void* top = cache.top(); + cache.pop(); + return static_cast(top); + } + + void do_deallocate(T* payload_ptr) + { + recycle_oper(*payload_ptr); + void* header_ptr = cache.get_node_header(static_cast(payload_ptr)); + cache.push(header_ptr); + } + + // args + const size_t objs_per_batch; + init_mem_oper_t init_oper; + recycle_oper_t recycle_oper; + + memblock_stack allocated; + memblock_node_list cache; +}; + +/** + * Thread-safe object pool specialized in allocating batches of objects in a preemptive way in a background thread + * to minimize latency. + * Note: The dispatched allocation jobs may outlive the pool. To handle this, the pool state is passed to jobs via a + * shared ptr. + */ +template +class background_obj_pool final : public obj_pool_itf +{ +public: + using init_mem_oper_t = typename growing_batch_obj_pool::init_mem_oper_t; + using recycle_oper_t = typename growing_batch_obj_pool::recycle_oper_t; + + explicit background_obj_pool(size_t nof_objs_per_batch, + size_t thres_, + int init_size = -1, + init_mem_oper_t init_oper_ = detail::inplace_default_ctor_operator{}, + recycle_oper_t recycle_oper_ = detail::noop_operator{}) : + thres(thres_), + state(std::make_shared(this)), + grow_pool(nof_objs_per_batch, init_size, std::move(init_oper_), std::move(recycle_oper_)) + { + srsran_assert(thres_ > 1, "The provided threshold=%zd is not valid", thres_); + } + ~background_obj_pool() + { + std::lock_guard lock(state->mutex); + state->pool = nullptr; + grow_pool.clear(); + } + + unique_pool_ptr make() final + { + return unique_pool_ptr(do_allocate(), [this](T* ptr) { + // dtor is not called, as object is going to be recycled + do_deallocate(ptr); + }); + } + + size_t cache_size() const { return grow_pool.cache_size(); } + +private: + T* do_allocate() + { + std::lock_guard lock(state->mutex); + T* obj = grow_pool.do_allocate(); + if (grow_pool.cache_size() < thres) { + allocate_batch_in_background_(); + } + return obj; + } + void do_deallocate(T* ptr) + { + std::lock_guard lock(state->mutex); + return grow_pool.do_deallocate(ptr); + } + + void allocate_batch_in_background_() + { + if (state->dispatched) { + // new batch allocation already ongoing + return; + } + state->dispatched = true; + std::shared_ptr state_sptr = state; + get_background_workers().push_task([state_sptr]() { + std::lock_guard lock(state_sptr->mutex); + if (state_sptr->pool != nullptr) { + auto* pool = state_sptr->pool; + do { + pool->grow_pool.allocate_batch(); + } while (pool->grow_pool.cache_size() < pool->thres); + } + state_sptr->dispatched = false; + }); + } + + size_t thres; + + // state of pool is detached because pool may be destroyed while batches are being allocated in the background + struct detached_pool_state { + std::mutex mutex; + background_obj_pool* pool; + bool dispatched = false; + explicit detached_pool_state(background_obj_pool* pool_) : pool(pool_) {} + }; + std::shared_ptr state; + + growing_batch_obj_pool grow_pool; +}; + +} // namespace srsran + +#endif // SRSRAN_OBJ_POOL_H diff --git a/lib/include/srsran/adt/pool/pool_interface.h b/lib/include/srsran/adt/pool/pool_interface.h new file mode 100644 index 000000000..2c6d0c750 --- /dev/null +++ b/lib/include/srsran/adt/pool/pool_interface.h @@ -0,0 +1,62 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSRAN_POOL_INTERFACE_H +#define SRSRAN_POOL_INTERFACE_H + +#include "srsran/adt/move_callback.h" + +namespace srsran { + +/// unique ptr with type-erased dtor, so that it can be used by any object or memory pool +constexpr size_t unique_pool_deleter_small_buffer = sizeof(void*) * 2u; +template +using unique_pool_ptr = std::unique_ptr >; + +/// Common object pool interface +template +class obj_pool_itf +{ +public: + using object_type = T; + + obj_pool_itf() = default; + // Object pool address should not change + obj_pool_itf(const obj_pool_itf&) = delete; + obj_pool_itf(obj_pool_itf&&) = delete; + obj_pool_itf& operator=(const obj_pool_itf&) = delete; + obj_pool_itf& operator=(obj_pool_itf&&) = delete; + + virtual ~obj_pool_itf() = default; + virtual unique_pool_ptr make() = 0; +}; + +/// Allocate object in memory pool +template +unique_pool_ptr make_pool_obj_with_heap_fallback(MemPool& mempool, Args&&... args) +{ + void* block = mempool.allocate(sizeof(T), alignof(T)); + if (block == nullptr) { + return unique_pool_ptr(new T(std::forward(args)...), std::default_delete()); + } + new (block) T(std::forward(args)...); + return unique_pool_ptr(block, [&mempool](T* ptr) { + if (ptr != nullptr) { + ptr->~T(); + mempool.deallocate(ptr); + } + }); +} + +} // namespace srsran + +#endif // SRSRAN_POOL_INTERFACE_H diff --git a/lib/include/srsran/adt/pool/pool_utils.h b/lib/include/srsran/adt/pool/pool_utils.h new file mode 100644 index 000000000..e6c4a9cd4 --- /dev/null +++ b/lib/include/srsran/adt/pool/pool_utils.h @@ -0,0 +1,64 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSRAN_POOL_UTILS_H +#define SRSRAN_POOL_UTILS_H + +#include "../move_callback.h" +#include + +namespace srsran { + +namespace detail { + +template +struct inplace_default_ctor_operator { + void operator()(void* ptr) { new (ptr) T(); } +}; + +struct noop_operator { + template + void operator()(T&& t) const + { + // do nothing + } +}; + +} // namespace detail + +/// check if alignment is power of 2 +constexpr bool is_valid_alignment(std::size_t alignment) +{ + return alignment && (alignment & (alignment - 1)) == 0u; +} + +inline bool is_aligned(void* ptr, std::size_t alignment) +{ + return (reinterpret_cast(ptr) & (alignment - 1)) == 0; +} + +constexpr std::uintptr_t align_next(std::uintptr_t pos, size_t alignment) +{ + return (pos + (alignment - 1)) & ~(alignment - 1); +} +inline void* align_to(void* pos, size_t alignment) +{ + return reinterpret_cast(align_next(reinterpret_cast(pos), alignment)); +} +inline void* offset_byte_ptr(void* pos, size_t offset) +{ + return static_cast(static_cast(pos) + offset); +} + +} // namespace srsran + +#endif // SRSRAN_POOL_UTILS_H diff --git a/lib/include/srsran/common/mac_pcap_net.h b/lib/include/srsran/common/mac_pcap_net.h index 815148bf7..e9b456c1c 100644 --- a/lib/include/srsran/common/mac_pcap_net.h +++ b/lib/include/srsran/common/mac_pcap_net.h @@ -45,8 +45,8 @@ private: void write_mac_lte_pdu_to_net(srsran::mac_pcap_base::pcap_pdu_t& pdu); void write_mac_nr_pdu_to_net(srsran::mac_pcap_base::pcap_pdu_t& pdu); - srsran::socket_handler_t socket; - struct sockaddr_in client_addr; + srsran::unique_socket socket; + struct sockaddr_in client_addr; }; } // namespace srsran diff --git a/lib/include/srsran/common/network_utils.h b/lib/include/srsran/common/network_utils.h index 52964a6c9..cfb6aacd2 100644 --- a/lib/include/srsran/common/network_utils.h +++ b/lib/include/srsran/common/network_utils.h @@ -23,20 +23,18 @@ #define SRSRAN_RX_SOCKET_HANDLER_H #include "srsran/common/buffer_pool.h" +#include "srsran/common/multiqueue.h" #include "srsran/common/threads.h" #include -#include #include #include #include #include #include #include -#include #include #include -#include // for the pipe namespace srsran { @@ -67,20 +65,19 @@ bool connect_to(int fd, const char* dest_addr_str, int dest_port, sockaddr_in* d /** * Description: Net socket class with convenience methods for connecting, binding, and opening socket */ -class socket_handler_t +class unique_socket { public: - socket_handler_t() = default; - socket_handler_t(const socket_handler_t&) = delete; - socket_handler_t(socket_handler_t&& other) noexcept; - ~socket_handler_t(); - socket_handler_t& operator=(const socket_handler_t&) = delete; - socket_handler_t& operator =(socket_handler_t&&) noexcept; + unique_socket() = default; + unique_socket(const unique_socket&) = delete; + unique_socket(unique_socket&& other) noexcept; + ~unique_socket(); + unique_socket& operator=(const unique_socket&) = delete; + unique_socket& operator =(unique_socket&&) noexcept; void close(); - void reset(); - bool is_init() const { return sockfd >= 0; } + bool is_open() const { return sockfd >= 0; } int fd() const { return sockfd; } const sockaddr_in& get_addr_in() const { return addr; } std::string get_ip() const { return net_utils::get_ip(addr); } @@ -89,7 +86,7 @@ public: bool bind_addr(const char* bind_addr_str, int port); bool connect_to(const char* dest_addr_str, int dest_port, sockaddr_in* dest_sockaddr = nullptr); bool open_socket(net_utils::addr_family ip, net_utils::socket_type socket_type, net_utils::protocol_type protocol); - int get_socket() { return sockfd; }; + int get_socket() const { return sockfd; }; protected: sockaddr_in addr = {}; @@ -98,14 +95,8 @@ protected: namespace net_utils { -bool sctp_init_client(socket_handler_t* socket, net_utils::socket_type socktype, const char* bind_addr_str); -bool sctp_init_server(socket_handler_t* socket, net_utils::socket_type socktype, const char* bind_addr_str, int port); - -// TODO: for TCP and UDP -bool tcp_make_server(socket_handler_t* socket, const char* bind_addr_str, int port, int nof_connections = 1); -int tcp_accept(socket_handler_t* socket, sockaddr_in* destaddr); -int tcp_read(int remotefd, void* buf, size_t nbytes); -int tcp_send(int remotefd, const void* buf, size_t nbytes); +bool sctp_init_client(unique_socket* socket, net_utils::socket_type socktype, const char* bind_addr_str); +bool sctp_init_server(unique_socket* socket, net_utils::socket_type socktype, const char* bind_addr_str, int port); } // namespace net_utils @@ -113,63 +104,97 @@ int tcp_send(int remotefd, const void* buf, size_t nbytes); * Rx multisocket handler ***************************/ +class socket_manager_itf +{ +public: + /// Callback called when socket fd (passed as argument) has data + using recv_callback_t = srsran::move_callback; + + explicit socket_manager_itf(srslog::basic_logger& logger_) : logger(logger_) {} + socket_manager_itf(socket_manager_itf&&) = delete; + socket_manager_itf(const socket_manager_itf&) = delete; + socket_manager_itf& operator=(const socket_manager_itf&) = delete; + socket_manager_itf& operator=(socket_manager_itf&&) = delete; + virtual ~socket_manager_itf() = default; + + /// Register (fd, callback). callback is called within socket thread when fd has data. + virtual bool add_socket_handler(int fd, recv_callback_t handler) = 0; + + /// remove registered socket fd + virtual bool remove_socket(int fd) = 0; + +protected: + srslog::basic_logger& logger; +}; + /** * Description - Instantiates a thread that will block waiting for IO from multiple sockets, via a select * The user can register their own (socket fd, data handler) in this class via the * add_socket_handler(fd, task) API or its other variants */ -class rx_multisocket_handler final : public thread +class socket_manager final : public thread, public socket_manager_itf { -public: - // polymorphic callback to handle the socket recv - class recv_task - { - public: - virtual ~recv_task() = default; - virtual bool operator()(int fd) = 0; // returns false, if socket needs to be removed - }; - using task_callback_t = std::unique_ptr; - using recvfrom_callback_t = std::function; - using sctp_recv_callback_t = - std::function; + using recv_callback_t = socket_manager_itf::recv_callback_t; - rx_multisocket_handler(std::string name_, srslog::basic_logger& logger, int thread_prio = 65); - rx_multisocket_handler(rx_multisocket_handler&&) = delete; - rx_multisocket_handler(const rx_multisocket_handler&) = delete; - rx_multisocket_handler& operator=(const rx_multisocket_handler&) = delete; - rx_multisocket_handler& operator=(rx_multisocket_handler&&) = delete; - ~rx_multisocket_handler(); +public: + socket_manager(); + ~socket_manager() final; void stop(); - bool remove_socket(int fd); - bool add_socket_handler(int fd, task_callback_t handler); - // convenience methods for recv using buffer pool - bool add_socket_pdu_handler(int fd, recvfrom_callback_t pdu_task); - bool add_socket_sctp_pdu_handler(int fd, sctp_recv_callback_t task); + bool remove_socket_nonblocking(int fd, bool signal_completion = false); + bool remove_socket(int fd) final; + bool add_socket_handler(int fd, recv_callback_t handler) final; void run_thread() override; private: + const int thread_prio = 65; + // used to unlock select struct ctrl_cmd_t { enum class cmd_id_t { EXIT, NEW_FD, RM_FD }; - cmd_id_t cmd = cmd_id_t::EXIT; - int new_fd = -1; + cmd_id_t cmd = cmd_id_t::EXIT; + int new_fd = -1; + bool signal_rm_complete = false; }; - std::map::iterator - remove_socket_unprotected(int fd, fd_set* total_fd_set, int* max_fd); - - // args - std::string name; - srslog::basic_logger& logger; + std::map::iterator remove_socket_unprotected(int fd, fd_set* total_fd_set, int* max_fd); // state std::mutex socket_mutex; - std::map active_sockets; + std::map active_sockets; bool running = false; - int pipefd[2] = {}; + int pipefd[2] = {-1, -1}; + std::vector rem_fd_tmp_list; + std::condition_variable rem_cvar; }; +/// Function signature for SDU byte buffers received from SCTP socket +using sctp_recv_callback_t = + srsran::move_callback; + +/// Function signature for SDU byte buffers received from any sockaddr_in-based socket +using recvfrom_callback_t = srsran::move_callback; + +/** + * Helper function that creates a callback that is called when a SCTP socket has data, and does the following tasks: + * 1. receive SDU byte buffer from SCTP socket and associated metadata - sockaddr_in, sctp_sndrcvinfo, flags + * 2. dispatches the received SDU+metadata+rx_callback into the "queue" + * 3. potentially on a separate thread, the SDU+metadata+callback are popped from the queue, and callback is called with + * the SDU+metadata as arguments + * @param logger logger used by recv_callback_t to log any failure/reception of an SDU + * @param queue queue to which the SDU+metadata+callback are going to be dispatched + * @param rx_callback callback that is run when a new SDU arrives, from the thread that calls queue.pop() + * @return callback void(int) that can be registered in socket_manager + */ +socket_manager_itf::recv_callback_t +make_sctp_sdu_handler(srslog::basic_logger& logger, srsran::task_queue_handle& queue, sctp_recv_callback_t rx_callback); + +/** + * Similar to make_sctp_sdu_handler, but for any sockaddr_in-based socket type + */ +socket_manager_itf::recv_callback_t +make_sdu_handler(srslog::basic_logger& logger, srsran::task_queue_handle& queue, recvfrom_callback_t rx_callback); + } // namespace srsran #endif // SRSRAN_RX_SOCKET_HANDLER_H diff --git a/lib/include/srsran/common/task_scheduler.h b/lib/include/srsran/common/task_scheduler.h index d07e13562..2c1079fc4 100644 --- a/lib/include/srsran/common/task_scheduler.h +++ b/lib/include/srsran/common/task_scheduler.h @@ -132,7 +132,8 @@ public: { sched->defer_callback(duration_ms, std::forward(func)); } - void defer_task(srsran::move_task_t func) { sched->defer_task(std::move(func)); } + void defer_task(srsran::move_task_t func) { sched->defer_task(std::move(func)); } + srsran::task_queue_handle make_task_queue() { return sched->make_task_queue(); } private: task_scheduler* sched; diff --git a/lib/include/srsran/common/thread_pool.h b/lib/include/srsran/common/thread_pool.h index fde31cca7..a34696a46 100644 --- a/lib/include/srsran/common/thread_pool.h +++ b/lib/include/srsran/common/thread_pool.h @@ -67,6 +67,7 @@ public: void run_thread(); void wait_to_start(); void finished(); + bool is_stopped() const; }; thread_pool(uint32_t nof_workers); @@ -97,7 +98,7 @@ private: class task_thread_pool { - using task_t = srsran::move_callback; + using task_t = srsran::move_callback; public: task_thread_pool(uint32_t nof_workers = 1, bool start_deferred = false, int32_t prio_ = -1, uint32_t mask_ = 255); diff --git a/lib/include/srsran/interfaces/enb_interfaces.h b/lib/include/srsran/interfaces/enb_interfaces.h index 2e5e5cbe3..6cc3930ba 100644 --- a/lib/include/srsran/interfaces/enb_interfaces.h +++ b/lib/include/srsran/interfaces/enb_interfaces.h @@ -29,19 +29,6 @@ namespace srsenb { class stack_interface_phy_lte; -class stack_interface_s1ap_lte -{ -public: - virtual void add_mme_socket(int fd) = 0; - virtual void remove_mme_socket(int fd) = 0; -}; - -class stack_interface_gtpu_lte -{ -public: - virtual void add_gtpu_s1u_socket_handler(int fd) = 0; - virtual void add_gtpu_m1u_socket_handler(int fd) = 0; -}; } // namespace srsenb diff --git a/lib/include/srsran/interfaces/enb_mac_interfaces.h b/lib/include/srsran/interfaces/enb_mac_interfaces.h index 9e97c1852..dd8545302 100644 --- a/lib/include/srsran/interfaces/enb_mac_interfaces.h +++ b/lib/include/srsran/interfaces/enb_mac_interfaces.h @@ -62,7 +62,7 @@ public: /** * List of DL scheduling results, one entry per cell/carrier */ - typedef std::vector dl_sched_list_t; + using dl_sched_list_t = srsran::bounded_vector; typedef struct { uint16_t rnti; @@ -94,7 +94,7 @@ public: /** * List of UL scheduling results, one entry per cell/carrier */ - typedef std::vector ul_sched_list_t; + using ul_sched_list_t = srsran::bounded_vector; virtual int sr_detected(uint32_t tti, uint16_t rnti) = 0; virtual void rach_detected(uint32_t tti, uint32_t primary_cc_idx, uint32_t preamble_idx, uint32_t time_adv) = 0; diff --git a/lib/include/srsran/interfaces/enb_metrics_interface.h b/lib/include/srsran/interfaces/enb_metrics_interface.h index 59f0dc992..94f67c14c 100644 --- a/lib/include/srsran/interfaces/enb_metrics_interface.h +++ b/lib/include/srsran/interfaces/enb_metrics_interface.h @@ -24,10 +24,10 @@ #include +#include "srsenb/hdr/common/common_enb.h" #include "srsenb/hdr/phy/phy_metrics.h" #include "srsenb/hdr/stack/mac/mac_metrics.h" #include "srsenb/hdr/stack/rrc/rrc_metrics.h" -#include "srsenb/hdr/stack/upper/common_enb.h" #include "srsenb/hdr/stack/upper/s1ap_metrics.h" #include "srsran/common/metrics_hub.h" #include "srsran/radio/radio_metrics.h" diff --git a/lib/include/srsran/interfaces/rrc_interface_types.h b/lib/include/srsran/interfaces/rrc_interface_types.h index 8d9cb055b..84a38f2ab 100644 --- a/lib/include/srsran/interfaces/rrc_interface_types.h +++ b/lib/include/srsran/interfaces/rrc_interface_types.h @@ -155,6 +155,27 @@ inline std::string to_string(const establishment_cause_t& cause) return enum_to_text(options, (uint32_t)establishment_cause_t::nulltype, (uint32_t)cause); } +enum class scg_failure_cause_t { + t310_expiry, + random_access_problem, + rlc_max_num_retx, + synch_recfg_fail_scg, + scg_recfg_fail, + srb3_integrity_fail, + nulltype +}; + +inline std::string to_string(const scg_failure_cause_t& cause) +{ + constexpr static const char* options[] = {"t310_expiry", + "random_access_problem", + "rlc_max_num_retx", + "synch_recfg_fail_scg", + "scg_recfg_fail", + "srb3_integrity_fail", + "nulltype"}; + return enum_to_text(options, (uint32_t)scg_failure_cause_t::nulltype, (uint32_t)cause); +} /*************************** * PHY Config **************************/ diff --git a/lib/include/srsran/interfaces/rrc_nr_interface_types.h b/lib/include/srsran/interfaces/rrc_nr_interface_types.h index fbccb1b8b..ac5f2f651 100644 --- a/lib/include/srsran/interfaces/rrc_nr_interface_types.h +++ b/lib/include/srsran/interfaces/rrc_nr_interface_types.h @@ -38,7 +38,7 @@ struct phy_cfg_nr_t { srsran_sch_hl_cfg_nr_t pusch = {}; srsran_pucch_nr_hl_cfg_t pucch = {}; srsran_prach_cfg_t prach = {}; - srsran_ue_dl_nr_pdcch_cfg_t pdcch = {}; + srsran_pdcch_cfg_nr_t pdcch = {}; srsran_ue_dl_nr_harq_ack_cfg_t harq_ack = {}; srsran_csi_hl_cfg_t csi = {}; @@ -61,7 +61,7 @@ struct phy_cfg_nr_t { // physicalCellGroupConfig // pdsch-HARQ-ACK-Codebook: dynamic (1) - harq_ack.pdsch_harq_ack_codebook = srsran_pdsch_harq_ack_codebook_dynamic; + harq_ack.harq_ack_codebook = srsran_pdsch_harq_ack_codebook_dynamic; // commonControlResourceSet // controlResourceSetId: 1 @@ -79,39 +79,6 @@ struct phy_cfg_nr_t { } pdcch.coreset_present[1] = true; - // SearchSpace - // searchSpaceId: 1 - // controlResourceSetId: 1 - // monitoringSlotPeriodicityAndOffset: sl1 (0) - // sl1: NULL - // monitoringSymbolsWithinSlot: 8000 [bit length 14, 2 LSB pad bits, 1000 0000 0000 00.. decimal value 8192] - // nrofCandidates - // aggregationLevel1: n0 (0) - // aggregationLevel2: n0 (0) - // aggregationLevel4: n1 (1) - // aggregationLevel8: n0 (0) - // aggregationLevel16: n0 (0) - // searchSpaceType: common (0) - // common - // dci-Format0-0-AndFormat1-0 - srsran_search_space_t search_space1 = {}; - search_space1.id = 1; - search_space1.coreset_id = 1; - search_space1.nof_candidates[0] = 1; - search_space1.nof_candidates[1] = 1; - search_space1.nof_candidates[2] = 1; - search_space1.nof_candidates[3] = 0; - search_space1.nof_candidates[4] = 0; - search_space1.type = srsran_search_space_type_common_3; - pdcch.search_space[1] = search_space1; - pdcch.search_space_present[1] = true; - - // ra-SearchSpace: 1 - pdcch.ra_rnti = 0x16; //< Supposed to be deduced from PRACH configuration - pdcch.ra_search_space = search_space1; - pdcch.ra_search_space.type = srsran_search_space_type_common_1; - pdcch.ra_search_space_present = true; - // spCellConfigDedicated // initialDownlinkBWP // pdcch-Config: setup (1) @@ -135,32 +102,6 @@ struct phy_cfg_nr_t { } pdcch.coreset_present[2] = true; - // searchSpacesToAddModList: 1 item - // Item 0 - // SearchSpace - // searchSpaceId: 2 - // controlResourceSetId: 2 - // monitoringSlotPeriodicityAndOffset: sl1 (0) - // sl1: NULL - // monitoringSymbolsWithinSlot: 8000 [bit length 14, 2 LSB pad bits, 1000 0000 0000 - // 00.. decimal value 8192] nrofCandidates - // aggregationLevel1: n0 (0) - // aggregationLevel2: n2 (2) - // aggregationLevel4: n1 (1) - // aggregationLevel8: n0 (0) - // aggregationLevel16: n0 (0) - // searchSpaceType: ue-Specific (1) - // ue-Specific - // dci-Formats: formats0-0-And-1-0 (0) - pdcch.search_space[2].id = 2; - pdcch.search_space[2].coreset_id = 2; - pdcch.search_space[2].nof_candidates[0] = 0; - pdcch.search_space[2].nof_candidates[1] = 2; - pdcch.search_space[2].nof_candidates[2] = 1; - pdcch.search_space[2].nof_candidates[3] = 0; - pdcch.search_space[2].nof_candidates[4] = 0; - pdcch.search_space[2].type = srsran_search_space_type_ue; - pdcch.search_space_present[2] = true; // pdsch-Config: setup (1) // setup // dmrs-DownlinkForPDSCH-MappingTypeA: setup (1) @@ -965,6 +906,71 @@ struct phy_cfg_nr_t { csi.reports[0].freq_cfg = SRSRAN_CSI_REPORT_FREQ_WIDEBAND; csi.reports[0].cqi_table = SRSRAN_CSI_CQI_TABLE_2; } + + /** + * @param carrier + */ + srsran_dci_cfg_nr_t get_dci_cfg(const srsran_carrier_nr_t& carrier) const + { + srsran_dci_cfg_nr_t dci_cfg = {}; + + // Set bandwidths + dci_cfg.coreset0_bw = pdcch.coreset_present[0] ? srsran_coreset_get_bw(&pdcch.coreset[0]) : 0; + dci_cfg.bwp_dl_initial_bw = carrier.nof_prb; + dci_cfg.bwp_dl_active_bw = carrier.nof_prb; + dci_cfg.bwp_ul_initial_bw = carrier.nof_prb; + dci_cfg.bwp_ul_active_bw = carrier.nof_prb; + + // Iterate over all SS to select monitoring options + for (uint32_t i = 0; i < SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE; i++) { + // Skip not configured SS + if (not pdcch.search_space_present[i]) { + continue; + } + + // Iterate all configured formats + for (uint32_t j = 0; j < pdcch.search_space[i].nof_formats; j++) { + if (pdcch.search_space[i].type == srsran_search_space_type_common_3 && + pdcch.search_space[i].formats[j] == srsran_dci_format_nr_0_0) { + dci_cfg.monitor_common_0_0 = true; + } else if (pdcch.search_space[i].type == srsran_search_space_type_ue && + pdcch.search_space[i].formats[j] == srsran_dci_format_nr_0_0) { + dci_cfg.monitor_0_0_and_1_0 = true; + } else if (pdcch.search_space[i].type == srsran_search_space_type_ue && + pdcch.search_space[i].formats[j] == srsran_dci_format_nr_0_1) { + dci_cfg.monitor_0_1_and_1_1 = true; + } + } + } + + // Set PUSCH parameters + dci_cfg.enable_sul = false; + dci_cfg.enable_hopping = false; + + // Set Format 0_1 and 1_1 parameters + dci_cfg.carrier_indicator_size = 0; + dci_cfg.harq_ack_codebok = harq_ack.harq_ack_codebook; + + // Format 0_1 specific configuration (for PUSCH only) + dci_cfg.nof_ul_bwp = 1; + dci_cfg.nof_ul_time_res = (pusch.nof_dedicated_time_ra > 0) + ? pusch.nof_dedicated_time_ra + : (pusch.nof_common_time_ra > 0) ? pusch.nof_common_time_ra : SRSRAN_MAX_NOF_TIME_RA; + dci_cfg.nof_srs = 1; + dci_cfg.nof_ul_layers = 1; + dci_cfg.nof_rb_groups = 1; + dci_cfg.pusch_alloc_type = pusch.alloc; + + // Format 1_1 specific configuration (for PDSCH only) + dci_cfg.nof_dl_bwp = 1; + dci_cfg.pdsch_alloc_type = pdsch.alloc; + dci_cfg.nof_dl_time_res = (pdsch.nof_dedicated_time_ra > 0) + ? pdsch.nof_dedicated_time_ra + : (pdsch.nof_common_time_ra > 0) ? pdsch.nof_common_time_ra : SRSRAN_MAX_NOF_TIME_RA; + dci_cfg.nof_aperiodic_zp = 0; + + return dci_cfg; + }; }; } // namespace srsran diff --git a/lib/include/srsran/interfaces/ue_gw_interfaces.h b/lib/include/srsran/interfaces/ue_gw_interfaces.h index eb5f35ea4..60e25a8af 100644 --- a/lib/include/srsran/interfaces/ue_gw_interfaces.h +++ b/lib/include/srsran/interfaces/ue_gw_interfaces.h @@ -60,6 +60,7 @@ class gw_interface_rrc public: virtual void add_mch_port(uint32_t lcid, uint32_t port) = 0; virtual int update_lcid(uint32_t eps_bearer_id, uint32_t new_lcid) = 0; + virtual bool is_running() = 0; }; class gw_interface_pdcp diff --git a/lib/include/srsran/interfaces/ue_nr_interfaces.h b/lib/include/srsran/interfaces/ue_nr_interfaces.h index e19cade54..3d37c3e36 100644 --- a/lib/include/srsran/interfaces/ue_nr_interfaces.h +++ b/lib/include/srsran/interfaces/ue_nr_interfaces.h @@ -53,7 +53,7 @@ public: uint32_t pid; uint16_t rnti; uint32_t tti; - uint32_t tbs; + uint32_t tbs; // transport block size in Bytes } mac_nr_grant_ul_t; /// For UL, payload buffer remains in MAC @@ -102,6 +102,14 @@ public: * @param ul_carrier_id The UL carrier used for Msg1 transmission (0 for NUL carrier, and 1 for SUL carrier). */ virtual void prach_sent(uint32_t tti, uint32_t s_id, uint32_t t_id, uint32_t f_id, uint32_t ul_carrier_id) = 0; + + /** + * @brief Indicate a valid SR transmission occasion on the valid PUCCH resource for SR configured; and the SR + * transmission occasion does not overlap with a measurement gap; and the PUCCH resource for the SR transmission + * occasion does not overlap with a UL-SCH resource; + * @param tti The TTI from the PHY viewpoint at which the SR occasion was sent over-the-air (not to the radio). + */ + virtual bool sr_opportunity(uint32_t tti, uint32_t sr_id, bool meas_gap, bool ul_sch_tx) = 0; }; class mac_interface_rrc_nr @@ -124,13 +132,13 @@ public: }; struct phy_args_nr_t { - uint32_t nof_carriers; - uint32_t nof_prb; - uint32_t nof_phy_threads; - uint32_t worker_cpu_mask; - srsran::phy_log_args_t log; - srsran_ue_dl_nr_args_t dl; - srsran_ue_ul_nr_args_t ul; + uint32_t nof_carriers = 1; + uint32_t nof_prb = 52; + uint32_t nof_phy_threads = 3; + uint32_t worker_cpu_mask = 0; + srsran::phy_log_args_t log = {}; + srsran_ue_dl_nr_args_t dl = {}; + srsran_ue_ul_nr_args_t ul = {}; std::set fixed_sr = {1}; uint32_t fix_wideband_cqi = 15; // Set to a non-zero value for fixing the wide-band CQI report @@ -172,8 +180,17 @@ public: const float preamble_received_target_power, const float ta_base_sec = 0.0f) = 0; - /// Instruct PHY to transmit SR for a given identifier - virtual void sr_send(uint32_t sr_id) = 0; + /** + * @brief Query PHY if there is a valid PUCCH SR resource configured for a given SR identifier + * @param sr_id SR identifier + * @return True if there is a valid PUCCH resource configured + */ + virtual bool has_valid_sr_resource(uint32_t sr_id) = 0; + + /** + * @brief Clear any configured downlink assignments and uplink grants + */ + virtual void clear_pending_grants() = 0; }; class phy_interface_rrc_nr diff --git a/lib/include/srsran/interfaces/ue_pdcp_interfaces.h b/lib/include/srsran/interfaces/ue_pdcp_interfaces.h index 0231f81fd..93f019f55 100644 --- a/lib/include/srsran/interfaces/ue_pdcp_interfaces.h +++ b/lib/include/srsran/interfaces/ue_pdcp_interfaces.h @@ -35,6 +35,7 @@ public: virtual void reset() = 0; virtual void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu, int sn = -1) = 0; virtual void add_bearer(uint32_t lcid, srsran::pdcp_config_t cnfg) = 0; + virtual void del_bearer(uint32_t lcid) = 0; virtual void change_lcid(uint32_t old_lcid, uint32_t new_lcid) = 0; virtual void config_security(uint32_t lcid, srsran::as_security_config_t sec_cfg) = 0; virtual void config_security_all(srsran::as_security_config_t sec_cfg) = 0; diff --git a/lib/include/srsran/interfaces/ue_rrc_interfaces.h b/lib/include/srsran/interfaces/ue_rrc_interfaces.h index 7df5254c2..2df8579db 100644 --- a/lib/include/srsran/interfaces/ue_rrc_interfaces.h +++ b/lib/include/srsran/interfaces/ue_rrc_interfaces.h @@ -41,8 +41,10 @@ public: class rrc_eutra_interface_rrc_nr { public: - virtual void new_cell_meas_nr(const std::vector& meas) = 0; - virtual void nr_rrc_con_reconfig_complete(bool status) = 0; + virtual void new_cell_meas_nr(const std::vector& meas) = 0; + virtual void nr_rrc_con_reconfig_complete(bool status) = 0; + virtual void nr_notify_reconfiguration_failure() = 0; + virtual void nr_scg_failure_information(const srsran::scg_failure_cause_t cause) = 0; }; class rrc_interface_phy_lte diff --git a/lib/include/srsran/mac/mac_sch_pdu_nr.h b/lib/include/srsran/mac/mac_sch_pdu_nr.h index cdd893b3e..b53293de3 100644 --- a/lib/include/srsran/mac/mac_sch_pdu_nr.h +++ b/lib/include/srsran/mac/mac_sch_pdu_nr.h @@ -60,7 +60,10 @@ public: PADDING = 0b111111, } nr_lcid_sch_t; - mac_sch_subpdu_nr(mac_sch_pdu_nr* parent_); + // SDUs up to 256 B can use the short 8-bit L field + static const int32_t MAC_SUBHEADER_LEN_THRESHOLD = 256; + + mac_sch_subpdu_nr(mac_sch_pdu_nr* parent_) : parent(parent_), logger(&srslog::fetch_basic_logger("MAC")){}; nr_lcid_sch_t get_type(); bool is_sdu(); @@ -88,12 +91,20 @@ public: static const uint8_t max_num_lcg_lbsr = 8; std::array get_lbsr(); + // TA + struct ta_t { + uint8_t tag_id; + uint8_t ta_command; + }; + ta_t get_ta(); + // setters void set_sdu(const uint32_t lcid_, const uint8_t* payload_, const uint32_t len_); void set_padding(const uint32_t len_); void set_c_rnti(const uint16_t crnti_); void set_se_phr(const uint8_t phr_, const uint8_t pcmax_); void set_sbsr(const lcg_bsr_t bsr_); + void set_lbsr(const std::array bsr_); uint32_t write_subpdu(const uint8_t* start_); @@ -101,7 +112,8 @@ public: static uint32_t sizeof_ce(uint32_t lcid, bool is_ul); private: - // protected: + srslog::basic_logger* logger; + uint32_t lcid = 0; int header_length = 0; int sdu_length = 0; @@ -129,11 +141,12 @@ public: void init_rx(bool ulsch_ = false); // Add SDU or CEs to PDU - // All functions will return SRSRAN_SUCCESS on success, and SRSLE_ERROR otherwise + // All functions will return SRSRAN_SUCCESS on success, and SRSRAN_ERROR otherwise uint32_t add_sdu(const uint32_t lcid_, const uint8_t* payload_, const uint32_t len_); uint32_t add_crnti_ce(const uint16_t crnti_); uint32_t add_se_phr_ce(const uint8_t phr_, const uint8_t pcmax_); uint32_t add_sbsr_ce(const mac_sch_subpdu_nr::lcg_bsr_t bsr_); + uint32_t add_lbsr_ce(const std::array bsr_); uint32_t get_remaing_len(); diff --git a/lib/include/srsran/mac/pdu_queue.h b/lib/include/srsran/mac/pdu_queue.h index a7d7162ca..2b77ca03b 100644 --- a/lib/include/srsran/mac/pdu_queue.h +++ b/lib/include/srsran/mac/pdu_queue.h @@ -67,8 +67,8 @@ private: } pdu_t; - static_blocking_queue pdu_q; buffer_pool pool; + static_blocking_queue pdu_q; process_callback* callback; srslog::basic_logger& logger; diff --git a/lib/include/srsran/phy/common/phy_common_nr.h b/lib/include/srsran/phy/common/phy_common_nr.h index 0ac1f1481..ea0145af2 100644 --- a/lib/include/srsran/phy/common/phy_common_nr.h +++ b/lib/include/srsran/phy/common/phy_common_nr.h @@ -122,7 +122,7 @@ extern "C" { * @brief Maximum number of PDSCH time domain resource allocations. This is defined by TS 38.331 v15.10.0 * as maxNrofDL-Allocations */ -#define SRSRAN_MAX_NOF_DL_ALLOCATION 16 +#define SRSRAN_MAX_NOF_TIME_RA 16 /** * @brief Maximum dl-DataToUL-ACK value. This is defined by TS 38.331 v15.10.1 in PUCCH-Config @@ -152,7 +152,7 @@ typedef enum SRSRAN_API { typedef enum SRSRAN_API { srsran_sch_mapping_type_A = 0, srsran_sch_mapping_type_B } srsran_sch_mapping_type_t; /** - * @brief Search spaces + * @brief Search Space (SS) type * @remark Described in TS 38.213 V15.10.0 Section 10.1 UE procedure for determining physical downlink control channel * assignment */ @@ -218,7 +218,8 @@ typedef enum SRSRAN_API { srsran_dci_format_nr_2_2, ///< @brief Transmission of TPC commands for PUCCH and PUSCH srsran_dci_format_nr_2_3, ///< @brief Transmission of a group of TPC commands for SRS transmissions by one or more UEs srsran_dci_format_nr_rar, ///< @brief Scheduling a transmission of PUSCH from RAR - srsran_dci_format_nr_cg ///< @brief Scheduling of PUSCH using a configured grant + srsran_dci_format_nr_cg, ///< @brief Scheduling of PUSCH using a configured grant + SRSRAN_DCI_FORMAT_NR_COUNT ///< @brief Number of DCI formats } srsran_dci_format_nr_t; /** @@ -241,7 +242,18 @@ typedef enum SRSRAN_API { srsran_pdsch_harq_ack_codebook_none = 0, srsran_pdsch_harq_ack_codebook_semi_static, srsran_pdsch_harq_ack_codebook_dynamic, -} srsran_pdsch_harq_ack_codebook_t; +} srsran_harq_ack_codebook_t; + +/** + * @brief PDSCH/PUSCH Resource allocation configuration + * @remark Described in TS 38.331 V15.10.0 PhysicalCellGroupConfig + */ +typedef enum SRSRAN_API { + srsran_resource_alloc_type0 = 0, + srsran_resource_alloc_type1, + srsran_resource_alloc_dynamic, +} srsran_resource_alloc_t; + /** * @brief NR carrier parameters. It is a combination of fixed cell and bandwidth-part (BWP) */ @@ -312,8 +324,10 @@ typedef struct SRSRAN_API { typedef struct SRSRAN_API { uint32_t id; uint32_t coreset_id; - uint32_t duration; // in slots - srsran_search_space_type_t type; + uint32_t duration; ///< SS duration length in slots + srsran_search_space_type_t type; ///< Sets the SS type, common (multiple types) or UE specific + srsran_dci_format_nr_t formats[SRSRAN_DCI_FORMAT_NR_COUNT]; ///< Specifies the DCI formats that shall be searched + uint32_t nof_formats; uint32_t nof_candidates[SRSRAN_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR]; } srsran_search_space_t; @@ -343,6 +357,13 @@ typedef struct SRSRAN_API { */ SRSRAN_API const char* srsran_rnti_type_str(srsran_rnti_type_t rnti_type); +/** + * @brief Get the RNTI type name for NR + * @param rnti_type RNTI type name + * @return Constant string with the RNTI type name + */ +SRSRAN_API const char* srsran_dci_format_nr_string(srsran_dci_format_nr_t format); + /** * @brief Calculates the bandwidth of a given CORESET in physical resource blocks (PRB) . This function uses the * frequency domain resources bit-map for counting the number of PRB. diff --git a/lib/include/srsran/phy/enb/enb_dl_nr.h b/lib/include/srsran/phy/enb/enb_dl_nr.h index c8ef78941..3857e9fcc 100644 --- a/lib/include/srsran/phy/enb/enb_dl_nr.h +++ b/lib/include/srsran/phy/enb/enb_dl_nr.h @@ -24,6 +24,7 @@ #include "srsran/phy/common/phy_common_nr.h" #include "srsran/phy/dft/ofdm.h" +#include "srsran/phy/phch/pdcch_cfg_nr.h" #include "srsran/phy/phch/pdcch_nr.h" #include "srsran/phy/phch/pdsch_nr.h" @@ -35,10 +36,10 @@ typedef struct SRSRAN_API { } srsran_enb_dl_nr_args_t; typedef struct SRSRAN_API { - uint32_t max_prb; - uint32_t nof_tx_antennas; - srsran_carrier_nr_t carrier; - srsran_coreset_t coreset; + uint32_t max_prb; + uint32_t nof_tx_antennas; + srsran_carrier_nr_t carrier; + srsran_pdcch_cfg_nr_t pdcch_cfg; srsran_ofdm_t fft[SRSRAN_MAX_PORTS]; @@ -46,6 +47,7 @@ typedef struct SRSRAN_API { srsran_pdsch_nr_t pdsch; srsran_dmrs_sch_t dmrs; + srsran_dci_nr_t dci; ///< Stores DCI configuration srsran_pdcch_nr_t pdcch; } srsran_enb_dl_nr_t; @@ -54,7 +56,9 @@ srsran_enb_dl_nr_init(srsran_enb_dl_nr_t* q, cf_t* output[SRSRAN_MAX_PORTS], con SRSRAN_API int srsran_enb_dl_nr_set_carrier(srsran_enb_dl_nr_t* q, const srsran_carrier_nr_t* carrier); -SRSRAN_API int srsran_enb_dl_nr_set_coreset(srsran_enb_dl_nr_t* q, const srsran_coreset_t* coreset); +SRSRAN_API int srsran_enb_dl_nr_set_pdcch_config(srsran_enb_dl_nr_t* q, + const srsran_pdcch_cfg_nr_t* cfg, + const srsran_dci_cfg_nr_t* dci_cfg); SRSRAN_API void srsran_enb_dl_nr_free(srsran_enb_dl_nr_t* q); diff --git a/lib/include/srsran/phy/fec/ldpc/ldpc_decoder.h b/lib/include/srsran/phy/fec/ldpc/ldpc_decoder.h index 28db3c54d..bf7ff438a 100644 --- a/lib/include/srsran/phy/fec/ldpc/ldpc_decoder.h +++ b/lib/include/srsran/phy/fec/ldpc/ldpc_decoder.h @@ -135,19 +135,6 @@ srsran_ldpc_decoder_decode_f(srsran_ldpc_decoder_t* q, const float* llrs, uint8_ SRSRAN_API int srsran_ldpc_decoder_decode_s(srsran_ldpc_decoder_t* q, const int16_t* llrs, uint8_t* message, uint32_t cdwd_rm_length); -/*! - * Carries out the actual decoding with 8-bit integer-valued LLRs. It is - * recommended to use a 7-bit representation for the LLRs, given that all - * values exceeding \f$ 2^{7}-1 \f$ (in magnitude) will be considered as infinity. - * \param[in] q A pointer to the LDPC decoder (a srsran_ldpc_decoder_t structure - * instance) that carries out the decoding. - * \param[in] llrs The LLRs obtained from the channel samples that correspond to - * the codeword to be decoded. - * \param[out] message The message (uncoded bits) resulting from the decoding - * operation. - */ -SRSRAN_API int srsran_ldpc_decoder_decode_c(srsran_ldpc_decoder_t* q, const int8_t* llrs, uint8_t* message); - /*! * Carries out the actual decoding with 8-bit integer-valued LLRs. It is * recommended to use a 7-bit representation for the LLRs, given that all @@ -160,9 +147,7 @@ SRSRAN_API int srsran_ldpc_decoder_decode_c(srsran_ldpc_decoder_t* q, const int8 * operation. * \param[in] cdwd_rm_length The number of bits forming the codeword (after rate matching). */ -SRSRAN_API int srsran_ldpc_decoder_decode_rm_c(srsran_ldpc_decoder_t* q, - const int8_t* llrs, - uint8_t* message, - uint32_t cdwd_rm_length); +SRSRAN_API int +srsran_ldpc_decoder_decode_c(srsran_ldpc_decoder_t* q, const int8_t* llrs, uint8_t* message, uint32_t cdwd_rm_length); #endif // SRSRAN_LDPCDECODER_H diff --git a/lib/include/srsran/phy/fec/ldpc/ldpc_rm.h b/lib/include/srsran/phy/fec/ldpc/ldpc_rm.h index 32cc7aa59..27c35434a 100644 --- a/lib/include/srsran/phy/fec/ldpc/ldpc_rm.h +++ b/lib/include/srsran/phy/fec/ldpc/ldpc_rm.h @@ -178,7 +178,7 @@ SRSRAN_API int srsran_ldpc_rm_rx_init_c(srsran_ldpc_rm_t* q); * \param[in] rv Redundancy version 0,1,2,3. * \param[in] mod_type Modulation type. * \param[in] Nref Size of limited buffer. - * \return An integer: 0 if the function executes correctly, -1 otherwise. + * \return An integer: The number of useful LLR if the function executes correctly, -1 otherwise. */ SRSRAN_API int srsran_ldpc_rm_rx_c(srsran_ldpc_rm_t* q, const int8_t* input, diff --git a/lib/include/srsran/phy/phch/dci_nr.h b/lib/include/srsran/phy/phch/dci_nr.h index 07a5e8d46..679678447 100644 --- a/lib/include/srsran/phy/phch/dci_nr.h +++ b/lib/include/srsran/phy/phch/dci_nr.h @@ -26,24 +26,131 @@ #include "srsran/phy/common/phy_common_nr.h" #include "srsran/phy/phch/phch_cfg_nr.h" +/** + * @brief Maximum number of NR DCI sizes the UE shall search for a given serving cell + */ +#define SRSRAN_DCI_NR_MAX_NOF_SIZES 4 + +/** + * @brief DCI configuration given a serving cell + */ typedef struct SRSRAN_API { - srsran_dci_location_t location; - srsran_search_space_type_t search_space; - uint32_t coreset_id; - uint8_t payload[50]; - srsran_rnti_type_t rnti_type; - uint32_t nof_bits; - srsran_dci_format_nr_t format; - uint16_t rnti; + /// Bandwidth parameters + uint32_t coreset0_bw; ///< CORESET0 DL bandwidth, set to 0 if not present + uint32_t bwp_dl_initial_bw; ///< Initial DL BWP bandwidth + uint32_t bwp_dl_active_bw; ///< Active DL BWP bandwidth in PRB + uint32_t bwp_ul_initial_bw; ///< Initial UL BWP bandwidth + uint32_t bwp_ul_active_bw; ///< Active UL BWP bandwidth in PRB + + /// Search space derived parameters + bool monitor_common_0_0; ///< Set to true if Format 0_0 is monitored in common SS + bool monitor_0_0_and_1_0; ///< Set to true if Format 0_0 is monitored in UE-specific SS + bool monitor_0_1_and_1_1; ///< Set to true if Formats 0_1 and 1_1 are monitored in UE-specific SS + + /// PUSCH configuration derived parameters + bool enable_sul; ///< Set to true if supplementary uplink is configured + bool enable_hopping; ///< Set to true if frequency hopping is enabled + + /// Common Formats 0_1 and 1_1 + uint32_t carrier_indicator_size; ///< Defined in TS 38.213 clause 10.1 + srsran_harq_ack_codebook_t harq_ack_codebok; ///< PDSCH HARQ-ACK codebook mode + uint32_t nof_rb_groups; ///< Defined in TS 38.214 clause 6.1.2.2.1 + + /// Format 0_1 specific configuration (for PUSCH only) + uint32_t nof_ul_bwp; ///< Number of UL BWPs excluding the initial UL BWP, mentioned in the TS as N_BWP_RRC + uint32_t nof_ul_time_res; ///< Number of dedicated PUSCH time domain resource assigment, set to 0 for default + uint32_t nof_srs; ///< Number of configured SRS resources + uint32_t nof_ul_layers; ///< Set to the maximum number of layers for PUSCH + uint32_t pusch_nof_cbg; ///< determined by maxCodeBlockGroupsPerTransportBlock for PUSCH + uint32_t report_trigger_size; ///< determined by reportTriggerSize + bool enable_transform_precoding; ///< Set to true if PUSCH transform precoding is enabled + bool dynamic_dual_harq_ack_codebook; ///< Set to true if HARQ-ACK codebook is set to dynamic with 2 sub-codebooks + bool pusch_tx_config_codebook; ///< Set to true if PUSCH txConfig is set to codebook + bool pusch_dmrs_type2; ///< Set to true if PUSCH DMRS are type 2 + bool pusch_dmrs_double; ///< Set to true if PUSCH DMRS are 2 symbol long + bool pusch_ptrs; ///< Set to true if PT-RS are enabled for PUSCH transmission + bool pusch_dynamic_betas; ///< Set to true if beta offsets operation is not semi-static + srsran_resource_alloc_t pusch_alloc_type; ///< PUSCH resource allocation type + + /// Format 1_1 specific configuration (for PDSCH only) + uint32_t nof_dl_bwp; ///< Number of DL BWPs excluding the initial UL BWP, mentioned in the TS as N_BWP_RRC + srsran_resource_alloc_t pdsch_alloc_type; ///< PDSCH resource allocation type, set to 0 for default + uint32_t nof_dl_time_res; ///< Number of dedicated PDSCH time domain resource assigment + uint32_t nof_aperiodic_zp; ///< Number of aperiodic ZP CSI-RS resource sets configured + bool pdsch_inter_prb_to_prb; ///< Set to true if interleaved VRB to PRB mapping is enabled + bool pdsch_rm_pattern1; ///< Set to true if rateMatchPatternGroup1 is configured + bool pdsch_rm_pattern2; ///< Set to true if rateMatchPatternGroup2 is configured + bool pdsch_2cw; ///< Set to true if maxNrofCodeWordsScheduledByDCI is set to 2 in any BWP + uint32_t pdsch_nof_cbg; ///< determined by maxCodeBlockGroupsPerTransportBlock for PDSCH + bool multiple_scell; ///< Set to true if configured with multiple serving cell + bool nof_dl_to_ul_ack; ///< Number of entries in the dl-DataToUL-ACK + bool pdsch_dmrs_type2; ///< Set to true if PDSCH DMRS are type 2 + bool pdsch_dmrs_double; ///< Set to true if PDSCH DMRS are 2 symbol long + bool pdsch_tci; ///< Set to true if tci-PresentInDCI is enabled + bool pdsch_cbg_flush; ///< Set to true if codeBlockGroupFlushIndicator is true + bool pdsch_dynamic_bundling; ///< Set to true if prb-BundlingType is set to dynamicBundling + +} srsran_dci_cfg_nr_t; + +/** + * @brief NR-DCI object. Stores DCI configuration and pre-calculated DCI sizes + */ +typedef struct SRSRAN_API { + /// Configuration parameters + srsran_dci_cfg_nr_t cfg; + + /// Formats 0_0 and 1_0 in common SS + uint32_t dci_0_0_and_1_0_common_size; ///< DCI format 0_0 and 1_0 in common SS size + uint32_t dci_0_0_common_trunc; ///< DCI format 0_0 in common SS truncation + uint32_t dci_0_0_common_padd; ///< DCI format 0_0 in common SS padding + + /// Formats 0_0 and 1_0 in UE-specific SS + uint32_t dci_0_0_and_1_0_ue_size; ///< DCI format 0_0 and 1_0 in UE-specific SS size + uint32_t dci_0_0_ue_padd; ///< DCI format 0_0 in ue SS padding + uint32_t dci_1_0_ue_padd; ///< DCI format 1_0 in ue SS padding + + /// Formats 0_1 and 1_0 (UE-specific SS only) + uint32_t dci_0_1_size; ///< DCI format 0_1 size + uint32_t dci_0_1_padd; ///< DCI format 0_1 padding + uint32_t dci_1_1_size; ///< DCI format 0_1 size + uint32_t dci_1_1_padd; ///< DCI format 1_1 padding + +} srsran_dci_nr_t; + +/** + * @brief Describes the NR DCI search context + */ +typedef struct SRSRAN_API { + srsran_dci_location_t location; ///< DCI location + srsran_search_space_type_t ss_type; ///< Search space type + uint32_t coreset_id; ///< CORESET identifier + srsran_rnti_type_t rnti_type; ///< RNTI type + srsran_dci_format_nr_t format; ///< DCI format + uint16_t rnti; ///< UE temporal RNTI + uint32_t coreset0_bw; ///< CORESET0 DL bandwidth, set to 0 if not present + uint32_t bwp_dl_initial_bw; ///< Initial DL/UL BWP bandwidth + uint32_t bwp_dl_active_bw; ///< Active DL/UL BWP bandwidth in PRB + uint32_t bwp_ul_initial_bw; ///< Initial UL BWP bandwidth + uint32_t bwp_ul_active_bw; ///< Active UL BWP bandwidth in PRB + bool monitor_common_0_0; ///< Set to true if Format 0_0 is monitored in common SS + bool enable_sul; ///< Set to true if supplementary uplink is configured + bool enable_hopping; ///< Set to true if frequency hopping is enabled +} srsran_dci_ctx_t; + +/** + * @brief Describes any packed format NR DCI message + */ +typedef struct SRSRAN_API { + srsran_dci_ctx_t ctx; ///< DCI context + uint8_t payload[50]; + uint32_t nof_bits; } srsran_dci_msg_nr_t; +/** + * @brief Describes an unpacked DL NR DCI message + */ typedef struct SRSRAN_API { - uint16_t rnti; - srsran_rnti_type_t rnti_type; - srsran_dci_format_nr_t format; - srsran_dci_location_t location; - srsran_search_space_type_t search_space; - uint32_t coreset_id; + srsran_dci_ctx_t ctx; ///< DCI search context // Common fields uint32_t freq_domain_assigment; ///< Frequency domain resource assignment @@ -71,14 +178,13 @@ typedef struct SRSRAN_API { } srsran_dci_dl_nr_t; +/** + * @brief Describes an unpacked UL NR DCI message + * @remark NR RAR UL DCI Described in TS 38.213 Table 8.2-1: Random Access Response Grant Content field size + */ typedef struct SRSRAN_API { // Context information - uint16_t rnti; - srsran_rnti_type_t rnti_type; - srsran_dci_format_nr_t format; - srsran_dci_location_t location; - srsran_search_space_type_t search_space; - uint32_t coreset_id; + srsran_dci_ctx_t ctx; ///< Context information // Common fields uint32_t freq_domain_assigment; ///< Frequency domain resource assignment @@ -100,60 +206,93 @@ typedef struct SRSRAN_API { // Random Access Response Grant uint32_t csi_request; + + // Other fields + uint32_t sul; ///< Supplementary Uplink flag } srsran_dci_ul_nr_t; /** - * @brief Indicates whether the provided DCI message format bit indicator belongs to DCI format 1_0 according according - * to the RNTI type. If invalid, the DCI message is likely to be format 0_0 - * @param dci_msg Provides DCI format 1_0 message - * @return true if the DCI message is format 1_0, false otherwise + * @brief Set NR-DCI configuration for a given cell. This function will pre-compute the DCI sizes, padding, truncation + * and so on from a given DCI configuration. + * @remark Implemented according TS 38.212 section 7.3.1.0 DCI size alignment + * @param[in,out] dci DCI object + * @param[in] cfg NR-DCI configuration + * @return SRSLTE_SUCCESS if the configuration is valid, SRSLTE_ERROR code otherwise */ -SRSRAN_API bool srsran_dci_nr_format_1_0_valid(const srsran_dci_msg_nr_t* dci_msg); - -SRSRAN_API int srsran_dci_nr_pack(const srsran_carrier_nr_t* carrier, - const srsran_coreset_t* coreset, - const srsran_dci_dl_nr_t* dci, - srsran_dci_msg_nr_t* msg); - -SRSRAN_API int srsran_dci_nr_format_0_0_sizeof(const srsran_carrier_nr_t* carrier, - const srsran_coreset_t* coreset, - srsran_rnti_type_t rnti_type); - -SRSRAN_API int srsran_dci_nr_format_0_0_pack(const srsran_carrier_nr_t* carrier, - const srsran_coreset_t* coreset0, - const srsran_dci_ul_nr_t* dci, - srsran_dci_msg_nr_t* msg); - -SRSRAN_API int srsran_dci_nr_format_0_0_unpack(const srsran_carrier_nr_t* carrier, - const srsran_coreset_t* coreset, - srsran_dci_msg_nr_t* msg, - srsran_dci_ul_nr_t* dci); +SRSRAN_API int srsran_dci_nr_set_cfg(srsran_dci_nr_t* dci, const srsran_dci_cfg_nr_t* cfg); /** - * @brief Unpacks DCI from Random Access Response Grant - * @remark Described in TS 38.213 Table 8.2-1: Random Access Response Grant Content field size - * @param msg - * @param dci - * @return SRSRAN_SUCCESS if unpacked correctly, SRSRAN_ERROR code otherwise + * @brief Calculates a DL NR-DCI size for a given SS type and format + * @attention Only DCI 0_0 and 1_0 can be in common search-space + * @param q NR DCI object + * @param ss_type Search Space type + * @param format NR DCI format + * @return The number of bis for the DCI message if configured, 0 otherwise */ -SRSRAN_API int srsran_dci_nr_rar_unpack(srsran_dci_msg_nr_t* msg, srsran_dci_ul_nr_t* dci); +SRSRAN_API uint32_t srsran_dci_nr_size(const srsran_dci_nr_t* q, + srsran_search_space_type_t ss_type, + srsran_dci_format_nr_t format); -SRSRAN_API int srsran_dci_nr_format_1_0_sizeof(const srsran_carrier_nr_t* carrier, - const srsran_coreset_t* coreset, - srsran_rnti_type_t rnti_type); +/** + * @brief Indicates whether the provided DCI message format bit indicator belongs to the a Dl DCI format (1_0 or 1_1) + * according according to the RNTI type. If invalid, the DCI message is likely to be an UL DCI + * @param dci Provides DCI format message + * @return true if the DCI message is for DL, false otherwise + */ +SRSRAN_API bool srsran_dci_nr_valid_direction(const srsran_dci_msg_nr_t* dci); -SRSRAN_API int srsran_dci_nr_format_1_0_pack(const srsran_carrier_nr_t* carrier, - const srsran_coreset_t* coreset, - const srsran_dci_dl_nr_t* dci, - srsran_dci_msg_nr_t* msg); +/** + * @brief Packs a DL NR DCI into a DCI message + * @param q NR DCI object with precomputed DCI parameters + * @param dci DL NR DCI to pack (serialize) + * @param[out] msg Resultant packed DCI message + * @return SRSLTE_SUCCESS if provided arguments are valid, SRSLTE_ERROR code otherwise + */ +SRSRAN_API int srsran_dci_nr_dl_pack(const srsran_dci_nr_t* q, const srsran_dci_dl_nr_t* dci, srsran_dci_msg_nr_t* msg); -SRSRAN_API int srsran_dci_nr_format_1_0_unpack(const srsran_carrier_nr_t* carrier, - const srsran_coreset_t* coreset, - srsran_dci_msg_nr_t* msg, - srsran_dci_dl_nr_t* dci); +/** + * @brief Unpacks an NR DCI message into a DL NR DCI + * @param q NR DCI object with precomputed DCI parameters + * @param msg DCI message to unpack (deserialize) + * @param[out] dci Resultant unpacked DL DCI + * @return SRSLTE_SUCCESS if provided arguments are valid, SRSLTE_ERROR code otherwise + */ +SRSRAN_API int srsran_dci_nr_dl_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_t* msg, srsran_dci_dl_nr_t* dci); -SRSRAN_API int srsran_dci_ul_nr_to_str(const srsran_dci_ul_nr_t* dci, char* str, uint32_t str_len); +/** + * @brief Packs an UL NR DCI into a DCI message + * @param q NR DCI object with precomputed DCI parameters + * @param dci UL NR DCI to pack (serialize) + * @param[out] msg resultant DCI message + * @return SRSLTE_SUCCESS if provided arguments are valid, SRSLTE_ERROR code otherwise + */ +SRSRAN_API int srsran_dci_nr_ul_pack(const srsran_dci_nr_t* q, const srsran_dci_ul_nr_t* dci, srsran_dci_msg_nr_t* msg); +/** + * @brief Unpacks an NR DCI message into an UL NR DCI + * @param q NR DCI object with precomputed DCI parameters + * @param msg DCI message to unpack (deserialize) + * @param[out] dci Resultant unpacked UL DCI + * @return SRSLTE_SUCCESS if provided arguments are valid, SRSLTE_ERROR code otherwise + */ +SRSRAN_API int srsran_dci_nr_ul_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_t* msg, srsran_dci_ul_nr_t* dci); + +/** + * @brief Stringifies an DL NR DCI structure + * @param dci DL NR SCI structure to stringify + * @param[out] str Destination string + * @param str_len Destination string length + * @return The number of written characters + */ SRSRAN_API int srsran_dci_dl_nr_to_str(const srsran_dci_dl_nr_t* dci, char* str, uint32_t str_len); +/** + * @brief Stringifies an UL NR DCI structure + * @param dci UL NR SCI structure to stringify + * @param[out] str Destination string + * @param str_len Destination string length + * @return The number of written characters + */ +SRSRAN_API int srsran_dci_ul_nr_to_str(const srsran_dci_ul_nr_t* dci, char* str, uint32_t str_len); + #endif // SRSRAN_DCI_NR_H diff --git a/lib/include/srsran/phy/phch/pdcch_cfg_nr.h b/lib/include/srsran/phy/phch/pdcch_cfg_nr.h new file mode 100644 index 000000000..edffb2d3f --- /dev/null +++ b/lib/include/srsran/phy/phch/pdcch_cfg_nr.h @@ -0,0 +1,44 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSRAN_PDCCH_CFG_NR_H +#define SRSRAN_PDCCH_CFG_NR_H + +#include "dci_nr.h" + +/** + * Maximum number of CORESET + * @remark Defined in TS 38.331 by maxNrofControlResourceSets-1 + */ +#define SRSRAN_UE_DL_NR_MAX_NOF_CORESET 12 + +/** + * Maximum number of Search spaces + * @remark Defined in TS 38.331 by maxNrofSearchSpaces-1 + */ +#define SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE 40 + +/** + * @brief PDCCH configuration provided by upper layers + */ +typedef struct SRSRAN_API { + srsran_coreset_t coreset[SRSRAN_UE_DL_NR_MAX_NOF_CORESET]; ///< PDCCH Control resource sets (CORESET) collection + bool coreset_present[SRSRAN_UE_DL_NR_MAX_NOF_CORESET]; ///< CORESET present flags + + srsran_search_space_t search_space[SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE]; + bool search_space_present[SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE]; + + srsran_search_space_t ra_search_space; + bool ra_search_space_present; +} srsran_pdcch_cfg_nr_t; + +#endif // SRSRAN_PDCCH_CFG_NR_H diff --git a/lib/include/srsran/phy/phch/phch_cfg_nr.h b/lib/include/srsran/phy/phch/phch_cfg_nr.h index 095a0b950..9ef79ac08 100644 --- a/lib/include/srsran/phy/phch/phch_cfg_nr.h +++ b/lib/include/srsran/phy/phch/phch_cfg_nr.h @@ -210,13 +210,14 @@ typedef struct SRSRAN_API { bool present; } dmrs_typeB; - srsran_sch_time_ra_t common_time_ra[SRSRAN_MAX_NOF_DL_ALLOCATION]; + srsran_sch_time_ra_t common_time_ra[SRSRAN_MAX_NOF_TIME_RA]; uint32_t nof_common_time_ra; - srsran_sch_time_ra_t dedicated_time_ra[SRSRAN_MAX_NOF_DL_ALLOCATION]; + srsran_sch_time_ra_t dedicated_time_ra[SRSRAN_MAX_NOF_TIME_RA]; uint32_t nof_dedicated_time_ra; - bool rbg_size_cfg_1; ///< RBG size configuration (1 or 2) + bool rbg_size_cfg_1; ///< RBG size configuration (1 or 2) + srsran_resource_alloc_t alloc; srsran_sch_cfg_t sch_cfg; ///< Common shared channel parameters diff --git a/lib/include/srsran/phy/ue/ue_dl_nr.h b/lib/include/srsran/phy/ue/ue_dl_nr.h index 3beb597cb..3183966aa 100644 --- a/lib/include/srsran/phy/ue/ue_dl_nr.h +++ b/lib/include/srsran/phy/ue/ue_dl_nr.h @@ -26,22 +26,11 @@ #include "srsran/phy/common/phy_common_nr.h" #include "srsran/phy/dft/ofdm.h" #include "srsran/phy/phch/dci_nr.h" +#include "srsran/phy/phch/pdcch_cfg_nr.h" #include "srsran/phy/phch/pdcch_nr.h" #include "srsran/phy/phch/pdsch_nr.h" #include "srsran/phy/phch/uci_cfg_nr.h" -/** - * Maximum number of CORESET - * @remark Defined in TS 38.331 by maxNrofControlResourceSets-1 - */ -#define SRSRAN_UE_DL_NR_MAX_NOF_CORESET 12 - -/** - * Maximum number of Search spaces - * @remark Defined in TS 38.331 by maxNrofSearchSpaces-1 - */ -#define SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE 40 - /** * Maximum number of DCI messages to receive */ @@ -56,18 +45,6 @@ typedef struct SRSRAN_API { float pdcch_dmrs_epre_thr; } srsran_ue_dl_nr_args_t; -typedef struct SRSRAN_API { - srsran_coreset_t coreset[SRSRAN_UE_DL_NR_MAX_NOF_CORESET]; ///< PDCCH Control resource sets (CORESET) collection - bool coreset_present[SRSRAN_UE_DL_NR_MAX_NOF_CORESET]; ///< CORESET present flags - - srsran_search_space_t search_space[SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE]; - bool search_space_present[SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE]; - - uint16_t ra_rnti; ///< Needs to be deduced from the PRACH configuration - srsran_search_space_t ra_search_space; - bool ra_search_space_present; -} srsran_ue_dl_nr_pdcch_cfg_t; - typedef struct { uint32_t scell_idx; ///< Serving cell index uint32_t v_dai_dl; ///< Downlink Assigment Index @@ -102,9 +79,9 @@ typedef struct { } srsran_pdsch_ack_nr_t; typedef struct SRSRAN_API { - bool harq_ack_spatial_bundling_pucch; ///< Param harq-ACK-SpatialBundlingPUCCH, set to true if provided - bool harq_ack_spatial_bundling_pusch; ///< Param harq-ACK-SpatialBundlingPUSCH, set to true if provided - srsran_pdsch_harq_ack_codebook_t pdsch_harq_ack_codebook; ///< pdsch-HARQ-ACK-Codebook configuration + bool harq_ack_spatial_bundling_pucch; ///< Param harq-ACK-SpatialBundlingPUCCH, set to true if provided + bool harq_ack_spatial_bundling_pusch; ///< Param harq-ACK-SpatialBundlingPUSCH, set to true if provided + srsran_harq_ack_codebook_t harq_ack_codebook; ///< pdsch-HARQ-ACK-Codebook configuration bool max_cw_sched_dci_is_2; ///< Param maxNrofCodeWordsScheduledByDCI, set to true if present and equal to 2 uint32_t dl_data_to_ul_ack[SRSRAN_MAX_NOF_DL_DATA_TO_UL]; @@ -112,11 +89,10 @@ typedef struct SRSRAN_API { } srsran_ue_dl_nr_harq_ack_cfg_t; typedef struct SRSRAN_API { - uint32_t coreset_id; - uint32_t ss_id; - srsran_dci_location_t location; + srsran_dci_ctx_t dci_ctx; srsran_dmrs_pdcch_measure_t measure; srsran_pdcch_nr_res_t result; + uint32_t nof_bits; } srsran_ue_dl_nr_pdcch_info_t; typedef struct SRSRAN_API { @@ -125,8 +101,8 @@ typedef struct SRSRAN_API { float pdcch_dmrs_corr_thr; float pdcch_dmrs_epre_thr; - srsran_carrier_nr_t carrier; - srsran_ue_dl_nr_pdcch_cfg_t cfg; + srsran_carrier_nr_t carrier; + srsran_pdcch_cfg_nr_t cfg; srsran_ofdm_t fft[SRSRAN_MAX_PORTS]; @@ -143,12 +119,15 @@ typedef struct SRSRAN_API { srsran_ue_dl_nr_pdcch_info_t pdcch_info[SRSRAN_MAX_NOF_CANDIDATES_SLOT_NR]; uint32_t pdcch_info_count; - /// Temporally stores Found DCI messages from all SS - srsran_dci_msg_nr_t dci_msg[SRSRAN_MAX_DCI_MSG_NR]; - uint32_t dci_msg_count; + /// DCI packing/unpacking object + srsran_dci_nr_t dci; - srsran_dci_msg_nr_t pending_ul_dci_msg[SRSRAN_MAX_DCI_MSG_NR]; - uint32_t pending_ul_dci_count; + /// Temporally stores Found DCI messages from all SS + srsran_dci_msg_nr_t dl_dci_msg[SRSRAN_MAX_DCI_MSG_NR]; + uint32_t dl_dci_msg_count; + + srsran_dci_msg_nr_t ul_dci_msg[SRSRAN_MAX_DCI_MSG_NR]; + uint32_t ul_dci_count; } srsran_ue_dl_nr_t; SRSRAN_API int @@ -156,7 +135,9 @@ srsran_ue_dl_nr_init(srsran_ue_dl_nr_t* q, cf_t* input[SRSRAN_MAX_PORTS], const SRSRAN_API int srsran_ue_dl_nr_set_carrier(srsran_ue_dl_nr_t* q, const srsran_carrier_nr_t* carrier); -SRSRAN_API int srsran_ue_dl_nr_set_pdcch_config(srsran_ue_dl_nr_t* q, const srsran_ue_dl_nr_pdcch_cfg_t* cfg); +SRSRAN_API int srsran_ue_dl_nr_set_pdcch_config(srsran_ue_dl_nr_t* q, + const srsran_pdcch_cfg_nr_t* cfg, + const srsran_dci_cfg_nr_t* dci_cfg); SRSRAN_API void srsran_ue_dl_nr_free(srsran_ue_dl_nr_t* q); @@ -195,4 +176,8 @@ SRSRAN_API int srsran_ue_dl_nr_gen_ack(const srsran_ue_dl_nr_harq_ack_cfg_t* cfg const srsran_pdsch_ack_nr_t* ack_info, srsran_uci_data_nr_t* uci_data); +SRSRAN_API int srsran_ue_dl_nr_ack_insert_m(srsran_pdsch_ack_nr_t* ack_info, srsran_pdsch_ack_m_nr_t* m); + +SRSRAN_API uint32_t srsran_ue_dl_nr_ack_info(const srsran_pdsch_ack_nr_t* ack_info, char* str, uint32_t str_len); + #endif // SRSRAN_UE_DL_NR_H diff --git a/lib/include/srsran/upper/bearer_mem_pool.h b/lib/include/srsran/upper/bearer_mem_pool.h new file mode 100644 index 000000000..f7953863a --- /dev/null +++ b/lib/include/srsran/upper/bearer_mem_pool.h @@ -0,0 +1,27 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSRAN_BEARER_MEM_POOL_H +#define SRSRAN_BEARER_MEM_POOL_H + +#include + +namespace srsran { + +// Allocation of objects in rnti-dedicated memory pool +void reserve_rlc_memblocks(size_t nof_blocks); +void* allocate_rlc_bearer(std::size_t size); +void deallocate_rlc_bearer(void* p); + +} // namespace srsran + +#endif // SRSRAN_BEARER_MEM_POOL_H diff --git a/lib/include/srsran/upper/pdcp.h b/lib/include/srsran/upper/pdcp.h index 166de2898..fc6af5708 100644 --- a/lib/include/srsran/upper/pdcp.h +++ b/lib/include/srsran/upper/pdcp.h @@ -49,7 +49,7 @@ public: void write_sdu_mch(uint32_t lcid, unique_byte_buffer_t sdu); void add_bearer(uint32_t lcid, pdcp_config_t cnfg) override; void add_bearer_mrb(uint32_t lcid, pdcp_config_t cnfg); - void del_bearer(uint32_t lcid); + void del_bearer(uint32_t lcid) override; void change_lcid(uint32_t old_lcid, uint32_t new_lcid) override; void config_security(uint32_t lcid, as_security_config_t sec_cfg) override; void config_security_all(as_security_config_t sec_cfg) override; diff --git a/lib/include/srsran/upper/pdcp_entity_base.h b/lib/include/srsran/upper/pdcp_entity_base.h index d7f7a8b70..bd99ab641 100644 --- a/lib/include/srsran/upper/pdcp_entity_base.h +++ b/lib/include/srsran/upper/pdcp_entity_base.h @@ -65,6 +65,7 @@ public: pdcp_entity_base(task_sched_handle task_sched_, srslog::basic_logger& logger); pdcp_entity_base(pdcp_entity_base&&) = default; virtual ~pdcp_entity_base(); + virtual bool configure(const pdcp_config_t& cnfg_) = 0; virtual void reset() = 0; virtual void reestablish() = 0; diff --git a/lib/include/srsran/upper/pdcp_entity_lte.h b/lib/include/srsran/upper/pdcp_entity_lte.h index 5182668e4..270564b1e 100644 --- a/lib/include/srsran/upper/pdcp_entity_lte.h +++ b/lib/include/srsran/upper/pdcp_entity_lte.h @@ -117,9 +117,9 @@ public: srsue::gw_interface_pdcp* gw_, srsran::task_sched_handle task_sched_, srslog::basic_logger& logger, - uint32_t lcid_, - pdcp_config_t cfg_); + uint32_t lcid_); ~pdcp_entity_lte() override; + bool configure(const pdcp_config_t& cnfg_) override; void reset() override; void reestablish() override; diff --git a/lib/include/srsran/upper/pdcp_entity_nr.h b/lib/include/srsran/upper/pdcp_entity_nr.h index 7fccc59a6..66e9bf330 100644 --- a/lib/include/srsran/upper/pdcp_entity_nr.h +++ b/lib/include/srsran/upper/pdcp_entity_nr.h @@ -48,9 +48,9 @@ public: srsue::gw_interface_pdcp* gw_, srsran::task_sched_handle task_sched_, srslog::basic_logger& logger, - uint32_t lcid, - pdcp_config_t cfg_); + uint32_t lcid); ~pdcp_entity_nr() final; + bool configure(const pdcp_config_t& cnfg_) final; void reset() final; void reestablish() final; diff --git a/lib/include/srsran/upper/rlc_am_lte.h b/lib/include/srsran/upper/rlc_am_lte.h index 69aa72191..af76cec56 100644 --- a/lib/include/srsran/upper/rlc_am_lte.h +++ b/lib/include/srsran/upper/rlc_am_lte.h @@ -24,6 +24,7 @@ #include "srsran/adt/accumulators.h" #include "srsran/adt/circular_array.h" +#include "srsran/adt/circular_map.h" #include "srsran/common/buffer_pool.h" #include "srsran/common/common.h" #include "srsran/common/srsran_assert.h" @@ -82,53 +83,39 @@ struct pdcp_sdu_info_t { template struct rlc_ringbuffer_t { - rlc_ringbuffer_t() { clear(); } T& add_pdu(size_t sn) { srsran_expect(not has_sn(sn), "The same SN=%zd should not be added twice", sn); + window.overwrite(sn, T{}); window[sn].rlc_sn = sn; - active_flag[sn] = true; - count++; return window[sn]; } void remove_pdu(size_t sn) { srsran_expect(has_sn(sn), "The removed SN=%zd is not in the window", sn); - window[sn] = {}; - active_flag[sn] = false; - count--; - } - T& operator[](size_t sn) - { - srsran_expect(has_sn(sn), "The accessed SN=%zd is not in the window", sn); - return window[sn]; - } - size_t size() const { return count; } - bool empty() const { return count == 0; } - void clear() - { - std::fill(active_flag.begin(), active_flag.end(), false); - count = 0; + window.erase(sn); } + T& operator[](size_t sn) { return window[sn]; } + size_t size() const { return window.size(); } + bool empty() const { return window.empty(); } + void clear() { window.clear(); } - bool has_sn(uint32_t sn) const { return active_flag[sn] and (window[sn].rlc_sn == sn); } + bool has_sn(uint32_t sn) const { return window.contains(sn); } // Return the sum data bytes of all active PDUs (check PDU is non-null) uint32_t get_buffered_bytes() { uint32_t buff_size = 0; for (const auto& pdu : window) { - if (pdu.buf != nullptr) { - buff_size += pdu.buf->N_bytes; + if (pdu.second.buf != nullptr) { + buff_size += pdu.second.buf->N_bytes; } } return buff_size; } private: - size_t count = 0; - srsran::circular_array active_flag = {}; - srsran::circular_array window; + srsran::static_circular_map window; }; struct buffered_pdcp_pdu_list { @@ -138,13 +125,20 @@ public: void add_pdcp_sdu(uint32_t sn) { - assert(not has_pdcp_sn(sn)); + srsran_assert(not has_pdcp_sn(sn), "Cannot re-add same PDCP SN twice"); + uint32_t sn_idx = get_idx(sn); + if (buffered_pdus[sn_idx].sn != invalid_sn) { + clear_pdcp_sdu(buffered_pdus[sn_idx].sn); + } buffered_pdus[get_idx(sn)].sn = sn; count++; } void clear_pdcp_sdu(uint32_t sn) { - uint32_t sn_idx = get_idx(sn); + uint32_t sn_idx = get_idx(sn); + if (buffered_pdus[sn_idx].sn == invalid_sn) { + return; + } buffered_pdus[sn_idx].sn = invalid_sn; buffered_pdus[sn_idx].fully_acked = false; buffered_pdus[sn_idx].fully_txed = false; diff --git a/lib/include/srsran/upper/rlc_common.h b/lib/include/srsran/upper/rlc_common.h index f1b3a0829..1177d2900 100644 --- a/lib/include/srsran/upper/rlc_common.h +++ b/lib/include/srsran/upper/rlc_common.h @@ -24,6 +24,7 @@ #include "srsran/adt/circular_buffer.h" #include "srsran/interfaces/rlc_interface_types.h" +#include "srsran/upper/bearer_mem_pool.h" #include "srsran/upper/rlc_metrics.h" #include @@ -279,6 +280,9 @@ public: virtual void set_bsr_callback(bsr_callback_t callback) = 0; + void* operator new(size_t sz) { return allocate_rlc_bearer(sz); } + void operator delete(void* p) { return deallocate_rlc_bearer(p); } + private: bool suspended = false; diff --git a/lib/src/asn1/rrc_nr_utils.cc b/lib/src/asn1/rrc_nr_utils.cc index ff5931691..bd941d66b 100644 --- a/lib/src/asn1/rrc_nr_utils.cc +++ b/lib/src/asn1/rrc_nr_utils.cc @@ -277,13 +277,13 @@ bool make_phy_harq_ack_cfg(const phys_cell_group_cfg_s& phys_cell_group_cfg, srsran_ue_dl_nr_harq_ack_cfg_t srsran_ue_dl_nr_harq_ack_cfg; switch (phys_cell_group_cfg.pdsch_harq_ack_codebook) { case phys_cell_group_cfg_s::pdsch_harq_ack_codebook_opts::dynamic_value: - srsran_ue_dl_nr_harq_ack_cfg.pdsch_harq_ack_codebook = srsran_pdsch_harq_ack_codebook_dynamic; + srsran_ue_dl_nr_harq_ack_cfg.harq_ack_codebook = srsran_pdsch_harq_ack_codebook_dynamic; break; case phys_cell_group_cfg_s::pdsch_harq_ack_codebook_opts::semi_static: - srsran_ue_dl_nr_harq_ack_cfg.pdsch_harq_ack_codebook = srsran_pdsch_harq_ack_codebook_semi_static; + srsran_ue_dl_nr_harq_ack_cfg.harq_ack_codebook = srsran_pdsch_harq_ack_codebook_semi_static; break; case phys_cell_group_cfg_s::pdsch_harq_ack_codebook_opts::nulltype: - srsran_ue_dl_nr_harq_ack_cfg.pdsch_harq_ack_codebook = srsran_pdsch_harq_ack_codebook_none; + srsran_ue_dl_nr_harq_ack_cfg.harq_ack_codebook = srsran_pdsch_harq_ack_codebook_none; break; default: asn1::log_warning("Invalid option for pdsch_harq_ack_codebook %s", @@ -321,9 +321,51 @@ bool make_phy_search_space_cfg(const search_space_s& search_space, srsran_search switch (search_space.search_space_type.type()) { case search_space_s::search_space_type_c_::types_opts::options::common: srsran_search_space.type = srsran_search_space_type_common_3; + + // dci-Format0-0-AndFormat1-0 + // If configured, the UE monitors the DCI formats 0_0 and 1_0 according to TS 38.213 [13], clause 10.1. + if (search_space.search_space_type.common().dci_format0_minus0_and_format1_minus0_present) { + srsran_search_space.formats[srsran_search_space.nof_formats++] = srsran_dci_format_nr_0_0; + srsran_search_space.formats[srsran_search_space.nof_formats++] = srsran_dci_format_nr_1_0; + } + + // dci-Format2-0 + // If configured, UE monitors the DCI format 2_0 according to TS 38.213 [13], clause 10.1, 11.1.1. + if (search_space.search_space_type.common().dci_format2_minus0_present) { + srsran_search_space.formats[srsran_search_space.nof_formats++] = srsran_dci_format_nr_2_0; + } + + // dci-Format2-1 + // If configured, UE monitors the DCI format 2_1 according to TS 38.213 [13], clause 10.1, 11.2. + if (search_space.search_space_type.common().dci_format2_minus1_present) { + srsran_search_space.formats[srsran_search_space.nof_formats++] = srsran_dci_format_nr_2_1; + } + + // dci-Format2-2 + // If configured, UE monitors the DCI format 2_2 according to TS 38.213 [13], clause 10.1, 11.3. + if (search_space.search_space_type.common().dci_format2_minus2_present) { + srsran_search_space.formats[srsran_search_space.nof_formats++] = srsran_dci_format_nr_2_2; + } + + // dci-Format2-3 + // If configured, UE monitors the DCI format 2_3 according to TS 38.213 [13], clause 10.1, 11.4 + if (search_space.search_space_type.common().dci_format2_minus3_present) { + srsran_search_space.formats[srsran_search_space.nof_formats++] = srsran_dci_format_nr_2_3; + } + break; case search_space_s::search_space_type_c_::types_opts::options::ue_specific: srsran_search_space.type = srsran_search_space_type_ue; + switch (search_space.search_space_type.ue_specific().dci_formats.value) { + case search_space_s::search_space_type_c_::ue_specific_s_::dci_formats_e_::formats0_minus0_and_minus1_minus0: + srsran_search_space.formats[srsran_search_space.nof_formats++] = srsran_dci_format_nr_0_0; + srsran_search_space.formats[srsran_search_space.nof_formats++] = srsran_dci_format_nr_1_0; + break; + case search_space_s::search_space_type_c_::ue_specific_s_::dci_formats_e_::formats0_minus1_and_minus1_minus1: + srsran_search_space.formats[srsran_search_space.nof_formats++] = srsran_dci_format_nr_0_1; + srsran_search_space.formats[srsran_search_space.nof_formats++] = srsran_dci_format_nr_1_1; + break; + } break; default: asn1::log_warning("Invalid option for search_space_type %s", search_space.search_space_type.type().to_string()); diff --git a/lib/src/common/mac_pcap_net.cc b/lib/src/common/mac_pcap_net.cc index d04eecf32..f80fe0a58 100644 --- a/lib/src/common/mac_pcap_net.cc +++ b/lib/src/common/mac_pcap_net.cc @@ -37,7 +37,7 @@ uint32_t mac_pcap_net::open(std::string client_ip_addr_, { std::lock_guard lock(mutex); - if (socket.is_init()) { + if (socket.is_open()) { logger.error("PCAP socket writer for %s already running. Close first.", bind_addr_str.c_str()); return SRSRAN_ERROR; } @@ -48,7 +48,7 @@ uint32_t mac_pcap_net::open(std::string client_ip_addr_, return SRSRAN_ERROR; } if (not socket.bind_addr(bind_addr_str.c_str(), bind_udp_port_)) { - socket.reset(); + socket.close(); logger.error("Couldn't bind socket %s to write PCAP", bind_addr_str.c_str()); return SRSRAN_ERROR; } @@ -73,7 +73,7 @@ uint32_t mac_pcap_net::close() { { std::lock_guard lock(mutex); - if (running == false || socket.is_init() == false) { + if (running == false || socket.is_open() == false) { return SRSRAN_ERROR; } @@ -85,7 +85,7 @@ uint32_t mac_pcap_net::close() wait_thread_finish(); // close socket handle - if (socket.is_init()) { + if (socket.is_open()) { std::lock_guard lock(mutex); socket.close(); } @@ -95,7 +95,7 @@ uint32_t mac_pcap_net::close() void mac_pcap_net::write_pdu(pcap_pdu_t& pdu) { - if (pdu.pdu != nullptr && socket.is_init()) { + if (pdu.pdu != nullptr && socket.is_open()) { switch (pdu.rat) { case srsran_rat_t::lte: write_mac_lte_pdu_to_net(pdu); diff --git a/lib/src/common/network_utils.cc b/lib/src/common/network_utils.cc index 47ac96d2e..fb94a331e 100644 --- a/lib/src/common/network_utils.cc +++ b/lib/src/common/network_utils.cc @@ -24,11 +24,12 @@ #include #include #include +#include // for the pipe -#define rxSockError(fmt, ...) logger.error("%s: " fmt, name.c_str(), ##__VA_ARGS__) -#define rxSockWarn(fmt, ...) logger.warning("%s: " fmt, name.c_str(), ##__VA_ARGS__) -#define rxSockInfo(fmt, ...) logger.info("%s: " fmt, name.c_str(), ##__VA_ARGS__) -#define rxSockDebug(fmt, ...) logger.debug("%s: " fmt, name.c_str(), ##__VA_ARGS__) +#define rxSockError(fmt, ...) logger.error("RxSockets: " fmt, ##__VA_ARGS__) +#define rxSockWarn(fmt, ...) logger.warning("RxSockets: " fmt, ##__VA_ARGS__) +#define rxSockInfo(fmt, ...) logger.info("RxSockets: " fmt, ##__VA_ARGS__) +#define rxSockDebug(fmt, ...) logger.debug("RxSockets: " fmt, ##__VA_ARGS__) namespace srsran { @@ -243,63 +244,53 @@ bool connect_to(int fd, const char* dest_addr_str, int dest_port, sockaddr_in* d * Socket Classes *******************************************/ -socket_handler_t::socket_handler_t(socket_handler_t&& other) noexcept +unique_socket::unique_socket(unique_socket&& other) noexcept : sockfd(other.sockfd), addr(other.addr) +{ + other.sockfd = -1; + other.addr = {}; +} +unique_socket::~unique_socket() +{ + close(); +} +unique_socket& unique_socket::operator=(unique_socket&& other) noexcept { sockfd = other.sockfd; addr = other.addr; - other.sockfd = 0; + other.sockfd = -1; other.addr = {}; -} -socket_handler_t::~socket_handler_t() -{ - reset(); -} -socket_handler_t& socket_handler_t::operator=(socket_handler_t&& other) noexcept -{ - if (this == &other) { - return *this; - } - addr = other.addr; - sockfd = other.sockfd; - other.addr = {}; - other.sockfd = 0; return *this; } -void socket_handler_t::close() +void unique_socket::close() { if (sockfd >= 0) { ::close(sockfd); sockfd = -1; + addr = {}; } } -void socket_handler_t::reset() -{ - this->close(); - addr = {}; -} - -bool socket_handler_t::bind_addr(const char* bind_addr_str, int port) +bool unique_socket::bind_addr(const char* bind_addr_str, int port) { return net_utils::bind_addr(sockfd, bind_addr_str, port, &addr); } -bool socket_handler_t::connect_to(const char* dest_addr_str, int dest_port, sockaddr_in* dest_sockaddr) +bool unique_socket::connect_to(const char* dest_addr_str, int dest_port, sockaddr_in* dest_sockaddr) { return net_utils::connect_to(sockfd, dest_addr_str, dest_port, dest_sockaddr); } -bool socket_handler_t::open_socket(net_utils::addr_family ip_type, - net_utils::socket_type socket_type, - net_utils::protocol_type protocol) +bool unique_socket::open_socket(net_utils::addr_family ip_type, + net_utils::socket_type socket_type, + net_utils::protocol_type protocol) { - if (sockfd >= 0) { + if (is_open()) { srslog::fetch_basic_logger(LOGSERVICE).error("Socket is already open."); return false; } sockfd = net_utils::open_socket(ip_type, socket_type, protocol); - return sockfd >= 0; + return is_open(); } /*********************************************************************** @@ -308,24 +299,24 @@ bool socket_handler_t::open_socket(net_utils::addr_family ip_type, namespace net_utils { -bool sctp_init_socket(socket_handler_t* socket, net_utils::socket_type socktype, const char* bind_addr_str, int port) +bool sctp_init_socket(unique_socket* socket, net_utils::socket_type socktype, const char* bind_addr_str, int port) { if (not socket->open_socket(net_utils::addr_family::ipv4, socktype, net_utils::protocol_type::SCTP)) { return false; } if (not socket->bind_addr(bind_addr_str, port)) { - socket->reset(); + socket->close(); return false; } return true; } -bool sctp_init_client(socket_handler_t* socket, net_utils::socket_type socktype, const char* bind_addr_str) +bool sctp_init_client(unique_socket* socket, net_utils::socket_type socktype, const char* bind_addr_str) { return sctp_init_socket(socket, socktype, bind_addr_str, 0); } -bool sctp_init_server(socket_handler_t* socket, net_utils::socket_type socktype, const char* bind_addr_str, int port) +bool sctp_init_server(unique_socket* socket, net_utils::socket_type socktype, const char* bind_addr_str, int port) { if (not sctp_init_socket(socket, socktype, bind_addr_str, port)) { return false; @@ -338,182 +329,25 @@ bool sctp_init_server(socket_handler_t* socket, net_utils::socket_type socktype, return true; } -/*************************************************************** - * TCP Socket - **************************************************************/ - -bool tcp_make_server(socket_handler_t* socket, const char* bind_addr_str, int port, int nof_connections) -{ - if (not socket->open_socket(addr_family::ipv4, socket_type::stream, protocol_type::TCP)) { - return false; - } - if (not socket->bind_addr(bind_addr_str, port)) { - socket->reset(); - return false; - } - // Listen for connections - if (listen(socket->fd(), nof_connections) != 0) { - srslog::fetch_basic_logger(LOGSERVICE).error("Failed to listen to incoming TCP connections"); - return false; - } - return true; -} - -int tcp_accept(socket_handler_t* socket, sockaddr_in* destaddr) -{ - socklen_t clilen = sizeof(destaddr); - int connfd = accept(socket->fd(), (struct sockaddr*)&destaddr, &clilen); - if (connfd < 0) { - srslog::fetch_basic_logger(LOGSERVICE).error("Failed to accept connection"); - perror("accept"); - return -1; - } - return connfd; -} - -int tcp_read(int remotefd, void* buf, size_t nbytes) -{ - int n = ::read(remotefd, buf, nbytes); - if (n == 0) { - srslog::fetch_basic_logger(LOGSERVICE).info("TCP connection closed"); - close(remotefd); - return 0; - } - if (n == -1) { - srslog::fetch_basic_logger(LOGSERVICE).error("Failed to read from TCP socket."); - perror("TCP read"); - } - return n; -} - -int tcp_send(int remotefd, const void* buf, size_t nbytes) -{ - // Loop until all bytes are sent - char* ptr = (char*)buf; - ssize_t nbytes_remaining = nbytes; - while (nbytes_remaining > 0) { - ssize_t i = ::send(remotefd, ptr, nbytes_remaining, 0); - if (i < 1) { - srslog::fetch_basic_logger(LOGSERVICE).error("Failed to send data to TCP socket"); - perror("Error calling send()\n"); - return i; - } - ptr += i; - nbytes_remaining -= i; - } - return nbytes - nbytes_remaining; -} - } // namespace net_utils -/*************************************************************** - * Rx Multisocket Task Types - **************************************************************/ - -/** - * Description: Specialization of recv_task for the case the received data is - * in the form of unique_byte_buffer, and a recvfrom(...) call is used - */ -class recvfrom_pdu_task final : public rx_multisocket_handler::recv_task -{ -public: - using callback_t = std::function; - explicit recvfrom_pdu_task(srslog::basic_logger& logger, callback_t func_) : logger(logger), func(std::move(func_)) {} - - bool operator()(int fd) override - { - srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); - if (pdu == nullptr) { - logger.error("Unable to allocate byte buffer"); - return true; - } - sockaddr_in from = {}; - socklen_t fromlen = sizeof(from); - - ssize_t n_recv = recvfrom(fd, pdu->msg, pdu->get_tailroom(), 0, (struct sockaddr*)&from, &fromlen); - if (n_recv == -1 and errno != EAGAIN) { - logger.error("Error reading from socket: %s", strerror(errno)); - return true; - } - if (n_recv == -1 and errno == EAGAIN) { - logger.debug("Socket timeout reached"); - return true; - } - - pdu->N_bytes = static_cast(n_recv); - func(std::move(pdu), from); - return true; - } - -private: - srslog::basic_logger& logger; - callback_t func; -}; - -class sctp_recvmsg_pdu_task final : public rx_multisocket_handler::recv_task -{ -public: - using callback_t = std::function< - void(srsran::unique_byte_buffer_t pdu, const sockaddr_in& from, const sctp_sndrcvinfo& sri, int flags)>; - explicit sctp_recvmsg_pdu_task(srslog::basic_logger& logger, callback_t func_) : - logger(logger), func(std::move(func_)) - {} - - bool operator()(int fd) override - { - // inside rx_sockets thread. Read socket - srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); - if (pdu == nullptr) { - logger.error("Unable to allocate byte buffer"); - return true; - } - sockaddr_in from = {}; - socklen_t fromlen = sizeof(from); - sctp_sndrcvinfo sri = {}; - int flags = 0; - ssize_t n_recv = sctp_recvmsg(fd, pdu->msg, pdu->get_tailroom(), (struct sockaddr*)&from, &fromlen, &sri, &flags); - if (n_recv == -1 and errno != EAGAIN) { - logger.error("Error reading from SCTP socket: %s", strerror(errno)); - return true; - } - if (n_recv == -1 and errno == EAGAIN) { - logger.debug("Socket timeout reached"); - return true; - } - - bool ret = true; - pdu->N_bytes = static_cast(n_recv); - // SCTP notifications handled in callback. - func(std::move(pdu), from, sri, flags); - return ret; - } - -private: - srslog::basic_logger& logger; - callback_t func; -}; - /*************************************************************** * Rx Multisocket Handler **************************************************************/ -rx_multisocket_handler::rx_multisocket_handler(std::string name_, srslog::basic_logger& logger, int thread_prio) : - thread(name_), name(std::move(name_)), logger(logger) +socket_manager::socket_manager() : thread("RXsockets"), socket_manager_itf(srslog::fetch_basic_logger("COMN")) { // register control pipe fd - if (pipe(pipefd) == -1) { - rxSockInfo("Failed to open control pipe"); - return; - } + srsran_assert(pipe(pipefd) != -1, "Failed to open control pipe"); start(thread_prio); } -rx_multisocket_handler::~rx_multisocket_handler() +socket_manager::~socket_manager() { stop(); } -void rx_multisocket_handler::stop() +void socket_manager::stop() { if (running) { // close thread @@ -538,27 +372,7 @@ void rx_multisocket_handler::stop() } } -/** - * Convenience method for read PDUs from socket - */ -bool rx_multisocket_handler::add_socket_pdu_handler(int fd, recvfrom_callback_t pdu_task) -{ - std::unique_ptr task; - task.reset(new srsran::recvfrom_pdu_task(logger, std::move(pdu_task))); - return add_socket_handler(fd, std::move(task)); -} - -/** - * Convenience method for reading PDUs from SCTP socket - */ -bool rx_multisocket_handler::add_socket_sctp_pdu_handler(int fd, sctp_recv_callback_t pdu_task) -{ - srsran::rx_multisocket_handler::task_callback_t task; - task.reset(new srsran::sctp_recvmsg_pdu_task(logger, std::move(pdu_task))); - return add_socket_handler(fd, std::move(task)); -} - -bool rx_multisocket_handler::add_socket_handler(int fd, task_callback_t handler) +bool socket_manager::add_socket_handler(int fd, recv_callback_t handler) { std::lock_guard lock(socket_mutex); if (fd < 0) { @@ -570,7 +384,7 @@ bool rx_multisocket_handler::add_socket_handler(int fd, task_callback_t handler) return false; } - active_sockets.insert(std::pair(fd, std::move(handler))); + active_sockets.insert(std::make_pair(fd, std::move(handler))); // this unlocks the reading thread to add new connections ctrl_cmd_t msg; @@ -585,18 +399,19 @@ bool rx_multisocket_handler::add_socket_handler(int fd, task_callback_t handler) return true; } -bool rx_multisocket_handler::remove_socket(int fd) +bool socket_manager::remove_socket_nonblocking(int fd, bool signal_completion) { std::lock_guard lock(socket_mutex); auto it = active_sockets.find(fd); if (it == active_sockets.end()) { - rxSockError("The socket fd=%d to be removed does not exist", fd); + rxSockWarn("The socket fd=%d to be removed does not exist", fd); return false; } ctrl_cmd_t msg; - msg.cmd = ctrl_cmd_t::cmd_id_t::RM_FD; - msg.new_fd = fd; + msg.cmd = ctrl_cmd_t::cmd_id_t::RM_FD; + msg.new_fd = fd; + msg.signal_rm_complete = signal_completion; if (write(pipefd[1], &msg, sizeof(msg)) != sizeof(msg)) { rxSockError("while writing to control pipe"); return false; @@ -604,8 +419,23 @@ bool rx_multisocket_handler::remove_socket(int fd) return true; } -std::map::iterator -rx_multisocket_handler::remove_socket_unprotected(int fd, fd_set* total_fd_set, int* max_fd) +bool socket_manager::remove_socket(int fd) +{ + bool result = remove_socket_nonblocking(fd, true); + + // block waiting for socket removal + if (result) { + std::unique_lock lock(socket_mutex); + while (std::count(rem_fd_tmp_list.begin(), rem_fd_tmp_list.end(), fd) == 0) { + rem_cvar.wait(lock); + } + rem_fd_tmp_list.erase(std::find(rem_fd_tmp_list.begin(), rem_fd_tmp_list.end(), fd)); + } + return result; +} + +std::map::iterator +socket_manager::remove_socket_unprotected(int fd, fd_set* total_fd_set, int* max_fd) { if (fd < 0) { rxSockError("fd to be removed is not valid"); @@ -620,7 +450,7 @@ rx_multisocket_handler::remove_socket_unprotected(int fd, fd_set* total_fd_set, return it; } -void rx_multisocket_handler::run_thread() +void socket_manager::run_thread() { running = true; fd_set total_fd_set, read_fd_set; @@ -649,13 +479,13 @@ void rx_multisocket_handler::run_thread() // call read callback for all SCTP/TCP/UDP connections for (auto handler_it = active_sockets.begin(); handler_it != active_sockets.end();) { - int fd = handler_it->first; - recv_task* callback = handler_it->second.get(); + int fd = handler_it->first; + recv_callback_t& callback = handler_it->second; if (not FD_ISSET(fd, &read_fd_set)) { ++handler_it; continue; } - bool socket_valid = callback->operator()(fd); + bool socket_valid = callback(fd); if (not socket_valid) { rxSockInfo("The socket fd=%d has been closed by peer", fd); handler_it = remove_socket_unprotected(fd, &total_fd_set, &max_fd); @@ -686,6 +516,10 @@ void rx_multisocket_handler::run_thread() break; case ctrl_cmd_t::cmd_id_t::RM_FD: remove_socket_unprotected(msg.new_fd, &total_fd_set, &max_fd); + if (msg.signal_rm_complete) { + rem_fd_tmp_list.push_back(msg.new_fd); + rem_cvar.notify_one(); + } rxSockDebug("Socket fd=%d has been successfully removed", msg.new_fd); break; default: @@ -695,4 +529,115 @@ void rx_multisocket_handler::run_thread() } } +/*************************************************************** + * Rx Multisocket Task Types + **************************************************************/ + +class sctp_recvmsg_pdu_task +{ +public: + using callback_t = sctp_recv_callback_t; + + explicit sctp_recvmsg_pdu_task(srslog::basic_logger& logger, srsran::task_queue_handle& queue_, callback_t func_) : + logger(logger), queue(queue_), func(std::move(func_)) + {} + + bool operator()(int fd) + { + // inside rx_sockets thread. Read socket + srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (pdu == nullptr) { + logger.error("Unable to allocate byte buffer"); + return true; + } + sockaddr_in from = {}; + socklen_t fromlen = sizeof(from); + sctp_sndrcvinfo sri = {}; + int flags = 0; + ssize_t n_recv = sctp_recvmsg(fd, pdu->msg, pdu->get_tailroom(), (struct sockaddr*)&from, &fromlen, &sri, &flags); + if (n_recv == -1 and errno != EAGAIN) { + logger.error("Error reading from SCTP socket: %s", strerror(errno)); + return true; + } + if (n_recv == -1 and errno == EAGAIN) { + logger.debug("Socket timeout reached"); + return true; + } + + bool ret = true; + pdu->N_bytes = static_cast(n_recv); + + // Defer handling of received packet to provided queue + // SCTP notifications handled in callback. + queue.push(std::bind( + [this, from, sri, flags](srsran::unique_byte_buffer_t& sdu) { func(std::move(sdu), from, sri, flags); }, + std::move(pdu))); + return ret; + } + +private: + srslog::basic_logger& logger; + srsran::task_queue_handle& queue; + callback_t func; +}; + +socket_manager_itf::recv_callback_t +make_sctp_sdu_handler(srslog::basic_logger& logger, srsran::task_queue_handle& queue, sctp_recv_callback_t rx_callback) +{ + return socket_manager_itf::recv_callback_t(sctp_recvmsg_pdu_task(logger, queue, std::move(rx_callback))); +} + +/** + * Description: Functor for the case the received data is + * in the form of unique_byte_buffer, and a recvfrom(...) call is used + */ +class recvfrom_pdu_task +{ +public: + using callback_t = recvfrom_callback_t; + explicit recvfrom_pdu_task(srslog::basic_logger& logger, srsran::task_queue_handle& queue_, callback_t func_) : + logger(logger), queue(queue_), func(std::move(func_)) + {} + + bool operator()(int fd) + { + srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (pdu == nullptr) { + logger.error("Unable to allocate byte buffer"); + return true; + } + sockaddr_in from = {}; + socklen_t fromlen = sizeof(from); + + ssize_t n_recv = recvfrom(fd, pdu->msg, pdu->get_tailroom(), 0, (struct sockaddr*)&from, &fromlen); + if (n_recv == -1 and errno != EAGAIN) { + logger.error("Error reading from socket: %s", strerror(errno)); + return true; + } + if (n_recv == -1 and errno == EAGAIN) { + logger.debug("Socket timeout reached"); + return true; + } + + pdu->N_bytes = static_cast(n_recv); + + // Defer handling of received packet to provided queue + queue.push( + std::bind([this, from](srsran::unique_byte_buffer_t& sdu) { func(std::move(sdu), from); }, std::move(pdu))); + + return true; + } + +private: + srslog::basic_logger& logger; + srsran::task_queue_handle& queue; + callback_t func; +}; + +socket_manager_itf::recv_callback_t +make_sdu_handler(srslog::basic_logger& logger, srsran::task_queue_handle& queue, recvfrom_callback_t rx_callback) +{ + return socket_manager_itf::recv_callback_t(recvfrom_pdu_task(logger, queue, std::move(rx_callback))); +} + } // namespace srsran diff --git a/lib/src/common/thread_pool.cc b/lib/src/common/thread_pool.cc index dbf760fcc..4ac2bea96 100644 --- a/lib/src/common/thread_pool.cc +++ b/lib/src/common/thread_pool.cc @@ -91,21 +91,22 @@ void thread_pool::init_worker(uint32_t id, worker* obj, uint32_t prio, uint32_t void thread_pool::stop() { - mutex_queue.lock(); + { + std::lock_guard lock(mutex_queue); - /* Stop any thread waiting for available worker */ - running = false; + /* Stop any thread waiting for available worker */ + running = false; - /* Now stop all workers */ - for (uint32_t i = 0; i < nof_workers; i++) { - if (workers[i]) { - debug_thread("stop(): stopping %d\n", i); - status[i] = STOP; - cvar_worker[i].notify_all(); - cvar_queue.notify_all(); + /* Now stop all workers */ + for (uint32_t i = 0; i < nof_workers; i++) { + if (workers[i]) { + debug_thread("stop(): stopping %d\n", i); + status[i] = STOP; + cvar_worker[i].notify_all(); + cvar_queue.notify_all(); + } } } - mutex_queue.unlock(); for (uint32_t i = 0; i < nof_workers; i++) { debug_thread("stop(): waiting %d\n", i); @@ -145,6 +146,12 @@ void thread_pool::worker::finished() } } +bool thread_pool::worker::is_stopped() const +{ + std::lock_guard lock(my_parent->mutex_queue); + return my_parent->status[my_id] == STOP; +} + bool thread_pool::find_finished_worker(uint32_t tti, uint32_t* id) { for (uint32_t i = 0; i < nof_workers; i++) { diff --git a/lib/src/mac/mac_rar_pdu_nr.cc b/lib/src/mac/mac_rar_pdu_nr.cc index 9ee27fd5f..de7a0cd9d 100644 --- a/lib/src/mac/mac_rar_pdu_nr.cc +++ b/lib/src/mac/mac_rar_pdu_nr.cc @@ -130,6 +130,11 @@ void mac_rar_subpdu_nr::set_backoff(const uint8_t backoff_indicator_) backoff_indicator = backoff_indicator_; } +uint8_t mac_rar_subpdu_nr::get_backoff() const +{ + return backoff_indicator; +} + std::array mac_rar_subpdu_nr::get_ul_grant() const { return ul_grant; diff --git a/lib/src/mac/mac_sch_pdu_nr.cc b/lib/src/mac/mac_sch_pdu_nr.cc index a59cf8fd6..ff3efc34c 100644 --- a/lib/src/mac/mac_sch_pdu_nr.cc +++ b/lib/src/mac/mac_sch_pdu_nr.cc @@ -23,8 +23,6 @@ namespace srsran { -mac_sch_subpdu_nr::mac_sch_subpdu_nr(mac_sch_pdu_nr* parent_) : parent(parent_) {} - mac_sch_subpdu_nr::nr_lcid_sch_t mac_sch_subpdu_nr::get_type() { if (lcid >= 32) { @@ -94,11 +92,11 @@ void mac_sch_subpdu_nr::set_sdu(const uint32_t lcid_, const uint8_t* payload_, c F_bit = false; sdu_length = sizeof_ce(lcid, parent->is_ulsch()); if (len_ != static_cast(sdu_length)) { - srslog::fetch_basic_logger("MAC").warning("Invalid SDU length of UL-SCH SDU (%d != %d)", len_, sdu_length); + logger->warning("Invalid SDU length of UL-SCH SDU (%d != %d)", len_, sdu_length); } } - if (sdu_length >= 256) { + if (sdu_length >= MAC_SUBHEADER_LEN_THRESHOLD) { F_bit = true; header_length += 1; } @@ -145,6 +143,9 @@ void mac_sch_subpdu_nr::set_sbsr(const lcg_bsr_t bsr_) ce_write_buffer.at(0) = ((bsr_.lcg_id & 0x07) << 5) | (bsr_.buffer_size & 0x1f); } +// Turn a subPDU into a long BSR with variable size +void mac_sch_subpdu_nr::set_lbsr(const std::array bsr_) {} + // Section 6.1.2 uint32_t mac_sch_subpdu_nr::write_subpdu(const uint8_t* start_) { @@ -166,7 +167,7 @@ uint32_t mac_sch_subpdu_nr::write_subpdu(const uint8_t* start_) } else if (header_length == 1) { // do nothing } else { - srslog::fetch_basic_logger("MAC").warning("Error while packing PDU. Unsupported header length (%d)", header_length); + logger->error("Error while packing PDU. Unsupported header length (%d)", header_length); } // copy SDU payload @@ -227,6 +228,16 @@ uint8_t mac_sch_subpdu_nr::get_pcmax() return 0; } +mac_sch_subpdu_nr::ta_t mac_sch_subpdu_nr::get_ta() +{ + ta_t ta = {}; + if (lcid == TA_CMD) { + ta.tag_id = (sdu[0] & 0xc0) >> 6; + ta.ta_command = sdu[0] & 0x3f; + } + return ta; +} + mac_sch_subpdu_nr::lcg_bsr_t mac_sch_subpdu_nr::get_sbsr() { lcg_bsr_t sbsr = {}; @@ -297,7 +308,7 @@ void mac_sch_pdu_nr::unpack(const uint8_t* payload, const uint32_t& len) while (offset < len) { mac_sch_subpdu_nr sch_pdu(this); if (sch_pdu.read_subheader(payload + offset) == SRSRAN_ERROR) { - fprintf(stderr, "Error parsing NR MAC PDU (len=%d, offset=%d)\n", len, offset); + logger.error("Error parsing NR MAC PDU (len=%d, offset=%d)\n", len, offset); return; } offset += sch_pdu.get_total_length(); @@ -310,7 +321,7 @@ void mac_sch_pdu_nr::unpack(const uint8_t* payload, const uint32_t& len) subpdus.push_back(sch_pdu); } if (offset != len) { - fprintf(stderr, "Error parsing NR MAC PDU (len=%d, offset=%d)\n", len, offset); + logger.error("Error parsing NR MAC PDU (len=%d, offset=%d)\n", len, offset); } } @@ -369,7 +380,7 @@ uint32_t mac_sch_pdu_nr::add_sdu(const uint32_t lcid_, const uint8_t* payload_, { int header_size = size_header_sdu(lcid_, len_); if (header_size + len_ > remaining_len) { - printf("Header and SDU exceed space in PDU (%d > %d).\n", header_size + len_, remaining_len); + logger.error("Header and SDU exceed space in PDU (%d + %d > %d)", header_size, len_, remaining_len); return SRSRAN_ERROR; } @@ -399,6 +410,14 @@ uint32_t mac_sch_pdu_nr::add_sbsr_ce(const mac_sch_subpdu_nr::lcg_bsr_t bsr_) return add_sudpdu(ce); } +uint32_t +mac_sch_pdu_nr::add_lbsr_ce(const std::array bsr_) +{ + mac_sch_subpdu_nr ce(this); + ce.set_lbsr(bsr_); + return add_sudpdu(ce); +} + uint32_t mac_sch_pdu_nr::add_sudpdu(mac_sch_subpdu_nr& subpdu) { uint32_t subpdu_len = subpdu.get_total_length(); diff --git a/lib/src/phy/ch_estimation/dmrs_pdcch.c b/lib/src/phy/ch_estimation/dmrs_pdcch.c index e9581ec45..a4bdb6263 100644 --- a/lib/src/phy/ch_estimation/dmrs_pdcch.c +++ b/lib/src/phy/ch_estimation/dmrs_pdcch.c @@ -38,6 +38,7 @@ /// @brief Enables interpolation at CCE frequency bandwidth to avoid interference with adjacent PDCCH DMRS #define DMRS_PDCCH_INTERPOLATE_GROUP 1 +#define DMRS_PDCCH_SMOOTH_FILTER 0 static uint32_t dmrs_pdcch_get_cinit(uint32_t slot_idx, uint32_t symbol_idx, uint32_t n_id) { @@ -370,6 +371,7 @@ int srsran_dmrs_pdcch_estimate(srsran_dmrs_pdcch_estimator_t* q, uint32_t group_size = NOF_PILOTS_X_FREQ_RES / q->coreset.duration; for (uint32_t l = 0; l < q->coreset.duration; l++) { for (uint32_t j = 0; j < group_count; j++) { +#if DMRS_PDCCH_SMOOTH_FILTER cf_t tmp[NOF_PILOTS_X_FREQ_RES]; // Smoothing filter group @@ -377,13 +379,20 @@ int srsran_dmrs_pdcch_estimate(srsran_dmrs_pdcch_estimator_t* q, srsran_interp_linear_offset( &q->interpolator, tmp, &q->ce[SRSRAN_NRE * q->coreset_bw * l + j * group_size * 4], 1, 3); +#else // DMRS_PDCCH_SMOOTH_FILTER + srsran_interp_linear_offset(&q->interpolator, + &q->lse[l][j * group_size], + &q->ce[SRSRAN_NRE * q->coreset_bw * l + j * group_size * 4], + 1, + 3); +#endif // DMRS_PDCCH_SMOOTH_FILTER } } -#else +#else // DMRS_PDCCH_INTERPOLATE_GROUP for (uint32_t l = 0; l < q->coreset.duration; l++) { srsran_interp_linear_offset(&q->interpolator, q->lse[l], &q->ce[SRSRAN_NRE * q->coreset_bw * l], 1, 3); } -#endif +#endif // DMRS_PDCCH_INTERPOLATE_GROUP return SRSRAN_SUCCESS; } diff --git a/lib/src/phy/common/phy_common_nr.c b/lib/src/phy/common/phy_common_nr.c index 799160d09..c874a4a7e 100644 --- a/lib/src/phy/common/phy_common_nr.c +++ b/lib/src/phy/common/phy_common_nr.c @@ -46,6 +46,36 @@ const char* srsran_rnti_type_str(srsran_rnti_type_t rnti_type) return "unknown"; } +const char* srsran_dci_format_nr_string(srsran_dci_format_nr_t format) +{ + switch (format) { + case srsran_dci_format_nr_0_0: + return "0_0"; + case srsran_dci_format_nr_0_1: + return "0_1"; + case srsran_dci_format_nr_1_0: + return "1_0"; + case srsran_dci_format_nr_1_1: + return "1_1"; + case srsran_dci_format_nr_2_0: + return "2_0"; + case srsran_dci_format_nr_2_1: + return "2_1"; + case srsran_dci_format_nr_2_2: + return "2_2"; + case srsran_dci_format_nr_2_3: + return "2_3"; + case srsran_dci_format_nr_rar: + return "RAR"; + case srsran_dci_format_nr_cg: + return "CG"; + default: + case SRSRAN_DCI_FORMAT_NR_COUNT: + break; + } + return "unknown"; +} + uint32_t srsran_coreset_get_bw(const srsran_coreset_t* coreset) { uint32_t prb_count = 0; diff --git a/lib/src/phy/enb/enb_dl_nr.c b/lib/src/phy/enb/enb_dl_nr.c index 2fd939a47..1e6d8877c 100644 --- a/lib/src/phy/enb/enb_dl_nr.c +++ b/lib/src/phy/enb/enb_dl_nr.c @@ -144,15 +144,21 @@ int srsran_enb_dl_nr_set_carrier(srsran_enb_dl_nr_t* q, const srsran_carrier_nr_ return SRSRAN_SUCCESS; } -int srsran_enb_dl_nr_set_coreset(srsran_enb_dl_nr_t* q, const srsran_coreset_t* coreset) +int srsran_enb_dl_nr_set_pdcch_config(srsran_enb_dl_nr_t* q, + const srsran_pdcch_cfg_nr_t* cfg, + const srsran_dci_cfg_nr_t* dci_cfg) { - if (q == NULL || coreset == NULL) { + if (q == NULL || cfg == NULL) { return SRSRAN_ERROR_INVALID_INPUTS; } - q->coreset = *coreset; + q->pdcch_cfg = *cfg; - if (srsran_pdcch_nr_set_carrier(&q->pdcch, &q->carrier, &q->coreset) < SRSRAN_SUCCESS) { + if (srsran_pdcch_nr_set_carrier(&q->pdcch, &q->carrier, &q->pdcch_cfg.coreset[0]) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + if (srsran_dci_nr_set_cfg(&q->dci, dci_cfg) < SRSRAN_SUCCESS) { return SRSRAN_ERROR; } @@ -191,15 +197,27 @@ int srsran_enb_dl_nr_pdcch_put(srsran_enb_dl_nr_t* q, return SRSRAN_ERROR_INVALID_INPUTS; } + if (dci_dl->ctx.coreset_id >= SRSRAN_UE_DL_NR_MAX_NOF_CORESET || + !q->pdcch_cfg.coreset_present[dci_dl->ctx.coreset_id]) { + ERROR("Invalid CORESET ID %d", dci_dl->ctx.coreset_id); + return SRSRAN_ERROR; + } + srsran_coreset_t* coreset = &q->pdcch_cfg.coreset[dci_dl->ctx.coreset_id]; + + if (srsran_pdcch_nr_set_carrier(&q->pdcch, &q->carrier, coreset) < SRSRAN_SUCCESS) { + ERROR("Error setting PDCCH carrier/CORESET"); + return SRSRAN_ERROR; + } + // Put DMRS - if (srsran_dmrs_pdcch_put(&q->carrier, &q->coreset, slot_cfg, &dci_dl->location, q->sf_symbols[0]) < SRSRAN_SUCCESS) { + if (srsran_dmrs_pdcch_put(&q->carrier, coreset, slot_cfg, &dci_dl->ctx.location, q->sf_symbols[0]) < SRSRAN_SUCCESS) { ERROR("Error putting PDCCH DMRS"); return SRSRAN_ERROR; } // Pack DCI srsran_dci_msg_nr_t dci_msg = {}; - if (srsran_dci_nr_pack(&q->carrier, &q->coreset, dci_dl, &dci_msg) < SRSRAN_SUCCESS) { + if (srsran_dci_nr_dl_pack(&q->dci, dci_dl, &dci_msg) < SRSRAN_SUCCESS) { ERROR("Error packing DL DCI"); return SRSRAN_ERROR; } @@ -210,7 +228,7 @@ int srsran_enb_dl_nr_pdcch_put(srsran_enb_dl_nr_t* q, return SRSRAN_ERROR; } - INFO("DCI DL NR: L=%d; ncce=%d;", dci_dl->location.L, dci_dl->location.ncce); + INFO("DCI DL NR: L=%d; ncce=%d;", dci_dl->ctx.location.L, dci_dl->ctx.location.ncce); return SRSRAN_SUCCESS; } diff --git a/lib/src/phy/fec/ldpc/ldpc_dec_c_avx2long.c b/lib/src/phy/fec/ldpc/ldpc_dec_c_avx2long.c index d7ea510f5..b2bb4f75c 100644 --- a/lib/src/phy/fec/ldpc/ldpc_dec_c_avx2long.c +++ b/lib/src/phy/fec/ldpc/ldpc_dec_c_avx2long.c @@ -137,93 +137,58 @@ void* create_ldpc_dec_c_avx2long(uint8_t bgN, uint8_t bgM, uint16_t ls, float sc uint8_t bgK = bgN - bgM; uint16_t hrr = bgK + 4; - if ((vp = srsran_vec_malloc(sizeof(struct ldpc_regs_c_avx2long))) == NULL) { + if ((vp = SRSRAN_MEM_ALLOC(struct ldpc_regs_c_avx2long, 1)) == NULL) { return NULL; } + SRSRAN_MEM_ZERO(vp, struct ldpc_regs_c_avx2long, 1); // compute number of subnodes int left_out = ls % SRSRAN_AVX2_B_SIZE; int n_subnodes = ls / SRSRAN_AVX2_B_SIZE + (left_out > 0); - if ((vp->soft_bits = srsran_vec_malloc(bgN * n_subnodes * sizeof(bg_node_t))) == NULL) { - free(vp); + if ((vp->soft_bits = SRSRAN_MEM_ALLOC(bg_node_t, bgN * n_subnodes)) == NULL) { + delete_ldpc_dec_c_avx2long(vp); return NULL; } - if ((vp->check_to_var = srsran_vec_malloc((hrr + 1) * bgM * n_subnodes * sizeof(__m256i))) == NULL) { - free(vp->soft_bits); - free(vp); + if ((vp->check_to_var = SRSRAN_MEM_ALLOC(__m256i, (hrr + 1) * bgM * n_subnodes)) == NULL) { + delete_ldpc_dec_c_avx2long(vp); return NULL; } - if ((vp->var_to_check_to_free = srsran_vec_malloc(((hrr + 1) * n_subnodes + 2) * sizeof(__m256i))) == NULL) { - free(vp->check_to_var); - free(vp->soft_bits); - free(vp); + if ((vp->var_to_check_to_free = SRSRAN_MEM_ALLOC(__m256i, (hrr + 1) * n_subnodes + 2)) == NULL) { + delete_ldpc_dec_c_avx2long(vp); return NULL; } vp->var_to_check = &vp->var_to_check_to_free[1]; - if ((vp->minp_v2c_epi8 = srsran_vec_malloc(n_subnodes * sizeof(__m256i))) == NULL) { - free(vp->var_to_check_to_free); - free(vp->check_to_var); - free(vp->soft_bits); - free(vp); + if ((vp->minp_v2c_epi8 = SRSRAN_MEM_ALLOC(__m256i, n_subnodes)) == NULL) { + delete_ldpc_dec_c_avx2long(vp); return NULL; } - if ((vp->mins_v2c_epi8 = srsran_vec_malloc(n_subnodes * sizeof(__m256i))) == NULL) { - free(vp->minp_v2c_epi8); - free(vp->var_to_check_to_free); - free(vp->check_to_var); - free(vp->soft_bits); - free(vp); + if ((vp->mins_v2c_epi8 = SRSRAN_MEM_ALLOC(__m256i, n_subnodes)) == NULL) { + delete_ldpc_dec_c_avx2long(vp); return NULL; } - if ((vp->prod_v2c_epi8 = srsran_vec_malloc(n_subnodes * sizeof(__m256i))) == NULL) { - free(vp->mins_v2c_epi8); - free(vp->minp_v2c_epi8); - free(vp->var_to_check_to_free); - free(vp->check_to_var); - free(vp->soft_bits); - free(vp); + if ((vp->prod_v2c_epi8 = SRSRAN_MEM_ALLOC(__m256i, n_subnodes)) == NULL) { + delete_ldpc_dec_c_avx2long(vp); return NULL; } - if ((vp->min_ix_epi8 = srsran_vec_malloc(n_subnodes * sizeof(__m256i))) == NULL) { - free(vp->prod_v2c_epi8); - free(vp->mins_v2c_epi8); - free(vp->minp_v2c_epi8); - free(vp->var_to_check_to_free); - free(vp->check_to_var); - free(vp->soft_bits); - free(vp); + if ((vp->min_ix_epi8 = SRSRAN_MEM_ALLOC(__m256i, n_subnodes)) == NULL) { + delete_ldpc_dec_c_avx2long(vp); return NULL; } - if ((vp->rotated_v2c = srsran_vec_malloc((hrr + 1) * n_subnodes * sizeof(__m256i))) == NULL) { - free(vp->min_ix_epi8); - free(vp->prod_v2c_epi8); - free(vp->mins_v2c_epi8); - free(vp->minp_v2c_epi8); - free(vp->var_to_check_to_free); - free(vp->check_to_var); - free(vp->soft_bits); - free(vp); + if ((vp->rotated_v2c = SRSRAN_MEM_ALLOC(__m256i, (hrr + 1) * n_subnodes)) == NULL) { + delete_ldpc_dec_c_avx2long(vp); return NULL; } - if ((vp->this_c2v_epi8_to_free = srsran_vec_malloc((n_subnodes + 2) * sizeof(__m256i))) == NULL) { - free(vp->rotated_v2c); - free(vp->min_ix_epi8); - free(vp->prod_v2c_epi8); - free(vp->mins_v2c_epi8); - free(vp->minp_v2c_epi8); - free(vp->var_to_check_to_free); - free(vp->check_to_var); - free(vp->soft_bits); - free(vp); + if ((vp->this_c2v_epi8_to_free = SRSRAN_MEM_ALLOC(__m256i, n_subnodes + 2)) == NULL) { + delete_ldpc_dec_c_avx2long(vp); return NULL; } vp->this_c2v_epi8 = @@ -246,18 +211,37 @@ void delete_ldpc_dec_c_avx2long(void* p) { struct ldpc_regs_c_avx2long* vp = p; - if (vp != NULL) { - free(vp->this_c2v_epi8_to_free); - free(vp->rotated_v2c); - free(vp->min_ix_epi8); - free(vp->prod_v2c_epi8); - free(vp->mins_v2c_epi8); - free(vp->minp_v2c_epi8); - free(vp->var_to_check_to_free); - free(vp->check_to_var); - free(vp->soft_bits); - free(vp); + if (vp == NULL) { + return; } + if (vp->this_c2v_epi8_to_free) { + free(vp->this_c2v_epi8_to_free); + } + if (vp->rotated_v2c != NULL) { + free(vp->rotated_v2c); + } + if (vp->min_ix_epi8 != NULL) { + free(vp->min_ix_epi8); + } + if (vp->prod_v2c_epi8 != NULL) { + free(vp->prod_v2c_epi8); + } + if (vp->mins_v2c_epi8 != NULL) { + free(vp->mins_v2c_epi8); + } + if (vp->minp_v2c_epi8 != NULL) { + free(vp->minp_v2c_epi8); + } + if (vp->var_to_check_to_free != NULL) { + free(vp->var_to_check_to_free); + } + if (vp->check_to_var != NULL) { + free(vp->check_to_var); + } + if (vp->soft_bits != NULL) { + free(vp->soft_bits); + } + free(vp); } int init_ldpc_dec_c_avx2long(void* p, const int8_t* llrs, uint16_t ls) @@ -281,11 +265,14 @@ int init_ldpc_dec_c_avx2long(void* p, const int8_t* llrs, uint16_t ls) vp->soft_bits[i * vp->n_subnodes + j].c[k] = llrs[(i - 2) * ls + j * SRSRAN_AVX2_B_SIZE + k]; } } - bzero(&(vp->soft_bits[i * vp->n_subnodes + j - 1].c[k]), (SRSRAN_AVX2_B_SIZE - k) * sizeof(int8_t)); + srsran_vec_i8_zero(&(vp->soft_bits[i * vp->n_subnodes + j - 1].c[k]), SRSRAN_AVX2_B_SIZE - k); } - bzero(vp->check_to_var, (vp->hrr + 1) * vp->bgM * vp->n_subnodes * sizeof(__m256i)); - bzero(vp->var_to_check, (vp->hrr + 1) * vp->n_subnodes * sizeof(__m256i)); + SRSRAN_MEM_ZERO(vp->check_to_var, __m256i, (vp->hrr + 1) * vp->bgM * vp->n_subnodes); + SRSRAN_MEM_ZERO(vp->var_to_check, __m256i, (vp->hrr + 1) * vp->n_subnodes); + SRSRAN_MEM_ZERO(vp->this_c2v_epi8_to_free, __m256i, vp->n_subnodes + 2); + SRSRAN_MEM_ZERO(vp->min_ix_epi8, __m256i, vp->n_subnodes); + SRSRAN_MEM_ZERO(vp->var_to_check_to_free, __m256i, (vp->hrr + 1) * vp->n_subnodes + 2); return 0; } diff --git a/lib/src/phy/fec/ldpc/ldpc_decoder.c b/lib/src/phy/fec/ldpc/ldpc_decoder.c index 2651d7433..55454a472 100644 --- a/lib/src/phy/fec/ldpc/ldpc_decoder.c +++ b/lib/src/phy/fec/ldpc/ldpc_decoder.c @@ -1037,15 +1037,10 @@ int srsran_ldpc_decoder_decode_s(srsran_ldpc_decoder_t* q, return q->decode_s(q, llrs, message, cdwd_rm_length); } -int srsran_ldpc_decoder_decode_c(srsran_ldpc_decoder_t* q, const int8_t* llrs, uint8_t* message) -{ - return q->decode_c(q, llrs, message, q->liftN - 2 * q->ls); -} - -int srsran_ldpc_decoder_decode_rm_c(srsran_ldpc_decoder_t* q, - const int8_t* llrs, - uint8_t* message, - uint32_t cdwd_rm_length) +int srsran_ldpc_decoder_decode_c(srsran_ldpc_decoder_t* q, + const int8_t* llrs, + uint8_t* message, + uint32_t cdwd_rm_length) { return q->decode_c(q, llrs, message, cdwd_rm_length); } diff --git a/lib/src/phy/fec/ldpc/ldpc_rm.c b/lib/src/phy/fec/ldpc/ldpc_rm.c index 7b26f1f9f..ca69738fd 100644 --- a/lib/src/phy/fec/ldpc/ldpc_rm.c +++ b/lib/src/phy/fec/ldpc/ldpc_rm.c @@ -695,5 +695,6 @@ int srsran_ldpc_rm_rx_c(srsran_ldpc_rm_t* q, bit_selection_rm_rx_c(tmp_rm_symbol, q->E, output, indices, ini_exclude, end_exclude, q->k0, q->Ncb); } - return 0; + // Return the number of useful LLR + return (int)SRSRAN_MIN(q->k0 + q->E, q->Ncb); } diff --git a/lib/src/phy/fec/ldpc/test/ldpc_chain_test.c b/lib/src/phy/fec/ldpc/test/ldpc_chain_test.c index eb64dbcb5..2e1a620bd 100644 --- a/lib/src/phy/fec/ldpc/test/ldpc_chain_test.c +++ b/lib/src/phy/fec/ldpc/test/ldpc_chain_test.c @@ -431,8 +431,7 @@ int main(int argc, char** argv) // Recover messages gettimeofday(&t[1], NULL); for (j = 0; j < batch_size; j++) { - srsran_ldpc_decoder_decode_rm_c( - &decoder_c, symbols_c + j * finalN, messages_sim_c + j * finalK, n_useful_symbols); + srsran_ldpc_decoder_decode_c(&decoder_c, symbols_c + j * finalN, messages_sim_c + j * finalK, n_useful_symbols); } gettimeofday(&t[2], NULL); get_time_interval(t); @@ -453,7 +452,7 @@ int main(int argc, char** argv) // Recover messages gettimeofday(&t[1], NULL); for (j = 0; j < batch_size; j++) { - srsran_ldpc_decoder_decode_rm_c( + srsran_ldpc_decoder_decode_c( &decoder_c_flood, symbols_c + j * finalN, messages_sim_c_flood + j * finalK, n_useful_symbols); } gettimeofday(&t[2], NULL); @@ -476,7 +475,7 @@ int main(int argc, char** argv) // Recover messages gettimeofday(&t[1], NULL); for (j = 0; j < batch_size; j++) { - srsran_ldpc_decoder_decode_rm_c( + srsran_ldpc_decoder_decode_c( &decoder_avx, symbols_c + j * finalN, messages_sim_avx + j * finalK, n_useful_symbols); } gettimeofday(&t[2], NULL); @@ -498,7 +497,7 @@ int main(int argc, char** argv) // Recover messages gettimeofday(&t[1], NULL); for (j = 0; j < batch_size; j++) { - srsran_ldpc_decoder_decode_rm_c( + srsran_ldpc_decoder_decode_c( &decoder_avx_flood, symbols_c + j * finalN, messages_sim_avx_flood + j * finalK, n_useful_symbols); } gettimeofday(&t[2], NULL); @@ -522,7 +521,7 @@ int main(int argc, char** argv) // Recover messages gettimeofday(&t[1], NULL); for (j = 0; j < batch_size; j++) { - srsran_ldpc_decoder_decode_rm_c( + srsran_ldpc_decoder_decode_c( &decoder_avx512, symbols_c + j * finalN, messages_sim_avx512 + j * finalK, n_useful_symbols); } gettimeofday(&t[2], NULL); @@ -543,7 +542,7 @@ int main(int argc, char** argv) // Recover messages gettimeofday(&t[1], NULL); for (j = 0; j < batch_size; j++) { - srsran_ldpc_decoder_decode_rm_c( + srsran_ldpc_decoder_decode_c( &decoder_avx512_flood, symbols_c + j * finalN, messages_sim_avx512_flood + j * finalK, n_useful_symbols); } gettimeofday(&t[2], NULL); diff --git a/lib/src/phy/fec/ldpc/test/ldpc_dec_avx2_test.c b/lib/src/phy/fec/ldpc/test/ldpc_dec_avx2_test.c index ba404d2bd..b3c2e1a56 100644 --- a/lib/src/phy/fec/ldpc/test/ldpc_dec_avx2_test.c +++ b/lib/src/phy/fec/ldpc/test/ldpc_dec_avx2_test.c @@ -216,7 +216,7 @@ int main(int argc, char** argv) printf(" codeword %d\n", j); gettimeofday(&t[1], NULL); for (l = 0; l < nof_reps; l++) { - srsran_ldpc_decoder_decode_rm_c(&decoder, symbols + j * finalN, messages_sim + j * finalK, finalN); + srsran_ldpc_decoder_decode_c(&decoder, symbols + j * finalN, messages_sim + j * finalK, finalN); } gettimeofday(&t[2], NULL); diff --git a/lib/src/phy/fec/ldpc/test/ldpc_dec_avx512_test.c b/lib/src/phy/fec/ldpc/test/ldpc_dec_avx512_test.c index 10a971062..c13086f03 100644 --- a/lib/src/phy/fec/ldpc/test/ldpc_dec_avx512_test.c +++ b/lib/src/phy/fec/ldpc/test/ldpc_dec_avx512_test.c @@ -215,7 +215,7 @@ int main(int argc, char** argv) printf(" codeword %d\n", j); gettimeofday(&t[1], NULL); for (l = 0; l < nof_reps; l++) { - srsran_ldpc_decoder_decode_rm_c(&decoder, symbols + j * finalN, messages_sim + j * finalK, finalN); + srsran_ldpc_decoder_decode_c(&decoder, symbols + j * finalN, messages_sim + j * finalK, finalN); } gettimeofday(&t[2], NULL); diff --git a/lib/src/phy/fec/ldpc/test/ldpc_dec_c_test.c b/lib/src/phy/fec/ldpc/test/ldpc_dec_c_test.c index 470c943a3..df011aeb2 100644 --- a/lib/src/phy/fec/ldpc/test/ldpc_dec_c_test.c +++ b/lib/src/phy/fec/ldpc/test/ldpc_dec_c_test.c @@ -206,7 +206,7 @@ int main(int argc, char** argv) gettimeofday(&t[1], NULL); for (j = 0; j < NOF_MESSAGES; j++) { printf(" codeword %d\n", j); - srsran_ldpc_decoder_decode_rm_c(&decoder, symbols + j * finalN, messages_sim + j * finalK, finalN); + srsran_ldpc_decoder_decode_c(&decoder, symbols + j * finalN, messages_sim + j * finalK, finalN); } gettimeofday(&t[2], NULL); get_time_interval(t); diff --git a/lib/src/phy/fec/ldpc/test/ldpc_rm_chain_test.c b/lib/src/phy/fec/ldpc/test/ldpc_rm_chain_test.c index 3d79cd3e5..e625acb6b 100644 --- a/lib/src/phy/fec/ldpc/test/ldpc_rm_chain_test.c +++ b/lib/src/phy/fec/ldpc/test/ldpc_rm_chain_test.c @@ -555,7 +555,7 @@ int main(int argc, char** argv) lift_size, rv, mod_type, - Nref)) { + Nref) < 0) { exit(-1); } } @@ -563,7 +563,7 @@ int main(int argc, char** argv) // Recover messages gettimeofday(&t[1], NULL); for (j = 0; j < batch_size; j++) { - srsran_ldpc_decoder_decode_rm_c( + srsran_ldpc_decoder_decode_c( &decoder_c, symbols_c + j * finalN, messages_sim_c + j * finalK, n_useful_symbols_dec); } gettimeofday(&t[2], NULL); @@ -585,7 +585,7 @@ int main(int argc, char** argv) // Recover messages gettimeofday(&t[1], NULL); for (j = 0; j < batch_size; j++) { - srsran_ldpc_decoder_decode_rm_c( + srsran_ldpc_decoder_decode_c( &decoder_c_flood, symbols_c + j * finalN, messages_sim_c_flood + j * finalK, n_useful_symbols_dec); } gettimeofday(&t[2], NULL); @@ -608,7 +608,7 @@ int main(int argc, char** argv) // Recover messages gettimeofday(&t[1], NULL); for (j = 0; j < batch_size; j++) { - srsran_ldpc_decoder_decode_rm_c( + srsran_ldpc_decoder_decode_c( &decoder_avx, symbols_c + j * finalN, messages_sim_avx + j * finalK, n_useful_symbols_dec); } gettimeofday(&t[2], NULL); @@ -630,7 +630,7 @@ int main(int argc, char** argv) // Recover messages gettimeofday(&t[1], NULL); for (j = 0; j < batch_size; j++) { - srsran_ldpc_decoder_decode_rm_c( + srsran_ldpc_decoder_decode_c( &decoder_avx_flood, symbols_c + j * finalN, messages_sim_avx_flood + j * finalK, n_useful_symbols_dec); } gettimeofday(&t[2], NULL); @@ -654,8 +654,7 @@ int main(int argc, char** argv) // Recover messages gettimeofday(&t[1], NULL); for (j = 0; j < batch_size; j++) { - srsran_ldpc_decoder_decode_rm_c( - &decoder_avx512, symbols_c + j * finalN, messages_sim_avx512 + j * finalK, finalN); + srsran_ldpc_decoder_decode_c(&decoder_avx512, symbols_c + j * finalN, messages_sim_avx512 + j * finalK, finalN); } gettimeofday(&t[2], NULL); get_time_interval(t); @@ -676,7 +675,7 @@ int main(int argc, char** argv) // Recover messages gettimeofday(&t[1], NULL); for (j = 0; j < batch_size; j++) { - srsran_ldpc_decoder_decode_rm_c( + srsran_ldpc_decoder_decode_c( &decoder_avx512_flood, symbols_c + j * finalN, messages_sim_avx512_flood + j * finalK, finalN); } gettimeofday(&t[2], NULL); diff --git a/lib/src/phy/fec/ldpc/test/ldpc_rm_test.c b/lib/src/phy/fec/ldpc/test/ldpc_rm_test.c index ef48f43a7..a5c875896 100644 --- a/lib/src/phy/fec/ldpc/test/ldpc_rm_test.c +++ b/lib/src/phy/fec/ldpc/test/ldpc_rm_test.c @@ -265,7 +265,8 @@ int main(int argc, char** argv) exit(-1); } if (srsran_ldpc_rm_rx_c( - &rm_rx_c, rm_symbols_c + r * E, unrm_symbols_c + r * N, E, F, base_graph, lift_size, rv, mod_type, Nref)) { + &rm_rx_c, rm_symbols_c + r * E, unrm_symbols_c + r * N, E, F, base_graph, lift_size, rv, mod_type, Nref) < + 0) { exit(-1); } @@ -278,7 +279,6 @@ int main(int argc, char** argv) ((unrm_symbols[i + r * N] < 0) && (codewords[i + r * N]))) { // any of these cases are ok } else { - error = -1; break; } diff --git a/lib/src/phy/fec/turbo/turbodecoder.c b/lib/src/phy/fec/turbo/turbodecoder.c index a84cf992f..554e3c495 100644 --- a/lib/src/phy/fec/turbo/turbodecoder.c +++ b/lib/src/phy/fec/turbo/turbodecoder.c @@ -99,7 +99,7 @@ srsran_tdec_8bit_impl_t avx8_win_impl = {tdec_winavx8_init, #ifdef HAVE_NEON #define WINIMP_IS_NEON16 -#include "srsran/phy/fec/turbodecoder_win.h" +#include "srsran/phy/fec/turbo/turbodecoder_win.h" #undef WINIMP_IS_NEON16 srsran_tdec_16bit_impl_t arm16_win_impl = {tdec_winarm16_init, diff --git a/lib/src/phy/phch/csi.c b/lib/src/phy/phch/csi.c index 3d1ee9a46..f022f4019 100644 --- a/lib/src/phy/phch/csi.c +++ b/lib/src/phy/phch/csi.c @@ -29,7 +29,7 @@ /// Implements SNRI to CQI conversion uint32_t csi_snri_db_to_cqi(srsran_csi_cqi_table_t table, float snri_db) { - return 6; + return 15; } // Implements CSI report triggers @@ -324,4 +324,4 @@ uint32_t srsran_csi_str(const srsran_csi_report_cfg_t* report_cfg, } } return len; -} \ No newline at end of file +} diff --git a/lib/src/phy/phch/dci_nr.c b/lib/src/phy/phch/dci_nr.c index 731afc7f7..5db6c68ef 100644 --- a/lib/src/phy/phch/dci_nr.c +++ b/lib/src/phy/phch/dci_nr.c @@ -22,103 +22,122 @@ #include "srsran/phy/phch/dci_nr.h" #include "srsran/phy/utils/bit.h" #include "srsran/phy/utils/debug.h" +#include "srsran/phy/utils/vector.h" -static int dci_nr_format_0_0_freq_resource_size(const srsran_carrier_nr_t* carrier) +/** + * Defines minimum size according to TS 38.212 section 7.3.1 DCI Formats: + * If the number of information bits in a DCI format is less than 12 bits, zeros shall be appended to the DCI format + * until the payload size equals 12. + */ +#define DCI_NR_MIN_SIZE 12 + +#define CEIL_LOG2(N) (((N) == 0) ? 0 : ceil(log2((double)(N)))) + +static uint32_t dci_nr_freq_resource_size_type1(uint32_t N) { - if (carrier == NULL) { - return SRSRAN_ERROR; + if (N == 0) { + return 0; } - return (int)ceil(log2(carrier->nof_prb * (carrier->nof_prb + 1) / 2.0)); + return (int)CEIL_LOG2(N * (N + 1) / 2.0); } -static int dci_nr_format_1_0_freq_resource_size(const srsran_carrier_nr_t* carrier, - const srsran_coreset_t* coreset0, - srsran_rnti_type_t rnti_type) +static uint32_t dci_nr_freq_resource_size(srsran_resource_alloc_t alloc_type, uint32_t N_RBG, uint32_t N_BWP_RB) { - if (carrier == NULL) { - return SRSRAN_ERROR; - } - - uint32_t N_DL_BWP_RB = carrier->nof_prb; - if (rnti_type == srsran_rnti_type_ra && coreset0 != NULL) { - N_DL_BWP_RB = srsran_coreset_get_bw(coreset0); - } else if (rnti_type == srsran_rnti_type_p || rnti_type == srsran_rnti_type_si) { - if (coreset0 == NULL) { - return SRSRAN_ERROR; - } - N_DL_BWP_RB = srsran_coreset_get_bw(coreset0); - } - - return (int)ceil(log2(N_DL_BWP_RB * (N_DL_BWP_RB + 1) / 2.0)); -} - -bool srsran_dci_nr_format_1_0_valid(const srsran_dci_msg_nr_t* dci) -{ - // Check pointer - if (dci == NULL) { - return false; - } - - // Wrong format - if (dci->format != srsran_dci_format_nr_1_0) { - return false; - } - - // The format bit is only present for these RNTI - if (dci->rnti_type == srsran_rnti_type_c || dci->rnti_type == srsran_rnti_type_tc) { - return dci->payload[0] == 1; - } - - // Otherwise, the message might be format 1_0 - return true; -} - -int srsran_dci_nr_pack(const srsran_carrier_nr_t* carrier, - const srsran_coreset_t* coreset, - const srsran_dci_dl_nr_t* dci, - srsran_dci_msg_nr_t* msg) -{ - // Copy DCI MSG fields - msg->location = dci->location; - msg->search_space = dci->search_space; - msg->coreset_id = dci->coreset_id; - msg->rnti_type = dci->rnti_type; - msg->rnti = dci->rnti; - msg->format = dci->format; - - // Pack DCI - switch (msg->format) { - case srsran_dci_format_nr_1_0: - if (srsran_dci_nr_format_1_0_pack(carrier, coreset, dci, msg) < SRSRAN_SUCCESS) { - ERROR("Error packing DL DCI"); - return SRSRAN_ERROR; - } - break; + // Frequency domain resource assignment + switch (alloc_type) { + case srsran_resource_alloc_type0: + return N_RBG; + case srsran_resource_alloc_type1: + return dci_nr_freq_resource_size_type1(N_BWP_RB); + case srsran_resource_alloc_dynamic: + return SRSRAN_MAX(N_RBG, dci_nr_freq_resource_size_type1(N_BWP_RB)) + 1; default: - ERROR("Unsupported DCI format %d", msg->format); - return SRSRAN_ERROR; + ERROR("Unhandled case"); } - return SRSRAN_SUCCESS; + return 0; } -int srsran_dci_nr_format_0_0_pack(const srsran_carrier_nr_t* carrier, - const srsran_coreset_t* coreset0, - const srsran_dci_ul_nr_t* dci, - srsran_dci_msg_nr_t* msg) +static uint32_t dci_nr_bwp_id_size(uint32_t N_BWP_RRC) { - uint32_t trim = 0; // hard-coded bit trimming - bool enable_hopping = false; // hard-coded PUSCH hopping - uint32_t padding = 8; // Hard-coded padding - bool supplementary_uplink = false; // Hard-coded supplementary Uplink - uint8_t* y = msg->payload; - srsran_rnti_type_t rnti_type = msg->rnti_type; - - if (carrier == NULL) { - return SRSRAN_ERROR; + uint32_t N_BWP = N_BWP_RRC; + if (N_BWP_RRC <= 3) { + N_BWP = N_BWP_RRC + 1; } + return (int)CEIL_LOG2(N_BWP); +} + +static uint32_t dci_nr_time_res_size(uint32_t nof_time_res) +{ + if (nof_time_res == 0) { + // 4 bits are necessary for PUSCH default time resource assigment (TS 38.214 Table 6.1.2.1.1-2) + return 4; + } + return (uint32_t)CEIL_LOG2(nof_time_res); +} + +// Determines DCI format 0_0 according to TS 38.212 clause 7.3.1.1.1 +static uint32_t dci_nr_format_0_0_sizeof(uint32_t N_UL_BWP_RB, const srsran_dci_cfg_nr_t* cfg) +{ + uint32_t count = 0; + + // Identifier for DCI formats – 1 bits + count++; + + // For PUSCH hopping with resource allocation type 1 N UL_hop MSB bits are used to indicate the frequency offset + uint32_t N_UL_hop = (cfg->enable_hopping) ? ((N_UL_BWP_RB < 50) ? 1 : 2) : 0; + count += N_UL_hop; + + // Frequency domain resource assignment + uint32_t N = dci_nr_freq_resource_size_type1(N_UL_BWP_RB); + if (N < N_UL_hop) { + return 0; + } + count += N - N_UL_hop; + + // Time domain resource assignment – 4 bits + count += 4; + + // Frequency hopping flag – 1 bit + count += 1; + + // Modulation and coding scheme – 5 bits + count += 5; + + // New data indicator – 1 bit + count += 1; + + // Redundancy version – 2 bits + count += 2; + + // HARQ process number – 4 bits + count += 4; + + // TPC command for scheduled PUSCH – 2 bits + count += 2; + + // UL/SUL indicator – 1 bit for UEs configured with supplementaryUplink in ServingCellConfig, otherwise 0 + if (cfg->enable_sul) { + count++; + } + + return count; +} + +static int dci_nr_format_0_0_pack(const srsran_dci_nr_t* q, const srsran_dci_ul_nr_t* dci, srsran_dci_msg_nr_t* msg) +{ + bool is_common_ss = SRSRAN_SEARCH_SPACE_IS_COMMON(msg->ctx.ss_type); + uint32_t trunc = is_common_ss ? q->dci_0_0_common_trunc : 0; // hard-coded bit truncation + uint32_t padding = is_common_ss ? q->dci_0_0_common_padd : q->dci_0_0_ue_padd; // Hard-coded padding + uint8_t* y = msg->payload; + srsran_rnti_type_t rnti_type = msg->ctx.rnti_type; + uint32_t N_UL_BWP_RB = is_common_ss ? q->cfg.bwp_ul_initial_bw : q->cfg.bwp_ul_active_bw; + + // 1st of all, copy DCI context + msg->ctx = dci->ctx; + // Check RNTI type if (rnti_type != srsran_rnti_type_c && rnti_type != srsran_rnti_type_cs && rnti_type != srsran_rnti_type_mcs_c) { return SRSRAN_ERROR; @@ -129,15 +148,15 @@ int srsran_dci_nr_format_0_0_pack(const srsran_carrier_nr_t* carrier, y++; // For PUSCH hopping with resource allocation type 1 N UL_hop MSB bits are used to indicate the frequency offset - int N_UL_hop = (enable_hopping) ? ((carrier->nof_prb < 50) ? 1 : 2) : 0; + int N_UL_hop = (q->cfg.enable_hopping) ? (N_UL_BWP_RB ? 1 : 2) : 0; srsran_bit_unpack(dci->frequency_offset, &y, N_UL_hop); // Frequency domain resource assignment - int N = dci_nr_format_0_0_freq_resource_size(carrier); - if (N < SRSRAN_SUCCESS || N - N_UL_hop <= 0) { + uint32_t N = dci_nr_freq_resource_size_type1(N_UL_BWP_RB); + if (N <= N_UL_hop) { return SRSRAN_ERROR; } - srsran_bit_unpack(dci->freq_domain_assigment, &y, N - N_UL_hop - trim); + srsran_bit_unpack(dci->freq_domain_assigment, &y, (int)(N - N_UL_hop - trunc)); // Time domain resource assignment – 4 bits srsran_bit_unpack(dci->time_domain_assigment, &y, 4); @@ -166,11 +185,11 @@ int srsran_dci_nr_format_0_0_pack(const srsran_carrier_nr_t* carrier, } // UL/SUL indicator – 1 bit for UEs configured with supplementaryUplink in ServingCellConfig, otherwise 0 - if (supplementary_uplink) { + if (q->cfg.enable_sul) { *(y++) = 0; } - msg->nof_bits = srsran_dci_nr_format_0_0_sizeof(carrier, coreset0, rnti_type); + msg->nof_bits = srsran_dci_nr_size(q, msg->ctx.ss_type, srsran_dci_format_nr_0_0); if (msg->nof_bits != y - msg->payload) { ERROR("Unpacked bits read (%d) do NOT match payload size (%d)", msg->nof_bits, (int)(y - msg->payload)); return SRSRAN_ERROR; @@ -179,29 +198,15 @@ int srsran_dci_nr_format_0_0_pack(const srsran_carrier_nr_t* carrier, return SRSRAN_SUCCESS; } -int srsran_dci_nr_format_0_0_unpack(const srsran_carrier_nr_t* carrier, - const srsran_coreset_t* coreset, - srsran_dci_msg_nr_t* msg, - srsran_dci_ul_nr_t* dci) +static int dci_nr_format_0_0_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_t* msg, srsran_dci_ul_nr_t* dci) { - uint32_t trim = 0; // hard-coded bit trimming - bool enable_hopping = false; // hard-coded PUSCH hopping - uint32_t padding = 8; // Hard-coded padding - bool supplementary_uplink = false; // Hard-coded supplementary Uplink - uint8_t* y = msg->payload; - srsran_rnti_type_t rnti_type = msg->rnti_type; - - // Copy DCI MSG fields - dci->location = msg->location; - dci->search_space = msg->search_space; - dci->coreset_id = msg->coreset_id; - dci->rnti_type = msg->rnti_type; - dci->rnti = msg->rnti; - dci->format = msg->format; - - if (carrier == NULL) { - return SRSRAN_ERROR; - } + uint32_t trim = 0; // hard-coded bit trimming + bool enable_hopping = false; // hard-coded PUSCH hopping + uint32_t padding = 8; // Hard-coded padding + uint8_t* y = msg->payload; + srsran_rnti_type_t rnti_type = msg->ctx.rnti_type; + srsran_search_space_type_t ss_type = msg->ctx.ss_type; + uint32_t N_UL_BWP_RB = SRSRAN_SEARCH_SPACE_IS_COMMON(ss_type) ? q->cfg.bwp_ul_initial_bw : q->cfg.bwp_ul_active_bw; // Check RNTI type if (rnti_type != srsran_rnti_type_c && rnti_type != srsran_rnti_type_cs && rnti_type != srsran_rnti_type_mcs_c) { @@ -209,10 +214,9 @@ int srsran_dci_nr_format_0_0_unpack(const srsran_carrier_nr_t* carrier, return SRSRAN_ERROR; } - if (msg->nof_bits != srsran_dci_nr_format_0_0_sizeof(carrier, coreset, rnti_type)) { - ERROR("Invalid number of bits %d, expected %d", - msg->nof_bits, - srsran_dci_nr_format_0_0_sizeof(carrier, coreset, rnti_type)); + uint32_t nof_bits = srsran_dci_nr_size(q, ss_type, srsran_dci_format_nr_0_0); + if (msg->nof_bits != nof_bits) { + ERROR("Invalid number of bits %d, expected %d", msg->nof_bits, nof_bits); return SRSRAN_ERROR; } @@ -223,12 +227,12 @@ int srsran_dci_nr_format_0_0_unpack(const srsran_carrier_nr_t* carrier, } // For PUSCH hopping with resource allocation type 1 N UL_hop MSB bits are used to indicate the frequency offset - int N_UL_hop = (enable_hopping) ? ((carrier->nof_prb < 50) ? 1 : 2) : 0; + uint32_t N_UL_hop = (enable_hopping) ? ((N_UL_BWP_RB < 50) ? 1 : 2) : 0; dci->frequency_offset = srsran_bit_pack(&y, N_UL_hop); // Frequency domain resource assignment - int N = dci_nr_format_0_0_freq_resource_size(carrier); - if (N < SRSRAN_SUCCESS || N - N_UL_hop <= 0) { + uint32_t N = dci_nr_freq_resource_size_type1(N_UL_BWP_RB); + if (N < N_UL_hop) { return SRSRAN_ERROR; } dci->freq_domain_assigment = srsran_bit_pack(&y, N - N_UL_hop - trim); @@ -260,76 +264,20 @@ int srsran_dci_nr_format_0_0_unpack(const srsran_carrier_nr_t* carrier, } // UL/SUL indicator – 1 bit for UEs configured with supplementaryUplink in ServingCellConfig, otherwise 0 - if (supplementary_uplink) { - y++; + if (q->cfg.enable_sul) { + dci->sul = srsran_bit_pack(&y, 1); } return SRSRAN_SUCCESS; } -int srsran_dci_nr_format_0_0_sizeof(const srsran_carrier_nr_t* carrier, - const srsran_coreset_t* coreset, - srsran_rnti_type_t rnti_type) -{ - uint32_t trim = 0; // hard-coded bit trimming - bool enable_hopping = false; // hard-coded PUSCH hopping - uint32_t padding = 8; // Hard-coded padding - bool supplementary_uplink = false; // Hard-coded supplementary Uplink - int count = 0; - - // Identifier for DCI formats – 1 bits - count++; - - // For PUSCH hopping with resource allocation type 1 N UL_hop MSB bits are used to indicate the frequency offset - int N_UL_hop = (enable_hopping) ? ((carrier->nof_prb < 50) ? 1 : 2) : 0; - count += N_UL_hop; - - // Frequency domain resource assignment - int N = dci_nr_format_0_0_freq_resource_size(carrier); - if (N < SRSRAN_SUCCESS || N - N_UL_hop <= 0) { - return SRSRAN_ERROR; - } - count += N - N_UL_hop - trim; - - // Time domain resource assignment – 4 bits - count += 4; - - // Frequency hopping flag – 1 bit - count += 1; - - // Modulation and coding scheme – 5 bits - count += 5; - - // New data indicator – 1 bit - count += 1; - - // Redundancy version – 2 bits - count += 2; - - // HARQ process number – 4 bits - count += 4; - - // TPC command for scheduled PUSCH – 2 bits - count += 2; - - // Padding goes here - count += padding; - - // UL/SUL indicator – 1 bit for UEs configured with supplementaryUplink in ServingCellConfig, otherwise 0 - if (supplementary_uplink) { - count++; - } - - return count; -} - static int dci_nr_format_0_0_to_str(const srsran_dci_ul_nr_t* dci, char* str, uint32_t str_len) { uint32_t len = 0; // Print format len = srsran_print_check( - str, str_len, len, "rnti=%04x L=%d cce=%d dci=0_0 ", dci->rnti, dci->location.L, dci->location.ncce); + str, str_len, len, "rnti=%04x L=%d cce=%d dci=0_0 ", dci->ctx.rnti, dci->ctx.location.L, dci->ctx.location.ncce); // Frequency domain resource assignment len = srsran_print_check(str, str_len, len, "f_alloc=0x%x ", dci->freq_domain_assigment); @@ -358,82 +306,245 @@ static int dci_nr_format_0_0_to_str(const srsran_dci_ul_nr_t* dci, char* str, ui return len; } -int srsran_dci_nr_rar_unpack(srsran_dci_msg_nr_t* msg, srsran_dci_ul_nr_t* dci) +static uint32_t dci_nr_format_0_1_sizeof(const srsran_dci_cfg_nr_t* cfg, srsran_rnti_type_t rnti_type) { - if (msg == NULL || dci == NULL) { + uint32_t count = 0; + + if (rnti_type != srsran_rnti_type_c && rnti_type != srsran_rnti_type_cs && rnti_type != srsran_rnti_type_sp_csi && + rnti_type != srsran_rnti_type_mcs_c) { + ERROR("Invalid RNTI (%s) for format 0_1", srsran_rnti_type_str(rnti_type)); return SRSRAN_ERROR; } - uint8_t* y = msg->payload; + // Identifier for DCI formats – 1 bit + count += 1; - // Copy DCI MSG fields - dci->location = msg->location; - dci->search_space = msg->search_space; - dci->coreset_id = msg->coreset_id; - dci->rnti_type = msg->rnti_type; - dci->rnti = msg->rnti; - dci->format = msg->format; + // Carrier indicator – 0 or 3 bits + count += SRSRAN_MIN(cfg->carrier_indicator_size, 3); - // Frequency hopping flag - 1 bit - dci->freq_hopping_flag = srsran_bit_pack(&y, 1); + // UL/SUL indicator – 0 bit for UEs not configured with supplementaryUplink ... otherwise, 1 bit + count += cfg->enable_sul ? 1 : 0; - // PUSCH frequency resource allocation - 14 bits - dci->freq_domain_assigment = srsran_bit_pack(&y, 14); + // Bandwidth part indicator – 0, 1 or 2 bits + count += SRSRAN_MIN(dci_nr_bwp_id_size(cfg->nof_ul_bwp), 2); - // PUSCH time resource allocation - 4 bits - dci->time_domain_assigment = srsran_bit_pack(&y, 4); + // Frequency domain resource assignment + count += dci_nr_freq_resource_size(cfg->pusch_alloc_type, cfg->nof_rb_groups, cfg->bwp_ul_active_bw); - // MCS -4 bits - dci->mcs = srsran_bit_pack(&y, 4); + // Time domain resource assigment - 0, 1, 2, 3, or 4 bits + count += dci_nr_time_res_size(cfg->nof_ul_time_res); - // TPC command for PUSCH - 3 bits - dci->tpc = srsran_bit_pack(&y, 3); + // Frequency hopping flag - 0 or 1 bit: + if (cfg->pusch_alloc_type == srsran_resource_alloc_type0 || !cfg->enable_hopping) { + count += 0; + } else { + count += 1; + } - // CSI request - 1 bits - dci->csi_request = srsran_bit_pack(&y, 3); + // Modulation and coding scheme – 5 bits + count += 5; + + // New data indicator – 1 bit + count += 1; + + // Redundancy version – 2 bits + count += 2; + + // HARQ process number – 4 bits + count += 4; + + // 1st DAI - 1 or 2 bits + if (cfg->pusch_tx_config_codebook == srsran_pdsch_harq_ack_codebook_semi_static) { + count += 1; + } else { + count += 2; + } + + // 2st DAI - 0 or 2 bits + if (cfg->dynamic_dual_harq_ack_codebook) { + count += 2; + } else { + count += 0; + } + + // TPC command for scheduled PUSCH – 2 bits + count += 2; + + // SRS resource indicator + uint32_t N_srs = SRSRAN_MIN(1, cfg->nof_srs); + if (cfg->pusch_tx_config_codebook) { + uint32_t N = 0; + for (uint32_t k = 1; k < SRSRAN_MIN(cfg->nof_ul_layers, cfg->nof_srs); k++) { + N += cfg->nof_srs / k; + } + count += (uint32_t)CEIL_LOG2(N); + } else { + count += (uint32_t)CEIL_LOG2(N_srs); + } + + // Precoding information and number of layers + if (cfg->pusch_tx_config_codebook) { + ERROR("Not implemented"); + return 0; + } + count += 0; + + // Antenna ports + if (!cfg->enable_transform_precoding && !cfg->pusch_dmrs_double) { + count += 3; + } else { + ERROR("Not implemented"); + return 0; + } + + // SRS request - 2 bits + count += 2; + + // CSI request - 0, 1, 2, 3, 4, 5, or 6 bits + count += SRSRAN_MIN(6, cfg->report_trigger_size); + + // CBG transmission information - 0, 2, 4, 6, or 8 bits + count += cfg->pusch_nof_cbg; + + // PTRS-DMRS association - 0 or 2 bits + if ((!cfg->pusch_ptrs && !cfg->enable_transform_precoding) || cfg->enable_transform_precoding || + cfg->nof_ul_layers <= 1) { + count += 0; + } else { + count += 3; + } + + // beta_offset indicator – 0 or 2 bits + if (cfg->pusch_dynamic_betas) { + count += 2; + } + + // DMRS sequence initialization - 0 or 1 bit + if (!cfg->enable_transform_precoding) { + count += 1; + } + + // UL-SCH indicator – 1 bit + count += 1; + + return count; +} + +static int dci_nr_format_0_1_pack(const srsran_dci_nr_t* q, const srsran_dci_ul_nr_t* dci, srsran_dci_msg_nr_t* msg) +{ + // TODO! return SRSRAN_SUCCESS; } -static int dci_nr_rar_to_str(const srsran_dci_ul_nr_t* dci, char* str, uint32_t str_len) +static int dci_nr_format_0_1_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_t* msg, srsran_dci_ul_nr_t* dci) { - uint32_t len = 0; + // TODO! - // Print format - len = srsran_print_check(str, str_len, len, "rnti=%04x dci=rar ", dci->rnti); - - // Frequency hopping flag - len = srsran_print_check(str, str_len, len, "hop=%d ", dci->freq_hopping_flag); - - // PUSCH frequency resource allocation - len = srsran_print_check(str, str_len, len, "f_alloc=0x%x ", dci->freq_domain_assigment); - - // PUSCH time resource allocation - len = srsran_print_check(str, str_len, len, "t_alloc=0x%x ", dci->time_domain_assigment); - - // Modulation and coding scheme - len = srsran_print_check(str, str_len, len, "mcs=%d ", dci->mcs); - - // TPC command for scheduled PUSCH - len = srsran_print_check(str, str_len, len, "tpc=%d ", dci->tpc); - - // CSI request - len = srsran_print_check(str, str_len, len, "csi=%d ", dci->csi_request); - - return len; + return SRSRAN_SUCCESS; } -int srsran_dci_nr_format_1_0_pack(const srsran_carrier_nr_t* carrier, - const srsran_coreset_t* coreset, - const srsran_dci_dl_nr_t* dci, - srsran_dci_msg_nr_t* msg) +static uint32_t dci_nr_format_1_0_sizeof(uint32_t N_DL_BWP_RB, srsran_rnti_type_t rnti_type) { - uint8_t* y = msg->payload; - srsran_rnti_type_t rnti_type = msg->rnti_type; + uint32_t count = 0; - if (carrier == NULL) { + // Identifier for DCI formats – 1 bits + if (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_tc) { + count += 1; + } + + if (rnti_type == srsran_rnti_type_p) { + // Short Messages Indicator – 2 bits + count += 2; + + // Short Messages – 8 bits + count += 8; + } + + // Frequency domain resource assignment + int N = dci_nr_freq_resource_size_type1(N_DL_BWP_RB); + if (N < SRSRAN_SUCCESS) { return SRSRAN_ERROR; } + count += N; + + // Time domain resource assignment – 4 bits + count += 4; + + // VRB-to-PRB mapping – 1 bit + count += 1; + + // Modulation and coding scheme – 5 bits + count += 5; + + // TB scaling – 2 bits + if (rnti_type == srsran_rnti_type_p || rnti_type == srsran_rnti_type_ra) { + count += 2; + } + + // New data indicator – 1 bit + if (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_tc) { + count += 1; + } + + // Redundancy version – 2 bits + if (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_si || rnti_type == srsran_rnti_type_tc) { + count += 2; + } + + // HARQ process number – 4 bits + if (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_tc) { + count += 4; + } + + // System information indicator – 1 bit + if (rnti_type == srsran_rnti_type_si) { + count += 1; + } + + // Downlink assignment index – 2 bits + if (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_tc) { + count += 2; + } + + // TPC command for scheduled PUCCH – 2 bits + if (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_tc) { + count += 2; + } + + // PUCCH resource indicator – 3 bits + if (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_tc) { + count += 3; + } + + // PDSCH-to-HARQ_feedback timing indicator – 3 bits + if (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_tc) { + count += 3; + } + + // Reserved bits ... + if (rnti_type == srsran_rnti_type_p) { + // ... – 6 bits + count += 2; + } else if (rnti_type == srsran_rnti_type_si) { + // ... – 15 bits + count += 15; + } else if (rnti_type == srsran_rnti_type_ra) { + // ... – 16 bits + count += 16; + } + + return count; +} + +static int dci_nr_format_1_0_pack(const srsran_dci_nr_t* q, const srsran_dci_dl_nr_t* dci, srsran_dci_msg_nr_t* msg) +{ + uint8_t* y = msg->payload; + srsran_rnti_type_t rnti_type = msg->ctx.rnti_type; + srsran_search_space_type_t ss_type = dci->ctx.ss_type; + uint32_t N_DL_BWP_RB = SRSRAN_SEARCH_SPACE_IS_COMMON(ss_type) + ? (q->cfg.coreset0_bw == 0) ? q->cfg.bwp_dl_initial_bw : q->cfg.coreset0_bw + : q->cfg.bwp_dl_active_bw; // Identifier for DCI formats – 1 bits if (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_tc) { @@ -450,7 +561,7 @@ int srsran_dci_nr_format_1_0_pack(const srsran_carrier_nr_t* carrier, } // Frequency domain resource assignment - int N = dci_nr_format_1_0_freq_resource_size(carrier, coreset, rnti_type); + int N = dci_nr_freq_resource_size_type1(N_DL_BWP_RB); if (N < SRSRAN_SUCCESS) { return SRSRAN_ERROR; } @@ -522,35 +633,28 @@ int srsran_dci_nr_format_1_0_pack(const srsran_carrier_nr_t* carrier, srsran_bit_unpack(dci->reserved, &y, 16); } - msg->nof_bits = srsran_dci_nr_format_1_0_sizeof(carrier, coreset, rnti_type); - if (msg->nof_bits != y - msg->payload) { - ERROR("Unpacked bits read (%d) do NOT match payload size (%d)", msg->nof_bits, (int)(y - msg->payload)); + msg->nof_bits = srsran_dci_nr_size(q, msg->ctx.ss_type, srsran_dci_format_nr_1_0); + uint32_t nof_bits = (uint32_t)(y - msg->payload); + if (msg->nof_bits != nof_bits) { + ERROR("Unpacked bits read (%d) do NOT match payload size (%d)", msg->nof_bits, nof_bits); return SRSRAN_ERROR; } return SRSRAN_SUCCESS; } -int srsran_dci_nr_format_1_0_unpack(const srsran_carrier_nr_t* carrier, - const srsran_coreset_t* coreset, - srsran_dci_msg_nr_t* msg, - srsran_dci_dl_nr_t* dci) +static int dci_nr_format_1_0_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_t* msg, srsran_dci_dl_nr_t* dci) { - uint8_t* y = msg->payload; - srsran_rnti_type_t rnti_type = msg->rnti_type; + uint8_t* y = msg->payload; + srsran_rnti_type_t rnti_type = msg->ctx.rnti_type; + srsran_search_space_type_t ss_type = msg->ctx.ss_type; + uint32_t N_DL_BWP_RB = SRSRAN_SEARCH_SPACE_IS_COMMON(ss_type) + ? (q->cfg.coreset0_bw == 0) ? q->cfg.bwp_dl_initial_bw : q->cfg.coreset0_bw + : q->cfg.bwp_dl_active_bw; - // Copy DCI MSG fields - dci->location = msg->location; - dci->search_space = msg->search_space; - dci->coreset_id = msg->coreset_id; - dci->rnti_type = msg->rnti_type; - dci->rnti = msg->rnti; - dci->format = msg->format; - - if (msg->nof_bits != srsran_dci_nr_format_1_0_sizeof(carrier, coreset, rnti_type)) { - ERROR("Invalid number of bits %d, expected %d", - msg->nof_bits, - srsran_dci_nr_format_1_0_sizeof(carrier, coreset, rnti_type)); + uint32_t nof_bits = srsran_dci_nr_size(q, ss_type, srsran_dci_format_nr_1_0); + if (msg->nof_bits != nof_bits) { + ERROR("Invalid number of bits %d, expected %d", msg->nof_bits, nof_bits); return SRSRAN_ERROR; } @@ -572,7 +676,7 @@ int srsran_dci_nr_format_1_0_unpack(const srsran_carrier_nr_t* carrier, } // Frequency domain resource assignment - int N = dci_nr_format_1_0_freq_resource_size(carrier, coreset, rnti_type); + int N = dci_nr_freq_resource_size_type1(N_DL_BWP_RB); if (N < SRSRAN_SUCCESS) { return SRSRAN_ERROR; } @@ -652,110 +756,16 @@ int srsran_dci_nr_format_1_0_unpack(const srsran_carrier_nr_t* carrier, return SRSRAN_SUCCESS; } -int srsran_dci_nr_format_1_0_sizeof(const srsran_carrier_nr_t* carrier, - const srsran_coreset_t* coreset, - srsran_rnti_type_t rnti_type) -{ - int count = 0; - - // Identifier for DCI formats – 1 bits - if (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_tc) { - count += 1; - } - - if (rnti_type == srsran_rnti_type_p) { - // Short Messages Indicator – 2 bits - count += 2; - - // Short Messages – 8 bits - count += 8; - } - - // Frequency domain resource assignment - int N = dci_nr_format_1_0_freq_resource_size(carrier, coreset, rnti_type); - if (N < SRSRAN_SUCCESS) { - return SRSRAN_ERROR; - } - count += N; - - // Time domain resource assignment – 4 bits - count += 4; - - // VRB-to-PRB mapping – 1 bit - count += 1; - - // Modulation and coding scheme – 5 bits - count += 5; - - // TB scaling – 2 bits - if (rnti_type == srsran_rnti_type_p || rnti_type == srsran_rnti_type_ra) { - count += 2; - } - - // New data indicator – 1 bit - if (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_tc) { - count += 1; - } - - // Redundancy version – 2 bits - if (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_si || rnti_type == srsran_rnti_type_tc) { - count += 2; - } - - // HARQ process number – 4 bits - if (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_tc) { - count += 4; - } - - // System information indicator – 1 bit - if (rnti_type == srsran_rnti_type_si) { - count += 1; - } - - // Downlink assignment index – 2 bits - if (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_tc) { - count += 2; - } - - // TPC command for scheduled PUCCH – 2 bits - if (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_tc) { - count += 2; - } - - // PUCCH resource indicator – 3 bits - if (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_tc) { - count += 3; - } - - // PDSCH-to-HARQ_feedback timing indicator – 3 bits - if (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_tc) { - count += 3; - } - - // Reserved bits ... - if (rnti_type == srsran_rnti_type_p) { - // ... – 6 bits - count += 2; - } else if (rnti_type == srsran_rnti_type_si) { - // ... – 15 bits - count += 15; - } else if (rnti_type == srsran_rnti_type_ra) { - // ... – 16 bits - count += 16; - } - - return count; -} - static int dci_nr_format_1_0_to_str(const srsran_dci_dl_nr_t* dci, char* str, uint32_t str_len) { - uint32_t len = 0; + uint32_t len = 0; + srsran_rnti_type_t rnti_type = dci->ctx.rnti_type; // Print format len = srsran_print_check( - str, str_len, len, "rnti=%04x L=%d cce=%d dci=1_0 ", dci->rnti, dci->location.L, dci->location.ncce); + str, str_len, len, "rnti=%04x L=%d cce=%d dci=1_0 ", dci->ctx.rnti, dci->ctx.location.L, dci->ctx.location.ncce); - if (dci->rnti_type == srsran_rnti_type_p) { + if (rnti_type == srsran_rnti_type_p) { len = srsran_print_check(str, str_len, len, "smi=%d sm=%d ", dci->smi, dci->sm); } @@ -772,64 +782,504 @@ static int dci_nr_format_1_0_to_str(const srsran_dci_dl_nr_t* dci, char* str, ui len = srsran_print_check(str, str_len, len, "mcs=%d ", dci->mcs); // TB scaling – 2 bits - if (dci->rnti_type == srsran_rnti_type_p || dci->rnti_type == srsran_rnti_type_ra) { + if (rnti_type == srsran_rnti_type_p || rnti_type == srsran_rnti_type_ra) { len = srsran_print_check(str, str_len, len, "tb_scaling=%d ", dci->tb_scaling); } // New data indicator – 1 bit - if (dci->rnti_type == srsran_rnti_type_c || dci->rnti_type == srsran_rnti_type_tc) { + if (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_tc) { len = srsran_print_check(str, str_len, len, "ndi=%d ", dci->ndi); } // Redundancy version – 2 bits - if (dci->rnti_type == srsran_rnti_type_c || dci->rnti_type == srsran_rnti_type_si || - dci->rnti_type == srsran_rnti_type_tc) { + if (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_si || rnti_type == srsran_rnti_type_tc) { len = srsran_print_check(str, str_len, len, "rv=%d ", dci->rv); } // HARQ process number – 4 bits - if (dci->rnti_type == srsran_rnti_type_c || dci->rnti_type == srsran_rnti_type_tc) { + if (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_tc) { len = srsran_print_check(str, str_len, len, "harq_id=%d ", dci->harq_feedback); } // System information indicator – 1 bit - if (dci->rnti_type == srsran_rnti_type_si) { + if (rnti_type == srsran_rnti_type_si) { len = srsran_print_check(str, str_len, len, "sii=%d ", dci->sii); } // Downlink assignment index – 2 bits - if (dci->rnti_type == srsran_rnti_type_c || dci->rnti_type == srsran_rnti_type_tc) { + if (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_tc) { len = srsran_print_check(str, str_len, len, "dai=%d ", dci->dai); } // TPC command for scheduled PUCCH – 2 bits - if (dci->rnti_type == srsran_rnti_type_c || dci->rnti_type == srsran_rnti_type_tc) { + if (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_tc) { len = srsran_print_check(str, str_len, len, "pucch_tpc=%d ", dci->tpc); } // PUCCH resource indicator – 3 bits - if (dci->rnti_type == srsran_rnti_type_c || dci->rnti_type == srsran_rnti_type_tc) { + if (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_tc) { len = srsran_print_check(str, str_len, len, "pucch_res=%d ", dci->pucch_resource); } // PDSCH-to-HARQ_feedback timing indicator – 3 bits - if (dci->rnti_type == srsran_rnti_type_c || dci->rnti_type == srsran_rnti_type_tc) { + if (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_tc) { len = srsran_print_check(str, str_len, len, "harq_feedback=%d ", dci->harq_feedback); } // Reserved bits ... - if (dci->rnti_type == srsran_rnti_type_p || dci->rnti_type == srsran_rnti_type_si || - dci->rnti_type == srsran_rnti_type_ra) { + if (rnti_type == srsran_rnti_type_p || rnti_type == srsran_rnti_type_si || rnti_type == srsran_rnti_type_ra) { len = srsran_print_check(str, str_len, len, "reserved=0x%x ", dci->reserved); } return len; } +static uint32_t dci_nr_format_1_1_sizeof(const srsran_dci_cfg_nr_t* cfg, srsran_rnti_type_t rnti_type) +{ + int count = 0; + + if (rnti_type != srsran_rnti_type_c && rnti_type != srsran_rnti_type_cs && rnti_type != srsran_rnti_type_mcs_c) { + ERROR("Invalid RNTI (%s) for format 1_1", srsran_rnti_type_str(rnti_type)); + return SRSRAN_ERROR; + } + + // Identifier for DCI formats – 1 bits + count += 1; + + // Carrier indicator – 0 or 3 bits + count += (int)SRSRAN_MIN(cfg->carrier_indicator_size, 3); + + // Bandwidth part indicator – 0, 1 or 2 bits + count += (int)SRSRAN_MIN(dci_nr_bwp_id_size(cfg->nof_ul_bwp), 2); + + // Frequency domain resource assignment + count += dci_nr_freq_resource_size(cfg->pdsch_alloc_type, cfg->nof_rb_groups, cfg->bwp_dl_active_bw); + + // Time domain resource assignment – 0, 1, 2, 3, or 4 bits + count += dci_nr_time_res_size(cfg->nof_dl_time_res); + + // VRB-to-PRB mapping – 0 or 1 + if (cfg->pdsch_alloc_type != srsran_resource_alloc_type0 && cfg->pdsch_inter_prb_to_prb) { + count += 1; + } + + // PRB bundling size indicator – 0 or 1 bits + // ... not implemented + + // Rate matching indicator – 0, 1, or 2 bits + if (cfg->pdsch_rm_pattern1) { + count += 1; + } + if (cfg->pdsch_rm_pattern2) { + count += 1; + } + + // ZP CSI-RS trigger - 0, 1, or 2 bits + count += (int)CEIL_LOG2(cfg->nof_aperiodic_zp + 1); + + // For transport block 1: + // Modulation and coding scheme – 5 bits + count += 5; + + // New data indicator – 1 bit + count += 1; + + // Redundancy version – 2 bits + count += 2; + + // For transport block 2: + if (cfg->pdsch_2cw) { + // Modulation and coding scheme – 5 bits + count += 5; + + // New data indicator – 1 bit + count += 1; + + // Redundancy version – 2 bits + count += 2; + } + + // HARQ process number – 4 bits + count += 4; + + // Downlink assignment index (dynamic HARQ-ACK codebook only) + if (cfg->harq_ack_codebok == srsran_pdsch_harq_ack_codebook_dynamic) { + if (cfg->multiple_scell) { + count += 4; + } else { + count += 2; + } + } + + // TPC command for scheduled PUCCH – 2 bits + count += 2; + + // PDSCH-to-HARQ_feedback timing indicator – 0, 1, 2, or 3 bits + count += (int)CEIL_LOG2(cfg->nof_dl_to_ul_ack); + + // Antenna port(s) – 4, 5, or 6 bits + count += 4; + if (cfg->pdsch_dmrs_type2) { + count++; + } + if (cfg->pdsch_dmrs_double) { + count++; + } + + // Transmission configuration indication – 0 or 3 bits + if (cfg->pdsch_tci) { + count += 3; + } + + // SRS request – 2 or 3 bits + count += 2; + if (cfg->enable_sul) { + count++; + } + + // CBG transmission information (CBGTI) – 0, 2, 4, 6, or 8 bits + count += cfg->pdsch_nof_cbg; + + // CBG flushing out information (CBGFI) – 0 or 1 bit + if (cfg->pdsch_cbg_flush) { + count += 1; + } + + // DMRS sequence initialization – 1 bit + count += 1; + + return count; +} + +static int dci_nr_format_1_1_pack(const srsran_dci_nr_t* q, const srsran_dci_dl_nr_t* dci, srsran_dci_msg_nr_t* msg) +{ + // TODO! + + return SRSRAN_SUCCESS; +} + +static int dci_nr_format_1_1_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_t* msg, srsran_dci_dl_nr_t* dci) +{ + // TODO! + + return SRSRAN_SUCCESS; +} + +int srsran_dci_nr_set_cfg(srsran_dci_nr_t* q, const srsran_dci_cfg_nr_t* cfg) +{ + // Reset current setup + SRSRAN_MEM_ZERO(q, srsran_dci_nr_t, 1); + + // Copy configuration + q->cfg = *cfg; + + // Step 0 + // - Determine DCI format 0_0 monitored in a common search space according to clause 7.3.1.1.1 where N_UL_BWP_RB is + // given by the size of the initial UL bandwidth part. + uint32_t size_dci_0_0_common = dci_nr_format_0_0_sizeof(cfg->bwp_ul_initial_bw, cfg); + + // - Determine DCI format 1_0 monitored in a common search space according to clause 7.3.1.2.1 where N_DL_BWP_RB given + // by: + // - the size of CORESET 0 if CORESET 0 is configured for the cell; and + // - the size of initial DL bandwidth part if CORESET 0 is not configured for the cell. + uint32_t size_dci_1_0_common = dci_nr_format_1_0_sizeof(cfg->bwp_dl_initial_bw, srsran_rnti_type_c); + if (cfg->coreset0_bw != 0) { + size_dci_1_0_common = dci_nr_format_1_0_sizeof(cfg->coreset0_bw, srsran_rnti_type_c); + } + + // - If DCI format 0_0 is monitored in common search space and if the number of information bits in the DCI format 0_0 + // prior to padding is less than the payload size of the DCI format 1_0 monitored in common search space for + // scheduling the same serving cell, a number of zero padding bits are generated for the DCI format 0_0 until the + // payload size equals that of the DCI format 1_0. + if (cfg->monitor_common_0_0 && size_dci_0_0_common < size_dci_1_0_common) { + q->dci_0_0_common_padd = size_dci_1_0_common - size_dci_0_0_common; + } else { + q->dci_0_0_common_padd = 0; + } + + // - If DCI format 0_0 is monitored in common search space and if the number of information bits in the DCI format 0_0 + // prior to truncation is larger than the payload size of the DCI format 1_0 monitored in common search space for + // scheduling the same serving cell, the bitwidth of the frequency domain resource assignment field in the DCI format + // 0_0 is reduced by truncating the first few most significant bits such that the size of DCI format 0_0 equals the + // size of the DCI format 1_0. + if (cfg->monitor_common_0_0 && size_dci_0_0_common > size_dci_1_0_common) { + q->dci_0_0_common_trunc = size_dci_0_0_common - size_dci_1_0_common; + } else { + q->dci_0_0_common_trunc = 0; + } + + q->dci_0_0_and_1_0_common_size = SRSRAN_MAX(size_dci_1_0_common, DCI_NR_MIN_SIZE); + + // Step 1 + // - Determine DCI format 0_0 monitored in a UE-specific search space according to clause 7.3.1.1.1 where N_UL_BWP_RB + // is the size of the active UL bandwidth part. + uint32_t size_dci_0_0_ue = dci_nr_format_0_0_sizeof(cfg->bwp_ul_active_bw, cfg); + + // - Determine DCI format 1_0 monitored in a UE-specific search space according to clause 7.3.1.2.1 where N_DL_BWP_RB + // is the size of the active DL bandwidth part. + uint32_t size_dci_1_0_ue = dci_nr_format_1_0_sizeof(cfg->bwp_dl_active_bw, srsran_rnti_type_c); + + // - For a UE configured with supplementaryUplink in ServingCellConfig in a cell, if PUSCH is configured to be + // transmitted on both the SUL and the non-SUL of the cell and if the number of information bits in DCI format 0_0 in + // UE-specific search space for the SUL is not equal to the number of information bits in DCI format 0_0 in + // UE-specific search space for the non-SUL, a number of zero padding bits are generated for the smaller DCI format + // 0_0 until the payload size equals that of the larger DCI format 0_0. + // ... Not implemented + + // - If DCI format 0_0 is monitored in UE-specific search space and if the number of information bits in the DCI + // format 0_0 prior to padding is less than the payload size of the DCI format 1_0 monitored in UE-specific search + // space for scheduling the same serving cell, a number of zero padding bits are generated for the DCI format 0_0 + // until the payload size equals that of the DCI format 1_0. + if (cfg->monitor_0_0_and_1_0 && size_dci_0_0_ue < size_dci_1_0_ue) { + q->dci_0_0_ue_padd = size_dci_1_0_ue - size_dci_0_0_ue; + } + + // - If DCI format 1_0 is monitored in UE-specific search space and if the number of information bits in the DCI + // format 1_0 prior to padding is less than the payload size of the DCI format 0_0 monitored in UE-specific search + // space for scheduling the same serving cell, zeros shall be appended to the DCI format 1_0 until the payload size + // equals that of the DCI format 0_0 + if (cfg->monitor_0_0_and_1_0 && size_dci_1_0_ue < size_dci_0_0_ue) { + q->dci_1_0_ue_padd = size_dci_0_0_ue - size_dci_1_0_ue; + } + + q->dci_0_0_and_1_0_ue_size = SRSRAN_MAX(SRSRAN_MAX(size_dci_0_0_ue, size_dci_1_0_ue), DCI_NR_MIN_SIZE); + + // Step 2 + // For a UE configured with supplementaryUplink in ServingCellConfig in a cell, if PUSCH is configured to be + // transmitted on both the SUL and the non-SUL of the cell and if the number of information bits in format 0_1 for + // the SUL is not equal to the number of information bits in format 0_1 for the non-SUL, zeros shall be appended + // to smaller format 0_1 until the payload size equals that of the larger format 0_1. + // ... Not implemented + + uint32_t size_dci_0_1 = dci_nr_format_0_1_sizeof(cfg, srsran_rnti_type_c); + uint32_t size_dci_1_1 = dci_nr_format_1_1_sizeof(cfg, srsran_rnti_type_c); + if (size_dci_0_1 == 0 || size_dci_1_1 == 0) { + return SRSRAN_ERROR; + } + + // If the size of DCI format 0_1 monitored in a UE-specific search space equals that of a DCI format 0_0/1_0 + // monitored in another UE-specific search space, one bit of zero padding shall be appended to DCI format 0_1. + if (size_dci_0_1 == q->dci_0_0_and_1_0_ue_size) { + q->dci_0_1_padd++; + } + + // If the size of DCI format 1_1 monitored in a UE-specific search space equals that of a DCI format 0_0/1_0 + // monitored in another UE-specific search space, one bit of zero padding shall be appended to DCI format 1_1. + if (size_dci_1_1 == q->dci_0_0_and_1_0_ue_size) { + q->dci_1_1_padd++; + } + + q->dci_0_1_size = size_dci_0_1 + q->dci_0_1_padd; + q->dci_1_1_size = size_dci_1_1 + q->dci_1_1_padd; + + // Step 3 + // If both of the following conditions are fulfilled the size alignment procedure is complete: + // - the total number of different DCI sizes configured to monitor is no more than 4 for the cell + // - the total number of different DCI sizes with C-RNTI configured to monitor is no more than 3 for the cell + // ... Current code is compatible with only possible sizes! + + return SRSRAN_SUCCESS; +} + +uint32_t srsran_dci_nr_size(const srsran_dci_nr_t* q, srsran_search_space_type_t ss_type, srsran_dci_format_nr_t format) +{ + // Check input + if (q == NULL) { + return 0; + } + + // For common search space, only formats 0_0 and 1_0 + if (SRSRAN_SEARCH_SPACE_IS_COMMON(ss_type)) { + return q->dci_0_0_and_1_0_common_size; + } + + // DCI formats 0_0 and 1_0 in UE-specific + if (format == srsran_dci_format_nr_0_0 || format == srsran_dci_format_nr_1_0) { + return q->dci_0_0_and_1_0_ue_size; + } + + // DCI format 0_1 in UE-specific + if (format == srsran_dci_format_nr_0_1) { + return q->dci_0_1_size; + } + + // DCI format 1_1 in UE-specific + if (format == srsran_dci_format_nr_1_1) { + return q->dci_1_1_size; + } + + // Not implemented + return 0; +} + +bool srsran_dci_nr_valid_direction(const srsran_dci_msg_nr_t* dci) +{ + // Check pointer + if (dci == NULL) { + return false; + } + + // UL direction + uint32_t expected_direction = 0; + + // Set DL direction if is DL grant + if (dci->ctx.format == srsran_dci_format_nr_1_0 || dci->ctx.format == srsran_dci_format_nr_1_1) { + expected_direction = 1; + } + + // The format bit is only present for these RNTI + if (dci->ctx.rnti_type == srsran_rnti_type_c || dci->ctx.rnti_type == srsran_rnti_type_tc) { + return dci->payload[0] == expected_direction; + } + + // For other RNTI types, assume always DL on 1_0 + return (dci->ctx.format == srsran_dci_format_nr_1_0); +} + +static int dci_nr_rar_pack(const srsran_dci_nr_t* q, const srsran_dci_ul_nr_t* dci, srsran_dci_msg_nr_t* msg) +{ + ERROR("Not implemented"); + return SRSRAN_ERROR; +} + +static int dci_nr_rar_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_t* msg, srsran_dci_ul_nr_t* dci) +{ + if (msg == NULL || dci == NULL) { + return SRSRAN_ERROR; + } + + uint8_t* y = msg->payload; + + // Copy DCI MSG fields + dci->ctx = msg->ctx; + + // Frequency hopping flag - 1 bit + dci->freq_hopping_flag = srsran_bit_pack(&y, 1); + + // PUSCH frequency resource allocation - 14 bits + dci->freq_domain_assigment = srsran_bit_pack(&y, 14); + + // PUSCH time resource allocation - 4 bits + dci->time_domain_assigment = srsran_bit_pack(&y, 4); + + // MCS -4 bits + dci->mcs = srsran_bit_pack(&y, 4); + + // TPC command for PUSCH - 3 bits + dci->tpc = srsran_bit_pack(&y, 3); + + // CSI request - 1 bits + dci->csi_request = srsran_bit_pack(&y, 3); + + return SRSRAN_SUCCESS; +} + +static int dci_nr_rar_to_str(const srsran_dci_ul_nr_t* dci, char* str, uint32_t str_len) +{ + uint32_t len = 0; + + // Print format + len = srsran_print_check(str, str_len, len, "rnti=%04x dci=rar ", dci->ctx.rnti); + + // Frequency hopping flag + len = srsran_print_check(str, str_len, len, "hop=%d ", dci->freq_hopping_flag); + + // PUSCH frequency resource allocation + len = srsran_print_check(str, str_len, len, "f_alloc=0x%x ", dci->freq_domain_assigment); + + // PUSCH time resource allocation + len = srsran_print_check(str, str_len, len, "t_alloc=0x%x ", dci->time_domain_assigment); + + // Modulation and coding scheme + len = srsran_print_check(str, str_len, len, "mcs=%d ", dci->mcs); + + // TPC command for scheduled PUSCH + len = srsran_print_check(str, str_len, len, "tpc=%d ", dci->tpc); + + // CSI request + len = srsran_print_check(str, str_len, len, "csi=%d ", dci->csi_request); + + return len; +} + +int srsran_dci_nr_dl_pack(const srsran_dci_nr_t* q, const srsran_dci_dl_nr_t* dci, srsran_dci_msg_nr_t* msg) +{ + // Copy DCI MSG fields + msg->ctx = dci->ctx; + + // Pack DCI + switch (msg->ctx.format) { + case srsran_dci_format_nr_1_0: + return dci_nr_format_1_0_pack(q, dci, msg); + case srsran_dci_format_nr_1_1: + return dci_nr_format_1_1_pack(q, dci, msg); + default: + ERROR("Unsupported DCI format %d", msg->ctx.format); + } + + return SRSRAN_ERROR; +} + +int srsran_dci_nr_dl_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_t* msg, srsran_dci_dl_nr_t* dci) +{ + // Copy DCI MSG fields + dci->ctx = msg->ctx; + + // Pack DCI + switch (msg->ctx.format) { + case srsran_dci_format_nr_1_0: + return dci_nr_format_1_0_unpack(q, msg, dci); + case srsran_dci_format_nr_1_1: + return dci_nr_format_1_1_unpack(q, msg, dci); + default: + ERROR("Unsupported DCI format %d", msg->ctx.format); + } + return SRSRAN_ERROR; +} + +int srsran_dci_nr_ul_pack(const srsran_dci_nr_t* q, const srsran_dci_ul_nr_t* dci, srsran_dci_msg_nr_t* msg) +{ + // Copy DCI MSG fields + msg->ctx = dci->ctx; + + // Pack DCI + switch (msg->ctx.format) { + case srsran_dci_format_nr_0_0: + return dci_nr_format_0_0_pack(q, dci, msg); + case srsran_dci_format_nr_0_1: + return dci_nr_format_0_1_pack(q, dci, msg); + case srsran_dci_format_nr_rar: + return dci_nr_rar_pack(q, dci, msg); + default: + ERROR("Unsupported DCI format %d", msg->ctx.format); + } + + return SRSRAN_ERROR; +} + +int srsran_dci_nr_ul_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_t* msg, srsran_dci_ul_nr_t* dci) +{ + // Copy DCI MSG fields + dci->ctx = msg->ctx; + + // Pack DCI + switch (msg->ctx.format) { + case srsran_dci_format_nr_0_0: + return dci_nr_format_0_0_unpack(q, msg, dci); + case srsran_dci_format_nr_0_1: + return dci_nr_format_0_1_unpack(q, msg, dci); + case srsran_dci_format_nr_rar: + return dci_nr_rar_unpack(q, msg, dci); + default: + ERROR("Unsupported DCI format %d", msg->ctx.format); + } + return SRSRAN_ERROR; +} + int srsran_dci_ul_nr_to_str(const srsran_dci_ul_nr_t* dci, char* str, uint32_t str_len) { // Pack DCI - switch (dci->format) { + switch (dci->ctx.format) { case srsran_dci_format_nr_0_0: return dci_nr_format_0_0_to_str(dci, str, str_len); case srsran_dci_format_nr_rar: @@ -843,7 +1293,7 @@ int srsran_dci_ul_nr_to_str(const srsran_dci_ul_nr_t* dci, char* str, uint32_t s int srsran_dci_dl_nr_to_str(const srsran_dci_dl_nr_t* dci, char* str, uint32_t str_len) { // Pack DCI - switch (dci->format) { + switch (dci->ctx.format) { case srsran_dci_format_nr_1_0: return dci_nr_format_1_0_to_str(dci, str, str_len); default:; // Do nothing diff --git a/lib/src/phy/phch/pdcch_nr.c b/lib/src/phy/phch/pdcch_nr.c index 1b2f54e1a..2eeb60485 100644 --- a/lib/src/phy/phch/pdcch_nr.c +++ b/lib/src/phy/phch/pdcch_nr.c @@ -333,11 +333,11 @@ static uint32_t pdcch_nr_cp(const srsran_pdcch_nr_t* q, static uint32_t pdcch_nr_c_init(const srsran_pdcch_nr_t* q, const srsran_dci_msg_nr_t* dci_msg) { - uint32_t n_id = (dci_msg->search_space == srsran_search_space_type_ue && q->coreset.dmrs_scrambling_id_present) + uint32_t n_id = (dci_msg->ctx.ss_type == srsran_search_space_type_ue && q->coreset.dmrs_scrambling_id_present) ? q->coreset.dmrs_scrambling_id : q->carrier.id; - uint32_t n_rnti = (dci_msg->search_space == srsran_search_space_type_ue && q->coreset.dmrs_scrambling_id_present) - ? dci_msg->rnti + uint32_t n_rnti = (dci_msg->ctx.ss_type == srsran_search_space_type_ue && q->coreset.dmrs_scrambling_id_present) + ? dci_msg->ctx.rnti : 0U; return ((n_rnti << 16U) + n_id) & 0x7fffffffU; } @@ -354,10 +354,10 @@ int srsran_pdcch_nr_encode(srsran_pdcch_nr_t* q, const srsran_dci_msg_nr_t* dci_ } // Calculate... - q->K = dci_msg->nof_bits + 24U; // Payload size including CRC - q->M = (1U << dci_msg->location.L) * (SRSRAN_NRE - 3U) * 6U; // Number of RE - q->E = q->M * 2; // Number of Rate-Matched bits - uint32_t cinit = pdcch_nr_c_init(q, dci_msg); // Pseudo-random sequence initiation + q->K = dci_msg->nof_bits + 24U; // Payload size including CRC + q->M = (1U << dci_msg->ctx.location.L) * (SRSRAN_NRE - 3U) * 6U; // Number of RE + q->E = q->M * 2; // Number of Rate-Matched bits + uint32_t cinit = pdcch_nr_c_init(q, dci_msg); // Pseudo-random sequence initiation // Get polar code if (srsran_polar_code_get(&q->code, q->K, q->E, 9U) < SRSRAN_SUCCESS) { @@ -380,7 +380,7 @@ int srsran_pdcch_nr_encode(srsran_pdcch_nr_t* q, const srsran_dci_msg_nr_t* dci_ // Unpack RNTI uint8_t unpacked_rnti[16] = {}; uint8_t* ptr = unpacked_rnti; - srsran_bit_unpack(dci_msg->rnti, &ptr, 16); + srsran_bit_unpack(dci_msg->ctx.rnti, &ptr, 16); // Scramble CRC with RNTI srsran_vec_xor_bbb(unpacked_rnti, &c[q->K - 16], &c[q->K - 16], 16); @@ -421,7 +421,7 @@ int srsran_pdcch_nr_encode(srsran_pdcch_nr_t* q, const srsran_dci_msg_nr_t* dci_ srsran_mod_modulate(&q->modem_table, q->f, q->symbols, q->E); // Put symbols in grid - uint32_t m = pdcch_nr_cp(q, &dci_msg->location, slot_symbols, q->symbols, true); + uint32_t m = pdcch_nr_cp(q, &dci_msg->ctx.location, slot_symbols, q->symbols, true); if (q->M != m) { ERROR("Unmatch number of RE (%d != %d)", m, q->M); return SRSRAN_ERROR; @@ -458,9 +458,9 @@ int srsran_pdcch_nr_decode(srsran_pdcch_nr_t* q, } // Calculate... - q->K = dci_msg->nof_bits + 24U; // Payload size including CRC - q->M = (1U << dci_msg->location.L) * (SRSRAN_NRE - 3U) * 6U; // Number of RE - q->E = q->M * 2; // Number of Rate-Matched bits + q->K = dci_msg->nof_bits + 24U; // Payload size including CRC + q->M = (1U << dci_msg->ctx.location.L) * (SRSRAN_NRE - 3U) * 6U; // Number of RE + q->E = q->M * 2; // Number of Rate-Matched bits // Check number of estimates is correct if (ce->nof_re != q->M) { @@ -475,7 +475,7 @@ int srsran_pdcch_nr_decode(srsran_pdcch_nr_t* q, PDCCH_INFO_RX("K=%d; E=%d; M=%d; n=%d;", q->K, q->E, q->M, q->code.n); // Get symbols from grid - uint32_t m = pdcch_nr_cp(q, &dci_msg->location, slot_symbols, q->symbols, false); + uint32_t m = pdcch_nr_cp(q, &dci_msg->ctx.location, slot_symbols, q->symbols, false); if (q->M != m) { ERROR("Unmatch number of RE (%d != %d)", m, q->M); return SRSRAN_ERROR; @@ -555,7 +555,7 @@ int srsran_pdcch_nr_decode(srsran_pdcch_nr_t* q, // Unpack RNTI uint8_t unpacked_rnti[16] = {}; uint8_t* ptr = unpacked_rnti; - srsran_bit_unpack(dci_msg->rnti, &ptr, 16); + srsran_bit_unpack(dci_msg->ctx.rnti, &ptr, 16); // De-Scramble CRC with RNTI srsran_vec_xor_bbb(unpacked_rnti, &c[q->K - 16], &c[q->K - 16], 16); diff --git a/lib/src/phy/phch/pucch_nr.c b/lib/src/phy/phch/pucch_nr.c index db91848ea..359ce6d5c 100644 --- a/lib/src/phy/phch/pucch_nr.c +++ b/lib/src/phy/phch/pucch_nr.c @@ -741,7 +741,7 @@ static uint32_t pucch_nr_resource_info(const srsran_pucch_nr_resource_t* r, char len = srsran_print_check(str, str_len, len, - "f=%d, prb=%d:%d, symb=%d:%d", + "f=%d prb=%d:%d symb=%d:%d ", (int)r->format, r->starting_prb, nof_prb, @@ -749,19 +749,19 @@ static uint32_t pucch_nr_resource_info(const srsran_pucch_nr_resource_t* r, char r->nof_symbols); if (r->intra_slot_hopping) { - len = srsran_print_check(str, str_len, len, ", hop=%d", r->second_hop_prb); + len = srsran_print_check(str, str_len, len, "hop=%d ", r->second_hop_prb); } if (r->format == SRSRAN_PUCCH_NR_FORMAT_0 || r->format == SRSRAN_PUCCH_NR_FORMAT_1) { - len = srsran_print_check(str, str_len, len, ", cs=%d", r->initial_cyclic_shift); + len = srsran_print_check(str, str_len, len, "cs=%d ", r->initial_cyclic_shift); } if (r->format == SRSRAN_PUCCH_NR_FORMAT_1) { - len = srsran_print_check(str, str_len, len, ", occ=%d", r->time_domain_occ); + len = srsran_print_check(str, str_len, len, "occ=%d ", r->time_domain_occ); } if (r->format == SRSRAN_PUCCH_NR_FORMAT_4) { - len = srsran_print_check(str, str_len, len, ", occ=%d:%d", r->occ_index, r->occ_lenth); + len = srsran_print_check(str, str_len, len, "occ=%d:%d ", r->occ_index, r->occ_lenth); } return len; @@ -776,7 +776,7 @@ uint32_t srsran_pucch_nr_tx_info(const srsran_pucch_nr_resource_t* resource, len += pucch_nr_resource_info(resource, &str[len], str_len - len); - len = srsran_print_check(str, str_len, len, ", "); + len = srsran_print_check(str, str_len, len, "rnti=0x%x ", uci_data->cfg.pucch.rnti); len += srsran_uci_nr_info(uci_data, &str[len], str_len - len); diff --git a/lib/src/phy/phch/pusch_nr.c b/lib/src/phy/phch/pusch_nr.c index 874a464dd..32219a32e 100644 --- a/lib/src/phy/phch/pusch_nr.c +++ b/lib/src/phy/phch/pusch_nr.c @@ -62,6 +62,8 @@ int pusch_nr_init_common(srsran_pusch_nr_t* q, const srsran_pusch_nr_args_t* arg return SRSRAN_ERROR; } + q->meas_time_en = args->measure_time; + return SRSRAN_SUCCESS; } @@ -106,8 +108,6 @@ int srsran_pusch_nr_init_gnb(srsran_pusch_nr_t* q, const srsran_pusch_nr_args_t* } } - q->meas_time_en = args->measure_time; - return SRSRAN_SUCCESS; } diff --git a/lib/src/phy/phch/ra_dl_nr.c b/lib/src/phy/phch/ra_dl_nr.c index f342bd8a9..34dd26136 100644 --- a/lib/src/phy/phch/ra_dl_nr.c +++ b/lib/src/phy/phch/ra_dl_nr.c @@ -71,7 +71,7 @@ int srsran_ra_dl_nr_time_default_A(uint32_t m, srsran_dmrs_sch_typeA_pos_t dmrs_ return SRSRAN_ERROR_INVALID_INPUTS; } - if (m >= SRSRAN_MAX_NOF_DL_ALLOCATION) { + if (m >= SRSRAN_MAX_NOF_TIME_RA) { ERROR("m (%d) is out-of-range", m); return SRSRAN_ERROR_INVALID_INPUTS; } @@ -98,10 +98,10 @@ int srsran_ra_dl_nr_time_default_A(uint32_t m, srsran_dmrs_sch_typeA_pos_t dmrs_ srsran_sch_mapping_type_B}; grant->mapping = pdsch_mapping_lut[m]; - static uint32_t S_pos2[SRSRAN_MAX_NOF_DL_ALLOCATION] = {2, 2, 2, 2, 2, 9, 4, 5, 5, 9, 12, 1, 1, 2, 4, 8}; - static uint32_t L_pos2[SRSRAN_MAX_NOF_DL_ALLOCATION] = {12, 10, 9, 7, 5, 4, 4, 7, 2, 2, 2, 13, 6, 4, 7, 4}; - static uint32_t S_pos3[SRSRAN_MAX_NOF_DL_ALLOCATION] = {3, 3, 3, 3, 3, 10, 6, 5, 5, 9, 12, 1, 1, 2, 4, 8}; - static uint32_t L_pos3[SRSRAN_MAX_NOF_DL_ALLOCATION] = {11, 9, 8, 6, 4, 4, 4, 7, 2, 2, 2, 13, 6, 4, 7, 4}; + static uint32_t S_pos2[SRSRAN_MAX_NOF_TIME_RA] = {2, 2, 2, 2, 2, 9, 4, 5, 5, 9, 12, 1, 1, 2, 4, 8}; + static uint32_t L_pos2[SRSRAN_MAX_NOF_TIME_RA] = {12, 10, 9, 7, 5, 4, 4, 7, 2, 2, 2, 13, 6, 4, 7, 4}; + static uint32_t S_pos3[SRSRAN_MAX_NOF_TIME_RA] = {3, 3, 3, 3, 3, 10, 6, 5, 5, 9, 12, 1, 1, 2, 4, 8}; + static uint32_t L_pos3[SRSRAN_MAX_NOF_TIME_RA] = {11, 9, 8, 6, 4, 4, 4, 7, 2, 2, 2, 13, 6, 4, 7, 4}; // Select start symbol (S) and length (L) switch (dmrs_typeA_pos) { @@ -141,7 +141,7 @@ int srsran_ra_dl_nr_time(const srsran_sch_hl_cfg_nr_t* cfg, return SRSRAN_ERROR_INVALID_INPUTS; } - if (m >= SRSRAN_MAX_NOF_DL_ALLOCATION) { + if (m >= SRSRAN_MAX_NOF_TIME_RA) { ERROR("m (%d) is out-of-range", m); return SRSRAN_ERROR_INVALID_INPUTS; } @@ -186,7 +186,7 @@ int srsran_ra_dl_nr_time(const srsran_sch_hl_cfg_nr_t* cfg, srsran_ra_dl_nr_time_default_A(m, cfg->typeA_pos, grant); } } else { - ERROR("Unhandled case"); + ERROR("Unhandled case %s, ss_type=%d", srsran_rnti_type_str(rnti_type), ss_type); } // Validate S and L parameters @@ -273,7 +273,7 @@ int srsran_ra_dl_nr_freq(const srsran_carrier_nr_t* carrier, } // RA scheme - if (dci_dl->format == srsran_dci_format_nr_1_0) { + if (dci_dl->ctx.format == srsran_dci_format_nr_1_0) { // when the scheduling grant is received with DCI format 1_0 , then downlink resource allocation type 1 is used. return ra_helper_freq_type1(carrier->nof_prb, dci_dl->freq_domain_assigment, grant); } diff --git a/lib/src/phy/phch/ra_nr.c b/lib/src/phy/phch/ra_nr.c index ac0fba5d5..f4a404661 100644 --- a/lib/src/phy/phch/ra_nr.c +++ b/lib/src/phy/phch/ra_nr.c @@ -662,9 +662,9 @@ int srsran_ra_dl_dci_to_grant_nr(const srsran_carrier_nr_t* carrier, { // 5.2.1.1 Resource allocation in time domain if (srsran_ra_dl_nr_time(pdsch_hl_cfg, - dci_dl->rnti_type, - dci_dl->search_space, - dci_dl->coreset_id, + dci_dl->ctx.rnti_type, + dci_dl->ctx.ss_type, + dci_dl->ctx.coreset_id, dci_dl->time_domain_assigment, pdsch_grant) < SRSRAN_SUCCESS) { ERROR("Error computing time domain resource allocation"); @@ -681,9 +681,9 @@ int srsran_ra_dl_dci_to_grant_nr(const srsran_carrier_nr_t* carrier, // ... pdsch_grant->nof_layers = 1; - pdsch_grant->dci_format = dci_dl->format; - pdsch_grant->rnti = dci_dl->rnti; - pdsch_grant->rnti_type = dci_dl->rnti_type; + pdsch_grant->dci_format = dci_dl->ctx.format; + pdsch_grant->rnti = dci_dl->ctx.rnti; + pdsch_grant->rnti_type = dci_dl->ctx.rnti_type; pdsch_grant->tb[0].rv = dci_dl->rv; // 5.1.4 PDSCH resource mapping @@ -714,7 +714,8 @@ ra_ul_dmrs(const srsran_sch_hl_cfg_nr_t* pusch_hl_cfg, srsran_sch_grant_nr_t* pu ? pusch_hl_cfg->dmrs_typeA.present : pusch_hl_cfg->dmrs_typeB.present; - if (pusch_grant->dci_format == srsran_dci_format_nr_0_0 || !dedicated_dmrs_present) { + if (pusch_grant->dci_format == srsran_dci_format_nr_0_0 || pusch_grant->dci_format == srsran_dci_format_nr_rar || + !dedicated_dmrs_present) { if (pusch_grant->mapping == srsran_sch_mapping_type_A) { // Absent default values are defined is TS 38.331 - DMRS-DownlinkConfig cfg->dmrs.additional_pos = srsran_dmrs_sch_add_pos_2; @@ -743,13 +744,13 @@ ra_ul_dmrs(const srsran_sch_hl_cfg_nr_t* pusch_hl_cfg, srsran_sch_grant_nr_t* pu } // Set number of DMRS CDM groups without data - if (pusch_grant->dci_format == srsran_dci_format_nr_0_0) { + if (pusch_grant->dci_format == srsran_dci_format_nr_0_0 || pusch_grant->dci_format == srsran_dci_format_nr_rar) { if (srsran_ra_ul_nr_nof_dmrs_cdm_groups_without_data_format_0_0(cfg, pusch_grant) < SRSRAN_SUCCESS) { ERROR("Error loading number of DMRS CDM groups"); return SRSRAN_ERROR; } } else { - ERROR("Invalid case"); + ERROR("DCI format not implemented %s", srsran_dci_format_nr_string(pusch_grant->dci_format)); return SRSRAN_ERROR; } @@ -770,9 +771,9 @@ int srsran_ra_ul_dci_to_grant_nr(const srsran_carrier_nr_t* carrier, { // 5.2.1.1 Resource allocation in time domain if (srsran_ra_ul_nr_time(pusch_hl_cfg, - dci_ul->rnti_type, - dci_ul->search_space, - dci_ul->coreset_id, + dci_ul->ctx.rnti_type, + dci_ul->ctx.ss_type, + dci_ul->ctx.coreset_id, dci_ul->time_domain_assigment, pusch_grant) < SRSRAN_SUCCESS) { ERROR("Error computing time domain resource allocation"); @@ -789,9 +790,9 @@ int srsran_ra_ul_dci_to_grant_nr(const srsran_carrier_nr_t* carrier, // ... pusch_grant->nof_layers = 1; - pusch_grant->dci_format = dci_ul->format; - pusch_grant->rnti = dci_ul->rnti; - pusch_grant->rnti_type = dci_ul->rnti_type; + pusch_grant->dci_format = dci_ul->ctx.format; + pusch_grant->rnti = dci_ul->ctx.rnti; + pusch_grant->rnti_type = dci_ul->ctx.rnti_type; // 5.1.6.2 DM-RS reception procedure if (ra_ul_dmrs(pusch_hl_cfg, pusch_grant, pusch_cfg) < SRSRAN_SUCCESS) { diff --git a/lib/src/phy/phch/ra_ul_nr.c b/lib/src/phy/phch/ra_ul_nr.c index 86009e4b4..b09a68c20 100644 --- a/lib/src/phy/phch/ra_ul_nr.c +++ b/lib/src/phy/phch/ra_ul_nr.c @@ -141,7 +141,7 @@ int srsran_ra_ul_nr_time(const srsran_sch_hl_cfg_nr_t* cfg, return SRSRAN_ERROR_INVALID_INPUTS; } - if (m >= SRSRAN_MAX_NOF_DL_ALLOCATION) { + if (m >= SRSRAN_MAX_NOF_TIME_RA) { ERROR("m (%d) is out-of-range", m); return SRSRAN_ERROR_INVALID_INPUTS; } @@ -151,12 +151,12 @@ int srsran_ra_ul_nr_time(const srsran_sch_hl_cfg_nr_t* cfg, // Row 1 if (cfg->nof_common_time_ra == 0) { srsran_ra_ul_nr_pusch_time_resource_default_A(cfg->scs_cfg, m, grant); - } else if (m < SRSRAN_MAX_NOF_DL_ALLOCATION && m < cfg->nof_common_time_ra) { + } else if (m < SRSRAN_MAX_NOF_TIME_RA && m < cfg->nof_common_time_ra) { ra_ul_nr_time_hl(&cfg->common_time_ra[m], grant); } else { ERROR("Time domain resource selection (m=%d) exceeds the maximum value (%d)", m, - SRSRAN_MIN(cfg->nof_common_time_ra, SRSRAN_MAX_NOF_DL_ALLOCATION)); + SRSRAN_MIN(cfg->nof_common_time_ra, SRSRAN_MAX_NOF_TIME_RA)); } } else if ((rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_mcs_c || rnti_type == srsran_rnti_type_tc || rnti_type == srsran_rnti_type_cs) && @@ -164,7 +164,7 @@ int srsran_ra_ul_nr_time(const srsran_sch_hl_cfg_nr_t* cfg, // Row 2 if (cfg->nof_common_time_ra == 0) { srsran_ra_ul_nr_pusch_time_resource_default_A(cfg->scs_cfg, m, grant); - } else if (m < SRSRAN_MAX_NOF_DL_ALLOCATION) { + } else if (m < SRSRAN_MAX_NOF_TIME_RA) { ra_ul_nr_time_hl(&cfg->common_time_ra[m], grant); } } else if ((rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_mcs_c || @@ -383,12 +383,12 @@ int srsran_ra_ul_nr_freq(const srsran_carrier_nr_t* carrier, } // RA scheme - if (dci_ul->format == srsran_dci_format_nr_0_0) { + if (dci_ul->ctx.format == srsran_dci_format_nr_0_0 || dci_ul->ctx.format == srsran_dci_format_nr_rar) { // when the scheduling grant is received with DCI format 1_0 , then downlink resource allocation type 1 is used. return ra_helper_freq_type1(carrier->nof_prb, dci_ul->freq_domain_assigment, grant); } - ERROR("Only DCI Format 0_0 is supported"); + ERROR("Unhandled DCI Format %s", srsran_dci_format_nr_string(dci_ul->ctx.format)); return SRSRAN_ERROR; } diff --git a/lib/src/phy/phch/sch_nr.c b/lib/src/phy/phch/sch_nr.c index 1c42e0c5e..72a55de3a 100644 --- a/lib/src/phy/phch/sch_nr.c +++ b/lib/src/phy/phch/sch_nr.c @@ -590,10 +590,15 @@ int sch_nr_decode(srsran_sch_nr_t* q, tb->rv, cfg.Qm, cfg.Nref); - srsran_ldpc_rm_rx_c(&q->rx_rm, input_ptr, rm_buffer, E, cfg.F, cfg.bg, cfg.Z, tb->rv, tb->mod, cfg.Nref); + int n_llr = + srsran_ldpc_rm_rx_c(&q->rx_rm, input_ptr, rm_buffer, E, cfg.F, cfg.bg, cfg.Z, tb->rv, tb->mod, cfg.Nref); + if (n_llr < SRSRAN_SUCCESS) { + ERROR("Error in LDPC rate mateching"); + return SRSRAN_ERROR; + } // Decode - srsran_ldpc_decoder_decode_c(decoder, rm_buffer, q->temp_cb); + srsran_ldpc_decoder_decode_c(decoder, rm_buffer, q->temp_cb, n_llr); // Compute CB CRC uint32_t cb_len = cfg.Kp - cfg.L_cb; @@ -652,7 +657,7 @@ int sch_nr_decode(srsran_sch_nr_t* q, // Check if TB is all zeros bool all_zeros = true; - for (uint32_t i = 0; i < tb->tbs && all_zeros; i++) { + for (uint32_t i = 0; i < tb->tbs / 8 && all_zeros; i++) { all_zeros = (data[i] == 0); } diff --git a/lib/src/phy/phch/test/CMakeLists.txt b/lib/src/phy/phch/test/CMakeLists.txt index 2c9115a52..36d9f52ff 100644 --- a/lib/src/phy/phch/test/CMakeLists.txt +++ b/lib/src/phy/phch/test/CMakeLists.txt @@ -628,13 +628,24 @@ endif(RF_FOUND) # NR ######################################################################## +add_executable(dci_nr_test dci_nr_test.c) +target_link_libraries(dci_nr_test srsran_phy) +add_nr_test(dci_nr_test dci_nr_test) + add_executable(pucch_nr_test pucch_nr_test.c) target_link_libraries(pucch_nr_test srsran_phy) add_nr_test(pucch_nr_test pucch_nr_test) add_executable(sch_nr_test sch_nr_test.c) target_link_libraries(sch_nr_test srsran_phy) -add_nr_test(sch_nr_test sch_nr_test -m 0 -p 1) +add_nr_test(sch_nr_test sch_nr_test -P 52 -p 1 -r 0) +add_nr_test(sch_nr_test sch_nr_test -P 52 -p 1 -r 1) +add_nr_test(sch_nr_test sch_nr_test -P 52 -p 10 -r 0) +add_nr_test(sch_nr_test sch_nr_test -P 52 -p 10 -r 1) +add_nr_test(sch_nr_test sch_nr_test -P 52 -p 20 -r 0) +add_nr_test(sch_nr_test sch_nr_test -P 52 -p 20 -r 1) +add_nr_test(sch_nr_test sch_nr_test -P 52 -p 52 -r 0) +add_nr_test(sch_nr_test sch_nr_test -P 52 -p 52 -r 1) add_executable(pdsch_nr_test pdsch_nr_test.c) target_link_libraries(pdsch_nr_test srsran_phy) diff --git a/lib/src/phy/phch/test/dci_nr_test.c b/lib/src/phy/phch/test/dci_nr_test.c new file mode 100644 index 000000000..2d8ed5d1b --- /dev/null +++ b/lib/src/phy/phch/test/dci_nr_test.c @@ -0,0 +1,53 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsran/common/test_common.h" +#include "srsran/phy/phch/dci_nr.h" + +static int test_52prb() +{ + // Default configuration with all options disabled + srsran_dci_cfg_nr_t cfg = {}; + + // Set bandwidths + cfg.coreset0_bw = 0; + cfg.bwp_dl_initial_bw = 52; + cfg.bwp_dl_active_bw = 52; + cfg.bwp_ul_initial_bw = 52; + cfg.bwp_ul_active_bw = 52; + + // Enable all monitoring + cfg.monitor_common_0_0 = true; + cfg.monitor_0_0_and_1_0 = true; + cfg.monitor_0_1_and_1_1 = true; + + // Configure DCI + srsran_dci_nr_t dci = {}; + TESTASSERT(srsran_dci_nr_set_cfg(&dci, &cfg) == SRSRAN_SUCCESS); + + // Check DCI sizes + TESTASSERT(srsran_dci_nr_size(&dci, srsran_search_space_type_common_3, srsran_dci_format_nr_0_0) == 39); + TESTASSERT(srsran_dci_nr_size(&dci, srsran_search_space_type_common_3, srsran_dci_format_nr_1_0) == 39); + TESTASSERT(srsran_dci_nr_size(&dci, srsran_search_space_type_ue, srsran_dci_format_nr_0_0) == 39); + TESTASSERT(srsran_dci_nr_size(&dci, srsran_search_space_type_ue, srsran_dci_format_nr_1_0) == 39); + TESTASSERT(srsran_dci_nr_size(&dci, srsran_search_space_type_ue, srsran_dci_format_nr_0_1) == 28); + TESTASSERT(srsran_dci_nr_size(&dci, srsran_search_space_type_ue, srsran_dci_format_nr_1_1) == 26); + + return SRSRAN_SUCCESS; +} + +int main(int argc, char** argv) +{ + TESTASSERT(test_52prb() == SRSRAN_SUCCESS); + + return SRSRAN_SUCCESS; +} diff --git a/lib/src/phy/phch/test/pdcch_nr_test.c b/lib/src/phy/phch/test/pdcch_nr_test.c index b50752ed4..159407907 100644 --- a/lib/src/phy/phch/test/pdcch_nr_test.c +++ b/lib/src/phy/phch/test/pdcch_nr_test.c @@ -52,8 +52,8 @@ static int test(srsran_pdcch_nr_t* tx, // Encode PDCCH TESTASSERT(srsran_pdcch_nr_encode(tx, dci_msg_tx, grid) == SRSRAN_SUCCESS); - enc_time[dci_msg_tx->location.L].time_us += tx->meas_time_us; - enc_time[dci_msg_tx->location.L].count++; + enc_time[dci_msg_tx->ctx.location.L].time_us += tx->meas_time_us; + enc_time[dci_msg_tx->ctx.location.L].count++; // Init Rx MSG srsran_pdcch_nr_res_t res = {}; @@ -63,8 +63,8 @@ static int test(srsran_pdcch_nr_t* tx, // Decode PDCCH TESTASSERT(srsran_pdcch_nr_decode(rx, grid, ce, &dci_msg_rx, &res) == SRSRAN_SUCCESS); - dec_time[dci_msg_tx->location.L].time_us += rx->meas_time_us; - dec_time[dci_msg_tx->location.L].count++; + dec_time[dci_msg_tx->ctx.location.L].time_us += rx->meas_time_us; + dec_time[dci_msg_tx->ctx.location.L].count++; // Assert TESTASSERT(res.evm < 0.01f); @@ -151,8 +151,27 @@ int main(int argc, char** argv) } for (coreset.duration = SRSRAN_CORESET_DURATION_MIN; coreset.duration <= SRSRAN_CORESET_DURATION_MAX; coreset.duration++) { - srsran_search_space_t search_space = {}; - search_space.type = srsran_search_space_type_ue; + srsran_search_space_t search_space = {}; + search_space.type = srsran_search_space_type_ue; + search_space.formats[search_space.nof_formats++] = srsran_dci_format_nr_0_0; + search_space.formats[search_space.nof_formats++] = srsran_dci_format_nr_1_0; + + srsran_dci_cfg_nr_t dci_cfg = {}; + dci_cfg.coreset0_bw = 0; + dci_cfg.bwp_dl_initial_bw = carrier.nof_prb; + dci_cfg.bwp_dl_active_bw = carrier.nof_prb; + dci_cfg.bwp_ul_initial_bw = carrier.nof_prb; + dci_cfg.bwp_ul_active_bw = carrier.nof_prb; + dci_cfg.monitor_common_0_0 = true; + dci_cfg.monitor_0_0_and_1_0 = true; + dci_cfg.monitor_0_1_and_1_1 = true; + + // Precompute DCI sizes + srsran_dci_nr_t dci = {}; + if (srsran_dci_nr_set_cfg(&dci, &dci_cfg) < SRSRAN_SUCCESS) { + ERROR("Error setting DCI configuratio"); + goto clean_exit; + } if (srsran_pdcch_nr_set_carrier(&pdcch_tx, &carrier, &coreset) < SRSRAN_SUCCESS) { ERROR("Error setting carrier"); @@ -194,11 +213,11 @@ int main(int argc, char** argv) for (uint32_t ncce_idx = 0; ncce_idx < n; ncce_idx++) { // Init MSG srsran_dci_msg_nr_t dci_msg = {}; - dci_msg.format = srsran_dci_format_nr_1_0; - dci_msg.rnti_type = srsran_rnti_type_c; - dci_msg.location.L = aggregation_level; - dci_msg.location.ncce = dci_locations[ncce_idx]; - dci_msg.nof_bits = srsran_dci_nr_format_1_0_sizeof(&carrier, &coreset, dci_msg.rnti_type); + dci_msg.ctx.format = srsran_dci_format_nr_1_0; + dci_msg.ctx.rnti_type = srsran_rnti_type_c; + dci_msg.ctx.location.L = aggregation_level; + dci_msg.ctx.location.ncce = dci_locations[ncce_idx]; + dci_msg.nof_bits = srsran_dci_nr_size(&dci, search_space.type, srsran_dci_format_nr_1_0); // Generate random payload for (uint32_t i = 0; i < dci_msg.nof_bits; i++) { diff --git a/lib/src/phy/phch/test/sch_nr_test.c b/lib/src/phy/phch/test/sch_nr_test.c index 3876c4bab..f76f91b3b 100644 --- a/lib/src/phy/phch/test/sch_nr_test.c +++ b/lib/src/phy/phch/test/sch_nr_test.c @@ -35,15 +35,17 @@ static srsran_carrier_nr_t carrier = { 1 // max_mimo_layers }; -static uint32_t n_prb = 0; // Set to 0 for steering -static uint32_t mcs = 30; // Set to 30 for steering -static srsran_sch_cfg_nr_t pdsch_cfg = {}; -static srsran_sch_grant_nr_t pdsch_grant = {}; +static uint32_t n_prb = 0; // Set to 0 for steering +static uint32_t mcs = 30; // Set to 30 for steering +static uint32_t rv = 4; // Set to 30 for steering +static srsran_sch_cfg_nr_t pdsch_cfg = {}; -void usage(char* prog) +static void usage(char* prog) { - printf("Usage: %s [pTL] \n", prog); + printf("Usage: %s [prTL] \n", prog); + printf("\t-P Number of carrier PRB [Default %d]\n", carrier.nof_prb); printf("\t-p Number of grant PRB, set to 0 for steering [Default %d]\n", n_prb); + printf("\t-r Redundancy version, set to 4 or higher for steering [Default %d]\n", rv); printf("\t-m MCS PRB, set to >28 for steering [Default %d]\n", mcs); printf("\t-T Provide MCS table (64qam, 256qam, 64qamLowSE) [Default %s]\n", srsran_mcs_table_to_str(pdsch_cfg.sch_cfg.mcs_table)); @@ -54,14 +56,20 @@ void usage(char* prog) int parse_args(int argc, char** argv) { int opt; - while ((opt = getopt(argc, argv, "pmTLv")) != -1) { + while ((opt = getopt(argc, argv, "PpmTLvr")) != -1) { switch (opt) { + case 'P': + carrier.nof_prb = (uint32_t)strtol(argv[optind], NULL, 10); + break; case 'p': n_prb = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'm': mcs = (uint32_t)strtol(argv[optind], NULL, 10); break; + case 'r': + rv = (uint32_t)strtol(argv[optind], NULL, 10); + break; case 'T': pdsch_cfg.sch_cfg.mcs_table = srsran_mcs_table_from_str(argv[optind]); break; @@ -141,12 +149,12 @@ int main(int argc, char** argv) } // Use grant default A time resources with m=0 - if (srsran_ra_dl_nr_time_default_A(0, pdsch_cfg.dmrs.typeA_pos, &pdsch_grant) < SRSRAN_SUCCESS) { + if (srsran_ra_dl_nr_time_default_A(0, pdsch_cfg.dmrs.typeA_pos, &pdsch_cfg.grant) < SRSRAN_SUCCESS) { ERROR("Error loading default grant"); goto clean_exit; } - pdsch_grant.nof_layers = carrier.max_mimo_layers; - pdsch_grant.dci_format = srsran_dci_format_nr_1_0; + pdsch_cfg.grant.nof_layers = carrier.max_mimo_layers; + pdsch_cfg.grant.dci_format = srsran_dci_format_nr_1_0; uint32_t n_prb_start = 1; uint32_t n_prb_end = carrier.nof_prb + 1; @@ -155,6 +163,13 @@ int main(int argc, char** argv) n_prb_end = SRSRAN_MIN(n_prb + 1, n_prb_end); } + uint32_t rv_start = 0; + uint32_t rv_end = 4; + if (rv < 4) { + rv_start = rv; + rv_end = rv + 1; + } + uint32_t mcs_start = 0; uint32_t mcs_end = pdsch_cfg.sch_cfg.mcs_table == srsran_mcs_table_256qam ? 28 : 29; if (mcs < mcs_end) { @@ -164,55 +179,66 @@ int main(int argc, char** argv) for (n_prb = n_prb_start; n_prb < n_prb_end; n_prb++) { for (mcs = mcs_start; mcs < mcs_end; mcs++) { - for (uint32_t n = 0; n < SRSRAN_MAX_PRB_NR; n++) { - pdsch_grant.prb_idx[n] = (n < n_prb); + for (rv = rv_start; rv < rv_end; rv++) { + for (uint32_t n = 0; n < SRSRAN_MAX_PRB_NR; n++) { + pdsch_cfg.grant.prb_idx[n] = (n < n_prb); + } + + if (srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data_format_1_0(&pdsch_cfg.dmrs, &pdsch_cfg.grant) < + SRSRAN_SUCCESS) { + ERROR("Error calculating number of DMRS CDM groups"); + goto clean_exit; + } + + srsran_sch_tb_t tb = {}; + tb.rv = rv; + if (srsran_ra_nr_fill_tb(&pdsch_cfg, &pdsch_cfg.grant, mcs, &tb) < SRSRAN_SUCCESS) { + ERROR("Error filing tb"); + goto clean_exit; + } + + for (uint32_t i = 0; i < tb.tbs; i++) { + data_tx[i] = (uint8_t)srsran_random_uniform_int_dist(rand_gen, 0, UINT8_MAX); + } + + tb.softbuffer.tx = &softbuffer_tx; + + if (srsran_dlsch_nr_encode(&sch_nr_tx, &pdsch_cfg.sch_cfg, &tb, data_tx, encoded) < SRSRAN_SUCCESS) { + ERROR("Error encoding"); + goto clean_exit; + } + + for (uint32_t i = 0; i < tb.nof_bits; i++) { + llr[i] = encoded[i] ? -10 : +10; + } + + tb.softbuffer.rx = &softbuffer_rx; + srsran_softbuffer_rx_reset(tb.softbuffer.rx); + + bool crc = false; + if (srsran_dlsch_nr_decode(&sch_nr_rx, &pdsch_cfg.sch_cfg, &tb, llr, data_rx, &crc) < SRSRAN_SUCCESS) { + ERROR("Error encoding"); + goto clean_exit; + } + + if (rv == 0) { + if (!crc) { + ERROR("Failed to match CRC; n_prb=%d; mcs=%d; TBS=%d;", n_prb, mcs, tb.tbs); + goto clean_exit; + } + + if (memcmp(data_tx, data_rx, tb.tbs / 8) != 0) { + ERROR("Failed to match Tx/Rx data; n_prb=%d; mcs=%d; TBS=%d;", n_prb, mcs, tb.tbs); + printf("Tx data: "); + srsran_vec_fprint_byte(stdout, data_tx, tb.tbs / 8); + printf("Rx data: "); + srsran_vec_fprint_byte(stdout, data_rx, tb.tbs / 8); + goto clean_exit; + } + } + + INFO("n_prb=%d; mcs=%d; rv=%d TBS=%d; PASSED!\n", n_prb, mcs, rv, tb.tbs); } - - srsran_sch_tb_t tb = {}; - if (srsran_ra_nr_fill_tb(&pdsch_cfg, &pdsch_grant, mcs, &tb) < SRSRAN_SUCCESS) { - ERROR("Error filing tb"); - goto clean_exit; - } - - for (uint32_t i = 0; i < tb.tbs; i++) { - data_tx[i] = (uint8_t)srsran_random_uniform_int_dist(rand_gen, 0, UINT8_MAX); - } - - tb.softbuffer.tx = &softbuffer_tx; - - if (srsran_dlsch_nr_encode(&sch_nr_tx, &pdsch_cfg.sch_cfg, &tb, data_tx, encoded) < SRSRAN_SUCCESS) { - ERROR("Error encoding"); - goto clean_exit; - } - - for (uint32_t i = 0; i < tb.nof_bits; i++) { - llr[i] = encoded[i] ? -10 : +10; - } - - tb.softbuffer.rx = &softbuffer_rx; - srsran_softbuffer_rx_reset(tb.softbuffer.rx); - - bool crc = false; - if (srsran_dlsch_nr_decode(&sch_nr_rx, &pdsch_cfg.sch_cfg, &tb, llr, data_rx, &crc) < SRSRAN_SUCCESS) { - ERROR("Error encoding"); - goto clean_exit; - } - - if (!crc) { - ERROR("Failed to match CRC; n_prb=%d; mcs=%d; TBS=%d;", n_prb, mcs, tb.tbs); - goto clean_exit; - } - - if (memcmp(data_tx, data_rx, tb.tbs / 8) != 0) { - ERROR("Failed to match Tx/Rx data; n_prb=%d; mcs=%d; TBS=%d;", n_prb, mcs, tb.tbs); - printf("Tx data: "); - srsran_vec_fprint_byte(stdout, data_tx, tb.tbs / 8); - printf("Rx data: "); - srsran_vec_fprint_byte(stdout, data_rx, tb.tbs / 8); - goto clean_exit; - } - - printf("n_prb=%d; mcs=%d; TBS=%d; PASSED!\n", n_prb, mcs, tb.tbs); } } @@ -237,5 +263,5 @@ clean_exit: srsran_softbuffer_tx_free(&softbuffer_tx); srsran_softbuffer_rx_free(&softbuffer_rx); - return SRSRAN_SUCCESS; + return ret; } diff --git a/lib/src/phy/phch/uci_nr.c b/lib/src/phy/phch/uci_nr.c index 0c2947518..2c66f5344 100644 --- a/lib/src/phy/phch/uci_nr.c +++ b/lib/src/phy/phch/uci_nr.c @@ -994,8 +994,6 @@ uint32_t srsran_uci_nr_info(const srsran_uci_data_nr_t* uci_data, char* str, uin { uint32_t len = 0; - len = srsran_print_check(str, str_len, len, "rnti=0x%x", uci_data->cfg.pucch.rnti); - if (uci_data->cfg.o_ack > 0) { char str2[10]; srsran_vec_sprint_bin(str2, 10, uci_data->value.ack, uci_data->cfg.o_ack); @@ -1265,4 +1263,4 @@ int srsran_uci_nr_decode_pusch_csi1(srsran_uci_nr_t* q, } return SRSRAN_SUCCESS; -} \ No newline at end of file +} diff --git a/lib/src/phy/ue/test/CMakeLists.txt b/lib/src/phy/ue/test/CMakeLists.txt index 5ff7a1fcd..e8226d9ac 100644 --- a/lib/src/phy/ue/test/CMakeLists.txt +++ b/lib/src/phy/ue/test/CMakeLists.txt @@ -26,6 +26,10 @@ add_executable(gen_ack_test gen_ack_test.c) target_link_libraries(gen_ack_test srsran_phy) add_test(gen_ack_test gen_ack_test) +add_executable(gen_ack_nr_test gen_ack_nr_test.c) +target_link_libraries(gen_ack_nr_test srsran_phy) +add_test(gen_ack_nr_test gen_ack_nr_test) + add_executable(pucch_resource_test pucch_resource_test.c) target_link_libraries(pucch_resource_test srsran_phy) add_test(pucch_resource_test pucch_resource_test) diff --git a/lib/src/phy/ue/test/gen_ack_nr_test.c b/lib/src/phy/ue/test/gen_ack_nr_test.c new file mode 100644 index 000000000..378df6b5b --- /dev/null +++ b/lib/src/phy/ue/test/gen_ack_nr_test.c @@ -0,0 +1,150 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsran/common/test_common.h" +#include "srsran/phy/ue/ue_dl_nr.h" +#include + +static int test_case_1() +{ + // Set configuration + srsran_ue_dl_nr_harq_ack_cfg_t cfg = {}; + cfg.harq_ack_codebook = srsran_pdsch_harq_ack_codebook_dynamic; + + // Generate ACK information + srsran_pdsch_ack_nr_t ack_info = {}; + ack_info.nof_cc = 1; + ack_info.use_pusch = true; + + srsran_pdsch_ack_m_nr_t m = {}; + m.value[0] = 1; + m.present = true; + + m.resource.k1 = 8; + m.resource.v_dai_dl = 0; + m.value[0] = 1; + m.present = true; + TESTASSERT(srsran_ue_dl_nr_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); + + m.resource.k1 = 5; + m.resource.v_dai_dl = 2; + TESTASSERT(srsran_ue_dl_nr_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); + + m.resource.k1 = 6; + m.resource.v_dai_dl = 1; + TESTASSERT(srsran_ue_dl_nr_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); + + m.resource.k1 = 4; + m.resource.v_dai_dl = 3; + TESTASSERT(srsran_ue_dl_nr_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); + + m.resource.k1 = 3; + m.resource.v_dai_dl = 0; + TESTASSERT(srsran_ue_dl_nr_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); + + // Print trace + char str[512] = {}; + TESTASSERT(srsran_ue_dl_nr_ack_info(&ack_info, str, (uint32_t)sizeof(str)) > SRSRAN_SUCCESS); + INFO("%s", str); + + // Generate UCI data + srsran_uci_data_nr_t uci_data = {}; + TESTASSERT(srsran_ue_dl_nr_gen_ack(&cfg, &ack_info, &uci_data) == SRSRAN_SUCCESS); + + // Assert UCI data + TESTASSERT(uci_data.cfg.o_ack == 5); + + return SRSRAN_SUCCESS; +} + +static int test_case_2() +{ + // Set configuration + srsran_ue_dl_nr_harq_ack_cfg_t cfg = {}; + cfg.harq_ack_codebook = srsran_pdsch_harq_ack_codebook_dynamic; + + // Generate ACK information + srsran_pdsch_ack_nr_t ack_info = {}; + ack_info.nof_cc = 1; + ack_info.use_pusch = true; + + srsran_pdsch_ack_m_nr_t m = {}; + m.value[0] = 1; + m.present = true; + + m.resource.k1 = 7; + m.resource.v_dai_dl = 1; + TESTASSERT(srsran_ue_dl_nr_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); + + m.resource.k1 = 6; + m.resource.v_dai_dl = 2; + TESTASSERT(srsran_ue_dl_nr_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); + + m.resource.k1 = 8; + m.resource.v_dai_dl = 0; + TESTASSERT(srsran_ue_dl_nr_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); + + m.resource.k1 = 5; + m.resource.v_dai_dl = 3; + TESTASSERT(srsran_ue_dl_nr_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); + + m.resource.k1 = 4; + m.resource.v_dai_dl = 0; + TESTASSERT(srsran_ue_dl_nr_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); + + // Print trace + char str[512] = {}; + TESTASSERT(srsran_ue_dl_nr_ack_info(&ack_info, str, (uint32_t)sizeof(str)) > SRSRAN_SUCCESS); + INFO("%s", str); + + // Generate UCI data + srsran_uci_data_nr_t uci_data = {}; + TESTASSERT(srsran_ue_dl_nr_gen_ack(&cfg, &ack_info, &uci_data) == SRSRAN_SUCCESS); + + // Assert UCI data + TESTASSERT(uci_data.cfg.o_ack == 5); + + return SRSRAN_SUCCESS; +} + +static void usage(char* prog) +{ + printf("Usage: %s [v]\n", prog); + printf("\t-v Increase srsran_verbose\n"); +} + +static void parse_args(int argc, char** argv) +{ + int opt; + while ((opt = getopt(argc, argv, "v")) != -1) { + switch (opt) { + case 'v': + srsran_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +int main(int argc, char** argv) +{ + parse_args(argc, argv); + + // Test only until Format1B - CS + TESTASSERT(test_case_1() == SRSRAN_SUCCESS); + TESTASSERT(test_case_2() == SRSRAN_SUCCESS); + + printf("Ok\n"); + return SRSRAN_SUCCESS; +} \ No newline at end of file diff --git a/lib/src/phy/ue/ue_dl_nr.c b/lib/src/phy/ue/ue_dl_nr.c index 1acb09c9b..61c7ac27d 100644 --- a/lib/src/phy/ue/ue_dl_nr.c +++ b/lib/src/phy/ue/ue_dl_nr.c @@ -23,7 +23,7 @@ #include #define UE_DL_NR_PDCCH_CORR_DEFAULT_THR 0.5f -#define UE_DL_NR_PDCCH_EPRE_DEFAULT_THR -10.0f +#define UE_DL_NR_PDCCH_EPRE_DEFAULT_THR -80.0f static int ue_dl_nr_alloc_prb(srsran_ue_dl_nr_t* q, uint32_t new_nof_prb) { @@ -174,7 +174,9 @@ int srsran_ue_dl_nr_set_carrier(srsran_ue_dl_nr_t* q, const srsran_carrier_nr_t* return SRSRAN_SUCCESS; } -int srsran_ue_dl_nr_set_pdcch_config(srsran_ue_dl_nr_t* q, const srsran_ue_dl_nr_pdcch_cfg_t* cfg) +int srsran_ue_dl_nr_set_pdcch_config(srsran_ue_dl_nr_t* q, + const srsran_pdcch_cfg_nr_t* cfg, + const srsran_dci_cfg_nr_t* dci_cfg) { if (q == NULL || cfg == NULL) { return SRSRAN_ERROR_INVALID_INPUTS; @@ -185,11 +187,20 @@ int srsran_ue_dl_nr_set_pdcch_config(srsran_ue_dl_nr_t* q, const srsran_ue_dl_nr // iterate over all possible CORESET and initialise/update the present ones for (uint32_t i = 0; i < SRSRAN_UE_DL_NR_MAX_NOF_CORESET; i++) { - if (cfg->coreset_present[i]) { - if (srsran_dmrs_pdcch_estimator_init(&q->dmrs_pdcch[i], &q->carrier, &cfg->coreset[i]) < SRSRAN_SUCCESS) { - return SRSRAN_ERROR; - } + // Skip CORESET if not present + if (!cfg->coreset_present[i]) { + continue; } + + // Initialise estimator for the CORESET + if (srsran_dmrs_pdcch_estimator_init(&q->dmrs_pdcch[i], &q->carrier, &cfg->coreset[i]) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + } + + // Configure DCI sizes + if (srsran_dci_nr_set_cfg(&q->dci, dci_cfg) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; } return SRSRAN_SUCCESS; @@ -229,28 +240,28 @@ static int ue_dl_nr_find_dci_ncce(srsran_ue_dl_nr_t* q, return SRSRAN_ERROR; } SRSRAN_MEM_ZERO(pdcch_info, srsran_ue_dl_nr_pdcch_info_t, 1); - pdcch_info->coreset_id = dci_msg->coreset_id; - pdcch_info->ss_id = dci_msg->search_space; - pdcch_info->location = dci_msg->location; + pdcch_info->dci_ctx = dci_msg->ctx; + pdcch_info->nof_bits = dci_msg->nof_bits; srsran_dmrs_pdcch_measure_t* m = &pdcch_info->measure; // Measures the PDCCH transmission DMRS - if (srsran_dmrs_pdcch_get_measure(&q->dmrs_pdcch[coreset_id], &dci_msg->location, m) < SRSRAN_SUCCESS) { - ERROR("Error getting measure location L=%d, ncce=%d", dci_msg->location.L, dci_msg->location.ncce); + srsran_dci_location_t location = dci_msg->ctx.location; + if (srsran_dmrs_pdcch_get_measure(&q->dmrs_pdcch[coreset_id], &location, m) < SRSRAN_SUCCESS) { + ERROR("Error getting measure location L=%d, ncce=%d", location.L, location.ncce); return SRSRAN_ERROR; } // If measured correlation is invalid, early return if (!isnormal(m->norm_corr)) { - INFO("Discarded PDCCH candidate L=%d;ncce=%d; Invalid measurement;", dci_msg->location.L, dci_msg->location.ncce); + INFO("Discarded PDCCH candidate L=%d;ncce=%d; Invalid measurement;", location.L, location.ncce); return SRSRAN_SUCCESS; } // Compare EPRE with threshold if (m->epre_dBfs < q->pdcch_dmrs_epre_thr) { INFO("Discarded PDCCH candidate L=%d;ncce=%d; EPRE is too weak (%.1f<%.1f);", - dci_msg->location.L, - dci_msg->location.ncce, + location.L, + location.ncce, m->epre_dBfs, q->pdcch_dmrs_epre_thr); return SRSRAN_SUCCESS; @@ -259,8 +270,8 @@ static int ue_dl_nr_find_dci_ncce(srsran_ue_dl_nr_t* q, // Compare DMRS correlation with threshold if (m->norm_corr < q->pdcch_dmrs_corr_thr) { INFO("Discarded PDCCH candidate L=%d;ncce=%d; Correlation is too low (%.1f<%.1f); EPRE=%+.2f; RSRP=%+.2f;", - dci_msg->location.L, - dci_msg->location.ncce, + location.L, + location.ncce, m->norm_corr, q->pdcch_dmrs_corr_thr, m->epre_dBfs, @@ -269,7 +280,7 @@ static int ue_dl_nr_find_dci_ncce(srsran_ue_dl_nr_t* q, } // Extract PDCCH channel estimates - if (srsran_dmrs_pdcch_get_ce(&q->dmrs_pdcch[coreset_id], &dci_msg->location, q->pdcch_ce) < SRSRAN_SUCCESS) { + if (srsran_dmrs_pdcch_get_ce(&q->dmrs_pdcch[coreset_id], &location, q->pdcch_ce) < SRSRAN_SUCCESS) { ERROR("Error extracting PDCCH DMRS"); return SRSRAN_ERROR; } @@ -302,12 +313,15 @@ static bool find_dci_msg(srsran_dci_msg_nr_t* dci_msg, uint32_t nof_dci_msg, srs return found; } -static int ue_dl_nr_find_dl_dci_ss(srsran_ue_dl_nr_t* q, - const srsran_slot_cfg_t* slot_cfg, - const srsran_search_space_t* search_space, - uint16_t rnti, - srsran_rnti_type_t rnti_type) +static int ue_dl_nr_find_dci_ss(srsran_ue_dl_nr_t* q, + const srsran_slot_cfg_t* slot_cfg, + const srsran_search_space_t* search_space, + uint16_t rnti, + srsran_rnti_type_t rnti_type) { + uint32_t dci_sizes[SRSRAN_DCI_NR_MAX_NOF_SIZES] = {}; + uint32_t dci_sizes_count = 0; + // Select CORESET uint32_t coreset_id = search_space->coreset_id; if (coreset_id >= SRSRAN_UE_DL_NR_MAX_NOF_CORESET || !q->cfg.coreset_present[coreset_id]) { @@ -322,81 +336,124 @@ static int ue_dl_nr_find_dl_dci_ss(srsran_ue_dl_nr_t* q, return SRSRAN_ERROR; } - // Hard-coded values - srsran_dci_format_nr_t dci_format = srsran_dci_format_nr_1_0; + // Iterate all possible formats + for (uint32_t format_idx = 0; format_idx < SRSRAN_MIN(search_space->nof_formats, SRSRAN_DCI_FORMAT_NR_COUNT); + format_idx++) { + srsran_dci_format_nr_t dci_format = search_space->formats[format_idx]; - // Calculate number of DCI bits - int dci_nof_bits = srsran_dci_nr_format_1_0_sizeof(&q->carrier, coreset, rnti_type); - if (dci_nof_bits <= SRSRAN_SUCCESS) { - ERROR("Error DCI size"); - return SRSRAN_ERROR; - } - - // Iterate all possible aggregation levels - for (uint32_t L = 0; L < SRSRAN_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR && q->dci_msg_count < SRSRAN_MAX_DCI_MSG_NR; - L++) { - // Calculate possible PDCCH DCI candidates - uint32_t candidates[SRSRAN_SEARCH_SPACE_MAX_NOF_CANDIDATES_NR] = {}; - int nof_candidates = srsran_pdcch_nr_locations_coreset( - coreset, search_space, rnti, L, SRSRAN_SLOT_NR_MOD(q->carrier.numerology, slot_cfg->idx), candidates); - if (nof_candidates < SRSRAN_SUCCESS) { - ERROR("Error calculating DCI candidate location"); + // Calculate number of DCI bits + uint32_t dci_nof_bits = srsran_dci_nr_size(&q->dci, search_space->type, dci_format); + if (dci_nof_bits == 0) { + ERROR("Error DCI size"); return SRSRAN_ERROR; } - // Iterate over the candidates - for (int ncce_idx = 0; ncce_idx < nof_candidates && q->dci_msg_count < SRSRAN_MAX_DCI_MSG_NR; ncce_idx++) { - // Set DCI context - srsran_dci_msg_nr_t dci_msg = {}; - dci_msg.location.L = L; - dci_msg.location.ncce = candidates[ncce_idx]; - dci_msg.search_space = search_space->type; - dci_msg.coreset_id = search_space->coreset_id; - dci_msg.rnti_type = rnti_type; - dci_msg.rnti = rnti; - dci_msg.format = dci_format; - dci_msg.nof_bits = (uint32_t)dci_nof_bits; + // Skip DCI format if the size was already searched for the search space + bool skip = false; + for (uint32_t i = 0; i < dci_sizes_count && !skip; i++) { + if (dci_nof_bits == dci_sizes[i]) { + skip = true; + } + } + if (skip) { + continue; + } - // Find and decode PDCCH transmission in the given ncce - srsran_pdcch_nr_res_t res = {}; - if (ue_dl_nr_find_dci_ncce(q, &dci_msg, &res, coreset_id) < SRSRAN_SUCCESS) { + // Append size + if (dci_sizes_count >= SRSRAN_DCI_NR_MAX_NOF_SIZES) { + ERROR("Exceed maximum number of DCI sizes"); + return SRSRAN_ERROR; + } + dci_sizes[dci_sizes_count++] = dci_nof_bits; + + // Iterate all possible aggregation levels + for (uint32_t L = 0; + L < SRSRAN_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR && q->dl_dci_msg_count < SRSRAN_MAX_DCI_MSG_NR; + L++) { + // Calculate possible PDCCH DCI candidates + uint32_t candidates[SRSRAN_SEARCH_SPACE_MAX_NOF_CANDIDATES_NR] = {}; + int nof_candidates = srsran_pdcch_nr_locations_coreset( + coreset, search_space, rnti, L, SRSRAN_SLOT_NR_MOD(q->carrier.numerology, slot_cfg->idx), candidates); + if (nof_candidates < SRSRAN_SUCCESS) { + ERROR("Error calculating DCI candidate location"); return SRSRAN_ERROR; } - // If the CRC was not match, move to next candidate - if (!res.crc) { - continue; - } + // Iterate over the candidates + for (int ncce_idx = 0; ncce_idx < nof_candidates && q->dl_dci_msg_count < SRSRAN_MAX_DCI_MSG_NR; ncce_idx++) { + // Build DCI context + srsran_dci_ctx_t ctx = {}; + ctx.location.L = L; + ctx.location.ncce = candidates[ncce_idx]; + ctx.ss_type = search_space->type; + ctx.coreset_id = search_space->coreset_id; + ctx.rnti_type = rnti_type; + ctx.rnti = rnti; + ctx.format = dci_format; - // Detect if the DCI was a format 0_0 - if (!srsran_dci_nr_format_1_0_valid(&dci_msg)) { - // Change grant format to 0_0 - dci_msg.format = srsran_dci_format_nr_0_0; + // Build DCI message + srsran_dci_msg_nr_t dci_msg = {}; + dci_msg.ctx = ctx; + dci_msg.nof_bits = (uint32_t)dci_nof_bits; - // If the pending UL grant list is full or has the dci message, keep moving - if (q->pending_ul_dci_count >= SRSRAN_MAX_DCI_MSG_NR || - find_dci_msg(q->pending_ul_dci_msg, q->pending_ul_dci_count, &dci_msg)) { + // Find and decode PDCCH transmission in the given ncce + srsran_pdcch_nr_res_t res = {}; + if (ue_dl_nr_find_dci_ncce(q, &dci_msg, &res, coreset_id) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // If the CRC was not match, move to next candidate + if (!res.crc) { continue; } - // Save the grant in the pending UL grant list - q->pending_ul_dci_msg[q->pending_ul_dci_count] = dci_msg; - q->pending_ul_dci_count++; + // Detect if the DCI is the right direction + if (!srsran_dci_nr_valid_direction(&dci_msg)) { + // Change grant format direction + switch (dci_msg.ctx.format) { + case srsran_dci_format_nr_0_0: + dci_msg.ctx.format = srsran_dci_format_nr_1_0; + break; + case srsran_dci_format_nr_0_1: + dci_msg.ctx.format = srsran_dci_format_nr_1_1; + break; + case srsran_dci_format_nr_1_0: + dci_msg.ctx.format = srsran_dci_format_nr_0_0; + break; + case srsran_dci_format_nr_1_1: + dci_msg.ctx.format = srsran_dci_format_nr_0_1; + break; + default: + continue; + } + } - // Move to next candidate - continue; + // If UL grant, enqueue in UL list + if (dci_msg.ctx.format == srsran_dci_format_nr_0_0 || dci_msg.ctx.format == srsran_dci_format_nr_0_1) { + // If the pending UL grant list is full or has the dci message, keep moving + if (q->ul_dci_count >= SRSRAN_MAX_DCI_MSG_NR || find_dci_msg(q->ul_dci_msg, q->ul_dci_count, &dci_msg)) { + continue; + } + + // Save the grant in the pending UL grant list + q->ul_dci_msg[q->ul_dci_count] = dci_msg; + q->ul_dci_count++; + + // Move to next candidate + continue; + } + + // Check if the grant exists already in the DL list + if (find_dci_msg(q->dl_dci_msg, q->dl_dci_msg_count, &dci_msg)) { + // The same DCI is in the list, keep moving + continue; + } + + INFO("Found DCI in L=%d,ncce=%d", dci_msg.ctx.location.L, dci_msg.ctx.location.ncce); + // Append DCI message into the list + q->dl_dci_msg[q->dl_dci_msg_count] = dci_msg; + q->dl_dci_msg_count++; } - - // Check if the grant exists already in the message list - if (find_dci_msg(q->dci_msg, q->dci_msg_count, &dci_msg)) { - // The same DCI is in the list, keep moving - continue; - } - - INFO("Found DCI in L=%d,ncce=%d", dci_msg.location.L, dci_msg.location.ncce); - // Append DCI message into the list - q->dci_msg[q->dci_msg_count] = dci_msg; - q->dci_msg_count++; } } @@ -419,27 +476,27 @@ int srsran_ue_dl_nr_find_dl_dci(srsran_ue_dl_nr_t* q, nof_dci_msg = SRSRAN_MIN(nof_dci_msg, SRSRAN_MAX_DCI_MSG_NR); // Reset grant and blind search information counters - q->dci_msg_count = 0; + q->dl_dci_msg_count = 0; q->pdcch_info_count = 0; // If the UE looks for a RAR and RA search space is provided, search for it if (q->cfg.ra_search_space_present && rnti_type == srsran_rnti_type_ra) { // Find DCIs in the RA search space - int ret = ue_dl_nr_find_dl_dci_ss(q, slot_cfg, &q->cfg.ra_search_space, rnti, rnti_type); + int ret = ue_dl_nr_find_dci_ss(q, slot_cfg, &q->cfg.ra_search_space, rnti, rnti_type); if (ret < SRSRAN_SUCCESS) { ERROR("Error searching RAR DCI"); return SRSRAN_ERROR; } } else { // Iterate all possible common and UE search spaces - for (uint32_t i = 0; i < SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE && q->dci_msg_count < nof_dci_msg; i++) { + for (uint32_t i = 0; i < SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE && q->dl_dci_msg_count < nof_dci_msg; i++) { // Skip search space if not present if (!q->cfg.search_space_present[i]) { continue; } // Find DCIs in the selected search space - int ret = ue_dl_nr_find_dl_dci_ss(q, slot_cfg, &q->cfg.search_space[i], rnti, rnti_type); + int ret = ue_dl_nr_find_dci_ss(q, slot_cfg, &q->cfg.search_space[i], rnti, rnti_type); if (ret < SRSRAN_SUCCESS) { ERROR("Error searching DCI"); return SRSRAN_ERROR; @@ -448,10 +505,12 @@ int srsran_ue_dl_nr_find_dl_dci(srsran_ue_dl_nr_t* q, } // Convert found DCI messages into DL grants - uint32_t dci_msg_count = SRSRAN_MIN(nof_dci_msg, q->dci_msg_count); + uint32_t dci_msg_count = SRSRAN_MIN(nof_dci_msg, q->dl_dci_msg_count); for (uint32_t i = 0; i < dci_msg_count; i++) { - const srsran_coreset_t* coreset = &q->cfg.coreset[q->dci_msg[i].coreset_id]; - srsran_dci_nr_format_1_0_unpack(&q->carrier, coreset, &q->dci_msg[i], &dci_dl_list[i]); + if (srsran_dci_nr_dl_unpack(&q->dci, &q->dl_dci_msg[i], &dci_dl_list[i]) < SRSRAN_SUCCESS) { + ERROR("Error unpacking grant %d;", i); + return SRSRAN_ERROR; + } } return (int)dci_msg_count; @@ -472,15 +531,14 @@ int srsran_ue_dl_nr_find_ul_dci(srsran_ue_dl_nr_t* q, } // Get DCI messages from the pending list - for (uint32_t i = 0; i < q->pending_ul_dci_count && count < nof_dci_msg; i++) { - srsran_dci_msg_nr_t* dci_msg = &q->pending_ul_dci_msg[i]; + for (uint32_t i = 0; i < q->ul_dci_count && count < nof_dci_msg; i++) { + srsran_dci_msg_nr_t* dci_msg = &q->ul_dci_msg[i]; - if (dci_msg->rnti_type != rnti_type || dci_msg->rnti != rnti) { + if (dci_msg->ctx.rnti_type != rnti_type || dci_msg->ctx.rnti != rnti) { continue; } - const srsran_coreset_t* coreset = &q->cfg.coreset[dci_msg->coreset_id]; - if (srsran_dci_nr_format_0_0_unpack(&q->carrier, coreset, dci_msg, &dci_ul_list[count]) < SRSRAN_SUCCESS) { + if (srsran_dci_nr_ul_unpack(&q->dci, dci_msg, &dci_ul_list[count]) < SRSRAN_SUCCESS) { ERROR("Unpacking DCI 0_0"); continue; } @@ -488,7 +546,7 @@ int srsran_ue_dl_nr_find_ul_dci(srsran_ue_dl_nr_t* q, } // Reset pending UL grant list - q->pending_ul_dci_count = 0; + q->ul_dci_count = 0; return count; } @@ -647,7 +705,7 @@ static int ue_dl_nr_gen_ack_type2(const srsran_ue_dl_nr_harq_ack_cfg_t* cfg, int ue_dl_nr_pdsch_k1(const srsran_ue_dl_nr_harq_ack_cfg_t* cfg, const srsran_dci_dl_nr_t* dci_dl) { // For DCI format 1_0, the PDSCH-to-HARQ_feedback timing indicator field values map to {1, 2, 3, 4, 5, 6, 7, 8} - if (dci_dl->format == srsran_dci_format_nr_1_0) { + if (dci_dl->ctx.format == srsran_dci_format_nr_1_0) { return (int)dci_dl->harq_feedback + 1; } @@ -677,10 +735,10 @@ int srsran_ue_dl_nr_pdsch_ack_resource(const srsran_ue_dl_nr_harq_ack_cfg_t* cfg } // Fill PDSCH resource - pdsch_ack_resource->dci_format_1_1 = (dci_dl->format == srsran_dci_format_nr_1_1); + pdsch_ack_resource->dci_format_1_1 = (dci_dl->ctx.format == srsran_dci_format_nr_1_1); pdsch_ack_resource->k1 = k1; pdsch_ack_resource->v_dai_dl = dci_dl->dai; - pdsch_ack_resource->rnti = dci_dl->rnti; + pdsch_ack_resource->rnti = dci_dl->ctx.rnti; pdsch_ack_resource->pucch_resource_id = dci_dl->pucch_resource; return SRSRAN_SUCCESS; @@ -696,14 +754,14 @@ int srsran_ue_dl_nr_gen_ack(const srsran_ue_dl_nr_harq_ack_cfg_t* cfg, } // According TS 38.213 9.1.2 Type-1 HARQ-ACK codebook determination - if (cfg->pdsch_harq_ack_codebook == srsran_pdsch_harq_ack_codebook_semi_static) { + if (cfg->harq_ack_codebook == srsran_pdsch_harq_ack_codebook_semi_static) { // This clause applies if the UE is configured with pdsch-HARQ-ACK-Codebook = semi-static. ERROR("Type-1 HARQ-ACK codebook determination is NOT implemented"); return SRSRAN_ERROR; } // According TS 38.213 9.1.3 Type-2 HARQ-ACK codebook determination - if (cfg->pdsch_harq_ack_codebook == srsran_pdsch_harq_ack_codebook_dynamic) { + if (cfg->harq_ack_codebook == srsran_pdsch_harq_ack_codebook_dynamic) { // This clause applies if the UE is configured with pdsch-HARQ-ACK-Codebook = dynamic. return ue_dl_nr_gen_ack_type2(cfg, ack_info, uci_data); } @@ -711,3 +769,72 @@ int srsran_ue_dl_nr_gen_ack(const srsran_ue_dl_nr_harq_ack_cfg_t* cfg, ERROR("No HARQ-ACK codebook determination is NOT implemented"); return SRSRAN_ERROR; } + +int srsran_ue_dl_nr_ack_insert_m(srsran_pdsch_ack_nr_t* ack_info, srsran_pdsch_ack_m_nr_t* m) +{ + // Check inputs + if (ack_info == NULL || m == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Protect SCell index and extract information + if (m->resource.scell_idx >= SRSRAN_MAX_CARRIERS) { + ERROR("Serving cell index (%d) exceeds maximum", m->resource.scell_idx); + return SRSRAN_ERROR; + } + srsran_pdsch_ack_cc_nr_t* cc = &ack_info->cc[m->resource.scell_idx]; + + // Find insertion index + uint32_t idx = cc->M; // Append at the end by default + for (uint32_t i = 0; i < cc->M; i++) { + if (cc->m[i].resource.k1 < m->resource.k1) { + idx = i; + break; + } + } + + // Increment count + cc->M += 1; + + // Make space for insertion + for (uint32_t i = cc->M - 1; i > idx; i--) { + cc->m[i] = cc->m[i - 1]; + } + + // Actual insertion + cc->m[idx] = *m; + + return SRSRAN_SUCCESS; +} + +uint32_t srsran_ue_dl_nr_ack_info(const srsran_pdsch_ack_nr_t* ack_info, char* str, uint32_t str_len) +{ + uint32_t len = 0; + + if (ack_info == NULL || str == NULL) { + return 0; + } + + // Print base info + len = srsran_print_check( + str, str_len, len, "use_pusch=%c nof_cc=%d\n", ack_info->use_pusch ? 'y' : 'n', ack_info->nof_cc); + + // Iterate all carriers + for (uint32_t cc = 0; cc < ack_info->nof_cc; cc++) { + len = srsran_print_check(str, str_len, len, " CC %d: M=%d\n", cc, ack_info->cc[cc].M); + for (uint32_t m = 0; m < ack_info->cc[cc].M; m++) { + if (ack_info->cc[cc].m[m].present) { + len = srsran_print_check(str, + str_len, + len, + " m %d: k1=%d dai=%d ack=%d\n", + m, + ack_info->cc[cc].m[m].resource.k1, + ack_info->cc[cc].m[m].resource.v_dai_dl, + ack_info->cc[cc].m[m].value[0]); + } + } + } + + return len; +} diff --git a/lib/src/phy/ue/ue_ul_nr.c b/lib/src/phy/ue/ue_ul_nr.c index 16c997539..0130f464b 100644 --- a/lib/src/phy/ue/ue_ul_nr.c +++ b/lib/src/phy/ue/ue_ul_nr.c @@ -233,6 +233,7 @@ void srsran_ue_ul_nr_free(srsran_ue_ul_nr_t* q) if (q->sf_symbols[0] != NULL) { free(q->sf_symbols[0]); } + srsran_pucch_nr_free(&q->pucch); srsran_pusch_nr_free(&q->pusch); srsran_dmrs_sch_free(&q->dmrs); diff --git a/lib/src/upper/CMakeLists.txt b/lib/src/upper/CMakeLists.txt index 551c8e2b5..e6298679b 100644 --- a/lib/src/upper/CMakeLists.txt +++ b/lib/src/upper/CMakeLists.txt @@ -30,8 +30,9 @@ set(SOURCES gtpu.cc rlc_am_lte.cc pdcp_entity_nr.cc rlc_um_nr.cc - rlc_am_nr.cc) + rlc_am_nr.cc + bearer_mem_pool.cc) add_library(srsran_upper STATIC ${SOURCES}) target_link_libraries(srsran_upper srsran_common srsran_asn1) -INSTALL(TARGETS srsran_upper DESTINATION ${LIBRARY_DIR}) \ No newline at end of file +INSTALL(TARGETS srsran_upper DESTINATION ${LIBRARY_DIR}) diff --git a/lib/src/upper/bearer_mem_pool.cc b/lib/src/upper/bearer_mem_pool.cc new file mode 100644 index 000000000..764d8d122 --- /dev/null +++ b/lib/src/upper/bearer_mem_pool.cc @@ -0,0 +1,44 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsran/upper/bearer_mem_pool.h" +#include "srsran/adt/pool/batch_mem_pool.h" +#include "srsran/upper/rlc_am_lte.h" +#include "srsran/upper/rlc_um_lte.h" +#include "srsran/upper/rlc_um_nr.h" + +namespace srsran { + +srsran::background_mem_pool* get_bearer_pool() +{ + static background_mem_pool pool( + 4, std::max(std::max(sizeof(rlc_am_lte), sizeof(rlc_um_lte)), sizeof(rlc_um_nr)), 8, 8); + return &pool; +} + +void reserve_rlc_memblocks(size_t nof_blocks) +{ + srsran::background_mem_pool* pool = get_bearer_pool(); + while (pool->cache_size() < nof_blocks) { + pool->allocate_batch(); + } +} +void* allocate_rlc_bearer(std::size_t sz) +{ + return get_bearer_pool()->allocate_node(sz); +} +void deallocate_rlc_bearer(void* p) +{ + get_bearer_pool()->deallocate_node(p); +} + +} // namespace srsran diff --git a/lib/src/upper/pdcp.cc b/lib/src/upper/pdcp.cc index 1d9c6b81e..6fc9aad20 100644 --- a/lib/src/upper/pdcp.cc +++ b/lib/src/upper/pdcp.cc @@ -104,7 +104,12 @@ void pdcp::add_bearer(uint32_t lcid, pdcp_config_t cfg) if (not valid_lcid(lcid)) { std::unique_ptr entity; // For now we create an pdcp entity lte for nr due to it's maturity - entity.reset(new pdcp_entity_lte{rlc, rrc, gw, task_sched, logger, lcid, cfg}); + entity.reset(new pdcp_entity_lte{rlc, rrc, gw, task_sched, logger, lcid}); + if (not entity->configure(cfg)) { + logger.error("Can not configure PDCP entity"); + return; + } + if (not pdcp_array.insert(std::make_pair(lcid, std::move(entity))).second) { logger.error("Error inserting PDCP entity in to array."); return; @@ -126,10 +131,17 @@ void pdcp::add_bearer(uint32_t lcid, pdcp_config_t cfg) void pdcp::add_bearer_mrb(uint32_t lcid, pdcp_config_t cfg) { if (not valid_mch_lcid(lcid)) { + std::unique_ptr entity; + entity.reset(new pdcp_entity_lte{rlc, rrc, gw, task_sched, logger, lcid}); + if(not entity->configure(cfg)){ + logger.error("Can not configure PDCP entity"); + return; + } + if (not pdcp_array_mrb .insert(std::make_pair( lcid, - std::unique_ptr(new pdcp_entity_lte(rlc, rrc, gw, task_sched, logger, lcid, cfg)))) + std::move(entity))) .second) { logger.error("Error inserting PDCP entity in to array."); return; diff --git a/lib/src/upper/pdcp_entity_lte.cc b/lib/src/upper/pdcp_entity_lte.cc index 0357016bf..8d28c298e 100644 --- a/lib/src/upper/pdcp_entity_lte.cc +++ b/lib/src/upper/pdcp_entity_lte.cc @@ -38,30 +38,37 @@ pdcp_entity_lte::pdcp_entity_lte(srsue::rlc_interface_pdcp* rlc_, srsue::gw_interface_pdcp* gw_, srsran::task_sched_handle task_sched_, srslog::basic_logger& logger, - uint32_t lcid_, - pdcp_config_t cfg_) : + uint32_t lcid_) : pdcp_entity_base(task_sched_, logger), rlc(rlc_), rrc(rrc_), gw(gw_) { - lcid = lcid_; - cfg = cfg_; - active = true; + // Initial state integrity_direction = DIRECTION_NONE; encryption_direction = DIRECTION_NONE; + st.next_pdcp_tx_sn = 0; + st.tx_hfn = 0; + st.rx_hfn = 0; + st.next_pdcp_rx_sn = 0; + + lcid = lcid_; +} + +pdcp_entity_lte::~pdcp_entity_lte() +{ + reset(); +} + +bool pdcp_entity_lte::configure(const pdcp_config_t& cnfg_) +{ + cfg = cnfg_; + maximum_pdcp_sn = (1u << cfg.sn_len) - 1u; + st.last_submitted_pdcp_rx_sn = maximum_pdcp_sn; if (is_srb()) { reordering_window = 0; } else if (is_drb()) { reordering_window = 2048; } - // Initial state - st.next_pdcp_tx_sn = 0; - st.tx_hfn = 0; - st.rx_hfn = 0; - st.next_pdcp_rx_sn = 0; - maximum_pdcp_sn = (1u << cfg.sn_len) - 1u; - st.last_submitted_pdcp_rx_sn = maximum_pdcp_sn; - if (is_drb() && not rlc->rb_is_um(lcid) && cfg.discard_timer == pdcp_discard_timer_t::infinity) { logger.warning( "Setting discard timer to 1500ms, to avoid issues with lingering SDUs in the Unacknowledged SDUs map. LCID=%d", @@ -89,14 +96,11 @@ pdcp_entity_lte::pdcp_entity_lte(srsue::rlc_interface_pdcp* rlc_, // Check supported config if (!check_valid_config()) { srsran::console("Warning: Invalid PDCP config.\n"); + return false; } + active = true; + return true; } - -pdcp_entity_lte::~pdcp_entity_lte() -{ - reset(); -} - // Reestablishment procedure: 36.323 5.2 void pdcp_entity_lte::reestablish() { diff --git a/lib/src/upper/pdcp_entity_nr.cc b/lib/src/upper/pdcp_entity_nr.cc index 0ff2a1a9e..1986083b2 100644 --- a/lib/src/upper/pdcp_entity_nr.cc +++ b/lib/src/upper/pdcp_entity_nr.cc @@ -29,8 +29,7 @@ pdcp_entity_nr::pdcp_entity_nr(srsue::rlc_interface_pdcp* rlc_, srsue::gw_interface_pdcp* gw_, srsran::task_sched_handle task_sched_, srslog::basic_logger& logger, - uint32_t lcid_, - pdcp_config_t cfg_) : + uint32_t lcid_) : pdcp_entity_base(task_sched_, logger), rlc(rlc_), rrc(rrc_), @@ -38,20 +37,8 @@ pdcp_entity_nr::pdcp_entity_nr(srsue::rlc_interface_pdcp* rlc_, reordering_fnc(new pdcp_entity_nr::reordering_callback(this)) { lcid = lcid_; - cfg = cfg_; - active = true; integrity_direction = DIRECTION_NONE; encryption_direction = DIRECTION_NONE; - - window_size = 1 << (cfg.sn_len - 1); - - // Timers - reordering_timer = task_sched.get_unique_timer(); - - // configure timer - if (static_cast(cfg.t_reordering) > 0) { - reordering_timer.set(static_cast(cfg.t_reordering), *reordering_fnc); - } } pdcp_entity_nr::~pdcp_entity_nr() {} @@ -63,6 +50,22 @@ void pdcp_entity_nr::reestablish() // TODO } +bool pdcp_entity_nr::configure(const pdcp_config_t& cnfg_) +{ + cfg = cnfg_; + window_size = 1 << (cfg.sn_len - 1); + + // Timers + reordering_timer = task_sched.get_unique_timer(); + + // configure timer + if (static_cast(cfg.t_reordering) > 0) { + reordering_timer.set(static_cast(cfg.t_reordering), *reordering_fnc); + } + active = true; + return true; +} + // Used to stop/pause the entity (called on RRC conn release) void pdcp_entity_nr::reset() { diff --git a/lib/src/upper/rlc.cc b/lib/src/upper/rlc.cc index 9eb8593a7..eb08bd0bf 100644 --- a/lib/src/upper/rlc.cc +++ b/lib/src/upper/rlc.cc @@ -303,6 +303,8 @@ int rlc::read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) logger.warning("LCID %d doesn't exist.", lcid); } + srsran_expect(ret <= nof_bytes, "Created too big RLC PDU (%d > %d)", ret, nof_bytes); + return ret; } @@ -318,6 +320,8 @@ int rlc::read_pdu_mch(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) logger.warning("LCID %d doesn't exist.", lcid); } + srsran_expect(ret <= nof_bytes, "Created too big RLC PDU for MCH (%d > %d)", ret, nof_bytes); + return ret; } diff --git a/lib/src/upper/rlc_am_lte.cc b/lib/src/upper/rlc_am_lte.cc index 33e016732..0e31db352 100644 --- a/lib/src/upper/rlc_am_lte.cc +++ b/lib/src/upper/rlc_am_lte.cc @@ -858,11 +858,7 @@ int rlc_am_lte::rlc_am_lte_tx::build_segment(uint8_t* payload, uint32_t nof_byte retx_queue.front().so_start = retx.so_end; } - // increment counter for retx of first segment - if (retx.so_start == 0) { - tx_window[retx.sn].retx_count++; - } - + tx_window[retx.sn].retx_count++; check_sn_reached_max_retx(retx.sn); // Write header and pdu @@ -1149,7 +1145,7 @@ void rlc_am_lte::rlc_am_lte_tx::handle_control_pdu(uint8_t* payload, uint32_t no update_vt_a = false; if (tx_window.has_sn(i)) { auto& pdu = tx_window[i]; - if (!retx_queue.has_sn(i)) { + if (not retx_queue.has_sn(i)) { rlc_amd_retx_t& retx = retx_queue.push(); srsran_expect(tx_window[i].rlc_sn == i, "Incorrect RLC SN=%d!=%d being accessed", tx_window[i].rlc_sn, i); retx.sn = i; @@ -1185,6 +1181,8 @@ void rlc_am_lte::rlc_am_lte_tx::handle_control_pdu(uint8_t* payload, uint32_t no pdu.buf->N_bytes); } } + } else { + logger.info("%s NACKed SN=%d already considered for retransmission", RB_NAME, i); } } else { logger.warning("%s NACKed SN=%d already removed from Tx window", RB_NAME, i); @@ -2139,6 +2137,7 @@ buffered_pdcp_pdu_list::buffered_pdcp_pdu_list() : buffered_pdus(max_buffer_idx void buffered_pdcp_pdu_list::clear() { + count = 0; for (auto& b : buffered_pdus) { b.sn = invalid_sn; b.fully_acked = false; diff --git a/lib/test/adt/mem_pool_test.cc b/lib/test/adt/mem_pool_test.cc index 465f6b710..0dead24bc 100644 --- a/lib/test/adt/mem_pool_test.cc +++ b/lib/test/adt/mem_pool_test.cc @@ -21,6 +21,7 @@ #include "srsran/adt/pool/fixed_size_pool.h" #include "srsran/adt/pool/mem_pool.h" +#include "srsran/adt/pool/obj_pool.h" #include "srsran/common/test_common.h" class C @@ -30,13 +31,14 @@ public: ~C() { dtor_counter++; } void* operator new(size_t sz); + void* operator new(size_t sz, void*& ptr) { return ptr; } void operator delete(void* ptr)noexcept; - static int default_ctor_counter; - static int dtor_counter; + static std::atomic default_ctor_counter; + static std::atomic dtor_counter; }; -int C::default_ctor_counter = 0; -int C::dtor_counter = 0; +std::atomic C::default_ctor_counter(0); +std::atomic C::dtor_counter(0); srsran::big_obj_pool pool; @@ -50,7 +52,7 @@ void C::operator delete(void* ptr)noexcept pool.deallocate_node(ptr); } -int test_nontrivial_obj_pool() +void test_nontrivial_obj_pool() { // No object creation on reservation { @@ -81,8 +83,6 @@ int test_nontrivial_obj_pool() } TESTASSERT(C::default_ctor_counter == 1); TESTASSERT(C::dtor_counter == 1); - - return SRSRAN_SUCCESS; } struct BigObj { @@ -124,6 +124,7 @@ void test_fixedsize_pool() fixed_pool->print_all_buffers(); } fixed_pool->print_all_buffers(); + TESTASSERT(C::default_ctor_counter == C::dtor_counter); // TEST: one thread allocates, and the other deallocates { @@ -147,15 +148,47 @@ void test_fixedsize_pool() t.join(); } fixed_pool->print_all_buffers(); + TESTASSERT(C::default_ctor_counter == C::dtor_counter); +} + +struct D : public C { + char val = '\0'; +}; + +void test_background_pool() +{ + C::default_ctor_counter = 0; + C::dtor_counter = 0; + { + auto init_D_val = [](void* ptr) { + new (ptr) D(); + static_cast(ptr)->val = 'c'; + }; + srsran::background_obj_pool obj_pool(16, 4, 16, init_D_val); + TESTASSERT(obj_pool.cache_size() == 16); + std::vector > objs; + + for (size_t i = 0; i < 16 - 4; ++i) { + objs.push_back(obj_pool.make()); + } + TESTASSERT( + std::all_of(objs.begin(), objs.end(), [](const srsran::unique_pool_ptr& d) { return d->val == 'c'; })); + TESTASSERT(C::default_ctor_counter == 16); + + // This will trigger a new batch allocation in the background + objs.push_back(obj_pool.make()); + } + TESTASSERT(C::dtor_counter == C::default_ctor_counter); } int main(int argc, char** argv) { srsran::test_init(argc, argv); - TESTASSERT(test_nontrivial_obj_pool() == SRSRAN_SUCCESS); + test_nontrivial_obj_pool(); test_fixedsize_pool(); + test_background_pool(); - srsran::console("Success\n"); + printf("Success\n"); return 0; -} \ No newline at end of file +} diff --git a/lib/test/asn1/rrc_nr_utils_test.cc b/lib/test/asn1/rrc_nr_utils_test.cc index eeb6aea0a..a1bab9c1c 100644 --- a/lib/test/asn1/rrc_nr_utils_test.cc +++ b/lib/test/asn1/rrc_nr_utils_test.cc @@ -119,7 +119,7 @@ int make_phy_harq_ack_cfg_test() srsran_ue_dl_nr_harq_ack_cfg_t srsran_ue_dl_nr_harq_ack_cfg; TESTASSERT(make_phy_harq_ack_cfg(phys_cell_group_cfg, &srsran_ue_dl_nr_harq_ack_cfg) == true); - TESTASSERT(srsran_ue_dl_nr_harq_ack_cfg.pdsch_harq_ack_codebook == srsran_pdsch_harq_ack_codebook_dynamic); + TESTASSERT(srsran_ue_dl_nr_harq_ack_cfg.harq_ack_codebook == srsran_pdsch_harq_ack_codebook_dynamic); return SRSRAN_SUCCESS; } diff --git a/lib/test/asn1/srsran_asn1_rrc_ul_dcch_test.cc b/lib/test/asn1/srsran_asn1_rrc_ul_dcch_test.cc index 554e42127..182c4b30d 100644 --- a/lib/test/asn1/srsran_asn1_rrc_ul_dcch_test.cc +++ b/lib/test/asn1/srsran_asn1_rrc_ul_dcch_test.cc @@ -36,6 +36,25 @@ using namespace asn1::rrc; } \ } +int rrc_nr_test_scg_fail_packing() +{ + ul_dcch_msg_s ul_dcch_msg; + scg_fail_info_nr_r15_s& scg_fail_info_nr = ul_dcch_msg.msg.set_msg_class_ext().set_c2().set_scg_fail_info_nr_r15(); + scg_fail_info_nr.crit_exts.set_c1(); + scg_fail_info_nr.crit_exts.c1().set_scg_fail_info_nr_r15(); + + scg_fail_info_nr.crit_exts.c1().scg_fail_info_nr_r15().fail_report_scg_nr_r15_present = true; + scg_fail_info_nr.crit_exts.c1().scg_fail_info_nr_r15().fail_report_scg_nr_r15.fail_type_r15 = + fail_report_scg_nr_r15_s::fail_type_r15_opts::options::scg_recfg_fail; + uint8_t buf[64]; + bzero(buf, sizeof(buf)); + asn1::bit_ref bref(buf, sizeof(buf)); + TESTASSERT(ul_dcch_msg.pack(bref) == SRSRAN_SUCCESS); + bref.align_bytes_zero(); + uint32_t cap_len = (uint32_t)bref.distance_bytes(buf); + return SRSRAN_SUCCESS; +} + int rrc_ue_cap_info_test(srsran::mac_pcap* pcap) { auto& rrc_logger = srslog::fetch_basic_logger("RRC", false); @@ -169,9 +188,10 @@ int main(int argc, char** argv) pcap.open("ul_dcch.pcap"); TESTASSERT(rrc_ue_cap_info_test(&pcap) == 0); #else - TESTASSERT(rrc_ue_cap_info_test(NULL) == 0); + // TESTASSERT(rrc_ue_cap_info_test(NULL) == 0); #endif TESTASSERT(pack_fail_test() == -1); + TESTASSERT(rrc_nr_test_scg_fail_packing() == SRSRAN_SUCCESS) #if PCAP pcap.close(); diff --git a/lib/test/common/network_utils_test.cc b/lib/test/common/network_utils_test.cc index 267e05cfc..0bc7c8e1e 100644 --- a/lib/test/common/network_utils_test.cc +++ b/lib/test/common/network_utils_test.cc @@ -20,15 +20,32 @@ */ #include "srsran/common/network_utils.h" +#include "srsran/common/task_scheduler.h" +#include "srsran/common/test_common.h" +#include #include -#define TESTASSERT(cond) \ - do { \ - if (!(cond)) { \ - std::cout << "[" << __FUNCTION__ << "][Line " << __LINE__ << "]: FAIL at " << (#cond) << std::endl; \ - return -1; \ - } \ - } while (0) +struct rx_thread_tester { + srsran::task_scheduler task_sched; + srsran::task_queue_handle task_queue; + std::atomic stop_token; + std::thread t; + + rx_thread_tester() : + task_queue(task_sched.make_task_queue()), t([this]() { + stop_token.store(false); + while (not stop_token.load(std::memory_order_relaxed)) { + task_sched.run_pending_tasks(); + std::this_thread::sleep_for(std::chrono::microseconds(100)); + } + }) + {} + ~rx_thread_tester() + { + stop_token.store(true, std::memory_order_relaxed); + t.join(); + } +}; int test_socket_handler() { @@ -36,10 +53,10 @@ int test_socket_handler() int counter = 0; - srsran::socket_handler_t server_socket, client_socket, client_socket2; - srsran::rx_multisocket_handler sockhandler("RXSOCKETS", logger); - int server_port = 36412; - const char* server_addr = "127.0.100.1"; + srsran::unique_socket server_socket, client_socket, client_socket2; + srsran::socket_manager sockhandler; + int server_port = 36412; + const char* server_addr = "127.0.100.1"; using namespace srsran::net_utils; TESTASSERT(sctp_init_server(&server_socket, socket_type::seqpacket, server_addr, server_port)); @@ -59,7 +76,9 @@ int test_socket_handler() counter++; } }; - sockhandler.add_socket_sctp_pdu_handler(server_socket.fd(), pdu_handler); + rx_thread_tester rx_tester; + sockhandler.add_socket_handler(server_socket.fd(), + srsran::make_sctp_sdu_handler(logger, rx_tester.task_queue, pdu_handler)); uint8_t buf[128] = {}; int32_t nof_counts = 5; @@ -69,7 +88,7 @@ int test_socket_handler() for (int32_t i = 0; i < nof_counts; ++i) { buf[i] = i; // Round-robin between clients - srsran::socket_handler_t* chosen = &client_socket; + srsran::unique_socket* chosen = &client_socket; if (i % 2 == 1) { chosen = &client_socket2; } diff --git a/lib/test/mac/mac_pdu_nr_test.cc b/lib/test/mac/mac_pdu_nr_test.cc index b3d3af7cb..5b4a0d38a 100644 --- a/lib/test/mac/mac_pdu_nr_test.cc +++ b/lib/test/mac/mac_pdu_nr_test.cc @@ -335,6 +335,26 @@ int mac_rar_pdu_unpack_test8() return SRSRAN_SUCCESS; } +int mac_dl_sch_pdu_unpack_test9() +{ + // MAC PDU with Timing Advance CE and padding + uint8_t tv[] = {0x3d, 0x1f, 0x3f, 0x00, 0x00, 0x00}; + + if (pcap_handle) { + pcap_handle->write_dl_crnti_nr(tv, sizeof(tv), PCAP_CRNTI, true, PCAP_TTI); + } + + srsran::mac_sch_pdu_nr pdu; + pdu.unpack(tv, sizeof(tv)); + TESTASSERT(pdu.get_num_subpdus() == 2); + mac_sch_subpdu_nr subpdu = pdu.get_subpdu(0); + + TESTASSERT(subpdu.get_ta().tag_id == 0); + TESTASSERT(subpdu.get_ta().ta_command == 31); + + return SRSRAN_SUCCESS; +} + int mac_ul_sch_pdu_unpack_test1() { // UL-SCH MAC PDU with fixed-size CE and DL-SCH subheader with 16-bit length field @@ -666,6 +686,11 @@ int main(int argc, char** argv) return SRSRAN_ERROR; } + if (mac_dl_sch_pdu_unpack_test9()) { + fprintf(stderr, "mac_dl_sch_pdu_unpack_test9() failed.\n"); + return SRSRAN_ERROR; + } + if (mac_ul_sch_pdu_unpack_test1()) { fprintf(stderr, "mac_ul_sch_pdu_unpack_test1() failed.\n"); return SRSRAN_ERROR; diff --git a/lib/test/phy/phy_dl_nr_test.c b/lib/test/phy/phy_dl_nr_test.c index 6b88306e4..1fb1c7606 100644 --- a/lib/test/phy/phy_dl_nr_test.c +++ b/lib/test/phy/phy_dl_nr_test.c @@ -41,6 +41,7 @@ static uint32_t n_prb = 0; // Set to 0 for steering static uint32_t mcs = 30; // Set to 30 for steering static srsran_sch_cfg_nr_t pdsch_cfg = {}; static uint32_t nof_slots = 10; +static uint32_t rv_idx = 0; static void usage(char* prog) { @@ -49,6 +50,7 @@ static void usage(char* prog) printf("\t-p Number of grant PRB, set to 0 for steering [Default %d]\n", n_prb); printf("\t-n Number of slots to simulate [Default %d]\n", nof_slots); printf("\t-m MCS PRB, set to >28 for steering [Default %d]\n", mcs); + printf("\t-r Redundancy version, set to >28 for steering [Default %d]\n", mcs); printf("\t-T Provide MCS table (64qam, 256qam, 64qamLowSE) [Default %s]\n", srsran_mcs_table_to_str(pdsch_cfg.sch_cfg.mcs_table)); printf("\t-R Reserve RE: [rb_begin] [rb_end] [rb_stride] [sc_mask] [symbol_mask]\n"); @@ -59,7 +61,7 @@ static void usage(char* prog) static int parse_args(int argc, char** argv) { int opt; - while ((opt = getopt(argc, argv, "RPpmnTLv")) != -1) { + while ((opt = getopt(argc, argv, "rRPpmnTLv")) != -1) { switch (opt) { case 'P': carrier.nof_prb = (uint32_t)strtol(argv[optind], NULL, 10); @@ -73,6 +75,9 @@ static int parse_args(int argc, char** argv) case 'm': mcs = (uint32_t)strtol(argv[optind], NULL, 10); break; + case 'r': + rv_idx = (uint32_t)strtol(argv[optind], NULL, 10); + break; case 'T': pdsch_cfg.sch_cfg.mcs_table = srsran_mcs_table_from_str(argv[optind]); break; @@ -122,12 +127,12 @@ static int work_gnb_dl(srsran_enb_dl_nr_t* enb_dl, // Hard-coded values srsran_dci_dl_nr_t dci_dl = {}; - dci_dl.rnti = pdsch_cfg.grant.rnti; - dci_dl.rnti_type = pdsch_cfg.grant.rnti_type; - dci_dl.format = srsran_dci_format_nr_1_0; - dci_dl.location = *dci_location; - dci_dl.search_space = search_space->type; - dci_dl.coreset_id = 1; + dci_dl.ctx.rnti = pdsch_cfg.grant.rnti; + dci_dl.ctx.rnti_type = pdsch_cfg.grant.rnti_type; + dci_dl.ctx.format = srsran_dci_format_nr_1_0; + dci_dl.ctx.location = *dci_location; + dci_dl.ctx.ss_type = search_space->type; + dci_dl.ctx.coreset_id = 1; dci_dl.freq_domain_assigment = 0; dci_dl.time_domain_assigment = 0; dci_dl.vrb_to_prb_mapping = 0; @@ -220,7 +225,7 @@ int main(int argc, char** argv) goto clean_exit; } - srsran_ue_dl_nr_pdcch_cfg_t pdcch_cfg = {}; + srsran_pdcch_cfg_nr_t pdcch_cfg = {}; // Configure CORESET srsran_coreset_t* coreset = &pdcch_cfg.coreset[1]; @@ -236,6 +241,9 @@ int main(int argc, char** argv) search_space->id = 0; search_space->coreset_id = 1; search_space->type = srsran_search_space_type_common_3; + search_space->formats[0] = srsran_dci_format_nr_0_0; + search_space->formats[1] = srsran_dci_format_nr_1_0; + search_space->nof_formats = 2; for (uint32_t L = 0; L < SRSRAN_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR; L++) { search_space->nof_candidates[L] = srsran_pdcch_nr_max_candidates_coreset(coreset, L); } @@ -253,9 +261,14 @@ int main(int argc, char** argv) if (srsran_ue_dl_nr_set_carrier(&ue_dl, &carrier)) { ERROR("Error setting SCH NR carrier"); goto clean_exit; + goto clean_exit; } - if (srsran_ue_dl_nr_set_pdcch_config(&ue_dl, &pdcch_cfg)) { + srsran_dci_cfg_nr_t dci_cfg = {}; + dci_cfg.bwp_dl_initial_bw = carrier.nof_prb; + dci_cfg.bwp_ul_initial_bw = carrier.nof_prb; + dci_cfg.monitor_common_0_0 = true; + if (srsran_ue_dl_nr_set_pdcch_config(&ue_dl, &pdcch_cfg, &dci_cfg)) { ERROR("Error setting CORESET"); goto clean_exit; } @@ -265,7 +278,7 @@ int main(int argc, char** argv) goto clean_exit; } - if (srsran_enb_dl_nr_set_coreset(&enb_dl, coreset)) { + if (srsran_enb_dl_nr_set_pdcch_config(&enb_dl, &pdcch_cfg, &dci_cfg)) { ERROR("Error setting CORESET"); goto clean_exit; } @@ -307,6 +320,7 @@ int main(int argc, char** argv) pdsch_cfg.grant.nof_dmrs_cdm_groups_without_data = 1; pdsch_cfg.grant.rnti_type = srsran_rnti_type_c; pdsch_cfg.grant.rnti = 0x4601; + pdsch_cfg.grant.tb[0].rv = rv_idx; uint32_t n_prb_start = 1; uint32_t n_prb_end = carrier.nof_prb + 1; @@ -349,10 +363,14 @@ int main(int argc, char** argv) } // Compute PDCCH candidate locations - uint32_t L = 0; + uint32_t L = 1; uint32_t ncce_candidates[SRSRAN_SEARCH_SPACE_MAX_NOF_CANDIDATES_NR] = {}; - int nof_candidates = srsran_pdcch_nr_locations_coreset( - coreset, search_space, pdsch_cfg.grant.rnti, L, slot.idx, ncce_candidates); + int nof_candidates = srsran_pdcch_nr_locations_coreset(coreset, + search_space, + pdsch_cfg.grant.rnti, + L, + SRSRAN_SLOT_NR_MOD(carrier.numerology, slot.idx), + ncce_candidates); if (nof_candidates < SRSRAN_SUCCESS) { ERROR("Error getting PDCCH candidates"); goto clean_exit; @@ -391,18 +409,21 @@ int main(int argc, char** argv) goto clean_exit; } - if (!pdsch_res[0].crc) { - ERROR("Failed to match CRC; n_prb=%d; mcs=%d; TBS=%d;", n_prb, mcs, pdsch_cfg.grant.tb[0].tbs); - goto clean_exit; - } + // Check CRC only for RV=0 + if (rv_idx == 0) { + if (!pdsch_res[0].crc) { + ERROR("Failed to match CRC; n_prb=%d; mcs=%d; TBS=%d;", n_prb, mcs, pdsch_cfg.grant.tb[0].tbs); + goto clean_exit; + } - if (memcmp(data_tx[0], data_rx[0], pdsch_cfg.grant.tb[0].tbs / 8) != 0) { - ERROR("Failed to match Tx/Rx data; n_prb=%d; mcs=%d; TBS=%d;", n_prb, mcs, pdsch_cfg.grant.tb[0].tbs); - printf("Tx data: "); - srsran_vec_fprint_byte(stdout, data_tx[0], pdsch_cfg.grant.tb[0].tbs / 8); - printf("Rx data: "); - srsran_vec_fprint_byte(stdout, data_rx[0], pdsch_cfg.grant.tb[0].tbs / 8); - goto clean_exit; + if (memcmp(data_tx[0], data_rx[0], pdsch_cfg.grant.tb[0].tbs / 8) != 0) { + ERROR("Failed to match Tx/Rx data; n_prb=%d; mcs=%d; TBS=%d;", n_prb, mcs, pdsch_cfg.grant.tb[0].tbs); + printf("Tx data: "); + srsran_vec_fprint_byte(stdout, data_tx[0], pdsch_cfg.grant.tb[0].tbs / 8); + printf("Rx data: "); + srsran_vec_fprint_byte(stdout, data_rx[0], pdsch_cfg.grant.tb[0].tbs / 8); + goto clean_exit; + } } INFO("n_prb=%d; mcs=%d; TBS=%d; EVM=%f; PASSED!", n_prb, mcs, pdsch_cfg.grant.tb[0].tbs, pdsch_res[0].evm); diff --git a/lib/test/upper/pdcp_lte_test.h b/lib/test/upper/pdcp_lte_test.h index 4b756a6a0..051c1c6f0 100644 --- a/lib/test/upper/pdcp_lte_test.h +++ b/lib/test/upper/pdcp_lte_test.h @@ -69,8 +69,9 @@ class pdcp_lte_test_helper { public: pdcp_lte_test_helper(srsran::pdcp_config_t cfg, srsran::as_security_config_t sec_cfg_, srslog::basic_logger& logger) : - rlc(logger), rrc(logger), gw(logger), pdcp(&rlc, &rrc, &gw, &stack.task_sched, logger, 0, cfg) + rlc(logger), rrc(logger), gw(logger), pdcp(&rlc, &rrc, &gw, &stack.task_sched, logger, 0) { + pdcp.configure(cfg); pdcp.config_security(sec_cfg_); pdcp.enable_integrity(srsran::DIRECTION_TXRX); pdcp.enable_encryption(srsran::DIRECTION_TXRX); diff --git a/lib/test/upper/pdcp_nr_test.h b/lib/test/upper/pdcp_nr_test.h index 317c2abc0..eb9aa61ac 100644 --- a/lib/test/upper/pdcp_nr_test.h +++ b/lib/test/upper/pdcp_nr_test.h @@ -97,8 +97,9 @@ class pdcp_nr_test_helper { public: pdcp_nr_test_helper(srsran::pdcp_config_t cfg, srsran::as_security_config_t sec_cfg_, srslog::basic_logger& logger) : - rlc(logger), rrc(logger), gw(logger), pdcp(&rlc, &rrc, &gw, &stack.task_sched, logger, 0, cfg) + rlc(logger), rrc(logger), gw(logger), pdcp(&rlc, &rrc, &gw, &stack.task_sched, logger, 0) { + pdcp.configure(cfg); pdcp.config_security(sec_cfg_); pdcp.enable_integrity(srsran::DIRECTION_TXRX); pdcp.enable_encryption(srsran::DIRECTION_TXRX); diff --git a/srsenb/hdr/stack/upper/common_enb.h b/srsenb/hdr/common/common_enb.h similarity index 99% rename from srsenb/hdr/stack/upper/common_enb.h rename to srsenb/hdr/common/common_enb.h index 53a2c77b0..56fe80378 100644 --- a/srsenb/hdr/stack/upper/common_enb.h +++ b/srsenb/hdr/common/common_enb.h @@ -26,6 +26,7 @@ INCLUDES *******************************************************************************/ +#include #include namespace srsenb { diff --git a/srsenb/hdr/common/rnti_pool.h b/srsenb/hdr/common/rnti_pool.h new file mode 100644 index 000000000..cdd15c8ce --- /dev/null +++ b/srsenb/hdr/common/rnti_pool.h @@ -0,0 +1,48 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSRAN_RNTI_POOL_H +#define SRSRAN_RNTI_POOL_H + +#include "srsran/adt/pool/pool_interface.h" +#include "srsran/phy/common/phy_common.h" +#include + +namespace srsenb { + +// Allocation of objects in rnti-dedicated memory pool +void reserve_rnti_memblocks(size_t nof_blocks); +void* allocate_rnti_dedicated_mem(uint16_t rnti, std::size_t size, std::size_t align); +void deallocate_rnti_dedicated_mem(uint16_t rnti, void* p); + +template +using unique_rnti_ptr = srsran::unique_pool_ptr; + +template +unique_rnti_ptr make_rnti_obj(uint16_t rnti, Args&&... args) +{ + void* block = allocate_rnti_dedicated_mem(rnti, sizeof(T), alignof(T)); + if (block == nullptr) { + // allocated with "new" as a fallback + return unique_rnti_ptr(new T(std::forward(args)...), std::default_delete()); + } + // allocation using rnti-dedicated memory pool was successful + new (block) T(std::forward(args)...); + return unique_rnti_ptr(static_cast(block), [rnti](T* ptr) { + ptr->~T(); + deallocate_rnti_dedicated_mem(rnti, ptr); + }); +} + +} // namespace srsenb + +#endif // SRSRAN_RNTI_POOL_H diff --git a/srsenb/hdr/phy/nr/cc_worker.h b/srsenb/hdr/phy/nr/cc_worker.h index 7adaa9435..f0837fe49 100644 --- a/srsenb/hdr/phy/nr/cc_worker.h +++ b/srsenb/hdr/phy/nr/cc_worker.h @@ -23,8 +23,10 @@ #define SRSENB_NR_CC_WORKER_H #include "srsran/interfaces/gnb_interfaces.h" +#include "srsran/interfaces/rrc_nr_interface_types.h" #include "srsran/phy/enb/enb_dl_nr.h" #include "srsran/srslog/srslog.h" +#include "srsran/srsran.h" #include #include @@ -36,15 +38,11 @@ typedef struct { srsran_enb_dl_nr_args_t dl; } phy_nr_args_t; -typedef struct { - srsran_sch_hl_cfg_nr_t pdsch; -} phy_nr_cfg_t; - class phy_nr_state { public: - phy_nr_args_t args = {}; - phy_nr_cfg_t cfg = {}; + phy_nr_args_t args = {}; + srsran::phy_cfg_nr_t cfg = {}; phy_nr_state() { @@ -53,8 +51,7 @@ public: args.dl.nof_tx_antennas = 1; args.dl.pdsch.measure_evm = true; args.dl.pdsch.measure_time = true; - args.dl.pdsch.sch.disable_simd = true; - cfg.pdsch.sch_cfg.mcs_table = srsran_mcs_table_256qam; + args.dl.pdsch.sch.disable_simd = false; } }; diff --git a/srsenb/hdr/phy/prach_worker.h b/srsenb/hdr/phy/prach_worker.h index d87efe6f4..a4f3924d4 100644 --- a/srsenb/hdr/phy/prach_worker.h +++ b/srsenb/hdr/phy/prach_worker.h @@ -27,6 +27,7 @@ #include "srsran/common/threads.h" #include "srsran/interfaces/enb_phy_interfaces.h" #include "srsran/srslog/srslog.h" +#include // Setting ENABLE_PRACH_GUI to non zero enables a GUI showing signal received in the PRACH window. #define ENABLE_PRACH_GUI 0 @@ -97,10 +98,10 @@ private: stack_interface_phy_lte* stack = nullptr; float max_prach_offset_us = 0.0f; bool initiated = false; - bool running = false; - uint32_t nof_sf = 0; - uint32_t sf_cnt = 0; - uint32_t nof_workers = 0; + std::atomic running; + uint32_t nof_sf = 0; + uint32_t sf_cnt = 0; + uint32_t nof_workers = 0; void run_thread() final; int run_tti(sf_buffer* b); diff --git a/srsenb/hdr/phy/txrx.h b/srsenb/hdr/phy/txrx.h index 42bd273e1..2fc6ff402 100644 --- a/srsenb/hdr/phy/txrx.h +++ b/srsenb/hdr/phy/txrx.h @@ -29,6 +29,7 @@ #include "srsran/config.h" #include "srsran/phy/channel/channel.h" #include "srsran/radio/radio.h" +#include namespace srsenb { @@ -60,9 +61,9 @@ private: // Main system TTI counter uint32_t tti = 0; - uint32_t tx_worker_cnt = 0; - uint32_t nof_workers = 0; - bool running = false; + uint32_t tx_worker_cnt = 0; + uint32_t nof_workers = 0; + std::atomic running; }; } // namespace srsenb diff --git a/srsenb/hdr/stack/enb_stack_lte.h b/srsenb/hdr/stack/enb_stack_lte.h index 3f73d823e..b11caa948 100644 --- a/srsenb/hdr/stack/enb_stack_lte.h +++ b/srsenb/hdr/stack/enb_stack_lte.h @@ -42,11 +42,7 @@ namespace srsenb { -class enb_stack_lte final : public enb_stack_base, - public stack_interface_phy_lte, - public stack_interface_s1ap_lte, - public stack_interface_gtpu_lte, - public srsran::thread +class enb_stack_lte final : public enb_stack_base, public stack_interface_phy_lte, public srsran::thread { public: enb_stack_lte(srslog::sink& log_sink); @@ -106,27 +102,19 @@ public: } void tti_clock() override; - /* STACK-S1AP interface*/ - void add_mme_socket(int fd) override; - void remove_mme_socket(int fd) override; - void add_gtpu_s1u_socket_handler(int fd) override; - void add_gtpu_m1u_socket_handler(int fd) override; - private: static const int STACK_MAIN_THREAD_PRIO = 4; // thread loop void run_thread() override; void stop_impl(); void tti_clock_impl(); - void handle_mme_rx_packet(srsran::unique_byte_buffer_t pdu, - const sockaddr_in& from, - const sctp_sndrcvinfo& sri, - int flags); // args stack_args_t args = {}; rrc_cfg_t rrc_cfg = {}; + srsran::socket_manager rx_sockets; + srslog::basic_logger& mac_logger; srslog::basic_logger& rlc_logger; srslog::basic_logger& pdcp_logger; @@ -144,9 +132,6 @@ private: srsran::task_scheduler task_sched; srsran::task_queue_handle enb_task_queue, gtpu_task_queue, mme_task_queue, sync_task_queue; - // components that layers depend on (need to be destroyed after layers) - std::unique_ptr rx_sockets; - srsenb::mac mac; srsenb::rlc rlc; srsenb::pdcp pdcp; diff --git a/srsenb/hdr/stack/mac/mac.h b/srsenb/hdr/stack/mac/mac.h index d667dbb60..bcb05b3eb 100644 --- a/srsenb/hdr/stack/mac/mac.h +++ b/srsenb/hdr/stack/mac/mac.h @@ -25,6 +25,7 @@ #include "sched.h" #include "srsenb/hdr/stack/mac/schedulers/sched_time_rr.h" #include "srsran/adt/circular_map.h" +#include "srsran/adt/pool/batch_mem_pool.h" #include "srsran/common/mac_pcap.h" #include "srsran/common/mac_pcap_net.h" #include "srsran/common/task_scheduler.h" @@ -189,6 +190,9 @@ private: // Number of rach preambles detected for a cc. std::vector detected_rachs; + + // Softbuffer pool + std::unique_ptr > softbuffer_pool; }; } // namespace srsenb diff --git a/srsenb/hdr/stack/mac/ue.h b/srsenb/hdr/stack/mac/ue.h index b5e6cf5a8..4cb597e0a 100644 --- a/srsenb/hdr/stack/mac/ue.h +++ b/srsenb/hdr/stack/mac/ue.h @@ -24,6 +24,8 @@ #include "mac_metrics.h" #include "srsran/adt/circular_array.h" +#include "srsran/adt/circular_map.h" +#include "srsran/adt/pool/pool_interface.h" #include "srsran/common/block_queue.h" #include "srsran/common/mac_pcap.h" #include "srsran/common/mac_pcap_net.h" @@ -43,10 +45,34 @@ class rrc_interface_mac; class rlc_interface_mac; class phy_interface_stack_lte; +struct ue_cc_softbuffers { + // List of Tx softbuffers for all HARQ processes of one carrier + using cc_softbuffer_tx_list_t = std::vector; + // List of Rx softbuffers for all HARQ processes of one carrier + using cc_softbuffer_rx_list_t = std::vector; + + const uint32_t nof_tx_harq_proc; + const uint32_t nof_rx_harq_proc; + cc_softbuffer_tx_list_t softbuffer_tx_list; + cc_softbuffer_rx_list_t softbuffer_rx_list; + + ue_cc_softbuffers(uint32_t nof_prb, uint32_t nof_tx_harq_proc_, uint32_t nof_rx_harq_proc_); + ue_cc_softbuffers(ue_cc_softbuffers&&) noexcept = default; + ~ue_cc_softbuffers(); + void clear(); + + srsran_softbuffer_tx_t& get_tx(uint32_t pid, uint32_t tb_idx) + { + return softbuffer_tx_list.at(pid * SRSRAN_MAX_TB + tb_idx); + } + srsran_softbuffer_rx_t& get_rx(uint32_t tti) { return softbuffer_rx_list.at(tti % nof_rx_harq_proc); } +}; + class cc_used_buffers_map { public: explicit cc_used_buffers_map(srsran::pdu_queue& shared_pdu_queue_); + ~cc_used_buffers_map(); uint8_t* request_pdu(tti_point tti, uint32_t len); @@ -68,30 +94,25 @@ private: srslog::basic_logger* logger; srsran::pdu_queue* shared_pdu_queue; - srsran::circular_array, SRSRAN_FDD_NOF_HARQ * 8> pdu_map; + srsran::static_circular_map pdu_map; }; class cc_buffer_handler { public: - // List of Tx softbuffers for all HARQ processes of one carrier - using cc_softbuffer_tx_list_t = std::vector; - // List of Rx softbuffers for all HARQ processes of one carrier - using cc_softbuffer_rx_list_t = std::vector; - explicit cc_buffer_handler(srsran::pdu_queue& shared_pdu_queue_); ~cc_buffer_handler(); void reset(); - void allocate_cc(uint32_t nof_prb, uint32_t nof_rx_harq_proc, uint32_t nof_tx_harq_proc); + void allocate_cc(srsran::unique_pool_ptr cc_softbuffers_); void deallocate_cc(); - bool empty() const { return softbuffer_tx_list.empty() and softbuffer_rx_list.empty(); } + bool empty() const { return cc_softbuffers == nullptr; } srsran_softbuffer_tx_t& get_tx_softbuffer(uint32_t pid, uint32_t tb_idx) { - return softbuffer_tx_list.at(pid * SRSRAN_MAX_TB + tb_idx); + return cc_softbuffers->get_tx(pid, tb_idx); } - srsran_softbuffer_rx_t& get_rx_softbuffer(uint32_t tti) { return softbuffer_rx_list.at(tti % nof_rx_harq_proc); } + srsran_softbuffer_rx_t& get_rx_softbuffer(uint32_t tti) { return cc_softbuffers->get_rx(tti); } srsran::byte_buffer_t* get_tx_payload_buffer(size_t harq_pid, size_t tb) { return tx_payload_buffer[harq_pid][tb].get(); @@ -99,15 +120,11 @@ public: cc_used_buffers_map& get_rx_used_buffers() { return rx_used_buffers; } private: - // args - uint32_t nof_prb; - uint32_t nof_rx_harq_proc; - uint32_t nof_tx_harq_proc; + // CC softbuffers + srsran::unique_pool_ptr cc_softbuffers; // buffers - cc_softbuffer_tx_list_t softbuffer_tx_list; ///< List of softbuffer lists for Tx - cc_softbuffer_rx_list_t softbuffer_rx_list; ///< List of softbuffer lists for Rx - cc_used_buffers_map rx_used_buffers; + cc_used_buffers_map rx_used_buffers; // One buffer per TB per HARQ process and per carrier is needed for each UE. std::array, SRSRAN_FDD_NOF_HARQ> tx_payload_buffer; @@ -116,16 +133,15 @@ private: class ue : public srsran::read_pdu_interface, public srsran::pdu_queue::process_callback, public mac_ta_ue_interface { public: - ue(uint16_t rnti, - uint32_t nof_prb, - sched_interface* sched, - rrc_interface_mac* rrc_, - rlc_interface_mac* rlc, - phy_interface_stack_lte* phy_, - srslog::basic_logger& logger, - uint32_t nof_cells_, - uint32_t nof_rx_harq_proc = SRSRAN_FDD_NOF_HARQ, - uint32_t nof_tx_harq_proc = SRSRAN_FDD_NOF_HARQ); + ue(uint16_t rnti, + uint32_t nof_prb, + sched_interface* sched, + rrc_interface_mac* rrc_, + rlc_interface_mac* rlc, + phy_interface_stack_lte* phy_, + srslog::basic_logger& logger, + uint32_t nof_cells_, + srsran::obj_pool_itf* softbuffer_pool); virtual ~ue(); void reset(); @@ -148,7 +164,7 @@ public: generate_mch_pdu(uint32_t harq_pid, sched_interface::dl_pdu_mch_t sched, uint32_t nof_pdu_elems, uint32_t grant_size); srsran_softbuffer_tx_t* - get_tx_softbuffer(const uint32_t ue_cc_idx, const uint32_t harq_process, const uint32_t tb_idx); + get_tx_softbuffer(const uint32_t ue_cc_idx, const uint32_t harq_process, const uint32_t tb_idx); srsran_softbuffer_rx_t* get_rx_softbuffer(const uint32_t ue_cc_idx, const uint32_t tti); bool process_pdus(); @@ -174,25 +190,26 @@ private: bool process_ce(srsran::sch_subh* subh); void allocate_ce(srsran::sch_pdu* pdu, uint32_t lcid); + rlc_interface_mac* rlc = nullptr; + rrc_interface_mac* rrc = nullptr; + phy_interface_stack_lte* phy = nullptr; + srslog::basic_logger& logger; + sched_interface* sched = nullptr; + + srsran::mac_pcap* pcap = nullptr; + srsran::mac_pcap_net* pcap_net = nullptr; + uint64_t conres_id = 0; + uint16_t rnti = 0; + uint32_t last_tti = 0; + uint32_t nof_failures = 0; + uint32_t phr_counter = 0; uint32_t dl_cqi_counter = 0; uint32_t dl_ri_counter = 0; uint32_t dl_pmi_counter = 0; mac_ue_metrics_t ue_metrics = {}; - srsran::mac_pcap* pcap = nullptr; - srsran::mac_pcap_net* pcap_net = nullptr; - uint64_t conres_id = 0; - uint16_t rnti = 0; - uint32_t nof_prb = 0; - uint32_t last_tti = 0; - uint32_t nof_failures = 0; - int nof_rx_harq_proc = 0; - int nof_tx_harq_proc = 0; - - srsran::bounded_vector cc_buffers; - - std::mutex rx_buffers_mutex; + srsran::obj_pool_itf* softbuffer_pool = nullptr; srsran::block_queue pending_ta_commands; ta ta_fsm; @@ -202,14 +219,11 @@ private: srsran::sch_pdu mac_msg_dl, mac_msg_ul; srsran::mch_pdu mch_mac_msg_dl; - rlc_interface_mac* rlc = nullptr; - rrc_interface_mac* rrc = nullptr; - phy_interface_stack_lte* phy = nullptr; - srslog::basic_logger& logger; - sched_interface* sched = nullptr; + srsran::bounded_vector cc_buffers; // Mutexes std::mutex mutex; + std::mutex rx_buffers_mutex; const uint8_t UL_CC_IDX = 0; ///< Passed to write CC index in PCAP (TODO: use actual CC idx) }; diff --git a/srsenb/hdr/stack/rrc/rrc.h b/srsenb/hdr/stack/rrc/rrc.h index 7861cd42e..0e841c7b0 100644 --- a/srsenb/hdr/stack/rrc/rrc.h +++ b/srsenb/hdr/stack/rrc/rrc.h @@ -25,7 +25,8 @@ #include "rrc_bearer_cfg.h" #include "rrc_cell_cfg.h" #include "rrc_metrics.h" -#include "srsenb/hdr/stack/upper/common_enb.h" +#include "srsenb/hdr/common/common_enb.h" +#include "srsenb/hdr/common/rnti_pool.h" #include "srsran/adt/circular_buffer.h" #include "srsran/common/buffer_pool.h" #include "srsran/common/common.h" @@ -165,7 +166,7 @@ private: // state std::unique_ptr cell_res_list; - std::map > users; // NOTE: has to have fixed addr + std::map > users; // NOTE: has to have fixed addr std::map pending_paging; void process_release_complete(uint16_t rnti); diff --git a/srsenb/hdr/stack/rrc/rrc_metrics.h b/srsenb/hdr/stack/rrc/rrc_metrics.h index 9686529ee..31dc0dccf 100644 --- a/srsenb/hdr/stack/rrc/rrc_metrics.h +++ b/srsenb/hdr/stack/rrc/rrc_metrics.h @@ -22,7 +22,7 @@ #ifndef SRSENB_RRC_METRICS_H #define SRSENB_RRC_METRICS_H -#include "srsenb/hdr/stack/upper/common_enb.h" +#include "srsenb/hdr/common/common_enb.h" #include namespace srsenb { diff --git a/srsenb/hdr/stack/rrc/rrc_ue.h b/srsenb/hdr/stack/rrc/rrc_ue.h index 0153d0985..5bbcbde31 100644 --- a/srsenb/hdr/stack/rrc/rrc_ue.h +++ b/srsenb/hdr/stack/rrc/rrc_ue.h @@ -24,7 +24,7 @@ #include "mac_controller.h" #include "rrc.h" -#include "srsran/adt/pool/mem_pool.h" +#include "srsran/adt/pool/batch_mem_pool.h" #include "srsran/interfaces/enb_phy_interfaces.h" #include "srsran/interfaces/pdcp_interface_types.h" @@ -153,18 +153,10 @@ public: rrc* parent = nullptr; bool connect_notified = false; - std::unique_ptr mobility_handler; + unique_rnti_ptr mobility_handler; bool is_csfb = false; - void* operator new(size_t sz); - void* operator new[](size_t sz) = delete; - void operator delete(void* ptr)noexcept; - void operator delete[](void* ptr) = delete; - - using ue_pool_t = srsran::background_allocator_obj_pool; - static ue_pool_t* get_ue_pool(); - private: // args srsran::timer_handler::unique_timer activity_timer; @@ -196,7 +188,6 @@ private: const static uint32_t UE_PCELL_CC_IDX = 0; uint32_t consecutive_kos = 0; - uint32_t max_mac_dl_retx; ue_cell_ded_list ue_cell_list; bearer_cfg_handler bearer_list; diff --git a/srsenb/hdr/stack/upper/gtpu.h b/srsenb/hdr/stack/upper/gtpu.h index 491c97071..01c41ad41 100644 --- a/srsenb/hdr/stack/upper/gtpu.h +++ b/srsenb/hdr/stack/upper/gtpu.h @@ -22,10 +22,11 @@ #include #include -#include "common_enb.h" +#include "srsenb/hdr/common/common_enb.h" #include "srsran/adt/bounded_vector.h" #include "srsran/adt/circular_map.h" #include "srsran/common/buffer_pool.h" +#include "srsran/common/network_utils.h" #include "srsran/common/task_scheduler.h" #include "srsran/common/threads.h" #include "srsran/interfaces/enb_gtpu_interfaces.h" @@ -127,8 +128,8 @@ private: pdcp_interface_gtpu* pdcp = nullptr; srslog::basic_logger& logger; - tunnel_list_t tunnels; srsran::static_circular_map ue_teidin_db; + tunnel_list_t tunnels; }; using gtpu_tunnel_state = gtpu_tunnel_manager::tunnel_state; @@ -137,16 +138,17 @@ using gtpu_tunnel = gtpu_tunnel_manager::tunnel; class gtpu final : public gtpu_interface_rrc, public gtpu_interface_pdcp { public: - explicit gtpu(srsran::task_sched_handle task_sched_, srslog::basic_logger& logger); + explicit gtpu(srsran::task_sched_handle task_sched_, + srslog::basic_logger& logger, + srsran::socket_manager_itf* rx_socket_handler_); ~gtpu(); - int init(std::string gtp_bind_addr_, - std::string mme_addr_, - std::string m1u_multiaddr_, - std::string m1u_if_addr_, - pdcp_interface_gtpu* pdcp_, - stack_interface_gtpu_lte* stack_, - bool enable_mbsfn = false); + int init(std::string gtp_bind_addr_, + std::string mme_addr_, + std::string m1u_multiaddr_, + std::string m1u_if_addr_, + pdcp_interface_gtpu* pdcp_, + bool enable_mbsfn = false); void stop(); // gtpu_interface_rrc @@ -172,7 +174,8 @@ private: void rem_tunnel(uint32_t teidin); - stack_interface_gtpu_lte* stack = nullptr; + srsran::socket_manager_itf* rx_socket_handler = nullptr; + srsran::task_queue_handle gtpu_queue; bool enable_mbsfn = false; std::string gtp_bind_addr; diff --git a/srsenb/hdr/stack/upper/pdcp.h b/srsenb/hdr/stack/upper/pdcp.h index 42a9d48ea..30178389b 100644 --- a/srsenb/hdr/stack/upper/pdcp.h +++ b/srsenb/hdr/stack/upper/pdcp.h @@ -19,6 +19,7 @@ * */ +#include "srsenb/hdr/common/rnti_pool.h" #include "srsran/common/timers.h" #include "srsran/interfaces/enb_metrics_interface.h" #include "srsran/interfaces/enb_pdcp_interfaces.h" @@ -113,10 +114,10 @@ private: class user_interface { public: - user_interface_rlc rlc_itf; - user_interface_gtpu gtpu_itf; - user_interface_rrc rrc_itf; - srsran::pdcp* pdcp; + user_interface_rlc rlc_itf; + user_interface_gtpu gtpu_itf; + user_interface_rrc rrc_itf; + unique_rnti_ptr pdcp; }; void clear_user(user_interface* ue); diff --git a/srsenb/hdr/stack/upper/rlc.h b/srsenb/hdr/stack/upper/rlc.h index 3a9e2f673..40504eed3 100644 --- a/srsenb/hdr/stack/upper/rlc.h +++ b/srsenb/hdr/stack/upper/rlc.h @@ -19,6 +19,7 @@ * */ +#include "srsenb/hdr/common/rnti_pool.h" #include "srsran/interfaces/enb_metrics_interface.h" #include "srsran/interfaces/enb_rlc_interfaces.h" #include "srsran/interfaces/ue_interfaces.h" @@ -47,7 +48,7 @@ class rlc : public rlc_interface_mac, public rlc_interface_rrc, public rlc_inter public: explicit rlc(srslog::basic_logger& logger) : logger(logger) {} void - init(pdcp_interface_rlc* pdcp_, rrc_interface_rlc* rrc_, mac_interface_rlc* mac_, srsran::timer_handler* timers_); + init(pdcp_interface_rlc* pdcp_, rrc_interface_rlc* rrc_, mac_interface_rlc* mac_, srsran::timer_handler* timers_); void stop(); void get_metrics(rlc_metrics_t& m, const uint32_t nof_tti); @@ -92,7 +93,7 @@ private: srsenb::pdcp_interface_rlc* pdcp; srsenb::rrc_interface_rlc* rrc; - std::unique_ptr rlc; + unique_rnti_ptr rlc; srsenb::rlc* parent; }; diff --git a/srsenb/hdr/stack/upper/s1ap.h b/srsenb/hdr/stack/upper/s1ap.h index 0ff53da01..86b56a75f 100644 --- a/srsenb/hdr/stack/upper/s1ap.h +++ b/srsenb/hdr/stack/upper/s1ap.h @@ -24,7 +24,7 @@ #include -#include "common_enb.h" +#include "srsenb/hdr/common/common_enb.h" #include "srsran/common/buffer_pool.h" #include "srsran/common/common.h" #include "srsran/common/s1ap_pcap.h" @@ -61,8 +61,10 @@ public: static const uint32_t ts1_reloc_prep_timeout_ms = 10000; static const uint32_t ts1_reloc_overall_timeout_ms = 10000; - s1ap(srsran::task_sched_handle task_sched_, srslog::basic_logger& logger); - int init(s1ap_args_t args_, rrc_interface_s1ap* rrc_, srsenb::stack_interface_s1ap_lte* stack_); + s1ap(srsran::task_sched_handle task_sched_, + srslog::basic_logger& logger, + srsran::socket_manager_itf* rx_socket_handler); + int init(s1ap_args_t args_, rrc_interface_s1ap* rrc_); void stop(); void get_metrics(s1ap_metrics_t& m); @@ -118,19 +120,20 @@ private: static const int NONUE_STREAM_ID = 0; // args - rrc_interface_s1ap* rrc = nullptr; - s1ap_args_t args; - srslog::basic_logger& logger; - srsenb::stack_interface_s1ap_lte* stack = nullptr; - srsran::task_sched_handle task_sched; + rrc_interface_s1ap* rrc = nullptr; + s1ap_args_t args; + srslog::basic_logger& logger; + srsran::task_sched_handle task_sched; + srsran::task_queue_handle mme_task_queue; + srsran::socket_manager_itf* rx_socket_handler; - srsran::socket_handler_t s1ap_socket; - struct sockaddr_in mme_addr = {}; // MME address - bool mme_connected = false; - bool running = false; - uint32_t next_enb_ue_s1ap_id = 1; // Next ENB-side UE identifier - uint16_t next_ue_stream_id = 1; // Next UE SCTP stream identifier - srsran::unique_timer mme_connect_timer, s1setup_timeout; + srsran::unique_socket mme_socket; + struct sockaddr_in mme_addr = {}; // MME address + bool mme_connected = false; + bool running = false; + uint32_t next_enb_ue_s1ap_id = 1; // Next ENB-side UE identifier + uint16_t next_ue_stream_id = 1; // Next UE SCTP stream identifier + srsran::unique_timer mme_connect_timer, s1setup_timeout; // Protocol IEs sent with every UL S1AP message asn1::s1ap::tai_s tai; diff --git a/srsenb/src/CMakeLists.txt b/srsenb/src/CMakeLists.txt index 4eea33c8f..74621e02f 100644 --- a/srsenb/src/CMakeLists.txt +++ b/srsenb/src/CMakeLists.txt @@ -18,6 +18,7 @@ # and at http://www.gnu.org/licenses/. # +add_subdirectory(common) add_subdirectory(phy) add_subdirectory(stack) @@ -37,7 +38,7 @@ target_link_libraries(enb_cfg_parser ${LIBCONFIGPP_LIBRARIES}) add_executable(srsenb main.cc enb.cc metrics_stdout.cc metrics_csv.cc metrics_json.cc) -set(SRSENB_SOURCES srsenb_phy srsenb_stack srsenb_upper srsenb_mac srsenb_rrc srslog system) +set(SRSENB_SOURCES srsenb_phy srsenb_stack srsenb_common srsenb_upper srsenb_mac srsenb_rrc srslog system) set(SRSRAN_SOURCES srsran_common srsran_mac srsran_phy srsran_upper srsran_radio rrc_asn1 s1ap_asn1 enb_cfg_parser srslog system) set(SRSENB_SOURCES ${SRSENB_SOURCES} srsgnb_phy srsgnb_stack srsgnb_upper srsgnb_mac srsgnb_rrc) @@ -46,7 +47,7 @@ set(SRSRAN_SOURCES ${SRSRAN_SOURCES} rrc_nr_asn1 ngap_nr_asn1) target_link_libraries(srsenb ${SRSENB_SOURCES} ${SRSRAN_SOURCES} ${CMAKE_THREAD_LIBS_INIT} - ${Boost_LIBRARIES} + ${Boost_LIBRARIES} ${SEC_LIBRARIES} ${LIBCONFIGPP_LIBRARIES} ${SCTP_LIBRARIES}) diff --git a/srsenb/src/common/CMakeLists.txt b/srsenb/src/common/CMakeLists.txt new file mode 100644 index 000000000..2c96b2c6c --- /dev/null +++ b/srsenb/src/common/CMakeLists.txt @@ -0,0 +1,10 @@ +# +# Copyright 2013-2021 Software Radio Systems Limited +# +# By using this file, you agree to the terms and conditions set +# forth in the LICENSE file which can be found at the top level of +# the distribution. +# + +set(SOURCES rnti_pool.cc) +add_library(srsenb_common STATIC ${SOURCES}) diff --git a/srsenb/src/common/rnti_pool.cc b/srsenb/src/common/rnti_pool.cc new file mode 100644 index 000000000..27f6a9bfd --- /dev/null +++ b/srsenb/src/common/rnti_pool.cc @@ -0,0 +1,42 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsenb/hdr/common/rnti_pool.h" +#include "srsenb/hdr/common/common_enb.h" +#include "srsran/adt/pool/circular_stack_pool.h" + +namespace srsenb { + +srsran::circular_stack_pool* get_rnti_pool() +{ + static std::unique_ptr > pool( + new srsran::circular_stack_pool(8, 32768, 4)); + return pool.get(); +} + +void reserve_rnti_memblocks(size_t nof_blocks) +{ + while (get_rnti_pool()->cache_size() < nof_blocks) { + get_rnti_pool()->allocate_batch(); + } +} + +void* allocate_rnti_dedicated_mem(uint16_t rnti, size_t size, size_t align) +{ + return get_rnti_pool()->allocate(rnti, size, align); +} +void deallocate_rnti_dedicated_mem(uint16_t rnti, void* ptr) +{ + get_rnti_pool()->deallocate(rnti, ptr); +} + +} // namespace srsenb diff --git a/srsenb/src/enb_cfg_parser.cc b/srsenb/src/enb_cfg_parser.cc index 040e246d4..a9ecdaa0f 100644 --- a/srsenb/src/enb_cfg_parser.cc +++ b/srsenb/src/enb_cfg_parser.cc @@ -41,6 +41,14 @@ } \ } while (0) +#define ASSERT_VALID_CFG(cond, msg_fmt, ...) \ + do { \ + if (not(cond)) { \ + fprintf(stderr, "Error: Invalid configuration - " msg_fmt "\n", ##__VA_ARGS__); \ + return SRSRAN_ERROR; \ + } \ + } while (0) + using namespace asn1::rrc; namespace srsenb { @@ -902,11 +910,12 @@ int parse_cfg_files(all_args_t* args_, rrc_cfg_t* rrc_cfg_, phy_cfg_t* phy_cfg_) int set_derived_args(all_args_t* args_, rrc_cfg_t* rrc_cfg_, phy_cfg_t* phy_cfg_, const srsran_cell_t& cell_cfg_) { - // Sanity check - if (rrc_cfg_->cell_list.empty()) { - ERROR("No cell specified in RR config."); - return SRSRAN_ERROR; - } + // Sanity checks + ASSERT_VALID_CFG(not rrc_cfg_->cell_list.empty(), "No cell specified in rr.conf."); + ASSERT_VALID_CFG(args_->stack.mac.max_nof_ues <= SRSENB_MAX_UES and args_->stack.mac.max_nof_ues > 0, + "mac.max_nof_ues=%d must be within [0, %d]", + args_->stack.mac.max_nof_ues, + SRSENB_MAX_UES); // Check for a forced DL EARFCN or frequency (only valid for a single cell config (Xico's favorite feature)) if (rrc_cfg_->cell_list.size() == 1) { diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index f66d6cedf..2e8473231 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -222,7 +222,7 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("expert.print_buffer_state", bpo::value(&args->general.print_buffer_state)->default_value(false), "Prints on the console the buffer state every 10 seconds") ("expert.eea_pref_list", bpo::value(&args->general.eea_pref_list)->default_value("EEA0, EEA2, EEA1"), "Ordered preference list for the selection of encryption algorithm (EEA) (default: EEA0, EEA2, EEA1).") ("expert.eia_pref_list", bpo::value(&args->general.eia_pref_list)->default_value("EIA2, EIA1, EIA0"), "Ordered preference list for the selection of integrity algorithm (EIA) (default: EIA2, EIA1, EIA0).") - ("expert.max_nof_ues", bpo::value(&args->stack.mac.max_nof_ues)->default_value(64), "Maximum number of connected UEs") + ("expert.max_nof_ues", bpo::value(&args->stack.mac.max_nof_ues)->default_value(8), "Maximum number of connected UEs") ("expert.max_mac_dl_kos", bpo::value(&args->general.max_mac_dl_kos)->default_value(100), "Maximum number of consecutive KOs before triggering the UE's release") // eMBMS section @@ -523,6 +523,7 @@ int main(int argc, char* argv[]) srslog::init(); srslog::fetch_basic_logger("ALL").set_level(srslog::basic_levels::warning); + srslog::fetch_basic_logger("POOL").set_level(srslog::basic_levels::warning); srsran::log_args(argc, argv, "ENB"); srsran::check_scaling_governor(args.rf.device_name); diff --git a/srsenb/src/phy/nr/cc_worker.cc b/srsenb/src/phy/nr/cc_worker.cc index 1cbf055a5..e779cc24f 100644 --- a/srsenb/src/phy/nr/cc_worker.cc +++ b/srsenb/src/phy/nr/cc_worker.cc @@ -69,7 +69,8 @@ bool cc_worker::set_carrier(const srsran_carrier_nr_t* carrier) coreset.freq_resources[0] = true; // Enable the bottom 6 PRB for PDCCH coreset.duration = 2; - if (srsran_enb_dl_nr_set_coreset(&enb_dl, &coreset) < SRSRAN_SUCCESS) { + srsran_dci_cfg_nr_t dci_cfg = phy_state->cfg.get_dci_cfg(*carrier); + if (srsran_enb_dl_nr_set_pdcch_config(&enb_dl, &phy_state->cfg.pdcch, &dci_cfg) < SRSRAN_SUCCESS) { ERROR("Error setting coreset"); return false; } diff --git a/srsenb/src/phy/nr/sf_worker.cc b/srsenb/src/phy/nr/sf_worker.cc index db81cfff5..846435fa2 100644 --- a/srsenb/src/phy/nr/sf_worker.cc +++ b/srsenb/src/phy/nr/sf_worker.cc @@ -109,17 +109,17 @@ void sf_worker::work_imp() grants.pdsch[0].softbuffer_tx[0] = &softbuffer_tx; srsran_softbuffer_tx_reset(&softbuffer_tx); - grants.pdsch[0].dci.rnti = 0x1234; - grants.pdsch[0].dci.format = srsran_dci_format_nr_1_0; + grants.pdsch[0].dci.ctx.rnti = 0x1234; + grants.pdsch[0].dci.ctx.format = srsran_dci_format_nr_1_0; grants.pdsch[0].dci.freq_domain_assigment = 0x1FFF; grants.pdsch[0].dci.time_domain_assigment = 0; grants.pdsch[0].dci.mcs = 27; - grants.pdsch[0].dci.search_space = srsran_search_space_type_ue; - grants.pdsch[0].dci.coreset_id = 1; - grants.pdsch[0].dci.location.L = 0; - grants.pdsch[0].dci.location.ncce = 0; + grants.pdsch[0].dci.ctx.ss_type = srsran_search_space_type_ue; + grants.pdsch[0].dci.ctx.coreset_id = 1; + grants.pdsch[0].dci.ctx.location.L = 0; + grants.pdsch[0].dci.ctx.location.ncce = 0; for (auto& w : cc_workers) { w->work_dl(dl_cfg, grants); diff --git a/srsenb/src/stack/enb_stack_lte.cc b/srsenb/src/stack/enb_stack_lte.cc index 297a169d3..5a1d17e2a 100644 --- a/srsenb/src/stack/enb_stack_lte.cc +++ b/srsenb/src/stack/enb_stack_lte.cc @@ -20,10 +20,11 @@ */ #include "srsenb/hdr/stack/enb_stack_lte.h" +#include "srsenb/hdr/common/rnti_pool.h" #include "srsenb/hdr/enb.h" -#include "srsran/common/network_utils.h" #include "srsran/interfaces/enb_metrics_interface.h" #include "srsran/srslog/event_trace.h" +#include "srsran/upper/bearer_mem_pool.h" using namespace srsran; @@ -42,8 +43,8 @@ enb_stack_lte::enb_stack_lte(srslog::sink& log_sink) : pdcp(&task_sched, pdcp_logger), mac(&task_sched, mac_logger), rlc(rlc_logger), - gtpu(&task_sched, gtpu_logger), - s1ap(&task_sched, s1ap_logger), + gtpu(&task_sched, gtpu_logger, &rx_sockets), + s1ap(&task_sched, s1ap_logger, &rx_sockets), rrc(&task_sched), mac_pcap(), pending_stack_metrics(64) @@ -80,6 +81,11 @@ int enb_stack_lte::init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_) args = args_; rrc_cfg = rrc_cfg_; + // Init RNTI and bearer memory pools + reserve_rnti_memblocks(args.mac.max_nof_ues); + uint32_t min_nof_bearers_per_ue = 4; + reserve_rlc_memblocks(args.mac.max_nof_ues * min_nof_bearers_per_ue); + // setup logging for each layer mac_logger.set_level(srslog::str_to_basic_level(args.log.mac_level)); mac_logger.set_hex_dump_max_size(args.log.mac_hex_limit); @@ -118,9 +124,6 @@ int enb_stack_lte::init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_) s1ap.start_pcap(&s1ap_pcap); } - // Init Rx socket handler - rx_sockets.reset(new srsran::rx_multisocket_handler("ENBSOCKETS", stack_logger)); - // add sync queue sync_task_queue = task_sched.make_task_queue(args.sync_queue_size); @@ -135,7 +138,7 @@ int enb_stack_lte::init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_) stack_logger.error("Couldn't initialize RRC"); return SRSRAN_ERROR; } - if (s1ap.init(args.s1ap, &rrc, this) != SRSRAN_SUCCESS) { + if (s1ap.init(args.s1ap, &rrc) != SRSRAN_SUCCESS) { stack_logger.error("Couldn't initialize S1AP"); return SRSRAN_ERROR; } @@ -144,7 +147,6 @@ int enb_stack_lte::init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_) args.embms.m1u_multiaddr, args.embms.m1u_if_addr, &pdcp, - this, args.embms.enable)) { stack_logger.error("Couldn't initialize GTPU"); return SRSRAN_ERROR; @@ -178,7 +180,7 @@ void enb_stack_lte::stop() void enb_stack_lte::stop_impl() { - rx_sockets->stop(); + rx_sockets.stop(); s1ap.stop(); gtpu.stop(); @@ -237,54 +239,4 @@ void enb_stack_lte::run_thread() } } -void enb_stack_lte::handle_mme_rx_packet(srsran::unique_byte_buffer_t pdu, - const sockaddr_in& from, - const sctp_sndrcvinfo& sri, - int flags) -{ - // Defer the handling of MME packet to eNB stack main thread - auto task_handler = [this, from, sri, flags](srsran::unique_byte_buffer_t& t) { - s1ap.handle_mme_rx_msg(std::move(t), from, sri, flags); - }; - // Defer the handling of MME packet to main stack thread - mme_task_queue.push(std::bind(task_handler, std::move(pdu))); -} - -void enb_stack_lte::add_mme_socket(int fd) -{ - // Pass MME Rx packet handler functor to socket handler to run in socket thread - auto mme_rx_handler = - [this](srsran::unique_byte_buffer_t pdu, const sockaddr_in& from, const sctp_sndrcvinfo& sri, int flags) { - handle_mme_rx_packet(std::move(pdu), from, sri, flags); - }; - rx_sockets->add_socket_sctp_pdu_handler(fd, mme_rx_handler); -} - -void enb_stack_lte::remove_mme_socket(int fd) -{ - rx_sockets->remove_socket(fd); -} - -void enb_stack_lte::add_gtpu_s1u_socket_handler(int fd) -{ - auto gtpu_s1u_handler = [this](srsran::unique_byte_buffer_t pdu, const sockaddr_in& from) { - auto task_handler = [this, from](srsran::unique_byte_buffer_t& t) { - gtpu.handle_gtpu_s1u_rx_packet(std::move(t), from); - }; - gtpu_task_queue.push(std::bind(task_handler, std::move(pdu))); - }; - rx_sockets->add_socket_pdu_handler(fd, gtpu_s1u_handler); -} - -void enb_stack_lte::add_gtpu_m1u_socket_handler(int fd) -{ - auto gtpu_m1u_handler = [this](srsran::unique_byte_buffer_t pdu, const sockaddr_in& from) { - auto task_handler = [this, from](srsran::unique_byte_buffer_t& t) { - gtpu.handle_gtpu_m1u_rx_packet(std::move(t), from); - }; - gtpu_task_queue.push(std::bind(task_handler, std::move(pdu))); - }; - rx_sockets->add_socket_pdu_handler(fd, gtpu_m1u_handler); -} - } // namespace srsenb diff --git a/srsenb/src/stack/mac/mac.cc b/srsenb/src/stack/mac/mac.cc index 0abc4fb64..387b1c5fb 100644 --- a/srsenb/src/stack/mac/mac.cc +++ b/srsenb/src/stack/mac/mac.cc @@ -23,6 +23,7 @@ #include #include "srsenb/hdr/stack/mac/mac.h" +#include "srsran/adt/pool/obj_pool.h" #include "srsran/common/rwlock_guard.h" #include "srsran/common/standard_streams.h" #include "srsran/common/time_prof.h" @@ -56,42 +57,51 @@ bool mac::init(const mac_args_t& args_, { started = false; - if (phy && rlc) { - phy_h = phy; - rlc_h = rlc; - rrc_h = rrc; + if (not phy or not rlc) { + return false; + } + phy_h = phy; + rlc_h = rlc; + rrc_h = rrc; - args = args_; - cells = cells_; + args = args_; + cells = cells_; - stack_task_queue = task_sched.make_task_queue(); + stack_task_queue = task_sched.make_task_queue(); - scheduler.init(rrc, args.sched); + scheduler.init(rrc, args.sched); - // Init softbuffer for SI messages - common_buffers.resize(cells.size()); - for (auto& cc : common_buffers) { - for (int i = 0; i < NOF_BCCH_DLSCH_MSG; i++) { - srsran_softbuffer_tx_init(&cc.bcch_softbuffer_tx[i], args.nof_prb); - } - // Init softbuffer for PCCH - srsran_softbuffer_tx_init(&cc.pcch_softbuffer_tx, args.nof_prb); - - // Init softbuffer for RAR - srsran_softbuffer_tx_init(&cc.rar_softbuffer_tx, args.nof_prb); + // Init softbuffer for SI messages + common_buffers.resize(cells.size()); + for (auto& cc : common_buffers) { + for (int i = 0; i < NOF_BCCH_DLSCH_MSG; i++) { + srsran_softbuffer_tx_init(&cc.bcch_softbuffer_tx[i], args.nof_prb); } + // Init softbuffer for PCCH + srsran_softbuffer_tx_init(&cc.pcch_softbuffer_tx, args.nof_prb); - reset(); - - // Pre-alloc UE objects for first attaching users - prealloc_ue(10); - - detected_rachs.resize(cells.size()); - - started = true; + // Init softbuffer for RAR + srsran_softbuffer_tx_init(&cc.rar_softbuffer_tx, args.nof_prb); } - return started; + reset(); + + // Initiate common pool of softbuffers + uint32_t nof_prb = args.nof_prb; + auto init_softbuffers = [nof_prb](void* ptr) { + new (ptr) ue_cc_softbuffers(nof_prb, SRSRAN_FDD_NOF_HARQ, SRSRAN_FDD_NOF_HARQ); + }; + auto recycle_softbuffers = [](ue_cc_softbuffers& softbuffers) { softbuffers.clear(); }; + softbuffer_pool.reset(new srsran::background_obj_pool( + 8, 8, args.max_nof_ues, init_softbuffers, recycle_softbuffers)); + + // Pre-alloc UE objects for first attaching users + prealloc_ue(10); + + detected_rachs.resize(cells.size()); + + started = true; + return true; } void mac::stop() @@ -461,8 +471,7 @@ uint16_t mac::allocate_ue() logger.error("UE pool empty. Ignoring RACH attempt."); return SRSRAN_INVALID_RNTI; } - uint16_t rnti = ue_ptr->get_rnti(); - size_t max_ues = std::min((size_t)args.max_nof_ues, ue_db.capacity()); + uint16_t rnti = ue_ptr->get_rnti(); // Add UE to map { @@ -472,7 +481,7 @@ uint16_t mac::allocate_ue() return SRSRAN_INVALID_RNTI; } if (ue_db.size() >= args.max_nof_ues) { - logger.warning("Maximum number of connected UEs %zd connected to the eNB. Ignoring PRACH", max_ues); + logger.warning("Maximum number of connected UEs %zd connected to the eNB. Ignoring PRACH", args.max_nof_ues); return SRSRAN_INVALID_RNTI; } auto ret = ue_db.insert(rnti, std::move(ue_ptr)); @@ -579,8 +588,8 @@ void mac::rach_detected(uint32_t tti, uint32_t enb_cc_idx, uint32_t preamble_idx void mac::prealloc_ue(uint32_t nof_ue) { for (uint32_t i = 0; i < nof_ue; i++) { - std::unique_ptr ptr = std::unique_ptr( - new ue(allocate_rnti(), args.nof_prb, &scheduler, rrc_h, rlc_h, phy_h, logger, cells.size())); + std::unique_ptr ptr = std::unique_ptr(new ue( + allocate_rnti(), args.nof_prb, &scheduler, rrc_h, rlc_h, phy_h, logger, cells.size(), softbuffer_pool.get())); if (not ue_pool.try_push(std::move(ptr))) { logger.info("Cannot preallocate more UEs as pool is full"); return; @@ -841,9 +850,9 @@ int mac::get_mch_sched(uint32_t tti, bool is_mcch, dl_sched_list_t& dl_sched_res int requested_bytes = (mcs_data.tbs / 8 > (int)mch.mtch_sched[mtch_index].lcid_buffer_size) ? (mch.mtch_sched[mtch_index].lcid_buffer_size) : ((mcs_data.tbs / 8) - 2); - int bytes_received = ue_db[SRSRAN_MRNTI]->read_pdu(current_lcid, mtch_payload_buffer, requested_bytes); - mch.pdu[0].lcid = current_lcid; - mch.pdu[0].nbytes = bytes_received; + int bytes_received = ue_db[SRSRAN_MRNTI]->read_pdu(current_lcid, mtch_payload_buffer, requested_bytes); + mch.pdu[0].lcid = current_lcid; + mch.pdu[0].nbytes = bytes_received; mch.mtch_sched[0].mtch_payload = mtch_payload_buffer; dl_sched_res->pdsch[0].dci.rnti = SRSRAN_MRNTI; if (bytes_received) { @@ -1009,8 +1018,8 @@ void mac::write_mcch(const srsran::sib2_mbms_t* sib2_, sib13 = *sib13_; memcpy(mcch_payload_buffer, mcch_payload, mcch_payload_length * sizeof(uint8_t)); current_mcch_length = mcch_payload_length; - ue_db[SRSRAN_MRNTI] = - std::unique_ptr{new ue(SRSRAN_MRNTI, args.nof_prb, &scheduler, rrc_h, rlc_h, phy_h, logger, cells.size())}; + ue_db[SRSRAN_MRNTI] = std::unique_ptr{ + new ue(SRSRAN_MRNTI, args.nof_prb, &scheduler, rrc_h, rlc_h, phy_h, logger, cells.size(), softbuffer_pool.get())}; rrc_h->add_user(SRSRAN_MRNTI, {}); } diff --git a/srsenb/src/stack/mac/sched_ue_ctrl/sched_harq.cc b/srsenb/src/stack/mac/sched_ue_ctrl/sched_harq.cc index a5ba9e1b7..73531f288 100644 --- a/srsenb/src/stack/mac/sched_ue_ctrl/sched_harq.cc +++ b/srsenb/src/stack/mac/sched_ue_ctrl/sched_harq.cc @@ -349,9 +349,7 @@ void harq_entity::reset() void harq_entity::new_tti(tti_point tti_rx) { last_ttis[tti_rx.to_uint() % last_ttis.size()] = tti_rx; - for (auto& hul : ul_harqs) { - hul.new_tti(); - } + get_ul_harq(to_tx_ul(tti_rx))->new_tti(); for (auto& hdl : dl_harqs) { hdl.new_tti(to_tx_dl(tti_rx)); } diff --git a/srsenb/src/stack/mac/ue.cc b/srsenb/src/stack/mac/ue.cc index b757b892e..aba545bdb 100644 --- a/srsenb/src/stack/mac/ue.cc +++ b/srsenb/src/stack/mac/ue.cc @@ -32,32 +32,75 @@ namespace srsenb { +ue_cc_softbuffers::ue_cc_softbuffers(uint32_t nof_prb, uint32_t nof_tx_harq_proc_, uint32_t nof_rx_harq_proc_) : + nof_tx_harq_proc(nof_tx_harq_proc_), nof_rx_harq_proc(nof_rx_harq_proc_) +{ + // Create and init Rx buffers + softbuffer_rx_list.resize(nof_rx_harq_proc); + for (srsran_softbuffer_rx_t& buffer : softbuffer_rx_list) { + srsran_softbuffer_rx_init(&buffer, nof_prb); + } + + // Create and init Tx buffers + softbuffer_tx_list.resize(nof_tx_harq_proc * SRSRAN_MAX_TB); + for (auto& buffer : softbuffer_tx_list) { + srsran_softbuffer_tx_init(&buffer, nof_prb); + } +} + +ue_cc_softbuffers::~ue_cc_softbuffers() +{ + for (auto& buffer : softbuffer_rx_list) { + srsran_softbuffer_rx_free(&buffer); + } + softbuffer_rx_list.clear(); + + for (auto& buffer : softbuffer_tx_list) { + srsran_softbuffer_tx_free(&buffer); + } + softbuffer_tx_list.clear(); +} + +void ue_cc_softbuffers::clear() +{ + for (auto& buffer : softbuffer_rx_list) { + srsran_softbuffer_rx_reset(&buffer); + } + for (auto& buffer : softbuffer_tx_list) { + srsran_softbuffer_tx_reset(&buffer); + } +} + cc_used_buffers_map::cc_used_buffers_map(srsran::pdu_queue& shared_pdu_queue_) : - pdu_map(), shared_pdu_queue(&shared_pdu_queue_), logger(&srslog::fetch_basic_logger("MAC")) + shared_pdu_queue(&shared_pdu_queue_), logger(&srslog::fetch_basic_logger("MAC")) {} +cc_used_buffers_map::~cc_used_buffers_map() +{ + clear(); +} + bool cc_used_buffers_map::push_pdu(tti_point tti, uint32_t len) { if (not has_tti(tti)) { return false; } - auto& pdu_pair = pdu_map[tti.to_uint()]; + uint8_t* buffer = pdu_map[tti.to_uint()]; if (len > 0) { - shared_pdu_queue->push(pdu_pair.second, len); + shared_pdu_queue->push(buffer, len); } else { - shared_pdu_queue->deallocate(pdu_pair.second); + shared_pdu_queue->deallocate(buffer); logger->error("Error pushing PDU: null length"); } // clear entry in map - pdu_pair.first = tti_point(); - pdu_pair.second = nullptr; + pdu_map.erase(tti.to_uint()); return len > 0; } uint8_t* cc_used_buffers_map::request_pdu(tti_point tti, uint32_t len) { - if (pdu_map[tti.to_uint()].second != nullptr) { - logger->error("UE buffers: buffer for tti=%d already allocated", tti.to_uint()); + if (not pdu_map.has_space(tti.to_uint())) { + logger->error("UE buffers: could not allocate buffer for tti=%d", tti.to_uint()); return nullptr; } @@ -67,8 +110,8 @@ uint8_t* cc_used_buffers_map::request_pdu(tti_point tti, uint32_t len) return nullptr; } - pdu_map[tti.to_uint()].first = tti; - pdu_map[tti.to_uint()].second = pdu; + bool inserted = pdu_map.insert(tti.to_uint(), pdu); + srsran_assert(inserted, "Failure to allocate new buffer"); return pdu; } @@ -78,29 +121,26 @@ void cc_used_buffers_map::clear_old_pdus(tti_point current_tti) tti_point max_tti{current_tti - old_tti_threshold}; for (auto& pdu_pair : pdu_map) { - if (pdu_pair.second != nullptr and pdu_pair.first < max_tti) { - logger->warning("UE buffers: Removing old buffer tti=%d, interval=%d", - pdu_pair.first.to_uint(), - current_tti - pdu_pair.first); - remove_pdu(pdu_pair.first); + tti_point t(pdu_pair.first); + if (t < max_tti) { + logger->warning("UE buffers: Removing old buffer tti=%d, interval=%d", t.to_uint(), current_tti - t); + remove_pdu(t); } } } void cc_used_buffers_map::remove_pdu(tti_point tti) { - auto& pdu_pair = pdu_map[tti.to_uint()]; - assert(pdu_pair.second != nullptr && "cannot remove inexistent PDU"); + uint8_t* buffer = pdu_map[tti.to_uint()]; // return pdus back to the queue - shared_pdu_queue->deallocate(pdu_pair.second); + shared_pdu_queue->deallocate(buffer); // clear entry in map - pdu_pair.first = tti_point(); - pdu_pair.second = nullptr; + pdu_map.erase(tti.to_uint()); } bool cc_used_buffers_map::try_deallocate_pdu(tti_point tti) { - if (pdu_map[tti.to_uint()].second != nullptr) { + if (has_tti(tti)) { remove_pdu(tti); return true; } @@ -109,20 +149,20 @@ bool cc_used_buffers_map::try_deallocate_pdu(tti_point tti) void cc_used_buffers_map::clear() { - for (auto& pdu : pdu_map) { - try_deallocate_pdu(pdu.first); + for (auto& buffer : pdu_map) { + shared_pdu_queue->deallocate(buffer.second); } + pdu_map.clear(); } uint8_t*& cc_used_buffers_map::operator[](tti_point tti) { - assert(has_tti(tti) && "Trying to access buffer that does not exist"); - return pdu_map[tti.to_uint()].second; + return pdu_map[tti.to_uint()]; } bool cc_used_buffers_map::has_tti(tti_point tti) const { - return pdu_map[tti.to_uint()].first == tti and pdu_map[tti.to_uint()].second != nullptr; + return pdu_map.contains(tti.to_uint()) and pdu_map[tti.to_uint()] != nullptr; } //////////////// @@ -152,61 +192,34 @@ cc_buffer_handler::~cc_buffer_handler() * @param num_cc Number of carriers to add buffers for (default 1) * @return number of carriers */ -void cc_buffer_handler::allocate_cc(uint32_t nof_prb_, uint32_t nof_rx_harq_proc_, uint32_t nof_tx_harq_proc_) +void cc_buffer_handler::allocate_cc(srsran::unique_pool_ptr cc_softbuffers_) { - assert(empty()); - nof_prb = nof_prb_; - nof_rx_harq_proc = nof_rx_harq_proc_; - nof_tx_harq_proc = nof_tx_harq_proc_; - - // Create and init Rx buffers - softbuffer_rx_list.resize(nof_rx_harq_proc); - for (srsran_softbuffer_rx_t& buffer : softbuffer_rx_list) { - srsran_softbuffer_rx_init(&buffer, nof_prb); - } - - // Create and init Tx buffers - softbuffer_tx_list.resize(nof_tx_harq_proc * SRSRAN_MAX_TB); - for (auto& buffer : softbuffer_tx_list) { - srsran_softbuffer_tx_init(&buffer, nof_prb); - } + srsran_assert(empty(), "Cannot allocate softbuffers in CC that is already initialized"); + cc_softbuffers = std::move(cc_softbuffers_); } void cc_buffer_handler::deallocate_cc() { - for (auto& buffer : softbuffer_rx_list) { - srsran_softbuffer_rx_free(&buffer); - } - softbuffer_rx_list.clear(); - - for (auto& buffer : softbuffer_tx_list) { - srsran_softbuffer_tx_free(&buffer); - } - softbuffer_tx_list.clear(); + cc_softbuffers.reset(); } void cc_buffer_handler::reset() { - for (auto& buffer : softbuffer_rx_list) { - srsran_softbuffer_rx_reset(&buffer); - } - for (auto& buffer : softbuffer_tx_list) { - srsran_softbuffer_tx_reset(&buffer); + if (not empty()) { + cc_softbuffers->clear(); } } -ue::ue(uint16_t rnti_, - uint32_t nof_prb_, - sched_interface* sched_, - rrc_interface_mac* rrc_, - rlc_interface_mac* rlc_, - phy_interface_stack_lte* phy_, - srslog::basic_logger& logger_, - uint32_t nof_cells_, - uint32_t nof_rx_harq_proc_, - uint32_t nof_tx_harq_proc_) : +ue::ue(uint16_t rnti_, + uint32_t nof_prb_, + sched_interface* sched_, + rrc_interface_mac* rrc_, + rlc_interface_mac* rlc_, + phy_interface_stack_lte* phy_, + srslog::basic_logger& logger_, + uint32_t nof_cells_, + srsran::obj_pool_itf* softbuffer_pool_) : rnti(rnti_), - nof_prb(nof_prb_), sched(sched_), rrc(rrc_), rlc(rlc_), @@ -216,9 +229,8 @@ ue::ue(uint16_t rnti_, mch_mac_msg_dl(10, logger_), mac_msg_ul(20, logger_), pdus(logger_), - nof_rx_harq_proc(nof_rx_harq_proc_), - nof_tx_harq_proc(nof_tx_harq_proc_), - ta_fsm(this) + ta_fsm(this), + softbuffer_pool(softbuffer_pool_) { for (size_t i = 0; i < nof_cells_; ++i) { cc_buffers.emplace_back(pdus); @@ -226,7 +238,7 @@ ue::ue(uint16_t rnti_, pdus.init(this); // Allocate buffer for PCell - cc_buffers[0].allocate_cc(nof_prb, nof_rx_harq_proc, nof_tx_harq_proc); + cc_buffers[0].allocate_cc(softbuffer_pool->make()); } ue::~ue() @@ -264,7 +276,7 @@ srsran_softbuffer_rx_t* ue::get_rx_softbuffer(const uint32_t ue_cc_idx, const ui return nullptr; } - return &cc_buffers[ue_cc_idx].get_rx_softbuffer(tti % nof_rx_harq_proc); + return &cc_buffers[ue_cc_idx].get_rx_softbuffer(tti); } srsran_softbuffer_tx_t* @@ -556,7 +568,7 @@ void ue::allocate_ce(srsran::sch_pdu* pdu, uint32_t lcid) // Allocate and initialize Rx/Tx softbuffers for new carriers (exclude PCell) for (size_t i = 0; i < std::min(active_scell_list.size(), cc_buffers.size()); ++i) { if (active_scell_list[i] and cc_buffers[i].empty()) { - cc_buffers[i].allocate_cc(nof_prb, nof_rx_harq_proc, nof_tx_harq_proc); + cc_buffers[i].allocate_cc(softbuffer_pool->make()); } } } else { diff --git a/srsenb/src/stack/rrc/mac_controller.cc b/srsenb/src/stack/rrc/mac_controller.cc index cd377e1f3..b9d16d535 100644 --- a/srsenb/src/stack/rrc/mac_controller.cc +++ b/srsenb/src/stack/rrc/mac_controller.cc @@ -20,7 +20,7 @@ */ #include "srsenb/hdr/stack/rrc/mac_controller.h" -#include "srsenb/hdr/stack/upper/common_enb.h" +#include "srsenb/hdr/common/common_enb.h" #include "srsran/asn1/rrc_utils.h" #include "srsran/interfaces/enb_mac_interfaces.h" @@ -311,6 +311,7 @@ void mac_controller::handle_ho_prep(const asn1::rrc::ho_prep_info_r8_ies_s& ho_p void mac_controller::handle_max_retx() { set_drb_activation(false); + update_mac(other); } void mac_controller::set_scell_activation(const std::bitset& scell_mask) diff --git a/srsenb/src/stack/rrc/rrc.cc b/srsenb/src/stack/rrc/rrc.cc index c504a62a6..0381945e7 100644 --- a/srsenb/src/stack/rrc/rrc.cc +++ b/srsenb/src/stack/rrc/rrc.cc @@ -25,7 +25,6 @@ #include "srsran/asn1/asn1_utils.h" #include "srsran/asn1/rrc_utils.h" #include "srsran/common/bcd_helpers.h" -#include "srsran/common/int_helpers.h" #include "srsran/common/standard_streams.h" #include "srsran/interfaces/enb_mac_interfaces.h" #include "srsran/interfaces/enb_pdcp_interfaces.h" @@ -33,8 +32,6 @@ #include "srsran/interfaces/sched_interface.h" using srsran::byte_buffer_t; -using srsran::uint32_to_uint8; -using srsran::uint8_to_uint32; using namespace asn1::rrc; @@ -42,10 +39,7 @@ namespace srsenb { rrc::rrc(srsran::task_sched_handle task_sched_) : logger(srslog::fetch_basic_logger("RRC")), task_sched(task_sched_), rx_pdu_queue(64) -{ - pending_paging.clear(); - rrc::ue::get_ue_pool()->allocate_batch_in_background(); -} +{} rrc::~rrc() {} @@ -183,7 +177,7 @@ int rrc::add_user(uint16_t rnti, const sched_interface::ue_cfg_t& sched_ue_cfg) if (user_it == users.end()) { if (rnti != SRSRAN_MRNTI) { // only non-eMBMS RNTIs are present in user map - std::unique_ptr u{new ue(this, rnti, sched_ue_cfg)}; + unique_rnti_ptr u = make_rnti_obj(rnti, this, rnti, sched_ue_cfg); if (u->init() != SRSRAN_SUCCESS) { logger.error("Adding user rnti=0x%x - Failed to allocate user resources", rnti); return SRSRAN_ERROR; diff --git a/srsenb/src/stack/rrc/rrc_bearer_cfg.cc b/srsenb/src/stack/rrc/rrc_bearer_cfg.cc index bfb668d50..76c44f166 100644 --- a/srsenb/src/stack/rrc/rrc_bearer_cfg.cc +++ b/srsenb/src/stack/rrc/rrc_bearer_cfg.cc @@ -20,7 +20,7 @@ */ #include "srsenb/hdr/stack/rrc/rrc_bearer_cfg.h" -#include "srsenb/hdr/stack/upper/common_enb.h" +#include "srsenb/hdr/common/common_enb.h" #include "srsran/asn1/rrc_utils.h" #include "srsran/rrc/rrc_cfg_utils.h" diff --git a/srsenb/src/stack/rrc/rrc_nr.cc b/srsenb/src/stack/rrc/rrc_nr.cc index 5ea6ca218..daf84172e 100644 --- a/srsenb/src/stack/rrc/rrc_nr.cc +++ b/srsenb/src/stack/rrc/rrc_nr.cc @@ -20,7 +20,7 @@ */ #include "srsenb/hdr/stack/rrc/rrc_nr.h" -#include "srsenb/hdr/stack/upper/common_enb.h" +#include "srsenb/hdr/common/common_enb.h" #include "srsran/asn1/rrc_nr_utils.h" #include "srsran/interfaces/nr_common_interface_types.h" @@ -225,7 +225,7 @@ int32_t rrc_nr::generate_sibs() logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); return SRSRAN_ERROR; } - asn1::bit_ref bref(mib_buf->msg, mib_buf->get_tailroom()); + asn1::bit_ref bref(mib_buf->msg, mib_buf->get_tailroom()); mib_msg.pack(bref); mib_buf->N_bytes = bref.distance_bytes(); logger.debug(mib_buf->msg, mib_buf->N_bytes, "MIB payload (%d B)", mib_buf->N_bytes); @@ -263,7 +263,7 @@ int32_t rrc_nr::generate_sibs() logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); return SRSRAN_ERROR; } - asn1::bit_ref bref(sib->msg, sib->get_tailroom()); + asn1::bit_ref bref(sib->msg, sib->get_tailroom()); msg[msg_index].pack(bref); sib->N_bytes = bref.distance_bytes(); sib_buffer.push_back(std::move(sib)); diff --git a/srsenb/src/stack/rrc/rrc_ue.cc b/srsenb/src/stack/rrc/rrc_ue.cc index 516a08a81..152746014 100644 --- a/srsenb/src/stack/rrc/rrc_ue.cc +++ b/srsenb/src/stack/rrc/rrc_ue.cc @@ -70,27 +70,10 @@ int rrc::ue::init() set_activity_timeout(MSG3_RX_TIMEOUT); // next UE response is Msg3 set_rlf_timeout(); - mobility_handler.reset(new rrc_mobility(this)); + mobility_handler = make_rnti_obj(rnti, this); return SRSRAN_SUCCESS; } -srsran::background_allocator_obj_pool* rrc::ue::get_ue_pool() -{ - // Note: batch allocation is going to be explicitly called in enb class construction. The pool object, therefore, - // will only be initialized if we instantiate an eNB - static rrc::ue::ue_pool_t ue_pool(true); - return &ue_pool; -} - -void* rrc::ue::operator new(size_t sz) -{ - return rrc::ue::get_ue_pool()->allocate_node(sz); -} -void rrc::ue::operator delete(void* ptr)noexcept -{ - rrc::ue::get_ue_pool()->deallocate_node(ptr); -} - rrc_state_t rrc::ue::get_state() { return state; @@ -212,7 +195,7 @@ void rrc::ue::set_rlf_timeout() uint32_t deadline = deadline_s * 1e3 + deadline_ms; rlf_timer.set(deadline, [this](uint32_t tid) { rlf_timer_expired(); }); - parent->logger.debug("Setting RLF timer for rnti=0x%x to %dms", rnti, deadline); + parent->logger.info("Setting RLF timer for rnti=0x%x to %dms", rnti, deadline); } void rrc::ue::set_activity_timeout(const activity_timeout_type_t type) diff --git a/srsenb/src/stack/upper/gtpu.cc b/srsenb/src/stack/upper/gtpu.cc index 23737d71a..e67d98874 100644 --- a/srsenb/src/stack/upper/gtpu.cc +++ b/srsenb/src/stack/upper/gtpu.cc @@ -167,7 +167,12 @@ bool gtpu_tunnel_manager::remove_bearer(uint16_t rnti, uint32_t lcid) logger.info("Removing rnti=0x%x,lcid=%d", rnti, lcid); for (lcid_tunnel& lcid_tun : to_rem) { - srsran_expect(tunnels.erase(lcid_tun.teid) > 0, "Inconsistency detected between two internal data structures"); + bool ret = tunnels.erase(lcid_tun.teid); + srsran_expect(ret, + "Inconsistency detected between internal data structures for rnti=0x%x,lcid=%d," TEID_IN_FMT, + rnti, + lcid, + lcid_tun.teid); } ue_teidin_db[rnti].erase(to_rem.begin(), to_rem.end()); return true; @@ -181,8 +186,11 @@ bool gtpu_tunnel_manager::remove_rnti(uint16_t rnti) } logger.info("Removing rnti=0x%x", rnti); - for (lcid_tunnel& ue_tuns : ue_teidin_db[rnti]) { - srsran_expect(tunnels.erase(ue_tuns.teid) > 0, "Inconsistency detected between two internal data structures"); + while (not ue_teidin_db[rnti].empty()) { + uint32_t teid = ue_teidin_db[rnti].front().teid; + bool ret = remove_tunnel(teid); + srsran_expect( + ret, "Inconsistency detected between internal data structures for rnti=0x%x," TEID_IN_FMT, rnti, teid); } ue_teidin_db.erase(rnti); return true; @@ -264,7 +272,18 @@ void gtpu_tunnel_manager::buffer_pdcp_sdu(uint32_t teid, uint32_t pdcp_sn, srsra tunnel& rx_tun = tunnels[teid]; srsran_assert(rx_tun.state == tunnel_state::buffering, "Buffering of PDCP SDUs only enabled when PDCP is not active"); - rx_tun.buffer->push_back(std::make_pair(pdcp_sn, std::move(sdu))); + if (not rx_tun.buffer->full()) { + rx_tun.buffer->push_back(std::make_pair(pdcp_sn, std::move(sdu))); + } else { + fmt::memory_buffer str_buffer; + if (pdcp_sn != undefined_pdcp_sn) { + fmt::format_to(str_buffer, " PDCP SN={}", pdcp_sn); + } + logger.warning("GTPU tunnel " TEID_IN_FMT " internal buffer of size=%zd is full. Discarding SDU%s.", + teid, + rx_tun.buffer->size(), + to_c_str(str_buffer)); + } } void gtpu_tunnel_manager::setup_forwarding(uint32_t rx_teid, uint32_t tx_teid) @@ -296,9 +315,17 @@ void gtpu_tunnel_manager::setup_forwarding(uint32_t rx_teid, uint32_t tx_teid) * GTPU class *******************/ -gtpu::gtpu(srsran::task_sched_handle task_sched_, srslog::basic_logger& logger) : - m1u(this), task_sched(task_sched_), logger(logger), tunnels(task_sched_, logger) -{} +gtpu::gtpu(srsran::task_sched_handle task_sched_, + srslog::basic_logger& logger, + srsran::socket_manager_itf* rx_socket_handler_) : + m1u(this), + task_sched(task_sched_), + logger(logger), + tunnels(task_sched_, logger), + rx_socket_handler(rx_socket_handler_) +{ + gtpu_queue = task_sched.make_task_queue(); +} gtpu::~gtpu() { @@ -310,13 +337,11 @@ int gtpu::init(std::string gtp_bind_addr_, std::string m1u_multiaddr_, std::string m1u_if_addr_, srsenb::pdcp_interface_gtpu* pdcp_, - stack_interface_gtpu_lte* stack_, bool enable_mbsfn_) { pdcp = pdcp_; gtp_bind_addr = gtp_bind_addr_; mme_addr = mme_addr_; - stack = stack_; tunnels.init(pdcp); @@ -351,7 +376,11 @@ int gtpu::init(std::string gtp_bind_addr_, return SRSRAN_ERROR; } - stack->add_gtpu_s1u_socket_handler(fd); + // Assign a handler to rx S1U packets + auto rx_callback = [this](srsran::unique_byte_buffer_t pdu, const sockaddr_in& from) { + handle_gtpu_s1u_rx_packet(std::move(pdu), from); + }; + rx_socket_handler->add_socket_handler(fd, srsran::make_sdu_handler(logger, gtpu_queue, rx_callback)); // Start MCH socket if enabled enable_mbsfn = enable_mbsfn_; @@ -860,8 +889,12 @@ bool gtpu::m1u_handler::init(std::string m1u_multiaddr_, std::string m1u_if_addr initiated = true; lcid_counter = 1; - // Register socket in stack rx sockets thread - parent->stack->add_gtpu_m1u_socket_handler(m1u_sd); + // Assign a handler to rx M1U packets + auto rx_callback = [this](srsran::unique_byte_buffer_t pdu, const sockaddr_in& from) { + parent->handle_gtpu_m1u_rx_packet(std::move(pdu), from); + }; + parent->rx_socket_handler->add_socket_handler(m1u_sd, + srsran::make_sdu_handler(logger, parent->gtpu_queue, rx_callback)); return true; } diff --git a/srsenb/src/stack/upper/pdcp.cc b/srsenb/src/stack/upper/pdcp.cc index efc991390..4e14f72c5 100644 --- a/srsenb/src/stack/upper/pdcp.cc +++ b/srsenb/src/stack/upper/pdcp.cc @@ -20,7 +20,7 @@ */ #include "srsenb/hdr/stack/upper/pdcp.h" -#include "srsenb/hdr/stack/upper/common_enb.h" +#include "srsenb/hdr/common/common_enb.h" #include "srsran/interfaces/enb_gtpu_interfaces.h" #include "srsran/interfaces/enb_rlc_interfaces.h" #include "srsran/interfaces/enb_rrc_interfaces.h" @@ -49,7 +49,7 @@ void pdcp::stop() void pdcp::add_user(uint16_t rnti) { if (users.count(rnti) == 0) { - srsran::pdcp* obj = new srsran::pdcp(task_sched, logger.id().c_str()); + unique_rnti_ptr obj = make_rnti_obj(rnti, task_sched, logger.id().c_str()); obj->init(&users[rnti].rlc_itf, &users[rnti].rrc_itf, &users[rnti].gtpu_itf); users[rnti].rlc_itf.rnti = rnti; users[rnti].gtpu_itf.rnti = rnti; @@ -58,7 +58,7 @@ void pdcp::add_user(uint16_t rnti) users[rnti].rrc_itf.rrc = rrc; users[rnti].rlc_itf.rlc = rlc; users[rnti].gtpu_itf.gtpu = gtpu; - users[rnti].pdcp = obj; + users[rnti].pdcp = std::move(obj); } } @@ -66,8 +66,7 @@ void pdcp::add_user(uint16_t rnti) void pdcp::clear_user(user_interface* ue) { ue->pdcp->stop(); - delete ue->pdcp; - ue->pdcp = NULL; + ue->pdcp.reset(); } void pdcp::rem_user(uint16_t rnti) diff --git a/srsenb/src/stack/upper/rlc.cc b/srsenb/src/stack/upper/rlc.cc index 1f656df1c..a1e72acf0 100644 --- a/srsenb/src/stack/upper/rlc.cc +++ b/srsenb/src/stack/upper/rlc.cc @@ -20,7 +20,7 @@ */ #include "srsenb/hdr/stack/upper/rlc.h" -#include "srsenb/hdr/stack/upper/common_enb.h" +#include "srsenb/hdr/common/common_enb.h" #include "srsran/interfaces/enb_mac_interfaces.h" #include "srsran/interfaces/enb_pdcp_interfaces.h" #include "srsran/interfaces/enb_rrc_interfaces.h" @@ -65,7 +65,7 @@ void rlc::add_user(uint16_t rnti) { pthread_rwlock_rdlock(&rwlock); if (users.count(rnti) == 0) { - std::unique_ptr obj(new srsran::rlc(logger.id().c_str())); + auto obj = make_rnti_obj(rnti, logger.id().c_str()); obj->init(&users[rnti], &users[rnti], timers, diff --git a/srsenb/src/stack/upper/s1ap.cc b/srsenb/src/stack/upper/s1ap.cc index 090510274..fe2469cdc 100644 --- a/srsenb/src/stack/upper/s1ap.cc +++ b/srsenb/src/stack/upper/s1ap.cc @@ -20,7 +20,7 @@ */ #include "srsenb/hdr/stack/upper/s1ap.h" -#include "srsenb/hdr/stack/upper/common_enb.h" +#include "srsenb/hdr/common/common_enb.h" #include "srsran/adt/scope_exit.h" #include "srsran/common/bcd_helpers.h" #include "srsran/common/enb_events.h" @@ -218,10 +218,10 @@ void s1ap::s1_setup_proc_t::then(const srsran::proc_state_t& result) const s1ap_ptr->mme_connect_timer.duration() / 1000); srsran::console("Failed to initiate S1 connection. Attempting reconnection in %d seconds\n", s1ap_ptr->mme_connect_timer.duration() / 1000); - s1ap_ptr->mme_connect_timer.run(); - s1ap_ptr->stack->remove_mme_socket(s1ap_ptr->s1ap_socket.get_socket()); - s1ap_ptr->s1ap_socket.reset(); + s1ap_ptr->rx_socket_handler->remove_socket(s1ap_ptr->mme_socket.get_socket()); + s1ap_ptr->mme_socket.close(); procInfo("S1AP socket closed."); + s1ap_ptr->mme_connect_timer.run(); // Try again with in 10 seconds } } @@ -230,24 +230,28 @@ void s1ap::s1_setup_proc_t::then(const srsran::proc_state_t& result) const * S1AP class *********************************************************/ -s1ap::s1ap(srsran::task_sched_handle task_sched_, srslog::basic_logger& logger) : - s1setup_proc(this), logger(logger), task_sched(task_sched_) -{} - -int s1ap::init(s1ap_args_t args_, rrc_interface_s1ap* rrc_, srsenb::stack_interface_s1ap_lte* stack_) +s1ap::s1ap(srsran::task_sched_handle task_sched_, + srslog::basic_logger& logger, + srsran::socket_manager_itf* rx_socket_handler_) : + s1setup_proc(this), logger(logger), task_sched(task_sched_), rx_socket_handler(rx_socket_handler_) { - rrc = rrc_; - args = args_; - stack = stack_; + mme_task_queue = task_sched.make_task_queue(); +} + +int s1ap::init(s1ap_args_t args_, rrc_interface_s1ap* rrc_) +{ + rrc = rrc_; + args = args_; build_tai_cgi(); // Setup MME reconnection timer mme_connect_timer = task_sched.get_unique_timer(); auto mme_connect_run = [this](uint32_t tid) { - if (not s1setup_proc.launch()) { + if (s1setup_proc.is_busy()) { logger.error("Failed to initiate S1Setup procedure."); } + s1setup_proc.launch(); }; mme_connect_timer.set(10000, mme_connect_run); // Setup S1Setup timeout @@ -272,7 +276,7 @@ int s1ap::init(s1ap_args_t args_, rrc_interface_s1ap* rrc_, srsenb::stack_interf void s1ap::stop() { running = false; - s1ap_socket.reset(); + mme_socket.close(); } void s1ap::get_metrics(s1ap_metrics_t& m) @@ -432,23 +436,29 @@ bool s1ap::is_mme_connected() bool s1ap::connect_mme() { + using namespace srsran::net_utils; logger.info("Connecting to MME %s:%d", args.mme_addr.c_str(), int(MME_PORT)); // Init SCTP socket and bind it - if (not srsran::net_utils::sctp_init_client( - &s1ap_socket, srsran::net_utils::socket_type::seqpacket, args.s1c_bind_addr.c_str())) { + if (not sctp_init_client(&mme_socket, socket_type::seqpacket, args.s1c_bind_addr.c_str())) { return false; } - logger.info("SCTP socket opened. fd=%d", s1ap_socket.fd()); + logger.info("SCTP socket opened. fd=%d", mme_socket.fd()); // Connect to the MME address - if (not s1ap_socket.connect_to(args.mme_addr.c_str(), MME_PORT, &mme_addr)) { + if (not mme_socket.connect_to(args.mme_addr.c_str(), MME_PORT, &mme_addr)) { return false; } - logger.info("SCTP socket connected with MME. fd=%d", s1ap_socket.fd()); + logger.info("SCTP socket connected with MME. fd=%d", mme_socket.fd()); - // Assign a handler to rx MME packets (going to run in a different thread) - stack->add_mme_socket(s1ap_socket.fd()); + // Assign a handler to rx MME packets + auto rx_callback = + [this](srsran::unique_byte_buffer_t pdu, const sockaddr_in& from, const sctp_sndrcvinfo& sri, int flags) { + // Defer the handling of MME packet to eNB stack main thread + handle_mme_rx_msg(std::move(pdu), from, sri, flags); + }; + rx_socket_handler->add_socket_handler(mme_socket.fd(), + srsran::make_sctp_sdu_handler(logger, mme_task_queue, rx_callback)); logger.info("SCTP socket established with MME"); return true; @@ -507,26 +517,28 @@ bool s1ap::handle_mme_rx_msg(srsran::unique_byte_buffer_t pdu, if (notification->sn_header.sn_type == SCTP_SHUTDOWN_EVENT) { logger.info("SCTP Association Shutdown. Association: %d", sri.sinfo_assoc_id); srsran::console("SCTP Association Shutdown. Association: %d\n", sri.sinfo_assoc_id); - stack->remove_mme_socket(s1ap_socket.get_socket()); - s1ap_socket.reset(); + rx_socket_handler->remove_socket(mme_socket.get_socket()); + mme_socket.close(); } else if (notification->sn_header.sn_type == SCTP_PEER_ADDR_CHANGE && notification->sn_paddr_change.spc_state == SCTP_ADDR_UNREACHABLE) { logger.info("SCTP peer addres unreachable. Association: %d", sri.sinfo_assoc_id); srsran::console("SCTP peer address unreachable. Association: %d\n", sri.sinfo_assoc_id); - stack->remove_mme_socket(s1ap_socket.get_socket()); - s1ap_socket.reset(); + rx_socket_handler->remove_socket(mme_socket.get_socket()); + mme_socket.close(); } } else if (pdu->N_bytes == 0) { logger.error("SCTP return 0 bytes. Closing socket"); - s1ap_socket.reset(); + mme_socket.close(); } // Restart MME connection procedure if we lost connection - if (not s1ap_socket.is_init()) { + if (not mme_socket.is_open()) { mme_connected = false; - if (not s1setup_proc.launch()) { - logger.error("Failed to initiate MME connection procedure."); + if (s1setup_proc.is_busy()) { + logger.error("Failed to initiate MME connection procedure, as it is already running."); + return false; } + s1setup_proc.launch(); return false; } @@ -1605,7 +1617,7 @@ bool s1ap::sctp_send_s1ap_pdu(const asn1::s1ap::s1ap_pdu_c& tx_pdu, uint32_t rnt } uint16_t streamid = rnti == SRSRAN_INVALID_RNTI ? NONUE_STREAM_ID : users.find_ue_rnti(rnti)->stream_id; - ssize_t n_sent = sctp_sendmsg(s1ap_socket.fd(), + ssize_t n_sent = sctp_sendmsg(mme_socket.fd(), buf->msg, buf->N_bytes, (struct sockaddr*)&mme_addr, diff --git a/srsenb/test/mac/sched_test_common.cc b/srsenb/test/mac/sched_test_common.cc index 49802eecc..9a026229f 100644 --- a/srsenb/test/mac/sched_test_common.cc +++ b/srsenb/test/mac/sched_test_common.cc @@ -20,8 +20,8 @@ */ #include "sched_test_common.h" +#include "srsenb/hdr/common/common_enb.h" #include "srsenb/hdr/stack/mac/sched.h" -#include "srsenb/hdr/stack/upper/common_enb.h" #include "sched_common_test_suite.h" #include "sched_ue_ded_test_suite.h" diff --git a/srsenb/test/mac/sched_test_utils.h b/srsenb/test/mac/sched_test_utils.h index a948ee2c7..5a8a2f935 100644 --- a/srsenb/test/mac/sched_test_utils.h +++ b/srsenb/test/mac/sched_test_utils.h @@ -22,8 +22,8 @@ #ifndef SRSRAN_SCHED_TEST_UTILS_H #define SRSRAN_SCHED_TEST_UTILS_H +#include "srsenb/hdr/common/common_enb.h" #include "srsenb/hdr/stack/mac/sched.h" -#include "srsenb/hdr/stack/upper/common_enb.h" #include "srsran/common/test_common.h" #include "srsran/interfaces/sched_interface.h" #include diff --git a/srsenb/test/phy/CMakeLists.txt b/srsenb/test/phy/CMakeLists.txt index e048b9004..f12167367 100644 --- a/srsenb/test/phy/CMakeLists.txt +++ b/srsenb/test/phy/CMakeLists.txt @@ -64,7 +64,7 @@ add_lte_test(enb_phy_test_tm4 enb_phy_test --duration=${ENB_PHY_TEST_DURATION} - # - 5 Aggregated carriers # - 6 PRB # - PUCCH format 3 ACK/NACK feedback mode and more than 2 ACK/NACK bits in PUSCH -add_lte_test(enb_phy_test_tm1_ca_pucch3 enb_phy_test --duration=${ENB_PHY_TEST_DURATION} --nof_enb_cells=6 --ue_cell_list=3,4,0,1,2 --ack_mode=pucch3 --cell.nof_prb=6 --tm=1) +add_lte_test(enb_phy_test_tm1_ca_pucch3 enb_phy_test --duration=${ENB_PHY_TEST_DURATION} --nof_enb_cells=5 --ue_cell_list=3,4,0,1,2 --ack_mode=pucch3 --cell.nof_prb=6 --tm=1) # Five carrier aggregation using PUCCH3: # - 6 eNb cell/carrier @@ -72,7 +72,7 @@ add_lte_test(enb_phy_test_tm1_ca_pucch3 enb_phy_test --duration=${ENB_PHY_TEST_D # - 5 Aggregated carriers # - 6 PRB # - PUCCH format 3 ACK/NACK feedback mode and more than 2 ACK/NACK bits in PUSCH -add_lte_test(enb_phy_test_tm4_ca_pucch3 enb_phy_test --duration=${ENB_PHY_TEST_DURATION} --nof_enb_cells=6 --ue_cell_list=0,4,3,1,2 --ack_mode=pucch3 --cell.nof_prb=6 --tm=4) +add_lte_test(enb_phy_test_tm4_ca_pucch3 enb_phy_test --duration=${ENB_PHY_TEST_DURATION} --nof_enb_cells=5 --ue_cell_list=0,4,3,1,2 --ack_mode=pucch3 --cell.nof_prb=6 --tm=4) # Two carrier aggregation using Channel Selection: # - 6 eNb cell/carrier @@ -80,7 +80,7 @@ add_lte_test(enb_phy_test_tm4_ca_pucch3 enb_phy_test --duration=${ENB_PHY_TEST_D # - 2 Aggregated carriers # - 6 PRB # - PUCCH format 1b with Channel selection ACK/NACK feedback mode -add_lte_test(enb_phy_test_tm1_ca_cs enb_phy_test --duration=${ENB_PHY_TEST_DURATION} --nof_enb_cells=6 --ue_cell_list=5,4 --ack_mode=cs --cell.nof_prb=6 --tm=1) +add_lte_test(enb_phy_test_tm1_ca_cs enb_phy_test --duration=${ENB_PHY_TEST_DURATION} --nof_enb_cells=5 --ue_cell_list=4,3 --ack_mode=cs --cell.nof_prb=6 --tm=1) # Two carrier aggregation using Channel Selection: # - 6 eNb cell/carrier @@ -88,7 +88,7 @@ add_lte_test(enb_phy_test_tm1_ca_cs enb_phy_test --duration=${ENB_PHY_TEST_DURAT # - 2 Aggregated carriers # - 6 PRB # - PUCCH format 1b with Channel selection ACK/NACK feedback mode -add_lte_test(enb_phy_test_tm4_ca_cs enb_phy_test --duration=${ENB_PHY_TEST_DURATION} --nof_enb_cells=6 --ue_cell_list=1,5 --ack_mode=cs --cell.nof_prb=6 --tm=4) +add_lte_test(enb_phy_test_tm4_ca_cs enb_phy_test --duration=${ENB_PHY_TEST_DURATION} --nof_enb_cells=5 --ue_cell_list=0,4 --ack_mode=cs --cell.nof_prb=6 --tm=4) # Two carrier aggregation using Channel Selection and HO: # - 3 eNb cell/carrier diff --git a/srsenb/test/upper/CMakeLists.txt b/srsenb/test/upper/CMakeLists.txt index a7150be22..ae6f4ede8 100644 --- a/srsenb/test/upper/CMakeLists.txt +++ b/srsenb/test/upper/CMakeLists.txt @@ -19,7 +19,7 @@ # add_library(test_helpers test_helpers.cc) -target_link_libraries(test_helpers srsenb_rrc rrc_asn1 s1ap_asn1 srsran_common enb_cfg_parser ${LIBCONFIGPP_LIBRARIES}) +target_link_libraries(test_helpers srsenb_rrc srsenb_common rrc_asn1 s1ap_asn1 srsran_common enb_cfg_parser ${LIBCONFIGPP_LIBRARIES}) # Simple PLMN -> MCC/MNC test add_executable(plmn_test plmn_test.cc) @@ -29,7 +29,7 @@ add_executable(rrc_mobility_test rrc_mobility_test.cc) target_link_libraries(rrc_mobility_test srsran_asn1 test_helpers) add_executable(erab_setup_test erab_setup_test.cc) -target_link_libraries(erab_setup_test srsenb_rrc rrc_asn1 s1ap_asn1 srsran_common srsran_asn1 enb_cfg_parser test_helpers ${LIBCONFIGPP_LIBRARIES}) +target_link_libraries(erab_setup_test test_helpers ${LIBCONFIGPP_LIBRARIES}) add_executable(rrc_meascfg_test rrc_meascfg_test.cc) target_link_libraries(rrc_meascfg_test test_helpers) diff --git a/srsenb/test/upper/gtpu_test.cc b/srsenb/test/upper/gtpu_test.cc index c110071c9..e046540cb 100644 --- a/srsenb/test/upper/gtpu_test.cc +++ b/srsenb/test/upper/gtpu_test.cc @@ -34,14 +34,6 @@ namespace srsenb { static const size_t PDU_HEADER_SIZE = 20; -class stack_tester : public stack_interface_gtpu_lte -{ -public: - int s1u_fd = -1; - void add_gtpu_s1u_socket_handler(int fd) { s1u_fd = fd; } - void add_gtpu_m1u_socket_handler(int fd) {} -}; - class pdcp_tester : public pdcp_dummy { public: @@ -76,7 +68,35 @@ public: uint32_t last_lcid = 0; }; -int GTPU_PORT = 2152; +struct dummy_socket_manager : public srsran::socket_manager_itf { + dummy_socket_manager() : srsran::socket_manager_itf(srslog::fetch_basic_logger("TEST")) {} + + /// Register (fd, callback). callback is called within socket thread when fd has data. + bool add_socket_handler(int fd, recv_callback_t handler) final + { + if (s1u_fd >= 0) { + return false; + } + s1u_fd = fd; + callback = std::move(handler); + return true; + } + + /// remove registered socket fd + bool remove_socket(int fd) final + { + if (s1u_fd < 0) { + return false; + } + s1u_fd = -1; + return true; + } + + int s1u_fd = -1; + recv_callback_t callback; +}; + +const int GTPU_PORT = 2152; srsran::unique_byte_buffer_t encode_ipv4_packet(srsran::span data, uint32_t teid, @@ -218,11 +238,11 @@ int test_gtpu_direct_tunneling(tunnel_test_event event) srslog::basic_logger& logger2 = srslog::fetch_basic_logger("GTPU2"); logger2.set_hex_dump_max_size(2048); srsran::task_scheduler task_sched; - srsenb::gtpu senb_gtpu(&task_sched, logger1), tenb_gtpu(&task_sched, logger2); - stack_tester senb_stack, tenb_stack; - pdcp_tester senb_pdcp, tenb_pdcp; - senb_gtpu.init(senb_addr_str, sgw_addr_str, "", "", &senb_pdcp, &senb_stack, false); - tenb_gtpu.init(tenb_addr_str, sgw_addr_str, "", "", &tenb_pdcp, &tenb_stack, false); + dummy_socket_manager senb_rx_sockets, tenb_rx_sockets; + srsenb::gtpu senb_gtpu(&task_sched, logger1, &senb_rx_sockets), tenb_gtpu(&task_sched, logger2, &tenb_rx_sockets); + pdcp_tester senb_pdcp, tenb_pdcp; + senb_gtpu.init(senb_addr_str, sgw_addr_str, "", "", &senb_pdcp, false); + tenb_gtpu.init(tenb_addr_str, sgw_addr_str, "", "", &tenb_pdcp, false); // create tunnels MME-SeNB and MME-TeNB uint32_t senb_teid_in = senb_gtpu.add_bearer(rnti, drb1, sgw_addr, sgw_teidout1).value(); @@ -253,9 +273,9 @@ int test_gtpu_direct_tunneling(tunnel_test_event event) srsran::span pdu_view{}; // TEST: GTPU buffers incoming PDCP buffered SNs until the TEID is explicitly activated - tenb_gtpu.handle_gtpu_s1u_rx_packet(read_socket(tenb_stack.s1u_fd), senb_sockaddr); + tenb_gtpu.handle_gtpu_s1u_rx_packet(read_socket(tenb_rx_sockets.s1u_fd), senb_sockaddr); TESTASSERT(tenb_pdcp.last_sdu == nullptr); - tenb_gtpu.handle_gtpu_s1u_rx_packet(read_socket(tenb_stack.s1u_fd), senb_sockaddr); + tenb_gtpu.handle_gtpu_s1u_rx_packet(read_socket(tenb_rx_sockets.s1u_fd), senb_sockaddr); TESTASSERT(tenb_pdcp.last_sdu == nullptr); tenb_gtpu.set_tunnel_status(dl_tenb_teid_in, true); pdu_view = srsran::make_span(tenb_pdcp.last_sdu); @@ -266,7 +286,7 @@ int test_gtpu_direct_tunneling(tunnel_test_event event) // TEST: verify that PDCP buffered SNs have been forwarded through SeNB->TeNB tunnel for (size_t sn = 8; sn < 10; ++sn) { - tenb_gtpu.handle_gtpu_s1u_rx_packet(read_socket(tenb_stack.s1u_fd), senb_sockaddr); + tenb_gtpu.handle_gtpu_s1u_rx_packet(read_socket(tenb_rx_sockets.s1u_fd), senb_sockaddr); pdu_view = srsran::make_span(tenb_pdcp.last_sdu); TESTASSERT(std::count(pdu_view.begin() + PDU_HEADER_SIZE, pdu_view.end(), sn) == 10); TESTASSERT(tenb_pdcp.last_rnti == rnti2); @@ -279,7 +299,7 @@ int test_gtpu_direct_tunneling(tunnel_test_event event) pdu = encode_gtpu_packet(data_vec, senb_teid_in, sgw_sockaddr, senb_sockaddr); encoded_data.assign(pdu->msg + 8u, pdu->msg + pdu->N_bytes); senb_gtpu.handle_gtpu_s1u_rx_packet(std::move(pdu), sgw_sockaddr); - tenb_gtpu.handle_gtpu_s1u_rx_packet(read_socket(tenb_stack.s1u_fd), senb_sockaddr); + tenb_gtpu.handle_gtpu_s1u_rx_packet(read_socket(tenb_rx_sockets.s1u_fd), senb_sockaddr); pdu_view = srsran::make_span(tenb_pdcp.last_sdu); TESTASSERT(pdu_view.size() == encoded_data.size() and std::equal(pdu_view.begin(), pdu_view.end(), encoded_data.begin())); @@ -301,7 +321,7 @@ int test_gtpu_direct_tunneling(tunnel_test_event event) pdu = encode_gtpu_packet(data_vec, senb_teid_in, sgw_sockaddr, senb_sockaddr); encoded_data.assign(pdu->msg + 8u, pdu->msg + pdu->N_bytes); senb_gtpu.handle_gtpu_s1u_rx_packet(std::move(pdu), sgw_sockaddr); - tenb_gtpu.handle_gtpu_s1u_rx_packet(read_socket(tenb_stack.s1u_fd), senb_sockaddr); + tenb_gtpu.handle_gtpu_s1u_rx_packet(read_socket(tenb_rx_sockets.s1u_fd), senb_sockaddr); TESTASSERT(tenb_pdcp.last_sdu->N_bytes == encoded_data.size() and memcmp(tenb_pdcp.last_sdu->msg, encoded_data.data(), encoded_data.size()) == 0); tenb_pdcp.clear(); @@ -316,7 +336,7 @@ int test_gtpu_direct_tunneling(tunnel_test_event event) // TEST: EndMarker is forwarded via MME->SeNB->TeNB, and TeNB buffered PDUs are flushed pdu = encode_end_marker(senb_teid_in); senb_gtpu.handle_gtpu_s1u_rx_packet(std::move(pdu), sgw_sockaddr); - tenb_gtpu.handle_gtpu_s1u_rx_packet(read_socket(tenb_stack.s1u_fd), senb_sockaddr); + tenb_gtpu.handle_gtpu_s1u_rx_packet(read_socket(tenb_rx_sockets.s1u_fd), senb_sockaddr); } srsran::span encoded_data2{tenb_pdcp.last_sdu->msg + 20u, tenb_pdcp.last_sdu->msg + 30u}; TESTASSERT(std::all_of(encoded_data2.begin(), encoded_data2.end(), [N_pdus](uint8_t b) { return b == N_pdus - 1; })); diff --git a/srsenb/test/upper/plmn_test.cc b/srsenb/test/upper/plmn_test.cc index 5f82c5cc0..fcf91d1ad 100644 --- a/srsenb/test/upper/plmn_test.cc +++ b/srsenb/test/upper/plmn_test.cc @@ -19,7 +19,7 @@ * */ -#include "srsenb/hdr/stack/upper/common_enb.h" +#include "srsenb/hdr/common/common_enb.h" #include "srsran/asn1/rrc/common.h" #include "srsran/asn1/rrc_utils.h" #include "srsran/common/bcd_helpers.h" diff --git a/srsenb/test/upper/s1ap_test.cc b/srsenb/test/upper/s1ap_test.cc index 19995abd3..16e7897c8 100644 --- a/srsenb/test/upper/s1ap_test.cc +++ b/srsenb/test/upper/s1ap_test.cc @@ -26,13 +26,6 @@ using namespace srsenb; -class stack_dummy : public srsenb::stack_interface_s1ap_lte -{ -public: - void add_mme_socket(int fd) {} - void remove_mme_socket(int fd) {} -}; - struct mme_dummy { mme_dummy(const char* addr_str_, int port_) : addr_str(addr_str_), port(port_) { @@ -78,6 +71,34 @@ struct mme_dummy { srsran::unique_byte_buffer_t last_sdu; }; +struct dummy_socket_manager : public srsran::socket_manager_itf { + dummy_socket_manager() : srsran::socket_manager_itf(srslog::fetch_basic_logger("TEST")) {} + + /// Register (fd, callback). callback is called within socket thread when fd has data. + bool add_socket_handler(int fd, recv_callback_t handler) final + { + if (s1u_fd > 0) { + return false; + } + s1u_fd = fd; + callback = std::move(handler); + return true; + } + + /// remove registered socket fd + bool remove_socket(int fd) final + { + if (s1u_fd < 0) { + return false; + } + s1u_fd = -1; + return true; + } + + int s1u_fd; + recv_callback_t callback; +}; + struct rrc_tester : public rrc_dummy { void modify_erabs(uint16_t rnti, const asn1::s1ap::erab_modify_request_s& msg, @@ -178,9 +199,9 @@ void test_s1ap_erab_setup(test_event event) { srsran::task_scheduler task_sched; srslog::basic_logger& logger = srslog::fetch_basic_logger("S1AP"); - s1ap s1ap_obj(&task_sched, logger); + dummy_socket_manager rx_sockets; + s1ap s1ap_obj(&task_sched, logger, &rx_sockets); rrc_tester rrc; - stack_dummy stack; asn1::s1ap::s1ap_pdu_c s1ap_pdu; srsran::unique_byte_buffer_t sdu; @@ -199,7 +220,7 @@ void test_s1ap_erab_setup(test_event event) args.mme_addr = mme_addr_str; args.enb_name = "srsenb01"; - TESTASSERT(s1ap_obj.init(args, &rrc, &stack) == SRSRAN_SUCCESS); + TESTASSERT(s1ap_obj.init(args, &rrc) == SRSRAN_SUCCESS); run_s1_setup(s1ap_obj, mme); add_rnti(s1ap_obj, mme); diff --git a/srsenb/test/upper/test_helpers.cc b/srsenb/test/upper/test_helpers.cc index e1e2a1057..d28cc3300 100644 --- a/srsenb/test/upper/test_helpers.cc +++ b/srsenb/test/upper/test_helpers.cc @@ -69,6 +69,7 @@ int parse_default_cfg(rrc_cfg_t* rrc_cfg, srsenb::all_args_t& args) args.enb.nof_ports = 1; args.general.eia_pref_list = "EIA2, EIA1, EIA0"; args.general.eea_pref_list = "EEA0, EEA2, EEA1"; + args.stack.mac.max_nof_ues = 2; args.general.rrc_inactivity_timer = 60000; diff --git a/srsepc/src/mme/nas.cc b/srsepc/src/mme/nas.cc index aa60563c1..3da42d11d 100644 --- a/srsepc/src/mme/nas.cc +++ b/srsepc/src/mme/nas.cc @@ -283,6 +283,10 @@ bool nas::handle_imsi_attach_request_unknown_ue(uint32_t // Pack NAS Authentication Request in Downlink NAS Transport msg nas_tx = srsran::make_byte_buffer(); + if (nas_tx == nullptr) { + nas_logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return false; + } nas_ctx->pack_authentication_request(nas_tx.get()); // Send reply to eNB @@ -392,6 +396,10 @@ bool nas::handle_guti_attach_request_unknown_ue(uint32_t // Send Identity Request nas_tx = srsran::make_byte_buffer(); + if (nas_tx == nullptr) { + srslog::fetch_basic_logger("NAS").error("Couldn't allocate PDU in %s().", __FUNCTION__); + return false; + } nas_ctx->pack_identity_request(nas_tx.get()); s1ap->send_downlink_nas_transport( nas_ctx->m_ecm_ctx.enb_ue_s1ap_id, nas_ctx->m_ecm_ctx.mme_ue_s1ap_id, nas_tx.get(), nas_ctx->m_ecm_ctx.enb_sri); @@ -468,6 +476,10 @@ bool nas::handle_guti_attach_request_known_ue(nas* // Send reply nas_tx = srsran::make_byte_buffer(); + if (nas_tx == nullptr) { + nas_logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return false; + } if (ecm_ctx->eit) { srsran::console("Secure ESM information transfer requested.\n"); nas_logger.info("Secure ESM information transfer requested."); @@ -540,7 +552,11 @@ bool nas::handle_guti_attach_request_known_ue(nas* // Restarting security context. Reseting eKSI to 0. sec_ctx->eksi = 0; - nas_tx = srsran::unique_byte_buffer_t(); + nas_tx = srsran::make_byte_buffer(); + if (nas_tx == nullptr) { + nas_logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return false; + } nas_ctx->pack_authentication_request(nas_tx.get()); // Send reply to eNB @@ -590,6 +606,10 @@ bool nas::handle_service_request(uint32_t m_tmsi, nas_tmp.m_ecm_ctx.mme_ue_s1ap_id = s1ap->get_next_mme_ue_s1ap_id(); srsran::unique_byte_buffer_t nas_tx = srsran::make_byte_buffer(); + if (nas_tx == nullptr) { + nas_logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return false; + } nas_tmp.pack_service_reject(nas_tx.get(), LIBLTE_MME_EMM_CAUSE_IMPLICITLY_DETACHED); s1ap->send_downlink_nas_transport(enb_ue_s1ap_id, nas_tmp.m_ecm_ctx.mme_ue_s1ap_id, nas_tx.get(), *enb_sri); return true; @@ -604,6 +624,10 @@ bool nas::handle_service_request(uint32_t m_tmsi, nas_tmp.m_ecm_ctx.mme_ue_s1ap_id = s1ap->get_next_mme_ue_s1ap_id(); srsran::unique_byte_buffer_t nas_tx = srsran::make_byte_buffer(); + if (nas_tx == nullptr) { + nas_logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return false; + } nas_tmp.pack_service_reject(nas_tx.get(), LIBLTE_MME_EMM_CAUSE_IMPLICITLY_DETACHED); s1ap->send_downlink_nas_transport(enb_ue_s1ap_id, nas_tmp.m_ecm_ctx.mme_ue_s1ap_id, nas_tx.get(), *enb_sri); return true; @@ -688,6 +712,10 @@ bool nas::handle_service_request(uint32_t m_tmsi, s1ap->add_nas_ctx_to_mme_ue_s1ap_id_map(nas_ctx); s1ap->add_ue_to_enb_set(enb_sri->sinfo_assoc_id, nas_ctx->m_ecm_ctx.mme_ue_s1ap_id); srsran::unique_byte_buffer_t nas_tx = srsran::make_byte_buffer(); + if (nas_tx == nullptr) { + nas_logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return false; + } nas_ctx->pack_service_reject(nas_tx.get(), LIBLTE_MME_EMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK); s1ap->send_downlink_nas_transport(ecm_ctx->enb_ue_s1ap_id, ecm_ctx->mme_ue_s1ap_id, nas_tx.get(), *enb_sri); @@ -792,6 +820,10 @@ bool nas::handle_tracking_area_update_request(uint32_t m_tmsi, nas_tmp.m_ecm_ctx.mme_ue_s1ap_id = s1ap->get_next_mme_ue_s1ap_id(); srsran::unique_byte_buffer_t nas_tx = srsran::make_byte_buffer(); + if (nas_tx == nullptr) { + nas_logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return false; + } nas_tmp.pack_tracking_area_update_reject(nas_tx.get(), LIBLTE_MME_EMM_CAUSE_IMPLICITLY_DETACHED); s1ap->send_downlink_nas_transport(enb_ue_s1ap_id, nas_tmp.m_ecm_ctx.mme_ue_s1ap_id, nas_tx.get(), *enb_sri); return true; @@ -895,6 +927,10 @@ bool nas::handle_attach_request(srsran::byte_buffer_t* nas_rx) // Pack NAS Authentication Request in Downlink NAS Transport msg srsran::unique_byte_buffer_t nas_tx = srsran::make_byte_buffer(); + if (nas_tx == nullptr) { + m_logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return false; + } pack_authentication_request(nas_tx.get()); // Send reply to eNB @@ -912,8 +948,7 @@ bool nas::handle_attach_request(srsran::byte_buffer_t* nas_rx) bool nas::handle_authentication_response(srsran::byte_buffer_t* nas_rx) { - srsran::unique_byte_buffer_t nas_tx; - LIBLTE_MME_AUTHENTICATION_RESPONSE_MSG_STRUCT auth_resp; + LIBLTE_MME_AUTHENTICATION_RESPONSE_MSG_STRUCT auth_resp = {}; bool ue_valid = true; // Get NAS authentication response @@ -936,7 +971,11 @@ bool nas::handle_authentication_response(srsran::byte_buffer_t* nas_rx) } } - nas_tx = srsran::make_byte_buffer(); + srsran::unique_byte_buffer_t nas_tx = srsran::make_byte_buffer(); + if (nas_tx == nullptr) { + m_logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return false; + } if (!ue_valid) { // Authentication rejected srsran::console("UE Authentication Rejected.\n"); @@ -964,8 +1003,7 @@ bool nas::handle_authentication_response(srsran::byte_buffer_t* nas_rx) bool nas::handle_security_mode_complete(srsran::byte_buffer_t* nas_rx) { - srsran::unique_byte_buffer_t nas_tx; - LIBLTE_MME_SECURITY_MODE_COMPLETE_MSG_STRUCT sm_comp; + LIBLTE_MME_SECURITY_MODE_COMPLETE_MSG_STRUCT sm_comp = {}; // Get NAS security mode complete LIBLTE_ERROR_ENUM err = liblte_mme_unpack_security_mode_complete_msg((LIBLTE_BYTE_MSG_STRUCT*)nas_rx, &sm_comp); @@ -979,7 +1017,11 @@ bool nas::handle_security_mode_complete(srsran::byte_buffer_t* nas_rx) srsran::console("Security Mode Command Complete -- IMSI: %015" PRIu64 "\n", m_emm_ctx.imsi); // Check wether secure ESM information transfer is required - nas_tx = srsran::make_byte_buffer(); + srsran::unique_byte_buffer_t nas_tx = srsran::make_byte_buffer(); + if (nas_tx == nullptr) { + m_logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return false; + } if (m_ecm_ctx.eit == true) { // Secure ESM information transfer is required srsran::console("Sending ESM information request\n"); @@ -1006,7 +1048,6 @@ bool nas::handle_attach_complete(srsran::byte_buffer_t* nas_rx) LIBLTE_MME_ATTACH_COMPLETE_MSG_STRUCT attach_comp; uint8_t pd, msg_type; LIBLTE_MME_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_ACCEPT_MSG_STRUCT act_bearer; - srsran::unique_byte_buffer_t nas_tx; // Get NAS authentication response std::memset(&attach_comp, 0, sizeof(attach_comp)); @@ -1036,7 +1077,11 @@ bool nas::handle_attach_complete(srsran::byte_buffer_t* nas_rx) m_emm_ctx.imsi, act_bearer.eps_bearer_id, &m_esm_ctx[act_bearer.eps_bearer_id].enb_fteid); // Send reply to EMM Info to UE - nas_tx = srsran::make_byte_buffer(); + srsran::unique_byte_buffer_t nas_tx = srsran::make_byte_buffer(); + if (nas_tx == nullptr) { + m_logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return false; + } pack_emm_information(nas_tx.get()); m_s1ap->send_downlink_nas_transport( @@ -1126,6 +1171,10 @@ bool nas::handle_identity_response(srsran::byte_buffer_t* nas_rx) // Pack NAS Authentication Request in Downlink NAS Transport msg nas_tx = srsran::make_byte_buffer(); + if (nas_tx == nullptr) { + m_logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return false; + } pack_authentication_request(nas_tx.get()); // Send reply to eNB @@ -1147,6 +1196,10 @@ bool nas::handle_tracking_area_update_request(srsran::byte_buffer_t* nas_rx) /* TAU handling unsupported, therefore send TAU reject with cause IMPLICITLY DETACHED. * this will trigger full re-attach by the UE, instead of going to a TAU request loop */ nas_tx = srsran::make_byte_buffer(); + if (nas_tx == nullptr) { + m_logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return false; + } // TODO we could enable integrity protection in some cases, but UE should comply anyway pack_tracking_area_update_reject(nas_tx.get(), LIBLTE_MME_EMM_CAUSE_IMPLICITLY_DETACHED); // Send reply @@ -1204,6 +1257,10 @@ bool nas::handle_authentication_failure(srsran::byte_buffer_t* nas_rx) // Pack NAS Authentication Request in Downlink NAS Transport msg nas_tx = srsran::make_byte_buffer(); + if (nas_tx == nullptr) { + m_logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return false; + } pack_authentication_request(nas_tx.get()); // Send reply to eNB diff --git a/srsue/hdr/metrics_csv.h b/srsue/hdr/metrics_csv.h index edbaa672c..1caf490bf 100644 --- a/srsue/hdr/metrics_csv.h +++ b/srsue/hdr/metrics_csv.h @@ -51,6 +51,14 @@ public: void stop(); private: + void set_metrics_helper(const srsran::rf_metrics_t rf, + const srsran::sys_metrics_t sys, + const phy_metrics_t phy, + const mac_metrics_t mac[SRSRAN_MAX_CARRIERS], + const rrc_metrics_t rrc, + const uint32_t cc, + const uint32_t r); + std::string float_to_string(float f, int digits, bool add_semicolon = true); std::ofstream file; diff --git a/srsue/hdr/metrics_stdout.h b/srsue/hdr/metrics_stdout.h index aa5efa49c..7c6694ffc 100644 --- a/srsue/hdr/metrics_stdout.h +++ b/srsue/hdr/metrics_stdout.h @@ -48,10 +48,14 @@ public: private: static const bool FORCE_NEIGHBOUR_CELL = false; // Set to true for printing always neighbour cells - - std::string float_to_string(float f, int digits); - std::string float_to_eng_string(float f, int digits); - void print_table(const bool display_neighbours); + void set_metrics_helper(const phy_metrics_t phy, + const mac_metrics_t mac[SRSRAN_MAX_CARRIERS], + const rrc_metrics_t rrc, + bool display_neighbours, + const uint32_t r); + std::string float_to_string(float f, int digits); + std::string float_to_eng_string(float f, int digits); + void print_table(const bool display_neighbours); bool do_print = false; bool table_has_neighbours = false; ///< state of last table head diff --git a/srsue/hdr/phy/nr/cc_worker.h b/srsue/hdr/phy/nr/cc_worker.h index c8bd885cf..86bb757a0 100644 --- a/srsue/hdr/phy/nr/cc_worker.h +++ b/srsue/hdr/phy/nr/cc_worker.h @@ -35,6 +35,7 @@ public: ~cc_worker(); bool set_carrier(const srsran_carrier_nr_t* carrier); + bool update_cfg(); void set_tti(uint32_t tti); cf_t* get_rx_buffer(uint32_t antenna_idx); @@ -47,6 +48,7 @@ public: int read_pdsch_d(cf_t* pdsch_d); private: + bool configured = false; srsran_slot_cfg_t dl_slot_cfg = {}; srsran_slot_cfg_t ul_slot_cfg = {}; uint32_t cc_idx = 0; diff --git a/srsue/hdr/phy/nr/sf_worker.h b/srsue/hdr/phy/nr/sf_worker.h index 0cbe6e0f6..4439f4863 100644 --- a/srsue/hdr/phy/nr/sf_worker.h +++ b/srsue/hdr/phy/nr/sf_worker.h @@ -44,6 +44,7 @@ public: ~sf_worker() = default; bool set_carrier_unlocked(uint32_t cc_idx, const srsran_carrier_nr_t* carrier_); + bool update_cfg(uint32_t cc_idx); /* Functions used by main PHY thread */ cf_t* get_buffer(uint32_t cc_idx, uint32_t antenna_idx); @@ -65,8 +66,8 @@ private: srslog::basic_logger& logger; uint32_t tti_rx = 0; - cf_t* prach_ptr = nullptr; - float prach_power = 0; + cf_t* prach_ptr = nullptr; + float prach_power = 0; }; } // namespace nr diff --git a/srsue/hdr/phy/nr/state.h b/srsue/hdr/phy/nr/state.h index 90dab16e2..16a52068b 100644 --- a/srsue/hdr/phy/nr/state.h +++ b/srsue/hdr/phy/nr/state.h @@ -56,9 +56,6 @@ private: srsran::circular_array pending_ack = {}; mutable std::mutex pending_ack_mutex; - /// Pending scheduling request identifiers - std::set pending_sr_id; - /// CSI-RS measurements std::array csi_measurements = {}; @@ -72,7 +69,9 @@ public: /// Physical layer higher layer configuration, provided by higher layers through configuration messages srsran::phy_cfg_nr_t cfg = {}; - uint16_t ra_rnti = 0; + /// Semaphore for aligning UL work + srsran::tti_semaphore dl_ul_semaphore; + uint32_t rar_grant_tti = 0; state() @@ -226,6 +225,12 @@ public: // Calculate Receive TTI uint32_t tti_tx = TTI_ADD(tti_rx, ack_resource.k1); + // Prepare ACK information + srsran_pdsch_ack_m_nr_t ack_m = {}; + ack_m.resource = ack_resource; + ack_m.value[0] = crc_ok ? 1 : 0; + ack_m.present = true; + // Scope mutex to protect read/write the list std::lock_guard lock(pending_ack_mutex); @@ -233,15 +238,10 @@ public: srsran_pdsch_ack_nr_t& ack = pending_ack[tti_tx]; ack.nof_cc = 1; - // Select serving cell - srsran_pdsch_ack_cc_nr_t& ack_cc = ack.cc[ack_resource.scell_idx]; - srsran_pdsch_ack_m_nr_t& ack_m = ack_cc.m[ack_cc.M]; - ack_cc.M++; - - // Set PDSCH transmission information - ack_m.resource = ack_resource; - ack_m.value[0] = crc_ok ? 1 : 0; - ack_m.present = true; + // Insert PDSCH transmission information + if (srsran_ue_dl_nr_ack_insert_m(&ack, &ack_m) < SRSRAN_SUCCESS) { + ERROR("Error inserting ACK m value"); + } } bool get_pending_ack(const uint32_t& tti_tx, srsran_pdsch_ack_nr_t& pdsch_ack) @@ -266,15 +266,31 @@ public: return true; } - void reset() { pending_sr_id.clear(); } + void reset() { clear_pending_grants(); } - void set_pending_sr(uint32_t value) { pending_sr_id.insert(value); } + bool has_valid_sr_resource(uint32_t sr_id) + { + for (const srsran_pucch_nr_sr_resource_t& r : cfg.pucch.sr_resources) { + if (r.configured && r.sr_id == sr_id) { + return true; + } + } + return false; + } + + void clear_pending_grants() + { + // Scope mutex to protect read/write the list + std::lock_guard lock(pending_ul_grant_mutex); + + // Clear all PDSCH assignments and PUSCH grants + pending_dl_grant = {}; + pending_ul_grant = {}; + pending_ack = {}; + } void get_pending_sr(const uint32_t& tti, srsran_uci_data_nr_t& uci_data) { - // Append fixed SR - pending_sr_id.insert(args.fixed_sr.begin(), args.fixed_sr.end()); - // Calculate all SR opportunities in the given TTI uint32_t sr_resource_id[SRSRAN_PUCCH_MAX_NOF_SR_RESOURCES] = {}; int n = srsran_ue_ul_nr_sr_send_slot(cfg.pucch.sr_resources, tti, sr_resource_id); @@ -293,12 +309,10 @@ public: uint32_t sr_id = cfg.pucch.sr_resources[sr_resource_id[i]].sr_id; // Check if the SR resource ID is pending - if (pending_sr_id.count(sr_id) > 0) { + if (args.fixed_sr.count(sr_id) > 0 || + stack->sr_opportunity(tti, sr_id, false, pending_ul_grant[TTI_TX(tti)].enable)) { // Count it as present sr_count_positive++; - - // Erase pending SR - pending_sr_id.erase(sr_id); } } diff --git a/srsue/hdr/phy/nr/worker_pool.h b/srsue/hdr/phy/nr/worker_pool.h index f26fb1824..88eb44b60 100644 --- a/srsue/hdr/phy/nr/worker_pool.h +++ b/srsue/hdr/phy/nr/worker_pool.h @@ -49,7 +49,8 @@ public: void send_prach(uint32_t prach_occasion, uint32_t preamble_index, int preamble_received_target_power); int set_ul_grant(std::array array, uint16_t rnti, srsran_rnti_type_t rnti_type); bool set_config(const srsran::phy_cfg_nr_t& cfg); - void sr_send(uint32_t sr_id); + bool has_valid_sr_resource(uint32_t sr_id); + void clear_pending_grants(); }; } // namespace nr diff --git a/srsue/hdr/phy/phy.h b/srsue/hdr/phy/phy.h index 8e1591349..4484d0245 100644 --- a/srsue/hdr/phy/phy.h +++ b/srsue/hdr/phy/phy.h @@ -185,7 +185,8 @@ public: const float ta_base_sec = 0.0f) final; int tx_request(const tx_request_t& request) final; void set_earfcn(std::vector earfcns) final; - void sr_send(uint32_t sr_id) final; + bool has_valid_sr_resource(uint32_t sr_id) final; + void clear_pending_grants() final; private: void run_thread() final; diff --git a/srsue/hdr/phy/vnf_phy_nr.h b/srsue/hdr/phy/vnf_phy_nr.h index 65a03650f..a88f29b8f 100644 --- a/srsue/hdr/phy/vnf_phy_nr.h +++ b/srsue/hdr/phy/vnf_phy_nr.h @@ -66,7 +66,8 @@ public: const int prach_occasion, const float target_power_dbm, const float ta_base_sec = 0.0f) override{}; - void sr_send(uint32_t sr_id) override; + bool has_valid_sr_resource(uint32_t sr_id) override; + void clear_pending_grants() override; private: std::unique_ptr vnf; diff --git a/srsue/hdr/stack/mac/proc_bsr.h b/srsue/hdr/stack/mac/proc_bsr.h index 358c9a981..2adaa3c9d 100644 --- a/srsue/hdr/stack/mac/proc_bsr.h +++ b/srsue/hdr/stack/mac/proc_bsr.h @@ -97,6 +97,8 @@ private: std::map lcgs[NOF_LCG]; // groups LCID in LCG + mac_buffer_states_t old_buffer_state; + uint32_t find_max_priority_lcg_with_data(); bsr_trigger_type_t triggered_bsr_type = NONE; diff --git a/srsue/hdr/stack/mac_common/mac_common.h b/srsue/hdr/stack/mac_common/mac_common.h index 013299e09..7e24fb87c 100644 --- a/srsue/hdr/stack/mac_common/mac_common.h +++ b/srsue/hdr/stack/mac_common/mac_common.h @@ -22,6 +22,10 @@ #ifndef SRSUE_MAC_COMMON_H #define SRSUE_MAC_COMMON_H +#include "srsran/common/string_helpers.h" +#include "srsran/srslog/srslog.h" +#include + /** * @brief Common definitions/interfaces between LTE/NR MAC components * @@ -34,6 +38,51 @@ namespace srsue { typedef enum { NONE, REGULAR, PADDING, PERIODIC } bsr_trigger_type_t; char* bsr_trigger_type_tostring(bsr_trigger_type_t type); +/// Helper class to store a snapshot of buffer states for all LCGs/LCIDs +class mac_buffer_states_t +{ +public: + explicit mac_buffer_states_t() {} + void reset() + { + nof_lcids_with_data = 0; + nof_lcgs_with_data = 0; + last_non_zero_lcg = -1; + lcid_buffer_size.clear(); + lcg_buffer_size.clear(); + } + std::string to_string() + { + fmt::memory_buffer buffer; + + fmt::format_to(buffer, + "nof_lcids_with_data={}, nof_lcgs_with_data={}, last_non_zero_lcg={} ", + nof_lcids_with_data, + nof_lcgs_with_data, + last_non_zero_lcg); + + fmt::format_to(buffer, "["); + for (const auto& lcg : lcg_buffer_size) { + fmt::format_to(buffer, "lcg{}={}, ", lcg.first, lcg.second); + } + fmt::format_to(buffer, "] "); + + fmt::format_to(buffer, "["); + for (const auto& lcid : lcid_buffer_size) { + fmt::format_to(buffer, "lcid{}={}, ", lcid.first, lcid.second); + } + fmt::format_to(buffer, "]"); + + return srsran::to_c_str(buffer); + } + + std::map lcid_buffer_size; // Buffer size entry for each LCID + std::map lcg_buffer_size; // Entry for each LCG (sum of LCIDs of that LCG) + uint32_t nof_lcids_with_data = 0; // Is incremented when a LCID is found with data to transmit + uint32_t nof_lcgs_with_data = 0; // Is incremented when a LCG is found with data to transmit + int32_t last_non_zero_lcg = -1; // only valid if nof_lcgs_with_data is at least one +}; + } // namespace srsue #endif // SRSUE_MAC_COMMON_H diff --git a/srsue/hdr/stack/mac_nr/mac_nr.h b/srsue/hdr/stack/mac_nr/mac_nr.h index 2252fc00a..7e5b1cb6d 100644 --- a/srsue/hdr/stack/mac_nr/mac_nr.h +++ b/srsue/hdr/stack/mac_nr/mac_nr.h @@ -31,6 +31,7 @@ #include "srsran/interfaces/mac_interface_types.h" #include "srsran/interfaces/ue_nr_interfaces.h" #include "srsran/srslog/srslog.h" +#include "srsue/hdr/stack/mac_common/mac_common.h" #include "srsue/hdr/stack/mac_nr/mux_nr.h" #include "srsue/hdr/stack/ue_stack_base.h" @@ -38,8 +39,7 @@ namespace srsue { class rlc_interface_mac; -struct mac_nr_args_t { -}; +struct mac_nr_args_t {}; class mac_nr final : public mac_interface_phy_nr, public mac_interface_rrc_nr, @@ -72,6 +72,7 @@ public: const uint32_t t_id, const uint32_t f_id, const uint32_t ul_carrier_id); + bool sr_opportunity(uint32_t tti, uint32_t sr_id, bool meas_gap, bool ul_sch_tx); /// Stack interface void timer_expired(uint32_t timer_id); @@ -90,21 +91,23 @@ public: uint64_t get_contention_id(); uint16_t get_crnti(); + /// Interface for MUX + srsran::mac_sch_subpdu_nr::lcg_bsr_t generate_sbsr(); + void msg3_flush() { mux.msg3_flush(); } bool msg3_is_transmitted() { return mux.msg3_is_transmitted(); } void msg3_prepare() { mux.msg3_prepare(); } bool msg3_is_pending() { return mux.msg3_is_pending(); } bool msg3_is_empty() { return mux.msg3_is_empty(); } + /// RRC + void rrc_ra_problem() { rrc->ra_problem(); } + /// stack interface void process_pdus(); static bool is_in_window(uint32_t tti, int* start, int* len); - // PHY Interface - void prach_sent(const uint32_t tti); - void tb_decoded_ok(const uint8_t cc_idx, const uint32_t tti); - private: void write_pcap(const uint32_t cc_idx, mac_nr_grant_dl_t& grant); // If PCAPs are enabled for this MAC void handle_pdu(srsran::unique_byte_buffer_t pdu); @@ -116,8 +119,14 @@ private: bool is_si_opportunity(); bool is_paging_opportunity(); - bool has_crnti(); - bool is_valid_crnti(const uint16_t crnti); + bool has_crnti(); + bool is_valid_crnti(const uint16_t crnti); + + std::vector logical_channels; // stores the raw configs provide by upper layers + + /// LCID and LCG related members and helper functions + void update_buffer_states(); + mac_buffer_states_t mac_buffer_states; /// Interaction with rest of the stack phy_interface_mac_nr* phy = nullptr; @@ -137,15 +146,15 @@ private: srsran::block_queue pdu_queue; ///< currently only DCH PDUs supported (add BCH, PCH, etc) - mac_metrics_t metrics[SRSRAN_MAX_CARRIERS] = {}; + std::array metrics = {}; /// Rx buffer srsran::mac_sch_pdu_nr rx_pdu; /// Tx buffer srsran::unique_byte_buffer_t ul_harq_buffer = nullptr; // store PDU generated from MUX - srsran::unique_byte_buffer_t rlc_buffer = nullptr; - srsran_softbuffer_tx_t softbuffer_tx = {}; /// UL HARQ (temporal) + srsran::unique_byte_buffer_t rlc_buffer = nullptr; + srsran_softbuffer_tx_t softbuffer_tx = {}; /// UL HARQ (temporal) srsran::task_multiqueue::queue_handle stack_task_dispatch_queue; diff --git a/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h b/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h index ec0f8c949..9afcc49df 100644 --- a/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h +++ b/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h @@ -23,6 +23,7 @@ #define SRSUE_MAC_NR_INTERFACES_H #include "srsran/common/interfaces_common.h" +#include "srsran/mac/mac_sch_pdu_nr.h" namespace srsue { /** @@ -41,6 +42,9 @@ public: virtual void msg3_flush() = 0; virtual void msg3_prepare() = 0; virtual bool msg3_is_empty() = 0; + + // RRC functions + virtual void rrc_ra_problem() = 0; }; /** @@ -51,6 +55,9 @@ class mac_interface_mux_nr public: // MUX can query MAC for current C-RNTI for Msg3 transmission virtual uint16_t get_crnti() = 0; + + // MUX queries MAC to return LCG state for SBSR + virtual srsran::mac_sch_subpdu_nr::lcg_bsr_t generate_sbsr() = 0; }; } // namespace srsue diff --git a/srsue/hdr/stack/mac_nr/mux_nr.h b/srsue/hdr/stack/mac_nr/mux_nr.h index 4c6e55231..59e5348ab 100644 --- a/srsue/hdr/stack/mac_nr/mux_nr.h +++ b/srsue/hdr/stack/mac_nr/mux_nr.h @@ -42,8 +42,6 @@ public: void reset(); int32_t init(rlc_interface_mac* rlc_); - void step(); - void msg3_flush(); void msg3_prepare(); void msg3_transmitted(); @@ -58,7 +56,7 @@ public: srsran::unique_byte_buffer_t get_pdu(uint32_t max_pdu_len); // Interface for BSR procedure - void generate_bsr_mac_ce(); + void generate_bsr_mac_ce(const bsr_interface_mux_nr::bsr_format_nr_t& format); private: // internal helper methods @@ -73,12 +71,14 @@ private: typedef enum { none, pending, transmitted } msg3_state_t; msg3_state_t msg3_state = none; - static constexpr uint32_t MIN_RLC_PDU_LEN = + static constexpr int32_t MIN_RLC_PDU_LEN = 5; ///< minimum bytes that need to be available in a MAC PDU for attempting to add another RLC SDU srsran::unique_byte_buffer_t rlc_buff = nullptr; - srsran::mac_sch_pdu_nr tx_pdu; + srsran::mac_sch_pdu_nr tx_pdu; /// single MAC PDU for packing + + enum { no_bsr, sbsr_ce, lbsr_ce } add_bsr_ce = no_bsr; /// BSR procedure requests MUX to add a BSR CE // Mutex for exclusive access std::mutex mutex; diff --git a/srsue/hdr/stack/mac_nr/proc_bsr_nr.h b/srsue/hdr/stack/mac_nr/proc_bsr_nr.h index 13089af7a..84b001151 100644 --- a/srsue/hdr/stack/mac_nr/proc_bsr_nr.h +++ b/srsue/hdr/stack/mac_nr/proc_bsr_nr.h @@ -27,6 +27,7 @@ #include "proc_sr_nr.h" #include "srsran/common/task_scheduler.h" +#include "srsran/mac/mac_sch_pdu_nr.h" #include "srsran/srslog/srslog.h" #include "srsue/hdr/stack/mac_common/mac_common.h" @@ -43,21 +44,15 @@ public: // TS 38.321 Sec 6.1.3.1 typedef enum { SHORT_BSR, LONG_BSR, SHORT_TRUNC_BSR, LONG_TRUNC_BSR } bsr_format_nr_t; - // FIXME: this will be replaced - typedef struct { - bsr_format_nr_t format; - uint32_t buff_size[4]; - } bsr_t; - - /// MUX calls BSR to let it generate a padding BSR if there is space in PDU. - virtual bool generate_padding_bsr(uint32_t nof_padding_bytes, bsr_t* bsr) = 0; + /// MUX calls BSR to receive the buffer state of a single LCG. + virtual srsran::mac_sch_subpdu_nr::lcg_bsr_t generate_sbsr() = 0; }; class mux_interface_bsr_nr { public: /// Inform MUX unit to that a BSR needs to be generated in the next UL transmission. - virtual void generate_bsr_mac_ce() = 0; + virtual void generate_bsr_mac_ce(const bsr_interface_mux_nr::bsr_format_nr_t& format) = 0; }; /** @@ -68,25 +63,24 @@ public: class proc_bsr_nr : public srsran::timer_callback, public bsr_interface_mux_nr { public: - explicit proc_bsr_nr(srslog::basic_logger& logger) : logger(logger) {} + explicit proc_bsr_nr(srslog::basic_logger& logger_) : logger(logger_) {} int init(proc_sr_nr* sr_proc, mux_interface_bsr_nr* mux_, rlc_interface_mac* rlc, srsran::ext_task_sched_handle* task_sched_); - void step(uint32_t tti); + void step(uint32_t tti, const mac_buffer_states_t& new_buffer_state); void reset(); int set_config(const srsran::bsr_cfg_nr_t& bsr_cfg); int setup_lcid(uint32_t lcid, uint32_t lcg, uint32_t priority); void timer_expired(uint32_t timer_id); - uint32_t get_buffer_state(); /// Called by MAC when an UL grant is received void new_grant_ul(uint32_t grant_size); - // bool need_to_send_bsr(); - bool generate_padding_bsr(uint32_t nof_padding_bytes, bsr_t* bsr); - void update_bsr_tti_end(const bsr_t* bsr); + /// MUX interface for BSR generation + srsran::mac_sch_subpdu_nr::lcg_bsr_t generate_sbsr(); + bool generate_padding_bsr(uint32_t nof_padding_bytes); private: const static int QUEUE_STATUS_PERIOD_MS = 1000; @@ -103,27 +97,20 @@ private: bool initiated = false; - const static int MAX_NOF_LCG = 8; + mac_buffer_states_t buffer_state; - typedef struct { - int priority; - uint32_t old_buffer; - uint32_t new_buffer; - } lcid_t; - - std::map lcgs[MAX_NOF_LCG]; // groups LCID in LCG + // map of LCGs and their priorities, key is the priority (sorted) and the value the LCG + std::map lcg_priorities; bsr_trigger_type_t triggered_bsr_type = NONE; void print_state(); void set_trigger(bsr_trigger_type_t new_trigger); - void update_new_data(); - void update_old_buffer(); bool check_highest_channel(); - bool check_new_data(); + bool check_new_data(const mac_buffer_states_t& new_buffer_state); bool check_any_channel(); - uint32_t get_buffer_state_lcg(uint32_t lcg); - bool generate_bsr(bsr_t* bsr, uint32_t nof_padding_bytes); + + uint8_t buff_size_bytes_to_field(uint32_t buffer_size, bsr_format_nr_t format); uint32_t find_max_priority_lcg_with_data(); diff --git a/srsue/hdr/stack/mac_nr/proc_ra_nr.h b/srsue/hdr/stack/mac_nr/proc_ra_nr.h index 8a74240d6..683fe0d0e 100644 --- a/srsue/hdr/stack/mac_nr/proc_ra_nr.h +++ b/srsue/hdr/stack/mac_nr/proc_ra_nr.h @@ -91,10 +91,12 @@ private: srsran::timer_handler::unique_timer prach_send_timer; srsran::timer_handler::unique_timer rar_timeout_timer; srsran::timer_handler::unique_timer contention_resolution_timer; + srsran::timer_handler::unique_timer backoff_timer; // 38.321 5.1.1 Variables uint32_t preamble_index = 0; uint32_t preamble_transmission_counter = 0; + uint32_t preamble_backoff = 0; // in ms uint32_t preamble_power_ramping_step = 0; int preamble_received_target_power = 0; uint32_t scaling_factor_bi = 0; diff --git a/srsue/hdr/stack/mac_nr/proc_sr_nr.h b/srsue/hdr/stack/mac_nr/proc_sr_nr.h index 6401715d7..16b71e94e 100644 --- a/srsue/hdr/stack/mac_nr/proc_sr_nr.h +++ b/srsue/hdr/stack/mac_nr/proc_sr_nr.h @@ -44,10 +44,9 @@ public: int32_t set_config(const srsran::sr_cfg_nr_t& cfg); void reset(); void start(); + bool sr_opportunity(uint32_t tti, uint32_t sr_id, bool meas_gap, bool ul_sch_tx); private: - bool need_tx(uint32_t tti); - int sr_counter = 0; bool is_pending_sr = 0; diff --git a/srsue/hdr/stack/rrc/rrc.h b/srsue/hdr/stack/rrc/rrc.h index 22549d74d..fc5bb55a3 100644 --- a/srsue/hdr/stack/rrc/rrc.h +++ b/srsue/hdr/stack/rrc/rrc.h @@ -348,6 +348,10 @@ private: // RLC interface void max_retx_attempted(); + // RRC NR interface + void nr_scg_failure_information(const srsran::scg_failure_cause_t cause); + void nr_notify_reconfiguration_failure(); + // Senders void send_con_request(srsran::establishment_cause_t cause); void send_con_restablish_request(asn1::rrc::reest_cause_e cause, uint16_t rnti, uint16_t pci, uint32_t cellid); @@ -413,7 +417,7 @@ private: void set_mac_default(); void set_rrc_default(); - bool nr_reconfiguration_proc(const asn1::rrc::rrc_conn_recfg_r8_ies_s& rx_recfg); + bool nr_reconfiguration_proc(const asn1::rrc::rrc_conn_recfg_r8_ies_s& rx_recfg, bool* has_5g_nr_reconfig); // Helpers for nr communicaiton asn1::rrc::ue_cap_rat_container_s get_eutra_nr_capabilities(); diff --git a/srsue/hdr/stack/rrc/rrc_nr.h b/srsue/hdr/stack/rrc/rrc_nr.h index 284698b7c..41852aa7b 100644 --- a/srsue/hdr/stack/rrc/rrc_nr.h +++ b/srsue/hdr/stack/rrc/rrc_nr.h @@ -49,7 +49,8 @@ struct core_less_args_t { struct rrc_nr_args_t { core_less_args_t coreless; - std::vector supported_bands; + std::vector supported_bands_nr; + std::vector supported_bands_eutra; std::string log_level; uint32_t log_hex_limit; }; @@ -104,13 +105,12 @@ public: void in_sync() final; void out_of_sync() final; - // MAC interface - void run_tti(uint32_t tti) final; // RLC interface void max_retx_attempted() final; // MAC interface + void run_tti(uint32_t tti) final; void ra_completed() final; void ra_problem() final; void release_pucch_srs() final; @@ -216,20 +216,25 @@ private: bool apply_csi_meas_cfg(const asn1::rrc_nr::csi_meas_cfg_s& csi_meas_cfg); bool apply_res_csi_report_cfg(const asn1::rrc_nr::csi_report_cfg_s& csi_report_cfg); bool apply_drb_add_mod(const asn1::rrc_nr::drb_to_add_mod_s& drb_cfg); + bool apply_drb_release(const uint8_t drb); bool apply_security_cfg(const asn1::rrc_nr::security_cfg_s& security_cfg); srsran::as_security_config_t sec_cfg; + typedef enum { mcg_srb1, en_dc_srb3, nr } reconf_initiator_t; + class connection_reconf_no_ho_proc { public: explicit connection_reconf_no_ho_proc(rrc_nr* parent_); - srsran::proc_outcome_t init(const bool endc_release_and_add_r15, - const asn1::rrc_nr::rrc_recfg_s& rrc_recfg, - const asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg, - bool sk_counter_r15_present, - const uint32_t sk_counter_r15, - const asn1::rrc_nr::radio_bearer_cfg_s& radio_bearer_cfg); + srsran::proc_outcome_t init(const reconf_initiator_t initiator_, + const bool endc_release_and_add_r15, + const bool nr_secondary_cell_group_cfg_r15_present, + const asn1::dyn_octstring nr_secondary_cell_group_cfg_r15, + const bool sk_counter_r15_present, + const uint32_t sk_counter_r15, + const bool nr_radio_bearer_cfg1_r15_present, + const asn1::dyn_octstring nr_radio_bearer_cfg1_r15); srsran::proc_outcome_t step() { return srsran::proc_outcome_t::yield; } static const char* name() { return "NR Connection Reconfiguration"; } srsran::proc_outcome_t react(const bool& config_complete); @@ -237,8 +242,8 @@ private: private: // const - rrc_nr* rrc_ptr; - + rrc_nr* rrc_ptr; + reconf_initiator_t initiator; asn1::rrc_nr::rrc_recfg_s rrc_recfg; asn1::rrc_nr::cell_group_cfg_s cell_group_cfg; }; diff --git a/srsue/hdr/stack/ue_stack_lte.h b/srsue/hdr/stack/ue_stack_lte.h index 89a5e53a6..e5a438813 100644 --- a/srsue/hdr/stack/ue_stack_lte.h +++ b/srsue/hdr/stack/ue_stack_lte.h @@ -152,6 +152,11 @@ public: mac_nr.prach_sent(tti, s_id, t_id, f_id, ul_carrier_id); } + bool sr_opportunity(uint32_t tti, uint32_t sr_id, bool meas_gap, bool ul_sch_tx) final + { + return mac_nr.sr_opportunity(tti, sr_id, meas_gap, ul_sch_tx); + } + // Interface for GW void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) final; diff --git a/srsue/hdr/stack/ue_stack_nr.h b/srsue/hdr/stack/ue_stack_nr.h index ed082ee47..0c63af17f 100644 --- a/srsue/hdr/stack/ue_stack_nr.h +++ b/srsue/hdr/stack/ue_stack_nr.h @@ -79,7 +79,7 @@ public: // RRC interface for PHY void in_sync() final; void out_of_sync() final; - void run_tti(uint32_t tti) final; + void run_tti(const uint32_t tti) final; // MAC interface for PHY sched_rnti_t get_dl_sched_rnti_nr(const uint32_t tti) final { return mac->get_dl_sched_rnti_nr(tti); } @@ -98,6 +98,10 @@ public: { mac->prach_sent(tti, s_id, t_id, f_id, ul_carrier_id); } + bool sr_opportunity(uint32_t tti, uint32_t sr_id, bool meas_gap, bool ul_sch_tx) + { + return mac->sr_opportunity(tti, sr_id, meas_gap, ul_sch_tx); + } // Interface for GW void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) final; diff --git a/srsue/hdr/stack/upper/gw.h b/srsue/hdr/stack/upper/gw.h index 6a2f556cb..a174b96c7 100644 --- a/srsue/hdr/stack/upper/gw.h +++ b/srsue/hdr/stack/upper/gw.h @@ -75,6 +75,7 @@ public: // RRC interface void add_mch_port(uint32_t lcid, uint32_t port); int update_lcid(uint32_t eps_bearer_id, uint32_t new_lcid); + bool is_running(); private: static const int GW_THREAD_PRIO = -1; diff --git a/srsue/hdr/ue_metrics_interface.h b/srsue/hdr/ue_metrics_interface.h index 8446508d9..fbc50e140 100644 --- a/srsue/hdr/ue_metrics_interface.h +++ b/srsue/hdr/ue_metrics_interface.h @@ -39,14 +39,17 @@ namespace srsue { typedef struct { uint32_t ul_dropped_sdus; mac_metrics_t mac[SRSRAN_MAX_CARRIERS]; + mac_metrics_t mac_nr[SRSRAN_MAX_CARRIERS]; srsran::rlc_metrics_t rlc; nas_metrics_t nas; rrc_metrics_t rrc; + rrc_metrics_t rrc_nr; } stack_metrics_t; typedef struct { srsran::rf_metrics_t rf; phy_metrics_t phy; + phy_metrics_t phy_nr; gw_metrics_t gw; stack_metrics_t stack; srsran::sys_metrics_t sys; diff --git a/srsue/src/metrics_csv.cc b/srsue/src/metrics_csv.cc index 70abfd263..8c5d7f45b 100644 --- a/srsue/src/metrics_csv.cc +++ b/srsue/src/metrics_csv.cc @@ -74,6 +74,108 @@ void metrics_csv::stop() } } +void metrics_csv::set_metrics_helper(const srsran::rf_metrics_t rf, + const srsran::sys_metrics_t sys, + const phy_metrics_t phy, + const mac_metrics_t mac[SRSRAN_MAX_CARRIERS], + const rrc_metrics_t rrc, + const uint32_t cc, + const uint32_t r) +{ + if (not file.is_open()) { + return; + } + + file << time_ms << ";"; + + // CC and PCI + file << cc << ";"; + file << phy.info[r].dl_earfcn << ";"; + file << phy.info[r].pci << ";"; + + // Print PHY metrics for first CC + file << float_to_string(phy.ch[r].rsrp, 2); + file << float_to_string(phy.ch[r].pathloss, 2); + file << float_to_string(phy.sync[r].cfo, 2); + + // Find strongest neighbour for this EARFCN (cells are ordered) + bool has_neighbour = false; + for (auto& c : rrc.neighbour_cells) { + if (c.earfcn == phy.info[r].dl_earfcn && c.pci != phy.info[r].pci) { + file << c.pci << ";"; + file << float_to_string(c.rsrp, 2); + file << float_to_string(c.cfo_hz, 2); + has_neighbour = true; + break; + } + } + if (!has_neighbour) { + file << "n/a;"; + file << "n/a;"; + file << "n/a;"; + } + + file << float_to_string(phy.dl[r].mcs, 2); + file << float_to_string(phy.ch[r].sinr, 2); + file << float_to_string(phy.dl[r].turbo_iters, 2); + + if (mac[r].rx_brate > 0) { + file << float_to_string(mac[r].rx_brate / (mac[r].nof_tti * 1e-3), 2); + } else { + file << float_to_string(0, 2); + } + + int rx_pkts = mac[r].rx_pkts; + int rx_errors = mac[r].rx_errors; + if (rx_pkts > 0) { + file << float_to_string((float)100 * rx_errors / rx_pkts, 1); + } else { + file << float_to_string(0, 2); + } + + file << float_to_string(phy.sync[r].ta_us, 2); + file << float_to_string(phy.sync[r].distance_km, 2); + file << float_to_string(phy.sync[r].speed_kmph, 2); + file << float_to_string(phy.ul[r].mcs, 2); + file << float_to_string((float)mac[r].ul_buffer, 2); + + if (mac[r].tx_brate > 0) { + file << float_to_string(mac[r].tx_brate / (mac[r].nof_tti * 1e-3), 2); + } else { + file << float_to_string(0, 2); + } + + // Sum UL BLER for all CCs + int tx_pkts = mac[r].tx_pkts; + int tx_errors = mac[r].tx_errors; + if (tx_pkts > 0) { + file << float_to_string((float)100 * tx_errors / tx_pkts, 1); + } else { + file << float_to_string(0, 2); + } + + file << float_to_string(rf.rf_o, 2); + file << float_to_string(rf.rf_u, 2); + file << float_to_string(rf.rf_l, 2); + file << (rrc.state == RRC_STATE_CONNECTED ? "1.0" : "0.0") << ";"; + + // Write system metrics. + const srsran::sys_metrics_t& m = sys; + file << float_to_string(m.process_realmem, 2); + file << std::to_string(m.process_realmem_kB) << ";"; + file << std::to_string(m.process_virtualmem_kB) << ";"; + file << float_to_string(m.system_mem, 2); + file << float_to_string(m.process_cpu_usage, 2); + file << std::to_string(m.thread_count) << ";"; + + // Write the cpu metrics. + for (uint32_t i = 0, e = m.cpu_count, last_cpu_index = e - 1; i != e; ++i) { + file << float_to_string(m.cpu_load[i], 2, (i != last_cpu_index)); + } + + file << "\n"; +} + void metrics_csv::set_metrics(const ue_metrics_t& metrics, const uint32_t period_usec) { std::unique_lock lock(mutex); @@ -98,95 +200,20 @@ void metrics_csv::set_metrics(const ue_metrics_t& metrics, const uint32_t period file << "\n"; } + // Metrics for LTE carrier for (uint32_t r = 0; r < metrics.phy.nof_active_cc; r++) { - file << time_ms << ";"; + set_metrics_helper(metrics.rf, metrics.sys, metrics.phy, metrics.stack.mac, metrics.stack.rrc, r, r); + } - // CC and PCI - file << r << ";"; - file << metrics.phy.info[r].dl_earfcn << ";"; - file << metrics.phy.info[r].pci << ";"; - - // Print PHY metrics for first CC - file << float_to_string(metrics.phy.ch[r].rsrp, 2); - file << float_to_string(metrics.phy.ch[r].pathloss, 2); - file << float_to_string(metrics.phy.sync[r].cfo, 2); - - // Find strongest neighbour for this EARFCN (cells are ordered) - bool has_neighbour = false; - for (auto& c : metrics.stack.rrc.neighbour_cells) { - if (c.earfcn == metrics.phy.info[r].dl_earfcn && c.pci != metrics.phy.info[r].pci) { - file << c.pci << ";"; - file << float_to_string(c.rsrp, 2); - file << float_to_string(c.cfo_hz, 2); - has_neighbour = true; - break; - } - } - if (!has_neighbour) { - file << "n/a;"; - file << "n/a;"; - file << "n/a;"; - } - - file << float_to_string(metrics.phy.dl[r].mcs, 2); - file << float_to_string(metrics.phy.ch[r].sinr, 2); - file << float_to_string(metrics.phy.dl[r].turbo_iters, 2); - - if (metrics.stack.mac[r].rx_brate > 0) { - file << float_to_string(metrics.stack.mac[r].rx_brate / (metrics.stack.mac[r].nof_tti * 1e-3), 2); - } else { - file << float_to_string(0, 2); - } - - int rx_pkts = metrics.stack.mac[r].rx_pkts; - int rx_errors = metrics.stack.mac[r].rx_errors; - if (rx_pkts > 0) { - file << float_to_string((float)100 * rx_errors / rx_pkts, 1); - } else { - file << float_to_string(0, 2); - } - - file << float_to_string(metrics.phy.sync[r].ta_us, 2); - file << float_to_string(metrics.phy.sync[r].distance_km, 2); - file << float_to_string(metrics.phy.sync[r].speed_kmph, 2); - file << float_to_string(metrics.phy.ul[r].mcs, 2); - file << float_to_string((float)metrics.stack.mac[r].ul_buffer, 2); - - if (metrics.stack.mac[r].tx_brate > 0) { - file << float_to_string(metrics.stack.mac[r].tx_brate / (metrics.stack.mac[r].nof_tti * 1e-3), 2); - } else { - file << float_to_string(0, 2); - } - - // Sum UL BLER for all CCs - int tx_pkts = metrics.stack.mac[r].tx_pkts; - int tx_errors = metrics.stack.mac[r].tx_errors; - if (tx_pkts > 0) { - file << float_to_string((float)100 * tx_errors / tx_pkts, 1); - } else { - file << float_to_string(0, 2); - } - - file << float_to_string(metrics.rf.rf_o, 2); - file << float_to_string(metrics.rf.rf_u, 2); - file << float_to_string(metrics.rf.rf_l, 2); - file << (metrics.stack.rrc.state == RRC_STATE_CONNECTED ? "1.0" : "0.0") << ";"; - - // Write system metrics. - const srsran::sys_metrics_t& m = metrics.sys; - file << float_to_string(m.process_realmem, 2); - file << std::to_string(m.process_realmem_kB) << ";"; - file << std::to_string(m.process_virtualmem_kB) << ";"; - file << float_to_string(m.system_mem, 2); - file << float_to_string(m.process_cpu_usage, 2); - file << std::to_string(m.thread_count) << ";"; - - // Write the cpu metrics. - for (uint32_t i = 0, e = m.cpu_count, last_cpu_index = e - 1; i != e; ++i) { - file << float_to_string(m.cpu_load[i], 2, (i != last_cpu_index)); - } - - file << "\n"; + // Metrics for NR carrier + for (uint32_t r = 0; r < metrics.phy_nr.nof_active_cc; r++) { + set_metrics_helper(metrics.rf, + metrics.sys, + metrics.phy_nr, + metrics.stack.mac_nr, + metrics.stack.rrc, + r, + metrics.phy.nof_active_cc + r); } n_reports++; diff --git a/srsue/src/metrics_stdout.cc b/srsue/src/metrics_stdout.cc index 77f6d2945..e7e991285 100644 --- a/srsue/src/metrics_stdout.cc +++ b/srsue/src/metrics_stdout.cc @@ -85,6 +85,62 @@ void metrics_stdout::print_table(const bool display_neighbours) n_reports = 0; } +void metrics_stdout::set_metrics_helper(const phy_metrics_t phy, + const mac_metrics_t mac[SRSRAN_MAX_CARRIERS], + const rrc_metrics_t rrc, + bool display_neighbours, + const uint32_t r) +{ + if (phy.info[r].pci != UINT32_MAX) { + cout << std::setw(4) << phy.info[r].pci << std::setw(0); + } else { + cout << " n/a"; + } + cout << float_to_string(phy.ch[r].rsrp, 2); + cout << float_to_string(phy.ch[r].pathloss, 2); + cout << float_to_eng_string(phy.sync[r].cfo, 2); + + // Find strongest neighbour for this EARFCN (cells are ordered) + if (display_neighbours) { + bool has_neighbour = false; + for (auto& c : rrc.neighbour_cells) { + if (c.earfcn == phy.info[r].dl_earfcn && c.pci != phy.info[r].pci) { + cout << std::setw(4) << c.pci << std::setw(0); + cout << float_to_string(c.rsrp, 2); + has_neighbour = true; + break; + } + } + if (!has_neighbour) { + cout << " n/a"; + cout << " n/a"; + } + } + + cout << float_to_string(phy.dl[r].mcs, 2); + cout << float_to_string(phy.ch[r].sinr, 2); + cout << float_to_string(phy.dl[r].turbo_iters, 2); + + cout << float_to_eng_string((float)mac[r].rx_brate / (mac[r].nof_tti * 1e-3), 2); + if (mac[r].rx_pkts > 0) { + cout << float_to_string((float)100 * mac[r].rx_errors / mac[r].rx_pkts, 1) << "%"; + } else { + cout << float_to_string(0, 1) << "%"; + } + + cout << float_to_string(phy.sync[r].ta_us, 2); + + cout << float_to_string(phy.ul[r].mcs, 2); + cout << float_to_eng_string((float)mac[r].ul_buffer, 2); + cout << float_to_eng_string((float)mac[r].tx_brate / (mac[r].nof_tti * 1e-3), 2); + if (mac[r].tx_pkts > 0) { + cout << float_to_string((float)100 * mac[r].tx_errors / mac[r].tx_pkts, 1) << "%"; + } else { + cout << float_to_string(0, 1) << "%"; + } + cout << endl; +} + void metrics_stdout::set_metrics(const ue_metrics_t& metrics, const uint32_t period_usec) { if (ue == nullptr) { @@ -124,54 +180,13 @@ void metrics_stdout::set_metrics(const ue_metrics_t& metrics, const uint32_t per for (uint32_t r = 0; r < metrics.phy.nof_active_cc; r++) { cout << std::setw(2) << r; - if (metrics.phy.info[r].pci != UINT32_MAX) { - cout << std::setw(4) << metrics.phy.info[r].pci << std::setw(0); - } else { - cout << " n/a"; - } - cout << float_to_string(metrics.phy.ch[r].rsrp, 2); - cout << float_to_string(metrics.phy.ch[r].pathloss, 2); - cout << float_to_eng_string(metrics.phy.sync[r].cfo, 2); + set_metrics_helper(metrics.phy, metrics.stack.mac, metrics.stack.rrc, display_neighbours, r); + } - // Find strongest neighbour for this EARFCN (cells are ordered) - if (display_neighbours) { - bool has_neighbour = false; - for (auto& c : metrics.stack.rrc.neighbour_cells) { - if (c.earfcn == metrics.phy.info[r].dl_earfcn && c.pci != metrics.phy.info[r].pci) { - cout << std::setw(4) << c.pci << std::setw(0); - cout << float_to_string(c.rsrp, 2); - has_neighbour = true; - break; - } - } - if (!has_neighbour) { - cout << " n/a"; - cout << " n/a"; - } - } - - cout << float_to_string(metrics.phy.dl[r].mcs, 2); - cout << float_to_string(metrics.phy.ch[r].sinr, 2); - cout << float_to_string(metrics.phy.dl[r].turbo_iters, 2); - - cout << float_to_eng_string((float)metrics.stack.mac[r].rx_brate / (metrics.stack.mac[r].nof_tti * 1e-3), 2); - if (metrics.stack.mac[r].rx_pkts > 0) { - cout << float_to_string((float)100 * metrics.stack.mac[r].rx_errors / metrics.stack.mac[r].rx_pkts, 1) << "%"; - } else { - cout << float_to_string(0, 1) << "%"; - } - - cout << float_to_string(metrics.phy.sync[r].ta_us, 2); - - cout << float_to_string(metrics.phy.ul[r].mcs, 2); - cout << float_to_eng_string((float)metrics.stack.mac[r].ul_buffer, 2); - cout << float_to_eng_string((float)metrics.stack.mac[r].tx_brate / (metrics.stack.mac[r].nof_tti * 1e-3), 2); - if (metrics.stack.mac[r].tx_pkts > 0) { - cout << float_to_string((float)100 * metrics.stack.mac[r].tx_errors / metrics.stack.mac[r].tx_pkts, 1) << "%"; - } else { - cout << float_to_string(0, 1) << "%"; - } - cout << endl; + for (uint32_t r = 0; r < metrics.phy_nr.nof_active_cc; r++) { + // Assumption LTE is followed by the NR carriers. + cout << std::setw(2) << metrics.phy.nof_active_cc + r; + set_metrics_helper(metrics.phy_nr, metrics.stack.mac_nr, metrics.stack.rrc, display_neighbours, r); } if (metrics.rf.rf_error) { diff --git a/srsue/src/phy/nr/cc_worker.cc b/srsue/src/phy/nr/cc_worker.cc index 62b8224f2..3a1324caa 100644 --- a/srsue/src/phy/nr/cc_worker.cc +++ b/srsue/src/phy/nr/cc_worker.cc @@ -79,11 +79,6 @@ bool cc_worker::set_carrier(const srsran_carrier_nr_t* carrier) return false; } - if (srsran_ue_dl_nr_set_pdcch_config(&ue_dl, &phy->cfg.pdcch) < SRSRAN_SUCCESS) { - ERROR("Error setting carrier"); - return false; - } - if (srsran_ue_ul_nr_set_carrier(&ue_ul, carrier) < SRSRAN_SUCCESS) { ERROR("Error setting carrier"); return false; @@ -95,10 +90,25 @@ bool cc_worker::set_carrier(const srsran_carrier_nr_t* carrier) return true; } +bool cc_worker::update_cfg() +{ + srsran_dci_cfg_nr_t dci_cfg = phy->cfg.get_dci_cfg(phy->carrier); + + if (srsran_ue_dl_nr_set_pdcch_config(&ue_dl, &phy->cfg.pdcch, &dci_cfg) < SRSRAN_SUCCESS) { + logger.error("Error setting NR PDCCH configuration"); + return false; + } + + configured = true; + + return true; +} + void cc_worker::set_tti(uint32_t tti) { dl_slot_cfg.idx = tti; ul_slot_cfg.idx = TTI_TX(tti); + logger.set_context(tti); } cf_t* cc_worker::get_rx_buffer(uint32_t antenna_idx) @@ -138,7 +148,7 @@ void cc_worker::decode_pdcch_dl() int n_dl = srsran_ue_dl_nr_find_dl_dci(&ue_dl, &dl_slot_cfg, rnti.id, rnti.type, dci_rx.data(), (uint32_t)dci_rx.size()); if (n_dl < SRSRAN_SUCCESS) { - logger.error("Error decoding DL NR-PDCCH"); + logger.error("Error decoding DL NR-PDCCH for %s=0x%x", srsran_rnti_type_str(rnti.type), rnti.id); return; } @@ -158,14 +168,17 @@ void cc_worker::decode_pdcch_dl() if (logger.debug.enabled()) { for (uint32_t i = 0; i < ue_dl.pdcch_info_count; i++) { const srsran_ue_dl_nr_pdcch_info_t* info = &ue_dl.pdcch_info[i]; - logger.debug("PDCCH: crst_id=%d, ss_id=%d, ncce=%d, al=%d, EPRE=%+.2f, RSRP=%+.2f, corr=%.3f; crc=%s", - info->coreset_id, - info->ss_id, - info->location.ncce, - info->location.L, + logger.debug("PDCCH: rnti=0x%x, crst_id=%d, ss_type=%d, ncce=%d, al=%d, EPRE=%+.2f, RSRP=%+.2f, corr=%.3f; " + "nof_bits=%d; crc=%s;", + info->dci_ctx.rnti, + info->dci_ctx.coreset_id, + info->dci_ctx.ss_type, + info->dci_ctx.location.ncce, + info->dci_ctx.location.L, info->measure.epre_dBfs, info->measure.rsrp_dBfs, info->measure.norm_corr, + info->nof_bits, info->result.crc ? "OK" : "KO"); } } @@ -205,6 +218,11 @@ void cc_worker::decode_pdcch_ul() bool cc_worker::work_dl() { + // Do NOT process any DL if it is not configured + if (not configured) { + return true; + } + // Check if it is a DL slot, if not skip if (!srsran_tdd_nr_is_dl(&phy->cfg.tdd, 0, dl_slot_cfg.idx)) { return true; @@ -224,13 +242,20 @@ bool cc_worker::work_dl() srsran_sch_cfg_nr_t pdsch_cfg = {}; srsran_pdsch_ack_resource_nr_t ack_resource = {}; if (phy->get_dl_pending_grant(dl_slot_cfg.idx, pdsch_cfg, ack_resource, pid)) { + // As HARQ processes are not implemented nor LDPC early-stop, retransmissions are disabled for performance reasons + if (pdsch_cfg.grant.tb[0].rv != 0) { + phy->set_pending_ack(dl_slot_cfg.idx, ack_resource, true); + logger.warning("PDSCH Retransmission with rv=%d not supported", pdsch_cfg.grant.tb[0].rv); + return true; + } + // Get data buffer srsran::unique_byte_buffer_t data = srsran::make_byte_buffer(); if (data == nullptr) { logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); return false; } - data->N_bytes = pdsch_cfg.grant.tb[0].tbs / 8U; + data->N_bytes = pdsch_cfg.grant.tb[0].tbs / 8U; // Get soft-buffer from MAC // ... @@ -303,6 +328,14 @@ bool cc_worker::work_ul() // If PDSCH UL ACK is available, load into UCI if (has_ul_ack) { pdsch_ack.use_pusch = has_pusch_grant; + + if (logger.debug.enabled()) { + std::array str = {}; + if (srsran_ue_dl_nr_ack_info(&pdsch_ack, str.data(), (uint32_t)str.size()) > 0) { + logger.debug("%s", str.data()); + } + } + if (srsran_ue_dl_nr_gen_ack(&phy->cfg.harq_ack, &pdsch_ack, &uci_data) < SRSRAN_SUCCESS) { ERROR("Filling UCI ACK bits"); return false; @@ -322,7 +355,7 @@ bool cc_worker::work_ul() mac_ul_grant.pid = pid; mac_ul_grant.rnti = pusch_cfg.grant.rnti; mac_ul_grant.tti = ul_slot_cfg.idx; - mac_ul_grant.tbs = pusch_cfg.grant.tb[0].tbs; + mac_ul_grant.tbs = pusch_cfg.grant.tb[0].tbs / 8; phy->stack->new_grant_ul(0, mac_ul_grant, &ul_action); // Set UCI configuration following procedures diff --git a/srsue/src/phy/nr/sf_worker.cc b/srsue/src/phy/nr/sf_worker.cc index 35743159a..1fbb1570c 100644 --- a/srsue/src/phy/nr/sf_worker.cc +++ b/srsue/src/phy/nr/sf_worker.cc @@ -54,6 +54,15 @@ bool sf_worker::set_carrier_unlocked(uint32_t cc_idx, const srsran_carrier_nr_t* return cc_workers.at(cc_idx)->set_carrier(carrier_); } +bool sf_worker::update_cfg(uint32_t cc_idx) +{ + if (cc_idx >= cc_workers.size()) { + return false; + } + + return cc_workers[cc_idx]->update_cfg(); +} + cf_t* sf_worker::get_buffer(uint32_t cc_idx, uint32_t antenna_idx) { if (cc_idx >= cc_workers.size()) { @@ -87,6 +96,10 @@ void sf_worker::work_imp() w->work_dl(); } + // Align workers, wait for previous workers to finish DL processing before starting UL processing + phy_state->dl_ul_semaphore.wait(this); + phy_state->dl_ul_semaphore.release(); + // Check if PRACH is available if (prach_ptr != nullptr) { // PRACH is available, set buffer, transmit and return diff --git a/srsue/src/phy/nr/worker_pool.cc b/srsue/src/phy/nr/worker_pool.cc index 77beff28a..391e40571 100644 --- a/srsue/src/phy/nr/worker_pool.cc +++ b/srsue/src/phy/nr/worker_pool.cc @@ -69,6 +69,10 @@ bool worker_pool::init(const phy_args_nr_t& args, phy_common* common, stack_inte void worker_pool::start_worker(sf_worker* w) { + // Push worker pointer for internal worker TTI synchronization + phy_state.dl_ul_semaphore.push(w); + + // Signal worker to start processing asynchronously pool.start_worker(w); } @@ -103,16 +107,16 @@ int worker_pool::set_ul_grant(std::array pac { // Copy DCI bits and setup DCI context srsran_dci_msg_nr_t dci_msg = {}; - dci_msg.format = srsran_dci_format_nr_0_0; // MAC RAR grant shall be unpacked as DCI 0_0 format - dci_msg.rnti_type = rnti_type; - dci_msg.search_space = srsran_search_space_type_rar; // This indicates it is a MAC RAR - dci_msg.rnti = rnti; + dci_msg.ctx.format = srsran_dci_format_nr_rar; // MAC RAR grant shall be unpacked as DCI 0_0 format + dci_msg.ctx.rnti_type = rnti_type; + dci_msg.ctx.ss_type = srsran_search_space_type_rar; // This indicates it is a MAC RAR + dci_msg.ctx.rnti = rnti; dci_msg.nof_bits = SRSRAN_RAR_UL_GRANT_NBITS; srsran_vec_u8_copy(dci_msg.payload, packed_ul_grant.data(), SRSRAN_RAR_UL_GRANT_NBITS); srsran_dci_ul_nr_t dci_ul = {}; - if (srsran_dci_nr_rar_unpack(&dci_msg, &dci_ul) < SRSRAN_SUCCESS) { + if (srsran_dci_nr_ul_unpack(NULL, &dci_msg, &dci_ul) < SRSRAN_SUCCESS) { return SRSRAN_ERROR; } @@ -140,12 +144,25 @@ bool worker_pool::set_config(const srsran::phy_cfg_nr_t& cfg) return false; } + // Request workers to run any procedure related to configuration update + for (auto& w : workers) { + if (not w->update_cfg(0)) { + return false; + } + } + return true; } -void worker_pool::sr_send(uint32_t sr_id) +bool worker_pool::has_valid_sr_resource(uint32_t sr_id) { - phy_state.set_pending_sr(sr_id); + return phy_state.has_valid_sr_resource(sr_id); } + +void worker_pool::clear_pending_grants() +{ + phy_state.clear_pending_grants(); +} + } // namespace nr } // namespace srsue diff --git a/srsue/src/phy/phy.cc b/srsue/src/phy/phy.cc index 15767fb0d..c41e037e5 100644 --- a/srsue/src/phy/phy.cc +++ b/srsue/src/phy/phy.cc @@ -630,8 +630,14 @@ bool phy::set_config(const srsran::phy_cfg_nr_t& cfg) return nr_workers.set_config(cfg); } -void phy::sr_send(uint32_t sr_id) +bool phy::has_valid_sr_resource(uint32_t sr_id) { - nr_workers.sr_send(sr_id); + return nr_workers.has_valid_sr_resource(sr_id); } + +void phy::clear_pending_grants() +{ + nr_workers.clear_pending_grants(); +} + } // namespace srsue diff --git a/srsue/src/phy/vnf_phy_nr.cc b/srsue/src/phy/vnf_phy_nr.cc index 18ee0f9c9..297216908 100644 --- a/srsue/src/phy/vnf_phy_nr.cc +++ b/srsue/src/phy/vnf_phy_nr.cc @@ -81,6 +81,11 @@ bool vnf_phy_nr::set_config(const srsran::phy_cfg_nr_t& cfg) { return false; } -void vnf_phy_nr::sr_send(uint32_t sr_id) {} +bool vnf_phy_nr::has_valid_sr_resource(uint32_t sr_id) +{ + return false; +} + +void vnf_phy_nr::clear_pending_grants() {} } // namespace srsue \ No newline at end of file diff --git a/srsue/src/stack/mac_nr/CMakeLists.txt b/srsue/src/stack/mac_nr/CMakeLists.txt index cf8d2ea85..c864f8b45 100644 --- a/srsue/src/stack/mac_nr/CMakeLists.txt +++ b/srsue/src/stack/mac_nr/CMakeLists.txt @@ -20,4 +20,6 @@ set(SOURCES mac_nr.cc proc_ra_nr.cc proc_bsr_nr.cc proc_sr_nr.cc mux_nr.cc) add_library(srsue_mac_nr STATIC ${SOURCES}) -target_link_libraries(srsue_mac_nr srsue_mac_common srsran_mac) \ No newline at end of file +target_link_libraries(srsue_mac_nr srsue_mac_common srsran_mac) + +add_subdirectory(test) \ No newline at end of file diff --git a/srsue/src/stack/mac_nr/mac_nr.cc b/srsue/src/stack/mac_nr/mac_nr.cc index f936e09f9..84f937b79 100644 --- a/srsue/src/stack/mac_nr/mac_nr.cc +++ b/srsue/src/stack/mac_nr/mac_nr.cc @@ -106,8 +106,43 @@ void mac_nr::reset() void mac_nr::run_tti(const uint32_t tti) { + // Early exit if MAC NR isn't used + if (not started) { + return; + } + // Step all procedures logger.debug("Running MAC tti=%d", tti); + + // Update state for all LCIDs/LCGs once so all procedures can use them + update_buffer_states(); + + proc_bsr.step(tti, mac_buffer_states); + proc_sr.step(tti); +} + +void mac_nr::update_buffer_states() +{ + // reset variables + mac_buffer_states.reset(); + for (auto& channel : logical_channels) { + uint32_t buffer_len = rlc->get_buffer_state(channel.lcid); + if (buffer_len > 0) { + mac_buffer_states.nof_lcids_with_data++; + if (channel.lcg != mac_buffer_states.last_non_zero_lcg) { + mac_buffer_states.nof_lcgs_with_data++; + } + mac_buffer_states.last_non_zero_lcg = channel.lcg; + } + mac_buffer_states.lcid_buffer_size[channel.lcid] += buffer_len; + mac_buffer_states.lcg_buffer_size[channel.lcg] += buffer_len; + } + logger.debug("%s", mac_buffer_states.to_string()); + + // Count TTI for metrics + for (uint32_t i = 0; i < SRSRAN_MAX_CARRIERS; ++i) { + metrics[i].nof_tti++; + } } mac_interface_phy_nr::sched_rnti_t mac_nr::get_ul_sched_rnti_nr(const uint32_t tti) @@ -165,6 +200,11 @@ uint16_t mac_nr::get_crnti() return c_rnti; } +srsran::mac_sch_subpdu_nr::lcg_bsr_t mac_nr::generate_sbsr() +{ + return proc_bsr.generate_sbsr(); +} + void mac_nr::bch_decoded_ok(uint32_t tti, srsran::unique_byte_buffer_t payload) { // Send MIB to RLC @@ -190,6 +230,11 @@ void mac_nr::prach_sent(const uint32_t tti, proc_ra.prach_sent(tti, s_id, t_id, f_id, ul_carrier_id); } +bool mac_nr::sr_opportunity(uint32_t tti, uint32_t sr_id, bool meas_gap, bool ul_sch_tx) +{ + return proc_sr.sr_opportunity(tti, sr_id, meas_gap, ul_sch_tx); +} + // This function handles all PCAP writing for a decoded DL TB void mac_nr::write_pcap(const uint32_t cc_idx, mac_nr_grant_dl_t& grant) { @@ -223,15 +268,16 @@ void mac_nr::tb_decoded(const uint32_t cc_idx, mac_nr_grant_dl_t& grant) if (proc_ra.has_rar_rnti() && grant.rnti == proc_ra.get_rar_rnti()) { proc_ra.handle_rar_pdu(grant); } else { - // Push DL PDUs to queue for back-ground processing + // Push DL PDUs to queue for background processing for (uint32_t i = 0; i < SRSRAN_MAX_CODEWORDS; ++i) { if (grant.tb[i] != nullptr) { + metrics[cc_idx].rx_pkts++; + metrics[cc_idx].rx_brate += grant.tb[i]->N_bytes * 8; pdu_queue.push(std::move(grant.tb[i])); } } } - metrics[cc_idx].rx_pkts++; stack_task_dispatch_queue.push([this]() { process_pdus(); }); } @@ -250,17 +296,22 @@ void mac_nr::new_grant_ul(const uint32_t cc_idx, const mac_nr_grant_ul_t& grant, ul_harq_buffer = mux.get_pdu(grant.tbs); // fill TB action (goes into UL harq eventually) - action->tb.payload = ul_harq_buffer.get(); // pass handle to PDU to PHY - action->tb.enabled = true; - action->tb.rv = 0; - action->tb.softbuffer = &softbuffer_tx; - srsran_softbuffer_tx_reset(&softbuffer_tx); + if (ul_harq_buffer != nullptr) { + action->tb.payload = ul_harq_buffer.get(); // pass handle to PDU to PHY + action->tb.enabled = true; + action->tb.rv = 0; + action->tb.softbuffer = &softbuffer_tx; + srsran_softbuffer_tx_reset(&softbuffer_tx); + } else { + action->tb.enabled = false; + } // store PCAP if (pcap) { pcap->write_ul_crnti_nr(ul_harq_buffer->msg, ul_harq_buffer->N_bytes, grant.rnti, grant.pid, grant.tti); } + metrics[cc_idx].tx_brate += grant.tbs * 8; metrics[cc_idx].tx_pkts++; } @@ -289,6 +340,9 @@ int mac_nr::setup_lcid(const srsran::logical_channel_config_t& config) config.BSD, config.bucket_size); + // store full config + logical_channels.push_back(config); + return SRSRAN_SUCCESS; } @@ -326,7 +380,7 @@ bool mac_nr::set_crnti(const uint16_t c_rnti_) void mac_nr::start_ra_procedure() { - proc_ra.start_by_rrc(); + stack_task_dispatch_queue.push([this]() {proc_ra.start_by_rrc();}); } bool mac_nr::is_valid_crnti(const uint16_t crnti) @@ -335,7 +389,31 @@ bool mac_nr::is_valid_crnti(const uint16_t crnti) return (crnti >= 0x0001 && crnti <= 0xFFEF); } -void mac_nr::get_metrics(mac_metrics_t m[SRSRAN_MAX_CARRIERS]) {} +void mac_nr::get_metrics(mac_metrics_t m[SRSRAN_MAX_CARRIERS]) +{ + int tx_pkts = 0; + int tx_errors = 0; + int tx_brate = 0; + int rx_pkts = 0; + int rx_errors = 0; + int rx_brate = 0; + int ul_buffer = 0; + float dl_avg_ret = 0; + int dl_avg_ret_count = 0; + + for (const auto& cc : metrics) { + tx_pkts += cc.tx_pkts; + tx_errors += cc.tx_errors; + tx_brate += cc.tx_brate; + rx_pkts += cc.rx_pkts; + rx_errors += cc.rx_errors; + rx_brate += cc.rx_brate; + ul_buffer += cc.ul_buffer; + } + + memcpy(m, metrics.data(), sizeof(mac_metrics_t) * SRSRAN_MAX_CARRIERS); + metrics = {}; +} /** * Called from the main stack thread to process received PDUs @@ -364,7 +442,23 @@ void mac_nr::handle_pdu(srsran::unique_byte_buffer_t pdu) subpdu.get_c_rnti(), subpdu.get_lcid(), subpdu.get_sdu_length()); - rlc->write_pdu(subpdu.get_lcid(), subpdu.get_sdu(), subpdu.get_sdu_length()); + + // Handle Timing Advance CE + switch (subpdu.get_lcid()) { + case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::DRX_CMD: + logger.info("DRX CE not implemented."); + break; + case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::TA_CMD: + logger.info("Timing Advance CE not implemented."); + break; + case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::CON_RES_ID: + logger.info("Contention Resolution CE not implemented."); + break; + default: + if (subpdu.is_sdu()) { + rlc->write_pdu(subpdu.get_lcid(), subpdu.get_sdu(), subpdu.get_sdu_length()); + } + } } } diff --git a/srsue/src/stack/mac_nr/mux_nr.cc b/srsue/src/stack/mac_nr/mux_nr.cc index 32d5ae0e4..a12917bbe 100644 --- a/srsue/src/stack/mac_nr/mux_nr.cc +++ b/srsue/src/stack/mac_nr/mux_nr.cc @@ -51,12 +51,23 @@ int mux_nr::setup_lcid(const srsran::logical_channel_config_t& config) srsran::unique_byte_buffer_t mux_nr::get_pdu(uint32_t max_pdu_len) { + // Lock MAC PDU from current access from PHY workers (will be moved to UL HARQ) + std::lock_guard lock(mutex); + // initialize MAC PDU srsran::unique_byte_buffer_t phy_tx_pdu = srsran::make_byte_buffer(); if (phy_tx_pdu == nullptr) { + logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); return nullptr; } + // verify buffer is large enough for UL grant + if (phy_tx_pdu->get_tailroom() < max_pdu_len) { + logger.error("Can't provide MAC PDU. Grant too big (%d < %d).", phy_tx_pdu->get_tailroom(), max_pdu_len); + return nullptr; + } + + logger.debug("Building new MAC PDU (%d B)", max_pdu_len); tx_pdu.init_tx(phy_tx_pdu.get(), max_pdu_len, true); if (msg3_is_pending()) { @@ -71,36 +82,65 @@ srsran::unique_byte_buffer_t mux_nr::get_pdu(uint32_t max_pdu_len) msg3_transmitted(); } else { // Pack normal UL data PDU + int32_t remaining_len = tx_pdu.get_remaing_len(); // local variable to reserv space for CEs - // TODO: Add proper priority handling + if (add_bsr_ce == sbsr_ce) { + // reserve space for SBSR + remaining_len -= 2; + } + + // First add MAC SDUs for (const auto& lc : logical_channels) { - while (tx_pdu.get_remaing_len() >= MIN_RLC_PDU_LEN) { + // TODO: Add proper priority handling + logger.debug("Adding SDUs for LCID=%d (max %d B)", lc.lcid, remaining_len); + while (remaining_len >= MIN_RLC_PDU_LEN) { // read RLC PDU rlc_buff->clear(); - uint8_t* rd = rlc_buff->msg; - int pdu_len = 0; - pdu_len = rlc->read_pdu(lc.lcid, rd, tx_pdu.get_remaing_len() - 2); + uint8_t* rd = rlc_buff->msg; - // Add SDU if RLC has something to tx - if (pdu_len > 0) { - rlc_buff->N_bytes = pdu_len; - logger.info(rlc_buff->msg, rlc_buff->N_bytes, "Read %d B from RLC", rlc_buff->N_bytes); + // Determine space for RLC + remaining_len -= remaining_len >= srsran::mac_sch_subpdu_nr::MAC_SUBHEADER_LEN_THRESHOLD ? 3 : 2; - // add to MAC PDU and pack - if (tx_pdu.add_sdu(lc.lcid, rlc_buff->msg, rlc_buff->N_bytes) != SRSRAN_SUCCESS) { - logger.error("Error packing MAC PDU"); - } - } else { + // Read PDU from RLC + int pdu_len = rlc->read_pdu(lc.lcid, rd, remaining_len); + + if (pdu_len > remaining_len) { + logger.error("Can't add SDU of %d B. Available space %d B", pdu_len, remaining_len); break; + } else { + // Add SDU if RLC has something to tx + if (pdu_len > 0) { + rlc_buff->N_bytes = pdu_len; + logger.debug(rlc_buff->msg, rlc_buff->N_bytes, "Read %d B from RLC", rlc_buff->N_bytes); + + // add to MAC PDU and pack + if (tx_pdu.add_sdu(lc.lcid, rlc_buff->msg, rlc_buff->N_bytes) != SRSRAN_SUCCESS) { + logger.error("Error packing MAC PDU"); + break; + } + } else { + break; + } + + remaining_len -= pdu_len; + logger.debug("%d B remaining PDU", remaining_len); } } } + + // Second add fixed-sized MAC CEs (e.g. SBSR) + if (add_bsr_ce == sbsr_ce) { + tx_pdu.add_sbsr_ce(mac.generate_sbsr()); + add_bsr_ce = no_bsr; + } + + // Lastly, add variable-sized MAC CEs } // Pack PDU tx_pdu.pack(); - logger.info(phy_tx_pdu->msg, phy_tx_pdu->N_bytes, "Generated MAC PDU (%d B)", phy_tx_pdu->N_bytes); + logger.debug(phy_tx_pdu->msg, phy_tx_pdu->N_bytes, "Generated MAC PDU (%d B)", phy_tx_pdu->N_bytes); return phy_tx_pdu; } @@ -136,6 +176,17 @@ bool mux_nr::msg3_is_empty() return msg3_buff->N_bytes == 0; } -void mux_nr::generate_bsr_mac_ce() {} +void mux_nr::generate_bsr_mac_ce(const bsr_interface_mux_nr::bsr_format_nr_t& format) +{ + switch (format) { + case bsr_interface_mux_nr::SHORT_BSR: + add_bsr_ce = sbsr_ce; + break; + case bsr_interface_mux_nr::LONG_BSR: + add_bsr_ce = lbsr_ce; + default: + logger.error("MUX can only be instructred to generate short or long BSRs."); + } +} } // namespace srsue diff --git a/srsue/src/stack/mac_nr/proc_bsr_nr.cc b/srsue/src/stack/mac_nr/proc_bsr_nr.cc index 5af45a630..1ec575b86 100644 --- a/srsue/src/stack/mac_nr/proc_bsr_nr.cc +++ b/srsue/src/stack/mac_nr/proc_bsr_nr.cc @@ -25,6 +25,68 @@ namespace srsue { +// TS 38.321, Table 6.1.3.1-1 Buffer size levels (in bytes) for 5-bit Buffer Size field, all values <= except marked +static const uint32_t buffer_size_levels_5bit_max_idx = 31; +static uint32_t buffer_size_levels_5bit[buffer_size_levels_5bit_max_idx + 1] = { + /* == */ 0, 10, 14, 20, 28, 38, 53, 74, 102, 142, 198, + 276, 384, 535, 745, 1038, 1446, 2014, 2806, 3909, 5446, 7587, + 10570, 14726, 20516, 28581, 39818, 55474, 77284, 107669, 150000, /* > */ 150000}; + +// TS 38.321, Table 6.1.3.1-2: Buffer size levels (in bytes) for 8-bit Buffer Size field, all values <= except marked +static const uint32_t buffer_size_levels_8bit_max_idx = 254; +static uint32_t buffer_size_levels_8bit[buffer_size_levels_8bit_max_idx + 1] = { + /* == */ 0, 10, 11, 12, 13, + 14, 15, 16, 17, 18, + 19, 20, 22, 23, 25, + 26, 28, 30, 32, 34, + 36, 38, 40, 43, 46, + 49, 52, 55, 59, 62, + 66, 71, 75, 80, 85, + 91, 97, 103, 110, 117, + 124, 132, 141, 150, 160, + 170, 181, 193, 205, 218, + 233, 248, 264, 281, 299, + 318, 339, 361, 384, 409, + 436, 464, 494, 526, 560, + 597, 635, 677, 720, 767, + 817, 870, 926, 987, 1051, + 1119, 1191, 1269, 1351, 1439, + 1532, 1631, 1737, 1850, 1970, + 2098, 2234, 2379, 2533, 2698, + 2873, 3059, 3258, 3469, 3694, + 3934, 4189, 4461, 4751, 5059, + 5387, 5737, 6109, 6506, 6928, + 7378, 7857, 8367, 8910, 9488, + 10104, 10760, 11458, 12202, 12994, + 13838, 14736, 15692, 16711, 17795, + 18951, 20181, 21491, 22885, 24371, + 25953, 27638, 29431, 31342, 33376, + 35543, 37850, 40307, 42923, 45709, + 48676, 51836, 55200, 58784, 62599, + 66663, 70990, 75598, 80505, 85730, + 91295, 97221, 103532, 110252, 117409, + 125030, 133146, 141789, 150992, 160793, + 171231, 182345, 194182, 206786, 220209, + 234503, 249725, 265935, 283197, 301579, + 321155, 342002, 364202, 387842, 413018, + 439827, 468377, 498780, 531156, 565634, + 602350, 641449, 683087, 727427, 774645, + 824928, 878475, 935498, 996222, 1060888, + 1129752, 1203085, 1281179, 1364342, 1452903, + 1547213, 1647644, 1754595, 1868488, 1989774, + 2118933, 2256475, 2402946, 2558924, 2725027, + 2901912, 3090279, 3290873, 3504487, 3731968, + 3974215, 4232186, 4506902, 4799451, 5110989, + 5442750, 5796046, 6172275, 6572925, 6999582, + 7453933, 7937777, 8453028, 9001725, 9586039, + 10208280, 10870913, 11576557, 12328006, 13128233, + 13980403, 14887889, 15854280, 16883401, 17979324, + 19146385, 20389201, 21712690, 23122088, 24622972, + 26221280, 27923336, 29735875, 31666069, 33721553, + 35910462, 38241455, 40723756, 43367187, 46182206, + 49179951, 52372284, 55771835, 59392055, 63247269, + 67352729, 71724679, 76380419, 81338368, /* > */ 81338368}; + int32_t proc_bsr_nr::init(proc_sr_nr* sr_, mux_interface_bsr_nr* mux_, rlc_interface_mac* rlc_, @@ -43,7 +105,7 @@ int32_t proc_bsr_nr::init(proc_sr_nr* sr_, // Print periodically the LCID queue status auto queue_status_print_task = [this](uint32_t tid) { - print_state(); + logger.debug("BSR: %s", buffer_state.to_string()); timer_queue_status_print.run(); }; timer_queue_status_print.set(QUEUE_STATUS_PERIOD_MS, queue_status_print_task); @@ -54,20 +116,6 @@ int32_t proc_bsr_nr::init(proc_sr_nr* sr_, return SRSRAN_SUCCESS; } -void proc_bsr_nr::print_state() -{ - char str[128]; - str[0] = '\0'; - int n = 0; - for (auto& lcg : lcgs) { - for (auto& iter : lcg) { - n = srsran_print_check(str, 128, n, "%d: %d ", iter.first, iter.second.old_buffer); - } - } - logger.info( - "BSR: triggered_bsr_type=%s, LCID QUEUE status: %s", bsr_trigger_type_tostring(triggered_bsr_type), str); -} - void proc_bsr_nr::set_trigger(bsr_trigger_type_t new_trigger) { triggered_bsr_type = new_trigger; @@ -75,7 +123,7 @@ void proc_bsr_nr::set_trigger(bsr_trigger_type_t new_trigger) // Trigger SR always when Regular BSR is triggered in the current TTI. Will be cancelled if a grant is received if (triggered_bsr_type == REGULAR) { logger.debug("BSR: Triggering SR procedure"); - sr->start(); + // sr->start(); } } @@ -128,49 +176,17 @@ void proc_bsr_nr::timer_expired(uint32_t timer_id) } } -uint32_t proc_bsr_nr::get_buffer_state() -{ - uint32_t buffer = 0; - for (int i = 0; i < MAX_NOF_LCG; i++) { - buffer += get_buffer_state_lcg(i); - } - return buffer; -} - // Checks if data is available for a channel with higher priority than others bool proc_bsr_nr::check_highest_channel() { // TODO: move 4G implementation to base class or rewrite - for (int i = 0; i < MAX_NOF_LCG; i++) { - for (std::map::iterator iter = lcgs[i].begin(); iter != lcgs[i].end(); ++iter) { - // If new data available - if (iter->second.new_buffer > iter->second.old_buffer) { - // Check if this LCID has higher priority than any other LCID ("belong to any LCG") for which data is already - // available for transmission - bool is_max_priority = true; - for (int j = 0; j < MAX_NOF_LCG; j++) { - for (std::map::iterator iter2 = lcgs[j].begin(); iter2 != lcgs[j].end(); ++iter2) { - // No max prio LCG if prio isn't higher or LCID already had buffered data - if (iter2->second.priority <= iter->second.priority && (iter2->second.old_buffer > 0)) { - is_max_priority = false; - } - } - } - if (is_max_priority) { - logger.debug("BSR: New data for lcid=%d with maximum priority in lcg=%d", iter->first, i); - return true; - } - } - } - } return false; } bool proc_bsr_nr::check_any_channel() { - // TODO: move 4G implementation to base class or rewrite - for (int i = 0; i < MAX_NOF_LCG; i++) { - if (get_buffer_state_lcg(i)) { + for (const auto& lcg : buffer_state.lcg_buffer_size) { + if (lcg.second > 0) { return true; } } @@ -178,66 +194,40 @@ bool proc_bsr_nr::check_any_channel() } // Checks if only one logical channel has data avaiable for Tx -bool proc_bsr_nr::check_new_data() +bool proc_bsr_nr::check_new_data(const mac_buffer_states_t& new_buffer_state) { - // TODO: move 4G implementation to base class or rewrite - for (int i = 0; i < MAX_NOF_LCG; i++) { - // If there was no data available in any LCID belonging to this LCG - if (get_buffer_state_lcg(i) == 0) { - for (std::map::iterator iter = lcgs[i].begin(); iter != lcgs[i].end(); ++iter) { - if (iter->second.new_buffer > 0) { - logger.debug("BSR: New data available for lcid=%d", iter->first); - return true; - } - } + for (const auto& lcg : buffer_state.lcg_buffer_size) { + if (lcg.second == 0 and new_buffer_state.lcg_buffer_size.at(lcg.first) > 0) { + logger.debug("BSR: New data available for LCG=%d", lcg.first); + return true; } } return false; } -void proc_bsr_nr::update_new_data() +srsran::mac_sch_subpdu_nr::lcg_bsr_t proc_bsr_nr::generate_sbsr() { - // TODO: move 4G implementation to base class or rewrite - for (int i = 0; i < MAX_NOF_LCG; i++) { - for (std::map::iterator iter = lcgs[i].begin(); iter != lcgs[i].end(); ++iter) { - iter->second.new_buffer = rlc->get_buffer_state(iter->first); - } + srsran::mac_sch_subpdu_nr::lcg_bsr_t sbsr = {}; + if (buffer_state.nof_lcgs_with_data > 0) { + sbsr.lcg_id = buffer_state.last_non_zero_lcg; + sbsr.buffer_size = buff_size_bytes_to_field(buffer_state.lcg_buffer_size.at(sbsr.lcg_id), SHORT_BSR); } + triggered_bsr_type = NONE; + return sbsr; } -void proc_bsr_nr::update_old_buffer() -{ - // TODO: move 4G implementation to base class or rewrite - for (int i = 0; i < MAX_NOF_LCG; i++) { - for (std::map::iterator iter = lcgs[i].begin(); iter != lcgs[i].end(); ++iter) { - iter->second.old_buffer = iter->second.new_buffer; - } - } -} - -uint32_t proc_bsr_nr::get_buffer_state_lcg(uint32_t lcg) -{ - // TODO: move 4G implementation to base class or rewrite - uint32_t n = 0; - for (std::map::iterator iter = lcgs[lcg].begin(); iter != lcgs[lcg].end(); ++iter) { - n += iter->second.old_buffer; - } - return n; -} - -// Generate BSR -bool proc_bsr_nr::generate_bsr(bsr_t* bsr, uint32_t pdu_space) -{ - // TODO: add BSR generation - bool send_bsr = false; - return send_bsr; -} - -// Called by MAC every TTI -// Checks if Regular BSR must be assembled, as defined in 5.4.5 -// Padding BSR is assembled when called by mux_unit when UL dci is received -// Periodic BSR is triggered by the expiration of the timers -void proc_bsr_nr::step(uint32_t tti) +/** + * @brief Called by MAC every TTI with the current state of each LCID/LCGs + * + * Checks if Regular BSR must be assembled, as defined in 5.4.5. + * Padding BSR is assembled when explicitly called by MUX when UL DCI is received + * Periodic BSR is triggered by the expiration of the timers + * + * @param tti The current TTI + * @param new_buffer_state Buffer state of all LCID/LCGs at the start of the TTI + * + */ +void proc_bsr_nr::step(uint32_t tti, const mac_buffer_states_t& new_buffer_state_) { std::lock_guard lock(mutex); @@ -245,30 +235,40 @@ void proc_bsr_nr::step(uint32_t tti) return; } - update_new_data(); - // Regular BSR triggered if new data arrives or channel with high priority has new data - if (check_new_data() || check_highest_channel()) { + if (check_new_data(new_buffer_state_) || check_highest_channel()) { logger.debug("BSR: Triggering Regular BSR tti=%d", tti); set_trigger(REGULAR); } - update_old_buffer(); + // store buffer state for comparision in next TTI + buffer_state = new_buffer_state_; } void proc_bsr_nr::new_grant_ul(uint32_t grant_size) { std::lock_guard lock(mutex); if (triggered_bsr_type != NONE) { - // inform MUX we need to generate a BSR - mux->generate_bsr_mac_ce(); - } + // Decide BSR type to be transmitted, state for all LCG/LCIDs has already been updated by step() + if (buffer_state.nof_lcgs_with_data > 1) { + // report Long BSR if more than one LCG has data to send + mux->generate_bsr_mac_ce(LONG_BSR); + } else { + // report Short BSR otherwise + mux->generate_bsr_mac_ce(SHORT_BSR); + } - // TODO: restart retxBSR-Timer + // 3> start or restart periodicBSR-Timer, except when all the generated BSRs are long or short Truncated BSRs + // TODO: add check if only truncated version can be included + timer_periodic.run(); + + // 3> start or restart retxBSR-Timer. + timer_retx.run(); + } } // This function is called by MUX only if Regular BSR has not been triggered before -bool proc_bsr_nr::generate_padding_bsr(uint32_t nof_padding_bytes, bsr_t* bsr) +bool proc_bsr_nr::generate_padding_bsr(uint32_t nof_padding_bytes) { std::lock_guard lock(mutex); @@ -281,7 +281,7 @@ bool proc_bsr_nr::generate_padding_bsr(uint32_t nof_padding_bytes, bsr_t* bsr) nof_padding_bytes <= LBSR_CE_SUBHEADER_LEN + srsran::mac_sch_subpdu_nr::sizeof_ce(LONG_BSR, true)) { // generate padding BSR set_trigger(PADDING); - generate_bsr(bsr, nof_padding_bytes); + // generate_bsr(bsr, nof_padding_bytes); set_trigger(NONE); return true; } @@ -292,40 +292,76 @@ bool proc_bsr_nr::generate_padding_bsr(uint32_t nof_padding_bytes, bsr_t* bsr) int proc_bsr_nr::setup_lcid(uint32_t lcid, uint32_t new_lcg, uint32_t priority) { // TODO: move 4G implementation to base class - if (new_lcg > MAX_NOF_LCG) { + if (new_lcg > srsran::mac_sch_subpdu_nr::max_num_lcg_lbsr) { logger.error("BSR: Invalid lcg=%d for lcid=%d", new_lcg, lcid); return SRSRAN_ERROR; } std::lock_guard lock(mutex); - // First see if it already exists and eliminate it - for (int i = 0; i < MAX_NOF_LCG; i++) { - if (lcgs[i].count(lcid)) { - lcgs[i].erase(lcid); - } + // Check that the new priority doesn't not already exist + if (lcg_priorities.find(priority) != lcg_priorities.end()) { + logger.error( + "BSR: Invalid config. Priority=%d already configured for lcg=%d", priority, lcg_priorities.at(priority)); + return SRSRAN_ERROR; } - // Now add it - lcgs[new_lcg][lcid].priority = priority; - lcgs[new_lcg][lcid].old_buffer = 0; + + lcg_priorities[priority] = new_lcg; return SRSRAN_SUCCESS; } uint32_t proc_bsr_nr::find_max_priority_lcg_with_data() { - // TODO: move 4G implementation to base class or rewrite - int32_t max_prio = 99; - uint32_t max_idx = 0; - for (int i = 0; i < MAX_NOF_LCG; i++) { - for (std::map::iterator iter = lcgs[i].begin(); iter != lcgs[i].end(); ++iter) { - if (iter->second.priority < max_prio && iter->second.old_buffer > 0) { - max_prio = iter->second.priority; - max_idx = i; - } + // iterate over LCGs in order of their priorities and check if there is one with data to transmit + for (const auto& lcg_prio : lcg_priorities) { + if (buffer_state.lcg_buffer_size.at(lcg_prio.second) > 0) { + return lcg_prio.second; } } - return max_idx; + return 0; +} + +/** Converts the buffer size levels (in Bytes) to the 5 or 8-bit Buffer Size field + * @param buffer_size The actual buffer size level in Bytes + * @param format The BSR format that determines the buffer size field length + * @return uint8_t The buffer size field that will be used for the MAC PDU + */ +uint8_t proc_bsr_nr::buff_size_bytes_to_field(uint32_t buffer_size, bsr_format_nr_t format) +{ + if (buffer_size == 0) { + } + + switch (format) { + case SHORT_BSR: + case SHORT_TRUNC_BSR: + if (buffer_size > buffer_size_levels_5bit[buffer_size_levels_5bit_max_idx]) { + return buffer_size_levels_5bit_max_idx; + } else { + for (uint32_t i = 1; i < buffer_size_levels_5bit_max_idx; i++) { + if (buffer_size <= buffer_size_levels_5bit[i]) { + return i; + } + } + return buffer_size_levels_5bit_max_idx - 1; + } + break; + case LONG_BSR: + case LONG_TRUNC_BSR: + if (buffer_size > buffer_size_levels_8bit[buffer_size_levels_8bit_max_idx]) { + return buffer_size_levels_8bit_max_idx; + } else { + for (uint32_t i = 1; i < buffer_size_levels_8bit_max_idx; i++) { + if (buffer_size <= buffer_size_levels_8bit[i + 1]) { + return i + 1; + } + } + return buffer_size_levels_8bit_max_idx - 1; + } + break; + } + + return 0; } } // namespace srsue diff --git a/srsue/src/stack/mac_nr/proc_ra_nr.cc b/srsue/src/stack/mac_nr/proc_ra_nr.cc index 4cf91935c..b3f322fc7 100644 --- a/srsue/src/stack/mac_nr/proc_ra_nr.cc +++ b/srsue/src/stack/mac_nr/proc_ra_nr.cc @@ -49,6 +49,7 @@ void proc_ra_nr::init(phy_interface_mac_nr* phy_, srsran::ext_task_sched_handle* prach_send_timer = task_sched->get_unique_timer(); rar_timeout_timer = task_sched->get_unique_timer(); contention_resolution_timer = task_sched->get_unique_timer(); + backoff_timer = task_sched->get_unique_timer(); } /* Sets a new configuration. The configuration is applied by initialization() function */ @@ -133,15 +134,18 @@ uint16_t proc_ra_nr::get_temp_rnti() void proc_ra_nr::timer_expired(uint32_t timer_id) { if (prach_send_timer.id() == timer_id) { - logger.error("PRACH Send timer expired. PRACH was not transmitted within %d ttis by phy. (TODO)", + logger.warning("PRACH Send timer expired. PRACH was not transmitted within %d ttis by phy. (TODO)", prach_send_timer.duration()); ra_error(); } else if (rar_timeout_timer.id() == timer_id) { - logger.error("RAR Timer expired. RA response not received within the response window Response Error (TODO)"); + logger.warning("RAR Timer expired. RA response not received within the response window"); ra_error(); } else if (contention_resolution_timer.id() == timer_id) { - logger.error("Contention Resolution Timer expired. Stopping PDCCH Search and going to Response Error (TODO)"); + logger.warning("Contention Resolution Timer expired. Stopping PDCCH Search and going to Response Error (TODO)"); ra_error(); + } else if (backoff_timer.id() == timer_id) { + logger.info("Transmitting new preamble (%d/%d)", preamble_transmission_counter, rach_cfg.preambleTransMax); + ra_resource_selection(); } else { logger.error("Timer not implemented"); } @@ -151,8 +155,10 @@ void proc_ra_nr::timer_expired(uint32_t timer_id) void proc_ra_nr::ra_procedure_initialization() { mac.msg3_flush(); + preamble_transmission_counter = 1; preamble_power_ramping_step = rach_cfg.powerRampingStep; scaling_factor_bi = 1; + preamble_backoff = 0; preambleTransMax = rach_cfg.preambleTransMax; ra_resource_selection(); } @@ -214,6 +220,13 @@ void proc_ra_nr::ra_response_reception(const mac_interface_phy_nr::mac_nr_grant_ rar_rnti = SRSRAN_INVALID_RNTI; mac.msg3_prepare(); current_ta = subpdu.get_ta(); + + // Set Backoff parameter + if (subpdu.has_backoff()) { + preamble_backoff = backoff_table_nr[subpdu.get_backoff() % 16]; // TODO multiplied with SCALING_FACTOR_BI. + } else { + preamble_backoff = 0; + } } } } @@ -274,7 +287,31 @@ void proc_ra_nr::ra_completion() void proc_ra_nr::ra_error() { - logger.error("NR random access procedure error recovery not implemented yet"); + temp_rnti = 0; + preamble_transmission_counter++; + contention_resolution_timer.stop(); + uint32_t backoff_wait; + bool ra_procedure_completed = false; // true = (unsuccessfully) completed, false = uncompleted + + if (preamble_transmission_counter >= rach_cfg.preambleTransMax + 1) { + logger.warning("Maximum number of transmissions reached (%d)", rach_cfg.preambleTransMax); + // if the Random Access Preamble is transmitted on the SpCell assumption (TODO) + mac.rrc_ra_problem(); // indicate a Random Access problem to upper layers; + if (started_by == initiators_t::MAC) { // if this Random Access procedure was triggered for SI request + ra_procedure_completed = true; // consider the Random Access procedure unsuccessfully completed. + reset(); + } + } else { + // if the Random Access procedure is not completed + if (preamble_backoff) { + backoff_wait = rand() % preamble_backoff; + } else { + backoff_wait = 0; + } + logger.warning("Backoff wait interval %d", backoff_wait); + backoff_timer.set(backoff_wait, [this](uint32_t tid) { timer_expired(tid); }); + backoff_timer.run(); + } } // Is called by PHY once it has transmitted the prach transmitted, than configure RA-RNTI and wait for RAR reception @@ -306,7 +343,7 @@ void proc_ra_nr::prach_sent(uint32_t tti, uint32_t s_id, uint32_t t_id, uint32_t tti); uint32_t rar_window_st = TTI_ADD(tti, 3); // TODO check ra_response window (delayed start)? // last 3 check if needed when we have a delayed start - rar_timeout_timer.set(rach_cfg.ra_responseWindow + 3 + 3, [this](uint32_t tid) { timer_expired(tid); }); + rar_timeout_timer.set(rach_cfg.ra_responseWindow + 3 + 10, [this](uint32_t tid) { timer_expired(tid); }); rar_timeout_timer.run(); // Wait for RAR reception ra_window_length = rach_cfg.ra_responseWindow; diff --git a/srsue/src/stack/mac_nr/proc_sr_nr.cc b/srsue/src/stack/mac_nr/proc_sr_nr.cc index 3d1882305..6cfde69a0 100644 --- a/srsue/src/stack/mac_nr/proc_sr_nr.cc +++ b/srsue/src/stack/mac_nr/proc_sr_nr.cc @@ -44,19 +44,6 @@ void proc_sr_nr::reset() is_pending_sr = false; } -bool proc_sr_nr::need_tx(uint32_t tti) -{ - int last_tx_tti = 0; // FIXME: phy->sr_last_tx_tti(); - logger.debug("SR: need_tx(): last_tx_tti=%d, tti=%d", last_tx_tti, tti); - if (last_tx_tti >= 0) { - // TODO: implement prohibit timer - if (TTI_SUB(last_tx_tti, tti) >= 8) { - return true; - } - } - return false; -} - int32_t proc_sr_nr::set_config(const srsran::sr_cfg_nr_t& cfg_) { // disable by default @@ -89,36 +76,79 @@ int32_t proc_sr_nr::set_config(const srsran::sr_cfg_nr_t& cfg_) void proc_sr_nr::step(uint32_t tti) { - if (initiated) { - if (is_pending_sr) { - if (cfg.enabled) { - if (sr_counter < cfg.item[0].trans_max) { - if (sr_counter == 0 || need_tx(tti)) { - sr_counter++; - logger.info("SR: Signalling PHY sr_counter=%d", sr_counter); - phy->sr_send(0); // @xavierarteaga what is the ID you expect here? - } - } else { - if (need_tx(tti)) { - logger.info("SR: Releasing PUCCH/SRS resources, sr_counter=%d, sr-TransMax=%d", - sr_counter, - cfg.item[0].trans_max); - srsran::console("Scheduling request failed: releasing RRC connection...\n"); - rrc->release_pucch_srs(); - // TODO: - // - clear any configured downlink assignments and uplink grants; - // - clear any PUSCH resources for semi-persistent CSI reporting; - ra->start_by_mac(); - reset(); - } - } - } else { - logger.info("SR: PUCCH not configured. Starting RA procedure"); - ra->start_by_mac(); - reset(); - } - } + if (!initiated) { + return; } + + // As long as at least one SR is pending, the MAC entity shall for each pending SR: + if (!is_pending_sr) { + return; + } + + // 1> if the MAC entity has no valid PUCCH resource configured for the pending SR: + if (!cfg.enabled || not phy->has_valid_sr_resource(0)) { + // 2> initiate a Random Access procedure (see clause 5.1) on the SpCell and cancel the pending SR. + logger.info("SR: PUCCH not configured. Starting RA procedure"); + ra->start_by_mac(); + reset(); + return; + } + + // Handle + if (sr_counter >= cfg.item[0].trans_max) { + logger.info( + "SR: Releasing PUCCH/SRS resources, sr_counter=%d, sr-TransMax=%d", sr_counter, cfg.item[0].trans_max); + srsran::console("Scheduling request failed: releasing RRC connection...\n"); + rrc->release_pucch_srs(); + + // 4> clear any configured downlink assignments and uplink grants; + phy->clear_pending_grants(); + + // 4> clear any PUSCH resources for semi-persistent CSI reporting; + // ... TODO + + ra->start_by_mac(); + reset(); + } +} + +bool proc_sr_nr::sr_opportunity(uint32_t tti, uint32_t sr_id, bool meas_gap, bool ul_sch_tx) +{ + // 2> when the MAC entity has an SR transmission occasion on the valid PUCCH resource for SR configured; and + if (!initiated || !cfg.enabled || !is_pending_sr) { + return false; + } + + // 2> if sr-ProhibitTimer is not running at the time of the SR transmission occasion; and + // ... TODO + + // 2> if the PUCCH resource for the SR transmission occasion does not overlap with a measurement gap; and + if (meas_gap) { + return false; + } + + // 2> if the PUCCH resource for the SR transmission occasion does not overlap with a UL-SCH resource: + if (ul_sch_tx) { + return false; + } + + // 3> if SR_COUNTER < sr-TransMax: + if (sr_counter < cfg.item[0].trans_max) { // + // 4> increment SR_COUNTER by 1; + sr_counter += 1; + + // 4> start the sr-ProhibitTimer. + // ... TODO + + // 4> instruct the physical layer to signal the SR on one valid PUCCH resource for SR; + logger.info("SR: Signalling PHY sr_counter=%d", sr_counter); + return true; + } + + // 3> else: + // step will execute + + return false; } void proc_sr_nr::start() diff --git a/srsue/test/mac_nr/CMakeLists.txt b/srsue/src/stack/mac_nr/test/CMakeLists.txt similarity index 83% rename from srsue/test/mac_nr/CMakeLists.txt rename to srsue/src/stack/mac_nr/test/CMakeLists.txt index c8dd82107..380f8af1e 100644 --- a/srsue/test/mac_nr/CMakeLists.txt +++ b/srsue/src/stack/mac_nr/test/CMakeLists.txt @@ -22,6 +22,10 @@ add_executable(proc_ra_nr_test proc_ra_nr_test.cc) target_link_libraries(proc_ra_nr_test srsue_mac_nr srsran_common) add_test(proc_ra_nr_test proc_ra_nr_test) +add_executable(proc_bsr_nr_test proc_bsr_nr_test.cc) +target_link_libraries(proc_bsr_nr_test srsue_mac_nr srsran_common) +add_test(proc_bsr_nr_test proc_bsr_nr_test) + add_executable(mac_nr_test mac_nr_test.cc) target_link_libraries(mac_nr_test srsue_mac_nr srsran_common) add_test(mac_nr_test mac_nr_test) \ No newline at end of file diff --git a/srsue/src/stack/mac_nr/test/mac_nr_test.cc b/srsue/src/stack/mac_nr/test/mac_nr_test.cc new file mode 100644 index 000000000..eeb1b8860 --- /dev/null +++ b/srsue/src/stack/mac_nr/test/mac_nr_test.cc @@ -0,0 +1,495 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ +#include "srsran/common/buffer_pool.h" +#include "srsran/common/common.h" +#include "srsran/common/test_common.h" +#include "srsran/test/ue_test_interfaces.h" +#include "srsue/hdr/stack/mac_nr/mac_nr.h" + +using namespace srsue; + +#define HAVE_PCAP 0 +#define UE_ID 0 + +static std::unique_ptr pcap_handle = nullptr; + +class dummy_phy : public phy_interface_mac_nr +{ +public: + dummy_phy() {} + void send_prach(const uint32_t prach_occasion_, + const int preamble_index_, + const float preamble_received_target_power_, + const float ta_base_sec_ = 0.0f) override + { + prach_occasion = prach_occasion_; + preamble_index = preamble_index_; + preamble_received_target_power = preamble_received_target_power_; + } + int tx_request(const tx_request_t& request) override { return 0; } + int set_ul_grant(std::array, uint16_t rnti, srsran_rnti_type_t rnti_type) override + { + return 0; + } + + void get_last_send_prach(uint32_t* prach_occasion_, uint32_t* preamble_index_, int* preamble_received_target_power_) + { + *prach_occasion_ = prach_occasion; + *preamble_index_ = preamble_index; + *preamble_received_target_power_ = preamble_received_target_power; + } + bool has_valid_sr_resource(uint32_t sr_id) override { return false; } + void clear_pending_grants() override {} + +private: + uint32_t prach_occasion = 0; + uint32_t preamble_index = 0; + int preamble_received_target_power = 0; +}; + +class rrc_dummy : public rrc_interface_mac +{ +public: + rrc_dummy() {} + virtual void ra_completed() {} + virtual void ra_problem() {} + virtual void release_pucch_srs() {} +}; + +class stack_dummy : public stack_test_dummy +{ +public: + void init(mac_nr* mac_, phy_interface_mac_nr* phy_) + { + mac_h = mac_; + phy_h = phy_; + } + void run_tti(uint32_t tti) + { + mac_h->run_tti(tti); + // flush all events + stack_test_dummy::run_tti(); + } + +private: + phy_interface_mac_nr* phy_h = nullptr; + mac_nr* mac_h = nullptr; +}; + +// TODO: refactor to common test dummy components +class rlc_dummy : public srsue::rlc_dummy_interface +{ +public: + rlc_dummy() : received_bytes(0) {} + bool has_data_locked(const uint32_t lcid) final { return ul_queues[lcid] > 0; } + uint32_t get_buffer_state(const uint32_t lcid) final { return ul_queues[lcid]; } + int read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) final + { + if (!read_enable || nof_bytes < read_min) { + return 0; + } + + if (read_len > 0 && read_len < (int32_t)nof_bytes) { + nof_bytes = read_len; + } + + uint32_t len = SRSRAN_MIN(ul_queues[lcid], nof_bytes); + + // set payload bytes to LCID so we can check later if the scheduling was correct + memset(payload, lcid > 0 ? lcid : 0xf, len); + + // remove from UL queue + ul_queues[lcid] -= len; + + return len; + }; + void write_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) final + { + logger.debug(payload, nof_bytes, "Received %d B on LCID %d", nof_bytes, lcid); + received_bytes += nof_bytes; + } + + void write_sdu(uint32_t lcid, uint32_t nof_bytes) { ul_queues[lcid] += nof_bytes; } + uint32_t get_received_bytes() { return received_bytes; } + + void disable_read() { read_enable = false; } + void set_read_len(uint32_t len) { read_len = len; } + void set_read_min(uint32_t len) { read_min = len; } + void reset_queues() + { + for (auto& q : ul_queues) { + q.second = 0; + } + } + +private: + bool read_enable = true; + int32_t read_len = -1; // read all + uint32_t read_min = 0; // minimum "grant size" for read_pdu() to return data + uint32_t received_bytes; + srslog::basic_logger& logger = srslog::fetch_basic_logger("RLC"); + // UL queues where key is LCID and value the queue length + std::map ul_queues; +}; + +// TODO: Add test +int msg3_test() +{ + return SRSRAN_SUCCESS; +} + +// Basic PDU generation test +int mac_nr_ul_logical_channel_prioritization_test1() +{ + // PDU layout (20B in total) + // - 2 B MAC subheader for SCH LCID=4 + // - 10 B sduPDU + // - 1 B subheader padding + // - 7 B padding + const uint8_t tv[] = {0x04, 0x0a, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + // dummy layers + dummy_phy phy; + rlc_dummy rlc; + rrc_dummy rrc; + stack_dummy stack; + + // the actual MAC + mac_nr mac(&stack.task_sched); + + mac_nr_args_t args = {}; + mac.init(args, &phy, &rlc, &rrc); + + stack.init(&mac, &phy); + const uint16_t crnti = 0x1001; + + // generate config (default DRB2 config for EN-DC) + std::vector lcids; + srsran::logical_channel_config_t config = {}; + config.lcid = 4; + config.lcg = 6; + config.PBR = 0; + config.BSD = 1000; // 1000ms + config.priority = 11; + lcids.push_back(config); + + // setup LCIDs in MAC + for (auto& channel : lcids) { + mac.setup_lcid(channel); + } + + // write dummy data to DRB2 + rlc.write_sdu(4, 10); + + // run TTI to setup Bj, BSR should be generated + stack.run_tti(0); + usleep(100); + + // create UL action and grant and read MAC PDU + { + mac_interface_phy_nr::tb_action_ul_t ul_action = {}; + mac_interface_phy_nr::mac_nr_grant_ul_t mac_grant = {}; + + mac_grant.rnti = crnti; // make sure MAC picks it up as valid UL grant + mac_grant.pid = 0; + mac_grant.rnti = 0x1001; + mac_grant.tti = 0; + mac_grant.tbs = 20; + int cc_idx = 0; + + // Send grant to MAC and get action for this TB, 0x + mac.new_grant_ul(cc_idx, mac_grant, &ul_action); + + // print generated PDU + srslog::fetch_basic_logger("MAC").info( + ul_action.tb.payload->msg, mac_grant.tbs, "Generated PDU (%d B)", mac_grant.tbs); +#if HAVE_PCAP + pcap_handle->write_ul_crnti_nr( + ul_action.tb.payload->msg, mac_grant.tbs, mac_grant.rnti, UE_ID, mac_grant.pid, mac_grant.tti); +#endif + + TESTASSERT(memcmp(ul_action.tb.payload->msg, tv, sizeof(tv)) == 0); + } + + // make sure MAC PDU thread picks up before stopping + stack.run_tti(0); + mac.stop(); + + return SRSRAN_SUCCESS; +} + +// Correct packing of MAC PDU with subHeader with 16bit L field +int mac_nr_ul_logical_channel_prioritization_test2() +{ + // PDU layout (260 B in total) + // - 3 B MAC subheader for SCH LCID=4 for 16bit L field + // - 257 B sduPDU + const uint8_t tv[] = { + 0x44, 0x01, 0x01, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + }; + + // dummy layers + dummy_phy phy; + rlc_dummy rlc; + rrc_dummy rrc; + stack_dummy stack; + + // the actual MAC + mac_nr mac(&stack.task_sched); + + mac_nr_args_t args = {}; + mac.init(args, &phy, &rlc, &rrc); + + stack.init(&mac, &phy); + const uint16_t crnti = 0x1001; + + // generate config (default DRB2 config for EN-DC) + std::vector lcids; + srsran::logical_channel_config_t config = {}; + config.lcid = 4; + config.lcg = 6; + config.PBR = 0; + config.BSD = 1000; // 1000ms + config.priority = 11; + lcids.push_back(config); + + // setup LCIDs in MAC + for (auto& channel : lcids) { + mac.setup_lcid(channel); + } + + // write dummy data to DRB2 + rlc.write_sdu(4, 1000); + + // run TTI to setup Bj, BSR should be generated + stack.run_tti(0); + usleep(100); + + // create UL action and grant and read MAC PDU + { + mac_interface_phy_nr::tb_action_ul_t ul_action = {}; + mac_interface_phy_nr::mac_nr_grant_ul_t mac_grant = {}; + + mac_grant.rnti = crnti; // make sure MAC picks it up as valid UL grant + mac_grant.pid = 0; + mac_grant.rnti = 0x1001; + mac_grant.tti = 0; + mac_grant.tbs = 260; + int cc_idx = 0; + + // Send grant to MAC and get action for this TB, 0x + mac.new_grant_ul(cc_idx, mac_grant, &ul_action); + + // print generated PDU + srslog::fetch_basic_logger("MAC").info( + ul_action.tb.payload->msg, mac_grant.tbs, "Generated PDU (%d B)", mac_grant.tbs); +#if HAVE_PCAP + pcap_handle->write_ul_crnti_nr( + ul_action.tb.payload->msg, mac_grant.tbs, mac_grant.rnti, UE_ID, mac_grant.pid, mac_grant.tti); +#endif + + TESTASSERT(memcmp(ul_action.tb.payload->msg, tv, sizeof(tv)) == 0); + } + + // make sure MAC PDU thread picks up before stopping + stack.run_tti(0); + mac.stop(); + + return SRSRAN_SUCCESS; +} + +// Basic test for periodic BSR transmission +int mac_nr_ul_periodic_bsr_test() +{ + // PDU layout (10 B in total) + // - 6B LCID=4 (+2B header, 8 B total) + // - 2B SBSR + const uint8_t tv1[] = {0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x3d, 0xd1}; + + // PDU layout (10 B in total) + // - 8B LCID=4 + const uint8_t tv2[] = {0x04, 0x08, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04}; + + // dummy layers + dummy_phy phy; + rlc_dummy rlc; + rrc_dummy rrc; + stack_dummy stack; + + // the actual MAC + mac_nr mac(&stack.task_sched); + + mac_nr_args_t args = {}; + mac.init(args, &phy, &rlc, &rrc); + + stack.init(&mac, &phy); + const uint16_t crnti = 0x1001; + + // generate config (default DRB2 config for EN-DC) + std::vector lcids; + srsran::logical_channel_config_t config = {}; + config.lcid = 4; + config.lcg = 6; + config.PBR = 0; + config.BSD = 1000; // 1000ms + config.priority = 11; + lcids.push_back(config); + + // setup LCIDs in MAC + for (auto& channel : lcids) { + mac.setup_lcid(channel); + } + + srsran::bsr_cfg_nr_t bsr_cfg = {}; + bsr_cfg.periodic_timer = 20; + bsr_cfg.retx_timer = 320; + TESTASSERT(mac.set_config(bsr_cfg) == SRSRAN_SUCCESS); + + // run TTI to establish LCGs old buffer states at BSR + uint32_t tti = 0; + stack.run_tti(tti++); + + // write large amount of dummy data to DRB2 + rlc.write_sdu(4, 2000); + + // run TTI to setup Bj, BSR should be generated + stack.run_tti(tti++); + usleep(100); + + // create UL action and grant and read MAC PDU + { + mac_interface_phy_nr::tb_action_ul_t ul_action = {}; + mac_interface_phy_nr::mac_nr_grant_ul_t mac_grant = {}; + + mac_grant.rnti = crnti; // make sure MAC picks it up as valid UL grant + mac_grant.pid = 0; + mac_grant.rnti = 0x1001; + mac_grant.tti = 0; + mac_grant.tbs = 10; + int cc_idx = 0; + + // Send grant to MAC and get action for this TB + mac.new_grant_ul(cc_idx, mac_grant, &ul_action); + + // print generated PDU + srslog::fetch_basic_logger("MAC").info( + ul_action.tb.payload->msg, mac_grant.tbs, "Generated PDU (%d B)", mac_grant.tbs); +#if HAVE_PCAP + pcap_handle->write_ul_crnti_nr( + ul_action.tb.payload->msg, mac_grant.tbs, mac_grant.rnti, UE_ID, mac_grant.pid, mac_grant.tti); +#endif + + TESTASSERT(memcmp(ul_action.tb.payload->msg, tv1, sizeof(tv1)) == 0); + } + + // for the next 19 TTI, until the periodic BSR is triggered again, no BSR should be included in the MAC PDU + for (int i = 0; i < bsr_cfg.periodic_timer - 1; ++i) { + stack.run_tti(tti++); + usleep(100); + + // create UL action and grant and read MAC PDU + { + mac_interface_phy_nr::tb_action_ul_t ul_action = {}; + mac_interface_phy_nr::mac_nr_grant_ul_t mac_grant = {}; + + mac_grant.rnti = crnti; // make sure MAC picks it up as valid UL grant + mac_grant.pid = 0; + mac_grant.rnti = 0x1001; + mac_grant.tti = 0; + mac_grant.tbs = 10; + int cc_idx = 0; + + // Send grant to MAC and get action for this TB + mac.new_grant_ul(cc_idx, mac_grant, &ul_action); + + // print generated PDU + srslog::fetch_basic_logger("MAC").info( + ul_action.tb.payload->msg, mac_grant.tbs, "Generated PDU (%d B)", mac_grant.tbs); +#if HAVE_PCAP + pcap_handle->write_ul_crnti_nr( + ul_action.tb.payload->msg, mac_grant.tbs, mac_grant.rnti, UE_ID, mac_grant.pid, mac_grant.tti); +#endif + + TESTASSERT(memcmp(ul_action.tb.payload->msg, tv2, sizeof(tv2)) == 0); + } + } + + // run TTI to setup Bj, the same BSR should be generated again + stack.run_tti(tti++); + usleep(100); + + // create UL action and grant and read MAC PDU + { + mac_interface_phy_nr::tb_action_ul_t ul_action = {}; + mac_interface_phy_nr::mac_nr_grant_ul_t mac_grant = {}; + + mac_grant.rnti = crnti; // make sure MAC picks it up as valid UL grant + mac_grant.pid = 0; + mac_grant.rnti = 0x1001; + mac_grant.tti = 0; + mac_grant.tbs = 10; + int cc_idx = 0; + + // Send grant to MAC and get action for this TB + mac.new_grant_ul(cc_idx, mac_grant, &ul_action); + + // print generated PDU + srslog::fetch_basic_logger("MAC").info( + ul_action.tb.payload->msg, mac_grant.tbs, "Generated PDU (%d B)", mac_grant.tbs); +#if HAVE_PCAP + pcap_handle->write_ul_crnti_nr( + ul_action.tb.payload->msg, mac_grant.tbs, mac_grant.rnti, UE_ID, mac_grant.pid, mac_grant.tti); +#endif + + TESTASSERT(memcmp(ul_action.tb.payload->msg, tv1, sizeof(tv1)) == 0); + } + + // make sure MAC PDU thread picks up before stopping + stack.run_tti(tti++); + mac.stop(); + + return SRSRAN_SUCCESS; +} + +int main() +{ +#if HAVE_PCAP + pcap_handle = std::unique_ptr(new srsran::mac_pcap()); + pcap_handle->open("mac_test_nr.pcap"); +#endif + + auto& mac_logger = srslog::fetch_basic_logger("MAC"); + mac_logger.set_level(srslog::basic_levels::debug); + mac_logger.set_hex_dump_max_size(-1); + srslog::init(); + + TESTASSERT(msg3_test() == SRSRAN_SUCCESS); + TESTASSERT(mac_nr_ul_logical_channel_prioritization_test1() == SRSRAN_SUCCESS); + TESTASSERT(mac_nr_ul_logical_channel_prioritization_test2() == SRSRAN_SUCCESS); + TESTASSERT(mac_nr_ul_periodic_bsr_test() == SRSRAN_SUCCESS); + + return SRSRAN_SUCCESS; +} diff --git a/srsue/src/stack/mac_nr/test/proc_bsr_nr_test.cc b/srsue/src/stack/mac_nr/test/proc_bsr_nr_test.cc new file mode 100644 index 000000000..a4858db88 --- /dev/null +++ b/srsue/src/stack/mac_nr/test/proc_bsr_nr_test.cc @@ -0,0 +1,94 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ +#include "srsran/common/buffer_pool.h" +#include "srsran/common/common.h" +#include "srsran/common/test_common.h" +#include "srsue/hdr/stack/mac_nr/proc_bsr_nr.h" + +using namespace srsue; + +int sbsr_tests() +{ + auto& mac_logger = srslog::fetch_basic_logger("MAC"); + mac_logger.set_level(srslog::basic_levels::debug); + mac_logger.set_hex_dump_max_size(-1); + + srsran::task_scheduler task_sched{5, 2}; + srsran::ext_task_sched_handle ext_task_sched_h(&task_sched); + + proc_bsr_nr proc(mac_logger); + proc.init(nullptr, nullptr, nullptr, &ext_task_sched_h); + + srsran::bsr_cfg_nr_t bsr_cfg = {}; + bsr_cfg.periodic_timer = 20; + bsr_cfg.retx_timer = 320; + TESTASSERT(proc.set_config(bsr_cfg) == SRSRAN_SUCCESS); + + uint32_t tti = 0; + mac_buffer_states_t buffer_state; + buffer_state.nof_lcgs_with_data = 1; + buffer_state.last_non_zero_lcg = 1; + buffer_state.lcg_buffer_size[1] = 10; + proc.step(tti++, buffer_state); + + // Buffer size == 10 should result in index 1 (<= 10) + srsran::mac_sch_subpdu_nr::lcg_bsr_t sbsr = proc.generate_sbsr(); + TESTASSERT(sbsr.lcg_id == 1); + TESTASSERT(sbsr.buffer_size == 1); + + buffer_state.last_non_zero_lcg = 1; + buffer_state.lcg_buffer_size[1] = 11; + proc.step(tti++, buffer_state); + + // Buffer size == 11 should result in index 1 + sbsr = proc.generate_sbsr(); + TESTASSERT(sbsr.lcg_id == 1); + TESTASSERT(sbsr.buffer_size == 2); + + buffer_state.last_non_zero_lcg = 1; + buffer_state.lcg_buffer_size[1] = 77285; // 77284 + 1 + proc.step(tti++, buffer_state); + + // Buffer size 77285 should result in index 29 (first value of that index) + sbsr = proc.generate_sbsr(); + TESTASSERT(sbsr.lcg_id == 1); + TESTASSERT(sbsr.buffer_size == 29); + + buffer_state.last_non_zero_lcg = 1; + buffer_state.lcg_buffer_size[1] = 150000; + proc.step(tti++, buffer_state); + + // Buffer size 150000 should result in index 30 + sbsr = proc.generate_sbsr(); + TESTASSERT(sbsr.lcg_id == 1); + TESTASSERT(sbsr.buffer_size == 30); + + buffer_state.last_non_zero_lcg = 1; + buffer_state.lcg_buffer_size[1] = 150001; + proc.step(tti++, buffer_state); + + // Buffer size 150001 should result in index 31 + sbsr = proc.generate_sbsr(); + TESTASSERT(sbsr.lcg_id == 1); + TESTASSERT(sbsr.buffer_size == 31); + + return SRSRAN_SUCCESS; +} + +int main() +{ + srslog::init(); + + TESTASSERT(sbsr_tests() == SRSRAN_SUCCESS); + + return SRSRAN_SUCCESS; +} diff --git a/srsue/test/mac_nr/proc_ra_nr_test.cc b/srsue/src/stack/mac_nr/test/proc_ra_nr_test.cc similarity index 68% rename from srsue/test/mac_nr/proc_ra_nr_test.cc rename to srsue/src/stack/mac_nr/test/proc_ra_nr_test.cc index d47a662ea..e6b184c73 100644 --- a/srsue/test/mac_nr/proc_ra_nr_test.cc +++ b/srsue/src/stack/mac_nr/test/proc_ra_nr_test.cc @@ -50,7 +50,8 @@ public: *preamble_index_ = preamble_index; *preamble_received_target_power_ = preamble_received_target_power; } - void sr_send(uint32_t sr_id) override {} + bool has_valid_sr_resource(uint32_t sr_id) override { return false; } + void clear_pending_grants() override {} private: uint32_t prach_occasion = 0; @@ -61,6 +62,7 @@ private: class dummy_mac : public mac_interface_proc_ra_nr { public: + dummy_mac() : logger(srslog::fetch_basic_logger("MAC")) {} uint64_t get_contention_id() { return 0xdeadbeaf; } uint16_t get_crnti() { return crnti; } bool set_crnti(uint16_t c_rnti) @@ -75,26 +77,24 @@ public: bool msg3_is_empty() { return true; } void msga_flush(){}; + // RRC RA problem + void rrc_ra_problem() { logger.warning("Dummy MAC RRC ra problem"); } private: - uint16_t crnti = SRSRAN_INVALID_RNTI; + uint16_t crnti = SRSRAN_INVALID_RNTI; + srslog::basic_logger& logger; }; -int main() +int proc_ra_normal_test() { - srslog::init(); - auto& mac_logger = srslog::fetch_basic_logger("MAC"); - mac_logger.set_level(srslog::basic_levels::debug); - mac_logger.set_hex_dump_max_size(-1); - dummy_phy dummy_phy; dummy_mac dummy_mac; srsran::task_scheduler task_sched{5, 2}; srsran::ext_task_sched_handle ext_task_sched_h(&task_sched); - proc_ra_nr proc_ra_nr(dummy_mac, mac_logger); - + proc_ra_nr proc_ra_nr(dummy_mac, srslog::fetch_basic_logger("MAC")); proc_ra_nr.init(&dummy_phy, &ext_task_sched_h); + TESTASSERT(proc_ra_nr.is_rar_opportunity(1) == false); srsran::rach_nr_cfg_t rach_cfg; rach_cfg.powerRampingStep = 4; @@ -113,7 +113,7 @@ int main() dummy_phy.get_last_send_prach(&prach_occasion, &preamble_index, &preamble_received_target_power); TESTASSERT(prach_occasion == 0); TESTASSERT(preamble_index == 0); - TESTASSERT(preamble_received_target_power == -114); + TESTASSERT(preamble_received_target_power == -110); // Simulate PHY and call prach_sent (random values) uint32_t tti_start = 0; proc_ra_nr.prach_sent(tti_start, 7, 1, 0, 0); @@ -143,9 +143,64 @@ int main() task_sched.tic(); task_sched.run_pending_tasks(); - proc_ra_nr.pdcch_to_crnti(); + return SRSRAN_SUCCESS; +} +int proc_ra_timeout_test() +{ + dummy_phy dummy_phy; + dummy_mac dummy_mac; + srsran::task_scheduler task_sched{5, 2}; + srsran::ext_task_sched_handle ext_task_sched_h(&task_sched); + + proc_ra_nr proc_ra_nr(dummy_mac, srslog::fetch_basic_logger("MAC")); + + proc_ra_nr.init(&dummy_phy, &ext_task_sched_h); + TESTASSERT(proc_ra_nr.is_rar_opportunity(1) == false); + srsran::rach_nr_cfg_t rach_cfg; + rach_cfg.powerRampingStep = 4; + rach_cfg.prach_ConfigurationIndex = 16; + rach_cfg.PreambleReceivedTargetPower = -110; + rach_cfg.preambleTransMax = 7; + rach_cfg.ra_ContentionResolutionTimer = 64; + rach_cfg.ra_responseWindow = 10; + proc_ra_nr.set_config(rach_cfg); + proc_ra_nr.start_by_rrc(); + + // Test send prach parameters + uint32_t prach_occasion = 0; + uint32_t preamble_index = 0; + int preamble_received_target_power = 0; + dummy_phy.get_last_send_prach(&prach_occasion, &preamble_index, &preamble_received_target_power); + TESTASSERT(prach_occasion == 0); + TESTASSERT(preamble_index == 0); + TESTASSERT(preamble_received_target_power == -110); + // Simulate PHY and call prach_sent (random values) + uint32_t tti = 0; + + for (uint32_t j = 0; j < rach_cfg.preambleTransMax; j++) { + proc_ra_nr.prach_sent(tti, 7, 1, 0, 0); + uint32_t i = 0; + for (i = tti; i < tti + rach_cfg.ra_responseWindow + 100; i++) { + // update clock and run internal tasks + task_sched.tic(); + task_sched.run_pending_tasks(); + } + tti = i; + } task_sched.tic(); task_sched.run_pending_tasks(); return SRSRAN_SUCCESS; } + +int main() +{ + srslog::init(); + + auto& mac_logger = srslog::fetch_basic_logger("MAC"); + mac_logger.set_level(srslog::basic_levels::debug); + mac_logger.set_hex_dump_max_size(-1); + // TESTASSERT(proc_ra_normal_test() == SRSRAN_SUCCESS); + TESTASSERT(proc_ra_timeout_test() == SRSRAN_SUCCESS); + return SRSRAN_SUCCESS; +} diff --git a/srsue/src/stack/rrc/rrc.cc b/srsue/src/stack/rrc/rrc.cc index 69101e10a..69594587b 100644 --- a/srsue/src/stack/rrc/rrc.cc +++ b/srsue/src/stack/rrc/rrc.cc @@ -757,7 +757,7 @@ void rrc::timer_expired(uint32_t timeout_id) } } -bool rrc::nr_reconfiguration_proc(const rrc_conn_recfg_r8_ies_s& rx_recfg) +bool rrc::nr_reconfiguration_proc(const rrc_conn_recfg_r8_ies_s& rx_recfg, bool *has_5g_nr_reconfig) { if (!(rx_recfg.non_crit_ext_present && rx_recfg.non_crit_ext.non_crit_ext_present && rx_recfg.non_crit_ext.non_crit_ext.non_crit_ext_present && @@ -786,13 +786,21 @@ bool rrc::nr_reconfiguration_proc(const rrc_conn_recfg_r8_ies_s& rx_recfg) bool nr_radio_bearer_cfg1_r15_present = false; asn1::dyn_octstring nr_radio_bearer_cfg1_r15; - endc_release_and_add_r15 = rrc_conn_recfg_v1510_ies->nr_cfg_r15.setup().endc_release_and_add_r15; - - if (rrc_conn_recfg_v1510_ies->nr_cfg_r15.setup().nr_secondary_cell_group_cfg_r15_present == true) { - nr_secondary_cell_group_cfg_r15_present = true; - nr_secondary_cell_group_cfg_r15 = rrc_conn_recfg_v1510_ies->nr_cfg_r15.setup().nr_secondary_cell_group_cfg_r15; + switch (rrc_conn_recfg_v1510_ies->nr_cfg_r15.type()) { + case setup_opts::options::release: + logger.info("NR config R15 of type release"); + break; + case setup_opts::options::setup: + endc_release_and_add_r15 = rrc_conn_recfg_v1510_ies->nr_cfg_r15.setup().endc_release_and_add_r15; + if (rrc_conn_recfg_v1510_ies->nr_cfg_r15.setup().nr_secondary_cell_group_cfg_r15_present) { + nr_secondary_cell_group_cfg_r15_present = true; + nr_secondary_cell_group_cfg_r15 = rrc_conn_recfg_v1510_ies->nr_cfg_r15.setup().nr_secondary_cell_group_cfg_r15; + } + break; + default: + logger.error("NR config R15 type not defined"); + break; } - if (rrc_conn_recfg_v1510_ies->sk_counter_r15_present) { sk_counter_r15_present = true; sk_counter_r15 = rrc_conn_recfg_v1510_ies->sk_counter_r15; @@ -802,14 +810,14 @@ bool rrc::nr_reconfiguration_proc(const rrc_conn_recfg_r8_ies_s& rx_recfg) nr_radio_bearer_cfg1_r15_present = true; nr_radio_bearer_cfg1_r15 = rrc_conn_recfg_v1510_ies->nr_radio_bearer_cfg1_r15; } - - return rrc_nr->rrc_reconfiguration(endc_release_and_add_r15, - nr_secondary_cell_group_cfg_r15_present, - nr_secondary_cell_group_cfg_r15, - sk_counter_r15_present, - sk_counter_r15, - nr_radio_bearer_cfg1_r15_present, - nr_radio_bearer_cfg1_r15); + *has_5g_nr_reconfig = true; + return rrc_nr->rrc_reconfiguration(endc_release_and_add_r15, + nr_secondary_cell_group_cfg_r15_present, + nr_secondary_cell_group_cfg_r15, + sk_counter_r15_present, + sk_counter_r15, + nr_radio_bearer_cfg1_r15_present, + nr_radio_bearer_cfg1_r15); } /******************************************************************************* * @@ -1583,7 +1591,16 @@ void rrc::send_ul_dcch_msg(uint32_t lcid, const ul_dcch_msg_s& msg) pdcp_buf->N_bytes = (uint32_t)bref.distance_bytes(pdcp_buf->msg); pdcp_buf->set_timestamp(); - log_rrc_message(get_rb_name(lcid).c_str(), Tx, pdcp_buf.get(), msg, msg.msg.c1().type().to_string()); + if (msg.msg.type() == ul_dcch_msg_type_c::types_opts::options::c1) { + log_rrc_message(get_rb_name(lcid).c_str(), Tx, pdcp_buf.get(), msg, msg.msg.c1().type().to_string()); + } else if (msg.msg.type() == ul_dcch_msg_type_c::types_opts::options::msg_class_ext) { + if (msg.msg.msg_class_ext().type() == ul_dcch_msg_type_c::msg_class_ext_c_::types_opts::options::c2) { + log_rrc_message( + get_rb_name(lcid).c_str(), Tx, pdcp_buf.get(), msg, msg.msg.msg_class_ext().c2().type().to_string()); + } else { + log_rrc_message(get_rb_name(lcid).c_str(), Tx, pdcp_buf.get(), msg, msg.msg.msg_class_ext().type().to_string()); + } + } pdcp->write_sdu(lcid, std::move(pdcp_buf)); } @@ -2641,6 +2658,10 @@ void rrc::add_drb(const drb_to_add_mod_s& drb_cnfg) drbs[drb_cnfg.drb_id] = drb_cnfg; drb_up = true; logger.info("Added DRB Id %d (LCID=%d)", drb_cnfg.drb_id, lcid); + // Update LCID if gw is running + if(gw->is_running()){ + gw->update_lcid(drb_cnfg.eps_bearer_id, lcid); + } } void rrc::release_drb(uint32_t drb_id) @@ -2751,4 +2772,24 @@ asn1::rrc::ue_cap_rat_container_s rrc::get_nr_capabilities() return cap; } +void rrc::nr_notify_reconfiguration_failure() +{ + logger.warning("Notify reconfiguration about NR reconfiguration failure"); + if (conn_recfg_proc.is_busy()) { + conn_recfg_proc.trigger(false); + } +} +// 5.6.13a NR SCG failure information +void rrc::nr_scg_failure_information(const scg_failure_cause_t cause) +{ + logger.warning("Sending NR SCG failure information with cause %s", to_string(cause)); + ul_dcch_msg_s ul_dcch_msg; + scg_fail_info_nr_r15_s& scg_fail_info_nr = ul_dcch_msg.msg.set_msg_class_ext().set_c2().set_scg_fail_info_nr_r15(); + scg_fail_info_nr.crit_exts.set_c1().set_scg_fail_info_nr_r15(); + scg_fail_info_nr.crit_exts.c1().scg_fail_info_nr_r15().fail_report_scg_nr_r15_present = true; + scg_fail_info_nr.crit_exts.c1().scg_fail_info_nr_r15().fail_report_scg_nr_r15.fail_type_r15 = + (fail_report_scg_nr_r15_s::fail_type_r15_opts::options)cause; + send_ul_dcch_msg(RB_ID_SRB1, ul_dcch_msg); +} + } // namespace srsue diff --git a/srsue/src/stack/rrc/rrc_nr.cc b/srsue/src/stack/rrc/rrc_nr.cc index 65f1a8b51..a688aa222 100644 --- a/srsue/src/stack/rrc/rrc_nr.cc +++ b/srsue/src/stack/rrc/rrc_nr.cc @@ -210,17 +210,19 @@ void rrc_nr::get_eutra_nr_capabilities(srsran::byte_buffer_t* eutra_nr_caps_pdu) band_combination_s band_combination; - struct band_params_c band_param_eutra; - band_param_eutra.set_eutra(); - band_param_eutra.eutra().ca_bw_class_dl_eutra_present = true; - band_param_eutra.eutra().ca_bw_class_ul_eutra_present = true; - band_param_eutra.eutra().band_eutra = 1; // TODO: this also needs to be set here? - band_param_eutra.eutra().ca_bw_class_dl_eutra = asn1::rrc_nr::ca_bw_class_eutra_opts::options::a; - band_param_eutra.eutra().ca_bw_class_ul_eutra = asn1::rrc_nr::ca_bw_class_eutra_opts::options::a; - band_combination.band_list.push_back(band_param_eutra); + for (const auto& band : args.supported_bands_eutra) { + struct band_params_c band_param_eutra; + band_param_eutra.set_eutra(); + band_param_eutra.eutra().ca_bw_class_dl_eutra_present = true; + band_param_eutra.eutra().ca_bw_class_ul_eutra_present = true; + band_param_eutra.eutra().band_eutra = band; + band_param_eutra.eutra().ca_bw_class_dl_eutra = asn1::rrc_nr::ca_bw_class_eutra_opts::options::a; + band_param_eutra.eutra().ca_bw_class_ul_eutra = asn1::rrc_nr::ca_bw_class_eutra_opts::options::a; + band_combination.band_list.push_back(band_param_eutra); + } // TODO check if band is requested - for (const auto& band : args.supported_bands) { + for (const auto& band : args.supported_bands_nr) { struct band_params_c band_param_nr; band_param_nr.set_nr(); band_param_nr.nr().ca_bw_class_dl_nr_present = true; @@ -237,14 +239,16 @@ void rrc_nr::get_eutra_nr_capabilities(srsran::byte_buffer_t* eutra_nr_caps_pdu) mrdc_cap.rf_params_mrdc.ext = true; // RF Params MRDC applied_freq_band_list_filt - freq_band_info_c band_info_eutra; - band_info_eutra.set_band_info_eutra(); - band_info_eutra.band_info_eutra().ca_bw_class_dl_eutra_present = false; - band_info_eutra.band_info_eutra().ca_bw_class_ul_eutra_present = false; - band_info_eutra.band_info_eutra().band_eutra = 1; - mrdc_cap.rf_params_mrdc.applied_freq_band_list_filt.push_back(band_info_eutra); + for (const auto& band : args.supported_bands_eutra) { + freq_band_info_c band_info_eutra; + band_info_eutra.set_band_info_eutra(); + band_info_eutra.band_info_eutra().ca_bw_class_dl_eutra_present = false; + band_info_eutra.band_info_eutra().ca_bw_class_ul_eutra_present = false; + band_info_eutra.band_info_eutra().band_eutra = band; + mrdc_cap.rf_params_mrdc.applied_freq_band_list_filt.push_back(band_info_eutra); + } - for (const auto& band : args.supported_bands) { + for (const auto& band : args.supported_bands_nr) { freq_band_info_c band_info_nr; band_info_nr.set_band_info_nr(); band_info_nr.band_info_nr().band_nr = band; @@ -294,7 +298,7 @@ void rrc_nr::get_eutra_nr_capabilities(srsran::byte_buffer_t* eutra_nr_caps_pdu) feature_sets_per_band.push_back(feature_set_eutra); feature_set_combination.push_back(feature_sets_per_band); - for (const auto& band : args.supported_bands) { + for (const auto& band : args.supported_bands_nr) { feature_sets_per_band.resize(0); feature_set_c feature_set_nr; feature_set_nr.set_nr(); @@ -337,67 +341,15 @@ bool rrc_nr::rrc_reconfiguration(bool endc_release_and_add_r15, bool nr_radio_bearer_cfg1_r15_present, asn1::dyn_octstring nr_radio_bearer_cfg1_r15) { - // sanity check only for now - if (nr_secondary_cell_group_cfg_r15_present == false || sk_counter_r15_present == false || - nr_radio_bearer_cfg1_r15_present == false) { - logger.error("RRC NR Reconfiguration failed sanity check failed"); - return false; - } - - rrc_recfg_s rrc_recfg; - cell_group_cfg_s cell_group_cfg; - radio_bearer_cfg_s radio_bearer_cfg; - asn1::SRSASN_CODE err; - - cbit_ref bref(nr_secondary_cell_group_cfg_r15.data(), nr_secondary_cell_group_cfg_r15.size()); - - err = rrc_recfg.unpack(bref); - if (err != asn1::SRSASN_SUCCESS) { - logger.error("Could not unpack NR reconfiguration message."); - return false; - } - - log_rrc_message( - "RRC NR Reconfiguration", Rx, nr_secondary_cell_group_cfg_r15, rrc_recfg, "NR Secondary Cell Group Cfg R15"); - - if (rrc_recfg.crit_exts.type() == asn1::rrc_nr::rrc_recfg_s::crit_exts_c_::types::rrc_recfg) { - if (rrc_recfg.crit_exts.rrc_recfg().secondary_cell_group_present == true) { - cbit_ref bref0(rrc_recfg.crit_exts.rrc_recfg().secondary_cell_group.data(), - rrc_recfg.crit_exts.rrc_recfg().secondary_cell_group.size()); - - err = cell_group_cfg.unpack(bref0); - if (err != asn1::SRSASN_SUCCESS) { - logger.error("Could not unpack cell group message message."); - return false; - } - - log_rrc_message("RRC NR Reconfiguration", - Rx, - rrc_recfg.crit_exts.rrc_recfg().secondary_cell_group, - cell_group_cfg, - "Secondary Cell Group Config"); - } else { - logger.error("Reconfiguration does not contain Secondary Cell Group Config"); - return false; - } - } - - cbit_ref bref1(nr_radio_bearer_cfg1_r15.data(), nr_radio_bearer_cfg1_r15.size()); - - err = radio_bearer_cfg.unpack(bref1); - if (err != asn1::SRSASN_SUCCESS) { - logger.error("Could not unpack radio bearer config."); - return false; - } - - log_rrc_message("RRC NR Reconfiguration", Rx, nr_radio_bearer_cfg1_r15, radio_bearer_cfg, "Radio Bearer Config R15"); - if (not conn_recfg_proc.launch(endc_release_and_add_r15, - rrc_recfg, - cell_group_cfg, + if (not conn_recfg_proc.launch(reconf_initiator_t::mcg_srb1, + endc_release_and_add_r15, + nr_secondary_cell_group_cfg_r15_present, + nr_secondary_cell_group_cfg_r15, sk_counter_r15_present, sk_counter_r15, - radio_bearer_cfg)) { - logger.error("Unable to launch NR RRC configuration procedure"); + nr_radio_bearer_cfg1_r15_present, + nr_radio_bearer_cfg1_r15)) { + logger.error("Unable to launch NR RRC reconfiguration procedure"); return false; } else { callback_list.add_proc(conn_recfg_proc); @@ -413,7 +365,7 @@ void rrc_nr::get_nr_capabilities(srsran::byte_buffer_t* nr_caps_pdu) // PDCP nr_cap.pdcp_params.max_num_rohc_context_sessions = pdcp_params_s::max_num_rohc_context_sessions_opts::cs2; - for (const auto& band : args.supported_bands) { + for (const auto& band : args.supported_bands_nr) { band_nr_s band_nr; band_nr.band_nr = band; band_nr.ue_pwr_class_present = true; @@ -567,8 +519,8 @@ bool rrc_nr::apply_mac_cell_group(const mac_cell_group_cfg_s& mac_cell_group_cfg if (mac_cell_group_cfg.bsr_cfg_present) { logger.debug("Handling MAC BSR config"); srsran::bsr_cfg_nr_t bsr_cfg = {}; - bsr_cfg.periodic_timer = mac_cell_group_cfg.bsr_cfg.periodic_bsr_timer.to_number(); - bsr_cfg.retx_timer = mac_cell_group_cfg.bsr_cfg.retx_bsr_timer.to_number(); + bsr_cfg.periodic_timer = mac_cell_group_cfg.bsr_cfg.periodic_bsr_timer.to_number(); + bsr_cfg.retx_timer = mac_cell_group_cfg.bsr_cfg.retx_bsr_timer.to_number(); if (mac->set_config(bsr_cfg) != SRSRAN_SUCCESS) { return false; } @@ -615,7 +567,7 @@ bool rrc_nr::apply_sp_cell_init_dl_pdsch(const asn1::rrc_nr::pdsch_cfg_s& pdsch_ bool rrc_nr::apply_res_csi_report_cfg(const asn1::rrc_nr::csi_report_cfg_s& csi_report_cfg) { - uint32_t report_cfg_id = csi_report_cfg.report_cfg_id; + uint32_t report_cfg_id = csi_report_cfg.report_cfg_id; srsran_csi_hl_report_cfg_t srsran_csi_hl_report_cfg; if (make_phy_csi_report(csi_report_cfg, &srsran_csi_hl_report_cfg) == true) { phy_cfg.csi.reports[report_cfg_id] = srsran_csi_hl_report_cfg; @@ -697,7 +649,7 @@ bool rrc_nr::apply_dl_common_cfg(const asn1::rrc_nr::dl_cfg_common_s& dl_cfg_com // phy_cfg.pdcch.ra_rnti = 0x16; //< Supposed to be deduced from PRACH configuration phy_cfg.pdcch.ra_search_space = phy_cfg.pdcch.search_space[pdcch_cfg_common.ra_search_space]; phy_cfg.pdcch.ra_search_space_present = true; - phy_cfg.pdcch.ra_search_space.type = srsran_search_space_type_common_3; + phy_cfg.pdcch.ra_search_space.type = srsran_search_space_type_common_1; } else { logger.warning("Search space %d not presenet for random access search space", pdcch_cfg_common.ra_search_space); @@ -753,7 +705,6 @@ bool rrc_nr::apply_ul_common_cfg(const asn1::rrc_nr::ul_cfg_common_s& ul_cfg_com if (ul_cfg_common.init_ul_bwp.rach_cfg_common_present) { if (ul_cfg_common.init_ul_bwp.rach_cfg_common.type() == setup_release_c::types_opts::setup) { rach_nr_cfg_t rach_nr_cfg = make_mac_rach_cfg(ul_cfg_common.init_ul_bwp.rach_cfg_common.setup()); - phy_cfg.pdcch.ra_rnti = ul_cfg_common.init_ul_bwp.rach_cfg_common.setup().rach_cfg_generic.prach_cfg_idx; mac->set_config(rach_nr_cfg); // Make the RACH configuration for PHY @@ -1126,6 +1077,22 @@ bool rrc_nr::apply_cell_group_cfg(const cell_group_cfg_s& cell_group_cfg) return true; } +bool rrc_nr::apply_drb_release(const uint8_t drb) +{ + uint32_t lcid = get_lcid_for_rbid(drb); + if(lcid == 0){ + logger.warning("Can not release bearer with lcid %d and drb %d", lcid, drb); + return false; + } + logger.info("Releasing bearer DRB: %d LCID: %d", drb, lcid); + pdcp->del_bearer(lcid); + // TODO + // 2> if the UE is operating in EN-DC + // 3> if a new bearer is not added either with NR or E-UTRA with same eps-BearerIdentity: + // 4> indicate the release of the DRB and the eps-BearerIdentity of the released DRB to upper layers. + return true; +} + bool rrc_nr::apply_drb_add_mod(const drb_to_add_mod_s& drb_cfg) { if (!drb_cfg.pdcp_cfg_present) { @@ -1236,6 +1203,13 @@ bool rrc_nr::apply_radio_bearer_cfg(const radio_bearer_cfg_s& radio_bearer_cfg) } } } + if (radio_bearer_cfg.drb_to_release_list_present) { + for (uint32_t i = 0; i < radio_bearer_cfg.drb_to_release_list.size(); i++) { + if (apply_drb_release(radio_bearer_cfg.drb_to_release_list[i]) == false) { + return false; + } + } + } if (radio_bearer_cfg.security_cfg_present) { if (apply_security_cfg(radio_bearer_cfg.security_cfg) == false) { return false; @@ -1248,7 +1222,11 @@ void rrc_nr::max_retx_attempted() {} // MAC interface void rrc_nr::ra_completed() {} -void rrc_nr::ra_problem() {} +void rrc_nr::ra_problem() +{ + rrc_eutra->nr_scg_failure_information(scg_failure_cause_t::random_access_problem); +} + void rrc_nr::release_pucch_srs() {} // STACK interface @@ -1258,18 +1236,63 @@ void rrc_nr::cell_search_completed(const rrc_interface_phy_lte::cell_search_ret_ /* Procedures */ rrc_nr::connection_reconf_no_ho_proc::connection_reconf_no_ho_proc(rrc_nr* parent_) : rrc_ptr(parent_) {} -proc_outcome_t rrc_nr::connection_reconf_no_ho_proc::init(const bool endc_release_and_add_r15, - const asn1::rrc_nr::rrc_recfg_s& rrc_recfg, - const asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg, - bool sk_counter_r15_present, - const uint32_t sk_counter_r15, - const asn1::rrc_nr::radio_bearer_cfg_s& radio_bearer_cfg) +proc_outcome_t rrc_nr::connection_reconf_no_ho_proc::init(const reconf_initiator_t initiator_, + const bool endc_release_and_add_r15, + const bool nr_secondary_cell_group_cfg_r15_present, + const asn1::dyn_octstring nr_secondary_cell_group_cfg_r15, + const bool sk_counter_r15_present, + const uint32_t sk_counter_r15, + const bool nr_radio_bearer_cfg1_r15_present, + const asn1::dyn_octstring nr_radio_bearer_cfg1_r15) { Info("Starting..."); + initiator = initiator_; - Info("Applying Cell Group Cfg"); - if (!rrc_ptr->apply_cell_group_cfg(cell_group_cfg)) { - return proc_outcome_t::error; + rrc_recfg_s rrc_recfg; + cell_group_cfg_s cell_group_cfg; + radio_bearer_cfg_s radio_bearer_cfg; + asn1::SRSASN_CODE err; + + if (nr_secondary_cell_group_cfg_r15_present) { + cbit_ref bref(nr_secondary_cell_group_cfg_r15.data(), nr_secondary_cell_group_cfg_r15.size()); + err = rrc_recfg.unpack(bref); + if (err != asn1::SRSASN_SUCCESS) { + Error("Could not unpack NR reconfiguration message."); + return proc_outcome_t::error; + } + + rrc_ptr->log_rrc_message( + "RRC NR Reconfiguration", Rx, nr_secondary_cell_group_cfg_r15, rrc_recfg, "NR Secondary Cell Group Cfg R15"); + + if (rrc_recfg.crit_exts.type() != asn1::rrc_nr::rrc_recfg_s::crit_exts_c_::types::rrc_recfg) { + Error("Reconfiguration does not contain Secondary Cell Group Config"); + return proc_outcome_t::error; + } + + if (not rrc_recfg.crit_exts.rrc_recfg().secondary_cell_group_present) { + Error("Reconfiguration does not contain Secondary Cell Group Config"); + return proc_outcome_t::error; + } + + cbit_ref bref0(rrc_recfg.crit_exts.rrc_recfg().secondary_cell_group.data(), + rrc_recfg.crit_exts.rrc_recfg().secondary_cell_group.size()); + + err = cell_group_cfg.unpack(bref0); + if (err != asn1::SRSASN_SUCCESS) { + Error("Could not unpack cell group message message."); + return proc_outcome_t::error; + } + + rrc_ptr->log_rrc_message("RRC NR Reconfiguration", + Rx, + rrc_recfg.crit_exts.rrc_recfg().secondary_cell_group, + cell_group_cfg, + "Secondary Cell Group Config"); + + Info("Applying Cell Group Cfg"); + if (!rrc_ptr->apply_cell_group_cfg(cell_group_cfg)) { + return proc_outcome_t::error; + } } if (sk_counter_r15_present) { @@ -1279,21 +1302,39 @@ proc_outcome_t rrc_nr::connection_reconf_no_ho_proc::init(const bool } } + if(nr_radio_bearer_cfg1_r15_present){ + cbit_ref bref1(nr_radio_bearer_cfg1_r15.data(), nr_radio_bearer_cfg1_r15.size()); + + err = radio_bearer_cfg.unpack(bref1); + if (err != asn1::SRSASN_SUCCESS) { + Error("Could not unpack radio bearer config."); + return proc_outcome_t::error; + } + + rrc_ptr->log_rrc_message( + "RRC NR Reconfiguration", Rx, nr_radio_bearer_cfg1_r15, radio_bearer_cfg, "Radio Bearer Config R15"); + Info("Applying Radio Bearer Cfg"); if (!rrc_ptr->apply_radio_bearer_cfg(radio_bearer_cfg)) { return proc_outcome_t::error; } + } + return proc_outcome_t::success; } proc_outcome_t rrc_nr::connection_reconf_no_ho_proc::react(const bool& config_complete) { if (not config_complete) { - Error("Failed to config PHY"); + Error("NR reconfiguration failed"); return proc_outcome_t::error; } - rrc_ptr->rrc_eutra->nr_rrc_con_reconfig_complete(true); + // TODO phy ctrl + // in case there are scell to configure, wait for second phy configuration + // if (not rrc_ptr->phy_ctrl->is_config_pending()) { + // return proc_outcome_t::yield; + // } Info("Reconfig NR return successful"); return proc_outcome_t::success; @@ -1304,11 +1345,21 @@ void rrc_nr::connection_reconf_no_ho_proc::then(const srsran::proc_state_t& resu if (result.is_success()) { Info("Finished %s successfully", name()); srsran::console("RRC NR reconfiguration successful.\n"); - return; + rrc_ptr->rrc_eutra->nr_rrc_con_reconfig_complete(true); + } else { + // 5.3.5.8.2 Inability to comply with RRCReconfiguration + switch (initiator) { + case reconf_initiator_t::mcg_srb1: + rrc_ptr->rrc_eutra->nr_notify_reconfiguration_failure(); + break; + default: + Warning("Reconfiguration failure not implemented for initiator %d", initiator); + break; + } + srsran::console("RRC NR reconfiguration failed.\n"); + Warning("Finished %s with failure", name()); } - - // Section 5.3.5.5 - Reconfiguration failure - // rrc_ptr->con_reconfig_failed(); + return; } } // namespace srsue diff --git a/srsue/src/stack/rrc/rrc_procedures.cc b/srsue/src/stack/rrc/rrc_procedures.cc index 04746dba8..de1e00e94 100644 --- a/srsue/src/stack/rrc/rrc_procedures.cc +++ b/srsue/src/stack/rrc/rrc_procedures.cc @@ -1004,12 +1004,11 @@ srsran::proc_outcome_t rrc::connection_reconf_no_ho_proc::init(const asn1::rrc:: } // Apply NR config - bool rtn = rrc_ptr->nr_reconfiguration_proc(rx_recfg); - if (rtn == false) { - rrc_ptr->logger.error("Can not launch NR RRC Reconfiguration procedure"); + bool rtn = rrc_ptr->nr_reconfiguration_proc(rx_recfg, &has_5g_nr_reconfig); + if (rtn == false && has_5g_nr_reconfig == true) { + rrc_ptr->logger.error("Unable to launch NR RRC Reconfiguration procedure"); return proc_outcome_t::error; } - has_5g_nr_reconfig = true; // No phy config was scheduled, run config completion immediately if (rrc_ptr->phy_ctrl->is_config_pending()) { @@ -1021,7 +1020,7 @@ srsran::proc_outcome_t rrc::connection_reconf_no_ho_proc::init(const asn1::rrc:: srsran::proc_outcome_t rrc::connection_reconf_no_ho_proc::react(const bool& config_complete) { if (not config_complete) { - rrc_ptr->logger.error("Failed to config PHY"); + rrc_ptr->logger.error("Reconfiguration Failed"); return proc_outcome_t::error; } @@ -1068,7 +1067,6 @@ void rrc::connection_reconf_no_ho_proc::then(const srsran::proc_state_t& result) } // Section 5.3.5.5 - Reconfiguration failure - // TODO: if RRC NR configuration this also need to be signaled via LTE rrc_ptr->con_reconfig_failed(); } @@ -1465,7 +1463,7 @@ rrc::connection_reest_proc::react(const cell_selection_proc::cell_selection_comp rrc_ptr->meas_cells.serving_cell().has_sib3()); return proc_outcome_t::yield; // wait for t311 expiry } - + Info("Apply Cell Criteria"); return cell_criteria(); } diff --git a/srsue/src/stack/ue_stack_lte.cc b/srsue/src/stack/ue_stack_lte.cc index 4c2208bb8..f5e4eddaf 100644 --- a/srsue/src/stack/ue_stack_lte.cc +++ b/srsue/src/stack/ue_stack_lte.cc @@ -308,6 +308,7 @@ bool ue_stack_lte::get_metrics(stack_metrics_t* metrics) stack_metrics_t metrics{}; metrics.ul_dropped_sdus = ul_dropped_sdus; mac.get_metrics(metrics.mac); + mac_nr.get_metrics(metrics.mac_nr); rlc.get_metrics(metrics.rlc, metrics.mac[0].nof_tti); nas.get_metrics(&metrics.nas); rrc.get_metrics(metrics.rrc); @@ -420,9 +421,11 @@ void ue_stack_lte::run_tti_impl(uint32_t tti, uint32_t tti_jump) for (uint32_t i = 0; i < tti_jump; ++i) { uint32_t next_tti = TTI_SUB(tti, (tti_jump - i - 1)); mac.run_tti(next_tti); + mac_nr.run_tti(next_tti); task_sched.tic(); } rrc.run_tti(); + rrc_nr.run_tti(tti); nas.run_tti(); if (args.have_tti_time_stats) { diff --git a/srsue/src/stack/upper/gw.cc b/srsue/src/stack/upper/gw.cc index ee34ab5b7..7583e3c06 100644 --- a/srsue/src/stack/upper/gw.cc +++ b/srsue/src/stack/upper/gw.cc @@ -204,6 +204,12 @@ int gw::setup_if_addr(uint32_t eps_bearer_id, return SRSRAN_SUCCESS; } + +bool gw::is_running() +{ + return running; +} + int gw::update_lcid(uint32_t eps_bearer_id, uint32_t new_lcid) { auto it = eps_lcid.find(eps_bearer_id); @@ -358,6 +364,8 @@ void gw::run_thread() logger.info("GW IP receiver thread exiting."); } + + /**************************/ /* TUN Interface Helpers */ /**************************/ diff --git a/srsue/src/ue.cc b/srsue/src/ue.cc index fb7512607..30cf0a1d0 100644 --- a/srsue/src/ue.cc +++ b/srsue/src/ue.cc @@ -237,6 +237,12 @@ int ue::parse_args(const all_args_t& args_) args.stack.rrc.supported_bands.end()) { args.stack.rrc.supported_bands[args.stack.rrc.nof_supported_bands++] = band; } + // RRC NR needs also information about supported eutra bands + if (std::find(args.stack.rrc_nr.supported_bands_eutra.begin(), + args.stack.rrc_nr.supported_bands_eutra.end(), + band) == args.stack.rrc_nr.supported_bands_eutra.end()) { + args.stack.rrc_nr.supported_bands_eutra.push_back(band); + } } } else { logger.error("Error: dl_earfcn list is empty"); @@ -269,9 +275,10 @@ int ue::parse_args(const all_args_t& args_) std::vector bands = bands_helper.get_bands_nr(arfcn); for (const auto& band : bands) { // make sure we don't add duplicates - if (std::find(args.stack.rrc_nr.supported_bands.begin(), args.stack.rrc_nr.supported_bands.end(), band) == - args.stack.rrc_nr.supported_bands.end()) { - args.stack.rrc_nr.supported_bands.push_back(band); + if (std::find(args.stack.rrc_nr.supported_bands_nr.begin(), + args.stack.rrc_nr.supported_bands_nr.end(), + band) == args.stack.rrc_nr.supported_bands_nr.end()) { + args.stack.rrc_nr.supported_bands_nr.push_back(band); } } } @@ -337,6 +344,7 @@ bool ue::get_metrics(ue_metrics_t* m) stack->get_metrics(&m->stack); gw_inst->get_metrics(m->gw, m->stack.mac[0].nof_tti); m->sys = sys_proc.get_metrics(); + m->phy_nr.nof_active_cc = args.phy.nof_nr_carriers; // FIXME: temporary until PHY metrics are complete return true; } diff --git a/srsue/test/CMakeLists.txt b/srsue/test/CMakeLists.txt index 44b96c7bf..60ba63060 100644 --- a/srsue/test/CMakeLists.txt +++ b/srsue/test/CMakeLists.txt @@ -20,7 +20,6 @@ add_subdirectory(phy) add_subdirectory(upper) -add_subdirectory(mac_nr) if (ENABLE_TTCN3) add_subdirectory(ttcn3) diff --git a/srsue/test/mac_nr/mac_nr_test.cc b/srsue/test/mac_nr/mac_nr_test.cc deleted file mode 100644 index 5c1526937..000000000 --- a/srsue/test/mac_nr/mac_nr_test.cc +++ /dev/null @@ -1,260 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsLTE. - * - * srsLTE is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsLTE 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 Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ -#include "srsran/common/buffer_pool.h" -#include "srsran/common/common.h" -#include "srsran/common/test_common.h" -#include "srsran/test/ue_test_interfaces.h" -#include "srsue/hdr/stack/mac_nr/mac_nr.h" - -using namespace srsue; - -#define HAVE_PCAP 0 -#define UE_ID 0 - -static std::unique_ptr pcap_handle = nullptr; - -class dummy_phy : public phy_interface_mac_nr -{ -public: - dummy_phy() {} - void send_prach(const uint32_t prach_occasion_, - const int preamble_index_, - const float preamble_received_target_power_, - const float ta_base_sec_ = 0.0f) override - { - prach_occasion = prach_occasion_; - preamble_index = preamble_index_; - preamble_received_target_power = preamble_received_target_power_; - } - int tx_request(const tx_request_t& request) override { return 0; } - int set_ul_grant(std::array, uint16_t rnti, srsran_rnti_type_t rnti_type) override - { - return 0; - } - - void get_last_send_prach(uint32_t* prach_occasion_, uint32_t* preamble_index_, int* preamble_received_target_power_) - { - *prach_occasion_ = prach_occasion; - *preamble_index_ = preamble_index; - *preamble_received_target_power_ = preamble_received_target_power; - } - void sr_send(uint32_t sr_id) override {} - -private: - uint32_t prach_occasion = 0; - uint32_t preamble_index = 0; - int preamble_received_target_power = 0; -}; - -class rrc_dummy : public rrc_interface_mac -{ -public: - rrc_dummy() {} - virtual void ra_completed() {} - virtual void ra_problem() {} - virtual void release_pucch_srs() {} -}; - -class stack_dummy : public stack_test_dummy -{ -public: - void init(mac_nr* mac_, phy_interface_mac_nr* phy_) - { - mac_h = mac_; - phy_h = phy_; - } - void run_tti(uint32_t tti) - { - mac_h->run_tti(tti); - // flush all events - stack_test_dummy::run_tti(); - } - -private: - phy_interface_mac_nr* phy_h = nullptr; - mac_nr* mac_h = nullptr; -}; - -// TODO: refactor to common test dummy components -class rlc_dummy : public srsue::rlc_dummy_interface -{ -public: - rlc_dummy() : received_bytes(0) {} - bool has_data_locked(const uint32_t lcid) final { return ul_queues[lcid] > 0; } - uint32_t get_buffer_state(const uint32_t lcid) final { return ul_queues[lcid]; } - int read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) final - { - if (!read_enable || nof_bytes < read_min) { - return 0; - } - - if (read_len > 0 && read_len < (int32_t)nof_bytes) { - nof_bytes = read_len; - } - - uint32_t len = SRSRAN_MIN(ul_queues[lcid], nof_bytes); - - // set payload bytes to LCID so we can check later if the scheduling was correct - memset(payload, lcid > 0 ? lcid : 0xf, len); - - // remove from UL queue - ul_queues[lcid] -= len; - - return len; - }; - void write_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) final - { - logger.debug(payload, nof_bytes, "Received %d B on LCID %d", nof_bytes, lcid); - received_bytes += nof_bytes; - } - - void write_sdu(uint32_t lcid, uint32_t nof_bytes) { ul_queues[lcid] += nof_bytes; } - uint32_t get_received_bytes() { return received_bytes; } - - void disable_read() { read_enable = false; } - void set_read_len(uint32_t len) { read_len = len; } - void set_read_min(uint32_t len) { read_min = len; } - void reset_queues() - { - for (auto& q : ul_queues) { - q.second = 0; - } - } - -private: - bool read_enable = true; - int32_t read_len = -1; // read all - uint32_t read_min = 0; // minimum "grant size" for read_pdu() to return data - uint32_t received_bytes; - srslog::basic_logger& logger = srslog::fetch_basic_logger("RLC"); - // UL queues where key is LCID and value the queue length - std::map ul_queues; -}; - -// TODO: Add test -int msg3_test() -{ - return SRSRAN_SUCCESS; -} - -// Basic PDU generation test -int mac_nr_ul_logical_channel_prioritization_test1() -{ - // PDU layout (20B in total) - // - 2 B MAC subheader for SCH LCID=4 - // - 10 B sduPDU - // - 1 B subheader padding - // - 7 B padding - const uint8_t tv[] = {0x04, 0x0a, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - - // dummy layers - dummy_phy phy; - rlc_dummy rlc; - rrc_dummy rrc; - stack_dummy stack; - - // the actual MAC - mac_nr mac(&stack.task_sched); - - mac_nr_args_t args = {}; - mac.init(args, &phy, &rlc, &rrc); - - stack.init(&mac, &phy); - const uint16_t crnti = 0x1001; - - // generate config (default DRB2 config for EN-DC) - std::vector lcids; - srsran::logical_channel_config_t config = {}; - config.lcid = 4; - config.lcg = 6; - config.PBR = 0; - config.BSD = 1000; // 1000ms - config.priority = 11; - lcids.push_back(config); - - // setup LCIDs in MAC - for (auto& channel : lcids) { - mac.setup_lcid(channel); - } - - srsran::bsr_cfg_nr_t bsr_cfg = {}; - bsr_cfg.periodic_timer = 20; - bsr_cfg.retx_timer = 320; - TESTASSERT(mac.set_config(bsr_cfg) == SRSRAN_SUCCESS); - - // write dummy data to DRB2 - rlc.write_sdu(4, 10); - - // run TTI to setup Bj, BSR should be generated - stack.run_tti(0); - usleep(100); - - // create UL action and grant and read MAC PDU - { - mac_interface_phy_nr::tb_action_ul_t ul_action = {}; - mac_interface_phy_nr::mac_nr_grant_ul_t mac_grant = {}; - - mac_grant.rnti = crnti; // make sure MAC picks it up as valid UL grant - mac_grant.pid = 0; - mac_grant.rnti = 0x1001; - mac_grant.tti = 0; - mac_grant.tbs = 20; - int cc_idx = 0; - - // Send grant to MAC and get action for this TB, 0x - mac.new_grant_ul(cc_idx, mac_grant, &ul_action); - - // print generated PDU - srslog::fetch_basic_logger("MAC").info( - ul_action.tb.payload->msg, mac_grant.tbs, "Generated PDU (%d B)", mac_grant.tbs); -#if HAVE_PCAP - pcap_handle->write_ul_crnti_nr( - ul_action.tb.payload->msg, mac_grant.tbs, mac_grant.rnti, UE_ID, mac_grant.pid, mac_grant.tti); -#endif - - TESTASSERT(memcmp(ul_action.tb.payload->msg, tv, sizeof(tv)) == 0); - } - - // make sure MAC PDU thread picks up before stopping - stack.run_tti(0); - mac.stop(); - - return SRSRAN_SUCCESS; -} - -int main() -{ -#if HAVE_PCAP - pcap_handle = std::unique_ptr(new srsran::mac_pcap()); - pcap_handle->open("mac_test_nr.pcap"); -#endif - - auto& mac_logger = srslog::fetch_basic_logger("MAC"); - mac_logger.set_level(srslog::basic_levels::debug); - mac_logger.set_hex_dump_max_size(-1); - srslog::init(); - - TESTASSERT(msg3_test() == SRSRAN_SUCCESS); - TESTASSERT(mac_nr_ul_logical_channel_prioritization_test1() == SRSRAN_SUCCESS); - - return SRSRAN_SUCCESS; -} diff --git a/srsue/test/metrics_test.cc b/srsue/test/metrics_test.cc index 44619efd0..1a35df021 100644 --- a/srsue/test/metrics_test.cc +++ b/srsue/test/metrics_test.cc @@ -69,6 +69,14 @@ public: m->stack.rrc.neighbour_cells.push_back(neighbor); // need to add twice since we use CA } + m->phy_nr.nof_active_cc = 1; + m->phy_nr.ch[0].rsrp = -10.0f; + m->phy_nr.ch[0].pathloss = 32; + m->stack.mac_nr[0].rx_pkts = 100; + m->stack.mac_nr[0].rx_errors = 2; + m->stack.mac_nr[0].rx_brate = 223; + m->stack.mac_nr[0].nof_tti = 1; + m->stack.rrc.state = (rand() % 2 == 0) ? RRC_STATE_CONNECTED : RRC_STATE_IDLE; return true; diff --git a/srsue/test/ttcn3/hdr/ttcn3_ue.h b/srsue/test/ttcn3/hdr/ttcn3_ue.h index 5a181d8ee..82a15877d 100644 --- a/srsue/test/ttcn3/hdr/ttcn3_ue.h +++ b/srsue/test/ttcn3/hdr/ttcn3_ue.h @@ -68,6 +68,7 @@ public: uint8_t* ipv6_if_id, char* err_str); int update_lcid(uint32_t eps_bearer_id, uint32_t new_lcid); + bool is_running(); int apply_traffic_flow_template(const uint8_t& eps_bearer_id, const uint8_t& lcid, diff --git a/srsue/test/ttcn3/src/ttcn3_ue.cc b/srsue/test/ttcn3/src/ttcn3_ue.cc index ad3c5f4f0..39265d86e 100644 --- a/srsue/test/ttcn3/src/ttcn3_ue.cc +++ b/srsue/test/ttcn3/src/ttcn3_ue.cc @@ -197,6 +197,12 @@ int ttcn3_ue::setup_if_addr(uint32_t eps_bearer_id, { return 0; } + +bool ttcn3_ue::is_running() +{ + return true; +} + int ttcn3_ue::update_lcid(uint32_t eps_bearer_id, uint32_t new_lcid) { return SRSRAN_SUCCESS;