diff --git a/CMakeLists.txt b/CMakeLists.txt index c12fc5a66..5242bf265 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,6 +97,8 @@ 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_ALL_TEST "Enable all unit/component test" 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 @@ -496,13 +498,13 @@ if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") endif (ENABLE_ASAN) if (ENABLE_TSAN) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=thread") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=thread -DHAVE_TSAN") 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") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=memory -fno-omit-frame-pointer -fsanitize-memory-track-origins -fPIE") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=memory -fno-omit-frame-pointer -fsanitize-memory-track-origins -fPIE") endif (ENABLE_MSAN AND CMAKE_C_COMPILER_ID MATCHES "Clang") if (ENABLE_GCOV) @@ -605,6 +607,18 @@ function(add_nr_test) set_tests_properties(${TNAME} PROPERTIES LABELS "nr;${CTEST_LABELS}") endfunction() +function(add_nr_advanced_test) + if (NOT ${ENABLE_ALL_TEST}) + return() + endif() + add_test(${ARGN}) + set(TNAME ${ARGV0}) + if(${TNAME} STREQUAL NAME) + set(TNAME ${ARGV1}) + endif() + set_tests_properties(${TNAME} PROPERTIES LABELS "nr;${CTEST_LABELS}") +endfunction() + ######################################################################## # Add general includes and dependencies ######################################################################## diff --git a/lib/include/srsran/adt/accumulators.h b/lib/include/srsran/adt/accumulators.h index 50b634416..2748b3032 100644 --- a/lib/include/srsran/adt/accumulators.h +++ b/lib/include/srsran/adt/accumulators.h @@ -47,8 +47,10 @@ private: template struct exp_average_fast_start { - exp_average_fast_start(T alpha_, uint32_t start_size = 100) : alpha(alpha_), start_count_size(start_size) + exp_average_fast_start(T alpha_val) : exp_average_fast_start(alpha_val, 1.0 / alpha_val) {} + exp_average_fast_start(T alpha_val, uint32_t start_size) : alpha_(alpha_val), start_count_size(start_size) { + assert(alpha_ < 1); assert(start_size > 0); } void push(T sample) @@ -57,16 +59,18 @@ struct exp_average_fast_start { avg_ += (sample - avg_) / (count + 1); count++; } else { - avg_ = (1 - alpha) * avg_ + alpha * sample; + avg_ = (1 - alpha_) * avg_ + alpha_ * sample; } } - T value() const { return count == 0 ? 0 : avg_; } + T value() const { return count == 0 ? 0 : avg_; } + T alpha() const { return alpha_; } + bool is_exp_average_mode() const { return count >= start_count_size; } private: T avg_ = 0; uint32_t count = 0; uint32_t start_count_size; - T alpha; + T alpha_; }; namespace detail { @@ -121,7 +125,6 @@ private: template struct null_sliding_average { - null_sliding_average(uint32_t N) : window(N, null_value()) {} void push(T sample) { window.push(sample); } void push_hole() { window.push(null_value()); } diff --git a/lib/include/srsran/adt/bounded_bitset.h b/lib/include/srsran/adt/bounded_bitset.h index 3313a1326..7c3234802 100644 --- a/lib/include/srsran/adt/bounded_bitset.h +++ b/lib/include/srsran/adt/bounded_bitset.h @@ -375,7 +375,8 @@ public: void from_uint64(uint64_t v) { srsran_assert(nof_words_() == 1, "ERROR: cannot convert bitset of size=%zd to uint64_t", size()); - srsran_assert(v < (1U << size()), "ERROR: Provided uint64=%ld does not fit in bitset of size=%zd", v, size()); + srsran_assert( + v < (1U << size()), "ERROR: Provided mask=0x%" PRIx64 " does not fit in bitset of size=%zd", v, size()); buffer[0] = v; } diff --git a/lib/include/srsran/adt/observer.h b/lib/include/srsran/adt/observer.h index 75a178f83..480c7a68e 100644 --- a/lib/include/srsran/adt/observer.h +++ b/lib/include/srsran/adt/observer.h @@ -29,8 +29,8 @@ namespace srsran { -using observer_id = std::size_t; -const size_t invalid_observer_id = std::numeric_limits::max(); +using observer_id = std::size_t; +const std::size_t invalid_observer_id = std::numeric_limits::max(); template class observer; @@ -93,7 +93,7 @@ public: template observer_id subscribe(Args2&&... args) { - size_t id = 0; + std::size_t id = 0; for (auto& slot : observers) { if (not static_cast(slot)) { // empty slot found @@ -117,9 +117,9 @@ public: return false; } - size_t nof_observers() const + std::size_t nof_observers() const { - size_t count = 0; + std::size_t count = 0; for (auto& slot : observers) { count += static_cast(slot) ? 1 : 0; } diff --git a/lib/include/srsran/adt/scope_exit.h b/lib/include/srsran/adt/scope_exit.h index 50b9a5554..25f92b54c 100644 --- a/lib/include/srsran/adt/scope_exit.h +++ b/lib/include/srsran/adt/scope_exit.h @@ -69,7 +69,7 @@ detail::scope_exit::type> make_scope_exit(Callable return detail::scope_exit::type>{std::forward(callable)}; } -#define DEFER(FUNC) auto on_exit_call = make_scope_exit([&]() { FUNC }) +#define DEFER(FUNC) auto on_exit_call##__LINE__ = srsran::make_scope_exit([&]() { FUNC }) } // namespace srsran diff --git a/lib/include/srsran/common/epoll_helper.h b/lib/include/srsran/common/epoll_helper.h index d55807c1f..905dac9fa 100644 --- a/lib/include/srsran/common/epoll_helper.h +++ b/lib/include/srsran/common/epoll_helper.h @@ -26,6 +26,7 @@ #ifndef SRSRAN_EPOLL_HELPER_H #define SRSRAN_EPOLL_HELPER_H +#include #include #include #include @@ -67,7 +68,7 @@ private: class epoll_signal_handler : public epoll_handler { public: - epoll_signal_handler(bool* running_) : running(running_) {} + epoll_signal_handler(std::atomic& running_) : running(running_) {} int handle_event(int fd, epoll_event e, int epoll_fd) { @@ -81,7 +82,7 @@ public: case SIGINT: case SIGHUP: case SIGQUIT: - *running = false; + running = false; break; default: fprintf(stderr, "got signal %d\n", info.ssi_signo); @@ -91,7 +92,7 @@ public: } private: - bool* running = nullptr; + std::atomic& running; }; ///< Create periodic epoll timer every 1ms diff --git a/lib/include/srsran/common/interfaces_common.h b/lib/include/srsran/common/interfaces_common.h index 9e8f7498c..5ecc78fb4 100644 --- a/lib/include/srsran/common/interfaces_common.h +++ b/lib/include/srsran/common/interfaces_common.h @@ -86,7 +86,7 @@ public: class read_pdu_interface { public: - virtual int read_pdu(uint32_t lcid, uint8_t* payload, uint32_t requested_bytes) = 0; + virtual uint32_t read_pdu(uint32_t lcid, uint8_t* payload, uint32_t requested_bytes) = 0; }; class stack_interface_phy_nr diff --git a/lib/include/srsran/common/mac_pcap_base.h b/lib/include/srsran/common/mac_pcap_base.h index eacced47b..7771fbfb7 100644 --- a/lib/include/srsran/common/mac_pcap_base.h +++ b/lib/include/srsran/common/mac_pcap_base.h @@ -102,11 +102,11 @@ protected: virtual void write_pdu(pcap_pdu_t& pdu) = 0; void run_thread() final; - std::mutex mutex; - srslog::basic_logger& logger; - bool running = false; - static_blocking_queue queue; - uint16_t ue_id = 0; + std::mutex mutex; + srslog::basic_logger& logger; + std::atomic running = {false}; + static_blocking_queue queue; + uint16_t ue_id = 0; private: void pack_and_queue(uint8_t* payload, diff --git a/lib/include/srsran/common/multiqueue.h b/lib/include/srsran/common/multiqueue.h index aa91f91fa..26af347cb 100644 --- a/lib/include/srsran/common/multiqueue.h +++ b/lib/include/srsran/common/multiqueue.h @@ -74,7 +74,6 @@ class multiqueue_handler std::lock_guard lock(q_mutex); return active_; } - void set_active(bool val) { std::unique_lock lock(q_mutex); @@ -82,8 +81,7 @@ class multiqueue_handler // no-op return; } - active_ = val; - consumer_notify_needed = true; + active_ = val; if (not active_) { buffer.clear(); @@ -123,18 +121,14 @@ class multiqueue_handler bool try_pop(myobj& obj) { std::unique_lock lock(q_mutex); - if (buffer.empty()) { - consumer_notify_needed = true; - return false; - } - obj = std::move(buffer.top()); - buffer.pop(); - consumer_notify_needed = false; - if (nof_waiting > 0) { - lock.unlock(); - cv_full.notify_one(); - } - return true; + return pop_(lock, obj); + } + + bool try_pop(myobj& obj, bool& try_lock_success) + { + std::unique_lock lock(q_mutex, std::try_to_lock); + try_lock_success = lock.owns_lock(); + return try_lock_success ? pop_(lock, obj) : false; } private: @@ -161,13 +155,19 @@ class multiqueue_handler } } buffer.push(std::forward(*o)); - if (consumer_notify_needed) { - // Note: The consumer thread only needs to be notified and awaken when queues transition from empty to non-empty - // To ensure that the consumer noticed that the queue was empty before a push, we store the last - // try_pop() return in a member variable. - // Doing this reduces the contention of multiple producers for the same condition variable + return true; + } + + bool pop_(std::unique_lock& lock, myobj& obj) + { + if (buffer.empty()) { + return false; + } + obj = std::move(buffer.top()); + buffer.pop(); + if (nof_waiting > 0) { lock.unlock(); - parent->signal_pushed_data(); + cv_full.notify_one(); } return true; } @@ -177,9 +177,8 @@ class multiqueue_handler mutable std::mutex q_mutex; srsran::dyn_circular_buffer buffer; std::condition_variable cv_full, cv_exit; - bool active_ = true; - bool consumer_notify_needed = true; - int nof_waiting = 0; + bool active_ = true; + int nof_waiting = 0; }; public: @@ -235,9 +234,7 @@ public: // signal deactivation to pushing threads in a non-blocking way q.set_active(false); } - while (wait_state) { - pushed_data = true; - cv_empty.notify_one(); + while (consumer_state) { cv_exit.wait(lock); } for (auto& q : queues) { @@ -258,8 +255,9 @@ public: if (not running) { return queue_handle(); } - for (; qidx < queues.size() and (queues[qidx].active() or (queues[qidx].capacity() != capacity_)); ++qidx) - ; + while (qidx < queues.size() and (queues[qidx].active() or (queues[qidx].capacity() != capacity_))) { + ++qidx; + } // check if there is a free queue of the required size if (qidx == queues.size()) { @@ -291,17 +289,18 @@ public: bool wait_pop(myobj* value) { std::unique_lock lock(mutex); + consumer_state = true; while (running) { if (round_robin_pop_(value)) { + consumer_state = false; return true; } - pushed_data = false; - wait_state = true; - while (not pushed_data) { - cv_empty.wait(lock); - } - wait_state = false; + lock.unlock(); + std::this_thread::sleep_for(std::chrono::microseconds(100)); + lock.lock(); } + consumer_state = false; + lock.unlock(); cv_exit.notify_one(); return false; } @@ -316,36 +315,29 @@ private: bool round_robin_pop_(myobj* value) { // Round-robin for all queues - auto it = queues.begin() + spin_idx; + auto q_it = queues.begin() + spin_idx; uint32_t count = 0; - for (; count < queues.size(); ++count, ++it) { - if (it == queues.end()) { - it = queues.begin(); // wrap-around + for (; count < queues.size(); ++count, ++q_it) { + if (q_it == queues.end()) { + q_it = queues.begin(); // wrap-around } - if (it->try_pop(*value)) { + bool try_lock_success = true; + if (q_it->try_pop(*value, try_lock_success)) { spin_idx = (spin_idx + count + 1) % queues.size(); return true; } - } - return false; - } - /// Called by the producer threads to signal the consumer to unlock in wait_pop - void signal_pushed_data() - { - { - std::lock_guard lock(mutex); - if (pushed_data) { - return; + if (not try_lock_success) { + // restart RR search, as there was a collision with a producer + count = 0; } - pushed_data = true; } - cv_empty.notify_one(); + return false; } mutable std::mutex mutex; - std::condition_variable cv_empty, cv_exit; + std::condition_variable cv_exit; uint32_t spin_idx = 0; - bool running = true, pushed_data = false, wait_state = false; + bool running = true, consumer_state = false; std::deque queues; uint32_t default_capacity = 0; }; diff --git a/lib/include/srsran/common/srsran_assert.h b/lib/include/srsran/common/srsran_assert.h index 9139f87c4..e22587a6c 100644 --- a/lib/include/srsran/common/srsran_assert.h +++ b/lib/include/srsran/common/srsran_assert.h @@ -28,8 +28,8 @@ #define srsran_unlikely(expr) __builtin_expect(!!(expr), 0) #define srsran_terminate(fmt, ...) \ - std::fprintf(stderr, "%s:%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); \ srslog::flush(); \ + std::fprintf(stderr, "%s:%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); \ std::abort() #ifdef ASSERTS_ENABLED diff --git a/lib/include/srsran/common/thread_pool.h b/lib/include/srsran/common/thread_pool.h index 7eb131623..a16a2af47 100644 --- a/lib/include/srsran/common/thread_pool.h +++ b/lib/include/srsran/common/thread_pool.h @@ -32,6 +32,7 @@ #include "srsran/adt/circular_buffer.h" #include "srsran/adt/move_callback.h" #include "srsran/srslog/srslog.h" +#include #include #include #include @@ -54,6 +55,7 @@ public: worker(); ~worker() = default; void setup(uint32_t id, thread_pool* parent, uint32_t prio = 0, uint32_t mask = 255); + void stop(); uint32_t get_id(); void release(); @@ -63,6 +65,7 @@ public: private: uint32_t my_id = 0; thread_pool* my_parent = nullptr; + std::atomic running = {true}; void run_thread(); void wait_to_start(); diff --git a/lib/include/srsran/common/threads.h b/lib/include/srsran/common/threads.h index c2f644b46..28c938054 100644 --- a/lib/include/srsran/common/threads.h +++ b/lib/include/srsran/common/threads.h @@ -44,6 +44,7 @@ void threads_print_self(); #ifdef __cplusplus } +#include #include namespace srsran { @@ -138,10 +139,10 @@ protected: virtual void run_period() = 0; private: - int wakeups_missed; - int timer_fd; - int period_us; - bool run_enable; + int wakeups_missed = 0; + int timer_fd = 0; + int period_us = 0; + std::atomic run_enable = {false}; void run_thread() { diff --git a/lib/include/srsran/common/timers.h b/lib/include/srsran/common/timers.h index 600478164..9a363aafe 100644 --- a/lib/include/srsran/common/timers.h +++ b/lib/include/srsran/common/timers.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -46,8 +47,9 @@ public: }; /** - * Class that manages stack timers. It allows creation of unique_timers, with different ids. Each unique_timer duration, + * Class that manages stack timers. It allows creation of unique_timers with different ids. Each unique_timer duration, * and callback can be set via the set(...) method. A timer can be started/stopped via run()/stop() methods. + * The timers access/alteration is thread-safe. Just beware non-atomic uses of its getters. * Internal Data structures: * - timer_list - std::deque that stores timer objects via push_back() to keep pointer/reference validity. * The timer index in the timer_list matches the timer object id field. @@ -62,20 +64,34 @@ public: */ class timer_handler { - using tic_diff_t = uint32_t; - using tic_t = uint32_t; - constexpr static uint32_t INVALID_ID = std::numeric_limits::max(); - constexpr static tic_diff_t INVALID_TIME_DIFF = std::numeric_limits::max(); - constexpr static size_t WHEEL_SHIFT = 16U; - constexpr static size_t WHEEL_SIZE = 1U << WHEEL_SHIFT; - constexpr static size_t WHEEL_MASK = WHEEL_SIZE - 1U; + using tic_diff_t = uint32_t; + using tic_t = uint32_t; + constexpr static uint32_t INVALID_ID = std::numeric_limits::max(); + constexpr static size_t WHEEL_SHIFT = 16U; + constexpr static size_t WHEEL_SIZE = 1U << WHEEL_SHIFT; + constexpr static size_t WHEEL_MASK = WHEEL_SIZE - 1U; + + constexpr static uint64_t STOPPED_FLAG = 0U; + constexpr static uint64_t RUNNING_FLAG = static_cast(1U) << 63U; + constexpr static uint64_t EXPIRED_FLAG = static_cast(1U) << 62U; + constexpr static tic_diff_t MAX_TIMER_DURATION = 0x3FFFFFFFU; + + static bool decode_is_running(uint64_t value) { return (value & RUNNING_FLAG) != 0; } + static bool decode_is_expired(uint64_t value) { return (value & EXPIRED_FLAG) != 0; } + static tic_diff_t decode_duration(uint64_t value) { return (value >> 32U) & MAX_TIMER_DURATION; } + static tic_t decode_timeout(uint64_t value) { return static_cast(value & 0xFFFFFFFFU); } + static uint64_t encode_state(uint64_t mode_flag, uint32_t duration, uint32_t timeout) + { + return mode_flag + (static_cast(duration) << 32U) + timeout; + } struct timer_impl : public intrusive_double_linked_list_element<>, public intrusive_forward_list_element<> { - timer_handler& parent; + // const const uint32_t id; - tic_diff_t duration = INVALID_TIME_DIFF; - tic_t timeout = 0; - enum state_t : int8_t { empty, stopped, running, expired } state = empty; + timer_handler& parent; + // writes protected by backend lock + bool allocated = false; + std::atomic state{0}; ///< read can be without lock, thus writes must be atomic srsran::move_callback callback; explicit timer_impl(timer_handler& parent_, uint32_t id_) : parent(parent_), id(id_) {} @@ -84,32 +100,38 @@ class timer_handler timer_impl& operator=(const timer_impl&) = delete; timer_impl& operator=(timer_impl&&) = delete; - bool is_empty() const { return state == empty; } - bool is_running() const { return state == running; } - bool is_expired() const { return state == expired; } - tic_diff_t time_left() const { return is_running() ? timeout - parent.cur_time : (is_expired() ? 0 : duration); } - uint32_t time_elapsed() const { return duration - time_left(); } + // unprotected + bool is_running_() const { return decode_is_running(state.load(std::memory_order_relaxed)); } + bool is_expired_() const { return decode_is_expired(state.load(std::memory_order_relaxed)); } + uint32_t duration_() const { return decode_duration(state.load(std::memory_order_relaxed)); } + bool is_set_() const { return duration_() > 0; } + tic_diff_t time_elapsed_() const + { + uint64_t state_snapshot = state.load(std::memory_order_relaxed); + bool running = decode_is_running(state_snapshot), expired = decode_is_expired(state_snapshot); + uint32_t duration = decode_duration(state_snapshot), timeout = decode_timeout(state_snapshot); + return running ? duration - (timeout - parent.cur_time) : (expired ? duration : 0); + } - bool set(uint32_t duration_) + void set(uint32_t duration_) { - duration = std::max(duration_, 1U); // the next step will be one place ahead of current one - if (is_running()) { - // if already running, just extends timer lifetime - run(); - } else { - state = stopped; - timeout = 0; - } - return true; + srsran_assert(duration_ <= MAX_TIMER_DURATION, + "Invalid timer duration=%" PRIu32 ">%" PRIu32, + duration_, + MAX_TIMER_DURATION); + std::lock_guard lock(parent.mutex); + set_(duration_); } - bool set(uint32_t duration_, srsran::move_callback callback_) + void set(uint32_t duration_, srsran::move_callback callback_) { - if (set(duration_)) { - callback = std::move(callback_); - return true; - } - return false; + srsran_assert(duration_ <= MAX_TIMER_DURATION, + "Invalid timer duration=%" PRIu32 ">%" PRIu32, + duration_, + MAX_TIMER_DURATION); + std::lock_guard lock(parent.mutex); + set_(duration_); + callback = std::move(callback_); } void run() @@ -125,7 +147,25 @@ class timer_handler parent.stop_timer_(*this, false); } - void deallocate() { parent.dealloc_timer(*this); } + void deallocate() + { + std::lock_guard lock(parent.mutex); + parent.dealloc_timer_(*this); + } + + private: + void set_(uint32_t duration_) + { + duration_ = std::max(duration_, 1U); // the next step will be one place ahead of current one + // called in locked context + uint64_t old_state = state.load(std::memory_order_relaxed); + if (decode_is_running(old_state)) { + // if already running, just extends timer lifetime + parent.start_run_(*this, duration_); + } else { + state.store(encode_state(STOPPED_FLAG, duration_, 0), std::memory_order_relaxed); + } + } }; public: @@ -160,17 +200,12 @@ public: handle->set(duration_); } - bool is_set() const { return is_valid() and handle->duration != INVALID_TIME_DIFF; } - - bool is_running() const { return is_valid() and handle->is_running(); } - - bool is_expired() const { return is_valid() and handle->is_expired(); } - - tic_diff_t time_elapsed() const { return is_valid() ? handle->time_elapsed() : INVALID_TIME_DIFF; } - - uint32_t id() const { return is_valid() ? handle->id : INVALID_ID; } - - tic_diff_t duration() const { return is_valid() ? handle->duration : INVALID_TIME_DIFF; } + uint32_t id() const { return is_valid() ? handle->id : INVALID_ID; } + bool is_set() const { return is_valid() and handle->is_set_(); } + bool is_running() const { return is_valid() and handle->is_running_(); } + bool is_expired() const { return is_valid() and handle->is_expired_(); } + tic_diff_t time_elapsed() const { return is_valid() ? handle->time_elapsed_() : 0; } + tic_diff_t duration() const { return is_valid() ? handle->duration_() : 0; } void run() { @@ -214,13 +249,13 @@ public: void step_all() { std::unique_lock lock(mutex); - cur_time++; - auto& wheel_list = time_wheel[cur_time & WHEEL_MASK]; + uint32_t cur_time_local = cur_time.load(std::memory_order_relaxed) + 1; + auto& wheel_list = time_wheel[cur_time_local & WHEEL_MASK]; for (auto it = wheel_list.begin(); it != wheel_list.end();) { timer_impl& timer = timer_list[it->id]; ++it; - if (timer.timeout == cur_time) { + if (decode_timeout(timer.state.load(std::memory_order_relaxed)) == cur_time_local) { // stop timer (callback has to see the timer has already expired) stop_timer_(timer, true); @@ -236,6 +271,8 @@ public: } } } + + cur_time.fetch_add(1, std::memory_order_relaxed); } void stop_all() @@ -261,6 +298,8 @@ public: return nof_timers_running_; } + constexpr static uint32_t max_timer_duration() { return MAX_TIMER_DURATION; } + template void defer_callback(uint32_t duration, const F& func) { @@ -284,7 +323,7 @@ private: timer_impl* t; if (not free_list.empty()) { t = &free_list.front(); - srsran_assert(t->is_empty(), "Invalid timer id=%d state", t->id); + srsran_assert(not t->allocated, "Invalid timer id=%d state", t->id); free_list.pop_front(); nof_free_timers--; } else { @@ -292,63 +331,71 @@ private: timer_list.emplace_back(*this, timer_list.size()); t = &timer_list.back(); } - t->state = timer_impl::stopped; + t->allocated = true; return *t; } - void dealloc_timer(timer_impl& timer) + void dealloc_timer_(timer_impl& timer) { - std::lock_guard lock(mutex); - if (timer.is_empty()) { + if (not timer.allocated) { // already deallocated return; } stop_timer_(timer, false); - timer.state = timer_impl::empty; - timer.duration = INVALID_TIME_DIFF; - timer.timeout = 0; + timer.allocated = false; + timer.state.store(encode_state(STOPPED_FLAG, 0, 0), std::memory_order_relaxed); timer.callback = srsran::move_callback(); free_list.push_front(&timer); nof_free_timers++; // leave id unchanged. } - void start_run_(timer_impl& timer) + void start_run_(timer_impl& timer, uint32_t duration_ = 0) { - uint32_t timeout = cur_time + timer.duration; - size_t new_wheel_pos = timeout & WHEEL_MASK; - if (timer.is_running() and (timer.timeout & WHEEL_MASK) == new_wheel_pos) { + uint64_t timer_old_state = timer.state.load(std::memory_order_relaxed); + duration_ = duration_ == 0 ? decode_duration(timer_old_state) : duration_; + uint32_t new_timeout = cur_time.load(std::memory_order_relaxed) + duration_; + size_t new_wheel_pos = new_timeout & WHEEL_MASK; + + uint32_t old_timeout = decode_timeout(timer_old_state); + bool was_running = decode_is_running(timer_old_state); + if (was_running and (old_timeout & WHEEL_MASK) == new_wheel_pos) { // If no change in timer wheel position. Just update absolute timeout - timer.timeout = timeout; + timer.state.store(encode_state(RUNNING_FLAG, duration_, new_timeout), std::memory_order_relaxed); return; } // Stop timer if it was running, removing it from wheel in the process - stop_timer_(timer, false); + if (was_running) { + time_wheel[old_timeout & WHEEL_MASK].pop(&timer); + nof_timers_running_--; + } // Insert timer in wheel time_wheel[new_wheel_pos].push_front(&timer); - timer.timeout = timeout; - timer.state = timer_impl::running; + timer.state.store(encode_state(RUNNING_FLAG, duration_, new_timeout), std::memory_order_relaxed); nof_timers_running_++; } /// called when user manually stops timer (as an alternative to expiry) void stop_timer_(timer_impl& timer, bool expiry) { - if (not timer.is_running()) { + uint64_t timer_old_state = timer.state.load(std::memory_order_relaxed); + if (not decode_is_running(timer_old_state)) { return; } // If already running, need to disconnect it from previous wheel - time_wheel[timer.timeout & WHEEL_MASK].pop(&timer); - - timer.state = expiry ? timer_impl::expired : timer_impl::stopped; + uint32_t old_timeout = decode_timeout(timer_old_state); + time_wheel[old_timeout & WHEEL_MASK].pop(&timer); + uint64_t new_state = + encode_state(expiry ? EXPIRED_FLAG : STOPPED_FLAG, decode_duration(timer_old_state), old_timeout); + timer.state.store(new_state, std::memory_order_relaxed); nof_timers_running_--; } - tic_t cur_time = 0; - size_t nof_timers_running_ = 0, nof_free_timers = 0; + std::atomic cur_time{0}; + size_t nof_timers_running_ = 0, nof_free_timers = 0; // using a deque to maintain reference validity on emplace_back. Also, this deque will only grow. std::deque timer_list; srsran::intrusive_forward_list free_list; diff --git a/lib/include/srsran/common/tsan_options.h b/lib/include/srsran/common/tsan_options.h new file mode 100644 index 000000000..96a4f2544 --- /dev/null +++ b/lib/include/srsran/common/tsan_options.h @@ -0,0 +1,49 @@ +/** + * + * \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_TSAN_OPTIONS_H +#define SRSRAN_TSAN_OPTIONS_H + +// Options taken from Mozilla project +// abort_on_error=1 - Causes TSan to abort instead of using exit(). +// halt_on_error=1 - Causes TSan to stop on the first race detected. +// +// report_signal_unsafe=0 - Required to avoid TSan deadlocks when +// receiving external signals (e.g. SIGINT manually on console). +// +// allocator_may_return_null=1 - Tell TSan to return NULL when an allocation +// fails instead of aborting the program. This allows us to handle failing +// allocations the same way we would handle them with a regular allocator and +// also uncovers potential bugs that might occur in these situations. + +#ifdef __cplusplus +extern "C" { +#endif + +const char* __tsan_default_options() +{ + return "halt_on_error=1:abort_on_error=1:report_signal_unsafe=0" + ":allocator_may_return_null=1"; +} + +const char* __tsan_default_suppressions() +{ + // External uninstrumented libraries + return "called_from_lib:libzmq.so\n" + "called_from_lib:libpgm-5.2.so\n"; +} + +#ifdef __cplusplus +} +#endif + +#endif // SRSRAN_TSAN_OPTIONS_H diff --git a/lib/include/srsran/interfaces/enb_mac_interfaces.h b/lib/include/srsran/interfaces/enb_mac_interfaces.h index b8d0b783d..f2ee096d7 100644 --- a/lib/include/srsran/interfaces/enb_mac_interfaces.h +++ b/lib/include/srsran/interfaces/enb_mac_interfaces.h @@ -192,9 +192,15 @@ public: * @param enb_cc_idx the eNb Cell/Carrier identifier * @param nof_bytes the number of grants carrierd by the PUSCH message * @param crc_res the CRC check, set to true if the message was decoded succesfully + * @param ul_nof_prbs Number of PRBs allocated to grant * @return SRSRAN_SUCCESS if no error occurs, SRSRAN_ERROR* if an error occurs */ - virtual int push_pdu(uint32_t tti_rx, uint16_t rnti, uint32_t enb_cc_idx, uint32_t nof_bytes, bool crc_res) = 0; + virtual int push_pdu(uint32_t tti_rx, + uint16_t rnti, + uint32_t enb_cc_idx, + uint32_t nof_bytes, + bool crc_res, + uint32_t ul_nof_prbs) = 0; virtual int get_dl_sched(uint32_t tti, dl_sched_list_t& dl_sched_res) = 0; virtual int get_mch_sched(uint32_t tti, bool is_mcch, dl_sched_list_t& dl_sched_res) = 0; @@ -212,8 +218,7 @@ class mac_interface_rrc { public: /* Provides cell configuration including SIB periodicity, etc. */ - virtual int cell_cfg(const std::vector& cell_cfg) = 0; - virtual void reset() = 0; + virtual int cell_cfg(const std::vector& cell_cfg) = 0; /* Manages UE configuration context */ virtual int ue_cfg(uint16_t rnti, sched_interface::ue_cfg_t* cfg) = 0; diff --git a/lib/include/srsran/interfaces/enb_rrc_interface_types.h b/lib/include/srsran/interfaces/enb_rrc_interface_types.h index 28ed19024..e9aae1079 100644 --- a/lib/include/srsran/interfaces/enb_rrc_interface_types.h +++ b/lib/include/srsran/interfaces/enb_rrc_interface_types.h @@ -51,6 +51,7 @@ struct rrc_meas_cfg_t { std::vector meas_reports; asn1::rrc::quant_cfg_eutra_s quant_cfg; uint32_t meas_gap_period; + std::vector meas_gap_offset_subframe; uint32_t allowed_meas_bw; }; @@ -69,6 +70,7 @@ struct cell_cfg_t { int target_pusch_sinr_db; uint32_t initial_dl_cqi; bool enable_phr_handling; + int min_phr_thres; asn1::rrc::mob_ctrl_info_s::t304_e_ t304; std::vector scell_list; rrc_meas_cfg_t meas_cfg; diff --git a/lib/include/srsran/interfaces/enb_rrc_interfaces.h b/lib/include/srsran/interfaces/enb_rrc_interfaces.h index 21cd61ce9..e050c6768 100644 --- a/lib/include/srsran/interfaces/enb_rrc_interfaces.h +++ b/lib/include/srsran/interfaces/enb_rrc_interfaces.h @@ -100,6 +100,7 @@ class rrc_interface_rlc { public: virtual void max_retx_attempted(uint16_t rnti) = 0; + virtual void protocol_failure(uint16_t rnti) = 0; virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu) = 0; }; diff --git a/lib/include/srsran/interfaces/enb_s1ap_interfaces.h b/lib/include/srsran/interfaces/enb_s1ap_interfaces.h index f911ffe7b..1ff458b2c 100644 --- a/lib/include/srsran/interfaces/enb_s1ap_interfaces.h +++ b/lib/include/srsran/interfaces/enb_s1ap_interfaces.h @@ -68,8 +68,9 @@ public: virtual bool user_release(uint16_t rnti, asn1::s1ap::cause_radio_network_e cause_radio) = 0; virtual bool is_mme_connected() = 0; - /// TS 36.413, 8.3.1 - Initial Context Setup - virtual void ue_ctxt_setup_complete(uint16_t rnti) = 0; + // Notify S1AP of RRC reconfiguration successful finish. + // Many S1AP procedures use this notification to indicate successful end (e.g InitialContextSetupRequest) + virtual void notify_rrc_reconf_complete(uint16_t rnti) = 0; /** * Command the s1ap to transmit a HandoverRequired message to MME. diff --git a/lib/include/srsran/interfaces/gnb_interfaces.h b/lib/include/srsran/interfaces/gnb_interfaces.h index 8f03e69b9..3a9494590 100644 --- a/lib/include/srsran/interfaces/gnb_interfaces.h +++ b/lib/include/srsran/interfaces/gnb_interfaces.h @@ -160,6 +160,7 @@ class rrc_interface_rlc_nr public: virtual void read_pdu_pcch(uint8_t* payload, uint32_t payload_size) = 0; virtual void max_retx_attempted(uint16_t rnti) = 0; + virtual void protocol_failure(uint16_t rnti) = 0; virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu) = 0; virtual const char* get_rb_name(uint32_t lcid) = 0; }; diff --git a/lib/include/srsran/interfaces/rrc_nr_interface_types.h b/lib/include/srsran/interfaces/rrc_nr_interface_types.h index c39558b57..f646031cc 100644 --- a/lib/include/srsran/interfaces/rrc_nr_interface_types.h +++ b/lib/include/srsran/interfaces/rrc_nr_interface_types.h @@ -38,9 +38,9 @@ struct phy_cfg_nr_t { * SSB configuration */ struct ssb_cfg_t { - uint32_t periodicity_ms; - std::array position_in_burst; - srsran_subcarrier_spacing_t scs; + uint32_t periodicity_ms; + std::array position_in_burst; + srsran_subcarrier_spacing_t scs; }; srsran_tdd_config_nr_t tdd = {}; diff --git a/lib/include/srsran/interfaces/sched_interface.h b/lib/include/srsran/interfaces/sched_interface.h index 90ab7577e..8dd5306b1 100644 --- a/lib/include/srsran/interfaces/sched_interface.h +++ b/lib/include/srsran/interfaces/sched_interface.h @@ -54,16 +54,25 @@ public: } cell_cfg_sib_t; struct sched_args_t { - std::string sched_policy = "time_pf"; - std::string sched_policy_args = "2"; - int pdsch_mcs = -1; - int pdsch_max_mcs = 28; - int pusch_mcs = -1; - int pusch_max_mcs = 28; - uint32_t min_nof_ctrl_symbols = 1; - uint32_t max_nof_ctrl_symbols = 3; - int max_aggr_level = 3; - bool pucch_mux_enabled = false; + std::string sched_policy = "time_pf"; + std::string sched_policy_args = "2"; + int pdsch_mcs = -1; + int pdsch_max_mcs = 28; + int pusch_mcs = -1; + int pusch_max_mcs = 28; + uint32_t min_nof_ctrl_symbols = 1; + uint32_t max_nof_ctrl_symbols = 3; + int min_aggr_level = 0; + int max_aggr_level = 3; + bool adaptive_aggr_level = true; + bool pucch_mux_enabled = false; + float target_bler = 0.05; + float max_delta_dl_cqi = 5; + float max_delta_ul_snr = 5; + float adaptive_link_step_size = 0.001; + uint32_t min_tpc_tti_interval = 1; + float ul_snr_avg_alpha = 0.05; + int init_ul_snr_value = 5; }; struct cell_cfg_t { @@ -80,6 +89,7 @@ public: /* pusch configuration */ srsran_pusch_hopping_cfg_t pusch_hopping_cfg; float target_pusch_ul_sinr; + int min_phr_thres; bool enable_phr_handling; bool enable_64qam; @@ -295,7 +305,7 @@ public: virtual int ul_crc_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, bool crc) = 0; virtual int ul_sr_info(uint32_t tti, uint16_t rnti) = 0; virtual int ul_bsr(uint16_t rnti, uint32_t lcg_id, uint32_t bsr) = 0; - virtual int ul_phr(uint16_t rnti, int phr) = 0; + virtual int ul_phr(uint16_t rnti, int phr, uint32_t ul_nof_prb) = 0; virtual int ul_snr_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, float snr, uint32_t ul_ch_code) = 0; /* Run Scheduler for this tti */ diff --git a/lib/include/srsran/interfaces/ue_phy_interfaces.h b/lib/include/srsran/interfaces/ue_phy_interfaces.h index efd5b13d1..4a5fcdba3 100644 --- a/lib/include/srsran/interfaces/ue_phy_interfaces.h +++ b/lib/include/srsran/interfaces/ue_phy_interfaces.h @@ -168,8 +168,6 @@ public: virtual bool cell_search() = 0; virtual bool cell_select(phy_cell_t cell) = 0; virtual bool cell_is_camping() = 0; - - virtual void enable_pregen_signals(bool enable) = 0; }; // Combined interface for stack (MAC and RRC) to access PHY diff --git a/lib/include/srsran/interfaces/ue_rlc_interfaces.h b/lib/include/srsran/interfaces/ue_rlc_interfaces.h index 42afff8bf..acd1007c4 100644 --- a/lib/include/srsran/interfaces/ue_rlc_interfaces.h +++ b/lib/include/srsran/interfaces/ue_rlc_interfaces.h @@ -76,7 +76,7 @@ public: /* MAC calls RLC to get RLC segment of nof_bytes length. * Segmentation happens in this function. RLC PDU is stored in payload. */ - virtual int read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) = 0; + virtual uint32_t read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) = 0; /* MAC calls RLC to push an RLC PDU. This function is called from an independent MAC thread. * PDU gets placed into the buffer and higher layer thread gets notified. */ diff --git a/lib/include/srsran/interfaces/ue_rrc_interfaces.h b/lib/include/srsran/interfaces/ue_rrc_interfaces.h index d2923c2b8..6cf7c303d 100644 --- a/lib/include/srsran/interfaces/ue_rrc_interfaces.h +++ b/lib/include/srsran/interfaces/ue_rrc_interfaces.h @@ -100,6 +100,7 @@ class rrc_interface_rlc { public: virtual void max_retx_attempted() = 0; + virtual void protocol_failure() = 0; virtual const char* get_rb_name(uint32_t lcid) = 0; virtual void write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0; }; diff --git a/lib/include/srsran/mac/pdu_queue.h b/lib/include/srsran/mac/pdu_queue.h index 09e9b2c84..add1a39cd 100644 --- a/lib/include/srsran/mac/pdu_queue.h +++ b/lib/include/srsran/mac/pdu_queue.h @@ -39,7 +39,7 @@ public: class process_callback { public: - virtual void process_pdu(uint8_t* buff, uint32_t len, channel_t channel) = 0; + virtual void process_pdu(uint8_t* buff, uint32_t len, channel_t channel, int ul_nof_prbs = -1) = 0; }; pdu_queue(srslog::basic_logger& logger) : pool(DEFAULT_POOL_SIZE), callback(NULL), logger(logger) {} @@ -47,7 +47,7 @@ public: uint8_t* request(uint32_t len); void deallocate(const uint8_t* pdu); - void push(const uint8_t* ptr, uint32_t len, channel_t channel = DCH); + void push(const uint8_t* ptr, uint32_t len, channel_t channel = DCH, int ul_nof_prbs = -1); bool process_pdus(); @@ -61,6 +61,7 @@ private: uint8_t ptr[MAX_PDU_LEN]; uint32_t len; channel_t channel; + int grant_nof_prbs; #ifdef SRSRAN_BUFFER_POOL_LOG_ENABLED char debug_name[128]; #endif diff --git a/lib/include/srsran/phy/ch_estimation/dmrs_pbch.h b/lib/include/srsran/phy/ch_estimation/dmrs_pbch.h new file mode 100644 index 000000000..588c11787 --- /dev/null +++ b/lib/include/srsran/phy/ch_estimation/dmrs_pbch.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_DMRS_PBCH_H +#define SRSRAN_DMRS_PBCH_H + +#include "srsran/phy/common/phy_common_nr.h" + +/** + * @brief Describes the DeModulation Reference Signals (DMRS) for NR PBCH configuration + */ +typedef struct SRSRAN_API { + uint32_t N_id; ///< Physical cell identifier + uint32_t n_hf; ///< Number of half radio frame, 0 or 1 + uint32_t ssb_idx; ///< SSB candidate index + uint32_t L_max; ///< Number of SSB opportunities in half radio frame + float beta; ///< Power allocation specified in TS 38.213 + srsran_subcarrier_spacing_t scs; ///< SSB configured subcarrier spacing +} srsran_dmrs_pbch_cfg_t; + +/** + * @brief Describes an NR PBCH DMRS based measurement + */ +typedef struct SRSRAN_API { + float corr; ///< Normalised correlation + float epre; ///< Linear energy per resource element + float rsrp; ///< Linear RSRP + float cfo_hz; ///< CFO in Hz + float avg_delay_us; ///< Average delay in us +} srsran_dmrs_pbch_meas_t; + +/** + * @brief Put NR PBCH DMRS in the SSB resource grid + * @param cfg PBCH DMRS configuration + * @param[out] ssb_grid SSB resource grid + * @return SRSRAN_SUCCESS if the inputs and configuration are valid, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_dmrs_pbch_put(const srsran_dmrs_pbch_cfg_t* cfg, cf_t ssb_grid[SRSRAN_SSB_NOF_RE]); + +/** + * @brief Estimates NR PBCH DMRS + * @param cfg PBCH DMRS configuration + * @param ssb_grid Demodulated SSB resource grid + * @param[out] ce Estimated channel + * @param[out] meas Estimated channel measurements + * @return SRSRAN_SUCCESS if the inputs and configuration are valid, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_dmrs_pbch_estimate(const srsran_dmrs_pbch_cfg_t* cfg, + const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], + cf_t ce[SRSRAN_SSB_NOF_RE], + srsran_dmrs_pbch_meas_t* meas); + +#endif // SRSRAN_DMRS_PBCH_H diff --git a/lib/include/srsran/phy/common/sequence.h b/lib/include/srsran/phy/common/sequence.h index ee5770e41..3870a1390 100644 --- a/lib/include/srsran/phy/common/sequence.h +++ b/lib/include/srsran/phy/common/sequence.h @@ -47,6 +47,12 @@ SRSRAN_API void srsran_sequence_state_gen_f(srsran_sequence_state_t* s, float va SRSRAN_API void srsran_sequence_state_apply_f(srsran_sequence_state_t* s, const float* in, float* out, uint32_t length); +SRSRAN_API void +srsran_sequence_state_apply_c(srsran_sequence_state_t* s, const int8_t* in, int8_t* out, uint32_t length); + +SRSRAN_API +void srsran_sequence_state_apply_bit(srsran_sequence_state_t* s, const uint8_t* in, uint8_t* out, uint32_t length); + SRSRAN_API void srsran_sequence_state_advance(srsran_sequence_state_t* s, uint32_t length); typedef struct SRSRAN_API { diff --git a/lib/include/srsran/phy/enb/enb_dl.h b/lib/include/srsran/phy/enb/enb_dl.h index 2401faf3c..6e79e6e38 100644 --- a/lib/include/srsran/phy/enb/enb_dl.h +++ b/lib/include/srsran/phy/enb/enb_dl.h @@ -102,7 +102,7 @@ SRSRAN_API void srsran_enb_dl_free(srsran_enb_dl_t* q); SRSRAN_API int srsran_enb_dl_set_cell(srsran_enb_dl_t* q, srsran_cell_t cell); -SRSRAN_API bool srsran_enb_dl_location_is_common_ncce(srsran_enb_dl_t* q, uint32_t ncce); +SRSRAN_API bool srsran_enb_dl_location_is_common_ncce(srsran_enb_dl_t* q, const srsran_dci_location_t* loc); SRSRAN_API void srsran_enb_dl_put_base(srsran_enb_dl_t* q, srsran_dl_sf_cfg_t* dl_sf); diff --git a/lib/include/srsran/phy/mimo/precoding.h b/lib/include/srsran/phy/mimo/precoding.h index a00ead41d..5168407de 100644 --- a/lib/include/srsran/phy/mimo/precoding.h +++ b/lib/include/srsran/phy/mimo/precoding.h @@ -89,7 +89,7 @@ SRSRAN_API int srsran_predecoding_diversity(cf_t* y, SRSRAN_API int srsran_predecoding_diversity_multi(cf_t* y[SRSRAN_MAX_PORTS], cf_t* h[SRSRAN_MAX_PORTS][SRSRAN_MAX_PORTS], cf_t* x[SRSRAN_MAX_LAYERS], - float* csi[SRSRAN_MAX_LAYERS], + float* csi[SRSRAN_MAX_CODEWORDS], int nof_rxant, int nof_ports, int nof_symbols, diff --git a/lib/include/srsran/phy/phch/cqi.h b/lib/include/srsran/phy/phch/cqi.h index 1040fb398..4b00070a6 100644 --- a/lib/include/srsran/phy/phch/cqi.h +++ b/lib/include/srsran/phy/phch/cqi.h @@ -144,7 +144,8 @@ typedef struct { SRSRAN_API int srsran_cqi_size(srsran_cqi_cfg_t* cfg); -SRSRAN_API int srsran_cqi_value_pack(srsran_cqi_cfg_t* cfg, srsran_cqi_value_t* value, uint8_t* buff); +SRSRAN_API int +srsran_cqi_value_pack(srsran_cqi_cfg_t* cfg, srsran_cqi_value_t* value, uint8_t buff[SRSRAN_CQI_MAX_BITS]); SRSRAN_API int srsran_cqi_value_unpack(srsran_cqi_cfg_t* cfg, uint8_t buff[SRSRAN_CQI_MAX_BITS], srsran_cqi_value_t* value); diff --git a/lib/include/srsran/phy/phch/dci.h b/lib/include/srsran/phy/phch/dci.h index 1763224fc..64f8e50ca 100644 --- a/lib/include/srsran/phy/phch/dci.h +++ b/lib/include/srsran/phy/phch/dci.h @@ -238,8 +238,9 @@ SRSRAN_API char* srsran_dci_format_string_short(srsran_dci_format_t format); SRSRAN_API bool srsran_location_find(const srsran_dci_location_t* locations, uint32_t nof_locations, srsran_dci_location_t x); -SRSRAN_API bool -srsran_location_find_ncce(const srsran_dci_location_t* locations, uint32_t nof_locations, uint32_t ncce); +SRSRAN_API bool srsran_location_find_location(const srsran_dci_location_t* locations, + uint32_t nof_locations, + const srsran_dci_location_t* location); SRSRAN_API int srsran_dci_location_set(srsran_dci_location_t* c, uint32_t L, uint32_t nCCE); diff --git a/lib/include/srsran/phy/phch/pbch_nr.h b/lib/include/srsran/phy/phch/pbch_nr.h index 751aa60ef..8fb5e88e9 100644 --- a/lib/include/srsran/phy/phch/pbch_nr.h +++ b/lib/include/srsran/phy/phch/pbch_nr.h @@ -22,13 +22,108 @@ #ifndef SRSRAN_PBCH_NR_H #define SRSRAN_PBCH_NR_H -#include "srsran/config.h" +#include "srsran/phy/common/phy_common_nr.h" +#include "srsran/phy/fec/crc.h" +#include "srsran/phy/fec/polar/polar_code.h" +#include "srsran/phy/fec/polar/polar_decoder.h" +#include "srsran/phy/fec/polar/polar_encoder.h" +#include "srsran/phy/fec/polar/polar_rm.h" +#include "srsran/phy/modem/modem_table.h" /** - * @brief Descibes the NR PBCH message + * @brief NR PBCH payload size generated by higher layers, deduced from TS 38.331 MIB description + */ +#define SRSRAN_PBCH_NR_PAYLOAD_SZ 24 + +/** + * @brief Describes the NR PBCH object initialisation arguments + */ +typedef struct SRSRAN_API { + bool enable_encode; ///< Enable encoder + bool enable_decode; ///< Enable decoder + bool disable_simd; ///< Disable SIMD polar encoder/decoder +} srsran_pbch_nr_args_t; + +/** + * @brief Describes the NR PBCH configuration + */ +typedef struct SRSRAN_API { + uint32_t N_id; ///< Physical cell identifier + srsran_subcarrier_spacing_t ssb_scs; ///< SSB Subcarrier spacing + uint32_t Lmax; ///< Number of SSB opportunities, described in TS 38.213 4.1 ... + float beta; ///< Scaling factor for PBCH symbols, set to zero for default + float beta_dmrs; ///< Scaling factor for PBCH DM-RS, set to zero for default +} srsran_pbch_nr_cfg_t; + +/** + * @brief Describes the NR PBCH object initialisation arguments + */ +typedef struct SRSRAN_API { + srsran_polar_code_t code; + srsran_polar_encoder_t polar_encoder; + srsran_polar_decoder_t polar_decoder; + srsran_polar_rm_t polar_rm_tx; + srsran_polar_rm_t polar_rm_rx; + srsran_crc_t crc; + srsran_modem_table_t qpsk; +} srsran_pbch_nr_t; + +/** + * @brief Describes the PBCH message */ typedef struct SRSRAN_API { - void* TBD; + uint8_t payload[SRSRAN_PBCH_NR_PAYLOAD_SZ]; ///< Actual PBCH payload provided by higher layers + uint8_t sfn_4lsb; ///< SFN 4 LSB + uint8_t ssb_idx; ///< SS/PBCH blocks index described in TS 38.213 4.1 + uint8_t k_ssb_msb; ///< Subcarrier offset MSB described in TS 38.211 7.4.3.1 + bool hrf; ///< Half Radio Frame bit + bool crc; ///< Decoder only, it is true only if the received CRC matches } srsran_pbch_msg_nr_t; +/** + * @brief Initialises an NR PBCH object with the provided arguments + * @param q NR PBCH object + * @param args Arguments providing the desired configuration + * @return SRSRAN_SUCCESS if initialization is successful, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_pbch_nr_init(srsran_pbch_nr_t* q, const srsran_pbch_nr_args_t* args); + +/** + * @brief Deallocates an NR PBCH object + * @param q NR PBCH object + */ +SRSRAN_API void srsran_pbch_nr_free(srsran_pbch_nr_t* q); + +/** + * @brief Encodes an NR PBCH message into a SSB resource grid + * @param q NR PBCH object + * @param cfg NR PBCH configuration + * @param msg NR PBCH message to transmit + * @param[out] ssb_grid SSB resource grid + * @return SRSRAN_SUCCESS if encoding is successful, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_pbch_nr_encode(srsran_pbch_nr_t* q, + const srsran_pbch_nr_cfg_t* cfg, + const srsran_pbch_msg_nr_t* msg, + cf_t ssb_grid[SRSRAN_SSB_NOF_RE]); + +/** + * @brief Decodes an NR PBCH message in the SSB resource grid + * @param q NR PBCH object + * @param cfg NR PBCH configuration + * @param ssb_idx SSB candidate index + * @param[in] ssb_grid SSB resource grid + * @param[in] ce Channel estimates for the SSB resource grid + * @param msg NR PBCH message received + * @return SRSRAN_SUCCESS if decoding is successful, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_pbch_nr_decode(srsran_pbch_nr_t* q, + const srsran_pbch_nr_cfg_t* cfg, + uint32_t ssb_idx, + const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], + const cf_t ce[SRSRAN_SSB_NOF_RE], + srsran_pbch_msg_nr_t* msg); + +SRSRAN_API uint32_t srsran_pbch_msg_info(const srsran_pbch_msg_nr_t* msg, char* str, uint32_t str_len); + #endif // SRSRAN_PBCH_NR_H diff --git a/lib/include/srsran/phy/phch/pdcch.h b/lib/include/srsran/phy/phch/pdcch.h index 364e907b5..b9682309d 100644 --- a/lib/include/srsran/phy/phch/pdcch.h +++ b/lib/include/srsran/phy/phch/pdcch.h @@ -104,6 +104,14 @@ SRSRAN_API int srsran_pdcch_extract_llr(srsran_pdcch_t* q, SRSRAN_API int srsran_pdcch_decode_msg(srsran_pdcch_t* q, srsran_dl_sf_cfg_t* sf, srsran_dci_cfg_t* dci_cfg, srsran_dci_msg_t* msg); +/** + * @brief Computes decoded DCI correlation. It encodes the given DCI message and compares it with the received LLRs + * @param q PDCCH object + * @param msg Previously decoded DCI message + * @return The normalized correlation between the restored symbols and the received LLRs + */ +SRSRAN_API float srsran_pdcch_msg_corr(srsran_pdcch_t* q, srsran_dci_msg_t* msg); + SRSRAN_API int srsran_pdcch_dci_decode(srsran_pdcch_t* q, float* e, uint8_t* data, uint32_t E, uint32_t nof_bits, uint16_t* crc); diff --git a/lib/include/srsran/phy/phch/pusch_nr.h b/lib/include/srsran/phy/phch/pusch_nr.h index 1cda3fb23..cf3b9b845 100644 --- a/lib/include/srsran/phy/phch/pusch_nr.h +++ b/lib/include/srsran/phy/phch/pusch_nr.h @@ -113,7 +113,7 @@ SRSRAN_API int srsran_pusch_nr_decode(srsran_pusch_nr_t* q, const srsran_sch_grant_nr_t* grant, srsran_chest_dl_res_t* channel, cf_t* sf_symbols[SRSRAN_MAX_PORTS], - srsran_pusch_res_nr_t data[SRSRAN_MAX_TB]); + srsran_pusch_res_nr_t* data[SRSRAN_MAX_TB]); SRSRAN_API uint32_t srsran_pusch_nr_rx_info(const srsran_pusch_nr_t* q, const srsran_sch_cfg_nr_t* cfg, diff --git a/lib/include/srsran/phy/sync/ssb.h b/lib/include/srsran/phy/sync/ssb.h index a2be06268..6d6670f62 100644 --- a/lib/include/srsran/phy/sync/ssb.h +++ b/lib/include/srsran/phy/sync/ssb.h @@ -46,18 +46,20 @@ /** * @brief Maximum number of SSB positions in burst. Defined in TS 38.331 ServingCellConfigCommon, ssb-PositionsInBurst */ -#define SRSRAN_SSB_NOF_POSITION 64 +#define SRSRAN_SSB_NOF_CANDIDATES 64 /** * @brief Describes SSB object initialization arguments */ typedef struct SRSRAN_API { - double max_srate_hz; ///< Maximum sampling rate in Hz, set to zero to use default - srsran_subcarrier_spacing_t min_scs; ///< Minimum subcarrier spacing - bool enable_search; ///< Enables PSS/SSS blind search - bool enable_measure; ///< Enables PSS/SSS CSI measurements and frequency domain search - bool enable_encode; ///< Enables PBCH Encoder - bool enable_decode; ///< Enables PBCH Decoder + double max_srate_hz; ///< Maximum sampling rate in Hz, set to zero to use default + srsran_subcarrier_spacing_t min_scs; ///< Minimum subcarrier spacing + bool enable_search; ///< Enables PSS/SSS blind search + bool enable_measure; ///< Enables PSS/SSS CSI measurements and frequency domain search + bool enable_encode; ///< Enables PBCH Encoder + bool enable_decode; ///< Enables PBCH Decoder + bool disable_polar_simd; ///< Disables polar encoder/decoder SIMD acceleration + float pbch_dmrs_thr; ///< NR-PBCH DMRS threshold for blind decoding, set to 0 for default } srsran_ssb_args_t; /** @@ -69,13 +71,12 @@ typedef struct SRSRAN_API { double ssb_freq_hz; ///< SSB center frequency srsran_subcarrier_spacing_t scs; ///< SSB configured Subcarrier spacing srsran_ssb_patern_t pattern; ///< SSB pattern as defined in TS 38.313 section 4.1 Cell search - bool position[SRSRAN_SSB_NOF_POSITION]; ///< Indicates the time domain positions of the transmitted SS-blocks - srsran_duplex_mode_t duplex_mode; ///< Set to true if the spectrum is paired (FDD) - uint32_t periodicity_ms; ///< SSB periodicity in ms - float beta_pss; ////< PSS power allocation - float beta_sss; ////< SSS power allocation - float beta_pbch; ////< PBCH power allocation - float beta_pbch_dmrs; ////< PBCH DMRS power allocation + srsran_duplex_mode_t duplex_mode; ///< Set to true if the spectrum is paired (FDD) + uint32_t periodicity_ms; ///< SSB periodicity in ms + float beta_pss; ////< PSS power allocation + float beta_sss; ////< SSS power allocation + float beta_pbch; ////< PBCH power allocation + float beta_pbch_dmrs; ////< PBCH DMRS power allocation } srsran_ssb_cfg_t; /** @@ -86,21 +87,25 @@ typedef struct SRSRAN_API { srsran_ssb_cfg_t cfg; ///< Stores last configuration /// Sampling rate dependent parameters - float scs_hz; ///< Subcarrier spacing in Hz - uint32_t max_symbol_sz; ///< Maximum symbol size given the minimum supported SCS and sampling rate - uint32_t max_corr_sz; ///< Maximum correlation size - uint32_t symbol_sz; ///< Current SSB symbol size (for the given base-band sampling rate) - uint32_t corr_sz; ///< Correlation size - uint32_t corr_window; ///< Correlation window length - int32_t f_offset; ///< Current SSB integer frequency offset (multiple of SCS) - uint32_t t_offset; ///< Current SSB integer time offset (number of samples) - uint32_t cp_sz[SRSRAN_SSB_DURATION_NSYMB]; ///< CP length for each SSB symbol + float scs_hz; ///< Subcarrier spacing in Hz + uint32_t max_symbol_sz; ///< Maximum symbol size given the minimum supported SCS and sampling rate + uint32_t max_corr_sz; ///< Maximum correlation size + uint32_t symbol_sz; ///< Current SSB symbol size (for the given base-band sampling rate) + uint32_t corr_sz; ///< Correlation size + uint32_t corr_window; ///< Correlation window length + int32_t f_offset; ///< Current SSB integer frequency offset (multiple of SCS) + uint32_t cp_sz; ///< CP length for the given symbol size + + /// Other parameters + uint32_t l_first[SRSRAN_SSB_NOF_CANDIDATES]; ///< Start symbol for each SSB candidate in half radio frame + uint32_t Lmax; ///< Number of SSB candidates /// Internal Objects srsran_dft_plan_t ifft; ///< IFFT object for modulating the SSB srsran_dft_plan_t fft; ///< FFT object for demodulate the SSB. srsran_dft_plan_t fft_corr; ///< FFT for correlation srsran_dft_plan_t ifft_corr; ///< IFFT for correlation + srsran_pbch_nr_t pbch; ///< PBCH encoder and decoder /// Frequency/Time domain temporal data cf_t* tmp_freq; ///< Temporal frequency domain buffer @@ -132,10 +137,20 @@ SRSRAN_API void srsran_ssb_free(srsran_ssb_t* q); SRSRAN_API int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg); /** * @brief Decodes PBCH in the given time domain signal + * @note It currently expects an input buffer of half radio frame * @param q SSB object + * @param N_id Physical Cell Identifier + * @param ssb_idx SSB candidate index + * @param n_hf Number of hald radio frame, 0 or 1 + * @param in Input baseband buffer * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise */ -SRSRAN_API int srsran_ssb_decode_pbch(srsran_ssb_t* q, const cf_t* in, srsran_pbch_msg_nr_t* msg); +SRSRAN_API int srsran_ssb_decode_pbch(srsran_ssb_t* q, + uint32_t N_id, + uint32_t ssb_idx, + uint32_t n_hf, + const cf_t* in, + srsran_pbch_msg_nr_t* msg); /** * @brief Decides if the SSB object is configured and a given subframe is configured for SSB transmission @@ -149,11 +164,16 @@ SRSRAN_API bool srsran_ssb_send(srsran_ssb_t* q, uint32_t sf_idx); * @brief Adds SSB to a given signal in time domain * @param q SSB object * @param N_id Physical Cell Identifier + * @param ssb_idx SSB candidate index * @param msg NR PBCH message to transmit * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise */ -SRSRAN_API int -srsran_ssb_add(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* msg, const cf_t* in, cf_t* out); +SRSRAN_API int srsran_ssb_add(srsran_ssb_t* q, + uint32_t N_id, + uint32_t ssb_idx, + const srsran_pbch_msg_nr_t* msg, + const cf_t* in, + cf_t* out); /** * @brief Perform cell search and measurement @@ -174,11 +194,15 @@ SRSRAN_API int srsran_ssb_csi_search(srsran_ssb_t* q, * @brief Perform Channel State Information (CSI) measurement from the SSB * @param q NR PSS object * @param N_id Physical Cell Identifier + * @param ssb_idx SSB candidate index * @param in Base-band signal * @param meas SSB-based CSI measurement * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise */ -SRSRAN_API int -srsran_ssb_csi_measure(srsran_ssb_t* q, uint32_t N_id, const cf_t* in, srsran_csi_trs_measurements_t* meas); +SRSRAN_API int srsran_ssb_csi_measure(srsran_ssb_t* q, + uint32_t N_id, + uint32_t ssb_idx, + const cf_t* in, + srsran_csi_trs_measurements_t* meas); #endif // SRSRAN_SSB_H diff --git a/lib/include/srsran/phy/ue/ue_dl.h b/lib/include/srsran/phy/ue/ue_dl.h index 360bffd59..2cf84ff95 100644 --- a/lib/include/srsran/phy/ue/ue_dl.h +++ b/lib/include/srsran/phy/ue/ue_dl.h @@ -208,7 +208,7 @@ SRSRAN_API int srsran_ue_dl_decode_pdsch(srsran_ue_dl_t* q, SRSRAN_API int srsran_ue_dl_decode_pmch(srsran_ue_dl_t* q, srsran_dl_sf_cfg_t* sf, srsran_pmch_cfg_t* pmch_cfg, - srsran_pdsch_res_t* data); + srsran_pdsch_res_t data[SRSRAN_MAX_CODEWORDS]); SRSRAN_API int srsran_ue_dl_decode_phich(srsran_ue_dl_t* q, srsran_dl_sf_cfg_t* sf, diff --git a/lib/include/srsran/phy/utils/random.h b/lib/include/srsran/phy/utils/random.h index c06799fb6..6c916b6ae 100644 --- a/lib/include/srsran/phy/utils/random.h +++ b/lib/include/srsran/phy/utils/random.h @@ -48,6 +48,8 @@ SRSRAN_API float srsran_random_gauss_dist(srsran_random_t q, float std_dev); SRSRAN_API bool srsran_random_bool(srsran_random_t q, float prob_true); +SRSRAN_API void srsran_random_bit_vector(srsran_random_t q, uint8_t* c, uint32_t nsamples); + SRSRAN_API void srsran_random_free(srsran_random_t q); #ifdef __cplusplus diff --git a/lib/include/srsran/phy/utils/vector.h b/lib/include/srsran/phy/utils/vector.h index d9ed102a7..603109547 100644 --- a/lib/include/srsran/phy/utils/vector.h +++ b/lib/include/srsran/phy/utils/vector.h @@ -132,16 +132,16 @@ SRSRAN_API void srsran_vec_u16_copy(uint16_t* dst, const uint16_t* src, uint32_t SRSRAN_API void srsran_vec_i16_copy(int16_t* dst, const int16_t* src, uint32_t len); /* print vectors */ -SRSRAN_API void srsran_vec_fprint_c(FILE* stream, const cf_t* x, const uint32_t len); -SRSRAN_API void srsran_vec_fprint_f(FILE* stream, const float* x, const uint32_t len); -SRSRAN_API void srsran_vec_fprint_b(FILE* stream, const uint8_t* x, const uint32_t len); -SRSRAN_API void srsran_vec_fprint_bs(FILE* stream, const int8_t* x, const uint32_t len); -SRSRAN_API void srsran_vec_fprint_byte(FILE* stream, const uint8_t* x, const uint32_t len); -SRSRAN_API void srsran_vec_fprint_i(FILE* stream, const int* x, const uint32_t len); -SRSRAN_API void srsran_vec_fprint_s(FILE* stream, const int16_t* x, const uint32_t len); -SRSRAN_API void srsran_vec_fprint_hex(FILE* stream, uint8_t* x, const uint32_t len); -SRSRAN_API void srsran_vec_sprint_hex(char* str, const uint32_t max_str_len, uint8_t* x, const uint32_t len); -SRSRAN_API void srsran_vec_sprint_bin(char* str, const uint32_t max_str_len, const uint8_t* x, const uint32_t len); +SRSRAN_API void srsran_vec_fprint_c(FILE* stream, const cf_t* x, const uint32_t len); +SRSRAN_API void srsran_vec_fprint_f(FILE* stream, const float* x, const uint32_t len); +SRSRAN_API void srsran_vec_fprint_b(FILE* stream, const uint8_t* x, const uint32_t len); +SRSRAN_API void srsran_vec_fprint_bs(FILE* stream, const int8_t* x, const uint32_t len); +SRSRAN_API void srsran_vec_fprint_byte(FILE* stream, const uint8_t* x, const uint32_t len); +SRSRAN_API void srsran_vec_fprint_i(FILE* stream, const int* x, const uint32_t len); +SRSRAN_API void srsran_vec_fprint_s(FILE* stream, const int16_t* x, const uint32_t len); +SRSRAN_API void srsran_vec_fprint_hex(FILE* stream, uint8_t* x, const uint32_t len); +SRSRAN_API uint32_t srsran_vec_sprint_hex(char* str, const uint32_t max_str_len, uint8_t* x, const uint32_t len); +SRSRAN_API void srsran_vec_sprint_bin(char* str, const uint32_t max_str_len, const uint8_t* x, const uint32_t len); /* Saves/loads a vector to a file */ SRSRAN_API void srsran_vec_save_file(char* filename, const void* buffer, const uint32_t len); diff --git a/lib/include/srsran/radio/channel_mapping.h b/lib/include/srsran/radio/channel_mapping.h index 218e1dd7c..3cfe2a28f 100644 --- a/lib/include/srsran/radio/channel_mapping.h +++ b/lib/include/srsran/radio/channel_mapping.h @@ -102,10 +102,10 @@ public: void set_channels(const std::list& channels_) { available_channels = channels_; } /** - * Finds an unused physical channel that supports the provided frequency and assigns it to logical channel - * logical_ch + * It deallocates the logical channel if it has been already allocated and it is not suitable, then finds an unused + * physical channel that supports the provided frequency and assigns it to logical channel logical_ch * @param logical_ch logical channel index - * @param freq Frequency (in Hz) that we want to receive/transmitt + * @param freq Frequency (in Hz) that we want to receive/transmit * @return true if a physical channel supporting this frequency was found or false otherwise */ bool allocate_freq(const uint32_t& logical_ch, const float& freq); @@ -147,6 +147,8 @@ private: mutable std::mutex mutex = {}; uint32_t nof_antennas = 1; uint32_t nof_channels_x_dev = 1; + + void release_freq_(const uint32_t& logical_ch); }; } // namespace srsran diff --git a/lib/include/srsran/srsran.h b/lib/include/srsran/srsran.h index ad68b921d..6e0283be1 100644 --- a/lib/include/srsran/srsran.h +++ b/lib/include/srsran/srsran.h @@ -95,6 +95,7 @@ extern "C" { #include "srsran/phy/phch/dci.h" #include "srsran/phy/phch/dci_nr.h" #include "srsran/phy/phch/pbch.h" +#include "srsran/phy/phch/pbch_nr.h" #include "srsran/phy/phch/pcfich.h" #include "srsran/phy/phch/pdcch.h" #include "srsran/phy/phch/pdcch_nr.h" diff --git a/lib/include/srsran/test/ue_test_interfaces.h b/lib/include/srsran/test/ue_test_interfaces.h index 6c50d3d0b..4445d6a12 100644 --- a/lib/include/srsran/test/ue_test_interfaces.h +++ b/lib/include/srsran/test/ue_test_interfaces.h @@ -59,7 +59,7 @@ class rlc_dummy_interface : public rlc_interface_mac public: bool has_data_locked(const uint32_t lcid) override { return false; } uint32_t get_buffer_state(const uint32_t lcid) override { return 0; } - int read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) override { return 0; } + uint32_t read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) override { return 0; } void write_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) override {} void write_pdu_bcch_bch(srsran::unique_byte_buffer_t payload) override {} void write_pdu_bcch_dlsch(uint8_t* payload, uint32_t nof_bytes) override {} @@ -85,8 +85,6 @@ class phy_dummy_interface : public phy_interface_rrc_lte bool cell_search() override { return true; } bool cell_select(phy_cell_t cell) override { return true; } bool cell_is_camping() override { return false; } - - void enable_pregen_signals(bool enable) override {} }; } // namespace srsue diff --git a/lib/include/srsran/upper/byte_buffer_queue.h b/lib/include/srsran/upper/byte_buffer_queue.h index a7d5179aa..c23707322 100644 --- a/lib/include/srsran/upper/byte_buffer_queue.h +++ b/lib/include/srsran/upper/byte_buffer_queue.h @@ -89,33 +89,37 @@ public: private: struct push_callback { - explicit push_callback(uint32_t& unread_bytes_, uint32_t& n_sdus_) : unread_bytes(&unread_bytes_), n_sdus(&n_sdus_) + explicit push_callback(std::atomic& unread_bytes_, std::atomic& n_sdus_) : + unread_bytes(unread_bytes_), n_sdus(n_sdus_) {} void operator()(const unique_byte_buffer_t& msg) { - *unread_bytes += msg->N_bytes; - (*n_sdus)++; + unread_bytes.fetch_add(msg->N_bytes, std::memory_order_relaxed); + n_sdus.fetch_add(1, std::memory_order_relaxed); } - uint32_t* unread_bytes; - uint32_t* n_sdus; + std::atomic& unread_bytes; + std::atomic& n_sdus; }; struct pop_callback { - explicit pop_callback(uint32_t& unread_bytes_, uint32_t& n_sdus_) : unread_bytes(&unread_bytes_), n_sdus(&n_sdus_) + explicit pop_callback(std::atomic& unread_bytes_, std::atomic& n_sdus_) : + unread_bytes(unread_bytes_), n_sdus(n_sdus_) {} void operator()(const unique_byte_buffer_t& msg) { if (msg == nullptr) { return; } - *unread_bytes -= std::min(msg->N_bytes, *unread_bytes); - *n_sdus = std::max(0, (int32_t)(*n_sdus) - 1); + // non-atomic update of both state variables + unread_bytes.fetch_sub(std::min(msg->N_bytes, unread_bytes.load(std::memory_order_relaxed)), + std::memory_order_relaxed); + n_sdus.store(std::max(0, (int32_t)(n_sdus.load(std::memory_order_relaxed)) - 1), std::memory_order_relaxed); } - uint32_t* unread_bytes; - uint32_t* n_sdus; + std::atomic& unread_bytes; + std::atomic& n_sdus; }; - uint32_t unread_bytes = 0; - uint32_t n_sdus = 0; + std::atomic unread_bytes = {0}; + std::atomic n_sdus = {0}; public: dyn_blocking_queue queue; diff --git a/lib/include/srsran/upper/rlc.h b/lib/include/srsran/upper/rlc.h index 8c1829fdc..1d05b33ee 100644 --- a/lib/include/srsran/upper/rlc.h +++ b/lib/include/srsran/upper/rlc.h @@ -75,8 +75,8 @@ public: bool has_data_locked(const uint32_t lcid); uint32_t get_buffer_state(const uint32_t lcid); uint32_t get_total_mch_buffer_state(uint32_t lcid); - int read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes); - int read_pdu_mch(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes); + uint32_t read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes); + uint32_t read_pdu_mch(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes); int get_increment_sequence_num(); void write_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes); void write_pdu_bcch_bch(srsran::unique_byte_buffer_t pdu); diff --git a/lib/include/srsran/upper/rlc_am_lte.h b/lib/include/srsran/upper/rlc_am_lte.h index 3c77c889e..220354a5b 100644 --- a/lib/include/srsran/upper/rlc_am_lte.h +++ b/lib/include/srsran/upper/rlc_am_lte.h @@ -354,7 +354,7 @@ public: // MAC interface bool has_data(); uint32_t get_buffer_state(); - int read_pdu(uint8_t* payload, uint32_t nof_bytes); + uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes); void write_pdu(uint8_t* payload, uint32_t nof_bytes); rlc_bearer_metrics_t get_metrics(); @@ -377,7 +377,7 @@ private: void stop(); int write_sdu(unique_byte_buffer_t sdu); - int read_pdu(uint8_t* payload, uint32_t nof_bytes); + uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes); void discard_sdu(uint32_t discard_sn); bool sdu_queue_is_full(); @@ -401,7 +401,7 @@ private: void debug_state(); - int required_buffer_size(rlc_amd_retx_t retx); + int required_buffer_size(const rlc_amd_retx_t& retx); void retransmit_pdu(uint32_t sn); // Helpers diff --git a/lib/include/srsran/upper/rlc_common.h b/lib/include/srsran/upper/rlc_common.h index 45c33df65..f24b3e8ae 100644 --- a/lib/include/srsran/upper/rlc_common.h +++ b/lib/include/srsran/upper/rlc_common.h @@ -283,7 +283,7 @@ public: virtual bool has_data() = 0; bool is_suspended() { return suspended; }; virtual uint32_t get_buffer_state() = 0; - virtual int read_pdu(uint8_t* payload, uint32_t nof_bytes) = 0; + virtual uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes) = 0; virtual void write_pdu(uint8_t* payload, uint32_t nof_bytes) = 0; virtual void set_bsr_callback(bsr_callback_t callback) = 0; @@ -329,4 +329,5 @@ private: }; } // namespace srsran + #endif // SRSRAN_RLC_COMMON_H diff --git a/lib/include/srsran/upper/rlc_tm.h b/lib/include/srsran/upper/rlc_tm.h index 246ea3e4c..5320ebd22 100644 --- a/lib/include/srsran/upper/rlc_tm.h +++ b/lib/include/srsran/upper/rlc_tm.h @@ -63,7 +63,7 @@ public: // MAC interface bool has_data() override; uint32_t get_buffer_state() override; - int read_pdu(uint8_t* payload, uint32_t nof_bytes) override; + uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes) override; void write_pdu(uint8_t* payload, uint32_t nof_bytes) override; void set_bsr_callback(bsr_callback_t callback) override {} @@ -75,7 +75,7 @@ private: srsue::pdcp_interface_rlc* pdcp = nullptr; srsue::rrc_interface_rlc* rrc = nullptr; - bool tx_enabled = true; + std::atomic tx_enabled = {true}; rlc_bearer_metrics_t metrics = {}; diff --git a/lib/include/srsran/upper/rlc_um_base.h b/lib/include/srsran/upper/rlc_um_base.h index adf6fc0b9..524d10d79 100644 --- a/lib/include/srsran/upper/rlc_um_base.h +++ b/lib/include/srsran/upper/rlc_um_base.h @@ -67,7 +67,7 @@ public: // MAC interface bool has_data(); uint32_t get_buffer_state(); - int read_pdu(uint8_t* payload, uint32_t nof_bytes); + uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes); void write_pdu(uint8_t* payload, uint32_t nof_bytes); int get_increment_sequence_num(); @@ -84,7 +84,7 @@ protected: rlc_um_base_tx(rlc_um_base* parent_); virtual ~rlc_um_base_tx(); virtual bool configure(const rlc_config_t& cfg, std::string rb_name) = 0; - int build_data_pdu(uint8_t* payload, uint32_t nof_bytes); + uint32_t build_data_pdu(uint8_t* payload, uint32_t nof_bytes); void stop(); void reestablish(); void empty_queue(); @@ -116,7 +116,7 @@ protected: srsran::rolling_average mean_pdu_latency_us; #endif - virtual int build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes) = 0; + virtual uint32_t build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes) = 0; // helper functions virtual void debug_state() = 0; diff --git a/lib/include/srsran/upper/rlc_um_lte.h b/lib/include/srsran/upper/rlc_um_lte.h index 48956a896..c833e6cb8 100644 --- a/lib/include/srsran/upper/rlc_um_lte.h +++ b/lib/include/srsran/upper/rlc_um_lte.h @@ -57,7 +57,7 @@ private: rlc_um_lte_tx(rlc_um_base* parent_); bool configure(const rlc_config_t& cfg, std::string rb_name); - int build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes); + uint32_t build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes); uint32_t get_buffer_state(); bool sdu_queue_is_full(); diff --git a/lib/include/srsran/upper/rlc_um_nr.h b/lib/include/srsran/upper/rlc_um_nr.h index 1fc3ad431..8e7752f2e 100644 --- a/lib/include/srsran/upper/rlc_um_nr.h +++ b/lib/include/srsran/upper/rlc_um_nr.h @@ -58,7 +58,7 @@ private: rlc_um_nr_tx(rlc_um_base* parent_); bool configure(const rlc_config_t& cfg, std::string rb_name); - int build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes); + uint32_t build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes); uint32_t get_buffer_state(); private: diff --git a/lib/src/asn1/rrc_nr_utils.cc b/lib/src/asn1/rrc_nr_utils.cc index 9d43d30b2..264e76ade 100644 --- a/lib/src/asn1/rrc_nr_utils.cc +++ b/lib/src/asn1/rrc_nr_utils.cc @@ -1309,10 +1309,10 @@ bool make_phy_carrier_cfg(const freq_info_dl_s& asn1_freq_info_dl, srsran_carrie } template -static inline void make_ssb_positions_in_burst(const bitstring_t& ans1_position_in_burst, - std::array& position_in_burst) +static inline void make_ssb_positions_in_burst(const bitstring_t& ans1_position_in_burst, + std::array& position_in_burst) { - for (uint32_t i = 0; i < SRSRAN_SSB_NOF_POSITION; i++) { + for (uint32_t i = 0; i < SRSRAN_SSB_NOF_CANDIDATES; i++) { if (i < ans1_position_in_burst.length()) { position_in_burst[i] = ans1_position_in_burst.get(ans1_position_in_burst.length() - 1 - i); } else { diff --git a/lib/src/common/thread_pool.cc b/lib/src/common/thread_pool.cc index 692770c50..3587b857a 100644 --- a/lib/src/common/thread_pool.cc +++ b/lib/src/common/thread_pool.cc @@ -51,15 +51,20 @@ void thread_pool::worker::setup(uint32_t id, thread_pool* parent, uint32_t prio, void thread_pool::worker::run_thread() { set_name(std::string("WORKER") + std::to_string(my_id)); - while (my_parent->status[my_id] != STOP) { + while (running.load(std::memory_order_relaxed)) { wait_to_start(); - if (my_parent->status[my_id] != STOP) { + if (running.load(std::memory_order_relaxed)) { work_imp(); finished(); } } } +void thread_pool::worker::stop() +{ + running = false; +} + uint32_t thread_pool::worker::get_id() { return my_id; @@ -101,6 +106,7 @@ void thread_pool::stop() for (uint32_t i = 0; i < nof_workers; i++) { if (workers[i]) { debug_thread("stop(): stopping %d\n", i); + workers[i]->stop(); status[i] = STOP; cvar_worker[i].notify_all(); cvar_queue.notify_all(); diff --git a/lib/src/common/threads.c b/lib/src/common/threads.c index 018ea7d40..a56df7818 100644 --- a/lib/src/common/threads.c +++ b/lib/src/common/threads.c @@ -145,6 +145,11 @@ bool threads_new_rt_cpu(pthread_t* thread, void* (*start_routine)(void*), void* } } +// TSAN seems to have issues with thread attributes when running as normal user, disable them in that case +#if HAVE_TSAN + attr_enable = false; +#endif + int err = pthread_create(thread, attr_enable ? &attr : NULL, start_routine, arg); if (err) { if (EPERM == err) { diff --git a/lib/src/mac/pdu.cc b/lib/src/mac/pdu.cc index 4abbcaa67..bf8d55548 100644 --- a/lib/src/mac/pdu.cc +++ b/lib/src/mac/pdu.cc @@ -559,7 +559,7 @@ bool sch_subh::is_var_len_ce() uint16_t sch_subh::get_c_rnti() { - return le16toh((uint16_t)payload[0] << 8 | payload[1]); + return (uint16_t)payload[0] << 8 | payload[1]; } uint64_t sch_subh::get_con_res_id() diff --git a/lib/src/mac/pdu_queue.cc b/lib/src/mac/pdu_queue.cc index 7cf669a36..8cbb8cbe4 100644 --- a/lib/src/mac/pdu_queue.cc +++ b/lib/src/mac/pdu_queue.cc @@ -62,12 +62,13 @@ void pdu_queue::deallocate(const uint8_t* pdu) * This function enqueues the packet and returns quicly because ACK * deadline is important here. */ -void pdu_queue::push(const uint8_t* ptr, uint32_t len, channel_t channel) +void pdu_queue::push(const uint8_t* ptr, uint32_t len, channel_t channel, int grant_nof_prbs) { if (ptr) { - pdu_t* pdu = (pdu_t*)ptr; - pdu->len = len; - pdu->channel = channel; + pdu_t* pdu = (pdu_t*)ptr; + pdu->len = len; + pdu->channel = channel; + pdu->grant_nof_prbs = grant_nof_prbs; if (!pdu_q.try_push(pdu)) { logger.warning("Error pushing pdu: queue is full"); } @@ -83,7 +84,7 @@ bool pdu_queue::process_pdus() pdu_t* pdu; while (pdu_q.try_pop(pdu)) { if (callback) { - callback->process_pdu(pdu->ptr, pdu->len, pdu->channel); + callback->process_pdu(pdu->ptr, pdu->len, pdu->channel, pdu->grant_nof_prbs); } cnt++; have_data = true; diff --git a/lib/src/phy/ch_estimation/chest_dl.c b/lib/src/phy/ch_estimation/chest_dl.c index b4e89f935..079ff7dd1 100644 --- a/lib/src/phy/ch_estimation/chest_dl.c +++ b/lib/src/phy/ch_estimation/chest_dl.c @@ -332,15 +332,18 @@ static float estimate_noise_pilots(srsran_chest_dl_t* q, srsran_dl_sf_cfg_t* sf, : srsran_refsignal_cs_nof_re(&q->csr_refs, sf, port_id); uint32_t nsymbols = (ch_mode == SRSRAN_SF_MBSFN) ? srsran_refsignal_mbsfn_nof_symbols() : srsran_refsignal_cs_nof_symbols(&q->csr_refs, sf, port_id); + if (nsymbols == 0) { + ERROR("Invalid number of CRS symbols\n"); + return SRSRAN_ERROR; + } + uint32_t nref = npilots / nsymbols; uint32_t fidx = (ch_mode == SRSRAN_SF_MBSFN) ? srsran_refsignal_mbsfn_fidx(1) : srsran_refsignal_cs_fidx(q->cell, 0, port_id, 0); - - cf_t* input2d[nsymbols + 2]; cf_t* tmp_noise = q->tmp_noise; - // Special case for 1 symbol - if (nsymbols == 1) { + // Special case for 1 or 2 symbol + if (nsymbols < 3) { srsran_vec_sc_prod_cfc(q->pilot_estimates + 1, weight, tmp_noise, nref - 2); srsran_vec_sum_ccc(q->pilot_estimates + 0, tmp_noise, tmp_noise, nref - 2); srsran_vec_sum_ccc(q->pilot_estimates + 2, tmp_noise, tmp_noise, nref - 2); @@ -350,30 +353,21 @@ static float estimate_noise_pilots(srsran_chest_dl_t* q, srsran_dl_sf_cfg_t* sf, return sum_power; } + // Convert pilots to 2D to ease access + cf_t* input2d[4]; // The maximum number of symbols is 4 for (int i = 0; i < nsymbols; i++) { - input2d[i + 1] = &q->pilot_estimates[i * nref]; - } - - input2d[0] = &q->tmp_noise[nref]; - if (nsymbols > 3) { - srsran_vec_sc_prod_cfc(input2d[2], 2.0f, input2d[0], nref); - srsran_vec_sub_ccc(input2d[0], input2d[4], input2d[0], nref); - } else { - srsran_vec_sc_prod_cfc(input2d[2], 1.0f, input2d[0], nref); + input2d[i] = &q->pilot_estimates[i * nref]; } - input2d[nsymbols + 1] = &q->tmp_noise[nref * 2]; - if (nsymbols > 3) { - srsran_vec_sc_prod_cfc(input2d[nsymbols - 1], 2.0f, input2d[nsymbols + 1], nref); - srsran_vec_sub_ccc(input2d[nsymbols + 1], input2d[nsymbols - 3], input2d[nsymbols + 1], nref); - } else { - srsran_vec_sc_prod_cfc(input2d[nsymbols - 1], 1.0f, input2d[nsymbols + 1], nref); - } - - for (int i = 1; i < nsymbols + 1; i++) { + // Compares surrounding pilots in time/frequency. It requires at least 3 symbols with pilots. + for (int i = 1; i < nsymbols - 1; i++) { + // Calculate previous and next symbol indexes offset uint32_t offset = ((fidx < 3) ^ (i & 1)) ? 0 : 1; + + // Write estimates from this symbols srsran_vec_sc_prod_cfc(input2d[i], weight, tmp_noise, nref); + // Add the previous symbol estimates and extrapolate first/last element srsran_vec_sum_ccc(&input2d[i - 1][0], &tmp_noise[offset], &tmp_noise[offset], nref - offset); srsran_vec_sum_ccc(&input2d[i - 1][1 - offset], &tmp_noise[0], &tmp_noise[0], nref + offset - 1); if (offset) { @@ -382,6 +376,7 @@ static float estimate_noise_pilots(srsran_chest_dl_t* q, srsran_dl_sf_cfg_t* sf, tmp_noise[nref - 1] += 2.0f * input2d[i - 1][nref - 2] - input2d[i - 1][nref - 1]; } + // Add the next symbol estimates and extrapolate first/last element srsran_vec_sum_ccc(&input2d[i + 1][0], &tmp_noise[offset], &tmp_noise[offset], nref - offset); srsran_vec_sum_ccc(&input2d[i + 1][1 - offset], &tmp_noise[0], &tmp_noise[0], nref + offset - 1); if (offset) { @@ -390,14 +385,18 @@ static float estimate_noise_pilots(srsran_chest_dl_t* q, srsran_dl_sf_cfg_t* sf, tmp_noise[nref - 1] += 2.0f * input2d[i + 1][nref - 2] - input2d[i + 1][nref - 1]; } + // Scale to normalise to this symbol srsran_vec_sc_prod_cfc(tmp_noise, 1.0f / (weight + 4.0f), tmp_noise, nref); + // Subtract this symbol srsran_vec_sub_ccc(input2d[i], tmp_noise, tmp_noise, nref); - sum_power = srsran_vec_avg_power_cf(tmp_noise, nref); + + // The left signal after the subtraction can be considered noise + sum_power += srsran_vec_avg_power_cf(tmp_noise, nref); count++; } - return sum_power / (float)count * sqrtf(weight + 4.0f); + return sum_power / (float)count; } static float estimate_noise_pss(srsran_chest_dl_t* q, cf_t* input, cf_t* ce) diff --git a/lib/src/phy/ch_estimation/dmrs_pbch.c b/lib/src/phy/ch_estimation/dmrs_pbch.c new file mode 100644 index 000000000..c6d38862b --- /dev/null +++ b/lib/src/phy/ch_estimation/dmrs_pbch.c @@ -0,0 +1,218 @@ +/** + * + * \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/phy/ch_estimation/dmrs_pbch.h" +#include "srsran/phy/common/sequence.h" +#include "srsran/phy/utils/debug.h" +#include "srsran/phy/utils/vector.h" +#include +#include + +/* + * Number of NR PBCH DMRS resource elements present in an SSB resource grid + */ +#define DMRS_PBCH_NOF_RE 144 + +static uint32_t dmrs_pbch_cinit(const srsran_dmrs_pbch_cfg_t* cfg) +{ + // Default values for L_max == 4 + uint64_t i_ssb = (cfg->ssb_idx & 0b11U) + 4UL * cfg->n_hf; // Least 2 significant bits + + if (cfg->L_max == 8 || cfg->L_max == 64) { + i_ssb = cfg->ssb_idx & 0b111U; // Least 3 significant bits + } + + return SRSRAN_SEQUENCE_MOD(((i_ssb + 1UL) * (SRSRAN_FLOOR(cfg->N_id, 4UL) + 1UL) << 11UL) + ((i_ssb + 1UL) << 6UL) + + (cfg->N_id % 4)); +} + +int srsran_dmrs_pbch_put(const srsran_dmrs_pbch_cfg_t* cfg, cf_t ssb_grid[SRSRAN_SSB_NOF_RE]) +{ + // Validate inputs + if (cfg == NULL || ssb_grid == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Calculate index shift + uint32_t v = cfg->N_id % 4; + + // Calculate power allocation + float beta = M_SQRT1_2; + if (isnormal(cfg->beta)) { + beta = cfg->beta; + } + + // Initialise sequence + uint32_t cinit = dmrs_pbch_cinit(cfg); + srsran_sequence_state_t sequence_state = {}; + srsran_sequence_state_init(&sequence_state, cinit); + + // Generate sequence + cf_t r[DMRS_PBCH_NOF_RE]; + srsran_sequence_state_gen_f(&sequence_state, beta, (float*)r, DMRS_PBCH_NOF_RE * 2); + + // r sequence read index + uint32_t r_idx = 0; + + // Put sequence in symbol 1 + for (uint32_t k = v; k < SRSRAN_SSB_BW_SUBC; k += 4) { + ssb_grid[SRSRAN_SSB_BW_SUBC * 1 + k] = r[r_idx++]; + } + + // Put sequence in symbol 2, lower section + for (uint32_t k = v; k < 48; k += 4) { + ssb_grid[SRSRAN_SSB_BW_SUBC * 2 + k] = r[r_idx++]; + } + + // Put sequence in symbol 2, upper section + for (uint32_t k = 192 + v; k < SRSRAN_SSB_BW_SUBC; k += 4) { + ssb_grid[SRSRAN_SSB_BW_SUBC * 2 + k] = r[r_idx++]; + } + + // Put sequence in symbol 3 + for (uint32_t k = v; k < SRSRAN_SSB_BW_SUBC; k += 4) { + ssb_grid[SRSRAN_SSB_BW_SUBC * 3 + k] = r[r_idx++]; + } + + return SRSRAN_SUCCESS; +} + +int dmrs_pbch_extract_lse(const srsran_dmrs_pbch_cfg_t* cfg, + const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], + cf_t lse[DMRS_PBCH_NOF_RE]) +{ + // Calculate index shift + uint32_t v = cfg->N_id % 4; + + // Calculate power allocation + float beta = M_SQRT1_2; + if (isnormal(cfg->beta)) { + beta = cfg->beta; + } + + // Initialise sequence + uint32_t cinit = dmrs_pbch_cinit(cfg); + srsran_sequence_state_t sequence_state = {}; + srsran_sequence_state_init(&sequence_state, cinit); + + // Generate sequence + cf_t r[DMRS_PBCH_NOF_RE]; + srsran_sequence_state_gen_f(&sequence_state, beta, (float*)r, DMRS_PBCH_NOF_RE * 2); + + // r sequence read index + uint32_t r_idx = 0; + + // Put sequence in symbol 1 + for (uint32_t k = v; k < SRSRAN_SSB_BW_SUBC; k += 4) { + lse[r_idx++] = ssb_grid[SRSRAN_SSB_BW_SUBC * 1 + k]; + } + + // Put sequence in symbol 2, lower section + for (uint32_t k = v; k < 48; k += 4) { + lse[r_idx++] = ssb_grid[SRSRAN_SSB_BW_SUBC * 2 + k]; + } + + // Put sequence in symbol 2, upper section + for (uint32_t k = 192 + v; k < SRSRAN_SSB_BW_SUBC; k += 4) { + lse[r_idx++] = ssb_grid[SRSRAN_SSB_BW_SUBC * 2 + k]; + } + + // Put sequence in symbol 3 + for (uint32_t k = v; k < SRSRAN_SSB_BW_SUBC; k += 4) { + lse[r_idx++] = ssb_grid[SRSRAN_SSB_BW_SUBC * 3 + k]; + } + + // Calculate actual least square estimates + srsran_vec_prod_conj_ccc(lse, r, lse, DMRS_PBCH_NOF_RE); + + return SRSRAN_SUCCESS; +} + +int srsran_dmrs_pbch_estimate(const srsran_dmrs_pbch_cfg_t* cfg, + const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], + cf_t ce[SRSRAN_SSB_NOF_RE], + srsran_dmrs_pbch_meas_t* meas) +{ + // Validate inputs + if (cfg == NULL || ssb_grid == NULL || ce == NULL || meas == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Extract least square estimates + cf_t lse[DMRS_PBCH_NOF_RE]; + if (dmrs_pbch_extract_lse(cfg, ssb_grid, lse) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + float scs_hz = SRSRAN_SUBC_SPACING_NR(cfg->scs); + if (!isnormal(scs_hz)) { + ERROR("Invalid SCS"); + return SRSRAN_ERROR; + } + + // Compute average delay in microseconds from the symbols 1 and 3 (symbol 2 does not carry PBCH in all the grid) + float avg_delay1_norm = srsran_vec_estimate_frequency(&lse[0], 60) / 4.0f; + float avg_delay3_norm = srsran_vec_estimate_frequency(&lse[84], 60) / 4.0f; + float avg_delay_norm = (avg_delay1_norm + avg_delay3_norm) / 2.0f; + float avg_delay_us = avg_delay_norm / scs_hz; + + // Generate a second SSB grid with the corrected average delay + cf_t ssb_grid_corrected[SRSRAN_SSB_NOF_RE]; + for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) { + srsran_vec_apply_cfo(&ssb_grid[SRSRAN_SSB_BW_SUBC * l], + avg_delay_norm, + &ssb_grid_corrected[SRSRAN_SSB_BW_SUBC * l], + SRSRAN_SSB_BW_SUBC); + } + + // Extract LSE from corrected grid + if (dmrs_pbch_extract_lse(cfg, ssb_grid_corrected, lse) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // Compute correlation of symbols 1 and 3 + cf_t corr1 = srsran_vec_acc_cc(&lse[0], 60) / 60.0f; + cf_t corr3 = srsran_vec_acc_cc(&lse[84], 60) / 60.0f; + + // Estimate CFO from correlation + float distance_s = srsran_symbol_distance_s(1, 3, cfg->scs); + float cfo_hz = 0.0f; + if (isnormal(distance_s)) { + cfo_hz = cargf(corr1 * conjf(corr3)) / (2.0f * (float)M_PI * distance_s); + } + + // Estimate wideband gain at symbol 0 + cf_t wideband_gain = (srsran_vec_acc_cc(lse, DMRS_PBCH_NOF_RE) / DMRS_PBCH_NOF_RE) * + cexpf(I * 2.0f * M_PI * srsran_symbol_offset_s(2, cfg->scs) * cfo_hz); + + // Compute RSRP from correlation + float rsrp = SRSRAN_CSQABS((corr1 + corr3) / 2.0f); + + // Compute EPRE + float epre = srsran_vec_avg_power_cf(lse, DMRS_PBCH_NOF_RE); + + // Write measurements + meas->corr = rsrp / epre; + meas->epre = epre; + meas->rsrp = rsrp; + meas->cfo_hz = cfo_hz; + meas->avg_delay_us = avg_delay_us; + + // Compute channel estimates + for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) { + float t_s = srsran_symbol_offset_s(l, cfg->scs); + cf_t symbol_wideband_gain = cexpf(-I * 2.0f * M_PI * cfo_hz * t_s) * wideband_gain; + srsran_vec_gen_sine(symbol_wideband_gain, -avg_delay_norm, &ce[l * SRSRAN_SSB_BW_SUBC], SRSRAN_SSB_BW_SUBC); + } + + return SRSRAN_SUCCESS; +} \ No newline at end of file diff --git a/lib/src/phy/common/sequence.c b/lib/src/phy/common/sequence.c index 3fbf4f4ea..c0113503d 100644 --- a/lib/src/phy/common/sequence.c +++ b/lib/src/phy/common/sequence.c @@ -560,16 +560,13 @@ void srsran_sequence_apply_s(const int16_t* in, int16_t* out, uint32_t length, u } } -void srsran_sequence_apply_c(const int8_t* in, int8_t* out, uint32_t length, uint32_t seed) +void srsran_sequence_state_apply_c(srsran_sequence_state_t* s, const int8_t* in, int8_t* out, uint32_t length) { - uint32_t x1 = sequence_x1_init; // X1 initial state is fix - uint32_t x2 = sequence_get_x2_init(seed); // loads x2 initial state - uint32_t i = 0; if (length >= SEQUENCE_PAR_BITS) { for (; i < length - (SEQUENCE_PAR_BITS - 1); i += SEQUENCE_PAR_BITS) { - uint32_t c = (uint32_t)(x1 ^ x2); + uint32_t c = (uint32_t)(s->x1 ^ s->x2); uint32_t j = 0; #ifdef LV_HAVE_SSE @@ -606,30 +603,34 @@ void srsran_sequence_apply_c(const int8_t* in, int8_t* out, uint32_t length, uin } // Step sequences - x1 = sequence_gen_LTE_pr_memless_step_par_x1(x1); - x2 = sequence_gen_LTE_pr_memless_step_par_x2(x2); + s->x1 = sequence_gen_LTE_pr_memless_step_par_x1(s->x1); + s->x2 = sequence_gen_LTE_pr_memless_step_par_x2(s->x2); } } for (; i < length; i++) { - out[i] = in[i] * (((x1 ^ x2) & 1U) ? -1 : +1); + out[i] = in[i] * (((s->x1 ^ s->x2) & 1U) ? -1 : +1); // Step sequences - x1 = sequence_gen_LTE_pr_memless_step_x1(x1); - x2 = sequence_gen_LTE_pr_memless_step_x2(x2); + s->x1 = sequence_gen_LTE_pr_memless_step_x1(s->x1); + s->x2 = sequence_gen_LTE_pr_memless_step_x2(s->x2); } } -void srsran_sequence_apply_bit(const uint8_t* in, uint8_t* out, uint32_t length, uint32_t seed) +void srsran_sequence_apply_c(const int8_t* in, int8_t* out, uint32_t length, uint32_t seed) { - uint32_t x1 = sequence_x1_init; // X1 initial state is fix - uint32_t x2 = sequence_get_x2_init(seed); // loads x2 initial state + srsran_sequence_state_t sequence_state; + srsran_sequence_state_init(&sequence_state, seed); + srsran_sequence_state_apply_c(&sequence_state, in, out, length); +} +void srsran_sequence_state_apply_bit(srsran_sequence_state_t* s, const uint8_t* in, uint8_t* out, uint32_t length) +{ uint32_t i = 0; if (length >= SEQUENCE_PAR_BITS) { for (; i < length - (SEQUENCE_PAR_BITS - 1); i += SEQUENCE_PAR_BITS) { - uint32_t c = (uint32_t)(x1 ^ x2); + uint32_t c = (uint32_t)(s->x1 ^ s->x2); uint32_t j = 0; #ifdef LV_HAVE_SSE @@ -665,20 +666,27 @@ void srsran_sequence_apply_bit(const uint8_t* in, uint8_t* out, uint32_t length, } // Step sequences - x1 = sequence_gen_LTE_pr_memless_step_par_x1(x1); - x2 = sequence_gen_LTE_pr_memless_step_par_x2(x2); + s->x1 = sequence_gen_LTE_pr_memless_step_par_x1(s->x1); + s->x2 = sequence_gen_LTE_pr_memless_step_par_x2(s->x2); } } for (; i < length; i++) { - out[i] = in[i] ^ ((x1 ^ x2) & 1U); + out[i] = in[i] ^ ((s->x1 ^ s->x2) & 1U); // Step sequences - x1 = sequence_gen_LTE_pr_memless_step_x1(x1); - x2 = sequence_gen_LTE_pr_memless_step_x2(x2); + s->x1 = sequence_gen_LTE_pr_memless_step_x1(s->x1); + s->x2 = sequence_gen_LTE_pr_memless_step_x2(s->x2); } } +void srsran_sequence_apply_bit(const uint8_t* in, uint8_t* out, uint32_t length, uint32_t seed) +{ + srsran_sequence_state_t sequence_state = {}; + srsran_sequence_state_init(&sequence_state, seed); + srsran_sequence_state_apply_bit(&sequence_state, in, out, length); +} + void srsran_sequence_apply_packed(const uint8_t* in, uint8_t* out, uint32_t length, uint32_t seed) { uint32_t x1 = sequence_x1_init; // X1 initial state is fix @@ -759,7 +767,7 @@ void srsran_sequence_apply_packed(const uint8_t* in, uint8_t* out, uint32_t leng out[i] = in[i] ^ reverse_lut[buffer & ((1U << rem8) - 1U) & 255U]; } -#else // SEQUENCE_PAR_BITS % 8 == 0 +#else // SEQUENCE_PAR_BITS % 8 == 0 while (i < (length / 8 - (SEQUENCE_PAR_BITS - 1) / 8)) { uint32_t c = (uint32_t)(x1 ^ x2); diff --git a/lib/src/phy/enb/enb_dl.c b/lib/src/phy/enb/enb_dl.c index ed9036636..f546dfa67 100644 --- a/lib/src/phy/enb/enb_dl.c +++ b/lib/src/phy/enb/enb_dl.c @@ -365,11 +365,11 @@ void srsran_enb_dl_put_phich(srsran_enb_dl_t* q, srsran_phich_grant_t* grant, bo srsran_phich_encode(&q->phich, &q->dl_sf, resource, ack, q->sf_symbols); } -bool srsran_enb_dl_location_is_common_ncce(srsran_enb_dl_t* q, uint32_t ncce) +bool srsran_enb_dl_location_is_common_ncce(srsran_enb_dl_t* q, const srsran_dci_location_t* loc) { if (SRSRAN_CFI_ISVALID(q->dl_sf.cfi)) { - return srsran_location_find_ncce( - q->common_locations[SRSRAN_CFI_IDX(q->dl_sf.cfi)], q->nof_common_locations[SRSRAN_CFI_IDX(q->dl_sf.cfi)], ncce); + return srsran_location_find_location( + q->common_locations[SRSRAN_CFI_IDX(q->dl_sf.cfi)], q->nof_common_locations[SRSRAN_CFI_IDX(q->dl_sf.cfi)], loc); } else { return false; } diff --git a/lib/src/phy/fec/convolutional/viterbi.c b/lib/src/phy/fec/convolutional/viterbi.c index ae35e6b17..c2ac751d9 100644 --- a/lib/src/phy/fec/convolutional/viterbi.c +++ b/lib/src/phy/fec/convolutional/viterbi.c @@ -34,11 +34,11 @@ #define DEB 0 -#define TB_ITER 3 +#define TB_ITER 5 #define DEFAULT_GAIN 100 -#define DEFAULT_GAIN_16 1000 +#define DEFAULT_GAIN_16 500 #define VITERBI_16 #ifndef LV_HAVE_AVX2 @@ -558,11 +558,10 @@ int srsran_viterbi_decode_f(srsran_viterbi_t* q, float* symbols, uint8_t* data, len = 3 * (frame_length + q->K - 1); } if (!q->decode_f) { - float max = 1e-9; - for (int i = 0; i < len; i++) { - if (fabs(symbols[i]) > max) { - max = fabs(symbols[i]); - } + float max = 1e-9; + uint32_t max_i = srsran_vec_max_abs_fi(symbols, len); + if (max_i < len && isnormal(symbols[max_i])) { + max = fabsf(symbols[max_i]); } #ifdef VITERBI_16 srsran_vec_quant_fus(symbols, q->symbols_us, q->gain_quant / max, 32767.5, 65535, len); diff --git a/lib/src/phy/fec/convolutional/viterbi37_avx2_16bit.c b/lib/src/phy/fec/convolutional/viterbi37_avx2_16bit.c index 9f0aaabaa..6d143dbb1 100644 --- a/lib/src/phy/fec/convolutional/viterbi37_avx2_16bit.c +++ b/lib/src/phy/fec/convolutional/viterbi37_avx2_16bit.c @@ -43,7 +43,6 @@ union branchtab27 { } Branchtab37_sse2[3]; -int firstGo_16bit; /* State info for instance of Viterbi decoder */ struct v37 { metric_t metrics1; /* path metric buffer 1 */ @@ -85,7 +84,6 @@ int init_viterbi37_avx2_16bit(void* p, int starting_state) vp->metrics1.c[i] = 63; clear_v37_avx2_16bit(vp); - firstGo_16bit = 1; vp->old_metrics = &vp->metrics1; vp->new_metrics = &vp->metrics2; vp->dp = vp->decisions; diff --git a/lib/src/phy/fec/ldpc/test/CMakeLists.txt b/lib/src/phy/fec/ldpc/test/CMakeLists.txt index ed5d0dcaa..d3e0b05e2 100644 --- a/lib/src/phy/fec/ldpc/test/CMakeLists.txt +++ b/lib/src/phy/fec/ldpc/test/CMakeLists.txt @@ -60,7 +60,7 @@ endif(HAVE_AVX512) ### Test LDPC libs function(ldpc_unit_tests) foreach(i IN LISTS ARGN) - add_nr_test(NAME ${test_name}-LS${i} COMMAND ${test_command} -l${i} + add_nr_advanced_test(NAME ${test_name}-LS${i} COMMAND ${test_command} -l${i} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ ) endforeach() @@ -179,36 +179,36 @@ function(ldpc_rm_unit_tests) math(EXPR tmpN "${N} - (${N} % ${Div})") math(EXPR E "${Ordval}*(${tmpN})/${Div}") #twice the rate - add_nr_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${N} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${N} + add_nr_advanced_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${N} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${N} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ ) math(EXPR M "${N} / 2") # Half size buffer - add_nr_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${M} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${M} + add_nr_advanced_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${M} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${M} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ ) math(EXPR Div "2*${Ordval}") math(EXPR tmpN "${N} - (${N} % ${Div})") math(EXPR E "${Ordval}*(${tmpN})/${Div}") #twice the rate - add_nr_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${N} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${N} + add_nr_advanced_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${N} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${N} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ ) math(EXPR M "${N}/ 2") # Half size buffer - add_nr_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${M} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${M} + add_nr_advanced_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${M} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${M} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ ) math(EXPR Div "${Ordval}") math(EXPR tmpN "2*${N} - (2*${N} % ${Div})") #Half the rate math(EXPR E "${Ordval}*(${tmpN})/${Div}") - add_nr_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${N} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${N} + add_nr_advanced_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${N} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${N} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ ) math(EXPR M "${N}/ 2") # Half size buffer - add_nr_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${M} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${M} + add_nr_advanced_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${M} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${M} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ ) endforeach() diff --git a/lib/src/phy/fec/polar/polar_rm.c b/lib/src/phy/fec/polar/polar_rm.c index 521efb66e..cc1d13fb4 100644 --- a/lib/src/phy/fec/polar/polar_rm.c +++ b/lib/src/phy/fec/polar/polar_rm.c @@ -389,7 +389,6 @@ int srsran_polar_rm_tx_init(srsran_polar_rm_t* p) int srsran_polar_rm_rx_init_f(srsran_polar_rm_t* p) { - if (p == NULL) { return -1; } @@ -414,7 +413,6 @@ int srsran_polar_rm_rx_init_f(srsran_polar_rm_t* p) int srsran_polar_rm_rx_init_s(srsran_polar_rm_t* p) { - if (p == NULL) { return -1; } @@ -439,7 +437,6 @@ int srsran_polar_rm_rx_init_s(srsran_polar_rm_t* p) int srsran_polar_rm_rx_init_c(srsran_polar_rm_t* p) { - if (p == NULL) { return -1; } @@ -464,41 +461,74 @@ int srsran_polar_rm_rx_init_c(srsran_polar_rm_t* p) void srsran_polar_rm_tx_free(srsran_polar_rm_t* q) { - if (q != NULL) { - struct pRM_tx* qq = q->ptr; + if (q == NULL) { + return; + } + + struct pRM_tx* qq = q->ptr; + if (qq == NULL) { + return; + } + + if (qq->y_e) { free(qq->y_e); - free(qq); } + + free(qq); } void srsran_polar_rm_rx_free_f(srsran_polar_rm_t* q) { - if (q != NULL) { - struct pRM_rx_f* qq = q->ptr; + if (q == NULL) { + return; + } + + struct pRM_rx_f* qq = q->ptr; + if (qq == NULL) { + return; + } + + if (qq->y_e) { free(qq->y_e); - // free(qq->indices); - free(qq); } + + free(qq); } void srsran_polar_rm_rx_free_s(srsran_polar_rm_t* q) { - if (q != NULL) { - struct pRM_rx_s* qq = q->ptr; + if (q == NULL) { + return; + } + + struct pRM_rx_s* qq = q->ptr; + if (qq == NULL) { + return; + } + + if (qq->y_e) { free(qq->y_e); - // free(qq->indices); - free(qq); } + + free(qq); } void srsran_polar_rm_rx_free_c(srsran_polar_rm_t* q) { - if (q != NULL) { - struct pRM_rx_c* qq = q->ptr; + if (q == NULL) { + return; + } + + struct pRM_rx_c* qq = q->ptr; + if (qq == NULL) { + return; + } + + if (qq->y_e) { free(qq->y_e); - // free(qq->indices); - free(qq); } + + free(qq); } int srsran_polar_rm_tx(srsran_polar_rm_t* q, @@ -538,7 +568,6 @@ int srsran_polar_rm_rx_f(srsran_polar_rm_t* q, const uint32_t K, const uint8_t ibil) { - struct pRM_rx_f* pp = q->ptr; float* y = NULL; float* e = pp->e; // length E @@ -566,7 +595,6 @@ int srsran_polar_rm_rx_s(srsran_polar_rm_t* q, const uint32_t K, const uint8_t ibil) { - struct pRM_rx_s* pp = q->ptr; int16_t* y = NULL; int16_t* e = pp->e; @@ -594,7 +622,6 @@ int srsran_polar_rm_rx_c(srsran_polar_rm_t* q, const uint32_t K, const uint8_t ibil) { - struct pRM_rx_c* pp = q->ptr; int8_t* y = NULL; int8_t* e = pp->e; diff --git a/lib/src/phy/fec/polar/test/CMakeLists.txt b/lib/src/phy/fec/polar/test/CMakeLists.txt index f9a834161..fd6ccc6e4 100644 --- a/lib/src/phy/fec/polar/test/CMakeLists.txt +++ b/lib/src/phy/fec/polar/test/CMakeLists.txt @@ -40,7 +40,7 @@ function(polar_tests_lite) list(GET listE ${num} eval) list(GET listK ${num} kval) list(GET listI ${num} ival) - add_nr_test(NAME ${test_name}-s${S}-n${nval}-e${eval}-k${kval}-i${ival} + add_nr_advanced_test(NAME ${test_name}-s${S}-n${nval}-e${eval}-k${kval}-i${ival} COMMAND ${test_command} -s${S} -n${nval} -e${eval} -k${kval} -i${ival} ) endforeach() @@ -54,7 +54,7 @@ function(polar_tests) foreach(Kval RANGE 36 164 32) math(EXPR Emin "${Kval} + 1") foreach(Eval RANGE ${Emin} 8192 128) - add_nr_test(NAME ${test_name}-s${S}-k${Kval}-e${Eval}-n${nval}-i0 + add_nr_advanced_test(NAME ${test_name}-s${S}-k${Kval}-e${Eval}-n${nval}-i0 COMMAND ${test_command} -s${S} -k${Kval} -e${Eval} -n${nval} -i0 ) endforeach() @@ -66,7 +66,7 @@ function(polar_tests) foreach(Kval RANGE 18 25) math(EXPR Emin "${Kval} + 3 + 1") foreach(Eval RANGE ${Emin} 8192 128) - add_nr_test(NAME ${test_name}-s${S}-k${Kval}-e${Eval}-n${nval}-i1 + add_nr_advanced_test(NAME ${test_name}-s${S}-k${Kval}-e${Eval}-n${nval}-i1 COMMAND ${test_command} -s${S} -k${Kval} -e${Eval} -n${nval} -i1 ) endforeach() @@ -75,7 +75,7 @@ function(polar_tests) foreach(Kval RANGE 32 1023 32) math(EXPR Emin "${Kval} + 1") foreach(Eval RANGE ${Emin} 8192 128) - add_nr_test(NAME ${test_name}-s${S}-k${Kval}-e${Eval}-n${nval}-i1 + add_nr_advanced_test(NAME ${test_name}-s${S}-k${Kval}-e${Eval}-n${nval}-i1 COMMAND ${test_command} -s${S} -k${Kval} -e${Eval} -n${nval} -i1 ) endforeach() diff --git a/lib/src/phy/phch/dci.c b/lib/src/phy/phch/dci.c index caa5a9b78..9efd082ae 100644 --- a/lib/src/phy/phch/dci.c +++ b/lib/src/phy/phch/dci.c @@ -1389,10 +1389,12 @@ bool srsran_location_find(const srsran_dci_location_t* locations, uint32_t nof_l return false; } -bool srsran_location_find_ncce(const srsran_dci_location_t* locations, uint32_t nof_locations, uint32_t ncce) +bool srsran_location_find_location(const srsran_dci_location_t* locations, + uint32_t nof_locations, + const srsran_dci_location_t* location) { for (uint32_t i = 0; i < nof_locations; i++) { - if (locations[i].ncce == ncce) { + if (locations[i].ncce == location->ncce && locations[i].L == location->L) { return true; } } diff --git a/lib/src/phy/phch/pbch.c b/lib/src/phy/phch/pbch.c index af23dc2e6..2c288053e 100644 --- a/lib/src/phy/phch/pbch.c +++ b/lib/src/phy/phch/pbch.c @@ -49,15 +49,11 @@ bool srsran_pbch_exists(int nframe, int nslot) return (!(nframe % 5) && nslot == 1); } -cf_t* offset_original; - int srsran_pbch_cp(cf_t* input, cf_t* output, srsran_cell_t cell, bool put) { int i; cf_t* ptr; - offset_original = input; - if (put) { ptr = input; output += cell.nof_prb * SRSRAN_NRE / 2 - 36; diff --git a/lib/src/phy/phch/pbch_nr.c b/lib/src/phy/phch/pbch_nr.c new file mode 100644 index 000000000..1a34ab903 --- /dev/null +++ b/lib/src/phy/phch/pbch_nr.c @@ -0,0 +1,677 @@ +/** + * + * \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/phy/phch/pbch_nr.h" +#include "srsran/phy/common/sequence.h" +#include "srsran/phy/fec/polar/polar_chanalloc.h" +#include "srsran/phy/fec/polar/polar_interleaver.h" +#include "srsran/phy/mimo/precoding.h" +#include "srsran/phy/modem/demod_soft.h" +#include "srsran/phy/modem/mod.h" +#include "srsran/phy/utils/debug.h" +#include "srsran/phy/utils/simd.h" +#include "srsran/phy/utils/vector.h" + +#define PBCH_NR_DEBUG_TX(...) DEBUG("PBCH-NR Tx: " __VA_ARGS__) +#define PBCH_NR_DEBUG_RX(...) DEBUG("PBCH-NR Rx: " __VA_ARGS__) + +/* + * CRC Parameters + */ +#define PBCH_NR_CRC SRSRAN_LTE_CRC24C +#define PBCH_NR_CRC_LEN 24 + +/* + * Polar code N_max + */ +#define PBCH_NR_POLAR_N_MAX 9U + +/* + * Polar rate matching I_BIL + */ +#define PBCH_NR_POLAR_RM_IBIL 0 + +/* + * Number of generated payload bits, called A + */ +#define PBCH_NR_A (SRSRAN_PBCH_NR_PAYLOAD_SZ + 8) + +/* + * Number of payload bits plus CRC + */ +#define PBCH_NR_K (PBCH_NR_A + PBCH_NR_CRC_LEN) + +/* + * Number of Polar encoded bits + */ +#define PBCH_NR_N (1U << PBCH_NR_POLAR_N_MAX) + +/* + * Number of RM bits + */ +#define PBCH_NR_E (864) + +/* + * Number of symbols + */ +#define PBCH_NR_M (PBCH_NR_E / 2) + +static int pbch_nr_init_encoder(srsran_pbch_nr_t* q, const srsran_pbch_nr_args_t* args) +{ + // Skip encoder init if not requested + if (!args->enable_encode) { + return SRSRAN_SUCCESS; + } + + srsran_polar_encoder_type_t encoder_type = SRSRAN_POLAR_ENCODER_PIPELINED; + +#ifdef LV_HAVE_AVX2 + if (!args->disable_simd) { + encoder_type = SRSRAN_POLAR_ENCODER_AVX2; + } +#endif /* LV_HAVE_AVX2 */ + + if (srsran_polar_encoder_init(&q->polar_encoder, encoder_type, PBCH_NR_POLAR_N_MAX) < SRSRAN_SUCCESS) { + ERROR("Error initiating polar encoder"); + return SRSRAN_ERROR; + } + + if (srsran_polar_rm_tx_init(&q->polar_rm_tx) < SRSRAN_SUCCESS) { + ERROR("Error initiating polar RM"); + return SRSRAN_ERROR; + } + + if (srsran_modem_table_lte(&q->qpsk, SRSRAN_MOD_QPSK) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +static int pbch_nr_init_decoder(srsran_pbch_nr_t* q, const srsran_pbch_nr_args_t* args) +{ + // Skip decoder init if not requested + if (!args->enable_decode) { + return SRSRAN_SUCCESS; + } + + srsran_polar_decoder_type_t decoder_type = SRSRAN_POLAR_DECODER_SSC_C; + +#ifdef LV_HAVE_AVX2 + if (!args->disable_simd) { + decoder_type = SRSRAN_POLAR_DECODER_SSC_C_AVX2; + } +#endif /* LV_HAVE_AVX2 */ + + if (srsran_polar_decoder_init(&q->polar_decoder, decoder_type, PBCH_NR_POLAR_N_MAX) < SRSRAN_SUCCESS) { + ERROR("Error initiating polar decoder"); + return SRSRAN_ERROR; + } + + if (srsran_polar_rm_rx_init_c(&q->polar_rm_rx) < SRSRAN_SUCCESS) { + ERROR("Error initiating polar RM"); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +int srsran_pbch_nr_init(srsran_pbch_nr_t* q, const srsran_pbch_nr_args_t* args) +{ + if (q == NULL || args == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + if (!args->enable_encode && !args->enable_decode) { + ERROR("Encoder and decoder are disabled, at least one of them shall be active"); + return SRSRAN_ERROR; + } + + if (pbch_nr_init_encoder(q, args) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + if (pbch_nr_init_decoder(q, args) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + if (srsran_crc_init(&q->crc, PBCH_NR_CRC, PBCH_NR_CRC_LEN) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + if (srsran_polar_code_init(&q->code) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + if (srsran_polar_code_get(&q->code, PBCH_NR_K, PBCH_NR_E, PBCH_NR_POLAR_N_MAX) < SRSRAN_SUCCESS) { + ERROR("Error Getting polar code"); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +void srsran_pbch_nr_free(srsran_pbch_nr_t* q) +{ + if (q == NULL) { + return; + } + srsran_polar_encoder_free(&q->polar_encoder); + srsran_polar_decoder_free(&q->polar_decoder); + srsran_polar_rm_rx_free_c(&q->polar_rm_rx); + srsran_polar_rm_tx_free(&q->polar_rm_tx); + srsran_polar_code_free(&q->code); + srsran_modem_table_free(&q->qpsk); + SRSRAN_MEM_ZERO(q, srsran_pbch_nr_t, 1); +} + +/* + * Implements TS 38.212 Table 7.1.1-1: Value of PBCH payload interleaver pattern G ( j ) + */ +static const uint32_t G[PBCH_NR_A] = {16, 23, 18, 17, 8, 30, 10, 6, 24, 7, 0, 5, 3, 2, 1, 4, + 9, 11, 12, 13, 14, 15, 19, 20, 21, 22, 25, 26, 27, 28, 29, 31}; + +static void +pbch_nr_pbch_msg_pack(const srsran_pbch_nr_cfg_t* cfg, const srsran_pbch_msg_nr_t* msg, uint8_t a[PBCH_NR_A]) +{ + // Extract actual payload size + uint32_t A_hat = SRSRAN_PBCH_NR_PAYLOAD_SZ; + + // Put SFN in a_hat[A_hat] to a_hat[A_hat + 3] + uint32_t j_sfn = 0; + a[G[j_sfn++]] = (uint8_t)((msg->sfn_4lsb >> 3U) & 1U); // 4th LSB of SFN + a[G[j_sfn++]] = (uint8_t)((msg->sfn_4lsb >> 2U) & 1U); // 3th LSB of SFN + a[G[j_sfn++]] = (uint8_t)((msg->sfn_4lsb >> 1U) & 1U); // 2th LSB of SFN + a[G[j_sfn++]] = (uint8_t)((msg->sfn_4lsb >> 0U) & 1U); // 1th LSB of SFN + + // Put HRF in a_hat[A_hat + 4] + a[G[10]] = (msg->hrf ? 1 : 0); + + // Put SSB related in a_hat[A_hat + 5] to a_hat[A_hat + 7] + if (cfg->Lmax == 64) { + a[G[11]] = (uint8_t)((msg->ssb_idx >> 5U) & 1U); // 6th bit of SSB index + a[G[12]] = (uint8_t)((msg->ssb_idx >> 4U) & 1U); // 5th bit of SSB index + a[G[13]] = (uint8_t)((msg->ssb_idx >> 3U) & 1U); // 4th bit of SSB index + } else { + a[G[11]] = (uint8_t)msg->k_ssb_msb; // 6th bit of SSB index + a[G[12]] = 0; // Reserved + a[G[13]] = 0; // Reserved + } + + // Put actual payload + uint32_t j_other = 14; + for (uint32_t i = 0; i < A_hat; i++) { + if (i > 0 && i < 7) { + a[G[j_sfn++]] = msg->payload[i]; + } else { + a[G[j_other++]] = msg->payload[i]; + } + } + + if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { + PBCH_NR_DEBUG_TX("Packed PBCH bits: "); + srsran_vec_fprint_byte(stdout, a, PBCH_NR_A); + } +} +static void +pbch_nr_pbch_msg_unpack(const srsran_pbch_nr_cfg_t* cfg, const uint8_t a[PBCH_NR_A], srsran_pbch_msg_nr_t* msg) +{ + if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { + PBCH_NR_DEBUG_RX("Packed PBCH bits: "); + srsran_vec_fprint_byte(stdout, a, PBCH_NR_A); + } + + // Extract actual payload size + uint32_t A_hat = SRSRAN_PBCH_NR_PAYLOAD_SZ; + + // Put SFN in a_hat[A_hat] to a_hat[A_hat + 3] + uint32_t j_sfn = 0; + msg->sfn_4lsb = 0; + msg->sfn_4lsb |= (uint8_t)(a[G[j_sfn++]] << 3U); // 4th LSB of SFN + msg->sfn_4lsb |= (uint8_t)(a[G[j_sfn++]] << 2U); // 3th LSB of SFN + msg->sfn_4lsb |= (uint8_t)(a[G[j_sfn++]] << 1U); // 2th LSB of SFN + msg->sfn_4lsb |= (uint8_t)(a[G[j_sfn++]] << 0U); // 1th LSB of SFN + + // Put HRF in a_hat[A_hat + 4] + msg->hrf = (a[G[10]] == 1); + + // Put SSB related in a_hat[A_hat + 5] to a_hat[A_hat + 7] + if (cfg->Lmax == 64) { + msg->ssb_idx = msg->ssb_idx & 0b111; + msg->ssb_idx |= (uint8_t)(a[G[11]] << 5U); // 6th bit of SSB index + msg->ssb_idx |= (uint8_t)(a[G[12]] << 4U); // 5th bit of SSB index + msg->ssb_idx |= (uint8_t)(a[G[13]] << 3U); // 4th bit of SSB index + } else { + msg->k_ssb_msb = a[G[11]]; + } + + // Put actual payload + uint32_t j_other = 14; + for (uint32_t i = 0; i < A_hat; i++) { + if (i > 0 && i < 7) { + msg->payload[i] = a[G[j_sfn++]]; + } else { + msg->payload[i] = a[G[j_other++]]; + } + } +} + +static void pbch_nr_scramble(const srsran_pbch_nr_cfg_t* cfg, const uint8_t a[PBCH_NR_A], uint8_t a_prime[PBCH_NR_A]) +{ + uint32_t i = 0; + uint32_t j = 0; + + // Initialise sequence + srsran_sequence_state_t sequence_state = {}; + srsran_sequence_state_init(&sequence_state, SRSRAN_SEQUENCE_MOD(cfg->N_id)); + + // Select value M + uint32_t M = PBCH_NR_A - 3; + if (cfg->Lmax == 64) { + M = PBCH_NR_A - 6; + } + + // Select value v + uint32_t v = 2 * a[G[1]] + a[G[2]]; + + // Advance sequence + srsran_sequence_state_advance(&sequence_state, M * v); + + // Generate actual sequence + uint8_t c[PBCH_NR_A] = {}; + srsran_sequence_state_apply_bit(&sequence_state, c, c, PBCH_NR_A); + + while (i < PBCH_NR_A) { + // a i corresponds to any one of the bits belonging to the SS/PBCH block index, the half frame index, and 2 nd and 3 + // rd least significant bits of the system frame number + uint8_t s_i = c[j]; + + // else + if (i == G[11] || i == G[12] || i == G[13] || i == G[10] || i == G[1] || i == G[2]) { + s_i = 0; + } else { + j++; + } + + a_prime[i] = a[i] ^ s_i; + i++; + } +} + +static int pbch_nr_polar_encode(srsran_pbch_nr_t* q, const uint8_t c[PBCH_NR_K], uint8_t d[PBCH_NR_N]) +{ + // Interleave + uint8_t c_prime[SRSRAN_POLAR_INTERLEAVER_K_MAX_IL]; + srsran_polar_interleaver_run_u8(c, c_prime, PBCH_NR_K, true); + + // Allocate channel + uint8_t allocated[PBCH_NR_N]; + srsran_polar_chanalloc_tx(c_prime, allocated, q->code.N, q->code.K, q->code.nPC, q->code.K_set, q->code.PC_set); + + // Encode bits + if (srsran_polar_encoder_encode(&q->polar_encoder, allocated, d, q->code.n) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { + PBCH_NR_DEBUG_TX("Allocated: "); + srsran_vec_fprint_byte(stdout, allocated, PBCH_NR_N); + } + + return SRSRAN_SUCCESS; +} + +static int pbch_nr_polar_decode(srsran_pbch_nr_t* q, const int8_t d[PBCH_NR_N], uint8_t c[PBCH_NR_K]) +{ + // Decode bits + uint8_t allocated[PBCH_NR_N]; + if (srsran_polar_decoder_decode_c(&q->polar_decoder, d, allocated, q->code.n, q->code.F_set, q->code.F_set_size) < + SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { + PBCH_NR_DEBUG_RX("Allocated: "); + srsran_vec_fprint_byte(stdout, allocated, PBCH_NR_N); + } + + // Allocate channel + uint8_t c_prime[SRSRAN_POLAR_INTERLEAVER_K_MAX_IL]; + srsran_polar_chanalloc_rx(allocated, c_prime, q->code.K, q->code.nPC, q->code.K_set, q->code.PC_set); + + // Interleave + srsran_polar_interleaver_run_u8(c_prime, c, PBCH_NR_K, false); + + return SRSRAN_SUCCESS; +} + +static int pbch_nr_polar_rm_tx(srsran_pbch_nr_t* q, const uint8_t d[PBCH_NR_N], uint8_t o[PBCH_NR_E]) +{ + if (srsran_polar_rm_tx(&q->polar_rm_tx, d, o, q->code.n, PBCH_NR_E, PBCH_NR_K, PBCH_NR_POLAR_RM_IBIL) < + SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { + PBCH_NR_DEBUG_TX("d: "); + srsran_vec_fprint_byte(stdout, d, PBCH_NR_N); + } + + return SRSRAN_SUCCESS; +} + +static int pbch_nr_polar_rm_rx(srsran_pbch_nr_t* q, const int8_t llr[PBCH_NR_E], int8_t d[PBCH_NR_N]) +{ + if (srsran_polar_rm_rx_c(&q->polar_rm_rx, llr, d, PBCH_NR_E, q->code.n, PBCH_NR_K, PBCH_NR_POLAR_RM_IBIL) < + SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // Negate all LLR + for (uint32_t i = 0; i < PBCH_NR_N; i++) { + d[i] *= -1; + } + + if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { + PBCH_NR_DEBUG_RX("d: "); + srsran_vec_fprint_bs(stdout, d, PBCH_NR_N); + } + + return SRSRAN_SUCCESS; +} + +static void pbch_nr_scramble_tx(const srsran_pbch_nr_cfg_t* cfg, + uint32_t ssb_idx, + const uint8_t b[PBCH_NR_E], + uint8_t b_hat[PBCH_NR_E]) +{ + // Initialise sequence + srsran_sequence_state_t sequence_state = {}; + srsran_sequence_state_init(&sequence_state, SRSRAN_SEQUENCE_MOD(cfg->N_id)); + + // Select value M + uint32_t M_bit = PBCH_NR_E; + + // Select value v + uint32_t v = (ssb_idx & 0x7U); + if (cfg->Lmax == 4) { + v = ssb_idx & 0x3U; + } + + // Advance sequence + srsran_sequence_state_advance(&sequence_state, v * M_bit); + + // Apply sequence + srsran_sequence_state_apply_bit(&sequence_state, b, b_hat, PBCH_NR_E); +} + +static void pbch_nr_scramble_rx(const srsran_pbch_nr_cfg_t* cfg, + uint32_t ssb_idx, + const int8_t b_hat[PBCH_NR_E], + int8_t b[PBCH_NR_E]) +{ + // Initialise sequence + srsran_sequence_state_t sequence_state = {}; + srsran_sequence_state_init(&sequence_state, SRSRAN_SEQUENCE_MOD(cfg->N_id)); + + // Select value M + uint32_t M_bit = PBCH_NR_E; + + // Select value v + uint32_t v = (ssb_idx & 0x7U); + if (cfg->Lmax == 4) { + v = ssb_idx & 0x3U; + } + + // Advance sequence + srsran_sequence_state_advance(&sequence_state, v * M_bit); + + // Apply sequence + srsran_sequence_state_apply_c(&sequence_state, b_hat, b, PBCH_NR_E); +} + +static void +pbch_nr_mapping(const srsran_pbch_nr_cfg_t* cfg, const cf_t symbols[PBCH_NR_M], cf_t ssb_grid[SRSRAN_SSB_NOF_RE]) +{ + uint32_t count = 0; + + // PBCH DMRS shift + uint32_t v = cfg->N_id % 4; + + // Symbol 1 + for (uint32_t k = 0; k < SRSRAN_SSB_BW_SUBC; k++) { + // Skip DMRS + if (k % 4 == v) { + continue; + } + + ssb_grid[1 * SRSRAN_SSB_BW_SUBC + k] = symbols[count++]; + } + + // Symbol 2 + for (uint32_t k = 0; k < 48; k++) { + // Skip DMRS + if (k % 4 == v) { + continue; + } + + ssb_grid[2 * SRSRAN_SSB_BW_SUBC + k] = symbols[count++]; + } + for (uint32_t k = 192; k < SRSRAN_SSB_BW_SUBC; k++) { + // Skip DMRS + if (k % 4 == v) { + continue; + } + + ssb_grid[2 * SRSRAN_SSB_BW_SUBC + k] = symbols[count++]; + } + + // Symbol 3 + for (uint32_t k = 0; k < SRSRAN_SSB_BW_SUBC; k++) { + // Skip DMRS + if (k % 4 == v) { + continue; + } + + ssb_grid[3 * SRSRAN_SSB_BW_SUBC + k] = symbols[count++]; + } + + // if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { + // PBCH_NR_DEBUG_TX("Symbols: "); + // srsran_vec_fprint_c(stdout, symbols, PBCH_NR_M); + // } +} + +static void +pbch_nr_demapping(const srsran_pbch_nr_cfg_t* cfg, const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], cf_t symbols[PBCH_NR_M]) +{ + uint32_t count = 0; + + // PBCH DMRS shift + uint32_t v = cfg->N_id % 4; + + // Symbol 1 + for (uint32_t k = 0; k < SRSRAN_SSB_BW_SUBC; k++) { + // Skip DMRS + if (k % 4 == v) { + continue; + } + + symbols[count++] = ssb_grid[1 * SRSRAN_SSB_BW_SUBC + k]; + } + + // Symbol 2 + for (uint32_t k = 0; k < 48; k++) { + // Skip DMRS + if (k % 4 == v) { + continue; + } + + symbols[count++] = ssb_grid[2 * SRSRAN_SSB_BW_SUBC + k]; + } + for (uint32_t k = 192; k < SRSRAN_SSB_BW_SUBC; k++) { + // Skip DMRS + if (k % 4 == v) { + continue; + } + + symbols[count++] = ssb_grid[2 * SRSRAN_SSB_BW_SUBC + k]; + } + + // Symbol 3 + for (uint32_t k = 0; k < SRSRAN_SSB_BW_SUBC; k++) { + // Skip DMRS + if (k % 4 == v) { + continue; + } + + symbols[count++] = ssb_grid[3 * SRSRAN_SSB_BW_SUBC + k]; + } + + // if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { + // PBCH_NR_DEBUG_RX("Symbols: "); + // srsran_vec_fprint_c(stdout, symbols, PBCH_NR_M); + // } +} + +int srsran_pbch_nr_encode(srsran_pbch_nr_t* q, + const srsran_pbch_nr_cfg_t* cfg, + const srsran_pbch_msg_nr_t* msg, + cf_t ssb_grid[SRSRAN_SSB_NOF_RE]) +{ + if (q == NULL || cfg == NULL || msg == NULL || ssb_grid == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // TS 38.212 7.1 Broadcast channel + // 7.1.1 PBCH payload generation + uint8_t a[PBCH_NR_A]; + pbch_nr_pbch_msg_pack(cfg, msg, a); + + // 7.1.2 Scrambling + uint8_t b[PBCH_NR_K]; + pbch_nr_scramble(cfg, a, b); + + // 7.1.3 Transport block CRC attachment + uint32_t checksum = srsran_crc_attach(&q->crc, b, PBCH_NR_A); + PBCH_NR_DEBUG_TX("checksum=%06x", checksum); + + // 7.1.4 Channel coding + uint8_t d[PBCH_NR_N]; + if (pbch_nr_polar_encode(q, b, d) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // 7.1.5 Rate matching + uint8_t f[PBCH_NR_E]; + if (pbch_nr_polar_rm_tx(q, d, f) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // TS 38.211 7.3.3 Physical broadcast channel + // 7.3.3.1 Scrambling + pbch_nr_scramble_tx(cfg, msg->ssb_idx, f, f); + + // 7.3.3.2 Modulation + cf_t symbols[PBCH_NR_M]; + srsran_mod_modulate(&q->qpsk, f, symbols, PBCH_NR_E); + + // 7.3.3.3 Mapping to physical resources + // 7.4.3.1.3 Mapping of PBCH and DM-RS within an SS/PBCH block + pbch_nr_mapping(cfg, symbols, ssb_grid); + + return SRSRAN_SUCCESS; +} + +int srsran_pbch_nr_decode(srsran_pbch_nr_t* q, + const srsran_pbch_nr_cfg_t* cfg, + uint32_t ssb_idx, + const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], + const cf_t ce_grid[SRSRAN_SSB_NOF_RE], + srsran_pbch_msg_nr_t* msg) +{ + if (q == NULL || cfg == NULL || msg == NULL || ssb_grid == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // 7.3.3.3 Mapping to physical resources + // 7.4.3.1.3 Mapping of PBCH and DM-RS within an SS/PBCH block + srsran_simd_aligned cf_t symbols[PBCH_NR_M]; + pbch_nr_demapping(cfg, ssb_grid, symbols); + + srsran_simd_aligned cf_t ce[PBCH_NR_M]; + pbch_nr_demapping(cfg, ce_grid, ce); + + // Channel equalizer + if (srsran_predecoding_single(symbols, ce, symbols, NULL, PBCH_NR_M, 1.0f, 0.0f) < SRSRAN_SUCCESS) { + ERROR("Error in predecoder"); + return SRSRAN_ERROR; + } + + // 7.3.3.2 Modulation + int8_t llr[PBCH_NR_E]; + srsran_demod_soft_demodulate_b(SRSRAN_MOD_QPSK, symbols, llr, PBCH_NR_M); + + // TS 38.211 7.3.3 Physical broadcast channel + // 7.3.3.1 Scrambling + pbch_nr_scramble_rx(cfg, ssb_idx, llr, llr); + + // 7.1.5 Rate matching + int8_t d[PBCH_NR_N]; + if (pbch_nr_polar_rm_rx(q, llr, d) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // TS 38.212 7.1 Broadcast channel + // 7.1.4 Channel coding + uint8_t b[PBCH_NR_K]; + if (pbch_nr_polar_decode(q, d, b) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // 7.1.3 Transport block CRC attachment + msg->crc = srsran_crc_match(&q->crc, b, PBCH_NR_A); + PBCH_NR_DEBUG_RX("crc=%s", msg->crc ? "OK" : "KO"); + + // 7.1.2 Scrambling + uint8_t a[PBCH_NR_A]; + pbch_nr_scramble(cfg, b, a); + + // 7.1.1 PBCH payload generation + pbch_nr_pbch_msg_unpack(cfg, a, msg); + + return SRSRAN_SUCCESS; +} + +uint32_t srsran_pbch_msg_info(const srsran_pbch_msg_nr_t* msg, char* str, uint32_t str_len) +{ + if (msg == NULL || str == NULL || str_len == 0) { + return 0; + } + + uint32_t len = 0; + + len = srsran_print_check(str, str_len, len, "payload="); + + len += srsran_vec_sprint_hex(&str[len], str_len - len, (uint8_t*)msg->payload, SRSRAN_PBCH_NR_PAYLOAD_SZ); + + len = srsran_print_check(str, + str_len, + len, + " sfn_lsb=%d ssb_idx=%d k_ssb_msb=%d hrf=%d ", + msg->sfn_4lsb, + msg->ssb_idx, + msg->k_ssb_msb, + msg->hrf); + + return len; +} diff --git a/lib/src/phy/phch/pdcch.c b/lib/src/phy/phch/pdcch.c index e07c3a195..adbeebb5d 100644 --- a/lib/src/phy/phch/pdcch.c +++ b/lib/src/phy/phch/pdcch.c @@ -19,6 +19,7 @@ * */ +#include #include #include #include @@ -45,7 +46,8 @@ float srsran_pdcch_coderate(uint32_t nof_bits, uint32_t l) { - return (float)(nof_bits + 16) / (4 * PDCCH_FORMAT_NOF_REGS(l)); + static const int nof_bits_x_symbol = 2; // QPSK + return (float)(nof_bits + 16) / (nof_bits_x_symbol * PDCCH_FORMAT_NOF_REGS(l)); } /** Initializes the PDCCH transmitter and receiver */ @@ -385,12 +387,14 @@ int srsran_pdcch_decode_msg(srsran_pdcch_t* q, srsran_dl_sf_cfg_t* sf, srsran_dc uint32_t nof_bits = srsran_dci_format_sizeof(&q->cell, sf, dci_cfg, msg->format); uint32_t e_bits = PDCCH_FORMAT_NOF_BITS(msg->location.L); + // Compute absolute mean of the LLRs double mean = 0; for (int i = 0; i < e_bits; i++) { mean += fabsf(q->llr[msg->location.ncce * 72 + i]); } mean /= e_bits; - if (mean > 0.3) { + + if (mean > 0.3f) { ret = srsran_pdcch_dci_decode(q, &q->llr[msg->location.ncce * 72], msg->payload, e_bits, nof_bits, &msg->rnti); if (ret == SRSRAN_SUCCESS) { msg->nof_bits = nof_bits; @@ -418,6 +422,27 @@ int srsran_pdcch_decode_msg(srsran_pdcch_t* q, srsran_dl_sf_cfg_t* sf, srsran_dc return ret; } +float srsran_pdcch_msg_corr(srsran_pdcch_t* q, srsran_dci_msg_t* msg) +{ + if (q == NULL || msg == NULL) { + return 0.0f; + } + + uint32_t E = PDCCH_FORMAT_NOF_BITS(msg->location.L); + uint32_t nof_llr = E / 2; + + // Encode same decoded message and compute correlation + srsran_pdcch_dci_encode(q, msg->payload, q->e, msg->nof_bits, E, msg->rnti); + + // Modulate + srsran_mod_modulate(&q->mod, q->e, q->d, E); + + // Correlate + cf_t corr = srsran_vec_dot_prod_conj_ccc((cf_t*)&q->llr[msg->location.ncce * 72], q->d, nof_llr); + + return cabsf(corr / nof_llr) * (float)M_SQRT1_2; +} + /** Performs PDCCH receiver processing to extract LLR for all control region. LLR bits are stored in srsran_pdcch_t * object. DCI can be decoded from given locations in successive calls to srsran_pdcch_decode_msg() */ diff --git a/lib/src/phy/phch/pdsch_nr.c b/lib/src/phy/phch/pdsch_nr.c index 7fcce240e..8aa58013d 100644 --- a/lib/src/phy/phch/pdsch_nr.c +++ b/lib/src/phy/phch/pdsch_nr.c @@ -491,7 +491,7 @@ int srsran_pdsch_nr_decode(srsran_pdsch_nr_t* q, srsran_pdsch_res_nr_t* data) { // Check input pointers - if (!q || !cfg || !grant || !data || !sf_symbols) { + if (!q || !cfg || !grant || !data || !sf_symbols || !channel) { return SRSRAN_ERROR_INVALID_INPUTS; } @@ -537,8 +537,7 @@ int srsran_pdsch_nr_decode(srsran_pdsch_nr_t* q, // Antenna port demapping // ... Not implemented - srsran_predecoding_type( - q->x, channel->ce, q->d, NULL, 1, 1, 1, 0, nof_re, SRSRAN_TXSCHEME_PORT0, 1.0f, channel->noise_estimate); + srsran_predecoding_single(q->x[0], channel->ce[0][0], q->d[0], NULL, nof_re, 1.0f, channel->noise_estimate); // Layer demapping if (grant->nof_layers > 1) { diff --git a/lib/src/phy/phch/pssch.c b/lib/src/phy/phch/pssch.c index ec92751cd..8a6594c55 100644 --- a/lib/src/phy/phch/pssch.c +++ b/lib/src/phy/phch/pssch.c @@ -210,6 +210,8 @@ int srsran_pssch_init(srsran_pssch_t* q, ERROR("Error allocating memory"); return SRSRAN_ERROR; } + srsran_vec_cf_zero(q->scfdma_symbols, q->nof_data_symbols * SRSRAN_NRE * SRSRAN_MAX_PRB); + if (srsran_dft_precoding_init(&q->dft_precoder, SRSRAN_MAX_PRB, true)) { ERROR("Error DFT precoder init"); return SRSRAN_ERROR; diff --git a/lib/src/phy/phch/pusch_nr.c b/lib/src/phy/phch/pusch_nr.c index 14d23bc4b..f28a4a87a 100644 --- a/lib/src/phy/phch/pusch_nr.c +++ b/lib/src/phy/phch/pusch_nr.c @@ -918,10 +918,10 @@ int srsran_pusch_nr_decode(srsran_pusch_nr_t* q, const srsran_sch_grant_nr_t* grant, srsran_chest_dl_res_t* channel, cf_t* sf_symbols[SRSRAN_MAX_PORTS], - srsran_pusch_res_nr_t* data) + srsran_pusch_res_nr_t* data[SRSRAN_MAX_TB]) { // Check input pointers - if (!q || !cfg || !grant || !data || !sf_symbols) { + if (!q || !cfg || !grant || !data || !sf_symbols || !channel) { return SRSRAN_ERROR_INVALID_INPUTS; } @@ -978,8 +978,7 @@ int srsran_pusch_nr_decode(srsran_pusch_nr_t* q, // Antenna port demapping // ... Not implemented - srsran_predecoding_type( - q->x, channel->ce, q->d, NULL, 1, 1, 1, 0, nof_re, SRSRAN_TXSCHEME_PORT0, 1.0f, channel->noise_estimate); + srsran_predecoding_single(q->x[0], channel->ce[0][0], q->d[0], NULL, nof_re, 1.0f, channel->noise_estimate); // Layer demapping if (grant->nof_layers > 1) { @@ -988,7 +987,7 @@ int srsran_pusch_nr_decode(srsran_pusch_nr_t* q, // SCH decode for (uint32_t tb = 0; tb < SRSRAN_MAX_TB; tb++) { - if (pusch_nr_decode_codeword(q, cfg, &grant->tb[tb], data, grant->rnti) < SRSRAN_SUCCESS) { + if (pusch_nr_decode_codeword(q, cfg, &grant->tb[tb], data[0], grant->rnti) < SRSRAN_SUCCESS) { ERROR("Error encoding TB %d", tb); return SRSRAN_ERROR; } diff --git a/lib/src/phy/phch/sch.c b/lib/src/phy/phch/sch.c index 4cbdc7ca9..d784172e1 100644 --- a/lib/src/phy/phch/sch.c +++ b/lib/src/phy/phch/sch.c @@ -550,7 +550,17 @@ static int decode_tb(srsran_sch_t* q, par_tx = ((uint32_t)data[cb_segm->tbs / 8 + 0]) << 16 | ((uint32_t)data[cb_segm->tbs / 8 + 1]) << 8 | ((uint32_t)data[cb_segm->tbs / 8 + 2]); - if (par_rx == par_tx && par_rx) { + // Check if all the bytes are zeros + bool all_zeros = true; + for (uint32_t i = 0; i < cb_segm->tbs / 8 && all_zeros; i++) { + all_zeros = (data[i] == 0); + } + if (all_zeros) { + INFO("Error in TB decode: it is all zeros!"); + return SRSRAN_ERROR; + } + + if (par_rx == par_tx) { INFO("TB decoded OK"); return SRSRAN_SUCCESS; } else { diff --git a/lib/src/phy/phch/test/CMakeLists.txt b/lib/src/phy/phch/test/CMakeLists.txt index 72968f832..dfdd76813 100644 --- a/lib/src/phy/phch/test/CMakeLists.txt +++ b/lib/src/phy/phch/test/CMakeLists.txt @@ -210,19 +210,20 @@ add_lte_test(phich_test_104 phich_test -p 4 -n 10 -e -l -g 1/2) add_executable(pdcch_test pdcch_test.c) target_link_libraries(pdcch_test srsran_phy) -add_lte_test(pdcch_test_6 pdcch_test -n 6) -add_lte_test(pdcch_test_15 pdcch_test -n 15) -add_lte_test(pdcch_test_25 pdcch_test -n 25) -add_lte_test(pdcch_test_50 pdcch_test -n 50) -add_lte_test(pdcch_test_75 pdcch_test -n 75) -add_lte_test(pdcch_test_100 pdcch_test -n 100) -add_lte_test(pdcch_test_6_mimo pdcch_test -n 6 -p 2) -add_lte_test(pdcch_test_15_mimo pdcch_test -n 15 -p 2) -add_lte_test(pdcch_test_25_mimo pdcch_test -n 25 -p 2) -add_lte_test(pdcch_test_50_mimo pdcch_test -n 50 -p 2) -add_lte_test(pdcch_test_75_mimo pdcch_test -n 75 -p 2) -add_lte_test(pdcch_test_100_mimo pdcch_test -n 100 -p 2) -#add_lte_test(pdcch_test_crosscarrier pdcch_test -x) +foreach (nof_prb 6 15 25 50 75 100) + foreach (nof_ports 1 2) + foreach (cfi 1 2 3) + foreach (snr auto 15 300) + set(pdcch_test_args "") + set(pdcch_test_args -n ${nof_prb} -p ${nof_ports} -f ${cfi} -S ${snr} -R 1 -F) + + string(REGEX REPLACE "\ " "" test_name_args ${pdcch_test_args}) + + add_lte_test(pdcch_test${test_name_args} pdcch_test ${pdcch_test_args}) + endforeach () + endforeach () + endforeach () +endforeach () ######################################################################## # PDSCH TEST @@ -465,11 +466,7 @@ add_lte_test(npdsch_npdcch_dci_formatN1_test npdsch_npdcch_file_test -c 0 -s 1 - add_executable(pusch_test pusch_test.c) target_link_libraries(pusch_test srsran_phy) -if (NOT DEFINED TEST_EXTENSION) - set(TEST_EXTENSION Normal) -endif(NOT DEFINED TEST_EXTENSION) - -if (TEST_EXTENSION STREQUAL Paranoid) +if (${ENABLE_ALL_TEST}) # All valid number of PRBs for PUSCH set(cell_n_prb_valid 1 2 3 4 5 6 8 9 10 12 15 16 18 20 24 25 27 30 32 36 40 45 48 50 54 60 64 72 75 80 81 90 96 100) @@ -481,7 +478,7 @@ if (TEST_EXTENSION STREQUAL Paranoid) set(pusch_cqi none wideband) -else (TEST_EXTENSION STREQUAL Paranoid) +else () set(cell_n_prb_valid 50) set(pusch_min_mcs 0) @@ -492,7 +489,7 @@ else (TEST_EXTENSION STREQUAL Paranoid) set(pusch_cqi none wideband) -endif (TEST_EXTENSION STREQUAL Paranoid) +endif () foreach (cell_n_prb 6 15 25 50 75 100) set(pusch_cell_n_prb) diff --git a/lib/src/phy/phch/test/pbch_file_test.c b/lib/src/phy/phch/test/pbch_file_test.c index 73d61820c..3d08de751 100644 --- a/lib/src/phy/phch/test/pbch_file_test.c +++ b/lib/src/phy/phch/test/pbch_file_test.c @@ -48,7 +48,7 @@ uint8_t bch_payload_file[SRSRAN_BCH_PAYLOAD_LEN] = {0, 1, 1, 0, 1, 0, 0, 0, 0, 0 #define FLEN (10 * SRSRAN_SF_LEN(srsran_symbol_sz(cell.nof_prb))) srsran_filesource_t fsrc; -cf_t * input_buffer, *fft_buffer[SRSRAN_MAX_CODEWORDS]; +cf_t * input_buffer, *fft_buffer[SRSRAN_MAX_PORTS]; srsran_pbch_t pbch; srsran_ofdm_t fft; srsran_chest_dl_t chest; diff --git a/lib/src/phy/phch/test/pcfich_file_test.c b/lib/src/phy/phch/test/pcfich_file_test.c index cfd97b0cb..fba2086fc 100644 --- a/lib/src/phy/phch/test/pcfich_file_test.c +++ b/lib/src/phy/phch/test/pcfich_file_test.c @@ -42,7 +42,7 @@ int flen; FILE* fmatlab = NULL; srsran_filesource_t fsrc; -cf_t * input_buffer, *fft_buffer[SRSRAN_MAX_CODEWORDS]; +cf_t * input_buffer, *fft_buffer[SRSRAN_MAX_PORTS]; srsran_pcfich_t pcfich; srsran_regs_t regs; srsran_ofdm_t fft; diff --git a/lib/src/phy/phch/test/pdcch_file_test.c b/lib/src/phy/phch/test/pdcch_file_test.c index 75e981bfb..6596da134 100644 --- a/lib/src/phy/phch/test/pdcch_file_test.c +++ b/lib/src/phy/phch/test/pdcch_file_test.c @@ -48,7 +48,7 @@ int max_frames = 10; srsran_dci_format_t dci_format = SRSRAN_DCI_FORMAT1A; srsran_filesource_t fsrc; srsran_pdcch_t pdcch; -cf_t * input_buffer, *fft_buffer[SRSRAN_MAX_CODEWORDS]; +cf_t * input_buffer, *fft_buffer[SRSRAN_MAX_PORTS]; srsran_regs_t regs; srsran_ofdm_t fft; srsran_chest_dl_t chest; diff --git a/lib/src/phy/phch/test/pdcch_test.c b/lib/src/phy/phch/test/pdcch_test.c index 2571f0782..d11574fa6 100644 --- a/lib/src/phy/phch/test/pdcch_test.c +++ b/lib/src/phy/phch/test/pdcch_test.c @@ -24,58 +24,70 @@ #include #include +#include "srsran/common/test_common.h" #include "srsran/srsran.h" -srsran_cell_t cell = {.nof_prb = 6, - .nof_ports = 1, - .id = 1, - .cp = SRSRAN_CP_NORM, - .phich_resources = SRSRAN_PHICH_R_1, - .phich_length = SRSRAN_PHICH_NORM}; - -uint32_t cfi = 1; -uint32_t nof_rx_ant = 1; -bool print_dci_table; -srsran_dci_cfg_t dci_cfg = {}; - -void usage(char* prog) +// Test parameters +static uint32_t pci = 1; +static uint16_t rnti = 0x46; +static uint32_t cfi = 2; +static uint32_t nof_ports = 1; +static srsran_dci_cfg_t dci_cfg = {}; +static uint32_t nof_prb = 100; +static float snr_dB = NAN; +static uint32_t repetitions = 1; +static bool false_check = false; + +// Test objects +static srsran_random_t random_gen = NULL; +static srsran_pdcch_t pdcch_tx = {}; +static srsran_pdcch_t pdcch_rx = {}; +static srsran_chest_dl_res_t chest_dl_res = {}; +static srsran_channel_awgn_t awgn = {}; +static cf_t* slot_symbols[SRSRAN_MAX_PORTS] = {}; + +static void usage(char* prog) { - printf("Usage: %s [cfpndxv]\n", prog); - printf("\t-c cell id [Default %d]\n", cell.id); + printf("Usage: %s [pfncxv]\n", prog); + printf("\t-c cell id [Default %d]\n", pci); printf("\t-f cfi [Default %d]\n", cfi); - printf("\t-p cell.nof_ports [Default %d]\n", cell.nof_ports); - printf("\t-n cell.nof_prb [Default %d]\n", cell.nof_prb); - printf("\t-A nof_rx_ant [Default %d]\n", nof_rx_ant); - printf("\t-d Print DCI table [Default %s]\n", print_dci_table ? "yes" : "no"); + printf("\t-p cell.nof_ports [Default %d]\n", nof_ports); + printf("\t-n cell.nof_prb [Default %d]\n", nof_prb); printf("\t-x Enable/Disable Cross-scheduling [Default %s]\n", dci_cfg.cif_enabled ? "enabled" : "disabled"); + printf("\t-F False detection check [Default %s]\n", false_check ? "enabled" : "disabled"); + printf("\t-R Repetitions [Default %d]\n", repetitions); + printf("\t-S SNR in dB [Default %+.1f]\n", snr_dB); printf("\t-v [set srsran_verbose to debug, default none]\n"); } -void parse_args(int argc, char** argv) +static void parse_args(int argc, char** argv) { int opt; - while ((opt = getopt(argc, argv, "cfpndvAx")) != -1) { + while ((opt = getopt(argc, argv, "pfncxvFRS")) != -1) { switch (opt) { case 'p': - cell.nof_ports = (uint32_t)strtol(argv[optind], NULL, 10); + nof_ports = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'f': cfi = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'n': - cell.nof_prb = (uint32_t)strtol(argv[optind], NULL, 10); + nof_prb = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'c': - cell.id = (uint32_t)strtol(argv[optind], NULL, 10); + pci = (uint32_t)strtol(argv[optind], NULL, 10); + break; + case 'x': + dci_cfg.cif_enabled = !dci_cfg.cif_enabled; break; - case 'A': - nof_rx_ant = (uint32_t)strtol(argv[optind], NULL, 10); + case 'F': + false_check = !false_check; break; - case 'd': - print_dci_table = true; + case 'R': + repetitions = (uint32_t)strtol(argv[optind], NULL, 10); break; - case 'x': - dci_cfg.cif_enabled ^= true; + case 'S': + snr_dB = (float)strtof(argv[optind], NULL); break; case 'v': srsran_verbose++; @@ -85,12 +97,32 @@ void parse_args(int argc, char** argv) exit(-1); } } + printf("params - pci=%d; rnti=0x%04x; cfi=%d; nof_ports=%d; cif_enabled=%d; nof_prb=%d; snr_db=%+.1f; " + "repetitions=%d; false_check=%d;\n", + pci, + rnti, + cfi, + nof_ports, + dci_cfg.cif_enabled, + nof_prb, + snr_dB, + repetitions, + false_check); } -int test_dci_payload_size() +static void print_dci_msg(const char* desc, const srsran_dci_msg_t* dci_msg) +{ + printf("%srnti=0x%04x; format=%s; L=%d; ncce=%d; payload=", + desc, + rnti, + srsran_dci_format_string(dci_msg->format), + dci_msg->location.L, + dci_msg->location.ncce); + srsran_vec_fprint_byte(stdout, dci_msg->payload, dci_msg->nof_bits); +} + +static int assert_payload_size(const srsran_cell_t* cell, srsran_dl_sf_cfg_t* dl_sf) { - int i, j; - int x[5]; const srsran_dci_format_t formats[] = { SRSRAN_DCI_FORMAT0, SRSRAN_DCI_FORMAT1, SRSRAN_DCI_FORMAT1A, SRSRAN_DCI_FORMAT1C, SRSRAN_DCI_FORMAT2A}; const int prb[6] = {6, 15, 25, 50, 75, 100}; @@ -101,297 +133,282 @@ int test_dci_payload_size() {27, 33, 27, 14, 42}, {28, 39, 28, 15, 48}}; - srsran_dl_sf_cfg_t dl_sf; - ZERO_OBJECT(dl_sf); - - srsran_cell_t cell_test; - ZERO_OBJECT(cell_test); - cell_test.nof_ports = 1; + // Skip if special options are requested + if (dci_cfg.cif_enabled || dci_cfg.multiple_csi_request_enabled) { + return SRSRAN_SUCCESS; + } - ZERO_OBJECT(dci_cfg); + // Skip if MIMO is enabled + if (cell->nof_ports > 1) { + return SRSRAN_SUCCESS; + } - printf("Testing DCI payload sizes...\n"); - printf(" PRB\t0\t1\t1A\t1C\t2A\n"); - for (i = 0; i < 6; i++) { - int n = prb[i]; - cell_test.nof_prb = n; + for (uint32_t i = 0; i < 6; i++) { + if (prb[i] != cell->nof_prb) { + continue; + } + int n = prb[i]; - for (j = 0; j < 5; j++) { - x[j] = srsran_dci_format_sizeof(&cell_test, &dl_sf, &dci_cfg, formats[j]); + uint32_t x[5]; + for (uint32_t j = 0; j < 5; j++) { + x[j] = srsran_dci_format_sizeof(cell, dl_sf, &dci_cfg, formats[j]); if (x[j] != dci_sz[i][j]) { ERROR("Invalid DCI payload size for %s and %d PRB. Is %d and should be %d", srsran_dci_format_string(formats[j]), n, x[j], dci_sz[i][j]); - return -1; - } - } - printf(" %2d:\t%2d\t%2d\t%2d\t%2d\t%2d\n", n, x[0], x[1], x[2], x[3], x[4]); - } - printf("Ok\n"); - - if (print_dci_table) { - printf("dci_sz_table[101][4] = {\n"); - for (i = 0; i <= 100; i++) { - printf(" {"); - for (j = 0; j < 4; j++) { - cell_test.nof_prb = i; - printf("%d", srsran_dci_format_sizeof(&cell, &dl_sf, &dci_cfg, formats[j])); - if (j < 3) { - printf(", "); - } - } - if (i < 100) { - printf("},\n"); - } else { - printf("}\n"); + return SRSRAN_ERROR; } } - printf("};\n"); + return SRSRAN_SUCCESS; } + return 0; } -typedef struct { - srsran_dci_msg_t dci_tx, dci_rx; - srsran_dci_location_t dci_location; - srsran_dci_format_t dci_format; - srsran_dci_dl_t ra_dl_tx; - srsran_dci_dl_t ra_dl_rx; -} testcase_dci_t; +static const srsran_dci_format_t formats[] = {SRSRAN_DCI_FORMAT0, + SRSRAN_DCI_FORMAT1A, + SRSRAN_DCI_FORMAT1, + SRSRAN_DCI_FORMAT2A, + SRSRAN_DCI_FORMAT2, + SRSRAN_DCI_NOF_FORMATS}; -int main(int argc, char** argv) +static float get_snr_dB(uint32_t L) { - srsran_chest_dl_res_t chest_dl_res; - srsran_pdcch_t pdcch_tx, pdcch_rx; - testcase_dci_t testcases[10]; - srsran_regs_t regs; - int i; - int nof_re; - cf_t* slot_symbols[SRSRAN_MAX_PORTS]; - int nof_dcis; + static const float snr_table_dB[4] = {15.0f, 6.0f, 5.0f, 0.0f}; - bzero(&testcases, sizeof(testcase_dci_t) * 10); - srsran_random_t random_gen = srsran_random_init(0x1234); + if (isnormal(snr_dB) && L < 4) { + return snr_dB; + } - int ret = -1; + return snr_table_dB[L]; +} - parse_args(argc, argv); +static int test_case1() +{ + uint32_t nof_re = SRSRAN_NOF_RE(pdcch_tx.cell); + + // Iterate all possible subframes + for (uint32_t f_idx = 0; formats[f_idx] != SRSRAN_DCI_NOF_FORMATS; f_idx++) { + srsran_dci_format_t format = formats[f_idx]; + struct timeval t[3] = {}; + uint64_t t_encode_us = 0; + uint64_t t_encode_count = 0; + uint64_t t_llr_us = 0; + uint64_t t_decode_us = 0; + uint64_t t_decode_count = 0; + uint32_t false_alarm_corr_count = 0; + float min_corr = INFINITY; + + for (uint32_t sf_idx = 0; sf_idx < repetitions * SRSRAN_NOF_SF_X_FRAME; sf_idx++) { + srsran_dl_sf_cfg_t dl_sf_cfg = {}; + dl_sf_cfg.cfi = cfi; + dl_sf_cfg.tti = sf_idx % 10240; + + // Generate PDCCH locations + srsran_dci_location_t locations[SRSRAN_MAX_CANDIDATES] = {}; + uint32_t locations_count = 0; + locations_count += + srsran_pdcch_common_locations(&pdcch_tx, &locations[locations_count], SRSRAN_MAX_CANDIDATES_COM, cfi); + locations_count += + srsran_pdcch_ue_locations(&pdcch_tx, &dl_sf_cfg, &locations[locations_count], SRSRAN_MAX_CANDIDATES_UE, rnti); + + // Iterate all possible locations + for (uint32_t loc = 0; loc < locations_count; loc++) { + srsran_dci_msg_t dci_tx = {}; + dci_tx.nof_bits = srsran_dci_format_sizeof(&pdcch_tx.cell, &dl_sf_cfg, &dci_cfg, format); + dci_tx.location = locations[loc]; + dci_tx.format = format; + dci_tx.rnti = rnti; + + // Assert DCI size + TESTASSERT(assert_payload_size(&pdcch_tx.cell, &dl_sf_cfg) == SRSRAN_SUCCESS); + + // Initialise resource grid for each Tx port + for (uint32_t p = 0; p < nof_ports; p++) { + srsran_vec_cf_zero(slot_symbols[p], nof_re); + } + + // Generate Tx DCI + srsran_random_bit_vector(random_gen, dci_tx.payload, dci_tx.nof_bits); + + // Encode + gettimeofday(&t[1], NULL); + TESTASSERT(srsran_pdcch_encode(&pdcch_tx, &dl_sf_cfg, &dci_tx, slot_symbols) == SRSRAN_SUCCESS); + gettimeofday(&t[2], NULL); + get_time_interval(t); + t_encode_us += (size_t)(t[0].tv_sec * 1e6 + t[0].tv_usec); + t_encode_count++; + + // Set noise level according to aggregation level + float n0_dB = -get_snr_dB(locations[loc].L); + TESTASSERT(srsran_channel_awgn_set_n0(&awgn, n0_dB) == SRSRAN_SUCCESS); + chest_dl_res.noise_estimate = srsran_convert_dB_to_power(n0_dB); + + // Apply AWGN + for (uint32_t p = 0; p < nof_ports; p++) { + srsran_channel_awgn_run_c(&awgn, slot_symbols[p], slot_symbols[p], nof_re); + } + + // Extract LLR + gettimeofday(&t[1], NULL); + TESTASSERT(srsran_pdcch_extract_llr(&pdcch_rx, &dl_sf_cfg, &chest_dl_res, slot_symbols) == SRSRAN_SUCCESS); + gettimeofday(&t[2], NULL); + get_time_interval(t); + t_llr_us += (size_t)(t[0].tv_sec * 1e6 + t[0].tv_usec); + + // Try decoding the PDCCH in all possible locations + for (uint32_t loc_rx = 0; loc_rx < locations_count; loc_rx++) { + // Skip location if: + // - False check is disabled and Tx/Rx dont match + // - Tx aggregation level is bigger than Rx aggregation level + if ((!false_check && loc_rx != loc) || locations[loc_rx].L < locations[loc].L) { + continue; + } + + // Prepare DCI message context + srsran_dci_msg_t dci_rx = {}; + dci_rx.location = locations[loc_rx]; + dci_rx.format = format; + + // Try to decode PDCCH message + gettimeofday(&t[1], NULL); + TESTASSERT(srsran_pdcch_decode_msg(&pdcch_rx, &dl_sf_cfg, &dci_cfg, &dci_rx) == SRSRAN_SUCCESS); + gettimeofday(&t[2], NULL); + get_time_interval(t); + t_decode_us += (size_t)(t[0].tv_sec * 1e6 + t[0].tv_usec); + t_decode_count++; + + // Compute LLR correlation + float corr = srsran_pdcch_msg_corr(&pdcch_rx, &dci_rx); + + bool rnti_match = (dci_tx.rnti == dci_rx.rnti); + bool location_match = (loc == loc_rx); + bool payload_match = (memcmp(dci_tx.payload, dci_rx.payload, dci_tx.nof_bits) == 0); + bool corr_thr = corr > 0.5f; + + // Skip location if the decoding is not successful in a different location than transmitted + if (!location_match && !rnti_match) { + continue; + } + + // Skip location if the correlation does not surpass the threshold + if (!location_match && !corr_thr) { + false_alarm_corr_count++; + continue; + } + + // Assert correlation only if location matches + if (location_match) { + TESTASSERT(corr_thr); + if (location_match && corr < min_corr) { + min_corr = corr; + } + } + + if (srsran_verbose >= SRSRAN_VERBOSE_INFO || !payload_match) { + // If payload is not match and there is no logging, set logging to info and run the decoder again + if (srsran_verbose < SRSRAN_VERBOSE_INFO) { + printf("-- Detected payload was not matched, repeating decode with INFO logs (n0: %+.1f dB, corr: %f)\n", + n0_dB, + corr); + srsran_verbose = SRSRAN_VERBOSE_INFO; + srsran_pdcch_decode_msg(&pdcch_rx, &dl_sf_cfg, &dci_cfg, &dci_rx); + } + print_dci_msg("Tx: ", &dci_tx); + print_dci_msg("Rx: ", &dci_rx); + } - nof_re = SRSRAN_CP_NORM_NSYMB * cell.nof_prb * SRSRAN_NRE; + // Assert received message + TESTASSERT(payload_match); + } + } + } - if (test_dci_payload_size()) { - exit(-1); + printf("test_case_1 - format %s - passed - %.1f usec/encode; %.1f usec/llr; %.1f usec/decode; min_corr=%f; " + "false_alarm_prob=%f;\n", + srsran_dci_format_string(format), + (double)t_encode_us / (double)(t_encode_count), + (double)t_llr_us / (double)(t_encode_count), + (double)t_decode_us / (double)(t_decode_count), + min_corr, + (double)false_alarm_corr_count / (double)t_decode_count); } - /* init memory */ + return SRSRAN_SUCCESS; +} + +int main(int argc, char** argv) +{ + srsran_regs_t regs; + int i; + int ret = SRSRAN_ERROR; - srsran_chest_dl_res_init(&chest_dl_res, cell.nof_prb); + parse_args(argc, argv); + random_gen = srsran_random_init(0x1234); + + // Create cell + srsran_cell_t cell = {}; + cell.nof_prb = nof_prb; + cell.nof_ports = nof_ports; + cell.cp = SRSRAN_CP_NORM; + cell.phich_resources = SRSRAN_PHICH_R_1; + cell.phich_length = SRSRAN_PHICH_NORM; + + // Initialise channel estimates with identity matrix + if (srsran_chest_dl_res_init(&chest_dl_res, cell.nof_prb) < SRSRAN_SUCCESS) { + ERROR("Error channel estimates"); + goto quit; + } srsran_chest_dl_res_set_identity(&chest_dl_res); + // Allocate grid + uint32_t nof_re = SRSRAN_NOF_RE(cell); for (i = 0; i < SRSRAN_MAX_PORTS; i++) { slot_symbols[i] = srsran_vec_cf_malloc(nof_re); - if (!slot_symbols[i]) { - perror("malloc"); - exit(-1); + if (slot_symbols[i] == NULL) { + ERROR("malloc"); + goto quit; } - srsran_vec_cf_zero(slot_symbols[i], nof_re); } if (srsran_regs_init(®s, cell)) { ERROR("Error initiating regs"); - exit(-1); + goto quit; } if (srsran_pdcch_init_enb(&pdcch_tx, cell.nof_prb)) { ERROR("Error creating PDCCH object"); - exit(-1); + goto quit; } if (srsran_pdcch_set_cell(&pdcch_tx, ®s, cell)) { ERROR("Error setting cell in PDCCH object"); - exit(-1); + goto quit; } - if (srsran_pdcch_init_ue(&pdcch_rx, cell.nof_prb, nof_rx_ant)) { + if (srsran_pdcch_init_ue(&pdcch_rx, cell.nof_prb, nof_ports)) { ERROR("Error creating PDCCH object"); - exit(-1); + goto quit; } + if (srsran_pdcch_set_cell(&pdcch_rx, ®s, cell)) { ERROR("Error setting cell in PDCCH object"); - exit(-1); + goto quit; } - /* Resource allocate init */ - nof_dcis = 0; - srsran_dci_dl_t dci; - ZERO_OBJECT(dci); - dci.pid = 5; - dci.tb[0].mcs_idx = 5; - dci.tb[0].ndi = 0; - dci.tb[0].rv = 1; - dci.alloc_type = SRSRAN_RA_ALLOC_TYPE0; - dci.type0_alloc.rbg_bitmask = 0x5; - dci.cif_present = dci_cfg.cif_enabled; - if (dci_cfg.cif_enabled) { - dci.cif = (uint32_t)srsran_random_uniform_int_dist(random_gen, 0, 7); + if (srsran_channel_awgn_init(&awgn, 0x1234) < SRSRAN_SUCCESS) { + ERROR("Error init AWGN"); + goto quit; } - /* Format 1 Test case */ - if (cell.nof_ports == 1) { - testcases[nof_dcis].dci_format = SRSRAN_DCI_FORMAT1; - if (dci_cfg.cif_enabled) { - dci.cif = (uint32_t)srsran_random_uniform_int_dist(random_gen, 0, 7); - } - testcases[nof_dcis].ra_dl_tx = dci; - nof_dcis++; - - /* Format 1 Test case */ - dci.tb[0].mcs_idx = 15; - testcases[nof_dcis].dci_format = SRSRAN_DCI_FORMAT1; - if (dci_cfg.cif_enabled) { - dci.cif = (uint32_t)srsran_random_uniform_int_dist(random_gen, 0, 7); - } - testcases[nof_dcis].ra_dl_tx = dci; - nof_dcis++; + // Execute actual test cases + if (test_case1() < SRSRAN_SUCCESS) { + ERROR("Test case 1 failed"); + goto quit; } - /* Tx Diversity Test case */ - if (cell.nof_ports > 1) { - dci.tb[1].mcs_idx = 13; - dci.tb[1].rv = 3; - dci.tb[1].ndi = true; - testcases[nof_dcis].dci_format = SRSRAN_DCI_FORMAT2A; - if (dci_cfg.cif_enabled) { - dci.cif = (uint32_t)srsran_random_uniform_int_dist(random_gen, 0, 7); - } - testcases[nof_dcis].ra_dl_tx = dci; - nof_dcis++; - } - - /* CDD Spatial Multiplexing Test case */ - if (cell.nof_ports > 1) { - dci.tb[1].mcs_idx = 28; - dci.tb[1].rv = 1; - dci.tb[1].ndi = false; - testcases[nof_dcis].dci_format = SRSRAN_DCI_FORMAT2; - if (dci_cfg.cif_enabled) { - dci.cif = (uint32_t)srsran_random_uniform_int_dist(random_gen, 0, 7); - } - testcases[nof_dcis].ra_dl_tx = dci; - nof_dcis++; - } - - srsran_dl_sf_cfg_t dl_sf; - ZERO_OBJECT(dl_sf); - dl_sf.cfi = cfi; - - for (int s = 0; s < 10; s++) { - dl_sf.tti = s; - printf("Encoding %d DCIs for sf_idx=%d\n", nof_dcis, s); - /* Execute Rx */ - for (i = 0; i < nof_dcis; i++) { - testcases[i].ra_dl_tx.rnti = (uint16_t)(1234 + i); - testcases[i].ra_dl_tx.format = testcases[i].dci_format; - - srsran_dci_msg_pack_pdsch(&cell, &dl_sf, &dci_cfg, &testcases[i].ra_dl_tx, &testcases[i].dci_tx); - srsran_dci_location_set(&testcases[i].dci_location, 0, (uint32_t)i); - - testcases[i].dci_tx.format = testcases[i].dci_format; - testcases[i].dci_tx.location = testcases[i].dci_location; - - // Enable just 1 TB per default - if (testcases[i].dci_format < SRSRAN_DCI_FORMAT2) { - for (int j = 1; j < SRSRAN_MAX_CODEWORDS; j++) { - SRSRAN_DCI_TB_DISABLE(testcases[i].ra_dl_tx.tb[j]); - } - } - - if (srsran_pdcch_encode(&pdcch_tx, &dl_sf, &testcases[i].dci_tx, slot_symbols)) { - ERROR("Error encoding DCI message"); - goto quit; - } - } - - /* Execute 'Rx' */ - if (srsran_pdcch_extract_llr(&pdcch_rx, &dl_sf, &chest_dl_res, slot_symbols)) { - ERROR("Error extracting LLRs"); - goto quit; - } - - /* Decode DCIs */ - for (i = 0; i < nof_dcis; i++) { - testcases[i].dci_rx.format = testcases[i].dci_format; - testcases[i].dci_rx.location = testcases[i].dci_location; - if (srsran_pdcch_decode_msg(&pdcch_rx, &dl_sf, &dci_cfg, &testcases[i].dci_rx)) { - ERROR("Error decoding DCI message"); - goto quit; - } - if (srsran_dci_msg_unpack_pdsch(&cell, &dl_sf, &dci_cfg, &testcases[i].dci_rx, &testcases[i].ra_dl_rx)) { - ERROR("Error unpacking DCI message"); - goto quit; - } - if (testcases[i].dci_rx.rnti >= 1234 && testcases[i].dci_rx.rnti < 1234 + nof_dcis) { - testcases[i].dci_rx.rnti -= 1234; - } else { - printf("Received invalid DCI CRC %d\n", testcases[i].dci_rx.rnti); - goto quit; - } - } - - /* Compare Tx and Rx */ - for (i = 0; i < nof_dcis; i++) { - if (memcmp(testcases[i].dci_tx.payload, testcases[i].dci_rx.payload, testcases[i].dci_tx.nof_bits)) { - printf("Error in DCI %d: Received data does not match\n", i); - goto quit; - } -#if SRSRAN_DCI_HEXDEBUG - // Ignore Hex str - bzero(testcases[i].ra_dl_rx.hex_str, sizeof(testcases[i].ra_dl_rx.hex_str)); - testcases[i].ra_dl_rx.nof_bits = 0; -#endif - // Ignore DCI location - testcases[i].ra_dl_rx.location = testcases[i].ra_dl_tx.location; - // Ignore cw_idx - for (int j = 0; j < SRSRAN_MAX_CODEWORDS; j++) { - testcases[i].ra_dl_rx.tb[j].cw_idx = testcases[i].ra_dl_tx.tb[j].cw_idx; - } - if (memcmp(&testcases[i].ra_dl_tx, &testcases[i].ra_dl_rx, sizeof(srsran_dci_dl_t))) { - uint8_t* x = (uint8_t*)&testcases[i].ra_dl_rx; - uint8_t* y = (uint8_t*)&testcases[i].ra_dl_tx; - for (int j = 0; j < sizeof(srsran_dci_dl_t); j++) { - if (x[j] != y[j]) { - printf("error in byte %d, rx=%d, tx=%d\n", j, x[j], y[j]); - } - } - printf("tx: "); - srsran_vec_fprint_byte(stdout, (uint8_t*)&testcases[i].ra_dl_tx, sizeof(srsran_dci_dl_t)); - printf("rx: "); - srsran_vec_fprint_byte(stdout, (uint8_t*)&testcases[i].ra_dl_rx, sizeof(srsran_dci_dl_t)); - printf("Error in RA %d: Received data does not match\n", i); - printf(" Field | Tx | Rx \n"); - printf("--------------+----------+----------\n"); - if (testcases[i].ra_dl_tx.cif) { - printf(" cif | %8d | %8d\n", testcases[i].ra_dl_tx.cif, testcases[i].ra_dl_rx.cif); - } - printf(" harq_process | %8d | %8d\n", testcases[i].ra_dl_tx.pid, testcases[i].ra_dl_rx.pid); - printf(" mcs_idx | %8d | %8d\n", testcases[i].ra_dl_tx.tb[0].mcs_idx, testcases[i].ra_dl_rx.tb[0].mcs_idx); - printf(" rv_idx | %8d | %8d\n", testcases[i].ra_dl_tx.tb[0].rv, testcases[i].ra_dl_rx.tb[0].rv); - printf(" ndi | %8d | %8d\n", testcases[i].ra_dl_tx.tb[0].ndi, testcases[i].ra_dl_rx.tb[0].ndi); - printf(" mcs_idx_1 | %8d | %8d\n", testcases[i].ra_dl_tx.tb[1].mcs_idx, testcases[i].ra_dl_rx.tb[1].mcs_idx); - printf(" rv_idx_1 | %8d | %8d\n", testcases[i].ra_dl_tx.tb[1].rv, testcases[i].ra_dl_rx.tb[1].rv); - printf(" ndi_1 | %8d | %8d\n", testcases[i].ra_dl_tx.tb[1].ndi, testcases[i].ra_dl_rx.tb[1].ndi); - printf(" tb_cw_swap | %8d | %8d\n", testcases[i].ra_dl_tx.tb_cw_swap, testcases[i].ra_dl_rx.tb_cw_swap); - printf(" sram_id | %8d | %8d\n", testcases[i].ra_dl_tx.sram_id, testcases[i].ra_dl_rx.sram_id); - printf(" pinfo | %8d | %8d\n", testcases[i].ra_dl_tx.pinfo, testcases[i].ra_dl_rx.pinfo); - printf(" pconf | %8d | %8d\n", testcases[i].ra_dl_tx.pconf, testcases[i].ra_dl_rx.pconf); - printf(" power_offset | %8d | %8d\n", testcases[i].ra_dl_tx.power_offset, testcases[i].ra_dl_rx.power_offset); - printf(" tpc_pucch | %8d | %8d\n", testcases[i].ra_dl_tx.tpc_pucch, testcases[i].ra_dl_rx.tpc_pucch); - goto quit; - } - } - } - ret = 0; + ret = SRSRAN_SUCCESS; quit: srsran_pdcch_free(&pdcch_tx); @@ -399,9 +416,12 @@ quit: srsran_chest_dl_res_free(&chest_dl_res); srsran_regs_free(®s); srsran_random_free(random_gen); + srsran_channel_awgn_free(&awgn); for (i = 0; i < SRSRAN_MAX_PORTS; i++) { - free(slot_symbols[i]); + if (slot_symbols[i]) { + free(slot_symbols[i]); + } } if (ret) { printf("Error\n"); diff --git a/lib/src/phy/phch/test/pdsch_pdcch_file_test.c b/lib/src/phy/phch/test/pdsch_pdcch_file_test.c index 91fdb6530..1bfb51f84 100644 --- a/lib/src/phy/phch/test/pdsch_pdcch_file_test.c +++ b/lib/src/phy/phch/test/pdsch_pdcch_file_test.c @@ -174,7 +174,7 @@ int main(int argc, char** argv) exit(-1); } - uint8_t* data[] = {malloc(100000)}; + uint8_t* data[SRSRAN_MAX_CODEWORDS] = {malloc(100000)}; if (!data[0]) { perror("malloc"); exit(-1); diff --git a/lib/src/phy/phch/test/pdsch_test.c b/lib/src/phy/phch/test/pdsch_test.c index d5d6bfc87..5c8c67118 100644 --- a/lib/src/phy/phch/test/pdsch_test.c +++ b/lib/src/phy/phch/test/pdsch_test.c @@ -159,7 +159,7 @@ static int check_softbits(srsran_pdsch_t* pdsch_enb, if (!pdsch_ue->llr_is_8bit && !tb_cw_swap) { // Scramble - srsran_sequence_pdsch_apply_c(pdsch_ue->e[tb], + srsran_sequence_pdsch_apply_s(pdsch_ue->e[tb], pdsch_ue->e[tb], rnti, pdsch_cfg->grant.tb[tb].cw_idx, @@ -177,6 +177,10 @@ static int check_softbits(srsran_pdsch_t* pdsch_enb, rx_bytes[i] = w; } if (memcmp(pdsch_ue->e[tb], pdsch_enb->e[tb], pdsch_cfg->grant.tb[tb].nof_bits / 8) != 0) { + printf("tx="); + srsran_vec_fprint_byte(stdout, pdsch_enb->e[tb], pdsch_cfg->grant.tb[tb].nof_bits / 8); + printf("rx="); + srsran_vec_fprint_byte(stdout, pdsch_ue->e[tb], pdsch_cfg->grant.tb[tb].nof_bits / 8); ret = SRSRAN_ERROR; } } @@ -204,6 +208,7 @@ int main(int argc, char** argv) srsran_chest_dl_res_t chest_res; srsran_pdsch_res_t pdsch_res[SRSRAN_MAX_CODEWORDS]; srsran_random_t random_gen = srsran_random_init(0x1234); + srsran_crc_t crc_tb; /* Initialise to zeros */ ZERO_OBJECT(softbuffers_tx); @@ -221,6 +226,7 @@ int main(int argc, char** argv) ZERO_OBJECT(chest); ZERO_OBJECT(chest_res); ZERO_OBJECT(pdsch_res); + ZERO_OBJECT(crc_tb); parse_args(argc, argv); @@ -393,11 +399,19 @@ int main(int argc, char** argv) } } + if (srsran_crc_init(&crc_tb, SRSRAN_LTE_CRC24A, 24) < SRSRAN_SUCCESS) { + ERROR("Error initiating CRC24A"); + goto quit; + } + + // Generate random data for (int tb = 0; tb < SRSRAN_MAX_CODEWORDS; tb++) { if (pdsch_cfg.grant.tb[tb].enabled) { for (int byte = 0; byte < pdsch_cfg.grant.tb[tb].tbs / 8; byte++) { data_tx[tb][byte] = (uint8_t)srsran_random_uniform_int_dist(random_gen, 0, 255); } + // Attach CRC for making sure TB with 0 CRC are detected + srsran_crc_attach_byte(&crc_tb, data_tx[tb], pdsch_cfg.grant.tb[tb].tbs - 24); } } @@ -508,10 +522,12 @@ int main(int argc, char** argv) for (int tb = 0; tb < SRSRAN_MAX_CODEWORDS; tb++) { if (pdsch_cfg.grant.tb[tb].enabled) { if (check_softbits(&pdsch_tx, &pdsch_rx, &pdsch_cfg, subframe, tb)) { - printf("TB%d: The received softbits in subframe %d DO NOT match the encoded bits (crc=%d)\n", - tb, - subframe, - pdsch_res[tb].crc); + ERROR("TB%d: The received softbits in subframe %d DO NOT match the encoded bits (crc=%d)\n", + tb, + subframe, + pdsch_res[tb].crc); + ret = SRSRAN_ERROR; + goto quit; } else { for (int byte = 0; byte < pdsch_cfg.grant.tb[tb].tbs / 8; byte++) { if (data_tx[tb][byte] != data_rx[tb][byte]) { diff --git a/lib/src/phy/phch/test/phich_file_test.c b/lib/src/phy/phch/test/phich_file_test.c index 8f8b57734..c41836e77 100644 --- a/lib/src/phy/phch/test/phich_file_test.c +++ b/lib/src/phy/phch/test/phich_file_test.c @@ -48,7 +48,7 @@ int numsubframe = 0; FILE* fmatlab = NULL; srsran_filesource_t fsrc; -cf_t * input_buffer, *fft_buffer[SRSRAN_MAX_CODEWORDS]; +cf_t * input_buffer, *fft_buffer[SRSRAN_MAX_PORTS]; srsran_phich_t phich; srsran_regs_t regs; srsran_ofdm_t fft; diff --git a/lib/src/phy/phch/test/pmch_file_test.c b/lib/src/phy/phch/test/pmch_file_test.c index 470b44029..4279089fe 100644 --- a/lib/src/phy/phch/test/pmch_file_test.c +++ b/lib/src/phy/phch/test/pmch_file_test.c @@ -216,9 +216,12 @@ int main(int argc, char** argv) SRSRAN_DCI_TB_DISABLE(dci.tb[1]); srsran_ra_dl_dci_to_grant(&cell, &dl_sf, SRSRAN_TM1, false, &dci, &pmch_cfg.pdsch_cfg.grant); - srsran_pdsch_res_t pdsch_res; - pdsch_res.payload = data; - ret = srsran_ue_dl_decode_pmch(&ue_dl, &dl_sf, &pmch_cfg, &pdsch_res); + srsran_pdsch_res_t pdsch_res = {}; + pdsch_res.payload = data; + + srsran_pdsch_res_t pdsch_res_vec[SRSRAN_MAX_CODEWORDS]; + pdsch_res_vec[0] = pdsch_res; + ret = srsran_ue_dl_decode_pmch(&ue_dl, &dl_sf, &pmch_cfg, pdsch_res_vec); if (pdsch_res.crc == 1) { printf("PMCH Decoded OK!\n"); } else if (pdsch_res.crc == 0) { diff --git a/lib/src/phy/phch/test/pscch_test.c b/lib/src/phy/phch/test/pscch_test.c index beb3a2bb8..af0401a7d 100644 --- a/lib/src/phy/phch/test/pscch_test.c +++ b/lib/src/phy/phch/test/pscch_test.c @@ -164,7 +164,7 @@ int main(int argc, char** argv) } srsran_sci_info(&sci, sci_msg, sizeof(sci_msg)); - fprintf(stdout, "%s", sci_msg); + fprintf(stdout, "%s\n", sci_msg); if (sci.riv == riv_txed) { ret = SRSRAN_SUCCESS; } diff --git a/lib/src/phy/phch/test/pssch_pscch_file_test.c b/lib/src/phy/phch/test/pssch_pscch_file_test.c index 9bec2122b..781638cd8 100644 --- a/lib/src/phy/phch/test/pssch_pscch_file_test.c +++ b/lib/src/phy/phch/test/pssch_pscch_file_test.c @@ -296,7 +296,7 @@ int main(int argc, char** argv) if (srsran_pscch_decode(&pscch, equalized_sf_buffer, sci_rx, pscch_prb_start_idx) == SRSRAN_SUCCESS) { if (srsran_sci_format0_unpack(&sci, sci_rx) == SRSRAN_SUCCESS) { srsran_sci_info(&sci, sci_msg, sizeof(sci_msg)); - fprintf(stdout, "%s", sci_msg); + fprintf(stdout, "%s\n", sci_msg); sci_decoded = true; num_decoded_sci++; @@ -357,7 +357,7 @@ int main(int argc, char** argv) if (srsran_pscch_decode(&pscch, equalized_sf_buffer, sci_rx, pscch_prb_start_idx) == SRSRAN_SUCCESS) { if (srsran_sci_format1_unpack(&sci, sci_rx) == SRSRAN_SUCCESS) { srsran_sci_info(&sci, sci_msg, sizeof(sci_msg)); - fprintf(stdout, "%s", sci_msg); + fprintf(stdout, "%s\n", sci_msg); num_decoded_sci++; diff --git a/lib/src/phy/phch/test/pucch_test.c b/lib/src/phy/phch/test/pucch_test.c index 07abb118c..28f09adfc 100644 --- a/lib/src/phy/phch/test/pucch_test.c +++ b/lib/src/phy/phch/test/pucch_test.c @@ -89,7 +89,7 @@ int test_uci_cqi_pucch(void) int ret = SRSRAN_SUCCESS; __attribute__((aligned(256))) uint8_t o_bits[SRSRAN_UCI_MAX_CQI_LEN_PUCCH] = {0}; __attribute__((aligned(256))) uint8_t e_bits[SRSRAN_UCI_CQI_CODED_PUCCH_B] = {0}; - __attribute__((aligned(256))) int16_t e_symb[SRSRAN_UCI_CQI_CODED_PUCCH_B] = {0}; + __attribute__((aligned(256))) int16_t e_symb[SRSRAN_CQI_MAX_BITS] = {0}; __attribute__((aligned(256))) uint8_t d_bits[SRSRAN_UCI_MAX_CQI_LEN_PUCCH] = {0}; srsran_uci_cqi_pucch_t uci_cqi_pucch = {0}; diff --git a/lib/src/phy/phch/test/pusch_nr_test.c b/lib/src/phy/phch/test/pusch_nr_test.c index 2ed3cddc2..33ade4e61 100644 --- a/lib/src/phy/phch/test/pusch_nr_test.c +++ b/lib/src/phy/phch/test/pusch_nr_test.c @@ -275,7 +275,9 @@ int main(int argc, char** argv) } chest.nof_re = pusch_cfg.grant.tb->nof_re; - if (srsran_pusch_nr_decode(&pusch_rx, &pusch_cfg, &pusch_cfg.grant, &chest, sf_symbols, &data_rx) < + srsran_pusch_res_nr_t* data_rx_vec[SRSRAN_MAX_TB] = {}; + data_rx_vec[0] = &data_rx; + if (srsran_pusch_nr_decode(&pusch_rx, &pusch_cfg, &pusch_cfg.grant, &chest, sf_symbols, data_rx_vec) < SRSRAN_SUCCESS) { ERROR("Error encoding"); goto clean_exit; diff --git a/lib/src/phy/phch/test/pusch_test.c b/lib/src/phy/phch/test/pusch_test.c index 791339cea..502280e55 100644 --- a/lib/src/phy/phch/test/pusch_test.c +++ b/lib/src/phy/phch/test/pusch_test.c @@ -193,8 +193,10 @@ int main(int argc, char** argv) srsran_pusch_cfg_t cfg; srsran_softbuffer_tx_t softbuffer_tx; srsran_softbuffer_rx_t softbuffer_rx; + srsran_crc_t crc_tb; ZERO_OBJECT(uci_data_tx); + ZERO_OBJECT(crc_tb); bzero(&cfg, sizeof(srsran_pusch_cfg_t)); @@ -292,9 +294,12 @@ int main(int argc, char** argv) srsran_softbuffer_tx_reset(&softbuffer_tx); srsran_softbuffer_rx_reset(&softbuffer_rx); + // Generate random data for (uint32_t i = 0; i < cfg.grant.tb.tbs / 8; i++) { data[i] = (uint8_t)srsran_random_uniform_int_dist(random_h, 0, 255); } + // Attach CRC for making sure TB with 0 CRC are detected + srsran_crc_attach_byte(&crc_tb, data, cfg.grant.tb.tbs - 24); for (uint32_t a = 0; a < uci_data_tx.cfg.ack[0].nof_acks; a++) { uci_data_tx.value.ack.ack_value[a] = (uint8_t)srsran_random_uniform_int_dist(random_h, 0, 1); diff --git a/lib/src/phy/phch/uci.c b/lib/src/phy/phch/uci.c index f14147213..0128f3968 100644 --- a/lib/src/phy/phch/uci.c +++ b/lib/src/phy/phch/uci.c @@ -129,7 +129,7 @@ int16_t srsran_uci_decode_cqi_pucch(srsran_uci_cqi_pucch_t* q, uint8_t* cqi_data, uint32_t cqi_len) { - if (cqi_len < SRSRAN_UCI_MAX_CQI_LEN_PUCCH && b_bits != NULL && cqi_data != NULL) { + if (q != NULL && cqi_len < SRSRAN_UCI_MAX_CQI_LEN_PUCCH && b_bits != NULL && cqi_data != NULL) { uint32_t max_w = 0; int32_t max_corr = INT32_MIN; uint32_t nwords = 1 << SRSRAN_UCI_MAX_CQI_LEN_PUCCH; @@ -655,9 +655,10 @@ int srsran_uci_decode_ack_ri(srsran_pusch_cfg_t* cfg, uint32_t Qm = srsran_mod_bits_x_symbol(cfg->grant.tb.mod); int16_t llr_acc[32] = {}; ///< LLR accumulator - uint32_t nof_acc = - (nof_bits == 1) ? Qm : (nof_bits == 2) ? Qm * 3 : SRSRAN_FEC_BLOCK_SIZE; ///< Number of required LLR - uint32_t count_acc = 0; ///< LLR counter + uint32_t nof_acc = (nof_bits == 1) ? Qm + : (nof_bits == 2) ? Qm * 3 + : SRSRAN_FEC_BLOCK_SIZE; ///< Number of required LLR + uint32_t count_acc = 0; ///< LLR counter for (uint32_t i = 0; i < Qprime; i++) { if (is_ri) { diff --git a/lib/src/phy/rf/rf_blade_imp.c b/lib/src/phy/rf/rf_blade_imp.c index b5160ba1b..52cdbc8b7 100644 --- a/lib/src/phy/rf/rf_blade_imp.c +++ b/lib/src/phy/rf/rf_blade_imp.c @@ -24,7 +24,9 @@ #include #include "rf_blade_imp.h" -#include "srsran/srsran.h" +#include "srsran/phy/common/timestamp.h" +#include "srsran/phy/utils/debug.h" +#include "srsran/phy/utils/vector.h" #define UNUSED __attribute__((unused)) #define CONVERT_BUFFER_SIZE (240 * 1024) diff --git a/lib/src/phy/rf/rf_imp.c b/lib/src/phy/rf/rf_imp.c index d056427a6..d453aa6d0 100644 --- a/lib/src/phy/rf/rf_imp.c +++ b/lib/src/phy/rf/rf_imp.c @@ -23,7 +23,7 @@ #include "rf_dev.h" #include "srsran/phy/rf/rf.h" -#include "srsran/srsran.h" +#include "srsran/phy/utils/debug.h" int rf_get_available_devices(char** devnames, int max_strlen) { @@ -184,7 +184,9 @@ int srsran_rf_close(srsran_rf_t* rf) { // Stop gain thread if (rf->thread_gain_run) { + pthread_mutex_lock(&rf->mutex); rf->thread_gain_run = false; + pthread_mutex_unlock(&rf->mutex); pthread_cond_signal(&rf->cond); pthread_join(rf->thread_gain, NULL); } diff --git a/lib/src/phy/rf/rf_soapy_imp.c b/lib/src/phy/rf/rf_soapy_imp.c index 4af516a8c..62dc9738b 100644 --- a/lib/src/phy/rf/rf_soapy_imp.c +++ b/lib/src/phy/rf/rf_soapy_imp.c @@ -26,7 +26,9 @@ #include "rf_helper.h" #include "rf_soapy_imp.h" -#include "srsran/srsran.h" +#include "srsran/phy/common/phy_common.h" +#include "srsran/phy/utils/debug.h" +#include "srsran/phy/utils/vector.h" #include #include diff --git a/lib/src/phy/rf/rf_uhd_imp.cc b/lib/src/phy/rf/rf_uhd_imp.cc index e2da0e144..ea940bc0d 100644 --- a/lib/src/phy/rf/rf_uhd_imp.cc +++ b/lib/src/phy/rf/rf_uhd_imp.cc @@ -28,7 +28,7 @@ #include #include "rf_helper.h" -#include "srsran/srsran.h" +#include "srsran/phy/utils/debug.h" #include "rf_uhd_generic.h" #include "rf_uhd_imp.h" diff --git a/lib/src/phy/rf/rf_zmq_imp.c b/lib/src/phy/rf/rf_zmq_imp.c index 43108e048..0d2b10622 100644 --- a/lib/src/phy/rf/rf_zmq_imp.c +++ b/lib/src/phy/rf/rf_zmq_imp.c @@ -606,12 +606,7 @@ int rf_zmq_recv_with_time(void* h, void* data, uint32_t nsamples, bool blocking, return rf_zmq_recv_with_time_multi(h, &data, nsamples, blocking, secs, frac_secs); } -int rf_zmq_recv_with_time_multi(void* h, - void* data[4], - uint32_t nsamples, - bool blocking, - time_t* secs, - double* frac_secs) +int rf_zmq_recv_with_time_multi(void* h, void** data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs) { int ret = SRSRAN_ERROR; @@ -683,7 +678,7 @@ int rf_zmq_recv_with_time_multi(void* h, rf_zmq_info(handler->id, " - next tx time: %d + %.3f\n", ts_tx.full_secs, ts_tx.frac_secs); // Leave time for the Tx to transmit - usleep((1000000 * nsamples_baserate) / handler->base_srate); + usleep((1000000UL * nsamples_baserate) / handler->base_srate); // check for tx gap if we're also transmitting on this radio for (int i = 0; i < handler->nof_channels; i++) { diff --git a/lib/src/phy/rf/rf_zmq_imp_rx.c b/lib/src/phy/rf/rf_zmq_imp_rx.c index 49aca7ed0..d676c9b97 100644 --- a/lib/src/phy/rf/rf_zmq_imp_rx.c +++ b/lib/src/phy/rf/rf_zmq_imp_rx.c @@ -26,11 +26,20 @@ #include #include +bool is_rx_running(rf_zmq_rx_t* q) +{ + bool running = false; + pthread_mutex_lock(&q->mutex); + running = q->running; + pthread_mutex_unlock(&q->mutex); + return running; +} + static void* rf_zmq_async_rx_thread(void* h) { rf_zmq_rx_t* q = (rf_zmq_rx_t*)h; - while (q->sock && q->running) { + while (q->sock && is_rx_running(q)) { int nbytes = 0; int n = SRSRAN_ERROR; uint8_t dummy = 0xFF; @@ -39,7 +48,7 @@ static void* rf_zmq_async_rx_thread(void* h) // Send request if socket type is REQUEST if (q->socket_type == ZMQ_REQ) { - while (n < 0 && q->running) { + while (n < 0 && is_rx_running(q)) { rf_zmq_info(q->id, " - tx'ing rx request\n"); n = zmq_send(q->sock, &dummy, sizeof(dummy), 0); if (n < 0) { @@ -53,7 +62,7 @@ static void* rf_zmq_async_rx_thread(void* h) } // Receive baseband - for (n = (n < 0) ? 0 : -1; n < 0 && q->running;) { + for (n = (n < 0) ? 0 : -1; n < 0 && is_rx_running(q);) { n = zmq_recv(q->sock, q->temp_buffer, ZMQ_MAX_BUFFER_SIZE, 0); if (n == -1) { if (rf_zmq_handle_error(q->id, "asynchronous rx baseband receive")) { @@ -77,7 +86,7 @@ static void* rf_zmq_async_rx_thread(void* h) n = -1; // Try to write in ring buffer - while (n < 0 && q->running) { + while (n < 0 && is_rx_running(q)) { n = srsran_ringbuffer_write_timed(&q->ringbuffer, q->temp_buffer, nbytes, q->trx_timeout_ms); if (n == SRSRAN_ERROR_TIMEOUT && q->log_trx_timeout) { fprintf(stderr, "Error: timeout writing samples to ringbuffer after %dms\n", q->trx_timeout_ms); @@ -262,13 +271,18 @@ bool rf_zmq_rx_match_freq(rf_zmq_rx_t* q, uint32_t freq_hz) void rf_zmq_rx_close(rf_zmq_rx_t* q) { rf_zmq_info(q->id, "Closing ...\n"); + + pthread_mutex_lock(&q->mutex); q->running = false; + pthread_mutex_unlock(&q->mutex); if (q->thread) { pthread_join(q->thread, NULL); pthread_detach(q->thread); } + pthread_mutex_destroy(&q->mutex); + srsran_ringbuffer_free(&q->ringbuffer); if (q->temp_buffer) { diff --git a/lib/src/phy/rf/rf_zmq_imp_tx.c b/lib/src/phy/rf/rf_zmq_imp_tx.c index d638e1a84..5583c050d 100644 --- a/lib/src/phy/rf/rf_zmq_imp_tx.c +++ b/lib/src/phy/rf/rf_zmq_imp_tx.c @@ -232,7 +232,11 @@ bool rf_zmq_tx_match_freq(rf_zmq_tx_t* q, uint32_t freq_hz) void rf_zmq_tx_close(rf_zmq_tx_t* q) { + pthread_mutex_lock(&q->mutex); q->running = false; + pthread_mutex_unlock(&q->mutex); + + pthread_mutex_destroy(&q->mutex); if (q->zeros) { free(q->zeros); diff --git a/lib/src/phy/rf/rf_zmq_test.c b/lib/src/phy/rf/rf_zmq_test.c index 70820845f..3e763e573 100644 --- a/lib/src/phy/rf/rf_zmq_test.c +++ b/lib/src/phy/rf/rf_zmq_test.c @@ -20,7 +20,10 @@ */ #include "rf_zmq_imp.h" -#include "srsran/srsran.h" +#include "srsran/common/tsan_options.h" +#include "srsran/phy/common/timestamp.h" +#include "srsran/phy/utils/debug.h" +#include #include #include #include diff --git a/lib/src/phy/sync/ssb.c b/lib/src/phy/sync/ssb.c index ad7ef10f2..a5ef0530f 100644 --- a/lib/src/phy/sync/ssb.c +++ b/lib/src/phy/sync/ssb.c @@ -20,6 +20,7 @@ */ #include "srsran/phy/sync/ssb.h" +#include "srsran/phy/ch_estimation/dmrs_pbch.h" #include "srsran/phy/sync/pss_nr.h" #include "srsran/phy/sync/sss_nr.h" #include "srsran/phy/utils/debug.h" @@ -42,6 +43,11 @@ */ #define SSB_CORR_SZ(SYMB_SZ) SRSRAN_MIN(1U << (uint32_t)ceil(log2((double)(SYMB_SZ)) + 3.0), 1U << 13U) +/* + * Default NR-PBCH DMRS normalised correlation (RSRP/EPRE) threshold + */ +#define SSB_PBCH_DMRS_DEFAULT_CORR_THR 0.6f + static int ssb_init_corr(srsran_ssb_t* q) { // Initialise correlation only if it is enabled @@ -62,6 +68,25 @@ static int ssb_init_corr(srsran_ssb_t* q) return SRSRAN_SUCCESS; } +static int ssb_init_pbch(srsran_ssb_t* q) +{ + srsran_pbch_nr_args_t args = {}; + args.enable_encode = q->args.enable_encode; + args.enable_decode = q->args.enable_decode; + args.disable_simd = q->args.disable_polar_simd; + + if (!args.enable_encode && !args.enable_decode) { + return SRSRAN_SUCCESS; + } + + if (srsran_pbch_nr_init(&q->pbch, &args) < SRSRAN_SUCCESS) { + ERROR("Error init NR PBCH"); + return SRSRAN_SUCCESS; + } + + return SRSRAN_SUCCESS; +} + int srsran_ssb_init(srsran_ssb_t* q, const srsran_ssb_args_t* args) { // Verify input parameters @@ -73,9 +98,8 @@ int srsran_ssb_init(srsran_ssb_t* q, const srsran_ssb_args_t* args) q->args = *args; // Check if the maximum sampling rate is in range, force default otherwise - if (!isnormal(q->args.max_srate_hz) || q->args.max_srate_hz < 0.0) { - q->args.max_srate_hz = SRSRAN_SSB_DEFAULT_MAX_SRATE_HZ; - } + q->args.max_srate_hz = (!isnormal(q->args.max_srate_hz)) ? SRSRAN_SSB_DEFAULT_MAX_SRATE_HZ : q->args.max_srate_hz; + q->args.pbch_dmrs_thr = (!isnormal(q->args.pbch_dmrs_thr)) ? SSB_PBCH_DMRS_DEFAULT_CORR_THR : q->args.pbch_dmrs_thr; q->scs_hz = (float)SRSRAN_SUBC_SPACING_NR(q->args.min_scs); q->max_symbol_sz = (uint32_t)round(q->args.max_srate_hz / q->scs_hz); @@ -95,6 +119,11 @@ int srsran_ssb_init(srsran_ssb_t* q, const srsran_ssb_args_t* args) return SRSRAN_ERROR; } + // PBCH + if (ssb_init_pbch(q) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + return SRSRAN_SUCCESS; } @@ -127,11 +156,12 @@ void srsran_ssb_free(srsran_ssb_t* q) srsran_dft_plan_free(&q->fft); srsran_dft_plan_free(&q->fft_corr); srsran_dft_plan_free(&q->ifft_corr); + srsran_pbch_nr_free(&q->pbch); SRSRAN_MEM_ZERO(q, srsran_ssb_t, 1); } -static uint32_t ssb_first_symbol_caseA(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_POSITION]) +static uint32_t ssb_first_symbol_caseA(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_CANDIDATES]) { // Case A - 15 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes of { 2 , 8 } + 14 â‹… n . For // carrier frequencies smaller than or equal to 3 GHz, n = 0 , 1 . For carrier frequencies within FR1 larger than 3 @@ -153,7 +183,7 @@ static uint32_t ssb_first_symbol_caseA(const srsran_ssb_cfg_t* cfg, uint32_t ind return count; } -static uint32_t ssb_first_symbol_caseB(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_POSITION]) +static uint32_t ssb_first_symbol_caseB(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_CANDIDATES]) { // Case B - 30 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes { 4 , 8 , 16 , 20 } + 28 â‹… n . // For carrier frequencies smaller than or equal to 3 GHz, n = 0 . For carrier frequencies within FR1 larger than 3 @@ -175,7 +205,7 @@ static uint32_t ssb_first_symbol_caseB(const srsran_ssb_cfg_t* cfg, uint32_t ind return count; } -static uint32_t ssb_first_symbol_caseC(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_POSITION]) +static uint32_t ssb_first_symbol_caseC(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_CANDIDATES]) { // Case C - 30 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes { 2 , 8 } +14 â‹… n . // - For paired spectrum operation @@ -202,7 +232,7 @@ static uint32_t ssb_first_symbol_caseC(const srsran_ssb_cfg_t* cfg, uint32_t ind return count; } -static uint32_t ssb_first_symbol_caseD(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_POSITION]) +static uint32_t ssb_first_symbol_caseD(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_CANDIDATES]) { // Case D - 120 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes { 4 , 8 , 16 , 20 } + 28 â‹… n . // For carrier frequencies within FR2, n = 0 , 1 , 2 , 3 , 5 , 6 , 7 , 8 , 10 , 11 , 12 , 13 , 15 , 16 , 17 , 18 . @@ -219,7 +249,7 @@ static uint32_t ssb_first_symbol_caseD(const srsran_ssb_cfg_t* cfg, uint32_t ind return count; } -static uint32_t ssb_first_symbol_caseE(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_POSITION]) +static uint32_t ssb_first_symbol_caseE(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_CANDIDATES]) { // Case E - 240 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes //{ 8 , 12 , 16 , 20 , 32 , 36 , 40 , 44 } + 56 â‹… n . For carrier frequencies within FR2, n = 0 , 1 , 2 , 3 , 5 , 6 , @@ -237,9 +267,8 @@ static uint32_t ssb_first_symbol_caseE(const srsran_ssb_cfg_t* cfg, uint32_t ind return count; } -static int ssb_first_symbol(const srsran_ssb_cfg_t* cfg, uint32_t ssb_i) +static uint32_t ssb_first_symbol(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_CANDIDATES]) { - uint32_t indexes[SRSRAN_SSB_NOF_POSITION]; uint32_t Lmax = 0; switch (cfg->pattern) { @@ -260,24 +289,8 @@ static int ssb_first_symbol(const srsran_ssb_cfg_t* cfg, uint32_t ssb_i) break; case SRSRAN_SSB_PATTERN_INVALID: ERROR("Invalid case"); - return SRSRAN_ERROR; } - - uint32_t ssb_count = 0; - - for (uint32_t i = 0; i < Lmax; i++) { - // There is a SSB transmission opportunity - if (cfg->position[i]) { - // Return the SSB transmission in burst - if (ssb_i == ssb_count) { - return (int)indexes[i]; - } - - ssb_count++; - } - } - - return SRSRAN_ERROR; + return Lmax; } // Modulates a given symbol l and stores the time domain signal in q->tmp_time @@ -378,6 +391,23 @@ static int ssb_setup_corr(srsran_ssb_t* q) return SRSRAN_SUCCESS; } +static inline int ssb_get_t_offset(srsran_ssb_t* q, uint32_t ssb_idx) +{ + // Get baseband time offset from the begining of the half radio frame to the first symbol + if (ssb_idx >= SRSRAN_SSB_NOF_CANDIDATES) { + ERROR("Invalid SSB candidate index (%d)", ssb_idx); + return SRSRAN_ERROR; + } + + float t_offset_s = srsran_symbol_offset_s(q->l_first[ssb_idx], q->cfg.scs); + if (isnan(t_offset_s) || isinf(t_offset_s) || t_offset_s < 0.0f) { + ERROR("Invalid first symbol (l_first=%d)", q->l_first[ssb_idx]); + return SRSRAN_ERROR; + } + + return (int)round(t_offset_s * q->cfg.srate_hz); +} + int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg) { // Verify input parameters @@ -389,34 +419,15 @@ int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg) q->scs_hz = (float)SRSRAN_SUBC_SPACING_NR(cfg->scs); // Get first symbol - int l_begin = ssb_first_symbol(cfg, 0); - if (l_begin < SRSRAN_SUCCESS) { - // set it to 2 in case it is not selected - l_begin = 2; - } + q->Lmax = ssb_first_symbol(cfg, q->l_first); - float t_offset_s = srsran_symbol_offset_s((uint32_t)l_begin, cfg->scs); - if (isnan(t_offset_s) || isinf(t_offset_s) || t_offset_s < 0.0f) { - ERROR("Invalid first symbol (l_first=%d)", l_begin); - return SRSRAN_ERROR; - } - - // Calculate SSB symbol size and integer offset + // Calculate SSB symbol size and integer frequency offset double freq_offset_hz = cfg->ssb_freq_hz - cfg->center_freq_hz; uint32_t symbol_sz = (uint32_t)round(cfg->srate_hz / q->scs_hz); q->f_offset = (int32_t)round(freq_offset_hz / q->scs_hz); - q->t_offset = (uint32_t)round(t_offset_s * cfg->srate_hz); - for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) { - uint32_t l_real = l + (uint32_t)l_begin; - - uint32_t ref_cp_sz = 144U; - if (l_real == 0 || l_real == SRSRAN_EXT_CP_SYMBOL(cfg->scs)) { - ref_cp_sz = 160U; - } - - q->cp_sz[l] = (ref_cp_sz * symbol_sz) / 2048U; - } + // Calculate cyclic prefix + q->cp_sz = (144U * symbol_sz) / 2048U; // Calculate SSB sampling error and check double ssb_srate_error_Hz = ((double)symbol_sz * q->scs_hz) - cfg->srate_hz; @@ -508,7 +519,12 @@ bool srsran_ssb_send(srsran_ssb_t* q, uint32_t sf_idx) return (sf_idx % q->cfg.periodicity_ms == 0); } -int srsran_ssb_add(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* msg, const cf_t* in, cf_t* out) +int srsran_ssb_add(srsran_ssb_t* q, + uint32_t N_id, + uint32_t ssb_idx, + const srsran_pbch_msg_nr_t* msg, + const cf_t* in, + cf_t* out) { // Verify input parameters if (q == NULL || N_id >= SRSRAN_NOF_NID_NR || msg == NULL || in == NULL || out == NULL) { @@ -537,27 +553,47 @@ int srsran_ssb_add(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* m } // Put PBCH DMRS - // ... + srsran_dmrs_pbch_cfg_t pbch_dmrs_cfg = {}; + pbch_dmrs_cfg.N_id = N_id; + pbch_dmrs_cfg.n_hf = msg->hrf ? 0 : 1; + pbch_dmrs_cfg.ssb_idx = msg->ssb_idx; + pbch_dmrs_cfg.L_max = q->Lmax; + pbch_dmrs_cfg.beta = 0.0f; + if (srsran_dmrs_pbch_put(&pbch_dmrs_cfg, ssb_grid) < SRSRAN_SUCCESS) { + ERROR("Error putting PBCH DMRS"); + return SRSRAN_ERROR; + } // Put PBCH payload - // ... + srsran_pbch_nr_cfg_t pbch_cfg = {}; + pbch_cfg.N_id = N_id; + pbch_cfg.ssb_scs = q->cfg.scs; + pbch_cfg.Lmax = q->Lmax; + if (srsran_pbch_nr_encode(&q->pbch, &pbch_cfg, msg, ssb_grid) < SRSRAN_SUCCESS) { + ERROR("Error encoding PBCH"); + return SRSRAN_ERROR; + } + + // Select start symbol from SSB candidate index + int t_offset = ssb_get_t_offset(q, ssb_idx); + if (t_offset < SRSRAN_SUCCESS) { + ERROR("Invalid SSB candidate index"); + return SRSRAN_ERROR; + } // Select input/ouput pointers considering the time offset in the slot - const cf_t* in_ptr = &in[q->t_offset]; - cf_t* out_ptr = &out[q->t_offset]; + const cf_t* in_ptr = &in[t_offset]; + cf_t* out_ptr = &out[t_offset]; // For each SSB symbol, modulate for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) { - // Get CP length - uint32_t cp_len = q->cp_sz[l]; - // Map SSB in resource grid and perform IFFT ssb_modulate_symbol(q, ssb_grid, l); // Add cyclic prefix to input; - srsran_vec_sum_ccc(in_ptr, &q->tmp_time[q->symbol_sz - cp_len], out_ptr, cp_len); - in_ptr += cp_len; - out_ptr += cp_len; + srsran_vec_sum_ccc(in_ptr, &q->tmp_time[q->symbol_sz - q->cp_sz], out_ptr, q->cp_sz); + in_ptr += q->cp_sz; + out_ptr += q->cp_sz; // Add symbol to the input baseband srsran_vec_sum_ccc(in_ptr, q->tmp_time, out_ptr, q->symbol_sz); @@ -572,21 +608,18 @@ static int ssb_demodulate(srsran_ssb_t* q, const cf_t* in, uint32_t t_offset, cf { const cf_t* in_ptr = &in[t_offset]; for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) { - // Get CP length - uint32_t cp_len = q->cp_sz[l]; - // Advance half CP, to avoid inter symbol interference - in_ptr += SRSRAN_FLOOR(cp_len, 2); + in_ptr += SRSRAN_FLOOR(q->cp_sz, 2); // Copy FFT window in temporal time domain buffer srsran_vec_cf_copy(q->tmp_time, in_ptr, q->symbol_sz); - in_ptr += q->symbol_sz + SRSRAN_CEIL(cp_len, 2); + in_ptr += q->symbol_sz + SRSRAN_CEIL(q->cp_sz, 2); // Convert to frequency domain srsran_dft_run_guru_c(&q->fft); // Compensate half CP delay - srsran_vec_apply_cfo(q->tmp_freq, SRSRAN_CEIL(cp_len, 2) / (float)(q->symbol_sz), q->tmp_freq, q->symbol_sz); + srsran_vec_apply_cfo(q->tmp_freq, SRSRAN_CEIL(q->cp_sz, 2) / (float)(q->symbol_sz), q->tmp_freq, q->symbol_sz); // Select symbol in grid cf_t* ptr = &ssb_grid[l * SRSRAN_SSB_BW_SUBC]; @@ -620,7 +653,7 @@ ssb_measure(srsran_ssb_t* q, const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], uint32_t N_ uint32_t N_id_1 = SRSRAN_NID_1_NR(N_id); uint32_t N_id_2 = SRSRAN_NID_2_NR(N_id); - // Extract PSS LSE + // Extract PSS and SSS LSE cf_t pss_lse[SRSRAN_PSS_NR_LEN]; cf_t sss_lse[SRSRAN_SSS_NR_LEN]; if (srsran_pss_nr_extract_lse(ssb_grid, N_id_2, pss_lse) < SRSRAN_SUCCESS || @@ -798,8 +831,8 @@ int srsran_ssb_csi_search(srsran_ssb_t* q, } // Remove CP offset prior demodulation - if (t_offset >= q->cp_sz[0]) { - t_offset -= q->cp_sz[0]; + if (t_offset >= q->cp_sz) { + t_offset -= q->cp_sz; } else { t_offset = 0; } @@ -834,7 +867,11 @@ int srsran_ssb_csi_search(srsran_ssb_t* q, return SRSRAN_SUCCESS; } -int srsran_ssb_csi_measure(srsran_ssb_t* q, uint32_t N_id, const cf_t* in, srsran_csi_trs_measurements_t* meas) +int srsran_ssb_csi_measure(srsran_ssb_t* q, + uint32_t N_id, + uint32_t ssb_idx, + const cf_t* in, + srsran_csi_trs_measurements_t* meas) { // Verify inputs if (q == NULL || N_id >= SRSRAN_NOF_NID_NR || in == NULL || meas == NULL || !isnormal(q->scs_hz)) { @@ -842,14 +879,19 @@ int srsran_ssb_csi_measure(srsran_ssb_t* q, uint32_t N_id, const cf_t* in, srsra } if (!q->args.enable_measure) { - ERROR("SSB is not configured for measure"); + ERROR("SSB is not configured to measure"); return SRSRAN_ERROR; } - cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {}; + // Select start symbol from SSB candidate index + int t_offset = ssb_get_t_offset(q, ssb_idx); + if (t_offset < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } // Demodulate - if (ssb_demodulate(q, in, q->t_offset, ssb_grid) < SRSRAN_SUCCESS) { + cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {}; + if (ssb_demodulate(q, in, (uint32_t)t_offset, ssb_grid) < SRSRAN_SUCCESS) { ERROR("Error demodulating"); return SRSRAN_ERROR; } @@ -862,3 +904,71 @@ int srsran_ssb_csi_measure(srsran_ssb_t* q, uint32_t N_id, const cf_t* in, srsra return SRSRAN_SUCCESS; } + +int srsran_ssb_decode_pbch(srsran_ssb_t* q, + uint32_t N_id, + uint32_t ssb_idx, + uint32_t n_hf, + const cf_t* in, + srsran_pbch_msg_nr_t* msg) +{ + // Verify inputs + if (q == NULL || N_id >= SRSRAN_NOF_NID_NR || in == NULL || msg == NULL || !isnormal(q->scs_hz)) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + if (!q->args.enable_decode) { + ERROR("SSB is not configured to decode"); + return SRSRAN_ERROR; + } + + // Select start symbol from SSB candidate index + int t_offset = ssb_get_t_offset(q, ssb_idx); + if (t_offset < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // Demodulate + cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {}; + if (ssb_demodulate(q, in, (uint32_t)t_offset, ssb_grid) < SRSRAN_SUCCESS) { + ERROR("Error demodulating"); + return SRSRAN_ERROR; + } + + // Prepare PBCH DMRS configuration + srsran_dmrs_pbch_cfg_t pbch_dmrs_cfg = {}; + pbch_dmrs_cfg.N_id = N_id; + pbch_dmrs_cfg.n_hf = n_hf; + pbch_dmrs_cfg.ssb_idx = ssb_idx; + pbch_dmrs_cfg.L_max = q->Lmax; + pbch_dmrs_cfg.beta = 0.0f; + pbch_dmrs_cfg.scs = q->cfg.scs; + + // Compute PBCH channel estimates + srsran_dmrs_pbch_meas_t meas = {}; + cf_t ce[SRSRAN_SSB_NOF_RE] = {}; + if (srsran_dmrs_pbch_estimate(&pbch_dmrs_cfg, ssb_grid, ce, &meas) < SRSRAN_SUCCESS) { + ERROR("Error estimating channel"); + return SRSRAN_ERROR; + } + + // Compare measurement with threshold + if (meas.corr < q->args.pbch_dmrs_thr) { + msg->crc = false; + return SRSRAN_SUCCESS; + } + + // Prepare PBCH configuration + srsran_pbch_nr_cfg_t pbch_cfg = {}; + pbch_cfg.N_id = N_id; + pbch_cfg.ssb_scs = q->cfg.scs; + pbch_cfg.Lmax = q->Lmax; + + // Decode + if (srsran_pbch_nr_decode(&q->pbch, &pbch_cfg, ssb_idx, ssb_grid, ce, msg) < SRSRAN_SUCCESS) { + ERROR("Error decoding PBCH"); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} diff --git a/lib/src/phy/sync/test/CMakeLists.txt b/lib/src/phy/sync/test/CMakeLists.txt index aeef97fd3..8e103c62e 100644 --- a/lib/src/phy/sync/test/CMakeLists.txt +++ b/lib/src/phy/sync/test/CMakeLists.txt @@ -136,10 +136,13 @@ add_test(cfo_test_2 cfo_test -f 0.99849 -n 1000) ######################################################################## -# NE TEST +# NR TEST ######################################################################## add_executable(ssb_measure_test ssb_measure_test.c) target_link_libraries(ssb_measure_test srsran_phy) +add_nr_test(ssb_measure_test ssb_measure_test) -add_test(ssb_measure_test ssb_measure_test) +add_executable(ssb_decode_test ssb_decode_test.c) +target_link_libraries(ssb_decode_test srsran_phy) +add_nr_test(ssb_decode_test ssb_decode_test) diff --git a/lib/src/phy/sync/test/ssb_decode_test.c b/lib/src/phy/sync/test/ssb_decode_test.c new file mode 100644 index 000000000..181beb33b --- /dev/null +++ b/lib/src/phy/sync/test/ssb_decode_test.c @@ -0,0 +1,210 @@ +/** + * + * \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/channel/ch_awgn.h" +#include "srsran/phy/sync/ssb.h" +#include "srsran/phy/utils/debug.h" +#include "srsran/phy/utils/vector.h" +#include +#include +#include +#include + +// NR parameters +static uint32_t carrier_nof_prb = 52; +static srsran_subcarrier_spacing_t carrier_scs = srsran_subcarrier_spacing_15kHz; +static srsran_subcarrier_spacing_t ssb_scs = srsran_subcarrier_spacing_30kHz; + +// Channel parameters +static cf_t wideband_gain = 1.0f + 0.5 * I; +static int32_t delay_n = 1; +static float cfo_hz = 1000.0f; +static float n0_dB = -10.0f; + +// Test context +static srsran_random_t random_gen = NULL; +static srsran_channel_awgn_t awgn = {}; +static double srate_hz = 0.0f; // Base-band sampling rate +static uint32_t hf_len = 0; // Half-frame length +static cf_t* buffer = NULL; // Base-band buffer + +static void usage(char* prog) +{ + printf("Usage: %s [v]\n", prog); + printf("\t-v [set srsran_verbose to debug, default none]\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); + } + } +} + +static void run_channel() +{ + // Delay + for (uint32_t i = 0; i < hf_len; i++) { + buffer[i] = buffer[(i + delay_n) % hf_len]; + } + + // CFO + srsran_vec_apply_cfo(buffer, -cfo_hz / srate_hz, buffer, hf_len); + + // AWGN + srsran_channel_awgn_run_c(&awgn, buffer, buffer, hf_len); + + // Wideband gain + srsran_vec_sc_prod_ccc(buffer, wideband_gain, buffer, hf_len); +} + +static void gen_pbch_msg(srsran_pbch_msg_nr_t* pbch_msg, uint32_t ssb_idx) +{ + // Default all to zero + SRSRAN_MEM_ZERO(pbch_msg, srsran_pbch_msg_nr_t, 1); + + // Generate payload + srsran_random_bit_vector(random_gen, pbch_msg->payload, SRSRAN_PBCH_NR_PAYLOAD_SZ); + + pbch_msg->ssb_idx = ssb_idx; +} + +static int test_case_1(srsran_ssb_t* ssb) +{ + // For benchmarking purposes + uint64_t t_encode_usec = 0; + uint64_t t_decode_usec = 0; + + // SSB configuration + srsran_ssb_cfg_t ssb_cfg = {}; + ssb_cfg.srate_hz = srate_hz; + ssb_cfg.center_freq_hz = 3.5e9; + ssb_cfg.ssb_freq_hz = 3.5e9 - 960e3; + ssb_cfg.scs = ssb_scs; + ssb_cfg.pattern = SRSRAN_SSB_PATTERN_C; + + TESTASSERT(srsran_ssb_set_cfg(ssb, &ssb_cfg) == SRSRAN_SUCCESS); + + // For each PCI... + uint64_t count = 0; + for (uint32_t pci = 0; pci < SRSRAN_NOF_NID_NR; pci += 23) { + for (uint32_t ssb_idx = 0; ssb_idx < ssb->Lmax; ssb_idx++, count++) { + struct timeval t[3] = {}; + + // Build PBCH message + srsran_pbch_msg_nr_t pbch_msg_tx = {}; + gen_pbch_msg(&pbch_msg_tx, ssb_idx); + + // Print encoded PBCH message + char str[512] = {}; + srsran_pbch_msg_info(&pbch_msg_tx, str, sizeof(str)); + INFO("test_case_1 - encoded pci=%d %s", pci, str); + + // Initialise baseband + srsran_vec_cf_zero(buffer, hf_len); + + // Add the SSB base-band + gettimeofday(&t[1], NULL); + TESTASSERT(srsran_ssb_add(ssb, pci, ssb_idx, &pbch_msg_tx, buffer, buffer) == SRSRAN_SUCCESS); + gettimeofday(&t[2], NULL); + get_time_interval(t); + t_encode_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL; + + // Run channel + run_channel(); + + // Decode + gettimeofday(&t[1], NULL); + srsran_pbch_msg_nr_t pbch_msg_rx = {}; + TESTASSERT(srsran_ssb_decode_pbch(ssb, pci, ssb_idx, 0, buffer, &pbch_msg_rx) == SRSRAN_SUCCESS); + gettimeofday(&t[2], NULL); + get_time_interval(t); + t_decode_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL; + + // Print decoded PBCH message + srsran_pbch_msg_info(&pbch_msg_rx, str, sizeof(str)); + INFO("test_case_1 - decoded pci=%d %s crc=%s", pci, str, pbch_msg_rx.crc ? "OK" : "KO"); + + // Assert PBCH message CRC + TESTASSERT(pbch_msg_rx.crc); + } + } + + INFO("test_case_1 - %.1f usec/encode; %.1f usec/decode;", + (double)t_encode_usec / (double)(count), + (double)t_decode_usec / (double)(count)); + + return SRSRAN_SUCCESS; +} + +int main(int argc, char** argv) +{ + int ret = SRSRAN_ERROR; + parse_args(argc, argv); + + random_gen = srsran_random_init(1234); + srate_hz = (double)SRSRAN_SUBC_SPACING_NR(carrier_scs) * srsran_min_symbol_sz_rb(carrier_nof_prb); + hf_len = (uint32_t)ceil(srate_hz * (5.0 / 1000.0)); + buffer = srsran_vec_cf_malloc(hf_len); + + srsran_ssb_t ssb = {}; + srsran_ssb_args_t ssb_args = {}; + ssb_args.enable_encode = true; + ssb_args.enable_decode = true; + + if (buffer == NULL) { + ERROR("Malloc"); + goto clean_exit; + } + + if (srsran_channel_awgn_init(&awgn, 0x0) < SRSRAN_SUCCESS) { + ERROR("AWGN"); + goto clean_exit; + } + + if (srsran_channel_awgn_set_n0(&awgn, n0_dB) < SRSRAN_SUCCESS) { + ERROR("AWGN"); + goto clean_exit; + } + + if (srsran_ssb_init(&ssb, &ssb_args) < SRSRAN_SUCCESS) { + ERROR("Init"); + goto clean_exit; + } + + if (test_case_1(&ssb) != SRSRAN_SUCCESS) { + ERROR("test case failed"); + } + + ret = SRSRAN_SUCCESS; + +clean_exit: + srsran_random_free(random_gen); + srsran_ssb_free(&ssb); + + srsran_channel_awgn_free(&awgn); + + if (buffer) { + free(buffer); + } + + return ret; +} \ No newline at end of file diff --git a/lib/src/phy/sync/test/ssb_measure_test.c b/lib/src/phy/sync/test/ssb_measure_test.c index 553f43daa..7b65aed62 100644 --- a/lib/src/phy/sync/test/ssb_measure_test.c +++ b/lib/src/phy/sync/test/ssb_measure_test.c @@ -100,6 +100,7 @@ static int assert_measure(const srsran_csi_trs_measurements_t* meas) static int test_case_1(srsran_ssb_t* ssb) { // For benchmarking purposes + uint64_t t_add_usec = 0; uint64_t t_find_usec = 0; uint64_t t_meas_usec = 0; @@ -110,7 +111,6 @@ static int test_case_1(srsran_ssb_t* ssb) ssb_cfg.ssb_freq_hz = 3.5e9 - 960e3; ssb_cfg.scs = ssb_scs; ssb_cfg.pattern = SRSRAN_SSB_PATTERN_C; - ssb_cfg.position[0] = true; // Rest to false TESTASSERT(srsran_ssb_set_cfg(ssb, &ssb_cfg) == SRSRAN_SUCCESS); @@ -124,7 +124,11 @@ static int test_case_1(srsran_ssb_t* ssb) srsran_vec_cf_zero(buffer, sf_len); // Add the SSB base-band - TESTASSERT(srsran_ssb_add(ssb, pci, &pbch_msg, buffer, buffer) == SRSRAN_SUCCESS); + gettimeofday(&t[1], NULL); + TESTASSERT(srsran_ssb_add(ssb, pci, 0, &pbch_msg, buffer, buffer) == SRSRAN_SUCCESS); + gettimeofday(&t[2], NULL); + get_time_interval(t); + t_add_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL; // Run channel run_channel(); @@ -149,7 +153,7 @@ static int test_case_1(srsran_ssb_t* ssb) // Measure gettimeofday(&t[1], NULL); srsran_csi_trs_measurements_t meas = {}; - TESTASSERT(srsran_ssb_csi_measure(ssb, pci, buffer, &meas) == SRSRAN_SUCCESS); + TESTASSERT(srsran_ssb_csi_measure(ssb, pci, 0, buffer, &meas) == SRSRAN_SUCCESS); gettimeofday(&t[2], NULL); get_time_interval(t); t_meas_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL; @@ -161,7 +165,8 @@ static int test_case_1(srsran_ssb_t* ssb) TESTASSERT(assert_measure(&meas) == SRSRAN_SUCCESS); } - INFO("test_case_1 - %.1f usec/search; Max srate %.1f MSps; %.1f usec/measurement", + INFO("test_case_1 - %.1f usec/encode; %.1f usec/search; Max srate %.1f MSps; %.1f usec/measurement", + (double)t_add_usec / (double)SRSRAN_NOF_NID_NR, (double)t_find_usec / (double)SRSRAN_NOF_NID_NR, (double)sf_len * (double)SRSRAN_NOF_NID_NR / (double)t_find_usec, (double)t_meas_usec / (double)SRSRAN_NOF_NID_NR); diff --git a/lib/src/phy/ue/ue_dl.c b/lib/src/phy/ue/ue_dl.c index d6b674125..d2fb6efce 100644 --- a/lib/src/phy/ue/ue_dl.c +++ b/lib/src/phy/ue/ue_dl.c @@ -449,8 +449,16 @@ static int dci_blind_search(srsran_ue_dl_t* q, return SRSRAN_ERROR; } + // Check if RNTI is matched if ((dci_msg[nof_dci].rnti == rnti) && (dci_msg[nof_dci].nof_bits > 0)) { - dci_msg[nof_dci].rnti = rnti; + // Compute decoded message correlation to drastically reduce false alarm probability + float corr = srsran_pdcch_msg_corr(&q->pdcch, &dci_msg[nof_dci]); + + // Skip candidate if the threshold is not reached + // 0.5 is set from pdcch_test + if (!isnormal(corr) || corr < 0.5f) { + continue; + } // Look for the messages found and apply the new format if the location is common if (search_in_common && (dci_cfg->multiple_csi_request_enabled || dci_cfg->srs_request_enabled)) { @@ -461,8 +469,8 @@ static int dci_blind_search(srsran_ue_dl_t* q, * that only the PDCCH in the common search space is transmitted by the primary cell. */ // Find a matching ncce in the common SS - if (srsran_location_find_ncce( - q->current_ss_common.loc, q->current_ss_common.nof_locations, dci_msg[nof_dci].location.ncce)) { + if (srsran_location_find_location( + q->current_ss_common.loc, q->current_ss_common.nof_locations, &dci_msg[nof_dci].location)) { srsran_dci_cfg_t cfg = *dci_cfg; srsran_dci_cfg_set_common_ss(&cfg); // if the payload size is the same that it would have in the common SS (only Format0/1A is allowed there) diff --git a/lib/src/phy/ue/ue_dl_nr.c b/lib/src/phy/ue/ue_dl_nr.c index d944b9810..8efdafa10 100644 --- a/lib/src/phy/ue/ue_dl_nr.c +++ b/lib/src/phy/ue/ue_dl_nr.c @@ -582,11 +582,11 @@ int srsran_ue_dl_nr_decode_pdsch(srsran_ue_dl_nr_t* q, return SRSRAN_SUCCESS; } -int srsran_ue_dl_nr_pdsch_info(const srsran_ue_dl_nr_t* q, - const srsran_sch_cfg_nr_t* cfg, - const srsran_pdsch_res_nr_t* res, - char* str, - uint32_t str_len) +int srsran_ue_dl_nr_pdsch_info(const srsran_ue_dl_nr_t* q, + const srsran_sch_cfg_nr_t* cfg, + const srsran_pdsch_res_nr_t res[SRSRAN_MAX_CODEWORDS], + char* str, + uint32_t str_len) { int len = 0; diff --git a/lib/src/phy/utils/random.cpp b/lib/src/phy/utils/random.cpp index 0bb4c859d..4b7049d31 100644 --- a/lib/src/phy/utils/random.cpp +++ b/lib/src/phy/utils/random.cpp @@ -122,6 +122,13 @@ bool srsran_random_bool(srsran_random_t q, float prob_true) return srsran_random_uniform_real_dist(q, 0, 1) < prob_true; } +void srsran_random_bit_vector(srsran_random_t q, uint8_t* c, uint32_t nsamples) +{ + for (uint32_t i = 0; i < nsamples; i++) { + c[i] = (uint8_t)srsran_random_uniform_int_dist(q, 0, 1); + } +} + void srsran_random_free(srsran_random_t q) { if (q) { diff --git a/lib/src/phy/utils/ringbuffer.c b/lib/src/phy/utils/ringbuffer.c index 19ac49613..f49f14f63 100644 --- a/lib/src/phy/utils/ringbuffer.c +++ b/lib/src/phy/utils/ringbuffer.c @@ -87,7 +87,11 @@ int srsran_ringbuffer_resize(srsran_ringbuffer_t* q, int capacity) int srsran_ringbuffer_status(srsran_ringbuffer_t* q) { - return q->count; + int status = 0; + pthread_mutex_lock(&q->mutex); + status = q->count; + pthread_mutex_unlock(&q->mutex); + return status; } int srsran_ringbuffer_space(srsran_ringbuffer_t* q) diff --git a/lib/src/phy/utils/vector.c b/lib/src/phy/utils/vector.c index 9fcf0587c..1d4226983 100644 --- a/lib/src/phy/utils/vector.c +++ b/lib/src/phy/utils/vector.c @@ -363,7 +363,7 @@ void srsran_vec_fprint_hex(FILE* stream, uint8_t* x, const uint32_t len) fprintf(stream, "];\n"); } -void srsran_vec_sprint_hex(char* str, const uint32_t max_str_len, uint8_t* x, const uint32_t len) +uint32_t srsran_vec_sprint_hex(char* str, const uint32_t max_str_len, uint8_t* x, const uint32_t len) { uint32_t i, nbytes; uint8_t byte; @@ -371,7 +371,7 @@ void srsran_vec_sprint_hex(char* str, const uint32_t max_str_len, uint8_t* x, co // check that hex string fits in buffer (every byte takes 3 characters, plus brackets) if ((3 * (len / 8 + ((len % 8) ? 1 : 0))) + 2 >= max_str_len) { ERROR("Buffer too small for printing hex string (max_str_len=%d, payload_len=%d).", max_str_len, len); - return; + return 0; } int n = 0; @@ -385,7 +385,10 @@ void srsran_vec_sprint_hex(char* str, const uint32_t max_str_len, uint8_t* x, co n += sprintf(&str[n], "%02x ", byte); } n += sprintf(&str[n], "]"); + str[n] = 0; str[max_str_len - 1] = 0; + + return n; } void srsran_vec_sprint_bin(char* str, const uint32_t max_str_len, const uint8_t* x, const uint32_t len) diff --git a/lib/src/radio/channel_mapping.cc b/lib/src/radio/channel_mapping.cc index 6a9b28cac..a27d6df2c 100644 --- a/lib/src/radio/channel_mapping.cc +++ b/lib/src/radio/channel_mapping.cc @@ -35,11 +35,15 @@ bool channel_mapping::allocate_freq(const uint32_t& logical_ch, const float& fre { std::lock_guard lock(mutex); - if (allocated_channels.count(logical_ch)) { - ERROR("allocate_freq: Carrier logical_ch=%d already allocated to channel=%d", - logical_ch, - allocated_channels[logical_ch].carrier_idx); - return false; + // Check if the logical channel has already been allocated + if (allocated_channels.count(logical_ch) > 0) { + // If the current channel contains the frequency, do nothing else + if (allocated_channels[logical_ch].band.contains(freq)) { + return true; + } + + // Otherwise, release logical channel before searching for a new available channel + release_freq_(logical_ch); } // Find first available channel that supports this frequency and allocated it @@ -54,12 +58,17 @@ bool channel_mapping::allocate_freq(const uint32_t& logical_ch, const float& fre return false; } +void channel_mapping::release_freq_(const uint32_t& logical_ch) +{ + available_channels.push_back(allocated_channels[logical_ch]); + allocated_channels.erase(logical_ch); +} + bool channel_mapping::release_freq(const uint32_t& logical_ch) { std::lock_guard lock(mutex); if (allocated_channels.count(logical_ch)) { - available_channels.push_back(allocated_channels[logical_ch]); - allocated_channels.erase(logical_ch); + release_freq_(logical_ch); return true; } return false; diff --git a/lib/src/radio/radio.cc b/lib/src/radio/radio.cc index a89e013e9..bfecd9384 100644 --- a/lib/src/radio/radio.cc +++ b/lib/src/radio/radio.cc @@ -631,12 +631,17 @@ void radio::set_rx_freq(const uint32_t& carrier_idx, const double& freq) return; } - // First release mapping - rx_channel_mapping.release_freq(carrier_idx); - // Map carrier index to physical channel if (rx_channel_mapping.allocate_freq(carrier_idx, freq)) { channel_mapping::device_mapping_t device_mapping = rx_channel_mapping.get_device_mapping(carrier_idx); + if (device_mapping.carrier_idx >= nof_channels_x_dev) { + logger.error("Invalid mapping RF channel %d to logical carrier %d on f_rx=%.1f MHz", + device_mapping.carrier_idx, + carrier_idx, + freq / 1e6); + return; + } + logger.info("Mapping RF channel %d (device=%d, channel=%d) to logical carrier %d on f_rx=%.1f MHz", device_mapping.carrier_idx, device_mapping.device_idx, @@ -648,6 +653,12 @@ void radio::set_rx_freq(const uint32_t& carrier_idx, const double& freq) cur_rx_freqs[device_mapping.carrier_idx] = freq; for (uint32_t i = 0; i < nof_antennas; i++) { channel_mapping::device_mapping_t dm = rx_channel_mapping.get_device_mapping(carrier_idx, i); + if (dm.device_idx >= rf_devices.size() or dm.carrier_idx >= nof_channels_x_dev) { + logger.error( + "Invalid port mapping %d:%d to logical carrier %d on f_rx=%.1f MHz", carrier_idx, i, freq / 1e6); + return; + } + srsran_rf_set_rx_freq(&rf_devices[dm.device_idx], dm.channel_idx, freq + freq_offset); } } else { @@ -749,12 +760,17 @@ void radio::set_tx_freq(const uint32_t& carrier_idx, const double& freq) return; } - // First release mapping - tx_channel_mapping.release_freq(carrier_idx); - // Map carrier index to physical channel if (tx_channel_mapping.allocate_freq(carrier_idx, freq)) { channel_mapping::device_mapping_t device_mapping = tx_channel_mapping.get_device_mapping(carrier_idx); + if (device_mapping.carrier_idx >= nof_channels_x_dev) { + logger.error("Invalid mapping RF channel %d to logical carrier %d on f_tx=%.1f MHz", + device_mapping.carrier_idx, + carrier_idx, + freq / 1e6); + return; + } + logger.info("Mapping RF channel %d (device=%d, channel=%d) to logical carrier %d on f_tx=%.1f MHz", device_mapping.carrier_idx, device_mapping.device_idx, @@ -766,6 +782,11 @@ void radio::set_tx_freq(const uint32_t& carrier_idx, const double& freq) cur_tx_freqs[device_mapping.carrier_idx] = freq; for (uint32_t i = 0; i < nof_antennas; i++) { device_mapping = tx_channel_mapping.get_device_mapping(carrier_idx, i); + if (device_mapping.device_idx >= rf_devices.size() or device_mapping.carrier_idx >= nof_channels_x_dev) { + logger.error( + "Invalid port mapping %d:%d to logical carrier %d on f_rx=%.1f MHz", carrier_idx, i, freq / 1e6); + return; + } srsran_rf_set_tx_freq(&rf_devices[device_mapping.device_idx], device_mapping.channel_idx, freq + freq_offset); } diff --git a/lib/src/radio/test/benchmark_radio.cc b/lib/src/radio/test/benchmark_radio.cc index d7b26d79c..28c46d753 100644 --- a/lib/src/radio/test/benchmark_radio.cc +++ b/lib/src/radio/test/benchmark_radio.cc @@ -34,6 +34,7 @@ extern "C" { //#undef I // Fix complex.h #define I nastiness when using C++ #endif +#include "srsran/common/tsan_options.h" #include "srsran/phy/utils/debug.h" #include "srsran/radio/radio.h" diff --git a/lib/src/upper/CMakeLists.txt b/lib/src/upper/CMakeLists.txt index aa06021b0..a47bcebc3 100644 --- a/lib/src/upper/CMakeLists.txt +++ b/lib/src/upper/CMakeLists.txt @@ -34,5 +34,5 @@ set(SOURCES gtpu.cc bearer_mem_pool.cc) add_library(srsran_upper STATIC ${SOURCES}) -target_link_libraries(srsran_upper srsran_common srsran_asn1) +target_link_libraries(srsran_upper srsran_common srsran_asn1 ${ATOMIC_LIBS}) INSTALL(TARGETS srsran_upper DESTINATION ${LIBRARY_DIR}) diff --git a/lib/src/upper/rlc.cc b/lib/src/upper/rlc.cc index f51c1d5c3..b6c7be50e 100644 --- a/lib/src/upper/rlc.cc +++ b/lib/src/upper/rlc.cc @@ -301,7 +301,7 @@ uint32_t rlc::get_total_mch_buffer_state(uint32_t lcid) return ret; } -int rlc::read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) +uint32_t rlc::read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) { uint32_t ret = 0; @@ -318,7 +318,7 @@ int rlc::read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) return ret; } -int rlc::read_pdu_mch(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) +uint32_t rlc::read_pdu_mch(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) { uint32_t ret = 0; diff --git a/lib/src/upper/rlc_am_lte.cc b/lib/src/upper/rlc_am_lte.cc index 6c1639902..5ed3b0ffd 100644 --- a/lib/src/upper/rlc_am_lte.cc +++ b/lib/src/upper/rlc_am_lte.cc @@ -257,9 +257,9 @@ uint32_t rlc_am_lte::get_buffer_state() return tx.get_buffer_state(); } -int rlc_am_lte::read_pdu(uint8_t* payload, uint32_t nof_bytes) +uint32_t rlc_am_lte::read_pdu(uint8_t* payload, uint32_t nof_bytes) { - int read_bytes = tx.read_pdu(payload, nof_bytes); + uint32_t read_bytes = tx.read_pdu(payload, nof_bytes); metrics.num_tx_pdus++; metrics.num_tx_pdu_bytes += read_bytes; return read_bytes; @@ -542,7 +542,7 @@ bool rlc_am_lte::rlc_am_lte_tx::sdu_queue_is_full() return tx_sdu_queue.is_full(); } -int rlc_am_lte::rlc_am_lte_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes) +uint32_t rlc_am_lte::rlc_am_lte_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes) { std::lock_guard lock(mutex); @@ -591,6 +591,8 @@ void rlc_am_lte::rlc_am_lte_tx::timer_expired(uint32_t timeout_id) if ((retx_queue.empty() && tx_sdu_queue.size() == 0) || tx_window.size() >= RLC_AM_WINDOW_SIZE) { retransmit_pdu(vt_a); // TODO: TS says to send vt_s - 1 here } + } else if (status_prohibit_timer.is_valid() && status_prohibit_timer.id() == timeout_id) { + logger.debug("%s Status prohibit timer expired after %dms", RB_NAME, status_prohibit_timer.duration()); } lock.unlock(); @@ -708,8 +710,15 @@ int rlc_am_lte::rlc_am_lte_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_byt if (!retx_queue.empty()) { retx = retx_queue.front(); } else { - logger.info("In build_retx_pdu(): retx_queue is empty during sanity check, sn=%d", retx.sn); - return 0; + logger.info("%s SN=%d not in Tx window. Ignoring retx.", RB_NAME, retx.sn); + if (tx_window.has_sn(vt_a)) { + // schedule next SN for retx + retransmit_pdu(vt_a); + retx = retx_queue.front(); + } else { + // empty tx window, can't provide retx PDU + return 0; + } } } @@ -1241,7 +1250,7 @@ void rlc_am_lte::rlc_am_lte_tx::handle_control_pdu(uint8_t* payload, uint32_t no 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); + logger.error("%s NACKed SN=%d already removed from Tx window", RB_NAME, i); } } } @@ -1263,8 +1272,9 @@ void rlc_am_lte::rlc_am_lte_tx::handle_control_pdu(uint8_t* payload, uint32_t no } // Make sure vt_a points to valid SN - if (not tx_window.empty()) { - srsran_expect(tx_window.has_sn(vt_a), "%s vt_a=%d points to invalid position in Tx window", RB_NAME, vt_a); + if (not tx_window.empty() && not tx_window.has_sn(vt_a)) { + logger.error("%s vt_a=%d points to invalid position in Tx window.", RB_NAME, vt_a); + parent->rrc->protocol_failure(); } debug_state(); @@ -1325,7 +1335,7 @@ void rlc_am_lte::rlc_am_lte_tx::debug_state() logger.debug("%s vt_a = %d, vt_ms = %d, vt_s = %d, poll_sn = %d", RB_NAME, vt_a, vt_ms, vt_s, poll_sn); } -int rlc_am_lte::rlc_am_lte_tx::required_buffer_size(rlc_amd_retx_t retx) +int rlc_am_lte::rlc_am_lte_tx::required_buffer_size(const rlc_amd_retx_t& retx) { if (!retx.is_segment) { if (tx_window.has_sn(retx.sn)) { @@ -1924,8 +1934,9 @@ int rlc_am_lte::rlc_am_lte_rx::get_status_pdu(rlc_status_pdu_t* status, const ui status->N_nack--; // make sure we don't have the current ACK_SN in the NACK list if (rlc_am_is_valid_status_pdu(*status) == false) { - // No space to send any NACKs - logger.debug("Resetting N_nack to zero"); + // No space to send any NACKs, play safe and just ack lower edge + logger.debug("Resetting ACK_SN and N_nack to initial state"); + status->ack_sn = vr_r; status->N_nack = 0; } } else { diff --git a/lib/src/upper/rlc_tm.cc b/lib/src/upper/rlc_tm.cc index f9773576f..bf313b18c 100644 --- a/lib/src/upper/rlc_tm.cc +++ b/lib/src/upper/rlc_tm.cc @@ -143,12 +143,12 @@ void rlc_tm::reset_metrics() metrics = {}; } -int rlc_tm::read_pdu(uint8_t* payload, uint32_t nof_bytes) +uint32_t rlc_tm::read_pdu(uint8_t* payload, uint32_t nof_bytes) { uint32_t pdu_size = ul_queue.size_tail_bytes(); if (pdu_size > nof_bytes) { logger.info("%s Tx PDU size larger than MAC opportunity (%d > %d)", rrc->get_rb_name(lcid), pdu_size, nof_bytes); - return -1; + return 0; } unique_byte_buffer_t buf; if (ul_queue.try_read(&buf)) { @@ -180,7 +180,7 @@ int rlc_tm::read_pdu(uint8_t* payload, uint32_t nof_bytes) void rlc_tm::write_pdu(uint8_t* payload, uint32_t nof_bytes) { unique_byte_buffer_t buf = make_byte_buffer(); - if (buf) { + if (buf != nullptr) { memcpy(buf->msg, payload, nof_bytes); buf->N_bytes = nof_bytes; buf->set_timestamp(); diff --git a/lib/src/upper/rlc_um_base.cc b/lib/src/upper/rlc_um_base.cc index 0f9406851..02938a0d8 100644 --- a/lib/src/upper/rlc_um_base.cc +++ b/lib/src/upper/rlc_um_base.cc @@ -139,7 +139,7 @@ uint32_t rlc_um_base::get_buffer_state() return 0; } -int rlc_um_base::read_pdu(uint8_t* payload, uint32_t nof_bytes) +uint32_t rlc_um_base::read_pdu(uint8_t* payload, uint32_t nof_bytes) { if (tx && tx_enabled) { uint32_t len = tx->build_data_pdu(payload, nof_bytes); @@ -292,7 +292,7 @@ bool rlc_um_base::rlc_um_base_tx::sdu_queue_is_full() return tx_sdu_queue.is_full(); } -int rlc_um_base::rlc_um_base_tx::build_data_pdu(uint8_t* payload, uint32_t nof_bytes) +uint32_t rlc_um_base::rlc_um_base_tx::build_data_pdu(uint8_t* payload, uint32_t nof_bytes) { unique_byte_buffer_t pdu; { diff --git a/lib/src/upper/rlc_um_lte.cc b/lib/src/upper/rlc_um_lte.cc index bb5fb285a..a067c410a 100644 --- a/lib/src/upper/rlc_um_lte.cc +++ b/lib/src/upper/rlc_um_lte.cc @@ -118,7 +118,7 @@ bool rlc_um_lte::rlc_um_lte_tx::configure(const rlc_config_t& cnfg_, std::string return true; } -int rlc_um_lte::rlc_um_lte_tx::build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes) +uint32_t rlc_um_lte::rlc_um_lte_tx::build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes) { std::lock_guard lock(mutex); rlc_umd_pdu_header_t header; diff --git a/lib/src/upper/rlc_um_nr.cc b/lib/src/upper/rlc_um_nr.cc index 56bd8bafb..fde561bed 100644 --- a/lib/src/upper/rlc_um_nr.cc +++ b/lib/src/upper/rlc_um_nr.cc @@ -124,7 +124,7 @@ bool rlc_um_nr::rlc_um_nr_tx::configure(const rlc_config_t& cnfg_, std::string r return true; } -int rlc_um_nr::rlc_um_nr_tx::build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes) +uint32_t rlc_um_nr::rlc_um_nr_tx::build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes) { // Sanity check (we need at least 2B for a SDU) if (nof_bytes < 2) { diff --git a/lib/test/adt/circular_buffer_test.cc b/lib/test/adt/circular_buffer_test.cc index bac5e5318..fc4a0104f 100644 --- a/lib/test/adt/circular_buffer_test.cc +++ b/lib/test/adt/circular_buffer_test.cc @@ -216,7 +216,7 @@ void test_dyn_circular_buffer() TESTASSERT(C::count == 0); } -int test_queue_block_api() +void test_queue_block_api() { dyn_blocking_queue queue(100); @@ -238,31 +238,26 @@ int test_queue_block_api() queue.stop(); t.join(); - return SRSRAN_SUCCESS; } -int test_queue_block_api_2() +void test_queue_block_api_2() { std::thread t; - { - dyn_blocking_queue queue(100); - - t = std::thread([&queue]() { - int count = 0; - while (queue.push_blocking(count++)) { - } - }); + dyn_blocking_queue queue(100); - for (int i = 0; i < 10000; ++i) { - TESTASSERT(queue.pop_blocking() == i); + t = std::thread([&queue]() { + int count = 0; + while (queue.push_blocking(count++)) { } + }); - // queue dtor called + for (int i = 0; i < 10000; ++i) { + TESTASSERT(queue.pop_blocking() == i); } + queue.stop(); t.join(); - return SRSRAN_SUCCESS; } } // namespace srsran @@ -276,8 +271,8 @@ int main(int argc, char** argv) TESTASSERT(srsran::test_static_circular_buffer() == SRSRAN_SUCCESS); srsran::test_dyn_circular_buffer(); - TESTASSERT(srsran::test_queue_block_api() == SRSRAN_SUCCESS); - TESTASSERT(srsran::test_queue_block_api_2() == SRSRAN_SUCCESS); + srsran::test_queue_block_api(); + srsran::test_queue_block_api_2(); srsran::console("Success\n"); return SRSRAN_SUCCESS; } \ No newline at end of file diff --git a/lib/test/common/CMakeLists.txt b/lib/test/common/CMakeLists.txt index fcae468cc..c03f458f8 100644 --- a/lib/test/common/CMakeLists.txt +++ b/lib/test/common/CMakeLists.txt @@ -66,7 +66,7 @@ target_link_libraries(queue_test srsran_common ${CMAKE_THREAD_LIBS_INIT}) add_test(queue_test queue_test) add_executable(timer_test timer_test.cc) -target_link_libraries(timer_test srsran_common) +target_link_libraries(timer_test srsran_common ${ATOMIC_LIBS}) add_test(timer_test timer_test) add_executable(network_utils_test network_utils_test.cc) @@ -82,7 +82,7 @@ target_link_libraries(choice_type_test srsran_common) add_test(choice_type_test choice_type_test) add_executable(task_scheduler_test task_scheduler_test.cc) -target_link_libraries(task_scheduler_test srsran_common) +target_link_libraries(task_scheduler_test srsran_common ${ATOMIC_LIBS}) add_test(task_scheduler_test task_scheduler_test) add_executable(pnf_dummy pnf_dummy.cc) diff --git a/lib/test/common/multiqueue_test.cc b/lib/test/common/multiqueue_test.cc index d2564c26b..f45e0d131 100644 --- a/lib/test/common/multiqueue_test.cc +++ b/lib/test/common/multiqueue_test.cc @@ -127,17 +127,17 @@ int test_multiqueue_threading() int capacity = 4, number = 0, start_number = 2, nof_pushes = capacity + 1; multiqueue_handler multiqueue(capacity); auto qid1 = multiqueue.add_queue(); - auto push_blocking_func = [](queue_handle* qid, int start_value, int nof_pushes, bool* is_running) { + std::atomic t1_running = {true}; + auto push_blocking_func = [&t1_running](queue_handle* qid, int start_value, int nof_pushes) { for (int i = 0; i < nof_pushes; ++i) { qid->push(start_value + i); std::cout << "t1: pushed item " << i << std::endl; } std::cout << "t1: pushed all items\n"; - *is_running = false; + t1_running = false; }; - bool t1_running = true; - std::thread t1(push_blocking_func, &qid1, start_number, nof_pushes, &t1_running); + std::thread t1(push_blocking_func, &qid1, start_number, nof_pushes); // Wait for queue to fill while ((int)qid1.size() != capacity) { @@ -357,9 +357,10 @@ int test_task_thread_pool2() // Description: push a very long task to all workers, and call thread_pool.stop() to check if it waits for the tasks // to be completed, and does not get stuck. - uint32_t nof_workers = 4; - uint8_t workers_started = 0, workers_finished = 0; - std::mutex mut; + uint32_t nof_workers = 4; + std::atomic workers_started{0}; + uint8_t workers_finished = 0; + std::mutex mut; task_thread_pool thread_pool(nof_workers); thread_pool.start(); @@ -369,7 +370,7 @@ int test_task_thread_pool2() std::lock_guard lock(mut); workers_started++; } - sleep(1); + std::this_thread::sleep_for(std::chrono::seconds{1}); std::lock_guard lock(mut); std::cout << "worker has finished\n"; workers_finished++; diff --git a/lib/test/common/network_utils_test.cc b/lib/test/common/network_utils_test.cc index 098c23fc5..03afc55fc 100644 --- a/lib/test/common/network_utils_test.cc +++ b/lib/test/common/network_utils_test.cc @@ -53,7 +53,7 @@ int test_socket_handler() { auto& logger = srslog::fetch_basic_logger("S1AP", false); - int counter = 0; + std::atomic counter = {0}; srsran::unique_socket server_socket, client_socket, client_socket2; srsran::socket_manager sockhandler; diff --git a/lib/test/common/task_scheduler_test.cc b/lib/test/common/task_scheduler_test.cc index d6cbfc387..bcb4f903d 100644 --- a/lib/test/common/task_scheduler_test.cc +++ b/lib/test/common/task_scheduler_test.cc @@ -52,7 +52,10 @@ int test_task_scheduler_no_pool() task_sched.notify_background_task_result([&state]() { state = task_result::external; }); }); TESTASSERT(state == task_result::null); - task_sched.run_next_task(); // runs notification + while (state != task_result::external) { + task_sched.run_pending_tasks(); // runs notification + std::this_thread::sleep_for(std::chrono::microseconds(100)); + } TESTASSERT(state == task_result::external); return SRSRAN_SUCCESS; @@ -67,8 +70,10 @@ int test_task_scheduler_with_pool() task_sched.notify_background_task_result([&state]() { state = task_result::external; }); }); TESTASSERT(state == task_result::null); - task_sched.run_next_task(); // waits and runs notification - TESTASSERT(state == task_result::external); + while (state != task_result::external) { + task_sched.run_pending_tasks(); // runs notification + std::this_thread::sleep_for(std::chrono::microseconds(100)); + } return SRSRAN_SUCCESS; } diff --git a/lib/test/common/timer_test.cc b/lib/test/common/timer_test.cc index 833bc7794..f420f0749 100644 --- a/lib/test/common/timer_test.cc +++ b/lib/test/common/timer_test.cc @@ -28,6 +28,8 @@ using namespace srsran; +static_assert(timer_handler::max_timer_duration() == 1073741823, "Invalid max duration"); + int timers_test1() { timer_handler timers; @@ -188,19 +190,23 @@ int timers_test3() } struct timers_test4_ctxt { - std::vector timers; - srsran::tti_sync_cv tti_sync1; - srsran::tti_sync_cv tti_sync2; - const uint32_t duration = 1000; + std::vector timers; + srsran::tti_sync_cv tti_sync1; + srsran::tti_sync_cv tti_sync2; + const uint32_t duration = 1000; }; static void timers2_test4_thread(timers_test4_ctxt* ctx) { - std::mt19937 mt19937(4); + std::random_device rd; + std::mt19937 mt19937(rd()); std::uniform_real_distribution real_dist(0.0f, 1.0f); for (uint32_t d = 0; d < ctx->duration; d++) { // make random events for (uint32_t i = 1; i < ctx->timers.size(); i++) { + // ensure the getters always return reasonable values + TESTASSERT(ctx->timers[i].time_elapsed() <= ctx->duration); + if (0.1f > real_dist(mt19937)) { ctx->timers[i].run(); } @@ -223,76 +229,82 @@ static void timers2_test4_thread(timers_test4_ctxt* ctx) int timers_test4() { - timers_test4_ctxt* ctx = new timers_test4_ctxt; timer_handler timers; + timers_test4_ctxt ctx; uint32_t nof_timers = 32; std::mt19937 mt19937(4); std::uniform_real_distribution real_dist(0.0f, 1.0f); // Generate all timers and start them for (uint32_t i = 0; i < nof_timers; i++) { - ctx->timers.push_back(timers.get_unique_timer()); - ctx->timers[i].set(ctx->duration); - ctx->timers[i].run(); + ctx.timers.push_back(timers.get_unique_timer()); + ctx.timers[i].set(ctx.duration); + ctx.timers[i].run(); } + /* ========== multithreaded region begin =========== */ + // Create side thread - std::thread thread(timers2_test4_thread, ctx); + std::thread thread(timers2_test4_thread, &ctx); - for (uint32_t d = 0; d < ctx->duration; d++) { + for (uint32_t d = 0; d < ctx.duration; d++) { // make random events for (uint32_t i = 1; i < nof_timers; i++) { + // ensure the getters always return reasonable values + TESTASSERT(ctx.timers[i].time_elapsed() <= ctx.duration); + if (0.1f > real_dist(mt19937)) { - ctx->timers[i].run(); + ctx.timers[i].run(); // restart run } if (0.1f > real_dist(mt19937)) { - ctx->timers[i].stop(); + ctx.timers[i].stop(); // stop run } if (0.1f > real_dist(mt19937)) { - ctx->timers[i].set(static_cast(ctx->duration * real_dist(mt19937))); - ctx->timers[i].run(); + ctx.timers[i].set(static_cast(ctx.duration * real_dist(mt19937))); + ctx.timers[i].run(); // start run with new duration } } - // first times, does not have event, it shall keep running - TESTASSERT(ctx->timers[0].is_running()); + // first timer does not get updated, so it shall keep running + TESTASSERT(ctx.timers[0].is_running()); // Increment time timers.step_all(); // wait second thread to finish events - ctx->tti_sync1.wait(); + ctx.tti_sync1.wait(); // assert no timer got wrong values for (uint32_t i = 0; i < nof_timers; i++) { - if (ctx->timers[i].is_running()) { - TESTASSERT(ctx->timers[i].time_elapsed() <= ctx->timers[i].duration()); + if (ctx.timers[i].is_running()) { + TESTASSERT(ctx.timers[i].time_elapsed() <= ctx.timers[i].duration()); + TESTASSERT(ctx.timers[i].duration() <= ctx.duration); } } // Start new TTI - ctx->tti_sync2.increase(); + ctx.tti_sync2.increase(); } // Finish asynchronous thread thread.join(); + /* ========== multithreaded region end =========== */ + // First timer should have expired - TESTASSERT(ctx->timers[0].is_expired()); - TESTASSERT(not ctx->timers[0].is_running()); + TESTASSERT(ctx.timers[0].is_expired()); + TESTASSERT(not ctx.timers[0].is_running()); // Run for the maximum period - for (uint32_t d = 0; d < ctx->duration; d++) { + for (uint32_t d = 0; d < ctx.duration; d++) { timers.step_all(); } // No timer should be running for (uint32_t i = 0; i < nof_timers; i++) { - TESTASSERT(not ctx->timers[i].is_running()); + TESTASSERT(not ctx.timers[i].is_running()); } - delete ctx; - return SRSRAN_SUCCESS; } @@ -364,12 +376,12 @@ int timers_test6() std::vector vals; - // Event: Add a timer that gets erased 1 tti after. + // Event: Add a timer that gets erased 1 tti after, and before expiring. { timer_handler::unique_timer t = timers.get_unique_timer(); t.set(2, [&vals](uint32_t tid) { vals.push_back(1); }); t.run(); - TESTASSERT(timers.nof_running_timers() == 1); + TESTASSERT(timers.nof_running_timers() == 1 and t.duration() == 2 and t.is_running()); timers.step_all(); } TESTASSERT(timers.nof_running_timers() == 0); @@ -384,7 +396,7 @@ int timers_test6() timer_handler::unique_timer t = timers.get_unique_timer(); t.set(2, [&vals](uint32_t tid) { vals.push_back(2); }); t.run(); - TESTASSERT(timers.nof_running_timers() == 1); + TESTASSERT(timers.nof_running_timers() == 1 and t.is_running()); timers.step_all(); TESTASSERT(t.time_elapsed() == 1); } diff --git a/lib/test/mac/pdu_test.cc b/lib/test/mac/pdu_test.cc index dc6c8f73b..26b96de52 100644 --- a/lib/test/mac/pdu_test.cc +++ b/lib/test/mac/pdu_test.cc @@ -178,7 +178,7 @@ int mac_rar_pdu_pack_test2() class rlc_dummy : public srsran::read_pdu_interface { public: - int read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) + uint32_t read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) { uint32_t len = std::min(ul_queues[lcid], nof_bytes); diff --git a/lib/test/phy/phy_dl_nr_test.c b/lib/test/phy/phy_dl_nr_test.c index 985993dad..5d012ece4 100644 --- a/lib/test/phy/phy_dl_nr_test.c +++ b/lib/test/phy/phy_dl_nr_test.c @@ -480,8 +480,10 @@ int main(int argc, char** argv) } if (srsran_verbose >= SRSRAN_VERBOSE_INFO) { - char str[512]; - srsran_ue_dl_nr_pdsch_info(&ue_dl, &pdsch_cfg, &pdsch_res, str, (uint32_t)sizeof(str)); + char str[512]; + srsran_pdsch_res_nr_t pdsch_res_vec[SRSRAN_MAX_CODEWORDS] = {}; + pdsch_res_vec[0] = pdsch_res; + srsran_ue_dl_nr_pdsch_info(&ue_dl, &pdsch_cfg, pdsch_res_vec, str, (uint32_t)sizeof(str)); char str_extra[2048]; srsran_sch_cfg_nr_info(&pdsch_cfg, str_extra, (uint32_t)sizeof(str_extra)); diff --git a/lib/test/phy/phy_dl_test.c b/lib/test/phy/phy_dl_test.c index ae2d84309..8036aa41c 100644 --- a/lib/test/phy/phy_dl_test.c +++ b/lib/test/phy/phy_dl_test.c @@ -44,6 +44,7 @@ static bool print_dci_table; static uint32_t mcs = 20; static int cross_carrier_indicator = -1; static bool enable_256qam = false; +static float snr_db = NAN; // SNR in dB void usage(char* prog) { @@ -56,6 +57,7 @@ void usage(char* prog) printf("\t-d Print DCI table [Default %s]\n", print_dci_table ? "yes" : "no"); printf("\t-t Transmission mode: 1,2,3,4 [Default %d]\n", transmission_mode + 1); printf("\t-m mcs [Default %d]\n", mcs); + printf("\t-S SNR in dB [Default %+.2f]\n", snr_db); printf("\tAdvanced parameters:\n"); if (cross_carrier_indicator >= 0) { printf("\t\t-a carrier-indicator [Default %d]\n", cross_carrier_indicator); @@ -94,7 +96,7 @@ void parse_args(int argc, char** argv) nof_rx_ant = 2; } - while ((opt = getopt(argc, argv, "cfapndvqstmE")) != -1) { + while ((opt = getopt(argc, argv, "cfapndvqstmES")) != -1) { switch (opt) { case 't': transmission_mode = (uint32_t)strtol(argv[optind], NULL, 10) - 1; @@ -121,6 +123,9 @@ void parse_args(int argc, char** argv) case 's': nof_subframes = (uint32_t)strtol(argv[optind], NULL, 10); break; + case 'S': + snr_db = strtof(argv[optind], NULL); + break; case 'E': cell.cp = ((uint32_t)strtol(argv[optind], NULL, 10)) ? SRSRAN_CP_EXT : SRSRAN_CP_NORM; break; @@ -325,6 +330,8 @@ int main(int argc, char** argv) uint32_t count_failures = 0, count_tbs = 0; size_t pdsch_decode_us = 0; size_t pdsch_encode_us = 0; + srsran_channel_awgn_t awgn = {}; + float snr_db_avg = 0.0; int ret = -1; @@ -343,6 +350,11 @@ int main(int argc, char** argv) } } + if (srsran_channel_awgn_init(&awgn, 0x1234) < SRSRAN_SUCCESS) { + ERROR("Error AWGN init"); + goto quit; + } + for (int i = 0; i < SRSRAN_MAX_TB; i++) { softbuffer_tx[i] = (srsran_softbuffer_tx_t*)calloc(sizeof(srsran_softbuffer_tx_t), 1); if (!softbuffer_tx[i]) { @@ -392,6 +404,17 @@ int main(int argc, char** argv) goto quit; } + /* + * Set AWGN N0 + */ + if (isnormal(snr_db)) { + if (srsran_channel_awgn_set_n0(&awgn, srsran_enb_dl_get_maximum_signal_power_dBfs(cell.nof_prb) - snr_db) < + SRSRAN_SUCCESS) { + ERROR("Error setting N0"); + goto quit; + } + } + /* * Initialise UE */ @@ -556,7 +579,10 @@ int main(int argc, char** argv) signal_buffer[0][i] = y0; signal_buffer[1][i] = y1; } + + srsran_channel_awgn_run_c(&awgn, signal_buffer[1], signal_buffer[1], SRSRAN_SF_LEN_PRB(cell.nof_prb)); } + srsran_channel_awgn_run_c(&awgn, signal_buffer[0], signal_buffer[0], SRSRAN_SF_LEN_PRB(cell.nof_prb)); /* * Run UE @@ -601,11 +627,13 @@ int main(int argc, char** argv) get_time_interval(t); pdsch_decode_us += (size_t)(t[0].tv_sec * 1e6 + t[0].tv_usec); + snr_db_avg += ue_dl->chest_res.snr_db; + for (int i = 0; i < SRSRAN_MAX_TB; i++) { if (ue_dl_cfg.cfg.pdsch.grant.tb[i].enabled) { - if (check_evm(enb_dl, ue_dl, &ue_dl_cfg, i)) { + if (!isnormal(snr_db) && check_evm(enb_dl, ue_dl, &ue_dl_cfg, i)) { count_failures++; - } else if (check_softbits(enb_dl, ue_dl, &ue_dl_cfg, sf_idx, i) != SRSRAN_SUCCESS) { + } else if (!isnormal(snr_db) && check_softbits(enb_dl, ue_dl, &ue_dl_cfg, sf_idx, i) != SRSRAN_SUCCESS) { printf("TB%d: The received softbits in subframe %d DO NOT match the encoded bits (crc=%d)\n", i, sf_idx, @@ -645,6 +673,10 @@ int main(int argc, char** argv) printf("BLER: %5.1f%%\n", (float)count_failures / (float)count_tbs * 100.0f); + if (isnormal(snr_db)) { + printf("SNR Real: %+.2f; estimated: %+.2f\n", snr_db, snr_db_avg / nof_subframes); + } + quit: srsran_enb_dl_free(enb_dl); srsran_ue_dl_free(ue_dl); @@ -681,6 +713,7 @@ quit: if (ue_dl) { free(ue_dl); } + srsran_channel_awgn_free(&awgn); if (ret) { printf("Error\n"); diff --git a/lib/test/upper/CMakeLists.txt b/lib/test/upper/CMakeLists.txt index af0d1dcab..135ce1173 100644 --- a/lib/test/upper/CMakeLists.txt +++ b/lib/test/upper/CMakeLists.txt @@ -37,7 +37,7 @@ target_link_libraries(rlc_am_nr_pdu_test srsran_upper srsran_phy) add_nr_test(rlc_am_nr_pdu_test rlc_am_nr_pdu_test) add_executable(rlc_stress_test rlc_stress_test.cc) -target_link_libraries(rlc_stress_test srsran_upper srsran_mac srsran_phy srsran_common ${Boost_LIBRARIES}) +target_link_libraries(rlc_stress_test srsran_upper srsran_mac srsran_phy srsran_common ${Boost_LIBRARIES} ${ATOMIC_LIBS}) add_lte_test(rlc_am_stress_test rlc_stress_test --mode=AM --loglevel 1 --sdu_gen_delay 250) add_lte_test(rlc_um_stress_test rlc_stress_test --mode=UM --loglevel 1) add_lte_test(rlc_tm_stress_test rlc_stress_test --mode=TM --loglevel 1 --random_opp=false) @@ -74,7 +74,7 @@ target_link_libraries(pdcp_nr_test_rx srsran_upper srsran_common) add_nr_test(pdcp_nr_test_rx pdcp_nr_test_rx) add_executable(pdcp_nr_test_discard_sdu pdcp_nr_test_discard_sdu.cc) -target_link_libraries(pdcp_nr_test_discard_sdu srsran_upper srsran_common) +target_link_libraries(pdcp_nr_test_discard_sdu srsran_upper srsran_common ${ATOMIC_LIBS}) add_nr_test(pdcp_nr_test_discard_sdu pdcp_nr_test_discard_sdu) add_executable(pdcp_lte_test_rx pdcp_lte_test_rx.cc) diff --git a/lib/test/upper/rlc_am_test.cc b/lib/test/upper/rlc_am_test.cc index 8df0dbdac..47aaf06c6 100644 --- a/lib/test/upper/rlc_am_test.cc +++ b/lib/test/upper/rlc_am_test.cc @@ -79,12 +79,14 @@ public: // RRC interface void max_retx_attempted() { max_retx_triggered = true; } + void protocol_failure() { protocol_failure_triggered = true; } const char* get_rb_name(uint32_t lcid) { return ""; } std::vector sdus; rlc_pcap* pcap = nullptr; bool max_retx_triggered = false; + bool protocol_failure_triggered = false; std::map notified_counts; // Map of PDCP SNs to number of notifications }; @@ -660,6 +662,7 @@ int segment_retx_test() // Step timers again until poll Retx timeout expires for (int cnt = 0; cnt < 5; cnt++) { + TESTASSERT(rlc1.get_buffer_state() == 0); // No status transmissions until pollRetx expires timers.step_all(); } @@ -3232,9 +3235,9 @@ bool status_pdu_test() TESTASSERT(0 == rlc1.get_buffer_state()); - // Only pass 2nd and last PDUs to RLC2 + // Only pass 1st and last PDUs to RLC2 for (uint32_t i = 0; i < n_pdus; ++i) { - if (i == 0 || i == 2 || i == n_pdus - 1) { + if (i == 0 || i == n_pdus - 1) { rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); } } @@ -3301,6 +3304,73 @@ bool status_pdu_test() return SRSRAN_SUCCESS; } +// This test checks the correct handling of a sending RLC entity when an incorrect status PDU is injected. +// In this test, the receiver requests the retransmission of a SN that he has acknowledeged before. +// The incidence is reported to the upper layers. +bool incorrect_status_pdu_test() +{ + rlc_am_tester tester; + srsran::timer_handler timers(8); + int len = 0; + + rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + + if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { + return -1; + } + + // Push 5 SDUs into RLC1 + const uint32_t n_sdus = 10; + unique_byte_buffer_t sdu_bufs[n_sdus]; + for (uint32_t i = 0; i < n_sdus; i++) { + sdu_bufs[i] = srsran::make_byte_buffer(); + sdu_bufs[i]->N_bytes = 1; // Give each buffer a size of 1 byte + sdu_bufs[i]->msg[0] = i; // Write the index into the buffer + rlc1.write_sdu(std::move(sdu_bufs[i])); + } + + // Read 5 PDUs from RLC1 (1 byte each) + const uint32_t n_pdus = n_sdus; + byte_buffer_t pdu_bufs[n_pdus]; + for (uint32_t i = 0; i < n_pdus; i++) { + len = rlc1.read_pdu(pdu_bufs[i].msg, 3); // 2 byte header + 1 byte payload + pdu_bufs[i].N_bytes = len; + } + + TESTASSERT(0 == rlc1.get_buffer_state()); + + // Construct a status PDU that ACKs SN 1 + rlc_status_pdu_t status_pdu = {}; + status_pdu.ack_sn = 4; + status_pdu.N_nack = 3; + status_pdu.nacks[0].nack_sn = 0; + status_pdu.nacks[1].nack_sn = 2; + TESTASSERT(rlc_am_is_valid_status_pdu(status_pdu)); + + // pack PDU and write to RLC + byte_buffer_t status_buf; + rlc_am_write_status_pdu(&status_pdu, &status_buf); + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + // This will remove SN=1 from the Tx window + + TESTASSERT(tester.protocol_failure_triggered == false); + + // construct a valid but conflicting status PDU that request SN=1 for retx + status_pdu.N_nack = 1; + status_pdu.nacks[0].nack_sn = 1; + TESTASSERT(rlc_am_is_valid_status_pdu(status_pdu)); + + // pack and write to RLC again + rlc_am_write_status_pdu(&status_pdu, &status_buf); + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + TESTASSERT(tester.protocol_failure_triggered == true); + + return SRSRAN_SUCCESS; +} + // This test checks the correct functioning of RLC reestablishment // after maxRetx attempt. bool reestablish_test() @@ -3691,6 +3761,11 @@ int main(int argc, char** argv) exit(-1); }; + if (incorrect_status_pdu_test()) { + printf("incorrect_status_pdu_test failed\n"); + exit(-1); + }; + if (discard_test()) { printf("discard_test failed\n"); exit(-1); diff --git a/lib/test/upper/rlc_common_test.cc b/lib/test/upper/rlc_common_test.cc index f2d1172bb..f60c215e3 100644 --- a/lib/test/upper/rlc_common_test.cc +++ b/lib/test/upper/rlc_common_test.cc @@ -63,6 +63,7 @@ public: // RRC interface void max_retx_attempted() {} + void protocol_failure() {} const char* get_rb_name(uint32_t lcid) { return "TestRB"; } void set_expected_sdu_len(uint32_t len) { expected_sdu_len = len; } diff --git a/lib/test/upper/rlc_stress_test.cc b/lib/test/upper/rlc_stress_test.cc index 017f6f837..ee9169a46 100644 --- a/lib/test/upper/rlc_stress_test.cc +++ b/lib/test/upper/rlc_stress_test.cc @@ -24,6 +24,7 @@ #include "srsran/common/rlc_pcap.h" #include "srsran/common/test_common.h" #include "srsran/common/threads.h" +#include "srsran/common/tsan_options.h" #include "srsran/upper/rlc.h" #include #include @@ -205,8 +206,7 @@ private: // Request data to transmit uint32_t buf_state = tx_rlc->get_buffer_state(lcid); if (buf_state > 0) { - int read = tx_rlc->read_pdu(lcid, pdu->msg, opp_size); - pdu->N_bytes = read; + pdu->N_bytes = tx_rlc->read_pdu(lcid, pdu->msg, opp_size); // Push PDU in the list pdu_list.push_back(std::move(pdu)); @@ -389,6 +389,12 @@ public: std::this_thread::sleep_for(std::chrono::seconds(1)); exit(1); } + void protocol_failure() + { + logger.error("RLC protocol error detected."); + std::this_thread::sleep_for(std::chrono::seconds(1)); + exit(1); + } const char* get_rb_name(uint32_t rx_lcid) { return "DRB1"; } int get_nof_rx_pdus() { return rx_pdus; } diff --git a/lib/test/upper/rlc_test_common.h b/lib/test/upper/rlc_test_common.h index 7eb3f1d86..6b2b6ed9d 100644 --- a/lib/test/upper/rlc_test_common.h +++ b/lib/test/upper/rlc_test_common.h @@ -65,6 +65,7 @@ public: // RRC interface void max_retx_attempted() {} + void protocol_failure() {} const char* get_rb_name(uint32_t lcid) { return ""; } void set_expected_sdu_len(uint32_t len) { expected_sdu_len = len; } diff --git a/srsenb/enb.conf.example b/srsenb/enb.conf.example index 937192caa..c656f6a55 100644 --- a/srsenb/enb.conf.example +++ b/srsenb/enb.conf.example @@ -160,19 +160,30 @@ enable = false # Scheduler configuration options # # sched_policy: User MAC scheduling policy (E.g. time_rr, time_pf) +# min_aggr_level: Optional minimum aggregation level index (l=log2(L) can be 0, 1, 2 or 3) # max_aggr_level: Optional maximum aggregation level index (l=log2(L) can be 0, 1, 2 or 3) +# adaptive_aggr_level: Boolean flag to enable/disable adaptive aggregation level based on target BLER # pdsch_mcs: Optional fixed PDSCH MCS (ignores reported CQIs if specified) # pdsch_max_mcs: Optional PDSCH MCS limit # pusch_mcs: Optional fixed PUSCH MCS (ignores reported CQIs if specified) # pusch_max_mcs: Optional PUSCH MCS limit # min_nof_ctrl_symbols: Minimum number of control symbols # max_nof_ctrl_symbols: Maximum number of control symbols +# target_bler: Target BLER (in decimal) to achieve via adaptive link +# max_delta_dl_cqi: Maximum shift in CQI for adaptive DL link +# max_delta_ul_snr: Maximum shift in UL SNR for adaptive UL link +# adaptive_link_step_size: Step size or learning rate used in adaptive link +# min_tpc_tti_interval: Minimum TTI interval between TPCs different than 1 +# ul_snr_avg_alpha: Exponential Average alpha coefficient used in estimation of UL SNR +# init_ul_snr_value: Initial UL SNR value used for computing MCS in the first UL grant # ##################################################################### [scheduler] #policy = time_pf #policy_args = 2 -#max_aggr_level = -1 +#min_aggr_level = 0 +#max_aggr_level = 3 +#adaptive_aggr_level = false #pdsch_mcs = -1 #pdsch_max_mcs = -1 #pusch_mcs = -1 @@ -180,6 +191,13 @@ enable = false #min_nof_ctrl_symbols = 1 #max_nof_ctrl_symbols = 3 #pucch_multiplex_enable = false +#target_bler = 0.05 +#max_delta_dl_cqi = 5 +#max_delta_ul_snr = 5 +#adaptive_link_step_size = 0.001 +#min_tpc_tti_interval = 1 +#ul_snr_avg_alpha=0.05 +#init_ul_snr_value=5 ##################################################################### # eMBMS configuration options diff --git a/srsenb/hdr/metrics_stdout.h b/srsenb/hdr/metrics_stdout.h index ed2d35313..30144c451 100644 --- a/srsenb/hdr/metrics_stdout.h +++ b/srsenb/hdr/metrics_stdout.h @@ -50,9 +50,9 @@ private: std::string int_to_hex_string(int value, int field_width); std::string float_to_eng_string(float f, int digits); - bool do_print; - uint8_t n_reports; - enb_metrics_interface* enb; + std::atomic do_print = {false}; + uint8_t n_reports = 0; + enb_metrics_interface* enb = nullptr; }; } // namespace srsenb diff --git a/srsenb/hdr/phy/lte/sf_worker.h b/srsenb/hdr/phy/lte/sf_worker.h index b9944c7aa..a3bfb14be 100644 --- a/srsenb/hdr/phy/lte/sf_worker.h +++ b/srsenb/hdr/phy/lte/sf_worker.h @@ -41,7 +41,7 @@ public: void init(phy_common* phy); cf_t* get_buffer_rx(uint32_t cc_idx, uint32_t antenna_idx); - void set_time(uint32_t tti_, uint32_t tx_worker_cnt_, const srsran::rf_timestamp_t& tx_time_); + void set_time(uint32_t tti_, const srsran::rf_timestamp_t& tx_time_); int add_rnti(uint16_t rnti, uint32_t cc_idx); void rem_rnti(uint16_t rnti); @@ -69,8 +69,6 @@ private: std::mutex work_mutex; uint32_t tti_rx = 0, tti_tx_dl = 0, tti_tx_ul = 0; - uint32_t t_rx = 0, t_tx_dl = 0, t_tx_ul = 0; - uint32_t tx_worker_cnt = 0; srsran::rf_timestamp_t tx_time = {}; std::vector > cc_workers; diff --git a/srsenb/hdr/phy/txrx.h b/srsenb/hdr/phy/txrx.h index 716c9be63..b44dab137 100644 --- a/srsenb/hdr/phy/txrx.h +++ b/srsenb/hdr/phy/txrx.h @@ -61,8 +61,6 @@ private: // Main system TTI counter uint32_t tti = 0; - uint32_t tx_worker_cnt = 0; - uint32_t nof_workers = 0; std::atomic running; }; diff --git a/srsenb/hdr/stack/enb_stack_lte.h b/srsenb/hdr/stack/enb_stack_lte.h index 4bd5ef8e9..6376e49f7 100644 --- a/srsenb/hdr/stack/enb_stack_lte.h +++ b/srsenb/hdr/stack/enb_stack_lte.h @@ -86,9 +86,14 @@ public: { return mac.crc_info(tti, rnti, enb_cc_idx, nof_bytes, crc_res); } - int push_pdu(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t nof_bytes, bool crc_res) final + int push_pdu(uint32_t tti, + uint16_t rnti, + uint32_t enb_cc_idx, + uint32_t nof_bytes, + bool crc_res, + uint32_t grant_nof_prbs) final { - return mac.push_pdu(tti, rnti, enb_cc_idx, nof_bytes, crc_res); + return mac.push_pdu(tti, rnti, enb_cc_idx, nof_bytes, crc_res, grant_nof_prbs); } int get_dl_sched(uint32_t tti, dl_sched_list_t& dl_sched_res) final { return mac.get_dl_sched(tti, dl_sched_res); } int get_mch_sched(uint32_t tti, bool is_mcch, dl_sched_list_t& dl_sched_res) final @@ -131,7 +136,7 @@ private: // task handling srsran::task_scheduler task_sched; - srsran::task_queue_handle enb_task_queue, sync_task_queue; + srsran::task_queue_handle enb_task_queue, sync_task_queue, metrics_task_queue; srsenb::mac mac; srsenb::rlc rlc; diff --git a/srsenb/hdr/stack/mac/mac.h b/srsenb/hdr/stack/mac/mac.h index d09df903e..99869e0f8 100644 --- a/srsenb/hdr/stack/mac/mac.h +++ b/srsenb/hdr/stack/mac/mac.h @@ -23,6 +23,7 @@ #define SRSENB_MAC_H #include "sched.h" +#include "srsenb/hdr/common/rnti_pool.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" @@ -68,7 +69,8 @@ public: int ta_info(uint32_t tti, uint16_t rnti, float ta_us) override; int ack_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t tb_idx, bool ack) override; int crc_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t nof_bytes, bool crc_res) override; - int push_pdu(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t nof_bytes, bool crc_res) override; + int push_pdu(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t nof_bytes, bool crc_res, uint32_t ul_nof_prbs) + override; int get_dl_sched(uint32_t tti_tx_dl, dl_sched_list_t& dl_sched_res) override; int get_ul_sched(uint32_t tti_tx_ul, ul_sched_list_t& ul_sched_res) override; @@ -81,8 +83,7 @@ public: /******** Interface from RRC (RRC -> MAC) ****************/ /* Provides cell configuration including SIB periodicity, etc. */ - int cell_cfg(const std::vector& cell_cfg) override; - void reset() override; + int cell_cfg(const std::vector& cell_cfg) override; /* Manages UE scheduling context */ int ue_cfg(uint16_t rnti, sched_interface::ue_cfg_t* cfg) override; @@ -100,8 +101,6 @@ public: /* Handover-related */ uint16_t reserve_new_crnti(const sched_interface::ue_cfg_t& ue_cfg) override; - bool process_pdus(); - void get_metrics(mac_metrics_t& metrics); void toggle_padding(); @@ -117,11 +116,9 @@ public: private: static const uint32_t cfi = 3; - bool check_ue_exists(uint16_t rnti); - uint16_t allocate_rnti(); + bool check_ue_active(uint16_t rnti); uint16_t allocate_ue(); - - std::mutex rnti_mutex; + bool is_valid_rnti_unprotected(uint16_t rnti); srslog::basic_logger& logger; @@ -150,12 +147,9 @@ private: sched_interface::dl_pdu_mch_t mch = {}; /* Map of active UEs */ - rnti_map_t > ue_db; - std::map > ues_to_rem; - uint16_t last_rnti = 70; - - srsran::static_blocking_queue, 32> ue_pool; ///< Pool of pre-allocated UE objects - void prealloc_ue(uint32_t nof_ue); + static const uint16_t FIRST_RNTI = 0x46; + rnti_map_t > ue_db; + std::atomic ue_counter; uint8_t* assemble_rar(sched_interface::dl_sched_rar_grant_t* grants, uint32_t enb_cc_idx, diff --git a/srsenb/hdr/stack/mac/sched.h b/srsenb/hdr/stack/mac/sched.h index b97c61a60..244eed143 100644 --- a/srsenb/hdr/stack/mac/sched.h +++ b/srsenb/hdr/stack/mac/sched.h @@ -73,7 +73,7 @@ public: int ul_crc_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, bool crc) final; int ul_sr_info(uint32_t tti, uint16_t rnti) override; int ul_bsr(uint16_t rnti, uint32_t lcg_id, uint32_t bsr) final; - int ul_phr(uint16_t rnti, int phr) final; + int ul_phr(uint16_t rnti, int phr, uint32_t ul_nof_prb) final; int ul_snr_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, float snr, uint32_t ul_ch_code) final; int dl_sched(uint32_t tti, uint32_t enb_cc_idx, dl_sched_res_t& sched_result) final; diff --git a/srsenb/hdr/stack/mac/sched_helpers.h b/srsenb/hdr/stack/mac/sched_helpers.h index f63aeeb7f..6279a25a1 100644 --- a/srsenb/hdr/stack/mac/sched_helpers.h +++ b/srsenb/hdr/stack/mac/sched_helpers.h @@ -78,6 +78,7 @@ inline uint32_t get_tbs_bytes(uint32_t mcs, uint32_t nof_alloc_prb, bool use_tbs /// Find lowest DCI aggregation level supported by the UE spectral efficiency uint32_t get_aggr_level(uint32_t nof_bits, uint32_t dl_cqi, + uint32_t min_aggr_lvl, uint32_t max_aggr_lvl, uint32_t cell_nof_prb, bool use_tbs_index_alt); diff --git a/srsenb/hdr/stack/mac/sched_phy_ch/sched_dci.h b/srsenb/hdr/stack/mac/sched_phy_ch/sched_dci.h index dfcc42652..0d46f590b 100644 --- a/srsenb/hdr/stack/mac/sched_phy_ch/sched_dci.h +++ b/srsenb/hdr/stack/mac/sched_phy_ch/sched_dci.h @@ -56,6 +56,19 @@ tbs_info compute_mcs_and_tbs(uint32_t nof_prb, bool ulqam64_enabled, bool use_tbs_index_alt); +/** + * Compute MCS, TBS based on maximum coderate, N_prb + * \remark See TS 36.213 - Table 7.1.7.1-1/1A + * @return resulting TBS (in bytes) and mcs. TBS=-1 if no valid solution was found. + */ +tbs_info compute_mcs_and_tbs(uint32_t nof_prb, + uint32_t nof_re, + float max_coderate, + uint32_t max_mcs, + bool is_ul, + bool ulqam64_enabled, + bool use_tbs_index_alt); + /** * Compute lowest MCS, TBS based on CQI, N_prb that satisfies TBS >= req_bytes (best effort) * \remark See TS 36.213 - Table 7.1.7.1-1/1A diff --git a/srsenb/hdr/stack/mac/sched_ue.h b/srsenb/hdr/stack/mac/sched_ue.h index 53cb4e9ba..489d909d2 100644 --- a/srsenb/hdr/stack/mac/sched_ue.h +++ b/srsenb/hdr/stack/mac/sched_ue.h @@ -62,7 +62,7 @@ public: void dl_buffer_state(uint8_t lc_id, uint32_t tx_queue, uint32_t retx_queue); void ul_buffer_state(uint8_t lcg_id, uint32_t bsr); - void ul_phr(int phr); + void ul_phr(int phr, uint32_t grant_nof_prb); void mac_buffer_state(uint32_t ce_code, uint32_t nof_cmds); void set_ul_snr(tti_point tti_rx, uint32_t enb_cc_idx, float snr, uint32_t ul_ch_code); @@ -147,6 +147,7 @@ public: bool pdsch_enabled(tti_point tti_rx, uint32_t enb_cc_idx) const; bool pusch_enabled(tti_point tti_rx, uint32_t enb_cc_idx, bool needs_pdcch) const; + bool phich_enabled(tti_point tti_rx, uint32_t enb_cc_idx) const; private: bool is_sr_triggered(); diff --git a/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_harq.h b/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_harq.h index d6363622c..0e15bb196 100644 --- a/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_harq.h +++ b/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_harq.h @@ -45,6 +45,7 @@ public: srsran::tti_point get_tti() const; bool get_ndi(uint32_t tb_idx) const; uint32_t max_nof_retx() const; + int get_mcs(uint32_t tb_idx) const { return last_mcs[tb_idx]; } protected: void new_tx_common(uint32_t tb_idx, srsran::tti_point tti, int mcs, int tbs, uint32_t max_retx_); @@ -55,17 +56,17 @@ protected: enum ack_t { NACK, ACK }; - srslog::basic_logger* logger; - bool ack_state[SRSRAN_MAX_TB]; - bool active[SRSRAN_MAX_TB]; - std::array ndi = {}; - uint32_t id; - uint32_t max_retx = 5; - uint32_t n_rtx[SRSRAN_MAX_TB]; - uint32_t tx_cnt[SRSRAN_MAX_TB]; - srsran::tti_point tti; - int last_mcs[SRSRAN_MAX_TB]; - int last_tbs[SRSRAN_MAX_TB]; + srslog::basic_logger* logger = nullptr; + std::array ack_state = {}; + std::array active = {}; + std::array ndi = {}; + uint32_t id = 0; + uint32_t max_retx = 5; + std::array n_rtx = {}; + std::array tx_cnt = {}; + std::array last_mcs = {}; + std::array last_tbs = {}; + srsran::tti_point tti; }; class dl_harq_proc : public harq_proc @@ -114,12 +115,15 @@ public: uint32_t get_pending_data() const; bool has_pending_phich() const; bool pop_pending_phich(); + void request_pdcch(); + void retx_skipped(); private: prb_interval allocation; int pending_data; - bool pending_phich = false; - bool is_msg3_ = false; + bool pending_phich = false; + bool is_msg3_ = false; + bool pdcch_requested = false; }; class harq_entity @@ -156,9 +160,9 @@ public: * @param tti_rx tti the DL ACK was received * @param tb_idx TB index for the given ACK * @param ack true for ACK and false for NACK - * @return pair with pid and size of TB of the DL harq that was ACKed + * @return tuple with pid, TBS and MCS of the DL harq that was ACKed */ - std::pair set_ack_info(tti_point tti_rx, uint32_t tb_idx, bool ack); + std::tuple set_ack_info(tti_point tti_rx, uint32_t tb_idx, bool ack); //! Get UL Harq for a given tti_tx_ul ul_harq_proc* get_ul_harq(tti_point tti_tx_ul); @@ -169,7 +173,7 @@ public: int set_ul_crc(srsran::tti_point tti_tx_ul, uint32_t tb_idx, bool ack_); //! Resets pending harq ACKs and cleans UL Harqs with maxretx == 0 - void reset_pending_data(srsran::tti_point tti_rx); + void finish_tti(srsran::tti_point tti_rx); private: dl_harq_proc* get_oldest_dl_harq(tti_point tti_tx_dl); diff --git a/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_ue_cell.h b/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_ue_cell.h index 89100f1ff..4bdc6e22d 100644 --- a/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_ue_cell.h +++ b/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_ue_cell.h @@ -42,7 +42,7 @@ struct sched_ue_cell { void clear_feedback(); void finish_tti(tti_point tti_rx); - void set_dl_wb_cqi(tti_point tti_rx, uint32_t dl_cqi_); + int set_dl_wb_cqi(tti_point tti_rx, uint32_t dl_cqi_); bool configured() const { return ue_cc_idx >= 0; } int get_ue_cc_idx() const { return ue_cc_idx; } @@ -52,6 +52,16 @@ struct sched_ue_cell { const sched_interface::ue_cfg_t* get_ue_cfg() const { return configured() ? ue_cfg : nullptr; } cc_st cc_state() const { return cc_state_; } + int get_dl_cqi() const; + int get_dl_cqi(const rbgmask_t& rbgs) const; + int get_ul_cqi() const; + + uint32_t get_aggr_level(uint32_t nof_bits) const; + + int set_ack_info(tti_point tti_rx, uint32_t tb_idx, bool ack); + int set_ul_crc(tti_point tti_rx, bool crc_res); + int set_ul_snr(tti_point tti_rx, float ul_snr, uint32_t ul_ch_code); + const uint16_t rnti; /// Cell const configuration @@ -72,7 +82,6 @@ struct sched_ue_cell { tti_point dl_ri_tti_rx{}; uint32_t dl_pmi = 0; tti_point dl_pmi_tti_rx{}; - uint32_t ul_cqi = 1; tti_point ul_cqi_tti_rx{}; uint32_t max_mcs_dl = 28, max_mcs_ul = 28; @@ -90,6 +99,11 @@ private: tti_point current_tti; cc_st cc_state_ = cc_st::idle; + // CQI + float delta_inc = 0, delta_dec = 0; + float dl_cqi_coeff = 0, ul_snr_coeff = 0; + float max_cqi_coeff = -5, max_snr_coeff = 5; + sched_dl_cqi dl_cqi_ctxt; }; diff --git a/srsenb/hdr/stack/mac/sched_ue_ctrl/tpc.h b/srsenb/hdr/stack/mac/sched_ue_ctrl/tpc.h index e191ffefb..93b6c365c 100644 --- a/srsenb/hdr/stack/mac/sched_ue_ctrl/tpc.h +++ b/srsenb/hdr/stack/mac/sched_ue_ctrl/tpc.h @@ -44,18 +44,27 @@ public: static constexpr uint32_t PUSCH_CODE = 0, PUCCH_CODE = 1; static constexpr int PHR_NEG_NOF_PRB = 1; - explicit tpc(uint32_t cell_nof_prb, - float target_pucch_snr_dB_ = -1.0, - float target_pusch_sn_dB_ = -1.0, - bool phr_handling_flag_ = false) : + explicit tpc(uint16_t rnti_, + uint32_t cell_nof_prb, + float target_pucch_snr_dB_ = -1.0, + float target_pusch_sn_dB_ = -1.0, + bool phr_handling_flag_ = false, + int min_phr_thres_ = 0, + uint32_t min_tpc_tti_interval_ = 1, + float ul_snr_avg_alpha = 0.05, + int init_ul_snr_value = 5) : + rnti(rnti_), nof_prb(cell_nof_prb), target_pucch_snr_dB(target_pucch_snr_dB_), target_pusch_snr_dB(target_pusch_sn_dB_), - snr_estim_list({ul_ch_snr_estim{target_pusch_snr_dB}, ul_ch_snr_estim{target_pucch_snr_dB}}), - phr_handling_flag(phr_handling_flag_) - { - max_prbs_cached = nof_prb; - } + min_phr_thres(min_phr_thres_), + snr_estim_list( + {ul_ch_snr_estim{ul_snr_avg_alpha, init_ul_snr_value}, ul_ch_snr_estim{ul_snr_avg_alpha, init_ul_snr_value}}), + phr_handling_flag(phr_handling_flag_), + max_prbs_cached(nof_prb), + min_tpc_tti_interval(min_tpc_tti_interval_), + logger(srslog::fetch_basic_logger("MAC")) + {} void set_cfg(float target_pusch_snr_dB_, float target_pucch_snr_dB_) { target_pucch_snr_dB = target_pucch_snr_dB_; @@ -64,11 +73,16 @@ public: void set_snr(float snr, uint32_t ul_ch_code) { + static const float MIN_UL_SNR = -4.0f; + if (snr < MIN_UL_SNR) { + // Assume signal was not sent + return; + } if (ul_ch_code < nof_ul_ch_code) { snr_estim_list[ul_ch_code].pending_snr = snr; } } - void set_phr(int phr_) + void set_phr(int phr_, uint32_t grant_nof_prbs) { last_phr = phr_; for (auto& ch_snr : snr_estim_list) { @@ -78,24 +92,27 @@ public: // compute and cache the max nof UL PRBs that avoids overflowing PHR if (phr_handling_flag) { max_prbs_cached = PHR_NEG_NOF_PRB; + int phr_x_prb = std::roundf(last_phr + 10.0F * log10f(grant_nof_prbs)); // get what the PHR would be if Nprb=1 for (int n = nof_prb; n > PHR_NEG_NOF_PRB; --n) { - if (last_phr >= 10 * log10(n)) { + if (phr_x_prb >= 10 * log10f(n) + min_phr_thres) { max_prbs_cached = n; break; } } + logger.info("SCHED: rnti=0x%x received PHR=%d for UL Nprb=%d. Max UL Nprb is now=%d", + rnti, + phr_, + grant_nof_prbs, + max_prbs_cached); } } void new_tti() { - for (size_t chidx = 0; chidx < 2; ++chidx) { + tti_count++; + for (size_t chidx = 0; chidx < nof_ul_ch_code; ++chidx) { float target_snr_dB = chidx == PUSCH_CODE ? target_pusch_snr_dB : target_pucch_snr_dB; auto& ch_snr = snr_estim_list[chidx]; - if (target_snr_dB < 0) { - ch_snr.pending_delta = 0; - continue; - } // Enqueue pending UL Channel SNR measurement if (ch_snr.pending_snr == null_snr) { @@ -109,7 +126,9 @@ public: ch_snr.pending_snr = null_snr; // Enqueue PUSCH/PUCCH TPC sent in last TTI (zero for both Delta_PUSCH/Delta_PUCCH=0 and TPC not sent) - ch_snr.win_tpc_values.push(ch_snr.pending_delta); + if (target_snr_dB >= 0) { + ch_snr.win_tpc_values.push(ch_snr.pending_delta); + } ch_snr.pending_delta = 0; } } @@ -130,6 +149,8 @@ public: uint32_t max_ul_prbs() const { return max_prbs_cached; } + float get_ul_snr_estim(uint32_t ul_ch_code = PUSCH_CODE) const { return snr_estim_list[ul_ch_code].snr_avg.value(); } + private: uint8_t encode_tpc_delta(int8_t delta) { @@ -143,39 +164,58 @@ private: case 3: return 3; default: - srslog::fetch_basic_logger("MAC").warning("Invalid TPC delta value=%d", delta); + logger.warning("Invalid TPC delta value=%d", delta); return 1; } } uint8_t encode_tpc(uint32_t cc) { - float target_snr_dB = cc == PUSCH_CODE ? target_pusch_snr_dB : target_pucch_snr_dB; - auto& ch_snr = snr_estim_list[cc]; + auto& ch_snr = snr_estim_list[cc]; assert(ch_snr.pending_delta == 0); // ensure called once per {cc,tti} + + float target_snr_dB = cc == PUSCH_CODE ? target_pusch_snr_dB : target_pucch_snr_dB; if (target_snr_dB < 0) { - // undefined target sinr case. - ch_snr.pending_delta = 0; - } else { - // target SINR is finite and there is power headroom - float diff = target_snr_dB - ch_snr.snr_avg.value(); - diff -= ch_snr.win_tpc_values.value() + ch_snr.acc_tpc_values; - int8_t diff_round = roundf(diff); - if (diff_round >= 1) { - ch_snr.pending_delta = diff_round > 3 ? 3 : 1; - } else if (diff_round <= -1) { - ch_snr.pending_delta = -1; - } - if (last_phr <= 0) { - // In case there is no headroom, forbid power increases - ch_snr.pending_delta = std::min(ch_snr.pending_delta, 0); + // undefined target sinr case, or no more PHR + return encode_tpc_delta(0); + } + if ((tti_count - ch_snr.last_tpc_tti_count) < min_tpc_tti_interval) { + // more time required before sending next TPC + return encode_tpc_delta(0); + } + if (cc == PUSCH_CODE and last_phr < 0 and not ch_snr.phr_flag) { + // if negative PHR and PUSCH + logger.info("TPC: rnti=0x%x, PUSCH command=0 due to PHR=%d<0", rnti, last_phr); + ch_snr.phr_flag = true; + return encode_tpc_delta(-1); + } + + // target SINR is finite and there is power headroom + float diff = target_snr_dB - ch_snr.snr_avg.value(); + diff -= ch_snr.win_tpc_values.value() + ch_snr.acc_tpc_values; + if (diff >= 1) { + ch_snr.pending_delta = diff > 3 ? 3 : 1; + if (cc == PUSCH_CODE and static_cast(ch_snr.pending_delta) > last_phr) { + // cap PUSCH TPC when PHR is low + ch_snr.pending_delta = last_phr > 1 ? 1 : 0; } + ch_snr.last_tpc_tti_count = tti_count; + } else if (diff <= -1) { + ch_snr.pending_delta = -1; + ch_snr.last_tpc_tti_count = tti_count; } return encode_tpc_delta(ch_snr.pending_delta); } - uint32_t nof_prb; - float target_pucch_snr_dB, target_pusch_snr_dB; - bool phr_handling_flag; + uint16_t rnti; + uint32_t nof_prb; + uint32_t min_tpc_tti_interval = 1; + float target_pucch_snr_dB, target_pusch_snr_dB; + int min_phr_thres; + bool phr_handling_flag; + srslog::basic_logger& logger; + + // state + uint32_t tti_count = 0; // PHR-related variables int last_phr = undefined_phr; @@ -192,11 +232,12 @@ private: srsran::exp_average_irreg_sampling snr_avg; // Accumulation of past TPC commands srsran::sliding_sum win_tpc_values; - int pending_delta = 0; - int acc_tpc_values = 0; + int8_t pending_delta = 0; + int acc_tpc_values = 0; + uint32_t last_tpc_tti_count = 0; - explicit ul_ch_snr_estim(float target_ul_snr = -1) : - snr_avg(0.1, target_ul_snr), win_tpc_values(FDD_HARQ_DELAY_UL_MS + FDD_HARQ_DELAY_DL_MS) + explicit ul_ch_snr_estim(float exp_avg_alpha, int initial_snr) : + snr_avg(exp_avg_alpha, initial_snr), win_tpc_values(FDD_HARQ_DELAY_UL_MS + FDD_HARQ_DELAY_DL_MS) {} }; std::array snr_estim_list; diff --git a/srsenb/hdr/stack/mac/ue.h b/srsenb/hdr/stack/mac/ue.h index e7e4fddbf..500f20c5a 100644 --- a/srsenb/hdr/stack/mac/ue.h +++ b/srsenb/hdr/stack/mac/ue.h @@ -45,6 +45,7 @@ class rrc_interface_mac; class rlc_interface_mac; class phy_interface_stack_lte; +/// Class to manage the allocation, deallocation & access to UE carrier DL + UL softbuffers struct ue_cc_softbuffers { // List of Tx softbuffers for all HARQ processes of one carrier using cc_softbuffer_tx_list_t = std::vector; @@ -68,39 +69,35 @@ struct ue_cc_softbuffers { srsran_softbuffer_rx_t& get_rx(uint32_t tti) { return softbuffer_rx_list.at(tti % nof_rx_harq_proc); } }; +/// Class to manage the allocation, deallocation & access to pending UL HARQ buffers class cc_used_buffers_map { public: - explicit cc_used_buffers_map(srsran::pdu_queue& shared_pdu_queue_); + explicit cc_used_buffers_map(); ~cc_used_buffers_map(); + void clear() { pdu_map.clear(); } + uint8_t* request_pdu(tti_point tti, uint32_t len); - bool push_pdu(tti_point tti, uint32_t len); + srsran::unique_byte_buffer_t release_pdu(tti_point tti); void clear_old_pdus(tti_point current_tti); - bool try_deallocate_pdu(tti_point tti); - - void clear(); - uint8_t*& operator[](tti_point tti); bool has_tti(tti_point tti) const; private: - void remove_pdu(tti_point tti); - srslog::basic_logger* logger; - srsran::pdu_queue* shared_pdu_queue; - srsran::static_circular_map pdu_map; + srsran::static_circular_map pdu_map; }; class cc_buffer_handler { public: - explicit cc_buffer_handler(srsran::pdu_queue& shared_pdu_queue_); + explicit cc_buffer_handler(); ~cc_buffer_handler(); void reset(); @@ -126,11 +123,11 @@ private: // buffers cc_used_buffers_map rx_used_buffers; - // One buffer per TB per HARQ process and per carrier is needed for each UE. + // One buffer per TB per DL HARQ process and per carrier is needed for each UE. std::array, SRSRAN_FDD_NOF_HARQ> tx_payload_buffer; }; -class ue : public srsran::read_pdu_interface, public srsran::pdu_queue::process_callback, public mac_ta_ue_interface +class ue : public srsran::read_pdu_interface, public mac_ta_ue_interface { public: ue(uint16_t rnti, @@ -148,12 +145,14 @@ public: void start_pcap(srsran::mac_pcap* pcap_); void start_pcap_net(srsran::mac_pcap_net* pcap_net_); void set_tti(uint32_t tti); - uint16_t get_rnti() { return rnti; } + uint16_t get_rnti() const { return rnti; } uint32_t set_ta(int ta) override; void start_ta() { ta_fsm.start(); }; uint32_t set_ta_us(float ta_us) { return ta_fsm.push_value(ta_us); }; void tic(); void trigger_padding(int lcid); + void set_active(bool active) { active_state.store(active, std::memory_order_relaxed); } + bool is_active() const { return active_state.load(std::memory_order_relaxed); } uint8_t* generate_pdu(uint32_t ue_cc_idx, uint32_t harq_pid, @@ -164,16 +163,13 @@ public: uint8_t* 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); - srsran_softbuffer_rx_t* get_rx_softbuffer(const uint32_t ue_cc_idx, const uint32_t tti); + srsran_softbuffer_tx_t* get_tx_softbuffer(uint32_t ue_cc_idx, uint32_t harq_process, uint32_t tb_idx); + srsran_softbuffer_rx_t* get_rx_softbuffer(uint32_t ue_cc_idx, uint32_t tti); - bool process_pdus(); - uint8_t* request_buffer(uint32_t tti, uint32_t ue_cc_idx, const uint32_t len); - void process_pdu(uint8_t* pdu, uint32_t nof_bytes, srsran::pdu_queue::channel_t channel) override; - void push_pdu(uint32_t tti, uint32_t ue_cc_idx, uint32_t len); - void deallocate_pdu(uint32_t tti, uint32_t ue_cc_idx); - void clear_old_buffers(uint32_t tti); + uint8_t* request_buffer(uint32_t tti, uint32_t ue_cc_idx, const uint32_t len); + void process_pdu(srsran::unique_byte_buffer_t pdu, uint32_t grant_nof_prbs); + srsran::unique_byte_buffer_t release_pdu(uint32_t tti, uint32_t ue_cc_idx); + void clear_old_buffers(uint32_t tti); void metrics_read(mac_ue_metrics_t* metrics_); void metrics_rx(bool crc, uint32_t tbs); @@ -184,11 +180,11 @@ public: void metrics_dl_cqi(uint32_t dl_cqi); void metrics_cnt(); - int read_pdu(uint32_t lcid, uint8_t* payload, uint32_t requested_bytes) final; + uint32_t read_pdu(uint32_t lcid, uint8_t* payload, uint32_t requested_bytes) final; private: void allocate_sdu(srsran::sch_pdu* pdu, uint32_t lcid, uint32_t sdu_len); - bool process_ce(srsran::sch_subh* subh); + bool process_ce(srsran::sch_subh* subh, uint32_t grant_nof_prbs); void allocate_ce(srsran::sch_pdu* pdu, uint32_t lcid); rlc_interface_mac* rlc = nullptr; @@ -204,6 +200,8 @@ private: uint32_t last_tti = 0; uint32_t nof_failures = 0; + std::atomic active_state{true}; + uint32_t phr_counter = 0; uint32_t dl_cqi_counter = 0; uint32_t dl_ri_counter = 0; @@ -216,9 +214,8 @@ private: ta ta_fsm; // For UL there are multiple buffers per PID and are managed by pdu_queue - srsran::pdu_queue pdus; - srsran::sch_pdu mac_msg_dl, mac_msg_ul; - srsran::mch_pdu mch_mac_msg_dl; + srsran::sch_pdu mac_msg_dl, mac_msg_ul; + srsran::mch_pdu mch_mac_msg_dl; srsran::bounded_vector cc_buffers; diff --git a/srsenb/hdr/stack/rrc/mac_controller.h b/srsenb/hdr/stack/rrc/mac_controller.h index 3bfc01a0f..a36804ee5 100644 --- a/srsenb/hdr/stack/rrc/mac_controller.h +++ b/srsenb/hdr/stack/rrc/mac_controller.h @@ -64,7 +64,7 @@ public: const srsran::rrc_ue_capabilities_t& uecaps); void handle_ho_prep(const asn1::rrc::ho_prep_info_r8_ies_s& ho_prep); - void handle_max_retx(); + void set_radio_bearer_state(sched_interface::ue_bearer_cfg_t::direction_t dir); const ue_cfg_t& get_ue_sched_cfg() const { return current_sched_ue_cfg; } bool is_crnti_set() const { return crnti_set; } diff --git a/srsenb/hdr/stack/rrc/rrc.h b/srsenb/hdr/stack/rrc/rrc.h index 3adc43133..755b32744 100644 --- a/srsenb/hdr/stack/rrc/rrc.h +++ b/srsenb/hdr/stack/rrc/rrc.h @@ -88,6 +88,7 @@ public: // rrc_interface_rlc void read_pdu_pcch(uint32_t tti_tx_dl, uint8_t* payload, uint32_t buffer_size) override; void max_retx_attempted(uint16_t rnti) override; + void protocol_failure(uint16_t rnti) override; // rrc_interface_s1ap void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) override; @@ -151,8 +152,9 @@ public: log_rrc_message(Tx, rnti, -1, pdu, msg, msg_type); } -private: class ue; + +private: // args srsran::task_sched_handle task_sched; phy_interface_rrc_lte* phy = nullptr; @@ -202,6 +204,7 @@ private: const static uint32_t LCID_RLC_RTX = 0xffff0005; const static uint32_t LCID_RADLINK_DL = 0xffff0006; const static uint32_t LCID_RADLINK_UL = 0xffff0007; + const static uint32_t LCID_PROT_FAIL = 0xffff0008; bool running = false; srsran::dyn_blocking_queue rx_pdu_queue; diff --git a/srsenb/hdr/stack/rrc/rrc_cell_cfg.h b/srsenb/hdr/stack/rrc/rrc_cell_cfg.h index e8494b56c..22e7193b6 100644 --- a/srsenb/hdr/stack/rrc/rrc_cell_cfg.h +++ b/srsenb/hdr/stack/rrc/rrc_cell_cfg.h @@ -82,10 +82,10 @@ public: const static uint32_t N_PUCCH_MAX_PRB = 4; // Maximum number of PRB to use for PUCCH ACK/NACK in CS mode const static uint32_t N_PUCCH_MAX_RES = 3 * SRSRAN_NRE * N_PUCCH_MAX_PRB; - uint32_t next_measgap_offset = 0; - pucch_idx_sched_t sr_sched = {}; - pucch_idx_sched_t cqi_sched = {}; - std::array n_pucch_cs_used = {}; + pucch_idx_sched_t sr_sched = {}; + pucch_idx_sched_t cqi_sched = {}; + std::array n_pucch_cs_used = {}; + std::array meas_gap_alloc_map = {}; }; /** Storage of CQI/SR/PUCCH CS resources across multiple frequencies and for multiple users */ diff --git a/srsenb/hdr/stack/rrc/rrc_nr.h b/srsenb/hdr/stack/rrc/rrc_nr.h index d47b3a2bc..4b3fa34f5 100644 --- a/srsenb/hdr/stack/rrc/rrc_nr.h +++ b/srsenb/hdr/stack/rrc/rrc_nr.h @@ -97,6 +97,7 @@ public: // TODO void read_pdu_pcch(uint8_t* payload, uint32_t payload_size) {} void max_retx_attempted(uint16_t rnti) {} + void protocol_failure(uint16_t rnti) {} const char* get_rb_name(uint32_t lcid) { return "invalid"; } // PDCP interface void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) final; diff --git a/srsenb/hdr/stack/rrc/rrc_paging.h b/srsenb/hdr/stack/rrc/rrc_paging.h index 5a88a4792..c2227b414 100644 --- a/srsenb/hdr/stack/rrc/rrc_paging.h +++ b/srsenb/hdr/stack/rrc/rrc_paging.h @@ -22,6 +22,7 @@ #ifndef SRSRAN_RRC_PAGING_H #define SRSRAN_RRC_PAGING_H +#include "srsran/adt/pool/cached_alloc.h" #include "srsran/adt/span.h" #include "srsran/asn1/rrc/paging.h" #include "srsran/common/tti_point.h" @@ -29,7 +30,9 @@ namespace srsenb { /** - * Class that handles the buffering of paging records and encoding of PCCH messages. It's thread-safe + * Class that handles the buffering of paging records and encoding of PCCH messages. + * It's thread-safe, and, assuming that threads contend for pending PCCH messages using different subframe indexes, + * should rarely blocking on mutexes */ class paging_manager { @@ -39,11 +42,11 @@ public: Nb(T * nb_), N(std::min(T, Nb)), Ns(std::max(nb_, 1u)), - pending_paging(T), logger(srslog::fetch_basic_logger("RRC")) { - for (auto& sfn_pcch_msgs : pending_paging) { - for (pcch_info& pcch : sfn_pcch_msgs) { + for (subframe_info& sf_obj : sf_pending_pcch) { + sf_obj.pending_paging.resize(T); + for (pcch_info& pcch : sf_obj.pending_paging) { pcch.pcch_msg.msg.set_c1().paging().paging_record_list_present = true; } } @@ -61,7 +64,10 @@ public: /** * Invoke "callable" for PCCH indexed by tti_tx_dl in a mutexed context. * Callable signature is bool(const_byte_span pdu, const pcch_msg& msg, bool is_first_tx) - * - "is_first_tx" tells if the PDU hasn't been transmitted yet. + * - "pdu" encoded ASN1 PCCH message + * - "msg" PCCH message in ASN1 form + * - "is_first_tx" tells if the PDU hasn't been transmitted yet. This may be useful to log the PCCH only for one + * of the carriers * - the return should be true if the PDU is being transmitted, and false otherwise */ template @@ -84,23 +90,8 @@ private: }; const static size_t nof_paging_subframes = 4; - bool add_paging_record(uint32_t ueid, const asn1::rrc::paging_record_s& paging_record); - pcch_info* get_pcch_info(tti_point tti_tx_dl) - { - int sf_key = get_sf_idx_key(tti_tx_dl.sf_idx()); - if (sf_key < 0) { - return nullptr; - } - return &pending_paging[tti_tx_dl.sfn() % T][sf_key]; - } - const pcch_info* get_pcch_info(tti_point tti_tx_dl) const - { - int sf_key = get_sf_idx_key(tti_tx_dl.sf_idx()); - if (sf_key < 0) { - return nullptr; - } - return &pending_paging[tti_tx_dl.sfn() % T][sf_key]; - } + bool add_paging_record(uint32_t ueid, const asn1::rrc::paging_record_s& paging_record); + static int get_sf_idx_key(uint32_t sf_idx) { switch (sf_idx) { @@ -125,8 +116,13 @@ private: uint32_t Ns; srslog::basic_logger& logger; - mutable std::array sf_idx_mutex; - std::vector > pending_paging; + struct subframe_info { + mutable std::mutex mutex; + srsran::deque transmitted_pcch; + std::vector pending_paging; + }; + + std::array sf_pending_pcch; }; bool paging_manager::add_imsi_paging(uint32_t ueid, srsran::const_byte_span imsi) @@ -166,16 +162,21 @@ bool paging_manager::add_paging_record(uint32_t ueid, const asn1::rrc::paging_re } size_t sf_key = static_cast(get_sf_idx_key(sf_idx)); + subframe_info& locked_sf = sf_pending_pcch[static_cast(sf_key)]; + std::lock_guard lock(locked_sf.mutex); + size_t sfn_cycle_idx = (T / N) * (ueid % N); - pcch_info& pending_pcch = pending_paging[sfn_cycle_idx][sf_key]; + pcch_info& pending_pcch = locked_sf.pending_paging[sfn_cycle_idx]; auto& record_list = pending_pcch.pcch_msg.msg.c1().paging().paging_record_list; - std::lock_guard lock(sf_idx_mutex[sf_key]); - if (record_list.size() >= ASN1_RRC_MAX_PAGE_REC) { logger.warning("Failed to add new paging record for ueid=%d. Cause: no paging record space left.", ueid); return false; } + if (pending_pcch.is_tx()) { + logger.error("Adding Paging records to ueid=%d PCCH that has been already sent but not cleared.", ueid); + pending_pcch.clear(); + } if (pending_pcch.pdu == nullptr) { pending_pcch.pdu = srsran::make_byte_buffer(); @@ -206,19 +207,24 @@ size_t paging_manager::pending_pcch_bytes(tti_point tti_tx_dl) return 0; } - std::lock_guard lock(sf_idx_mutex[static_cast(sf_key)]); + subframe_info& locked_sf = sf_pending_pcch[static_cast(sf_key)]; + std::unique_lock lock(locked_sf.mutex, std::try_to_lock); + if (not lock.owns_lock()) { + // If the scheduler fails to lock, it will postpone the PCCH transmission to the next paging cycle + return 0; + } // clear old PCCH that has been transmitted at this point - pcch_info* old_pcch = get_pcch_info(tti_tx_dl - SRSRAN_NOF_SF_X_FRAME); - if (old_pcch != nullptr and not old_pcch->empty()) { - old_pcch->clear(); + while (not locked_sf.transmitted_pcch.empty() and locked_sf.transmitted_pcch.front()->tti_tx_dl < tti_tx_dl) { + locked_sf.transmitted_pcch.front()->clear(); + locked_sf.transmitted_pcch.pop_front(); } - const pcch_info* pending_pcch = get_pcch_info(tti_tx_dl); - if (pending_pcch->empty()) { + const pcch_info& pending_pcch = locked_sf.pending_paging[tti_tx_dl.sfn() % T]; + if (pending_pcch.empty()) { return 0; } - return pending_pcch->pdu->size(); + return pending_pcch.pdu->size(); } template @@ -230,18 +236,24 @@ bool paging_manager::read_pdu_pcch(tti_point tti_tx_dl, const Callable& func) return false; } - std::lock_guard lock(sf_idx_mutex[static_cast(sf_key)]); + subframe_info& locked_sf = sf_pending_pcch[static_cast(sf_key)]; + std::lock_guard lock(locked_sf.mutex); - pcch_info* pending_pcch = get_pcch_info(tti_tx_dl); + pcch_info& pending_pcch = locked_sf.pending_paging[tti_tx_dl.sfn() % T]; - if (pending_pcch->empty()) { + if (pending_pcch.empty()) { logger.warning("read_pdu_pdcch(...) called for tti=%d, but there is no pending pcch message", tti_tx_dl.to_uint()); return false; } // Call callable for existing PCCH pdu - if (func(*pending_pcch->pdu, pending_pcch->pcch_msg, not pending_pcch->is_tx())) { - pending_pcch->tti_tx_dl = tti_tx_dl; + if (func(*pending_pcch.pdu, pending_pcch.pcch_msg, not pending_pcch.is_tx())) { + if (not pending_pcch.is_tx()) { + // first tx. Enqueue in list of transmitted pcch. We do not erase the PCCH yet because it may be transmitted + // by other carriers + pending_pcch.tti_tx_dl = tti_tx_dl; + locked_sf.transmitted_pcch.push_back(&pending_pcch); + } return true; } return false; diff --git a/srsenb/hdr/stack/rrc/rrc_ue.h b/srsenb/hdr/stack/rrc/rrc_ue.h index bb78d7702..29f8e8b22 100644 --- a/srsenb/hdr/stack/rrc/rrc_ue.h +++ b/srsenb/hdr/stack/rrc/rrc_ue.h @@ -34,6 +34,12 @@ class rrc::ue { public: class rrc_mobility; + enum activity_timeout_type_t { + MSG3_RX_TIMEOUT = 0, ///< Msg3 has its own timeout to quickly remove fake UEs from random PRACHs + UE_INACTIVITY_TIMEOUT, ///< UE inactivity timeout (usually bigger than reestablishment timeout) + MSG5_RX_TIMEOUT, ///< UE timeout for receiving RRCConnectionSetupComplete / RRCReestablishmentComplete + nulltype + }; ue(rrc* outer_rrc, uint16_t rnti, const sched_interface::ue_cfg_t& ue_cfg); ~ue(); @@ -41,20 +47,16 @@ public: bool is_connected(); bool is_idle(); - typedef enum { - MSG3_RX_TIMEOUT = 0, ///< Msg3 has its own timeout to quickly remove fake UEs from random PRACHs - UE_INACTIVITY_TIMEOUT, ///< UE inactivity timeout (usually bigger than reestablishment timeout) - nulltype - } activity_timeout_type_t; - std::string to_string(const activity_timeout_type_t& type); - void set_activity_timeout(const activity_timeout_type_t type); - void set_activity(); + void set_activity_timeout(activity_timeout_type_t type); + void set_activity(bool enabled = true); void set_radiolink_dl_state(bool crc_res); void set_radiolink_ul_state(bool crc_res); void activity_timer_expired(const activity_timeout_type_t type); void rlf_timer_expired(uint32_t timeout_id); void max_rlc_retx_reached(); + void protocol_failure(); + void deactivate_bearers() { mac_ctrl.set_radio_bearer_state(sched_interface::ue_bearer_cfg_t::IDLE); } rrc_state_t get_state(); void get_metrics(rrc_ue_metrics_t& ue_metrics) const; @@ -70,6 +72,7 @@ public: error_unknown_rnti, radio_conn_with_ue_lost, msg3_timeout, + fail_in_radio_interface_proc, unspecified }; diff --git a/srsenb/hdr/stack/s1ap/s1ap.h b/srsenb/hdr/stack/s1ap/s1ap.h index 0ed6edbac..a17453e87 100644 --- a/srsenb/hdr/stack/s1ap/s1ap.h +++ b/srsenb/hdr/stack/s1ap/s1ap.h @@ -89,7 +89,7 @@ public: bool user_exists(uint16_t rnti) override; void user_mod(uint16_t old_rnti, uint16_t new_rnti) override; bool user_release(uint16_t rnti, asn1::s1ap::cause_radio_network_e cause_radio) override; - void ue_ctxt_setup_complete(uint16_t rnti) override; + void notify_rrc_reconf_complete(uint16_t rnti) override; bool is_mme_connected() override; bool send_ho_required(uint16_t rnti, uint32_t target_eci, @@ -119,7 +119,7 @@ public: // Stack interface bool - handle_mme_rx_msg(srsran::unique_byte_buffer_t pdu, const sockaddr_in& from, const sctp_sndrcvinfo& sri, int flags); + handle_mme_rx_msg(srsran::unique_byte_buffer_t pdu, const sockaddr_in& from, const sctp_sndrcvinfo& sri, int flags); void start_pcap(srsran::s1ap_pcap* pcap_); private: @@ -177,6 +177,8 @@ private: bool handle_erabreleasecommand(const asn1::s1ap::erab_release_cmd_s& msg); bool handle_uecontextmodifyrequest(const asn1::s1ap::ue_context_mod_request_s& msg); + void ue_ctxt_setup_complete(uint16_t rnti); + // handover /** * Source eNB Handler for S1AP "HANDOVER PREPARATION FAILURE" Message @@ -256,6 +258,7 @@ private: uint32_t m_tmsi = 0, uint8_t mmec = 0); void ue_ctxt_setup_complete(); + void notify_rrc_reconf_complete(); bool send_erab_setup_response(const erab_id_list& erabs_setup, const erab_item_list& erabs_failed); bool send_erab_release_response(const erab_id_list& erabs_released, const erab_item_list& erabs_failed); bool send_erab_modify_response(const erab_id_list& erabs_modified, const erab_item_list& erabs_failed); @@ -286,6 +289,7 @@ private: bool release_requested = false; srsran::unique_timer ts1_reloc_prep; ///< TS1_{RELOCprep} - max time for HO preparation srsran::unique_timer ts1_reloc_overall; ///< TS1_{RELOCOverall} + srsran::unique_timer overall_procedure_timeout; // Procedure state s1ap_proc_id_t current_state = s1ap_proc_id_t::nulltype; diff --git a/srsenb/hdr/stack/upper/rlc.h b/srsenb/hdr/stack/upper/rlc.h index cd9896e94..2abb48216 100644 --- a/srsenb/hdr/stack/upper/rlc.h +++ b/srsenb/hdr/stack/upper/rlc.h @@ -87,6 +87,7 @@ private: void write_pdu_pcch(srsran::unique_byte_buffer_t sdu); void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t sdu) {} void max_retx_attempted(); + void protocol_failure(); const char* get_rb_name(uint32_t lcid); uint16_t rnti; diff --git a/srsenb/hdr/stack/upper/rlc_nr.h b/srsenb/hdr/stack/upper/rlc_nr.h index 2b9261f96..cfca6ca5f 100644 --- a/srsenb/hdr/stack/upper/rlc_nr.h +++ b/srsenb/hdr/stack/upper/rlc_nr.h @@ -76,6 +76,7 @@ private: void write_pdu_pcch(srsran::unique_byte_buffer_t sdu); void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t sdu) {} void max_retx_attempted() final; + void protocol_failure() final; const char* get_rb_name(uint32_t lcid) final; uint16_t rnti; diff --git a/srsenb/rr.conf.example b/srsenb/rr.conf.example index 37db345cd..ee1b285a9 100644 --- a/srsenb/rr.conf.example +++ b/srsenb/rr.conf.example @@ -62,8 +62,11 @@ cell_list = //ul_earfcn = 21400; ho_active = false; //meas_gap_period = 0; // 0 (inactive), 40 or 80 + //meas_gap_offset_subframe = [6, 12, 18, 24, 30]; // target_pusch_sinr = -1; // target_pucch_sinr = -1; + // enable_phr_handling = false; + // min_phr_thres = 0; // allowed_meas_bw = 6; // t304 = 2000; // in msec. possible values: 50, 100, 150, 200, 500, 1000, 2000 diff --git a/srsenb/src/common/rnti_pool.cc b/srsenb/src/common/rnti_pool.cc index 1b095597e..aca4f610e 100644 --- a/srsenb/src/common/rnti_pool.cc +++ b/srsenb/src/common/rnti_pool.cc @@ -21,14 +21,22 @@ #include "srsenb/hdr/common/rnti_pool.h" #include "srsenb/hdr/common/common_enb.h" +#include "srsenb/hdr/stack/mac/ue.h" +#include "srsenb/hdr/stack/rrc/rrc_mobility.h" +#include "srsenb/hdr/stack/rrc/rrc_ue.h" #include "srsran/adt/pool/circular_stack_pool.h" +#include "srsran/upper/pdcp.h" +#include "srsran/upper/rlc.h" namespace srsenb { +const static size_t UE_MEM_BLOCK_SIZE = + sizeof(ue) + sizeof(rrc::ue) + sizeof(rrc::ue::rrc_mobility) + sizeof(srsran::rlc) + sizeof(srsran::pdcp); + srsran::circular_stack_pool* get_rnti_pool() { static std::unique_ptr > pool( - new srsran::circular_stack_pool(8, 32768, 4)); + new srsran::circular_stack_pool(8, UE_MEM_BLOCK_SIZE, 4)); return pool.get(); } diff --git a/srsenb/src/enb_cfg_parser.cc b/srsenb/src/enb_cfg_parser.cc index 7f256eba3..9514c0b9f 100644 --- a/srsenb/src/enb_cfg_parser.cc +++ b/srsenb/src/enb_cfg_parser.cc @@ -769,9 +769,18 @@ static int parse_cell_list(all_args_t* args, rrc_cfg_t* rrc_cfg, Setting& root) cell_cfg.root_seq_idx, cellroot, "root_seq_idx", rrc_cfg->sibs[1].sib2().rr_cfg_common.prach_cfg.root_seq_idx); parse_default_field(cell_cfg.initial_dl_cqi, cellroot, "initial_dl_cqi", 5u); parse_default_field(cell_cfg.meas_cfg.meas_gap_period, cellroot, "meas_gap_period", 0u); + if (cellroot.exists("meas_gap_offset_subframe")) { + cell_cfg.meas_cfg.meas_gap_offset_subframe.resize(cellroot["meas_gap_offset_subframe"].getLength()); + for (uint32_t j = 0; j < (uint32_t)cellroot["meas_gap_offset_subframe"].getLength(); ++j) { + cell_cfg.meas_cfg.meas_gap_offset_subframe[j] = (uint32_t)cellroot["meas_gap_offset_subframe"][j]; + srsran_assert(cell_cfg.meas_cfg.meas_gap_offset_subframe[j] < cell_cfg.meas_cfg.meas_gap_period, + "meas gap offsets must be smaller than meas gap period"); + } + } HANDLEPARSERCODE(parse_default_field(cell_cfg.target_pusch_sinr_db, cellroot, "target_pusch_sinr", -1)); HANDLEPARSERCODE(parse_default_field(cell_cfg.target_pucch_sinr_db, cellroot, "target_pucch_sinr", -1)); HANDLEPARSERCODE(parse_default_field(cell_cfg.enable_phr_handling, cellroot, "enable_phr_handling", false)); + HANDLEPARSERCODE(parse_default_field(cell_cfg.min_phr_thres, cellroot, "min_phr_thres", 0)); parse_default_field(cell_cfg.meas_cfg.allowed_meas_bw, cellroot, "allowed_meas_bw", 6u); srsran_assert(srsran::is_lte_cell_nof_prb(cell_cfg.meas_cfg.allowed_meas_bw), "Invalid measurement Bandwidth"); HANDLEPARSERCODE(asn1_parsers::default_number_to_enum( diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index a2a72decc..f03946a9e 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -154,10 +154,19 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("scheduler.pdsch_max_mcs", bpo::value(&args->stack.mac.sched.pdsch_max_mcs)->default_value(-1), "Optional PDSCH MCS limit") ("scheduler.pusch_mcs", bpo::value(&args->stack.mac.sched.pusch_mcs)->default_value(-1), "Optional fixed PUSCH MCS (ignores reported CQIs if specified)") ("scheduler.pusch_max_mcs", bpo::value(&args->stack.mac.sched.pusch_max_mcs)->default_value(-1), "Optional PUSCH MCS limit") - ("scheduler.max_aggr_level", bpo::value(&args->stack.mac.sched.max_aggr_level)->default_value(-1), "Optional maximum aggregation level index (l=log2(L)) ") + ("scheduler.min_aggr_level", bpo::value(&args->stack.mac.sched.min_aggr_level)->default_value(0), "Optional minimum aggregation level index (l=log2(L)) ") + ("scheduler.max_aggr_level", bpo::value(&args->stack.mac.sched.max_aggr_level)->default_value(3), "Optional maximum aggregation level index (l=log2(L)) ") + ("scheduler.adaptive_aggr_level", bpo::value(&args->stack.mac.sched.adaptive_aggr_level)->default_value(false), "Boolean flag to enable/disable adaptive aggregation level based on target BLER") ("scheduler.max_nof_ctrl_symbols", bpo::value(&args->stack.mac.sched.max_nof_ctrl_symbols)->default_value(3), "Number of control symbols") ("scheduler.min_nof_ctrl_symbols", bpo::value(&args->stack.mac.sched.min_nof_ctrl_symbols)->default_value(1), "Minimum number of control symbols") ("scheduler.pucch_multiplex_enable", bpo::value(&args->stack.mac.sched.pucch_mux_enabled)->default_value(false), "Enable PUCCH multiplexing") + ("scheduler.target_bler", bpo::value(&args->stack.mac.sched.target_bler)->default_value(0.05), "Target BLER (in decimal) to achieve via adaptive link") + ("scheduler.max_delta_dl_cqi", bpo::value(&args->stack.mac.sched.max_delta_dl_cqi)->default_value(5.0), "Maximum shift in CQI for adaptive DL link") + ("scheduler.max_delta_ul_snr", bpo::value(&args->stack.mac.sched.max_delta_ul_snr)->default_value(5.0), "Maximum shift in UL SNR for adaptive UL link") + ("scheduler.adaptive_link_step_size", bpo::value(&args->stack.mac.sched.max_delta_ul_snr)->default_value(0.001), "Step size or learning rate used in adaptive link") + ("scheduler.min_tpc_tti_interval", bpo::value(&args->stack.mac.sched.min_tpc_tti_interval)->default_value(1), "Minimum TTI interval between positive or negative TPCs") + ("scheduler.ul_snr_avg_alpha", bpo::value(&args->stack.mac.sched.ul_snr_avg_alpha)->default_value(0.05), "Exponential Average alpha coefficient used in estimation of UL SNR") + ("scheduler.init_ul_snr_value", bpo::value(&args->stack.mac.sched.init_ul_snr_value)->default_value(5), "Initial UL SNR value used for computing MCS in the first UL grant") /* Downlink Channel emulator section */ diff --git a/srsenb/src/metrics_stdout.cc b/srsenb/src/metrics_stdout.cc index 1644ae4af..b8fff249e 100644 --- a/srsenb/src/metrics_stdout.cc +++ b/srsenb/src/metrics_stdout.cc @@ -74,6 +74,12 @@ void metrics_stdout::toggle_print(bool b) do_print = b; } +// Define iszero() here since it's not defined in some platforms +static bool iszero(float x) +{ + return fabsf(x) < 2 * DBL_EPSILON; +} + void metrics_stdout::set_metrics(const enb_metrics_t& metrics, const uint32_t period_usec) { if (!do_print || enb == nullptr) { @@ -94,7 +100,7 @@ void metrics_stdout::set_metrics(const enb_metrics_t& metrics, const uint32_t pe n_reports = 0; cout << endl; cout << "------DL-------------------------------UL--------------------------------------------" << endl; - cout << "rnti cqi ri mcs brate ok nok (%) pusch pucch phr mcs brate ok nok (%) bsr" << endl; + cout << "rnti cqi ri mcs brate ok nok (%) pusch pucch phr mcs brate ok nok (%) bsr" << endl; } for (size_t i = 0; i < metrics.stack.rrc.ues.size(); i++) { @@ -110,52 +116,53 @@ void metrics_stdout::set_metrics(const enb_metrics_t& metrics, const uint32_t pe } cout << int_to_hex_string(metrics.stack.mac.ues[i].rnti, 4) << " "; - cout << float_to_string(SRSRAN_MAX(0.1, metrics.stack.mac.ues[i].dl_cqi), 1, 3); + if (not iszero(metrics.stack.mac.ues[i].dl_cqi)) { + cout << float_to_string(metrics.stack.mac.ues[i].dl_cqi, 1, 3); + } else { + cout << "n/a"; + } cout << float_to_string(metrics.stack.mac.ues[i].dl_ri, 1, 4); if (not isnan(metrics.phy[i].dl.mcs)) { - cout << float_to_string(SRSRAN_MAX(0.1, metrics.phy[i].dl.mcs), 1, 4); + cout << float_to_string(metrics.phy[i].dl.mcs, 1, 4); } else { cout << float_to_string(0, 2, 4); } if (metrics.stack.mac.ues[i].tx_brate > 0) { - cout << float_to_eng_string( - SRSRAN_MAX(0.1, (float)metrics.stack.mac.ues[i].tx_brate / (metrics.stack.mac.ues[i].nof_tti * 1e-3)), 1); + cout << float_to_eng_string((float)metrics.stack.mac.ues[i].tx_brate / (metrics.stack.mac.ues[i].nof_tti * 1e-3), 1); } else { cout << float_to_string(0, 1, 6) << ""; } cout << std::setw(5) << metrics.stack.mac.ues[i].tx_pkts - metrics.stack.mac.ues[i].tx_errors; cout << std::setw(5) << metrics.stack.mac.ues[i].tx_errors; if (metrics.stack.mac.ues[i].tx_pkts > 0 && metrics.stack.mac.ues[i].tx_errors) { - cout << float_to_string( - SRSRAN_MAX(0.1, (float)100 * metrics.stack.mac.ues[i].tx_errors / metrics.stack.mac.ues[i].tx_pkts), 1, 4) + cout << float_to_string((float)100 * metrics.stack.mac.ues[i].tx_errors / metrics.stack.mac.ues[i].tx_pkts, 1, 4) << "%"; } else { cout << float_to_string(0, 1, 4) << "%"; } cout << " "; - if (not isnan(metrics.phy[i].ul.pusch_sinr)) { - cout << float_to_string(SRSRAN_MAX(0.1, metrics.phy[i].ul.pusch_sinr), 2, 5); + if (not isnan(metrics.phy[i].ul.pusch_sinr) and not iszero(metrics.phy[i].ul.pusch_sinr)) { + cout << float_to_string(metrics.phy[i].ul.pusch_sinr, 2, 5); } else { - cout << float_to_string(0, 2, 5); + cout << " n/a"; } - if (not isnan(metrics.phy[i].ul.pucch_sinr)) { - cout << float_to_string(SRSRAN_MAX(0.1, metrics.phy[i].ul.pucch_sinr), 2, 5); + if (not isnan(metrics.phy[i].ul.pucch_sinr) and not iszero(metrics.phy[i].ul.pucch_sinr)) { + cout << float_to_string(metrics.phy[i].ul.pucch_sinr, 2, 6); } else { - cout << float_to_string(0, 2, 5); + cout << " n/a"; } cout << " "; cout << float_to_string(metrics.stack.mac.ues[i].phr, 2, 5); if (not isnan(metrics.phy[i].ul.mcs)) { - cout << float_to_string(SRSRAN_MAX(0.1, metrics.phy[i].ul.mcs), 1, 4); + cout << float_to_string(metrics.phy[i].ul.mcs, 1, 4); } else { cout << float_to_string(0, 1, 4); } if (metrics.stack.mac.ues[i].rx_brate > 0) { - cout << float_to_eng_string( - SRSRAN_MAX(0.1, (float)metrics.stack.mac.ues[i].rx_brate / (metrics.stack.mac.ues[i].nof_tti * 1e-3)), 1); + cout << float_to_eng_string((float)metrics.stack.mac.ues[i].rx_brate / (metrics.stack.mac.ues[i].nof_tti * 1e-3), 1); } else { cout << float_to_string(0, 1) << ""; } @@ -163,8 +170,7 @@ void metrics_stdout::set_metrics(const enb_metrics_t& metrics, const uint32_t pe cout << std::setw(5) << metrics.stack.mac.ues[i].rx_errors; if (metrics.stack.mac.ues[i].rx_pkts > 0 && metrics.stack.mac.ues[i].rx_errors > 0) { - cout << float_to_string( - SRSRAN_MAX(0.1, (float)100 * metrics.stack.mac.ues[i].rx_errors / metrics.stack.mac.ues[i].rx_pkts), 1, 4) + cout << float_to_string((float)100 * metrics.stack.mac.ues[i].rx_errors / metrics.stack.mac.ues[i].rx_pkts, 1, 4) << "%"; } else { cout << float_to_string(0, 1, 4) << "%"; @@ -184,7 +190,7 @@ std::string metrics_stdout::float_to_string(float f, int digits, int field_width f = 0.0; precision = digits - 1; } else { - precision = digits - (int)(log10f(fabs(f)) - 2 * DBL_EPSILON); + precision = digits - (int)(log10f(fabs(f + 0.0001)) - 2 * DBL_EPSILON); } if (precision == -1) { precision = 0; diff --git a/srsenb/src/phy/lte/cc_worker.cc b/srsenb/src/phy/lte/cc_worker.cc index 2f1ef9def..61324b338 100644 --- a/srsenb/src/phy/lte/cc_worker.cc +++ b/srsenb/src/phy/lte/cc_worker.cc @@ -196,8 +196,6 @@ void cc_worker::rem_rnti(uint16_t rnti) if (ue_db.count(rnti)) { delete ue_db[rnti]; ue_db.erase(rnti); - } else { - Error("Removing user: rnti=0x%x does not exist\n", rnti); } } @@ -282,7 +280,8 @@ void cc_worker::decode_pusch_rnti(stack_interface_phy_lte::ul_sched_grant_t& ul_ // Get UE configuration if (phy->ue_db.get_ul_config(rnti, cc_idx, ul_cfg) < SRSRAN_SUCCESS) { - Error("Error retrieving UL configuration for RNTI %x and CC %d", rnti, cc_idx); + // It could happen that the UL configuration is missing due to intra-enb HO which is not an error + Info("Failed retrieving UL configuration for cc=%d rnti=0x%x", cc_idx, rnti); return; } @@ -375,7 +374,8 @@ void cc_worker::decode_pusch(stack_interface_phy_lte::ul_sched_grant_t* grants, // Inform MAC about the CRC result phy->stack->crc_info(tti_rx, rnti, cc_idx, ul_cfg.pusch.grant.tb.tbs / 8, pusch_res.crc); // Push PDU buffer - phy->stack->push_pdu(tti_rx, rnti, cc_idx, ul_cfg.pusch.grant.tb.tbs / 8, pusch_res.crc); + phy->stack->push_pdu( + tti_rx, rnti, cc_idx, ul_cfg.pusch.grant.tb.tbs / 8, pusch_res.crc, ul_cfg.pusch.grant.L_prb); // Logging if (logger.info.enabled()) { char str[512]; @@ -472,7 +472,7 @@ int cc_worker::encode_pdcch_ul(stack_interface_phy_lte::ul_sched_grant_t* grants } if (SRSRAN_RNTI_ISUSER(grants[i].dci.rnti)) { - if (srsran_enb_dl_location_is_common_ncce(&enb_dl, grants[i].dci.location.ncce) && + if (srsran_enb_dl_location_is_common_ncce(&enb_dl, &grants[i].dci.location) && phy->ue_db.is_pcell(grants[i].dci.rnti, cc_idx)) { // Disable extended CSI request and SRS request in common SS srsran_dci_cfg_set_common_ss(&dci_cfg); @@ -488,7 +488,7 @@ int cc_worker::encode_pdcch_ul(stack_interface_phy_lte::ul_sched_grant_t* grants if (logger.info.enabled()) { char str[512]; srsran_dci_ul_info(&grants[i].dci, str, 512); - logger.info("PDCCH: cc=%d, %s, tti_tx_dl=%d", cc_idx, str, tti_tx_dl); + logger.info("PDCCH: cc=%d, rnti=0x%x, %s, tti_tx_dl=%d", cc_idx, grants[i].dci.rnti, str, tti_tx_dl); } } } @@ -507,8 +507,10 @@ int cc_worker::encode_pdcch_dl(stack_interface_phy_lte::dl_sched_grant_t* grants continue; } + // Detect if the DCI location is in common SS, if that is the case, flag it as common SS + // This makes possible UE specific DCI fields to be disabled, so it uses a fallback DCI size if (SRSRAN_RNTI_ISUSER(grants[i].dci.rnti) && grants[i].dci.format == SRSRAN_DCI_FORMAT1A) { - if (srsran_enb_dl_location_is_common_ncce(&enb_dl, grants[i].dci.location.ncce) && + if (srsran_enb_dl_location_is_common_ncce(&enb_dl, &grants[i].dci.location) && phy->ue_db.is_pcell(grants[i].dci.rnti, cc_idx)) { srsran_dci_cfg_set_common_ss(&dci_cfg); } @@ -523,7 +525,7 @@ int cc_worker::encode_pdcch_dl(stack_interface_phy_lte::dl_sched_grant_t* grants // Logging char str[512]; srsran_dci_dl_info(&grants[i].dci, str, 512); - logger.info("PDCCH: cc=%d, %s, tti_tx_dl=%d", cc_idx, str, tti_tx_dl); + logger.info("PDCCH: cc=%d, rnti=0x%x, %s, tti_tx_dl=%d", cc_idx, grants[i].dci.rnti, str, tti_tx_dl); } } } diff --git a/srsenb/src/phy/lte/sf_worker.cc b/srsenb/src/phy/lte/sf_worker.cc index b2efd01be..baf99ae83 100644 --- a/srsenb/src/phy/lte/sf_worker.cc +++ b/srsenb/src/phy/lte/sf_worker.cc @@ -110,17 +110,12 @@ cf_t* sf_worker::get_buffer_rx(uint32_t cc_idx, uint32_t antenna_idx) return cc_workers[cc_idx]->get_buffer_rx(antenna_idx); } -void sf_worker::set_time(uint32_t tti_, uint32_t tx_worker_cnt_, const srsran::rf_timestamp_t& tx_time_) +void sf_worker::set_time(uint32_t tti_, const srsran::rf_timestamp_t& tx_time_) { tti_rx = tti_; tti_tx_dl = TTI_ADD(tti_rx, FDD_HARQ_DELAY_UL_MS); tti_tx_ul = TTI_RX_ACK(tti_rx); - t_tx_dl = TTIMOD(tti_tx_dl); - t_rx = TTIMOD(tti_rx); - t_tx_ul = TTIMOD(tti_tx_ul); - - tx_worker_cnt = tx_worker_cnt_; tx_time.copy(tx_time_); for (auto& w : cc_workers) { @@ -176,9 +171,9 @@ void sf_worker::work_imp() srsran_sf_t sf_type = phy->is_mbsfn_sf(&mbsfn_cfg, tti_tx_dl) ? SRSRAN_SF_MBSFN : SRSRAN_SF_NORM; // Uplink grants to receive this TTI - stack_interface_phy_lte::ul_sched_list_t ul_grants = phy->get_ul_grants(t_rx); + stack_interface_phy_lte::ul_sched_list_t ul_grants = phy->get_ul_grants(tti_rx); // Uplink grants to transmit this tti and receive in the future - stack_interface_phy_lte::ul_sched_list_t ul_grants_tx = phy->get_ul_grants(t_tx_ul); + stack_interface_phy_lte::ul_sched_list_t ul_grants_tx = phy->get_ul_grants(tti_tx_ul); // Downlink grants to transmit this TTI stack_interface_phy_lte::dl_sched_list_t dl_grants(phy->get_nof_carriers_lte()); @@ -193,8 +188,8 @@ void sf_worker::work_imp() ul_sf.tti = tti_rx; // Set UL grant availability prior to any UL processing - if (phy->ue_db.set_ul_grant_available(tti_rx, ul_grants)) { - Error("Error setting UL grants. Some grant's RNTI does not exist."); + if (phy->ue_db.set_ul_grant_available(tti_rx, ul_grants) < SRSRAN_SUCCESS) { + Info("Failed setting UL grants. Some grant's RNTI does not exist."); } // Process UL @@ -244,8 +239,7 @@ void sf_worker::work_imp() } // Save grants - phy->set_ul_grants(t_tx_ul, ul_grants_tx); - phy->set_ul_grants(t_rx, ul_grants); + phy->set_ul_grants(tti_tx_ul, ul_grants_tx); Debug("Sending to radio"); tx_buffer.set_nof_samples(SRSRAN_SF_LEN_PRB(phy->get_nof_prb(0))); diff --git a/srsenb/src/phy/phy.cc b/srsenb/src/phy/phy.cc index c55bdcc85..779d45c0a 100644 --- a/srsenb/src/phy/phy.cc +++ b/srsenb/src/phy/phy.cc @@ -114,9 +114,10 @@ int phy::init(const phy_args_t& args, } // Add PHY lib log. - srslog::basic_levels log_lvl = srslog::str_to_basic_level(args.log.phy_lib_level); + srslog::basic_levels lib_log_lvl = srslog::str_to_basic_level(args.log.phy_lib_level); + srslog::basic_levels log_lvl = srslog::str_to_basic_level(args.log.phy_level); - phy_lib_log.set_level(log_lvl); + phy_lib_log.set_level(lib_log_lvl); phy_lib_log.set_hex_dump_max_size(args.log.phy_hex_limit); if (log_lvl != srslog::basic_levels::none) { srsran_phy_log_register_handler(this, srsran_phy_handler); diff --git a/srsenb/src/phy/phy_ue_db.cc b/srsenb/src/phy/phy_ue_db.cc index 685227951..38cdace8f 100644 --- a/srsenb/src/phy/phy_ue_db.cc +++ b/srsenb/src/phy/phy_ue_db.cc @@ -749,6 +749,7 @@ int phy_ue_db::get_last_ul_tb(uint16_t rnti, uint32_t enb_cc_idx, uint32_t pid, int phy_ue_db::set_ul_grant_available(uint32_t tti, const stack_interface_phy_lte::ul_sched_list_t& ul_sched_list) { + int ret = SRSRAN_SUCCESS; std::lock_guard lock(mutex); // Reset all available grants flags for the given TTI @@ -766,12 +767,13 @@ int phy_ue_db::set_ul_grant_available(uint32_t tti, const stack_interface_phy_lt uint16_t rnti = ul_sched_grant.dci.rnti; // Check that eNb Cell/Carrier is active for the given RNTI if (_assert_active_enb_cc(rnti, enb_cc_idx) != SRSRAN_SUCCESS) { - return SRSRAN_ERROR; + ret = SRSRAN_ERROR; + continue; } // Rise Grant available flag ue_db[rnti].cell_info[_get_ue_cc_idx(rnti, enb_cc_idx)].is_grant_available[tti] = true; } } - return SRSRAN_SUCCESS; + return ret; } diff --git a/srsenb/src/phy/txrx.cc b/srsenb/src/phy/txrx.cc index e452a41f7..792faa659 100644 --- a/srsenb/src/phy/txrx.cc +++ b/srsenb/src/phy/txrx.cc @@ -62,11 +62,8 @@ bool txrx::init(stack_interface_phy_lte* stack_, nr_workers = nr_workers_; worker_com = worker_com_; prach = prach_; - tx_worker_cnt = 0; running = true; - nof_workers = lte_workers->get_nof_workers(); - // Instantiate UL channel emulator if (worker_com->params.ul_channel_args.enable) { ul_channel = srsran::channel_ptr( @@ -176,15 +173,13 @@ void txrx::run_thread() // Compute TX time: Any transmission happens in TTI+4 thus advance 4 ms the reception time timestamp.add(FDD_HARQ_DELAY_UL_MS * 1e-3); - Debug("Setting TTI=%d, tx_mutex=%d, tx_time=%ld:%f to worker %d", + Debug("Setting TTI=%d, tx_time=%ld:%f to worker %d", tti, - tx_worker_cnt, timestamp.get(0).full_secs, timestamp.get(0).frac_secs, lte_worker->get_id()); - lte_worker->set_time(tti, tx_worker_cnt, timestamp); - tx_worker_cnt = (tx_worker_cnt + 1) % nof_workers; + lte_worker->set_time(tti, timestamp); // Trigger prach worker execution for (uint32_t cc = 0; cc < worker_com->get_nof_carriers_lte(); cc++) { diff --git a/srsenb/src/stack/enb_stack_lte.cc b/srsenb/src/stack/enb_stack_lte.cc index 694764921..ebabd6282 100644 --- a/srsenb/src/stack/enb_stack_lte.cc +++ b/srsenb/src/stack/enb_stack_lte.cc @@ -50,7 +50,8 @@ enb_stack_lte::enb_stack_lte(srslog::sink& log_sink) : pending_stack_metrics(64) { get_background_workers().set_nof_workers(2); - enb_task_queue = task_sched.make_task_queue(); + enb_task_queue = task_sched.make_task_queue(); + metrics_task_queue = task_sched.make_task_queue(); // sync_queue is added in init() } @@ -105,7 +106,7 @@ int enb_stack_lte::init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_) // Set up pcap and trace if (args.mac_pcap.enable) { - mac_pcap.open(args.mac_pcap.filename.c_str()); + mac_pcap.open(args.mac_pcap.filename); mac.start_pcap(&mac_pcap); } @@ -166,7 +167,6 @@ void enb_stack_lte::tti_clock() void enb_stack_lte::tti_clock_impl() { - trace_complete_event("enb_stack_lte::tti_clock_impl", "total_time"); task_sched.tic(); rrc.tti_clock(); } @@ -211,7 +211,7 @@ void enb_stack_lte::stop_impl() bool enb_stack_lte::get_metrics(stack_metrics_t* metrics) { // use stack thread to query metrics - auto ret = enb_task_queue.try_push([this]() { + auto ret = metrics_task_queue.try_push([this]() { stack_metrics_t metrics{}; mac.get_metrics(metrics.mac); if (not metrics.mac.ues.empty()) { diff --git a/srsenb/src/stack/mac/mac.cc b/srsenb/src/stack/mac/mac.cc index 23fa04419..f57ebd88d 100644 --- a/srsenb/src/stack/mac/mac.cc +++ b/srsenb/src/stack/mac/mac.cc @@ -41,6 +41,7 @@ mac::mac(srsran::ext_task_sched_handle task_sched_, srslog::basic_logger& logger logger(logger), rar_payload(), common_buffers(SRSRAN_MAX_CARRIERS), task_sched(task_sched_) { pthread_rwlock_init(&rwlock, nullptr); + stack_task_queue = task_sched.make_task_queue(); } mac::~mac() @@ -56,19 +57,13 @@ bool mac::init(const mac_args_t& args_, rrc_interface_mac* rrc) { started = false; - - if (not phy or not rlc) { - return false; - } - phy_h = phy; - rlc_h = rlc; - rrc_h = rrc; + phy_h = phy; + rlc_h = rlc; + rrc_h = rrc; args = args_; cells = cells_; - stack_task_queue = task_sched.make_task_queue(); - scheduler.init(rrc, args.sched); // Init softbuffer for SI messages @@ -84,8 +79,6 @@ bool mac::init(const mac_args_t& args_, srsran_softbuffer_tx_init(&cc.rar_softbuffer_tx, args.nof_prb); } - reset(); - // Initiate common pool of softbuffers uint32_t nof_prb = args.nof_prb; auto init_softbuffers = [nof_prb](void* ptr) { @@ -95,9 +88,6 @@ bool mac::init(const mac_args_t& args_, softbuffer_pool.reset(new srsran::background_obj_pool( 8, 8, args.nof_prealloc_ues, init_softbuffers, recycle_softbuffers)); - // Pre-alloc UE objects for first attaching users - prealloc_ue(10); - detected_rachs.resize(cells.size()); started = true; @@ -118,23 +108,12 @@ void mac::stop() srsran_softbuffer_tx_free(&cc.pcch_softbuffer_tx); srsran_softbuffer_tx_free(&cc.rar_softbuffer_tx); } - ue_pool.stop(); } } -// Implement Section 5.9 -void mac::reset() -{ - logger.info("Resetting MAC"); - - last_rnti = 70; - - /* Setup scheduler */ - scheduler.reset(); -} - void mac::start_pcap(srsran::mac_pcap* pcap_) { + srsran::rwlock_read_guard lock(rwlock); pcap = pcap_; // Set pcap in all UEs for UL messages for (auto& u : ue_db) { @@ -144,6 +123,7 @@ void mac::start_pcap(srsran::mac_pcap* pcap_) void mac::start_pcap_net(srsran::mac_pcap_net* pcap_net_) { + srsran::rwlock_read_guard lock(rwlock); pcap_net = pcap_net_; // Set pcap in all UEs for UL messages for (auto& u : ue_db) { @@ -156,11 +136,12 @@ void mac::start_pcap_net(srsran::mac_pcap_net* pcap_net_) * RLC interface * *******************************************************/ + int mac::rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue) { srsran::rwlock_read_guard lock(rwlock); int ret = -1; - if (ue_db.contains(rnti)) { + if (check_ue_active(rnti)) { if (rnti != SRSRAN_MRNTI) { ret = scheduler.dl_rlc_buffer_state(rnti, lc_id, tx_queue, retx_queue); } else { @@ -171,34 +152,20 @@ int mac::rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint } ret = 0; } - } else { - logger.error("User rnti=0x%x not found", rnti); } return ret; } int mac::bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, sched_interface::ue_bearer_cfg_t* cfg) { - int ret = -1; srsran::rwlock_read_guard lock(rwlock); - if (ue_db.contains(rnti)) { - ret = scheduler.bearer_ue_cfg(rnti, lc_id, *cfg); - } else { - logger.error("User rnti=0x%x not found", rnti); - } - return ret; + return check_ue_active(rnti) ? scheduler.bearer_ue_cfg(rnti, lc_id, *cfg) : -1; } int mac::bearer_ue_rem(uint16_t rnti, uint32_t lc_id) { srsran::rwlock_read_guard lock(rwlock); - int ret = -1; - if (ue_db.contains(rnti)) { - ret = scheduler.bearer_ue_rem(rnti, lc_id); - } else { - logger.error("User rnti=0x%x not found", rnti); - } - return ret; + return check_ue_active(rnti) ? scheduler.bearer_ue_rem(rnti, lc_id) : -1; } void mac::phy_config_enabled(uint16_t rnti, bool enabled) @@ -210,14 +177,10 @@ void mac::phy_config_enabled(uint16_t rnti, bool enabled) int mac::ue_cfg(uint16_t rnti, sched_interface::ue_cfg_t* cfg) { srsran::rwlock_read_guard lock(rwlock); - - auto it = ue_db.find(rnti); - ue* ue_ptr = nullptr; - if (it == ue_db.end()) { - logger.error("User rnti=0x%x not found", rnti); + if (not check_ue_active(rnti)) { return SRSRAN_ERROR; } - ue_ptr = it->second.get(); + ue* ue_ptr = ue_db[rnti].get(); // Start TA FSM in UE entity ue_ptr->start_ta(); @@ -235,10 +198,9 @@ int mac::ue_rem(uint16_t rnti) { // Remove UE from the perspective of L2/L3 { - srsran::rwlock_write_guard lock(rwlock); - if (ue_db.contains(rnti)) { - ues_to_rem[rnti] = std::move(ue_db[rnti]); - ue_db.erase(rnti); + srsran::rwlock_read_guard lock(rwlock); + if (check_ue_active(rnti)) { + ue_db[rnti]->set_active(false); } else { logger.error("User rnti=0x%x not found", rnti); return SRSRAN_ERROR; @@ -250,7 +212,8 @@ int mac::ue_rem(uint16_t rnti) // Note: Let any pending retx ACK to arrive, so that PHY recognizes rnti task_sched.defer_callback(FDD_HARQ_DELAY_DL_MS + FDD_HARQ_DELAY_UL_MS, [this, rnti]() { phy_h->rem_rnti(rnti); - ues_to_rem.erase(rnti); + srsran::rwlock_write_guard lock(rwlock); + ue_db.erase(rnti); logger.info("User rnti=0x%x removed from MAC/PHY", rnti); }); return SRSRAN_SUCCESS; @@ -267,11 +230,7 @@ int mac::ue_set_crnti(uint16_t temp_crnti, uint16_t crnti, sched_interface::ue_c // Schedule ConRes Msg4 scheduler.dl_mac_buffer_state(crnti, (uint32_t)srsran::dl_sch_lcid::CON_RES_ID); } - int ret = ue_cfg(crnti, cfg); - if (ret != SRSRAN_SUCCESS) { - return ret; - } - return ret; + return ue_cfg(crnti, cfg); } int mac::cell_cfg(const std::vector& cell_cfg_) @@ -301,6 +260,7 @@ void mac::toggle_padding() void mac::add_padding() { + srsran::rwlock_read_guard lock(rwlock); for (auto it = ue_db.begin(); it != ue_db.end(); ++it) { uint16_t cur_rnti = it->first; auto ue = it; @@ -320,7 +280,7 @@ int mac::ack_info(uint32_t tti_rx, uint16_t rnti, uint32_t enb_cc_idx, uint32_t logger.set_context(tti_rx); srsran::rwlock_read_guard lock(rwlock); - if (not check_ue_exists(rnti)) { + if (not check_ue_active(rnti)) { return SRSRAN_ERROR; } @@ -337,7 +297,7 @@ int mac::crc_info(uint32_t tti_rx, uint16_t rnti, uint32_t enb_cc_idx, uint32_t logger.set_context(tti_rx); srsran::rwlock_read_guard lock(rwlock); - if (not check_ue_exists(rnti)) { + if (not check_ue_active(rnti)) { return SRSRAN_ERROR; } @@ -350,11 +310,16 @@ int mac::crc_info(uint32_t tti_rx, uint16_t rnti, uint32_t enb_cc_idx, uint32_t return scheduler.ul_crc_info(tti_rx, rnti, enb_cc_idx, crc); } -int mac::push_pdu(uint32_t tti_rx, uint16_t rnti, uint32_t enb_cc_idx, uint32_t nof_bytes, bool crc) +int mac::push_pdu(uint32_t tti_rx, + uint16_t rnti, + uint32_t enb_cc_idx, + uint32_t nof_bytes, + bool crc, + uint32_t ul_nof_prbs) { srsran::rwlock_read_guard lock(rwlock); - if (not check_ue_exists(rnti)) { + if (not check_ue_active(rnti)) { return SRSRAN_ERROR; } @@ -365,14 +330,32 @@ int mac::push_pdu(uint32_t tti_rx, uint16_t rnti, uint32_t enb_cc_idx, uint32_t } uint32_t ue_cc_idx = enb_ue_cc_map[enb_cc_idx]; + srsran::unique_byte_buffer_t pdu = ue_db[rnti]->release_pdu(tti_rx, ue_cc_idx); + if (pdu == nullptr) { + logger.warning("Could not find MAC UL PDU for rnti=0x%x, cc=%d, tti=%d", rnti, enb_cc_idx, tti_rx); + return SRSRAN_ERROR; + } + // push the pdu through the queue if received correctly if (crc) { logger.info("Pushing PDU rnti=0x%x, tti_rx=%d, nof_bytes=%d", rnti, tti_rx, nof_bytes); - ue_db[rnti]->push_pdu(tti_rx, ue_cc_idx, nof_bytes); - stack_task_queue.push([this]() { process_pdus(); }); + srsran_expect(nof_bytes == pdu->size(), + "Inconsistent PDU length for rnti=0x%x, tti_rx=%d (%d!=%d)", + rnti, + tti_rx, + nof_bytes, + (int)pdu->size()); + auto process_pdu_task = [this, rnti, ul_nof_prbs](srsran::unique_byte_buffer_t& pdu) { + srsran::rwlock_read_guard lock(rwlock); + if (check_ue_active(rnti)) { + ue_db[rnti]->process_pdu(std::move(pdu), ul_nof_prbs); + } else { + logger.debug("Discarding PDU rnti=0x%x", rnti); + } + }; + auto ret = stack_task_queue.try_push(std::bind(process_pdu_task, std::move(pdu))); } else { - logger.debug("Discarting PDU rnti=0x%x, tti_rx=%d, nof_bytes=%d", rnti, tti_rx, nof_bytes); - ue_db[rnti]->deallocate_pdu(tti_rx, ue_cc_idx); + logger.debug("Discarding PDU rnti=0x%x, tti_rx=%d, nof_bytes=%d", rnti, tti_rx, nof_bytes); } return SRSRAN_SUCCESS; } @@ -382,7 +365,7 @@ int mac::ri_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t ri_v logger.set_context(tti); srsran::rwlock_read_guard lock(rwlock); - if (not check_ue_exists(rnti)) { + if (not check_ue_active(rnti)) { return SRSRAN_ERROR; } @@ -397,7 +380,7 @@ int mac::pmi_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t pmi logger.set_context(tti); srsran::rwlock_read_guard lock(rwlock); - if (not check_ue_exists(rnti)) { + if (not check_ue_active(rnti)) { return SRSRAN_ERROR; } @@ -412,7 +395,7 @@ int mac::cqi_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t cqi logger.set_context(tti); srsran::rwlock_read_guard lock(rwlock); - if (not check_ue_exists(rnti)) { + if (not check_ue_active(rnti)) { return SRSRAN_ERROR; } @@ -427,10 +410,12 @@ int mac::snr_info(uint32_t tti_rx, uint16_t rnti, uint32_t enb_cc_idx, float snr logger.set_context(tti_rx); srsran::rwlock_read_guard lock(rwlock); - if (not check_ue_exists(rnti)) { + if (not check_ue_active(rnti)) { return SRSRAN_ERROR; } + rrc_h->set_radiolink_ul_state(rnti, snr > 0); + return scheduler.ul_snr_info(tti_rx, rnti, enb_cc_idx, snr, (uint32_t)ch); } @@ -438,13 +423,13 @@ int mac::ta_info(uint32_t tti, uint16_t rnti, float ta_us) { srsran::rwlock_read_guard lock(rwlock); - if (not check_ue_exists(rnti)) { + if (not check_ue_active(rnti)) { return SRSRAN_ERROR; } uint32_t nof_ta_count = ue_db[rnti]->set_ta_us(ta_us); - if (nof_ta_count) { - scheduler.dl_mac_buffer_state(rnti, (uint32_t)srsran::dl_sch_lcid::TA_CMD, nof_ta_count); + if (nof_ta_count > 0) { + return scheduler.dl_mac_buffer_state(rnti, (uint32_t)srsran::dl_sch_lcid::TA_CMD, nof_ta_count); } return SRSRAN_SUCCESS; } @@ -454,65 +439,64 @@ int mac::sr_detected(uint32_t tti, uint16_t rnti) logger.set_context(tti); srsran::rwlock_read_guard lock(rwlock); - if (not check_ue_exists(rnti)) { + if (not check_ue_active(rnti)) { return SRSRAN_ERROR; } return scheduler.ul_sr_info(tti, rnti); } -uint16_t mac::allocate_rnti() +bool mac::is_valid_rnti_unprotected(uint16_t rnti) { - std::lock_guard lock(rnti_mutex); - - // Assign a c-rnti - uint16_t rnti = last_rnti++; - if (last_rnti >= 60000) { - last_rnti = 70; + if (not started) { + logger.info("RACH ignored as eNB is being shutdown"); + return false; } - - return rnti; + if (ue_db.full()) { + logger.warning("Maximum number of connected UEs %zd connected to the eNB. Ignoring PRACH", SRSENB_MAX_UES); + return false; + } + if (not ue_db.has_space(rnti)) { + logger.info("Failed to allocate rnti=0x%x. Attempting a different rnti.", rnti); + return false; + } + return true; } uint16_t mac::allocate_ue() { - ue* inserted_ue = nullptr; + ue* inserted_ue = nullptr; + uint16_t rnti = SRSRAN_INVALID_RNTI; + do { - // Get pre-allocated UE object - std::unique_ptr ue_ptr; - if (not ue_pool.try_pop(ue_ptr)) { - logger.error("UE pool empty. Ignoring RACH attempt."); - return SRSRAN_INVALID_RNTI; - } - uint16_t rnti = ue_ptr->get_rnti(); + // Assign new RNTI + rnti = FIRST_RNTI + (ue_counter.fetch_add(1, std::memory_order_relaxed) % 60000); - // Add UE to map + // Pre-check if rnti is valid { - srsran::rwlock_write_guard lock(rwlock); - if (not started) { - logger.info("RACH ignored as eNB is being shutdown"); - return SRSRAN_INVALID_RNTI; - } - if (ue_db.size() >= SRSENB_MAX_UES) { - logger.warning("Maximum number of connected UEs %zd connected to the eNB. Ignoring PRACH", SRSENB_MAX_UES); - return SRSRAN_INVALID_RNTI; - } - auto ret = ue_db.insert(rnti, std::move(ue_ptr)); - if (ret) { - inserted_ue = ret.value()->second.get(); - } else { - logger.info("Failed to allocate rnti=0x%x. Attempting a different rnti.", rnti); + srsran::rwlock_read_guard read_lock(rwlock); + if (not is_valid_rnti_unprotected(rnti)) { + continue; } } - // Allocate one new UE object in advance - srsran::get_background_workers().push_task([this]() { prealloc_ue(1); }); + // Allocate and initialize UE object + unique_rnti_ptr ue_ptr = make_rnti_obj( + rnti, rnti, args.nof_prb, &scheduler, rrc_h, rlc_h, phy_h, logger, cells.size(), softbuffer_pool.get()); + // Add UE to rnti map + srsran::rwlock_write_guard rw_lock(rwlock); + if (not is_valid_rnti_unprotected(rnti)) { + continue; + } + auto ret = ue_db.insert(rnti, std::move(ue_ptr)); + if (ret.has_value()) { + inserted_ue = ret.value()->second.get(); + } else { + logger.info("Failed to allocate rnti=0x%x. Attempting a different rnti.", rnti); + } } while (inserted_ue == nullptr); - // RNTI allocation was successful - uint16_t rnti = inserted_ue->get_rnti(); - // Set PCAP if available if (pcap != nullptr) { inserted_ue->start_pcap(pcap); @@ -547,12 +531,12 @@ void mac::rach_detected(uint32_t tti, uint32_t enb_cc_idx, uint32_t preamble_idx logger.set_context(tti); auto rach_tprof_meas = rach_tprof.start(); - uint16_t rnti = allocate_ue(); - if (rnti == SRSRAN_INVALID_RNTI) { - return; - } + stack_task_queue.push([this, tti, enb_cc_idx, preamble_idx, time_adv, rach_tprof_meas]() mutable { + uint16_t rnti = allocate_ue(); + if (rnti == SRSRAN_INVALID_RNTI) { + return; + } - stack_task_queue.push([this, rnti, tti, enb_cc_idx, preamble_idx, time_adv, rach_tprof_meas]() mutable { rach_tprof_meas.defer_stop(); // Generate RAR data sched_interface::dl_sched_rar_info_t rar_info = {}; @@ -598,25 +582,13 @@ 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(), softbuffer_pool.get())); - if (not ue_pool.try_push(std::move(ptr))) { - logger.info("Cannot preallocate more UEs as pool is full"); - return; - } - } -} - int mac::get_dl_sched(uint32_t tti_tx_dl, dl_sched_list_t& dl_sched_res_list) { if (!started) { return 0; } - trace_complete_event("mac::get_dl_sched", "total_time"); + trace_threshold_complete_event("mac::get_dl_sched", "total_time", std::chrono::microseconds(100)); logger.set_context(TTI_SUB(tti_tx_dl, FDD_HARQ_DELAY_UL_MS)); if (do_padding) { add_padding(); @@ -819,7 +791,8 @@ void mac::build_mch_sched(uint32_t tbs) int mac::get_mch_sched(uint32_t tti, bool is_mcch, dl_sched_list_t& dl_sched_res_list) { - dl_sched_t* dl_sched_res = &dl_sched_res_list[0]; + srsran::rwlock_read_guard lock(rwlock); + dl_sched_t* dl_sched_res = &dl_sched_res_list[0]; logger.set_context(tti); srsran_ra_tb_t mcs = {}; srsran_ra_tb_t mcs_data = {}; @@ -932,6 +905,8 @@ int mac::get_ul_sched(uint32_t tti_tx_ul, ul_sched_list_t& ul_sched_res_list) logger.set_context(TTI_SUB(tti_tx_ul, FDD_HARQ_DELAY_UL_MS + FDD_HARQ_DELAY_DL_MS)); + srsran::rwlock_read_guard lock(rwlock); + // Execute UE FSMs (e.g. TA) for (auto& ue : ue_db) { ue.second->tic(); @@ -947,52 +922,46 @@ int mac::get_ul_sched(uint32_t tti_tx_ul, ul_sched_list_t& ul_sched_res_list) return SRSRAN_ERROR; } - { - srsran::rwlock_read_guard lock(rwlock); + // Copy DCI grants + phy_ul_sched_res->nof_grants = 0; + int n = 0; + for (uint32_t i = 0; i < sched_result.pusch.size(); i++) { + if (sched_result.pusch[i].tbs > 0) { + // Get UE + uint16_t rnti = sched_result.pusch[i].dci.rnti; - // Copy DCI grants - phy_ul_sched_res->nof_grants = 0; - int n = 0; - for (uint32_t i = 0; i < sched_result.pusch.size(); i++) { - if (sched_result.pusch[i].tbs > 0) { - // Get UE - uint16_t rnti = sched_result.pusch[i].dci.rnti; - - if (ue_db.contains(rnti)) { - // Copy grant info - phy_ul_sched_res->pusch[n].current_tx_nb = sched_result.pusch[i].current_tx_nb; - phy_ul_sched_res->pusch[n].pid = TTI_RX(tti_tx_ul) % SRSRAN_FDD_NOF_HARQ; - phy_ul_sched_res->pusch[n].needs_pdcch = sched_result.pusch[i].needs_pdcch; - phy_ul_sched_res->pusch[n].dci = sched_result.pusch[i].dci; - phy_ul_sched_res->pusch[n].softbuffer_rx = - ue_db[rnti]->get_rx_softbuffer(sched_result.pusch[i].dci.ue_cc_idx, tti_tx_ul); - - // If the Rx soft-buffer is not given, abort reception - if (phy_ul_sched_res->pusch[n].softbuffer_rx == nullptr) { - continue; - } + if (ue_db.contains(rnti)) { + // Copy grant info + phy_ul_sched_res->pusch[n].current_tx_nb = sched_result.pusch[i].current_tx_nb; + phy_ul_sched_res->pusch[n].pid = TTI_RX(tti_tx_ul) % SRSRAN_FDD_NOF_HARQ; + phy_ul_sched_res->pusch[n].needs_pdcch = sched_result.pusch[i].needs_pdcch; + phy_ul_sched_res->pusch[n].dci = sched_result.pusch[i].dci; + phy_ul_sched_res->pusch[n].softbuffer_rx = + ue_db[rnti]->get_rx_softbuffer(sched_result.pusch[i].dci.ue_cc_idx, tti_tx_ul); + + // If the Rx soft-buffer is not given, abort reception + if (phy_ul_sched_res->pusch[n].softbuffer_rx == nullptr) { + continue; + } - if (sched_result.pusch[n].current_tx_nb == 0) { - srsran_softbuffer_rx_reset_tbs(phy_ul_sched_res->pusch[n].softbuffer_rx, sched_result.pusch[i].tbs * 8); - } - phy_ul_sched_res->pusch[n].data = - ue_db[rnti]->request_buffer(tti_tx_ul, sched_result.pusch[i].dci.ue_cc_idx, sched_result.pusch[i].tbs); - if (phy_ul_sched_res->pusch[n].data) { - phy_ul_sched_res->nof_grants++; - } else { - logger.error("Grant for rnti=0x%x could not be allocated due to lack of buffers", rnti); - } - n++; + if (sched_result.pusch[n].current_tx_nb == 0) { + srsran_softbuffer_rx_reset_tbs(phy_ul_sched_res->pusch[n].softbuffer_rx, sched_result.pusch[i].tbs * 8); + } + phy_ul_sched_res->pusch[n].data = + ue_db[rnti]->request_buffer(tti_tx_ul, sched_result.pusch[i].dci.ue_cc_idx, sched_result.pusch[i].tbs); + if (phy_ul_sched_res->pusch[n].data) { + phy_ul_sched_res->nof_grants++; } else { - logger.warning("Invalid UL scheduling result. User 0x%x does not exist", rnti); + logger.error("Grant for rnti=0x%x could not be allocated due to lack of buffers", rnti); } - + n++; } else { - logger.warning("Grant %d for rnti=0x%x has zero TBS", i, sched_result.pusch[i].dci.rnti); + logger.warning("Invalid UL scheduling result. User 0x%x does not exist", rnti); } - } - // No more uses of ue_db beyond here + } else { + logger.warning("Grant %d for rnti=0x%x has zero TBS", i, sched_result.pusch[i].dci.rnti); + } } // Copy PHICH actions @@ -1009,22 +978,13 @@ int mac::get_ul_sched(uint32_t tti_tx_ul, ul_sched_list_t& ul_sched_res_list) return SRSRAN_SUCCESS; } -bool mac::process_pdus() -{ - srsran::rwlock_read_guard lock(rwlock); - bool ret = false; - for (auto& u : ue_db) { - ret |= u.second->process_pdus(); - } - return ret; -} - void mac::write_mcch(const srsran::sib2_mbms_t* sib2_, const srsran::sib13_t* sib13_, const srsran::mcch_msg_t* mcch_, const uint8_t* mcch_payload, const uint8_t mcch_payload_length) { + srsran::rwlock_write_guard lock(rwlock); mcch = *mcch_; mch.num_mtch_sched = this->mcch.pmch_info_list[0].nof_mbms_session_info; for (uint32_t i = 0; i < mch.num_mtch_sched; ++i) { @@ -1040,15 +1000,14 @@ void mac::write_mcch(const srsran::sib2_mbms_t* sib2_, rrc_h->add_user(SRSRAN_MRNTI, {}); } -bool mac::check_ue_exists(uint16_t rnti) +// Internal helper function, caller must hold UE DB rwlock +bool mac::check_ue_active(uint16_t rnti) { if (not ue_db.contains(rnti)) { - if (not ues_to_rem.count(rnti)) { - logger.error("User rnti=0x%x not found", rnti); - } + logger.error("User rnti=0x%x not found", rnti); return false; } - return true; + return ue_db[rnti]->is_active(); } } // namespace srsenb diff --git a/srsenb/src/stack/mac/sched.cc b/srsenb/src/stack/mac/sched.cc index 70d45715f..572e73640 100644 --- a/srsenb/src/stack/mac/sched.cc +++ b/srsenb/src/stack/mac/sched.cc @@ -240,10 +240,10 @@ int sched::ul_buffer_add(uint16_t rnti, uint32_t lcid, uint32_t bytes) return ue_db_access_locked(rnti, [lcid, bytes](sched_ue& ue) { ue.ul_buffer_add(lcid, bytes); }); } -int sched::ul_phr(uint16_t rnti, int phr) +int sched::ul_phr(uint16_t rnti, int phr, uint32_t ul_nof_prb) { return ue_db_access_locked( - rnti, [phr](sched_ue& ue) { ue.ul_phr(phr); }, __PRETTY_FUNCTION__); + rnti, [phr, ul_nof_prb](sched_ue& ue) { ue.ul_phr(phr, ul_nof_prb); }, __PRETTY_FUNCTION__); } int sched::ul_sr_info(uint32_t tti, uint16_t rnti) diff --git a/srsenb/src/stack/mac/sched_grid.cc b/srsenb/src/stack/mac/sched_grid.cc index 57ab4e143..8ecfdc491 100644 --- a/srsenb/src/stack/mac/sched_grid.cc +++ b/srsenb/src/stack/mac/sched_grid.cc @@ -632,12 +632,6 @@ alloc_result sf_sched::alloc_phich(sched_ue* user) { using phich_t = sched_interface::ul_sched_phich_t; - auto* ul_sf_result = &cc_results->get_cc(cc_cfg->enb_cc_idx)->ul_sched_result; - if (ul_sf_result->phich.full()) { - logger.warning("SCHED: Maximum number of PHICH allocations has been reached"); - return alloc_result::no_grant_space; - } - auto p = user->get_active_cell_index(cc_cfg->enb_cc_idx); if (not p.first) { // user does not support this carrier @@ -645,15 +639,35 @@ alloc_result sf_sched::alloc_phich(sched_ue* user) } ul_harq_proc* h = user->get_ul_harq(get_tti_tx_ul(), cc_cfg->enb_cc_idx); + if (not h->has_pending_phich()) { + // No PHICH pending + return alloc_result::no_rnti_opportunity; + } - /* Indicate PHICH acknowledgment if needed */ - if (h->has_pending_phich()) { - ul_sf_result->phich.emplace_back(); - ul_sf_result->phich.back().rnti = user->get_rnti(); - ul_sf_result->phich.back().phich = h->pop_pending_phich() ? phich_t::ACK : phich_t::NACK; - return alloc_result::success; + auto* ul_sf_result = &cc_results->get_cc(cc_cfg->enb_cc_idx)->ul_sched_result; + if (ul_sf_result->phich.full()) { + logger.warning( + "SCHED: UL skipped retx rnti=0x%x, pid=%d. Cause: No PHICH space left", user->get_rnti(), h->get_id()); + h->pop_pending_phich(); + return alloc_result::no_grant_space; + } + + if (not user->phich_enabled(get_tti_rx(), cc_cfg->enb_cc_idx)) { + // PHICH falls in measGap. PHICH hi=1 is assumed by UE. In case of NACK, the HARQ is going to be resumed later on. + logger.info( + "SCHED: UL skipped retx rnti=0x%x, pid=%d. Cause: PHICH-measGap collision", user->get_rnti(), h->get_id()); + h->pop_pending_phich(); // empty pending PHICH + // Note: Given that the UE assumes PHICH hi=1, it is not expecting PUSCH grants for tti_tx_ul. Requesting PDCCH + // for the UL Harq has the effect of forbidding PUSCH grants, since phich_tti == pdcch_tti. + h->request_pdcch(); + return alloc_result::no_cch_space; } - return alloc_result::no_rnti_opportunity; + + /* Indicate PHICH acknowledgment if needed */ + ul_sf_result->phich.emplace_back(); + ul_sf_result->phich.back().rnti = user->get_rnti(); + ul_sf_result->phich.back().phich = h->pop_pending_phich() ? phich_t::ACK : phich_t::NACK; + return alloc_result::success; } void sf_sched::set_dl_data_sched_result(const sf_cch_allocator::alloc_result_t& dci_result, @@ -697,8 +711,8 @@ void sf_sched::set_dl_data_sched_result(const sf_cch_allocator::alloc_result_t& // Print Resulting DL Allocation fmt::memory_buffer str_buffer; fmt::format_to(str_buffer, - "SCHED: DL {} rnti=0x{:x}, cc={}, pid={}, mask=0x{:x}, dci=({}, {}), n_rtx={}, tbs={}, " - "buffer={}/{}, tti_tx_dl={}", + "SCHED: DL {} rnti=0x{:x}, cc={}, pid={}, mask=0x{:x}, dci=({}, {}), n_rtx={}, cfi={}, " + "tbs={}, buffer={}/{}, tti_tx_dl={}", is_newtx ? "tx" : "retx", user->get_rnti(), cc_cfg->enb_cc_idx, @@ -707,6 +721,7 @@ void sf_sched::set_dl_data_sched_result(const sf_cch_allocator::alloc_result_t& data->dci.location.L, data->dci.location.ncce, dl_harq.nof_retx(0) + dl_harq.nof_retx(1), + tti_alloc.get_cfi(), tbs, data_before, user->get_pending_dl_bytes(cc_cfg->enb_cc_idx), @@ -858,21 +873,23 @@ void sf_sched::set_ul_sched_result(const sf_cch_allocator::alloc_result_t& dci_r uint32_t old_pending_bytes = user->get_pending_ul_old_data(); if (logger.info.enabled()) { fmt::memory_buffer str_buffer; - fmt::format_to(str_buffer, - "SCHED: {} {} rnti=0x{:x}, cc={}, pid={}, dci=({},{}), prb={}, n_rtx={}, tbs={}, bsr={} ({}-{})", - ul_alloc.is_msg3 ? "Msg3" : "UL", - ul_alloc.is_retx() ? "retx" : "tx", - user->get_rnti(), - cc_cfg->enb_cc_idx, - h->get_id(), - pusch.dci.location.L, - pusch.dci.location.ncce, - ul_alloc.alloc, - h->nof_retx(0), - tbs, - new_pending_bytes, - total_data_before, - old_pending_bytes); + fmt::format_to( + str_buffer, + "SCHED: {} {} rnti=0x{:x}, cc={}, pid={}, dci=({},{}), prb={}, n_rtx={}, cfi={}, tbs={}, bsr={} ({}-{})", + ul_alloc.is_msg3 ? "Msg3" : "UL", + ul_alloc.is_retx() ? "retx" : "tx", + user->get_rnti(), + cc_cfg->enb_cc_idx, + h->get_id(), + pusch.dci.location.L, + pusch.dci.location.ncce, + ul_alloc.alloc, + h->nof_retx(0), + tti_alloc.get_cfi(), + tbs, + new_pending_bytes, + total_data_before, + old_pending_bytes); logger.info("%s", srsran::to_c_str(str_buffer)); } @@ -901,15 +918,23 @@ void sf_sched::generate_sched_results(sched_ue_list& ue_db) /* Resume UL HARQs with pending retxs that did not get allocated */ using phich_t = sched_interface::ul_sched_phich_t; auto& phich_list = cc_result->ul_sched_result.phich; - for (uint32_t i = 0; i < cc_result->ul_sched_result.phich.size(); ++i) { - auto& phich = phich_list[i]; - if (phich.phich == phich_t::NACK) { - auto& ue = *ue_db[phich.rnti]; - ul_harq_proc* h = ue.get_ul_harq(get_tti_tx_ul(), cc_cfg->enb_cc_idx); - if (not is_ul_alloc(ue.get_rnti()) and h != nullptr and not h->is_empty()) { - // There was a missed UL harq retx. Halt+Resume the HARQ - phich.phich = phich_t::ACK; - logger.debug("SCHED: rnti=0x%x UL harq pid=%d is being resumed", ue.get_rnti(), h->get_id()); + for (auto& ue_pair : ue_db) { + auto& ue = *ue_pair.second; + uint16_t rnti = ue.get_rnti(); + ul_harq_proc* h = ue.get_ul_harq(get_tti_tx_ul(), cc_cfg->enb_cc_idx); + if (h != nullptr and not h->is_empty() and not is_ul_alloc(rnti)) { + // There was a missed UL harq retx. Halt+Resume the HARQ + h->retx_skipped(); + auto same_rnti = [rnti](const phich_t& p) { return p.rnti == rnti; }; + phich_t* phich = std::find_if(phich_list.begin(), phich_list.end(), same_rnti); + if (phich != phich_list.end()) { + srsran_assert(phich->phich == phich_t::NACK, "Expected hi=0 in case of active UL HARQ that was not retx"); + logger.info("SCHED: UL skipped retx rnti=0x%x, pid=%d. Cause: %s", + ue.get_rnti(), + h->get_id(), + ue.pusch_enabled(get_tti_rx(), cc_cfg->enb_cc_idx, false) ? "lack of PHY resources" + : "PUSCH-measGap collision"); + phich->phich = phich_t::ACK; } } } diff --git a/srsenb/src/stack/mac/sched_helpers.cc b/srsenb/src/stack/mac/sched_helpers.cc index be80149bf..5b2625ed9 100644 --- a/srsenb/src/stack/mac/sched_helpers.cc +++ b/srsenb/src/stack/mac/sched_helpers.cc @@ -391,24 +391,28 @@ void generate_cce_location(srsran_regs_t* regs_, * DCI-specific helper functions *******************************************************/ -uint32_t -get_aggr_level(uint32_t nof_bits, uint32_t dl_cqi, uint32_t max_aggr_lvl, uint32_t cell_nof_prb, bool use_tbs_index_alt) +uint32_t get_aggr_level(uint32_t nof_bits, + uint32_t dl_cqi, + uint32_t min_aggr_lvl, + uint32_t max_aggr_lvl, + uint32_t cell_nof_prb, + bool use_tbs_index_alt) { - uint32_t l = 0; float max_coderate = srsran_cqi_to_coderate(dl_cqi, use_tbs_index_alt); - float coderate; - float factor = 1.5; - uint32_t l_max = 3; + float factor = 1.5; + uint32_t l_max = 3; if (cell_nof_prb == 6) { factor = 1.0; l_max = 2; } - l_max = SRSRAN_MIN(max_aggr_lvl, l_max); + l_max = std::min(max_aggr_lvl, l_max); - do { - coderate = srsran_pdcch_coderate(nof_bits, l); + uint32_t l = std::min(min_aggr_lvl, l_max); + float coderate = srsran_pdcch_coderate(nof_bits, l); + while (factor * coderate > max_coderate and l < l_max) { l++; - } while (l < l_max && factor * coderate > max_coderate); + coderate = srsran_pdcch_coderate(nof_bits, l); + } Debug("SCHED: CQI=%d, l=%d, nof_bits=%d, coderate=%.2f, max_coderate=%.2f", dl_cqi, diff --git a/srsenb/src/stack/mac/sched_phy_ch/sched_dci.cc b/srsenb/src/stack/mac/sched_phy_ch/sched_dci.cc index 908b4f149..042df23da 100644 --- a/srsenb/src/stack/mac/sched_phy_ch/sched_dci.cc +++ b/srsenb/src/stack/mac/sched_phy_ch/sched_dci.cc @@ -86,16 +86,26 @@ tbs_info compute_mcs_and_tbs(uint32_t nof_prb, bool is_ul, bool ulqam64_enabled, bool use_tbs_index_alt) +{ + float max_coderate = srsran_cqi_to_coderate(std::min(cqi + 1U, 15U), use_tbs_index_alt); + return compute_mcs_and_tbs(nof_prb, nof_re, max_coderate, max_mcs, is_ul, ulqam64_enabled, use_tbs_index_alt); +} + +tbs_info compute_mcs_and_tbs(uint32_t nof_prb, + uint32_t nof_re, + float max_coderate, + uint32_t max_mcs, + bool is_ul, + bool ulqam64_enabled, + bool use_tbs_index_alt) { assert((not is_ul or not use_tbs_index_alt) && "UL cannot use Alt CQI Table"); assert((is_ul or not ulqam64_enabled) && "DL cannot use UL-QAM64 enable flag"); - float max_coderate = srsran_cqi_to_coderate(std::min(cqi + 1U, 15U), use_tbs_index_alt); - uint32_t max_Qm = (is_ul) ? (ulqam64_enabled ? 6 : 4) : (use_tbs_index_alt ? 8 : 6); - max_coderate = std::min(max_coderate, 0.932F * max_Qm); + uint32_t max_Qm = (is_ul) ? (ulqam64_enabled ? 6 : 4) : (use_tbs_index_alt ? 8 : 6); + max_coderate = std::min(max_coderate, 0.930F * max_Qm); - int mcs = 0; - float prev_max_coderate = 0; + int mcs = 0; do { // update max TBS based on max coderate int max_tbs = coderate_to_tbs(max_coderate, nof_re); @@ -122,7 +132,7 @@ tbs_info compute_mcs_and_tbs(uint32_t nof_prb, // update max coderate based on mcs srsran_mod_t mod = (is_ul) ? srsran_ra_ul_mod_from_mcs(mcs) : srsran_ra_dl_mod_from_mcs(mcs, use_tbs_index_alt); uint32_t Qm = srsran_mod_bits_x_symbol(mod); - max_coderate = std::min(0.932F * Qm, max_coderate); + max_coderate = std::min(0.930F * Qm, max_coderate); if (coderate <= max_coderate) { // solution was found @@ -134,7 +144,7 @@ tbs_info compute_mcs_and_tbs(uint32_t nof_prb, // start with smaller max mcs in next iteration max_mcs = mcs - 1; - } while (mcs > 0 and max_coderate != prev_max_coderate); + } while (mcs > 0); return tbs_info{}; } diff --git a/srsenb/src/stack/mac/sched_ue.cc b/srsenb/src/stack/mac/sched_ue.cc index 4701309d4..fb75d6e00 100644 --- a/srsenb/src/stack/mac/sched_ue.cc +++ b/srsenb/src/stack/mac/sched_ue.cc @@ -102,15 +102,9 @@ void sched_ue::new_subframe(tti_point tti_rx, uint32_t enb_cc_idx) current_tti = tti_rx; lch_handler.new_tti(); for (auto& cc : cells) { - if (cc.configured()) { - cc.harq_ent.new_tti(tti_rx); - } + cc.new_tti(tti_rx); } } - - if (cells[enb_cc_idx].configured()) { - cells[enb_cc_idx].tpc_fsm.new_tti(); - } } /******************************************************* @@ -146,9 +140,9 @@ void sched_ue::ul_buffer_add(uint8_t lcid, uint32_t bytes) lch_handler.ul_buffer_add(lcid, bytes); } -void sched_ue::ul_phr(int phr) +void sched_ue::ul_phr(int phr, uint32_t grant_nof_prb) { - cells[cfg.supported_cc_list[0].enb_cc_idx].tpc_fsm.set_phr(phr); + cells[cfg.supported_cc_list[0].enb_cc_idx].tpc_fsm.set_phr(phr, grant_nof_prb); } void sched_ue::dl_buffer_state(uint8_t lc_id, uint32_t tx_queue, uint32_t retx_queue) @@ -229,7 +223,7 @@ bool sched_ue::pusch_enabled(tti_point tti_rx, uint32_t enb_cc_idx, bool needs_p tti_interval meas_gap{mgap_tti, mgap_tti + 6}; // disable TTIs that leads to PUSCH tx or PHICH rx falling in measGap - if (meas_gap.contains(tti_tx_ul) or meas_gap.contains(to_tx_ul_ack(tti_rx))) { + if (meas_gap.contains(tti_tx_ul)) { return false; } // disable TTIs which respective PDCCH falls in measGap (in case PDCCH is needed) @@ -240,34 +234,32 @@ bool sched_ue::pusch_enabled(tti_point tti_rx, uint32_t enb_cc_idx, bool needs_p return true; } -int sched_ue::set_ack_info(tti_point tti_rx, uint32_t enb_cc_idx, uint32_t tb_idx, bool ack) +bool sched_ue::phich_enabled(tti_point tti_rx, uint32_t enb_cc_idx) const { - int tbs_acked = -1; - if (cells[enb_cc_idx].cc_state() != cc_st::idle) { - std::pair p2 = cells[enb_cc_idx].harq_ent.set_ack_info(tti_rx, tb_idx, ack); - tbs_acked = p2.second; - if (tbs_acked > 0) { - logger.debug( - "SCHED: Set DL ACK=%d for rnti=0x%x, pid=%d, tb=%d, tti=%d", ack, rnti, p2.first, tb_idx, tti_rx.to_uint()); - } else { - logger.warning("SCHED: Received ACK info for unknown TTI=%d", tti_rx.to_uint()); + if (cfg.supported_cc_list[0].enb_cc_idx != enb_cc_idx) { + return true; + } + + // Check measGap collision with PHICH + if (cfg.measgap_period > 0) { + tti_point tti_tx_dl = to_tx_dl(tti_rx); + tti_point mgap_tti = nearest_meas_gap(tti_tx_dl, cfg.measgap_period, cfg.measgap_offset); + tti_interval meas_gap{mgap_tti, mgap_tti + 6}; + if (meas_gap.contains(tti_tx_dl)) { + return false; } - } else { - logger.warning("Received DL ACK for invalid cell index %d", enb_cc_idx); } - return tbs_acked; + return true; +} + +int sched_ue::set_ack_info(tti_point tti_rx, uint32_t enb_cc_idx, uint32_t tb_idx, bool ack) +{ + return cells[enb_cc_idx].set_ack_info(tti_rx, tb_idx, ack); } void sched_ue::set_ul_crc(tti_point tti_rx, uint32_t enb_cc_idx, bool crc_res) { - if (cells[enb_cc_idx].cc_state() != cc_st::idle) { - int ret = cells[enb_cc_idx].harq_ent.set_ul_crc(tti_rx, 0, crc_res); - if (ret < 0) { - logger.warning("Received UL CRC for invalid tti_rx=%d", (int)tti_rx.to_uint()); - } - } else { - logger.warning("Received UL CRC for invalid cell index %d", enb_cc_idx); - } + cells[enb_cc_idx].set_ul_crc(tti_rx, crc_res); } void sched_ue::set_dl_ri(tti_point tti_rx, uint32_t enb_cc_idx, uint32_t ri) @@ -301,15 +293,7 @@ void sched_ue::set_dl_cqi(tti_point tti_rx, uint32_t enb_cc_idx, uint32_t cqi) void sched_ue::set_ul_snr(tti_point tti_rx, uint32_t enb_cc_idx, float snr, uint32_t ul_ch_code) { - if (cells[enb_cc_idx].cc_state() != cc_st::idle) { - cells[enb_cc_idx].tpc_fsm.set_snr(snr, ul_ch_code); - if (ul_ch_code == tpc::PUSCH_CODE) { - cells[enb_cc_idx].ul_cqi = srsran_cqi_from_snr(snr); - cells[enb_cc_idx].ul_cqi_tti_rx = tti_rx; - } - } else { - logger.warning("Received SNR info for invalid cell index %d", enb_cc_idx); - } + cells[enb_cc_idx].set_ul_snr(tti_rx, snr, ul_ch_code); } /******************************************************* @@ -845,7 +829,7 @@ uint32_t sched_ue::get_expected_dl_bitrate(uint32_t enb_cc_idx, int nof_rbgs) co auto& cc = cells[enb_cc_idx]; uint32_t nof_re = cc.cell_cfg->get_dl_lb_nof_re(to_tx_dl(current_tti), count_prb_per_tb_approx(nof_rbgs, cc.cell_cfg->nof_prb())); - float max_coderate = srsran_cqi_to_coderate(std::min(cc.dl_cqi().get_avg_cqi() + 1u, 15u), cfg.use_tbs_index_alt); + float max_coderate = srsran_cqi_to_coderate(std::min(cc.get_dl_cqi() + 1u, 15u), cfg.use_tbs_index_alt); // Inverse of srsran_coderate(tbs, nof_re) uint32_t tbs = max_coderate * nof_re - 24; @@ -859,7 +843,7 @@ uint32_t sched_ue::get_expected_ul_bitrate(uint32_t enb_cc_idx, int nof_prbs) co uint32_t N_srs = 0; uint32_t nof_symb = 2 * (SRSRAN_CP_NSYMB(cell.cp) - 1) - N_srs; uint32_t nof_re = nof_symb * nof_prbs_alloc * SRSRAN_NRE; - float max_coderate = srsran_cqi_to_coderate(std::min(cells[enb_cc_idx].ul_cqi + 1u, 15u), false); + float max_coderate = srsran_cqi_to_coderate(std::min(cells[enb_cc_idx].get_ul_cqi() + 1u, 15u), false); // Inverse of srsran_coderate(tbs, nof_re) uint32_t tbs = max_coderate * nof_re - 24; @@ -914,7 +898,7 @@ uint32_t sched_ue::get_pending_ul_data_total(tti_point tti_tx_ul, int this_enb_c uint32_t max_cqi = 0, max_cc_idx = 0; for (uint32_t cc = 0; cc < cells.size(); ++cc) { if (cells[cc].configured()) { - uint32_t sum_cqi = cells[cc].dl_cqi().get_avg_cqi() + cells[cc].ul_cqi; + uint32_t sum_cqi = cells[cc].get_dl_cqi() + cells[cc].get_ul_cqi(); if (cells[cc].cc_state() == cc_st::active and sum_cqi > max_cqi) { max_cqi = sum_cqi; max_cc_idx = cc; @@ -1006,9 +990,7 @@ std::pair sched_ue::get_active_cell_index(uint32_t enb_cc_idx) c uint32_t sched_ue::get_aggr_level(uint32_t enb_cc_idx, uint32_t nof_bits) { - const auto& cc = cells[enb_cc_idx]; - return srsenb::get_aggr_level( - nof_bits, cc.dl_cqi().get_avg_cqi(), cc.max_aggr_level, cc.cell_cfg->nof_prb(), cfg.use_tbs_index_alt); + return cells[enb_cc_idx].get_aggr_level(nof_bits); } void sched_ue::finish_tti(tti_point tti_rx, uint32_t enb_cc_idx) 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 89422716f..4a71037ff 100644 --- a/srsenb/src/stack/mac/sched_ue_ctrl/sched_harq.cc +++ b/srsenb/src/stack/mac/sched_ue_ctrl/sched_harq.cc @@ -121,11 +121,8 @@ void harq_proc::new_retx_common(uint32_t tb_idx, tti_point tti_, int* mcs, int* void harq_proc::reset_pending_data_common() { - // reuse harqs with no retxs - if (max_retx == 0 and not is_empty()) { - for (bool& tb : active) { - tb = false; - } + for (bool& tb : active) { + tb = false; } } @@ -259,9 +256,10 @@ void ul_harq_proc::new_tx(tti_point tti_, int mcs, int tbs, prb_interval alloc, { allocation = alloc; new_tx_common(0, tti_point{tti_}, mcs, tbs, max_retx_); - pending_data = tbs; - pending_phich = true; - is_msg3_ = is_msg3; + pending_data = tbs; + pending_phich = true; + is_msg3_ = is_msg3; + pdcch_requested = false; } void ul_harq_proc::new_retx(tti_point tti_, int* mcs, int* tbs, prb_interval alloc) @@ -269,12 +267,15 @@ void ul_harq_proc::new_retx(tti_point tti_, int* mcs, int* tbs, prb_interval all // If PRBs changed, or there was no tx in last oportunity (e.g. HARQ is being resumed) allocation = alloc; new_retx_common(0, tti_point{tti_}, mcs, tbs); - pending_phich = true; + pending_phich = true; + pdcch_requested = false; } -bool ul_harq_proc::retx_requires_pdcch(srsran::tti_point tti_, prb_interval alloc) const +bool ul_harq_proc::retx_requires_pdcch(tti_point tti_, prb_interval alloc) const { - return alloc != allocation or tti_ != to_tx_ul(tti); + // Adaptive retx if: (1) PRBs changed, (2) HARQ resumed due to last PUSCH retx being skipped (3) HARQ resumed due to + // last PHICH alloc being skipped (e.g. due to measGaps) + return alloc != allocation or pdcch_requested; } bool ul_harq_proc::set_ack(uint32_t tb_idx, bool ack_) @@ -291,15 +292,27 @@ bool ul_harq_proc::has_pending_phich() const return pending_phich; } +void ul_harq_proc::request_pdcch() +{ + pdcch_requested = true; +} + +void ul_harq_proc::retx_skipped() +{ + // Note: This function should be called in case of PHICH allocation is successful + // Flagging "PDCCH required" for next retx, as HARQ is being resumed + pdcch_requested = true; + n_rtx[0]++; +} + bool ul_harq_proc::pop_pending_phich() { - assert(pending_phich); + srsran_assert(pending_phich, "pop_pending_phich called for HARQ with no pending PHICH"); bool ret = ack_state[0]; pending_phich = false; if (is_empty(0)) { - // fully reset UL HARQ once PHICH is dispatched - is_msg3_ = false; - pending_data = 0; + // fully reset HARQ info once PHICH is dispatched for an acked / maxretx reached HARQ + reset_pending_data(); } return ret; } @@ -307,10 +320,9 @@ bool ul_harq_proc::pop_pending_phich() void ul_harq_proc::reset_pending_data() { reset_pending_data_common(); - if (is_empty(0)) { - pending_data = 0; - is_msg3_ = false; - } + pending_data = 0; + is_msg3_ = false; + pdcch_requested = false; } uint32_t ul_harq_proc::get_pending_data() const @@ -375,17 +387,17 @@ dl_harq_proc* harq_entity::get_pending_dl_harq(tti_point tti_tx_dl) return get_oldest_dl_harq(tti_tx_dl); } -std::pair harq_entity::set_ack_info(tti_point tti_rx, uint32_t tb_idx, bool ack) +std::tuple harq_entity::set_ack_info(tti_point tti_rx, uint32_t tb_idx, bool ack) { for (auto& h : dl_harqs) { if (h.get_tti() + FDD_HARQ_DELAY_DL_MS == tti_rx) { if (h.set_ack(tb_idx, ack) == SRSRAN_SUCCESS) { - return {h.get_id(), h.get_tbs(tb_idx)}; + return std::make_tuple(h.get_id(), h.get_tbs(tb_idx), h.get_mcs(tb_idx)); } - return {h.get_id(), -1}; + return std::make_tuple(h.get_id(), -1, -1); } } - return {dl_harqs.size(), -1}; + return std::make_tuple(dl_harqs.size(), -1, -1); } ul_harq_proc* harq_entity::get_ul_harq(tti_point tti_tx_ul) @@ -400,16 +412,20 @@ int harq_entity::set_ul_crc(tti_point tti_rx, uint32_t tb_idx, bool ack_) return h->set_ack(tb_idx, ack_) ? pid : -1; } -void harq_entity::reset_pending_data(tti_point tti_rx) +void harq_entity::finish_tti(tti_point tti_rx) { - tti_point tti_tx_ul = to_tx_ul(tti_rx); - - // Reset ACK state of UL Harq - get_ul_harq(tti_tx_ul)->reset_pending_data(); + // Reset UL HARQ if no retxs + auto* hul = get_ul_harq(to_tx_ul(tti_rx)); + if (not hul->is_empty() and hul->max_nof_retx() == 0) { + hul->reset_pending_data(); + } - // Reset any DL harq which has 0 retxs + // Reset DL harq which has 0 retxs for (auto& h : dl_harqs) { - h.reset_pending_data(); + if (not h.is_empty() and h.max_nof_retx() == 0) { + // reuse harqs with no retxs + h.reset_pending_data(); + } } } diff --git a/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc b/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc index 07fd64298..acff80d8a 100644 --- a/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc +++ b/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc @@ -22,9 +22,16 @@ #include "srsenb/hdr/stack/mac/sched_ue_ctrl/sched_ue_cell.h" #include "srsenb/hdr/stack/mac/sched_helpers.h" #include "srsenb/hdr/stack/mac/sched_phy_ch/sched_dci.h" -#include "srsenb/hdr/stack/mac/schedulers/sched_base.h" #include +#define CHECK_VALID_CC(feedback_type) \ + do { \ + if (cc_state() == cc_st::idle) { \ + logger.warning("SCHED: rnti=0x%x received " feedback_type " for idle cc=%d", rnti, cell_cfg->enb_cc_idx); \ + return SRSRAN_ERROR; \ + } \ + } while (0) + namespace srsenb { /******************************************************* @@ -37,10 +44,15 @@ sched_ue_cell::sched_ue_cell(uint16_t rnti_, const sched_cell_params_t& cell_cfg cell_cfg(&cell_cfg_), dci_locations(generate_cce_location_table(rnti_, cell_cfg_)), harq_ent(SCHED_MAX_HARQ_PROC, SCHED_MAX_HARQ_PROC), - tpc_fsm(cell_cfg->nof_prb(), + tpc_fsm(rnti_, + cell_cfg->nof_prb(), cell_cfg->cfg.target_pucch_ul_sinr, cell_cfg->cfg.target_pusch_ul_sinr, - cell_cfg->cfg.enable_phr_handling), + cell_cfg->cfg.enable_phr_handling, + cell_cfg->cfg.min_phr_thres, + cell_cfg->sched_cfg->min_tpc_tti_interval, + cell_cfg->sched_cfg->ul_snr_avg_alpha, + cell_cfg->sched_cfg->init_ul_snr_value), fixed_mcs_dl(cell_cfg_.sched_cfg->pdsch_mcs), fixed_mcs_ul(cell_cfg_.sched_cfg->pusch_mcs), current_tti(current_tti_), @@ -48,6 +60,12 @@ sched_ue_cell::sched_ue_cell(uint16_t rnti_, const sched_cell_params_t& cell_cfg dl_cqi_ctxt(cell_cfg_.nof_prb(), 0, 1) { clear_feedback(); + + float target_bler = cell_cfg->sched_cfg->target_bler; + delta_inc = cell_cfg->sched_cfg->adaptive_link_step_size; // delta_{down} of OLLA + delta_dec = (1 - target_bler) * delta_inc / target_bler; + max_cqi_coeff = cell_cfg->sched_cfg->max_delta_dl_cqi; + max_snr_coeff = cell_cfg->sched_cfg->max_delta_ul_snr; } void sched_ue_cell::set_ue_cfg(const sched_interface::ue_cfg_t& ue_cfg_) @@ -123,8 +141,14 @@ void sched_ue_cell::set_ue_cfg(const sched_interface::ue_cfg_t& ue_cfg_) void sched_ue_cell::new_tti(tti_point tti_rx) { + if (not configured()) { + return; + } current_tti = tti_rx; + harq_ent.new_tti(tti_rx); + tpc_fsm.new_tti(); + // Check if cell state needs to be updated if (ue_cc_idx > 0 and cc_state_ == cc_st::deactivating) { // wait for all ACKs to be received before completely deactivating SCell @@ -143,24 +167,135 @@ void sched_ue_cell::clear_feedback() dl_pmi = 0; dl_pmi_tti_rx = tti_point{}; dl_cqi_ctxt.reset_cqi(ue_cc_idx == 0 ? cell_cfg->cfg.initial_dl_cqi : 1); - ul_cqi = 1; ul_cqi_tti_rx = tti_point{}; } void sched_ue_cell::finish_tti(tti_point tti_rx) { // clear_feedback PIDs with pending data or blocked - harq_ent.reset_pending_data(tti_rx); + harq_ent.finish_tti(tti_rx); } -void sched_ue_cell::set_dl_wb_cqi(tti_point tti_rx, uint32_t dl_cqi_) +int sched_ue_cell::set_dl_wb_cqi(tti_point tti_rx, uint32_t dl_cqi_) { + CHECK_VALID_CC("DL CQI"); dl_cqi_ctxt.cqi_wb_info(tti_rx, dl_cqi_); if (ue_cc_idx > 0 and cc_state_ == cc_st::activating and dl_cqi_ > 0) { // Wait for SCell to receive a positive CQI before activating it cc_state_ = cc_st::active; logger.info("SCHED: SCell index=%d is now active", ue_cc_idx); } + return SRSRAN_SUCCESS; +} + +int sched_ue_cell::set_ul_crc(tti_point tti_rx, bool crc_res) +{ + CHECK_VALID_CC("UL CRC"); + + // Adapt UL MCS based on BLER + if (cell_cfg->sched_cfg->target_bler > 0 and fixed_mcs_ul < 0) { + auto* ul_harq = harq_ent.get_ul_harq(tti_rx); + if (ul_harq != nullptr) { + int mcs = ul_harq->get_mcs(0); + // Note: Avoid keeping increasing the snr delta offset, if MCS is already is at its limit + float delta_dec_eff = mcs <= 0 ? 0 : delta_dec; + float delta_inc_eff = mcs >= (int)max_mcs_ul ? 0 : delta_inc; + ul_snr_coeff += crc_res ? delta_inc_eff : -delta_dec_eff; + ul_snr_coeff = std::min(std::max(-max_snr_coeff, ul_snr_coeff), max_snr_coeff); + logger.info("SCHED: UL adaptive link: rnti=0x%x, snr_estim=%.2f, last_mcs=%d, snr_offset=%f", + rnti, + tpc_fsm.get_ul_snr_estim(), + mcs, + ul_snr_coeff); + } + } + + // Update HARQ process + int pid = harq_ent.set_ul_crc(tti_rx, 0, crc_res); + if (pid < 0) { + logger.warning("SCHED: rnti=0x%x received UL CRC for invalid tti_rx=%d", rnti, (int)tti_rx.to_uint()); + return SRSRAN_ERROR; + } + + return pid; +} + +int sched_ue_cell::set_ack_info(tti_point tti_rx, uint32_t tb_idx, bool ack) +{ + CHECK_VALID_CC("DL ACK Info"); + + std::tuple p2 = harq_ent.set_ack_info(tti_rx, tb_idx, ack); + int tbs_acked = std::get<1>(p2); + if (tbs_acked <= 0) { + logger.warning("SCHED: Received ACK info for unknown TTI=%d", tti_rx.to_uint()); + return tbs_acked; + } + + // Adapt DL MCS based on BLER + if (cell_cfg->sched_cfg->target_bler > 0 and fixed_mcs_dl < 0) { + int mcs = std::get<2>(p2); + // Note: Avoid keeping increasing the snr delta offset, if MCS is already is at its limit + float delta_dec_eff = mcs <= 0 ? 0 : delta_dec; + float delta_inc_eff = mcs >= (int)max_mcs_dl ? 0 : delta_inc; + dl_cqi_coeff += ack ? delta_inc_eff : -delta_dec_eff; + dl_cqi_coeff = std::min(std::max(-max_cqi_coeff, dl_cqi_coeff), max_cqi_coeff); + logger.info("SCHED: DL adaptive link: rnti=0x%x, cqi=%d, last_mcs=%d, cqi_offset=%f", + rnti, + dl_cqi_ctxt.get_avg_cqi(), + mcs, + dl_cqi_coeff); + } + return tbs_acked; +} + +int sched_ue_cell::set_ul_snr(tti_point tti_rx, float ul_snr, uint32_t ul_ch_code) +{ + CHECK_VALID_CC("UL SNR estimate"); + if (ue_cfg->ue_bearers[1].direction == sched_interface::ue_bearer_cfg_t::IDLE) { + // Ignore Msg3 SNR samples as Msg3 uses a separate power control loop + return SRSRAN_SUCCESS; + } + tpc_fsm.set_snr(ul_snr, ul_ch_code); + if (ul_ch_code == tpc::PUSCH_CODE) { + ul_cqi_tti_rx = tti_rx; + } + return SRSRAN_SUCCESS; +} + +int sched_ue_cell::get_ul_cqi() const +{ + if (not ul_cqi_tti_rx.is_valid()) { + return 1; + } + float snr = tpc_fsm.get_ul_snr_estim(); + return srsran_cqi_from_snr(snr + ul_snr_coeff); +} + +int sched_ue_cell::get_dl_cqi(const rbgmask_t& rbgs) const +{ + float dl_cqi = std::get<1>(find_min_cqi_rbg(rbgs, dl_cqi_ctxt)); + return std::max(0, (int)std::min(dl_cqi + dl_cqi_coeff, 15.0f)); +} + +int sched_ue_cell::get_dl_cqi() const +{ + return std::max(0, (int)std::min(dl_cqi_ctxt.get_avg_cqi() + dl_cqi_coeff, 15.0f)); +} + +uint32_t sched_ue_cell::get_aggr_level(uint32_t nof_bits) const +{ + uint32_t dl_cqi = 0; + if (cell_cfg->sched_cfg->adaptive_aggr_level) { + dl_cqi = get_dl_cqi(); + } else { + dl_cqi = dl_cqi_ctxt.get_avg_cqi(); + } + return srsenb::get_aggr_level(nof_bits, + dl_cqi, + cell_cfg->sched_cfg->min_aggr_level, + max_aggr_level, + cell_cfg->nof_prb(), + ue_cfg->use_tbs_index_alt); } /************************************************************* @@ -252,7 +387,7 @@ tbs_info cqi_to_tbs_dl(const sched_ue_cell& cell, tbs_info ret; if (cell.fixed_mcs_dl < 0 or not cell.dl_cqi().is_cqi_info_received()) { // Dynamic MCS configured or first Tx - uint32_t dl_cqi = std::get<1>(find_min_cqi_rbg(rbgs, cell.dl_cqi())); + uint32_t dl_cqi = cell.get_dl_cqi(rbgs); ret = compute_min_mcs_and_tbs_from_required_bytes( nof_prbs, nof_re, dl_cqi, cell.max_mcs_dl, req_bytes, false, false, use_tbs_index_alt); @@ -281,7 +416,7 @@ tbs_info cqi_to_tbs_ul(const sched_ue_cell& cell, uint32_t nof_prb, uint32_t nof if (mcs < 0) { // Dynamic MCS ret = compute_min_mcs_and_tbs_from_required_bytes( - nof_prb, nof_re, cell.ul_cqi, cell.max_mcs_ul, req_bytes, true, ulqam64_enabled, false); + nof_prb, nof_re, cell.get_ul_cqi(), cell.max_mcs_ul, req_bytes, true, ulqam64_enabled, false); // If coderate > SRSRAN_MIN(max_coderate, 0.932 * Qm) we should set TBS=0. We don't because it's not correctly // handled by the scheduler, but we might be scheduling undecodable codewords at very low SNR @@ -319,7 +454,7 @@ int get_required_prb_dl(const sched_ue_cell& cell, uint32_t get_required_prb_ul(const sched_ue_cell& cell, uint32_t req_bytes) { - const static int MIN_ALLOC_BYTES = 10; + const static int MIN_ALLOC_BYTES = 10; /// There should be enough space for RLC header + BSR + some payload if (req_bytes == 0) { return 0; } @@ -338,9 +473,7 @@ uint32_t get_required_prb_ul(const sched_ue_cell& cell, uint32_t req_bytes) uint32_t final_tbs = std::get<3>(ret); while (final_tbs < MIN_ALLOC_BYTES and req_prbs < cell.cell_cfg->nof_prb()) { // Note: If PHR<0 is limiting the max nof PRBs per UL grant, the UL grant may become too small to fit any - // data other than headers + BSR. Besides, forcing unnecessary segmentation, it may additionally - // forbid the UE from fitting small RRC messages (e.g. RRCReconfComplete) in the UL grants. - // To avoid TBS<10, we force an increase the nof required PRBs. + // data other than headers + BSR. In this edge-case, force an increase the nof required PRBs. req_prbs++; final_tbs = compute_tbs_approx(req_prbs); } diff --git a/srsenb/src/stack/mac/ue.cc b/srsenb/src/stack/mac/ue.cc index 682611194..f4b46aa54 100644 --- a/srsenb/src/stack/mac/ue.cc +++ b/srsenb/src/stack/mac/ue.cc @@ -71,30 +71,25 @@ void ue_cc_softbuffers::clear() } } -cc_used_buffers_map::cc_used_buffers_map(srsran::pdu_queue& shared_pdu_queue_) : - shared_pdu_queue(&shared_pdu_queue_), logger(&srslog::fetch_basic_logger("MAC")) -{} +cc_used_buffers_map::cc_used_buffers_map() : 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) +srsran::unique_byte_buffer_t cc_used_buffers_map::release_pdu(tti_point tti) { if (not has_tti(tti)) { - return false; - } - uint8_t* buffer = pdu_map[tti.to_uint()]; - if (len > 0) { - shared_pdu_queue->push(buffer, len); - } else { - shared_pdu_queue->deallocate(buffer); - logger->error("Error pushing PDU: null length"); + return nullptr; } + + // Extract PDU from PDU map + srsran::unique_byte_buffer_t pdu = std::move(pdu_map[tti.to_uint()]); + // clear entry in map pdu_map.erase(tti.to_uint()); - return len > 0; + return pdu; } uint8_t* cc_used_buffers_map::request_pdu(tti_point tti, uint32_t len) @@ -104,15 +99,17 @@ uint8_t* cc_used_buffers_map::request_pdu(tti_point tti, uint32_t len) return nullptr; } - uint8_t* pdu = shared_pdu_queue->request(len); + srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); if (pdu == nullptr) { - logger->error("UE buffers: Requesting buffer from pool"); + logger->error("UE buffers: Requesting buffer from byte buffer pool"); return nullptr; } + srsran_assert(len < pdu->get_tailroom(), "Requested UL pdu doesn't fit in byte_buffer"); + pdu->N_bytes = len; - bool inserted = pdu_map.insert(tti.to_uint(), pdu); - srsran_assert(inserted, "Failure to allocate new buffer"); - return pdu; + auto inserted_elem = pdu_map.insert(tti.to_uint(), std::move(pdu)); + srsran_assert(inserted_elem.has_value(), "Failure to allocate new buffer in mac::ue"); + return inserted_elem.value()->second->data(); } void cc_used_buffers_map::clear_old_pdus(tti_point current_tti) @@ -124,40 +121,14 @@ void cc_used_buffers_map::clear_old_pdus(tti_point current_tti) 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); + pdu_map.erase(t.to_uint()); } } } -void cc_used_buffers_map::remove_pdu(tti_point tti) -{ - uint8_t* buffer = pdu_map[tti.to_uint()]; - // return pdus back to the queue - shared_pdu_queue->deallocate(buffer); - // clear entry in map - pdu_map.erase(tti.to_uint()); -} - -bool cc_used_buffers_map::try_deallocate_pdu(tti_point tti) -{ - if (has_tti(tti)) { - remove_pdu(tti); - return true; - } - return false; -} - -void cc_used_buffers_map::clear() -{ - for (auto& buffer : pdu_map) { - shared_pdu_queue->deallocate(buffer.second); - } - pdu_map.clear(); -} - uint8_t*& cc_used_buffers_map::operator[](tti_point tti) { - return pdu_map[tti.to_uint()]; + return pdu_map[tti.to_uint()]->msg; } bool cc_used_buffers_map::has_tti(tti_point tti) const @@ -167,7 +138,7 @@ bool cc_used_buffers_map::has_tti(tti_point tti) const //////////////// -cc_buffer_handler::cc_buffer_handler(srsran::pdu_queue& shared_pdu_queue_) : rx_used_buffers(shared_pdu_queue_) +cc_buffer_handler::cc_buffer_handler() { for (auto& harq_buffers : tx_payload_buffer) { for (srsran::unique_byte_buffer_t& tb_buffer : harq_buffers) { @@ -228,26 +199,15 @@ ue::ue(uint16_t rnti_, mac_msg_dl(20, logger_), mch_mac_msg_dl(10, logger_), mac_msg_ul(20, logger_), - pdus(logger_), ta_fsm(this), - softbuffer_pool(softbuffer_pool_) + softbuffer_pool(softbuffer_pool_), + cc_buffers(nof_cells_) { - for (size_t i = 0; i < nof_cells_; ++i) { - cc_buffers.emplace_back(pdus); - } - pdus.init(this); - // Allocate buffer for PCell cc_buffers[0].allocate_cc(softbuffer_pool->make()); } -ue::~ue() -{ - std::unique_lock lock(rx_buffers_mutex); - for (auto& cc : cc_buffers) { - cc.get_rx_used_buffers().clear(); - } -} +ue::~ue() {} void ue::reset() { @@ -269,7 +229,7 @@ void ue::start_pcap(srsran::mac_pcap* pcap_) pcap = pcap_; } -srsran_softbuffer_rx_t* ue::get_rx_softbuffer(const uint32_t ue_cc_idx, const uint32_t tti) +srsran_softbuffer_rx_t* ue::get_rx_softbuffer(uint32_t ue_cc_idx, uint32_t tti) { if ((size_t)ue_cc_idx >= cc_buffers.size()) { ERROR("UE CC Index (%d/%zd) out-of-range", ue_cc_idx, cc_buffers.size()); @@ -279,8 +239,7 @@ srsran_softbuffer_rx_t* ue::get_rx_softbuffer(const uint32_t ue_cc_idx, const ui return &cc_buffers[ue_cc_idx].get_rx_softbuffer(tti); } -srsran_softbuffer_tx_t* -ue::get_tx_softbuffer(const uint32_t ue_cc_idx, const uint32_t harq_process, const uint32_t tb_idx) +srsran_softbuffer_tx_t* ue::get_tx_softbuffer(uint32_t ue_cc_idx, uint32_t harq_process, uint32_t tb_idx) { if ((size_t)ue_cc_idx >= cc_buffers.size()) { ERROR("UE CC Index (%d/%zd) out-of-range", ue_cc_idx, cc_buffers.size()); @@ -292,31 +251,20 @@ ue::get_tx_softbuffer(const uint32_t ue_cc_idx, const uint32_t harq_process, con uint8_t* ue::request_buffer(uint32_t tti, uint32_t ue_cc_idx, const uint32_t len) { + srsran_assert(len > 0, "UE buffers: Requesting buffer for zero bytes"); std::unique_lock lock(rx_buffers_mutex); - uint8_t* pdu = nullptr; - if (len > 0) { - pdu = cc_buffers[ue_cc_idx].get_rx_used_buffers().request_pdu(tti_point(tti), len); - } else { - logger.error("UE buffers: Requesting buffer for zero bytes"); - } - return pdu; + return cc_buffers[ue_cc_idx].get_rx_used_buffers().request_pdu(tti_point(tti), len); } void ue::clear_old_buffers(uint32_t tti) { std::unique_lock lock(rx_buffers_mutex); - // remove old buffers for (auto& cc : cc_buffers) { cc.get_rx_used_buffers().clear_old_pdus(tti_point{tti}); } } -bool ue::process_pdus() -{ - return pdus.process_pdus(); -} - void ue::set_tti(uint32_t tti) { last_tti = tti; @@ -338,11 +286,11 @@ uint32_t ue::set_ta(int ta_) return nof_cmd; } -void ue::process_pdu(uint8_t* pdu, uint32_t nof_bytes, srsran::pdu_queue::channel_t channel) +void ue::process_pdu(srsran::unique_byte_buffer_t pdu, uint32_t grant_nof_prbs) { // Unpack ULSCH MAC PDU - mac_msg_ul.init_rx(nof_bytes, true); - mac_msg_ul.parse_packet(pdu); + mac_msg_ul.init_rx(pdu->size(), true); + mac_msg_ul.parse_packet(pdu->data()); if (logger.info.enabled()) { fmt::memory_buffer str_buffer; @@ -350,16 +298,14 @@ void ue::process_pdu(uint8_t* pdu, uint32_t nof_bytes, srsran::pdu_queue::channe logger.info("0x%x %s", rnti, srsran::to_c_str(str_buffer)); } - if (pcap) { - pcap->write_ul_crnti(pdu, nof_bytes, rnti, true, last_tti, UL_CC_IDX); + if (pcap != nullptr) { + pcap->write_ul_crnti(pdu->data(), pdu->size(), rnti, true, last_tti, UL_CC_IDX); } - if (pcap_net) { - pcap_net->write_ul_crnti(pdu, nof_bytes, rnti, true, last_tti, UL_CC_IDX); + if (pcap_net != nullptr) { + pcap_net->write_ul_crnti(pdu->data(), pdu->size(), rnti, true, last_tti, UL_CC_IDX); } - pdus.deallocate(pdu); - uint32_t lcid_most_data = 0; int most_data = -99; @@ -395,7 +341,7 @@ void ue::process_pdu(uint8_t* pdu, uint32_t nof_bytes, srsran::pdu_queue::channe // Indicate DRB activity in UL to RRC if (mac_msg_ul.get()->get_sdu_lcid() > 2) { rrc->set_activity_user(rnti); - logger.debug("UL activity rnti=0x%x, n_bytes=%d", rnti, nof_bytes); + logger.debug("UL activity rnti=0x%x, n_bytes=%d", rnti, pdu->size()); } if ((int)mac_msg_ul.get()->get_payload_size() > most_data) { @@ -426,7 +372,7 @@ void ue::process_pdu(uint8_t* pdu, uint32_t nof_bytes, srsran::pdu_queue::channe assert(mac_msg_ul.get()); if (!mac_msg_ul.get()->is_sdu()) { // Process MAC Control Element - bsr_received |= process_ce(mac_msg_ul.get()); + bsr_received |= process_ce(mac_msg_ul.get(), grant_nof_prbs); } } @@ -440,24 +386,13 @@ void ue::process_pdu(uint8_t* pdu, uint32_t nof_bytes, srsran::pdu_queue::channe logger.debug("MAC PDU processed"); } -void ue::deallocate_pdu(uint32_t tti, uint32_t ue_cc_idx) -{ - std::unique_lock lock(rx_buffers_mutex); - if (not cc_buffers[ue_cc_idx].get_rx_used_buffers().try_deallocate_pdu(tti_point(tti))) { - logger.warning( - "UE buffers: Null RX PDU pointer in deallocate_pdu for rnti=0x%x tti=%d cc_idx=%d", rnti, tti, ue_cc_idx); - } -} - -void ue::push_pdu(uint32_t tti, uint32_t ue_cc_idx, uint32_t len) +srsran::unique_byte_buffer_t ue::release_pdu(uint32_t tti, uint32_t ue_cc_idx) { - std::unique_lock lock(rx_buffers_mutex); - if (not cc_buffers[ue_cc_idx].get_rx_used_buffers().push_pdu(tti_point(tti), len)) { - logger.warning("UE buffers: Failed to push RX PDU for rnti=0x%x tti=%d cc_idx=%d", rnti, tti, ue_cc_idx); - } + std::lock_guard lock(rx_buffers_mutex); + return cc_buffers[ue_cc_idx].get_rx_used_buffers().release_pdu(tti_point(tti)); } -bool ue::process_ce(srsran::sch_subh* subh) +bool ue::process_ce(srsran::sch_subh* subh, uint32_t grant_nof_prbs) { uint32_t buff_size_idx[4] = {}; uint32_t buff_size_bytes[4] = {}; @@ -468,7 +403,8 @@ bool ue::process_ce(srsran::sch_subh* subh) switch (subh->ul_sch_ce_type()) { case srsran::ul_sch_lcid::PHR_REPORT: phr = subh->get_phr(); - sched->ul_phr(rnti, (int)phr); + srsran_assert(grant_nof_prbs > 0, "Invalid nof prbs=%d provided for PHR handling", grant_nof_prbs); + sched->ul_phr(rnti, (int)phr, grant_nof_prbs); metrics_phr(phr); break; case srsran::ul_sch_lcid::CRNTI: @@ -511,7 +447,7 @@ bool ue::process_ce(srsran::sch_subh* subh) return is_bsr; } -int ue::read_pdu(uint32_t lcid, uint8_t* payload, uint32_t requested_bytes) +uint32_t ue::read_pdu(uint32_t lcid, uint8_t* payload, uint32_t requested_bytes) { return rlc->read_pdu(rnti, lcid, payload, requested_bytes); } diff --git a/srsenb/src/stack/rrc/mac_controller.cc b/srsenb/src/stack/rrc/mac_controller.cc index 9bc889207..8ee094d25 100644 --- a/srsenb/src/stack/rrc/mac_controller.cc +++ b/srsenb/src/stack/rrc/mac_controller.cc @@ -306,10 +306,13 @@ void mac_controller::handle_ho_prep(const asn1::rrc::ho_prep_info_r8_ies_s& ho_p } } -void mac_controller::handle_max_retx() +void mac_controller::set_radio_bearer_state(sched_interface::ue_bearer_cfg_t::direction_t dir) { - for (auto& ue_bearer : current_sched_ue_cfg.ue_bearers) { - ue_bearer.direction = sched_interface::ue_bearer_cfg_t::IDLE; + for (uint32_t i = srb_to_lcid(lte_srb::srb0); i <= srb_to_lcid(lte_srb::srb2); ++i) { + current_sched_ue_cfg.ue_bearers[i].direction = dir; + } + for (auto& drb : bearer_list.get_established_drbs()) { + current_sched_ue_cfg.ue_bearers[drb.lc_ch_id].direction = dir; } update_mac(config_tx); } diff --git a/srsenb/src/stack/rrc/rrc.cc b/srsenb/src/stack/rrc/rrc.cc index f0a23a184..c6cc48c4c 100644 --- a/srsenb/src/stack/rrc/rrc.cc +++ b/srsenb/src/stack/rrc/rrc.cc @@ -40,7 +40,7 @@ using namespace asn1::rrc; namespace srsenb { rrc::rrc(srsran::task_sched_handle task_sched_) : - logger(srslog::fetch_basic_logger("RRC")), task_sched(task_sched_), rx_pdu_queue(64) + logger(srslog::fetch_basic_logger("RRC")), task_sched(task_sched_), rx_pdu_queue(128) {} rrc::~rrc() {} @@ -146,7 +146,7 @@ void rrc::set_radiolink_dl_state(uint16_t rnti, bool crc_res) rrc_pdu p = {rnti, LCID_RADLINK_DL, crc_res, nullptr}; if (not rx_pdu_queue.try_push(std::move(p))) { - logger.error("Failed to push UE activity command to RRC queue"); + logger.error("Failed to push radio link DL state"); } } @@ -156,7 +156,7 @@ void rrc::set_radiolink_ul_state(uint16_t rnti, bool crc_res) rrc_pdu p = {rnti, LCID_RADLINK_UL, crc_res, nullptr}; if (not rx_pdu_queue.try_push(std::move(p))) { - logger.error("Failed to push UE activity command to RRC queue"); + logger.error("Failed to push radio link UL state"); } } @@ -190,6 +190,14 @@ void rrc::max_retx_attempted(uint16_t rnti) } } +void rrc::protocol_failure(uint16_t rnti) +{ + rrc_pdu p = {rnti, LCID_PROT_FAIL, false, nullptr}; + if (not rx_pdu_queue.try_push(std::move(p))) { + logger.error("Failed to push protocol failure to RRC queue"); + } +} + // This function is called from PRACH worker (can wait) int rrc::add_user(uint16_t rnti, const sched_interface::ue_cfg_t& sched_ue_cfg) { @@ -231,12 +239,16 @@ int rrc::add_user(uint16_t rnti, const sched_interface::ue_cfg_t& sched_ue_cfg) void rrc::upd_user(uint16_t new_rnti, uint16_t old_rnti) { // Remove new_rnti - rem_user_thread(new_rnti); + auto new_ue_it = users.find(new_rnti); + if (new_ue_it != users.end()) { + new_ue_it->second->deactivate_bearers(); + rem_user_thread(new_rnti); + } // Send Reconfiguration to old_rnti if is RRC_CONNECT or RRC Release if already released here auto old_it = users.find(old_rnti); if (old_it == users.end()) { - send_rrc_connection_reject(old_rnti); + logger.info("rnti=0x%x received MAC CRNTI CE: 0x%x, but old context is unavailable", new_rnti, old_rnti); return; } ue* ue_ptr = old_it->second.get(); @@ -640,6 +652,7 @@ void rrc::config_mac() item.target_pucch_ul_sinr = cfg.cell_list[ccidx].target_pucch_sinr_db; item.target_pusch_ul_sinr = cfg.cell_list[ccidx].target_pusch_sinr_db; item.enable_phr_handling = cfg.cell_list[ccidx].enable_phr_handling; + item.min_phr_thres = cfg.cell_list[ccidx].min_phr_thres; item.delta_pucch_shift = cfg.sibs[1].sib2().rr_cfg_common.pucch_cfg_common.delta_pucch_shift.to_number(); item.ncs_an = cfg.sibs[1].sib2().rr_cfg_common.pucch_cfg_common.ncs_an; item.n1pucch_an = cfg.sibs[1].sib2().rr_cfg_common.pucch_cfg_common.n1_pucch_an; @@ -948,6 +961,9 @@ void rrc::tti_clock() case LCID_RLC_RTX: user_it->second->max_rlc_retx_reached(); break; + case LCID_PROT_FAIL: + user_it->second->protocol_failure(); + break; case LCID_EXIT: logger.info("Exiting thread"); break; diff --git a/srsenb/src/stack/rrc/rrc_cell_cfg.cc b/srsenb/src/stack/rrc/rrc_cell_cfg.cc index 1f7d306c6..f4086f97e 100644 --- a/srsenb/src/stack/rrc/rrc_cell_cfg.cc +++ b/srsenb/src/stack/rrc/rrc_cell_cfg.cc @@ -227,6 +227,7 @@ bool ue_cell_ded_list::rem_last_cell() bool ue_cell_ded_list::alloc_cell_resources(uint32_t ue_cc_idx) { + const uint32_t meas_gap_duration = 6; // Allocate CQI, SR, and PUCCH CS resources. If failure, do not add new cell if (ue_cc_idx == UE_PCELL_CC_IDX) { if (not alloc_sr_resources(cfg.sr_cfg.period)) { @@ -235,9 +236,30 @@ bool ue_cell_ded_list::alloc_cell_resources(uint32_t ue_cc_idx) } ue_cell_ded* cell = get_ue_cc_idx(UE_PCELL_CC_IDX); + cell->meas_gap_offset = 0; cell->meas_gap_period = cell->cell_common->cell_cfg.meas_cfg.meas_gap_period; - cell->meas_gap_offset = pucch_res->next_measgap_offset; - pucch_res->next_measgap_offset += 6; + if (cell->meas_gap_period > 0) { + if (not cell->cell_common->cell_cfg.meas_cfg.meas_gap_offset_subframe.empty()) { + // subframes specified + uint32_t min_users = std::numeric_limits::max(); + for (uint32_t i = 0; i < cell->cell_common->cell_cfg.meas_cfg.meas_gap_offset_subframe.size(); ++i) { + uint32_t idx_offset = cell->cell_common->cell_cfg.meas_cfg.meas_gap_offset_subframe[i] / meas_gap_duration; + if (pucch_res->meas_gap_alloc_map[idx_offset] < min_users) { + min_users = pucch_res->meas_gap_alloc_map[idx_offset]; + cell->meas_gap_offset = cell->cell_common->cell_cfg.meas_cfg.meas_gap_offset_subframe[i]; + } + } + } else { + uint32_t min_users = std::numeric_limits::max(); + for (uint32_t meas_offset = 0; meas_offset < cell->cell_common->cell_cfg.meas_cfg.meas_gap_period; + meas_offset += meas_gap_duration) { + if (pucch_res->meas_gap_alloc_map[meas_offset / meas_gap_duration] < min_users) { + min_users = pucch_res->meas_gap_alloc_map[meas_offset / meas_gap_duration]; + cell->meas_gap_offset = meas_offset; + } + } + } + } } else { if (ue_cc_idx == 1 and not n_pucch_cs_present) { // Allocate resources for Format1b CS (will be optional PUCCH3/CS) diff --git a/srsenb/src/stack/rrc/rrc_mobility.cc b/srsenb/src/stack/rrc/rrc_mobility.cc index 4ba904aa3..2c4e93453 100644 --- a/srsenb/src/stack/rrc/rrc_mobility.cc +++ b/srsenb/src/stack/rrc/rrc_mobility.cc @@ -185,6 +185,7 @@ uint16_t rrc::start_ho_ue_resource_alloc(const asn1::s1ap::ho_request_s& ue* ue_ptr = it->second.get(); // Reset activity timer (Response is not expected) ue_ptr->set_activity_timeout(ue::UE_INACTIVITY_TIMEOUT); + ue_ptr->set_activity(false); // /* Setup e-RABs & DRBs / establish an UL/DL S1 bearer to the S-GW */ // if (not setup_ue_erabs(rnti, msg)) { diff --git a/srsenb/src/stack/rrc/rrc_ue.cc b/srsenb/src/stack/rrc/rrc_ue.cc index 4ad857ae5..d4a152339 100644 --- a/srsenb/src/stack/rrc/rrc_ue.cc +++ b/srsenb/src/stack/rrc/rrc_ue.cc @@ -52,7 +52,12 @@ rrc::ue::ue(rrc* outer_rrc, uint16_t rnti_, const sched_interface::ue_cfg_t& sch mac_ctrl(rnti, ue_cell_list, bearer_list, parent->cfg, parent->mac, *parent->cell_common_list, sched_ue_cfg) {} -rrc::ue::~ue() {} +rrc::ue::~ue() +{ + if (old_reest_rnti != SRSRAN_INVALID_RNTI and parent->users.count(old_reest_rnti) > 0) { + parent->rem_user_thread(old_reest_rnti); + } +} int rrc::ue::init() { @@ -71,12 +76,20 @@ int rrc::ue::init() set_activity_timeout(MSG3_RX_TIMEOUT); // next UE response is Msg3 // Set timeout to release UE context after RLF detection - uint32_t deadline_ms = parent->cfg.rlf_release_timer_ms; - auto timer_expire_func = [this](uint32_t tid) { rlf_timer_expired(tid); }; - phy_dl_rlf_timer.set(deadline_ms, timer_expire_func); - phy_ul_rlf_timer.set(deadline_ms, timer_expire_func); - rlc_rlf_timer.set(deadline_ms, timer_expire_func); - parent->logger.info("Setting RLF timer for rnti=0x%x to %dms", rnti, deadline_ms); + uint32_t deadline_ms = parent->cfg.rlf_release_timer_ms; + if (rnti != SRSRAN_MRNTI) { + auto timer_expire_func = [this](uint32_t tid) { rlf_timer_expired(tid); }; + phy_dl_rlf_timer.set(deadline_ms, timer_expire_func); + phy_ul_rlf_timer.set(deadline_ms, timer_expire_func); + rlc_rlf_timer.set(deadline_ms, timer_expire_func); + parent->logger.info("Setting RLF timer for rnti=0x%x to %dms", rnti, deadline_ms); + } else { + // in case of M-RNTI do not handle rlf timer expiration + auto timer_expire_func = [](uint32_t tid) {}; + phy_dl_rlf_timer.set(deadline_ms, timer_expire_func); + phy_ul_rlf_timer.set(deadline_ms, timer_expire_func); + rlc_rlf_timer.set(deadline_ms, timer_expire_func); + } mobility_handler = make_rnti_obj(rnti, this); return SRSRAN_SUCCESS; @@ -101,13 +114,22 @@ void rrc::ue::get_metrics(rrc_ue_metrics_t& ue_metrics) const } } -void rrc::ue::set_activity() +void rrc::ue::set_activity(bool enabled) { + if (rnti == SRSRAN_MRNTI) { + return; + } + if (not enabled) { + if (activity_timer.is_running()) { + parent->logger.debug("Inactivity timer interrupted for rnti=0x%x", rnti); + } + activity_timer.stop(); + return; + } + // re-start activity timer with current timeout value activity_timer.run(); - if (parent) { - parent->logger.debug("Activity registered for rnti=0x%x (timeout_value=%dms)", rnti, activity_timer.duration()); - } + parent->logger.debug("Activity registered for rnti=0x%x (timeout_value=%dms)", rnti, activity_timer.duration()); } void rrc::ue::set_radiolink_dl_state(bool crc_res) @@ -122,6 +144,7 @@ void rrc::ue::set_radiolink_dl_state(bool crc_res) parent->logger.info( "DL RLF timer stopped for rnti=0x%x (time elapsed=%dms)", rnti, phy_dl_rlf_timer.time_elapsed()); phy_dl_rlf_timer.stop(); + mac_ctrl.set_radio_bearer_state(sched_interface::ue_bearer_cfg_t::BOTH); } return; } @@ -136,7 +159,7 @@ void rrc::ue::set_radiolink_dl_state(bool crc_res) consecutive_kos_dl++; if (consecutive_kos_dl > parent->cfg.max_mac_dl_kos) { parent->logger.info("Max KOs in DL reached, starting RLF timer rnti=0x%x", rnti); - mac_ctrl.handle_max_retx(); + mac_ctrl.set_radio_bearer_state(sched_interface::ue_bearer_cfg_t::IDLE); phy_dl_rlf_timer.run(); } } @@ -153,10 +176,18 @@ void rrc::ue::set_radiolink_ul_state(bool crc_res) parent->logger.info( "UL RLF timer stopped for rnti=0x%x (time elapsed=%dms)", rnti, phy_ul_rlf_timer.time_elapsed()); phy_ul_rlf_timer.stop(); + mac_ctrl.set_radio_bearer_state(sched_interface::ue_bearer_cfg_t::BOTH); } return; } + if (mobility_handler->is_ho_running()) { + // Do not count UL KOs if handover is on-going. + // Source eNB should only rely in relocation timer + // Target eNB should either wait for UE to handover or explicit release by the MME + return; + } + // Count KOs in MAC and trigger release if it goes above a certain value. // This is done to detect out-of-coverage UEs if (phy_ul_rlf_timer.is_running()) { @@ -167,38 +198,36 @@ void rrc::ue::set_radiolink_ul_state(bool crc_res) consecutive_kos_ul++; if (consecutive_kos_ul > parent->cfg.max_mac_ul_kos) { parent->logger.info("Max KOs in UL reached, starting RLF timer rnti=0x%x", rnti); - mac_ctrl.handle_max_retx(); + mac_ctrl.set_radio_bearer_state(sched_interface::ue_bearer_cfg_t::IDLE); phy_ul_rlf_timer.run(); } } void rrc::ue::activity_timer_expired(const activity_timeout_type_t type) { - if (parent) { - parent->logger.info("Activity timer for rnti=0x%x expired after %d ms", rnti, activity_timer.time_elapsed()); + parent->logger.info("Activity timer for rnti=0x%x expired after %d ms", rnti, activity_timer.time_elapsed()); - if (parent->s1ap->user_exists(rnti)) { - if (type == UE_INACTIVITY_TIMEOUT) { - if (not parent->s1ap->user_release(rnti, asn1::s1ap::cause_radio_network_opts::user_inactivity)) { - parent->rem_user_thread(rnti); - } + if (parent->s1ap->user_exists(rnti)) { + switch (type) { + case UE_INACTIVITY_TIMEOUT: + parent->s1ap->user_release(rnti, asn1::s1ap::cause_radio_network_opts::user_inactivity); con_release_result = procedure_result_code::activity_timeout; - } else if (type == MSG3_RX_TIMEOUT) { + break; + case MSG3_RX_TIMEOUT: + case MSG5_RX_TIMEOUT: // MSG3 timeout, no need to notify S1AP, just remove UE parent->rem_user_thread(rnti); con_release_result = procedure_result_code::msg3_timeout; - } else { + break; + default: // Unhandled activity timeout, just remove UE and log an error parent->rem_user_thread(rnti); con_release_result = procedure_result_code::activity_timeout; parent->logger.error( "Unhandled reason for activity timer expiration. rnti=0x%x, cause %d", rnti, static_cast(type)); - } - } else { - if (rnti != SRSRAN_MRNTI) { - parent->rem_user_thread(rnti); - } } + } else { + parent->rem_user_thread(rnti); } state = RRC_STATE_RELEASE_REQUEST; @@ -215,53 +244,56 @@ void rrc::ue::rlf_timer_expired(uint32_t timeout_id) parent->logger.info("RLC RLF timer for rnti=0x%x expired after %d ms", rnti, rlc_rlf_timer.time_elapsed()); } - if (parent->s1ap->user_release(rnti, asn1::s1ap::cause_radio_network_opts::radio_conn_with_ue_lost)) { - con_release_result = procedure_result_code::radio_conn_with_ue_lost; - phy_ul_rlf_timer.stop(); - phy_dl_rlf_timer.stop(); - rlc_rlf_timer.stop(); - } else { - if (rnti != SRSRAN_MRNTI) { - parent->rem_user(rnti); - } - } - + phy_ul_rlf_timer.stop(); + phy_dl_rlf_timer.stop(); + rlc_rlf_timer.stop(); state = RRC_STATE_RELEASE_REQUEST; + + parent->s1ap->user_release(rnti, asn1::s1ap::cause_radio_network_opts::radio_conn_with_ue_lost); + con_release_result = procedure_result_code::radio_conn_with_ue_lost; } void rrc::ue::max_rlc_retx_reached() { - if (parent) { - parent->logger.info("Max RLC retx reached for rnti=0x%x", rnti); + parent->logger.info("Max RLC retx reached for rnti=0x%x", rnti); - // Turn off DRB scheduling but give UE chance to start re-establishment - rlc_rlf_timer.run(); - mac_ctrl.handle_max_retx(); - } + // Turn off scheduling but give UE chance to start re-establishment + mac_ctrl.set_radio_bearer_state(sched_interface::ue_bearer_cfg_t::IDLE); + rlc_rlf_timer.run(); } -void rrc::ue::set_activity_timeout(const activity_timeout_type_t type) +void rrc::ue::protocol_failure() +{ + parent->logger.info("RLC protocol failure for rnti=0x%x", rnti); + + // Release UE immediately with appropiate cause + state = RRC_STATE_RELEASE_REQUEST; + + parent->s1ap->user_release(rnti, asn1::s1ap::cause_radio_network_opts::fail_in_radio_interface_proc); + con_release_result = procedure_result_code::fail_in_radio_interface_proc; +} + +void rrc::ue::set_activity_timeout(activity_timeout_type_t type) { - uint32_t deadline_s = 0; uint32_t deadline_ms = 0; switch (type) { case MSG3_RX_TIMEOUT: - deadline_s = 0; deadline_ms = static_cast( (get_ue_cc_cfg(UE_PCELL_CC_IDX)->sib2.rr_cfg_common.rach_cfg_common.max_harq_msg3_tx + 1) * 16); break; case UE_INACTIVITY_TIMEOUT: - deadline_s = parent->cfg.inactivity_timeout_ms / 1000; - deadline_ms = parent->cfg.inactivity_timeout_ms % 1000; + deadline_ms = parent->cfg.inactivity_timeout_ms; + break; + case MSG5_RX_TIMEOUT: + deadline_ms = get_ue_cc_cfg(UE_PCELL_CC_IDX)->sib2.ue_timers_and_consts.t301.to_number(); break; default: parent->logger.error("Unknown timeout type %d", type); } - uint32_t deadline = deadline_s * 1e3 + deadline_ms; - activity_timer.set(deadline, [this, type](uint32_t tid) { activity_timer_expired(type); }); - parent->logger.debug("Setting timer for %s for rnti=0x%x to %dms", to_string(type).c_str(), rnti, deadline); + activity_timer.set(deadline_ms, [this, type](uint32_t tid) { activity_timer_expired(type); }); + parent->logger.debug("Setting timer for %s for rnti=0x%x to %dms", to_string(type).c_str(), rnti, deadline_ms); set_activity(); } @@ -343,9 +375,7 @@ void rrc::ue::parse_ul_dcch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) break; case ul_dcch_msg_type_c::c1_c_::types::ue_cap_info: if (handle_ue_cap_info(&ul_dcch_msg.msg.c1().ue_cap_info())) { - parent->s1ap->ue_ctxt_setup_complete(rnti); send_connection_reconf(std::move(pdu)); - state = RRC_STATE_WAIT_FOR_CON_RECONF_COMPLETE; } else { send_connection_reject(procedure_result_code::none); state = RRC_STATE_IDLE; @@ -403,9 +433,7 @@ void rrc::ue::handle_rrc_con_req(rrc_conn_request_s* msg) if (user.first != rnti && user.second->has_tmsi && user.second->mmec == mmec && user.second->m_tmsi == m_tmsi) { parent->logger.info("RRC connection request: UE context already exists. M-TMSI=%d", m_tmsi); user.second->state = RRC_STATE_IDLE; // Set old rnti to IDLE so that enb doesn't send RRC Connection Release - if (not parent->s1ap->user_release(user.first, asn1::s1ap::cause_radio_network_opts::radio_conn_with_ue_lost)) { - parent->rem_user_thread(user.first); - } + parent->s1ap->user_release(user.first, asn1::s1ap::cause_radio_network_opts::interaction_with_other_proc); break; } } @@ -488,7 +516,6 @@ void rrc::ue::handle_rrc_con_setup_complete(rrc_conn_setup_complete_s* msg, srsr } else { parent->s1ap->initial_ue(rnti, enb_cc_idx, s1ap_cause, std::move(pdu)); } - state = RRC_STATE_WAIT_FOR_CON_RECONF_COMPLETE; // 2> if the UE has radio link failure or handover failure information available if (msg->crit_exts.type().value == c1_or_crit_ext_opts::c1 and @@ -534,9 +561,9 @@ void rrc::ue::handle_rrc_con_reest_req(rrc_conn_reest_request_s* msg) uint16_t old_rnti = req_r8.ue_id.c_rnti.to_number(); if (not parent->s1ap->is_mme_connected()) { - parent->logger.error("MME isn't connected. Sending Connection Reject"); + parent->logger.error("RRCReestablishmentReject for rnti=0x%x. Cause: MME not connected", rnti); send_connection_reest_rej(procedure_result_code::error_mme_not_connected); - srsran::console("User 0x%x RRC Reestablishment Request rejected\n", rnti); + srsran::console("RRCReestablishmentReject for rnti=0x%x. Cause: MME not connected.\n", rnti); return; } parent->logger.debug("rnti=0x%x, phyid=0x%x, smac=0x%x, cause=%s", @@ -547,9 +574,10 @@ void rrc::ue::handle_rrc_con_reest_req(rrc_conn_reest_request_s* msg) if (not is_idle()) { // The created RNTI has to receive ReestablishmentRequest as first message - parent->logger.error("Received ReestablishmentRequest from an rnti=0x%x not in IDLE", rnti); + parent->logger.error( + "RRCReestablishmentReject for rnti=0x%x. Cause: old rnti=0x%x is not in RRC_IDLE", rnti, old_rnti); send_connection_reest_rej(procedure_result_code::error_unknown_rnti); - srsran::console("ERROR: User 0x%x requesting Reestablishment is not in RRC_IDLE\n", rnti); + srsran::console("ERROR: RRCReestablishmentReject for rnti=0x%x not in RRC_IDLE\n", rnti); return; } @@ -560,10 +588,10 @@ void rrc::ue::handle_rrc_con_reest_req(rrc_conn_reest_request_s* msg) // Reject unrecognized rntis, and PCIs that do not belong to eNB if (old_ue_it == parent->users.end() or old_cell == nullptr or old_ue_it->second->ue_cell_list.get_enb_cc_idx(old_cell->enb_cc_idx) == nullptr) { - parent->logger.error("Received ConnectionReestablishment for rnti=0x%x without context", old_rnti); send_connection_reest_rej(procedure_result_code::error_unknown_rnti); - srsran::console( - "User 0x%x RRC Reestablishment Request rejected. Cause: no rnti=0x%x context available\n", rnti, old_rnti); + parent->logger.info( + "RRCReestablishmentReject for rnti=0x%x. Cause: no rnti=0x%x context available", rnti, old_rnti); + srsran::console("RRCReestablishmentReject for rnti=0x%x. Cause: no context available\n", rnti); return; } ue* old_ue = old_ue_it->second.get(); @@ -621,7 +649,7 @@ void rrc::ue::handle_rrc_con_reest_req(rrc_conn_reest_request_s* msg) old_reest_rnti = old_rnti; state = RRC_STATE_WAIT_FOR_CON_REEST_COMPLETE; - set_activity_timeout(UE_INACTIVITY_TIMEOUT); + set_activity_timeout(MSG5_RX_TIMEOUT); } void rrc::ue::send_connection_reest(uint8_t ncc) @@ -704,6 +732,7 @@ void rrc::ue::handle_rrc_con_reest_complete(rrc_conn_reest_complete_s* msg, srsr // remove old RNTI parent->rem_user(old_reest_rnti); + old_reest_rnti = SRSRAN_INVALID_RNTI; state = RRC_STATE_REESTABLISHMENT_COMPLETE; @@ -840,6 +869,9 @@ void rrc::ue::handle_rrc_reconf_complete(rrc_conn_recfg_complete_s* msg, srsran: rlf_info_pending = false; send_ue_info_req(); } + + // Many S1AP procedures end with RRC Reconfiguration. Notify S1AP accordingly. + parent->s1ap->notify_rrc_reconf_complete(rnti); } void rrc::ue::send_ue_info_req() diff --git a/srsenb/src/stack/s1ap/s1ap.cc b/srsenb/src/stack/s1ap/s1ap.cc index fd885bf49..a8aec4fa1 100644 --- a/srsenb/src/stack/s1ap/s1ap.cc +++ b/srsenb/src/stack/s1ap/s1ap.cc @@ -423,17 +423,14 @@ bool s1ap::user_release(uint16_t rnti, asn1::s1ap::cause_radio_network_e cause_r ue* u = users.find_ue_rnti(rnti); if (u == nullptr) { logger.warning("Released UE with rnti=0x%x not found", rnti); + rrc->release_ue(rnti); return false; } cause_c cause; cause.set_radio_network().value = cause_radio.value; - if (not u->send_uectxtreleaserequest(cause)) { - users.erase(u); - return false; - } - return true; + return u->send_uectxtreleaserequest(cause); } bool s1ap::user_exists(uint16_t rnti) @@ -464,6 +461,15 @@ void s1ap::ue_ctxt_setup_complete(uint16_t rnti) u->ue_ctxt_setup_complete(); } +void s1ap::notify_rrc_reconf_complete(uint16_t rnti) +{ + ue* u = users.find_ue_rnti(rnti); + if (u == nullptr) { + return; + } + u->notify_rrc_reconf_complete(); +} + bool s1ap::is_mme_connected() { return mme_connected; @@ -1407,16 +1413,28 @@ bool s1ap::ue::send_ulnastransport(srsran::unique_byte_buffer_t pdu) bool s1ap::ue::send_uectxtreleaserequest(const cause_c& cause) { - if (was_uectxtrelease_requested()) { - logger.warning("UE context for RNTI:0x%x is in zombie state. Releasing...", ctxt.rnti); - return false; - } if (not ctxt.mme_ue_s1ap_id.has_value()) { logger.error("Cannot send UE context release request without a MME-UE-S1AP-Id allocated."); + s1ap_ptr->rrc->release_ue(ctxt.rnti); + s1ap_ptr->users.erase(this); + return false; + } + + if (ts1_reloc_overall.is_running() and cause.type().value == asn1::s1ap::cause_c::types_opts::radio_network and + (cause.radio_network().value == asn1::s1ap::cause_radio_network_opts::user_inactivity or + cause.radio_network().value == asn1::s1ap::cause_radio_network_opts::radio_conn_with_ue_lost)) { + logger.info("Ignoring UE context release request from lower layers for UE rnti=0x%x performing S1 Handover.", + ctxt.rnti); + // Leave the UE context alive during S1 Handover until ts1_reloc_overall expiry. Ignore releases due to + // UE inactivity or RLF + return false; + } + + if (was_uectxtrelease_requested()) { + // let timeout auto-remove user. return false; } - release_requested = true; s1ap_pdu_c tx_pdu; tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_UE_CONTEXT_RELEASE_REQUEST); ue_context_release_request_ies_container& container = @@ -1427,11 +1445,25 @@ bool s1ap::ue::send_uectxtreleaserequest(const cause_c& cause) // Cause container.cause.value = cause; - return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "UEContextReleaseRequest"); + release_requested = s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "UEContextReleaseRequest"); + if (not release_requested) { + s1ap_ptr->rrc->release_ue(ctxt.rnti); + s1ap_ptr->users.erase(this); + } else { + overall_procedure_timeout.set(10000, [this](uint32_t tid) { + logger.warning("UE context for RNTI:0x%x is in zombie state. Releasing...", ctxt.rnti); + s1ap_ptr->rrc->release_ue(ctxt.rnti); + s1ap_ptr->users.erase(this); + }); + overall_procedure_timeout.run(); + } + return release_requested; } bool s1ap::ue::send_uectxtreleasecomplete() { + overall_procedure_timeout.stop(); + s1ap_pdu_c tx_pdu; tx_pdu.set_successful_outcome().load_info_obj(ASN1_S1AP_ID_UE_CONTEXT_RELEASE); auto& container = tx_pdu.successful_outcome().value.ue_context_release_complete().protocol_ies; @@ -1447,6 +1479,17 @@ bool s1ap::ue::send_uectxtreleasecomplete() return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "UEContextReleaseComplete"); } +void s1ap::ue::notify_rrc_reconf_complete() +{ + if (current_state == s1ap_elem_procs_o::init_msg_c::types_opts::init_context_setup_request) { + logger.info("Procedure %s,rnti=0x%x - Received RRC reconf complete. Finishing UE context setup.", + s1ap_elem_procs_o::init_msg_c::types_opts{current_state}.to_string(), + ctxt.rnti); + ue_ctxt_setup_complete(); + return; + } +} + void s1ap::ue::ue_ctxt_setup_complete() { if (current_state != s1ap_elem_procs_o::init_msg_c::types_opts::init_context_setup_request) { @@ -1465,7 +1508,14 @@ void s1ap::ue::ue_ctxt_setup_complete() container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); - container.cause.value = failed_cfg_erabs.front().cause; + if (not failed_cfg_erabs.empty()) { + container.cause.value = failed_cfg_erabs.front().cause; + } else { + logger.warning("Procedure %s,rnti=0x%x - no specified cause for failed configuration", + s1ap_elem_procs_o::init_msg_c::types_opts{current_state}.to_string(), + ctxt.rnti); + container.cause.value.set_misc().value = cause_misc_opts::unspecified; + } s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "UEContextModificationFailure"); return; } @@ -1496,7 +1546,7 @@ void s1ap::ue::ue_ctxt_setup_complete() // Log event. event_logger::get().log_s1_ctx_create(ctxt.enb_cc_idx, ctxt.mme_ue_s1ap_id.value(), ctxt.enb_ue_s1ap_id, ctxt.rnti); - s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "E-RABSetupResponse"); + s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "InitialContextSetupResponse"); } bool s1ap::ue::send_erab_setup_response(const erab_id_list& erabs_setup, const erab_item_list& erabs_failed) @@ -1802,7 +1852,6 @@ void s1ap::user_list::erase(ue* ue_ptr) /******************************************************************************* /* General helpers ********************************************************************************/ - bool s1ap::sctp_send_s1ap_pdu(const asn1::s1ap::s1ap_pdu_c& tx_pdu, uint32_t rnti, const char* procedure_name) { if (not mme_connected and rnti != SRSRAN_INVALID_RNTI) { @@ -1810,6 +1859,19 @@ bool s1ap::sctp_send_s1ap_pdu(const asn1::s1ap::s1ap_pdu_c& tx_pdu, uint32_t rnt return false; } + // Reset the state if it is a successful or unsucessfull message + if (tx_pdu.type() == s1ap_pdu_c::types_opts::successful_outcome || + tx_pdu.type() == s1ap_pdu_c::types_opts::unsuccessful_outcome) { + if (rnti != SRSRAN_INVALID_RNTI) { + s1ap::ue* u = users.find_ue_rnti(rnti); + if (u == nullptr) { + logger.warning("Could not find user for %s. RNTI=%x", procedure_name, rnti); + } else { + u->set_state(s1ap_proc_id_t::nulltype, {}, {}); + } + } + } + srsran::unique_byte_buffer_t buf = srsran::make_byte_buffer(); if (buf == nullptr) { logger.error("Fatal Error: Couldn't allocate buffer for %s.", procedure_name); @@ -1961,6 +2023,8 @@ s1ap::ue::ue(s1ap* s1ap_ptr_) : s1ap_ptr(s1ap_ptr_), ho_prep_proc(this), logger( // TS1RELOCOverall, the eNB shall request the MME to release the UE context. s1ap_ptr->user_release(ctxt.rnti, asn1::s1ap::cause_radio_network_opts::ts1relocoverall_expiry); }); + overall_procedure_timeout = s1ap_ptr->task_sched.get_unique_timer(); + overall_procedure_timeout.set(10000); } bool s1ap::ue::send_ho_required(uint32_t target_eci, @@ -2094,7 +2158,7 @@ bool s1ap::ue::send_enb_status_transfer_proc(std::vector& be void s1ap::log_s1ap_msg(const asn1::s1ap::s1ap_pdu_c& msg, srsran::const_span sdu, bool is_rx) { - std::string msg_type; + const char* msg_type; switch (msg.type().value) { case s1ap_pdu_c::types_opts::init_msg: @@ -2111,7 +2175,7 @@ void s1ap::log_s1ap_msg(const asn1::s1ap::s1ap_pdu_c& msg, srsran::const_span to_remove; for (lcid_tunnel& bearer : new_rnti_obj) { tunnels[bearer.teid].rnti = new_rnti; + if (tunnels[bearer.teid].state == tunnel_state::forward_to) { + // Remove forwarding path + tunnels[bearer.teid].state = tunnel_state::pdcp_active; + tunnels[bearer.teid].fwd_tunnel = nullptr; + } else if (tunnels[bearer.teid].state == tunnel_state::forwarded_from) { + to_remove.push_back(bearer.teid); + } + } + while (not to_remove.empty()) { + remove_tunnel(to_remove.back()); + to_remove.pop_back(); } // Leave old_rnti as zombie to be removed later diff --git a/srsenb/src/stack/upper/rlc.cc b/srsenb/src/stack/upper/rlc.cc index a6edfd5d5..fa50e2194 100644 --- a/srsenb/src/stack/upper/rlc.cc +++ b/srsenb/src/stack/upper/rlc.cc @@ -264,6 +264,11 @@ void rlc::user_interface::max_retx_attempted() rrc->max_retx_attempted(rnti); } +void rlc::user_interface::protocol_failure() +{ + rrc->protocol_failure(rnti); +} + void rlc::user_interface::write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) { if (lcid == srb_to_lcid(lte_srb::srb0)) { diff --git a/srsenb/src/stack/upper/rlc_nr.cc b/srsenb/src/stack/upper/rlc_nr.cc index bc9f5506f..46e6514c4 100644 --- a/srsenb/src/stack/upper/rlc_nr.cc +++ b/srsenb/src/stack/upper/rlc_nr.cc @@ -186,6 +186,11 @@ void rlc_nr::user_interface::max_retx_attempted() m_rrc->max_retx_attempted(rnti); } +void rlc_nr::user_interface::protocol_failure() +{ + m_rrc->protocol_failure(rnti); +} + void rlc_nr::user_interface::write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) { if (lcid == (int)srsran::nr_srb::srb0) { diff --git a/srsenb/test/common/dummy_classes.h b/srsenb/test/common/dummy_classes.h index 023ad1a7a..dd6f219f9 100644 --- a/srsenb/test/common/dummy_classes.h +++ b/srsenb/test/common/dummy_classes.h @@ -37,7 +37,6 @@ class mac_dummy : public mac_interface_rrc { public: int cell_cfg(const std::vector& cell_cfg) override { return 0; } - void reset() override {} int ue_cfg(uint16_t rnti, sched_interface::ue_cfg_t* cfg) override { return 0; } int ue_rem(uint16_t rnti) override { return 0; } int ue_set_crnti(uint16_t temp_crnti, uint16_t crnti, sched_interface::ue_cfg_t* cfg) override { return 0; } @@ -113,7 +112,7 @@ public: void write_pdu(uint16_t rnti, srsran::unique_byte_buffer_t pdu) override {} bool user_exists(uint16_t rnti) override { return true; } bool user_release(uint16_t rnti, asn1::s1ap::cause_radio_network_e cause_radio) override { return true; } - void ue_ctxt_setup_complete(uint16_t rnti) override {} + void notify_rrc_reconf_complete(uint16_t rnti) override {} bool is_mme_connected() override { return true; } bool send_ho_required(uint16_t rnti, uint32_t target_eci, diff --git a/srsenb/test/mac/sched_benchmark.cc b/srsenb/test/mac/sched_benchmark.cc index c0e43f50b..46743f467 100644 --- a/srsenb/test/mac/sched_benchmark.cc +++ b/srsenb/test/mac/sched_benchmark.cc @@ -255,7 +255,7 @@ run_data expected_run_result(run_params params) break; default: ret.avg_dl_throughput *= 0.96; - ret.avg_ul_throughput *= 0.85; + ret.avg_ul_throughput *= 0.84; break; } return ret; diff --git a/srsenb/test/mac/sched_common_test_suite.cc b/srsenb/test/mac/sched_common_test_suite.cc index 9200b40a6..3205cca32 100644 --- a/srsenb/test/mac/sched_common_test_suite.cc +++ b/srsenb/test/mac/sched_common_test_suite.cc @@ -226,7 +226,7 @@ int test_pdcch_collisions(const sf_output_res_t& sf_out, // Helper Function: checks if there is any collision. If not, fills the PDCCH mask auto try_cce_fill = [&](const srsran_dci_location_t& dci_loc, const char* ch) { uint32_t cce_start = dci_loc.ncce, cce_stop = dci_loc.ncce + (1u << dci_loc.L); - CONDERROR(dci_loc.L == 0, "The aggregation level %d is not valid", dci_loc.L); + CONDERROR(dci_loc.L > 3, "The aggregation level %d is not valid", dci_loc.L); CONDERROR( cce_start >= ncce or cce_stop > ncce, "The CCE positions (%u, %u) do not fit in PDCCH", cce_start, cce_stop); CONDERROR( @@ -275,7 +275,9 @@ int test_dci_content_common(const sf_output_res_t& sf_out, uint32_t enb_cc_idx) CONDERROR(pusch.tbs == 0, "Allocated PUSCH with invalid TBS=%d", pusch.tbs); CONDERROR(alloc_rntis.count(rnti) > 0, "The user rnti=0x%x got allocated multiple times in UL", rnti); alloc_rntis.insert(pusch.dci.rnti); - CONDERROR(not((pusch.current_tx_nb == 0) xor (pusch.dci.tb.rv != 0)), "Number of txs incorrectly set"); + CONDERROR(not(((pusch.current_tx_nb % 4) == 0) xor (pusch.dci.tb.rv != 0)), + "[rnti=0x%x] Number of txs incorrectly set", + rnti); if (not pusch.needs_pdcch) { // In case of non-adaptive retx or Msg3 continue; diff --git a/srsenb/test/mac/sched_sim_ue.cc b/srsenb/test/mac/sched_sim_ue.cc index 193d6a6bd..92d9ced7d 100644 --- a/srsenb/test/mac/sched_sim_ue.cc +++ b/srsenb/test/mac/sched_sim_ue.cc @@ -115,35 +115,44 @@ void ue_sim::update_ul_harqs(const sf_output_res_t& sf_out) { uint32_t pid = to_tx_ul(sf_out.tti_rx).to_uint() % (FDD_HARQ_DELAY_UL_MS + FDD_HARQ_DELAY_DL_MS); for (uint32_t cc = 0; cc < sf_out.cc_params.size(); ++cc) { + const auto *cc_cfg = ctxt.get_cc_cfg(cc), *start = &ctxt.ue_cfg.supported_cc_list[0]; + if (cc_cfg == nullptr) { + continue; + } + uint32_t ue_cc_idx = std::distance(start, cc_cfg); + auto& ue_cc_ctxt = ctxt.cc_list[ue_cc_idx]; + auto& h = ue_cc_ctxt.ul_harqs[pid]; + // Update UL harqs with PHICH info + bool found_phich = false; + bool is_msg3 = h.nof_txs == h.nof_retxs + 1 and ctxt.msg3_tti_rx.is_valid() and h.first_tti_rx == ctxt.msg3_tti_rx; + uint32_t max_retxs = is_msg3 ? sf_out.cc_params[0].cfg.maxharq_msg3tx : ctxt.ue_cfg.maxharq_tx; + bool last_retx = h.nof_retxs + 1 >= max_retxs; for (uint32_t i = 0; i < sf_out.ul_cc_result[cc].phich.size(); ++i) { const auto& phich = sf_out.ul_cc_result[cc].phich[i]; if (phich.rnti != ctxt.rnti) { continue; } - - const auto *cc_cfg = ctxt.get_cc_cfg(cc), *start = &ctxt.ue_cfg.supported_cc_list[0]; - uint32_t ue_cc_idx = std::distance(start, cc_cfg); - auto& ue_cc_ctxt = ctxt.cc_list[ue_cc_idx]; - auto& h = ue_cc_ctxt.ul_harqs[pid]; + found_phich = true; bool is_ack = phich.phich == phich_t::ACK; - bool is_msg3 = - h.nof_txs == h.nof_retxs + 1 and ctxt.msg3_tti_rx.is_valid() and h.first_tti_rx == ctxt.msg3_tti_rx; - bool last_retx = h.nof_retxs + 1 >= (is_msg3 ? sf_out.cc_params[0].cfg.maxharq_msg3tx : ctxt.ue_cfg.maxharq_tx); if (is_ack or last_retx) { h.active = false; } } + if (not found_phich and h.active) { + // There can be missing PHICH due to measGap collisions. In such case, we deactivate the harq and assume hi=1 + h.active = false; + } // Update UL harqs with PUSCH grants + bool pusch_found = false; for (uint32_t i = 0; i < sf_out.ul_cc_result[cc].pusch.size(); ++i) { const auto& data = sf_out.ul_cc_result[cc].pusch[i]; if (data.dci.rnti != ctxt.rnti) { continue; } - auto& ue_cc_ctxt = ctxt.cc_list[data.dci.ue_cc_idx]; - auto& h = ue_cc_ctxt.ul_harqs[to_tx_ul(sf_out.tti_rx).to_uint() % ue_cc_ctxt.ul_harqs.size()]; + pusch_found = true; if (h.nof_txs == 0 or h.ndi != data.dci.tb.ndi) { // newtx @@ -159,6 +168,11 @@ void ue_sim::update_ul_harqs(const sf_output_res_t& sf_out) h.riv = data.dci.type2_alloc.riv; h.nof_txs++; } + if (not pusch_found and h.nof_retxs < max_retxs) { + // PUSCH *may* be skipped due to measGap. nof_retxs keeps getting incremented + h.nof_retxs++; + h.nof_txs++; + } } } diff --git a/srsenb/test/mac/sched_sim_ue.h b/srsenb/test/mac/sched_sim_ue.h index 9b7b2c82b..409b6d9ec 100644 --- a/srsenb/test/mac/sched_sim_ue.h +++ b/srsenb/test/mac/sched_sim_ue.h @@ -35,7 +35,7 @@ struct ue_harq_ctxt_t { bool ndi = false; uint32_t pid = 0; uint32_t nof_txs = 0; - uint32_t nof_retxs = 0; + uint32_t nof_retxs = std::numeric_limits::max(); uint32_t riv = 0; srsran_dci_location_t dci_loc = {}; uint32_t tbs = 0; diff --git a/srsenb/test/mac/sched_test_rand.cc b/srsenb/test/mac/sched_test_rand.cc index 466354d8a..c755c45ce 100644 --- a/srsenb/test/mac/sched_test_rand.cc +++ b/srsenb/test/mac/sched_test_rand.cc @@ -283,6 +283,7 @@ sched_sim_events rand_sim_params(uint32_t nof_ttis) sim_gen.sim_args.cell_cfg[0].target_pucch_ul_sinr = pick_random_uniform({10, 15, 20, -1}); sim_gen.sim_args.cell_cfg[0].target_pusch_ul_sinr = pick_random_uniform({10, 15, 20, -1}); sim_gen.sim_args.cell_cfg[0].enable_phr_handling = false; + sim_gen.sim_args.cell_cfg[0].min_phr_thres = 0; sim_gen.sim_args.default_ue_sim_cfg.ue_cfg = generate_default_ue_cfg(); sim_gen.sim_args.default_ue_sim_cfg.periodic_cqi = true; sim_gen.sim_args.default_ue_sim_cfg.ue_cfg.maxharq_tx = std::uniform_int_distribution<>{1, 5}(srsenb::get_rand_gen()); @@ -294,7 +295,7 @@ sched_sim_events rand_sim_params(uint32_t nof_ttis) sim_gen.sim_args.default_ue_sim_cfg.prob_ul_ack_mask.back() = 1; sim_gen.sim_args.default_ue_sim_cfg.ue_cfg.measgap_period = pick_random_uniform({0, 40, 80}); sim_gen.sim_args.default_ue_sim_cfg.ue_cfg.measgap_offset = std::uniform_int_distribution{ - 0, sim_gen.sim_args.default_ue_sim_cfg.ue_cfg.measgap_period}(srsenb::get_rand_gen()); + 0, std::max(sim_gen.sim_args.default_ue_sim_cfg.ue_cfg.measgap_period, 1u) - 1}(srsenb::get_rand_gen()); sim_gen.sim_args.default_ue_sim_cfg.ue_cfg.pucch_cfg.n_pucch_sr = std::uniform_int_distribution{0, 2047}(srsenb::get_rand_gen()); @@ -303,6 +304,7 @@ sched_sim_events rand_sim_params(uint32_t nof_ttis) boolean_dist() ? -1 : std::uniform_int_distribution<>{0, 24}(srsenb::get_rand_gen()); sim_gen.sim_args.sched_args.pusch_mcs = boolean_dist() ? -1 : std::uniform_int_distribution<>{0, 24}(srsenb::get_rand_gen()); + sim_gen.sim_args.sched_args.min_aggr_level = std::uniform_int_distribution<>{0, 3}(srsenb::get_rand_gen()); generator.tti_events.resize(nof_ttis); diff --git a/srsenb/test/mac/sched_test_utils.h b/srsenb/test/mac/sched_test_utils.h index 2b7a25e19..d4b7da168 100644 --- a/srsenb/test/mac/sched_test_utils.h +++ b/srsenb/test/mac/sched_test_utils.h @@ -61,6 +61,8 @@ inline srsenb::sched_interface::cell_cfg_t generate_default_cell_cfg(uint32_t no cell_cfg.initial_dl_cqi = 6; cell_cfg.target_pusch_ul_sinr = -1; cell_cfg.target_pucch_ul_sinr = -1; + cell_cfg.enable_phr_handling = false; + cell_cfg.min_phr_thres = 0; cell_cfg.nrb_cqi = 1; cell_cfg.n1pucch_an = 12; cell_cfg.delta_pucch_shift = 1; diff --git a/srsenb/test/mac/sched_tpc_test.cc b/srsenb/test/mac/sched_tpc_test.cc index 1ce863351..6c9221928 100644 --- a/srsenb/test/mac/sched_tpc_test.cc +++ b/srsenb/test/mac/sched_tpc_test.cc @@ -35,9 +35,14 @@ int test_finite_target_snr() const uint32_t nof_prbs = 50; const int target_snr = 15; - tpc tpcfsm(nof_prbs, 15, 15, true); + tpc tpcfsm(0x46, nof_prbs, 15, 15, true); - // TEST: While no SNR info is provided, no TPC commands are sent + // TEST: While UL SNR ~ target, no TPC commands are sent + for (uint32_t i = 0; i < 100 and tpcfsm.get_ul_snr_estim(0) < 14; ++i) { + tpcfsm.set_snr(15, 0); + tpcfsm.set_snr(15, 1); + tpcfsm.new_tti(); + } for (uint32_t i = 0; i < 100; ++i) { tpcfsm.new_tti(); TESTASSERT(decode_tpc(tpcfsm.encode_pucch_tpc()) == 0); @@ -75,6 +80,25 @@ int test_finite_target_snr() TESTASSERT(sum_pucch > 0 and sum_pucch <= -snr_diff); } + // TEST: PHR is negative. Checks: + // - one TPC should be sent to decrease power. No more TPCs != 0 should be sent until the next PHR + snr_diff = -10; + tpcfsm.set_snr(target_snr + snr_diff, tpc::PUSCH_CODE); + tpcfsm.set_snr(target_snr + snr_diff, tpc::PUCCH_CODE); + for (uint32_t i = 0; i < 3; ++i) { + tpcfsm.set_phr(-2, 1); + tpcfsm.new_tti(); + TESTASSERT(decode_tpc(tpcfsm.encode_pusch_tpc()) == -1); + TESTASSERT(decode_tpc(tpcfsm.encode_pucch_tpc()) == 3); // PUCCH doesnt get affected by neg PHR + for (uint32_t j = 0; j < 100; ++j) { + tpcfsm.new_tti(); + TESTASSERT(decode_tpc(tpcfsm.encode_pusch_tpc()) == 0); + } + } + tpcfsm.set_phr(20, 1); + tpcfsm.new_tti(); + TESTASSERT(decode_tpc(tpcfsm.encode_pusch_tpc()) == 3); + return SRSRAN_SUCCESS; } @@ -82,7 +106,7 @@ int test_undefined_target_snr() { const uint32_t nof_prbs = 50; - tpc tpcfsm(nof_prbs, -1, -1, true); + tpc tpcfsm(0x46, nof_prbs, -1, -1, true); TESTASSERT(tpcfsm.max_ul_prbs() == 50); // TEST: While the PHR is not updated, a limited number of TPC commands should be sent @@ -111,7 +135,7 @@ int test_undefined_target_snr() // TEST: Check that high PHR allows full utilization of available PRBs, TPC remains at zero (no target SINR) int phr = 30; - tpcfsm.set_phr(phr); + tpcfsm.set_phr(phr, 1); TESTASSERT(tpcfsm.max_ul_prbs() == 50); sum_pusch = 0; sum_pucch = 0; @@ -124,7 +148,7 @@ int test_undefined_target_snr() // TEST: PHR is too low to allow all PRBs to be allocated. This event should not affect TPC commands phr = 5; - tpcfsm.set_phr(phr); + tpcfsm.set_phr(phr, 1); TESTASSERT(tpcfsm.max_ul_prbs() < 50); for (uint32_t i = 0; i < 100; ++i) { tpcfsm.new_tti(); @@ -134,7 +158,7 @@ int test_undefined_target_snr() // TEST: PHR is negative. The TPC should slightly decrease Tx UL power until next PHR phr = -1; - tpcfsm.set_phr(phr); + tpcfsm.set_phr(phr, 1); TESTASSERT(tpcfsm.max_ul_prbs() == tpc::PHR_NEG_NOF_PRB); sum_pusch = 0; sum_pucch = 0; diff --git a/srsenb/test/mac/sched_ue_cell_test.cc b/srsenb/test/mac/sched_ue_cell_test.cc index 366c53b24..89b7f90da 100644 --- a/srsenb/test/mac/sched_ue_cell_test.cc +++ b/srsenb/test/mac/sched_ue_cell_test.cc @@ -37,6 +37,7 @@ void test_neg_phr_scenario() sched_interface::cell_cfg_t cell_cfg = generate_default_cell_cfg(50); cell_cfg.target_pucch_ul_sinr = 20; cell_cfg.target_pusch_ul_sinr = 20; + cell_cfg.min_phr_thres = 0; cell_cfg.enable_phr_handling = true; sched_interface::sched_args_t sched_cfg = {}; sched_cell_params_t cell_params; @@ -46,16 +47,18 @@ void test_neg_phr_scenario() sched_ue_cell ue_cc(0x46, cell_params, tti_point(0)); ue_cc.set_ue_cfg(ue_cfg); - float snr = 0; - ue_cc.tpc_fsm.set_snr(snr, 0); - ue_cc.tpc_fsm.set_snr(snr, 1); - ue_cc.ul_cqi = srsran_cqi_from_snr(snr); - ue_cc.tpc_fsm.set_phr(-5); - ue_cc.new_tti(tti_point(0)); + float snr = 20; + tti_point tti{0}; + for (; ue_cc.tpc_fsm.get_ul_snr_estim(0) < snr - 2; ++tti) { + ue_cc.set_ul_snr(tti, snr, 0); + ue_cc.set_ul_snr(tti, snr, 1); + ue_cc.tpc_fsm.set_phr(-5, 1); + ue_cc.new_tti(tti); + } uint32_t req_bytes = 10000; uint32_t pending_prbs = get_required_prb_ul(ue_cc, req_bytes); - TESTASSERT(pending_prbs < 10); // The PHR<0 is limiting the number of allocated PRBs + TESTASSERT(pending_prbs == 1); // The PHR<0 is limiting the number of allocated PRBs uint32_t N_srs = 0; uint32_t prb_grant_size = pending_prbs; diff --git a/srsenb/test/mac/sched_ue_ded_test_suite.cc b/srsenb/test/mac/sched_ue_ded_test_suite.cc index 60f40e695..5c30a957c 100644 --- a/srsenb/test/mac/sched_ue_ded_test_suite.cc +++ b/srsenb/test/mac/sched_ue_ded_test_suite.cc @@ -50,6 +50,13 @@ int sim_ue_ctxt_t::enb_to_ue_cc_idx(uint32_t enb_cc_idx) const return it == ue_cfg.supported_cc_list.end() ? -1 : std::distance(ue_cfg.supported_cc_list.begin(), it); } +const phich_t* find_phich_grant(uint16_t rnti, const sched_interface::ul_sched_res_t& ul_cc_res) +{ + const phich_t* phich_ptr = std::find_if( + ul_cc_res.phich.begin(), ul_cc_res.phich.end(), [rnti](const phich_t& phich) { return phich.rnti == rnti; }); + return phich_ptr == ul_cc_res.phich.end() ? nullptr : phich_ptr; +} + const pusch_t* find_pusch_grant(uint16_t rnti, const sched_interface::ul_sched_res_t& ul_cc_res) { const pusch_t* ptr = std::find_if( @@ -138,6 +145,22 @@ int test_dl_sched_result(const sim_enb_ctxt_t& enb_ctxt, const sf_output_res_t& return SRSRAN_SUCCESS; } +bool is_in_measgap(srsran::tti_point tti, uint32_t period, uint32_t offset) +{ + if (period == 0) { + return false; + } + uint32_t T = period / 10; + for (uint32_t i = 0; i < 6; ++i) { + tti_point tti_gap_start = tti - i; + bool is_gap_start = (tti_gap_start.sfn() % T == offset / 10) and (tti_gap_start.sf_idx() == offset % 10); + if (is_gap_start) { + return true; + } + } + return false; +} + int test_ul_sched_result(const sim_enb_ctxt_t& enb_ctxt, const sf_output_res_t& sf_out) { uint32_t pid = to_tx_ul(sf_out.tti_rx).to_uint() % (FDD_HARQ_DELAY_UL_MS + FDD_HARQ_DELAY_DL_MS); @@ -165,9 +188,7 @@ int test_ul_sched_result(const sim_enb_ctxt_t& enb_ctxt, const sf_output_res_t& uint16_t rnti = ue.rnti; int ue_cc_idx = ue.enb_to_ue_cc_idx(cc); - const phich_t* phich_ptr = - std::find_if(phich_begin, phich_end, [rnti](const phich_t& phich) { return phich.rnti == rnti; }); - phich_ptr = phich_ptr == phich_end ? nullptr : phich_ptr; + const phich_t* phich_ptr = find_phich_grant(rnti, sf_out.ul_cc_result[cc]); const pusch_t* pusch_ptr = find_pusch_grant(rnti, sf_out.ul_cc_result[cc]); // TEST: Check that idle CCs do not receive PUSCH grants or PHICH @@ -177,22 +198,32 @@ int test_ul_sched_result(const sim_enb_ctxt_t& enb_ctxt, const sf_output_res_t& continue; } - const auto& h = ue.cc_list[ue_cc_idx].ul_harqs[pid]; - bool phich_ack = phich_ptr != nullptr and phich_ptr->phich == phich_t::ACK; - bool is_msg3 = h.first_tti_rx == ue.msg3_tti_rx and h.nof_txs == h.nof_retxs + 1; - bool last_retx = h.nof_retxs + 1 >= (is_msg3 ? sf_out.cc_params[0].cfg.maxharq_msg3tx : ue.ue_cfg.maxharq_tx); - bool h_inactive = (not h.active) or (phich_ack or last_retx); - - // TEST: Already active UL HARQs have to receive PHICH - CONDERROR(h.active and phich_ptr == nullptr, "PHICH not received for rnti=0x%x active UL HARQ pid=%d", rnti, pid); + const auto& h = ue.cc_list[ue_cc_idx].ul_harqs[pid]; + bool phich_ack = phich_ptr != nullptr and phich_ptr->phich == phich_t::ACK; + bool is_msg3 = h.first_tti_rx == ue.msg3_tti_rx and h.nof_txs == h.nof_retxs + 1; + uint32_t max_nof_retxs = is_msg3 ? sf_out.cc_params[0].cfg.maxharq_msg3tx : ue.ue_cfg.maxharq_tx; + bool last_retx = h.nof_retxs + 1 >= max_nof_retxs; + tti_point tti_tx_phich = to_tx_dl(sf_out.tti_rx); + bool phich_in_meas_gap = is_in_measgap(tti_tx_phich, ue.ue_cfg.measgap_period, ue.ue_cfg.measgap_offset); + bool pusch_in_meas_gap = + is_in_measgap(to_tx_ul(sf_out.tti_rx), ue.ue_cfg.measgap_period, ue.ue_cfg.measgap_offset); + bool h_cleared = (not h.active) or (phich_ack or last_retx); + + // TEST: Already active UL HARQs have to receive PHICH (unless MeasGap collision) + CONDERROR(h.active and phich_ptr == nullptr and not phich_in_meas_gap, + "PHICH not received for rnti=0x%x active UL HARQ pid=%d", + rnti, + pid); CONDERROR(not h.active and phich_ptr != nullptr, "PHICH for rnti=0x%x corresponds to inactive UL HARQ pid=%d", rnti, pid); // TEST: absent PUSCH grants for active UL HARQs must be either ACKs, last retx, or interrupted HARQs - if ((phich_ptr != nullptr) and (pusch_ptr == nullptr)) { - CONDERROR(not h_inactive, "PHICH NACK received for rnti=0x%x but no PUSCH retx reallocated", rnti); + if (phich_ptr != nullptr) { + CONDERROR(not h_cleared and pusch_ptr == nullptr and not pusch_in_meas_gap, + "PHICH NACK received for rnti=0x%x but no PUSCH retx reallocated", + rnti); } if (pusch_ptr != nullptr) { @@ -205,11 +236,13 @@ int test_ul_sched_result(const sim_enb_ctxt_t& enb_ctxt, const sf_output_res_t& // newtx CONDERROR(nof_retx != 0, "Invalid rv index for new UL tx"); CONDERROR(pusch_ptr->current_tx_nb != 0, "UL HARQ retxs need to have been previously transmitted"); - CONDERROR(not h_inactive, "New tx for already active UL HARQ"); + CONDERROR(not h_cleared, "New tx for already active UL HARQ"); CONDERROR(not pusch_ptr->needs_pdcch and ue.msg3_tti_rx.is_valid() and sf_out.tti_rx > ue.msg3_tti_rx, "In case of newtx, PDCCH allocation is required, unless it is Msg3"); } else { CONDERROR(pusch_ptr->current_tx_nb == 0, "UL retx has to have nof tx > 0"); + CONDERROR(h.nof_retxs >= max_nof_retxs, "UL max nof retxs exceeded"); + CONDERROR(pusch_ptr->current_tx_nb != h.nof_retxs + 1, "UL HARQ nof_retx mismatch"); if (not h.active) { // the HARQ is being resumed. PDCCH must be active with the exception of Msg3 CONDERROR(ue.msg4_tti_rx.is_valid() and not pusch_ptr->needs_pdcch, @@ -222,7 +255,7 @@ int test_ul_sched_result(const sim_enb_ctxt_t& enb_ctxt, const sf_output_res_t& CONDERROR(pusch_ptr->dci.type2_alloc.riv != h.riv, "Non-adaptive retx must keep the same riv"); } } - CONDERROR(get_rvidx(h.nof_retxs + 1) != (uint32_t)pusch_ptr->dci.tb.rv, "Invalid rv index for retx"); + CONDERROR(get_rvidx(h.nof_retxs + 1) != (uint32_t)pusch_ptr->dci.tb.rv, "Invalid rv index for UL retx"); CONDERROR(h.tbs != pusch_ptr->tbs, "TBS changed during HARQ retx"); CONDERROR(to_tx_ul(h.last_tti_rx) > sf_out.tti_rx, "UL harq pid=%d was reused too soon", h.pid); } @@ -364,12 +397,6 @@ int test_ra(const sim_enb_ctxt_t& enb_ctxt, const sf_output_res_t& sf_out) return SRSRAN_SUCCESS; } -bool is_in_measgap(srsran::tti_point tti, uint32_t period, uint32_t offset) -{ - uint32_t T = period / 10; - return (tti.sfn() % T == offset / 10) and (tti.sf_idx() == offset % 10); -} - int test_meas_gaps(const sim_enb_ctxt_t& enb_ctxt, const sf_output_res_t& sf_out) { for (uint32_t cc = 0; cc < enb_ctxt.cell_params.size(); ++cc) { @@ -380,16 +407,19 @@ int test_meas_gaps(const sim_enb_ctxt_t& enb_ctxt, const sf_output_res_t& sf_out uint16_t rnti = ue.rnti; uint32_t ue_cc_idx = ue.enb_to_ue_cc_idx(cc); srsran::tti_point tti_tx_ul = to_tx_ul(sf_out.tti_rx), tti_tx_dl = to_tx_dl(sf_out.tti_rx), - tti_tx_dl_ack = to_tx_dl_ack(sf_out.tti_rx), tti_tx_phich = to_tx_ul_ack(sf_out.tti_rx); + tti_tx_dl_ack = to_tx_dl_ack(sf_out.tti_rx); if (ue_cc_idx != 0 or ue.ue_cfg.measgap_period == 0) { continue; } - if (is_in_measgap(tti_tx_ul, ue.ue_cfg.measgap_period, ue.ue_cfg.measgap_offset) or - is_in_measgap(tti_tx_phich, ue.ue_cfg.measgap_period, ue.ue_cfg.measgap_offset)) { + if (is_in_measgap(tti_tx_dl, ue.ue_cfg.measgap_period, ue.ue_cfg.measgap_offset)) { + const phich_t* phich_ptr = find_phich_grant(rnti, ul_cc_res); + CONDERROR(phich_ptr != nullptr, "PHICH grants cannot fall in UE measGap"); + } + if (is_in_measgap(tti_tx_ul, ue.ue_cfg.measgap_period, ue.ue_cfg.measgap_offset)) { const pusch_t* pusch_ptr = find_pusch_grant(rnti, ul_cc_res); - CONDERROR(pusch_ptr != nullptr, "PUSCH grants and PHICH cannot fall in UE measGap"); + CONDERROR(pusch_ptr != nullptr, "PUSCH grants cannot fall in UE measGap"); } if (is_in_measgap(tti_tx_dl, ue.ue_cfg.measgap_period, ue.ue_cfg.measgap_offset) or is_in_measgap(tti_tx_dl_ack, ue.ue_cfg.measgap_period, ue.ue_cfg.measgap_offset)) { diff --git a/srsenb/test/phy/enb_phy_test.cc b/srsenb/test/phy/enb_phy_test.cc index 6e34d81f6..c8f203346 100644 --- a/srsenb/test/phy/enb_phy_test.cc +++ b/srsenb/test/phy/enb_phy_test.cc @@ -83,7 +83,7 @@ private: std::vector ringbuffers_rx; srsran::rf_timestamp_t ts_rx = {}; double rx_srate = 0.0; - bool running = true; + std::atomic running = {true}; CALLBACK(tx); CALLBACK(tx_end); @@ -327,6 +327,7 @@ private: uint32_t cqi; } tti_cqi_info_t; + std::mutex phy_mac_mutex; std::queue tti_dl_info_sched_queue; std::queue tti_dl_info_ack_queue; std::queue tti_ul_info_sched_queue; @@ -414,10 +415,16 @@ public: srsran_random_free(random_gen); } - void set_active_cell_list(std::vector& active_cell_list_) { active_cell_list = active_cell_list_; } + void set_active_cell_list(std::vector& active_cell_list_) + { + std::lock_guard lock(phy_mac_mutex); + active_cell_list = active_cell_list_; + } int sr_detected(uint32_t tti, uint16_t rnti) override { + std::lock_guard lock(phy_mac_mutex); + tti_sr_info_t tti_sr_info = {}; tti_sr_info.tti = tti; tti_sr_info_queue.push(tti_sr_info); @@ -450,6 +457,8 @@ public: } int cqi_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t cqi_value) override { + std::lock_guard lock(phy_mac_mutex); + tti_cqi_info_t tti_cqi_info = {}; tti_cqi_info.tti = tti; tti_cqi_info.cc_idx = cc_idx; @@ -475,6 +484,8 @@ public: } int ack_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t tb_idx, bool ack) override { + std::lock_guard lock(phy_mac_mutex); + // Push grant info in queue tti_dl_info_t tti_dl_info = {}; tti_dl_info.tti = tti; @@ -489,6 +500,8 @@ public: } int crc_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t nof_bytes, bool crc_res) override { + std::lock_guard lock(phy_mac_mutex); + // Push grant info in queue tti_ul_info_t tti_ul_info = {}; tti_ul_info.tti = tti; @@ -501,7 +514,8 @@ public: return 0; } - int push_pdu(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t nof_bytes, bool crc_res) override + int push_pdu(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t nof_bytes, bool crc_res, uint32_t grant_nof_prbs) + override { logger.info("Received push_pdu tti=%d; rnti=0x%x; ack=%d;", tti, rnti, crc_res); notify_push_pdu(); @@ -510,6 +524,8 @@ public: } int get_dl_sched(uint32_t tti, dl_sched_list_t& dl_sched_res) override { + std::lock_guard lock(phy_mac_mutex); + // Notify test engine notify_get_dl_sched(); @@ -622,6 +638,8 @@ public: // Notify test engine notify_get_ul_sched(); + std::lock_guard lock(phy_mac_mutex); + // Iterate for each carrier following the eNb/Cell order for (uint32_t cc_idx = 0; cc_idx < ul_sched_res.size(); cc_idx++) { auto scell_idx = active_cell_list.size(); @@ -697,6 +715,8 @@ public: void tti_clock() override { notify_tti_clock(); } int run_tti(bool enable_assert) { + std::lock_guard lock(phy_mac_mutex); + // Check DL ACKs match with grants while (not tti_dl_info_ack_queue.empty()) { // Get both Info diff --git a/srsenb/test/rrc/CMakeLists.txt b/srsenb/test/rrc/CMakeLists.txt index ef63adc1c..ecccbdc23 100644 --- a/srsenb/test/rrc/CMakeLists.txt +++ b/srsenb/test/rrc/CMakeLists.txt @@ -18,7 +18,22 @@ # and at http://www.gnu.org/licenses/. # +add_library(test_helpers test_helpers.cc) +target_link_libraries(test_helpers srsenb_rrc srsenb_common rrc_asn1 s1ap_asn1 srsran_common enb_cfg_parser ${LIBCONFIGPP_LIBRARIES}) + add_executable(rrc_nr_test rrc_nr_test.cc) -target_link_libraries(rrc_nr_test srsgnb_rrc srsran_common rrc_nr_asn1) +target_link_libraries(rrc_nr_test srsgnb_rrc srsran_common rrc_nr_asn1 ${ATOMIC_LIBS}) add_test(rrc_nr_test rrc_nr_test) +add_executable(rrc_meascfg_test rrc_meascfg_test.cc) +target_link_libraries(rrc_meascfg_test test_helpers ${ATOMIC_LIBS}) + +add_executable(erab_setup_test erab_setup_test.cc) +target_link_libraries(erab_setup_test test_helpers ${LIBCONFIGPP_LIBRARIES} ${ATOMIC_LIBS}) + +add_executable(rrc_mobility_test rrc_mobility_test.cc) +target_link_libraries(rrc_mobility_test srsran_asn1 test_helpers ${ATOMIC_LIBS}) + +add_test(rrc_mobility_test rrc_mobility_test -i ${CMAKE_CURRENT_SOURCE_DIR}/../..) +add_test(erab_setup_test erab_setup_test -i ${CMAKE_CURRENT_SOURCE_DIR}/../..) +add_test(rrc_meascfg_test rrc_meascfg_test -i ${CMAKE_CURRENT_SOURCE_DIR}/../..) diff --git a/srsenb/test/upper/erab_setup_test.cc b/srsenb/test/rrc/erab_setup_test.cc similarity index 99% rename from srsenb/test/upper/erab_setup_test.cc rename to srsenb/test/rrc/erab_setup_test.cc index 3d3a22df9..b43b64d74 100644 --- a/srsenb/test/upper/erab_setup_test.cc +++ b/srsenb/test/rrc/erab_setup_test.cc @@ -21,9 +21,9 @@ #include "srsenb/hdr/enb.h" #include "srsenb/src/enb_cfg_parser.h" +#include "srsenb/test/rrc/test_helpers.h" #include "srsran/asn1/rrc_utils.h" #include "srsran/common/test_common.h" -#include "test_helpers.h" #include int test_erab_setup(srsran::log_sink_spy& spy, bool qci_exists) diff --git a/srsenb/test/upper/rrc_meascfg_test.cc b/srsenb/test/rrc/rrc_meascfg_test.cc similarity index 100% rename from srsenb/test/upper/rrc_meascfg_test.cc rename to srsenb/test/rrc/rrc_meascfg_test.cc diff --git a/srsenb/test/upper/rrc_mobility_test.cc b/srsenb/test/rrc/rrc_mobility_test.cc similarity index 100% rename from srsenb/test/upper/rrc_mobility_test.cc rename to srsenb/test/rrc/rrc_mobility_test.cc diff --git a/srsenb/test/upper/test_helpers.cc b/srsenb/test/rrc/test_helpers.cc similarity index 100% rename from srsenb/test/upper/test_helpers.cc rename to srsenb/test/rrc/test_helpers.cc diff --git a/srsenb/test/upper/test_helpers.h b/srsenb/test/rrc/test_helpers.h similarity index 100% rename from srsenb/test/upper/test_helpers.h rename to srsenb/test/rrc/test_helpers.h diff --git a/srsenb/test/s1ap/s1ap_test.cc b/srsenb/test/s1ap/s1ap_test.cc index 85647868e..b5fc247be 100644 --- a/srsenb/test/s1ap/s1ap_test.cc +++ b/srsenb/test/s1ap/s1ap_test.cc @@ -96,7 +96,7 @@ struct dummy_socket_manager : public srsran::socket_manager_itf { return true; } - int s1u_fd; + int s1u_fd = -1; recv_callback_t callback; }; @@ -194,7 +194,7 @@ void add_rnti(s1ap& s1ap_obj, mme_dummy& mme) 0x40, 0x0a, 0x0a, 0x1f, 0x7f, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01}; cbref = asn1::cbit_ref(icsresp, sizeof(icsresp)); TESTASSERT(s1ap_pdu.unpack(cbref) == SRSRAN_SUCCESS); - s1ap_obj.ue_ctxt_setup_complete(0x46); + s1ap_obj.notify_rrc_reconf_complete(0x46); sdu = mme.read_msg(); TESTASSERT(sdu->N_bytes > 0); cbref = asn1::cbit_ref{sdu->msg, sdu->N_bytes}; @@ -259,9 +259,10 @@ void test_s1ap_erab_setup(test_event event) erab_ptr->erab_level_qos_params.alloc_retention_prio.pre_emption_vulnerability.value = asn1::s1ap::pre_emption_vulnerability_opts::not_pre_emptable; erab_ptr->nas_pdu.resize(1); - erab_list[1] = erab_list[0]; - erab_ptr = &erab_list[1].value.erab_to_be_modified_item_bearer_mod_req(); - erab_ptr->erab_id = event == test_event::repeated_erabid_mod ? 5 : 6; + erab_ptr->nas_pdu[0] = 0; + erab_list[1] = erab_list[0]; + erab_ptr = &erab_list[1].value.erab_to_be_modified_item_bearer_mod_req(); + erab_ptr->erab_id = event == test_event::repeated_erabid_mod ? 5 : 6; if (event == test_event::wrong_erabid_mod) { rrc.next_erabs_failed_to_modify.push_back(6); } @@ -337,4 +338,4 @@ int main(int argc, char** argv) test_s1ap_erab_setup(test_event::wrong_erabid_mod); test_s1ap_erab_setup(test_event::wrong_mme_s1ap_id); test_s1ap_erab_setup(test_event::repeated_erabid_mod); -} \ No newline at end of file +} diff --git a/srsenb/test/upper/CMakeLists.txt b/srsenb/test/upper/CMakeLists.txt index 1ec2e9cb9..91ecd3ffa 100644 --- a/srsenb/test/upper/CMakeLists.txt +++ b/srsenb/test/upper/CMakeLists.txt @@ -18,28 +18,13 @@ # and at http://www.gnu.org/licenses/. # -add_library(test_helpers test_helpers.cc) -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) target_link_libraries(plmn_test rrc_asn1) -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 test_helpers ${LIBCONFIGPP_LIBRARIES}) - -add_executable(rrc_meascfg_test rrc_meascfg_test.cc) -target_link_libraries(rrc_meascfg_test test_helpers) - add_executable(gtpu_test gtpu_test.cc) target_link_libraries(gtpu_test srsran_common s1ap_asn1 srsenb_upper srsran_upper ${SCTP_LIBRARIES}) -add_test(rrc_mobility_test rrc_mobility_test -i ${CMAKE_CURRENT_SOURCE_DIR}/../..) -add_test(erab_setup_test erab_setup_test -i ${CMAKE_CURRENT_SOURCE_DIR}/../..) -add_test(rrc_meascfg_test rrc_meascfg_test -i ${CMAKE_CURRENT_SOURCE_DIR}/../..) add_test(plmn_test plmn_test) add_test(gtpu_test gtpu_test) diff --git a/srsue/hdr/metrics_stdout.h b/srsue/hdr/metrics_stdout.h index f2370e09f..00848750b 100644 --- a/srsue/hdr/metrics_stdout.h +++ b/srsue/hdr/metrics_stdout.h @@ -59,7 +59,7 @@ private: std::string float_to_eng_string(float f, int digits); void print_table(const bool display_neighbours, const bool is_nr); - bool do_print = false; + std::atomic do_print = {false}; bool table_has_neighbours = false; ///< state of last table head uint8_t n_reports = 10; ue_metrics_interface* ue = nullptr; diff --git a/srsue/hdr/phy/lte/cc_worker.h b/srsue/hdr/phy/lte/cc_worker.h index 482f50be5..cc57d0895 100644 --- a/srsue/hdr/phy/lte/cc_worker.h +++ b/srsue/hdr/phy/lte/cc_worker.h @@ -49,9 +49,8 @@ public: void reset_cell_unlocked(); bool set_cell_unlocked(srsran_cell_t cell_); void set_tdd_config_unlocked(srsran_tdd_config_t config); - void set_config_unlocked(srsran::phy_cfg_t& phy_cfg); - void upd_config_dci_unlocked(srsran_dci_cfg_t& dci_cfg); - void enable_pregen_signals_unlocked(bool enabled); + void set_config_unlocked(const srsran::phy_cfg_t& phy_cfg); + void upd_config_dci_unlocked(const srsran_dci_cfg_t& dci_cfg); void set_uci_periodic_cqi(srsran_uci_data_t* uci_data); @@ -102,7 +101,6 @@ private: srsran_ul_sf_cfg_t sf_cfg_ul = {}; uint32_t cc_idx = 0; - bool pregen_enabled = false; bool cell_initiated = false; cf_t* signal_buffer_rx[SRSRAN_MAX_PORTS] = {}; cf_t* signal_buffer_tx[SRSRAN_MAX_PORTS] = {}; diff --git a/srsue/hdr/phy/lte/sf_worker.h b/srsue/hdr/phy/lte/sf_worker.h index 232fc99ec..98c0b7d8f 100644 --- a/srsue/hdr/phy/lte/sf_worker.h +++ b/srsue/hdr/phy/lte/sf_worker.h @@ -57,9 +57,7 @@ public: void set_cfo_unlocked(const uint32_t& cc_idx, float cfo); void set_tdd_config_unlocked(srsran_tdd_config_t config); - void set_config_unlocked(uint32_t cc_idx, srsran::phy_cfg_t phy_cfg); - void set_crnti_unlocked(uint16_t rnti); - void enable_pregen_signals_unlocked(bool enabled); + void set_config_unlocked(uint32_t cc_idx, const srsran::phy_cfg_t& phy_cfg); ///< Methods for plotting called from GUI thread int read_ce_abs(float* ce_abs, uint32_t tx_antenna, uint32_t rx_antenna); diff --git a/srsue/hdr/phy/lte/worker_pool.h b/srsue/hdr/phy/lte/worker_pool.h index 7a35d3110..bee88afbe 100644 --- a/srsue/hdr/phy/lte/worker_pool.h +++ b/srsue/hdr/phy/lte/worker_pool.h @@ -30,9 +30,25 @@ namespace lte { class worker_pool { +private: srsran::thread_pool pool; std::vector > workers; + class phy_cfg_stash_t + { + private: + std::vector pending; ///< Indicates for each SF worker if it has pending configuration + srsran::phy_cfg_t cfg; ///< Actual CC configuration + + public: + phy_cfg_stash_t(uint32_t max_workers) : pending(max_workers) {} + void set_cfg(const srsran::phy_cfg_t& c); + bool is_pending(uint32_t sf_idx); + const srsran::phy_cfg_t& get_cfg(uint32_t sf_idx); + }; + std::mutex phy_cfg_mutex; ///< Protects configuration stash + std::array phy_cfg_stash; ///< Stores the latest worker configuration + public: sf_worker* operator[](std::size_t pos) { return workers.at(pos).get(); } @@ -42,6 +58,14 @@ public: sf_worker* wait_worker_id(uint32_t id); void start_worker(sf_worker* w); void stop(); + + /** + * @brief Sets a new configuration for a given CC, it copies the new configuration into the stash and it will be + * applied to the sf_worker at the time it is reserved. + * @param cc_idx CC index + * @param phy_cfg Actual PHY configuration + */ + void set_config(uint32_t cc_idx, const srsran::phy_cfg_t& phy_cfg); }; } // namespace lte diff --git a/srsue/hdr/phy/phy.h b/srsue/hdr/phy/phy.h index a1f5bf1fc..30f8533df 100644 --- a/srsue/hdr/phy/phy.h +++ b/srsue/hdr/phy/phy.h @@ -103,8 +103,6 @@ public: void get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* m) final; void srsran_phy_logger(phy_logger_level_t log_level, char* str); - void enable_pregen_signals(bool enable) final; - void radio_overflow() final; void radio_failure() final; diff --git a/srsue/hdr/phy/phy_common.h b/srsue/hdr/phy/phy_common.h index 7e7bbc9df..6b9ebfeab 100644 --- a/srsue/hdr/phy/phy_common.h +++ b/srsue/hdr/phy/phy_common.h @@ -78,6 +78,9 @@ public: // Time Aligment Controller, internal thread safe ta_control ta; + // Last reported RI + std::atomic last_ri = {0}; + phy_common(srslog::basic_logger& logger); ~phy_common(); diff --git a/srsue/hdr/phy/prach.h b/srsue/hdr/phy/prach.h index 084ee6ecd..5aa1776a9 100644 --- a/srsue/hdr/phy/prach.h +++ b/srsue/hdr/phy/prach.h @@ -27,6 +27,7 @@ #include "srsran/srslog/srslog.h" #include "srsran/srsran.h" #include +#include namespace srsue { @@ -79,6 +80,7 @@ private: bool mem_initiated = false; bool cell_initiated = false; std::bitset buffer_bitmask; + mutable std::mutex mutex; }; } // namespace srsue diff --git a/srsue/hdr/phy/scell/intra_measure_base.h b/srsue/hdr/phy/scell/intra_measure_base.h index 0fc9ddc92..1e7220da1 100644 --- a/srsue/hdr/phy/scell/intra_measure_base.h +++ b/srsue/hdr/phy/scell/intra_measure_base.h @@ -197,7 +197,11 @@ private: * @brief Get the internal state * @return protected state */ - state_t get_state() const { return state; } + state_t get_state() + { + std::lock_guard lock(mutex); + return state; + } /** * @brief Transitions to a different state, all transitions are allowed except from quit @@ -230,7 +234,7 @@ private: /** * @brief Computes the measurement trigger based on TTI and the last TTI trigger */ - bool receive_tti_trigger(uint32_t tti) const + bool receive_tti_trigger(uint32_t tti) { // If the elapsed time does not satisfy with the minimum time, do not trigger uint32_t elapsed_tti = TTI_SUB(tti, last_measure_tti); diff --git a/srsue/hdr/phy/scell/intra_measure_lte.h b/srsue/hdr/phy/scell/intra_measure_lte.h index 3751d4fe8..523afeda5 100644 --- a/srsue/hdr/phy/scell/intra_measure_lte.h +++ b/srsue/hdr/phy/scell/intra_measure_lte.h @@ -81,8 +81,8 @@ private: bool measure_rat(measure_context_t context, std::vector& buffer) override; srslog::basic_logger& logger; - srsran_cell_t serving_cell = {}; ///< Current serving cell in the EARFCN, to avoid reporting it - uint32_t current_earfcn; ///< Current EARFCN + srsran_cell_t serving_cell = {}; ///< Current serving cell in the EARFCN, to avoid reporting it + uint32_t current_earfcn = 0; ///< Current EARFCN /// LTE-based measuring objects scell_recv scell_rx; ///< Secondary cell searcher diff --git a/srsue/hdr/phy/scell/scell_state.h b/srsue/hdr/phy/scell/scell_state.h index a31650b86..b9df6b61c 100644 --- a/srsue/hdr/phy/scell/scell_state.h +++ b/srsue/hdr/phy/scell/scell_state.h @@ -109,7 +109,6 @@ public: std::unique_lock lock(mutex); switch (activation_state) { - case idle: // waiting for receiving a command, do nothing break; @@ -123,7 +122,6 @@ public: case transition: // Detect when the TTI has increased enough to make sure there arent workers, set the configuration if (TTI_SUB(tti, activation_tti) >= activation_margin_tti) { - // Reload cell states for (uint32_t i = 1; i < SRSRAN_MAX_CARRIERS; i++) { // Get Activation command value @@ -158,7 +156,6 @@ public: bool is_active(uint32_t cc_idx, uint32_t tti) const { - if (cc_idx == 0) { return true; } @@ -180,7 +177,6 @@ public: bool is_configured(uint32_t cc_idx) const { - if (cc_idx == 0) { return true; } @@ -194,6 +190,22 @@ public: return scell_cfg[cc_idx].status != cfg::none; } + void reset(uint32_t cc_idx) + { + if (cc_idx == 0 or cc_idx >= SRSRAN_MAX_CARRIERS) { + return; + } + + std::unique_lock lock(mutex); + + activation_state = idle; + + cfg& e = scell_cfg[cc_idx]; + e.status = cfg::none; + e.earfcn = 0; + e.pci = UINT32_MAX; + } + void reset() { std::unique_lock lock(mutex); diff --git a/srsue/hdr/phy/sync.h b/srsue/hdr/phy/sync.h index 084134a85..3f69a240a 100644 --- a/srsue/hdr/phy/sync.h +++ b/srsue/hdr/phy/sync.h @@ -198,7 +198,7 @@ private: bool set_frequency(); bool set_cell(float cfo); - bool running = false; + std::atomic running = {false}; bool is_overflow = false; srsran::rf_timestamp_t last_rx_time; @@ -238,11 +238,14 @@ private: srsran::rf_buffer_t dummy_buffer; // Sync metrics + std::atomic sfo = {}; // SFO estimate updated after each sync-cycle + std::atomic cfo = {}; // CFO estimate updated after each sync-cycle + std::atomic ref_cfo = {}; // provided adjustment value applied before sync sync_metrics_t metrics = {}; // in-sync / out-of-sync counters - uint32_t out_of_sync_cnt = 0; - uint32_t in_sync_cnt = 0; + std::atomic out_of_sync_cnt = {0}; + std::atomic in_sync_cnt = {0}; std::mutex rrc_mutex; enum { @@ -277,10 +280,10 @@ private: float dl_freq = -1; float ul_freq = -1; - const static int MIN_TTI_JUMP = 1; // Time gap reported to stack after receiving subframe - const static int MAX_TTI_JUMP = 1000; // Maximum time gap tolerance in RF stream metadata - - const uint8_t SYNC_CC_IDX = 0; ///< From the sync POV, the CC idx is always the first + const static int MIN_TTI_JUMP = 1; ///< Time gap reported to stack after receiving subframe + const static int MAX_TTI_JUMP = 1000; ///< Maximum time gap tolerance in RF stream metadata + const uint8_t SYNC_CC_IDX = 0; ///< From the sync POV, the CC idx is always the first + const uint32_t TIMEOUT_TO_IDLE_MS = 2; ///< Timeout in milliseconds for transitioning to IDLE }; } // namespace srsue diff --git a/srsue/hdr/phy/sync_state.h b/srsue/hdr/phy/sync_state.h index ee3b9e4fe..7b8deb129 100644 --- a/srsue/hdr/phy/sync_state.h +++ b/srsue/hdr/phy/sync_state.h @@ -39,7 +39,7 @@ public: */ state_t run_state() { - std::lock_guard lock(inside); + std::lock_guard lock(mutex); cur_state = next_state; if (state_setting) { state_setting = false; @@ -52,7 +52,7 @@ public: // Called by the main thread at the end of each state to indicate it has finished. void state_exit(bool exit_ok = true) { - std::lock_guard lock(inside); + std::lock_guard lock(mutex); if (cur_state == SFN_SYNC && exit_ok == true) { next_state = CAMPING; } else { @@ -63,7 +63,7 @@ public: } void force_sfn_sync() { - std::lock_guard lock(inside); + std::lock_guard lock(mutex); next_state = SFN_SYNC; } @@ -74,20 +74,17 @@ public: */ void go_idle() { - std::lock_guard lock(outside); // Do not wait when transitioning to IDLE to avoid blocking - go_state_nowait(IDLE); + next_state = IDLE; } void run_cell_search() { - std::lock_guard lock(outside); go_state(CELL_SEARCH); wait_state_run(); wait_state_next(); } void run_sfn_sync() { - std::lock_guard lock(outside); go_state(SFN_SYNC); wait_state_run(); wait_state_next(); @@ -96,9 +93,36 @@ public: /* Helpers below this */ bool is_idle() { return cur_state == IDLE; } bool is_camping() { return cur_state == CAMPING; } + bool wait_idle(uint32_t timeout_ms) + { + std::unique_lock lock(mutex); + + // Avoid wasting time if the next state will not be IDLE + if (next_state != IDLE) { + return cur_state == IDLE; + } + + // Calculate timeout + std::chrono::system_clock::time_point expire_time = std::chrono::system_clock::now(); + expire_time += std::chrono::milliseconds(timeout_ms); + + // Wait until the state is IDLE + while (cur_state != IDLE) { + std::cv_status cv_status = cvar.wait_until(lock, expire_time); + + // Return if it timeouts + if (cv_status != std::cv_status::timeout) { + return cur_state == IDLE; + } + } + + // Return true if the state is IDLE + return true; + } const char* to_string() { + std::lock_guard lock(mutex); switch (cur_state) { case IDLE: return "IDLE"; @@ -118,7 +142,7 @@ public: private: void go_state(state_t s) { - std::unique_lock ul(inside); + std::unique_lock ul(mutex); next_state = s; state_setting = true; while (state_setting) { @@ -128,7 +152,7 @@ private: void go_state_nowait(state_t s) { - std::unique_lock ul(inside); + std::unique_lock ul(mutex); next_state = s; state_setting = true; } @@ -136,14 +160,14 @@ private: /* Waits until there is a call to set_state() and then run_state(). Returns when run_state() returns */ void wait_state_run() { - std::unique_lock ul(inside); + std::unique_lock ul(mutex); while (state_running) { cvar.wait(ul); } } void wait_state_next() { - std::unique_lock ul(inside); + std::unique_lock ul(mutex); while (cur_state != next_state) { cvar.wait(ul); } @@ -151,10 +175,9 @@ private: bool state_running = false; bool state_setting = false; - state_t cur_state = IDLE; - state_t next_state = IDLE; - std::mutex inside; - std::mutex outside; + std::atomic next_state = {IDLE}; // can be updated from outside (i.e. other thread) + state_t cur_state = IDLE; // will only be accessed when holding the mutex + std::mutex mutex; std::condition_variable cvar; }; diff --git a/srsue/hdr/stack/mac/demux.h b/srsue/hdr/stack/mac/demux.h index 3f3ba93c6..c5c213b93 100644 --- a/srsue/hdr/stack/mac/demux.h +++ b/srsue/hdr/stack/mac/demux.h @@ -65,7 +65,7 @@ public: bool get_uecrid_successful(); - void process_pdu(uint8_t* pdu, uint32_t nof_bytes, srsran::pdu_queue::channel_t channel); + void process_pdu(uint8_t* pdu, uint32_t nof_bytes, srsran::pdu_queue::channel_t channel, int ul_nof_prbs); void mch_start_rx(uint32_t lcid); private: diff --git a/srsue/hdr/stack/mac/dl_harq.h b/srsue/hdr/stack/mac/dl_harq.h index 1611863be..ebc48c7b2 100644 --- a/srsue/hdr/stack/mac/dl_harq.h +++ b/srsue/hdr/stack/mac/dl_harq.h @@ -72,7 +72,7 @@ private: ~dl_tb_process(); bool init(int pid, dl_harq_entity* parent, uint32_t tb_idx); - void reset(bool lock = true); + void reset(); void reset_ndi(); void new_grant_dl(mac_interface_phy_lte::mac_grant_dl_t grant, mac_interface_phy_lte::tb_action_dl_t* action); @@ -82,6 +82,9 @@ private: // Determine if it's a new transmission 5.3.2.2 bool calc_is_new_transmission(mac_interface_phy_lte::mac_grant_dl_t grant); + // Internal function to reset process, caller must hold the mutex + void reset_unsafe(); + std::mutex mutex; bool is_initiated; diff --git a/srsue/hdr/stack/rrc/rrc.h b/srsue/hdr/stack/rrc/rrc.h index 594967d1e..7a584307b 100644 --- a/srsue/hdr/stack/rrc/rrc.h +++ b/srsue/hdr/stack/rrc/rrc.h @@ -291,6 +291,7 @@ private: class si_acquire_proc; class serving_cell_config_proc; class cell_selection_proc; + class connection_setup_proc; class connection_request_proc; class connection_reconf_no_ho_proc; class plmn_search_proc; @@ -306,6 +307,7 @@ private: srsran::proc_t idle_setter; srsran::proc_t pcch_processor; srsran::proc_t conn_req_proc; + srsran::proc_t conn_setup_proc; srsran::proc_t plmn_searcher; srsran::proc_t cell_reselector; srsran::proc_t connection_reest; @@ -325,6 +327,7 @@ private: // RLC interface void max_retx_attempted(); + void protocol_failure(); // RRC NR interface void nr_scg_failure_information(const srsran::scg_failure_cause_t cause); diff --git a/srsue/hdr/stack/rrc/rrc_nr.h b/srsue/hdr/stack/rrc/rrc_nr.h index 61dd31d08..c0afb6c6e 100644 --- a/srsue/hdr/stack/rrc/rrc_nr.h +++ b/srsue/hdr/stack/rrc/rrc_nr.h @@ -110,6 +110,7 @@ public: // RLC interface void max_retx_attempted() final; + void protocol_failure() final; // MAC interface void run_tti(uint32_t tti) final; diff --git a/srsue/hdr/stack/rrc/rrc_procedures.h b/srsue/hdr/stack/rrc/rrc_procedures.h index ad8c9cd2d..efbc925a4 100644 --- a/srsue/hdr/stack/rrc/rrc_procedures.h +++ b/srsue/hdr/stack/rrc/rrc_procedures.h @@ -202,6 +202,24 @@ private: srsran::proc_future_t serv_cfg_fut; }; +class rrc::connection_setup_proc +{ +public: + explicit connection_setup_proc(rrc* parent_); + srsran::proc_outcome_t init(const asn1::rrc::rr_cfg_ded_s* cnfg_, srsran::unique_byte_buffer_t dedicated_info_nas_); + srsran::proc_outcome_t step() { return srsran::proc_outcome_t::yield; } + void then(const srsran::proc_state_t& result); + srsran::proc_outcome_t react(const bool& config_complete); + static const char* name() { return "Connection Setup"; } + +private: + // const + rrc* rrc_ptr; + srslog::basic_logger& logger; + // args + srsran::unique_byte_buffer_t dedicated_info_nas; +}; + class rrc::connection_reconf_no_ho_proc { public: diff --git a/srsue/src/CMakeLists.txt b/srsue/src/CMakeLists.txt index 0a4ddec4a..02adf600a 100644 --- a/srsue/src/CMakeLists.txt +++ b/srsue/src/CMakeLists.txt @@ -41,7 +41,8 @@ set(SRSRAN_SOURCES ${SRSRAN_SOURCES} rrc_nr_asn1 ngap_nr_asn1) target_link_libraries(srsue ${SRSUE_SOURCES} ${SRSRAN_SOURCES} ${CMAKE_THREAD_LIBS_INIT} - ${Boost_LIBRARIES}) + ${Boost_LIBRARIES} + ${ATOMIC_LIBS}) if (RPATH) set_target_properties(srsue PROPERTIES INSTALL_RPATH ".") diff --git a/srsue/src/phy/lte/cc_worker.cc b/srsue/src/phy/lte/cc_worker.cc index 7ee696e75..ccabe2bc7 100644 --- a/srsue/src/phy/lte/cc_worker.cc +++ b/srsue/src/phy/lte/cc_worker.cc @@ -196,11 +196,6 @@ void cc_worker::set_tdd_config_unlocked(srsran_tdd_config_t config) sf_cfg_ul.tdd_config = config; } -void cc_worker::enable_pregen_signals_unlocked(bool enabled) -{ - pregen_enabled = enabled; -} - /************ * * Downlink Functions @@ -821,10 +816,16 @@ uint32_t cc_worker::get_wideband_cqi() void cc_worker::set_uci_periodic_cqi(srsran_uci_data_t* uci_data) { + // Load last reported RI + ue_dl_cfg.last_ri = phy->last_ri; + srsran_ue_dl_gen_cqi_periodic(&ue_dl, &ue_dl_cfg, get_wideband_cqi(), CURRENT_TTI_TX, uci_data); // Store serving cell index for logging purposes uci_data->cfg.cqi.scell_index = cc_idx; + + // Store the reported RI + phy->last_ri = ue_dl_cfg.last_ri; } void cc_worker::set_uci_aperiodic_cqi(srsran_uci_data_t* uci_data) @@ -874,23 +875,16 @@ void cc_worker::set_uci_ack(srsran_uci_data_t* uci_data, /* Translates RRC structs into PHY structs */ -void cc_worker::set_config_unlocked(srsran::phy_cfg_t& phy_cfg) +void cc_worker::set_config_unlocked(const srsran::phy_cfg_t& phy_cfg) { // Save configuration ue_dl_cfg.cfg = phy_cfg.dl_cfg; ue_ul_cfg.ul_cfg = phy_cfg.ul_cfg; phy->set_pdsch_cfg(&ue_dl_cfg.cfg.pdsch); - - // Update signals - if (pregen_enabled) { - Info("Pre-generating UL signals..."); - srsran_ue_ul_pregen_signals(&ue_ul, &ue_ul_cfg); - Info("Done pre-generating signals worker..."); - } } -void cc_worker::upd_config_dci_unlocked(srsran_dci_cfg_t& dci_cfg) +void cc_worker::upd_config_dci_unlocked(const srsran_dci_cfg_t& dci_cfg) { ue_dl_cfg.cfg.dci = dci_cfg; } diff --git a/srsue/src/phy/lte/sf_worker.cc b/srsue/src/phy/lte/sf_worker.cc index 996ecf8ac..7ae722ea9 100644 --- a/srsue/src/phy/lte/sf_worker.cc +++ b/srsue/src/phy/lte/sf_worker.cc @@ -147,14 +147,7 @@ void sf_worker::set_tdd_config_unlocked(srsran_tdd_config_t config) tdd_config = config; } -void sf_worker::enable_pregen_signals_unlocked(bool enabled) -{ - for (auto& cc_worker : cc_workers) { - cc_worker->enable_pregen_signals_unlocked(enabled); - } -} - -void sf_worker::set_config_unlocked(uint32_t cc_idx, srsran::phy_cfg_t phy_cfg) +void sf_worker::set_config_unlocked(uint32_t cc_idx, const srsran::phy_cfg_t& phy_cfg) { if (cc_idx < cc_workers.size()) { cc_workers[cc_idx]->set_config_unlocked(phy_cfg); diff --git a/srsue/src/phy/lte/worker_pool.cc b/srsue/src/phy/lte/worker_pool.cc index 6787b9c94..0a3895bb8 100644 --- a/srsue/src/phy/lte/worker_pool.cc +++ b/srsue/src/phy/lte/worker_pool.cc @@ -23,7 +23,34 @@ namespace srsue { namespace lte { -worker_pool::worker_pool(uint32_t max_workers) : pool(max_workers) {} +void worker_pool::phy_cfg_stash_t::set_cfg(const srsran::phy_cfg_t& c) +{ + for (auto it = pending.begin(); it < pending.end(); it++) { + *it = true; + } + + cfg = c; +} + +bool worker_pool::phy_cfg_stash_t::is_pending(uint32_t sf_idx) +{ + if (sf_idx >= (uint32_t)pending.size()) { + return false; + } + return pending[sf_idx]; +} + +const srsran::phy_cfg_t& worker_pool::phy_cfg_stash_t::get_cfg(uint32_t sf_idx) +{ + if (sf_idx < (uint32_t)pending.size()) { + pending[sf_idx] = false; + } + return cfg; +} + +worker_pool::worker_pool(uint32_t max_workers) : + pool(max_workers), phy_cfg_stash{{max_workers, max_workers, max_workers, max_workers, max_workers}} +{} bool worker_pool::init(phy_common* common, int prio) { @@ -48,7 +75,23 @@ void worker_pool::start_worker(sf_worker* w) sf_worker* worker_pool::wait_worker(uint32_t tti) { - return (sf_worker*)pool.wait_worker(tti); + sf_worker* w = (sf_worker*)pool.wait_worker(tti); + if (w == nullptr) { + return w; + } + + // Protect configuration + std::unique_lock lock(phy_cfg_mutex); + + // Iterate all CC searching for a pending configuration + uint32_t worker_id = w->get_id(); + for (uint32_t cc_idx = 0; cc_idx < SRSRAN_MAX_CARRIERS; cc_idx++) { + if (phy_cfg_stash[cc_idx].is_pending(worker_id)) { + w->set_config_unlocked(cc_idx, phy_cfg_stash[cc_idx].get_cfg(worker_id)); + } + } + + return w; } sf_worker* worker_pool::wait_worker_id(uint32_t id) @@ -61,5 +104,16 @@ void worker_pool::stop() pool.stop(); } +void worker_pool::set_config(uint32_t cc_idx, const srsran::phy_cfg_t& phy_cfg) +{ + // Protect CC index bounds + if (cc_idx >= SRSRAN_MAX_CARRIERS) { + return; + } + + // Protect configuration + std::unique_lock lock(phy_cfg_mutex); + phy_cfg_stash[cc_idx].set_cfg(phy_cfg); +} }; // namespace lte }; // namespace srsue \ No newline at end of file diff --git a/srsue/src/phy/nr/cc_worker.cc b/srsue/src/phy/nr/cc_worker.cc index 98f4953de..b21d71f18 100644 --- a/srsue/src/phy/nr/cc_worker.cc +++ b/srsue/src/phy/nr/cc_worker.cc @@ -106,7 +106,6 @@ bool cc_worker::update_cfg() ssb_cfg.ssb_freq_hz = abs_freq_ssb_freq; ssb_cfg.scs = phy->cfg.ssb.scs; ssb_cfg.pattern = srsran::srsran_band_helper().get_ssb_pattern(band, phy->cfg.ssb.scs); - memcpy(ssb_cfg.position, phy->cfg.ssb.position_in_burst.data(), sizeof(bool) * SRSRAN_SSB_NOF_POSITION); ssb_cfg.duplex_mode = srsran::srsran_band_helper().get_duplex_mode(band); ssb_cfg.periodicity_ms = phy->cfg.ssb.periodicity_ms; @@ -343,31 +342,44 @@ bool cc_worker::measure_csi() if (srsran_ssb_send(&ssb, dl_slot_cfg.idx)) { srsran_csi_trs_measurements_t meas = {}; - if (srsran_ssb_csi_measure(&ssb, phy->cfg.carrier.pci, rx_buffer[0], &meas) < SRSRAN_SUCCESS) { - logger.error("Error measuring SSB"); - return false; - } + // Iterate all possible candidates + const std::array position_in_burst = phy->cfg.ssb.position_in_burst; + for (uint32_t ssb_idx = 0; ssb_idx < SRSRAN_SSB_NOF_CANDIDATES; ssb_idx++) { + // Skip SSB candidate if not enabled + if (not position_in_burst[ssb_idx]) { + continue; + } - if (logger.debug.enabled()) { - std::array str = {}; - srsran_csi_meas_info(&meas, str.data(), (uint32_t)str.size()); - logger.debug("SSB-CSI: %s", str.data()); - } + // Measure SSB candidate + if (srsran_ssb_csi_measure(&ssb, phy->cfg.carrier.pci, ssb_idx, rx_buffer[0], &meas) < SRSRAN_SUCCESS) { + logger.error("Error measuring SSB"); + return false; + } - // Compute channel metrics and push it - ch_metrics_t ch_metrics = {}; - ch_metrics.sinr = meas.snr_dB; - ch_metrics.rsrp = meas.rsrp_dB; - ch_metrics.rsrq = 0.0f; // Not supported - ch_metrics.rssi = 0.0f; // Not supported - ch_metrics.sync_err = - meas.delay_us / (float)(ue_dl.fft->fft_plan.size * SRSRAN_SUBC_SPACING_NR(phy->cfg.carrier.scs)); - phy->set_channel_metrics(ch_metrics); + if (logger.debug.enabled()) { + std::array str = {}; + srsran_csi_meas_info(&meas, str.data(), (uint32_t)str.size()); + logger.debug("SSB-CSI: %s", str.data()); + } - // Compute synch metrics and report it to the PHY state - sync_metrics_t sync_metrics = {}; - sync_metrics.cfo = meas.cfo_hz; - phy->set_sync_metrics(sync_metrics); + // Compute channel metrics and push it + ch_metrics_t ch_metrics = {}; + ch_metrics.sinr = meas.snr_dB; + ch_metrics.rsrp = meas.rsrp_dB; + ch_metrics.rsrq = 0.0f; // Not supported + ch_metrics.rssi = 0.0f; // Not supported + ch_metrics.sync_err = + meas.delay_us / (float)(ue_dl.fft->fft_plan.size * SRSRAN_SUBC_SPACING_NR(phy->cfg.carrier.scs)); + phy->set_channel_metrics(ch_metrics); + + // Compute synch metrics and report it to the PHY state + sync_metrics_t sync_metrics = {}; + sync_metrics.cfo = meas.cfo_hz; + phy->set_sync_metrics(sync_metrics); + + // Report SSB candidate channel measurement to the PHY state + // ... + } } // Iterate all NZP-CSI-RS marked as TRS and perform channel measurements diff --git a/srsue/src/phy/phy.cc b/srsue/src/phy/phy.cc index bf36b631f..3eae6e5b0 100644 --- a/srsue/src/phy/phy.cc +++ b/srsue/src/phy/phy.cc @@ -155,9 +155,6 @@ void phy::run_thread() sfsync.init( radio, stack, &prach_buffer, <e_workers, &nr_workers, &common, SF_RECV_THREAD_PRIO, args.sync_cpu_affinity); - // Disable UL signal pregeneration until the attachment - enable_pregen_signals(false); - is_configured = true; config_cond.notify_all(); } @@ -261,42 +258,47 @@ void phy::configure_prach_params() void phy::set_cells_to_meas(uint32_t earfcn, const std::set& pci) { - // Check if the EARFCN matches with serving cell - uint32_t pcell_earfcn = 0; - sfsync.get_current_cell(nullptr, &pcell_earfcn); - bool available = (pcell_earfcn == earfcn); - - // Find if there is secondary serving cell configured with the specified EARFCN - uint32_t cc_empty = 0; - for (uint32_t cc = 1; cc < args.nof_lte_carriers and not available; cc++) { - // If it is configured... - if (common.cell_state.is_configured(cc)) { - // ... Check if the EARFCN match - if (common.cell_state.get_earfcn(cc) == earfcn) { - available = true; + // As the SCell configuration is performed asynchronously through the cmd_worker, append the command adding the + // measurements to avoid a concurrency issue + cmd_worker.add_cmd([this, earfcn, pci]() { + // Check if the EARFCN matches with serving cell + uint32_t pcell_earfcn = 0; + sfsync.get_current_cell(nullptr, &pcell_earfcn); + bool available = (pcell_earfcn == earfcn); + + // Find if there is secondary serving cell configured with the specified EARFCN + uint32_t cc_empty = 0; + for (uint32_t cc = 1; cc < args.nof_lte_carriers and not available; cc++) { + // If it is configured... + if (common.cell_state.is_configured(cc)) { + // ... Check if the EARFCN match + if (common.cell_state.get_earfcn(cc) == earfcn) { + available = true; + } + } else if (cc_empty == 0) { + // ... otherwise, save the CC as non-configured + cc_empty = cc; } - } else if (cc_empty == 0) { - // ... otherwise, save the CC as non-configured - cc_empty = cc; } - } - // If not available and a non-configured carrier is available, configure it. - if (not available and cc_empty != 0) { - // Copy all attributes from serving cell - srsran_cell_t cell = selected_cell; + // If not available and a non-configured carrier is available, configure it. + if (not available and cc_empty != 0) { + // Copy all attributes from serving cell + srsran_cell_t cell = selected_cell; - // Select the first PCI in the list - if (not pci.empty()) { - cell.id = *pci.begin(); - } + // Select the first PCI in the list + if (not pci.empty()) { + cell.id = *pci.begin(); + } - // Configure a the empty carrier as it was CA - set_scell(cell, cc_empty, earfcn); - } + // Configure a the empty carrier as it was CA + logger_phy.info("Setting new SCell measurement cc_idx=%d, earfcn=%d, pci=%d...", cc_empty, earfcn, cell.id); + set_scell(cell, cc_empty, earfcn); + } - // Finally, set the serving cell measure - sfsync.set_cells_to_meas(earfcn, pci); + // Finally, set the serving cell measure + sfsync.set_cells_to_meas(earfcn, pci); + }); } void phy::meas_stop() @@ -396,6 +398,13 @@ void phy::reset() Info("Resetting PHY..."); common.ta.set_base_sec(0); common.reset(); + + // Release mapping of secondary cells + if (radio != nullptr) { + for (uint32_t i = 1; i < args.nof_lte_carriers; i++) { + radio->release_freq(i); + } + } } uint32_t phy::get_current_tti() @@ -429,13 +438,6 @@ void phy::start_plot() } } -void phy::enable_pregen_signals(bool enable) -{ - for (uint32_t i = 0; i < args.nof_phy_threads; i++) { - lte_workers[i]->enable_pregen_signals_unlocked(enable); - } -} - bool phy::set_config(const srsran::phy_cfg_t& config_, uint32_t cc_idx) { if (!is_initiated()) { @@ -459,18 +461,10 @@ bool phy::set_config(const srsran::phy_cfg_t& config_, uint32_t cc_idx) prach_cfg = config_.prach_cfg; } - // Apply configuration after the worker is finished to avoid race conditions + // Apply configurations asynchronously to avoid race conditions cmd_worker.add_cmd([this, config_, cc_idx]() { logger_phy.info("Setting new PHY configuration cc_idx=%d...", cc_idx); - for (uint32_t i = 0; i < args.nof_phy_threads; i++) { - // set_cell is not protected so run when worker is finished - lte::sf_worker* w = lte_workers.wait_worker_id(i); - if (w) { - w->set_config_unlocked(cc_idx, config_); - w->release(); - } - } - logger_phy.info("Finished setting new PHY configuration cc_idx=%d", cc_idx); + lte_workers.set_config(cc_idx, config_); // It is up to the PRACH component to detect whether the cell or the configuration have changed to reconfigure configure_prach_params(); @@ -509,23 +503,27 @@ bool phy::set_scell(srsran_cell_t cell_info, uint32_t cc_idx, uint32_t earfcn) // Set inter-frequency measurement sfsync.set_inter_frequency_measurement(cc_idx, earfcn, cell_info); - // Store secondary serving cell EARFCN and PCI - common.cell_state.configure(cc_idx, earfcn, cell_info.id); - - // Reset cell configuration - for (uint32_t i = 0; i < args.nof_phy_threads; i++) { - lte_workers[i]->reset_cell_unlocked(cc_idx); - } + // Reset secondary serving cell state, prevents this component carrier from executing any new PHY processing. It does + // not stop any current work + common.cell_state.reset(cc_idx); // Component carrier index zero should be reserved for PCell // Send configuration to workers cmd_worker.add_cmd([this, cell_info, cc_idx, earfcn, earfcn_is_different]() { - logger_phy.info("Setting new SCell configuration cc_idx=%d, earfcn=%d...", cc_idx, earfcn); + logger_phy.info("Setting new SCell configuration cc_idx=%d, earfcn=%d, pci=%d...", cc_idx, earfcn, cell_info.id); for (uint32_t i = 0; i < args.nof_phy_threads; i++) { - // set_cell is not protected so run when worker is finished + // set_cell is not protected so run when worker has finished to ensure no PHY processing is done at the time of + // cell setting lte::sf_worker* w = lte_workers.wait_worker_id(i); if (w) { + // Reset secondary serving cell configuration, this needs to be done when the sf_worker is reserved to prevent + // resetting the cell while it is working + w->reset_cell_unlocked(cc_idx); + + // Set the new cell w->set_cell_unlocked(cc_idx, cell_info); + + // Release the new worker, it should not start processing until the SCell state is set to configured w->release(); } } @@ -541,7 +539,11 @@ bool phy::set_scell(srsran_cell_t cell_info, uint32_t cc_idx, uint32_t earfcn) // Set secondary serving cell synchronization sfsync.scell_sync_set(cc_idx, cell_info); - logger_phy.info("Finished setting new SCell configuration cc_idx=%d, earfcn=%d", cc_idx, earfcn); + logger_phy.info( + "Finished setting new SCell configuration cc_idx=%d, earfcn=%d, pci=%d", cc_idx, earfcn, cell_info.id); + + // Configure secondary serving cell, allows this component carrier to execute PHY processing + common.cell_state.configure(cc_idx, earfcn, cell_info.id); stack->set_scell_complete(true); }); diff --git a/srsue/src/phy/phy_common.cc b/srsue/src/phy/phy_common.cc index c543df73b..dacc5b337 100644 --- a/srsue/src/phy/phy_common.cc +++ b/srsue/src/phy/phy_common.cc @@ -880,6 +880,7 @@ void phy_common::reset() cur_pusch_power = 0; sr_last_tx_tti = -1; pcell_report_period = 20; + last_ri = 0; ZERO_OBJECT(pathloss); ZERO_OBJECT(avg_sinr_db); @@ -915,13 +916,6 @@ void phy_common::reset() i = {}; } } - - // Release mapping of secondary cells - if (args != nullptr && radio_h != nullptr) { - for (uint32_t i = 1; i < args->nof_lte_carriers; i++) { - radio_h->release_freq(i); - } - } } /* Convert 6-bit maps to 10-element subframe tables diff --git a/srsue/src/phy/prach.cc b/srsue/src/phy/prach.cc index 4ad721cce..4fff8b729 100644 --- a/srsue/src/phy/prach.cc +++ b/srsue/src/phy/prach.cc @@ -92,6 +92,8 @@ void prach::stop() bool prach::set_cell(srsran_cell_t cell_, srsran_prach_cfg_t prach_cfg) { + std::lock_guard lock(mutex); + if (!mem_initiated) { ERROR("PRACH: Error must call init() first"); return false; @@ -156,6 +158,7 @@ bool prach::generate_buffer(uint32_t f_idx) bool prach::prepare_to_send(uint32_t preamble_idx_, int allowed_subframe_, float target_power_dbm_) { + std::lock_guard lock(mutex); if (preamble_idx_ >= max_preambles) { Error("PRACH: Invalid preamble %d", preamble_idx_); return false; @@ -171,7 +174,11 @@ bool prach::prepare_to_send(uint32_t preamble_idx_, int allowed_subframe_, float bool prach::is_pending() const { - return cell_initiated && preamble_idx >= 0 && unsigned(preamble_idx) < max_preambles; + std::unique_lock lock(mutex, std::try_to_lock); + if (lock.owns_lock()) { + return cell_initiated && preamble_idx >= 0 && unsigned(preamble_idx) < max_preambles; + } + return false; } bool prach::is_ready_to_send(uint32_t current_tti_, uint32_t current_pci) diff --git a/srsue/src/phy/search.cc b/srsue/src/phy/search.cc index b708cacf1..32006da29 100644 --- a/srsue/src/phy/search.cc +++ b/srsue/src/phy/search.cc @@ -173,7 +173,7 @@ search::ret_code search::run(srsran_cell_t* cell_, std::arrayargs->dl_earfcn_list.size()); phy_state.go_idle(); - worker_com->reset(); // Stop all intra-frequency measurement before changing frequency meas_stop(); @@ -229,6 +228,18 @@ rrc_interface_phy_lte::cell_search_ret_t sync::cell_search_start(phy_cell_t* fou rrc_proc_state = PROC_SEARCH_RUNNING; + // Wait for SYNC thread to transition to IDLE (max. 2000ms) + if (not phy_state.wait_idle(TIMEOUT_TO_IDLE_MS)) { + Error("SYNC: Error transitioning to IDLE. Cell search cannot start."); + return ret; + } + + // Wait for workers to finish PHY processing + worker_com->semaphore.wait_all(); + + // Reset worker once SYNC is IDLE to flush any worker states such as ACKs and pending grants + worker_com->reset(); + if (srate_mode != SRATE_FIND) { srate_mode = SRATE_FIND; radio_h->set_rx_srate(1.92e6); @@ -310,7 +321,6 @@ bool sync::cell_select_init(phy_cell_t new_cell) Info("Cell Select: Going to IDLE"); phy_state.go_idle(); - worker_com->reset(); // Stop intra-frequency measurements if need to change frequency if ((int)new_cell.earfcn != current_earfcn) { @@ -334,11 +344,11 @@ bool sync::cell_select_start(phy_cell_t new_cell) rrc_proc_state = PROC_SELECT_RUNNING; + // Reset SFN and cell search FSMs. They can safely be done while it is CAMPING or IDLE sfn_p.reset(); search_p.reset(); - srsran_ue_sync_reset(&ue_sync); - /* Reconfigure cell if necessary */ + // Reconfigure cell if necessary cell.id = new_cell.pci; if (not set_cell(new_cell.cfo_hz)) { Error("Cell Select: Reconfiguring cell"); @@ -481,8 +491,9 @@ void sync::run_camping_in_sync_state(lte::sf_worker* lte_worker, Debug("SYNC: Worker %d synchronized", lte_worker->get_id()); - metrics.sfo = srsran_ue_sync_get_sfo(&ue_sync); - metrics.cfo = srsran_ue_sync_get_cfo(&ue_sync); + // Collect and provide metrics from last successful sync + metrics.sfo = sfo; + metrics.cfo = cfo; metrics.ta_us = worker_com->ta.get_usec(); for (uint32_t i = 0; i < worker_com->args->nof_lte_carriers; i++) { worker_com->set_sync_metrics(i, metrics); @@ -504,7 +515,7 @@ void sync::run_camping_in_sync_state(lte::sf_worker* lte_worker, // Set CFO for all Carriers for (uint32_t cc = 0; cc < worker_com->args->nof_lte_carriers; cc++) { lte_worker->set_cfo_unlocked(cc, get_tx_cfo()); - worker_com->update_cfo_measurement(cc, srsran_ue_sync_get_cfo(&ue_sync)); + worker_com->update_cfo_measurement(cc, cfo); } lte_worker->set_tti(tti); @@ -567,8 +578,18 @@ void sync::run_camping_state() } } + // Apply CFO adjustment if available + if (ref_cfo != 0.0) { + srsran_ue_sync_set_cfo_ref(&ue_sync, ref_cfo); + ref_cfo = 0.0; // reset until value changes again + } + // Primary Cell (PCell) Synchronization - switch (srsran_ue_sync_zerocopy(&ue_sync, sync_buffer.to_cf_t(), lte_worker->get_buffer_len())) { + int sync_result = srsran_ue_sync_zerocopy(&ue_sync, sync_buffer.to_cf_t(), lte_worker->get_buffer_len()); + cfo = srsran_ue_sync_get_cfo(&ue_sync); + sfo = srsran_ue_sync_get_sfo(&ue_sync); + + switch (sync_result) { case 1: run_camping_in_sync_state(lte_worker, nr_worker, sync_buffer); break; @@ -620,7 +641,7 @@ void sync::run_idle_state() void sync::run_thread() { - while (running) { + while (running.load(std::memory_order_relaxed)) { phy_lib_logger.set_context(tti); Debug("SYNC: state=%s, tti=%d", phy_state.to_string(), tti); @@ -682,7 +703,7 @@ void sync::in_sync() void sync::out_of_sync() { // Send RRC out-of-sync signal after NOF_OUT_OF_SYNC_SF consecutive subframes - Info("Out-of-sync %d/%d", out_of_sync_cnt, worker_com->args->nof_out_of_sync_events); + Info("Out-of-sync %d/%d", out_of_sync_cnt.load(std::memory_order_relaxed), worker_com->args->nof_out_of_sync_events); out_of_sync_cnt++; if (out_of_sync_cnt == worker_com->args->nof_out_of_sync_events) { Info("Sending to RRC"); @@ -694,7 +715,7 @@ void sync::out_of_sync() void sync::set_cfo(float cfo) { - srsran_ue_sync_set_cfo_ref(&ue_sync, cfo); + ref_cfo = cfo; } void sync::set_agc_enable(bool enable) @@ -723,7 +744,7 @@ void sync::set_agc_enable(bool enable) return; } - // Enable AGC + // Enable AGC (unprotected call to ue_sync must not happen outside of thread calling recv) srsran_ue_sync_start_agc( &ue_sync, callback_set_rx_gain, rf_info->min_rx_gain, rf_info->max_rx_gain, radio_h->get_rx_gain()); search_p.set_agc_enable(true); @@ -731,8 +752,7 @@ void sync::set_agc_enable(bool enable) float sync::get_tx_cfo() { - float cfo = srsran_ue_sync_get_cfo(&ue_sync); - + // Use CFO estimate from last successful sync float ret = cfo * ul_dl_factor; if (worker_com->args->cfo_is_doppler) { @@ -794,17 +814,20 @@ void sync::set_ue_sync_opts(srsran_ue_sync_t* q, float cfo) bool sync::set_cell(float cfo) { // Wait for SYNC thread to transition to IDLE (max. 2000ms) - uint32_t cnt = 0; - while (!phy_state.is_idle() && cnt <= 4000) { - Info("SYNC: PHY state is_idle=%d, cnt=%d", phy_state.is_idle(), cnt); - usleep(500); - cnt++; - } - if (!phy_state.is_idle()) { - Error("Can not change Cell while not in IDLE"); + if (not phy_state.wait_idle(TIMEOUT_TO_IDLE_MS)) { + Error("SYNC: Can not change Cell while not in IDLE"); return false; } + // Reset UE sync. Attention: doing this reset when the FSM is NOT IDLE can cause PSS/SSS out-of-sync + srsran_ue_sync_reset(&ue_sync); + + // Wait for workers to finish PHY processing + worker_com->semaphore.wait_all(); + + // Reset worker once SYNC is IDLE to flush any worker states such as ACKs and pending grants + worker_com->reset(); + if (!srsran_cell_isvalid(&cell)) { Error("SYNC: Setting cell: invalid cell (nof_prb=%d, pci=%d, ports=%d)", cell.nof_prb, cell.id, cell.nof_ports); return false; diff --git a/srsue/src/stack/mac/demux.cc b/srsue/src/stack/mac/demux.cc index 27d163395..1d1d306eb 100644 --- a/srsue/src/stack/mac/demux.cc +++ b/srsue/src/stack/mac/demux.cc @@ -156,7 +156,7 @@ bool demux::process_pdus() return pdus.process_pdus(); } -void demux::process_pdu(uint8_t* mac_pdu, uint32_t nof_bytes, srsran::pdu_queue::channel_t channel) +void demux::process_pdu(uint8_t* mac_pdu, uint32_t nof_bytes, srsran::pdu_queue::channel_t channel, int ul_nof_prbs) { Debug("Processing MAC PDU channel %d", channel); switch (channel) { diff --git a/srsue/src/stack/mac/dl_harq.cc b/srsue/src/stack/mac/dl_harq.cc index ba8765ce3..47ed79fee 100644 --- a/srsue/src/stack/mac/dl_harq.cc +++ b/srsue/src/stack/mac/dl_harq.cc @@ -158,7 +158,9 @@ void dl_harq_entity::dl_harq_process::tb_decoded(mac_interface_phy_lte::mac_gran { /* For each subprocess... */ for (uint32_t i = 0; i < SRSRAN_MAX_TB; i++) { - subproc[i].tb_decoded(grant, &ack[i]); + if (grant.tb[i].tbs) { + subproc[i].tb_decoded(grant, &ack[i]); + } } } @@ -205,12 +207,14 @@ bool dl_harq_entity::dl_harq_process::dl_tb_process::init(int pid, dl_harq_entit return true; } -void dl_harq_entity::dl_harq_process::dl_tb_process::reset(bool lock) +void dl_harq_entity::dl_harq_process::dl_tb_process::reset() { - if (lock) { - mutex.lock(); - } + std::lock_guard lock(mutex); + reset_unsafe(); +} +void dl_harq_entity::dl_harq_process::dl_tb_process::reset_unsafe() +{ bzero(&cur_grant, sizeof(mac_interface_phy_lte::mac_grant_dl_t)); is_first_tb = true; ack = false; @@ -222,10 +226,6 @@ void dl_harq_entity::dl_harq_process::dl_tb_process::reset(bool lock) } payload_buffer_ptr = NULL; } - - if (lock) { - mutex.unlock(); - } } void dl_harq_entity::dl_harq_process::dl_tb_process::reset_ndi() @@ -302,7 +302,8 @@ void dl_harq_entity::dl_harq_process::dl_tb_process::new_grant_dl(mac_interface_ n_retx, n_retx > RESET_DUPLICATE_TIMEOUT ? "yes" : "no"); if (n_retx > RESET_DUPLICATE_TIMEOUT) { - reset(false); + // reset without trying to acquire the mutex again + reset_unsafe(); } } @@ -360,11 +361,12 @@ void dl_harq_entity::dl_harq_process::dl_tb_process::tb_decoded(mac_interface_ph cur_grant.tb[tid].ndi); } - mutex.unlock(); - if (ack && is_bcch) { - reset(); + // reset without trying to acquire the mutex again + reset_unsafe(); } + + mutex.unlock(); } // Determine if it's a new transmission 5.3.2.2 diff --git a/srsue/src/stack/mac_nr/test/CMakeLists.txt b/srsue/src/stack/mac_nr/test/CMakeLists.txt index 3c9d60b55..b466fc216 100644 --- a/srsue/src/stack/mac_nr/test/CMakeLists.txt +++ b/srsue/src/stack/mac_nr/test/CMakeLists.txt @@ -19,11 +19,11 @@ # add_executable(proc_ra_nr_test proc_ra_nr_test.cc) -target_link_libraries(proc_ra_nr_test srsue_mac_nr srsran_common) +target_link_libraries(proc_ra_nr_test srsue_mac_nr srsran_common ${ATOMIC_LIBS}) 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) +target_link_libraries(proc_bsr_nr_test srsue_mac_nr srsran_common ${ATOMIC_LIBS}) add_test(proc_bsr_nr_test proc_bsr_nr_test) add_executable(proc_sr_nr_test proc_sr_nr_test.cc) @@ -31,5 +31,5 @@ target_link_libraries(proc_sr_nr_test srsue_mac_nr srsran_common) add_test(proc_sr_nr_test proc_sr_nr_test) add_executable(mac_nr_test mac_nr_test.cc) -target_link_libraries(mac_nr_test srsue_mac_nr srsran_common) +target_link_libraries(mac_nr_test srsue_mac_nr srsran_common ${ATOMIC_LIBS}) 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 index e15ff0d51..d4647264a 100644 --- a/srsue/src/stack/mac_nr/test/mac_nr_test.cc +++ b/srsue/src/stack/mac_nr/test/mac_nr_test.cc @@ -101,7 +101,7 @@ 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 + uint32_t read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) final { if (!read_enable || nof_bytes < read_min) { return 0; diff --git a/srsue/src/stack/rrc/rrc.cc b/srsue/src/stack/rrc/rrc.cc index 21225b242..0dd3b1a87 100644 --- a/srsue/src/stack/rrc/rrc.cc +++ b/srsue/src/stack/rrc/rrc.cc @@ -75,6 +75,7 @@ rrc::rrc(stack_interface_rrc* stack_, srsran::task_sched_handle task_sched_) : plmn_searcher(this), cell_reselector(this), connection_reest(this), + conn_setup_proc(this), ho_handler(this), conn_recfg_proc(this), meas_cells_nr(task_sched_), @@ -373,6 +374,9 @@ void rrc::set_config_complete(bool status) { // Signal Reconfiguration Procedure that PHY configuration has completed phy_ctrl->set_config_complete(); + if (conn_setup_proc.is_busy()) { + conn_setup_proc.trigger(status); + } if (conn_recfg_proc.is_busy()) { conn_recfg_proc.trigger(status); } @@ -724,6 +728,11 @@ void rrc::max_retx_attempted() radio_link_failure_push_cmd(); } +void rrc::protocol_failure() +{ + logger.warning("RLC protocol failure detected"); +} + void rrc::timer_expired(uint32_t timeout_id) { if (timeout_id == t310.id()) { @@ -2547,16 +2556,12 @@ void rrc::handle_con_setup(const rrc_conn_setup_s& setup) t302.stop(); srsran::console("RRC Connected\n"); - // Apply the Radio Resource configuration - apply_rr_config_dedicated(&setup.crit_exts.c1().rrc_conn_setup_r8().rr_cfg_ded); - - nas->set_barring(srsran::barring_t::none); - - if (dedicated_info_nas.get()) { - send_con_setup_complete(std::move(dedicated_info_nas)); - } else { - logger.error("Pending to transmit a ConnectionSetupComplete but no dedicatedInfoNAS was in queue"); + // defer transmission of Setup Complete until PHY reconfiguration has been completed + if (not conn_setup_proc.launch(&setup.crit_exts.c1().rrc_conn_setup_r8().rr_cfg_ded, std::move(dedicated_info_nas))) { + logger.error("Failed to initiate connection setup procedure"); + return; } + callback_list.add_proc(conn_setup_proc); } /* Reception of RRCConnectionReestablishment by the UE 5.3.7.5 */ diff --git a/srsue/src/stack/rrc/rrc_nr.cc b/srsue/src/stack/rrc/rrc_nr.cc index bdd8a988f..90292b213 100644 --- a/srsue/src/stack/rrc/rrc_nr.cc +++ b/srsue/src/stack/rrc/rrc_nr.cc @@ -1470,6 +1470,7 @@ bool rrc_nr::apply_radio_bearer_cfg(const radio_bearer_cfg_s& radio_bearer_cfg) } // RLC interface void rrc_nr::max_retx_attempted() {} +void rrc_nr::protocol_failure() {} // MAC interface void rrc_nr::ra_completed() {} diff --git a/srsue/src/stack/rrc/rrc_procedures.cc b/srsue/src/stack/rrc/rrc_procedures.cc index 6698f710d..a16f83963 100644 --- a/srsue/src/stack/rrc/rrc_procedures.cc +++ b/srsue/src/stack/rrc/rrc_procedures.cc @@ -943,6 +943,60 @@ srsran::proc_outcome_t rrc::connection_request_proc::react(const cell_selection_ } } +/****************************************** + * Connection Setup Procedure + *****************************************/ + +// Simple procedure mainly do defer the transmission of the SetupComplete until all PHY reconfiguration are done +rrc::connection_setup_proc::connection_setup_proc(srsue::rrc* parent_) : + rrc_ptr(parent_), logger(srslog::fetch_basic_logger("RRC")) +{} + +srsran::proc_outcome_t rrc::connection_setup_proc::init(const asn1::rrc::rr_cfg_ded_s* cnfg_, + srsran::unique_byte_buffer_t dedicated_info_nas_) +{ + Info("Starting..."); + + if (dedicated_info_nas_.get() == nullptr) { + rrc_ptr->logger.error("Connection Setup Failed, no dedicatedInfoNAS available"); + return proc_outcome_t::error; + } + + dedicated_info_nas = std::move(dedicated_info_nas_); + + // Apply the Radio Resource configuration + if (!rrc_ptr->apply_rr_config_dedicated(cnfg_)) { + return proc_outcome_t::error; + } + + rrc_ptr->nas->set_barring(srsran::barring_t::none); + + // No phy config was scheduled, run config completion immediately + if (rrc_ptr->phy_ctrl->is_config_pending()) { + return react(true); + } + return proc_outcome_t::yield; +} + +srsran::proc_outcome_t rrc::connection_setup_proc::react(const bool& config_complete) +{ + if (not config_complete) { + rrc_ptr->logger.error("Connection Setup Failed"); + return proc_outcome_t::error; + } + + rrc_ptr->send_con_setup_complete(std::move(dedicated_info_nas)); + return proc_outcome_t::success; +} + +void rrc::connection_setup_proc::then(const srsran::proc_state_t& result) +{ + if (result.is_success()) { + rrc_ptr->logger.info("Finished %s successfully", name()); + return; + } +} + /****************************************** * Connection Reconfiguration Procedure *****************************************/ diff --git a/srsue/test/mac_test.cc b/srsue/test/mac_test.cc index a95ae0a6b..52e00e0aa 100644 --- a/srsue/test/mac_test.cc +++ b/srsue/test/mac_test.cc @@ -23,6 +23,7 @@ #include "srsran/asn1/rrc_utils.h" #include "srsran/common/mac_pcap.h" #include "srsran/common/test_common.h" +#include "srsran/common/tsan_options.h" #include "srsran/test/ue_test_interfaces.h" #include "srsue/hdr/stack/mac/mac.h" #include "srsue/hdr/stack/mac/mux.h" @@ -45,7 +46,7 @@ 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 + uint32_t read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) final { if (!read_enable || nof_bytes < read_min) { return 0; diff --git a/srsue/test/phy/nr_cell_search_test.cc b/srsue/test/phy/nr_cell_search_test.cc index 4ab2dae9a..4e764562f 100644 --- a/srsue/test/phy/nr_cell_search_test.cc +++ b/srsue/test/phy/nr_cell_search_test.cc @@ -90,7 +90,6 @@ public: ssb_cfg.ssb_freq_hz = args.ssb_freq_hz; ssb_cfg.scs = args.ssb_scs; ssb_cfg.pattern = args.get_ssb_pattern(); - ssb_cfg.position[0] = true; ssb_cfg.duplex_mode = args.get_duplex_mode(); ssb_cfg.periodicity_ms = args.ssb_period_ms; if (srsran_ssb_set_cfg(&ssb, &ssb_cfg) < SRSRAN_SUCCESS) { @@ -115,7 +114,7 @@ public: srsran_pbch_msg_nr_t msg = {}; // Add SSB - if (srsran_ssb_add(&ssb, pci, &msg, buffer.data(), buffer.data()) < SRSRAN_SUCCESS) { + if (srsran_ssb_add(&ssb, pci, 0, &msg, buffer.data(), buffer.data()) < SRSRAN_SUCCESS) { logger.error("Error adding SSB"); return SRSRAN_ERROR; } diff --git a/srsue/test/phy/ue_phy_test.cc b/srsue/test/phy/ue_phy_test.cc index 18c2b86b4..daf38f1ed 100644 --- a/srsue/test/phy/ue_phy_test.cc +++ b/srsue/test/phy/ue_phy_test.cc @@ -21,10 +21,10 @@ #include #include +#include #include #include #include -#include #include #define CALLBACK(NAME, ...) \ @@ -384,6 +384,9 @@ public: phy = std::unique_ptr(new srsue::phy); phy->init(phy_args, &stack, &radio); + // Wait PHY init to end + phy->wait_initialize(); + // Initialise DL baseband buffers for (uint32_t i = 0; i < cell.nof_ports; i++) { enb_dl_buffer[i] = srsran_vec_cf_malloc(sf_len); @@ -395,9 +398,6 @@ public: // Initialise eNb DL srsran_enb_dl_init(&enb_dl, enb_dl_buffer, SRSRAN_MAX_PRB); srsran_enb_dl_set_cell(&enb_dl, cell); - - // Wait PHY init to end - phy->wait_initialize(); } ~phy_test_bench() diff --git a/srsue/test/ttcn3/hdr/lte_ttcn3_phy.h b/srsue/test/ttcn3/hdr/lte_ttcn3_phy.h index aafe3f2d4..2d9ce941f 100644 --- a/srsue/test/ttcn3/hdr/lte_ttcn3_phy.h +++ b/srsue/test/ttcn3/hdr/lte_ttcn3_phy.h @@ -64,7 +64,6 @@ public: void set_cell_map(const cell_list_t& cells_); // phy_interface_rrc_lte - void enable_pregen_signals(bool enable) override; void deactivate_scells() override; void set_activation_deactivation_scell(uint32_t cmd, uint32_t tti) override; bool set_config(const srsran::phy_cfg_t& config, uint32_t cc_idx = 0) override; diff --git a/srsue/test/ttcn3/hdr/ttcn3_syssim.h b/srsue/test/ttcn3/hdr/ttcn3_syssim.h index 06ea9f9e2..9126e02bc 100644 --- a/srsue/test/ttcn3/hdr/ttcn3_syssim.h +++ b/srsue/test/ttcn3/hdr/ttcn3_syssim.h @@ -34,6 +34,7 @@ #include "ttcn3_sys_interface.h" #include "ttcn3_ue.h" #include "ttcn3_ut_interface.h" +#include #include class ttcn3_syssim : public syssim_interface_phy, @@ -112,7 +113,7 @@ public: uint32_t get_tti(); - void process_pdu(uint8_t* buff, uint32_t len, pdu_queue::channel_t channel); + void process_pdu(uint8_t* buff, uint32_t len, pdu_queue::channel_t channel, int ul_nof_prbs); void set_cell_config(const ttcn3_helpers::timing_info_t timing, const cell_config_t cell); void set_cell_config_impl(const cell_config_t cell); @@ -165,6 +166,7 @@ public: void write_pdu_pcch(unique_byte_buffer_t pdu); void write_pdu_mch(uint32_t lcid, unique_byte_buffer_t pdu); void max_retx_attempted(); + void protocol_failure(); const char* get_rb_name(uint32_t lcid); @@ -232,8 +234,8 @@ private: all_args_t args = {}; // Simulator vars - ttcn3_ue* ue = nullptr; - bool running = false; + ttcn3_ue* ue = nullptr; + std::atomic running = {false}; typedef enum { UE_SWITCH_ON = 0, UE_SWITCH_OFF, ENABLE_DATA, DISABLE_DATA } ss_events_t; block_queue event_queue; diff --git a/srsue/test/ttcn3/src/lte_ttcn3_phy.cc b/srsue/test/ttcn3/src/lte_ttcn3_phy.cc index 68b0a6694..0da8e4053 100644 --- a/srsue/test/ttcn3/src/lte_ttcn3_phy.cc +++ b/srsue/test/ttcn3/src/lte_ttcn3_phy.cc @@ -70,11 +70,6 @@ void lte_ttcn3_phy::set_cell_map(const cell_list_t& cells_) void lte_ttcn3_phy::set_config_tdd(srsran_tdd_config_t& tdd_config) {} -void lte_ttcn3_phy::enable_pregen_signals(bool enable) -{ - logger.debug("%s not implemented.", __FUNCTION__); -} - void lte_ttcn3_phy::deactivate_scells() { logger.debug("%s not implemented.", __FUNCTION__); diff --git a/srsue/test/ttcn3/src/ttcn3_syssim.cc b/srsue/test/ttcn3/src/ttcn3_syssim.cc index 46d39d034..7926e0b7b 100644 --- a/srsue/test/ttcn3/src/ttcn3_syssim.cc +++ b/srsue/test/ttcn3/src/ttcn3_syssim.cc @@ -58,7 +58,7 @@ ttcn3_syssim::ttcn3_syssim(ttcn3_ue* ue_) : mac_msg_dl(20, ss_mac_logger), pdus(logger), ue(ue_), - signal_handler(&running), + signal_handler(running), timer_handler(create_tti_timer(), [&](uint64_t res) { new_tti_indication(res); }) { if (ue->init(all_args_t{}, this, "INIT_TEST") != SRSRAN_SUCCESS) { @@ -768,7 +768,7 @@ uint32_t ttcn3_syssim::get_tti() return tti; } -void ttcn3_syssim::process_pdu(uint8_t* buff, uint32_t len, pdu_queue::channel_t channel) {} +void ttcn3_syssim::process_pdu(uint8_t* buff, uint32_t len, pdu_queue::channel_t channel, int ul_nof_prbs) {} void ttcn3_syssim::set_cell_config(const ttcn3_helpers::timing_info_t timing, const cell_config_t cell) { @@ -1143,6 +1143,10 @@ void ttcn3_syssim::max_retx_attempted() { logger.error("%s not implemented.", __FUNCTION__); } +void ttcn3_syssim::protocol_failure() +{ + logger.error("%s not implemented.", __FUNCTION__); +} const char* ttcn3_syssim::get_rb_name(uint32_t lcid) { diff --git a/srsue/test/upper/CMakeLists.txt b/srsue/test/upper/CMakeLists.txt index da5d2538a..44d60552d 100644 --- a/srsue/test/upper/CMakeLists.txt +++ b/srsue/test/upper/CMakeLists.txt @@ -48,7 +48,7 @@ target_link_libraries(tft_test srsue_upper srsran_upper srsran_phy) add_test(tft_test tft_test) add_executable(rrc_phy_ctrl_test rrc_phy_ctrl_test.cc) -target_link_libraries(rrc_phy_ctrl_test srsran_common srsue_rrc) +target_link_libraries(rrc_phy_ctrl_test srsran_common srsue_rrc ${ATOMIC_LIBS}) add_test(rrc_phy_ctrl_test rrc_phy_ctrl_test) add_executable(rrc_cell_test rrc_cell_test.cc) diff --git a/srsue/test/upper/nas_test.cc b/srsue/test/upper/nas_test.cc index efd192851..4d615602e 100644 --- a/srsue/test/upper/nas_test.cc +++ b/srsue/test/upper/nas_test.cc @@ -21,6 +21,7 @@ #include "srsran/common/bcd_helpers.h" #include "srsran/common/test_common.h" +#include "srsran/common/tsan_options.h" #include "srsran/interfaces/ue_pdcp_interfaces.h" #include "srsran/srslog/srslog.h" #include "srsran/test/ue_test_interfaces.h" @@ -154,10 +155,7 @@ public: void run_thread() { - std::unique_lock lk(init_mutex); running = true; - init_cv.notify_all(); - lk.unlock(); while (running) { task_sched.tic(); task_sched.run_pending_tasks(); @@ -166,18 +164,15 @@ public: } void stop() { - std::unique_lock lk(init_mutex); while (not running) { - init_cv.wait(lk); + usleep(1000); } running = false; wait_thread_finish(); } pdcp_interface_gw* pdcp = nullptr; srsue::nas* nas = nullptr; - bool running = false; - std::mutex init_mutex; - std::condition_variable init_cv; + std::atomic running = {false}; }; class gw_dummy : public gw_interface_nas, public gw_interface_pdcp diff --git a/srsue/test/upper/rrc_meas_test.cc b/srsue/test/upper/rrc_meas_test.cc index 5a69e53a6..6aabf6ae5 100644 --- a/srsue/test/upper/rrc_meas_test.cc +++ b/srsue/test/upper/rrc_meas_test.cc @@ -57,7 +57,6 @@ public: last_selected_cell = cell; return true; } - void enable_pregen_signals(bool enable) override {} void set_cells_to_meas(uint32_t earfcn, const std::set& pci) override { diff --git a/test/run_lte.sh b/test/run_lte.sh index cea490c73..47766ffa7 100755 --- a/test/run_lte.sh +++ b/test/run_lte.sh @@ -312,8 +312,8 @@ ue_args="$build_path/../srsue/ue.conf.example \ --gw.netns=$ue_netns \ --log.all_level=info \ --log.filename=./${nof_prb}prb_ue.log \ - --pcap.enable=true \ - --pcap.filename=./${nof_prb}prb_ue.pcap" + --pcap.enable=mac \ + --pcap.mac_filename=./${nof_prb}prb_ue.pcap" if ([ "$num_cc" == "2" ]) then