From 65519eaf3d0165ccc6753687f72ca4880b16f17b Mon Sep 17 00:00:00 2001 From: Alejandro Leal Date: Thu, 17 Feb 2022 09:57:26 +0100 Subject: [PATCH 001/195] Fixes the comparison. uint8_t was promoted to int. --- lib/src/rlc/rlc_am_nr_packing.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/rlc/rlc_am_nr_packing.cc b/lib/src/rlc/rlc_am_nr_packing.cc index 88565d2ef..c2aad71b0 100644 --- a/lib/src/rlc/rlc_am_nr_packing.cc +++ b/lib/src/rlc/rlc_am_nr_packing.cc @@ -211,7 +211,7 @@ uint32_t rlc_am_nr_read_status_pdu(const uint8_t* payload, ptr++; } status->N_nack++; - if ((ptr - payload) > nof_bytes) { + if (uint32_t(ptr - payload) > nof_bytes) { fprintf(stderr, "Malformed PDU, trying to read more bytes than it is available\n"); return 0; } From 767feddd73165e1617187cce47b5e140ea8c5a15 Mon Sep 17 00:00:00 2001 From: Bedran Karakoc Date: Fri, 11 Feb 2022 00:12:59 +0100 Subject: [PATCH 002/195] lib,nas_5g: Implement to_string() functionality for options --- lib/include/srsran/asn1/nas_5g_ies.h | 108 +-- lib/src/asn1/nas_5g_ies.cc | 950 ++++++++++++++++++++++++++- 2 files changed, 1002 insertions(+), 56 deletions(-) diff --git a/lib/include/srsran/asn1/nas_5g_ies.h b/lib/include/srsran/asn1/nas_5g_ies.h index 9b7ad2dd1..d405d315d 100644 --- a/lib/include/srsran/asn1/nas_5g_ies.h +++ b/lib/include/srsran/asn1/nas_5g_ies.h @@ -41,7 +41,7 @@ public: reserved = 0b111, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated registration_type_type; @@ -51,7 +51,7 @@ public: follow_on_request_pending = 0b1, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated follow_on_request_bit_type; @@ -74,7 +74,7 @@ public: mapped_security_context = 0b1, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated security_context_flag_type; @@ -83,7 +83,7 @@ public: no_key_is_available_or_reserved = 0b111, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated nas_key_set_identifier_type; @@ -113,7 +113,7 @@ public: eui_64 = 0b111, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated identity_types; @@ -131,7 +131,7 @@ public: gli = 0b100, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated supi_format_type; @@ -142,7 +142,7 @@ public: ecies_scheme_profile_b = 0b0010, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated protection_scheme_id_type; @@ -415,7 +415,7 @@ public: sst_sd_mapped_hplmn_sst_and_mapped_hplmn_sd = 0b00001000, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated SST_type; @@ -646,7 +646,7 @@ public: data_centric = 0b1, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated UE_usage_setting_type; @@ -671,7 +671,7 @@ public: drx_cycle_parameter_t_256 = 0b0100, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated drx_value_type; @@ -737,7 +737,7 @@ public: multiple_payloads = 0b1111, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated Payload_container_type_type; @@ -784,7 +784,7 @@ public: sms_over_nas_supported = 0b1, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated SMS_requested_type; @@ -794,7 +794,7 @@ public: ue_radio_capability_update_needed = 0b1, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated NG_RAN_RCU_type; @@ -806,7 +806,7 @@ public: reserved = 0b11, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated PNB_5GS_CIoT_type; @@ -818,7 +818,7 @@ public: reserved = 0b11, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated PNB_EPS_CIoT_type; @@ -916,7 +916,7 @@ public: seconds_20 = 0b1111, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated Paging_Time_Window_type; @@ -940,7 +940,7 @@ public: second_20_48 = 0b1111, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated eDRX_value_type; @@ -969,7 +969,7 @@ public: value_indicates_that_the_timer_is_deactivated = 0b111, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated Unit_type; @@ -1053,7 +1053,7 @@ public: drx_cycle_parameter_t_1024 = 0b0111, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated nb_n1_mode_drx_value_type; @@ -1075,7 +1075,7 @@ public: registered_for_emergency_services = 0b1, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated Emergency_registered_type; @@ -1085,7 +1085,7 @@ public: nssaa_is_to_be_performed = 0b1, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated NSSAA_to_be_performed_type; @@ -1095,7 +1095,7 @@ public: sms_over_nas_allowed = 0b1, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated SMS_allowed_type; @@ -1107,7 +1107,7 @@ public: reserved = 0b111, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated registration_result_type; @@ -1145,7 +1145,7 @@ public: reserved = 0b11, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated type_of_list_type; @@ -1298,7 +1298,7 @@ public: nssai_inclusion_mode_d = 0b11, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated NSSAI_inclusion_mode_type; @@ -1342,7 +1342,7 @@ public: network_assigned_ue_radio_capability_i_ds_deletion_requested = 0b001, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated Deletion_request_type; @@ -1438,7 +1438,7 @@ public: protocol_error_unspecified = 0b01101111, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated cause_5gmm_type; @@ -1460,7 +1460,7 @@ public: switch_off = 0b1, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated switch_off_type; @@ -1470,7 +1470,7 @@ public: re_registration_required = 0b1, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated re_registration_required_type; @@ -1481,7 +1481,7 @@ public: access_3_gpp_and_non_3_gpp_access = 0b11, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated access_type_type; @@ -1526,7 +1526,7 @@ public: unused_shall_be_interpreted_as_data_2 = 0b1011, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated Service_type_value_type; @@ -1550,7 +1550,7 @@ public: emergency_services_fallback = 0b100, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated control_plane_service_type_value_type; @@ -1615,7 +1615,7 @@ public: reserved = 0b11, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated value_type; @@ -1649,7 +1649,7 @@ public: release_of_n1_nas_signalling_connection_not_required = 0b1, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated SCMR_type; @@ -1736,7 +1736,7 @@ public: eui_64 = 0b111, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated identity_types; @@ -1764,7 +1764,7 @@ public: ia7_5g = 0b0111, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated integrity_protection_algorithm_type; @@ -1780,7 +1780,7 @@ public: ea7_5g = 0b0111, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated ciphering_algorithm_type; @@ -1804,7 +1804,7 @@ public: imeisv_requested = 0b001, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated imeisv_request_type; @@ -1832,7 +1832,7 @@ public: eia7 = 0b111, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated integrity_protection_algorithm_type; @@ -1848,7 +1848,7 @@ public: eea7 = 0b111, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated ciphering_algorithm_type; @@ -1934,7 +1934,7 @@ public: non_3_gpp_access = 0b10, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated Access_type_value_type; @@ -1973,7 +1973,7 @@ public: reserved = 0b111, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated Request_type_value_type; @@ -2006,7 +2006,7 @@ public: ma_pdu_session_network_upgrade_is_allowed = 0b0001, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated MA_PDU_session_information_value_type; @@ -2031,7 +2031,7 @@ public: reserved = 0b11, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated Downlink_data_expected_type; @@ -2055,7 +2055,7 @@ public: full_data_rate = 0b11111111, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated max_data_rate_UPIP_uplink_type; @@ -2066,7 +2066,7 @@ public: full_data_rate = 0b11111111, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated max_data_rate_UPIP_downlink_type; @@ -2093,7 +2093,7 @@ public: reserved = 0b111, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated PDU_session_type_value_type; @@ -2120,7 +2120,7 @@ public: reserved = 0b111, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated SSC_mode_value_type; @@ -2245,7 +2245,7 @@ public: bits_15 = 0b10, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated CID_Length_type; @@ -2268,7 +2268,7 @@ public: ipv4v6 = 0b011, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated PDU_session_type_value_type; @@ -2331,7 +2331,7 @@ public: inc_by_256_pbps = 0b00011001, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated unit_session_AMBR_type; @@ -2421,7 +2421,7 @@ public: value_indicates_that_the_timer_is_deactivated = 0b111, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated Unit_type; @@ -2476,7 +2476,7 @@ public: ethernet_pdn_type_in_s1_mode_supported = 0b1, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated EPT_S1_type; @@ -2548,7 +2548,7 @@ public: the_back_off_timer_is_applied_in_all_plm_ns = 0b1, } value; - const char* to_string(); + const char* to_string() const; }; typedef nas_enumerated abo_type; diff --git a/lib/src/asn1/nas_5g_ies.cc b/lib/src/asn1/nas_5g_ies.cc index d5ee5a54c..65aba7dcf 100644 --- a/lib/src/asn1/nas_5g_ies.cc +++ b/lib/src/asn1/nas_5g_ies.cc @@ -42,6 +42,34 @@ SRSASN_CODE registration_type_5gs_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* registration_type_5gs_t::registration_type_type_::to_string() const +{ + switch (value) { + case registration_type_type_::initial_registration: + return "Initial Registration"; + case registration_type_type_::mobility_registration_updating: + return "Mobility Registration Updating"; + case registration_type_type_::periodic_registration_updating: + return "Periodic Registration Updating"; + case registration_type_type_::emergency_registration: + return "Emergency Registration"; + case registration_type_type_::reserved: + return "Reserved"; + default: + return "Invalid Choice"; + } +} +const char* registration_type_5gs_t::follow_on_request_bit_type_::to_string() const +{ + switch (value) { + case follow_on_request_bit_type_::no_follow_on_request_pending: + return "no_follow_on_request_pending "; + case follow_on_request_bit_type_::follow_on_request_pending: + return "follow_on_request_pending"; + default: + return "Invalid Choice"; + } +} // IE: key set identifier // Reference: 9.11.3.32 SRSASN_CODE key_set_identifier_t::pack(asn1::bit_ref& bref) @@ -60,6 +88,26 @@ SRSASN_CODE key_set_identifier_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* key_set_identifier_t::security_context_flag_type_::to_string() const +{ + switch (value) { + case security_context_flag_type_::native_security_context: + return "native security context"; + case security_context_flag_type_::mapped_security_context: + return "mapped security context"; + default: + return "Invalid Choice"; + } +} +const char* key_set_identifier_t::nas_key_set_identifier_type_::to_string() const +{ + switch (value) { + case nas_key_set_identifier_type_::no_key_is_available_or_reserved: + return "no key is available or reserved"; + default: + return "Invalid Choice"; + } +} // IE: 5GS mobile identity // Reference: 9.11.3.4 SRSASN_CODE mobile_identity_5gs_t::pack(asn1::bit_ref& bref) @@ -180,7 +228,7 @@ SRSASN_CODE mobile_identity_5gs_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } -const char* mobile_identity_5gs_t::identity_types_::to_string() +const char* mobile_identity_5gs_t::identity_types_::to_string() const { switch (value) { case identity_types_::no_identity: @@ -1173,6 +1221,17 @@ SRSASN_CODE ue_usage_setting_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* ue_usage_setting_t::UE_usage_setting_type_::to_string() const +{ + switch (value) { + case UE_usage_setting_type_::voice_centric: + return "voice centric"; + case UE_usage_setting_type_::data_centric: + return "data centric "; + default: + return "Invalid Choice"; + } +} // IE: 5GS DRX parameters // Reference: 9.11.3.2A SRSASN_CODE drx_parameters_5gs_t::pack(asn1::bit_ref& bref) @@ -1213,6 +1272,23 @@ SRSASN_CODE drx_parameters_5gs_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* drx_parameters_5gs_t::drx_value_type_::to_string() const +{ + switch (value) { + case drx_value_type_::drx_value_not_specified: + return "DRX value not specified"; + case drx_value_type_::drx_cycle_parameter_t_32: + return "DRX cycle parameter T 32 "; + case drx_value_type_::drx_cycle_parameter_t_64: + return "DRX cycle parameter T 64 "; + case drx_value_type_::drx_cycle_parameter_t_128: + return "DRX cycle parameter T 128 "; + case drx_value_type_::drx_cycle_parameter_t_256: + return "DRX cycle parameter T 256 "; + default: + return "Invalid Choice"; + } +} // IE: EPS NAS message container // Reference: 9.11.3.24 SRSASN_CODE eps_nas_message_container_t::pack(asn1::bit_ref& bref) @@ -1300,6 +1376,31 @@ SRSASN_CODE payload_container_type_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* payload_container_type_t::Payload_container_type_type_::to_string() const +{ + switch (value) { + case Payload_container_type_type_::n1_sm_information: + return "N1 SM information"; + case Payload_container_type_type_::sms: + return "SMS"; + case Payload_container_type_type_::lte_positioning_protocol_lpp_message_container: + return "LTE Positioning Protocol LPP message container"; + case Payload_container_type_type_::sor_transparent_container: + return "SOR transparent container"; + case Payload_container_type_type_::ue_policy_container: + return "UE policy container"; + case Payload_container_type_type_::ue_parameters_update_transparent_container: + return "UE parameters update transparent container"; + case Payload_container_type_type_::location_services_message_container: + return "Location services message container"; + case Payload_container_type_type_::c_io_t_user_data_container: + return "CIoT user data container"; + case Payload_container_type_type_::multiple_payloads: + return "Multiple payloads"; + default: + return "Invalid Choice"; + } +} // IE: Payload container // Reference: 9.11.3.39 SRSASN_CODE payload_container_t::pack(asn1::bit_ref& bref) @@ -1395,6 +1496,58 @@ SRSASN_CODE update_type_5gs_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* update_type_5gs_t::SMS_requested_type_::to_string() const +{ + switch (value) { + case SMS_requested_type_::sms_over_nas_not_supported: + return "SMS over NAS not supported"; + case SMS_requested_type_::sms_over_nas_supported: + return "SMS over NAS supported"; + default: + return "Invalid Choice"; + } +} +const char* update_type_5gs_t::NG_RAN_RCU_type_::to_string() const +{ + switch (value) { + case NG_RAN_RCU_type_::ue_radio_capability_update_not_needed: + return "UE radio capability update not needed"; + case NG_RAN_RCU_type_::ue_radio_capability_update_needed: + return "UE radio capability update needed"; + default: + return "Invalid Choice"; + } +} +const char* update_type_5gs_t::PNB_5GS_CIoT_type_::to_string() const +{ + switch (value) { + case PNB_5GS_CIoT_type_::no_additional_information: + return "no additional information"; + case PNB_5GS_CIoT_type_::control_plane_c_io_t_5gs_optimization: + return "control plane CIoT 5GS optimization"; + case PNB_5GS_CIoT_type_::user_plane_c_io_t_5gs_optimization: + return "user plane CIoT 5GS optimization"; + case PNB_5GS_CIoT_type_::reserved: + return "reserved"; + default: + return "Invalid Choice"; + } +} +const char* update_type_5gs_t::PNB_EPS_CIoT_type_::to_string() const +{ + switch (value) { + case PNB_EPS_CIoT_type_::no_additional_information: + return "no additional information"; + case PNB_EPS_CIoT_type_::control_plane_c_io_t_eps_optimization: + return "control plane CIoT EPS optimization"; + case PNB_EPS_CIoT_type_::user_plane_c_io_t_eps_optimization: + return "user plane CIoT EPS optimization"; + case PNB_EPS_CIoT_type_::reserved: + return "reserved"; + default: + return "Invalid Choice"; + } +} // IE: Mobile station classmark 2 // Reference: 9.11.3.31C SRSASN_CODE mobile_station_classmark_2_t::pack(asn1::bit_ref& bref) @@ -1615,6 +1768,84 @@ SRSASN_CODE extended_drx_parameters_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* extended_drx_parameters_t::Paging_Time_Window_type_::to_string() const +{ + switch (value) { + case Paging_Time_Window_type_::seconds_0: + return "seconds_0"; + case Paging_Time_Window_type_::second_1: + return "second_1"; + case Paging_Time_Window_type_::seconds_2: + return "seconds_2"; + case Paging_Time_Window_type_::seconds_3: + return "seconds_3"; + case Paging_Time_Window_type_::seconds_4: + return "seconds_4"; + case Paging_Time_Window_type_::seconds_5: + return "seconds_5"; + case Paging_Time_Window_type_::seconds_6: + return "seconds_6"; + case Paging_Time_Window_type_::seconds_7: + return "seconds_7"; + case Paging_Time_Window_type_::seconds_8: + return "seconds_8"; + case Paging_Time_Window_type_::seconds_9: + return "seconds_9"; + case Paging_Time_Window_type_::seconds_10: + return "seconds_10"; + case Paging_Time_Window_type_::seconds_12: + return "seconds_12"; + case Paging_Time_Window_type_::seconds_14: + return "seconds_14"; + case Paging_Time_Window_type_::seconds_16: + return "seconds_16"; + case Paging_Time_Window_type_::seconds_18: + return "seconds_18"; + case Paging_Time_Window_type_::seconds_20: + return "seconds_20"; + default: + return "Invalid Choice"; + } +} +const char* extended_drx_parameters_t::eDRX_value_type_::to_string() const +{ + switch (value) { + case eDRX_value_type_::second_1_28: + return "second_1_28"; + case eDRX_value_type_::second_2_56: + return "second_2_56"; + case eDRX_value_type_::second_3_84: + return "second_3_84"; + case eDRX_value_type_::second_5_12: + return "second_5_12"; + case eDRX_value_type_::second_6_4: + return "second_6_4"; + case eDRX_value_type_::second_7_68: + return "second_7_68"; + case eDRX_value_type_::second_8_96: + return "second_8_96"; + case eDRX_value_type_::second_10_24: + return "second_10_24"; + case eDRX_value_type_::second_11_52: + return "second_11_52"; + case eDRX_value_type_::second_12_8: + return "second_12_8"; + case eDRX_value_type_::second_14_08: + return "second_14_08"; + case eDRX_value_type_::second_15_36: + return "second_15_36"; + case eDRX_value_type_::second_16_64: + return "second_16_64"; + case eDRX_value_type_::second_17_92: + return "second_17_92"; + case eDRX_value_type_::second_19_20: + return "second_19_20"; + case eDRX_value_type_::second_20_48: + return "second_20_48"; + default: + return "Invalid Choice"; + } +} // IE: GPRS timer 3 // Reference: 9.11.2.5 SRSASN_CODE gprs_timer_3_t::pack(asn1::bit_ref& bref) @@ -1652,6 +1883,29 @@ SRSASN_CODE gprs_timer_3_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* gprs_timer_3_t::Unit_type_::to_string() const +{ + switch (value) { + case Unit_type_::value_is_incremented_in_multiples_of_10_minutes: + return "value is incremented in multiples of 10 minutes"; + case Unit_type_::value_is_incremented_in_multiples_of_1_hour: + return "value is incremented in multiples of 1 hour"; + case Unit_type_::value_is_incremented_in_multiples_of_10_hours: + return "value is incremented in multiples of 10 hours"; + case Unit_type_::value_is_incremented_in_multiples_of_2_seconds: + return "value is incremented in multiples of 2 seconds"; + case Unit_type_::value_is_incremented_in_multiples_of_30_seconds: + return "value is incremented in multiples of 30 seconds"; + case Unit_type_::value_is_incremented_in_multiples_of_1_minute: + return "value is incremented in multiples of 1 minute"; + case Unit_type_::value_is_incremented_in_multiples_of_320_hours: + return "value is incremented in multiples of 320 hours"; + case Unit_type_::value_indicates_that_the_timer_is_deactivated: + return "value indicates that the timer is deactivated"; + default: + return "Invalid Choice"; + } +} // IE: UE radio capability ID // Reference: 9.11.3.68 SRSASN_CODE ue_radio_capability_id_t::pack(asn1::bit_ref& bref) @@ -1847,6 +2101,27 @@ SRSASN_CODE nb_n1_mode_drx_parameters_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* nb_n1_mode_drx_parameters_t::nb_n1_mode_drx_value_type_::to_string() const +{ + switch (value) { + case nb_n1_mode_drx_value_type_::drx_value_not_specified: + return "DRX value not specified"; + case nb_n1_mode_drx_value_type_::drx_cycle_parameter_t_32: + return "DRX cycle parameter T 32"; + case nb_n1_mode_drx_value_type_::drx_cycle_parameter_t_64: + return "DRX cycle parameter T 64"; + case nb_n1_mode_drx_value_type_::drx_cycle_parameter_t_128: + return "DRX cycle parameter T 128"; + case nb_n1_mode_drx_value_type_::drx_cycle_parameter_t_256: + return "DRX cycle parameter T 256"; + case nb_n1_mode_drx_value_type_::drx_cycle_parameter_t_512: + return "DRX cycle parameter T 512"; + case nb_n1_mode_drx_value_type_::drx_cycle_parameter_t_1024: + return "DRX cycle parameter T 1024"; + default: + return "Invalid Choice"; + } +} // IE: 5GS registration result // Reference: 9.11.3.6 SRSASN_CODE registration_result_5gs_t::pack(asn1::bit_ref& bref) @@ -1893,6 +2168,54 @@ SRSASN_CODE registration_result_5gs_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* registration_result_5gs_t::Emergency_registered_type_::to_string() const +{ + switch (value) { + case Emergency_registered_type_::not_registered_for_emergency_services: + return "Not registered for emergency services"; + case Emergency_registered_type_::registered_for_emergency_services: + return "Registered for emergency services"; + default: + return "Invalid Choice"; + } +} +const char* registration_result_5gs_t::NSSAA_to_be_performed_type_::to_string() const +{ + switch (value) { + case NSSAA_to_be_performed_type_::nssaa_is_not_to_be_performed: + return "NSSAA is not to be performed"; + case NSSAA_to_be_performed_type_::nssaa_is_to_be_performed: + return "NSSAA is to be performed"; + default: + return "Invalid Choice"; + } +} +const char* registration_result_5gs_t::SMS_allowed_type_::to_string() const +{ + switch (value) { + case SMS_allowed_type_::sms_over_nas_not_allowed: + return "SMS over NAS not allowed"; + case SMS_allowed_type_::sms_over_nas_allowed: + return "SMS over NAS allowed"; + default: + return "Invalid Choice"; + } +} +const char* registration_result_5gs_t::registration_result_type_::to_string() const +{ + switch (value) { + case registration_result_type_::access_3_gpp: + return "access 3GPP"; + case registration_result_type_::non_3_gpp_access: + return "Non-3GPP access"; + case registration_result_type_::access_3_gpp_and_non_3_gpp_access: + return "access 3GPP and non-3GPP access"; + case registration_result_type_::reserved: + return "reserved"; + default: + return "Invalid Choice"; + } +} // IE: PLMN list // Reference: 9.11.3.45 SRSASN_CODE plmn_list_t::pack(asn1::bit_ref& bref) @@ -1961,6 +2284,21 @@ SRSASN_CODE tracking_area_identity_list_5gs_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* tracking_area_identity_list_5gs_t::type_of_list_type_::to_string() const +{ + switch (value) { + case type_of_list_type_::list_of_ta_cs_belonging_to_one_plmn_or_snpn_with_non_consecutive_tac_values: + return "list of TACs belonging to one PLMN or SNPN, with non-consecutive TAC values"; + case type_of_list_type_::list_of_ta_cs_belonging_to_one_plmn_or_snpn_with_consecutive_tac_values: + return "list of TACs belonging to one PLMN or SNPN, with consecutive TAC values"; + case type_of_list_type_::list_of_ta_is_belonging_to_different_plm_ns: + return "list of TAIs belonging to different PLMNs "; + case type_of_list_type_::reserved: + return "Reserved"; + default: + return "Invalid Choice"; + } +} // IE: Rejected NSSAI // Reference: 9.11.3.46 SRSASN_CODE rejected_nssai_t::pack(asn1::bit_ref& bref) @@ -2423,6 +2761,21 @@ SRSASN_CODE nssai_inclusion_mode_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* nssai_inclusion_mode_t::NSSAI_inclusion_mode_type_::to_string() const +{ + switch (value) { + case NSSAI_inclusion_mode_type_::nssai_inclusion_mode_a: + return "NSSAI inclusion mode A"; + case NSSAI_inclusion_mode_type_::nssai_inclusion_mode_b: + return "NSSAI inclusion mode B"; + case NSSAI_inclusion_mode_type_::nssai_inclusion_mode_c: + return "NSSAI inclusion mode C"; + case NSSAI_inclusion_mode_type_::nssai_inclusion_mode_d: + return "NSSAI inclusion mode D"; + default: + return "Invalid Choice"; + } +} // IE: Operator-defined access category definitions // Reference: 9.11.3.38 SRSASN_CODE operator_defined_access_category_definitions_t::pack(asn1::bit_ref& bref) @@ -2491,6 +2844,17 @@ SRSASN_CODE ue_radio_capability_id_deletion_indication_t::unpack(asn1::cbit_ref& return SRSASN_SUCCESS; } +const char* ue_radio_capability_id_deletion_indication_t::Deletion_request_type_::to_string() const +{ + switch (value) { + case Deletion_request_type_::ue_radio_capability_id_deletion_not_requested: + return "UE radio capability ID deletion not requested"; + case Deletion_request_type_::network_assigned_ue_radio_capability_i_ds_deletion_requested: + return "Network-assigned UE radio capability IDs deletion requested"; + default: + return "Invalid Choice"; + } +} // IE: Ciphering key data // Reference: 9.11.3.18C SRSASN_CODE ciphering_key_data_t::pack(asn1::bit_ref& bref) @@ -2615,6 +2979,97 @@ SRSASN_CODE cause_5gmm_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* cause_5gmm_t::cause_5gmm_type_::to_string() const +{ + switch (value) { + case cause_5gmm_type_::illegal_ue: + return "Illegal UE"; + case cause_5gmm_type_::pei_not_accepted: + return "PEI not accepted"; + case cause_5gmm_type_::illegal_me: + return "Illegal ME"; + case cause_5gmm_type_::services_not_allowed_5gs: + return "Services not allowed 5GS"; + case cause_5gmm_type_::ue_identity_cannot_be_derived_by_the_network: + return "UE identity cannot be derived by the network"; + case cause_5gmm_type_::implicitly_de_registered: + return "Implicitly de-registered"; + case cause_5gmm_type_::plmn_not_allowed: + return "PLMN not allowed"; + case cause_5gmm_type_::tracking_area_not_allowed: + return "Tracking area not allowed"; + case cause_5gmm_type_::roaming_not_allowed_in_this_tracking_area: + return "Roaming not allowed in this tracking area"; + case cause_5gmm_type_::no_suitable_cells_in_tracking_area: + return "No suitable cells in tracking area"; + case cause_5gmm_type_::mac_failure: + return "MAC failure"; + case cause_5gmm_type_::synch_failure: + return "Synch failure"; + case cause_5gmm_type_::congestion: + return "Congestion"; + case cause_5gmm_type_::ue_security_capabilities_mismatch: + return "UE security capabilities mismatch"; + case cause_5gmm_type_::security_mode_rejected_unspecified: + return "Security mode rejected, unspecified"; + case cause_5gmm_type_::non_5g_authentication_unacceptable: + return "Non-5G authentication unacceptable"; + case cause_5gmm_type_::n1_mode_not_allowed: + return "N1 mode not allowed"; + case cause_5gmm_type_::restricted_service_area: + return "Restricted service area"; + case cause_5gmm_type_::redirection_to_epc_required: + return "Redirection to EPC required"; + case cause_5gmm_type_::ladn_not_available: + return "LADN not available"; + case cause_5gmm_type_::no_network_slices_available: + return "No network slices available"; + case cause_5gmm_type_::maximum_number_of_pdu_sessions_reached_: + return "Maximum number of PDU sessions reached "; + case cause_5gmm_type_::insufficient_resources_for_specific_slice_and_dnn: + return "Insufficient resources for specific slice and DNN"; + case cause_5gmm_type_::insufficient_resources_for_specific_slice: + return "Insufficient resources for specific slice "; + case cause_5gmm_type_::ng_ksi_already_in_use: + return "ngKSI already in use"; + case cause_5gmm_type_::non_3_gpp_access_to_5gcn_not_allowed: + return "Non-3GPP access to 5GCN not allowed"; + case cause_5gmm_type_::serving_network_not_authorized: + return "Serving network not authorized"; + case cause_5gmm_type_::temporarily_not_authorized_for_this_snpn: + return "Temporarily not authorized for this SNPN "; + case cause_5gmm_type_::permanently_not_authorized_for_this_snpn: + return "Permanently not authorized for this SNPN"; + case cause_5gmm_type_::not_authorized_for_this_cag_or_authorized_for_cag_cells_only: + return "Not authorized for this CAG or authorized for CAG cells only"; + case cause_5gmm_type_::wireline_access_area_not_allowed: + return "Wireline access area not allowed"; + case cause_5gmm_type_::payload_was_not_forwarded: + return "Payload was not forwarded"; + case cause_5gmm_type_::dnn_not_supported_or_not_subscribed_in_the_slice: + return "DNN not supported or not subscribed in the slice"; + case cause_5gmm_type_::insufficient_user_plane_resources_for_the_pdu_session: + return "Insufficient user-plane resources for the PDU session"; + case cause_5gmm_type_::semantically_incorrect_message: + return "Semantically incorrect message"; + case cause_5gmm_type_::invalid_mandatory_information: + return "Invalid mandatory information"; + case cause_5gmm_type_::message_type_non_existent_or_not_implemented: + return "Message type non-existent or not implemented"; + case cause_5gmm_type_::message_type_not_compatible_with_the_protocol_state: + return "Message type not compatible with the protocol state"; + case cause_5gmm_type_::information_element_non_existent_or_not_implemented: + return "Information element non-existent or not implemented"; + case cause_5gmm_type_::conditional_ie_error: + return "Conditional IE error"; + case cause_5gmm_type_::message_not_compatible_with_the_protocol_state: + return "Message not compatible with the protocol state"; + case cause_5gmm_type_::protocol_error_unspecified: + return "Protocol error, unspecified"; + default: + return "Invalid Choice"; + } +} // IE: De-registration type // Reference: 9.11.3.20 SRSASN_CODE de_registration_type_t::pack(asn1::bit_ref& bref) @@ -2635,6 +3090,41 @@ SRSASN_CODE de_registration_type_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* de_registration_type_t::switch_off_type_::to_string() const +{ + switch (value) { + case switch_off_type_::normal_de_registration: + return "Normal de-registration"; + case switch_off_type_::switch_off: + return "Switch Off"; + default: + return "Invalid Choice"; + } +} +const char* de_registration_type_t::re_registration_required_type_::to_string() const +{ + switch (value) { + case re_registration_required_type_::re_registration_not_required: + return "re-registration not required"; + case re_registration_required_type_::re_registration_required: + return "re-registration required"; + default: + return "Invalid Choice"; + } +} +const char* de_registration_type_t::access_type_type_::to_string() const +{ + switch (value) { + case access_type_type_::access_3_gpp: + return "access 3GPP"; + case access_type_type_::non_3_gpp_access: + return "Non-3GPP access"; + case access_type_type_::access_3_gpp_and_non_3_gpp_access: + return "access 3GPP and non-3GPP access"; + default: + return "Invalid Choice"; + } +} // IE: Spare half octet // Reference: 9.5 SRSASN_CODE spare_half_octet_t::pack(asn1::bit_ref& bref) @@ -2669,6 +3159,37 @@ SRSASN_CODE service_type_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* service_type_t::Service_type_value_type_::to_string() const +{ + switch (value) { + case Service_type_value_type_::signalling: + return "signalling"; + case Service_type_value_type_::data: + return "data"; + case Service_type_value_type_::mobile_terminated_services: + return "mobile terminated services"; + case Service_type_value_type_::emergency_services: + return "emergency services"; + case Service_type_value_type_::emergency_services_fallback: + return "emergency services fallback"; + case Service_type_value_type_::high_priority_access: + return "high priority access"; + case Service_type_value_type_::elevated_signalling: + return "elevated signalling"; + case Service_type_value_type_::unused_shall_be_interpreted_as_signalling: + return "unused shall be interpreted as signalling"; + case Service_type_value_type_::unused_shall_be_interpreted_as_signalling_1: + return "unused shall be interpreted as signalling_1"; + case Service_type_value_type_::unused_shall_be_interpreted_as_data: + return "unused shall be interpreted as data"; + case Service_type_value_type_::unused_shall_be_interpreted_as_data_1: + return "unused shall be interpreted as data_1"; + case Service_type_value_type_::unused_shall_be_interpreted_as_data_2: + return "unused shall be interpreted as data_2"; + default: + return "Invalid Choice"; + } +} // IE: Configuration update indication // Reference: 9.11.3.18 SRSASN_CODE configuration_update_indication_t::pack(asn1::bit_ref& bref) @@ -2689,6 +3210,21 @@ SRSASN_CODE configuration_update_indication_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* configuration_update_indication_t::control_plane_service_type_value_type_::to_string() const +{ + switch (value) { + case control_plane_service_type_value_type_::mobile_originating_request: + return "mobile originating request"; + case control_plane_service_type_value_type_::mobile_terminating_request: + return "mobile terminating request"; + case control_plane_service_type_value_type_::emergency_services: + return "emergency services"; + case control_plane_service_type_value_type_::emergency_services_fallback: + return "emergency services fallback"; + default: + return "Invalid Choice"; + } +} // IE: Network name // Reference: 9.11.3.35 SRSASN_CODE network_name_t::pack(asn1::bit_ref& bref) @@ -2797,6 +3333,21 @@ SRSASN_CODE daylight_saving_time_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* daylight_saving_time_t::value_type_::to_string() const +{ + switch (value) { + case value_type_::no_adjustment_for_daylight_saving_time: + return "No adjustment for Daylight Saving Time"; + case value_type_::hour_1_adjustment_for_daylight_saving_time: + return "hour 1 adjustment for Daylight Saving Time"; + case value_type_::hours_2_adjustment_for_daylight_saving_time: + return "hours 2 adjustment for Daylight Saving Time"; + case value_type_::reserved: + return "Reserved"; + default: + return "Invalid Choice"; + } +} // IE: SMS indication // Reference: 9.11.3.50A SRSASN_CODE sms_indication_t::pack(asn1::bit_ref& bref) @@ -2837,6 +3388,17 @@ SRSASN_CODE additional_configuration_indication_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* additional_configuration_indication_t::SCMR_type_::to_string() const +{ + switch (value) { + case SCMR_type_::no_additional_information: + return "no additional information"; + case SCMR_type_::release_of_n1_nas_signalling_connection_not_required: + return "release of N1 NAS signalling connection not required"; + default: + return "Invalid Choice"; + } +} // IE: ABBA // Reference: 9.11.3.10 SRSASN_CODE abba_t::pack(asn1::bit_ref& bref) @@ -3022,6 +3584,27 @@ SRSASN_CODE identity_type_5gs_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* identity_type_5gs_t::identity_types_::to_string() const +{ + switch (value) { + case identity_types_::suci: + return "SUCI"; + case identity_types_::guti_5g: + return "5G-GUTI"; + case identity_types_::imei: + return "IMEI"; + case identity_types_::s_tmsi_5g: + return "5G-S-TMSI"; + case identity_types_::imeisv: + return "IMEISV"; + case identity_types_::mac_address: + return "MAC address"; + case identity_types_::eui_64: + return "EUI-64"; + default: + return "Invalid Choice"; + } +} // IE: security algorithms // Reference: 9.11.3.34 SRSASN_CODE security_algorithms_t::pack(asn1::bit_ref& bref) @@ -3040,6 +3623,52 @@ SRSASN_CODE security_algorithms_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* security_algorithms_t::integrity_protection_algorithm_type_::to_string() const +{ + switch (value) { + case integrity_protection_algorithm_type_::ia0_5g: + return "IA0-5G"; + case integrity_protection_algorithm_type_::ia1_128_5g: + return "IA1-128-5G"; + case integrity_protection_algorithm_type_::ia2_128_5g: + return "IA2-128-5G"; + case integrity_protection_algorithm_type_::ia3_128_5g: + return "IA3-128-5G"; + case integrity_protection_algorithm_type_::ia4_5g: + return "IA4-5G"; + case integrity_protection_algorithm_type_::ia5_5g: + return "IA5-5G"; + case integrity_protection_algorithm_type_::ia6_5g: + return "IA6-5G"; + case integrity_protection_algorithm_type_::ia7_5g: + return "IA7-5G"; + default: + return "Invalid Choice"; + } +} +const char* security_algorithms_t::ciphering_algorithm_type_::to_string() const +{ + switch (value) { + case ciphering_algorithm_type_::ea0_5g: + return "EA0-5G"; + case ciphering_algorithm_type_::ea1_128_5g: + return "EA1-128-5G"; + case ciphering_algorithm_type_::ea2_128_5g: + return "EA2-128-5G"; + case ciphering_algorithm_type_::ea3_128_5g: + return "EA3-128-5G"; + case ciphering_algorithm_type_::ea4_5g: + return "EA4-5G"; + case ciphering_algorithm_type_::ea5_5g: + return "EA5-5G"; + case ciphering_algorithm_type_::ea6_5g: + return "EA6-5G"; + case ciphering_algorithm_type_::ea7_5g: + return "EA7-5G"; + default: + return "Invalid Choice"; + } +} // IE: IMEISV request // Reference: 9.11.3.28 SRSASN_CODE imeisv_request_t::pack(asn1::bit_ref& bref) @@ -3060,6 +3689,17 @@ SRSASN_CODE imeisv_request_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* imeisv_request_t::imeisv_request_type_::to_string() const +{ + switch (value) { + case imeisv_request_type_::imeisv_not_requested: + return "IMEISV not requested "; + case imeisv_request_type_::imeisv_requested: + return "IMEISV requested "; + default: + return "Invalid Choice"; + } +} // IE: EPS NAS security algorithms // Reference: 9.11.3.25 SRSASN_CODE eps_nas_security_algorithms_t::pack(asn1::bit_ref& bref) @@ -3087,6 +3727,52 @@ SRSASN_CODE eps_nas_security_algorithms_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* eps_nas_security_algorithms_t::integrity_protection_algorithm_type_::to_string() const +{ + switch (value) { + case integrity_protection_algorithm_type_::eia0: + return "EIA0"; + case integrity_protection_algorithm_type_::eia1_128: + return "EIA1-128"; + case integrity_protection_algorithm_type_::eia2_128: + return "EIA2-128"; + case integrity_protection_algorithm_type_::eia3_128: + return "EIA3-128"; + case integrity_protection_algorithm_type_::eia4: + return "EIA4"; + case integrity_protection_algorithm_type_::eia5: + return "EIA5"; + case integrity_protection_algorithm_type_::eia6: + return "EIA6"; + case integrity_protection_algorithm_type_::eia7: + return "EIA7"; + default: + return "Invalid Choice"; + } +} +const char* eps_nas_security_algorithms_t::ciphering_algorithm_type_::to_string() const +{ + switch (value) { + case ciphering_algorithm_type_::eea0: + return "EEA0"; + case ciphering_algorithm_type_::eea1_128: + return "EEA1-128"; + case ciphering_algorithm_type_::eea2_128: + return "EEA2-128"; + case ciphering_algorithm_type_::eea3_128: + return "EEA3-128"; + case ciphering_algorithm_type_::eea4: + return "EEA4"; + case ciphering_algorithm_type_::eea5: + return "EEA5"; + case ciphering_algorithm_type_::eea6: + return "EEA6"; + case ciphering_algorithm_type_::eea7: + return "EEA7"; + default: + return "Invalid Choice"; + } +} // IE: Additional 5G security information // Reference: 9.11.3.12 SRSASN_CODE additional_5g_security_information_t::pack(asn1::bit_ref& bref) @@ -3287,6 +3973,17 @@ SRSASN_CODE access_type_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* access_type_t::Access_type_value_type_::to_string() const +{ + switch (value) { + case Access_type_value_type_::access_3_gpp: + return "access_3GPP"; + case Access_type_value_type_::non_3_gpp_access: + return "Non_3GPP_access"; + default: + return "Invalid Choice"; + } +} // IE: PDU session identity 2 // Reference: 9.11.3.41 SRSASN_CODE pdu_session_identity_2_t::pack(asn1::bit_ref& bref) @@ -3323,6 +4020,27 @@ SRSASN_CODE request_type_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* request_type_t::Request_type_value_type_::to_string() const +{ + switch (value) { + case Request_type_value_type_::initial_request: + return "initial request"; + case Request_type_value_type_::existing_pdu_session: + return "existing PDU session"; + case Request_type_value_type_::initial_emergency_request: + return "initial emergency request"; + case Request_type_value_type_::existing_emergency_pdu_session: + return "existing emergency PDU session"; + case Request_type_value_type_::modification_request: + return "modification request"; + case Request_type_value_type_::ma_pdu_request: + return "MA PDU request"; + case Request_type_value_type_::reserved: + return "reserved "; + default: + return "Invalid Choice"; + } +} // IE: S-NSSAI // Reference: 9.11.2.8 SRSASN_CODE s_nssai_t::pack(asn1::bit_ref& bref) @@ -3394,6 +4112,23 @@ SRSASN_CODE s_nssai_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* s_nssai_t::SST_type_::to_string() const +{ + switch (value) { + case SST_type_::sst: + return "SST"; + case SST_type_::sst_and_mapped_hplmn_sst: + return "SST and mapped HPLMN SST"; + case SST_type_::sst_and_sd: + return "SST and SD"; + case SST_type_::sst_sd_and_mapped_hplmn_sst: + return "SST, SD and mapped HPLMN SST"; + case SST_type_::sst_sd_mapped_hplmn_sst_and_mapped_hplmn_sd: + return "SST, SD, mapped HPLMN SST and mapped HPLMN SD"; + default: + return "Invalid Choice"; + } +} // IE: DNN // Reference: 9.11.2.1B SRSASN_CODE dnn_t::pack(asn1::bit_ref& bref) @@ -3481,6 +4216,15 @@ SRSASN_CODE ma_pdu_session_information_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* ma_pdu_session_information_t::MA_PDU_session_information_value_type_::to_string() const +{ + switch (value) { + case MA_PDU_session_information_value_type_::ma_pdu_session_network_upgrade_is_allowed: + return "MA PDU session network upgrade is allowed"; + default: + return "Invalid Choice"; + } +} // IE: Release assistance indication // Reference: 9.11.3.46A SRSASN_CODE release_assistance_indication_t::pack(asn1::bit_ref& bref) @@ -3501,6 +4245,21 @@ SRSASN_CODE release_assistance_indication_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* release_assistance_indication_t::Downlink_data_expected_type_::to_string() const +{ + switch (value) { + case Downlink_data_expected_type_::no_information_regarding_ddx_is_conveyed: + return "No information regarding DDX is conveyed"; + case Downlink_data_expected_type_::no_further_uplink_and_no_further_downlink_data: + return "No further uplink and no further downlink data"; + case Downlink_data_expected_type_::only_a_single_downlink_data_transmission: + return "Only a single downlink data transmission"; + case Downlink_data_expected_type_::reserved: + return "reserved"; + default: + return "Invalid Choice"; + } +} // IE: Integrity protection maximum data rate // Reference: 9.11.4.7 SRSASN_CODE integrity_protection_maximum_data_rate_t::pack(asn1::bit_ref& bref) @@ -3519,6 +4278,32 @@ SRSASN_CODE integrity_protection_maximum_data_rate_t::unpack(asn1::cbit_ref& bre return SRSASN_SUCCESS; } +const char* integrity_protection_maximum_data_rate_t::max_data_rate_UPIP_uplink_type_::to_string() const +{ + switch (value) { + case max_data_rate_UPIP_uplink_type_::kbps_64: + return "kbps 64"; + case max_data_rate_UPIP_uplink_type_::null: + return "NULL"; + case max_data_rate_UPIP_uplink_type_::full_data_rate: + return "Full data rate"; + default: + return "Invalid Choice"; + } +} +const char* integrity_protection_maximum_data_rate_t::max_data_rate_UPIP_downlink_type_::to_string() const +{ + switch (value) { + case max_data_rate_UPIP_downlink_type_::kbps_64: + return "kbps 64"; + case max_data_rate_UPIP_downlink_type_::null: + return "NULL"; + case max_data_rate_UPIP_downlink_type_::full_data_rate: + return "Full data rate"; + default: + return "Invalid Choice"; + } +} // IE: PDU session type // Reference: 9.11.4.11 SRSASN_CODE pdu_session_type_t::pack(asn1::bit_ref& bref) @@ -3539,6 +4324,25 @@ SRSASN_CODE pdu_session_type_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* pdu_session_type_t::PDU_session_type_value_type_::to_string() const +{ + switch (value) { + case PDU_session_type_value_type_::ipv4: + return "ipv4"; + case PDU_session_type_value_type_::ipv6: + return "ipv6"; + case PDU_session_type_value_type_::ipv4v6: + return "ipv4v6"; + case PDU_session_type_value_type_::unstructured: + return "Unstructured"; + case PDU_session_type_value_type_::ethernet: + return "Ethernet"; + case PDU_session_type_value_type_::reserved: + return "reserved "; + default: + return "Invalid Choice"; + } +} // IE: SSC mode // Reference: 9.11.4.16 SRSASN_CODE ssc_mode_t::pack(asn1::bit_ref& bref) @@ -3559,6 +4363,27 @@ SRSASN_CODE ssc_mode_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* ssc_mode_t::SSC_mode_value_type_::to_string() const +{ + switch (value) { + case SSC_mode_value_type_::ssc_mode_1: + return "SSC mode 1"; + case SSC_mode_value_type_::ssc_mode_2: + return "SSC mode 2"; + case SSC_mode_value_type_::ssc_mode_3: + return "SSC mode 3"; + case SSC_mode_value_type_::unused_or_ssc_mode_1: + return "unused or SSC mode 1"; + case SSC_mode_value_type_::unused_or_ssc_mode_2: + return "unused or SSC mode 2"; + case SSC_mode_value_type_::unused_or_ssc_mode_3: + return "unused or SSC mode 3"; + case SSC_mode_value_type_::reserved: + return "reserved"; + default: + return "Invalid Choice"; + } +} // IE: 5GSM capability // Reference: 9.11.4.1 SRSASN_CODE capability_5gsm_t::pack(asn1::bit_ref& bref) @@ -3921,6 +4746,19 @@ SRSASN_CODE ethernet_header_compression_configuration_t::unpack(asn1::cbit_ref& return SRSASN_SUCCESS; } +const char* ethernet_header_compression_configuration_t::CID_Length_type_::to_string() const +{ + switch (value) { + case CID_Length_type_::ethernet_header_compression_not_used: + return "Ethernet header compression not used"; + case CID_Length_type_::bits_7: + return "bits_7"; + case CID_Length_type_::bits_15: + return "bits_15"; + default: + return "Invalid Choice"; + } +} // IE: PDU address // Reference: 9.11.4.10 SRSASN_CODE pdu_address_t::pack(asn1::bit_ref& bref) @@ -3999,6 +4837,19 @@ SRSASN_CODE pdu_address_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* pdu_address_t::PDU_session_type_value_type_::to_string() const +{ + switch (value) { + case PDU_session_type_value_type_::ipv4: + return "ipv4"; + case PDU_session_type_value_type_::ipv6: + return "ipv6"; + case PDU_session_type_value_type_::ipv4v6: + return "ipv4v6"; + default: + return "Invalid Choice"; + } +} // IE: QoS rules // Reference: 9.11.4.13 SRSASN_CODE qo_s_rules_t::pack(asn1::bit_ref& bref) @@ -4075,6 +4926,65 @@ SRSASN_CODE session_ambr_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* session_ambr_t::unit_session_AMBR_type_::to_string() const +{ + switch (value) { + case unit_session_AMBR_type_::not_used: + return "not used"; + case unit_session_AMBR_type_::inc_by_1_kbps: + return "inc by 1 Kbps"; + case unit_session_AMBR_type_::inc_by_4_kbps: + return "inc by 4 Kbps"; + case unit_session_AMBR_type_::inc_by_16_kbps: + return "inc by 16 Kbps"; + case unit_session_AMBR_type_::inc_by_64_kbps: + return "inc by 64 Kbps"; + case unit_session_AMBR_type_::inc_by_256_kbps: + return "inc by 256 kbps"; + case unit_session_AMBR_type_::inc_by_1_mbps: + return "inc by 1 Mbps"; + case unit_session_AMBR_type_::inc_by_4_mbps: + return "inc by 4 Mbps"; + case unit_session_AMBR_type_::inc_by_16_mbps: + return "inc by 16 Mbps"; + case unit_session_AMBR_type_::inc_by_64_mbps: + return "inc by 64 Mbps"; + case unit_session_AMBR_type_::inc_by_256_mbps: + return "inc by 256 Mbps"; + case unit_session_AMBR_type_::inc_by_1_gbps: + return "inc by 1 Gbps"; + case unit_session_AMBR_type_::inc_by_4_gbps: + return "inc by 4 Gbps"; + case unit_session_AMBR_type_::inc_by_16_gbps: + return "inc by 16 Gbps"; + case unit_session_AMBR_type_::inc_by_64_gbps: + return "inc by 64 Gbps"; + case unit_session_AMBR_type_::inc_by_256_gbps: + return "inc by 256 Gbps"; + case unit_session_AMBR_type_::inc_by_1_tbps: + return "inc by 1 Tbps"; + case unit_session_AMBR_type_::inc_by_4_tbps: + return "inc by 4 Tbps"; + case unit_session_AMBR_type_::inc_by_16_tbps: + return "inc by 16 Tbps"; + case unit_session_AMBR_type_::inc_by_64_tbps: + return "inc by 64 Tbps"; + case unit_session_AMBR_type_::inc_by_256_tbps: + return "inc by 256 Tbps"; + case unit_session_AMBR_type_::inc_by_1_pbps: + return "inc by 1 Pbps"; + case unit_session_AMBR_type_::inc_by_4_pbps: + return "inc by 4 Pbps"; + case unit_session_AMBR_type_::inc_by_16_pbps: + return "inc by 16 Pbps"; + case unit_session_AMBR_type_::inc_by_64_pbps: + return "inc by 64 Pbps"; + case unit_session_AMBR_type_::inc_by_256_pbps: + return "inc by 256 Pbps"; + default: + return "Invalid Choice"; + } +} // IE: 5GSM cause // Reference: 9.11.4.2 SRSASN_CODE cause_5gsm_t::pack(asn1::bit_ref& bref) @@ -4184,7 +5094,6 @@ const char* cause_5gsm_t::cause_value_type_::to_string() const return "Invalid Choice"; } } - // IE: GPRS timer // Reference: 9.11.2.3 SRSASN_CODE gprs_timer_t::pack(asn1::bit_ref& bref) @@ -4203,6 +5112,21 @@ SRSASN_CODE gprs_timer_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* gprs_timer_t::Unit_type_::to_string() const +{ + switch (value) { + case Unit_type_::value_is_incremented_in_multiples_of_2_seconds: + return "value is incremented in multiples of 2 seconds "; + case Unit_type_::value_is_incremented_in_multiples_of_1_minute: + return "value is incremented in multiples of 1 minute"; + case Unit_type_::value_is_incremented_in_multiples_of_decihours: + return "value is incremented in multiples of decihours"; + case Unit_type_::value_indicates_that_the_timer_is_deactivated: + return "value indicates that the timer is deactivated"; + default: + return "Invalid Choice"; + } +} // IE: Always-on PDU session indication // Reference: 9.11.4.3 SRSASN_CODE always_on_pdu_session_indication_t::pack(asn1::bit_ref& bref) @@ -4350,6 +5274,17 @@ SRSASN_CODE network_feature_support_5gsm_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* network_feature_support_5gsm_t::EPT_S1_type_::to_string() const +{ + switch (value) { + case EPT_S1_type_::ethernet_pdn_type_in_s1_mode_not_supported: + return "Ethernet PDN type in S1 mode not supported"; + case EPT_S1_type_::ethernet_pdn_type_in_s1_mode_supported: + return "Ethernet PDN type in S1 mode supported"; + default: + return "Invalid Choice"; + } +} // IE: Serving PLMN rate control // Reference: 9.11.4.20 SRSASN_CODE serving_plmn_rate_control_t::pack(asn1::bit_ref& bref) @@ -4481,6 +5416,17 @@ SRSASN_CODE congestion_re_attempt_indicator_5gsm_t::unpack(asn1::cbit_ref& bref) return SRSASN_SUCCESS; } +const char* congestion_re_attempt_indicator_5gsm_t::abo_type_::to_string() const +{ + switch (value) { + case abo_type_::the_back_off_timer_is_applied_in_the_registered_plmn: + return "The back-off timer is applied in the registered PLMN"; + case abo_type_::the_back_off_timer_is_applied_in_all_plm_ns: + return "The back-off timer is applied in all PLMNs"; + default: + return "Invalid Choice"; + } +} // IE: Re-attempt indicator // Reference: 9.11.4.17 SRSASN_CODE re_attempt_indicator_t::pack(asn1::bit_ref& bref) From 28493ec55326126ee39031caa1fa9fba8b4b1d53 Mon Sep 17 00:00:00 2001 From: Bedran Karakoc Date: Wed, 16 Feb 2022 13:56:38 +0100 Subject: [PATCH 003/195] lib,nas_5g: Remove whitespaces in strings --- lib/src/asn1/nas_5g_ies.cc | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/src/asn1/nas_5g_ies.cc b/lib/src/asn1/nas_5g_ies.cc index 65aba7dcf..f3c679bb2 100644 --- a/lib/src/asn1/nas_5g_ies.cc +++ b/lib/src/asn1/nas_5g_ies.cc @@ -63,7 +63,7 @@ const char* registration_type_5gs_t::follow_on_request_bit_type_::to_string() co { switch (value) { case follow_on_request_bit_type_::no_follow_on_request_pending: - return "no_follow_on_request_pending "; + return "no_follow_on_request_pending"; case follow_on_request_bit_type_::follow_on_request_pending: return "follow_on_request_pending"; default: @@ -1227,7 +1227,7 @@ const char* ue_usage_setting_t::UE_usage_setting_type_::to_string() const case UE_usage_setting_type_::voice_centric: return "voice centric"; case UE_usage_setting_type_::data_centric: - return "data centric "; + return "data centric"; default: return "Invalid Choice"; } @@ -1278,13 +1278,13 @@ const char* drx_parameters_5gs_t::drx_value_type_::to_string() const case drx_value_type_::drx_value_not_specified: return "DRX value not specified"; case drx_value_type_::drx_cycle_parameter_t_32: - return "DRX cycle parameter T 32 "; + return "DRX cycle parameter T 32"; case drx_value_type_::drx_cycle_parameter_t_64: - return "DRX cycle parameter T 64 "; + return "DRX cycle parameter T 64"; case drx_value_type_::drx_cycle_parameter_t_128: - return "DRX cycle parameter T 128 "; + return "DRX cycle parameter T 128"; case drx_value_type_::drx_cycle_parameter_t_256: - return "DRX cycle parameter T 256 "; + return "DRX cycle parameter T 256"; default: return "Invalid Choice"; } @@ -2292,7 +2292,7 @@ const char* tracking_area_identity_list_5gs_t::type_of_list_type_::to_string() c case type_of_list_type_::list_of_ta_cs_belonging_to_one_plmn_or_snpn_with_consecutive_tac_values: return "list of TACs belonging to one PLMN or SNPN, with consecutive TAC values"; case type_of_list_type_::list_of_ta_is_belonging_to_different_plm_ns: - return "list of TAIs belonging to different PLMNs "; + return "list of TAIs belonging to different PLMNs"; case type_of_list_type_::reserved: return "Reserved"; default: @@ -3025,11 +3025,11 @@ const char* cause_5gmm_t::cause_5gmm_type_::to_string() const case cause_5gmm_type_::no_network_slices_available: return "No network slices available"; case cause_5gmm_type_::maximum_number_of_pdu_sessions_reached_: - return "Maximum number of PDU sessions reached "; + return "Maximum number of PDU sessions reached"; case cause_5gmm_type_::insufficient_resources_for_specific_slice_and_dnn: return "Insufficient resources for specific slice and DNN"; case cause_5gmm_type_::insufficient_resources_for_specific_slice: - return "Insufficient resources for specific slice "; + return "Insufficient resources for specific slice"; case cause_5gmm_type_::ng_ksi_already_in_use: return "ngKSI already in use"; case cause_5gmm_type_::non_3_gpp_access_to_5gcn_not_allowed: @@ -3037,7 +3037,7 @@ const char* cause_5gmm_t::cause_5gmm_type_::to_string() const case cause_5gmm_type_::serving_network_not_authorized: return "Serving network not authorized"; case cause_5gmm_type_::temporarily_not_authorized_for_this_snpn: - return "Temporarily not authorized for this SNPN "; + return "Temporarily not authorized for this SNPN"; case cause_5gmm_type_::permanently_not_authorized_for_this_snpn: return "Permanently not authorized for this SNPN"; case cause_5gmm_type_::not_authorized_for_this_cag_or_authorized_for_cag_cells_only: @@ -3693,9 +3693,9 @@ const char* imeisv_request_t::imeisv_request_type_::to_string() const { switch (value) { case imeisv_request_type_::imeisv_not_requested: - return "IMEISV not requested "; + return "IMEISV not requested"; case imeisv_request_type_::imeisv_requested: - return "IMEISV requested "; + return "IMEISV requested"; default: return "Invalid Choice"; } @@ -4036,7 +4036,7 @@ const char* request_type_t::Request_type_value_type_::to_string() const case Request_type_value_type_::ma_pdu_request: return "MA PDU request"; case Request_type_value_type_::reserved: - return "reserved "; + return "reserved"; default: return "Invalid Choice"; } @@ -4338,7 +4338,7 @@ const char* pdu_session_type_t::PDU_session_type_value_type_::to_string() const case PDU_session_type_value_type_::ethernet: return "Ethernet"; case PDU_session_type_value_type_::reserved: - return "reserved "; + return "reserved"; default: return "Invalid Choice"; } @@ -5116,7 +5116,7 @@ const char* gprs_timer_t::Unit_type_::to_string() const { switch (value) { case Unit_type_::value_is_incremented_in_multiples_of_2_seconds: - return "value is incremented in multiples of 2 seconds "; + return "value is incremented in multiples of 2 seconds"; case Unit_type_::value_is_incremented_in_multiples_of_1_minute: return "value is incremented in multiples of 1 minute"; case Unit_type_::value_is_incremented_in_multiples_of_decihours: From acecb1c303f43eb1e52c8b6c53b93938dc3f016e Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Fri, 18 Feb 2022 11:07:16 +0100 Subject: [PATCH 004/195] emergency_handlers: increase max handlers to 256 in the ttcn3_dut application many "virtuaL" UE instances are created and destroyed when executing the tests. With the previous limit of 12 handlers the application stopped after a few tests. With the limit raised to 256 we should be able to run all white_listed TTCN3 tests for the UE without problems. This fixes #3886 --- lib/src/support/emergency_handlers.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/support/emergency_handlers.cc b/lib/src/support/emergency_handlers.cc index fb170f848..6d6a5556e 100644 --- a/lib/src/support/emergency_handlers.cc +++ b/lib/src/support/emergency_handlers.cc @@ -26,7 +26,7 @@ struct handler_instance { // Handlers are added in a thread safe manner without using locks to avoid possible issues if a signal is emitted while // modifying the callback array. -static constexpr unsigned max_handlers = 12; +static constexpr unsigned max_handlers = 256; static handler_instance registered_handlers[max_handlers]; static std::atomic num_handlers; From 3a73d84294059117ccfbcb45039020fbb58ec2d2 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Mon, 14 Feb 2022 14:08:36 +0100 Subject: [PATCH 005/195] srsran_rf: support dynamic loading of RF plugins This adds flexible plugin-like loading of the individual RF implementations (zmq, uhd, soapy,...) as per availability at runtime. Unlike before, the binary application is not terminated if the dependencies of individual RF interfaces are not installed on the executing system. To do this, the plugins are not linked by the linker at compile time, but opened at runtime via dlopen() and dropped in case of failure due to missing dependencies. --- CMakeLists.txt | 1 + lib/examples/CMakeLists.txt | 2 +- lib/include/srsran/phy/rf/rf.h | 67 ++++++++ lib/src/phy/rf/CMakeLists.txt | 127 +++++++++----- lib/src/phy/rf/rf_blade_imp.c | 42 +++++ lib/src/phy/rf/rf_blade_imp.h | 7 + lib/src/phy/rf/rf_dev.h | 303 +++++---------------------------- lib/src/phy/rf/rf_file_imp.c | 33 +++- lib/src/phy/rf/rf_file_imp.h | 2 + lib/src/phy/rf/rf_imp.c | 129 +++++++++++--- lib/src/phy/rf/rf_plugin.h | 28 +++ lib/src/phy/rf/rf_skiq_imp.c | 41 +++++ lib/src/phy/rf/rf_skiq_imp.h | 2 + lib/src/phy/rf/rf_soapy_imp.c | 42 +++++ lib/src/phy/rf/rf_soapy_imp.h | 2 + lib/src/phy/rf/rf_uhd_imp.cc | 42 +++++ lib/src/phy/rf/rf_uhd_imp.h | 2 + lib/src/phy/rf/rf_zmq_imp.c | 42 +++++ lib/src/phy/rf/rf_zmq_imp.h | 2 + lib/src/phy/rf/rf_zmq_test.c | 1 + 20 files changed, 594 insertions(+), 323 deletions(-) create mode 100644 lib/src/phy/rf/rf_plugin.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 018b3e9e2..7764f3c50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,6 +57,7 @@ option(DISABLE_SIMD "Disable SIMD instructions" OFF) option(AUTO_DETECT_ISA "Autodetect supported ISA extensions" ON) option(ENABLE_GUI "Enable GUI (using srsGUI)" ON) +option(ENABLE_RF_PLUGINS "Enable RF plugins" ON) option(ENABLE_UHD "Enable UHD" ON) option(ENABLE_BLADERF "Enable BladeRF" ON) option(ENABLE_SOAPYSDR "Enable SoapySDR" ON) diff --git a/lib/examples/CMakeLists.txt b/lib/examples/CMakeLists.txt index c6439be03..6d74b435c 100644 --- a/lib/examples/CMakeLists.txt +++ b/lib/examples/CMakeLists.txt @@ -54,7 +54,7 @@ endif(SRSGUI_FOUND) if (ZEROMQ_FOUND) add_executable(zmq_remote_rx zmq_remote_rx.c) - target_link_libraries(zmq_remote_rx srsran_phy srsran_rf) + target_link_libraries(zmq_remote_rx srsran_phy srsran_rf ${ZEROMQ_LIBRARIES}) endif (ZEROMQ_FOUND) ################################################################# diff --git a/lib/include/srsran/phy/rf/rf.h b/lib/include/srsran/phy/rf/rf.h index f21bfe717..ce8a12a5c 100644 --- a/lib/include/srsran/phy/rf/rf.h +++ b/lib/include/srsran/phy/rf/rf.h @@ -63,6 +63,73 @@ typedef struct { typedef void (*srsran_rf_error_handler_t)(void* arg, srsran_rf_error_t error); +/* RF frontend API */ +typedef struct { + const char* name; + const char* (*srsran_rf_devname)(void* h); + int (*srsran_rf_start_rx_stream)(void* h, bool now); + int (*srsran_rf_stop_rx_stream)(void* h); + void (*srsran_rf_flush_buffer)(void* h); + bool (*srsran_rf_has_rssi)(void* h); + float (*srsran_rf_get_rssi)(void* h); + void (*srsran_rf_suppress_stdout)(void* h); + void (*srsran_rf_register_error_handler)(void* h, srsran_rf_error_handler_t error_handler, void* arg); + int (*srsran_rf_open)(char* args, void** h); + int (*srsran_rf_open_multi)(char* args, void** h, uint32_t nof_channels); + int (*srsran_rf_close)(void* h); + double (*srsran_rf_set_rx_srate)(void* h, double freq); + int (*srsran_rf_set_rx_gain)(void* h, double gain); + int (*srsran_rf_set_rx_gain_ch)(void* h, uint32_t ch, double gain); + int (*srsran_rf_set_tx_gain)(void* h, double gain); + int (*srsran_rf_set_tx_gain_ch)(void* h, uint32_t ch, double gain); + double (*srsran_rf_get_rx_gain)(void* h); + double (*srsran_rf_get_tx_gain)(void* h); + srsran_rf_info_t* (*srsran_rf_get_info)(void* h); + double (*srsran_rf_set_rx_freq)(void* h, uint32_t ch, double freq); + double (*srsran_rf_set_tx_srate)(void* h, double freq); + double (*srsran_rf_set_tx_freq)(void* h, uint32_t ch, double freq); + void (*srsran_rf_get_time)(void* h, time_t* secs, double* frac_secs); + void (*srsran_rf_sync_pps)(void* h); + int (*srsran_rf_recv_with_time)(void* h, + void* data, + uint32_t nsamples, + bool blocking, + time_t* secs, + double* frac_secs); + int (*srsran_rf_recv_with_time_multi)(void* h, + void** data, + uint32_t nsamples, + bool blocking, + time_t* secs, + double* frac_secs); + int (*srsran_rf_send_timed)(void* h, + void* data, + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst); + int (*srsran_rf_send_timed_multi)(void* h, + void** data, + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst); +} rf_dev_t; + +typedef struct { + const char* plugin_name; + void* dl_handle; + rf_dev_t* rf_api; +} srsran_rf_plugin_t; + +SRSRAN_API int srsran_rf_load_plugins(); + SRSRAN_API int srsran_rf_open(srsran_rf_t* h, char* args); SRSRAN_API int srsran_rf_open_multi(srsran_rf_t* h, char* args, uint32_t nof_channels); diff --git a/lib/src/phy/rf/CMakeLists.txt b/lib/src/phy/rf/CMakeLists.txt index 77d60bfff..3581d5ab0 100644 --- a/lib/src/phy/rf/CMakeLists.txt +++ b/lib/src/phy/rf/CMakeLists.txt @@ -11,13 +11,32 @@ if(RF_FOUND) add_library(srsran_rf_utils STATIC rf_utils.c) target_link_libraries(srsran_rf_utils srsran_phy) - # Include common RF files + # Top-level RF library sources set(SOURCES_RF "") list(APPEND SOURCES_RF rf_imp.c) + # Lists of static (builtin) and dynamic RF plugins + set(STATIC_PLUGINS "") + set(DYNAMIC_PLUGINS "") + + if (ENABLE_RF_PLUGINS) + add_definitions(-DENABLE_RF_PLUGINS) + endif (ENABLE_RF_PLUGINS) + + # RF plugins if (UHD_FOUND) add_definitions(-DENABLE_UHD) - list(APPEND SOURCES_RF rf_uhd_imp.cc) + set(SOURCES_UHD rf_uhd_imp.cc) + if (ENABLE_RF_PLUGINS) + add_library(srsran_rf_uhd SHARED ${SOURCES_UHD}) + set_target_properties(srsran_rf_uhd PROPERTIES VERSION ${SRSRAN_VERSION_STRING} SOVERSION ${SRSRAN_SOVERSION}) + list(APPEND DYNAMIC_PLUGINS srsran_rf_uhd) + else (ENABLE_RF_PLUGINS) + add_library(srsran_rf_uhd STATIC ${SOURCES_UHD}) + list(APPEND STATIC_PLUGINS srsran_rf_uhd) + endif (ENABLE_RF_PLUGINS) + target_link_libraries(srsran_rf_uhd srsran_rf_utils srsran_phy ${UHD_LIBRARIES} ${Boost_LIBRARIES}) + install(TARGETS srsran_rf_uhd DESTINATION ${LIBRARY_DIR}) # If found, add a macro to inform the UHD driver about the available feature if (UHD_ENABLE_X300_FW_RESET) @@ -31,70 +50,100 @@ if(RF_FOUND) endif(UHD_ENABLE_CUSTOM_RFNOC) endif (UHD_FOUND) - if (UHD_FOUND AND UHD_ENABLE_CUSTOM_RFNOC) - add_executable(rfnoc_test rfnoc_test.cc) - target_link_libraries(rfnoc_test srsran_rf ${UHD_LIBRARIES} ${Boost_LIBRARIES} /usr/lib/x86_64-linux-gnu/libboost_system.so) - message(info ${Boost_LIBRARIES}) - endif (UHD_FOUND AND UHD_ENABLE_CUSTOM_RFNOC) - if (BLADERF_FOUND) add_definitions(-DENABLE_BLADERF) - list(APPEND SOURCES_RF rf_blade_imp.c) + set(SOURCES_BLADE rf_blade_imp.c) + if (ENABLE_RF_PLUGINS) + add_library(srsran_rf_blade SHARED ${SOURCES_BLADE}) + set_target_properties(srsran_rf_blade PROPERTIES VERSION ${SRSRAN_VERSION_STRING} SOVERSION ${SRSRAN_SOVERSION}) + list(APPEND DYNAMIC_PLUGINS srsran_rf_blade) + else (ENABLE_RF_PLUGINS) + add_library(srsran_rf_blade STATIC ${SOURCES_BLADE}) + list(APPEND STATIC_PLUGINS srsran_rf_blade) + endif (ENABLE_RF_PLUGINS) + target_link_libraries(srsran_rf_blade srsran_rf_utils srsran_phy ${BLADERF_LIBRARIES}) + install(TARGETS srsran_rf_blade DESTINATION ${LIBRARY_DIR}) endif (BLADERF_FOUND) if (SOAPYSDR_FOUND AND ENABLE_SOAPYSDR) add_definitions(-DENABLE_SOAPYSDR) - list(APPEND SOURCES_RF rf_soapy_imp.c) + set(SOURCES_SOAPY rf_soapy_imp.c) + if (ENABLE_RF_PLUGINS) + add_library(srsran_rf_soapy SHARED ${SOURCES_SOAPY}) + set_target_properties(srsran_rf_soapy PROPERTIES VERSION ${SRSRAN_VERSION_STRING} SOVERSION ${SRSRAN_SOVERSION}) + list(APPEND DYNAMIC_PLUGINS srsran_rf_soapy) + else (ENABLE_RF_PLUGINS) + add_library(srsran_rf_soapy STATIC ${SOURCES_SOAPY}) + list(APPEND STATIC_PLUGINS srsran_rf_soapy) + endif (ENABLE_RF_PLUGINS) + target_link_libraries(srsran_rf_soapy srsran_rf_utils srsran_phy ${SOAPYSDR_LIBRARIES}) + install(TARGETS srsran_rf_soapy DESTINATION ${LIBRARY_DIR}) endif (SOAPYSDR_FOUND AND ENABLE_SOAPYSDR) if(SKIQ_FOUND) add_executable(skiq_pps_test skiq_pps_test.c) target_link_libraries(skiq_pps_test ${SKIQ_LIBRARIES} rt pthread m) add_definitions(-DENABLE_SIDEKIQ) - list(APPEND SOURCES_RF rf_skiq_imp.c rf_skiq_imp_card.c rf_skiq_imp_port.c) + set(SOURCES_SKIQ rf_skiq_imp.c rf_skiq_imp_card.c rf_skiq_imp_port.c) + if (ENABLE_RF_PLUGINS) + add_library(srsran_rf_skiq SHARED ${SOURCES_SKIQ}) + set_target_properties(srsran_rf_skiq PROPERTIES VERSION ${SRSRAN_VERSION_STRING} SOVERSION ${SRSRAN_SOVERSION}) + list(APPEND DYNAMIC_PLUGINS srsran_rf_skiq) + else (ENABLE_RF_PLUGINS) + add_library(srsran_rf_skiq STATIC ${SOURCES_SKIQ}) + list(APPEND STATIC_PLUGINS srsran_rf_skiq) + endif (ENABLE_RF_PLUGINS) + target_link_libraries(srsran_rf_skiq srsran_rf_utils srsran_phy ${SKIQ_LIBRARIES} rt) + install(TARGETS srsran_rf_skiq DESTINATION ${LIBRARY_DIR}) endif(SKIQ_FOUND) if (ZEROMQ_FOUND) add_definitions(-DENABLE_ZEROMQ) - list(APPEND SOURCES_RF rf_zmq_imp.c rf_zmq_imp_tx.c rf_zmq_imp_rx.c) + set(SOURCES_ZMQ rf_zmq_imp.c rf_zmq_imp_tx.c rf_zmq_imp_rx.c) + if (ENABLE_RF_PLUGINS) + add_library(srsran_rf_zmq SHARED ${SOURCES_ZMQ}) + set_target_properties(srsran_rf_zmq PROPERTIES VERSION ${SRSRAN_VERSION_STRING} SOVERSION ${SRSRAN_SOVERSION}) + list(APPEND DYNAMIC_PLUGINS srsran_rf_zmq) + else (ENABLE_RF_PLUGINS) + add_library(srsran_rf_zmq STATIC ${SOURCES_ZMQ}) + list(APPEND STATIC_PLUGINS srsran_rf_zmq) + endif (ENABLE_RF_PLUGINS) + target_link_libraries(srsran_rf_zmq srsran_rf_utils srsran_phy ${ZEROMQ_LIBRARIES}) + install(TARGETS srsran_rf_zmq DESTINATION ${LIBRARY_DIR}) endif (ZEROMQ_FOUND) + # Add sources of file-based RF directly to the RF library (not as a plugin) list(APPEND SOURCES_RF rf_file_imp.c rf_file_imp_tx.c rf_file_imp_rx.c) + # Top-level RF library add_library(srsran_rf_object OBJECT ${SOURCES_RF}) set_property(TARGET srsran_rf_object PROPERTY POSITION_INDEPENDENT_CODE 1) - - add_library(srsran_rf STATIC $) - add_library(srsran_rf_shared SHARED $) + if (ENABLE_RF_PLUGINS) + add_dependencies(srsran_rf_object ${DYNAMIC_PLUGINS}) + add_library(srsran_rf SHARED $) + target_link_libraries(srsran_rf dl) + # Add $ORIGIN (i.e. current location of this library) to rpath of srsran_rf. + # This ensures that it will find the plugins that reside in the same directory as the library + set_target_properties(srsran_rf PROPERTIES BUILD_RPATH "\$ORIGIN/") + set_target_properties(srsran_rf PROPERTIES INSTALL_RPATH "\$ORIGIN/") + else (ENABLE_RF_PLUGINS) + # Without RF plugins, we aggregate everything in a static library (builtin plugins) + add_library(srsran_rf STATIC $) + target_link_libraries(srsran_rf ${STATIC_PLUGINS}) + endif (ENABLE_RF_PLUGINS) target_link_libraries(srsran_rf srsran_rf_utils srsran_phy) set_target_properties(srsran_rf PROPERTIES VERSION ${SRSRAN_VERSION_STRING} SOVERSION ${SRSRAN_SOVERSION}) - target_link_libraries(srsran_rf_shared srsran_rf_utils srsran_phy) - set_target_properties(srsran_rf_shared PROPERTIES VERSION ${SRSRAN_VERSION_STRING} SOVERSION ${SRSRAN_SOVERSION}) - - if (UHD_FOUND) - target_link_libraries(srsran_rf ${UHD_LIBRARIES} ${Boost_LIBRARIES}) # Ubuntu 18.04 requires 'system' from Boost_LIBRARIES - target_link_libraries(srsran_rf_shared ${UHD_LIBRARIES} ${Boost_LIBRARIES}) - endif (UHD_FOUND) + install(TARGETS srsran_rf DESTINATION ${LIBRARY_DIR}) - if (BLADERF_FOUND) - target_link_libraries(srsran_rf ${BLADERF_LIBRARIES}) - target_link_libraries(srsran_rf_shared ${BLADERF_LIBRARIES}) - endif (BLADERF_FOUND) - - if (SOAPYSDR_FOUND AND ENABLE_SOAPYSDR) - target_link_libraries(srsran_rf ${SOAPYSDR_LIBRARIES}) - target_link_libraries(srsran_rf_shared ${SOAPYSDR_LIBRARIES}) - endif (SOAPYSDR_FOUND AND ENABLE_SOAPYSDR) - - if(SKIQ_FOUND) - target_link_libraries(srsran_rf ${SKIQ_LIBRARIES} rt) - target_link_libraries(srsran_rf_shared ${SKIQ_LIBRARIES} rt) - endif(SKIQ_FOUND) + # Tests + if (UHD_FOUND AND UHD_ENABLE_CUSTOM_RFNOC) + add_executable(rfnoc_test rfnoc_test.cc) + target_link_libraries(rfnoc_test srsran_rf ${UHD_LIBRARIES} ${Boost_LIBRARIES} /usr/lib/x86_64-linux-gnu/libboost_system.so) + message(info ${Boost_LIBRARIES}) + endif (UHD_FOUND AND UHD_ENABLE_CUSTOM_RFNOC) if (ZEROMQ_FOUND) - target_link_libraries(srsran_rf ${ZEROMQ_LIBRARIES}) - target_link_libraries(srsran_rf_shared ${ZEROMQ_LIBRARIES}) add_executable(rf_zmq_test rf_zmq_test.c) target_link_libraries(rf_zmq_test srsran_rf) #add_test(rf_zmq_test rf_zmq_test) @@ -103,6 +152,4 @@ if(RF_FOUND) add_executable(rf_file_test rf_file_test.c) target_link_libraries(rf_file_test srsran_rf) add_test(rf_file_test rf_file_test) - - INSTALL(TARGETS srsran_rf DESTINATION ${LIBRARY_DIR}) endif(RF_FOUND) diff --git a/lib/src/phy/rf/rf_blade_imp.c b/lib/src/phy/rf/rf_blade_imp.c index 888f8a421..d0c0dc815 100644 --- a/lib/src/phy/rf/rf_blade_imp.c +++ b/lib/src/phy/rf/rf_blade_imp.c @@ -15,6 +15,7 @@ #include #include "rf_blade_imp.h" +#include "rf_plugin.h" #include "srsran/phy/common/timestamp.h" #include "srsran/phy/utils/debug.h" #include "srsran/phy/utils/vector.h" @@ -535,3 +536,44 @@ int rf_blade_send_timed(void* h, return nsamples; } + +rf_dev_t srsran_rf_dev_blade = {"bladeRF", + rf_blade_devname, + rf_blade_start_rx_stream, + rf_blade_stop_rx_stream, + rf_blade_flush_buffer, + rf_blade_has_rssi, + rf_blade_get_rssi, + rf_blade_suppress_stdout, + rf_blade_register_error_handler, + rf_blade_open, + .srsran_rf_open_multi = rf_blade_open_multi, + rf_blade_close, + rf_blade_set_rx_srate, + rf_blade_set_rx_gain, + rf_blade_set_rx_gain_ch, + rf_blade_set_tx_gain, + rf_blade_set_tx_gain_ch, + rf_blade_get_rx_gain, + rf_blade_get_tx_gain, + rf_blade_get_info, + rf_blade_set_rx_freq, + rf_blade_set_tx_srate, + rf_blade_set_tx_freq, + rf_blade_get_time, + NULL, + rf_blade_recv_with_time, + rf_blade_recv_with_time_multi, + rf_blade_send_timed, + .srsran_rf_send_timed_multi = rf_blade_send_timed_multi}; + +#ifdef ENABLE_RF_PLUGINS +int register_plugin(rf_dev_t** rf_api) +{ + if (rf_api == NULL) { + return SRSRAN_ERROR; + } + *rf_api = &srsran_rf_dev_blade; + return SRSRAN_SUCCESS; +} +#endif /* ENABLE_RF_PLUGINS */ diff --git a/lib/src/phy/rf/rf_blade_imp.h b/lib/src/phy/rf/rf_blade_imp.h index f86e1e570..668892842 100644 --- a/lib/src/phy/rf/rf_blade_imp.h +++ b/lib/src/phy/rf/rf_blade_imp.h @@ -10,11 +10,16 @@ * */ +#ifndef SRSRAN_RF_BLADE_IMP_H_ +#define SRSRAN_RF_BLADE_IMP_H_ + #include "srsran/config.h" #include "srsran/phy/rf/rf.h" #define DEVNAME "bladerf" +extern rf_dev_t srsran_rf_dev_blade; + SRSRAN_API int rf_blade_open(char* args, void** handler); SRSRAN_API int rf_blade_open_multi(char* args, void** handler, uint32_t nof_channels); @@ -90,3 +95,5 @@ SRSRAN_API int rf_blade_send_timed(void* h, bool blocking, bool is_start_of_burst, bool is_end_of_burst); + +#endif /* SRSRAN_RF_BLADE_IMP_H_ */ diff --git a/lib/src/phy/rf/rf_dev.h b/lib/src/phy/rf/rf_dev.h index b31e9d65d..2e2a1c099 100644 --- a/lib/src/phy/rf/rf_dev.h +++ b/lib/src/phy/rf/rf_dev.h @@ -13,276 +13,58 @@ #include "srsran/phy/rf/rf.h" #include -/* RF frontend API */ -typedef struct { - const char* name; - const char* (*srsran_rf_devname)(void* h); - int (*srsran_rf_start_rx_stream)(void* h, bool now); - int (*srsran_rf_stop_rx_stream)(void* h); - void (*srsran_rf_flush_buffer)(void* h); - bool (*srsran_rf_has_rssi)(void* h); - float (*srsran_rf_get_rssi)(void* h); - void (*srsran_rf_suppress_stdout)(void* h); - void (*srsran_rf_register_error_handler)(void* h, srsran_rf_error_handler_t error_handler, void* arg); - int (*srsran_rf_open)(char* args, void** h); - int (*srsran_rf_open_multi)(char* args, void** h, uint32_t nof_channels); - int (*srsran_rf_close)(void* h); - double (*srsran_rf_set_rx_srate)(void* h, double freq); - int (*srsran_rf_set_rx_gain)(void* h, double gain); - int (*srsran_rf_set_rx_gain_ch)(void* h, uint32_t ch, double gain); - int (*srsran_rf_set_tx_gain)(void* h, double gain); - int (*srsran_rf_set_tx_gain_ch)(void* h, uint32_t ch, double gain); - double (*srsran_rf_get_rx_gain)(void* h); - double (*srsran_rf_get_tx_gain)(void* h); - srsran_rf_info_t* (*srsran_rf_get_info)(void* h); - double (*srsran_rf_set_rx_freq)(void* h, uint32_t ch, double freq); - double (*srsran_rf_set_tx_srate)(void* h, double freq); - double (*srsran_rf_set_tx_freq)(void* h, uint32_t ch, double freq); - void (*srsran_rf_get_time)(void* h, time_t* secs, double* frac_secs); - void (*srsran_rf_sync_pps)(void* h); - int (*srsran_rf_recv_with_time)(void* h, - void* data, - uint32_t nsamples, - bool blocking, - time_t* secs, - double* frac_secs); - int (*srsran_rf_recv_with_time_multi)(void* h, - void** data, - uint32_t nsamples, - bool blocking, - time_t* secs, - double* frac_secs); - int (*srsran_rf_send_timed)(void* h, - void* data, - int nsamples, - time_t secs, - double frac_secs, - bool has_time_spec, - bool blocking, - bool is_start_of_burst, - bool is_end_of_burst); - int (*srsran_rf_send_timed_multi)(void* h, - void** data, - int nsamples, - time_t secs, - double frac_secs, - bool has_time_spec, - bool blocking, - bool is_start_of_burst, - bool is_end_of_burst); -} rf_dev_t; - /* Define implementation for UHD */ #ifdef ENABLE_UHD - +#ifdef ENABLE_RF_PLUGINS +static srsran_rf_plugin_t plugin_uhd = {"libsrsran_rf_uhd.so", NULL, NULL}; +#else #include "rf_uhd_imp.h" - -static rf_dev_t dev_uhd = {"UHD", - rf_uhd_devname, - rf_uhd_start_rx_stream, - rf_uhd_stop_rx_stream, - rf_uhd_flush_buffer, - rf_uhd_has_rssi, - rf_uhd_get_rssi, - rf_uhd_suppress_stdout, - rf_uhd_register_error_handler, - rf_uhd_open, - .srsran_rf_open_multi = rf_uhd_open_multi, - rf_uhd_close, - rf_uhd_set_rx_srate, - rf_uhd_set_rx_gain, - rf_uhd_set_rx_gain_ch, - rf_uhd_set_tx_gain, - rf_uhd_set_tx_gain_ch, - rf_uhd_get_rx_gain, - rf_uhd_get_tx_gain, - rf_uhd_get_info, - rf_uhd_set_rx_freq, - rf_uhd_set_tx_srate, - rf_uhd_set_tx_freq, - rf_uhd_get_time, - rf_uhd_sync_pps, - rf_uhd_recv_with_time, - rf_uhd_recv_with_time_multi, - rf_uhd_send_timed, - .srsran_rf_send_timed_multi = rf_uhd_send_timed_multi}; +static srsran_rf_plugin_t plugin_uhd = {"", NULL, &srsran_rf_dev_uhd}; +#endif #endif /* Define implementation for bladeRF */ #ifdef ENABLE_BLADERF - +#ifdef ENABLE_RF_PLUGINS +static srsran_rf_plugin_t plugin_blade = {"libsrsran_rf_blade.so", NULL, NULL}; +#else #include "rf_blade_imp.h" - -static rf_dev_t dev_blade = {"bladeRF", - rf_blade_devname, - rf_blade_start_rx_stream, - rf_blade_stop_rx_stream, - rf_blade_flush_buffer, - rf_blade_has_rssi, - rf_blade_get_rssi, - rf_blade_suppress_stdout, - rf_blade_register_error_handler, - rf_blade_open, - .srsran_rf_open_multi = rf_blade_open_multi, - rf_blade_close, - rf_blade_set_rx_srate, - rf_blade_set_rx_gain, - rf_blade_set_rx_gain_ch, - rf_blade_set_tx_gain, - rf_blade_set_tx_gain_ch, - rf_blade_get_rx_gain, - rf_blade_get_tx_gain, - rf_blade_get_info, - rf_blade_set_rx_freq, - rf_blade_set_tx_srate, - rf_blade_set_tx_freq, - rf_blade_get_time, - NULL, - rf_blade_recv_with_time, - rf_blade_recv_with_time_multi, - rf_blade_send_timed, - .srsran_rf_send_timed_multi = rf_blade_send_timed_multi}; +static srsran_rf_plugin_t plugin_blade = {"", NULL, &srsran_rf_dev_blade}; +#endif #endif +/* Define implementation for SoapySDR */ #ifdef ENABLE_SOAPYSDR - +#ifdef ENABLE_RF_PLUGINS +static srsran_rf_plugin_t plugin_soapy = {"libsrsran_rf_soapy.so", NULL, NULL}; +#else #include "rf_soapy_imp.h" - -static rf_dev_t dev_soapy = {"soapy", - rf_soapy_devname, - rf_soapy_start_rx_stream, - rf_soapy_stop_rx_stream, - rf_soapy_flush_buffer, - rf_soapy_has_rssi, - rf_soapy_get_rssi, - rf_soapy_suppress_stdout, - rf_soapy_register_error_handler, - rf_soapy_open, - rf_soapy_open_multi, - rf_soapy_close, - rf_soapy_set_rx_srate, - rf_soapy_set_rx_gain, - rf_soapy_set_rx_gain_ch, - rf_soapy_set_tx_gain, - rf_soapy_set_tx_gain_ch, - rf_soapy_get_rx_gain, - rf_soapy_get_tx_gain, - rf_soapy_get_info, - rf_soapy_set_rx_freq, - rf_soapy_set_tx_srate, - rf_soapy_set_tx_freq, - rf_soapy_get_time, - NULL, - rf_soapy_recv_with_time, - rf_soapy_recv_with_time_multi, - rf_soapy_send_timed, - .srsran_rf_send_timed_multi = rf_soapy_send_timed_multi}; - +static srsran_rf_plugin_t plugin_soapy = {"", NULL, &srsran_rf_dev_soapy}; +#endif #endif -/* Define implementation for UHD */ +/* Define implementation for ZeroMQ */ #ifdef ENABLE_ZEROMQ - +#ifdef ENABLE_RF_PLUGINS +static srsran_rf_plugin_t plugin_zmq = {"libsrsran_rf_zmq.so", NULL, NULL}; +#else #include "rf_zmq_imp.h" - -static rf_dev_t dev_zmq = {"zmq", - rf_zmq_devname, - rf_zmq_start_rx_stream, - rf_zmq_stop_rx_stream, - rf_zmq_flush_buffer, - rf_zmq_has_rssi, - rf_zmq_get_rssi, - rf_zmq_suppress_stdout, - rf_zmq_register_error_handler, - rf_zmq_open, - .srsran_rf_open_multi = rf_zmq_open_multi, - rf_zmq_close, - rf_zmq_set_rx_srate, - rf_zmq_set_rx_gain, - rf_zmq_set_rx_gain_ch, - rf_zmq_set_tx_gain, - rf_zmq_set_tx_gain_ch, - rf_zmq_get_rx_gain, - rf_zmq_get_tx_gain, - rf_zmq_get_info, - rf_zmq_set_rx_freq, - rf_zmq_set_tx_srate, - rf_zmq_set_tx_freq, - rf_zmq_get_time, - NULL, - rf_zmq_recv_with_time, - rf_zmq_recv_with_time_multi, - rf_zmq_send_timed, - .srsran_rf_send_timed_multi = rf_zmq_send_timed_multi}; +static srsran_rf_plugin_t plugin_zmq = {"", NULL, &srsran_rf_dev_zmq}; +#endif #endif /* Define implementation for file-based RF */ - #include "rf_file_imp.h" - -static rf_dev_t dev_file = {"file", - rf_file_devname, - rf_file_start_rx_stream, - rf_file_stop_rx_stream, - rf_file_flush_buffer, - rf_file_has_rssi, - rf_file_get_rssi, - rf_file_suppress_stdout, - rf_file_register_error_handler, - rf_file_open, - .srsran_rf_open_multi = rf_file_open_multi, - rf_file_close, - rf_file_set_rx_srate, - rf_file_set_rx_gain, - rf_file_set_rx_gain_ch, - rf_file_set_tx_gain, - rf_file_set_tx_gain_ch, - rf_file_get_rx_gain, - rf_file_get_tx_gain, - rf_file_get_info, - rf_file_set_rx_freq, - rf_file_set_tx_srate, - rf_file_set_tx_freq, - rf_file_get_time, - NULL, - rf_file_recv_with_time, - rf_file_recv_with_time_multi, - rf_file_send_timed, - .srsran_rf_send_timed_multi = rf_file_send_timed_multi}; +static srsran_rf_plugin_t plugin_file = {"", NULL, &srsran_rf_dev_file}; /* Define implementation for Sidekiq */ #ifdef ENABLE_SIDEKIQ - +#ifdef ENABLE_RF_PLUGINS +static srsran_rf_plugin_t plugin_skiq = {"libsrsran_rf_skiq.so", NULL, NULL}; +#else #include "rf_skiq_imp.h" - -static rf_dev_t dev_skiq = {.name = "Sidekiq", - .srsran_rf_devname = rf_skiq_devname, - .srsran_rf_start_rx_stream = rf_skiq_start_rx_stream, - .srsran_rf_stop_rx_stream = rf_skiq_stop_rx_stream, - .srsran_rf_flush_buffer = rf_skiq_flush_buffer, - .srsran_rf_has_rssi = rf_skiq_has_rssi, - .srsran_rf_get_rssi = rf_skiq_get_rssi, - .srsran_rf_suppress_stdout = rf_skiq_suppress_stdout, - .srsran_rf_register_error_handler = rf_skiq_register_error_handler, - .srsran_rf_open = rf_skiq_open, - .srsran_rf_open_multi = rf_skiq_open_multi, - .srsran_rf_close = rf_skiq_close, - .srsran_rf_set_rx_srate = rf_skiq_set_rx_srate, - .srsran_rf_set_tx_srate = rf_skiq_set_tx_srate, - .srsran_rf_set_rx_gain = rf_skiq_set_rx_gain, - .srsran_rf_set_tx_gain = rf_skiq_set_tx_gain, - .srsran_rf_set_tx_gain_ch = rf_skiq_set_tx_gain_ch, - .srsran_rf_set_rx_gain_ch = rf_skiq_set_rx_gain_ch, - .srsran_rf_get_rx_gain = rf_skiq_get_rx_gain, - .srsran_rf_get_tx_gain = rf_skiq_get_tx_gain, - .srsran_rf_get_info = rf_skiq_get_info, - .srsran_rf_set_rx_freq = rf_skiq_set_rx_freq, - .srsran_rf_set_tx_freq = rf_skiq_set_tx_freq, - .srsran_rf_get_time = rf_skiq_get_time, - .srsran_rf_recv_with_time = rf_skiq_recv_with_time, - .srsran_rf_recv_with_time_multi = rf_skiq_recv_with_time_multi, - .srsran_rf_send_timed = rf_skiq_send_timed, - .srsran_rf_send_timed_multi = rf_skiq_send_timed_multi}; +static srsran_rf_plugin_t plugin_skiq = {"", NULL, &srsran_rf_dev_skiq}; +#endif #endif //#define ENABLE_DUMMY_DEV @@ -295,34 +77,37 @@ int dummy_rcv() } void dummy_fnc() {} -static rf_dev_t dev_dummy = {"dummy", dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, - dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, - dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_rcv, - dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc}; +static rf_dev_t srsran_rf_dev_dummy = { + "dummy", dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, + dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, + dummy_fnc, dummy_fnc, dummy_fnc, dummy_rcv, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc}; +static srsran_rf_plugin_t plugin_dummy = {"", NULL, &srsran_rf_dev_dummy}; + #endif + /** - * Collection of all currently supported RF devices + * Collection of all currently available RF plugins */ -static rf_dev_t* available_devices[] = { +static srsran_rf_plugin_t* rf_plugins[] = { #ifdef ENABLE_UHD - &dev_uhd, + &plugin_uhd, #endif #ifdef ENABLE_SOAPYSDR - &dev_soapy, + &plugin_soapy, #endif #ifdef ENABLE_BLADERF - &dev_blade, + &plugin_blade, #endif #ifdef ENABLE_ZEROMQ - &dev_zmq, + &plugin_zmq, #endif #ifdef ENABLE_SIDEKIQ - &dev_skiq, + &plugin_skiq, #endif #ifdef ENABLE_DUMMY_DEV - &dev_dummy, + &plugin_dummy, #endif - &dev_file, + &plugin_file, NULL}; diff --git a/lib/src/phy/rf/rf_file_imp.c b/lib/src/phy/rf/rf_file_imp.c index 16e98a5a8..f558af363 100644 --- a/lib/src/phy/rf/rf_file_imp.c +++ b/lib/src/phy/rf/rf_file_imp.c @@ -10,9 +10,6 @@ * */ -#ifndef SRSRAN_RF_IMP_TRX_H -#define SRSRAN_RF_IMP_TRX_H - #include "rf_file_imp.h" #include "rf_file_imp_trx.h" #include "rf_helper.h" @@ -856,4 +853,32 @@ clean_exit: return ret; } -#endif +rf_dev_t srsran_rf_dev_file = {"file", + rf_file_devname, + rf_file_start_rx_stream, + rf_file_stop_rx_stream, + rf_file_flush_buffer, + rf_file_has_rssi, + rf_file_get_rssi, + rf_file_suppress_stdout, + rf_file_register_error_handler, + rf_file_open, + .srsran_rf_open_multi = rf_file_open_multi, + rf_file_close, + rf_file_set_rx_srate, + rf_file_set_rx_gain, + rf_file_set_rx_gain_ch, + rf_file_set_tx_gain, + rf_file_set_tx_gain_ch, + rf_file_get_rx_gain, + rf_file_get_tx_gain, + rf_file_get_info, + rf_file_set_rx_freq, + rf_file_set_tx_srate, + rf_file_set_tx_freq, + rf_file_get_time, + NULL, + rf_file_recv_with_time, + rf_file_recv_with_time_multi, + rf_file_send_timed, + .srsran_rf_send_timed_multi = rf_file_send_timed_multi}; diff --git a/lib/src/phy/rf/rf_file_imp.h b/lib/src/phy/rf/rf_file_imp.h index 5db7e697f..439ec6ec3 100644 --- a/lib/src/phy/rf/rf_file_imp.h +++ b/lib/src/phy/rf/rf_file_imp.h @@ -23,6 +23,8 @@ #define PARAM_LEN (128) #define PARAM_LEN_SHORT (PARAM_LEN / 2) +extern rf_dev_t srsran_rf_dev_file; + SRSRAN_API const char* rf_file_devname(void* h); SRSRAN_API int rf_file_start_rx_stream(void* h, bool now); diff --git a/lib/src/phy/rf/rf_imp.c b/lib/src/phy/rf/rf_imp.c index 2b58e595e..30ff775f0 100644 --- a/lib/src/phy/rf/rf_imp.c +++ b/lib/src/phy/rf/rf_imp.c @@ -10,17 +10,19 @@ * */ -#include - #include "rf_dev.h" #include "srsran/phy/rf/rf.h" #include "srsran/phy/utils/debug.h" +#include +#include int rf_get_available_devices(char** devnames, int max_strlen) { int i = 0; - while (available_devices[i]->name) { - strncpy(devnames[i], available_devices[i]->name, max_strlen); + while (rf_plugins[i] != NULL) { + if (rf_plugins[i]->rf_api != NULL) { + strncpy(devnames[i], rf_plugins[i]->rf_api->name, max_strlen); + } i++; } return i; @@ -92,23 +94,30 @@ const char* srsran_rf_get_devname(srsran_rf_t* rf) int srsran_rf_open_devname(srsran_rf_t* rf, const char* devname, char* args, uint32_t nof_channels) { + if (srsran_rf_load_plugins() != SRSRAN_SUCCESS) { + ERROR("Failed to load RF plugins"); + return SRSRAN_ERROR; + } + rf->thread_gain_run = false; bool no_rf_devs_detected = true; - printf("Available RF device list:"); - for (unsigned int i = 0; available_devices[i]; i++) { + printf("Supported RF device list:"); + for (unsigned int i = 0; rf_plugins[i] && rf_plugins[i]->rf_api; i++) { no_rf_devs_detected = false; - printf(" %s ", available_devices[i]->name); + printf(" %s", rf_plugins[i]->rf_api->name); } printf("%s\n", no_rf_devs_detected ? " " : ""); // Try to open the device if name is provided if (devname && devname[0] != '\0') { int i = 0; - while (available_devices[i] != NULL) { - if (!strcasecmp(available_devices[i]->name, devname)) { - rf->dev = available_devices[i]; - return available_devices[i]->srsran_rf_open_multi(args, &rf->handler, nof_channels); + while (rf_plugins[i] != NULL) { + if (rf_plugins[i]->rf_api) { + if (!strcasecmp(rf_plugins[i]->rf_api->name, devname)) { + rf->dev = rf_plugins[i]->rf_api; + return rf_plugins[i]->rf_api->srsran_rf_open_multi(args, &rf->handler, nof_channels); + } } i++; } @@ -120,16 +129,16 @@ int srsran_rf_open_devname(srsran_rf_t* rf, const char* devname, char* args, uin return SRSRAN_ERROR; } - // auto-mode, try to open in order of apperance in available_devices[] array + // auto-mode, try to open in order of apperance in rf_plugins[] array int i = 0; - while (available_devices[i] != NULL) { - printf("Trying to open RF device '%s'\n", available_devices[i]->name); - if (!available_devices[i]->srsran_rf_open_multi(args, &rf->handler, nof_channels)) { - rf->dev = available_devices[i]; - printf("RF device '%s' successfully opened\n", available_devices[i]->name); + while (rf_plugins[i] != NULL && rf_plugins[i]->rf_api != NULL) { + printf("Trying to open RF device '%s'\n", rf_plugins[i]->rf_api->name); + if (!rf_plugins[i]->rf_api->srsran_rf_open_multi(args, &rf->handler, nof_channels)) { + rf->dev = rf_plugins[i]->rf_api; + printf("RF device '%s' successfully opened\n", rf_plugins[i]->rf_api->name); return SRSRAN_SUCCESS; } - printf("Unable to open RF device '%s'\n", available_devices[i]->name); + printf("Unable to open RF device '%s'\n", rf_plugins[i]->rf_api->name); i++; } @@ -141,7 +150,7 @@ int srsran_rf_open_devname(srsran_rf_t* rf, const char* devname, char* args, uin int srsran_rf_open_file(srsran_rf_t* rf, FILE** rx_files, FILE** tx_files, uint32_t nof_channels, uint32_t base_srate) { - rf->dev = &dev_file; + rf->dev = &srsran_rf_dev_file; // file abstraction has custom "open" function with file-related args return rf_file_open_file(&rf->handler, rx_files, tx_files, nof_channels, base_srate); @@ -391,3 +400,85 @@ int srsran_rf_send_timed2(srsran_rf_t* rf, { return srsran_rf_send_timed3(rf, data, nsamples, secs, frac_secs, true, true, is_start_of_burst, is_end_of_burst); } + +#ifdef ENABLE_RF_PLUGINS +static void unload_plugin(srsran_rf_plugin_t* rf_plugin) +{ + if (rf_plugin == NULL) { + return; + } + if (rf_plugin->dl_handle != NULL) { + rf_plugin->rf_api = NULL; + dlclose(rf_plugin->dl_handle); + rf_plugin->dl_handle = NULL; + } +} + +static int load_plugin(srsran_rf_plugin_t* rf_plugin) +{ + if (rf_plugin->rf_api != NULL) { + // already loaded + return SRSRAN_SUCCESS; + } + + rf_plugin->dl_handle = dlopen(rf_plugin->plugin_name, RTLD_NOW); + if (rf_plugin->dl_handle == NULL) { + // Not an error, if loading failed due to missing dependencies. + // Mark this plugin as not available and return SUCCESS. + INFO("Failed to load RF plugin %s: %s", rf_plugin->plugin_name, dlerror()); + rf_plugin->rf_api = NULL; + return SRSRAN_SUCCESS; + } + + // clear errors + dlerror(); + char* err = NULL; + + // load symbols + int (*register_plugin)(rf_dev_t * *rf_api) = dlsym(rf_plugin->dl_handle, "register_plugin"); + if ((err = dlerror()) != NULL) { + ERROR("Error loading symbol '%s': %s", "register_plugin", err); + goto clean_exit; + } + + // register plugin + int ret = register_plugin(&rf_plugin->rf_api); + if (ret != SRSRAN_SUCCESS) { + ERROR("Failed to register RF API for plugin %s", rf_plugin->plugin_name); + goto clean_exit; + } + return SRSRAN_SUCCESS; +clean_exit: + unload_plugin(rf_plugin); + return SRSRAN_ERROR; +} +#endif /* ENABLE_RF_PLUGINS */ + +int srsran_rf_load_plugins() +{ +#ifdef ENABLE_RF_PLUGINS + for (unsigned int i = 0; rf_plugins[i]; i++) { + if (load_plugin(rf_plugins[i]) != SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + } + + printf("Active RF plugins:"); + for (unsigned int i = 0; rf_plugins[i]; i++) { + if (rf_plugins[i]->dl_handle != NULL) { + printf(" %s", rf_plugins[i]->plugin_name); + } + } + printf("\n"); + + printf("Inactive RF plugins:"); + for (unsigned int i = 0; rf_plugins[i]; i++) { + if (rf_plugins[i]->dl_handle == NULL) { + printf(" %s", rf_plugins[i]->plugin_name); + } + } + printf("\n"); + +#endif /* ENABLE_RF_PLUGINS */ + return SRSRAN_SUCCESS; +} diff --git a/lib/src/phy/rf/rf_plugin.h b/lib/src/phy/rf/rf_plugin.h new file mode 100644 index 000000000..6e0f83106 --- /dev/null +++ b/lib/src/phy/rf/rf_plugin.h @@ -0,0 +1,28 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2022 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_RF_PLUGIN_H +#define SRSRAN_RF_PLUGIN_H + +#include "srsran/phy/rf/rf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +SRSRAN_API int register_plugin(rf_dev_t **rf_api); + +#ifdef __cplusplus +} +#endif + +#endif /* SRSRAN_RF_PLUGIN_H */ diff --git a/lib/src/phy/rf/rf_skiq_imp.c b/lib/src/phy/rf/rf_skiq_imp.c index 483241496..55bd08651 100644 --- a/lib/src/phy/rf/rf_skiq_imp.c +++ b/lib/src/phy/rf/rf_skiq_imp.c @@ -15,6 +15,7 @@ #include #include "rf_helper.h" +#include "rf_plugin.h" #include "rf_skiq_imp.h" #include "rf_skiq_imp_card.h" @@ -939,3 +940,43 @@ int rf_skiq_send_timed_multi(void* h_, return (int)rpm; } + +rf_dev_t srsran_rf_dev_skiq = {.name = "Sidekiq", + .srsran_rf_devname = rf_skiq_devname, + .srsran_rf_start_rx_stream = rf_skiq_start_rx_stream, + .srsran_rf_stop_rx_stream = rf_skiq_stop_rx_stream, + .srsran_rf_flush_buffer = rf_skiq_flush_buffer, + .srsran_rf_has_rssi = rf_skiq_has_rssi, + .srsran_rf_get_rssi = rf_skiq_get_rssi, + .srsran_rf_suppress_stdout = rf_skiq_suppress_stdout, + .srsran_rf_register_error_handler = rf_skiq_register_error_handler, + .srsran_rf_open = rf_skiq_open, + .srsran_rf_open_multi = rf_skiq_open_multi, + .srsran_rf_close = rf_skiq_close, + .srsran_rf_set_rx_srate = rf_skiq_set_rx_srate, + .srsran_rf_set_tx_srate = rf_skiq_set_tx_srate, + .srsran_rf_set_rx_gain = rf_skiq_set_rx_gain, + .srsran_rf_set_tx_gain = rf_skiq_set_tx_gain, + .srsran_rf_set_tx_gain_ch = rf_skiq_set_tx_gain_ch, + .srsran_rf_set_rx_gain_ch = rf_skiq_set_rx_gain_ch, + .srsran_rf_get_rx_gain = rf_skiq_get_rx_gain, + .srsran_rf_get_tx_gain = rf_skiq_get_tx_gain, + .srsran_rf_get_info = rf_skiq_get_info, + .srsran_rf_set_rx_freq = rf_skiq_set_rx_freq, + .srsran_rf_set_tx_freq = rf_skiq_set_tx_freq, + .srsran_rf_get_time = rf_skiq_get_time, + .srsran_rf_recv_with_time = rf_skiq_recv_with_time, + .srsran_rf_recv_with_time_multi = rf_skiq_recv_with_time_multi, + .srsran_rf_send_timed = rf_skiq_send_timed, + .srsran_rf_send_timed_multi = rf_skiq_send_timed_multi}; + +#ifdef ENABLE_RF_PLUGINS +int register_plugin(rf_dev_t** rf_api) +{ + if (rf_api == NULL) { + return SRSRAN_ERROR; + } + *rf_api = &srsran_rf_dev_skiq; + return SRSRAN_SUCCESS; +} +#endif /* ENABLE_RF_PLUGINS */ diff --git a/lib/src/phy/rf/rf_skiq_imp.h b/lib/src/phy/rf/rf_skiq_imp.h index 54a33dcbe..9d014b61b 100644 --- a/lib/src/phy/rf/rf_skiq_imp.h +++ b/lib/src/phy/rf/rf_skiq_imp.h @@ -16,6 +16,8 @@ #include "srsran/config.h" #include "srsran/phy/rf/rf.h" +extern rf_dev_t srsran_rf_dev_skiq; + SRSRAN_API int rf_skiq_open(char* args, void** handler); SRSRAN_API int rf_skiq_open_multi(char* args, void** handler, uint32_t nof_rx_antennas); diff --git a/lib/src/phy/rf/rf_soapy_imp.c b/lib/src/phy/rf/rf_soapy_imp.c index 8110126d3..cb0148081 100644 --- a/lib/src/phy/rf/rf_soapy_imp.c +++ b/lib/src/phy/rf/rf_soapy_imp.c @@ -16,6 +16,7 @@ #include #include "rf_helper.h" +#include "rf_plugin.h" #include "rf_soapy_imp.h" #include "srsran/phy/common/phy_common.h" #include "srsran/phy/utils/debug.h" @@ -994,3 +995,44 @@ int rf_soapy_send_timed_multi(void* h, return n; } + +rf_dev_t srsran_rf_dev_soapy = {"soapy", + rf_soapy_devname, + rf_soapy_start_rx_stream, + rf_soapy_stop_rx_stream, + rf_soapy_flush_buffer, + rf_soapy_has_rssi, + rf_soapy_get_rssi, + rf_soapy_suppress_stdout, + rf_soapy_register_error_handler, + rf_soapy_open, + rf_soapy_open_multi, + rf_soapy_close, + rf_soapy_set_rx_srate, + rf_soapy_set_rx_gain, + rf_soapy_set_rx_gain_ch, + rf_soapy_set_tx_gain, + rf_soapy_set_tx_gain_ch, + rf_soapy_get_rx_gain, + rf_soapy_get_tx_gain, + rf_soapy_get_info, + rf_soapy_set_rx_freq, + rf_soapy_set_tx_srate, + rf_soapy_set_tx_freq, + rf_soapy_get_time, + NULL, + rf_soapy_recv_with_time, + rf_soapy_recv_with_time_multi, + rf_soapy_send_timed, + .srsran_rf_send_timed_multi = rf_soapy_send_timed_multi}; + +#ifdef ENABLE_RF_PLUGINS +int register_plugin(rf_dev_t** rf_api) +{ + if (rf_api == NULL) { + return SRSRAN_ERROR; + } + *rf_api = &srsran_rf_dev_soapy; + return SRSRAN_SUCCESS; +} +#endif /* ENABLE_RF_PLUGINS */ diff --git a/lib/src/phy/rf/rf_soapy_imp.h b/lib/src/phy/rf/rf_soapy_imp.h index ff7facf34..c9f7869a9 100644 --- a/lib/src/phy/rf/rf_soapy_imp.h +++ b/lib/src/phy/rf/rf_soapy_imp.h @@ -20,6 +20,8 @@ #include #define DEVNAME_SOAPY "soapy" +extern rf_dev_t srsran_rf_dev_soapy; + SRSRAN_API int rf_soapy_open(char* args, void** handler); SRSRAN_API int rf_soapy_open_multi(char* args, void** handler, uint32_t num_requested_channels); diff --git a/lib/src/phy/rf/rf_uhd_imp.cc b/lib/src/phy/rf/rf_uhd_imp.cc index ac82113b1..d4bf609bc 100644 --- a/lib/src/phy/rf/rf_uhd_imp.cc +++ b/lib/src/phy/rf/rf_uhd_imp.cc @@ -20,6 +20,7 @@ #include #include "rf_helper.h" +#include "rf_plugin.h" #include "srsran/phy/utils/debug.h" #include "srsran/phy/utils/vector.h" @@ -1535,3 +1536,44 @@ int rf_uhd_send_timed_multi(void* h, return nsamples; } + +rf_dev_t srsran_rf_dev_uhd = {"UHD", + rf_uhd_devname, + rf_uhd_start_rx_stream, + rf_uhd_stop_rx_stream, + rf_uhd_flush_buffer, + rf_uhd_has_rssi, + rf_uhd_get_rssi, + rf_uhd_suppress_stdout, + rf_uhd_register_error_handler, + rf_uhd_open, + rf_uhd_open_multi, + rf_uhd_close, + rf_uhd_set_rx_srate, + rf_uhd_set_rx_gain, + rf_uhd_set_rx_gain_ch, + rf_uhd_set_tx_gain, + rf_uhd_set_tx_gain_ch, + rf_uhd_get_rx_gain, + rf_uhd_get_tx_gain, + rf_uhd_get_info, + rf_uhd_set_rx_freq, + rf_uhd_set_tx_srate, + rf_uhd_set_tx_freq, + rf_uhd_get_time, + rf_uhd_sync_pps, + rf_uhd_recv_with_time, + rf_uhd_recv_with_time_multi, + rf_uhd_send_timed, + rf_uhd_send_timed_multi}; + +#ifdef ENABLE_RF_PLUGINS +int register_plugin(rf_dev_t** rf_api) +{ + if (rf_api == NULL) { + return SRSRAN_ERROR; + } + *rf_api = &srsran_rf_dev_uhd; + return SRSRAN_SUCCESS; +} +#endif /* ENABLE_RF_PLUGINS */ diff --git a/lib/src/phy/rf/rf_uhd_imp.h b/lib/src/phy/rf/rf_uhd_imp.h index c668c770e..6b961653a 100644 --- a/lib/src/phy/rf/rf_uhd_imp.h +++ b/lib/src/phy/rf/rf_uhd_imp.h @@ -29,6 +29,8 @@ extern "C" { #define DEVNAME_E3X0 "uhd_e3x0" #define DEVNAME_UNKNOWN "uhd_unknown" +extern rf_dev_t srsran_rf_dev_uhd; + SRSRAN_API int rf_uhd_open(char* args, void** handler); SRSRAN_API int rf_uhd_open_multi(char* args, void** handler, uint32_t nof_channels); diff --git a/lib/src/phy/rf/rf_zmq_imp.c b/lib/src/phy/rf/rf_zmq_imp.c index 0bc822667..048ec9f0d 100644 --- a/lib/src/phy/rf/rf_zmq_imp.c +++ b/lib/src/phy/rf/rf_zmq_imp.c @@ -12,6 +12,7 @@ #include "rf_zmq_imp.h" #include "rf_helper.h" +#include "rf_plugin.h" #include "rf_zmq_imp_trx.h" #include #include @@ -983,3 +984,44 @@ clean_exit: return ret; } + +rf_dev_t srsran_rf_dev_zmq = {"zmq", + rf_zmq_devname, + rf_zmq_start_rx_stream, + rf_zmq_stop_rx_stream, + rf_zmq_flush_buffer, + rf_zmq_has_rssi, + rf_zmq_get_rssi, + rf_zmq_suppress_stdout, + rf_zmq_register_error_handler, + rf_zmq_open, + .srsran_rf_open_multi = rf_zmq_open_multi, + rf_zmq_close, + rf_zmq_set_rx_srate, + rf_zmq_set_rx_gain, + rf_zmq_set_rx_gain_ch, + rf_zmq_set_tx_gain, + rf_zmq_set_tx_gain_ch, + rf_zmq_get_rx_gain, + rf_zmq_get_tx_gain, + rf_zmq_get_info, + rf_zmq_set_rx_freq, + rf_zmq_set_tx_srate, + rf_zmq_set_tx_freq, + rf_zmq_get_time, + NULL, + rf_zmq_recv_with_time, + rf_zmq_recv_with_time_multi, + rf_zmq_send_timed, + .srsran_rf_send_timed_multi = rf_zmq_send_timed_multi}; + +#ifdef ENABLE_RF_PLUGINS +int register_plugin(rf_dev_t** rf_api) +{ + if (rf_api == NULL) { + return SRSRAN_ERROR; + } + *rf_api = &srsran_rf_dev_zmq; + return SRSRAN_SUCCESS; +} +#endif /* ENABLE_RF_PLUGINS */ diff --git a/lib/src/phy/rf/rf_zmq_imp.h b/lib/src/phy/rf/rf_zmq_imp.h index df409a9c2..0f484bc5e 100644 --- a/lib/src/phy/rf/rf_zmq_imp.h +++ b/lib/src/phy/rf/rf_zmq_imp.h @@ -21,6 +21,8 @@ #define DEVNAME_ZMQ "ZeroMQ" +extern rf_dev_t srsran_rf_dev_zmq; + SRSRAN_API int rf_zmq_open(char* args, void** handler); SRSRAN_API int rf_zmq_open_multi(char* args, void** handler, uint32_t nof_channels); diff --git a/lib/src/phy/rf/rf_zmq_test.c b/lib/src/phy/rf/rf_zmq_test.c index 19c7c50c1..261acbf4f 100644 --- a/lib/src/phy/rf/rf_zmq_test.c +++ b/lib/src/phy/rf/rf_zmq_test.c @@ -234,6 +234,7 @@ int param_test(const char* args_param, const int num_channels) int main() { + srsran_rf_load_plugins(); // // two Rx ports // if (param_test("rx_port=ipc://dl0,rx_port1=ipc://dl1", 2)) { // fprintf(stderr, "Param test failed!\n"); From 2570c2ce77818c2144074003a06e0152bf289262 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Fri, 18 Feb 2022 09:45:26 +0100 Subject: [PATCH 006/195] srsran_rf: cosmetic changes --- lib/src/phy/rf/rf_dev.h | 7 ++-- lib/src/phy/rf/rf_file_imp.h | 3 +- lib/src/phy/rf/rf_plugin.h | 2 +- lib/src/phy/rf/rf_soapy_imp.c | 4 +-- lib/src/phy/rf/rf_zmq_imp.c | 2 +- lib/src/phy/rf/rf_zmq_test.c | 63 +++++++++++++++++------------------ 6 files changed, 40 insertions(+), 41 deletions(-) diff --git a/lib/src/phy/rf/rf_dev.h b/lib/src/phy/rf/rf_dev.h index 2e2a1c099..2900e5816 100644 --- a/lib/src/phy/rf/rf_dev.h +++ b/lib/src/phy/rf/rf_dev.h @@ -19,7 +19,7 @@ static srsran_rf_plugin_t plugin_uhd = {"libsrsran_rf_uhd.so", NULL, NULL}; #else #include "rf_uhd_imp.h" -static srsran_rf_plugin_t plugin_uhd = {"", NULL, &srsran_rf_dev_uhd}; +static srsran_rf_plugin_t plugin_uhd = {"", NULL, &srsran_rf_dev_uhd}; #endif #endif @@ -49,7 +49,7 @@ static srsran_rf_plugin_t plugin_soapy = {"", NULL, &srsran_rf_dev_soapy}; static srsran_rf_plugin_t plugin_zmq = {"libsrsran_rf_zmq.so", NULL, NULL}; #else #include "rf_zmq_imp.h" -static srsran_rf_plugin_t plugin_zmq = {"", NULL, &srsran_rf_dev_zmq}; +static srsran_rf_plugin_t plugin_zmq = {"", NULL, &srsran_rf_dev_zmq}; #endif #endif @@ -63,7 +63,7 @@ static srsran_rf_plugin_t plugin_file = {"", NULL, &srsran_rf_dev_file}; static srsran_rf_plugin_t plugin_skiq = {"libsrsran_rf_skiq.so", NULL, NULL}; #else #include "rf_skiq_imp.h" -static srsran_rf_plugin_t plugin_skiq = {"", NULL, &srsran_rf_dev_skiq}; +static srsran_rf_plugin_t plugin_skiq = {"", NULL, &srsran_rf_dev_skiq}; #endif #endif @@ -85,7 +85,6 @@ static srsran_rf_plugin_t plugin_dummy = {"", NULL, &srsran_rf_dev_dummy}; #endif - /** * Collection of all currently available RF plugins */ diff --git a/lib/src/phy/rf/rf_file_imp.h b/lib/src/phy/rf/rf_file_imp.h index 439ec6ec3..c1856993e 100644 --- a/lib/src/phy/rf/rf_file_imp.h +++ b/lib/src/phy/rf/rf_file_imp.h @@ -129,6 +129,7 @@ SRSRAN_API int rf_file_send_timed_multi(void* h, * @param[in] base_srate Sample rate of RX and TX files * @return SRSRAN_SUCCESS on success, otherwise error code */ -SRSRAN_API int rf_file_open_file(void** h, FILE** rx_files, FILE** tx_files, uint32_t nof_channels, uint32_t base_srate); +SRSRAN_API int +rf_file_open_file(void** h, FILE** rx_files, FILE** tx_files, uint32_t nof_channels, uint32_t base_srate); #endif // SRSRAN_RF_FILE_IMP_H diff --git a/lib/src/phy/rf/rf_plugin.h b/lib/src/phy/rf/rf_plugin.h index 6e0f83106..177f4eef0 100644 --- a/lib/src/phy/rf/rf_plugin.h +++ b/lib/src/phy/rf/rf_plugin.h @@ -19,7 +19,7 @@ extern "C" { #endif -SRSRAN_API int register_plugin(rf_dev_t **rf_api); +SRSRAN_API int register_plugin(rf_dev_t** rf_api); #ifdef __cplusplus } diff --git a/lib/src/phy/rf/rf_soapy_imp.c b/lib/src/phy/rf/rf_soapy_imp.c index cb0148081..73664771e 100644 --- a/lib/src/phy/rf/rf_soapy_imp.c +++ b/lib/src/phy/rf/rf_soapy_imp.c @@ -26,8 +26,8 @@ #include #include #include -#include #include +#include #define HAVE_ASYNC_THREAD 0 @@ -854,7 +854,7 @@ int rf_soapy_recv_with_time_multi(void* h, int rf_soapy_recv_with_time(void* h, void* data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs) { void* data_multi[SRSRAN_MAX_PORTS] = {NULL}; - data_multi[0] = data; + data_multi[0] = data; return rf_soapy_recv_with_time_multi(h, data_multi, nsamples, blocking, secs, frac_secs); } diff --git a/lib/src/phy/rf/rf_zmq_imp.c b/lib/src/phy/rf/rf_zmq_imp.c index 048ec9f0d..0ed634eab 100644 --- a/lib/src/phy/rf/rf_zmq_imp.c +++ b/lib/src/phy/rf/rf_zmq_imp.c @@ -962,7 +962,7 @@ int rf_zmq_send_timed_multi(void* h, } // Scale according to current gain - srsran_vec_sc_prod_cfc(buf, tx_gain, buf, nsamples_baseband); + srsran_vec_sc_prod_cfc(buf, tx_gain, buf, nsamples_baseband); // Finally, transmit baseband int n = rf_zmq_tx_baseband(&handler->transmitter[i], buf, nsamples_baseband); diff --git a/lib/src/phy/rf/rf_zmq_test.c b/lib/src/phy/rf/rf_zmq_test.c index 261acbf4f..be42022e5 100644 --- a/lib/src/phy/rf/rf_zmq_test.c +++ b/lib/src/phy/rf/rf_zmq_test.c @@ -93,12 +93,11 @@ void enb_tx_function(const char* tx_args, bool timed_tx) // send data subframe per subframe uint32_t num_txed_samples = 0; - // initial transmission without ts void* data_ptr[SRSRAN_MAX_PORTS] = {NULL}; - cf_t tx_buffer[NOF_RX_ANT][SF_LEN]; + cf_t tx_buffer[NOF_RX_ANT][SF_LEN]; for (int c = 0; c < NOF_RX_ANT; c++) { - memcpy(&tx_buffer[c], &enb_tx_buffer[c][num_txed_samples], SF_LEN * sizeof (cf_t)); + memcpy(&tx_buffer[c], &enb_tx_buffer[c][num_txed_samples], SF_LEN * sizeof(cf_t)); data_ptr[c] = &tx_buffer[c][0]; } int ret = srsran_rf_send_multi(&enb_radio, (void**)data_ptr, SF_LEN, true, true, false); @@ -116,7 +115,7 @@ void enb_tx_function(const char* tx_args, bool timed_tx) // prepare data buffer for (int c = 0; c < NOF_RX_ANT; c++) { - memcpy(&tx_buffer[c], &enb_tx_buffer[c][num_txed_samples], SF_LEN * sizeof (cf_t)); + memcpy(&tx_buffer[c], &enb_tx_buffer[c][num_txed_samples], SF_LEN * sizeof(cf_t)); data_ptr[c] = &tx_buffer[c][0]; } @@ -235,34 +234,34 @@ int param_test(const char* args_param, const int num_channels) int main() { srsran_rf_load_plugins(); -// // two Rx ports -// if (param_test("rx_port=ipc://dl0,rx_port1=ipc://dl1", 2)) { -// fprintf(stderr, "Param test failed!\n"); -// return SRSRAN_ERROR; -// } - -// // multiple rx ports, no channel index provided -// if (param_test("rx_port=ipc://dl0,rx_port=ipc://dl1,rx_port=ipc://dl2,rx_port=ipc://dl3,base_srate=1.92e6", 4)) { -// fprintf(stderr, "Param test failed!\n"); -// return SRSRAN_ERROR; -// } - -// // One Rx, one Tx and all generic options -// if (param_test("rx_port0=tcp://" -// "localhost:2000,rx_format=sc16,tx_format=sc16,tx_type=pub,rx_type=sub,base_srate=1.92e6,id=test", -// 1)) { -// fprintf(stderr, "Param test failed!\n"); -// return SRSRAN_ERROR; -// } - -// // 1 port, 2 antennas, MIMO freq config -// if (param_test( -// "tx_port0=tcp://*:2001,tx_port1=tcp://*:2003,rx_port0=tcp://localhost:2000,rx_port1=tcp://" -// "localhost:2002,id=ue,base_srate=23.04e6,tx_freq0=2510e6,tx_freq1=2510e6,rx_freq0=2630e6,,rx_freq1=2630e6", -// 2)) { -// fprintf(stderr, "Param test failed!\n"); -// return SRSRAN_ERROR; -// } + // // two Rx ports + // if (param_test("rx_port=ipc://dl0,rx_port1=ipc://dl1", 2)) { + // fprintf(stderr, "Param test failed!\n"); + // return SRSRAN_ERROR; + // } + + // // multiple rx ports, no channel index provided + // if (param_test("rx_port=ipc://dl0,rx_port=ipc://dl1,rx_port=ipc://dl2,rx_port=ipc://dl3,base_srate=1.92e6", 4)) { + // fprintf(stderr, "Param test failed!\n"); + // return SRSRAN_ERROR; + // } + + // // One Rx, one Tx and all generic options + // if (param_test("rx_port0=tcp://" + // "localhost:2000,rx_format=sc16,tx_format=sc16,tx_type=pub,rx_type=sub,base_srate=1.92e6,id=test", + // 1)) { + // fprintf(stderr, "Param test failed!\n"); + // return SRSRAN_ERROR; + // } + + // // 1 port, 2 antennas, MIMO freq config + // if (param_test( + // "tx_port0=tcp://*:2001,tx_port1=tcp://*:2003,rx_port0=tcp://localhost:2000,rx_port1=tcp://" + // "localhost:2002,id=ue,base_srate=23.04e6,tx_freq0=2510e6,tx_freq1=2510e6,rx_freq0=2630e6,,rx_freq1=2630e6", + // 2)) { + // fprintf(stderr, "Param test failed!\n"); + // return SRSRAN_ERROR; + // } #if NOF_RX_ANT == 1 // single tx, single rx with continuous transmissions (no timed tx) using IPC transport From c2d4dedafc4a941d65851c8ba160bf7746e5caaa Mon Sep 17 00:00:00 2001 From: Carlo Galiotto Date: Wed, 26 Jan 2022 16:15:46 +0100 Subject: [PATCH 007/195] sched: plug dyn CQI->MCS mapping into scheduler Signed-off-by: Carlo Galiotto --- srsgnb/src/stack/mac/sched_nr_grant_allocator.cc | 10 ++++++++++ srsgnb/src/stack/rrc/cell_asn1_config.cc | 7 ++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc b/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc index e86de3673..d8c4d8671 100644 --- a/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc +++ b/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc @@ -329,6 +329,16 @@ alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, uint32_t ss_id, const // Allocate HARQ int mcs = ue->fixed_pdsch_mcs(); if (ue.h_dl->empty()) { + mcs = srsran_ra_nr_cqi_to_mcs(/* cqi */ ue.dl_cqi(), + /* cqi_table_idx */ ue.cfg().phy().csi.reports->cqi_table, + /* mcs_table */ pdsch.sch.sch_cfg.mcs_table, + /* dci_format */ pdcch.dci.ctx.format, + /* search_space_type*/ pdcch.dci.ctx.ss_type, + /* rnti_type */ rnti_type); + if (mcs < 0) { + logger.warning("SCHED: UE rnti=0x%x reported CQI=0 - Using lowest MCS=0", ue->rnti); + mcs = 0; + } bool success = ue.h_dl->new_tx(ue.pdsch_slot, ue.uci_slot, dl_grant, mcs, 4, pdcch.dci); srsran_assert(success, "Failed to allocate DL HARQ"); } else { diff --git a/srsgnb/src/stack/rrc/cell_asn1_config.cc b/srsgnb/src/stack/rrc/cell_asn1_config.cc index af90efa99..9da82a9a1 100644 --- a/srsgnb/src/stack/rrc/cell_asn1_config.cc +++ b/srsgnb/src/stack/rrc/cell_asn1_config.cc @@ -173,7 +173,7 @@ int fill_csi_report_from_enb_cfg(const rrc_nr_cfg_t& cfg, csi_meas_cfg_s& csi_me csi_report.group_based_beam_report.set_disabled(); // Skip CQI table (optional) csi_report.cqi_table_present = true; - csi_report.cqi_table = asn1::rrc_nr::csi_report_cfg_s::cqi_table_opts::table2; + csi_report.cqi_table = asn1::rrc_nr::csi_report_cfg_s::cqi_table_opts::table1; csi_report.subband_size = asn1::rrc_nr::csi_report_cfg_s::subband_size_opts::value1; if (cfg.cell_list[0].duplex_mode == SRSRAN_DUPLEX_MODE_FDD) { @@ -519,6 +519,11 @@ void fill_pdsch_cfg_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, pdsch_cfg out.prb_bundling_type.static_bundling().bundle_size = pdsch_cfg_s::prb_bundling_type_c_::static_bundling_s_::bundle_size_opts::wideband; + // MCS Table + // NOTE: For Table 1 or QAM64, set false and comment value + // out.mcs_table_present = true; + // out.mcs_table.value = pdsch_cfg_s::mcs_table_opts::qam256; + // ZP-CSI out.zp_csi_rs_res_to_add_mod_list.resize(1); out.zp_csi_rs_res_to_add_mod_list[0].zp_csi_rs_res_id = 0; From 65dae777b12176e7743accfd05033fa769a837d1 Mon Sep 17 00:00:00 2001 From: Carlo Galiotto Date: Fri, 4 Feb 2022 17:56:17 +0100 Subject: [PATCH 008/195] sched-nr: add option for predefined fixed DL MCS Signed-off-by: Carlo Galiotto --- srsgnb/src/stack/mac/sched_nr_cfg.cc | 1 - .../src/stack/mac/sched_nr_grant_allocator.cc | 24 +++++++++++-------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/srsgnb/src/stack/mac/sched_nr_cfg.cc b/srsgnb/src/stack/mac/sched_nr_cfg.cc index 532af8c95..854080e69 100644 --- a/srsgnb/src/stack/mac/sched_nr_cfg.cc +++ b/srsgnb/src/stack/mac/sched_nr_cfg.cc @@ -168,7 +168,6 @@ cell_config_manager::cell_config_manager(uint32_t cc_, sched_params_t::sched_params_t(const sched_args_t& sched_cfg_) : sched_cfg(sched_cfg_) { - srsran_assert(sched_cfg.fixed_dl_mcs >= 0, "Dynamic DL MCS not supported"); srsran_assert(sched_cfg.fixed_ul_mcs >= 0, "Dynamic DL MCS not supported"); } diff --git a/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc b/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc index d8c4d8671..34a2c33de 100644 --- a/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc +++ b/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc @@ -329,15 +329,17 @@ alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, uint32_t ss_id, const // Allocate HARQ int mcs = ue->fixed_pdsch_mcs(); if (ue.h_dl->empty()) { - mcs = srsran_ra_nr_cqi_to_mcs(/* cqi */ ue.dl_cqi(), - /* cqi_table_idx */ ue.cfg().phy().csi.reports->cqi_table, - /* mcs_table */ pdsch.sch.sch_cfg.mcs_table, - /* dci_format */ pdcch.dci.ctx.format, - /* search_space_type*/ pdcch.dci.ctx.ss_type, - /* rnti_type */ rnti_type); if (mcs < 0) { - logger.warning("SCHED: UE rnti=0x%x reported CQI=0 - Using lowest MCS=0", ue->rnti); - mcs = 0; + mcs = srsran_ra_nr_cqi_to_mcs(/* cqi */ ue.dl_cqi(), + /* cqi_table_idx */ ue.cfg().phy().csi.reports->cqi_table, + /* mcs_table */ pdsch.sch.sch_cfg.mcs_table, + /* dci_format */ pdcch.dci.ctx.format, + /* search_space_type*/ pdcch.dci.ctx.ss_type, + /* rnti_type */ rnti_type); + if (mcs < 0) { + logger.warning("SCHED: UE rnti=0x%x reported CQI=0 - Using lowest MCS=0", ue->rnti); + mcs = 0; + } } bool success = ue.h_dl->new_tx(ue.pdsch_slot, ue.uci_slot, dl_grant, mcs, 4, pdcch.dci); srsran_assert(success, "Failed to allocate DL HARQ"); @@ -351,6 +353,7 @@ alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, uint32_t ss_id, const slot_cfg.idx = ue.pdsch_slot.to_uint(); // Value 0.95 is from TS 38.214 v15.14.00, Section 5.1.3, page 17 const static float max_R = 0.95; + double R_prime = max_R; while (true) { // Generate PDSCH bool success = ue->phy().get_pdsch_cfg(slot_cfg, pdcch.dci, pdsch.sch); @@ -358,14 +361,15 @@ alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, uint32_t ss_id, const if (ue.h_dl->nof_retx() != 0) { srsran_assert(pdsch.sch.grant.tb[0].tbs == (int)ue.h_dl->tbs(), "The TBS did not remain constant in retx"); } - if (ue.h_dl->nof_retx() > 0 or pdsch.sch.grant.tb[0].R_prime < max_R or mcs <= 0) { + R_prime = pdsch.sch.grant.tb[0].R_prime; + if (ue.h_dl->nof_retx() > 0 or R_prime < max_R or mcs <= 0) { break; } // Decrease MCS if first tx and rate is too high mcs--; pdcch.dci.mcs = mcs; } - if (mcs == 0) { + if (R_prime >= max_R and mcs == 0) { logger.warning("Couldn't find mcs that leads to R<0.95"); } ue.h_dl->set_mcs(mcs); From 2b3158536a3ab151db74b22172b9001394531a32 Mon Sep 17 00:00:00 2001 From: Carlo Galiotto Date: Tue, 8 Feb 2022 13:48:31 +0100 Subject: [PATCH 009/195] sched-nr: fix sched not allocating CCCH at low MCS Signed-off-by: Carlo Galiotto --- srsgnb/hdr/stack/mac/sched_nr_ue.h | 6 +- .../src/stack/mac/sched_nr_grant_allocator.cc | 62 ++++++++++++------- srsgnb/src/stack/mac/sched_nr_ue.cc | 30 ++++++++- 3 files changed, 71 insertions(+), 27 deletions(-) diff --git a/srsgnb/hdr/stack/mac/sched_nr_ue.h b/srsgnb/hdr/stack/mac/sched_nr_ue.h index 0fd4eb123..9fcbed9b6 100644 --- a/srsgnb/hdr/stack/mac/sched_nr_ue.h +++ b/srsgnb/hdr/stack/mac/sched_nr_ue.h @@ -61,7 +61,7 @@ public: struct pdu_builder { pdu_builder() = default; explicit pdu_builder(uint32_t cc_, ue_buffer_manager& parent_) : cc(cc_), parent(&parent_) {} - void alloc_subpdus(uint32_t rem_bytes, sched_nr_interface::dl_pdu_t& pdu); + bool alloc_subpdus(uint32_t rem_bytes, sched_nr_interface::dl_pdu_t& pdu); private: uint32_t cc = SRSRAN_MAX_CARRIERS; @@ -180,9 +180,9 @@ public: ul_harq_proc* find_empty_ul_harq() { return ue->harq_ent.find_empty_ul_harq(); } /// Build PDU with MAC CEs and MAC SDUs - void build_pdu(uint32_t rem_bytes, sched_nr_interface::dl_pdu_t& pdu) + bool build_pdu(uint32_t rem_bytes, sched_nr_interface::dl_pdu_t& pdu) { - ue->pdu_builder.alloc_subpdus(rem_bytes, pdu); + return ue->pdu_builder.alloc_subpdus(rem_bytes, pdu); } /// Channel Information Getters diff --git a/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc b/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc index 34a2c33de..f1aa3e404 100644 --- a/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc +++ b/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc @@ -353,33 +353,51 @@ alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, uint32_t ss_id, const slot_cfg.idx = ue.pdsch_slot.to_uint(); // Value 0.95 is from TS 38.214 v15.14.00, Section 5.1.3, page 17 const static float max_R = 0.95; - double R_prime = max_R; + double R_prime; + const static int min_MCS_ccch = 4; + // The purpose of the external loop is to reset the MCS to a min value of 4 if there are not enough PRBs to + // allocate the SRB0/CCCH. This loop only affects the low MCS values while (true) { - // Generate PDSCH - bool success = ue->phy().get_pdsch_cfg(slot_cfg, pdcch.dci, pdsch.sch); - srsran_assert(success, "Error converting DCI to grant"); - if (ue.h_dl->nof_retx() != 0) { - srsran_assert(pdsch.sch.grant.tb[0].tbs == (int)ue.h_dl->tbs(), "The TBS did not remain constant in retx"); + // The purpose of the internal loop is to decrease the MCS if the effective coderate is too high. This loop + // only affects the high MCS values + while (true) { + // Generate PDSCH + bool success = ue->phy().get_pdsch_cfg(slot_cfg, pdcch.dci, pdsch.sch); + srsran_assert(success, "Error converting DCI to grant"); + if (ue.h_dl->nof_retx() != 0) { + srsran_assert(pdsch.sch.grant.tb[0].tbs == (int)ue.h_dl->tbs(), "The TBS did not remain constant in retx"); + } + R_prime = pdsch.sch.grant.tb[0].R_prime; + if (ue.h_dl->nof_retx() > 0 or R_prime < max_R or mcs <= 0) { + break; + } + // Decrease MCS if first tx and rate is too high + mcs--; + pdcch.dci.mcs = mcs; } - R_prime = pdsch.sch.grant.tb[0].R_prime; - if (ue.h_dl->nof_retx() > 0 or R_prime < max_R or mcs <= 0) { + if (R_prime >= max_R and mcs == 0) { + logger.warning("Couldn't find mcs that leads to R<0.95"); + } + ue.h_dl->set_mcs(mcs); + ue.h_dl->set_tbs(pdsch.sch.grant.tb[0].tbs); // set HARQ TBS + pdsch.sch.grant.tb[0].softbuffer.tx = ue.h_dl->get_softbuffer().get(); + pdsch.data[0] = ue.h_dl->get_tx_pdu()->get(); + + // Select scheduled LCIDs and update UE buffer state + bwp_pdsch_slot.dl.data.emplace_back(); + // NOTE: ue.h_dl->tbs() has to be converted from bits to bytes + bool segmented_ccch_pdu = not ue.build_pdu(ue.h_dl->tbs() / 8, bwp_pdsch_slot.dl.data.back()); + if (segmented_ccch_pdu) { + // In case of segmented PDU for CCCH, set minimum MCS to 4 and re-run the outer while loop + bwp_pdsch_slot.dl.data.pop_back(); + mcs = min_MCS_ccch; + pdcch.dci.mcs = mcs; + logger.warning( + "SCHED: MCS increased to min value %d to allocate SRB0/CCCH for rnti=0x%x", min_MCS_ccch, ue->rnti); + } else { break; } - // Decrease MCS if first tx and rate is too high - mcs--; - pdcch.dci.mcs = mcs; - } - if (R_prime >= max_R and mcs == 0) { - logger.warning("Couldn't find mcs that leads to R<0.95"); } - ue.h_dl->set_mcs(mcs); - ue.h_dl->set_tbs(pdsch.sch.grant.tb[0].tbs); // set HARQ TBS - pdsch.sch.grant.tb[0].softbuffer.tx = ue.h_dl->get_softbuffer().get(); - pdsch.data[0] = ue.h_dl->get_tx_pdu()->get(); - - // Select scheduled LCIDs and update UE buffer state - bwp_pdsch_slot.dl.data.emplace_back(); - ue.build_pdu(ue.h_dl->tbs(), bwp_pdsch_slot.dl.data.back()); // Generate PUCCH bwp_uci_slot.pending_acks.emplace_back(); diff --git a/srsgnb/src/stack/mac/sched_nr_ue.cc b/srsgnb/src/stack/mac/sched_nr_ue.cc index 8f9b78ef6..061ea98e9 100644 --- a/srsgnb/src/stack/mac/sched_nr_ue.cc +++ b/srsgnb/src/stack/mac/sched_nr_ue.cc @@ -27,8 +27,16 @@ int ue_buffer_manager::get_dl_tx_total() const return total_bytes; } -void ue_buffer_manager::pdu_builder::alloc_subpdus(uint32_t rem_bytes, sched_nr_interface::dl_pdu_t& pdu) +// Return true if there is no SRB0/CCCH MAC PDU segmentation, false otherwise +bool ue_buffer_manager::pdu_builder::alloc_subpdus(uint32_t rem_bytes, sched_nr_interface::dl_pdu_t& pdu) { + // In case of SRB0/CCCH PDUs, we need to check whether there is PDU segmentation; if LCID = 0 has emtpy buffer, no + // need to perform this check + bool check_ccch_pdu_segmentation = + parent->get_dl_tx_total(srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::CCCH) > 0 ? true : false; + + // First step: allocate MAC CEs until resources allow + srsran::deque restore_ces; for (ce_t ce : parent->pending_ces) { if (ce.cc == cc) { // Note: This check also avoids thread collisions across UE carriers @@ -38,17 +46,35 @@ void ue_buffer_manager::pdu_builder::alloc_subpdus(uint32_t rem_bytes, sched_nr_ } rem_bytes -= size_ce; pdu.subpdus.push_back(ce.lcid); - parent->pending_ces.pop_front(); + // If there is possibility of CCCH segmentation, we need to save the MAC CEs in a tmp queue to be later restored + if (check_ccch_pdu_segmentation) { + restore_ces.push_back(parent->pending_ces.front()); + parent->pending_ces.pop_front(); + } } } + // Second step: allocate the remaining LCIDs (LCIDs for MAC CEs are addressed above) for (uint32_t lcid = 0; rem_bytes > 0 and is_lcid_valid(lcid); ++lcid) { uint32_t pending_lcid_bytes = parent->get_dl_tx_total(lcid); + // Verify if the TBS is big enough to store the entire CCCH buffer + // Note: (pending_lcid_bytes > rem_bytes) implies (check_ccch_pdu_segmentation == true) + if (lcid == srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::CCCH and pending_lcid_bytes > rem_bytes) { + // restore the MAC CEs as they were at the beginning of the function + for (ce_t ce : restore_ces) { + parent->pending_ces.push_back(ce); + } + // double check if this line is required + pdu.subpdus.clear(); + return false; + } if (pending_lcid_bytes > 0) { rem_bytes -= std::min(rem_bytes, pending_lcid_bytes); pdu.subpdus.push_back(lcid); } } + + return true; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// From c935484102368218e05a0a71b3bcd703f8df4eb8 Mon Sep 17 00:00:00 2001 From: Carlo Galiotto Date: Wed, 9 Feb 2022 12:29:26 +0100 Subject: [PATCH 010/195] sched-nr: fix scheduler to pass nr tests Signed-off-by: Carlo Galiotto --- srsgnb/hdr/stack/mac/sched_nr_ue.h | 6 ++-- .../src/stack/mac/sched_nr_grant_allocator.cc | 17 +++++++--- srsgnb/src/stack/mac/sched_nr_ue.cc | 32 +++++++++++++------ 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/srsgnb/hdr/stack/mac/sched_nr_ue.h b/srsgnb/hdr/stack/mac/sched_nr_ue.h index 9fcbed9b6..3ab7d1852 100644 --- a/srsgnb/hdr/stack/mac/sched_nr_ue.h +++ b/srsgnb/hdr/stack/mac/sched_nr_ue.h @@ -61,7 +61,7 @@ public: struct pdu_builder { pdu_builder() = default; explicit pdu_builder(uint32_t cc_, ue_buffer_manager& parent_) : cc(cc_), parent(&parent_) {} - bool alloc_subpdus(uint32_t rem_bytes, sched_nr_interface::dl_pdu_t& pdu); + bool alloc_subpdus(uint32_t rem_bytes, sched_nr_interface::dl_pdu_t& pdu, bool reset_buf_states = false); private: uint32_t cc = SRSRAN_MAX_CARRIERS; @@ -180,9 +180,9 @@ public: ul_harq_proc* find_empty_ul_harq() { return ue->harq_ent.find_empty_ul_harq(); } /// Build PDU with MAC CEs and MAC SDUs - bool build_pdu(uint32_t rem_bytes, sched_nr_interface::dl_pdu_t& pdu) + bool build_pdu(uint32_t rem_bytes, sched_nr_interface::dl_pdu_t& pdu, bool reset_buf_states = false) { - return ue->pdu_builder.alloc_subpdus(rem_bytes, pdu); + return ue->pdu_builder.alloc_subpdus(rem_bytes, pdu, reset_buf_states); } /// Channel Information Getters diff --git a/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc b/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc index f1aa3e404..41e37d5b5 100644 --- a/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc +++ b/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc @@ -385,15 +385,22 @@ alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, uint32_t ss_id, const // Select scheduled LCIDs and update UE buffer state bwp_pdsch_slot.dl.data.emplace_back(); - // NOTE: ue.h_dl->tbs() has to be converted from bits to bytes - bool segmented_ccch_pdu = not ue.build_pdu(ue.h_dl->tbs() / 8, bwp_pdsch_slot.dl.data.back()); - if (segmented_ccch_pdu) { + // NOTES: 1) ue.h_dl->tbs() has to be converted from bits to bytes + // 2) In case of CCCH segmentation, we'll need to repeat the scheduling with a higher MCS. Hence, the + // function ue.build_pdu() will reset the LCIDs and UE buffer states as before its execution if the flag + // "mcstbs() / 8, bwp_pdsch_slot.dl.data.back(), mcs < min_MCS_ccch); + if (segmented_ccch_pdu and mcs < min_MCS_ccch) { // In case of segmented PDU for CCCH, set minimum MCS to 4 and re-run the outer while loop bwp_pdsch_slot.dl.data.pop_back(); mcs = min_MCS_ccch; pdcch.dci.mcs = mcs; - logger.warning( - "SCHED: MCS increased to min value %d to allocate SRB0/CCCH for rnti=0x%x", min_MCS_ccch, ue->rnti); + logger.info("SCHED: MCS increased to min value %d to allocate SRB0/CCCH for rnti=0x%x", min_MCS_ccch, ue->rnti); + } else if (segmented_ccch_pdu /* and mcs >= min_MCS_ccch */) { + // With MCS >= then min_MCS_ccch, it is not possible to allocate SRB0/CCCH without PDU segmentation + logger.error("SCHED: Insufficient resources to allocate SRB0/CCCH without PDU segmentation for rnti=0x%x", + ue->rnti); + break; } else { break; } diff --git a/srsgnb/src/stack/mac/sched_nr_ue.cc b/srsgnb/src/stack/mac/sched_nr_ue.cc index 061ea98e9..549da55d6 100644 --- a/srsgnb/src/stack/mac/sched_nr_ue.cc +++ b/srsgnb/src/stack/mac/sched_nr_ue.cc @@ -27,8 +27,18 @@ int ue_buffer_manager::get_dl_tx_total() const return total_bytes; } -// Return true if there is no SRB0/CCCH MAC PDU segmentation, false otherwise -bool ue_buffer_manager::pdu_builder::alloc_subpdus(uint32_t rem_bytes, sched_nr_interface::dl_pdu_t& pdu) +/** + * @brief Allocates LCIDs and update US buffer states depending on available resources and checks if there is SRB0/CCCH + MAC PDU segmentation + + * @param rem_bytes TBS to be filled with MAC CEs and MAC SDUs [in bytes] + * @param reset_buf_states If true, when there is SRB0/CCCH MAC PDU segmentation, restore the UE buffers and scheduled + LCIDs as before running this function + * @return true if there is no SRB0/CCCH MAC PDU segmentation, false otherwise + */ +bool ue_buffer_manager::pdu_builder::alloc_subpdus(uint32_t rem_bytes, + sched_nr_interface::dl_pdu_t& pdu, + bool reset_buf_states) { // In case of SRB0/CCCH PDUs, we need to check whether there is PDU segmentation; if LCID = 0 has emtpy buffer, no // need to perform this check @@ -47,10 +57,10 @@ bool ue_buffer_manager::pdu_builder::alloc_subpdus(uint32_t rem_bytes, sched_nr_ rem_bytes -= size_ce; pdu.subpdus.push_back(ce.lcid); // If there is possibility of CCCH segmentation, we need to save the MAC CEs in a tmp queue to be later restored - if (check_ccch_pdu_segmentation) { + if (check_ccch_pdu_segmentation and reset_buf_states) { restore_ces.push_back(parent->pending_ces.front()); - parent->pending_ces.pop_front(); } + parent->pending_ces.pop_front(); } } @@ -60,12 +70,16 @@ bool ue_buffer_manager::pdu_builder::alloc_subpdus(uint32_t rem_bytes, sched_nr_ // Verify if the TBS is big enough to store the entire CCCH buffer // Note: (pending_lcid_bytes > rem_bytes) implies (check_ccch_pdu_segmentation == true) if (lcid == srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::CCCH and pending_lcid_bytes > rem_bytes) { - // restore the MAC CEs as they were at the beginning of the function - for (ce_t ce : restore_ces) { - parent->pending_ces.push_back(ce); + if (reset_buf_states) { + // restore the MAC CEs as they were at the beginning of the function + for (ce_t ce : restore_ces) { + parent->pending_ces.push_back(ce); + } + // double check if this line is required + pdu.subpdus.clear(); + } else { + pdu.subpdus.push_back(lcid); } - // double check if this line is required - pdu.subpdus.clear(); return false; } if (pending_lcid_bytes > 0) { From f73286727eadfb5cc92e062dc8683a7cd1af4b9a Mon Sep 17 00:00:00 2001 From: Carlo Galiotto Date: Thu, 10 Feb 2022 13:41:42 +0100 Subject: [PATCH 011/195] sched-nr: simplfy code for MCS with SRB0/CCCH Signed-off-by: Carlo Galiotto --- srsenb/enb.conf.example | 2 +- srsgnb/hdr/stack/mac/sched_nr_ue.h | 7 +- .../src/stack/mac/sched_nr_grant_allocator.cc | 88 +++++++++---------- srsgnb/src/stack/mac/sched_nr_ue.cc | 27 +----- 4 files changed, 49 insertions(+), 75 deletions(-) diff --git a/srsenb/enb.conf.example b/srsenb/enb.conf.example index 4d5cce198..c18387112 100644 --- a/srsenb/enb.conf.example +++ b/srsenb/enb.conf.example @@ -217,7 +217,7 @@ enable = false #init_dl_cqi=5 #max_sib_coderate=0.3 #pdcch_cqi_offset=0 -#nr_pdsch_mcs=28 +nr_pdsch_mcs=28 #nr_pusch_mcs=28 ##################################################################### diff --git a/srsgnb/hdr/stack/mac/sched_nr_ue.h b/srsgnb/hdr/stack/mac/sched_nr_ue.h index 3ab7d1852..54787b43b 100644 --- a/srsgnb/hdr/stack/mac/sched_nr_ue.h +++ b/srsgnb/hdr/stack/mac/sched_nr_ue.h @@ -61,7 +61,8 @@ public: struct pdu_builder { pdu_builder() = default; explicit pdu_builder(uint32_t cc_, ue_buffer_manager& parent_) : cc(cc_), parent(&parent_) {} - bool alloc_subpdus(uint32_t rem_bytes, sched_nr_interface::dl_pdu_t& pdu, bool reset_buf_states = false); + bool alloc_subpdus(uint32_t rem_bytes, sched_nr_interface::dl_pdu_t& pdu); + uint32_t pending_bytes(uint32_t lcid) const { return parent->get_dl_tx(lcid); } private: uint32_t cc = SRSRAN_MAX_CARRIERS; @@ -182,9 +183,11 @@ public: /// Build PDU with MAC CEs and MAC SDUs bool build_pdu(uint32_t rem_bytes, sched_nr_interface::dl_pdu_t& pdu, bool reset_buf_states = false) { - return ue->pdu_builder.alloc_subpdus(rem_bytes, pdu, reset_buf_states); + return ue->pdu_builder.alloc_subpdus(rem_bytes, pdu); } + bool get_pending_bytes(uint32_t lcid) const { return ue->pdu_builder.pending_bytes(lcid); } + /// Channel Information Getters uint32_t dl_cqi() const { return ue->dl_cqi; } uint32_t ul_cqi() const { return ue->ul_cqi; } diff --git a/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc b/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc index 41e37d5b5..963c834d1 100644 --- a/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc +++ b/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc @@ -13,6 +13,7 @@ #include "srsgnb/hdr/stack/mac/sched_nr_grant_allocator.h" #include "srsgnb/hdr/stack/mac/sched_nr_bwp.h" #include "srsgnb/hdr/stack/mac/sched_nr_helpers.h" +#include "srsran/mac/mac_sch_pdu_nr.h" namespace srsenb { namespace sched_nr_impl { @@ -326,8 +327,9 @@ alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, uint32_t ss_id, const // Allocate PDSCH pdsch_t& pdsch = bwp_pdcch_slot.pdschs.alloc_ue_pdsch_unchecked(ss_id, dci_fmt, dl_grant, ue.cfg(), pdcch.dci); - // Allocate HARQ - int mcs = ue->fixed_pdsch_mcs(); + // Select MCS and Allocate HARQ + int mcs = ue->fixed_pdsch_mcs(); + const static int min_MCS_ccch = 4; if (ue.h_dl->empty()) { if (mcs < 0) { mcs = srsran_ra_nr_cqi_to_mcs(/* cqi */ ue.dl_cqi(), @@ -341,6 +343,13 @@ alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, uint32_t ss_id, const mcs = 0; } } + // Overwrite MCS if there are pending bytes for LCID. The optimal way would be to verify that there are pending + // bytes and that the MAC SDU for CCCH gets segmented. But since the event of segmentation happens at most a couple + // of times (e.g., to send msg4/RRCSetup), we opt for the less optimal but simpler approach. + if (ue.get_pending_bytes(srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::CCCH) and mcs < min_MCS_ccch) { + mcs = min_MCS_ccch; + logger.info("SCHED: MCS increased to min value %d to allocate SRB0/CCCH for rnti=0x%x", min_MCS_ccch, ue->rnti); + } bool success = ue.h_dl->new_tx(ue.pdsch_slot, ue.uci_slot, dl_grant, mcs, 4, pdcch.dci); srsran_assert(success, "Failed to allocate DL HARQ"); } else { @@ -354,56 +363,39 @@ alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, uint32_t ss_id, const // Value 0.95 is from TS 38.214 v15.14.00, Section 5.1.3, page 17 const static float max_R = 0.95; double R_prime; - const static int min_MCS_ccch = 4; - // The purpose of the external loop is to reset the MCS to a min value of 4 if there are not enough PRBs to - // allocate the SRB0/CCCH. This loop only affects the low MCS values + // The purpose of the internal loop is to decrease the MCS if the effective coderate is too high. This loop + // only affects the high MCS values while (true) { - // The purpose of the internal loop is to decrease the MCS if the effective coderate is too high. This loop - // only affects the high MCS values - while (true) { - // Generate PDSCH - bool success = ue->phy().get_pdsch_cfg(slot_cfg, pdcch.dci, pdsch.sch); - srsran_assert(success, "Error converting DCI to grant"); - if (ue.h_dl->nof_retx() != 0) { - srsran_assert(pdsch.sch.grant.tb[0].tbs == (int)ue.h_dl->tbs(), "The TBS did not remain constant in retx"); - } - R_prime = pdsch.sch.grant.tb[0].R_prime; - if (ue.h_dl->nof_retx() > 0 or R_prime < max_R or mcs <= 0) { - break; - } - // Decrease MCS if first tx and rate is too high - mcs--; - pdcch.dci.mcs = mcs; - } - if (R_prime >= max_R and mcs == 0) { - logger.warning("Couldn't find mcs that leads to R<0.95"); + // Generate PDSCH + bool success = ue->phy().get_pdsch_cfg(slot_cfg, pdcch.dci, pdsch.sch); + srsran_assert(success, "Error converting DCI to grant"); + if (ue.h_dl->nof_retx() != 0) { + srsran_assert(pdsch.sch.grant.tb[0].tbs == (int)ue.h_dl->tbs(), "The TBS did not remain constant in retx"); } - ue.h_dl->set_mcs(mcs); - ue.h_dl->set_tbs(pdsch.sch.grant.tb[0].tbs); // set HARQ TBS - pdsch.sch.grant.tb[0].softbuffer.tx = ue.h_dl->get_softbuffer().get(); - pdsch.data[0] = ue.h_dl->get_tx_pdu()->get(); - - // Select scheduled LCIDs and update UE buffer state - bwp_pdsch_slot.dl.data.emplace_back(); - // NOTES: 1) ue.h_dl->tbs() has to be converted from bits to bytes - // 2) In case of CCCH segmentation, we'll need to repeat the scheduling with a higher MCS. Hence, the - // function ue.build_pdu() will reset the LCIDs and UE buffer states as before its execution if the flag - // "mcstbs() / 8, bwp_pdsch_slot.dl.data.back(), mcs < min_MCS_ccch); - if (segmented_ccch_pdu and mcs < min_MCS_ccch) { - // In case of segmented PDU for CCCH, set minimum MCS to 4 and re-run the outer while loop - bwp_pdsch_slot.dl.data.pop_back(); - mcs = min_MCS_ccch; - pdcch.dci.mcs = mcs; - logger.info("SCHED: MCS increased to min value %d to allocate SRB0/CCCH for rnti=0x%x", min_MCS_ccch, ue->rnti); - } else if (segmented_ccch_pdu /* and mcs >= min_MCS_ccch */) { - // With MCS >= then min_MCS_ccch, it is not possible to allocate SRB0/CCCH without PDU segmentation - logger.error("SCHED: Insufficient resources to allocate SRB0/CCCH without PDU segmentation for rnti=0x%x", - ue->rnti); - break; - } else { + R_prime = pdsch.sch.grant.tb[0].R_prime; + if (ue.h_dl->nof_retx() > 0 or R_prime < max_R or mcs <= 0 or + (ue.get_pending_bytes(srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::CCCH) and mcs <= min_MCS_ccch)) { break; } + // Decrease MCS if first tx and rate is too high + mcs--; + pdcch.dci.mcs = mcs; + } + if (R_prime >= max_R and mcs == 0) { + logger.warning("Couldn't find mcs that leads to R<0.95"); + } + + ue.h_dl->set_mcs(mcs); + ue.h_dl->set_tbs(pdsch.sch.grant.tb[0].tbs); // set HARQ TBS + pdsch.sch.grant.tb[0].softbuffer.tx = ue.h_dl->get_softbuffer().get(); + pdsch.data[0] = ue.h_dl->get_tx_pdu()->get(); + + // Select scheduled LCIDs and update UE buffer state + bwp_pdsch_slot.dl.data.emplace_back(); + // NOTE: ue.h_dl->tbs() has to be converted from bits to bytes + bool segmented_ccch_pdu = not ue.build_pdu(ue.h_dl->tbs() / 8, bwp_pdsch_slot.dl.data.back()); + if (segmented_ccch_pdu) { + logger.error("SCHED: Insufficient resources to allocate SRB0/CCCH for rnti=0x%x", min_MCS_ccch, ue->rnti); } // Generate PUCCH diff --git a/srsgnb/src/stack/mac/sched_nr_ue.cc b/srsgnb/src/stack/mac/sched_nr_ue.cc index 549da55d6..ef205bb55 100644 --- a/srsgnb/src/stack/mac/sched_nr_ue.cc +++ b/srsgnb/src/stack/mac/sched_nr_ue.cc @@ -36,15 +36,8 @@ int ue_buffer_manager::get_dl_tx_total() const LCIDs as before running this function * @return true if there is no SRB0/CCCH MAC PDU segmentation, false otherwise */ -bool ue_buffer_manager::pdu_builder::alloc_subpdus(uint32_t rem_bytes, - sched_nr_interface::dl_pdu_t& pdu, - bool reset_buf_states) +bool ue_buffer_manager::pdu_builder::alloc_subpdus(uint32_t rem_bytes, sched_nr_interface::dl_pdu_t& pdu) { - // In case of SRB0/CCCH PDUs, we need to check whether there is PDU segmentation; if LCID = 0 has emtpy buffer, no - // need to perform this check - bool check_ccch_pdu_segmentation = - parent->get_dl_tx_total(srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::CCCH) > 0 ? true : false; - // First step: allocate MAC CEs until resources allow srsran::deque restore_ces; for (ce_t ce : parent->pending_ces) { @@ -56,10 +49,6 @@ bool ue_buffer_manager::pdu_builder::alloc_subpdus(uint32_t } rem_bytes -= size_ce; pdu.subpdus.push_back(ce.lcid); - // If there is possibility of CCCH segmentation, we need to save the MAC CEs in a tmp queue to be later restored - if (check_ccch_pdu_segmentation and reset_buf_states) { - restore_ces.push_back(parent->pending_ces.front()); - } parent->pending_ces.pop_front(); } } @@ -67,19 +56,9 @@ bool ue_buffer_manager::pdu_builder::alloc_subpdus(uint32_t // Second step: allocate the remaining LCIDs (LCIDs for MAC CEs are addressed above) for (uint32_t lcid = 0; rem_bytes > 0 and is_lcid_valid(lcid); ++lcid) { uint32_t pending_lcid_bytes = parent->get_dl_tx_total(lcid); - // Verify if the TBS is big enough to store the entire CCCH buffer - // Note: (pending_lcid_bytes > rem_bytes) implies (check_ccch_pdu_segmentation == true) + // Return false if the TBS is too small to store the entire CCCH buffer without segmentation if (lcid == srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::CCCH and pending_lcid_bytes > rem_bytes) { - if (reset_buf_states) { - // restore the MAC CEs as they were at the beginning of the function - for (ce_t ce : restore_ces) { - parent->pending_ces.push_back(ce); - } - // double check if this line is required - pdu.subpdus.clear(); - } else { - pdu.subpdus.push_back(lcid); - } + pdu.subpdus.push_back(lcid); return false; } if (pending_lcid_bytes > 0) { From 2dca581741b6357fffbf87610c17a4985cc32ff1 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Mon, 21 Feb 2022 12:38:27 +0100 Subject: [PATCH 012/195] srsran_rf: only build each plugin when found AND enabled Since [PLUGINNAME]_FOUND are cached variables, some plugins were always built if they were found once, regardless if the current value of ENABLE_[PLUGINNAME] --- lib/src/phy/rf/CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/src/phy/rf/CMakeLists.txt b/lib/src/phy/rf/CMakeLists.txt index 3581d5ab0..7374ba209 100644 --- a/lib/src/phy/rf/CMakeLists.txt +++ b/lib/src/phy/rf/CMakeLists.txt @@ -24,7 +24,7 @@ if(RF_FOUND) endif (ENABLE_RF_PLUGINS) # RF plugins - if (UHD_FOUND) + if (UHD_FOUND AND ENABLE_UHD) add_definitions(-DENABLE_UHD) set(SOURCES_UHD rf_uhd_imp.cc) if (ENABLE_RF_PLUGINS) @@ -50,7 +50,7 @@ if(RF_FOUND) endif(UHD_ENABLE_CUSTOM_RFNOC) endif (UHD_FOUND) - if (BLADERF_FOUND) + if (BLADERF_FOUND AND ENABLE_BLADERF) add_definitions(-DENABLE_BLADERF) set(SOURCES_BLADE rf_blade_imp.c) if (ENABLE_RF_PLUGINS) @@ -80,7 +80,7 @@ if(RF_FOUND) install(TARGETS srsran_rf_soapy DESTINATION ${LIBRARY_DIR}) endif (SOAPYSDR_FOUND AND ENABLE_SOAPYSDR) - if(SKIQ_FOUND) + if(SKIQ_FOUND AND ENABLE_SKIQ) add_executable(skiq_pps_test skiq_pps_test.c) target_link_libraries(skiq_pps_test ${SKIQ_LIBRARIES} rt pthread m) add_definitions(-DENABLE_SIDEKIQ) @@ -97,7 +97,7 @@ if(RF_FOUND) install(TARGETS srsran_rf_skiq DESTINATION ${LIBRARY_DIR}) endif(SKIQ_FOUND) - if (ZEROMQ_FOUND) + if (ZEROMQ_FOUND AND ENABLE_ZEROMQ) add_definitions(-DENABLE_ZEROMQ) set(SOURCES_ZMQ rf_zmq_imp.c rf_zmq_imp_tx.c rf_zmq_imp_rx.c) if (ENABLE_RF_PLUGINS) From e086479a7b6962f152d383c4e73ea6312d8aa887 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Mon, 21 Feb 2022 12:38:56 +0100 Subject: [PATCH 013/195] srsran_rf: fix build without any active RF plugin --- lib/src/phy/rf/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/src/phy/rf/CMakeLists.txt b/lib/src/phy/rf/CMakeLists.txt index 7374ba209..3abc51de0 100644 --- a/lib/src/phy/rf/CMakeLists.txt +++ b/lib/src/phy/rf/CMakeLists.txt @@ -119,7 +119,9 @@ if(RF_FOUND) add_library(srsran_rf_object OBJECT ${SOURCES_RF}) set_property(TARGET srsran_rf_object PROPERTY POSITION_INDEPENDENT_CODE 1) if (ENABLE_RF_PLUGINS) - add_dependencies(srsran_rf_object ${DYNAMIC_PLUGINS}) + if (DYNAMIC_PLUGINS) + add_dependencies(srsran_rf_object ${DYNAMIC_PLUGINS}) + endif (DYNAMIC_PLUGINS) add_library(srsran_rf SHARED $) target_link_libraries(srsran_rf dl) # Add $ORIGIN (i.e. current location of this library) to rpath of srsran_rf. From d2d76b7d972c7f5b2efbaac059108e8572e81952 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Mon, 21 Feb 2022 12:01:37 +0100 Subject: [PATCH 014/195] srsran_rf, ASAN: Enforce RPATH instead of RUNPATH to find RF plugins in build tree when built with ASAN. --- CMakeLists.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7764f3c50..2262fa370 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -498,8 +498,12 @@ if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") endif () if (ENABLE_ASAN) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") + # Note: When using ASAN, we need to ensure the use of RPATH instead of RUNPATH via "-Wl,--disable-new-dtags" + # While RPATH is default, some systems (e.g. Ubuntu 18.04 and 20.04) use RUNPATH by default, which is non-transitive. + # Since ASAN intercepts dlopen(), by which it replaces the dynamic string token "$ORIGIN" to its own location, + # the RF plugins won't be found by "libsrsran_rf.so" when using ASAN + RUNPATH in the top-level executable. + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer -Wl,--disable-new-dtags") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -Wl,--disable-new-dtags") endif (ENABLE_ASAN) if (ENABLE_TSAN) From b399724717ab5307e90fd2e71022b221bacdf2d3 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Fri, 18 Feb 2022 11:03:54 +0000 Subject: [PATCH 015/195] lib,rlc_am_nr: fix wrong if checking if the grant was too small to generate a segment --- lib/src/rlc/rlc_am_nr.cc | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 8b06b7dee..efd888931 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -229,8 +229,10 @@ uint32_t rlc_am_nr_tx::build_new_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, uint8_t* // Sanity check: can this SDU be sent considering header overhead? if (nof_bytes <= min_hdr_size) { // Small header as SO is not present - RlcError("cannot build new sdu_segment, there are not enough bytes allocated to tx header plus data. nof_bytes=%d", - nof_bytes); + RlcError("cannot build new sdu_segment, there are not enough bytes allocated to tx header plus data. nof_bytes=%d, " + "min_hdr_size=%d", + nof_bytes, + min_hdr_size); return 0; } @@ -296,9 +298,11 @@ uint32_t rlc_am_nr_tx::build_continuation_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, } // Sanity check: can this SDU be sent considering header overhead? - if ((max_hdr_size + 1) < nof_bytes) { // Larger header size, as SO is present - RlcError("cannot build new sdu_segment, there are not enough bytes allocated to tx header plus data. nof_bytes=%d", - nof_bytes); + if (nof_bytes <= max_hdr_size) { // Larger header size, as SO is present + RlcError("cannot build new sdu_segment, there are not enough bytes allocated to tx header plus data. nof_bytes=%d, " + "max_header_size=%d", + nof_bytes, + max_hdr_size); return 0; } From ddbd857867dd63e85d37d5d9af3189c61a8e2d2f Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Fri, 18 Feb 2022 11:14:31 +0000 Subject: [PATCH 016/195] lib,rlc_am_nr: make sure that the RB name is logged when logging the header and status PDUs in NR --- lib/include/srsran/rlc/rlc_am_nr_packing.h | 8 ++++++-- lib/src/rlc/rlc_am_nr.cc | 18 +++++++++--------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_am_nr_packing.h b/lib/include/srsran/rlc/rlc_am_nr_packing.h index adf6eb6ca..f52591632 100644 --- a/lib/include/srsran/rlc/rlc_am_nr_packing.h +++ b/lib/include/srsran/rlc/rlc_am_nr_packing.h @@ -122,6 +122,7 @@ template void log_rlc_am_nr_status_pdu_to_string(srslog::log_channel& log_ch, const char* fmt_str, rlc_am_nr_status_pdu_t* status, + const std::string& rb_name, Args&&... args) { if (not log_ch.enabled()) { @@ -146,14 +147,17 @@ void log_rlc_am_nr_status_pdu_to_string(srslog::log_channel& log_ch, /* * Log NR AMD PDUs */ -inline void log_rlc_am_nr_pdu_header_to_string(srslog::log_channel& log_ch, const rlc_am_nr_pdu_header_t& header) +inline void log_rlc_am_nr_pdu_header_to_string(srslog::log_channel& log_ch, + const rlc_am_nr_pdu_header_t& header, + const std::string& rb_name) { if (not log_ch.enabled()) { return; } fmt::memory_buffer buffer; fmt::format_to(buffer, - "[{}, P={}, SI={}, SN_SIZE={}, SN={}, SO={}", + "{}: [{}, P={}, SI={}, SN_SIZE={}, SN={}, SO={}", + rb_name, rlc_dc_field_text[header.dc], (header.p ? "1" : "0"), to_string_short(header.si), diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index efd888931..cd18a8250 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -187,7 +187,7 @@ uint32_t rlc_am_nr_tx::build_new_pdu(uint8_t* payload, uint32_t nof_bytes) hdr.sn_size = rlc_am_nr_sn_size_t::size12bits; hdr.sn = st.tx_next; tx_pdu.header = hdr; - log_rlc_am_nr_pdu_header_to_string(logger.info, hdr); + log_rlc_am_nr_pdu_header_to_string(logger.info, hdr, rb_name); // Write header uint32_t len = rlc_am_nr_write_data_pdu_header(hdr, tx_sdu.get()); @@ -245,7 +245,7 @@ uint32_t rlc_am_nr_tx::build_new_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, uint8_t* hdr.sn = st.tx_next; hdr.so = 0; tx_pdu.header = hdr; - log_rlc_am_nr_pdu_header_to_string(logger.info, hdr); + log_rlc_am_nr_pdu_header_to_string(logger.info, hdr, rb_name); // Write header uint32_t hdr_len = rlc_am_nr_write_data_pdu_header(hdr, payload); @@ -347,7 +347,7 @@ uint32_t rlc_am_nr_tx::build_continuation_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, hdr.sn = st.tx_next; hdr.so = last_byte; tx_pdu.header = hdr; - log_rlc_am_nr_pdu_header_to_string(logger.info, hdr); + log_rlc_am_nr_pdu_header_to_string(logger.info, hdr, rb_name); // Write header uint32_t hdr_len = rlc_am_nr_write_data_pdu_header(hdr, payload); @@ -520,7 +520,7 @@ uint32_t rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_t& retx, tx_window[retx.sn].retx_count + 1, cfg.max_retx_thresh); RlcHexInfo(payload, nof_bytes, "retx PDU SN=%d (%d B)", retx.sn, nof_bytes); - log_rlc_am_nr_pdu_header_to_string(logger.debug, new_header); + log_rlc_am_nr_pdu_header_to_string(logger.debug, new_header, rb_name); debug_state(); return pdu_bytes; @@ -589,11 +589,11 @@ uint32_t rlc_am_nr_tx::build_retx_pdu_with_segmentation(rlc_amd_retx_t& retx, ui hdr.si = si; uint32_t hdr_len = rlc_am_nr_write_data_pdu_header(hdr, payload); if (hdr_len >= nof_bytes || hdr_len != expected_hdr_len) { - log_rlc_am_nr_pdu_header_to_string(logger.error, hdr); + log_rlc_am_nr_pdu_header_to_string(logger.error, hdr, rb_name); RlcError("Error writing AMD PDU header. nof_bytes=%d, hdr_len=%d", nof_bytes, hdr_len); return 0; } - log_rlc_am_nr_pdu_header_to_string(logger.info, hdr); + log_rlc_am_nr_pdu_header_to_string(logger.info, hdr, rb_name); // Copy SDU segment into payload srsran_assert((hdr_len + retx_pdu_payload_size) <= nof_bytes, "Error calculating hdr_len and segment_payload_len"); @@ -670,7 +670,7 @@ uint32_t rlc_am_nr_tx::build_status_pdu(byte_buffer_t* payload, uint32_t nof_byt pdu_len = 0; } else if (pdu_len > 0 && nof_bytes >= static_cast(pdu_len)) { RlcDebug("generated status PDU. Bytes:%d", pdu_len); - log_rlc_am_nr_status_pdu_to_string(logger.info, "%s tx status PDU - %s", &tx_status, rb_name); + log_rlc_am_nr_status_pdu_to_string(logger.info, "tx status PDU - %s", &tx_status, rb_name); pdu_len = rlc_am_nr_write_status_pdu(tx_status, rlc_am_nr_sn_size_t::size12bits, payload); } else { RlcInfo("cannot tx status PDU - %d bytes available, %d bytes required", nof_bytes, pdu_len); @@ -689,7 +689,7 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) rlc_am_nr_status_pdu_t status = {}; RlcHexDebug(payload, nof_bytes, "%s Rx control PDU", parent->rb_name); rlc_am_nr_read_status_pdu(payload, nof_bytes, rlc_am_nr_sn_size_t::size12bits, &status); - log_rlc_am_nr_status_pdu_to_string(logger.info, "%s Rx Status PDU: %s", &status, parent->rb_name); + log_rlc_am_nr_status_pdu_to_string(logger.info, "Rx Status PDU: %s", &status, parent->rb_name); // Local variables for handling Status PDU will be updated with lock /* * - if the SN of the corresponding RLC SDU falls within the range @@ -934,7 +934,7 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) uint32_t hdr_len = rlc_am_nr_read_data_pdu_header(payload, nof_bytes, rlc_am_nr_sn_size_t::size12bits, &header); RlcHexInfo(payload, nof_bytes, "Rx data PDU SN=%d (%d B)", header.sn, nof_bytes); - log_rlc_am_nr_pdu_header_to_string(logger.debug, header); + log_rlc_am_nr_pdu_header_to_string(logger.debug, header, rb_name); // Check whether SDU is within Rx Window if (!inside_rx_window(header.sn)) { From e6d976d1151408052484423c0d93143b815f55da Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Mon, 21 Feb 2022 18:09:20 +0000 Subject: [PATCH 017/195] lib,rlc_am_nr: fixed stack smashing bug that happened when too many NACKs were present in the status report --- lib/include/srsran/rlc/rlc_am_nr_packing.h | 6 ++++-- lib/src/rlc/rlc_am_nr.cc | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_am_nr_packing.h b/lib/include/srsran/rlc/rlc_am_nr_packing.h index f52591632..1297880ce 100644 --- a/lib/include/srsran/rlc/rlc_am_nr_packing.h +++ b/lib/include/srsran/rlc/rlc_am_nr_packing.h @@ -19,7 +19,9 @@ namespace srsran { -const uint32_t INVALID_RLC_SN = 0xFFFFFFFF; +const uint32_t RLC_AM_NR_WINDOW_SIZE_12BIT = 4096; +const uint32_t RLC_AM_NR_WINDOW_SIZE_18BIT = 262144; +const uint32_t INVALID_RLC_SN = 0xFFFFFFFF; ///< AM NR PDU header struct rlc_am_nr_pdu_header_t { @@ -82,7 +84,7 @@ typedef struct { uint32_t ack_sn; ///< SN of the next not received RLC Data PDU uint16_t N_nack; ///< number of NACKs uint8_t nack_range; ///< number of consecutively lost RLC SDUs starting from and including NACK_SN - rlc_status_nack_t nacks[RLC_AM_WINDOW_SIZE]; + rlc_status_nack_t nacks[RLC_AM_NR_WINDOW_SIZE_12BIT]; } rlc_am_nr_status_pdu_t; /**************************************************************************** diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index cd18a8250..bcc81c6b7 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -1149,7 +1149,7 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m { std::unique_lock lock(mutex, std::try_to_lock); if (not lock.owns_lock()) { - return SRSRAN_ERROR; + return 0; } status->N_nack = 0; From 9b44d13471cb138c8cabf979766ce95298a37bbf Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Fri, 18 Feb 2022 16:15:40 +0100 Subject: [PATCH 018/195] rlc, nr: notify PDCP about fully acked SDUs --- lib/include/srsran/rlc/rlc_am_nr.h | 4 +++- lib/src/rlc/rlc_am_nr.cc | 9 ++++++++- lib/test/rlc/rlc_am_nr_test.cc | 8 ++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_am_nr.h b/lib/include/srsran/rlc/rlc_am_nr.h index fc9172910..e0d44bd55 100644 --- a/lib/include/srsran/rlc/rlc_am_nr.h +++ b/lib/include/srsran/rlc/rlc_am_nr.h @@ -16,6 +16,7 @@ #include "srsran/common/buffer_pool.h" #include "srsran/common/common.h" #include "srsran/common/timers.h" +#include "srsran/interfaces/pdcp_interface_types.h" #include "srsran/rlc/rlc_am_base.h" #include "srsran/rlc/rlc_am_data_structs.h" #include "srsran/rlc/rlc_am_nr_packing.h" @@ -72,7 +73,7 @@ struct rlc_am_nr_tx_state_t { struct rlc_amd_tx_pdu_nr { const uint32_t rlc_sn = INVALID_RLC_SN; - const uint32_t pdcp_sn = INVALID_RLC_SN; + uint32_t pdcp_sn = INVALID_RLC_SN; rlc_am_nr_pdu_header_t header = {}; unique_byte_buffer_t sdu_buf = nullptr; uint32_t retx_count = 0; @@ -152,6 +153,7 @@ private: // Queues and buffers pdu_retx_queue retx_queue; uint32_t sdu_under_segmentation_sn = INVALID_RLC_SN; // SN of the SDU currently being segmented. + pdcp_sn_vector_t notify_info_vec; // Helper constants uint32_t min_hdr_size = 2; diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index bcc81c6b7..13e25ef3a 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -163,6 +163,7 @@ uint32_t rlc_am_nr_tx::build_new_pdu(uint8_t* payload, uint32_t nof_bytes) // insert newly assigned SN into window and use reference for in-place operations // NOTE: from now on, we can't return from this function anymore before increasing tx_next rlc_amd_tx_pdu_nr& tx_pdu = tx_window.add_pdu(st.tx_next); + tx_pdu.pdcp_sn = tx_sdu->md.pdcp_sn; tx_pdu.sdu_buf = srsran::make_byte_buffer(); if (tx_pdu.sdu_buf == nullptr) { RlcError("Couldn't allocate PDU in %s().", __FUNCTION__); @@ -707,15 +708,21 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) } for (uint32_t sn = st.tx_next_ack; sn < stop_sn; sn++) { if (tx_window.has_sn(sn)) { + notify_info_vec.push_back(tx_window[sn].pdcp_sn); tx_window.remove_pdu(sn); st.tx_next_ack = sn + 1; - // TODO notify PDCP } else { RlcError("Missing ACKed SN from TX window"); break; } } + // Notify PDCP + if (not notify_info_vec.empty()) { + parent->pdcp->notify_delivery(parent->lcid, notify_info_vec); + } + notify_info_vec.clear(); + // Process N_acks for (uint32_t nack_idx = 0; nack_idx < status.N_nack; nack_idx++) { if (st.tx_next_ack <= status.nacks[nack_idx].nack_sn && status.nacks[nack_idx].nack_sn <= st.tx_next) { diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index fb08b94b2..b8a8d0bc8 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -258,6 +258,14 @@ int basic_test() TESTASSERT_EQ(5, st.tx_next_ack); TESTASSERT_EQ(0, tx1->get_tx_window_size()); + // Check PDCP notifications + TESTASSERT_EQ(5, tester.notified_counts.size()); + for (uint16_t i = 0; i < tester.sdus.size(); i++) { + TESTASSERT_EQ(1, tester.sdus[i]->N_bytes); + TESTASSERT_EQ(i, *(tester.sdus[i]->msg)); + TESTASSERT_EQ(1, tester.notified_counts[i]); + } + // Check statistics rlc_bearer_metrics_t metrics1 = rlc1.get_metrics(); rlc_bearer_metrics_t metrics2 = rlc2.get_metrics(); From 95ebc06ec1418ce136390aedb2364e3634e0a396 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Tue, 22 Feb 2022 07:16:37 +0100 Subject: [PATCH 019/195] rlc: implement SDU discard for AM+UM in NR+LTE - Implemented in common base classes - Added locking --- lib/include/srsran/rlc/rlc_am_base.h | 2 +- lib/include/srsran/rlc/rlc_am_lte.h | 1 - lib/include/srsran/rlc/rlc_am_nr.h | 1 - lib/include/srsran/rlc/rlc_um_lte.h | 1 + lib/include/srsran/rlc/rlc_um_nr.h | 1 + lib/src/rlc/rlc_am_base.cc | 19 +++++++++++++++++++ lib/src/rlc/rlc_am_lte.cc | 18 ------------------ lib/src/rlc/rlc_am_nr.cc | 2 -- lib/src/rlc/rlc_um_base.cc | 13 ++++++++++++- 9 files changed, 34 insertions(+), 24 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_am_base.h b/lib/include/srsran/rlc/rlc_am_base.h index 27f1f3555..c5f8786f6 100644 --- a/lib/include/srsran/rlc/rlc_am_base.h +++ b/lib/include/srsran/rlc/rlc_am_base.h @@ -134,7 +134,6 @@ public: virtual void get_buffer_state(uint32_t& tx_queue, uint32_t& prio_tx_queue) = 0; virtual void reestablish() = 0; virtual void empty_queue() = 0; - virtual void discard_sdu(uint32_t pdcp_sn) = 0; virtual bool sdu_queue_is_full() = 0; virtual bool has_data() = 0; virtual void stop() = 0; @@ -142,6 +141,7 @@ public: void set_bsr_callback(bsr_callback_t callback); int write_sdu(unique_byte_buffer_t sdu); + virtual void discard_sdu(uint32_t pdcp_sn); virtual uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes) = 0; bool tx_enabled = false; diff --git a/lib/include/srsran/rlc/rlc_am_lte.h b/lib/include/srsran/rlc/rlc_am_lte.h index 4a1cf9c4b..90031dae8 100644 --- a/lib/include/srsran/rlc/rlc_am_lte.h +++ b/lib/include/srsran/rlc/rlc_am_lte.h @@ -59,7 +59,6 @@ public: void stop(); uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes); - void discard_sdu(uint32_t discard_sn); bool sdu_queue_is_full(); bool has_data(); diff --git a/lib/include/srsran/rlc/rlc_am_nr.h b/lib/include/srsran/rlc/rlc_am_nr.h index e0d44bd55..b7ec41552 100644 --- a/lib/include/srsran/rlc/rlc_am_nr.h +++ b/lib/include/srsran/rlc/rlc_am_nr.h @@ -97,7 +97,6 @@ public: uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes) final; void handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) final; - void discard_sdu(uint32_t discard_sn) final; bool sdu_queue_is_full() final; void reestablish() final; diff --git a/lib/include/srsran/rlc/rlc_um_lte.h b/lib/include/srsran/rlc/rlc_um_lte.h index 8da49c646..48bf889ca 100644 --- a/lib/include/srsran/rlc/rlc_um_lte.h +++ b/lib/include/srsran/rlc/rlc_um_lte.h @@ -49,6 +49,7 @@ private: bool configure(const rlc_config_t& cfg, std::string rb_name); uint32_t build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes); + void discard_sdu(uint32_t discard_sn); uint32_t get_buffer_state(); bool sdu_queue_is_full(); diff --git a/lib/include/srsran/rlc/rlc_um_nr.h b/lib/include/srsran/rlc/rlc_um_nr.h index 7e5b860ba..f74d59964 100644 --- a/lib/include/srsran/rlc/rlc_um_nr.h +++ b/lib/include/srsran/rlc/rlc_um_nr.h @@ -53,6 +53,7 @@ private: bool configure(const rlc_config_t& cfg, std::string rb_name); uint32_t build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes); + void discard_sdu(uint32_t discard_sn); uint32_t get_buffer_state(); private: diff --git a/lib/src/rlc/rlc_am_base.cc b/lib/src/rlc/rlc_am_base.cc index c2f7978b0..605b1f494 100644 --- a/lib/src/rlc/rlc_am_base.cc +++ b/lib/src/rlc/rlc_am_base.cc @@ -235,6 +235,25 @@ int rlc_am::rlc_am_base_tx::write_sdu(unique_byte_buffer_t sdu) return SRSRAN_SUCCESS; } +void rlc_am::rlc_am_base_tx::discard_sdu(uint32_t discard_sn) +{ + std::lock_guard lock(mutex); + + if (!tx_enabled) { + return; + } + bool discarded = tx_sdu_queue.apply_first([&discard_sn, this](unique_byte_buffer_t& sdu) { + if (sdu != nullptr && sdu->md.pdcp_sn == discard_sn) { + tx_sdu_queue.queue.pop_func(sdu); + sdu = nullptr; + } + return false; + }); + + // Discard fails when the PDCP PDU is already in Tx window. + RlcInfo("%s PDU with PDCP_SN=%d", discarded ? "Discarding" : "Couldn't discard", discard_sn); +} + void rlc_am::rlc_am_base_tx::set_bsr_callback(bsr_callback_t callback) { bsr_callback = callback; diff --git a/lib/src/rlc/rlc_am_lte.cc b/lib/src/rlc/rlc_am_lte.cc index 1b3218c84..097acc769 100644 --- a/lib/src/rlc/rlc_am_lte.cc +++ b/lib/src/rlc/rlc_am_lte.cc @@ -268,24 +268,6 @@ void rlc_am_lte_tx::get_buffer_state_nolock(uint32_t& n_bytes_newtx, uint32_t& n } } -void rlc_am_lte_tx::discard_sdu(uint32_t discard_sn) -{ - if (!tx_enabled) { - return; - } - - bool discarded = tx_sdu_queue.apply_first([&discard_sn, this](unique_byte_buffer_t& sdu) { - if (sdu != nullptr && sdu->md.pdcp_sn == discard_sn) { - tx_sdu_queue.queue.pop_func(sdu); - sdu = nullptr; - } - return false; - }); - - // Discard fails when the PDCP PDU is already in Tx window. - RlcInfo("%s PDU with PDCP_SN=%d", discarded ? "Discarding" : "Couldn't discard", discard_sn); -} - bool rlc_am_lte_tx::sdu_queue_is_full() { return tx_sdu_queue.is_full(); diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 13e25ef3a..bdb1c2792 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -856,8 +856,6 @@ void rlc_am_nr_tx::reestablish() stop(); } -void rlc_am_nr_tx::discard_sdu(uint32_t discard_sn) {} - bool rlc_am_nr_tx::sdu_queue_is_full() { return false; diff --git a/lib/src/rlc/rlc_um_base.cc b/lib/src/rlc/rlc_um_base.cc index b39e7d34c..0c8604004 100644 --- a/lib/src/rlc/rlc_um_base.cc +++ b/lib/src/rlc/rlc_um_base.cc @@ -298,7 +298,18 @@ int rlc_um_base::rlc_um_base_tx::try_write_sdu(unique_byte_buffer_t sdu) void rlc_um_base::rlc_um_base_tx::discard_sdu(uint32_t discard_sn) { - RlcWarning("RLC UM: Discard SDU not implemented yet."); + std::lock_guard lock(mutex); + + bool discarded = tx_sdu_queue.apply_first([&discard_sn, this](unique_byte_buffer_t& sdu) { + if (sdu != nullptr && sdu->md.pdcp_sn == discard_sn) { + tx_sdu_queue.queue.pop_func(sdu); + sdu = nullptr; + } + return false; + }); + + // Discard fails when the PDCP PDU is already in Tx window. + RlcInfo("%s PDU with PDCP_SN=%d", discarded ? "Discarding" : "Couldn't discard", discard_sn); } bool rlc_um_base::rlc_um_base_tx::sdu_queue_is_full() From 42a8e957d464b57766f0d0991c09f49c2b1369ba Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Tue, 22 Feb 2022 14:27:48 +0100 Subject: [PATCH 020/195] rlc, nr: fix rlc_am_nr_tx::has_data() --- lib/src/rlc/rlc_am_nr.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index bdb1c2792..ce13ead86 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -66,7 +66,7 @@ bool rlc_am_nr_tx::configure(const rlc_config_t& cfg_) bool rlc_am_nr_tx::has_data() { return do_status() || // if we have a status PDU to transmit - tx_sdu_queue.get_n_sdus() != 1; // or if there is a SDU queued up for transmission + tx_sdu_queue.get_n_sdus() != 0; // or if there is a SDU queued up for transmission } /** From 8f6e3996d3c6f54aaadf1c25b5e9c581e005ab49 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Tue, 22 Feb 2022 14:28:36 +0100 Subject: [PATCH 021/195] rlc, nr: add discard_test for AM --- lib/test/rlc/rlc_am_nr_test.cc | 103 +++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index b8a8d0bc8..99b203f83 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -883,6 +883,108 @@ int retx_segment_test() return SRSRAN_SUCCESS; } +// This test checks the correct functioning of RLC discard functionality +int discard_test() +{ + rlc_am_tester tester; + timer_handler timers(8); + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + + srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); + srslog::fetch_basic_logger("RLC_AM_2").set_hex_dump_max_size(100); + srslog::fetch_basic_logger("RLC").set_hex_dump_max_size(100); + + if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config())) { + return SRSRAN_ERROR; + } + + if (not rlc2.configure(rlc_config_t::default_rlc_am_nr_config())) { + return SRSRAN_ERROR; + } + + // Test discarding the single SDU from the queue + { + uint32_t num_tx_sdus = 1; + for (uint32_t i = 0; i < num_tx_sdus; ++i) { + // Write SDU + unique_byte_buffer_t sdu = srsran::make_byte_buffer(); + TESTASSERT(sdu != nullptr); + sdu->N_bytes = 5; + for (uint32_t k = 0; k < sdu->N_bytes; ++k) { + sdu->msg[k] = i; // Write the index into the buffer + } + sdu->md.pdcp_sn = i; + rlc1.write_sdu(std::move(sdu)); + } + } + rlc1.discard_sdu(0); // Try to discard PDCP_SN=0 + TESTASSERT(rlc1.has_data() == false); + + // Test discarding two SDUs in the middle (SN=3) and end (SN=9) of the queue and read PDUs after + { + uint32_t num_tx_sdus = 10; + for (uint32_t i = 0; i < num_tx_sdus; ++i) { + // Write SDU + unique_byte_buffer_t sdu = srsran::make_byte_buffer(); + TESTASSERT(sdu != nullptr); + sdu->N_bytes = 7; + for (uint32_t k = 0; k < sdu->N_bytes; ++k) { + sdu->msg[k] = i; // Write the index into the buffer + } + sdu->md.pdcp_sn = i; + rlc1.write_sdu(std::move(sdu)); + } + } + TESTASSERT(rlc1.get_buffer_state() == 90); // 10 * (2B Header + 7B Payload) + rlc1.discard_sdu(3); // Try to discard PDCP_SN=3 + TESTASSERT(rlc1.has_data() == true); + TESTASSERT(rlc1.get_buffer_state() == 81); + rlc1.discard_sdu(9); // Try to discard PDCP_SN=9 + TESTASSERT(rlc1.has_data() == true); + TESTASSERT(rlc1.get_buffer_state() == 72); + + { + uint32_t num_tx_sdus = 8; + for (uint32_t i = 0; i < num_tx_sdus; ++i) { + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + uint32_t len = rlc1.read_pdu(pdu->msg, 50); // sufficient space to read without segmentation + pdu->N_bytes = len; + TESTASSERT((2 + 7) == len); + // Check that we don't have any SN gaps + rlc_am_nr_pdu_header_t header = {}; + rlc_am_nr_read_data_pdu_header(pdu.get(), rlc_am_nr_sn_size_t::size12bits, &header); + TESTASSERT(header.sn == i); + } + } + TESTASSERT(rlc1.has_data() == false); + + srslog::fetch_basic_logger("TEST").info("Received %zd SDUs", tester.sdus.size()); + + // Test discarding non-existing SDU from the queue + { + uint32_t num_tx_sdus = 3; + for (uint32_t i = 0; i < num_tx_sdus; ++i) { + // Write SDU + unique_byte_buffer_t sdu = srsran::make_byte_buffer(); + TESTASSERT(sdu != nullptr); + sdu->N_bytes = 7; + for (uint32_t k = 0; k < sdu->N_bytes; ++k) { + sdu->msg[k] = i; // Write the index into the buffer + } + sdu->md.pdcp_sn = i; + rlc1.write_sdu(std::move(sdu)); + } + } + TESTASSERT(rlc1.get_buffer_state() == 27); // 3 * (2B Header + 7B Payload) + rlc1.discard_sdu(8); // Try to discard PDCP_SN=8, which doesn't exist + TESTASSERT(rlc1.get_buffer_state() == 27); // 3 * (2B Header + 7B Payload) + + return SRSRAN_SUCCESS; +} + int main() { // Setup the log message spy to intercept error and warning log entries from RLC @@ -914,5 +1016,6 @@ int main() TESTASSERT(basic_segmentation_test() == SRSRAN_SUCCESS); TESTASSERT(segment_retx_test() == SRSRAN_SUCCESS); TESTASSERT(retx_segment_test() == SRSRAN_SUCCESS); + TESTASSERT(discard_test() == SRSRAN_SUCCESS); return SRSRAN_SUCCESS; } From c75b8d1708a2a96331ed338a8df2f6076bd093ca Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Mon, 21 Feb 2022 10:58:28 +0100 Subject: [PATCH 022/195] rlc, nr: increment sequence numbers using modulus 'mod_nr' --- lib/src/rlc/rlc_am_nr.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index ce13ead86..692a9fed7 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -197,7 +197,7 @@ uint32_t rlc_am_nr_tx::build_new_pdu(uint8_t* payload, uint32_t nof_bytes) } // Update TX Next - st.tx_next = (st.tx_next + 1) % MOD; + st.tx_next = (st.tx_next + 1) % mod_nr; memcpy(payload, tx_sdu->msg, tx_sdu->N_bytes); RlcDebug("wrote RLC PDU - %d bytes", tx_sdu->N_bytes); @@ -374,8 +374,8 @@ uint32_t rlc_am_nr_tx::build_continuation_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, RlcInfo("grant is large enough for full SDU." "Removing current SDU info"); sdu_under_segmentation_sn = INVALID_RLC_SN; - // SDU is fully TX'ed. Increment TX_NEXt - st.tx_next++; + // SDU is fully TX'ed. Increment TX_NEXT + st.tx_next = (st.tx_next + 1) % mod_nr; } return hdr_len + segment_payload_len; @@ -710,7 +710,7 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) if (tx_window.has_sn(sn)) { notify_info_vec.push_back(tx_window[sn].pdcp_sn); tx_window.remove_pdu(sn); - st.tx_next_ack = sn + 1; + st.tx_next_ack = (sn + 1) % mod_nr; } else { RlcError("Missing ACKed SN from TX window"); break; @@ -978,7 +978,7 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) // 5.2.3.2.3 Actions when an AMD PDU is placed in the reception buffer // Update Rx_Next_Highest if (rx_mod_base_nr(header.sn) >= rx_mod_base_nr(st.rx_next_highest)) { - st.rx_next_highest = (header.sn + 1) % MOD; + st.rx_next_highest = (header.sn + 1) % mod_nr; } // Update RX_Highest_Status @@ -1203,7 +1203,7 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m // make sure we don't exceed grant size (FIXME) rlc_am_nr_write_status_pdu(*status, rlc_am_nr_sn_size_t::size12bits, &tmp_buf); // TODO - i = (i + 1) % MOD; + i = (i + 1) % mod_nr; } if (max_len != UINT32_MAX) { From 35146bcb71c98bb207a7952a72e688c5bee32361 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Tue, 22 Feb 2022 16:46:39 +0000 Subject: [PATCH 023/195] lib,rlc_am_nr: fix shadowed variable in RLC RETXes --- lib/src/rlc/rlc_am_nr.cc | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 692a9fed7..9d9b65301 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -497,17 +497,15 @@ uint32_t rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_t& retx, uint32_t hdr_len = rlc_am_nr_write_data_pdu_header(new_header, payload); // Write payload into PDU - uint32_t pdu_bytes = 0; uint32_t retx_pdu_payload_size = 0; if (not retx.is_segment) { // RETX full SDU retx_pdu_payload_size = tx_window[retx.sn].sdu_buf->N_bytes; - pdu_bytes = hdr_len + tx_window[retx.sn].sdu_buf->N_bytes; } else { // RETX SDU segment - uint32_t retx_pdu_payload_size = (retx.so_end - retx.current_so); - pdu_bytes = hdr_len + retx_pdu_payload_size; + retx_pdu_payload_size = (retx.so_end - retx.current_so); } + uint32_t pdu_bytes = hdr_len + retx_pdu_payload_size; srsran_assert(pdu_bytes <= nof_bytes, "Error calculating hdr_len and pdu_payload_len"); memcpy(&payload[hdr_len], &tx_pdu.sdu_buf->msg[retx.current_so], retx_pdu_payload_size); @@ -520,7 +518,7 @@ uint32_t rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_t& retx, tx_window[retx.sn].sdu_buf->N_bytes, tx_window[retx.sn].retx_count + 1, cfg.max_retx_thresh); - RlcHexInfo(payload, nof_bytes, "retx PDU SN=%d (%d B)", retx.sn, nof_bytes); + RlcHexInfo(payload, pdu_bytes, "RETX PDU SN=%d (%d B)", retx.sn, pdu_bytes); log_rlc_am_nr_pdu_header_to_string(logger.debug, new_header, rb_name); debug_state(); From 431ccb2754bc88ce26dc068e337759b7f2563a11 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Fri, 18 Feb 2022 11:16:50 +0000 Subject: [PATCH 024/195] lib,rlc_am_nr: Cleaned up rlc_stress test. Split test into .cc and .h --- lib/test/rlc/rlc_stress_test.cc | 564 ++++++++++---------------------- lib/test/rlc/rlc_stress_test.h | 281 ++++++++++++++++ 2 files changed, 456 insertions(+), 389 deletions(-) create mode 100644 lib/test/rlc/rlc_stress_test.h diff --git a/lib/test/rlc/rlc_stress_test.cc b/lib/test/rlc/rlc_stress_test.cc index b8e7def3a..8e8c70c48 100644 --- a/lib/test/rlc/rlc_stress_test.cc +++ b/lib/test/rlc/rlc_stress_test.cc @@ -10,6 +10,7 @@ * */ +#include "rlc_stress_test.h" #include "srsran/common/block_queue.h" #include "srsran/common/crash_handler.h" #include "srsran/common/rlc_pcap.h" @@ -27,429 +28,213 @@ #define LOG_HEX_LIMIT (-1) -#define PCAP_CRNTI (0x1001) -#define PCAP_TTI (666) +#define MIN_SDU_SIZE (5) +#define MAX_SDU_SIZE (1500) #include "srsran/common/mac_pcap.h" #include "srsran/mac/mac_sch_pdu_nr.h" -static std::unique_ptr pcap_handle = nullptr; - -int write_pdu_to_pcap(const bool is_dl, const uint32_t lcid, const uint8_t* payload, const uint32_t len) -{ - if (pcap_handle) { - srsran::byte_buffer_t tx_buffer; - srsran::mac_sch_pdu_nr tx_pdu; - tx_pdu.init_tx(&tx_buffer, len + 10); - tx_pdu.add_sdu(lcid, payload, len); - tx_pdu.pack(); - if (is_dl) { - pcap_handle->write_dl_crnti_nr(tx_buffer.msg, tx_buffer.N_bytes, PCAP_CRNTI, true, PCAP_TTI); - } else { - pcap_handle->write_ul_crnti_nr(tx_buffer.msg, tx_buffer.N_bytes, PCAP_CRNTI, true, PCAP_TTI); - } - return SRSRAN_SUCCESS; - } - return SRSRAN_ERROR; -} - -using namespace std; -using namespace srsue; -using namespace srsran; namespace bpo = boost::program_options; #define MIN_SDU_SIZE (5) #define MAX_SDU_SIZE (1500) -typedef struct { - std::string rat; - std::string mode; - int32_t sdu_size; - uint32_t test_duration_sec; - float pdu_drop_rate; - float pdu_cut_rate; - float pdu_duplicate_rate; - uint32_t sdu_gen_delay_usec; - uint32_t pdu_tx_delay_usec; - uint32_t log_level; - bool single_tx; - bool write_pcap; - uint32_t avg_opp_size; - bool random_opp; - bool zero_seed; - uint32_t nof_pdu_tti; - uint32_t max_retx; -} stress_test_args_t; - -void parse_args(stress_test_args_t* args, int argc, char* argv[]) +/*********************** + * MAC tester class + ***********************/ +void mac_dummy::run_thread() { - // Command line only options - bpo::options_description general("General options"); - - general.add_options()("help,h", "Produce help message")("version,v", "Print version information and exit"); - - // clang-format off - - // Command line or config file options - bpo::options_description common("Configuration options"); - common.add_options() - ("rat", bpo::value(&args->rat)->default_value("LTE"), "The RLC version to use (LTE/NR)") - ("mode", bpo::value(&args->mode)->default_value("AM"), "Whether to test RLC acknowledged or unacknowledged mode (AM/UM for LTE) (UM6/UM12 for NR)") - ("duration", bpo::value(&args->test_duration_sec)->default_value(5), "Duration (sec)") - ("sdu_size", bpo::value(&args->sdu_size)->default_value(-1), "Size of SDUs (-1 means random)") - ("random_opp", bpo::value(&args->random_opp)->default_value(true), "Whether to generate random MAC opportunities") - ("avg_opp_size", bpo::value(&args->avg_opp_size)->default_value(1505), "Size of the MAC opportunity (if not random)") - ("sdu_gen_delay", bpo::value(&args->sdu_gen_delay_usec)->default_value(0), "SDU generation delay (usec)") - ("pdu_tx_delay", bpo::value(&args->pdu_tx_delay_usec)->default_value(0), "Delay in MAC for transfering PDU from tx'ing RLC to rx'ing RLC (usec)") - ("pdu_drop_rate", bpo::value(&args->pdu_drop_rate)->default_value(0.1), "Rate at which RLC PDUs are dropped") - ("pdu_cut_rate", bpo::value(&args->pdu_cut_rate)->default_value(0.0), "Rate at which RLC PDUs are chopped in length") - ("pdu_duplicate_rate", bpo::value(&args->pdu_duplicate_rate)->default_value(0.0), "Rate at which RLC PDUs are duplicated") - ("loglevel", bpo::value(&args->log_level)->default_value((int)srslog::basic_levels::debug), "Log level (1=Error,2=Warning,3=Info,4=Debug)") - ("singletx", bpo::value(&args->single_tx)->default_value(false), "If set to true, only one node is generating data") - ("pcap", bpo::value(&args->write_pcap)->default_value(false), "Whether to write all RLC PDU to PCAP file") - ("zeroseed", bpo::value(&args->zero_seed)->default_value(false), "Whether to initialize random seed to zero") - ("max_retx", bpo::value(&args->max_retx)->default_value(32), "Maximum number of RLC retransmission attempts") - ("nof_pdu_tti", bpo::value(&args->nof_pdu_tti)->default_value(1), "Number of PDUs processed in a TTI"); - // clang-format on - - // these options are allowed on the command line - bpo::options_description cmdline_options; - cmdline_options.add(common).add(general); - - // parse the command line and store result in vm - bpo::variables_map vm; - bpo::store(bpo::command_line_parser(argc, argv).options(cmdline_options).run(), vm); - bpo::notify(vm); - - // help option was given - print usage and exit - if (vm.count("help") > 0) { - cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << endl << endl; - cout << common << endl << general << endl; - exit(0); - } + srsran::move_task_t task; + while (run_enable) { + // Downlink direction first (RLC1->RLC2) + run_tti(rlc1, rlc2, true); - if (args->log_level > 4) { - args->log_level = 4; - printf("Set log level to %d (%s)\n", - args->log_level, - srslog::basic_level_to_string(static_cast(args->log_level))); - } + // UL direction (RLC2->RLC1) + run_tti(rlc2, rlc1, false); + + // step timer + timers->step_all(); - // convert mode to upper case - for (auto& c : args->mode) { - c = toupper(c); + if (pending_tasks.try_pop(&task)) { + task(); + } + } + if (pending_tasks.try_pop(&task)) { + task(); } } -class mac_dummy : public srsran::thread +void mac_dummy::run_tti(srsue::rlc_interface_mac* tx_rlc, srsue::rlc_interface_mac* rx_rlc, bool is_dl) { -public: - mac_dummy(rlc_interface_mac* rlc1_, - rlc_interface_mac* rlc2_, - stress_test_args_t args_, - uint32_t lcid_, - timer_handler* timers_, - rlc_pcap* pcap_, - uint32_t seed_) : - run_enable(true), - rlc1(rlc1_), - rlc2(rlc2_), - args(args_), - pcap(pcap_), - lcid(lcid_), - timers(timers_), - logger(srslog::fetch_basic_logger("MAC", false)), - thread("MAC_DUMMY"), - real_dist(0.0, 1.0), - mt19937(seed_) - { - logger.set_level(static_cast(args.log_level)); - logger.set_hex_dump_max_size(LOG_HEX_LIMIT); - } + std::vector pdu_list; - void stop() - { - run_enable = false; - wait_thread_finish(); - } + // Run Tx + run_tx_tti(tx_rlc, rx_rlc, pdu_list); - void enqueue_task(srsran::move_task_t task) { pending_tasks.push(std::move(task)); } - -private: - void run_tx_tti(rlc_interface_mac* tx_rlc, rlc_interface_mac* rx_rlc, std::vector& pdu_list) - { - // Generate A number of MAC PDUs - for (uint32_t i = 0; i < args.nof_pdu_tti; i++) { - // Create PDU unique buffer - unique_byte_buffer_t pdu = srsran::make_byte_buffer(); - if (!pdu) { - printf("Fatal Error: Could not allocate PDU in mac_reader::run_thread\n"); - exit(-1); - } + // Reverse PDUs + std::reverse(pdu_list.begin(), pdu_list.end()); - // Get MAC PDU size - float factor = 1.0f; - if (args.random_opp) { - factor = 0.5f + real_dist(mt19937); - } - int opp_size = static_cast(args.avg_opp_size * factor); - - // Request data to transmit - uint32_t buf_state = tx_rlc->get_buffer_state(lcid); - if (buf_state > 0) { - pdu->N_bytes = tx_rlc->read_pdu(lcid, pdu->msg, opp_size); + // Run Rx + run_rx_tti(tx_rlc, rx_rlc, is_dl, pdu_list); +} - // Push PDU in the list - pdu_list.push_back(std::move(pdu)); - } +void mac_dummy::run_tx_tti(srsue::rlc_interface_mac* tx_rlc, + srsue::rlc_interface_mac* rx_rlc, + std::vector& pdu_list) +{ + // Generate A number of MAC PDUs + for (uint32_t i = 0; i < args.nof_pdu_tti; i++) { + // Create PDU unique buffer + srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (!pdu) { + printf("Fatal Error: Could not allocate PDU in mac_reader::run_thread\n"); + exit(-1); } - } - void run_rx_tti(rlc_interface_mac* tx_rlc, - rlc_interface_mac* rx_rlc, - bool is_dl, - std::vector& pdu_list) - { - // Sleep if necessary - if (args.pdu_tx_delay_usec > 0) { - std::this_thread::sleep_for(std::chrono::microseconds(args.pdu_tx_delay_usec)); + // Get MAC PDU size + float factor = 1.0f; + if (args.random_opp) { + factor = 0.5f + real_dist(mt19937); } + int opp_size = static_cast(args.avg_opp_size * factor); - auto it = pdu_list.begin(); // PDU iterator - bool skip_action = false; // Avoid discarding a duplicated or duplicating a discarded - - while (it != pdu_list.end()) { - // Get PDU unique buffer - unique_byte_buffer_t& pdu = *it; - - // Drop - float rnd = real_dist(mt19937); - if (std::isnan(rnd) || (((rnd > args.pdu_drop_rate) || skip_action) && pdu->N_bytes > 0)) { - uint32_t pdu_len = pdu->N_bytes; - - // Cut - if ((real_dist(mt19937) < args.pdu_cut_rate)) { - int cut_pdu_len = static_cast(pdu_len * real_dist(mt19937)); - logger.info("Cutting MAC PDU len (%d B -> %d B)", pdu_len, cut_pdu_len); - pdu_len = cut_pdu_len; - } - - // Write PDU in RX - rx_rlc->write_pdu(lcid, pdu->msg, pdu_len); - - // Write PCAP - write_pdu_to_pcap(is_dl, 4, pdu->msg, pdu_len); // Only handles NR rat - if (is_dl) { - pcap->write_dl_ccch(pdu->msg, pdu_len); - } else { - pcap->write_ul_ccch(pdu->msg, pdu_len); - } - } else { - logger.info(pdu->msg, pdu->N_bytes, "Dropping RLC PDU (%d B)", pdu->N_bytes); - skip_action = true; // Avoid drop duplicating this PDU - } + // Request data to transmit + uint32_t buf_state = tx_rlc->get_buffer_state(lcid); + if (buf_state > 0) { + pdu->N_bytes = tx_rlc->read_pdu(lcid, pdu->msg, opp_size); - // Duplicate - if (real_dist(mt19937) > args.pdu_duplicate_rate || skip_action) { - it++; - skip_action = false; // Allow action on the next PDU - } else { - logger.info(pdu->msg, pdu->N_bytes, "Duplicating RLC PDU (%d B)", pdu->N_bytes); - skip_action = true; // Avoid drop of this PDU - } + // Push PDU in the list + pdu_list.push_back(std::move(pdu)); } } +} - void run_tti(rlc_interface_mac* tx_rlc, rlc_interface_mac* rx_rlc, bool is_dl) - { - std::vector pdu_list; - - // Run Tx - run_tx_tti(tx_rlc, rx_rlc, pdu_list); +void mac_dummy::run_rx_tti(srsue::rlc_interface_mac* tx_rlc, + srsue::rlc_interface_mac* rx_rlc, + bool is_dl, + std::vector& pdu_list) +{ + // Sleep if necessary + if (args.pdu_tx_delay_usec > 0) { + std::this_thread::sleep_for(std::chrono::microseconds(args.pdu_tx_delay_usec)); + } - // Reverse PDUs - std::reverse(pdu_list.begin(), pdu_list.end()); + auto it = pdu_list.begin(); // PDU iterator + bool skip_action = false; // Avoid discarding a duplicated or duplicating a discarded - // Run Rx - run_rx_tti(tx_rlc, rx_rlc, is_dl, pdu_list); - } + while (it != pdu_list.end()) { + // Get PDU unique buffer + srsran::unique_byte_buffer_t& pdu = *it; - void run_thread() override - { - srsran::move_task_t task; - while (run_enable) { - // Downlink direction first (RLC1->RLC2) - run_tti(rlc1, rlc2, true); + // Drop + float rnd = real_dist(mt19937); + if (std::isnan(rnd) || (((rnd > args.pdu_drop_rate) || skip_action) && pdu->N_bytes > 0)) { + uint32_t pdu_len = pdu->N_bytes; - // UL direction (RLC2->RLC1) - run_tti(rlc2, rlc1, false); + // Cut + if ((real_dist(mt19937) < args.pdu_cut_rate)) { + int cut_pdu_len = static_cast(pdu_len * real_dist(mt19937)); + logger.info("Cutting MAC PDU len (%d B -> %d B)", pdu_len, cut_pdu_len); + pdu_len = cut_pdu_len; + } - // step timer - timers->step_all(); + // Write PDU in RX + rx_rlc->write_pdu(lcid, pdu->msg, pdu_len); - if (pending_tasks.try_pop(&task)) { - task(); + // Write PCAP + write_pdu_to_pcap(is_dl, 4, pdu->msg, pdu_len); // Only handles NR rat + if (is_dl) { + pcap->write_dl_ccch(pdu->msg, pdu_len); + } else { + pcap->write_ul_ccch(pdu->msg, pdu_len); } + } else { + logger.info(pdu->msg, pdu->N_bytes, "Dropping RLC PDU (%d B)", pdu->N_bytes); + skip_action = true; // Avoid drop duplicating this PDU } - if (pending_tasks.try_pop(&task)) { - task(); + + // Duplicate + if (real_dist(mt19937) > args.pdu_duplicate_rate || skip_action) { + it++; + skip_action = false; // Allow action on the next PDU + } else { + logger.info(pdu->msg, pdu->N_bytes, "Duplicating RLC PDU (%d B)", pdu->N_bytes); + skip_action = true; // Avoid drop of this PDU } } +} - rlc_interface_mac* rlc1 = nullptr; - rlc_interface_mac* rlc2 = nullptr; - - std::atomic run_enable = {false}; - stress_test_args_t args = {}; - rlc_pcap* pcap = nullptr; - uint32_t lcid = 0; - srslog::basic_logger& logger; - srsran::timer_handler* timers = nullptr; - - srsran::block_queue pending_tasks; - - std::mt19937 mt19937; - std::uniform_real_distribution real_dist; -}; - -class rlc_tester : public pdcp_interface_rlc, public rrc_interface_rlc, public srsran::thread +/*********************** + * RLC tester class + ***********************/ +// PDCP interface +void rlc_tester::write_pdu(uint32_t rx_lcid, srsran::unique_byte_buffer_t sdu) { -public: - rlc_tester(rlc_interface_pdcp* rlc_pdcp_, - std::string name_, - stress_test_args_t args_, - uint32_t lcid_, - uint32_t seed_) : - logger(srslog::fetch_basic_logger(name_.c_str(), false)), - rlc_pdcp(rlc_pdcp_), - name(name_), - args(args_), - lcid(lcid_), - thread("RLC_TESTER"), - int_dist(MIN_SDU_SIZE, MAX_SDU_SIZE), - mt19937(seed_) - { - logger.set_level(srslog::basic_levels::error); - logger.set_hex_dump_max_size(LOG_HEX_LIMIT); + assert(rx_lcid == lcid); + if (args.mode != "AM") { + // Only AM will guarantee to deliver SDUs, take first byte as reference for other modes + next_expected_sdu = sdu->msg[0]; } - void stop() - { - run_enable = false; - wait_thread_finish(); + // check SDU content (consider faster alternative) + for (uint32_t i = 0; i < sdu->N_bytes; ++i) { + if (sdu->msg[i] != next_expected_sdu) { + logger.error(sdu->msg, + sdu->N_bytes, + "Received malformed SDU with size %d, expected data 0x%X", + sdu->N_bytes, + next_expected_sdu); + fprintf(stderr, "Received malformed SDU with size %d, expected data 0x%X\n", sdu->N_bytes, next_expected_sdu); + fprintf(stdout, "Received malformed SDU with size %d, expected data 0x%X\n", sdu->N_bytes, next_expected_sdu); + + std::this_thread::sleep_for(std::chrono::seconds(1)); // give some time to flush logs + exit(-1); + } } + next_expected_sdu += 1; + rx_pdus++; +} - // PDCP interface - void write_pdu(uint32_t rx_lcid, unique_byte_buffer_t sdu) - { - assert(rx_lcid == lcid); - if (args.mode != "AM") { - // Only AM will guarantee to deliver SDUs, take first byte as reference for other modes - next_expected_sdu = sdu->msg[0]; +void rlc_tester::run_thread() +{ + uint32_t pdcp_sn = 0; + uint32_t sdu_size = 0; + uint8_t payload = 0x0; // increment for each SDU + while (run_enable) { + // SDU queue is full, don't assign PDCP SN + if (rlc_pdcp->sdu_queue_is_full(lcid)) { + continue; } - // check SDU content (consider faster alternative) - for (uint32_t i = 0; i < sdu->N_bytes; ++i) { - if (sdu->msg[i] != next_expected_sdu) { - logger.error(sdu->msg, - sdu->N_bytes, - "Received malformed SDU with size %d, expected data 0x%X", - sdu->N_bytes, - next_expected_sdu); - fprintf(stderr, "Received malformed SDU with size %d\n", sdu->N_bytes); - fprintf(stdout, "Received malformed SDU with size %d\n", sdu->N_bytes); - std::this_thread::sleep_for(std::chrono::seconds(1)); // give some time to flush logs - exit(-1); - } + srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (pdu == nullptr) { + printf("Error: Could not allocate PDU in rlc_tester::run_thread\n\n\n"); + // backoff for a bit + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + continue; } - next_expected_sdu += 1; - rx_pdus++; - } - void write_pdu_bcch_bch(unique_byte_buffer_t sdu) {} - void write_pdu_bcch_dlsch(unique_byte_buffer_t sdu) {} - void write_pdu_pcch(unique_byte_buffer_t sdu) {} - void write_pdu_mch(uint32_t lcid_, srsran::unique_byte_buffer_t sdu) {} - void notify_delivery(uint32_t lcid_, const srsran::pdcp_sn_vector_t& pdcp_sns) {} - void notify_failure(uint32_t lcid_, const srsran::pdcp_sn_vector_t& pdcp_sns) {} - - // RRC interface - void max_retx_attempted() - { - logger.error( - "Maximum number of RLC retransmission reached. Consider increasing threshold or lowering channel drop rate."); - 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; } - -private: - const static size_t max_pdcp_sn = 262143u; // 18bit SN - void run_thread() - { - uint32_t pdcp_sn = 0; - uint32_t sdu_size = 0; - uint8_t payload = 0x0; // increment for each SDU - while (run_enable) { - // SDU queue is full, don't assign PDCP SN - if (rlc_pdcp->sdu_queue_is_full(lcid)) { - continue; - } + pdu->md.pdcp_sn = pdcp_sn; - unique_byte_buffer_t pdu = srsran::make_byte_buffer(); - if (pdu == NULL) { - printf("Error: Could not allocate PDU in rlc_tester::run_thread\n\n\n"); - // backoff for a bit - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - continue; - } - pdu->md.pdcp_sn = pdcp_sn; - - // random or fixed SDU size - if (args.sdu_size < 1) { - sdu_size = int_dist(mt19937); - } else { - sdu_size = args.sdu_size; - } + // random or fixed SDU size + if (args.sdu_size < 1) { + sdu_size = int_dist(mt19937); + } else { + sdu_size = args.sdu_size; + } - for (uint32_t i = 0; i < sdu_size; i++) { - pdu->msg[i] = payload; - } - pdu->N_bytes = sdu_size; - payload++; + for (uint32_t i = 0; i < sdu_size; i++) { + pdu->msg[i] = payload; + } + pdu->N_bytes = sdu_size; + payload++; - rlc_pdcp->write_sdu(lcid, std::move(pdu)); - pdcp_sn = (pdcp_sn + 1) % max_pdcp_sn; - if (args.sdu_gen_delay_usec > 0) { - std::this_thread::sleep_for(std::chrono::microseconds(args.sdu_gen_delay_usec)); - } + rlc_pdcp->write_sdu(lcid, std::move(pdu)); + pdcp_sn = (pdcp_sn + 1) % max_pdcp_sn; + if (args.sdu_gen_delay_usec > 0) { + std::this_thread::sleep_for(std::chrono::microseconds(args.sdu_gen_delay_usec)); } } - - std::atomic run_enable = {true}; - - /// Tx uses thread-local PDCP SN to set SDU content, the Rx uses this variable to check received SDUs - uint8_t next_expected_sdu = 0; - uint64_t rx_pdus = 0; - uint32_t lcid = 0; - srslog::basic_logger& logger; - - std::string name; - - stress_test_args_t args = {}; - - rlc_interface_pdcp* rlc_pdcp = nullptr; // used by run_thread to push PDCP SDUs to RLC - - std::mt19937 mt19937; - std::uniform_int_distribution<> int_dist; -}; +} void stress_test(stress_test_args_t args) { @@ -460,23 +245,23 @@ void stress_test(stress_test_args_t args) log2.set_level(static_cast(args.log_level)); log2.set_hex_dump_max_size(LOG_HEX_LIMIT); - rlc_pcap pcap; - uint32_t lcid = 1; + srsran::rlc_pcap pcap; + uint32_t lcid = 1; - rlc_config_t cnfg_ = {}; + srsran::rlc_config_t cnfg_ = {}; if (args.rat == "LTE") { if (args.mode == "AM") { // config RLC AM bearer - cnfg_ = rlc_config_t::default_rlc_am_config(); + cnfg_ = srsran::rlc_config_t::default_rlc_am_config(); cnfg_.am.max_retx_thresh = args.max_retx; } else if (args.mode == "UM") { // config UM bearer - cnfg_ = rlc_config_t::default_rlc_um_config(); + cnfg_ = srsran::rlc_config_t::default_rlc_um_config(); } else if (args.mode == "TM") { // use default LCID in TM lcid = 0; } else { - cout << "Unsupported RLC mode " << args.mode << ", exiting." << endl; + std::cout << "Unsupported RLC mode " << args.mode << ", exiting." << std::endl; exit(-1); } @@ -485,13 +270,13 @@ void stress_test(stress_test_args_t args) } } else if (args.rat == "NR") { if (args.mode == "UM6") { - cnfg_ = rlc_config_t::default_rlc_um_nr_config(6); + cnfg_ = srsran::rlc_config_t::default_rlc_um_nr_config(6); } else if (args.mode == "UM12") { - cnfg_ = rlc_config_t::default_rlc_um_nr_config(12); + cnfg_ = srsran::rlc_config_t::default_rlc_um_nr_config(12); } else if (args.mode == "AM12") { - cnfg_ = rlc_config_t::default_rlc_am_nr_config(); + cnfg_ = srsran::rlc_config_t::default_rlc_am_nr_config(); } else { - cout << "Unsupported RLC mode " << args.mode << ", exiting." << endl; + std::cout << "Unsupported RLC mode " << args.mode << ", exiting." << std::endl; exit(-1); } @@ -500,7 +285,7 @@ void stress_test(stress_test_args_t args) pcap_handle->open("rlc_stress_test_nr.pcap"); } } else { - cout << "Unsupported RAT mode " << args.rat << ", exiting." << endl; + std::cout << "Unsupported RAT mode " << args.rat << ", exiting." << std::endl; exit(-1); } @@ -513,8 +298,8 @@ void stress_test(stress_test_args_t args) srsran::timer_handler timers(8); - rlc rlc1(log1.id().c_str()); - rlc rlc2(log2.id().c_str()); + srsran::rlc rlc1(log1.id().c_str()); + srsran::rlc rlc2(log2.id().c_str()); rlc_tester tester1(&rlc1, "tester1", args, lcid, seed); rlc_tester tester2(&rlc2, "tester2", args, lcid, seed); @@ -529,7 +314,7 @@ void stress_test(stress_test_args_t args) rlc2.add_bearer(lcid, cnfg_); } - printf("Starting test ..\n"); + printf("Starting test ... Seed: %u\n", seed); tester1.start(7); if (!args.single_tx) { @@ -540,6 +325,8 @@ void stress_test(stress_test_args_t args) // wait until test is over std::this_thread::sleep_for(std::chrono::seconds(args.test_duration_sec)); + srslog::flush(); + fflush(stdout); printf("Test finished, tearing down ..\n"); // Stop RLC instances first to release blocking writers @@ -549,7 +336,6 @@ void stress_test(stress_test_args_t args) }); printf("RLC entities stopped.\n"); - // Stop upper layer writers tester1.stop(); tester2.stop(); @@ -561,10 +347,10 @@ void stress_test(stress_test_args_t args) pcap.close(); } - rlc_metrics_t metrics = {}; + srsran::rlc_metrics_t metrics = {}; rlc1.get_metrics(metrics, 1); - printf("RLC1 received %d SDUs in %ds (%.2f/s), Tx=%" PRIu64 " B, Rx=%" PRIu64 " B\n", + printf("RLC1 received %" PRIu64 " SDUs in %ds (%.2f/s), Tx=%" PRIu64 " B, Rx=%" PRIu64 " B\n", tester1.get_nof_rx_pdus(), args.test_duration_sec, static_cast(tester1.get_nof_rx_pdus() / args.test_duration_sec), @@ -573,7 +359,7 @@ void stress_test(stress_test_args_t args) rlc_bearer_metrics_print(metrics.bearer[lcid]); rlc2.get_metrics(metrics, 1); - printf("RLC2 received %d SDUs in %ds (%.2f/s), Tx=%" PRIu64 " B, Rx=%" PRIu64 " B\n", + printf("RLC2 received %" PRIu64 " SDUs in %ds (%.2f/s), Tx=%" PRIu64 " B, Rx=%" PRIu64 " B\n", tester2.get_nof_rx_pdus(), args.test_duration_sec, static_cast(tester2.get_nof_rx_pdus() / args.test_duration_sec), diff --git a/lib/test/rlc/rlc_stress_test.h b/lib/test/rlc/rlc_stress_test.h new file mode 100644 index 000000000..99a1ac42d --- /dev/null +++ b/lib/test/rlc/rlc_stress_test.h @@ -0,0 +1,281 @@ +/** + * + * \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/block_queue.h" +#include "srsran/common/crash_handler.h" +#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/rlc/rlc.h" +#include +#include +#include +#include +#include +#include +#include + +#include "srsran/common/mac_pcap.h" +#include "srsran/mac/mac_sch_pdu_nr.h" +static std::unique_ptr pcap_handle = nullptr; + +namespace bpo = boost::program_options; + +inline int write_pdu_to_pcap(const bool is_dl, const uint32_t lcid, const uint8_t* payload, const uint32_t len) +{ + const uint32_t PCAP_CRNTI = 0x1001; + const uint32_t PCAP_TTI = 666; + if (pcap_handle) { + srsran::byte_buffer_t tx_buffer; + srsran::mac_sch_pdu_nr tx_pdu; + tx_pdu.init_tx(&tx_buffer, len + 10); + tx_pdu.add_sdu(lcid, payload, len); + tx_pdu.pack(); + if (is_dl) { + pcap_handle->write_dl_crnti_nr(tx_buffer.msg, tx_buffer.N_bytes, PCAP_CRNTI, true, PCAP_TTI); + } else { + pcap_handle->write_ul_crnti_nr(tx_buffer.msg, tx_buffer.N_bytes, PCAP_CRNTI, true, PCAP_TTI); + } + + return SRSRAN_SUCCESS; + } + return SRSRAN_ERROR; +} + +typedef struct { + std::string rat; + std::string mode; + int32_t sdu_size; + uint32_t test_duration_sec; + float pdu_drop_rate; + float pdu_cut_rate; + float pdu_duplicate_rate; + uint32_t sdu_gen_delay_usec; + uint32_t pdu_tx_delay_usec; + uint32_t log_level; + bool single_tx; + bool write_pcap; + uint32_t avg_opp_size; + bool random_opp; + bool zero_seed; + uint32_t seed; + uint32_t nof_pdu_tti; + uint32_t max_retx; + int32_t log_hex_limit; + uint32_t min_sdu_size; + uint32_t max_sdu_size; +} stress_test_args_t; + +void parse_args(stress_test_args_t* args, int argc, char* argv[]) +{ + // Command line only options + bpo::options_description general("General options"); + + general.add_options()("help,h", "Produce help message")("version,v", "Print version information and exit"); + + // clang-format off + + // Command line or config file options + bpo::options_description common("Configuration options"); + common.add_options() + ("rat", bpo::value(&args->rat)->default_value("LTE"), "The RLC version to use (LTE/NR)") + ("mode", bpo::value(&args->mode)->default_value("AM"), "Whether to test RLC acknowledged or unacknowledged mode (AM/UM for LTE) (UM6/UM12 for NR)") + ("duration", bpo::value(&args->test_duration_sec)->default_value(5), "Duration (sec)") + ("sdu_size", bpo::value(&args->sdu_size)->default_value(-1), "Size of SDUs (-1 means random)") + ("random_opp", bpo::value(&args->random_opp)->default_value(true), "Whether to generate random MAC opportunities") + ("avg_opp_size", bpo::value(&args->avg_opp_size)->default_value(1505), "Size of the MAC opportunity (if not random)") + ("sdu_gen_delay", bpo::value(&args->sdu_gen_delay_usec)->default_value(0), "SDU generation delay (usec)") + ("pdu_tx_delay", bpo::value(&args->pdu_tx_delay_usec)->default_value(0), "Delay in MAC for transfering PDU from tx'ing RLC to rx'ing RLC (usec)") + ("pdu_drop_rate", bpo::value(&args->pdu_drop_rate)->default_value(0.1), "Rate at which RLC PDUs are dropped") + ("pdu_cut_rate", bpo::value(&args->pdu_cut_rate)->default_value(0.0), "Rate at which RLC PDUs are chopped in length") + ("pdu_duplicate_rate", bpo::value(&args->pdu_duplicate_rate)->default_value(0.0), "Rate at which RLC PDUs are duplicated") + ("loglevel", bpo::value(&args->log_level)->default_value((int)srslog::basic_levels::debug), "Log level (1=Error,2=Warning,3=Info,4=Debug)") + ("singletx", bpo::value(&args->single_tx)->default_value(false), "If set to true, only one node is generating data") + ("pcap", bpo::value(&args->write_pcap)->default_value(false), "Whether to write all RLC PDU to PCAP file") + ("zeroseed", bpo::value(&args->zero_seed)->default_value(false), "Whether to initialize random seed to zero") + ("seed", bpo::value(&args->seed)->default_value(0), "Seed to use in run. 0 means the seed is randomly generated") + ("max_retx", bpo::value(&args->max_retx)->default_value(32), "Maximum number of RLC retransmission attempts") + ("nof_pdu_tti", bpo::value(&args->nof_pdu_tti)->default_value(1), "Number of PDUs processed in a TTI") + ("log_hex_limit", bpo::value(&args->log_hex_limit)->default_value(-1), "Maximum bytes in hex log") + ("min_sdu_size", bpo::value(&args->min_sdu_size)->default_value(5), "Minimum SDU size") + ("max_sdu_size", bpo::value(&args->max_sdu_size)->default_value(1500), "Maximum SDU size"); + // clang-format on + + // these options are allowed on the command line + bpo::options_description cmdline_options; + cmdline_options.add(common).add(general); + + // parse the command line and store result in vm + bpo::variables_map vm; + bpo::store(bpo::command_line_parser(argc, argv).options(cmdline_options).run(), vm); + bpo::notify(vm); + + // help option was given - print usage and exit + if (vm.count("help") > 0) { + std::cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << std::endl << std::endl; + std::cout << common << std::endl << general << std::endl; + exit(0); + } + + if (args->log_level > 4) { + args->log_level = 4; + printf("Set log level to %d (%s)\n", + args->log_level, + srslog::basic_level_to_string(static_cast(args->log_level))); + } + + // convert mode to upper case + for (auto& c : args->mode) { + c = toupper(c); + } +} + +class mac_dummy : public srsran::thread +{ +public: + mac_dummy(srsue::rlc_interface_mac* rlc1_, + srsue::rlc_interface_mac* rlc2_, + stress_test_args_t args_, + uint32_t lcid_, + srsran::timer_handler* timers_, + srsran::rlc_pcap* pcap_, + uint32_t seed_) : + run_enable(true), + rlc1(rlc1_), + rlc2(rlc2_), + args(args_), + pcap(pcap_), + lcid(lcid_), + timers(timers_), + logger(srslog::fetch_basic_logger("MAC", false)), + thread("MAC_DUMMY"), + real_dist(0.0, 1.0), + mt19937(seed_) + { + logger.set_level(static_cast(args.log_level)); + logger.set_hex_dump_max_size(args.log_hex_limit); + } + + void stop() + { + run_enable = false; + wait_thread_finish(); + } + + void enqueue_task(srsran::move_task_t task) { pending_tasks.push(std::move(task)); } + +private: + void run_thread() final; + + void run_tti(srsue::rlc_interface_mac* tx_rlc, srsue::rlc_interface_mac* rx_rlc, bool is_dl); + + void run_tx_tti(srsue::rlc_interface_mac* tx_rlc, + srsue::rlc_interface_mac* rx_rlc, + std::vector& pdu_list); + + void run_rx_tti(srsue::rlc_interface_mac* tx_rlc, + srsue::rlc_interface_mac* rx_rlc, + bool is_dl, + std::vector& pdu_list); + srsue::rlc_interface_mac* rlc1 = nullptr; + srsue::rlc_interface_mac* rlc2 = nullptr; + + std::atomic run_enable = {false}; + stress_test_args_t args = {}; + srsran::rlc_pcap* pcap = nullptr; + uint32_t lcid = 0; + srslog::basic_logger& logger; + srsran::timer_handler* timers = nullptr; + + srsran::block_queue pending_tasks; + + std::mt19937 mt19937; + std::uniform_real_distribution real_dist; +}; + +class rlc_tester : public srsue::pdcp_interface_rlc, public srsue::rrc_interface_rlc, public srsran::thread +{ +public: + rlc_tester(srsue::rlc_interface_pdcp* rlc_pdcp_, + std::string name_, + stress_test_args_t args_, + uint32_t lcid_, + uint32_t seed_) : + logger(srslog::fetch_basic_logger(name_.c_str(), false)), + rlc_pdcp(rlc_pdcp_), + name(name_), + args(args_), + lcid(lcid_), + thread("RLC_TESTER"), + int_dist(args_.min_sdu_size, args_.max_sdu_size), + mt19937(seed_) + { + logger.set_level(srslog::basic_levels::error); + logger.set_hex_dump_max_size(args_.log_hex_limit); + } + + void stop() + { + run_enable = false; + wait_thread_finish(); + } + + // PDCP interface + void write_pdu(uint32_t rx_lcid, srsran::unique_byte_buffer_t sdu) final; + void write_pdu_bcch_bch(srsran::unique_byte_buffer_t sdu) final {} + void write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t sdu) final {} + void write_pdu_pcch(srsran::unique_byte_buffer_t sdu) final {} + void write_pdu_mch(uint32_t lcid_, srsran::unique_byte_buffer_t sdu) final {} + void notify_delivery(uint32_t lcid_, const srsran::pdcp_sn_vector_t& pdcp_sns) final {} + void notify_failure(uint32_t lcid_, const srsran::pdcp_sn_vector_t& pdcp_sns) final {} + + // RRC interface + void max_retx_attempted() final + { + logger.error( + "Maximum number of RLC retransmission reached. Consider increasing threshold or lowering channel drop rate."); + std::this_thread::sleep_for(std::chrono::seconds(1)); + exit(1); + } + void protocol_failure() final + { + logger.error("RLC protocol error detected."); + std::this_thread::sleep_for(std::chrono::seconds(1)); + exit(1); + } + const char* get_rb_name(uint32_t lcid) final { return "DRB1"; } + + uint64_t get_nof_rx_pdus() const { return rx_pdus; } + +private: + const static size_t max_pdcp_sn = 262143U; // 18bit SN + void run_thread() final; + + std::atomic run_enable = {true}; + + /// Tx uses thread-local PDCP SN to set SDU content, the Rx uses this variable to check received SDUs + uint8_t next_expected_sdu = 0; + uint64_t rx_pdus = 0; + uint32_t lcid = 0; + srslog::basic_logger& logger; + + std::string name; + + stress_test_args_t args = {}; + + srsue::rlc_interface_pdcp* rlc_pdcp = nullptr; // used by run_thread to push PDCP SDUs to RLC + + std::mt19937 mt19937; + std::uniform_int_distribution<> int_dist; +}; + From 7bcec48e19e67855037c5eca029b83ed63b529e5 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Tue, 22 Feb 2022 18:12:56 +0000 Subject: [PATCH 025/195] lib,rlc_stress_test: fixed issue in making log_hex_limit configurable. --- lib/test/rlc/rlc_stress_test.cc | 11 ++--------- lib/test/rlc/rlc_stress_test.h | 2 +- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/lib/test/rlc/rlc_stress_test.cc b/lib/test/rlc/rlc_stress_test.cc index 8e8c70c48..141419d07 100644 --- a/lib/test/rlc/rlc_stress_test.cc +++ b/lib/test/rlc/rlc_stress_test.cc @@ -26,19 +26,12 @@ #include #include -#define LOG_HEX_LIMIT (-1) - #define MIN_SDU_SIZE (5) #define MAX_SDU_SIZE (1500) #include "srsran/common/mac_pcap.h" #include "srsran/mac/mac_sch_pdu_nr.h" -namespace bpo = boost::program_options; - -#define MIN_SDU_SIZE (5) -#define MAX_SDU_SIZE (1500) - /*********************** * MAC tester class ***********************/ @@ -240,10 +233,10 @@ void stress_test(stress_test_args_t args) { auto& log1 = srslog::fetch_basic_logger("RLC_1", false); log1.set_level(static_cast(args.log_level)); - log1.set_hex_dump_max_size(LOG_HEX_LIMIT); + log1.set_hex_dump_max_size(args.log_hex_limit); auto& log2 = srslog::fetch_basic_logger("RLC_2", false); log2.set_level(static_cast(args.log_level)); - log2.set_hex_dump_max_size(LOG_HEX_LIMIT); + log2.set_hex_dump_max_size(args.log_hex_limit); srsran::rlc_pcap pcap; uint32_t lcid = 1; diff --git a/lib/test/rlc/rlc_stress_test.h b/lib/test/rlc/rlc_stress_test.h index 99a1ac42d..bb99d948b 100644 --- a/lib/test/rlc/rlc_stress_test.h +++ b/lib/test/rlc/rlc_stress_test.h @@ -220,7 +220,7 @@ public: int_dist(args_.min_sdu_size, args_.max_sdu_size), mt19937(seed_) { - logger.set_level(srslog::basic_levels::error); + logger.set_level(static_cast(args.log_level)); logger.set_hex_dump_max_size(args_.log_hex_limit); } From 0c0642c289cf602858cd3b91fe34a44432cf0d69 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Tue, 22 Feb 2022 18:32:30 +0000 Subject: [PATCH 026/195] lib,rlc_stress_test: changed global pcap handler from .h to .cc. Deleted unecessary #define --- lib/test/rlc/rlc_stress_test.cc | 8 +++----- lib/test/rlc/rlc_stress_test.h | 10 ++++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/test/rlc/rlc_stress_test.cc b/lib/test/rlc/rlc_stress_test.cc index 141419d07..b1ea4ba29 100644 --- a/lib/test/rlc/rlc_stress_test.cc +++ b/lib/test/rlc/rlc_stress_test.cc @@ -26,12 +26,10 @@ #include #include -#define MIN_SDU_SIZE (5) -#define MAX_SDU_SIZE (1500) - #include "srsran/common/mac_pcap.h" #include "srsran/mac/mac_sch_pdu_nr.h" +static std::unique_ptr pcap_handle = nullptr; /*********************** * MAC tester class ***********************/ @@ -80,7 +78,7 @@ void mac_dummy::run_tx_tti(srsue::rlc_interface_mac* tx_rlc, // Create PDU unique buffer srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); if (!pdu) { - printf("Fatal Error: Could not allocate PDU in mac_reader::run_thread\n"); + printf("Fatal Error: Could not allocate PDU in %s\n", __FUNCTION__); exit(-1); } @@ -135,7 +133,7 @@ void mac_dummy::run_rx_tti(srsue::rlc_interface_mac* tx_rlc, rx_rlc->write_pdu(lcid, pdu->msg, pdu_len); // Write PCAP - write_pdu_to_pcap(is_dl, 4, pdu->msg, pdu_len); // Only handles NR rat + write_pdu_to_pcap(pcap_handle, is_dl, 4, pdu->msg, pdu_len); // Only handles NR rat if (is_dl) { pcap->write_dl_ccch(pdu->msg, pdu_len); } else { diff --git a/lib/test/rlc/rlc_stress_test.h b/lib/test/rlc/rlc_stress_test.h index bb99d948b..a904586ac 100644 --- a/lib/test/rlc/rlc_stress_test.h +++ b/lib/test/rlc/rlc_stress_test.h @@ -27,11 +27,12 @@ #include "srsran/common/mac_pcap.h" #include "srsran/mac/mac_sch_pdu_nr.h" -static std::unique_ptr pcap_handle = nullptr; -namespace bpo = boost::program_options; - -inline int write_pdu_to_pcap(const bool is_dl, const uint32_t lcid, const uint8_t* payload, const uint32_t len) +inline int write_pdu_to_pcap(const std::unique_ptr& pcap_handle, + const bool is_dl, + const uint32_t lcid, + const uint8_t* payload, + const uint32_t len) { const uint32_t PCAP_CRNTI = 0x1001; const uint32_t PCAP_TTI = 666; @@ -78,6 +79,7 @@ typedef struct { void parse_args(stress_test_args_t* args, int argc, char* argv[]) { + namespace bpo = boost::program_options; // Command line only options bpo::options_description general("General options"); From 034aa6a6d45d66c01aaab8e57306d8f9ddeec9b9 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Thu, 24 Feb 2022 11:50:42 +0100 Subject: [PATCH 027/195] rlc, nr: inform upper layer when max retransmissions is exceeded --- lib/include/srsran/rlc/rlc_am_nr.h | 3 +- lib/include/srsran/rlc/rlc_am_nr_packing.h | 1 + lib/src/rlc/rlc_am_nr.cc | 39 +++++++++++- lib/test/rlc/rlc_am_nr_test.cc | 69 ++++++++++++++++++++++ 4 files changed, 108 insertions(+), 4 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_am_nr.h b/lib/include/srsran/rlc/rlc_am_nr.h index b7ec41552..0763a445d 100644 --- a/lib/include/srsran/rlc/rlc_am_nr.h +++ b/lib/include/srsran/rlc/rlc_am_nr.h @@ -76,7 +76,7 @@ struct rlc_amd_tx_pdu_nr { uint32_t pdcp_sn = INVALID_RLC_SN; rlc_am_nr_pdu_header_t header = {}; unique_byte_buffer_t sdu_buf = nullptr; - uint32_t retx_count = 0; + uint32_t retx_count = RETX_COUNT_NOT_STARTED; struct pdu_segment { uint32_t so = 0; uint32_t retx_count = 0; @@ -135,6 +135,7 @@ private: uint32_t mod_nr = 4096; inline uint32_t tx_mod_base_nr(uint32_t sn) const; + void check_sn_reached_max_retx(uint32_t sn); /**************************************************************************** * Configurable parameters diff --git a/lib/include/srsran/rlc/rlc_am_nr_packing.h b/lib/include/srsran/rlc/rlc_am_nr_packing.h index 1297880ce..234b4ea22 100644 --- a/lib/include/srsran/rlc/rlc_am_nr_packing.h +++ b/lib/include/srsran/rlc/rlc_am_nr_packing.h @@ -22,6 +22,7 @@ namespace srsran { const uint32_t RLC_AM_NR_WINDOW_SIZE_12BIT = 4096; const uint32_t RLC_AM_NR_WINDOW_SIZE_18BIT = 262144; const uint32_t INVALID_RLC_SN = 0xFFFFFFFF; +const uint32_t RETX_COUNT_NOT_STARTED = 0xFFFFFFFF; ///< AM NR PDU header struct rlc_am_nr_pdu_header_t { diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 9d9b65301..3636ed5f6 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -747,14 +747,23 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) retx.so_end = segm->so + segm->payload_len; } } + + // TODO: Handle retx_count for segments + } else { // NACK'ing full SDU. // add to retx queue if it's not already there if (not retx_queue.has_sn(nack_sn)) { - // increment Retx counter and inform upper layers if needed - pdu.retx_count++; + // Increment retx_count and inform upper layers if needed + if (pdu.retx_count == RETX_COUNT_NOT_STARTED) { + // Set retx_count = 0 on first RE-transmission (38.322 Sec. 5.3.2) + pdu.retx_count = 0; + } else { + // Increment otherwise + pdu.retx_count++; + } - // check_sn_reached_max_retx(nack_sn); + check_sn_reached_max_retx(nack_sn); rlc_amd_retx_t& retx = retx_queue.push(); retx.sn = nack_sn; retx.is_segment = false; @@ -776,6 +785,30 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) */ } +/** + * Helper to check if a SN has reached the max reTx threshold + * + * Caller _must_ hold the mutex when calling the function. + * If the retx has been reached for a SN the upper layers (i.e. RRC/PDCP) will be informed. + * The SN is _not_ removed from the Tx window, so retransmissions of that SN can still occur. + * + * + * @param sn The SN of the PDU to check + */ +void rlc_am_nr_tx::check_sn_reached_max_retx(uint32_t sn) +{ + if (tx_window[sn].retx_count == cfg.max_retx_thresh) { + RlcWarning("Signaling max number of reTx=%d for SN=%d", tx_window[sn].retx_count, sn); + parent->rrc->max_retx_attempted(); + srsran::pdcp_sn_vector_t pdcp_sns; + pdcp_sns.push_back(tx_window[sn].pdcp_sn); + parent->pdcp->notify_failure(parent->lcid, pdcp_sns); + + std::lock_guard lock(parent->metrics_mutex); + parent->metrics.num_lost_pdus++; + } +} + uint32_t rlc_am_nr_tx::get_buffer_state() { uint32_t tx_queue = 0; diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index 99b203f83..79f92ab1f 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -883,6 +883,74 @@ int retx_segment_test() return SRSRAN_SUCCESS; } +// This test checks whether RLC informs upper layer when max retransmission has been reached +int max_retx_test() +{ + rlc_am_tester tester; + timer_handler timers(8); + int len = 0; + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); + srslog::fetch_basic_logger("RLC").set_hex_dump_max_size(100); + + const rlc_config_t rlc_cfg = rlc_config_t::default_rlc_am_nr_config(); + if (not rlc1.configure(rlc_cfg)) { + return SRSRAN_ERROR; + } + + // Push 2 SDUs into RLC1 + const uint32_t n_sdus = 2; + 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]->msg[0] = i; // Write the index into the buffer + sdu_bufs[i]->N_bytes = 1; // Give each buffer a size of 1 byte + sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications + rlc1.write_sdu(std::move(sdu_bufs[i])); + } + + // Read 2 PDUs from RLC1 (1 byte each) + const uint32_t n_pdus = 2; + 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()); + + // Fake status PDU that ack SN=1 and nack SN=0 + rlc_am_nr_status_pdu_t fake_status = {}; + fake_status.ack_sn = 2; // delivered up to SN=1 + fake_status.N_nack = 1; // one SN was lost + fake_status.nacks[0].nack_sn = 0; // it was SN=0 that was lost + + // pack into PDU + byte_buffer_t status_pdu; + rlc_am_nr_write_status_pdu(fake_status, rlc_cfg.am_nr.tx_sn_field_length, &status_pdu); + + // Exceed the number of tolerated retransmissions by one additional retransmission + // to trigger notification of the higher protocol layers. Note that the initial transmission + // (before starting retransmissions) does not count. See TS 38.322 Sec. 5.3.2 + for (uint32_t retx_count = 0; retx_count < rlc_cfg.am_nr.max_retx_thresh + 1; ++retx_count) { + // we've not yet reached max attempts + TESTASSERT(tester.max_retx_triggered == false); + + // Write status PDU to RLC1 + rlc1.write_pdu(status_pdu.msg, status_pdu.N_bytes); + + byte_buffer_t pdu_buf; + len = rlc1.read_pdu(pdu_buf.msg, 3); // 2 byte header + 1 byte payload + } + + // Now maxRetx should have been triggered + TESTASSERT(tester.max_retx_triggered == true); + + return SRSRAN_SUCCESS; +} + // This test checks the correct functioning of RLC discard functionality int discard_test() { @@ -1016,6 +1084,7 @@ int main() TESTASSERT(basic_segmentation_test() == SRSRAN_SUCCESS); TESTASSERT(segment_retx_test() == SRSRAN_SUCCESS); TESTASSERT(retx_segment_test() == SRSRAN_SUCCESS); + TESTASSERT(max_retx_test() == SRSRAN_SUCCESS); TESTASSERT(discard_test() == SRSRAN_SUCCESS); return SRSRAN_SUCCESS; } From 511ad9ed25fb2026fafdc2457f69cc1a2a4528e8 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Thu, 24 Feb 2022 15:34:27 +0100 Subject: [PATCH 028/195] rlc, nr: Increment retx_count for segments --- .../srsran/interfaces/rlc_interface_types.h | 1 + lib/include/srsran/rlc/rlc_am_nr.h | 1 - lib/src/rlc/rlc_am_nr.cc | 36 +++++---- lib/test/rlc/rlc_am_nr_test.cc | 75 ++++++++++++++++++- 4 files changed, 96 insertions(+), 17 deletions(-) diff --git a/lib/include/srsran/interfaces/rlc_interface_types.h b/lib/include/srsran/interfaces/rlc_interface_types.h index 2570d032d..0f4e069cc 100644 --- a/lib/include/srsran/interfaces/rlc_interface_types.h +++ b/lib/include/srsran/interfaces/rlc_interface_types.h @@ -225,6 +225,7 @@ public: rlc_cnfg.rat = srsran_rat_t::nr; rlc_cnfg.rlc_mode = rlc_mode_t::am; rlc_cnfg.am_nr.t_status_prohibit = 8; + rlc_cnfg.am_nr.max_retx_thresh = 4; rlc_cnfg.am_nr.t_reassembly = 35; rlc_cnfg.am_nr.poll_pdu = 4; return rlc_cnfg; diff --git a/lib/include/srsran/rlc/rlc_am_nr.h b/lib/include/srsran/rlc/rlc_am_nr.h index 0763a445d..09a31b44e 100644 --- a/lib/include/srsran/rlc/rlc_am_nr.h +++ b/lib/include/srsran/rlc/rlc_am_nr.h @@ -79,7 +79,6 @@ struct rlc_amd_tx_pdu_nr { uint32_t retx_count = RETX_COUNT_NOT_STARTED; struct pdu_segment { uint32_t so = 0; - uint32_t retx_count = 0; uint32_t payload_len = 0; }; std::list segment_list; diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 3636ed5f6..c48af151a 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -723,11 +723,13 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) // Process N_acks for (uint32_t nack_idx = 0; nack_idx < status.N_nack; nack_idx++) { + // TODO: Possibly loop NACK range if (st.tx_next_ack <= status.nacks[nack_idx].nack_sn && status.nacks[nack_idx].nack_sn <= st.tx_next) { auto nack = status.nacks[nack_idx]; uint32_t nack_sn = nack.nack_sn; if (tx_window.has_sn(nack_sn)) { - auto& pdu = tx_window[nack_sn]; + auto& pdu = tx_window[nack_sn]; + bool retx_enqueued = false; if (nack.has_so) { // NACK'ing missing bytes in SDU segment. @@ -739,39 +741,45 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) segm != pdu.segment_list.end(); segm++) { if (segm->so >= nack.so_start && segm->so < nack.so_end) { + // TODO: Check if this segment is not already queued for retransmission rlc_amd_retx_t& retx = retx_queue.push(); retx.sn = nack_sn; retx.is_segment = true; retx.so_start = segm->so; retx.current_so = segm->so; retx.so_end = segm->so + segm->payload_len; + retx_enqueued = true; } } - - // TODO: Handle retx_count for segments - } else { // NACK'ing full SDU. // add to retx queue if it's not already there if (not retx_queue.has_sn(nack_sn)) { - // Increment retx_count and inform upper layers if needed - if (pdu.retx_count == RETX_COUNT_NOT_STARTED) { - // Set retx_count = 0 on first RE-transmission (38.322 Sec. 5.3.2) - pdu.retx_count = 0; - } else { - // Increment otherwise - pdu.retx_count++; - } - - check_sn_reached_max_retx(nack_sn); rlc_amd_retx_t& retx = retx_queue.push(); retx.sn = nack_sn; retx.is_segment = false; retx.so_start = 0; retx.current_so = 0; retx.so_end = pdu.sdu_buf->N_bytes; + retx_enqueued = true; } } + + if (retx_enqueued) { + // Increment retx_count + if (pdu.retx_count == RETX_COUNT_NOT_STARTED) { + // Set retx_count = 0 on first RE-transmission (38.322 Sec. 5.3.2) + pdu.retx_count = 0; + } else { + // Increment otherwise + pdu.retx_count++; + } + + // Inform upper layers if needed + check_sn_reached_max_retx(nack_sn); + + RlcInfo("Schedule SN=%d for retx", nack_sn); + } } } } diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index 79f92ab1f..b7fb60973 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -884,7 +884,8 @@ int retx_segment_test() } // This test checks whether RLC informs upper layer when max retransmission has been reached -int max_retx_test() +// due to lost SDUs as a whole +int max_retx_lost_sdu_test() { rlc_am_tester tester; timer_handler timers(8); @@ -951,6 +952,75 @@ int max_retx_test() return SRSRAN_SUCCESS; } +// This test checks whether RLC informs upper layer when max retransmission has been reached +// due to lost SDU segments +int max_retx_lost_segments_test() +{ + rlc_am_tester tester; + timer_handler timers(8); + int len = 0; + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); + srslog::fetch_basic_logger("RLC").set_hex_dump_max_size(100); + + const rlc_config_t rlc_cfg = rlc_config_t::default_rlc_am_nr_config(); + if (not rlc1.configure(rlc_cfg)) { + return SRSRAN_ERROR; + } + + // Push 2 SDUs into RLC1 + const uint32_t n_sdus = 2; + 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]->msg[0] = i; // Write the index into the buffer + sdu_bufs[i]->N_bytes = 20; // Give each buffer a size of 20 bytes + sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications + rlc1.write_sdu(std::move(sdu_bufs[i])); + } + + // Read 4 PDUs from RLC1 (max 15 byte each) + const uint32_t n_pdus = 4; + byte_buffer_t pdu_bufs[n_pdus]; + for (uint32_t i = 0; i < n_pdus; i++) { + len = rlc1.read_pdu(pdu_bufs[i].msg, 15); // 2 byte header + 13 byte payload + pdu_bufs[i].N_bytes = len; + } + + TESTASSERT(0 == rlc1.get_buffer_state()); + + // Fake status PDU that ack SN=1 and nack SN=0 + rlc_am_nr_status_pdu_t fake_status = {}; + fake_status.ack_sn = 2; // delivered up to SN=1 + fake_status.N_nack = 1; // one SN was lost + fake_status.nacks[0].nack_sn = 0; // it was SN=0 that was lost + + // pack into PDU + byte_buffer_t status_pdu; + rlc_am_nr_write_status_pdu(fake_status, rlc_cfg.am_nr.tx_sn_field_length, &status_pdu); + + // Exceed the number of tolerated retransmissions by one additional retransmission + // to trigger notification of the higher protocol layers. Note that the initial transmission + // (before starting retransmissions) does not count. See TS 38.322 Sec. 5.3.2 + for (uint32_t retx_count = 0; retx_count < rlc_cfg.am_nr.max_retx_thresh + 1; ++retx_count) { + // we've not yet reached max attempts + TESTASSERT(tester.max_retx_triggered == false); + + // Write status PDU to RLC1 + rlc1.write_pdu(status_pdu.msg, status_pdu.N_bytes); + + byte_buffer_t pdu_buf; + len = rlc1.read_pdu(pdu_buf.msg, 3); // 2 byte header + 1 byte payload + } + + // Now maxRetx should have been triggered + TESTASSERT(tester.max_retx_triggered == true); + + return SRSRAN_SUCCESS; +} + // This test checks the correct functioning of RLC discard functionality int discard_test() { @@ -1084,7 +1154,8 @@ int main() TESTASSERT(basic_segmentation_test() == SRSRAN_SUCCESS); TESTASSERT(segment_retx_test() == SRSRAN_SUCCESS); TESTASSERT(retx_segment_test() == SRSRAN_SUCCESS); - TESTASSERT(max_retx_test() == SRSRAN_SUCCESS); + TESTASSERT(max_retx_lost_sdu_test() == SRSRAN_SUCCESS); + TESTASSERT(max_retx_lost_segments_test() == SRSRAN_SUCCESS); TESTASSERT(discard_test() == SRSRAN_SUCCESS); return SRSRAN_SUCCESS; } From 0fb6420e8b90e585bbeaa4f7a0182b475981f5e4 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Fri, 25 Feb 2022 11:37:41 +0100 Subject: [PATCH 029/195] rlc, nr: avoid multi increments of SDU's retx_count within one status message --- lib/include/srsran/rlc/rlc_am_data_structs.h | 4 +- lib/src/rlc/rlc_am_nr.cc | 46 +++++++------- lib/test/rlc/rlc_am_nr_test.cc | 63 ++++++++++++++++---- 3 files changed, 77 insertions(+), 36 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_am_data_structs.h b/lib/include/srsran/rlc/rlc_am_data_structs.h index a7fa667d0..a9ce6cac5 100644 --- a/lib/include/srsran/rlc/rlc_am_data_structs.h +++ b/lib/include/srsran/rlc/rlc_am_data_structs.h @@ -303,8 +303,8 @@ private: struct rlc_amd_retx_t { uint32_t sn; bool is_segment; - uint32_t so_start; - uint32_t so_end; + uint32_t so_start; // offset to first byte of this segment + uint32_t so_end; // offset to first byte beyond the end of this segment uint32_t current_so; }; diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index c48af151a..5e6f0eee3 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -18,6 +18,7 @@ #include "srsran/rlc/rlc_am_nr_packing.h" #include "srsran/srslog/event_trace.h" #include +#include #define RLC_AM_NR_WINDOW_SIZE 2048 @@ -721,7 +722,8 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) } notify_info_vec.clear(); - // Process N_acks + // Process N_nacks + std::set retx_sn_set; // Set of PDU SNs added for retransmission (no duplicates) for (uint32_t nack_idx = 0; nack_idx < status.N_nack; nack_idx++) { // TODO: Possibly loop NACK range if (st.tx_next_ack <= status.nacks[nack_idx].nack_sn && status.nacks[nack_idx].nack_sn <= st.tx_next) { @@ -729,7 +731,6 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) uint32_t nack_sn = nack.nack_sn; if (tx_window.has_sn(nack_sn)) { auto& pdu = tx_window[nack_sn]; - bool retx_enqueued = false; if (nack.has_so) { // NACK'ing missing bytes in SDU segment. @@ -748,7 +749,9 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) retx.so_start = segm->so; retx.current_so = segm->so; retx.so_end = segm->so + segm->payload_len; - retx_enqueued = true; + retx_sn_set.insert(nack_sn); + RlcInfo( + "Schedule PDU segment SN=%d, so_start=%d, so_end=%d for retx", retx.sn, retx.so_start, retx.so_end); } } } else { @@ -761,27 +764,28 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) retx.so_start = 0; retx.current_so = 0; retx.so_end = pdu.sdu_buf->N_bytes; - retx_enqueued = true; + retx_sn_set.insert(nack_sn); + RlcInfo("Schedule PDU SN=%d for retx", retx.sn); } } - - if (retx_enqueued) { - // Increment retx_count - if (pdu.retx_count == RETX_COUNT_NOT_STARTED) { - // Set retx_count = 0 on first RE-transmission (38.322 Sec. 5.3.2) - pdu.retx_count = 0; - } else { - // Increment otherwise - pdu.retx_count++; - } - - // Inform upper layers if needed - check_sn_reached_max_retx(nack_sn); - - RlcInfo("Schedule SN=%d for retx", nack_sn); - } - } + } // TX window containts NACK SN + } // NACK SN within expected range + } // NACK loop + + // Process retx_count and inform upper layers if needed + for (auto retx_sn_it = retx_sn_set.begin(); retx_sn_it != retx_sn_set.end(); retx_sn_it++) { + auto& pdu = tx_window[*retx_sn_it]; + // Increment retx_count + if (pdu.retx_count == RETX_COUNT_NOT_STARTED) { + // Set retx_count = 0 on first RE-transmission of associated SDU (38.322 Sec. 5.3.2) + pdu.retx_count = 0; + } else { + // Increment otherwise + pdu.retx_count++; } + + // Inform upper layers if needed + check_sn_reached_max_retx(*retx_sn_it); } /** diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index b7fb60973..68908d591 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -981,38 +981,75 @@ int max_retx_lost_segments_test() rlc1.write_sdu(std::move(sdu_bufs[i])); } - // Read 4 PDUs from RLC1 (max 15 byte each) + // Read 2*2=4 PDUs from RLC1 and limit to 15 byte to force segmentation in two parts: + // Segment 1: 2 byte header + 13 byte payload; space fully used + // Segment 2: 4 byte header + 7 byte payload; space not fully used, 4 bytes left over const uint32_t n_pdus = 4; byte_buffer_t pdu_bufs[n_pdus]; for (uint32_t i = 0; i < n_pdus; i++) { - len = rlc1.read_pdu(pdu_bufs[i].msg, 15); // 2 byte header + 13 byte payload + len = rlc1.read_pdu(pdu_bufs[i].msg, 15); pdu_bufs[i].N_bytes = len; } TESTASSERT(0 == rlc1.get_buffer_state()); - // Fake status PDU that ack SN=1 and nack SN=0 - rlc_am_nr_status_pdu_t fake_status = {}; - fake_status.ack_sn = 2; // delivered up to SN=1 - fake_status.N_nack = 1; // one SN was lost - fake_status.nacks[0].nack_sn = 0; // it was SN=0 that was lost + // Fake status PDU that ack SN=1 and nack {SN=0 segment 0, SN=0 segment 1} + rlc_am_nr_status_pdu_t status_lost_both_segments = {}; + status_lost_both_segments.ack_sn = 2; // delivered up to SN=1 + status_lost_both_segments.N_nack = 2; // two segments lost + status_lost_both_segments.nacks[0].nack_sn = 0; // it was SN=0 that was lost + status_lost_both_segments.nacks[0].has_so = true; // this NACKs a segment + status_lost_both_segments.nacks[0].so_start = 0; // segment starts at (and includes) byte 0 + status_lost_both_segments.nacks[0].so_end = 12; // segment ends at (and includes) byte 12 + status_lost_both_segments.nacks[1].nack_sn = 0; // it was SN=0 that was lost + status_lost_both_segments.nacks[1].has_so = true; // this NACKs a segment + status_lost_both_segments.nacks[1].so_start = 13; // segment starts at (and includes) byte 13 + status_lost_both_segments.nacks[1].so_end = 19; // segment ends at (and includes) byte 19 // pack into PDU - byte_buffer_t status_pdu; - rlc_am_nr_write_status_pdu(fake_status, rlc_cfg.am_nr.tx_sn_field_length, &status_pdu); + byte_buffer_t status_pdu_lost_both_segments; + rlc_am_nr_write_status_pdu( + status_lost_both_segments, rlc_cfg.am_nr.tx_sn_field_length, &status_pdu_lost_both_segments); + + // Fake status PDU that ack SN=1 and nack {SN=0 segment 2} + rlc_am_nr_status_pdu_t status_lost_second_segment = {}; + status_lost_second_segment.ack_sn = 2; // delivered up to SN=1 + status_lost_second_segment.N_nack = 1; // one SN was lost + status_lost_second_segment.nacks[0].nack_sn = 0; // it was SN=0 that was lost + status_lost_second_segment.nacks[0].has_so = true; // this NACKs a segment + status_lost_second_segment.nacks[0].so_start = 13; // segment starts at (and includes) byte 13 + status_lost_second_segment.nacks[0].so_end = 19; // segment ends at (and includes) byte 19 + + // pack into PDU + byte_buffer_t status_pdu_lost_second_segment; + rlc_am_nr_write_status_pdu( + status_lost_second_segment, rlc_cfg.am_nr.tx_sn_field_length, &status_pdu_lost_second_segment); // Exceed the number of tolerated retransmissions by one additional retransmission // to trigger notification of the higher protocol layers. Note that the initial transmission // (before starting retransmissions) does not count. See TS 38.322 Sec. 5.3.2 for (uint32_t retx_count = 0; retx_count < rlc_cfg.am_nr.max_retx_thresh + 1; ++retx_count) { + byte_buffer_t pdu_buf; + // we've not yet reached max attempts TESTASSERT(tester.max_retx_triggered == false); - // Write status PDU to RLC1 - rlc1.write_pdu(status_pdu.msg, status_pdu.N_bytes); + if (retx_count < rlc_cfg.am_nr.max_retx_thresh / 2) { + // Send NACK for segment 1 and segment 2 + // Although two segments, this must count as one retransmission, + // because both segments NACK the same SDU in the same status message. + rlc1.write_pdu(status_pdu_lost_both_segments.msg, status_pdu_lost_both_segments.N_bytes); - byte_buffer_t pdu_buf; - len = rlc1.read_pdu(pdu_buf.msg, 3); // 2 byte header + 1 byte payload + // read the retransmitted PDUs + len = rlc1.read_pdu(pdu_buf.msg, 15); // 2 byte header + 13 byte payload + len = rlc1.read_pdu(pdu_buf.msg, 15); // 4 byte header + 7 byte payload + } else { + // Send NACK for segment 2 (assume at least segment 1 was finally received) + rlc1.write_pdu(status_pdu_lost_second_segment.msg, status_pdu_lost_second_segment.N_bytes); + + // read the retransmitted PDUs + len = rlc1.read_pdu(pdu_buf.msg, 15); // 4 byte header + 7 byte payload + } } // Now maxRetx should have been triggered From b55f8ea624174158d8c8cab57b0e7ec667deea59 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Mon, 28 Feb 2022 06:04:16 +0100 Subject: [PATCH 030/195] rlc, nr: cosmetic changes --- lib/src/rlc/rlc_am_nr.cc | 10 +++++----- lib/test/rlc/rlc_am_nr_test.cc | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 5e6f0eee3..65c478695 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -751,7 +751,7 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) retx.so_end = segm->so + segm->payload_len; retx_sn_set.insert(nack_sn); RlcInfo( - "Schedule PDU segment SN=%d, so_start=%d, so_end=%d for retx", retx.sn, retx.so_start, retx.so_end); + "Scheduled RETX of SDU segment SN=%d, so_start=%d, so_end=%d", retx.sn, retx.so_start, retx.so_end); } } } else { @@ -765,7 +765,7 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) retx.current_so = 0; retx.so_end = pdu.sdu_buf->N_bytes; retx_sn_set.insert(nack_sn); - RlcInfo("Schedule PDU SN=%d for retx", retx.sn); + RlcInfo("Scheduled RETX of SDU SN=%d", retx.sn); } } } // TX window containts NACK SN @@ -773,8 +773,8 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) } // NACK loop // Process retx_count and inform upper layers if needed - for (auto retx_sn_it = retx_sn_set.begin(); retx_sn_it != retx_sn_set.end(); retx_sn_it++) { - auto& pdu = tx_window[*retx_sn_it]; + for (uint32_t retx_sn : retx_sn_set) { + auto& pdu = tx_window[retx_sn]; // Increment retx_count if (pdu.retx_count == RETX_COUNT_NOT_STARTED) { // Set retx_count = 0 on first RE-transmission of associated SDU (38.322 Sec. 5.3.2) @@ -785,7 +785,7 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) } // Inform upper layers if needed - check_sn_reached_max_retx(*retx_sn_it); + check_sn_reached_max_retx(retx_sn); } /** diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index 68908d591..ed513db5d 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -895,6 +895,7 @@ int max_retx_lost_sdu_test() rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); srslog::fetch_basic_logger("RLC").set_hex_dump_max_size(100); + test_delimit_logger delimiter("max retx lost SDU"); const rlc_config_t rlc_cfg = rlc_config_t::default_rlc_am_nr_config(); if (not rlc1.configure(rlc_cfg)) { @@ -964,6 +965,7 @@ int max_retx_lost_segments_test() rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); srslog::fetch_basic_logger("RLC").set_hex_dump_max_size(100); + test_delimit_logger delimiter("max retx lost SDU segment"); const rlc_config_t rlc_cfg = rlc_config_t::default_rlc_am_nr_config(); if (not rlc1.configure(rlc_cfg)) { From f7515e98cfea33164192f702569d41b0e35c6656 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Fri, 25 Feb 2022 05:42:13 +0100 Subject: [PATCH 031/195] rlc, nr: add pack/unpack of nack_range in status PDUs --- lib/include/srsran/rlc/rlc_common.h | 20 ++++++---- lib/src/rlc/rlc_am_nr.cc | 7 +++- lib/src/rlc/rlc_am_nr_packing.cc | 31 ++++++++++++--- lib/test/rlc/rlc_am_nr_pdu_test.cc | 62 +++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+), 14 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_common.h b/lib/include/srsran/rlc/rlc_common.h index f7992a472..935096907 100644 --- a/lib/include/srsran/rlc/rlc_common.h +++ b/lib/include/srsran/rlc/rlc_common.h @@ -162,17 +162,21 @@ public: // NACK helper (for LTE and NR) struct rlc_status_nack_t { - uint32_t nack_sn; - bool has_so; - uint16_t so_start; - uint16_t so_end; + uint32_t nack_sn; // Sequence Number (SN) of first missing SDU + bool has_so; // NACKs continuous sequence of bytes [so_start..so_end] + uint16_t so_start; // First missing byte in SDU with SN=nack_sn + uint16_t so_end; // Last missing byte in SDU with SN=nack_sn or SN=nack_sn+nack_range-1 if has_nack_range. + bool has_nack_range; // NACKs continuous sequence of SDUs + uint8_t nack_range; // Number of SDUs being NACKed (including SN=nack_sn) rlc_status_nack_t() { - has_so = false; - nack_sn = 0; - so_start = 0; - so_end = 0; + has_so = false; + nack_sn = 0; + so_start = 0; + so_end = 0; + has_nack_range = false; + nack_range = 0; } }; diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 65c478695..e8b92fab0 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -725,7 +725,12 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) // Process N_nacks std::set retx_sn_set; // Set of PDU SNs added for retransmission (no duplicates) for (uint32_t nack_idx = 0; nack_idx < status.N_nack; nack_idx++) { - // TODO: Possibly loop NACK range + if (status.nacks[nack_idx].has_nack_range) { + RlcError("Handling NACK ranges is not yet implemented. Ignoring NACK across %d SDU(s) starting from SN=%d", + status.nacks[nack_idx].nack_range, + status.nacks[nack_idx].nack_sn); + continue; + } if (st.tx_next_ack <= status.nacks[nack_idx].nack_sn && status.nacks[nack_idx].nack_sn <= st.tx_next) { auto nack = status.nacks[nack_idx]; uint32_t nack_sn = nack.nack_sn; diff --git a/lib/src/rlc/rlc_am_nr_packing.cc b/lib/src/rlc/rlc_am_nr_packing.cc index c2aad71b0..2f31ca695 100644 --- a/lib/src/rlc/rlc_am_nr_packing.cc +++ b/lib/src/rlc/rlc_am_nr_packing.cc @@ -191,10 +191,15 @@ uint32_t rlc_am_nr_read_status_pdu(const uint8_t* payload, nack.nack_sn = (*ptr & 0xff) << 4; ptr++; - e1 = *ptr & 0x08; - uint8_t e2 = *ptr & 0x04; + e1 = *ptr & 0x08; // 1 = further NACKs follow + uint8_t e2 = *ptr & 0x04; // 1 = set of {so_start, so_end} follows + uint8_t e3 = *ptr & 0x02; // 1 = NACK range follows (i.e. NACK across multiple SNs) - // uint8_t len2 = (*ptr & 0xF0) >> 4; + // sanity check for reserved bits + if ((*ptr & 0x01) != 0) { + fprintf(stderr, "Malformed PDU, reserved bits are set.\n"); + return 0; + } nack.nack_sn |= (*ptr & 0xF0) >> 4; status->nacks[status->N_nack] = nack; @@ -210,6 +215,11 @@ uint32_t rlc_am_nr_read_status_pdu(const uint8_t* payload, status->nacks[status->N_nack].so_end |= (*ptr); ptr++; } + if (e3 != 0) { + status->nacks[status->N_nack].has_nack_range = true; + status->nacks[status->N_nack].nack_range = (*ptr); + ptr++; + } status->N_nack++; if (uint32_t(ptr - payload) > nof_bytes) { fprintf(stderr, "Malformed PDU, trying to read more bytes than it is available\n"); @@ -267,8 +277,15 @@ int32_t rlc_am_nr_write_status_pdu(const rlc_am_nr_status_pdu_t& status_pdu, if (status_pdu.nacks[i].has_so) { // Set E2 *ptr |= 0x04; + } - ptr++; + if (status_pdu.nacks[i].has_nack_range) { + // Set E3 + *ptr |= 0x02; + } + + ptr++; + if (status_pdu.nacks[i].has_so) { (*ptr) = status_pdu.nacks[i].so_start >> 8; ptr++; (*ptr) = status_pdu.nacks[i].so_start; @@ -276,8 +293,12 @@ int32_t rlc_am_nr_write_status_pdu(const rlc_am_nr_status_pdu_t& status_pdu, (*ptr) = status_pdu.nacks[i].so_end >> 8; ptr++; (*ptr) = status_pdu.nacks[i].so_end; + ptr++; + } + if (status_pdu.nacks[i].has_nack_range) { + (*ptr) = status_pdu.nacks[i].nack_range; + ptr++; } - ptr++; } } } else { diff --git a/lib/test/rlc/rlc_am_nr_pdu_test.cc b/lib/test/rlc/rlc_am_nr_pdu_test.cc index df299ecdc..dbeed0423 100644 --- a/lib/test/rlc/rlc_am_nr_pdu_test.cc +++ b/lib/test/rlc/rlc_am_nr_pdu_test.cc @@ -376,6 +376,63 @@ int rlc_am_nr_control_pdu_test5() return SRSRAN_SUCCESS; } +// Status PDU for 12bit SN with ACK_SN=2065, +// NACK range0: 3 full SDUs, NACK_SN=273..275 +// NACK range1: missing segment sequence across 4 SDUs +// starting at NACK_SN=276, SO_START=2, +// ending at NACK_SN=279, SO_END=5 +// E1 and E3 bit set on first NACK, E2 and E3 bit set on the second. +int rlc_am_nr_control_pdu_test_nack_range() +{ + test_delimit_logger delimiter("Control PDU test NACK range"); + const int len = 13; + std::array tv = {0x08, // D/C | CPT | ACK_SN_upper + 0x11, // ACK_SN_lower + 0x80, // E1 | R + 0x11, // NACK_SN_upper + 0x1a, // NACK_SN_lower | E1 | E2 | E3 | R + 0x03, // NACK_range + 0x11, // NACK_SN_upper + 0x46, // NACK_SN_lower | E1 | E2 | E3 | R + 0x00, // SO_START_upper + 0x02, // SO_START_lower + 0x00, // SO_END_upper + 0x05, // SO_END_lower + 0x04}; // NACK_range + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); + + // unpack PDU + rlc_am_nr_status_pdu_t status_pdu = {}; + TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &status_pdu) == SRSRAN_SUCCESS); + TESTASSERT(status_pdu.ack_sn == 2065); + TESTASSERT(status_pdu.N_nack == 2); + TESTASSERT(status_pdu.nacks[0].nack_sn == 273); + TESTASSERT(status_pdu.nacks[0].has_so == false); + TESTASSERT(status_pdu.nacks[0].has_nack_range == true); + TESTASSERT(status_pdu.nacks[0].nack_range == 3); + + TESTASSERT(status_pdu.nacks[1].nack_sn == 276); + TESTASSERT(status_pdu.nacks[1].has_so == true); + TESTASSERT(status_pdu.nacks[1].so_start == 2); + TESTASSERT(status_pdu.nacks[1].so_end == 5); + TESTASSERT(status_pdu.nacks[1].has_nack_range == true); + TESTASSERT(status_pdu.nacks[1].nack_range == 4); + + // reset status PDU + pdu.clear(); + + // pack again + TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &pdu) == SRSRAN_SUCCESS); + TESTASSERT(pdu.N_bytes == tv.size()); + + write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); + TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); + + return SRSRAN_SUCCESS; +} + int main(int argc, char** argv) { static const struct option long_options[] = {{"pcap", no_argument, nullptr, 'p'}, {nullptr, 0, nullptr, 0}}; @@ -457,5 +514,10 @@ int main(int argc, char** argv) return SRSRAN_ERROR; } + if (rlc_am_nr_control_pdu_test_nack_range()) { + fprintf(stderr, "rlc_am_nr_control_pdu_test_nack_range() failed.\n"); + return SRSRAN_ERROR; + } + return SRSRAN_SUCCESS; } From 60c3d79f477356cd0fef368a7ed51721dfc9c352 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Mon, 28 Feb 2022 16:20:52 +0100 Subject: [PATCH 032/195] rlc, nr: complete packing of status PDUs with 18bit SNs --- lib/include/srsran/rlc/rlc_am_nr_packing.h | 6 + lib/src/rlc/rlc_am_nr_packing.cc | 355 +++++++++++++------ lib/test/rlc/rlc_am_nr_pdu_test.cc | 375 ++++++++++++++++++--- 3 files changed, 598 insertions(+), 138 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_am_nr_packing.h b/lib/include/srsran/rlc/rlc_am_nr_packing.h index 234b4ea22..0bdc845d1 100644 --- a/lib/include/srsran/rlc/rlc_am_nr_packing.h +++ b/lib/include/srsran/rlc/rlc_am_nr_packing.h @@ -113,10 +113,16 @@ uint32_t rlc_am_nr_read_status_pdu(const uint8_t* payload, const uint32_t nof_bytes, const rlc_am_nr_sn_size_t sn_size, rlc_am_nr_status_pdu_t* status); +uint32_t +rlc_am_nr_read_status_pdu_12bit_sn(const uint8_t* payload, const uint32_t nof_bytes, rlc_am_nr_status_pdu_t* status); +uint32_t +rlc_am_nr_read_status_pdu_18bit_sn(const uint8_t* payload, const uint32_t nof_bytes, rlc_am_nr_status_pdu_t* status); int32_t rlc_am_nr_write_status_pdu(const rlc_am_nr_status_pdu_t& status_pdu, const rlc_am_nr_sn_size_t sn_size, byte_buffer_t* pdu); +int32_t rlc_am_nr_write_status_pdu_12bit_sn(const rlc_am_nr_status_pdu_t& status_pdu, byte_buffer_t* pdu); +int32_t rlc_am_nr_write_status_pdu_18bit_sn(const rlc_am_nr_status_pdu_t& status_pdu, byte_buffer_t* pdu); /** * Logs Status PDU into provided log channel, using fmt_str as format string diff --git a/lib/src/rlc/rlc_am_nr_packing.cc b/lib/src/rlc/rlc_am_nr_packing.cc index 2f31ca695..eac502c8e 100644 --- a/lib/src/rlc/rlc_am_nr_packing.cc +++ b/lib/src/rlc/rlc_am_nr_packing.cc @@ -141,6 +141,11 @@ uint32_t rlc_am_nr_write_data_pdu_header(const rlc_am_nr_pdu_header_t& header, b return len; } +/**************************************************************************** + * Status PDU pack/unpack helper functions + * Ref: 3GPP TS 38.322 v16.2.0 Section 6.2.2.5 + ***************************************************************************/ + uint32_t rlc_am_nr_read_status_pdu(const byte_buffer_t* pdu, const rlc_am_nr_sn_size_t sn_size, rlc_am_nr_status_pdu_t* status) { @@ -151,6 +156,16 @@ uint32_t rlc_am_nr_read_status_pdu(const uint8_t* payload, const uint32_t nof_bytes, const rlc_am_nr_sn_size_t sn_size, rlc_am_nr_status_pdu_t* status) +{ + if (sn_size == rlc_am_nr_sn_size_t::size12bits) { + return rlc_am_nr_read_status_pdu_12bit_sn(payload, nof_bytes, status); + } else { // 18bit SN + return rlc_am_nr_read_status_pdu_18bit_sn(payload, nof_bytes, status); + } +} + +uint32_t +rlc_am_nr_read_status_pdu_12bit_sn(const uint8_t* payload, const uint32_t nof_bytes, rlc_am_nr_status_pdu_t* status) { uint8_t* ptr = const_cast(payload); @@ -163,68 +178,151 @@ uint32_t rlc_am_nr_read_status_pdu(const uint8_t* payload, return 0; } - if (sn_size == rlc_am_nr_sn_size_t::size12bits) { - status->ack_sn = (*ptr & 0x0F) << 8; // first 4 bits SN - ptr++; + status->ack_sn = (*ptr & 0x0F) << 8; // first 4 bits SN + ptr++; + + status->ack_sn |= (*ptr & 0xFF); // last 8 bits SN + ptr++; + + // read E1 flag + uint8_t e1 = *ptr & 0x80; + + // sanity check for reserved bits + if ((*ptr & 0x7f) != 0) { + fprintf(stderr, "Malformed PDU, reserved bits are set.\n"); + return 0; + } - status->ack_sn |= (*ptr & 0xFF); // last 8 bits SN + // all good, continue with next byte depending on E1 + ptr++; + + // reset number of acks + status->N_nack = 0; + + while (e1 != 0) { + // E1 flag set, read a NACK_SN + rlc_status_nack_t nack = {}; + nack.nack_sn = (*ptr & 0xff) << 4; ptr++; - // read E1 flag - uint8_t e1 = *ptr & 0x80; + e1 = *ptr & 0x08; // 1 = further NACKs follow + uint8_t e2 = *ptr & 0x04; // 1 = set of {so_start, so_end} follows + uint8_t e3 = *ptr & 0x02; // 1 = NACK range follows (i.e. NACK across multiple SNs) // sanity check for reserved bits - if ((*ptr & 0x7f) != 0) { + if ((*ptr & 0x01) != 0) { fprintf(stderr, "Malformed PDU, reserved bits are set.\n"); return 0; } + nack.nack_sn |= (*ptr & 0xF0) >> 4; + status->nacks[status->N_nack] = nack; - // all good, continue with next byte depending on E1 ptr++; + if (e2 != 0) { + status->nacks[status->N_nack].has_so = true; + status->nacks[status->N_nack].so_start = (*ptr) << 8; + ptr++; + status->nacks[status->N_nack].so_start |= (*ptr); + ptr++; + status->nacks[status->N_nack].so_end = (*ptr) << 8; + ptr++; + status->nacks[status->N_nack].so_end |= (*ptr); + ptr++; + } + if (e3 != 0) { + status->nacks[status->N_nack].has_nack_range = true; + status->nacks[status->N_nack].nack_range = (*ptr); + ptr++; + } + status->N_nack++; + if (uint32_t(ptr - payload) > nof_bytes) { + fprintf(stderr, "Malformed PDU, trying to read more bytes than it is available\n"); + return 0; + } + } - // reset number of acks - status->N_nack = 0; + return SRSRAN_SUCCESS; +} - while (e1 != 0) { - // E1 flag set, read a NACK_SN - rlc_status_nack_t nack = {}; - nack.nack_sn = (*ptr & 0xff) << 4; - ptr++; +uint32_t +rlc_am_nr_read_status_pdu_18bit_sn(const uint8_t* payload, const uint32_t nof_bytes, rlc_am_nr_status_pdu_t* status) +{ + uint8_t* ptr = const_cast(payload); - e1 = *ptr & 0x08; // 1 = further NACKs follow - uint8_t e2 = *ptr & 0x04; // 1 = set of {so_start, so_end} follows - uint8_t e3 = *ptr & 0x02; // 1 = NACK range follows (i.e. NACK across multiple SNs) + // fixed part + status->cpt = (rlc_am_nr_control_pdu_type_t)((*ptr >> 4) & 0x07); // 3 bits CPT - // sanity check for reserved bits - if ((*ptr & 0x01) != 0) { - fprintf(stderr, "Malformed PDU, reserved bits are set.\n"); - return 0; - } - nack.nack_sn |= (*ptr & 0xF0) >> 4; - status->nacks[status->N_nack] = nack; + // sanity check + if (status->cpt != rlc_am_nr_control_pdu_type_t::status_pdu) { + fprintf(stderr, "Malformed PDU, reserved bits are set.\n"); + return 0; + } + + status->ack_sn = (*ptr & 0x0F) << 14; // upper 4 bits of SN + ptr++; + + status->ack_sn |= (*ptr & 0xFF) << 6; // center 8 bits of SN + ptr++; + + status->ack_sn |= (*ptr & 0xFC) >> 2; // lower 6 bits of SN + + // read E1 flag + uint8_t e1 = *ptr & 0x02; + + // sanity check for reserved bits + if ((*ptr & 0x01) != 0) { + fprintf(stderr, "Malformed PDU, reserved bit is set.\n"); + return 0; + } + + // all good, continue with next byte depending on E1 + ptr++; + // reset number of acks + status->N_nack = 0; + + while (e1 != 0) { + // E1 flag set, read a NACK_SN + rlc_status_nack_t nack = {}; + + nack.nack_sn = (*ptr & 0xFF) << 10; // upper 8 bits of SN + ptr++; + nack.nack_sn |= (*ptr & 0xFF) << 2; // center 8 bits of SN + ptr++; + nack.nack_sn |= (*ptr & 0xC0) >> 6; // lower 2 bits of SN + + e1 = *ptr & 0x20; // 1 = further NACKs follow + uint8_t e2 = *ptr & 0x10; // 1 = set of {so_start, so_end} follows + uint8_t e3 = *ptr & 0x08; // 1 = NACK range follows (i.e. NACK across multiple SNs) + + // sanity check for reserved bits + if ((*ptr & 0x07) != 0) { + fprintf(stderr, "Malformed PDU, reserved bits are set.\n"); + return 0; + } + status->nacks[status->N_nack] = nack; + + ptr++; + if (e2 != 0) { + status->nacks[status->N_nack].has_so = true; + status->nacks[status->N_nack].so_start = (*ptr) << 8; + ptr++; + status->nacks[status->N_nack].so_start |= (*ptr); + ptr++; + status->nacks[status->N_nack].so_end = (*ptr) << 8; + ptr++; + status->nacks[status->N_nack].so_end |= (*ptr); + ptr++; + } + if (e3 != 0) { + status->nacks[status->N_nack].has_nack_range = true; + status->nacks[status->N_nack].nack_range = (*ptr); ptr++; - if (e2 != 0) { - status->nacks[status->N_nack].has_so = true; - status->nacks[status->N_nack].so_start = (*ptr) << 8; - ptr++; - status->nacks[status->N_nack].so_start |= (*ptr); - ptr++; - status->nacks[status->N_nack].so_end = (*ptr) << 8; - ptr++; - status->nacks[status->N_nack].so_end |= (*ptr); - ptr++; - } - if (e3 != 0) { - status->nacks[status->N_nack].has_nack_range = true; - status->nacks[status->N_nack].nack_range = (*ptr); - ptr++; - } - status->N_nack++; - if (uint32_t(ptr - payload) > nof_bytes) { - fprintf(stderr, "Malformed PDU, trying to read more bytes than it is available\n"); - return 0; - } + } + status->N_nack++; + if (uint32_t(ptr - payload) > nof_bytes) { + fprintf(stderr, "Malformed PDU, trying to read more bytes than it is available\n"); + return 0; } } @@ -240,75 +338,134 @@ uint32_t rlc_am_nr_read_status_pdu(const uint8_t* payload, int32_t rlc_am_nr_write_status_pdu(const rlc_am_nr_status_pdu_t& status_pdu, const rlc_am_nr_sn_size_t sn_size, byte_buffer_t* pdu) +{ + if (sn_size == rlc_am_nr_sn_size_t::size12bits) { + return rlc_am_nr_write_status_pdu_12bit_sn(status_pdu, pdu); + } else { // 18bit SN + return rlc_am_nr_write_status_pdu_18bit_sn(status_pdu, pdu); + } +} + +int32_t rlc_am_nr_write_status_pdu_12bit_sn(const rlc_am_nr_status_pdu_t& status_pdu, byte_buffer_t* pdu) { uint8_t* ptr = pdu->msg; // fixed header part *ptr = 0; ///< 1 bit D/C field and 3bit CPT are all zero - if (sn_size == rlc_am_nr_sn_size_t::size12bits) { - // write first 4 bit of ACK_SN - *ptr |= (status_pdu.ack_sn >> 8) & 0x0f; // 4 bit ACK_SN - ptr++; - *ptr = status_pdu.ack_sn & 0xff; // remaining 8 bit of SN - ptr++; + // write first 4 bit of ACK_SN + *ptr |= (status_pdu.ack_sn >> 8) & 0x0f; // 4 bit ACK_SN + ptr++; + *ptr = status_pdu.ack_sn & 0xff; // remaining 8 bit of SN + ptr++; - // write E1 flag in octet 3 - if (status_pdu.N_nack > 0) { - *ptr = 0x80; - } else { - *ptr = 0x00; - } - ptr++; + // write E1 flag in octet 3 + if (status_pdu.N_nack > 0) { + *ptr = 0x80; + } else { + *ptr = 0x00; + } + ptr++; + + if (status_pdu.N_nack > 0) { + for (uint32_t i = 0; i < status_pdu.N_nack; i++) { + // write first 8 bit of NACK_SN + *ptr = (status_pdu.nacks[i].nack_sn >> 4) & 0xff; + ptr++; + + // write remaining 4 bits of NACK_SN + *ptr = (status_pdu.nacks[i].nack_sn & 0x0f) << 4; + // Set E1 if necessary + if (i < (uint32_t)(status_pdu.N_nack - 1)) { + *ptr |= 0x08; + } - if (status_pdu.N_nack > 0) { - for (uint32_t i = 0; i < status_pdu.N_nack; i++) { - // write first 8 bit of NACK_SN - *ptr = (status_pdu.nacks[i].nack_sn >> 4) & 0xff; + if (status_pdu.nacks[i].has_so) { + // Set E2 + *ptr |= 0x04; + } + + if (status_pdu.nacks[i].has_nack_range) { + // Set E3 + *ptr |= 0x02; + } + + ptr++; + if (status_pdu.nacks[i].has_so) { + (*ptr) = status_pdu.nacks[i].so_start >> 8; + ptr++; + (*ptr) = status_pdu.nacks[i].so_start; + ptr++; + (*ptr) = status_pdu.nacks[i].so_end >> 8; + ptr++; + (*ptr) = status_pdu.nacks[i].so_end; ptr++; + } + if (status_pdu.nacks[i].has_nack_range) { + (*ptr) = status_pdu.nacks[i].nack_range; + ptr++; + } + } + } + + pdu->N_bytes = ptr - pdu->msg; + + return SRSRAN_SUCCESS; +} + +int32_t rlc_am_nr_write_status_pdu_18bit_sn(const rlc_am_nr_status_pdu_t& status_pdu, byte_buffer_t* pdu) +{ + uint8_t* ptr = pdu->msg; + + // fixed header part + *ptr = 0; ///< 1 bit D/C field and 3bit CPT are all zero + + *ptr |= (status_pdu.ack_sn >> 14) & 0x0F; // upper 4 bits of SN + ptr++; + *ptr = (status_pdu.ack_sn >> 6) & 0xFF; // center 8 bits of SN + ptr++; + *ptr = (status_pdu.ack_sn << 2) & 0xFC; // lower 6 bits of SN - // write remaining 4 bits of NACK_SN - *ptr = (status_pdu.nacks[i].nack_sn & 0x0f) << 4; - // Set E1 if necessary - if (i < (uint32_t)(status_pdu.N_nack - 1)) { - *ptr |= 0x08; - } + // set E1 flag if necessary + if (status_pdu.N_nack > 0) { + *ptr |= 0x02; + } + ptr++; - if (status_pdu.nacks[i].has_so) { - // Set E2 - *ptr |= 0x04; - } + if (status_pdu.N_nack > 0) { + for (uint32_t i = 0; i < status_pdu.N_nack; i++) { + *ptr = (status_pdu.nacks[i].nack_sn >> 10) & 0xFF; // upper 8 bits of SN + ptr++; + *ptr = (status_pdu.nacks[i].nack_sn >> 2) & 0xFF; // center 8 bits of SN + ptr++; + *ptr = (status_pdu.nacks[i].nack_sn << 6) & 0xC0; // lower 2 bits of SN - if (status_pdu.nacks[i].has_nack_range) { - // Set E3 - *ptr |= 0x02; - } + if (i < (uint32_t)(status_pdu.N_nack - 1)) { + *ptr |= 0x20; // Set E1 + } + if (status_pdu.nacks[i].has_so) { + *ptr |= 0x10; // Set E2 + } + if (status_pdu.nacks[i].has_nack_range) { + *ptr |= 0x08; // Set E3 + } + ptr++; + if (status_pdu.nacks[i].has_so) { + (*ptr) = status_pdu.nacks[i].so_start >> 8; + ptr++; + (*ptr) = status_pdu.nacks[i].so_start; + ptr++; + (*ptr) = status_pdu.nacks[i].so_end >> 8; + ptr++; + (*ptr) = status_pdu.nacks[i].so_end; + ptr++; + } + if (status_pdu.nacks[i].has_nack_range) { + (*ptr) = status_pdu.nacks[i].nack_range; ptr++; - if (status_pdu.nacks[i].has_so) { - (*ptr) = status_pdu.nacks[i].so_start >> 8; - ptr++; - (*ptr) = status_pdu.nacks[i].so_start; - ptr++; - (*ptr) = status_pdu.nacks[i].so_end >> 8; - ptr++; - (*ptr) = status_pdu.nacks[i].so_end; - ptr++; - } - if (status_pdu.nacks[i].has_nack_range) { - (*ptr) = status_pdu.nacks[i].nack_range; - ptr++; - } } } - } else { - // 18bit SN - *ptr |= (status_pdu.ack_sn >> 14) & 0x0f; // 4 bit ACK_SN - ptr++; - *ptr = status_pdu.ack_sn >> 8; // bit 3 - 10 of SN - ptr++; - *ptr = (status_pdu.ack_sn & 0xff); // remaining 6 bit of SN - ptr++; } pdu->N_bytes = ptr - pdu->msg; diff --git a/lib/test/rlc/rlc_am_nr_pdu_test.cc b/lib/test/rlc/rlc_am_nr_pdu_test.cc index dbeed0423..00c9d9861 100644 --- a/lib/test/rlc/rlc_am_nr_pdu_test.cc +++ b/lib/test/rlc/rlc_am_nr_pdu_test.cc @@ -221,11 +221,11 @@ int rlc_am_nr_pdu_test6() return SRSRAN_SUCCESS; } -///< Control PDU tests +///< Control PDU tests (12bit SN) // Status PDU for 12bit SN with ACK_SN=2065 and no further NACK_SN (E1 bit not set) -int rlc_am_nr_control_pdu_test1() +int rlc_am_nr_control_pdu_12bit_sn_test1() { - test_delimit_logger delimiter("Control PDU test 1"); + test_delimit_logger delimiter("Control PDU (12bit SN) test 1"); const int len = 3; std::array tv = {0x08, 0x11, 0x00}; srsran::byte_buffer_t pdu = make_pdu_and_log(tv); @@ -253,9 +253,9 @@ int rlc_am_nr_control_pdu_test1() } // Status PDU for 12bit SN with ACK_SN=2065 and NACK_SN=273 (E1 bit set) -int rlc_am_nr_control_pdu_test2() +int rlc_am_nr_control_pdu_12bit_sn_test2() { - test_delimit_logger delimiter("Control PDU test 2"); + test_delimit_logger delimiter("Control PDU (12bit SN) test 2"); const int len = 5; std::array tv = {0x08, 0x11, 0x80, 0x11, 0x10}; srsran::byte_buffer_t pdu = make_pdu_and_log(tv); @@ -274,7 +274,7 @@ int rlc_am_nr_control_pdu_test2() // pack again TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &pdu) == SRSRAN_SUCCESS); - // TESTASSERT(pdu.N_bytes == tv.size()); + TESTASSERT(pdu.N_bytes == tv.size()); write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); @@ -285,9 +285,9 @@ int rlc_am_nr_control_pdu_test2() // Status PDU for 12bit SN with ACK_SN=2065, NACK_SN=273, SO_START=2, SO_END=5, NACK_SN=275, SO_START=5, SO_END=0xFFFF // E1 and E2 bit set on first NACK, only E2 on second. -int rlc_am_nr_control_pdu_test3() +int rlc_am_nr_control_pdu_12bit_sn_test3() { - test_delimit_logger delimiter("Control PDU test 3"); + test_delimit_logger delimiter("Control PDU (12bit SN) test 3"); const int len = 15; std::array tv = { 0x08, 0x11, 0x80, 0x11, 0x1c, 0x00, 0x02, 0x00, 0x05, 0x11, 0x34, 0x00, 0x05, 0xFF, 0xFF}; @@ -322,9 +322,9 @@ int rlc_am_nr_control_pdu_test3() // Status PDU for 12bit SN with ACK_SN=2065, NACK_SN=273, SO_START=2, SO_END=5, NACK_SN=275 // E1 and E2 bit set on first NACK, neither E1 or E2 on the second. -int rlc_am_nr_control_pdu_test4() +int rlc_am_nr_control_pdu_12bit_sn_test4() { - test_delimit_logger delimiter("Control PDU test 4"); + test_delimit_logger delimiter("Control PDU (12bit SN) test 4"); const int len = 11; std::array tv = {0x08, 0x11, 0x80, 0x11, 0x1c, 0x00, 0x02, 0x00, 0x05, 0x11, 0x30}; srsran::byte_buffer_t pdu = make_pdu_and_log(tv); @@ -359,9 +359,9 @@ int rlc_am_nr_control_pdu_test4() // Malformed Status PDU, with E1 still set at the end of the PDU // 12bit SN with ACK_SN=2065, NACK_SN=273, SO_START=2, SO_END=5, NACK_SN=275, SO_START=5, SO_END=0xFFFF // E1 and E2 bit set on first NACK, only E2 on second. -int rlc_am_nr_control_pdu_test5() +int rlc_am_nr_control_pdu_12bit_sn_test5() { - test_delimit_logger delimiter("Control PDU test 5"); + test_delimit_logger delimiter("Control PDU (12bit SN) test 5"); const int len = 15; std::array tv = { 0x08, 0x11, 0x80, 0x11, 0x1c, 0x00, 0x02, 0x00, 0x05, 0x11, 0x3c, 0x00, 0x05, 0xFF, 0xFF}; @@ -382,23 +382,23 @@ int rlc_am_nr_control_pdu_test5() // starting at NACK_SN=276, SO_START=2, // ending at NACK_SN=279, SO_END=5 // E1 and E3 bit set on first NACK, E2 and E3 bit set on the second. -int rlc_am_nr_control_pdu_test_nack_range() +int rlc_am_nr_control_pdu_12bit_sn_test_nack_range() { - test_delimit_logger delimiter("Control PDU test NACK range"); + test_delimit_logger delimiter("Control PDU (12bit SN) test NACK range"); const int len = 13; - std::array tv = {0x08, // D/C | CPT | ACK_SN_upper - 0x11, // ACK_SN_lower - 0x80, // E1 | R - 0x11, // NACK_SN_upper - 0x1a, // NACK_SN_lower | E1 | E2 | E3 | R - 0x03, // NACK_range - 0x11, // NACK_SN_upper - 0x46, // NACK_SN_lower | E1 | E2 | E3 | R - 0x00, // SO_START_upper - 0x02, // SO_START_lower - 0x00, // SO_END_upper - 0x05, // SO_END_lower - 0x04}; // NACK_range + std::array tv = {0x08, // D/C | 3CPT | 4ACK_SN_upper + 0x11, // 8ACK_SN_lower + 0x80, // E1 | 7R + 0x11, // 8NACK_SN_upper + 0x1a, // 4NACK_SN_lower | E1 | E2 | E3 | R + 0x03, // 8NACK_range + 0x11, // 8NACK_SN_upper + 0x46, // 4NACK_SN_lower | E1 | E2 | E3 | R + 0x00, // 8SO_START_upper + 0x02, // 8SO_START_lower + 0x00, // 8SO_END_upper + 0x05, // 8SO_END_lower + 0x04}; // 8NACK_range srsran::byte_buffer_t pdu = make_pdu_and_log(tv); TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); @@ -433,6 +433,273 @@ int rlc_am_nr_control_pdu_test_nack_range() return SRSRAN_SUCCESS; } +///< Control PDU tests (18bit SN) +// Status PDU for 18bit SN with ACK_SN=235929=0x39999=0b11 1001 1001 1001 1001 and no further NACK_SN (E1 bit not set) +int rlc_am_nr_control_pdu_18bit_sn_test1() +{ + test_delimit_logger delimiter("Control PDU (18bit SN) test 1"); + const int len = 3; + std::array tv = {0x0E, 0x66, 0x64}; + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); + + // unpack PDU + rlc_am_nr_status_pdu_t status_pdu = {}; + TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == SRSRAN_SUCCESS); + TESTASSERT(status_pdu.ack_sn == 235929); + TESTASSERT(status_pdu.N_nack == 0); + + // reset status PDU + pdu.clear(); + + // pack again + TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &pdu) == SRSRAN_SUCCESS); + TESTASSERT(pdu.N_bytes == tv.size()); + + write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); + + TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); + + return SRSRAN_SUCCESS; +} + +// Status PDU for 18bit SN with ACK_SN=235929=0x39999=0b11 1001 1001 1001 1001 (E1 bit set) +// and NACK_SN=222822=0x36666=0b11 0110 0110 0110 0110 +int rlc_am_nr_control_pdu_18bit_sn_test2() +{ + test_delimit_logger delimiter("Control PDU (18bit SN) test 2"); + const int len = 6; + std::array tv = {0x0E, 0x66, 0x66, 0xD9, 0x99, 0x80}; + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); + + // unpack PDU + rlc_am_nr_status_pdu_t status_pdu = {}; + TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == SRSRAN_SUCCESS); + TESTASSERT(status_pdu.ack_sn == 235929); + TESTASSERT(status_pdu.N_nack == 1); + TESTASSERT(status_pdu.nacks[0].nack_sn == 222822); + + // reset status PDU + pdu.clear(); + + // pack again + TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &pdu) == SRSRAN_SUCCESS); + TESTASSERT(pdu.N_bytes == tv.size()); + + write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); + + TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); + + return SRSRAN_SUCCESS; +} + +// Status PDU for 18bit SN with ACK_SN=235929=0x39999=0b11 1001 1001 1001 1001 (E1 bit set), +// NACK_SN=222822=0x36666=0b11 0110 0110 0110 0110 (E1 and E2 bit set), +// SO_START=2, SO_END=5, +// NACK_SN=222975=0x366ff=0b11 0110 0110 1111 1111 (E2 bit set), +// SO_START=5, SO_END=0xFFFF +int rlc_am_nr_control_pdu_18bit_sn_test3() +{ + test_delimit_logger delimiter("Control PDU (18bit SN) test 3"); + const int len = 17; + std::array tv = {0b00001110, // D/C | 3CPT | 4ACK_SN_upper + 0b01100110, // 8ACK_SN_center + 0b01100110, // 6ACK_SN_lower | E1 | R + 0b11011001, // 8NACK_SN_upper + 0b10011001, // 8NACK_SN_center + 0b10110000, // 2NACK_SN_lower | E1 | E2 | E3 | 3R + 0x00, // 8SO_START_upper + 0x02, // 8SO_START_lower + 0x00, // 8SO_END_upper + 0x05, // 8SO_END_lower + 0b11011001, // 8NACK_SN_upper + 0b10111111, // 8NACK_SN_center + 0b11010000, // 2NACK_SN_lower | E1 | E2 | E3 | 3R + 0x00, // 8SO_START_upper + 0x05, // 8SO_START_lower + 0xFF, // 8SO_END_upper + 0xFF}; // 8SO_END_lower + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); + + // unpack PDU + rlc_am_nr_status_pdu_t status_pdu = {}; + TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == SRSRAN_SUCCESS); + TESTASSERT(status_pdu.ack_sn == 235929); + TESTASSERT(status_pdu.N_nack == 2); + TESTASSERT(status_pdu.nacks[0].nack_sn == 222822); + TESTASSERT(status_pdu.nacks[0].has_so == true); + TESTASSERT(status_pdu.nacks[0].so_start == 2); + TESTASSERT(status_pdu.nacks[0].so_end == 5); + TESTASSERT(status_pdu.nacks[1].nack_sn == 222975); + TESTASSERT(status_pdu.nacks[1].has_so == true); + TESTASSERT(status_pdu.nacks[1].so_start == 5); + TESTASSERT(status_pdu.nacks[1].so_end == 0xFFFF); + + // reset status PDU + pdu.clear(); + + // pack again + TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &pdu) == SRSRAN_SUCCESS); + TESTASSERT(pdu.N_bytes == tv.size()); + + write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); + TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); + + return SRSRAN_SUCCESS; +} + +// Status PDU for 18bit SN with ACK_SN=235929=0x39999=0b11 1001 1001 1001 1001 (E1 bit set), +// NACK_SN=222822=0x36666=0b11 0110 0110 0110 0110 (E1 and E2 bit set), +// SO_START=2, SO_END=5, +// NACK_SN=222975=0x366ff=0b11 0110 0110 1111 1111 (E1 and E2 bit not set), +int rlc_am_nr_control_pdu_18bit_sn_test4() +{ + test_delimit_logger delimiter("Control PDU (18bit SN) test 4"); + const int len = 13; + std::array tv = {0b00001110, // D/C | 3CPT | 4ACK_SN_upper + 0b01100110, // 8ACK_SN_center + 0b01100110, // 6ACK_SN_lower | E1 | R + 0b11011001, // 8NACK_SN_upper + 0b10011001, // 8NACK_SN_center + 0b10110000, // 2NACK_SN_lower | E1 | E2 | E3 | 3R + 0x00, // 8SO_START_upper + 0x02, // 8SO_START_lower + 0x00, // 8SO_END_upper + 0x05, // 8SO_END_lower + 0b11011001, // 8NACK_SN_upper + 0b10111111, // 8NACK_SN_center + 0b11000000}; // 2NACK_SN_lower | E1 | E2 | E3 | 3R + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); + + // unpack PDU + rlc_am_nr_status_pdu_t status_pdu = {}; + TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == SRSRAN_SUCCESS); + TESTASSERT(status_pdu.ack_sn == 235929); + TESTASSERT(status_pdu.N_nack == 2); + TESTASSERT(status_pdu.nacks[0].nack_sn == 222822); + TESTASSERT(status_pdu.nacks[0].has_so == true); + TESTASSERT(status_pdu.nacks[0].so_start == 2); + TESTASSERT(status_pdu.nacks[0].so_end == 5); + TESTASSERT(status_pdu.nacks[1].nack_sn == 222975); + TESTASSERT(status_pdu.nacks[1].has_so == false); + + // reset status PDU + pdu.clear(); + + // pack again + TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &pdu) == SRSRAN_SUCCESS); + TESTASSERT(pdu.N_bytes == tv.size()); + + write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); + TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); + + return SRSRAN_SUCCESS; +} + +// Malformed Status PDU, similar to test3 but with E1 still set at the end of the PDU +// Status PDU for 18bit SN with ACK_SN=235929=0x39999=0b11 1001 1001 1001 1001 (E1 bit set), +// NACK_SN=222822=0x36666=0b11 0110 0110 0110 0110 (E1 and E2 bit set), +// SO_START=2, SO_END=5, +// NACK_SN=222975=0x366ff=0b11 0110 0110 1111 1111 ([!E1!] and E2 bit set), +// SO_START=5, SO_END=0xFFFF +int rlc_am_nr_control_pdu_18bit_sn_test5() +{ + test_delimit_logger delimiter("Control PDU (18bit SN) test 5"); + const int len = 17; + std::array tv = {0b00001110, // D/C | 3CPT | 4ACK_SN_upper + 0b01100110, // 8ACK_SN_center + 0b01100110, // 6ACK_SN_lower | E1 | R + 0b11011001, // 8NACK_SN_upper + 0b10011001, // 8NACK_SN_center + 0b10110000, // 2NACK_SN_lower | E1 | E2 | E3 | 3R + 0x00, // 8SO_START_upper + 0x02, // 8SO_START_lower + 0x00, // 8SO_END_upper + 0x05, // 8SO_END_lower + 0b11011001, // 8NACK_SN_upper + 0b10111111, // 8NACK_SN_center + 0b11110000, // 2NACK_SN_lower | [!E1!] | E2 | E3 | 3R + 0x00, // 8SO_START_upper + 0x05, // 8SO_START_lower + 0xFF, // 8SO_END_upper + 0xFF}; // 8SO_END_lower + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); + + // unpack PDU + rlc_am_nr_status_pdu_t status_pdu = {}; + TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == 0); + + return SRSRAN_SUCCESS; +} + +// Status PDU for 18bit SN with ACK_SN=200977=0x31111=0b11 0001 0001 0001 0001, +// NACK range0: 3 full SDUs, NACK_SN=69905=0x11111=0b01 0001 0001 0001 0001 +// NACK range1: missing segment sequence across 4 SDUs +// starting at NACK_SN=69913=0x11119=0b01 0001 0001 0001 1001, SO_START=2, +// ending at NACK_SN=69916, SO_END=5 +// E1 and E3 bit set on first NACK, E2 and E3 bit set on the second. +int rlc_am_nr_control_pdu_18bit_sn_test_nack_range() +{ + test_delimit_logger delimiter("Control PDU (18bit SN) test NACK range"); + const int len = 15; + std::array tv = {0b00001100, // D/C | 3CPT | 4ACK_SN_upper + 0b01000100, // 8ACK_SN_center + 0b01000110, // 6ACK_SN_lower | E1 | R + 0b01000100, // 8NACK_SN_upper + 0b01000100, // 8NACK_SN_center + 0b01101000, // 2NACK_SN_lower | E1 | E2 | E3 | 3R + 0x03, // 8NACK_range + 0b01000100, // 8NACK_SN_upper + 0b01000110, // 8NACK_SN_center + 0b01011000, // 2NACK_SN_lower | E1 | E2 | E3 | 3R + 0x00, // 8SO_START_upper + 0x02, // 8SO_START_lower + 0x00, // 8SO_END_upper + 0x05, // 8SO_END_lower + 0x04}; // 8NACK_range + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); + + // unpack PDU + rlc_am_nr_status_pdu_t status_pdu = {}; + TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == SRSRAN_SUCCESS); + TESTASSERT(status_pdu.ack_sn == 200977); + TESTASSERT(status_pdu.N_nack == 2); + TESTASSERT(status_pdu.nacks[0].nack_sn == 69905); + TESTASSERT(status_pdu.nacks[0].has_so == false); + TESTASSERT(status_pdu.nacks[0].has_nack_range == true); + TESTASSERT(status_pdu.nacks[0].nack_range == 3); + + TESTASSERT(status_pdu.nacks[1].nack_sn == 69913); + TESTASSERT(status_pdu.nacks[1].has_so == true); + TESTASSERT(status_pdu.nacks[1].so_start == 2); + TESTASSERT(status_pdu.nacks[1].so_end == 5); + TESTASSERT(status_pdu.nacks[1].has_nack_range == true); + TESTASSERT(status_pdu.nacks[1].nack_range == 4); + + // reset status PDU + pdu.clear(); + + // pack again + TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &pdu) == SRSRAN_SUCCESS); + TESTASSERT(pdu.N_bytes == tv.size()); + + write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); + TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); + + return SRSRAN_SUCCESS; +} + int main(int argc, char** argv) { static const struct option long_options[] = {{"pcap", no_argument, nullptr, 'p'}, {nullptr, 0, nullptr, 0}}; @@ -489,33 +756,63 @@ int main(int argc, char** argv) return SRSRAN_ERROR; } - if (rlc_am_nr_control_pdu_test1()) { - fprintf(stderr, "rlc_am_nr_control_pdu_test1() failed.\n"); + if (rlc_am_nr_control_pdu_12bit_sn_test1()) { + fprintf(stderr, "rlc_am_nr_control_pdu_12bit_sn_test1() failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_12bit_sn_test2()) { + fprintf(stderr, "rlc_am_nr_control_pdu_12bit_sn_test2() failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_12bit_sn_test3()) { + fprintf(stderr, "rlc_am_nr_control_pdu_12bit_sn_test3() failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_12bit_sn_test4()) { + fprintf(stderr, "rlc_am_nr_control_pdu_12bit_sn_test4() failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_12bit_sn_test5()) { + fprintf(stderr, "rlc_am_nr_control_pdu_12bit_sn_test5() failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_12bit_sn_test_nack_range()) { + fprintf(stderr, "rlc_am_nr_control_pdu_12bit_sn_test_nack_range() failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_18bit_sn_test1()) { + fprintf(stderr, "rlc_am_nr_control_pdu_18bit_sn_test1() failed.\n"); return SRSRAN_ERROR; } - if (rlc_am_nr_control_pdu_test2()) { - fprintf(stderr, "rlc_am_nr_control_pdu_test2() failed.\n"); + if (rlc_am_nr_control_pdu_18bit_sn_test2()) { + fprintf(stderr, "rlc_am_nr_control_pdu_18bit_sn_test2() failed.\n"); return SRSRAN_ERROR; } - if (rlc_am_nr_control_pdu_test3()) { - fprintf(stderr, "rlc_am_nr_control_pdu_test3() failed.\n"); + if (rlc_am_nr_control_pdu_18bit_sn_test3()) { + fprintf(stderr, "rlc_am_nr_control_pdu_18bit_sn_test3() failed.\n"); return SRSRAN_ERROR; } - if (rlc_am_nr_control_pdu_test4()) { - fprintf(stderr, "rlc_am_nr_control_pdu_test4() failed.\n"); + if (rlc_am_nr_control_pdu_18bit_sn_test4()) { + fprintf(stderr, "rlc_am_nr_control_pdu_18bit_sn_test4() failed.\n"); return SRSRAN_ERROR; } - if (rlc_am_nr_control_pdu_test5()) { - fprintf(stderr, "rlc_am_nr_control_pdu_test5() failed.\n"); + if (rlc_am_nr_control_pdu_18bit_sn_test5()) { + fprintf(stderr, "rlc_am_nr_control_pdu_18bit_sn_test5() failed.\n"); return SRSRAN_ERROR; } - if (rlc_am_nr_control_pdu_test_nack_range()) { - fprintf(stderr, "rlc_am_nr_control_pdu_test_nack_range() failed.\n"); + if (rlc_am_nr_control_pdu_18bit_sn_test_nack_range()) { + fprintf(stderr, "rlc_am_nr_control_pdu_18bit_sn_test_nack_range() failed.\n"); return SRSRAN_ERROR; } From e4d012388b96d147753e204d5c1a18ac37aca734 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Tue, 1 Mar 2022 09:35:37 +0100 Subject: [PATCH 033/195] phy,ctest: change anchor of relative paths used for test inputs Use ${CMAKE_CURRENT_SOURCE_DIR} instead of ${CMAKE_HOME_DIRECTORY} as anchor when specifying input files of tests. Otherwise input files won't be found, when this project (srsRAN) is not the root CMake project. --- lib/src/phy/phch/test/CMakeLists.txt | 32 ++++++++++++++-------------- lib/src/phy/sync/test/CMakeLists.txt | 26 +++++++++++----------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/lib/src/phy/phch/test/CMakeLists.txt b/lib/src/phy/phch/test/CMakeLists.txt index fdec173c3..10175e2a2 100644 --- a/lib/src/phy/phch/test/CMakeLists.txt +++ b/lib/src/phy/phch/test/CMakeLists.txt @@ -45,15 +45,15 @@ add_executable(psbch_file_test psbch_file_test.c) target_link_libraries(psbch_file_test srsran_phy) # TM2 file tests -add_lte_test(psbch_file_test_ideal_tm2_p6_c0 psbch_file_test -p 6 -c 0 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p6_c0_s1.92e6.dat) -add_lte_test(psbch_file_test_ideal_tm2_p15_c84 psbch_file_test -p 15 -c 84 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p15_c84_s3.84e6.dat) -add_lte_test(psbch_file_test_ideal_tm2_p25_c168 psbch_file_test -p 25 -c 168 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p25_c168_s7.68e6.dat) -add_lte_test(psbch_file_test_ideal_tm2_p50_c252 psbch_file_test -p 50 -c 252 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p50_c252_s15.36e6.dat) -add_lte_test(psbch_file_test_ideal_tm2_p100_c335 psbch_file_test -p 100 -c 335 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p100_c335_s30.72e6.dat) -add_lte_test(psbch_file_test_ideal_tm2_p50_c252_ext psbch_file_test -p 50 -c 252 -e -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p50_c252_s15.36e6_ext.dat) +add_lte_test(psbch_file_test_ideal_tm2_p6_c0 psbch_file_test -p 6 -c 0 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_ideal_tm2_p6_c0_s1.92e6.dat) +add_lte_test(psbch_file_test_ideal_tm2_p15_c84 psbch_file_test -p 15 -c 84 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_ideal_tm2_p15_c84_s3.84e6.dat) +add_lte_test(psbch_file_test_ideal_tm2_p25_c168 psbch_file_test -p 25 -c 168 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_ideal_tm2_p25_c168_s7.68e6.dat) +add_lte_test(psbch_file_test_ideal_tm2_p50_c252 psbch_file_test -p 50 -c 252 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_ideal_tm2_p50_c252_s15.36e6.dat) +add_lte_test(psbch_file_test_ideal_tm2_p100_c335 psbch_file_test -p 100 -c 335 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_ideal_tm2_p100_c335_s30.72e6.dat) +add_lte_test(psbch_file_test_ideal_tm2_p50_c252_ext psbch_file_test -p 50 -c 252 -e -d -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_ideal_tm2_p50_c252_s15.36e6_ext.dat) # TM4 file tests -add_lte_test(psbch_file_test_cmw_tm4_p50_c169 psbch_file_test -p 50 -c 169 -t 4 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_cmw500_f5.92e9_s11.52e6_50prb_slss_id169.dat) +add_lte_test(psbch_file_test_cmw_tm4_p50_c169 psbch_file_test -p 50 -c 169 -t 4 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_cmw500_f5.92e9_s11.52e6_50prb_slss_id169.dat) ######################################################################## # PSCCH TEST @@ -111,38 +111,38 @@ add_executable(pssch_pscch_file_test pssch_pscch_file_test.c) target_link_libraries(pssch_pscch_file_test srsran_phy) # TM2 file tests -add_lte_test(pssch_pscch_file_test_ideal_tm2_p100 pssch_pscch_file_test -p 100 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p100_c335_s30.72e6.dat) +add_lte_test(pssch_pscch_file_test_ideal_tm2_p100 pssch_pscch_file_test -p 100 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_ideal_tm2_p100_c335_s30.72e6.dat) set_property(TEST pssch_pscch_file_test_ideal_tm2_p100 PROPERTY PASS_REGULAR_EXPRESSION "num_decoded_sci=[2,3] num_decoded_tb=1") # TM4 file tests (first SF is sf_idx = 6 such that the PSSCH sf_idx=0) -add_lte_test(pssch_pscch_file_test_ideal_tm4_p100 pssch_pscch_file_test -p 100 -t 4 -s 10 -n 10 -d -m 6 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm4_p100_c335_size10_num10_cshift0_s30.72e6.dat) +add_lte_test(pssch_pscch_file_test_ideal_tm4_p100 pssch_pscch_file_test -p 100 -t 4 -s 10 -n 10 -d -m 6 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_ideal_tm4_p100_c335_size10_num10_cshift0_s30.72e6.dat) set_property(TEST pssch_pscch_file_test_ideal_tm4_p100 PROPERTY PASS_REGULAR_EXPRESSION "num_decoded_sci=1") -add_lte_test(pssch_pscch_test_tm4_p50_qc pssch_pscch_file_test -p 50 -t 4 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_qc9150_f5.92e9_s15.36e6_50prb_20offset.dat) +add_lte_test(pssch_pscch_test_tm4_p50_qc pssch_pscch_file_test -p 50 -t 4 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_qc9150_f5.92e9_s15.36e6_50prb_20offset.dat) set_property(TEST pssch_pscch_test_tm4_p50_qc PROPERTY PASS_REGULAR_EXPRESSION "num_decoded_sci=1 num_decoded_tb=1") # Capture has a SFO offset of ~64 samples, but offsetting by 20 is enough to decode it -add_lte_test(pssch_pscch_test_tm4_p50_cmw pssch_pscch_file_test -p 50 -t 4 -o 20 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_cmw500_f5.92e9_s11.52e6_50prb_0offset_1ms.dat) +add_lte_test(pssch_pscch_test_tm4_p50_cmw pssch_pscch_file_test -p 50 -t 4 -o 20 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_cmw500_f5.92e9_s11.52e6_50prb_0offset_1ms.dat) set_property(TEST pssch_pscch_test_tm4_p50_cmw PROPERTY PASS_REGULAR_EXPRESSION "num_decoded_sci=1 num_decoded_tb=1") # With PHY retransmission (3 TTI offset) first SF at sf_idx=5 -add_lte_test(pssch_pscch_test_tm4_p50_huawei pssch_pscch_file_test -p 50 -t 4 -m 5 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_huawei_s11.52e6_50prb_10prb_offset_with_retx.dat) +add_lte_test(pssch_pscch_test_tm4_p50_huawei pssch_pscch_file_test -p 50 -t 4 -m 5 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_huawei_s11.52e6_50prb_10prb_offset_with_retx.dat) set_property(TEST pssch_pscch_test_tm4_p50_huawei PROPERTY PASS_REGULAR_EXPRESSION "num_decoded_sci=2 num_decoded_tb=2") # With PHY ReTx (0 TTI offset?) -add_lte_test(pssch_pscch_test_tm4_p50_uxm1 pssch_pscch_file_test -p 50 -d -t 4 -s 5 -n 10 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_uxm_s15.36e6_50prb_0prb_offset_mcs12.dat) +add_lte_test(pssch_pscch_test_tm4_p50_uxm1 pssch_pscch_file_test -p 50 -d -t 4 -s 5 -n 10 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_uxm_s15.36e6_50prb_0prb_offset_mcs12.dat) set_property(TEST pssch_pscch_test_tm4_p50_uxm1 PROPERTY PASS_REGULAR_EXPRESSION "mcs=12.*num_decoded_sci=2 num_decoded_tb=2") # 100 PRB startOffset 1 MCS12 MAC padding, first SF is index 0 -add_lte_test(pssch_pscch_test_tm4_p100_uxm2 pssch_pscch_file_test -p 100 -t 4 -s 10 -n 10 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_uxm_s23.04e6_100prb_1prb_offset_mcs12_padding.dat) +add_lte_test(pssch_pscch_test_tm4_p100_uxm2 pssch_pscch_file_test -p 100 -t 4 -s 10 -n 10 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_uxm_s23.04e6_100prb_1prb_offset_mcs12_padding.dat) set_property(TEST pssch_pscch_test_tm4_p100_uxm2 PROPERTY PASS_REGULAR_EXPRESSION "mcs=12.*num_decoded_sci=4") # 100 PRB LTE sampling rate, startOffset1 MCS12 ITS data, first SF is index 6 -add_lte_test(pssch_pscch_test_tm4_p100_uxm3 pssch_pscch_file_test -p 100 -d -t 4 -s 10 -n 10 -m 6 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_uxm_s30.72e6_100prb_1prb_offset_mcs12_its.dat) +add_lte_test(pssch_pscch_test_tm4_p100_uxm3 pssch_pscch_file_test -p 100 -d -t 4 -s 10 -n 10 -m 6 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_uxm_s30.72e6_100prb_1prb_offset_mcs12_its.dat) set_property(TEST pssch_pscch_test_tm4_p100_uxm3 PROPERTY PASS_REGULAR_EXPRESSION "mcs=12.*num_decoded_sci=1") # 50 PRB LTE sampling rate, startOffset0 MCS28 MAC padding, first SF is index 1 -add_lte_test(pssch_pscch_test_tm4_p50_uxm4 pssch_pscch_file_test -p 50 -d -t 4 -s 5 -n 10 -m 1 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_uxm_s15.36e6_50prb_0prb_offset_mcs28_padding_5ms.dat) +add_lte_test(pssch_pscch_test_tm4_p50_uxm4 pssch_pscch_file_test -p 50 -d -t 4 -s 5 -n 10 -m 1 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_uxm_s15.36e6_50prb_0prb_offset_mcs28_padding_5ms.dat) set_property(TEST pssch_pscch_test_tm4_p50_uxm4 PROPERTY PASS_REGULAR_EXPRESSION "mcs=28.*num_decoded_sci=5") ######################################################################## diff --git a/lib/src/phy/sync/test/CMakeLists.txt b/lib/src/phy/sync/test/CMakeLists.txt index afde5fe7b..be963f09f 100644 --- a/lib/src/phy/sync/test/CMakeLists.txt +++ b/lib/src/phy/sync/test/CMakeLists.txt @@ -83,13 +83,13 @@ add_executable(psss_file_test psss_file_test.c) target_link_libraries(psss_file_test srsran_phy) # SL TM 2 -add_test(sync_sl_test_tm2_p6_c_0 sync_sl_test -p 6 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p6_c0_s1.92e6.dat) -add_test(sync_sl_test_tm2_p15_c_84 sync_sl_test -p 15 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p15_c84_s3.84e6.dat) -add_test(sync_sl_test_tm2_p25_c_168 sync_sl_test -p 25 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p25_c168_s7.68e6.dat) -add_test(sync_sl_test_tm2_p50_c_252 sync_sl_test -p 50 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p50_c252_s15.36e6.dat) -add_test(sync_sl_test_tm2_p100_c_335 sync_sl_test -p 100 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p100_c335_s30.72e6.dat) +add_test(sync_sl_test_tm2_p6_c_0 sync_sl_test -p 6 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm2_p6_c0_s1.92e6.dat) +add_test(sync_sl_test_tm2_p15_c_84 sync_sl_test -p 15 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm2_p15_c84_s3.84e6.dat) +add_test(sync_sl_test_tm2_p25_c_168 sync_sl_test -p 25 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm2_p25_c168_s7.68e6.dat) +add_test(sync_sl_test_tm2_p50_c_252 sync_sl_test -p 50 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm2_p50_c252_s15.36e6.dat) +add_test(sync_sl_test_tm2_p100_c_335 sync_sl_test -p 100 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm2_p100_c335_s30.72e6.dat) # Sample offset -add_test(sync_sl_test_tm2_p25_c_168_so sync_sl_test -p 25 -d -o 300 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p25_c168_s7.68e6.dat) +add_test(sync_sl_test_tm2_p25_c_168_so sync_sl_test -p 25 -d -o 300 -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm2_p25_c168_s7.68e6.dat) # Self-test add_test(sync_sl_test_tm2_self_test_p25_c_168 sync_sl_test -p 25 -c 168 -d) # Self-test with frequency offset @@ -98,13 +98,13 @@ add_test(sync_sl_test_tm2_self_test_p25_c_168_fo sync_sl_test -p 25 -c 168 -d -f add_test(sync_sl_test_tm2_self_test_p25_c_168_fo_so sync_sl_test -p 25 -c 168 -d -f 100 -o 3600) # SL TM 4 -add_test(sync_sl_test_tm4_p6_c_0 sync_sl_test -p 6 -t 4 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm4_p6_c0_size6_num1_cshift0_s1.92e6.dat) -add_test(sync_sl_test_tm4_p15_c_84 sync_sl_test -p 15 -t 4 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm4_p15_c84_size5_num3_cshift0_s3.84e6.dat) -add_test(sync_sl_test_tm4_p25_c_168 sync_sl_test -p 25 -t 4 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm4_p25_c168_size5_num5_cshift0_s7.68e6.dat) -add_test(sync_sl_test_tm4_p50_c_252 sync_sl_test -p 50 -t 4 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm4_p50_c252_size10_num5_cshift0_s15.36e6.dat) -#add_test(sync_sl_test_tm4_p100_c_335 sync_sl_test -p 100 -t 4 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm4_p100_c335_size10_num10_cshift0_s30.72e6.dat) +add_test(sync_sl_test_tm4_p6_c_0 sync_sl_test -p 6 -t 4 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm4_p6_c0_size6_num1_cshift0_s1.92e6.dat) +add_test(sync_sl_test_tm4_p15_c_84 sync_sl_test -p 15 -t 4 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm4_p15_c84_size5_num3_cshift0_s3.84e6.dat) +add_test(sync_sl_test_tm4_p25_c_168 sync_sl_test -p 25 -t 4 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm4_p25_c168_size5_num5_cshift0_s7.68e6.dat) +add_test(sync_sl_test_tm4_p50_c_252 sync_sl_test -p 50 -t 4 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm4_p50_c252_size10_num5_cshift0_s15.36e6.dat) +#add_test(sync_sl_test_tm4_p100_c_335 sync_sl_test -p 100 -t 4 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm4_p100_c335_size10_num10_cshift0_s30.72e6.dat) # Sample offset -add_test(sync_sl_test_tm4_p25_c_168_so sync_sl_test -p 25 -t 4 -d -o 300 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm4_p25_c168_size5_num5_cshift0_s7.68e6.dat ) +add_test(sync_sl_test_tm4_p25_c_168_so sync_sl_test -p 25 -t 4 -d -o 300 -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm4_p25_c168_size5_num5_cshift0_s7.68e6.dat ) # Self-test add_test(sync_sl_test_self_test_tm4_p25_c_168 sync_sl_test -p 25 -t 4 -c 168 -d) # Self-test with frequency offset @@ -173,4 +173,4 @@ target_link_libraries(ssb_file_test srsran_phy) # Captured with command: lib/examples/usrp_capture -a type=x300,clock=external,sampling_rate=46.08e6,rx_subdev_spec=B:0 -g 20 -r 46.08e6 -n 460800 -f 3502.8e6 -o /tmp/n78.fo35028.fs2304M.data add_nr_test(ssb_file_test_tdd ssb_file_test -i ${CMAKE_CURRENT_SOURCE_DIR}/n78.fo35028.fs4608M.data -v -r 46.08e6 -f 3502.8e6 -F 3512.64e6 -n 460800 -A 500 357802 2 0 1 0) # Capture with third-party gNB on band n3 (FDD) 15kHz SSB SCS, f_s=15.36e6, f_c=1842.5e6, f_c_ssb=1842.05e6, PCI=500 -add_nr_test(ssb_file_test_fdd ssb_file_test -i ${CMAKE_CURRENT_SOURCE_DIR}/../../ue/test/ue_dl_nr_pci500_rb52_si_coreset0_idx6_s15.36e6.dat -v -r 15.36e6 -f 1842.5e6 -F 1842.05e6 -n 15360 -d fdd -s 15 -A 500 2200 0 0 0 0) \ No newline at end of file +add_nr_test(ssb_file_test_fdd ssb_file_test -i ${CMAKE_CURRENT_SOURCE_DIR}/../../ue/test/ue_dl_nr_pci500_rb52_si_coreset0_idx6_s15.36e6.dat -v -r 15.36e6 -f 1842.5e6 -F 1842.05e6 -n 15360 -d fdd -s 15 -A 500 2200 0 0 0 0) From 3daa43e7328549c165cf2158d0de15fadc3663c4 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Fri, 4 Mar 2022 06:49:46 +0100 Subject: [PATCH 034/195] srsran_rf: also build shared library when built without plugins --- lib/src/phy/rf/CMakeLists.txt | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/lib/src/phy/rf/CMakeLists.txt b/lib/src/phy/rf/CMakeLists.txt index 3abc51de0..24973fadd 100644 --- a/lib/src/phy/rf/CMakeLists.txt +++ b/lib/src/phy/rf/CMakeLists.txt @@ -118,25 +118,38 @@ if(RF_FOUND) # Top-level RF library add_library(srsran_rf_object OBJECT ${SOURCES_RF}) set_property(TARGET srsran_rf_object PROPERTY POSITION_INDEPENDENT_CODE 1) + set(TOP_RF_LIBS) if (ENABLE_RF_PLUGINS) + # Build as shared library with optional RF plugins (optional dependencies) if (DYNAMIC_PLUGINS) add_dependencies(srsran_rf_object ${DYNAMIC_PLUGINS}) endif (DYNAMIC_PLUGINS) add_library(srsran_rf SHARED $) target_link_libraries(srsran_rf dl) + list(APPEND TOP_RF_LIBS srsran_rf) + # Add $ORIGIN (i.e. current location of this library) to rpath of srsran_rf. # This ensures that it will find the plugins that reside in the same directory as the library set_target_properties(srsran_rf PROPERTIES BUILD_RPATH "\$ORIGIN/") set_target_properties(srsran_rf PROPERTIES INSTALL_RPATH "\$ORIGIN/") else (ENABLE_RF_PLUGINS) - # Without RF plugins, we aggregate everything in a static library (builtin plugins) + # Build as static library with built-in RF plugins (mandatory dependencies) add_library(srsran_rf STATIC $) target_link_libraries(srsran_rf ${STATIC_PLUGINS}) + list(APPEND TOP_RF_LIBS srsran_rf) + + # Also build as shared library with built-in RF plugins (mandatory dependencies) + add_library(srsran_rf_shared SHARED $) + target_link_libraries(srsran_rf_shared ${STATIC_PLUGINS}) + list(APPEND TOP_RF_LIBS srsran_rf_shared) + set_target_properties(srsran_rf_shared PROPERTIES OUTPUT_NAME srsran_rf) endif (ENABLE_RF_PLUGINS) - target_link_libraries(srsran_rf srsran_rf_utils srsran_phy) - set_target_properties(srsran_rf PROPERTIES VERSION ${SRSRAN_VERSION_STRING} SOVERSION ${SRSRAN_SOVERSION}) - install(TARGETS srsran_rf DESTINATION ${LIBRARY_DIR}) + foreach (TOP_RF_LIB ${TOP_RF_LIBS}) + target_link_libraries(${TOP_RF_LIB} srsran_rf_utils srsran_phy) + set_target_properties(${TOP_RF_LIB} PROPERTIES VERSION ${SRSRAN_VERSION_STRING} SOVERSION ${SRSRAN_SOVERSION}) + install(TARGETS ${TOP_RF_LIB} DESTINATION ${LIBRARY_DIR}) + endforeach () # Tests if (UHD_FOUND AND UHD_ENABLE_CUSTOM_RFNOC) From 7f6ca43e68cf902e8ff826eb9c9fd35938cfacb9 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Tue, 1 Mar 2022 15:08:28 +0100 Subject: [PATCH 035/195] cmake,MKL: fix cmake warning: (mkl) does not match the name of the calling package (MKL) --- cmake/modules/FindMKL.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/modules/FindMKL.cmake b/cmake/modules/FindMKL.cmake index 089c54c15..f64a1a9a9 100644 --- a/cmake/modules/FindMKL.cmake +++ b/cmake/modules/FindMKL.cmake @@ -49,7 +49,7 @@ set(MKL_INCLUDE_DIRS ${MKL_INCLUDE_DIR} ${MKL_FFTW_INCLUDE_DIR}) include(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set MKL_FOUND to TRUE # if all listed variables are TRUE -find_package_handle_standard_args(mkl DEFAULT_MSG +find_package_handle_standard_args(MKL DEFAULT_MSG MKL_LIBRARIES MKL_CORE MKL_ILP MKL_SEQ MKL_INCLUDE_DIRS) if(MKL_FOUND) From f7eb2a237b06248227cb01db0e143339493f0f92 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Tue, 1 Mar 2022 15:05:28 +0100 Subject: [PATCH 036/195] cmake,MKL: add default library search paths - /opt/intel/oneapi/mkl/latest (intel-oneapi-mkl 2022.0.2) - /opt/intel/mkl (intel-mkl 2020.4.304 on Archlinux) --- cmake/modules/FindMKL.cmake | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/cmake/modules/FindMKL.cmake b/cmake/modules/FindMKL.cmake index f64a1a9a9..42e3d9197 100644 --- a/cmake/modules/FindMKL.cmake +++ b/cmake/modules/FindMKL.cmake @@ -16,31 +16,45 @@ find_path(MKL_INCLUDE_DIR NAMES mkl.h HINTS $ENV{MKL_DIR}/include + /opt/intel/oneapi/mkl/latest/include + /opt/intel/mkl/include + /usr/include/mkl PATHS) find_path(MKL_FFTW_INCLUDE_DIR NAMES fftw3.h HINTS $ENV{MKL_DIR}/include/fftw + /opt/intel/oneapi/mkl/latest/include/fftw + /opt/intel/mkl/include/fftw + /usr/include/mkl/fftw PATHS) find_library(MKL_LIBRARIES NAMES mkl_rt HINTS $ENV{MKL_DIR}/lib/intel64 + /opt/intel/oneapi/mkl/latest/lib/intel64 + /opt/intel/mkl/lib/intel64 PATHS) find_library(MKL_CORE NAMES libmkl_core.a HINTS $ENV{MKL_DIR}/lib/intel64 + /opt/intel/oneapi/mkl/latest/lib/intel64/ + /opt/intel/mkl/lib/intel64 PATHS) find_library(MKL_ILP NAMES libmkl_intel_ilp64.a HINTS $ENV{MKL_DIR}/lib/intel64 + /opt/intel/oneapi/mkl/latest/lib/intel64/ + /opt/intel/mkl/lib/intel64 PATHS) find_library(MKL_SEQ NAMES libmkl_sequential.a HINTS $ENV{MKL_DIR}/lib/intel64 + /opt/intel/oneapi/mkl/latest/lib/intel64/ + /opt/intel/mkl/lib/intel64 PATHS) set(MKL_STATIC_LIBRARIES -Wl,--start-group ${MKL_CORE} ${MKL_ILP} ${MKL_SEQ} -Wl,--end-group -lpthread -lm -ldl) From 7410182c648fe944491fdc75e4136bede4208f99 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Tue, 1 Mar 2022 15:09:50 +0100 Subject: [PATCH 037/195] srsran_rf: fix warning of mismatching expressions in if() and endif() --- lib/src/phy/rf/CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/src/phy/rf/CMakeLists.txt b/lib/src/phy/rf/CMakeLists.txt index 24973fadd..d29d464f3 100644 --- a/lib/src/phy/rf/CMakeLists.txt +++ b/lib/src/phy/rf/CMakeLists.txt @@ -48,7 +48,7 @@ if(RF_FOUND) if (UHD_ENABLE_CUSTOM_RFNOC) add_definitions(-DUHD_ENABLE_CUSTOM_RFNOC) endif(UHD_ENABLE_CUSTOM_RFNOC) - endif (UHD_FOUND) + endif (UHD_FOUND AND ENABLE_UHD) if (BLADERF_FOUND AND ENABLE_BLADERF) add_definitions(-DENABLE_BLADERF) @@ -63,7 +63,7 @@ if(RF_FOUND) endif (ENABLE_RF_PLUGINS) target_link_libraries(srsran_rf_blade srsran_rf_utils srsran_phy ${BLADERF_LIBRARIES}) install(TARGETS srsran_rf_blade DESTINATION ${LIBRARY_DIR}) - endif (BLADERF_FOUND) + endif (BLADERF_FOUND AND ENABLE_BLADERF) if (SOAPYSDR_FOUND AND ENABLE_SOAPYSDR) add_definitions(-DENABLE_SOAPYSDR) @@ -95,7 +95,7 @@ if(RF_FOUND) endif (ENABLE_RF_PLUGINS) target_link_libraries(srsran_rf_skiq srsran_rf_utils srsran_phy ${SKIQ_LIBRARIES} rt) install(TARGETS srsran_rf_skiq DESTINATION ${LIBRARY_DIR}) - endif(SKIQ_FOUND) + endif(SKIQ_FOUND AND ENABLE_SKIQ) if (ZEROMQ_FOUND AND ENABLE_ZEROMQ) add_definitions(-DENABLE_ZEROMQ) @@ -110,7 +110,7 @@ if(RF_FOUND) endif (ENABLE_RF_PLUGINS) target_link_libraries(srsran_rf_zmq srsran_rf_utils srsran_phy ${ZEROMQ_LIBRARIES}) install(TARGETS srsran_rf_zmq DESTINATION ${LIBRARY_DIR}) - endif (ZEROMQ_FOUND) + endif (ZEROMQ_FOUND AND ENABLE_ZEROMQ) # Add sources of file-based RF directly to the RF library (not as a plugin) list(APPEND SOURCES_RF rf_file_imp.c rf_file_imp_tx.c rf_file_imp_rx.c) From 520128162ecf271c5622be19e71e26dd426cab79 Mon Sep 17 00:00:00 2001 From: Joaquim Broquetas Date: Mon, 7 Mar 2022 17:05:33 +0100 Subject: [PATCH 038/195] Add CFR to srsue LTE UL (#3865) lte,phy: Add CFR to srsue LTE UL This commit adds the configuration steps needed to enable and configure the CFR module for the srsue's uplink signal. Parsing of the CFR manual threshold has been streamlined. --- lib/examples/pdsch_enodeb.c | 17 +++--------- .../srsran/interfaces/ue_phy_interfaces.h | 11 ++++++++ lib/include/srsran/phy/cfr/cfr.h | 20 +++++++++++--- lib/include/srsran/phy/ue/ue_ul.h | 4 +++ lib/src/phy/cfr/cfr.c | 26 ++++++++++++++++++ lib/src/phy/cfr/test/cfr_test.c | 24 +++++++---------- lib/src/phy/dft/ofdm.c | 3 ++- lib/src/phy/ue/ue_ul.c | 19 +++++++++++++ srsenb/src/main.cc | 17 ++++-------- srsue/hdr/phy/phy_common.h | 4 +++ srsue/src/main.cc | 18 ++++++++++++- srsue/src/phy/lte/cc_worker.cc | 7 +++++ srsue/src/phy/phy_common.cc | 8 ++++++ srsue/src/ue.cc | 27 +++++++++++++++++++ srsue/ue.conf.example | 27 +++++++++++++++++++ 15 files changed, 187 insertions(+), 45 deletions(-) diff --git a/lib/examples/pdsch_enodeb.c b/lib/examples/pdsch_enodeb.c index 291ab1f61..f53400959 100644 --- a/lib/examples/pdsch_enodeb.c +++ b/lib/examples/pdsch_enodeb.c @@ -77,10 +77,6 @@ static bool enable_256qam = false; static float output_file_snr = +INFINITY; static bool use_standard_lte_rate = false; -// CFR type test args -static char cfr_manual_str[] = "manual"; -static char cfr_auto_cma_str[] = "auto_cma"; -static char cfr_auto_ema_str[] = "auto_ema"; // CFR runtime control flags static bool cfr_thr_inc = false; @@ -96,7 +92,7 @@ typedef struct { } cfr_args_t; static cfr_args_t cfr_args = {.enable = 0, - .mode = cfr_manual_str, + .mode = "manual", .manual_thres = 1.0f, .strength = 1.0f, .auto_target_papr = 8.0f, @@ -292,14 +288,9 @@ static int parse_cfr_args() cfr_config.alpha = cfr_args.strength; cfr_config.ema_alpha = cfr_args.ema_alpha; - if (!strcmp(cfr_args.mode, cfr_manual_str)) { - cfr_config.cfr_mode = SRSRAN_CFR_THR_MANUAL; - } else if (!strcmp(cfr_args.mode, cfr_auto_cma_str)) { - cfr_config.cfr_mode = SRSRAN_CFR_THR_AUTO_CMA; - } else if (!strcmp(cfr_args.mode, cfr_auto_ema_str)) { - cfr_config.cfr_mode = SRSRAN_CFR_THR_AUTO_EMA; - } else { - ERROR("CFR mode is not recognised"); + cfr_config.cfr_mode = srsran_cfr_str2mode(cfr_args.mode); + if (cfr_config.cfr_mode == SRSRAN_CFR_THR_INVALID) { + ERROR("CFR mode not recognised"); return SRSRAN_ERROR; } diff --git a/lib/include/srsran/interfaces/ue_phy_interfaces.h b/lib/include/srsran/interfaces/ue_phy_interfaces.h index 06dac7979..7dfaa22eb 100644 --- a/lib/include/srsran/interfaces/ue_phy_interfaces.h +++ b/lib/include/srsran/interfaces/ue_phy_interfaces.h @@ -26,6 +26,15 @@ namespace srsue { +struct cfr_args_t { + bool enable = false; + srsran_cfr_mode_t mode = SRSRAN_CFR_THR_MANUAL; + float manual_thres = 2.0f; + float strength = 1.0f; + float auto_target_papr = 7.0f; + float ema_alpha = 1.0f / (float)SRSRAN_CP_NORM_NSYMB; +}; + struct phy_args_t { std::string type = "lte"; srsran::phy_log_args_t log; @@ -96,6 +105,8 @@ struct phy_args_t { srsran::channel::args_t dl_channel_args; srsran::channel::args_t ul_channel_args; + + cfr_args_t cfr_args; ///< Stores user-defined CFR configuration }; /* RAT agnostic Interface MAC -> PHY */ diff --git a/lib/include/srsran/phy/cfr/cfr.h b/lib/include/srsran/phy/cfr/cfr.h index a53204de5..c3744c67d 100644 --- a/lib/include/srsran/phy/cfr/cfr.h +++ b/lib/include/srsran/phy/cfr/cfr.h @@ -20,12 +20,14 @@ #define CFR_EMA_INIT_AVG_PWR 0.1 /** - * @brief CFR manual threshold or PAPR limiting with Moving Average or EMA power averaging + * @brief CFR manual threshold or PAPR limiting with CMA or EMA power averaging */ typedef enum SRSRAN_API { - SRSRAN_CFR_THR_MANUAL = 1, - SRSRAN_CFR_THR_AUTO_CMA = 2, - SRSRAN_CFR_THR_AUTO_EMA = 3 + SRSRAN_CFR_THR_INVALID, + SRSRAN_CFR_THR_MANUAL, + SRSRAN_CFR_THR_AUTO_CMA, + SRSRAN_CFR_THR_AUTO_EMA, + SRSRAN_CFR_NOF_MODES } srsran_cfr_mode_t; /** @@ -115,4 +117,14 @@ SRSRAN_API int srsran_cfr_set_threshold(srsran_cfr_t* q, float thres); */ SRSRAN_API int srsran_cfr_set_papr(srsran_cfr_t* q, float papr); +/** + * @brief Converts a string representing a CFR mode from the config files into srsran_cfr_mode_t type + * + * @param[in] mode_str the cfr.mode string coming from the config file + * @return SRSRAN_CFR_THR_INVALID if mode_str is empty, + * SRSRAN_CFR_THR_INVALID if mode_str is not recognised, + * otherwise it returns the corresponding srsran_cfr_mode_t value. + */ +SRSRAN_API srsran_cfr_mode_t srsran_cfr_str2mode(const char* mode_str); + #endif // SRSRAN_CFR_H diff --git a/lib/include/srsran/phy/ue/ue_ul.h b/lib/include/srsran/phy/ue/ue_ul.h index bc8567af1..f1f01f4e8 100644 --- a/lib/include/srsran/phy/ue/ue_ul.h +++ b/lib/include/srsran/phy/ue/ue_ul.h @@ -99,6 +99,8 @@ typedef struct SRSRAN_API { srsran_ra_ul_pusch_hopping_t hopping; + srsran_cfr_cfg_t cfr_config; + cf_t* out_buffer; cf_t* refsignal; cf_t* srs_signal; @@ -112,6 +114,8 @@ SRSRAN_API void srsran_ue_ul_free(srsran_ue_ul_t* q); SRSRAN_API int srsran_ue_ul_set_cell(srsran_ue_ul_t* q, srsran_cell_t cell); +SRSRAN_API int srsran_ue_ul_set_cfr(srsran_ue_ul_t* q, const srsran_cfr_cfg_t* cfr); + SRSRAN_API int srsran_ue_ul_pregen_signals(srsran_ue_ul_t* q, srsran_ue_ul_cfg_t* cfg); SRSRAN_API int srsran_ue_ul_dci_to_pusch_grant(srsran_ue_ul_t* q, diff --git a/lib/src/phy/cfr/cfr.c b/lib/src/phy/cfr/cfr.c index 325c3cc2a..82c202716 100644 --- a/lib/src/phy/cfr/cfr.c +++ b/lib/src/phy/cfr/cfr.c @@ -153,6 +153,10 @@ int srsran_cfr_init(srsran_cfr_t* q, srsran_cfr_cfg_t* cfg) ERROR("Error, invalid configuration"); goto clean_exit; } + if (cfg->cfr_mode == SRSRAN_CFR_THR_INVALID) { + ERROR("Error, invalid CFR mode"); + goto clean_exit; + } if (cfg->cfr_mode == SRSRAN_CFR_THR_MANUAL && cfg->manual_thr <= 0) { ERROR("Error, invalid configuration for manual threshold"); goto clean_exit; @@ -321,6 +325,9 @@ bool srsran_cfr_params_valid(srsran_cfr_cfg_t* cfr_conf) if (cfr_conf == NULL) { return false; } + if (cfr_conf->cfr_mode == SRSRAN_CFR_THR_INVALID) { + return false; + } if (cfr_conf->alpha < 0 || cfr_conf->alpha > 1) { return false; } @@ -365,3 +372,22 @@ int srsran_cfr_set_papr(srsran_cfr_t* q, float papr) q->max_papr_lin = srsran_convert_dB_to_power(q->cfg.max_papr_db); return SRSRAN_SUCCESS; } + +srsran_cfr_mode_t srsran_cfr_str2mode(const char* mode_str) +{ + srsran_cfr_mode_t ret; + if (strcmp(mode_str, "")) { + if (!strcmp(mode_str, "manual")) { + ret = SRSRAN_CFR_THR_MANUAL; + } else if (!strcmp(mode_str, "auto_cma")) { + ret = SRSRAN_CFR_THR_AUTO_CMA; + } else if (!strcmp(mode_str, "auto_ema")) { + ret = SRSRAN_CFR_THR_AUTO_EMA; + } else { + ret = SRSRAN_CFR_THR_INVALID; // mode_str is not recognised + } + } else { + ret = SRSRAN_CFR_THR_INVALID; // mode_str is empty + } + return ret; +} diff --git a/lib/src/phy/cfr/test/cfr_test.c b/lib/src/phy/cfr/test/cfr_test.c index 368178457..5e9a758fc 100644 --- a/lib/src/phy/cfr/test/cfr_test.c +++ b/lib/src/phy/cfr/test/cfr_test.c @@ -21,13 +21,9 @@ #define MAX_ACPR_DB -100 -// CFR type test args -static char cfr_manual_str[] = "manual"; -static char cfr_auto_cma_str[] = "auto_cma"; -static char cfr_auto_ema_str[] = "auto_ema"; // Default CFR type -static char* cfr_type_arg = cfr_manual_str; +static char* cfr_mode_str = "manual"; static int nof_prb = -1; static srsran_cp_t cp = SRSRAN_CP_NORM; @@ -60,7 +56,7 @@ static void usage(char* prog) printf("\t-e extended cyclic prefix [Default Normal]\n"); printf("\t-f Number of frames [Default %d]\n", nof_frames); printf("\t-r Number of repetitions [Default %d]\n", nof_repetitions); - printf("\t-m CFR mode: %s, %s, %s [Default %s]\n", cfr_manual_str, cfr_auto_cma_str, cfr_auto_ema_str, cfr_type_arg); + printf("\t-m CFR mode: manual, auto_cma, auto_ema [Default %s]\n", cfr_mode_str); printf("\t-d Use DC subcarrier: [Default DC empty]\n"); printf("\t-a CFR alpha: [Default %.2f]\n", alpha); printf("\t-t CFR manual threshold: [Default %.2f]\n", thr_manual); @@ -89,7 +85,7 @@ static int parse_args(int argc, char** argv) nof_frames = (int)strtol(argv[optind], NULL, 10); break; case 'm': - cfr_type_arg = argv[optind]; + cfr_mode_str = argv[optind]; break; case 'a': alpha = strtof(argv[optind], NULL); @@ -140,13 +136,8 @@ int main(int argc, char** argv) goto clean_exit; } - if (!strcmp(cfr_type_arg, cfr_manual_str)) { - cfr_mode = SRSRAN_CFR_THR_MANUAL; - } else if (!strcmp(cfr_type_arg, cfr_auto_cma_str)) { - cfr_mode = SRSRAN_CFR_THR_AUTO_CMA; - } else if (!strcmp(cfr_type_arg, cfr_auto_ema_str)) { - cfr_mode = SRSRAN_CFR_THR_AUTO_EMA; - } else { + cfr_mode = srsran_cfr_str2mode(cfr_mode_str); + if (cfr_mode == SRSRAN_CFR_THR_INVALID) { ERROR("CFR mode is not recognised"); goto clean_exit; } @@ -193,6 +184,11 @@ int main(int argc, char** argv) cfr_tx_cfg.ema_alpha = ema_alpha; cfr_tx_cfg.dc_sc = dc_empty; + if (!srsran_cfr_params_valid(&cfr_tx_cfg)) { + ERROR("Invalid CFR configuration"); + goto clean_exit; + } + if (srsran_cfr_init(&cfr, &cfr_tx_cfg)) { ERROR("Error initializing CFR"); goto clean_exit; diff --git a/lib/src/phy/dft/ofdm.c b/lib/src/phy/dft/ofdm.c index fa349fb31..0e2315b6f 100644 --- a/lib/src/phy/dft/ofdm.c +++ b/lib/src/phy/dft/ofdm.c @@ -702,7 +702,8 @@ int srsran_ofdm_set_cfr(srsran_ofdm_t* q, srsran_cfr_cfg_t* cfr) q->cfg.cfr_tx_cfg.symbol_sz = q->cfg.symbol_sz; q->cfg.cfr_tx_cfg.symbol_bw = q->nof_re; - // in the DL, the DC carrier is empty but still counts when designing the filter BW + // in the LTE DL, the DC carrier is empty but still counts when designing the filter BW + // in the LTE UL, the DC carrier is used q->cfg.cfr_tx_cfg.dc_sc = (!q->cfg.keep_dc) && (!isnormal(q->cfg.freq_shift_f)); if (q->cfg.cfr_tx_cfg.cfr_enable) { if (srsran_cfr_init(&q->tx_cfr, &q->cfg.cfr_tx_cfg) < SRSRAN_SUCCESS) { diff --git a/lib/src/phy/ue/ue_ul.c b/lib/src/phy/ue/ue_ul.c index ea821a6f2..c71567506 100644 --- a/lib/src/phy/ue/ue_ul.c +++ b/lib/src/phy/ue/ue_ul.c @@ -165,6 +165,25 @@ int srsran_ue_ul_set_cell(srsran_ue_ul_t* q, srsran_cell_t cell) return ret; } +int srsran_ue_ul_set_cfr(srsran_ue_ul_t* q, const srsran_cfr_cfg_t* cfr) +{ + if (q == NULL || cfr == NULL) { + ERROR("Error, invalid inputs"); + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Copy the cfr config into the UE + q->cfr_config = *cfr; + + // Set the cfr for the fft's + if (srsran_ofdm_set_cfr(&q->fft, &q->cfr_config) < SRSRAN_SUCCESS) { + ERROR("Error setting the CFR for the fft"); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + int srsran_ue_ul_pregen_signals(srsran_ue_ul_t* q, srsran_ue_ul_cfg_t* cfg) { if (q->signals_pregenerated) { diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index 0cc0e0db9..2118e1048 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -378,18 +378,11 @@ void parse_args(all_args_t* args, int argc, char* argv[]) exit(1); } - // convert CFR mode - if (!cfr_mode.empty()) { - if (cfr_mode == "manual") { - args->phy.cfr_args.mode = SRSRAN_CFR_THR_MANUAL; - } else if (cfr_mode == "auto_cma") { - args->phy.cfr_args.mode = SRSRAN_CFR_THR_AUTO_CMA; - } else if (cfr_mode == "auto_ema") { - args->phy.cfr_args.mode = SRSRAN_CFR_THR_AUTO_EMA; - } else { - cout << "Error, invalid CFR mode: " << cfr_mode << endl; - exit(1); - } + // parse the CFR mode string + args->phy.cfr_args.mode = srsran_cfr_str2mode(cfr_mode.c_str()); + if (args->phy.cfr_args.mode == SRSRAN_CFR_THR_INVALID) { + cout << "Error, invalid CFR mode: " << cfr_mode << endl; + exit(1); } // Apply all_level to any unset layers diff --git a/srsue/hdr/phy/phy_common.h b/srsue/hdr/phy/phy_common.h index 167c607b5..6de05adec 100644 --- a/srsue/hdr/phy/phy_common.h +++ b/srsue/hdr/phy/phy_common.h @@ -306,6 +306,8 @@ public: } } + srsran_cfr_cfg_t get_cfr_config() { return cfr_config; } + private: std::mutex meas_mutex; @@ -323,6 +325,8 @@ private: std::array avg_noise = {}; std::array avg_rsrp_neigh = {}; + srsran_cfr_cfg_t cfr_config = {}; + static constexpr uint32_t pcell_report_period = 20; static constexpr uint32_t update_rxgain_period = 10; diff --git a/srsue/src/main.cc b/srsue/src/main.cc index 8d89be9b4..d6dbe5e25 100644 --- a/srsue/src/main.cc +++ b/srsue/src/main.cc @@ -59,8 +59,9 @@ string config_file; static int parse_args(all_args_t* args, int argc, char* argv[]) { - bool use_standard_lte_rates = false; + bool use_standard_lte_rates = false; std::string scs_khz, ssb_scs_khz; // temporary value to store integer + std::string cfr_mode; // Command line only options bpo::options_description general("General options"); @@ -239,6 +240,14 @@ static int parse_args(all_args_t* args, int argc, char* argv[]) ("channel.ul.hst.fd_hz", bpo::value(&args->phy.ul_channel_args.hst_fd_hz)->default_value(+750.0f), "Doppler frequency in Hz") ("channel.ul.hst.init_time_s", bpo::value(&args->phy.ul_channel_args.hst_init_time_s)->default_value(0), "Initial time in seconds") + /* CFR section */ + ("cfr.enable", bpo::value(&args->phy.cfr_args.enable)->default_value(args->phy.cfr_args.enable), "CFR enable") + ("cfr.mode", bpo::value(&cfr_mode)->default_value("manual"), "CFR mode") + ("cfr.manual_thres", bpo::value(&args->phy.cfr_args.manual_thres)->default_value(args->phy.cfr_args.manual_thres), "Fixed manual clipping threshold for CFR manual mode") + ("cfr.strength", bpo::value(&args->phy.cfr_args.strength)->default_value(args->phy.cfr_args.strength), "CFR ratio between amplitude-limited vs original signal (0 to 1)") + ("cfr.auto_target_papr", bpo::value(&args->phy.cfr_args.auto_target_papr)->default_value(args->phy.cfr_args.auto_target_papr), "Signal PAPR target (in dB) in CFR auto modes") + ("cfr.ema_alpha", bpo::value(&args->phy.cfr_args.ema_alpha)->default_value(args->phy.cfr_args.ema_alpha), "Alpha coefficient for the power average in auto_ema mode (0 to 1)") + /* PHY section */ ("phy.worker_cpu_mask", bpo::value(&args->phy.worker_cpu_mask)->default_value(-1), @@ -548,6 +557,13 @@ static int parse_args(all_args_t* args, int argc, char* argv[]) args->stack.usim.using_op = vm.count("usim.op"); } + // parse the CFR mode string + args->phy.cfr_args.mode = srsran_cfr_str2mode(cfr_mode.c_str()); + if (args->phy.cfr_args.mode == SRSRAN_CFR_THR_INVALID) { + cout << "Error, invalid CFR mode: " << cfr_mode << endl; + exit(1); + } + // Apply all_level to any unset layers if (vm.count("log.all_level")) { if (!vm.count("log.rf_level")) { diff --git a/srsue/src/phy/lte/cc_worker.cc b/srsue/src/phy/lte/cc_worker.cc index e06bbde39..26880884d 100644 --- a/srsue/src/phy/lte/cc_worker.cc +++ b/srsue/src/phy/lte/cc_worker.cc @@ -47,6 +47,8 @@ cc_worker::cc_worker(uint32_t cc_idx_, uint32_t max_prb, srsue::phy_common* phy_ cc_idx = cc_idx_; phy = phy_; + srsran_cfr_cfg_t cfr_config = phy->get_cfr_config(); + signal_buffer_max_samples = 3 * SRSRAN_SF_LEN_PRB(max_prb); for (uint32_t i = 0; i < phy->args->nof_rx_ant; i++) { @@ -72,6 +74,11 @@ cc_worker::cc_worker(uint32_t cc_idx_, uint32_t max_prb, srsue::phy_common* phy_ return; } + if (srsran_ue_ul_set_cfr(&ue_ul, &cfr_config) < SRSRAN_SUCCESS) { + Error("Setting the CFR"); + return; + } + phy->set_ue_dl_cfg(&ue_dl_cfg); phy->set_ue_ul_cfg(&ue_ul_cfg); phy->set_pdsch_cfg(&ue_dl_cfg.cfg.pdsch); diff --git a/srsue/src/phy/phy_common.cc b/srsue/src/phy/phy_common.cc index 653180e4d..0ca17c9ec 100644 --- a/srsue/src/phy/phy_common.cc +++ b/srsue/src/phy/phy_common.cc @@ -56,6 +56,14 @@ void phy_common::init(phy_args_t* _args, ul_channel = srsran::channel_ptr( new srsran::channel(args->ul_channel_args, args->nof_lte_carriers * args->nof_rx_ant, logger)); } + + // Init the CFR config struct with the CFR args + cfr_config.cfr_enable = args->cfr_args.enable; + cfr_config.cfr_mode = args->cfr_args.mode; + cfr_config.alpha = args->cfr_args.strength; + cfr_config.manual_thr = args->cfr_args.manual_thres; + cfr_config.max_papr_db = args->cfr_args.auto_target_papr; + cfr_config.ema_alpha = args->cfr_args.ema_alpha; } void phy_common::set_ue_dl_cfg(srsran_ue_dl_cfg_t* ue_dl_cfg) diff --git a/srsue/src/ue.cc b/srsue/src/ue.cc index 8e6f2709f..f51ee9e47 100644 --- a/srsue/src/ue.cc +++ b/srsue/src/ue.cc @@ -284,6 +284,33 @@ int ue::parse_args(const all_args_t& args_) args.stack.nas_5g.pdu_session_cfgs.push_back({args.stack.nas.apn_name}); } + // Validate the CFR args + srsran_cfr_cfg_t cfr_test_cfg = {}; + cfr_test_cfg.cfr_enable = args.phy.cfr_args.enable; + cfr_test_cfg.cfr_mode = args.phy.cfr_args.mode; + cfr_test_cfg.alpha = args.phy.cfr_args.strength; + cfr_test_cfg.manual_thr = args.phy.cfr_args.manual_thres; + cfr_test_cfg.max_papr_db = args.phy.cfr_args.auto_target_papr; + cfr_test_cfg.ema_alpha = args.phy.cfr_args.ema_alpha; + + if (!srsran_cfr_params_valid(&cfr_test_cfg)) { + srsran::console("Invalid CFR parameters: cfr_mode=%d, alpha=%.2f, manual_thr=%.2f, \n " + "max_papr_db=%.2f, ema_alpha=%.2f\n", + cfr_test_cfg.cfr_mode, + cfr_test_cfg.alpha, + cfr_test_cfg.manual_thr, + cfr_test_cfg.max_papr_db, + cfr_test_cfg.ema_alpha); + + logger.error("Invalid CFR parameters: cfr_mode=%d, alpha=%.2f, manual_thr=%.2f, max_papr_db=%.2f, ema_alpha=%.2f\n", + cfr_test_cfg.cfr_mode, + cfr_test_cfg.alpha, + cfr_test_cfg.manual_thr, + cfr_test_cfg.max_papr_db, + cfr_test_cfg.ema_alpha); + return SRSRAN_ERROR; + } + return SRSRAN_SUCCESS; } diff --git a/srsue/ue.conf.example b/srsue/ue.conf.example index 59f36dacd..746c45979 100644 --- a/srsue/ue.conf.example +++ b/srsue/ue.conf.example @@ -389,6 +389,33 @@ enable = false [phy.nr] #store_pdsch_ko = false +##################################################################### +# CFR configuration options +# +# The CFR module provides crest factor reduction for the transmitted signal. +# +# enable: Enable or disable the CFR. Default: disabled +# +# mode: manual: CFR threshold is set by cfr_manual_thres (default). +# auto_ema: CFR threshold is adaptive based on the signal PAPR. Power avg. with Exponential Moving Average. +# The time constant of the averaging can be tweaked with the ema_alpha parameter. +# auto_cma: CFR threshold is adaptive based on the signal PAPR. Power avg. with Cumulative Moving Average. +# Use with care, as CMA's increasingly slow response may be unsuitable for most use cases. +# +# strength: Ratio between amplitude-limited vs unprocessed signal (0 to 1). Default: 1 +# manual_thres: Fixed manual clipping threshold for CFR manual mode. Default: 2 +# auto_target_papr: Signal PAPR target (in dB) in CFR auto modes. output PAPR can be higher due to peak smoothing. Default: 7 +# ema_alpha: Alpha coefficient for the power average in auto_ema mode. Default: 1/7 +# +##################################################################### +[cfr] +#enable = false +#mode = manual +#manual_thres = 2.0 +#strength = 1.0 +#auto_target_papr = 7.0 +#ema_alpha = 0.0143 + ##################################################################### # Simulation configuration options # From 031b91a1807b3e693bea3e78f17e58e543f6af93 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Tue, 1 Mar 2022 12:23:59 +0000 Subject: [PATCH 039/195] ue,nas5g: fix using wrong serving network name in authentication request --- srsue/src/stack/upper/nas_5g.cc | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/srsue/src/stack/upper/nas_5g.cc b/srsue/src/stack/upper/nas_5g.cc index 6f98d1a7f..01c9fc668 100644 --- a/srsue/src/stack/upper/nas_5g.cc +++ b/srsue/src/stack/upper/nas_5g.cc @@ -836,11 +836,8 @@ int nas_5g::handle_authentication_request(authentication_request_t& authenticati logger.info("Handling Authentication Request"); ctxt_base.rx_count++; // Generate authentication response using RAND, AUTN & KSI-ASME - uint16 mcc, mnc; - mcc = rrc_nr->get_mcc(); - mnc = rrc_nr->get_mnc(); plmn_id_t plmn_id; - plmn_id.from_number(mcc, mnc); + usim->get_home_plmn_id(&plmn_id); if (authentication_request.authentication_parameter_rand_present == false) { logger.error("authentication_parameter_rand_present is not present"); @@ -1359,4 +1356,4 @@ int nas_5g::add_pdu_session(uint16_t pdu_session_id, return SRSRAN_SUCCESS; } -} // namespace srsue \ No newline at end of file +} // namespace srsue From 4886dc83404ccb7c833e3edddbc8b0317540a25c Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Fri, 25 Feb 2022 13:57:46 +0100 Subject: [PATCH 040/195] rlc, nr: NACK's so_end shall point to last missing byte and not to first byte received again. --- lib/src/rlc/rlc_am_nr.cc | 4 ++-- lib/test/rlc/rlc_am_nr_test.cc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index e8b92fab0..8eead146b 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -746,7 +746,7 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) for (std::list::iterator segm = pdu.segment_list.begin(); segm != pdu.segment_list.end(); segm++) { - if (segm->so >= nack.so_start && segm->so < nack.so_end) { + if (segm->so >= nack.so_start && segm->so <= nack.so_end) { // TODO: Check if this segment is not already queued for retransmission rlc_amd_retx_t& retx = retx_queue.push(); retx.sn = nack_sn; @@ -1230,7 +1230,7 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m status->nacks[status->N_nack].nack_sn = i; status->nacks[status->N_nack].has_so = true; status->nacks[status->N_nack].so_start = last_so; - status->nacks[status->N_nack].so_end = segm->header.so; + status->nacks[status->N_nack].so_end = segm->header.so - 1; // set to last missing byte status->N_nack++; } if (segm->header.si == rlc_nr_si_field_t::last_segment) { diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index ed513db5d..3b8a16be6 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -790,11 +790,11 @@ int retx_segment_test() TESTASSERT_EQ(1, status_check.nacks[0].nack_sn); // Lost SDU on SN=1. TESTASSERT_EQ(true, status_check.nacks[0].has_so); // Lost SDU on SN=1. TESTASSERT_EQ(0, status_check.nacks[0].so_start); // Lost SDU on SN=1. - TESTASSERT_EQ(1, status_check.nacks[0].so_end); // Lost SDU on SN=1. + TESTASSERT_EQ(0, status_check.nacks[0].so_end); // Lost SDU on SN=1. TESTASSERT_EQ(2, status_check.nacks[1].nack_sn); // Lost SDU on SN=1. TESTASSERT_EQ(true, status_check.nacks[1].has_so); // Lost SDU on SN=1. TESTASSERT_EQ(1, status_check.nacks[1].so_start); // Lost SDU on SN=1. - TESTASSERT_EQ(2, status_check.nacks[1].so_end); // Lost SDU on SN=1. + TESTASSERT_EQ(1, status_check.nacks[1].so_end); // Lost SDU on SN=1. TESTASSERT_EQ(3, status_check.nacks[2].nack_sn); // Lost SDU on SN=1. TESTASSERT_EQ(true, status_check.nacks[2].has_so); // Lost SDU on SN=1. TESTASSERT_EQ(2, status_check.nacks[2].so_start); // Lost SDU on SN=1. From bb7339910fbdc9b705dbbe6341d9a6da554e2f49 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Mon, 7 Mar 2022 15:01:57 +0100 Subject: [PATCH 041/195] rlc: separate types of rlc_amd_retx_t for LTE and NR --- lib/include/srsran/rlc/rlc_am_data_structs.h | 24 ++++++++----- lib/include/srsran/rlc/rlc_am_lte.h | 6 ++-- lib/include/srsran/rlc/rlc_am_lte_packing.h | 2 +- lib/include/srsran/rlc/rlc_am_nr.h | 10 +++--- lib/src/rlc/rlc_am_lte.cc | 20 +++++------ lib/src/rlc/rlc_am_nr.cc | 37 ++++++++++---------- lib/test/rlc/rlc_am_nr_test.cc | 36 +++++++++---------- 7 files changed, 72 insertions(+), 63 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_am_data_structs.h b/lib/include/srsran/rlc/rlc_am_data_structs.h index a9ce6cac5..f114a4a4d 100644 --- a/lib/include/srsran/rlc/rlc_am_data_structs.h +++ b/lib/include/srsran/rlc/rlc_am_data_structs.h @@ -300,7 +300,7 @@ private: uint32_t count = 0; }; -struct rlc_amd_retx_t { +struct rlc_amd_retx_lte_t { uint32_t sn; bool is_segment; uint32_t so_start; // offset to first byte of this segment @@ -308,21 +308,29 @@ struct rlc_amd_retx_t { uint32_t current_so; }; -template +struct rlc_amd_retx_nr_t { + uint32_t sn; + bool is_segment; + uint32_t so_start; // offset to first byte of this segment + uint32_t so_end; // offset to first byte beyond the end of this segment + uint32_t current_so; +}; + +template class pdu_retx_queue { public: - rlc_amd_retx_t& push() + T& push() { assert(not full()); - rlc_amd_retx_t& p = buffer[wpos]; + T& p = buffer[wpos]; wpos = (wpos + 1) % WINDOW_SIZE; return p; } void pop() { rpos = (rpos + 1) % WINDOW_SIZE; } - rlc_amd_retx_t& front() + T& front() { assert(not empty()); return buffer[rpos]; @@ -349,9 +357,9 @@ public: bool full() const { return size() == WINDOW_SIZE - 1; } private: - std::array buffer; - size_t wpos = 0; - size_t rpos = 0; + std::array buffer; + size_t wpos = 0; + size_t rpos = 0; }; } // namespace srsran diff --git a/lib/include/srsran/rlc/rlc_am_lte.h b/lib/include/srsran/rlc/rlc_am_lte.h index 90031dae8..f3500d48d 100644 --- a/lib/include/srsran/rlc/rlc_am_lte.h +++ b/lib/include/srsran/rlc/rlc_am_lte.h @@ -79,11 +79,11 @@ private: int build_status_pdu(uint8_t* payload, uint32_t nof_bytes); int build_retx_pdu(uint8_t* payload, uint32_t nof_bytes); - int build_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_retx_t retx); + int build_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_retx_lte_t retx); int build_data_pdu(uint8_t* payload, uint32_t nof_bytes); void update_notification_ack_info(uint32_t rlc_sn); - int required_buffer_size(const rlc_amd_retx_t& retx); + int required_buffer_size(const rlc_amd_retx_lte_t& retx); void retransmit_pdu(uint32_t sn); // Helpers @@ -137,7 +137,7 @@ private: // Tx windows rlc_ringbuffer_t, RLC_AM_WINDOW_SIZE> tx_window; - pdu_retx_queue retx_queue; + pdu_retx_queue retx_queue; pdcp_sn_vector_t notify_info_vec; // Mutexes diff --git a/lib/include/srsran/rlc/rlc_am_lte_packing.h b/lib/include/srsran/rlc/rlc_am_lte_packing.h index ea866bde8..6bb88d87e 100644 --- a/lib/include/srsran/rlc/rlc_am_lte_packing.h +++ b/lib/include/srsran/rlc/rlc_am_lte_packing.h @@ -53,7 +53,7 @@ int rlc_am_write_status_pdu(rlc_status_pdu_t* status, uint8_t* payload); uint32_t rlc_am_packed_length(rlc_amd_pdu_header_t* header); uint32_t rlc_am_packed_length(rlc_status_pdu_t* status); -uint32_t rlc_am_packed_length(rlc_amd_retx_t retx); +uint32_t rlc_am_packed_length(rlc_amd_retx_lte_t retx); bool rlc_am_is_pdu_segment(uint8_t* payload); bool rlc_am_is_valid_status_pdu(const rlc_status_pdu_t& status, uint32_t rx_win_min = 0); bool rlc_am_start_aligned(const uint8_t fi); diff --git a/lib/include/srsran/rlc/rlc_am_nr.h b/lib/include/srsran/rlc/rlc_am_nr.h index 09a31b44e..220d1a41e 100644 --- a/lib/include/srsran/rlc/rlc_am_nr.h +++ b/lib/include/srsran/rlc/rlc_am_nr.h @@ -107,10 +107,10 @@ public: uint32_t build_new_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, uint8_t* payload, uint32_t nof_bytes); uint32_t build_continuation_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, uint8_t* payload, uint32_t nof_bytes); uint32_t build_retx_pdu(uint8_t* payload, uint32_t nof_bytes); - uint32_t build_retx_pdu_without_segmentation(rlc_amd_retx_t& retx, uint8_t* payload, uint32_t nof_bytes); - uint32_t build_retx_pdu_with_segmentation(rlc_amd_retx_t& retx, uint8_t* payload, uint32_t nof_bytes); - bool is_retx_segmentation_required(const rlc_amd_retx_t& retx, uint32_t nof_bytes); - uint32_t get_retx_expected_hdr_len(const rlc_amd_retx_t& retx); + uint32_t build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8_t* payload, uint32_t nof_bytes); + uint32_t build_retx_pdu_with_segmentation(rlc_amd_retx_nr_t& retx, uint8_t* payload, uint32_t nof_bytes); + bool is_retx_segmentation_required(const rlc_amd_retx_nr_t& retx, uint32_t nof_bytes); + uint32_t get_retx_expected_hdr_len(const rlc_amd_retx_nr_t& retx); // Buffer State bool has_data() final; @@ -150,7 +150,7 @@ private: rlc_ringbuffer_t tx_window; // Queues and buffers - pdu_retx_queue retx_queue; + pdu_retx_queue retx_queue; uint32_t sdu_under_segmentation_sn = INVALID_RLC_SN; // SN of the SDU currently being segmented. pdcp_sn_vector_t notify_info_vec; diff --git a/lib/src/rlc/rlc_am_lte.cc b/lib/src/rlc/rlc_am_lte.cc index 097acc769..a7af7555a 100644 --- a/lib/src/rlc/rlc_am_lte.cc +++ b/lib/src/rlc/rlc_am_lte.cc @@ -223,7 +223,7 @@ void rlc_am_lte_tx::get_buffer_state_nolock(uint32_t& n_bytes_newtx, uint32_t& n // Bytes needed for retx if (not retx_queue.empty()) { - rlc_amd_retx_t& retx = retx_queue.front(); + rlc_amd_retx_lte_t& retx = retx_queue.front(); RlcDebug("Buffer state - retx - SN=%d, Segment: %s, %d:%d", retx.sn, retx.is_segment ? "true" : "false", @@ -353,11 +353,11 @@ void rlc_am_lte_tx::retransmit_pdu(uint32_t sn) RlcInfo("Schedule SN=%d for retx", pdu.rlc_sn); - rlc_amd_retx_t& retx = retx_queue.push(); - retx.is_segment = false; - retx.so_start = 0; - retx.so_end = pdu.buf->N_bytes; - retx.sn = pdu.rlc_sn; + rlc_amd_retx_lte_t& retx = retx_queue.push(); + retx.is_segment = false; + retx.so_start = 0; + retx.so_end = pdu.buf->N_bytes; + retx.sn = pdu.rlc_sn; } /**************************************************************************** @@ -443,7 +443,7 @@ int rlc_am_lte_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_bytes) return -1; } - rlc_amd_retx_t retx = retx_queue.front(); + rlc_amd_retx_lte_t retx = retx_queue.front(); // Sanity check - drop any retx SNs not present in tx_window while (not tx_window.has_sn(retx.sn)) { @@ -516,7 +516,7 @@ int rlc_am_lte_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_bytes) return (ptr - payload) + tx_window[retx.sn].buf->N_bytes; } -int rlc_am_lte_tx::build_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_retx_t retx) +int rlc_am_lte_tx::build_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_retx_lte_t retx) { if (tx_window[retx.sn].buf == NULL) { RlcError("In build_segment: retx.sn=%d has null buffer", retx.sn); @@ -956,7 +956,7 @@ void rlc_am_lte_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) pdu.retx_count++; check_sn_reached_max_retx(i); - rlc_amd_retx_t& retx = retx_queue.push(); + rlc_amd_retx_lte_t& retx = retx_queue.push(); srsran_expect(tx_window[i].rlc_sn == i, "Incorrect RLC SN=%d!=%d being accessed", tx_window[i].rlc_sn, i); retx.sn = i; retx.is_segment = false; @@ -1080,7 +1080,7 @@ void rlc_am_lte_tx::debug_state() RlcDebug("vt_a = %d, vt_ms = %d, vt_s = %d, poll_sn = %d", vt_a, vt_ms, vt_s, poll_sn); } -int rlc_am_lte_tx::required_buffer_size(const rlc_amd_retx_t& retx) +int rlc_am_lte_tx::required_buffer_size(const rlc_amd_retx_lte_t& retx) { if (!retx.is_segment) { if (tx_window.has_sn(retx.sn)) { diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 8eead146b..78e378bc7 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -404,7 +404,7 @@ uint32_t rlc_am_nr_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_bytes) return 0; } - rlc_amd_retx_t& retx = retx_queue.front(); + rlc_amd_retx_nr_t& retx = retx_queue.front(); // Sanity check - drop any retx SNs not present in tx_window while (not tx_window.has_sn(retx.sn)) { @@ -447,7 +447,8 @@ uint32_t rlc_am_nr_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_bytes) * \remark this function will not update the SI. This means that if the retx is of the last * SDU segment, the SI should already be of the `last_segment` type. */ -uint32_t rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_t& retx, uint8_t* payload, uint32_t nof_bytes) +uint32_t +rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8_t* payload, uint32_t nof_bytes) { srsran_assert(tx_window.has_sn(retx.sn), "Called %s without checking retx SN", __FUNCTION__); srsran_assert(not is_retx_segmentation_required(retx, nof_bytes), @@ -536,7 +537,7 @@ uint32_t rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_t& retx, * \returns the number of bytes written to the payload buffer. * \remark: This functions assumes that the SDU has already been copied to tx_pdu.sdu_buf. */ -uint32_t rlc_am_nr_tx::build_retx_pdu_with_segmentation(rlc_amd_retx_t& retx, uint8_t* payload, uint32_t nof_bytes) +uint32_t rlc_am_nr_tx::build_retx_pdu_with_segmentation(rlc_amd_retx_nr_t& retx, uint8_t* payload, uint32_t nof_bytes) { // Get tx_pdu info from tx_window srsran_assert(tx_window.has_sn(retx.sn), "Called %s without checking retx SN", __FUNCTION__); @@ -633,7 +634,7 @@ uint32_t rlc_am_nr_tx::build_retx_pdu_with_segmentation(rlc_amd_retx_t& retx, ui return hdr_len + retx_pdu_payload_size; } -bool rlc_am_nr_tx::is_retx_segmentation_required(const rlc_amd_retx_t& retx, uint32_t nof_bytes) +bool rlc_am_nr_tx::is_retx_segmentation_required(const rlc_amd_retx_nr_t& retx, uint32_t nof_bytes) { bool segmentation_required = false; if (retx.is_segment) { @@ -651,7 +652,7 @@ bool rlc_am_nr_tx::is_retx_segmentation_required(const rlc_amd_retx_t& retx, uin return segmentation_required; } -uint32_t rlc_am_nr_tx::get_retx_expected_hdr_len(const rlc_amd_retx_t& retx) +uint32_t rlc_am_nr_tx::get_retx_expected_hdr_len(const rlc_amd_retx_nr_t& retx) { uint32_t expected_hdr_len = min_hdr_size; if (retx.is_segment && retx.current_so != 0) { @@ -748,12 +749,12 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) segm++) { if (segm->so >= nack.so_start && segm->so <= nack.so_end) { // TODO: Check if this segment is not already queued for retransmission - rlc_amd_retx_t& retx = retx_queue.push(); - retx.sn = nack_sn; - retx.is_segment = true; - retx.so_start = segm->so; - retx.current_so = segm->so; - retx.so_end = segm->so + segm->payload_len; + rlc_amd_retx_nr_t& retx = retx_queue.push(); + retx.sn = nack_sn; + retx.is_segment = true; + retx.so_start = segm->so; + retx.current_so = segm->so; + retx.so_end = segm->so + segm->payload_len; retx_sn_set.insert(nack_sn); RlcInfo( "Scheduled RETX of SDU segment SN=%d, so_start=%d, so_end=%d", retx.sn, retx.so_start, retx.so_end); @@ -763,12 +764,12 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) // NACK'ing full SDU. // add to retx queue if it's not already there if (not retx_queue.has_sn(nack_sn)) { - rlc_amd_retx_t& retx = retx_queue.push(); - retx.sn = nack_sn; - retx.is_segment = false; - retx.so_start = 0; - retx.current_so = 0; - retx.so_end = pdu.sdu_buf->N_bytes; + rlc_amd_retx_nr_t& retx = retx_queue.push(); + retx.sn = nack_sn; + retx.is_segment = false; + retx.so_start = 0; + retx.current_so = 0; + retx.so_end = pdu.sdu_buf->N_bytes; retx_sn_set.insert(nack_sn); RlcInfo("Scheduled RETX of SDU SN=%d", retx.sn); } @@ -847,7 +848,7 @@ void rlc_am_nr_tx::get_buffer_state(uint32_t& n_bytes_new, uint32_t& n_bytes_pri // Bytes needed for retx if (not retx_queue.empty()) { - rlc_amd_retx_t& retx = retx_queue.front(); + rlc_amd_retx_nr_t& retx = retx_queue.front(); RlcDebug("buffer state - retx - SN=%d, Segment: %s, %d:%d", retx.sn, retx.is_segment ? "true" : "false", diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index 3b8a16be6..1f841712d 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -147,10 +147,10 @@ int retx_segmentation_required_checker_test() // Test full SDU retx { - uint32_t nof_bytes = 8; - rlc_amd_retx_t retx = {}; - retx.sn = 0; - retx.is_segment = false; + uint32_t nof_bytes = 8; + rlc_amd_retx_nr_t retx = {}; + retx.sn = 0; + retx.is_segment = false; tx->is_retx_segmentation_required(retx, nof_bytes); TESTASSERT_EQ(false, tx->is_retx_segmentation_required(retx, nof_bytes)); @@ -158,8 +158,8 @@ int retx_segmentation_required_checker_test() // Test SDU retx segmentation required { - uint32_t nof_bytes = 4; - rlc_amd_retx_t retx; + uint32_t nof_bytes = 4; + rlc_amd_retx_nr_t retx; retx.sn = 0; retx.is_segment = false; @@ -169,12 +169,12 @@ int retx_segmentation_required_checker_test() // Test full SDU segment retx { - uint32_t nof_bytes = 40; - rlc_amd_retx_t retx = {}; - retx.sn = 0; - retx.is_segment = true; - retx.so_start = 4; - retx.so_end = 6; + uint32_t nof_bytes = 40; + rlc_amd_retx_nr_t retx = {}; + retx.sn = 0; + retx.is_segment = true; + retx.so_start = 4; + retx.so_end = 6; tx->is_retx_segmentation_required(retx, nof_bytes); TESTASSERT_EQ(false, tx->is_retx_segmentation_required(retx, nof_bytes)); @@ -182,12 +182,12 @@ int retx_segmentation_required_checker_test() // Test SDU segment retx segmentation required { - uint32_t nof_bytes = 4; - rlc_amd_retx_t retx = {}; - retx.sn = 0; - retx.is_segment = true; - retx.so_start = 4; - retx.so_end = 6; + uint32_t nof_bytes = 4; + rlc_amd_retx_nr_t retx = {}; + retx.sn = 0; + retx.is_segment = true; + retx.so_start = 4; + retx.so_end = 6; tx->is_retx_segmentation_required(retx, nof_bytes); TESTASSERT_EQ(true, tx->is_retx_segmentation_required(retx, nof_bytes)); From b6d93577a021fa086aa3800dfd86e4bdbc71e21a Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Tue, 8 Mar 2022 12:46:05 +0100 Subject: [PATCH 042/195] rlc, nr: replace so_end by segment_length --- lib/include/srsran/rlc/rlc_am_data_structs.h | 4 +- lib/src/rlc/rlc_am_nr.cc | 47 +++++++++++--------- lib/test/rlc/rlc_am_nr_test.cc | 4 +- 3 files changed, 29 insertions(+), 26 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_am_data_structs.h b/lib/include/srsran/rlc/rlc_am_data_structs.h index f114a4a4d..e2025925f 100644 --- a/lib/include/srsran/rlc/rlc_am_data_structs.h +++ b/lib/include/srsran/rlc/rlc_am_data_structs.h @@ -311,8 +311,8 @@ struct rlc_amd_retx_lte_t { struct rlc_amd_retx_nr_t { uint32_t sn; bool is_segment; - uint32_t so_start; // offset to first byte of this segment - uint32_t so_end; // offset to first byte beyond the end of this segment + uint32_t so_start; // offset to first byte of this segment + uint32_t segment_length; // number of bytes contained in this segment uint32_t current_so; }; diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 78e378bc7..eed20d144 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -418,12 +418,12 @@ uint32_t rlc_am_nr_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_bytes) } } - RlcDebug("RETX - SN=%d, is_segment=%s, current_so=%d, so_start=%d, so_end=%d", + RlcDebug("RETX - SN=%d, is_segment=%s, current_so=%d, so_start=%d, segment_length=%d", retx.sn, retx.is_segment ? "true" : "false", retx.current_so, retx.so_start, - retx.so_end); + retx.segment_length); // Is segmentation/re-segmentation required? bool segmentation_required = is_retx_segmentation_required(retx, nof_bytes); @@ -460,7 +460,8 @@ rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8 // Get expected header and payload len uint32_t expected_hdr_len = get_retx_expected_hdr_len(retx); - uint32_t retx_payload_len = retx.is_segment ? (retx.so_end - retx.current_so) : tx_window[retx.sn].sdu_buf->N_bytes; + uint32_t retx_payload_len = + retx.is_segment ? (retx.so_start + retx.segment_length - retx.current_so) : tx_window[retx.sn].sdu_buf->N_bytes; srsran_assert(nof_bytes >= (expected_hdr_len + retx_payload_len), "Called %s but segmentation is required. nof_bytes=%d, expeced_hdr_len=%d, retx_payload_len=%d", __FUNCTION__, @@ -470,14 +471,14 @@ rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8 // Log RETX info RlcDebug("SDU%scan be fully re-transmitted. SN=%d, nof_bytes=%d, expected_hdr_len=%d, " - "current_so=%d, so_start=%d, so_end=%d", + "current_so=%d, so_start=%d, segment_length=%d", retx.is_segment ? " segment " : " ", retx.sn, nof_bytes, expected_hdr_len, retx.current_so, retx.so_start, - retx.so_end); + retx.segment_length); // Update & write header uint32_t current_so = 0; @@ -505,7 +506,7 @@ rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8 retx_pdu_payload_size = tx_window[retx.sn].sdu_buf->N_bytes; } else { // RETX SDU segment - retx_pdu_payload_size = (retx.so_end - retx.current_so); + retx_pdu_payload_size = (retx.so_start + retx.segment_length - retx.current_so); } uint32_t pdu_bytes = hdr_len + retx_pdu_payload_size; srsran_assert(pdu_bytes <= nof_bytes, "Error calculating hdr_len and pdu_payload_len"); @@ -555,11 +556,11 @@ uint32_t rlc_am_nr_tx::build_retx_pdu_with_segmentation(rlc_amd_retx_nr_t& retx, nof_bytes); } else { - RlcDebug("Creating SDU segment from SDU segment. SN=%d, current_so=%d, so_start=%d, so_end=%d", + RlcDebug("Creating SDU segment from SDU segment. SN=%d, current_so=%d, so_start=%d, segment_length=%d", retx.sn, retx.current_so, retx.so_start, - retx.so_end); + retx.segment_length); } uint32_t expected_hdr_len = min_hdr_size; @@ -604,28 +605,28 @@ uint32_t rlc_am_nr_tx::build_retx_pdu_with_segmentation(rlc_amd_retx_nr_t& retx, retx.is_segment = true; retx.current_so = retx.current_so + retx_pdu_payload_size; - RlcDebug("Updated RETX info. is_segment=%s, current_so=%d, so_start=%d, so_end=%d", + RlcDebug("Updated RETX info. is_segment=%s, current_so=%d, so_start=%d, segment_length=%d", retx.is_segment ? "true" : "false", retx.current_so, retx.so_start, - retx.so_end); + retx.segment_length); if (retx.current_so >= tx_pdu.sdu_buf->N_bytes) { RlcError("Current SO larger or equal to SDU size when creating SDU segment. SN=%d, current SO=%d, SO_start=%d, " - "SO_end=%d", + "segment_length=%d", retx.sn, retx.current_so, retx.so_start, - retx.so_end); + retx.segment_length); return 0; } - if (retx.current_so >= retx.so_end) { - RlcError("Current SO larger than SO end. SN=%d, current SO=%d, SO_start=%d, SO_end=%s", + if (retx.current_so >= retx.so_start + retx.segment_length) { + RlcError("Current SO larger than SO_start + segment_length. SN=%d, current SO=%d, SO_start=%d, segment_length=%s", retx.sn, retx.current_so, retx.so_start, - retx.so_end); + retx.segment_length); return 0; } @@ -639,7 +640,7 @@ bool rlc_am_nr_tx::is_retx_segmentation_required(const rlc_amd_retx_nr_t& retx, bool segmentation_required = false; if (retx.is_segment) { uint32_t expected_hdr_size = retx.current_so == 0 ? min_hdr_size : max_hdr_size; - if (nof_bytes < ((retx.so_end - retx.current_so) + expected_hdr_size)) { + if (nof_bytes < ((retx.so_start + retx.segment_length - retx.current_so) + expected_hdr_size)) { RlcInfo("Re-segmentation required for RETX. SN=%d", retx.sn); segmentation_required = true; } @@ -754,10 +755,12 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) retx.is_segment = true; retx.so_start = segm->so; retx.current_so = segm->so; - retx.so_end = segm->so + segm->payload_len; + retx.segment_length = segm->payload_len; retx_sn_set.insert(nack_sn); - RlcInfo( - "Scheduled RETX of SDU segment SN=%d, so_start=%d, so_end=%d", retx.sn, retx.so_start, retx.so_end); + RlcInfo("Scheduled RETX of SDU segment SN=%d, so_start=%d, segment_length=%d", + retx.sn, + retx.so_start, + retx.segment_length); } } } else { @@ -769,7 +772,7 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) retx.is_segment = false; retx.so_start = 0; retx.current_so = 0; - retx.so_end = pdu.sdu_buf->N_bytes; + retx.segment_length = pdu.sdu_buf->N_bytes; retx_sn_set.insert(nack_sn); RlcInfo("Scheduled RETX of SDU SN=%d", retx.sn); } @@ -853,9 +856,9 @@ void rlc_am_nr_tx::get_buffer_state(uint32_t& n_bytes_new, uint32_t& n_bytes_pri retx.sn, retx.is_segment ? "true" : "false", retx.so_start, - retx.so_end); + retx.so_start + retx.segment_length - 1); if (tx_window.has_sn(retx.sn)) { - int req_bytes = retx.so_end - retx.so_start; + int req_bytes = retx.segment_length; int hdr_req_bytes = retx.is_segment ? max_hdr_size : min_hdr_size; // Segmentation not supported yet if (req_bytes <= 0) { RlcError("in get_buffer_state(): Removing retx with SN=%d from queue", retx.sn); diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index 1f841712d..645ba3440 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -174,7 +174,7 @@ int retx_segmentation_required_checker_test() retx.sn = 0; retx.is_segment = true; retx.so_start = 4; - retx.so_end = 6; + retx.segment_length = 2; tx->is_retx_segmentation_required(retx, nof_bytes); TESTASSERT_EQ(false, tx->is_retx_segmentation_required(retx, nof_bytes)); @@ -187,7 +187,7 @@ int retx_segmentation_required_checker_test() retx.sn = 0; retx.is_segment = true; retx.so_start = 4; - retx.so_end = 6; + retx.segment_length = 2; tx->is_retx_segmentation_required(retx, nof_bytes); TESTASSERT_EQ(true, tx->is_retx_segmentation_required(retx, nof_bytes)); From 50ac1c6c0a4fff9ea5f9b1b57f3731c8f37a9d71 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Sat, 5 Mar 2022 08:16:23 +0100 Subject: [PATCH 043/195] cmake: flag install() of compiled targets as OPTIONAL This enables `make install` for partial builds of selected targets, which otherwise fails because the omitted targets are not present. --- lib/src/asn1/CMakeLists.txt | 12 ++++++------ lib/src/common/CMakeLists.txt | 2 +- lib/src/gtpu/CMakeLists.txt | 2 +- lib/src/mac/CMakeLists.txt | 2 +- lib/src/pdcp/CMakeLists.txt | 2 +- lib/src/phy/CMakeLists.txt | 2 +- lib/src/phy/rf/CMakeLists.txt | 12 ++++++------ lib/src/radio/CMakeLists.txt | 2 +- lib/src/rlc/CMakeLists.txt | 2 +- lib/src/srslog/CMakeLists.txt | 2 +- srsenb/src/CMakeLists.txt | 2 +- srsepc/src/CMakeLists.txt | 4 ++-- srsue/src/CMakeLists.txt | 2 +- 13 files changed, 24 insertions(+), 24 deletions(-) diff --git a/lib/src/asn1/CMakeLists.txt b/lib/src/asn1/CMakeLists.txt index ba164a56f..a95fe2b44 100644 --- a/lib/src/asn1/CMakeLists.txt +++ b/lib/src/asn1/CMakeLists.txt @@ -16,7 +16,7 @@ add_library(srsran_asn1 STATIC # ASN1 utils add_library(asn1_utils STATIC asn1_utils.cc) target_link_libraries(asn1_utils srsran_common) -INSTALL(TARGETS asn1_utils DESTINATION ${LIBRARY_DIR}) +install(TARGETS asn1_utils DESTINATION ${LIBRARY_DIR} OPTIONAL) # RRC ASN1 lib add_library(rrc_asn1 STATIC @@ -42,29 +42,29 @@ add_library(rrc_asn1 STATIC # Compile RRC ASN1 optimized for size target_compile_options(rrc_asn1 PRIVATE "-Os") target_link_libraries(rrc_asn1 asn1_utils srsran_common) -INSTALL(TARGETS rrc_asn1 DESTINATION ${LIBRARY_DIR}) +install(TARGETS rrc_asn1 DESTINATION ${LIBRARY_DIR} OPTIONAL) # S1AP ASN1 lib add_library(s1ap_asn1 STATIC s1ap.cc s1ap_utils.cc) target_compile_options(s1ap_asn1 PRIVATE "-Os") target_link_libraries(s1ap_asn1 asn1_utils srsran_common) -INSTALL(TARGETS s1ap_asn1 DESTINATION ${LIBRARY_DIR}) +install(TARGETS s1ap_asn1 DESTINATION ${LIBRARY_DIR} OPTIONAL) # RRC NR ASN1 add_library(rrc_nr_asn1 STATIC rrc_nr.cc rrc_nr_utils.cc) target_compile_options(rrc_nr_asn1 PRIVATE "-Os") target_link_libraries(rrc_nr_asn1 asn1_utils srsran_common) -INSTALL(TARGETS rrc_nr_asn1 DESTINATION ${LIBRARY_DIR}) +install(TARGETS rrc_nr_asn1 DESTINATION ${LIBRARY_DIR} OPTIONAL) # NGAP ASN1 add_library(ngap_nr_asn1 STATIC ngap.cc) target_compile_options(ngap_nr_asn1 PRIVATE "-Os") target_link_libraries(ngap_nr_asn1 asn1_utils srsran_common) -INSTALL(TARGETS ngap_nr_asn1 DESTINATION ${LIBRARY_DIR}) +install(TARGETS ngap_nr_asn1 DESTINATION ${LIBRARY_DIR} OPTIONAL) # NAS 5G add_library(nas_5g_msg STATIC nas_5g_msg.cc nas_5g_ies.cc nas_5g_utils.cc) target_compile_options(nas_5g_msg PRIVATE "-Os") target_link_libraries(nas_5g_msg asn1_utils srsran_common) -INSTALL(TARGETS nas_5g_msg DESTINATION ${LIBRARY_DIR}) +install(TARGETS nas_5g_msg DESTINATION ${LIBRARY_DIR} OPTIONAL) diff --git a/lib/src/common/CMakeLists.txt b/lib/src/common/CMakeLists.txt index 1ddf87b07..c0054503e 100644 --- a/lib/src/common/CMakeLists.txt +++ b/lib/src/common/CMakeLists.txt @@ -51,6 +51,6 @@ target_include_directories(srsran_common PUBLIC ${SEC_INCLUDE_DIRS} ${CMAKE_SOUR target_link_libraries(srsran_common srsran_phy support srslog ${SEC_LIBRARIES} ${BACKWARD_LIBRARIES} ${SCTP_LIBRARIES}) target_compile_definitions(srsran_common PRIVATE ${BACKWARD_DEFINITIONS}) -INSTALL(TARGETS srsran_common DESTINATION ${LIBRARY_DIR}) +install(TARGETS srsran_common DESTINATION ${LIBRARY_DIR} OPTIONAL) add_subdirectory(test) diff --git a/lib/src/gtpu/CMakeLists.txt b/lib/src/gtpu/CMakeLists.txt index a2a9f8ff4..38c500ee2 100644 --- a/lib/src/gtpu/CMakeLists.txt +++ b/lib/src/gtpu/CMakeLists.txt @@ -10,4 +10,4 @@ set(SOURCES gtpu.cc) add_library(srsran_gtpu STATIC ${SOURCES}) target_link_libraries(srsran_gtpu srsran_common srsran_asn1 ${ATOMIC_LIBS}) -INSTALL(TARGETS srsran_gtpu DESTINATION ${LIBRARY_DIR}) +install(TARGETS srsran_gtpu DESTINATION ${LIBRARY_DIR} OPTIONAL) diff --git a/lib/src/mac/CMakeLists.txt b/lib/src/mac/CMakeLists.txt index 1974aee4e..206cc5538 100644 --- a/lib/src/mac/CMakeLists.txt +++ b/lib/src/mac/CMakeLists.txt @@ -10,6 +10,6 @@ SET(SOURCES pdu.cc pdu_queue.cc mac_sch_pdu_nr.cc mac_rar_pdu_nr.cc) add_library(srsran_mac STATIC ${SOURCES}) target_link_libraries(srsran_mac srsran_common) -INSTALL(TARGETS srsran_mac DESTINATION ${LIBRARY_DIR}) +install(TARGETS srsran_mac DESTINATION ${LIBRARY_DIR} OPTIONAL) add_subdirectory(test) \ No newline at end of file diff --git a/lib/src/pdcp/CMakeLists.txt b/lib/src/pdcp/CMakeLists.txt index 2c1f8578f..d3ab45fdc 100644 --- a/lib/src/pdcp/CMakeLists.txt +++ b/lib/src/pdcp/CMakeLists.txt @@ -13,4 +13,4 @@ set(SOURCES pdcp.cc add_library(srsran_pdcp STATIC ${SOURCES}) target_link_libraries(srsran_pdcp srsran_common srsran_asn1 ${ATOMIC_LIBS}) -INSTALL(TARGETS srsran_pdcp DESTINATION ${LIBRARY_DIR}) +install(TARGETS srsran_pdcp DESTINATION ${LIBRARY_DIR} OPTIONAL) diff --git a/lib/src/phy/CMakeLists.txt b/lib/src/phy/CMakeLists.txt index 880e9ef09..c11ff47de 100644 --- a/lib/src/phy/CMakeLists.txt +++ b/lib/src/phy/CMakeLists.txt @@ -47,4 +47,4 @@ set(srsran_srcs $ add_library(srsran_phy STATIC ${srsran_srcs} ) target_link_libraries(srsran_phy pthread m ${FFT_LIBRARIES}) -INSTALL(TARGETS srsran_phy DESTINATION ${LIBRARY_DIR}) +install(TARGETS srsran_phy DESTINATION ${LIBRARY_DIR} OPTIONAL) diff --git a/lib/src/phy/rf/CMakeLists.txt b/lib/src/phy/rf/CMakeLists.txt index d29d464f3..5faf68f5e 100644 --- a/lib/src/phy/rf/CMakeLists.txt +++ b/lib/src/phy/rf/CMakeLists.txt @@ -36,7 +36,7 @@ if(RF_FOUND) list(APPEND STATIC_PLUGINS srsran_rf_uhd) endif (ENABLE_RF_PLUGINS) target_link_libraries(srsran_rf_uhd srsran_rf_utils srsran_phy ${UHD_LIBRARIES} ${Boost_LIBRARIES}) - install(TARGETS srsran_rf_uhd DESTINATION ${LIBRARY_DIR}) + install(TARGETS srsran_rf_uhd DESTINATION ${LIBRARY_DIR} OPTIONAL) # If found, add a macro to inform the UHD driver about the available feature if (UHD_ENABLE_X300_FW_RESET) @@ -62,7 +62,7 @@ if(RF_FOUND) list(APPEND STATIC_PLUGINS srsran_rf_blade) endif (ENABLE_RF_PLUGINS) target_link_libraries(srsran_rf_blade srsran_rf_utils srsran_phy ${BLADERF_LIBRARIES}) - install(TARGETS srsran_rf_blade DESTINATION ${LIBRARY_DIR}) + install(TARGETS srsran_rf_blade DESTINATION ${LIBRARY_DIR} OPTIONAL) endif (BLADERF_FOUND AND ENABLE_BLADERF) if (SOAPYSDR_FOUND AND ENABLE_SOAPYSDR) @@ -77,7 +77,7 @@ if(RF_FOUND) list(APPEND STATIC_PLUGINS srsran_rf_soapy) endif (ENABLE_RF_PLUGINS) target_link_libraries(srsran_rf_soapy srsran_rf_utils srsran_phy ${SOAPYSDR_LIBRARIES}) - install(TARGETS srsran_rf_soapy DESTINATION ${LIBRARY_DIR}) + install(TARGETS srsran_rf_soapy DESTINATION ${LIBRARY_DIR} OPTIONAL) endif (SOAPYSDR_FOUND AND ENABLE_SOAPYSDR) if(SKIQ_FOUND AND ENABLE_SKIQ) @@ -94,7 +94,7 @@ if(RF_FOUND) list(APPEND STATIC_PLUGINS srsran_rf_skiq) endif (ENABLE_RF_PLUGINS) target_link_libraries(srsran_rf_skiq srsran_rf_utils srsran_phy ${SKIQ_LIBRARIES} rt) - install(TARGETS srsran_rf_skiq DESTINATION ${LIBRARY_DIR}) + install(TARGETS srsran_rf_skiq DESTINATION ${LIBRARY_DIR} OPTIONAL) endif(SKIQ_FOUND AND ENABLE_SKIQ) if (ZEROMQ_FOUND AND ENABLE_ZEROMQ) @@ -109,7 +109,7 @@ if(RF_FOUND) list(APPEND STATIC_PLUGINS srsran_rf_zmq) endif (ENABLE_RF_PLUGINS) target_link_libraries(srsran_rf_zmq srsran_rf_utils srsran_phy ${ZEROMQ_LIBRARIES}) - install(TARGETS srsran_rf_zmq DESTINATION ${LIBRARY_DIR}) + install(TARGETS srsran_rf_zmq DESTINATION ${LIBRARY_DIR} OPTIONAL) endif (ZEROMQ_FOUND AND ENABLE_ZEROMQ) # Add sources of file-based RF directly to the RF library (not as a plugin) @@ -148,7 +148,7 @@ if(RF_FOUND) foreach (TOP_RF_LIB ${TOP_RF_LIBS}) target_link_libraries(${TOP_RF_LIB} srsran_rf_utils srsran_phy) set_target_properties(${TOP_RF_LIB} PROPERTIES VERSION ${SRSRAN_VERSION_STRING} SOVERSION ${SRSRAN_SOVERSION}) - install(TARGETS ${TOP_RF_LIB} DESTINATION ${LIBRARY_DIR}) + install(TARGETS ${TOP_RF_LIB} DESTINATION ${LIBRARY_DIR} OPTIONAL) endforeach () # Tests diff --git a/lib/src/radio/CMakeLists.txt b/lib/src/radio/CMakeLists.txt index 515e0a9cf..eb5a08080 100644 --- a/lib/src/radio/CMakeLists.txt +++ b/lib/src/radio/CMakeLists.txt @@ -9,7 +9,7 @@ if(RF_FOUND) add_library(srsran_radio STATIC radio.cc channel_mapping.cc) target_link_libraries(srsran_radio srsran_rf srsran_common) - INSTALL(TARGETS srsran_radio DESTINATION ${LIBRARY_DIR}) + install(TARGETS srsran_radio DESTINATION ${LIBRARY_DIR} OPTIONAL) endif(RF_FOUND) add_subdirectory(test) diff --git a/lib/src/rlc/CMakeLists.txt b/lib/src/rlc/CMakeLists.txt index 89b44fba6..23e2debdc 100644 --- a/lib/src/rlc/CMakeLists.txt +++ b/lib/src/rlc/CMakeLists.txt @@ -20,4 +20,4 @@ set(SOURCES rlc.cc add_library(srsran_rlc STATIC ${SOURCES}) target_link_libraries(srsran_rlc srsran_common ${ATOMIC_LIBS}) -INSTALL(TARGETS srsran_rlc DESTINATION ${LIBRARY_DIR}) +install(TARGETS srsran_rlc DESTINATION ${LIBRARY_DIR} OPTIONAL) diff --git a/lib/src/srslog/CMakeLists.txt b/lib/src/srslog/CMakeLists.txt index 794285555..4995bf8b9 100644 --- a/lib/src/srslog/CMakeLists.txt +++ b/lib/src/srslog/CMakeLists.txt @@ -30,4 +30,4 @@ find_package(Threads REQUIRED) add_library(srslog STATIC ${SOURCES}) target_link_libraries(srslog ${CMAKE_THREAD_LIBS_INIT}) -INSTALL(TARGETS srslog DESTINATION ${LIBRARY_DIR}) +install(TARGETS srslog DESTINATION ${LIBRARY_DIR} OPTIONAL) diff --git a/srsenb/src/CMakeLists.txt b/srsenb/src/CMakeLists.txt index 2db4196cb..8c92d1b67 100644 --- a/srsenb/src/CMakeLists.txt +++ b/srsenb/src/CMakeLists.txt @@ -54,4 +54,4 @@ else(NOT ${BUILDENB_CMD} STREQUAL "") message(STATUS "No post-build-ENB command defined") endif (NOT ${BUILDENB_CMD} STREQUAL "") -install(TARGETS srsenb DESTINATION ${RUNTIME_DIR}) +install(TARGETS srsenb DESTINATION ${RUNTIME_DIR} OPTIONAL) diff --git a/srsepc/src/CMakeLists.txt b/srsepc/src/CMakeLists.txt index b88e0517c..46c84ac3b 100644 --- a/srsepc/src/CMakeLists.txt +++ b/srsepc/src/CMakeLists.txt @@ -64,5 +64,5 @@ else(NOT ${BUILDEPC_CMD} STREQUAL "") message(STATUS "No post-build-EPC command defined") endif (NOT ${BUILDEPC_CMD} STREQUAL "") -install(TARGETS srsepc DESTINATION ${RUNTIME_DIR}) -install(TARGETS srsmbms DESTINATION ${RUNTIME_DIR}) +install(TARGETS srsepc DESTINATION ${RUNTIME_DIR} OPTIONAL) +install(TARGETS srsmbms DESTINATION ${RUNTIME_DIR} OPTIONAL) diff --git a/srsue/src/CMakeLists.txt b/srsue/src/CMakeLists.txt index 8b4043623..c98d9aae7 100644 --- a/srsue/src/CMakeLists.txt +++ b/srsue/src/CMakeLists.txt @@ -64,4 +64,4 @@ else(NOT ${BUILDUE_CMD} STREQUAL "") message(STATUS "No post-build-UE command defined") endif (NOT ${BUILDUE_CMD} STREQUAL "") -install(TARGETS srsue DESTINATION ${RUNTIME_DIR}) +install(TARGETS srsue DESTINATION ${RUNTIME_DIR} OPTIONAL) From bb9eaf4390a9bef5b0b579765f43aed725f2e671 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Tue, 1 Mar 2022 17:31:42 +0000 Subject: [PATCH 044/195] gnb,ngap: Added some comments to ngap.cc to make it easier to navigate the file. Re-ordered NGAP UE procedures have the same order as in the standard. --- .../srsran/interfaces/gnb_rrc_nr_interfaces.h | 10 +- srsgnb/hdr/stack/ngap/ngap_ue_proc.h | 62 +++---- srsgnb/src/stack/ngap/ngap_ue_proc.cc | 161 +++++++++--------- .../src/stack/rrc/test/rrc_nr_test_helpers.cc | 2 +- 4 files changed, 118 insertions(+), 117 deletions(-) diff --git a/lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h b/lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h index f695cc21f..aaa81a1bf 100644 --- a/lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h +++ b/lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h @@ -26,11 +26,11 @@ public: virtual int ue_set_security_cfg_capabilities(uint16_t rnti, const asn1::ngap::ue_security_cap_s& caps) = 0; virtual int start_security_mode_procedure(uint16_t rnti, srsran::unique_byte_buffer_t nas_pdu) = 0; virtual int - establish_rrc_bearer(uint16_t rnti, uint16_t pdu_session_id, srsran::const_byte_span nas_pdu, uint32_t lcid) = 0; - virtual int allocate_lcid(uint16_t rnti) = 0; - virtual int release_bearers(uint16_t rnti) = 0; - virtual void release_user(uint16_t rnti) = 0; - virtual void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) = 0; + establish_rrc_bearer(uint16_t rnti, uint16_t pdu_session_id, srsran::const_byte_span nas_pdu, uint32_t lcid) = 0; + virtual int allocate_lcid(uint16_t rnti) = 0; + virtual int release_bearers(uint16_t rnti) = 0; + virtual void release_user(uint16_t rnti) = 0; + virtual void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) = 0; }; } // namespace srsenb diff --git a/srsgnb/hdr/stack/ngap/ngap_ue_proc.h b/srsgnb/hdr/stack/ngap/ngap_ue_proc.h index fa58d7b7c..6b021d6b3 100644 --- a/srsgnb/hdr/stack/ngap/ngap_ue_proc.h +++ b/srsgnb/hdr/stack/ngap/ngap_ue_proc.h @@ -28,8 +28,33 @@ namespace srsenb { -// TS 38.413 - Section 8.3 - UE Context Management Procedures +/* + * TS 38.413 - Section 8.2 - PDU Session Management Procedures + */ +// TS 38.413 - Section 8.2.1 PDU Session Resource Setup +class ngap_ue_pdu_session_res_setup_proc +{ +public: + explicit ngap_ue_pdu_session_res_setup_proc(ngap_interface_ngap_proc* parent_, + rrc_interface_ngap_nr* rrc_, + ngap_ue_ctxt_t* ue_ctxt, + ngap_ue_bearer_manager* bearer_manager, + srslog::basic_logger& logger_); + srsran::proc_outcome_t init(const asn1::ngap::pdu_session_res_setup_request_s& msg); + srsran::proc_outcome_t step(); + static const char* name() { return "UE PDU Session Resource Setup"; } +private: + ngap_ue_ctxt_t* ue_ctxt; + ngap_interface_ngap_proc* parent; + ngap_ue_bearer_manager* bearer_manager; + rrc_interface_ngap_nr* rrc = nullptr; + srslog::basic_logger& logger; +}; + +/* + * TS 38.413 - Section 8.3 - UE Context Management Procedures + */ // TS 38.413 - Section 8.3.1 - Initial Context Setup class ngap_ue_initial_context_setup_proc { @@ -71,39 +96,6 @@ private: srslog::basic_logger& logger; }; -// TS 38.413 - Section 8.3.4 - UE Context Modification -class ngap_ue_ue_context_modification_proc -{ -public: - explicit ngap_ue_ue_context_modification_proc(ngap_interface_ngap_proc* parent_, srslog::basic_logger& logger_); - srsran::proc_outcome_t init(); - srsran::proc_outcome_t step(); - static const char* name() { return "UE Context Modification"; } - -private: - ngap_interface_ngap_proc* parent; -}; - -class ngap_ue_pdu_session_res_setup_proc -{ -public: - explicit ngap_ue_pdu_session_res_setup_proc(ngap_interface_ngap_proc* parent_, - rrc_interface_ngap_nr* rrc_, - ngap_ue_ctxt_t* ue_ctxt, - ngap_ue_bearer_manager* bearer_manager, - srslog::basic_logger& logger_); - srsran::proc_outcome_t init(const asn1::ngap::pdu_session_res_setup_request_s& msg); - srsran::proc_outcome_t step(); - static const char* name() { return "UE PDU Session Resource Setup"; } - -private: - ngap_ue_ctxt_t* ue_ctxt; - ngap_interface_ngap_proc* parent; - ngap_ue_bearer_manager* bearer_manager; - rrc_interface_ngap_nr* rrc = nullptr; - srslog::basic_logger& logger; -}; - } // namespace srsenb -#endif \ No newline at end of file +#endif diff --git a/srsgnb/src/stack/ngap/ngap_ue_proc.cc b/srsgnb/src/stack/ngap/ngap_ue_proc.cc index 12e94a260..17512d922 100644 --- a/srsgnb/src/stack/ngap/ngap_ue_proc.cc +++ b/srsgnb/src/stack/ngap/ngap_ue_proc.cc @@ -16,82 +16,9 @@ using namespace srsran; namespace srsenb { -ngap_ue_initial_context_setup_proc::ngap_ue_initial_context_setup_proc(ngap_interface_ngap_proc* parent_, - rrc_interface_ngap_nr* rrc_, - ngap_ue_ctxt_t* ue_ctxt_, - srslog::basic_logger& logger_) : - logger(logger_), parent(parent_), rrc(rrc_), ue_ctxt(ue_ctxt_){}; - -proc_outcome_t ngap_ue_initial_context_setup_proc::init(const asn1::ngap::init_context_setup_request_s& msg) -{ - ue_ctxt->amf_pointer = msg->guami.value.amf_pointer.to_number(); - ue_ctxt->amf_set_id = msg->guami.value.amf_set_id.to_number(); - ue_ctxt->amf_region_id = msg->guami.value.amf_region_id.to_number(); - - if (msg->ue_aggregate_maximum_bit_rate_present == true) { - rrc->ue_set_bitrates(ue_ctxt->rnti, msg->ue_aggregate_maximum_bit_rate.value); - } - rrc->ue_set_security_cfg_capabilities(ue_ctxt->rnti, msg->ue_security_cap.value); - rrc->ue_set_security_cfg_key(ue_ctxt->rnti, msg->security_key.value); - - if (msg->nas_pdu_present) { - srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); - if (pdu == nullptr) { - logger.error("Fatal Error: Couldn't allocate buffer in %s.", __FUNCTION__); - return proc_outcome_t::error; - } - memcpy(pdu->msg, msg->nas_pdu.value.data(), msg->nas_pdu.value.size()); - pdu->N_bytes = msg->nas_pdu.value.size(); - rrc->start_security_mode_procedure(ue_ctxt->rnti, std::move(pdu)); - } else { - rrc->start_security_mode_procedure(ue_ctxt->rnti, nullptr); - } - - return proc_outcome_t::yield; -}; - -proc_outcome_t ngap_ue_initial_context_setup_proc::react(bool rrc_reconf_outcome) -{ - if (rrc_reconf_outcome == true) { - parent->send_initial_ctxt_setup_response(); - return proc_outcome_t::success; - } - - return proc_outcome_t::error; -} - -proc_outcome_t ngap_ue_initial_context_setup_proc::step() -{ - return proc_outcome_t::yield; -} - -ngap_ue_ue_context_release_proc::ngap_ue_ue_context_release_proc(ngap_interface_ngap_proc* parent_, - rrc_interface_ngap_nr* rrc_, - ngap_ue_ctxt_t* ue_ctxt_, - ngap_ue_bearer_manager* bearer_manager_, - srslog::basic_logger& logger_) : - logger(logger_) -{ - parent = parent_; - rrc = rrc_; - ue_ctxt = ue_ctxt_; - bearer_manager = bearer_manager_; -}; - -proc_outcome_t ngap_ue_ue_context_release_proc::init(const asn1::ngap::ue_context_release_cmd_s& msg) -{ - logger.info("Started %s", name()); - bearer_manager->reset_pdu_sessions(ue_ctxt->rnti); - rrc->release_user(ue_ctxt->rnti); - parent->send_ue_ctxt_release_complete(); - return proc_outcome_t::success; -} - -proc_outcome_t ngap_ue_ue_context_release_proc::step() -{ - return proc_outcome_t::success; -} - +/* + * TS 38.413 - Section 8.2.1 PDU Session Resource Setup + */ ngap_ue_pdu_session_res_setup_proc::ngap_ue_pdu_session_res_setup_proc(ngap_interface_ngap_proc* parent_, rrc_interface_ngap_nr* rrc_, ngap_ue_ctxt_t* ue_ctxt_, @@ -182,4 +109,86 @@ proc_outcome_t ngap_ue_pdu_session_res_setup_proc::step() return proc_outcome_t::success; } +/* + * TS 38.413 - Section 8.3.1 - Initial Context Setup + */ +ngap_ue_initial_context_setup_proc::ngap_ue_initial_context_setup_proc(ngap_interface_ngap_proc* parent_, + rrc_interface_ngap_nr* rrc_, + ngap_ue_ctxt_t* ue_ctxt_, + srslog::basic_logger& logger_) : + logger(logger_), parent(parent_), rrc(rrc_), ue_ctxt(ue_ctxt_){}; + +proc_outcome_t ngap_ue_initial_context_setup_proc::init(const asn1::ngap::init_context_setup_request_s& msg) +{ + ue_ctxt->amf_pointer = msg->guami.value.amf_pointer.to_number(); + ue_ctxt->amf_set_id = msg->guami.value.amf_set_id.to_number(); + ue_ctxt->amf_region_id = msg->guami.value.amf_region_id.to_number(); + + if (msg->ue_aggregate_maximum_bit_rate_present == true) { + rrc->ue_set_bitrates(ue_ctxt->rnti, msg->ue_aggregate_maximum_bit_rate.value); + } + rrc->ue_set_security_cfg_capabilities(ue_ctxt->rnti, msg->ue_security_cap.value); + rrc->ue_set_security_cfg_key(ue_ctxt->rnti, msg->security_key.value); + + if (msg->nas_pdu_present) { + srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (pdu == nullptr) { + logger.error("Fatal Error: Couldn't allocate buffer in %s.", __FUNCTION__); + return proc_outcome_t::error; + } + memcpy(pdu->msg, msg->nas_pdu.value.data(), msg->nas_pdu.value.size()); + pdu->N_bytes = msg->nas_pdu.value.size(); + rrc->start_security_mode_procedure(ue_ctxt->rnti, std::move(pdu)); + } else { + rrc->start_security_mode_procedure(ue_ctxt->rnti, nullptr); + } + + return proc_outcome_t::yield; +}; + +proc_outcome_t ngap_ue_initial_context_setup_proc::react(bool rrc_reconf_outcome) +{ + if (rrc_reconf_outcome == true) { + parent->send_initial_ctxt_setup_response(); + return proc_outcome_t::success; + } + + return proc_outcome_t::error; +} + +proc_outcome_t ngap_ue_initial_context_setup_proc::step() +{ + return proc_outcome_t::yield; +} + +/* + * TS 38.413 - Section 8.3.2 - UE Context Release Request (NG-RAN node initiated) + */ +ngap_ue_ue_context_release_proc::ngap_ue_ue_context_release_proc(ngap_interface_ngap_proc* parent_, + rrc_interface_ngap_nr* rrc_, + ngap_ue_ctxt_t* ue_ctxt_, + ngap_ue_bearer_manager* bearer_manager_, + srslog::basic_logger& logger_) : + logger(logger_) +{ + parent = parent_; + rrc = rrc_; + ue_ctxt = ue_ctxt_; + bearer_manager = bearer_manager_; +}; + +proc_outcome_t ngap_ue_ue_context_release_proc::init(const asn1::ngap::ue_context_release_cmd_s& msg) +{ + logger.info("Started %s", name()); + bearer_manager->reset_pdu_sessions(ue_ctxt->rnti); + rrc->release_user(ue_ctxt->rnti); + parent->send_ue_ctxt_release_complete(); + return proc_outcome_t::success; +} + +proc_outcome_t ngap_ue_ue_context_release_proc::step() +{ + return proc_outcome_t::success; +} + } // namespace srsenb diff --git a/srsgnb/src/stack/rrc/test/rrc_nr_test_helpers.cc b/srsgnb/src/stack/rrc/test/rrc_nr_test_helpers.cc index 31765f6b4..047693a09 100644 --- a/srsgnb/src/stack/rrc/test/rrc_nr_test_helpers.cc +++ b/srsgnb/src/stack/rrc/test/rrc_nr_test_helpers.cc @@ -100,7 +100,7 @@ void test_rrc_nr_connection_establishment(srsran::task_scheduler& task_sched, complete_ies.guami_type.value = rrc_setup_complete_ies_s::guami_type_opts::native; std::string NAS_msg_str = "7E01280E534C337E004109000BF200F110800101347B80802E02F07071002D7E004109000BF200F11080010134" "7B80801001002E02F0702F0201015200F11000006418010174000090530101"; - auto& ded_nas_msg = complete_ies.ded_nas_msg.from_string(NAS_msg_str); + auto& ded_nas_msg = complete_ies.ded_nas_msg.from_string(NAS_msg_str); { pdu = srsran::make_byte_buffer(); From 99d2cd068f198aa1bb7980ebf9031df39b248bbc Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Thu, 10 Mar 2022 18:29:22 +0100 Subject: [PATCH 045/195] PHY: Initial PUCCH Format 1 frequency hopping --- lib/src/phy/ch_estimation/dmrs_pucch.c | 191 +++++++++++++++---------- lib/src/phy/phch/pucch_cfg_nr.c | 3 +- lib/src/phy/phch/pucch_nr.c | 172 ++++++++++++++-------- lib/src/phy/phch/test/pucch_nr_test.c | 114 ++++++++------- 4 files changed, 291 insertions(+), 189 deletions(-) diff --git a/lib/src/phy/ch_estimation/dmrs_pucch.c b/lib/src/phy/ch_estimation/dmrs_pucch.c index cc8dc5e11..49c34e962 100644 --- a/lib/src/phy/ch_estimation/dmrs_pucch.c +++ b/lib/src/phy/ch_estimation/dmrs_pucch.c @@ -111,43 +111,58 @@ int srsran_dmrs_pucch_format1_put(const srsran_pucch_nr_t* q, return SRSRAN_ERROR; } - uint32_t n_pucch = dmrs_pucch_format1_n_pucch(resource, 0); - if (n_pucch == 0) { - ERROR("Error getting number of symbols"); - return SRSRAN_ERROR; - } - + // First symbol index uint32_t l_prime = resource->start_symbol_idx; - for (uint32_t m = 0; m < n_pucch; m++) { - // Clause 6.4.1.3.1.2 specifies l=0,2,4... - uint32_t l = m * 2; - - // Get start of the sequence in resource grid - cf_t* slot_symbols_ptr = &slot_symbols[(q->carrier.nof_prb * (l + l_prime) + resource->starting_prb) * SRSRAN_NRE]; - - // Get Alpha index - uint32_t alpha_idx = 0; - if (srsran_pucch_nr_alpha_idx(carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, 0, &alpha_idx) < - SRSRAN_SUCCESS) { - ERROR("Calculating alpha"); - } - // get r_uv sequence from LUT object - const cf_t* r_uv = srsran_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx); - if (r_uv == NULL) { - ERROR("Getting r_uv sequence"); + // Clause 6.4.1.3.1.2 specifies l=0,2,4... + for (uint32_t m_prime = 0, l = 0; m_prime < (resource->intra_slot_hopping ? 2 : 1); m_prime++) { + // Get number of symbols carrying DMRS + uint32_t n_pucch = dmrs_pucch_format1_n_pucch(resource, m_prime); + if (n_pucch == 0) { + ERROR("Error getting number of symbols"); return SRSRAN_ERROR; } - // Get w_i_m - cf_t w_i_m = srsran_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m); + // Get the starting PRB + uint32_t starting_prb = (m_prime == 0) ? resource->starting_prb : resource->second_hop_prb; + + for (uint32_t m = 0; m < n_pucch; m++, l += 2) { + // Get start of the sequence in resource grid + cf_t* slot_symbols_ptr = &slot_symbols[(q->carrier.nof_prb * (l + l_prime) + starting_prb) * SRSRAN_NRE]; + + // Get Alpha index + uint32_t alpha_idx = 0; + if (srsran_pucch_nr_alpha_idx(carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, 0, &alpha_idx) < + SRSRAN_SUCCESS) { + ERROR("Calculating alpha"); + } + + // get r_uv sequence from LUT object + const cf_t* r_uv = srsran_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx); + if (r_uv == NULL) { + ERROR("Getting r_uv sequence"); + return SRSRAN_ERROR; + } + + // Get w_i_m + cf_t w_i_m = srsran_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m); - // Compute z(n) = w(i) * r_uv(n) - cf_t z[SRSRAN_NRE]; - srsran_vec_sc_prod_ccc(r_uv, w_i_m, z, SRSRAN_NRE); + // Compute z(n) = w(i) * r_uv(n) + cf_t z[SRSRAN_NRE]; + srsran_vec_sc_prod_ccc(r_uv, w_i_m, z, SRSRAN_NRE); + + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { + printf("[PUCCH Format 1 DMRS TX] m_prime=%d; m=%d; w_i_m=%+.3f%+.3f; z=", + m_prime, + m, + __real__ w_i_m, + __imag__ w_i_m); + srsran_vec_fprint_c(stdout, z, SRSRAN_NRE); + } - // Put z in the grid - srsran_vec_cf_copy(slot_symbols_ptr, z, SRSRAN_NRE); + // Put z in the grid + srsran_vec_cf_copy(slot_symbols_ptr, z, SRSRAN_NRE); + } } return SRSRAN_SUCCESS; @@ -177,50 +192,71 @@ int srsran_dmrs_pucch_format1_estimate(const srsran_pucch_nr_t* q, return SRSRAN_ERROR; } - uint32_t n_pucch = dmrs_pucch_format1_n_pucch(resource, 0); - if (n_pucch == 0) { - ERROR("Error getting number of symbols"); - return SRSRAN_ERROR; - } - - cf_t ce[SRSRAN_PUCCH_NR_FORMAT1_N_MAX][SRSRAN_NRE]; - - // Prevent ce[m] overflow - assert(n_pucch <= SRSRAN_PUCCH_NR_FORMAT1_N_MAX); + uint32_t start_rb_idx[SRSRAN_PUCCH_NR_FORMAT1_N_MAX]; + uint32_t symbol_idx[SRSRAN_PUCCH_NR_FORMAT1_N_MAX]; + cf_t ce[SRSRAN_PUCCH_NR_FORMAT1_N_MAX][SRSRAN_NRE]; + // First symbol index uint32_t l_prime = resource->start_symbol_idx; - for (uint32_t m = 0; m < n_pucch; m++) { - // Clause 6.4.1.3.1.2 specifies l=0,2,4... - uint32_t l = m * 2; - - // Get start of the sequence in resource grid - const cf_t* slot_symbols_ptr = - &slot_symbols[(q->carrier.nof_prb * (l + l_prime) + resource->starting_prb) * SRSRAN_NRE]; - - // Get Alpha index - uint32_t alpha_idx = 0; - if (srsran_pucch_nr_alpha_idx(&q->carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, 0, &alpha_idx) < - SRSRAN_SUCCESS) { - ERROR("Calculating alpha"); - } - // get r_uv sequence from LUT object - const cf_t* r_uv = srsran_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx); - if (r_uv == NULL) { - ERROR("Getting r_uv sequence"); + uint32_t n_pucch_sum = 0; + for (uint32_t m_prime = 0, l = 0; m_prime < (resource->intra_slot_hopping ? 2 : 1); m_prime++) { + // Get number of symbols carrying DMRS + uint32_t n_pucch = dmrs_pucch_format1_n_pucch(resource, m_prime); + if (n_pucch == 0) { + ERROR("Error getting number of symbols"); return SRSRAN_ERROR; } - // Get w_i_m - cf_t w_i_m = srsran_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m); + // Prevent ce[m] overflow + assert(n_pucch <= SRSRAN_PUCCH_NR_FORMAT1_N_MAX); + + // Get the starting PRB + uint32_t starting_prb = (m_prime == 0) ? resource->starting_prb : resource->second_hop_prb; + start_rb_idx[n_pucch_sum] = starting_prb; + + for (uint32_t m = 0; m < n_pucch; m++, l += 2) { // Clause 6.4.1.3.1.2 specifies l=0,2,4... + symbol_idx[n_pucch_sum] = l + l_prime; - // Compute z(n) = w(i) * r_uv(n) - cf_t z[SRSRAN_NRE]; - srsran_vec_sc_prod_ccc(r_uv, w_i_m, z, SRSRAN_NRE); + // Get start of the sequence in resource grid + const cf_t* slot_symbols_ptr = &slot_symbols[(q->carrier.nof_prb * (l + l_prime) + starting_prb) * SRSRAN_NRE]; + + // Get Alpha index + uint32_t alpha_idx = 0; + if (srsran_pucch_nr_alpha_idx(&q->carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, 0, &alpha_idx) < + SRSRAN_SUCCESS) { + ERROR("Calculating alpha"); + } + + // get r_uv sequence from LUT object + const cf_t* r_uv = srsran_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx); + if (r_uv == NULL) { + ERROR("Getting r_uv sequence"); + return SRSRAN_ERROR; + } - // TODO: can ce[m] overflow? - // Calculate least square estimates for this symbol - srsran_vec_prod_conj_ccc(slot_symbols_ptr, z, ce[m], SRSRAN_NRE); + // Get w_i_m + cf_t w_i_m = srsran_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m); + + // Compute z(n) = w(i) * r_uv(n) + cf_t z[SRSRAN_NRE]; + srsran_vec_sc_prod_ccc(r_uv, w_i_m, z, SRSRAN_NRE); + + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { + INFO("[PUCCH Format 1 DMRS RX] m_prime=%d; m=%d; w_i_m=%+.3f%+.3f", m_prime, m, __real__ w_i_m, __imag__ w_i_m); + srsran_vec_fprint_c(stdout, z, SRSRAN_NRE); + } + + // TODO: can ce[m] overflow? + // Calculate least square estimates for this symbol + srsran_vec_prod_conj_ccc(slot_symbols_ptr, z, ce[n_pucch_sum], SRSRAN_NRE); + + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { + printf("[PUCCH Format 1 DMRS RX] ce[%d]=", n_pucch_sum); + srsran_vec_fprint_c(stdout, ce[n_pucch_sum], SRSRAN_NRE); + } + n_pucch_sum++; + } } // Perform measurements @@ -228,7 +264,7 @@ int srsran_dmrs_pucch_format1_estimate(const srsran_pucch_nr_t* q, float epre = 0.0f; float ta_err = 0.0f; cf_t corr[SRSRAN_PUCCH_NR_FORMAT1_N_MAX] = {}; - for (uint32_t m = 0; m < n_pucch; m++) { + for (uint32_t m = 0; m < n_pucch_sum; m++) { corr[m] = srsran_vec_acc_cc(ce[m], SRSRAN_NRE) / SRSRAN_NRE; rsrp += SRSRAN_CSQABS(corr[m]); epre += srsran_vec_avg_power_cf(ce[m], SRSRAN_NRE); @@ -236,9 +272,9 @@ int srsran_dmrs_pucch_format1_estimate(const srsran_pucch_nr_t* q, } // Average measurements - rsrp /= n_pucch; - epre /= n_pucch; - ta_err /= n_pucch; + rsrp /= n_pucch_sum; + epre /= n_pucch_sum; + ta_err /= n_pucch_sum; // Set power measures rsrp = SRSRAN_MIN(rsrp, epre); @@ -262,16 +298,16 @@ int srsran_dmrs_pucch_format1_estimate(const srsran_pucch_nr_t* q, } // Measure CFO - if (n_pucch > 1) { + if (n_pucch_sum > 1) { float cfo_avg_hz = 0.0f; - for (uint32_t m = 0; m < n_pucch - 1; m++) { + for (uint32_t m = 0; m < n_pucch_sum - 1; m++) { uint32_t l0 = resource->start_symbol_idx + m * 2; uint32_t l1 = resource->start_symbol_idx + (m + 1) * 2; float time_diff = srsran_symbol_distance_s(l0, l1, q->carrier.scs); float phase_diff = cargf(corr[m + 1] * conjf(corr[m])); if (isnormal(time_diff)) { - cfo_avg_hz += phase_diff / (2.0f * M_PI * time_diff * (n_pucch - 1)); + cfo_avg_hz += phase_diff / (2.0f * M_PI * time_diff * (n_pucch_sum - 1)); } } res->cfo_hz = cfo_avg_hz; @@ -283,11 +319,10 @@ int srsran_dmrs_pucch_format1_estimate(const srsran_pucch_nr_t* q, // ... Not implemented // Interpolates between DMRS symbols - for (uint32_t m = 0; m < n_pucch; m++) { - uint32_t l = m * 2 + 1; - cf_t* ce_ptr = &res->ce[(q->carrier.nof_prb * (l + l_prime) + resource->starting_prb) * SRSRAN_NRE]; + for (uint32_t m = 0; m < n_pucch_sum; m++) { + cf_t* ce_ptr = &res->ce[m * SRSRAN_NRE]; - if (m != n_pucch - 1) { + if (m != n_pucch_sum - 1) { // If it is not the last symbol with DMRS, average between srsran_vec_sum_ccc(ce[m], ce[m + 1], ce_ptr, SRSRAN_NRE); srsran_vec_sc_prod_cfc(ce_ptr, 0.5f, ce_ptr, SRSRAN_NRE); @@ -297,7 +332,7 @@ int srsran_dmrs_pucch_format1_estimate(const srsran_pucch_nr_t* q, srsran_vec_sub_ccc(ce_ptr, ce[m - 1], ce_ptr, SRSRAN_NRE); srsran_vec_sc_prod_cfc(ce_ptr, 0.5f, ce_ptr, SRSRAN_NRE); } else { - // Simply copy the + // Simply copy the estimated channel srsran_vec_cf_copy(ce_ptr, ce[m], SRSRAN_NRE); } } diff --git a/lib/src/phy/phch/pucch_cfg_nr.c b/lib/src/phy/phch/pucch_cfg_nr.c index 3e79902cb..1f9675538 100644 --- a/lib/src/phy/phch/pucch_cfg_nr.c +++ b/lib/src/phy/phch/pucch_cfg_nr.c @@ -160,7 +160,8 @@ int srsran_pucch_nr_cfg_resource_valid(const srsran_pucch_nr_resource_t* resourc return SRSRAN_ERROR; } - if (resource->intra_slot_hopping) { + // Frequency hopping is only possible with Format 1 + if (resource->intra_slot_hopping && resource->format != SRSRAN_PUCCH_NR_FORMAT_1) { ERROR("Intra-slot hopping is not implemented"); return SRSRAN_ERROR; } diff --git a/lib/src/phy/phch/pucch_nr.c b/lib/src/phy/phch/pucch_nr.c index 9c0472d38..72e74d816 100644 --- a/lib/src/phy/phch/pucch_nr.c +++ b/lib/src/phy/phch/pucch_nr.c @@ -397,6 +397,8 @@ int srsran_pucch_nr_format1_encode(const srsran_pucch_nr_t* q, srsran_mod_modulate(&q->qpsk, b, d, 2); } + INFO("[PUCCH Format 1 Data TX] d=%+.3f%+.3f", __real__ d[0], __imag__ d[0]); + // Get group sequence uint32_t u = 0; uint32_t v = 0; @@ -405,41 +407,60 @@ int srsran_pucch_nr_format1_encode(const srsran_pucch_nr_t* q, return SRSRAN_ERROR; } - // Calculate number of symbols carrying PUCCH (No DMRS) - uint32_t n_pucch = pucch_nr_format1_n_pucch(resource, 0); - + // First symbol of this PUCCH transmission uint32_t l_prime = resource->start_symbol_idx; - for (uint32_t l = 1, m = 0; l < resource->nof_symbols; l += 2, m++) { - // Get start of the sequence in resource grid - cf_t* slot_symbols_ptr = &slot_symbols[(q->carrier.nof_prb * (l + l_prime) + resource->starting_prb) * SRSRAN_NRE]; - // Get Alpha index - uint32_t alpha_idx = 0; - if (srsran_pucch_nr_alpha_idx(&q->carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, 0, &alpha_idx) < - SRSRAN_SUCCESS) { - return SRSRAN_ERROR; - } + // For each hop + for (uint32_t m_prime = 0, l = 1; m_prime < (resource->intra_slot_hopping ? 2 : 1); m_prime++) { + // Calculate number of symbols carrying PUCCH (No DMRS) + uint32_t n_pucch = pucch_nr_format1_n_pucch(resource, m_prime); - // get r_uv sequence from LUT object - const cf_t* r_uv = srsran_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx); - if (r_uv == NULL) { - ERROR("Getting r_uv sequence"); - return SRSRAN_ERROR; - } + // Get the starting PRB + uint32_t starting_prb = (m_prime == 0) ? resource->starting_prb : resource->second_hop_prb; - // Compute y = d(0) * r_uv - cf_t y[SRSRAN_NRE]; - srsran_vec_sc_prod_ccc(r_uv, d[0], y, SRSRAN_NRE); + // For each symbol carrying PUCCH data + for (uint32_t m = 0; m < n_pucch; m++, l += 2) { + // Get start of the sequence in resource grid + cf_t* slot_symbols_ptr = &slot_symbols[(q->carrier.nof_prb * (l + l_prime) + starting_prb) * SRSRAN_NRE]; - // Get w_i_m - cf_t w_i_m = srsran_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m); + // Get Alpha index + uint32_t alpha_idx = 0; + if (srsran_pucch_nr_alpha_idx(&q->carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, 0, &alpha_idx) < + SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // get r_uv sequence from LUT object + const cf_t* r_uv = srsran_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx); + if (r_uv == NULL) { + ERROR("Getting r_uv sequence"); + return SRSRAN_ERROR; + } + + // Get w_i_m + cf_t w_i_m = srsran_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m); - // Compute z(n) = w(i) * y(n) - cf_t z[SRSRAN_NRE]; - srsran_vec_sc_prod_ccc(y, w_i_m, z, SRSRAN_NRE); + // Compute z(n) = w(i) * r_uv(n) + cf_t z[SRSRAN_NRE]; + srsran_vec_sc_prod_ccc(r_uv, w_i_m, z, SRSRAN_NRE); - // Put z in the grid - srsran_vec_cf_copy(slot_symbols_ptr, z, SRSRAN_NRE); + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { + printf("[PUCCH Format 1 Data TX] m_prime=%d; m=%d; w_i_m=%+.3f%+.3f z=", + m_prime, + m, + __real__ w_i_m, + __imag__ w_i_m); + srsran_vec_fprint_c(stdout, z, SRSRAN_NRE); + } + + // Put z in the grid + srsran_vec_sc_prod_ccc(z, d[0], slot_symbols_ptr, SRSRAN_NRE); + + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { + printf("[PUCCH Format 1 TX] l=%d; x=", l + l_prime); + srsran_vec_fprint_c(stdout, slot_symbols_ptr, SRSRAN_NRE); + } + } } return SRSRAN_SUCCESS; @@ -484,46 +505,79 @@ int srsran_pucch_nr_format1_decode(srsran_pucch_nr_t* q, return SRSRAN_ERROR; } - // Calculate number of symbols carrying PUCCH (No DMRS) - uint32_t n_pucch = pucch_nr_format1_n_pucch(resource, 0); - + // First symbol of this PUCCH transmission uint32_t l_prime = resource->start_symbol_idx; - for (uint32_t l = 1, m = 0; l < resource->nof_symbols; l += 2, m++) { - // Get start of the sequence in resource grid - cf_t* slot_symbols_ptr = &slot_symbols[(q->carrier.nof_prb * (l + l_prime) + resource->starting_prb) * SRSRAN_NRE]; - cf_t* ce_ptr = &chest_res->ce[(q->carrier.nof_prb * (l + l_prime) + resource->starting_prb) * SRSRAN_NRE]; - // Equalise x = w(i) * d' * r_uv(n) - cf_t x[SRSRAN_NRE]; - srsran_predecoding_single(slot_symbols_ptr, ce_ptr, x, NULL, SRSRAN_NRE, 1.0f, chest_res->noise_estimate); + // For each hop + uint32_t n_pucch_sum = 0; + for (uint32_t m_prime = 0, l = 1; m_prime < (resource->intra_slot_hopping ? 2 : 1); m_prime++) { + // Calculate number of symbols carrying PUCCH (No DMRS) + uint32_t n_pucch = pucch_nr_format1_n_pucch(resource, m_prime); + + // Get the starting PRB + uint32_t starting_prb = (m_prime == 0) ? resource->starting_prb : resource->second_hop_prb; + + // For each symbol carrying PUCCH data + for (uint32_t m = 0; m < n_pucch; m++, l += 2) { + // Get start of the sequence in resource grid + cf_t* slot_symbols_ptr = &slot_symbols[(q->carrier.nof_prb * (l + l_prime) + starting_prb) * SRSRAN_NRE]; + cf_t* ce_ptr = &chest_res->ce[SRSRAN_NRE * n_pucch_sum]; + n_pucch_sum++; + + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { + printf("[PUCCH Format 1 CE RX] ce="); + srsran_vec_fprint_c(stdout, ce_ptr, SRSRAN_NRE); + } - // Get Alpha index - uint32_t alpha_idx = 0; - if (srsran_pucch_nr_alpha_idx( - &q->carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, m_cs, &alpha_idx) < SRSRAN_SUCCESS) { - return SRSRAN_ERROR; - } + // Equalise x = w(i) * d' * r_uv(n) + cf_t x[SRSRAN_NRE]; + srsran_predecoding_single(slot_symbols_ptr, ce_ptr, x, NULL, SRSRAN_NRE, 1.0f, chest_res->noise_estimate); - // get r_uv sequence from LUT object - const cf_t* r_uv = srsran_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx); - if (r_uv == NULL) { - ERROR("Getting r_uv sequence"); - return SRSRAN_ERROR; - } - // Get w_i_m - cf_t w_i_m = srsran_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m); + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { + printf("[PUCCH Format 1 RX] l=%d; x=", l + l_prime); + srsran_vec_fprint_c(stdout, x, SRSRAN_NRE); + } - // Compute z(n) = w(i) * r_uv(n) - cf_t z[SRSRAN_NRE]; - srsran_vec_sc_prod_ccc(r_uv, w_i_m, z, SRSRAN_NRE); + // Get Alpha index + uint32_t alpha_idx = 0; + if (srsran_pucch_nr_alpha_idx( + &q->carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, m_cs, &alpha_idx) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // get r_uv sequence from LUT object + const cf_t* r_uv = srsran_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx); + if (r_uv == NULL) { + ERROR("Getting r_uv sequence"); + return SRSRAN_ERROR; + } - // Compute d = sum(x * conj(w(i) * r_uv(n))) = sum(w(i) * d' * r_uv(n) * conj(w(i) * r_uv(n))) = d' - d += srsran_vec_dot_prod_conj_ccc(x, z, SRSRAN_NRE) / SRSRAN_NRE; + // Get w_i_m + cf_t w_i_m = srsran_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m); - // Compute and accumulate average symbol power - pwr_acc += srsran_vec_avg_power_cf(x, SRSRAN_NRE); + // Compute z(n) = w(i) * r_uv(n) + cf_t z[SRSRAN_NRE]; + srsran_vec_sc_prod_ccc(r_uv, w_i_m, z, SRSRAN_NRE); + + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { + printf("[PUCCH Format 1 Data RX] m_prime=%d; m=%d; w_i_m=%+.3f%+.3f z=", + m_prime, + m, + __real__ w_i_m, + __imag__ w_i_m); + srsran_vec_fprint_c(stdout, z, SRSRAN_NRE); + } + + // Compute d = sum(x * conj(w(i) * r_uv(n))) = sum(w(i) * d' * r_uv(n) * conj(w(i) * r_uv(n))) = d' + d += srsran_vec_dot_prod_conj_ccc(x, z, SRSRAN_NRE) / SRSRAN_NRE; + + // Compute and accumulate average symbol power + pwr_acc += srsran_vec_avg_power_cf(x, SRSRAN_NRE); + } } + INFO("[PUCCH Format 1 Data RX] d=%+.3f%+.3f", __real__ d, __imag__ d); + // Demodulate d float llr[SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS]; srsran_demod_soft_demodulate((nof_bits == 1) ? SRSRAN_MOD_BPSK : SRSRAN_MOD_QPSK, &d, llr, 1); diff --git a/lib/src/phy/phch/test/pucch_nr_test.c b/lib/src/phy/phch/test/pucch_nr_test.c index 6779549de..d485b8099 100644 --- a/lib/src/phy/phch/test/pucch_nr_test.c +++ b/lib/src/phy/phch/test/pucch_nr_test.c @@ -79,63 +79,71 @@ static int test_pucch_format0(srsran_pucch_nr_t* pucch, const srsran_pucch_nr_co static int test_pucch_format1(srsran_pucch_nr_t* pucch, const srsran_pucch_nr_common_cfg_t* cfg, srsran_chest_ul_res_t* chest_res, - cf_t* slot_symbols) + cf_t* slot_symbols, + bool enable_intra_slot_hopping) { srsran_slot_cfg_t slot = {}; srsran_pucch_nr_resource_t resource = {}; resource.format = SRSRAN_PUCCH_NR_FORMAT_1; + resource.intra_slot_hopping = enable_intra_slot_hopping; for (slot.idx = 0; slot.idx < SRSRAN_NSLOTS_PER_FRAME_NR(carrier.scs); slot.idx++) { for (resource.starting_prb = 0; resource.starting_prb < carrier.nof_prb; resource.starting_prb += starting_prb_stride) { - for (resource.nof_symbols = SRSRAN_PUCCH_NR_FORMAT1_MIN_NSYMB; - resource.nof_symbols <= SRSRAN_PUCCH_NR_FORMAT1_MAX_NSYMB; - resource.nof_symbols++) { - for (resource.start_symbol_idx = 0; - resource.start_symbol_idx <= - SRSRAN_MIN(SRSRAN_PUCCH_NR_FORMAT1_MAX_STARTSYMB, SRSRAN_NSYMB_PER_SLOT_NR - resource.nof_symbols); - resource.start_symbol_idx += starting_symbol_stride) { - for (resource.time_domain_occ = 0; resource.time_domain_occ <= SRSRAN_PUCCH_NR_FORMAT1_MAX_TOCC; - resource.time_domain_occ++) { - for (resource.initial_cyclic_shift = 0; resource.initial_cyclic_shift <= SRSRAN_PUCCH_NR_FORMAT1_MAX_CS; - resource.initial_cyclic_shift++) { - for (uint32_t nof_bits = 1; nof_bits <= SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS; nof_bits++) { - for (uint32_t word = 0; word < (1U << nof_bits); word++) { - // Generate bits - uint8_t b[SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS] = {}; - for (uint32_t i = 0; i < nof_bits; i++) { - b[i] = (word >> i) & 1U; - } - - // Encode PUCCH - TESTASSERT(srsran_pucch_nr_format1_encode(pucch, cfg, &slot, &resource, b, nof_bits, slot_symbols) == - SRSRAN_SUCCESS); - - // Put DMRS - TESTASSERT(srsran_dmrs_pucch_format1_put(pucch, &carrier, cfg, &slot, &resource, slot_symbols) == - SRSRAN_SUCCESS); - - // Apply AWGN - srsran_channel_awgn_run_c( - &awgn, slot_symbols, slot_symbols, carrier.nof_prb * SRSRAN_NRE * SRSRAN_NSYMB_PER_SLOT_NR); - - // Estimate channel - TESTASSERT(srsran_dmrs_pucch_format1_estimate( - pucch, cfg, &slot, &resource, slot_symbols, chest_res) == SRSRAN_SUCCESS); - - TESTASSERT(fabsf(chest_res->rsrp_dBfs - 0.0f) < 3.0f); - TESTASSERT(fabsf(chest_res->epre_dBfs - 0.0f) < 3.0f); - TESTASSERT(fabsf(chest_res->snr_db - snr_db) < 10.0f); - - // Decode PUCCH - uint8_t b_rx[SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS]; - TESTASSERT(srsran_pucch_nr_format1_decode( - pucch, cfg, &slot, &resource, chest_res, slot_symbols, b_rx, nof_bits, NULL) == - SRSRAN_SUCCESS); - - // Check received bits - for (uint32_t i = 0; i < nof_bits; i++) { - TESTASSERT(b[i] == b_rx[i]); + for (resource.second_hop_prb = 0; resource.second_hop_prb < (enable_intra_slot_hopping) ? carrier.nof_prb : 0; + resource.second_hop_prb += starting_prb_stride) { + for (resource.nof_symbols = SRSRAN_PUCCH_NR_FORMAT1_MIN_NSYMB; + resource.nof_symbols <= SRSRAN_PUCCH_NR_FORMAT1_MAX_NSYMB; + resource.nof_symbols++) { + for (resource.start_symbol_idx = 0; + resource.start_symbol_idx <= + SRSRAN_MIN(SRSRAN_PUCCH_NR_FORMAT1_MAX_STARTSYMB, SRSRAN_NSYMB_PER_SLOT_NR - resource.nof_symbols); + resource.start_symbol_idx += starting_symbol_stride) { + for (resource.time_domain_occ = 0; resource.time_domain_occ <= SRSRAN_PUCCH_NR_FORMAT1_MAX_TOCC; + resource.time_domain_occ++) { + for (resource.initial_cyclic_shift = 0; resource.initial_cyclic_shift <= SRSRAN_PUCCH_NR_FORMAT1_MAX_CS; + resource.initial_cyclic_shift++) { + for (uint32_t nof_bits = 1; nof_bits <= SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS; nof_bits++) { + for (uint32_t word = 0; word < (1U << nof_bits); word++) { + // Generate bits + uint8_t b[SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS] = {}; + for (uint32_t i = 0; i < nof_bits; i++) { + b[i] = (word >> i) & 1U; + } + + // Encode PUCCH + TESTASSERT(srsran_pucch_nr_format1_encode( + pucch, cfg, &slot, &resource, b, nof_bits, slot_symbols) == SRSRAN_SUCCESS); + + // Put DMRS + TESTASSERT(srsran_dmrs_pucch_format1_put(pucch, &carrier, cfg, &slot, &resource, slot_symbols) == + SRSRAN_SUCCESS); + + // Apply AWGN + srsran_channel_awgn_run_c( + &awgn, slot_symbols, slot_symbols, carrier.nof_prb * SRSRAN_NRE * SRSRAN_NSYMB_PER_SLOT_NR); + + // Estimate channel + TESTASSERT(srsran_dmrs_pucch_format1_estimate( + pucch, cfg, &slot, &resource, slot_symbols, chest_res) == SRSRAN_SUCCESS); + + TESTASSERT(fabsf(chest_res->rsrp_dBfs - 0.0f) < 3.0f); + TESTASSERT(fabsf(chest_res->epre_dBfs - 0.0f) < 3.0f); + TESTASSERT(fabsf(chest_res->snr_db - snr_db) < 10.0f); + + // Decode PUCCH + uint8_t b_rx[SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS]; + TESTASSERT(srsran_pucch_nr_format1_decode( + pucch, cfg, &slot, &resource, chest_res, slot_symbols, b_rx, nof_bits, NULL) == + SRSRAN_SUCCESS); + + // Check received bits + for (uint32_t i = 0; i < nof_bits; i++) { + if (b[i] != b_rx[i]) { + printf("aaa"); + } + TESTASSERT(b[i] == b_rx[i]); + } } } } @@ -336,9 +344,13 @@ int main(int argc, char** argv) } } - // Test Format 1 + // Test Format 1 with and without intra slot frequency hopping if (format < 0 || format == 1) { - if (test_pucch_format1(&pucch, &common_cfg, &chest_res, slot_symb) < SRSRAN_SUCCESS) { + if (test_pucch_format1(&pucch, &common_cfg, &chest_res, slot_symb, false) < SRSRAN_SUCCESS) { + ERROR("Failed PUCCH format 1"); + goto clean_exit; + } + if (test_pucch_format1(&pucch, &common_cfg, &chest_res, slot_symb, true) < SRSRAN_SUCCESS) { ERROR("Failed PUCCH format 1"); goto clean_exit; } From f57cf0b19512a4a6835cb9f0cfd070e2fbd7db39 Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Thu, 10 Mar 2022 18:53:57 +0100 Subject: [PATCH 046/195] PHY: Initial PUCCH default resource selection (hard-coded row index 11) --- lib/include/srsran/phy/phch/harq_ack_cfg.h | 1 + lib/src/phy/phch/harq_ack.c | 2 ++ lib/src/phy/phch/ra_ul_nr.c | 25 ++++++++++++++++++++-- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/lib/include/srsran/phy/phch/harq_ack_cfg.h b/lib/include/srsran/phy/phch/harq_ack_cfg.h index 870947e93..cca04bfba 100644 --- a/lib/include/srsran/phy/phch/harq_ack_cfg.h +++ b/lib/include/srsran/phy/phch/harq_ack_cfg.h @@ -33,6 +33,7 @@ typedef struct { uint32_t pid; ///< HARQ process identifier uint16_t rnti; uint32_t pucch_resource_id; + uint32_t n_cce; } srsran_harq_ack_resource_t; /** diff --git a/lib/src/phy/phch/harq_ack.c b/lib/src/phy/phch/harq_ack.c index f53c52f70..d67fdb813 100644 --- a/lib/src/phy/phch/harq_ack.c +++ b/lib/src/phy/phch/harq_ack.c @@ -79,6 +79,7 @@ static int harq_ack_gen_ack_type2(const srsran_harq_ack_cfg_hl_t* cfg, if (ack->present) { // Load ACK resource data into UCI info uci_cfg->pucch.resource_id = ack_info->cc[c].m[m].resource.pucch_resource_id; + uci_cfg->pucch.n_cce_0 = ack_info->cc[c].m[m].resource.n_cce; uci_cfg->pucch.rnti = ack_info->cc[c].m[m].resource.rnti; if (V_DL_CDAI <= V_temp) { @@ -174,6 +175,7 @@ int srsran_harq_ack_resource(const srsran_harq_ack_cfg_hl_t* cfg, pdsch_ack_resource->v_dai_dl = dci_dl->dai; pdsch_ack_resource->rnti = dci_dl->ctx.rnti; pdsch_ack_resource->pucch_resource_id = dci_dl->pucch_resource; + pdsch_ack_resource->n_cce = dci_dl->ctx.location.ncce; pdsch_ack_resource->pid = dci_dl->pid; return SRSRAN_SUCCESS; diff --git a/lib/src/phy/phch/ra_ul_nr.c b/lib/src/phy/phch/ra_ul_nr.c index 92ff87088..66567301e 100644 --- a/lib/src/phy/phch/ra_ul_nr.c +++ b/lib/src/phy/phch/ra_ul_nr.c @@ -473,7 +473,27 @@ int srsran_ra_ul_nr_freq(const srsran_carrier_nr_t* carrier, // Implements TS 38.213 Table 9.2.1-1: PUCCH resource sets before dedicated PUCCH resource configuration static int ra_ul_nr_pucch_resource_default(uint32_t r_pucch, srsran_pucch_nr_resource_t* resource) { - ERROR("Not implemented"); + // From BWP config + uint32_t N_size_bwp = 52; + + // From row of index 11 + uint32_t rb_offset_bwp = 0; + uint32_t N_cs = 2; + uint32_t cs_v[2] = {0, 6}; + + resource->start_symbol_idx = 0; + resource->nof_symbols = 14; + + if (r_pucch / 8 == 0) { + resource->starting_prb = rb_offset_bwp + SRSRAN_FLOOR(r_pucch, N_cs); + resource->second_hop_prb = N_size_bwp - 1 - resource->starting_prb; + resource->initial_cyclic_shift = cs_v[r_pucch % N_cs]; + } else { + resource->second_hop_prb = rb_offset_bwp + SRSRAN_FLOOR(r_pucch - 8, N_cs); + resource->starting_prb = N_size_bwp - 1 - resource->second_hop_prb; + resource->initial_cyclic_shift = cs_v[(r_pucch - 8) % N_cs]; + } + return SRSRAN_ERROR; } @@ -623,7 +643,8 @@ int srsran_ra_ul_nr_pucch_resource(const srsran_pucch_nr_hl_cfg_t* pucch_cfg, // a PUCCH resource set is provided by pucch-ResourceCommon through an index to a row of Table 9.2.1-1 for size // transmission of HARQ-ACK information on PUCCH in an initial UL BWP of N BWP PRBs. if (!pucch_cfg->enabled) { - uint32_t r_pucch = (2 * uci_cfg->pucch.n_cce_0) + 2 * uci_cfg->pucch.resource_id; + uint32_t N_cce = SRSRAN_FLOOR(52, 6); + uint32_t r_pucch = ((2 * uci_cfg->pucch.n_cce_0) / N_cce) + 2 * uci_cfg->pucch.resource_id; return ra_ul_nr_pucch_resource_default(r_pucch, resource); } return ra_ul_nr_pucch_resource_hl(pucch_cfg, uci_cfg, uci_cfg->pucch.resource_id, resource); From 21cb5858f04e1ea5cdd9dc4071a1fcd61a71788e Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Wed, 9 Mar 2022 12:05:53 +0100 Subject: [PATCH 047/195] cmake: fix warning when searching for SKIQ Replace INCLUDE(FindPkgConfig) by FIND_PACKAGE(PkgConfig REQUIRED) to avoid following warning: warning: The package name passed to `find_package_handle_standard_args` (PkgConfig) does not match the name of the calling package (SKIQ). This can lead to problems in calling code that expects `find_package` result variables (e.g., `_FOUND`) to follow a certain pattern. --- cmake/modules/FindSKIQ.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/modules/FindSKIQ.cmake b/cmake/modules/FindSKIQ.cmake index 45555a676..764ccfcf2 100644 --- a/cmake/modules/FindSKIQ.cmake +++ b/cmake/modules/FindSKIQ.cmake @@ -6,7 +6,7 @@ # the distribution. # -INCLUDE(FindPkgConfig) +FIND_PACKAGE(PkgConfig REQUIRED) #PKG_CHECK_MODULES(SKIQ SKIQ) IF(NOT SKIQ_FOUND) @@ -14,7 +14,7 @@ FIND_PATH( SKIQ_INCLUDE_DIRS NAMES sidekiq_api.h HINTS $ENV{SKIQ_DIR}/inc - $ENV{SKIQ_DIR}/sidekiq_core/inc + $ENV{SKIQ_DIR}/sidekiq_core/inc PATHS /usr/local/include /usr/include ) From 11ac8c25294c75fb0a0a9a9f93072fa117117fba Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Thu, 10 Mar 2022 14:10:07 +0100 Subject: [PATCH 048/195] lib,rlc_am_nr: add support for 18-bit SNs and first bunch of corresponding unit tests --- .../srsran/interfaces/rlc_interface_types.h | 26 ++- lib/include/srsran/rlc/rlc_am_nr.h | 22 +- lib/include/srsran/rlc/rlc_common.h | 2 + lib/src/rlc/rlc_am_nr.cc | 49 ++--- lib/test/rlc/rlc_am_nr_test.cc | 190 +++++++++++------- 5 files changed, 181 insertions(+), 108 deletions(-) diff --git a/lib/include/srsran/interfaces/rlc_interface_types.h b/lib/include/srsran/interfaces/rlc_interface_types.h index 0f4e069cc..9c6ba8512 100644 --- a/lib/include/srsran/interfaces/rlc_interface_types.h +++ b/lib/include/srsran/interfaces/rlc_interface_types.h @@ -69,6 +69,15 @@ inline uint16_t to_number(const rlc_am_nr_sn_size_t& sn_size) constexpr static uint16_t options[] = {12, 18}; return enum_to_number(options, (uint32_t)rlc_mode_t::nulltype, (uint32_t)sn_size); } +/** + * @brief Value range of the serial numbers + * @param sn_size Length of the serial number field in bits + * @return cardianlity + */ +inline uint32_t cardinality(const rlc_am_nr_sn_size_t& sn_size) +{ + return (1 << to_number(sn_size)); +} struct rlc_am_config_t { /**************************************************************************** @@ -219,11 +228,20 @@ public: rlc_cnfg.am.t_poll_retx = 5; return rlc_cnfg; } - static rlc_config_t default_rlc_am_nr_config() + static rlc_config_t default_rlc_am_nr_config(uint32_t sn_size = 12) { - rlc_config_t rlc_cnfg = {}; - rlc_cnfg.rat = srsran_rat_t::nr; - rlc_cnfg.rlc_mode = rlc_mode_t::am; + rlc_config_t rlc_cnfg = {}; + rlc_cnfg.rat = srsran_rat_t::nr; + rlc_cnfg.rlc_mode = rlc_mode_t::am; + if (sn_size == 12) { + rlc_cnfg.am_nr.tx_sn_field_length = srsran::rlc_am_nr_sn_size_t::size12bits; + rlc_cnfg.am_nr.rx_sn_field_length = srsran::rlc_am_nr_sn_size_t::size12bits; + } else if (sn_size == 18) { + rlc_cnfg.am_nr.tx_sn_field_length = srsran::rlc_am_nr_sn_size_t::size18bits; + rlc_cnfg.am_nr.rx_sn_field_length = srsran::rlc_am_nr_sn_size_t::size18bits; + } else { + return {}; + } rlc_cnfg.am_nr.t_status_prohibit = 8; rlc_cnfg.am_nr.max_retx_thresh = 4; rlc_cnfg.am_nr.t_reassembly = 35; diff --git a/lib/include/srsran/rlc/rlc_am_nr.h b/lib/include/srsran/rlc/rlc_am_nr.h index 220d1a41e..bf6ea2dd4 100644 --- a/lib/include/srsran/rlc/rlc_am_nr.h +++ b/lib/include/srsran/rlc/rlc_am_nr.h @@ -147,10 +147,10 @@ private: * Ref: 3GPP TS 38.322 version 16.2.0 Section 7.1 ***************************************************************************/ struct rlc_am_nr_tx_state_t st = {}; - rlc_ringbuffer_t tx_window; + rlc_ringbuffer_t tx_window; // Queues and buffers - pdu_retx_queue retx_queue; + pdu_retx_queue retx_queue; uint32_t sdu_under_segmentation_sn = INVALID_RLC_SN; // SN of the SDU currently being segmented. pdcp_sn_vector_t notify_info_vec; @@ -159,6 +159,12 @@ private: uint32_t so_size = 2; uint32_t max_hdr_size = 4; + /**************************************************************************** + * Tx constants + * Ref: 3GPP TS 38.322 version 16.2.0 Section 7.2 + ***************************************************************************/ + inline uint32_t tx_window_size() const; + public: // Getters/Setters void set_tx_state(const rlc_am_nr_tx_state_t& st_) { st = st_; } // This should only be used for testing. @@ -240,11 +246,11 @@ private: rlc_am_nr_tx* tx = nullptr; byte_buffer_pool* pool = nullptr; - uint32_t mod_nr = 4096; + uint32_t mod_nr = cardinality(rlc_am_nr_sn_size_t()); uint32_t rx_mod_base_nr(uint32_t sn) const; // RX Window - rlc_ringbuffer_t rx_window; + rlc_ringbuffer_t rx_window; // Mutexes std::mutex mutex; @@ -263,11 +269,17 @@ private: rlc_am_nr_config_t cfg = {}; /**************************************************************************** - * Tx state variables + * Rx state variables * Ref: 3GPP TS 38.322 version 16.2.0 Section 7.1 ***************************************************************************/ struct rlc_am_nr_rx_state_t st = {}; + /**************************************************************************** + * Rx constants + * Ref: 3GPP TS 38.322 version 16.2.0 Section 7.2 + ***************************************************************************/ + inline uint32_t rx_window_size() const; + public: // Getters/Setters void set_rx_state(const rlc_am_nr_rx_state_t& st_) { st = st_; } // This should only be used for testing. diff --git a/lib/include/srsran/rlc/rlc_common.h b/lib/include/srsran/rlc/rlc_common.h index 935096907..049c7983c 100644 --- a/lib/include/srsran/rlc/rlc_common.h +++ b/lib/include/srsran/rlc/rlc_common.h @@ -34,6 +34,8 @@ namespace srsran { #define RLC_MAX_SDU_SIZE ((1 << 11) - 1) // Length of LI field is 11bits #define RLC_AM_MIN_DATA_PDU_SIZE (3) // AMD PDU with 10 bit SN (length of LI field is 11 bits) (No LI) +#define RLC_AM_NR_WINDOW_SIZE 2048 + #define RlcDebug(fmt, ...) logger.debug("%s: " fmt, rb_name, ##__VA_ARGS__) #define RlcInfo(fmt, ...) logger.info("%s: " fmt, rb_name, ##__VA_ARGS__) #define RlcWarning(fmt, ...) logger.warning("%s: " fmt, rb_name, ##__VA_ARGS__) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index eed20d144..0456e1e56 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -20,8 +20,6 @@ #include #include -#define RLC_AM_NR_WINDOW_SIZE 2048 - namespace srsran { const static uint32_t max_tx_queue_size = 256; @@ -41,11 +39,6 @@ bool rlc_am_nr_tx::configure(const rlc_config_t& cfg_) cfg = cfg_.am_nr; rb_name = parent->rb_name; - if (cfg.tx_sn_field_length != rlc_am_nr_sn_size_t::size12bits) { - RlcWarning("RLC AM NR only supports 12 bit SN length."); - return false; - } - if (cfg_.tx_queue_length > max_tx_queue_size) { RlcError("configuring tx queue length of %d PDUs too big. Maximum value is %d.", cfg_.tx_queue_length, @@ -53,7 +46,7 @@ bool rlc_am_nr_tx::configure(const rlc_config_t& cfg_) return false; } - mod_nr = cfg.tx_sn_field_length == rlc_am_nr_sn_size_t::size12bits ? 4096 : 262144; + mod_nr = cardinality(cfg.tx_sn_field_length); min_hdr_size = cfg.tx_sn_field_length == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; max_hdr_size = min_hdr_size + so_size; @@ -186,7 +179,7 @@ uint32_t rlc_am_nr_tx::build_new_pdu(uint8_t* payload, uint32_t nof_bytes) hdr.dc = RLC_DC_FIELD_DATA_PDU; hdr.p = get_pdu_poll(); hdr.si = rlc_nr_si_field_t::full_sdu; - hdr.sn_size = rlc_am_nr_sn_size_t::size12bits; + hdr.sn_size = cfg.tx_sn_field_length; hdr.sn = st.tx_next; tx_pdu.header = hdr; log_rlc_am_nr_pdu_header_to_string(logger.info, hdr, rb_name); @@ -243,7 +236,7 @@ uint32_t rlc_am_nr_tx::build_new_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, uint8_t* hdr.dc = RLC_DC_FIELD_DATA_PDU; hdr.p = get_pdu_poll(); hdr.si = rlc_nr_si_field_t::first_segment; - hdr.sn_size = rlc_am_nr_sn_size_t::size12bits; + hdr.sn_size = cfg.tx_sn_field_length; hdr.sn = st.tx_next; hdr.so = 0; tx_pdu.header = hdr; @@ -345,7 +338,7 @@ uint32_t rlc_am_nr_tx::build_continuation_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, hdr.dc = RLC_DC_FIELD_DATA_PDU; hdr.p = get_pdu_poll(); hdr.si = si; - hdr.sn_size = rlc_am_nr_sn_size_t::size12bits; + hdr.sn_size = cfg.tx_sn_field_length; hdr.sn = st.tx_next; hdr.so = last_byte; tx_pdu.header = hdr; @@ -673,7 +666,7 @@ uint32_t rlc_am_nr_tx::build_status_pdu(byte_buffer_t* payload, uint32_t nof_byt } else if (pdu_len > 0 && nof_bytes >= static_cast(pdu_len)) { RlcDebug("generated status PDU. Bytes:%d", pdu_len); log_rlc_am_nr_status_pdu_to_string(logger.info, "tx status PDU - %s", &tx_status, rb_name); - pdu_len = rlc_am_nr_write_status_pdu(tx_status, rlc_am_nr_sn_size_t::size12bits, payload); + pdu_len = rlc_am_nr_write_status_pdu(tx_status, cfg.tx_sn_field_length, payload); } else { RlcInfo("cannot tx status PDU - %d bytes available, %d bytes required", nof_bytes, pdu_len); pdu_len = 0; @@ -690,7 +683,7 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) rlc_am_nr_status_pdu_t status = {}; RlcHexDebug(payload, nof_bytes, "%s Rx control PDU", parent->rb_name); - rlc_am_nr_read_status_pdu(payload, nof_bytes, rlc_am_nr_sn_size_t::size12bits, &status); + rlc_am_nr_read_status_pdu(payload, nof_bytes, cfg.tx_sn_field_length, &status); log_rlc_am_nr_status_pdu_to_string(logger.info, "Rx Status PDU: %s", &status, parent->rb_name); // Local variables for handling Status PDU will be updated with lock /* @@ -925,10 +918,15 @@ uint32_t rlc_am_nr_tx::tx_mod_base_nr(uint32_t sn) const return (sn - st.tx_next_ack) % mod_nr; } +uint32_t rlc_am_nr_tx::tx_window_size() const +{ + return cardinality(cfg.tx_sn_field_length) / 2; +} + bool rlc_am_nr_tx::inside_tx_window(uint32_t sn) const { // TX_Next_Ack <= SN < TX_Next_Ack + AM_Window_Size - return tx_mod_base_nr(sn) < RLC_AM_NR_WINDOW_SIZE; + return tx_mod_base_nr(sn) < tx_window_size(); } /* @@ -970,7 +968,7 @@ bool rlc_am_nr_rx::configure(const rlc_config_t& cfg_) RlcInfo("configured reassembly timer. t-Reassembly=%d ms", cfg.t_reassembly); } - mod_nr = (cfg.rx_sn_field_length == rlc_am_nr_sn_size_t::size12bits) ? 4096 : 262144; + mod_nr = cardinality(cfg.rx_sn_field_length); RlcDebug("RLC AM NR configured rx entity."); @@ -987,15 +985,15 @@ void rlc_am_nr_rx::reestablish() void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) { // Get AMD PDU Header - rlc_am_nr_pdu_header_t header = {}; - uint32_t hdr_len = rlc_am_nr_read_data_pdu_header(payload, nof_bytes, rlc_am_nr_sn_size_t::size12bits, &header); + rlc_am_nr_pdu_header_t header = {}; + uint32_t hdr_len = rlc_am_nr_read_data_pdu_header(payload, nof_bytes, cfg.rx_sn_field_length, &header); RlcHexInfo(payload, nof_bytes, "Rx data PDU SN=%d (%d B)", header.sn, nof_bytes); log_rlc_am_nr_pdu_header_to_string(logger.debug, header, rb_name); // Check whether SDU is within Rx Window if (!inside_rx_window(header.sn)) { - RlcInfo("SN=%d outside rx window [%d:%d] - discarding", header.sn, st.rx_next, st.rx_next + RLC_AM_NR_WINDOW_SIZE); + RlcInfo("SN=%d outside rx window [%d:%d] - discarding", header.sn, st.rx_next, st.rx_next + rx_window_size()); return; } @@ -1041,7 +1039,7 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) */ if (rx_mod_base_nr(header.sn) == rx_mod_base_nr(st.rx_highest_status)) { uint32_t sn_upd = 0; - uint32_t window_top = st.rx_next + RLC_AM_WINDOW_SIZE; + uint32_t window_top = st.rx_next + RLC_AM_NR_WINDOW_SIZE; for (sn_upd = st.rx_highest_status; sn_upd < window_top; ++sn_upd) { if (rx_window.has_sn(sn_upd)) { if (not rx_window[sn_upd].fully_received) { @@ -1063,7 +1061,7 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) */ if (rx_mod_base_nr(header.sn) == rx_mod_base_nr(st.rx_next)) { uint32_t sn_upd = 0; - uint32_t window_top = st.rx_next + RLC_AM_WINDOW_SIZE; + uint32_t window_top = st.rx_next + RLC_AM_NR_WINDOW_SIZE; for (sn_upd = st.rx_next; sn_upd < window_top; ++sn_upd) { if (rx_window.has_sn(sn_upd)) { if (not rx_window[sn_upd].fully_received) { @@ -1253,7 +1251,7 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m } // make sure we don't exceed grant size (FIXME) - rlc_am_nr_write_status_pdu(*status, rlc_am_nr_sn_size_t::size12bits, &tmp_buf); + rlc_am_nr_write_status_pdu(*status, cfg.rx_sn_field_length, &tmp_buf); // TODO i = (i + 1) % mod_nr; } @@ -1301,7 +1299,7 @@ void rlc_am_nr_rx::timer_expired(uint32_t timeout_id) * - start t-Reassembly; * - set RX_Next_Status_Trigger to RX_Next_Highest. */ - for (uint32_t tmp_sn = st.rx_next_status_trigger; tmp_sn < st.rx_next_status_trigger + RLC_AM_WINDOW_SIZE; + for (uint32_t tmp_sn = st.rx_next_status_trigger; tmp_sn < st.rx_next_status_trigger + RLC_AM_NR_WINDOW_SIZE; tmp_sn++) { if (not rx_window.has_sn(tmp_sn)) { st.rx_highest_status = tmp_sn; @@ -1347,10 +1345,15 @@ uint32_t rlc_am_nr_rx::rx_mod_base_nr(uint32_t sn) const return (sn - st.rx_next) % mod_nr; } +uint32_t rlc_am_nr_rx::rx_window_size() const +{ + return cardinality(cfg.rx_sn_field_length) / 2; +} + bool rlc_am_nr_rx::inside_rx_window(uint32_t sn) { // RX_Next <= SN < RX_Next + AM_Window_Size - return rx_mod_base_nr(sn) < RLC_AM_NR_WINDOW_SIZE; + return rx_mod_base_nr(sn) < rx_window_size(); } /* diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index 645ba3440..d0333f746 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -26,25 +26,29 @@ using namespace srsue; using namespace srsran; -int basic_test_tx(rlc_am* rlc, byte_buffer_t pdu_bufs[NBUFS]) +int basic_test_tx(rlc_am* rlc, byte_buffer_t pdu_bufs[NBUFS], rlc_am_nr_sn_size_t sn_size) { // Push 5 SDUs into RLC1 unique_byte_buffer_t sdu_bufs[NBUFS]; + constexpr uint32_t payload_size = 1; // Give each buffer a size of 1 byte for (int i = 0; i < NBUFS; i++) { sdu_bufs[i] = srsran::make_byte_buffer(); - sdu_bufs[i]->msg[0] = i; // Write the index into the buffer - sdu_bufs[i]->N_bytes = 1; // Give each buffer a size of 1 byte - sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications + sdu_bufs[i]->msg[0] = i; // Write the index into the buffer + sdu_bufs[i]->N_bytes = payload_size; // Give each buffer a size of 1 byte + sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications rlc->write_sdu(std::move(sdu_bufs[i])); } - TESTASSERT_EQ(15, rlc->get_buffer_state()); // 2 Bytes * NBUFFS (header size) + NBUFFS (data) = 15 + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + uint32_t data_pdu_size = header_size + payload_size; + uint32_t expect_buffer_state = NBUFS * data_pdu_size; + TESTASSERT_EQ(expect_buffer_state, rlc->get_buffer_state()); // Read 5 PDUs from RLC1 (1 byte each) for (int i = 0; i < NBUFS; i++) { - uint32_t len = rlc->read_pdu(pdu_bufs[i].msg, 3); // 2 bytes for header + 1 byte payload + uint32_t len = rlc->read_pdu(pdu_bufs[i].msg, data_pdu_size); pdu_bufs[i].N_bytes = len; - TESTASSERT_EQ(3, len); + TESTASSERT_EQ(data_pdu_size, len); } TESTASSERT_EQ(0, rlc->get_buffer_state()); @@ -55,7 +59,7 @@ int basic_test_tx(rlc_am* rlc, byte_buffer_t pdu_bufs[NBUFS]) * Test the limits of the TX/RX window checkers * */ -int window_checker_test() +int window_checker_test(rlc_am_nr_sn_size_t sn_size) { rlc_am_tester tester; timer_handler timers(8); @@ -67,16 +71,16 @@ int window_checker_test() rlc_am_nr_tx* tx = dynamic_cast(rlc1.get_tx()); rlc_am_nr_rx* rx = dynamic_cast(rlc1.get_rx()); - if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config())) { + if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { return SRSRAN_ERROR; } { // RLC1 RX_NEXT == 0 and RLC2 TX_NEXT_ACK == 0 uint32_t sn_inside_below = 0; - uint32_t sn_inside_above = 2047; - uint32_t sn_outside_below = 4095; - uint32_t sn_outside_above = 2048; + uint32_t sn_inside_above = cardinality(sn_size) / 2 - 1; + uint32_t sn_outside_below = cardinality(sn_size) - 1; + uint32_t sn_outside_above = cardinality(sn_size) / 2; TESTASSERT_EQ(true, rx->inside_rx_window(sn_inside_below)); TESTASSERT_EQ(true, rx->inside_rx_window(sn_inside_above)); TESTASSERT_EQ(false, rx->inside_rx_window(sn_outside_below)); @@ -88,9 +92,11 @@ int window_checker_test() } rlc_am_nr_rx_state_t rx_st = {}; - rx_st.rx_next = 4095; + rx_st.rx_next = cardinality(sn_size) - 1; + ; rlc_am_nr_tx_state_t tx_st = {}; - tx_st.tx_next_ack = 4095; + tx_st.tx_next_ack = cardinality(sn_size) - 1; + ; rx->set_rx_state(rx_st); tx->set_tx_state(tx_st); @@ -98,9 +104,9 @@ int window_checker_test() { // RX_NEXT == 4095 TX_NEXT_ACK == 4095 uint32_t sn_inside_below = 0; - uint32_t sn_inside_above = 2046; - uint32_t sn_outside_below = 4094; - uint32_t sn_outside_above = 2048; + uint32_t sn_inside_above = cardinality(sn_size) / 2 - 2; + uint32_t sn_outside_below = cardinality(sn_size) - 2; + uint32_t sn_outside_above = cardinality(sn_size) / 2; TESTASSERT_EQ(true, rx->inside_rx_window(sn_inside_below)); TESTASSERT_EQ(true, rx->inside_rx_window(sn_inside_above)); TESTASSERT_EQ(false, rx->inside_rx_window(sn_outside_below)); @@ -117,7 +123,7 @@ int window_checker_test() * Test is retx_segmentation required * */ -int retx_segmentation_required_checker_test() +int retx_segmentation_required_checker_test(rlc_am_nr_sn_size_t sn_size) { rlc_am_tester tester; timer_handler timers(8); @@ -129,7 +135,7 @@ int retx_segmentation_required_checker_test() rlc_am_nr_tx* tx = dynamic_cast(rlc1.get_tx()); rlc_am_nr_rx* rx = dynamic_cast(rlc1.get_rx()); - if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config())) { + if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { return SRSRAN_ERROR; } @@ -203,7 +209,7 @@ int retx_segmentation_required_checker_test() * will trigger the status report. * Poll PDU is configured to 4, so the 5th PDU should set the polling bit. */ -int basic_test() +int basic_test(rlc_am_nr_sn_size_t sn_size) { rlc_am_tester tester; timer_handler timers(8); @@ -222,15 +228,15 @@ int basic_test() // before configuring entity TESTASSERT_EQ(0, rlc1.get_buffer_state()); - if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config())) { + if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { return -1; } - if (not rlc2.configure(rlc_config_t::default_rlc_am_nr_config())) { + if (not rlc2.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { return -1; } - basic_test_tx(&rlc1, pdu_bufs); + basic_test_tx(&rlc1, pdu_bufs, sn_size); // Write 5 PDUs into RLC2 for (int i = 0; i < NBUFS; i++) { @@ -247,7 +253,7 @@ int basic_test() // Assert status is correct rlc_am_nr_status_pdu_t status_check = {}; - rlc_am_nr_read_status_pdu(&status_buf, rlc_am_nr_sn_size_t::size12bits, &status_check); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the last SN that was not received. // Write status PDU to RLC1 @@ -270,6 +276,13 @@ int basic_test() rlc_bearer_metrics_t metrics1 = rlc1.get_metrics(); rlc_bearer_metrics_t metrics2 = rlc2.get_metrics(); + constexpr uint32_t payload_size = 1; + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + uint32_t data_pdu_size = header_size + payload_size; + constexpr uint32_t status_pdu_size = 3; + uint32_t total_tx_pdu_bytes = NBUFS * data_pdu_size; // NBUFS * PDU size + uint32_t total_rx_pdu_bytes = status_pdu_size; // One status PDU + // RLC1 PDU metrics TESTASSERT_EQ(5, metrics1.num_tx_sdus); TESTASSERT_EQ(0, metrics1.num_rx_sdus); @@ -278,10 +291,10 @@ int basic_test() TESTASSERT_EQ(0, metrics1.num_lost_sdus); // RLC1 SDU metrics TESTASSERT_EQ(5, metrics1.num_tx_pdus); - TESTASSERT_EQ(1, metrics1.num_rx_pdus); // One status PDU - TESTASSERT_EQ(15, metrics1.num_tx_pdu_bytes); // 2 Bytes * NBUFFS (header size) + NBUFFS (data) = 15 - TESTASSERT_EQ(3, metrics1.num_rx_pdu_bytes); // One status PDU - TESTASSERT_EQ(0, metrics1.num_lost_sdus); // No lost SDUs + TESTASSERT_EQ(1, metrics1.num_rx_pdus); // One status PDU + TESTASSERT_EQ(total_tx_pdu_bytes, metrics1.num_tx_pdu_bytes); // NBUFS * PDU size + TESTASSERT_EQ(total_rx_pdu_bytes, metrics1.num_rx_pdu_bytes); // One status PDU + TESTASSERT_EQ(0, metrics1.num_lost_sdus); // No lost SDUs // RLC2 PDU metrics TESTASSERT_EQ(0, metrics2.num_tx_sdus); @@ -290,11 +303,11 @@ int basic_test() TESTASSERT_EQ(5, metrics2.num_rx_sdu_bytes); TESTASSERT_EQ(0, metrics2.num_lost_sdus); // RLC2 SDU metrics - TESTASSERT_EQ(1, metrics2.num_tx_pdus); // One status PDU - TESTASSERT_EQ(5, metrics2.num_rx_pdus); // 5 SDUs - TESTASSERT_EQ(3, metrics2.num_tx_pdu_bytes); // One status PDU - TESTASSERT_EQ(15, metrics2.num_rx_pdu_bytes); // 2 Bytes * NBUFFS (header size) + NBUFFS (data) = 15 - TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs + TESTASSERT_EQ(1, metrics2.num_tx_pdus); // One status PDU + TESTASSERT_EQ(5, metrics2.num_rx_pdus); // 5 SDUs + TESTASSERT_EQ(total_rx_pdu_bytes, metrics2.num_tx_pdu_bytes); // One status PDU + TESTASSERT_EQ(total_tx_pdu_bytes, metrics2.num_rx_pdu_bytes); // NBUFS * PDU size + TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs return SRSRAN_SUCCESS; } @@ -303,7 +316,7 @@ int basic_test() * NACK should be visible in the status report. * Retx after NACK should be present too. */ -int lost_pdu_test() +int lost_pdu_test(rlc_am_nr_sn_size_t sn_size) { rlc_am_tester tester; timer_handler timers(8); @@ -314,18 +327,23 @@ int lost_pdu_test() rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); test_delimit_logger delimiter("lost PDU"); + constexpr uint32_t payload_size = 1; + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + uint32_t data_pdu_size = header_size + payload_size; + uint32_t expect_buffer_state = NBUFS * data_pdu_size; + // before configuring entity TESTASSERT(0 == rlc1.get_buffer_state()); - if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config())) { + if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { return -1; } - if (not rlc2.configure(rlc_config_t::default_rlc_am_nr_config())) { + if (not rlc2.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { return -1; } - basic_test_tx(&rlc1, pdu_bufs); + basic_test_tx(&rlc1, pdu_bufs, sn_size); // Write 5 PDUs into RLC2 for (int i = 0; i < NBUFS; i++) { @@ -346,7 +364,7 @@ int lost_pdu_test() // Assert status is correct rlc_am_nr_status_pdu_t status_check = {}; - rlc_am_nr_read_status_pdu(&status_buf, rlc_am_nr_sn_size_t::size12bits, &status_check); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); TESTASSERT_EQ(3, status_check.ack_sn); // 3 is the next expected SN (i.e. the lost packet.) // Write status PDU to RLC1 @@ -359,18 +377,20 @@ int lost_pdu_test() } // t-reassembly has expired. There should be a NACK in the status report. - TESTASSERT_EQ(5, rlc2.get_buffer_state()); + constexpr uint32_t status_pdu_ack_size = 3; + uint32_t status_pdu_nack_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + TESTASSERT_EQ(status_pdu_ack_size + status_pdu_nack_size, rlc2.get_buffer_state()); { // Read status PDU from RLC2 byte_buffer_t status_buf; - int len = rlc2.read_pdu(status_buf.msg, 5); + uint32_t len = rlc2.read_pdu(status_buf.msg, status_pdu_ack_size + status_pdu_nack_size); status_buf.N_bytes = len; TESTASSERT_EQ(0, rlc2.get_buffer_state()); // Assert status is correct rlc_am_nr_status_pdu_t status_check = {}; - rlc_am_nr_read_status_pdu(&status_buf, rlc_am_nr_sn_size_t::size12bits, &status_check); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. TESTASSERT_EQ(1, status_check.N_nack); // We lost one PDU. TESTASSERT_EQ(3, status_check.nacks[0].nack_sn); // Lost PDU SN=3. @@ -379,15 +399,15 @@ int lost_pdu_test() rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); // Check there is an Retx of SN=3 - TESTASSERT_EQ(3, rlc1.get_buffer_state()); + TESTASSERT_EQ(data_pdu_size, rlc1.get_buffer_state()); } { // Check correct re-transmission byte_buffer_t retx_buf; - int len = rlc1.read_pdu(retx_buf.msg, 3); + uint32_t len = rlc1.read_pdu(retx_buf.msg, data_pdu_size); retx_buf.N_bytes = len; - TESTASSERT_EQ(3, len); + TESTASSERT_EQ(data_pdu_size, len); rlc2.write_pdu(retx_buf.msg, retx_buf.N_bytes); @@ -398,6 +418,11 @@ int lost_pdu_test() rlc_bearer_metrics_t metrics1 = rlc1.get_metrics(); rlc_bearer_metrics_t metrics2 = rlc2.get_metrics(); + uint32_t total_tx_pdu_bytes1 = (NBUFS + 1) * data_pdu_size; // (NBUFS + 1 RETX) * PDU size + uint32_t total_rx_pdu_bytes1 = 2 * status_pdu_ack_size + status_pdu_nack_size; // Two status PDU (one with a NACK) + uint32_t total_tx_pdu_bytes2 = total_rx_pdu_bytes1; // Two status PDU (one with a NACK) + uint32_t total_rx_pdu_bytes2 = (NBUFS)*data_pdu_size; // (NBUFS - 1 Lost + 1 RETX) * PDU size + // SDU metrics TESTASSERT_EQ(5, metrics1.num_tx_sdus); TESTASSERT_EQ(0, metrics1.num_rx_sdus); @@ -405,11 +430,11 @@ int lost_pdu_test() TESTASSERT_EQ(0, metrics1.num_rx_sdu_bytes); TESTASSERT_EQ(0, metrics1.num_lost_sdus); // PDU metrics - TESTASSERT_EQ(5 + 1, metrics1.num_tx_pdus); // One re-transmission - TESTASSERT_EQ(2, metrics1.num_rx_pdus); // One status PDU - TESTASSERT_EQ(18, metrics1.num_tx_pdu_bytes); // 2 Bytes * NBUFFS (header size) + NBUFFS (data) + 1 rext (3) = 18 - TESTASSERT_EQ(3 + 5, metrics1.num_rx_pdu_bytes); // Two status PDU (one with a NACK) - TESTASSERT_EQ(0, metrics1.num_lost_sdus); // No lost SDUs + TESTASSERT_EQ(5 + 1, metrics1.num_tx_pdus); // One re-transmission + TESTASSERT_EQ(2, metrics1.num_rx_pdus); // One status PDU + TESTASSERT_EQ(total_tx_pdu_bytes1, metrics1.num_tx_pdu_bytes); // (NBUFS + 1 RETX) * PDU size + TESTASSERT_EQ(total_rx_pdu_bytes1, metrics1.num_rx_pdu_bytes); // Two status PDU (one with a NACK) + TESTASSERT_EQ(0, metrics1.num_lost_sdus); // No lost SDUs // PDU metrics TESTASSERT_EQ(0, metrics2.num_tx_sdus); @@ -418,11 +443,11 @@ int lost_pdu_test() TESTASSERT_EQ(5, metrics2.num_rx_sdu_bytes); TESTASSERT_EQ(0, metrics2.num_lost_sdus); // SDU metrics - TESTASSERT_EQ(2, metrics2.num_tx_pdus); // Two status PDUs - TESTASSERT_EQ(5, metrics2.num_rx_pdus); // 5 PDUs (6 tx'ed, but one was lost) - TESTASSERT_EQ(5 + 3, metrics2.num_tx_pdu_bytes); // Two status PDU (one with a NACK) - TESTASSERT_EQ(15, metrics2.num_rx_pdu_bytes); // 2 Bytes * NBUFFS (header size) + NBUFFS (data) = 15 - TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs + TESTASSERT_EQ(2, metrics2.num_tx_pdus); // Two status PDUs + TESTASSERT_EQ(5, metrics2.num_rx_pdus); // 5 PDUs (6 tx'ed, but one was lost) + TESTASSERT_EQ(total_tx_pdu_bytes2, metrics2.num_tx_pdu_bytes); // Two status PDU (one with a NACK) + TESTASSERT_EQ(total_rx_pdu_bytes2, metrics2.num_rx_pdu_bytes); // (NBUFS - 1 Lost + 1 RETX) * PDU size + TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs return SRSRAN_SUCCESS; } @@ -430,7 +455,7 @@ int lost_pdu_test() * Test the basic segmentation of a single SDU. * A single SDU of 3 bytes is segmented into 3 PDUs */ -int basic_segmentation_test() +int basic_segmentation_test(rlc_am_nr_sn_size_t sn_size) { rlc_am_tester tester; timer_handler timers(8); @@ -447,58 +472,66 @@ int basic_segmentation_test() // before configuring entity TESTASSERT_EQ(0, rlc1.get_buffer_state()); - if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config())) { + if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { return -1; } - if (not rlc2.configure(rlc_config_t::default_rlc_am_nr_config())) { + if (not rlc2.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { return -1; } // Push 1 SDU into RLC1 unique_byte_buffer_t sdu; + constexpr uint32_t payload_size = 3; // Give the SDU the size of 3 bytes sdu = srsran::make_byte_buffer(); TESTASSERT(nullptr != sdu); - sdu->msg[0] = 0; // Write the index into the buffer - sdu->N_bytes = 3; // Give the SDU the size of 3 bytes - sdu->md.pdcp_sn = 0; // PDCP SN for notifications + sdu->msg[0] = 0; // Write the index into the buffer + sdu->N_bytes = payload_size; // Give the SDU the size of 3 bytes + sdu->md.pdcp_sn = 0; // PDCP SN for notifications rlc1.write_sdu(std::move(sdu)); // Read 3 PDUs constexpr uint16_t n_pdus = 3; unique_byte_buffer_t pdu_bufs[n_pdus]; - for (int i = 0; i < 3; i++) { + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + constexpr uint32_t so_size = 2; + constexpr uint32_t segment_size = 1; + uint32_t pdu_size_first = header_size + segment_size; + uint32_t pdu_size_continued = header_size + so_size + segment_size; + for (int i = 0; i < n_pdus; i++) { pdu_bufs[i] = srsran::make_byte_buffer(); TESTASSERT(nullptr != pdu_bufs[i]); if (i == 0) { - pdu_bufs[i]->N_bytes = rlc1.read_pdu(pdu_bufs[i]->msg, 3); - TESTASSERT_EQ(3, pdu_bufs[i]->N_bytes); + pdu_bufs[i]->N_bytes = rlc1.read_pdu(pdu_bufs[i]->msg, pdu_size_first); + TESTASSERT_EQ(pdu_size_first, pdu_bufs[i]->N_bytes); } else { - pdu_bufs[i]->N_bytes = rlc1.read_pdu(pdu_bufs[i]->msg, 5); - TESTASSERT_EQ(5, pdu_bufs[i]->N_bytes); + pdu_bufs[i]->N_bytes = rlc1.read_pdu(pdu_bufs[i]->msg, pdu_size_continued); + TESTASSERT_EQ(pdu_size_continued, pdu_bufs[i]->N_bytes); } } - // Write 5 PDUs into RLC2 + // Write 3 PDUs into RLC2 for (int i = 0; i < n_pdus; i++) { - rlc2.write_pdu(pdu_bufs[i]->msg, pdu_bufs[i]->N_bytes); // Don't write RLC_SN=3. + rlc2.write_pdu(pdu_bufs[i]->msg, pdu_bufs[i]->N_bytes); } // Check statistics rlc_bearer_metrics_t metrics1 = rlc1.get_metrics(); rlc_bearer_metrics_t metrics2 = rlc2.get_metrics(); + uint32_t total_rx_pdu_bytes = pdu_size_first + (n_pdus - 1) * pdu_size_continued; // 1 PDU (No SO) + 2 PDUs (with SO) + // SDU metrics TESTASSERT_EQ(0, metrics2.num_tx_sdus); TESTASSERT_EQ(1, metrics2.num_rx_sdus); TESTASSERT_EQ(0, metrics2.num_tx_sdu_bytes); - TESTASSERT_EQ(3, metrics2.num_rx_sdu_bytes); + TESTASSERT_EQ(payload_size, metrics2.num_rx_sdu_bytes); TESTASSERT_EQ(0, metrics2.num_lost_sdus); // PDU metrics TESTASSERT_EQ(0, metrics2.num_tx_pdus); - TESTASSERT_EQ(3, metrics2.num_rx_pdus); // 5 PDUs (6 tx'ed, but one was lost) - TESTASSERT_EQ(0, metrics2.num_tx_pdu_bytes); // Two status PDU (one with a NACK) - TESTASSERT_EQ(13, metrics2.num_rx_pdu_bytes); // 1 PDU (No SO) + 2 PDUs (with SO) = 3 + 2*5 + TESTASSERT_EQ(n_pdus, metrics2.num_rx_pdus); // 3 PDUs + TESTASSERT_EQ(0, metrics2.num_tx_pdu_bytes); + TESTASSERT_EQ(total_rx_pdu_bytes, metrics2.num_rx_pdu_bytes); // 1 PDU (No SO) + 2 PDUs (with SO) TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs // Check state @@ -1186,11 +1219,16 @@ int main() // start log back-end srslog::init(); - TESTASSERT(window_checker_test() == SRSRAN_SUCCESS); - TESTASSERT(retx_segmentation_required_checker_test() == SRSRAN_SUCCESS); - TESTASSERT(basic_test() == SRSRAN_SUCCESS); - TESTASSERT(lost_pdu_test() == SRSRAN_SUCCESS); - TESTASSERT(basic_segmentation_test() == SRSRAN_SUCCESS); + TESTASSERT(window_checker_test(rlc_am_nr_sn_size_t::size12bits) == SRSRAN_SUCCESS); + TESTASSERT(window_checker_test(rlc_am_nr_sn_size_t::size18bits) == SRSRAN_SUCCESS); + TESTASSERT(retx_segmentation_required_checker_test(rlc_am_nr_sn_size_t::size12bits) == SRSRAN_SUCCESS); + TESTASSERT(retx_segmentation_required_checker_test(rlc_am_nr_sn_size_t::size18bits) == SRSRAN_SUCCESS); + TESTASSERT(basic_test(rlc_am_nr_sn_size_t::size12bits) == SRSRAN_SUCCESS); + TESTASSERT(basic_test(rlc_am_nr_sn_size_t::size18bits) == SRSRAN_SUCCESS); + TESTASSERT(lost_pdu_test(rlc_am_nr_sn_size_t::size12bits) == SRSRAN_SUCCESS); + TESTASSERT(lost_pdu_test(rlc_am_nr_sn_size_t::size18bits) == SRSRAN_SUCCESS); + TESTASSERT(basic_segmentation_test(rlc_am_nr_sn_size_t::size12bits) == SRSRAN_SUCCESS); + TESTASSERT(basic_segmentation_test(rlc_am_nr_sn_size_t::size18bits) == SRSRAN_SUCCESS); TESTASSERT(segment_retx_test() == SRSRAN_SUCCESS); TESTASSERT(retx_segment_test() == SRSRAN_SUCCESS); TESTASSERT(max_retx_lost_sdu_test() == SRSRAN_SUCCESS); From 47c1845cdc3508490a9b8fe47169a4def07ac76e Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Fri, 11 Mar 2022 10:34:41 +0100 Subject: [PATCH 049/195] lib,rlc_am_nr: add pdu_retx_queue_base, rlc_ringbuffer_base as interfaces that are independent from templated capacity. --- lib/include/srsran/common/common.h | 2 +- .../srsran/interfaces/rlc_interface_types.h | 6 +- lib/include/srsran/rlc/rlc_am_data_structs.h | 65 ++++++-- lib/include/srsran/rlc/rlc_am_nr.h | 18 +- lib/src/rlc/rlc_am_nr.cc | 157 +++++++++++------- 5 files changed, 158 insertions(+), 90 deletions(-) diff --git a/lib/include/srsran/common/common.h b/lib/include/srsran/common/common.h index bda5a02a4..a74828537 100644 --- a/lib/include/srsran/common/common.h +++ b/lib/include/srsran/common/common.h @@ -80,7 +80,7 @@ inline const char* enum_to_text(const char* const array[], uint32_t nof_types, u } template -ItemType enum_to_number(ItemType* array, uint32_t nof_types, uint32_t enum_val) +constexpr ItemType enum_to_number(ItemType* array, uint32_t nof_types, uint32_t enum_val) { return enum_val >= nof_types ? -1 : array[enum_val]; } diff --git a/lib/include/srsran/interfaces/rlc_interface_types.h b/lib/include/srsran/interfaces/rlc_interface_types.h index 9c6ba8512..5d574826f 100644 --- a/lib/include/srsran/interfaces/rlc_interface_types.h +++ b/lib/include/srsran/interfaces/rlc_interface_types.h @@ -64,9 +64,9 @@ inline std::string to_string(const rlc_am_nr_sn_size_t& sn_size) constexpr static const char* options[] = {"12 bits", "18 bits"}; return enum_to_text(options, (uint32_t)rlc_mode_t::nulltype, (uint32_t)sn_size); } -inline uint16_t to_number(const rlc_am_nr_sn_size_t& sn_size) +constexpr uint16_t to_number(const rlc_am_nr_sn_size_t& sn_size) { - constexpr static uint16_t options[] = {12, 18}; + constexpr uint16_t options[] = {12, 18}; return enum_to_number(options, (uint32_t)rlc_mode_t::nulltype, (uint32_t)sn_size); } /** @@ -74,7 +74,7 @@ inline uint16_t to_number(const rlc_am_nr_sn_size_t& sn_size) * @param sn_size Length of the serial number field in bits * @return cardianlity */ -inline uint32_t cardinality(const rlc_am_nr_sn_size_t& sn_size) +constexpr uint32_t cardinality(const rlc_am_nr_sn_size_t& sn_size) { return (1 << to_number(sn_size)); } diff --git a/lib/include/srsran/rlc/rlc_am_data_structs.h b/lib/include/srsran/rlc/rlc_am_data_structs.h index e2025925f..499b54626 100644 --- a/lib/include/srsran/rlc/rlc_am_data_structs.h +++ b/lib/include/srsran/rlc/rlc_am_data_structs.h @@ -195,25 +195,39 @@ public: const_iterator end() const { return list.end(); } }; +template +struct rlc_ringbuffer_base { + virtual ~rlc_ringbuffer_base() = default; + virtual T& add_pdu(size_t sn) = 0; + virtual void remove_pdu(size_t sn) = 0; + virtual T& operator[](size_t sn) = 0; + virtual size_t size() const = 0; + virtual bool empty() const = 0; + virtual void clear() = 0; + virtual bool has_sn(uint32_t sn) const = 0; +}; + template -struct rlc_ringbuffer_t { - T& add_pdu(size_t sn) +struct rlc_ringbuffer_t : public rlc_ringbuffer_base { + ~rlc_ringbuffer_t() = default; + + T& add_pdu(size_t sn) override { srsran_expect(not has_sn(sn), "The same SN=%zd should not be added twice", sn); window.overwrite(sn, T(sn)); return window[sn]; } - void remove_pdu(size_t sn) + void remove_pdu(size_t sn) override { srsran_expect(has_sn(sn), "The removed SN=%zd is not in the window", sn); window.erase(sn); } - T& operator[](size_t sn) { return window[sn]; } - size_t size() const { return window.size(); } - bool empty() const { return window.empty(); } - void clear() { window.clear(); } + T& operator[](size_t sn) override { return window[sn]; } + size_t size() const override { return window.size(); } + bool empty() const override { return window.empty(); } + void clear() override { window.clear(); } - bool has_sn(uint32_t sn) const { return window.contains(sn); } + bool has_sn(uint32_t sn) const override { return window.contains(sn); } // Return the sum data bytes of all active PDUs (check PDU is non-null) uint32_t get_buffered_bytes() @@ -316,11 +330,28 @@ struct rlc_amd_retx_nr_t { uint32_t current_so; }; +template +class pdu_retx_queue_base +{ +public: + virtual ~pdu_retx_queue_base() = default; + virtual T& push() = 0; + virtual void pop() = 0; + virtual T& front() = 0; + virtual void clear() = 0; + virtual bool has_sn(uint32_t sn) const = 0; + virtual size_t size() const = 0; + virtual bool empty() const = 0; + virtual bool full() const = 0; +}; + template -class pdu_retx_queue +class pdu_retx_queue : public pdu_retx_queue_base { public: - T& push() + ~pdu_retx_queue() = default; + + T& push() override { assert(not full()); T& p = buffer[wpos]; @@ -328,21 +359,21 @@ public: return p; } - void pop() { rpos = (rpos + 1) % WINDOW_SIZE; } + void pop() override { rpos = (rpos + 1) % WINDOW_SIZE; } - T& front() + T& front() override { assert(not empty()); return buffer[rpos]; } - void clear() + void clear() override { wpos = 0; rpos = 0; } - bool has_sn(uint32_t sn) const + bool has_sn(uint32_t sn) const override { for (size_t i = rpos; i != wpos; i = (i + 1) % WINDOW_SIZE) { if (buffer[i].sn == sn) { @@ -352,9 +383,9 @@ public: return false; } - size_t size() const { return (wpos >= rpos) ? wpos - rpos : WINDOW_SIZE + wpos - rpos; } - bool empty() const { return wpos == rpos; } - bool full() const { return size() == WINDOW_SIZE - 1; } + size_t size() const override { return (wpos >= rpos) ? wpos - rpos : WINDOW_SIZE + wpos - rpos; } + bool empty() const override { return wpos == rpos; } + bool full() const override { return size() == WINDOW_SIZE - 1; } private: std::array buffer; diff --git a/lib/include/srsran/rlc/rlc_am_nr.h b/lib/include/srsran/rlc/rlc_am_nr.h index bf6ea2dd4..07cf6c123 100644 --- a/lib/include/srsran/rlc/rlc_am_nr.h +++ b/lib/include/srsran/rlc/rlc_am_nr.h @@ -132,7 +132,7 @@ private: rlc_am* parent = nullptr; rlc_am_nr_rx* rx = nullptr; - uint32_t mod_nr = 4096; + uint32_t mod_nr = cardinality(rlc_am_nr_sn_size_t()); inline uint32_t tx_mod_base_nr(uint32_t sn) const; void check_sn_reached_max_retx(uint32_t sn); @@ -147,10 +147,14 @@ private: * Ref: 3GPP TS 38.322 version 16.2.0 Section 7.1 ***************************************************************************/ struct rlc_am_nr_tx_state_t st = {}; - rlc_ringbuffer_t tx_window; + std::unique_ptr > tx_window = + std::unique_ptr >( + new rlc_ringbuffer_t); // Queues and buffers - pdu_retx_queue retx_queue; + std::unique_ptr > retx_queue = + std::unique_ptr >( + new pdu_retx_queue); uint32_t sdu_under_segmentation_sn = INVALID_RLC_SN; // SN of the SDU currently being segmented. pdcp_sn_vector_t notify_info_vec; @@ -169,7 +173,7 @@ public: // Getters/Setters void set_tx_state(const rlc_am_nr_tx_state_t& st_) { st = st_; } // This should only be used for testing. rlc_am_nr_tx_state_t get_tx_state() { return st; } // This should only be used for testing. - uint32_t get_tx_window_size() { return tx_window.size(); } // This should only be used for testing. + uint32_t get_tx_window_size() { return tx_window->size(); } // This should only be used for testing. // Debug Helper void debug_state() const; @@ -250,7 +254,9 @@ private: uint32_t rx_mod_base_nr(uint32_t sn) const; // RX Window - rlc_ringbuffer_t rx_window; + std::unique_ptr > rx_window = + std::unique_ptr >( + new rlc_ringbuffer_t); // Mutexes std::mutex mutex; @@ -284,7 +290,7 @@ public: // Getters/Setters void set_rx_state(const rlc_am_nr_rx_state_t& st_) { st = st_; } // This should only be used for testing. rlc_am_nr_rx_state_t get_rx_state() { return st; } // This should only be used for testing. - uint32_t get_rx_window_size() { return rx_window.size(); } // This should only be used for testing. + uint32_t get_rx_window_size() { return rx_window->size(); } // This should only be used for testing. }; } // namespace srsran diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 0456e1e56..08591f4d9 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -47,8 +47,26 @@ bool rlc_am_nr_tx::configure(const rlc_config_t& cfg_) } mod_nr = cardinality(cfg.tx_sn_field_length); + switch (cfg.tx_sn_field_length) { + case rlc_am_nr_sn_size_t::size12bits: + min_hdr_size = 2; + tx_window = std::unique_ptr >( + new rlc_ringbuffer_t); + retx_queue = std::unique_ptr >( + new pdu_retx_queue); + break; + case rlc_am_nr_sn_size_t::size18bits: + min_hdr_size = 3; + tx_window = std::unique_ptr >( + new rlc_ringbuffer_t); + retx_queue = std::unique_ptr >( + new pdu_retx_queue); + break; + default: + RlcError("attempt to configure unsupported tx_sn_field_length %s", to_string(cfg.tx_sn_field_length)); + return false; + } - min_hdr_size = cfg.tx_sn_field_length == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; max_hdr_size = min_hdr_size + so_size; tx_enabled = true; @@ -83,7 +101,7 @@ uint32_t rlc_am_nr_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes) RlcDebug("RLC entity not active. Not generating PDU."); return 0; } - RlcDebug("MAC opportunity - bytes=%d, tx_window size=%zu PDUs", nof_bytes, tx_window.size()); + RlcDebug("MAC opportunity - bytes=%d, tx_window size=%zu PDUs", nof_bytes, tx_window->size()); // Tx STATUS if requested if (do_status()) { @@ -99,20 +117,20 @@ uint32_t rlc_am_nr_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes) } // Retransmit if required - if (not retx_queue.empty()) { - RlcInfo("Re-transmission required. Retransmission queue size: %d", retx_queue.size()); + if (not retx_queue->empty()) { + RlcInfo("Re-transmission required. Retransmission queue size: %d", retx_queue->size()); return build_retx_pdu(payload, nof_bytes); } // Send remaining segment, if it exists if (sdu_under_segmentation_sn != INVALID_RLC_SN) { - if (not tx_window.has_sn(sdu_under_segmentation_sn)) { + if (not tx_window->has_sn(sdu_under_segmentation_sn)) { sdu_under_segmentation_sn = INVALID_RLC_SN; RlcError("SDU currently being segmented does not exist in tx_window. Aborting segmentation SN=%d", sdu_under_segmentation_sn); return 0; } - return build_continuation_sdu_segment(tx_window[sdu_under_segmentation_sn], payload, nof_bytes); + return build_continuation_sdu_segment((*tx_window)[sdu_under_segmentation_sn], payload, nof_bytes); } // Check whether there is something to TX @@ -156,7 +174,7 @@ uint32_t rlc_am_nr_tx::build_new_pdu(uint8_t* payload, uint32_t nof_bytes) // insert newly assigned SN into window and use reference for in-place operations // NOTE: from now on, we can't return from this function anymore before increasing tx_next - rlc_amd_tx_pdu_nr& tx_pdu = tx_window.add_pdu(st.tx_next); + rlc_amd_tx_pdu_nr& tx_pdu = tx_window->add_pdu(st.tx_next); tx_pdu.pdcp_sn = tx_sdu->md.pdcp_sn; tx_pdu.sdu_buf = srsran::make_byte_buffer(); if (tx_pdu.sdu_buf == nullptr) { @@ -392,19 +410,19 @@ uint32_t rlc_am_nr_tx::build_continuation_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, uint32_t rlc_am_nr_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_bytes) { // Check there is at least 1 element before calling front() - if (retx_queue.empty()) { + if (retx_queue->empty()) { RlcError("in build_retx_pdu(): retx_queue is empty"); return 0; } - rlc_amd_retx_nr_t& retx = retx_queue.front(); + rlc_amd_retx_nr_t& retx = retx_queue->front(); // Sanity check - drop any retx SNs not present in tx_window - while (not tx_window.has_sn(retx.sn)) { + while (not tx_window->has_sn(retx.sn)) { RlcWarning("SN=%d not in tx window. Ignoring retx.", retx.sn); - retx_queue.pop(); - if (!retx_queue.empty()) { - retx = retx_queue.front(); + retx_queue->pop(); + if (!retx_queue->empty()) { + retx = retx_queue->front(); } else { RlcWarning("empty retx queue, cannot provide retx PDU"); return 0; @@ -443,18 +461,18 @@ uint32_t rlc_am_nr_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_bytes) uint32_t rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8_t* payload, uint32_t nof_bytes) { - srsran_assert(tx_window.has_sn(retx.sn), "Called %s without checking retx SN", __FUNCTION__); + srsran_assert(tx_window->has_sn(retx.sn), "Called %s without checking retx SN", __FUNCTION__); srsran_assert(not is_retx_segmentation_required(retx, nof_bytes), "Called %s without checking if segmentation was required", __FUNCTION__); // Get tx_pdu info from tx_window - rlc_amd_tx_pdu_nr& tx_pdu = tx_window[retx.sn]; + rlc_amd_tx_pdu_nr& tx_pdu = (*tx_window)[retx.sn]; // Get expected header and payload len uint32_t expected_hdr_len = get_retx_expected_hdr_len(retx); - uint32_t retx_payload_len = - retx.is_segment ? (retx.so_start + retx.segment_length - retx.current_so) : tx_window[retx.sn].sdu_buf->N_bytes; + uint32_t retx_payload_len = retx.is_segment ? (retx.so_start + retx.segment_length - retx.current_so) + : (*tx_window)[retx.sn].sdu_buf->N_bytes; srsran_assert(nof_bytes >= (expected_hdr_len + retx_payload_len), "Called %s but segmentation is required. nof_bytes=%d, expeced_hdr_len=%d, retx_payload_len=%d", __FUNCTION__, @@ -496,7 +514,7 @@ rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8 uint32_t retx_pdu_payload_size = 0; if (not retx.is_segment) { // RETX full SDU - retx_pdu_payload_size = tx_window[retx.sn].sdu_buf->N_bytes; + retx_pdu_payload_size = (*tx_window)[retx.sn].sdu_buf->N_bytes; } else { // RETX SDU segment retx_pdu_payload_size = (retx.so_start + retx.segment_length - retx.current_so); @@ -506,13 +524,13 @@ rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8 memcpy(&payload[hdr_len], &tx_pdu.sdu_buf->msg[retx.current_so], retx_pdu_payload_size); // Update RETX queue and log - retx_queue.pop(); - RlcHexInfo(tx_window[retx.sn].sdu_buf->msg, - tx_window[retx.sn].sdu_buf->N_bytes, + retx_queue->pop(); + RlcHexInfo((*tx_window)[retx.sn].sdu_buf->msg, + (*tx_window)[retx.sn].sdu_buf->N_bytes, "Original SDU SN=%d (%d B) (attempt %d/%d)", retx.sn, - tx_window[retx.sn].sdu_buf->N_bytes, - tx_window[retx.sn].retx_count + 1, + (*tx_window)[retx.sn].sdu_buf->N_bytes, + (*tx_window)[retx.sn].retx_count + 1, cfg.max_retx_thresh); RlcHexInfo(payload, pdu_bytes, "RETX PDU SN=%d (%d B)", retx.sn, pdu_bytes); log_rlc_am_nr_pdu_header_to_string(logger.debug, new_header, rb_name); @@ -534,12 +552,12 @@ rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8 uint32_t rlc_am_nr_tx::build_retx_pdu_with_segmentation(rlc_amd_retx_nr_t& retx, uint8_t* payload, uint32_t nof_bytes) { // Get tx_pdu info from tx_window - srsran_assert(tx_window.has_sn(retx.sn), "Called %s without checking retx SN", __FUNCTION__); + srsran_assert(tx_window->has_sn(retx.sn), "Called %s without checking retx SN", __FUNCTION__); srsran_assert(is_retx_segmentation_required(retx, nof_bytes), "Called %s without checking if segmentation was not required", __FUNCTION__); - rlc_amd_tx_pdu_nr& tx_pdu = tx_window[retx.sn]; + rlc_amd_tx_pdu_nr& tx_pdu = (*tx_window)[retx.sn]; // Is this an SDU segment or a full SDU? if (not retx.is_segment) { @@ -638,7 +656,7 @@ bool rlc_am_nr_tx::is_retx_segmentation_required(const rlc_amd_retx_nr_t& retx, segmentation_required = true; } } else { - if (nof_bytes < (tx_window[retx.sn].sdu_buf->N_bytes + min_hdr_size)) { + if (nof_bytes < ((*tx_window)[retx.sn].sdu_buf->N_bytes + min_hdr_size)) { RlcInfo("Segmentation required for RETX. SN=%d", retx.sn); segmentation_required = true; } @@ -701,9 +719,9 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) return; } for (uint32_t sn = st.tx_next_ack; sn < stop_sn; sn++) { - if (tx_window.has_sn(sn)) { - notify_info_vec.push_back(tx_window[sn].pdcp_sn); - tx_window.remove_pdu(sn); + if (tx_window->has_sn(sn)) { + notify_info_vec.push_back((*tx_window)[sn].pdcp_sn); + tx_window->remove_pdu(sn); st.tx_next_ack = (sn + 1) % mod_nr; } else { RlcError("Missing ACKed SN from TX window"); @@ -729,8 +747,8 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) if (st.tx_next_ack <= status.nacks[nack_idx].nack_sn && status.nacks[nack_idx].nack_sn <= st.tx_next) { auto nack = status.nacks[nack_idx]; uint32_t nack_sn = nack.nack_sn; - if (tx_window.has_sn(nack_sn)) { - auto& pdu = tx_window[nack_sn]; + if (tx_window->has_sn(nack_sn)) { + auto& pdu = (*tx_window)[nack_sn]; if (nack.has_so) { // NACK'ing missing bytes in SDU segment. @@ -743,7 +761,7 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) segm++) { if (segm->so >= nack.so_start && segm->so <= nack.so_end) { // TODO: Check if this segment is not already queued for retransmission - rlc_amd_retx_nr_t& retx = retx_queue.push(); + rlc_amd_retx_nr_t& retx = retx_queue->push(); retx.sn = nack_sn; retx.is_segment = true; retx.so_start = segm->so; @@ -759,8 +777,8 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) } else { // NACK'ing full SDU. // add to retx queue if it's not already there - if (not retx_queue.has_sn(nack_sn)) { - rlc_amd_retx_nr_t& retx = retx_queue.push(); + if (not retx_queue->has_sn(nack_sn)) { + rlc_amd_retx_nr_t& retx = retx_queue->push(); retx.sn = nack_sn; retx.is_segment = false; retx.so_start = 0; @@ -776,7 +794,7 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) // Process retx_count and inform upper layers if needed for (uint32_t retx_sn : retx_sn_set) { - auto& pdu = tx_window[retx_sn]; + auto& pdu = (*tx_window)[retx_sn]; // Increment retx_count if (pdu.retx_count == RETX_COUNT_NOT_STARTED) { // Set retx_count = 0 on first RE-transmission of associated SDU (38.322 Sec. 5.3.2) @@ -811,11 +829,11 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) */ void rlc_am_nr_tx::check_sn_reached_max_retx(uint32_t sn) { - if (tx_window[sn].retx_count == cfg.max_retx_thresh) { - RlcWarning("Signaling max number of reTx=%d for SN=%d", tx_window[sn].retx_count, sn); + if ((*tx_window)[sn].retx_count == cfg.max_retx_thresh) { + RlcWarning("Signaling max number of reTx=%d for SN=%d", (*tx_window)[sn].retx_count, sn); parent->rrc->max_retx_attempted(); srsran::pdcp_sn_vector_t pdcp_sns; - pdcp_sns.push_back(tx_window[sn].pdcp_sn); + pdcp_sns.push_back((*tx_window)[sn].pdcp_sn); parent->pdcp->notify_failure(parent->lcid, pdcp_sns); std::lock_guard lock(parent->metrics_mutex); @@ -843,19 +861,19 @@ void rlc_am_nr_tx::get_buffer_state(uint32_t& n_bytes_new, uint32_t& n_bytes_pri } // Bytes needed for retx - if (not retx_queue.empty()) { - rlc_amd_retx_nr_t& retx = retx_queue.front(); + if (not retx_queue->empty()) { + rlc_amd_retx_nr_t& retx = retx_queue->front(); RlcDebug("buffer state - retx - SN=%d, Segment: %s, %d:%d", retx.sn, retx.is_segment ? "true" : "false", retx.so_start, retx.so_start + retx.segment_length - 1); - if (tx_window.has_sn(retx.sn)) { + if (tx_window->has_sn(retx.sn)) { int req_bytes = retx.segment_length; int hdr_req_bytes = retx.is_segment ? max_hdr_size : min_hdr_size; // Segmentation not supported yet if (req_bytes <= 0) { RlcError("in get_buffer_state(): Removing retx with SN=%d from queue", retx.sn); - retx_queue.pop(); + retx_queue->pop(); } else { n_bytes_prio += (req_bytes + hdr_req_bytes); RlcDebug("buffer state - retx: %d bytes", n_bytes_prio); @@ -969,6 +987,19 @@ bool rlc_am_nr_rx::configure(const rlc_config_t& cfg_) } mod_nr = cardinality(cfg.rx_sn_field_length); + switch (cfg.rx_sn_field_length) { + case rlc_am_nr_sn_size_t::size12bits: + rx_window = std::unique_ptr >( + new rlc_ringbuffer_t); + break; + case rlc_am_nr_sn_size_t::size18bits: + rx_window = std::unique_ptr >( + new rlc_ringbuffer_t); + break; + default: + RlcError("attempt to configure unsupported rx_sn_field_length %s", to_string(cfg.rx_sn_field_length)); + return false; + } RlcDebug("RLC AM NR configured rx entity."); @@ -998,7 +1029,7 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) } // Section 5.2.3.2.2, discard duplicate PDUs - if (rx_window.has_sn(header.sn) && rx_window[header.sn].fully_received) { + if (rx_window->has_sn(header.sn) && (*rx_window)[header.sn].fully_received) { RlcInfo("discarding duplicate SN=%d", header.sn); return; } @@ -1041,8 +1072,8 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) uint32_t sn_upd = 0; uint32_t window_top = st.rx_next + RLC_AM_NR_WINDOW_SIZE; for (sn_upd = st.rx_highest_status; sn_upd < window_top; ++sn_upd) { - if (rx_window.has_sn(sn_upd)) { - if (not rx_window[sn_upd].fully_received) { + if (rx_window->has_sn(sn_upd)) { + if (not(*rx_window)[sn_upd].fully_received) { break; // first SDU not fully received } } else { @@ -1063,13 +1094,13 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) uint32_t sn_upd = 0; uint32_t window_top = st.rx_next + RLC_AM_NR_WINDOW_SIZE; for (sn_upd = st.rx_next; sn_upd < window_top; ++sn_upd) { - if (rx_window.has_sn(sn_upd)) { - if (not rx_window[sn_upd].fully_received) { + if (rx_window->has_sn(sn_upd)) { + if (not(*rx_window)[sn_upd].fully_received) { break; // first SDU not fully received } // RX_Next serves as the lower edge of the receiving window // As such, we remove any SDU from the window if we update this value - rx_window.remove_pdu(sn_upd); + rx_window->remove_pdu(sn_upd); } else { break; // first SDU not fully received } @@ -1101,8 +1132,8 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) if (st.rx_next_highest > st.rx_next + 1) { restart_reassembly_timer = true; } - if (st.rx_next_highest == st.rx_next + 1 && rx_window.has_sn(st.rx_next + 1) && - not rx_window[st.rx_next + 1].fully_received) { + if (st.rx_next_highest == st.rx_next + 1 && rx_window->has_sn(st.rx_next + 1) && + not(*rx_window)[st.rx_next + 1].fully_received) { restart_reassembly_timer = true; } if (restart_reassembly_timer) { @@ -1119,11 +1150,11 @@ int rlc_am_nr_rx::handle_full_data_sdu(const rlc_am_nr_pdu_header_t& header, con { uint32_t hdr_len = rlc_am_nr_packed_length(header); // Full SDU received. Add SDU to Rx Window and copy full PDU into SDU buffer. - rlc_amd_rx_sdu_nr_t& rx_sdu = rx_window.add_pdu(header.sn); + rlc_amd_rx_sdu_nr_t& rx_sdu = rx_window->add_pdu(header.sn); rx_sdu.buf = srsran::make_byte_buffer(); if (rx_sdu.buf == nullptr) { RlcError("fatal error. Couldn't allocate PDU in %s.", __FUNCTION__); - rx_window.remove_pdu(header.sn); + rx_window->remove_pdu(header.sn); return SRSRAN_ERROR; } rx_sdu.buf->set_timestamp(); @@ -1131,13 +1162,13 @@ int rlc_am_nr_rx::handle_full_data_sdu(const rlc_am_nr_pdu_header_t& header, con // check available space for payload if (nof_bytes > rx_sdu.buf->get_tailroom()) { RlcError("discarding SN=%d of size %d B (available space %d B)", header.sn, nof_bytes, rx_sdu.buf->get_tailroom()); - rx_window.remove_pdu(header.sn); + rx_window->remove_pdu(header.sn); return SRSRAN_ERROR; } memcpy(rx_sdu.buf->msg, payload + hdr_len, nof_bytes - hdr_len); // Don't copy header rx_sdu.buf->N_bytes = nof_bytes - hdr_len; rx_sdu.fully_received = true; - write_to_upper_layers(parent->lcid, std::move(rx_window[header.sn].buf)); + write_to_upper_layers(parent->lcid, std::move((*rx_window)[header.sn].buf)); return SRSRAN_SUCCESS; } @@ -1162,7 +1193,7 @@ int rlc_am_nr_rx::handle_segment_data_sdu(const rlc_am_nr_pdu_header_t& header, } // Add a new SDU to the RX window if necessary - rlc_amd_rx_sdu_nr_t& rx_sdu = rx_window.has_sn(header.sn) ? rx_window[header.sn] : rx_window.add_pdu(header.sn); + rlc_amd_rx_sdu_nr_t& rx_sdu = rx_window->has_sn(header.sn) ? (*rx_window)[header.sn] : rx_window->add_pdu(header.sn); // Create PDU segment info, to be stored later rlc_amd_rx_pdu_nr pdu_segment = {}; @@ -1185,14 +1216,14 @@ int rlc_am_nr_rx::handle_segment_data_sdu(const rlc_am_nr_pdu_header_t& header, rx_sdu.buf = srsran::make_byte_buffer(); if (rx_sdu.buf == nullptr) { RlcError("fatal error. Couldn't allocate PDU in %s.", __FUNCTION__); - rx_window.remove_pdu(header.sn); + rx_window->remove_pdu(header.sn); return SRSRAN_ERROR; } for (const auto& it : rx_sdu.segments) { memcpy(&rx_sdu.buf->msg[rx_sdu.buf->N_bytes], it.buf->msg, it.buf->N_bytes); rx_sdu.buf->N_bytes += it.buf->N_bytes; } - write_to_upper_layers(parent->lcid, std::move(rx_window[header.sn].buf)); + write_to_upper_layers(parent->lcid, std::move((*rx_window)[header.sn].buf)); } return SRSRAN_SUCCESS; } @@ -1213,20 +1244,20 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m uint32_t i = status->ack_sn; while (rx_mod_base_nr(i) <= rx_mod_base_nr(st.rx_highest_status)) { - if ((rx_window.has_sn(i) && rx_window[i].fully_received) || i == st.rx_highest_status) { + if ((rx_window->has_sn(i) && (*rx_window)[i].fully_received) || i == st.rx_highest_status) { // only update ACK_SN if this SN has been fully received, or if we reached the maximum possible SN status->ack_sn = i; } else { - if (not rx_window.has_sn(i)) { + if (not rx_window->has_sn(i)) { // No segment received, NACK the whole SDU status->nacks[status->N_nack].nack_sn = i; status->N_nack++; - } else if (not rx_window[i].fully_received) { + } else if (not(*rx_window)[i].fully_received) { // Some segments were received, but not all. // NACK non consecutive missing bytes uint32_t last_so = 0; bool last_segment_rx = false; - for (auto segm = rx_window[i].segments.begin(); segm != rx_window[i].segments.end(); segm++) { + for (auto segm = (*rx_window)[i].segments.begin(); segm != (*rx_window)[i].segments.end(); segm++) { if (segm->header.so != last_so) { // Some bytes were not received status->nacks[status->N_nack].nack_sn = i; @@ -1301,7 +1332,7 @@ void rlc_am_nr_rx::timer_expired(uint32_t timeout_id) */ for (uint32_t tmp_sn = st.rx_next_status_trigger; tmp_sn < st.rx_next_status_trigger + RLC_AM_NR_WINDOW_SIZE; tmp_sn++) { - if (not rx_window.has_sn(tmp_sn)) { + if (not rx_window->has_sn(tmp_sn)) { st.rx_highest_status = tmp_sn; break; } @@ -1310,7 +1341,7 @@ void rlc_am_nr_rx::timer_expired(uint32_t timeout_id) if (st.rx_next_highest > st.rx_highest_status + 1) { restart_reassembly_timer = true; } - if (st.rx_next_highest == st.rx_highest_status + 1 && not rx_window[st.rx_next_highest].fully_received) { + if (st.rx_next_highest == st.rx_highest_status + 1 && not(*rx_window)[st.rx_next_highest].fully_received) { restart_reassembly_timer = true; } if (restart_reassembly_timer) { From 717132e0c330ef56cd0923cfe844d8e692ede9d8 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Sat, 12 Mar 2022 06:19:54 +0100 Subject: [PATCH 050/195] lib,rlc_am_nr: fix access of unconfigured entity --- lib/src/rlc/rlc_am_nr.cc | 5 +++++ lib/test/rlc/rlc_am_nr_test.cc | 30 +++++++++++++++--------------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 08591f4d9..331842123 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -854,6 +854,11 @@ void rlc_am_nr_tx::get_buffer_state(uint32_t& n_bytes_new, uint32_t& n_bytes_pri std::lock_guard lock(mutex); RlcDebug("buffer state - do_status=%s", do_status() ? "yes" : "no"); + if (!tx_enabled) { + RlcError("get_buffer_state() failed: TX is not enabled."); + return; + } + // Bytes needed for status report if (do_status()) { n_bytes_prio += rx->get_status_pdu_length(); diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index d0333f746..20082552b 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -225,9 +225,6 @@ int basic_test(rlc_am_nr_sn_size_t sn_size) rlc_am_nr_tx* tx2 = dynamic_cast(rlc2.get_tx()); rlc_am_nr_rx* rx2 = dynamic_cast(rlc2.get_rx()); - // before configuring entity - TESTASSERT_EQ(0, rlc1.get_buffer_state()); - if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { return -1; } @@ -236,6 +233,9 @@ int basic_test(rlc_am_nr_sn_size_t sn_size) return -1; } + // after configuring entity + TESTASSERT_EQ(0, rlc1.get_buffer_state()); + basic_test_tx(&rlc1, pdu_bufs, sn_size); // Write 5 PDUs into RLC2 @@ -332,9 +332,6 @@ int lost_pdu_test(rlc_am_nr_sn_size_t sn_size) uint32_t data_pdu_size = header_size + payload_size; uint32_t expect_buffer_state = NBUFS * data_pdu_size; - // before configuring entity - TESTASSERT(0 == rlc1.get_buffer_state()); - if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { return -1; } @@ -343,6 +340,9 @@ int lost_pdu_test(rlc_am_nr_sn_size_t sn_size) return -1; } + // after configuring entity + TESTASSERT(0 == rlc1.get_buffer_state()); + basic_test_tx(&rlc1, pdu_bufs, sn_size); // Write 5 PDUs into RLC2 @@ -469,9 +469,6 @@ int basic_segmentation_test(rlc_am_nr_sn_size_t sn_size) rlc_am_nr_tx* tx2 = dynamic_cast(rlc2.get_tx()); rlc_am_nr_rx* rx2 = dynamic_cast(rlc2.get_rx()); - // before configuring entity - TESTASSERT_EQ(0, rlc1.get_buffer_state()); - if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { return -1; } @@ -480,6 +477,9 @@ int basic_segmentation_test(rlc_am_nr_sn_size_t sn_size) return -1; } + // after configuring entity + TESTASSERT_EQ(0, rlc1.get_buffer_state()); + // Push 1 SDU into RLC1 unique_byte_buffer_t sdu; constexpr uint32_t payload_size = 3; // Give the SDU the size of 3 bytes @@ -557,9 +557,6 @@ int segment_retx_test() rlc_am_nr_tx* tx2 = dynamic_cast(rlc2.get_tx()); rlc_am_nr_rx* rx2 = dynamic_cast(rlc2.get_rx()); - // before configuring entity - TESTASSERT_EQ(0, rlc1.get_buffer_state()); - if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config())) { return -1; } @@ -568,6 +565,9 @@ int segment_retx_test() return -1; } + // after configuring entity + TESTASSERT_EQ(0, rlc1.get_buffer_state()); + // Push 5 SDUs into RLC1 unique_byte_buffer_t sdu_bufs[NBUFS]; for (int i = 0; i < NBUFS; i++) { @@ -728,9 +728,6 @@ int retx_segment_test() rlc_am_nr_tx* tx2 = dynamic_cast(rlc2.get_tx()); rlc_am_nr_rx* rx2 = dynamic_cast(rlc2.get_rx()); - // before configuring entity - TESTASSERT(0 == rlc1.get_buffer_state()); - if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config())) { return -1; } @@ -739,6 +736,9 @@ int retx_segment_test() return -1; } + // after configuring entity + TESTASSERT(0 == rlc1.get_buffer_state()); + int n_sdu_bufs = 5; int n_pdu_bufs = 15; From 80e23624f82fe81c316258027b0d91405a4c7d22 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Sat, 12 Mar 2022 06:21:43 +0100 Subject: [PATCH 051/195] lib,rlc_am_nr: extract function am_window_size() --- .../srsran/interfaces/rlc_interface_types.h | 8 ++++++++ lib/include/srsran/rlc/rlc_am_nr.h | 12 +++--------- lib/src/rlc/rlc_am_nr.cc | 16 ++++++++-------- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/lib/include/srsran/interfaces/rlc_interface_types.h b/lib/include/srsran/interfaces/rlc_interface_types.h index 5d574826f..903dc3f81 100644 --- a/lib/include/srsran/interfaces/rlc_interface_types.h +++ b/lib/include/srsran/interfaces/rlc_interface_types.h @@ -78,6 +78,14 @@ constexpr uint32_t cardinality(const rlc_am_nr_sn_size_t& sn_size) { return (1 << to_number(sn_size)); } +/**************************************************************************** + * Tx constants + * Ref: 3GPP TS 38.322 version 16.2.0 Section 7.2 + ***************************************************************************/ +constexpr uint32_t am_window_size(const rlc_am_nr_sn_size_t& sn_size) +{ + return cardinality(sn_size) / 2; +} struct rlc_am_config_t { /**************************************************************************** diff --git a/lib/include/srsran/rlc/rlc_am_nr.h b/lib/include/srsran/rlc/rlc_am_nr.h index 07cf6c123..a5312a89d 100644 --- a/lib/include/srsran/rlc/rlc_am_nr.h +++ b/lib/include/srsran/rlc/rlc_am_nr.h @@ -147,14 +147,10 @@ private: * Ref: 3GPP TS 38.322 version 16.2.0 Section 7.1 ***************************************************************************/ struct rlc_am_nr_tx_state_t st = {}; - std::unique_ptr > tx_window = - std::unique_ptr >( - new rlc_ringbuffer_t); + std::unique_ptr > tx_window; // Queues and buffers - std::unique_ptr > retx_queue = - std::unique_ptr >( - new pdu_retx_queue); + std::unique_ptr > retx_queue; uint32_t sdu_under_segmentation_sn = INVALID_RLC_SN; // SN of the SDU currently being segmented. pdcp_sn_vector_t notify_info_vec; @@ -254,9 +250,7 @@ private: uint32_t rx_mod_base_nr(uint32_t sn) const; // RX Window - std::unique_ptr > rx_window = - std::unique_ptr >( - new rlc_ringbuffer_t); + std::unique_ptr > rx_window; // Mutexes std::mutex mutex; diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 331842123..c183bf5ca 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -51,16 +51,16 @@ bool rlc_am_nr_tx::configure(const rlc_config_t& cfg_) case rlc_am_nr_sn_size_t::size12bits: min_hdr_size = 2; tx_window = std::unique_ptr >( - new rlc_ringbuffer_t); + new rlc_ringbuffer_t); retx_queue = std::unique_ptr >( - new pdu_retx_queue); + new pdu_retx_queue); break; case rlc_am_nr_sn_size_t::size18bits: min_hdr_size = 3; tx_window = std::unique_ptr >( - new rlc_ringbuffer_t); + new rlc_ringbuffer_t); retx_queue = std::unique_ptr >( - new pdu_retx_queue); + new pdu_retx_queue); break; default: RlcError("attempt to configure unsupported tx_sn_field_length %s", to_string(cfg.tx_sn_field_length)); @@ -943,7 +943,7 @@ uint32_t rlc_am_nr_tx::tx_mod_base_nr(uint32_t sn) const uint32_t rlc_am_nr_tx::tx_window_size() const { - return cardinality(cfg.tx_sn_field_length) / 2; + return am_window_size(cfg.tx_sn_field_length); } bool rlc_am_nr_tx::inside_tx_window(uint32_t sn) const @@ -995,11 +995,11 @@ bool rlc_am_nr_rx::configure(const rlc_config_t& cfg_) switch (cfg.rx_sn_field_length) { case rlc_am_nr_sn_size_t::size12bits: rx_window = std::unique_ptr >( - new rlc_ringbuffer_t); + new rlc_ringbuffer_t); break; case rlc_am_nr_sn_size_t::size18bits: rx_window = std::unique_ptr >( - new rlc_ringbuffer_t); + new rlc_ringbuffer_t); break; default: RlcError("attempt to configure unsupported rx_sn_field_length %s", to_string(cfg.rx_sn_field_length)); @@ -1383,7 +1383,7 @@ uint32_t rlc_am_nr_rx::rx_mod_base_nr(uint32_t sn) const uint32_t rlc_am_nr_rx::rx_window_size() const { - return cardinality(cfg.rx_sn_field_length) / 2; + return am_window_size(cfg.rx_sn_field_length); } bool rlc_am_nr_rx::inside_rx_window(uint32_t sn) From 0cf052e31d1d6fa16f7879d1ddf7ea2d538baf88 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Sat, 12 Mar 2022 06:42:12 +0100 Subject: [PATCH 052/195] lib,rlc_am_nr: replace hard-coded WINDOWS_SIZE --- lib/include/srsran/rlc/rlc_am_nr_packing.h | 4 +--- lib/include/srsran/rlc/rlc_common.h | 2 +- lib/src/rlc/rlc_am_nr.cc | 7 ++++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_am_nr_packing.h b/lib/include/srsran/rlc/rlc_am_nr_packing.h index 0bdc845d1..457b14e8a 100644 --- a/lib/include/srsran/rlc/rlc_am_nr_packing.h +++ b/lib/include/srsran/rlc/rlc_am_nr_packing.h @@ -19,8 +19,6 @@ namespace srsran { -const uint32_t RLC_AM_NR_WINDOW_SIZE_12BIT = 4096; -const uint32_t RLC_AM_NR_WINDOW_SIZE_18BIT = 262144; const uint32_t INVALID_RLC_SN = 0xFFFFFFFF; const uint32_t RETX_COUNT_NOT_STARTED = 0xFFFFFFFF; @@ -85,7 +83,7 @@ typedef struct { uint32_t ack_sn; ///< SN of the next not received RLC Data PDU uint16_t N_nack; ///< number of NACKs uint8_t nack_range; ///< number of consecutively lost RLC SDUs starting from and including NACK_SN - rlc_status_nack_t nacks[RLC_AM_NR_WINDOW_SIZE_12BIT]; + rlc_status_nack_t nacks[RLC_AM_NR_MAX_NACKS]; } rlc_am_nr_status_pdu_t; /**************************************************************************** diff --git a/lib/include/srsran/rlc/rlc_common.h b/lib/include/srsran/rlc/rlc_common.h index 049c7983c..0e1dacc94 100644 --- a/lib/include/srsran/rlc/rlc_common.h +++ b/lib/include/srsran/rlc/rlc_common.h @@ -34,7 +34,7 @@ namespace srsran { #define RLC_MAX_SDU_SIZE ((1 << 11) - 1) // Length of LI field is 11bits #define RLC_AM_MIN_DATA_PDU_SIZE (3) // AMD PDU with 10 bit SN (length of LI field is 11 bits) (No LI) -#define RLC_AM_NR_WINDOW_SIZE 2048 +#define RLC_AM_NR_MAX_NACKS 2048 #define RlcDebug(fmt, ...) logger.debug("%s: " fmt, rb_name, ##__VA_ARGS__) #define RlcInfo(fmt, ...) logger.info("%s: " fmt, rb_name, ##__VA_ARGS__) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index c183bf5ca..643af2d05 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -1075,7 +1075,7 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) */ if (rx_mod_base_nr(header.sn) == rx_mod_base_nr(st.rx_highest_status)) { uint32_t sn_upd = 0; - uint32_t window_top = st.rx_next + RLC_AM_NR_WINDOW_SIZE; + uint32_t window_top = st.rx_next + am_window_size(cfg.rx_sn_field_length); for (sn_upd = st.rx_highest_status; sn_upd < window_top; ++sn_upd) { if (rx_window->has_sn(sn_upd)) { if (not(*rx_window)[sn_upd].fully_received) { @@ -1097,7 +1097,7 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) */ if (rx_mod_base_nr(header.sn) == rx_mod_base_nr(st.rx_next)) { uint32_t sn_upd = 0; - uint32_t window_top = st.rx_next + RLC_AM_NR_WINDOW_SIZE; + uint32_t window_top = st.rx_next + am_window_size(cfg.rx_sn_field_length); for (sn_upd = st.rx_next; sn_upd < window_top; ++sn_upd) { if (rx_window->has_sn(sn_upd)) { if (not(*rx_window)[sn_upd].fully_received) { @@ -1335,7 +1335,8 @@ void rlc_am_nr_rx::timer_expired(uint32_t timeout_id) * - start t-Reassembly; * - set RX_Next_Status_Trigger to RX_Next_Highest. */ - for (uint32_t tmp_sn = st.rx_next_status_trigger; tmp_sn < st.rx_next_status_trigger + RLC_AM_NR_WINDOW_SIZE; + for (uint32_t tmp_sn = st.rx_next_status_trigger; + tmp_sn < st.rx_next_status_trigger + am_window_size(cfg.rx_sn_field_length); tmp_sn++) { if (not rx_window->has_sn(tmp_sn)) { st.rx_highest_status = tmp_sn; From 5bb6cdec1e8167b9c0ae823f55513f22573ad582 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Sat, 12 Mar 2022 07:44:51 +0100 Subject: [PATCH 053/195] lib,rlc_am_nr: use std:vector for nacks in rlc_am_nr_status_pdu_t - Also create tx_status a member to avoid frequent allocations for each created PDU. - Remove unused member nack_range from rlc_am_nr_status_pdu_t. --- lib/include/srsran/rlc/rlc_am_nr.h | 3 ++- lib/include/srsran/rlc/rlc_am_nr_packing.h | 24 +++++++++++++++------- lib/include/srsran/rlc/rlc_common.h | 3 ++- lib/src/rlc/rlc_am_nr.cc | 6 ++++-- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_am_nr.h b/lib/include/srsran/rlc/rlc_am_nr.h index a5312a89d..de65f0f3e 100644 --- a/lib/include/srsran/rlc/rlc_am_nr.h +++ b/lib/include/srsran/rlc/rlc_am_nr.h @@ -149,10 +149,11 @@ private: struct rlc_am_nr_tx_state_t st = {}; std::unique_ptr > tx_window; - // Queues and buffers + // Queues, buffers and container std::unique_ptr > retx_queue; uint32_t sdu_under_segmentation_sn = INVALID_RLC_SN; // SN of the SDU currently being segmented. pdcp_sn_vector_t notify_info_vec; + rlc_am_nr_status_pdu_t tx_status; // Helper constants uint32_t min_hdr_size = 2; diff --git a/lib/include/srsran/rlc/rlc_am_nr_packing.h b/lib/include/srsran/rlc/rlc_am_nr_packing.h index 457b14e8a..574d89c17 100644 --- a/lib/include/srsran/rlc/rlc_am_nr_packing.h +++ b/lib/include/srsran/rlc/rlc_am_nr_packing.h @@ -78,13 +78,23 @@ struct rlc_amd_tx_sdu_nr_t { }; ///< AM NR Status PDU header (perhaps merge with LTE version) -typedef struct { - rlc_am_nr_control_pdu_type_t cpt; - uint32_t ack_sn; ///< SN of the next not received RLC Data PDU - uint16_t N_nack; ///< number of NACKs - uint8_t nack_range; ///< number of consecutively lost RLC SDUs starting from and including NACK_SN - rlc_status_nack_t nacks[RLC_AM_NR_MAX_NACKS]; -} rlc_am_nr_status_pdu_t; +struct rlc_am_nr_status_pdu_t { + rlc_am_nr_control_pdu_type_t cpt; + uint32_t ack_sn; ///< SN of the next not received RLC Data PDU + uint16_t N_nack; ///< number of NACKs + std::vector nacks; + + rlc_am_nr_status_pdu_t() : + cpt(rlc_am_nr_control_pdu_type_t::status_pdu), ack_sn(INVALID_RLC_SN), N_nack(0), nacks(RLC_AM_NR_TYP_NACKS) + {} + void reset() + { + cpt = rlc_am_nr_control_pdu_type_t::status_pdu; + ack_sn = INVALID_RLC_SN; + N_nack = 0; + nacks.clear(); + } +}; /**************************************************************************** * Header pack/unpack helper functions for NR diff --git a/lib/include/srsran/rlc/rlc_common.h b/lib/include/srsran/rlc/rlc_common.h index 0e1dacc94..796704823 100644 --- a/lib/include/srsran/rlc/rlc_common.h +++ b/lib/include/srsran/rlc/rlc_common.h @@ -34,7 +34,8 @@ namespace srsran { #define RLC_MAX_SDU_SIZE ((1 << 11) - 1) // Length of LI field is 11bits #define RLC_AM_MIN_DATA_PDU_SIZE (3) // AMD PDU with 10 bit SN (length of LI field is 11 bits) (No LI) -#define RLC_AM_NR_MAX_NACKS 2048 +#define RLC_AM_NR_TYP_NACKS 512 // Expected number of NACKs in status PDU before expanding space by alloc +#define RLC_AM_NR_MAX_NACKS 2048 // Maximum number of NACKs in status PDU #define RlcDebug(fmt, ...) logger.debug("%s: " fmt, rb_name, ##__VA_ARGS__) #define RlcInfo(fmt, ...) logger.info("%s: " fmt, rb_name, ##__VA_ARGS__) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 643af2d05..c2cf5fc52 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -676,8 +676,8 @@ uint32_t rlc_am_nr_tx::get_retx_expected_hdr_len(const rlc_amd_retx_nr_t& retx) uint32_t rlc_am_nr_tx::build_status_pdu(byte_buffer_t* payload, uint32_t nof_bytes) { RlcInfo("generating status PDU. Bytes available:%d", nof_bytes); - rlc_am_nr_status_pdu_t tx_status; - int pdu_len = rx->get_status_pdu(&tx_status, nof_bytes); + tx_status.reset(); + int pdu_len = rx->get_status_pdu(&tx_status, nof_bytes); if (pdu_len == SRSRAN_ERROR) { RlcDebug("deferred status PDU. Cause: Failed to acquire rx lock"); pdu_len = 0; @@ -1286,6 +1286,8 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m } } + // TODO: add check to not exceed status->N_nack >= RLC_AM_NR_MAX_NACKS + // make sure we don't exceed grant size (FIXME) rlc_am_nr_write_status_pdu(*status, cfg.rx_sn_field_length, &tmp_buf); // TODO From b52a102021e63340ef6f6596824f7eddfa639ce7 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Tue, 15 Mar 2022 18:53:39 +0100 Subject: [PATCH 054/195] lib,rlc_am_nr: extend unit tests for 18-bit SNs --- lib/test/rlc/rlc_am_nr_test.cc | 146 ++++++++++++++++++--------------- 1 file changed, 81 insertions(+), 65 deletions(-) diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index 20082552b..ad0f57ffa 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -65,7 +65,7 @@ int window_checker_test(rlc_am_nr_sn_size_t sn_size) timer_handler timers(8); auto& test_logger = srslog::fetch_basic_logger("TESTER "); - test_delimit_logger delimiter("window checkers"); + test_delimit_logger delimiter("window checkers (%d bit SN)", to_number(sn_size)); rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); rlc_am_nr_tx* tx = dynamic_cast(rlc1.get_tx()); @@ -129,7 +129,7 @@ int retx_segmentation_required_checker_test(rlc_am_nr_sn_size_t sn_size) timer_handler timers(8); auto& test_logger = srslog::fetch_basic_logger("TESTER "); - test_delimit_logger delimiter("retx segmentation required checkers"); + test_delimit_logger delimiter("retx segmentation required checkers (%d bit SN)", to_number(sn_size)); rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); rlc_am_nr_tx* tx = dynamic_cast(rlc1.get_tx()); @@ -216,7 +216,7 @@ int basic_test(rlc_am_nr_sn_size_t sn_size) byte_buffer_t pdu_bufs[NBUFS]; auto& test_logger = srslog::fetch_basic_logger("TESTER "); - test_delimit_logger delimiter("basic tx/rx"); + test_delimit_logger delimiter("basic tx/rx (%d bit SN)", to_number(sn_size)); rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); @@ -325,7 +325,7 @@ int lost_pdu_test(rlc_am_nr_sn_size_t sn_size) auto& test_logger = srslog::fetch_basic_logger("TESTER "); rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); - test_delimit_logger delimiter("lost PDU"); + test_delimit_logger delimiter("lost PDU (%d bit SN)", to_number(sn_size)); constexpr uint32_t payload_size = 1; uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; @@ -460,7 +460,7 @@ int basic_segmentation_test(rlc_am_nr_sn_size_t sn_size) rlc_am_tester tester; timer_handler timers(8); auto& test_logger = srslog::fetch_basic_logger("TESTER "); - test_delimit_logger delimiter("basic segmentation"); + test_delimit_logger delimiter("basic segmentation (%d bit SN)", to_number(sn_size)); rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); @@ -541,7 +541,7 @@ int basic_segmentation_test(rlc_am_nr_sn_size_t sn_size) return SRSRAN_SUCCESS; } -int segment_retx_test() +int segment_retx_test(rlc_am_nr_sn_size_t sn_size) { rlc_am_tester tester; timer_handler timers(8); @@ -550,18 +550,18 @@ int segment_retx_test() auto& test_logger = srslog::fetch_basic_logger("TESTER "); rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); - test_delimit_logger delimiter("segment retx PDU"); + test_delimit_logger delimiter("segment retx PDU (%d bit SN)", to_number(sn_size)); rlc_am_nr_tx* tx1 = dynamic_cast(rlc1.get_tx()); rlc_am_nr_rx* rx1 = dynamic_cast(rlc1.get_rx()); rlc_am_nr_tx* tx2 = dynamic_cast(rlc2.get_tx()); rlc_am_nr_rx* rx2 = dynamic_cast(rlc2.get_rx()); - if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config())) { + if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { return -1; } - if (not rlc2.configure(rlc_config_t::default_rlc_am_nr_config())) { + if (not rlc2.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { return -1; } @@ -570,21 +570,24 @@ int segment_retx_test() // Push 5 SDUs into RLC1 unique_byte_buffer_t sdu_bufs[NBUFS]; + constexpr uint32_t payload_size = 3; // Give the SDU the size of 3 bytes + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; for (int i = 0; i < NBUFS; i++) { sdu_bufs[i] = srsran::make_byte_buffer(); - sdu_bufs[i]->msg[0] = i; // Write the index into the buffer - sdu_bufs[i]->N_bytes = 3; // Give each buffer a size of 3 bytes - sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications + sdu_bufs[i]->msg[0] = i; // Write the index into the buffer + sdu_bufs[i]->N_bytes = payload_size; // Give each buffer a size of 3 bytes + sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications rlc1.write_sdu(std::move(sdu_bufs[i])); } - TESTASSERT_EQ(25, rlc1.get_buffer_state()); // 2 Bytes * NBUFFS (header size) + NBUFFS * 3 (data) = 25 + uint32_t expected_buffer_state = (header_size + payload_size) * NBUFS; + TESTASSERT_EQ(expected_buffer_state, rlc1.get_buffer_state()); // Read 5 PDUs from RLC1 (1 byte each) for (int i = 0; i < NBUFS; i++) { - uint32_t len = rlc1.read_pdu(pdu_bufs[i].msg, 5); // 2 bytes for header + 3 byte payload + uint32_t len = rlc1.read_pdu(pdu_bufs[i].msg, header_size + payload_size); pdu_bufs[i].N_bytes = len; - TESTASSERT_EQ(5, len); + TESTASSERT_EQ(header_size + payload_size, len); } TESTASSERT_EQ(0, rlc1.get_buffer_state()); @@ -608,7 +611,7 @@ int segment_retx_test() // Assert status is correct rlc_am_nr_status_pdu_t status_check = {}; - rlc_am_nr_read_status_pdu(&status_buf, rlc_am_nr_sn_size_t::size12bits, &status_check); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); TESTASSERT_EQ(3, status_check.ack_sn); // 3 is the next expected SN (i.e. the lost packet.) // Write status PDU to RLC1 @@ -621,18 +624,20 @@ int segment_retx_test() } // t-reassembly has expired. There should be a NACK in the status report. - TESTASSERT_EQ(5, rlc2.get_buffer_state()); + constexpr uint32_t status_pdu_ack_size = 3; + uint32_t status_pdu_nack_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + TESTASSERT_EQ(status_pdu_ack_size + status_pdu_nack_size, rlc2.get_buffer_state()); { // Read status PDU from RLC2 byte_buffer_t status_buf; - int len = rlc2.read_pdu(status_buf.msg, 5); + int len = rlc2.read_pdu(status_buf.msg, status_pdu_ack_size + status_pdu_nack_size); status_buf.N_bytes = len; TESTASSERT_EQ(0, rlc2.get_buffer_state()); // Assert status is correct rlc_am_nr_status_pdu_t status_check = {}; - rlc_am_nr_read_status_pdu(&status_buf, rlc_am_nr_sn_size_t::size12bits, &status_check); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. TESTASSERT_EQ(1, status_check.N_nack); // We lost one PDU. TESTASSERT_EQ(3, status_check.nacks[0].nack_sn); // Lost PDU SN=3. @@ -641,25 +646,29 @@ int segment_retx_test() rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); // Check there is an Retx of SN=3 - TESTASSERT_EQ(5, rlc1.get_buffer_state()); + TESTASSERT_EQ(header_size + payload_size, rlc1.get_buffer_state()); } + constexpr uint32_t so_size = 2; + constexpr uint32_t segment_size = 1; + uint32_t pdu_size_first = header_size + segment_size; + uint32_t pdu_size_continued = header_size + so_size + segment_size; { // Re-transmit PDU in 3 segments for (int i = 0; i < 3; i++) { byte_buffer_t retx_buf; uint32_t len = 0; if (i == 0) { - len = rlc1.read_pdu(retx_buf.msg, 3); - TESTASSERT_EQ(3, len); + len = rlc1.read_pdu(retx_buf.msg, pdu_size_first); + TESTASSERT_EQ(pdu_size_first, len); } else { - len = rlc1.read_pdu(retx_buf.msg, 5); - TESTASSERT_EQ(5, len); + len = rlc1.read_pdu(retx_buf.msg, pdu_size_continued); + TESTASSERT_EQ(pdu_size_continued, len); } retx_buf.N_bytes = len; rlc_am_nr_pdu_header_t header_check = {}; - uint32_t hdr_len = rlc_am_nr_read_data_pdu_header(&retx_buf, rlc_am_nr_sn_size_t::size12bits, &header_check); + uint32_t hdr_len = rlc_am_nr_read_data_pdu_header(&retx_buf, sn_size, &header_check); // Double check header. TESTASSERT_EQ(3, header_check.sn); // Double check RETX SN if (i == 0) { @@ -679,6 +688,12 @@ int segment_retx_test() rlc_bearer_metrics_t metrics1 = rlc1.get_metrics(); rlc_bearer_metrics_t metrics2 = rlc2.get_metrics(); + uint32_t data_pdu_size = header_size + payload_size; + uint32_t total_tx_pdu_bytes1 = NBUFS * data_pdu_size + pdu_size_first + 2 * pdu_size_continued; + uint32_t total_rx_pdu_bytes1 = 2 * status_pdu_ack_size + status_pdu_nack_size; // Two status PDU (one with a NACK) + uint32_t total_tx_pdu_bytes2 = total_rx_pdu_bytes1; + uint32_t total_rx_pdu_bytes2 = (NBUFS - 1) * data_pdu_size + pdu_size_first + 2 * pdu_size_continued; + // SDU metrics TESTASSERT_EQ(5, metrics1.num_tx_sdus); TESTASSERT_EQ(0, metrics1.num_rx_sdus); @@ -686,12 +701,11 @@ int segment_retx_test() TESTASSERT_EQ(0, metrics1.num_rx_sdu_bytes); TESTASSERT_EQ(0, metrics1.num_lost_sdus); // PDU metrics - TESTASSERT_EQ(5 + 3, metrics1.num_tx_pdus); // 3 re-transmissions - TESTASSERT_EQ(2, metrics1.num_rx_pdus); // Two status PDU - TESTASSERT_EQ(38, metrics1.num_tx_pdu_bytes); // 2 Bytes * NBUFFS (header size) + NBUFFS * 3 (data) + - // 3 (1 retx no SO) + 2 * 5 (2 retx with SO) = 38 - TESTASSERT_EQ(3 + 5, metrics1.num_rx_pdu_bytes); // Two status PDU (one with a NACK) - TESTASSERT_EQ(0, metrics1.num_lost_sdus); // No lost SDUs + TESTASSERT_EQ(5 + 3, metrics1.num_tx_pdus); // 3 re-transmissions + TESTASSERT_EQ(2, metrics1.num_rx_pdus); // Two status PDU + TESTASSERT_EQ(total_tx_pdu_bytes1, metrics1.num_tx_pdu_bytes); + TESTASSERT_EQ(total_rx_pdu_bytes1, metrics1.num_rx_pdu_bytes); + TESTASSERT_EQ(0, metrics1.num_lost_sdus); // No lost SDUs // PDU metrics TESTASSERT_EQ(0, metrics2.num_tx_sdus); @@ -702,8 +716,9 @@ int segment_retx_test() // SDU metrics TESTASSERT_EQ(2, metrics2.num_tx_pdus); // Two status PDUs TESTASSERT_EQ(7, metrics2.num_rx_pdus); // 7 PDUs (8 tx'ed, but one was lost) - TESTASSERT_EQ(5 + 3, metrics2.num_tx_pdu_bytes); // Two status PDU (one with a NACK) - TESTASSERT_EQ(33, metrics2.num_rx_pdu_bytes); // 2 Bytes * (NBUFFS-1) (header size) + (NBUFFS-1) * 3 (data) + TESTASSERT_EQ(total_tx_pdu_bytes2, metrics2.num_tx_pdu_bytes); + TESTASSERT_EQ(total_rx_pdu_bytes2, + metrics2.num_rx_pdu_bytes); // 2 Bytes * (NBUFFS-1) (header size) + (NBUFFS-1) * 3 (data) // 3 (1 retx no SO) + 2 * 5 (2 retx with SO) = 33 TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs @@ -713,7 +728,7 @@ int segment_retx_test() return SRSRAN_SUCCESS; } -int retx_segment_test() +int retx_segment_test(rlc_am_nr_sn_size_t sn_size) { rlc_am_tester tester; timer_handler timers(8); @@ -721,18 +736,18 @@ int retx_segment_test() auto& test_logger = srslog::fetch_basic_logger("TESTER "); rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); - test_delimit_logger delimiter("retx segment PDU"); + test_delimit_logger delimiter("retx segment PDU (%d bit SN)", to_number(sn_size)); rlc_am_nr_tx* tx1 = dynamic_cast(rlc1.get_tx()); rlc_am_nr_rx* rx1 = dynamic_cast(rlc1.get_rx()); rlc_am_nr_tx* tx2 = dynamic_cast(rlc2.get_tx()); rlc_am_nr_rx* rx2 = dynamic_cast(rlc2.get_rx()); - if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config())) { + if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { return -1; } - if (not rlc2.configure(rlc_config_t::default_rlc_am_nr_config())) { + if (not rlc2.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { return -1; } @@ -792,7 +807,7 @@ int retx_segment_test() // Assert status is correct rlc_am_nr_status_pdu_t status_check = {}; - rlc_am_nr_read_status_pdu(&status_buf, rlc_am_nr_sn_size_t::size12bits, &status_check); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); TESTASSERT_EQ(1, status_check.ack_sn); // 1 is the next expected SN (i.e. the first lost packet.) // Write status PDU to RLC1 @@ -817,7 +832,7 @@ int retx_segment_test() // Assert status is correct rlc_am_nr_status_pdu_t status_check = {}; - rlc_am_nr_read_status_pdu(&status_buf, rlc_am_nr_sn_size_t::size12bits, &status_check); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. TESTASSERT_EQ(3, status_check.N_nack); // We lost one PDU. TESTASSERT_EQ(1, status_check.nacks[0].nack_sn); // Lost SDU on SN=1. @@ -855,7 +870,7 @@ int retx_segment_test() retx_buf.N_bytes = len; rlc_am_nr_pdu_header_t header_check = {}; - uint32_t hdr_len = rlc_am_nr_read_data_pdu_header(&retx_buf, rlc_am_nr_sn_size_t::size12bits, &header_check); + uint32_t hdr_len = rlc_am_nr_read_data_pdu_header(&retx_buf, sn_size, &header_check); // Double check header. if (i == 0) { TESTASSERT_EQ(1, header_check.sn); // Double check RETX SN @@ -918,7 +933,7 @@ int retx_segment_test() // This test checks whether RLC informs upper layer when max retransmission has been reached // due to lost SDUs as a whole -int max_retx_lost_sdu_test() +int max_retx_lost_sdu_test(rlc_am_nr_sn_size_t sn_size) { rlc_am_tester tester; timer_handler timers(8); @@ -928,9 +943,9 @@ int max_retx_lost_sdu_test() rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); srslog::fetch_basic_logger("RLC").set_hex_dump_max_size(100); - test_delimit_logger delimiter("max retx lost SDU"); + test_delimit_logger delimiter("max retx lost SDU (%d bit SN)", to_number(sn_size)); - const rlc_config_t rlc_cfg = rlc_config_t::default_rlc_am_nr_config(); + const rlc_config_t rlc_cfg = rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)); if (not rlc1.configure(rlc_cfg)) { return SRSRAN_ERROR; } @@ -988,7 +1003,7 @@ int max_retx_lost_sdu_test() // This test checks whether RLC informs upper layer when max retransmission has been reached // due to lost SDU segments -int max_retx_lost_segments_test() +int max_retx_lost_segments_test(rlc_am_nr_sn_size_t sn_size) { rlc_am_tester tester; timer_handler timers(8); @@ -998,9 +1013,9 @@ int max_retx_lost_segments_test() rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); srslog::fetch_basic_logger("RLC").set_hex_dump_max_size(100); - test_delimit_logger delimiter("max retx lost SDU segment"); + test_delimit_logger delimiter("max retx lost SDU segment (%d bit SN)", to_number(sn_size)); - const rlc_config_t rlc_cfg = rlc_config_t::default_rlc_am_nr_config(); + const rlc_config_t rlc_cfg = rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)); if (not rlc1.configure(rlc_cfg)) { return SRSRAN_ERROR; } @@ -1094,7 +1109,7 @@ int max_retx_lost_segments_test() } // This test checks the correct functioning of RLC discard functionality -int discard_test() +int discard_test(rlc_am_nr_sn_size_t sn_size) { rlc_am_tester tester; timer_handler timers(8); @@ -1102,16 +1117,17 @@ int discard_test() auto& test_logger = srslog::fetch_basic_logger("TESTER "); rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + test_delimit_logger delimiter("discard test (%d bit SN)", to_number(sn_size)); srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); srslog::fetch_basic_logger("RLC_AM_2").set_hex_dump_max_size(100); srslog::fetch_basic_logger("RLC").set_hex_dump_max_size(100); - if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config())) { + if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { return SRSRAN_ERROR; } - if (not rlc2.configure(rlc_config_t::default_rlc_am_nr_config())) { + if (not rlc2.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { return SRSRAN_ERROR; } @@ -1165,7 +1181,7 @@ int discard_test() TESTASSERT((2 + 7) == len); // Check that we don't have any SN gaps rlc_am_nr_pdu_header_t header = {}; - rlc_am_nr_read_data_pdu_header(pdu.get(), rlc_am_nr_sn_size_t::size12bits, &header); + rlc_am_nr_read_data_pdu_header(pdu.get(), sn_size, &header); TESTASSERT(header.sn == i); } } @@ -1219,20 +1235,20 @@ int main() // start log back-end srslog::init(); - TESTASSERT(window_checker_test(rlc_am_nr_sn_size_t::size12bits) == SRSRAN_SUCCESS); - TESTASSERT(window_checker_test(rlc_am_nr_sn_size_t::size18bits) == SRSRAN_SUCCESS); - TESTASSERT(retx_segmentation_required_checker_test(rlc_am_nr_sn_size_t::size12bits) == SRSRAN_SUCCESS); - TESTASSERT(retx_segmentation_required_checker_test(rlc_am_nr_sn_size_t::size18bits) == SRSRAN_SUCCESS); - TESTASSERT(basic_test(rlc_am_nr_sn_size_t::size12bits) == SRSRAN_SUCCESS); - TESTASSERT(basic_test(rlc_am_nr_sn_size_t::size18bits) == SRSRAN_SUCCESS); - TESTASSERT(lost_pdu_test(rlc_am_nr_sn_size_t::size12bits) == SRSRAN_SUCCESS); - TESTASSERT(lost_pdu_test(rlc_am_nr_sn_size_t::size18bits) == SRSRAN_SUCCESS); - TESTASSERT(basic_segmentation_test(rlc_am_nr_sn_size_t::size12bits) == SRSRAN_SUCCESS); - TESTASSERT(basic_segmentation_test(rlc_am_nr_sn_size_t::size18bits) == SRSRAN_SUCCESS); - TESTASSERT(segment_retx_test() == SRSRAN_SUCCESS); - TESTASSERT(retx_segment_test() == SRSRAN_SUCCESS); - TESTASSERT(max_retx_lost_sdu_test() == SRSRAN_SUCCESS); - TESTASSERT(max_retx_lost_segments_test() == SRSRAN_SUCCESS); - TESTASSERT(discard_test() == SRSRAN_SUCCESS); + std::initializer_list sn_sizes = {rlc_am_nr_sn_size_t::size12bits, + rlc_am_nr_sn_size_t::size18bits}; + for (auto sn_size : sn_sizes) { + TESTASSERT(window_checker_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(retx_segmentation_required_checker_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(basic_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(lost_pdu_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(basic_segmentation_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(segment_retx_test(sn_size) == SRSRAN_SUCCESS); + } + rlc_am_nr_sn_size_t sns = rlc_am_nr_sn_size_t::size12bits; + TESTASSERT(retx_segment_test(sns) == SRSRAN_SUCCESS); // Fixme + TESTASSERT(max_retx_lost_sdu_test(sns) == SRSRAN_SUCCESS); // Fixme + TESTASSERT(max_retx_lost_segments_test(sns) == SRSRAN_SUCCESS); // Fixme + TESTASSERT(discard_test(sns) == SRSRAN_SUCCESS); // Fixme return SRSRAN_SUCCESS; } From eaa8fff6a0ca34d9abaf036254930be186fcb3ac Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Tue, 8 Mar 2022 17:55:03 +0000 Subject: [PATCH 055/195] lib,rlc_am_nr: Passing bool to get_pdu_poll() to differentiate the behaviour between RETX and non-RETX PDUs. --- lib/include/srsran/rlc/rlc_am_nr.h | 16 ++++++------- lib/src/rlc/rlc_am_nr.cc | 38 +++++++++++++++++++++--------- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_am_nr.h b/lib/include/srsran/rlc/rlc_am_nr.h index de65f0f3e..1b1a94047 100644 --- a/lib/include/srsran/rlc/rlc_am_nr.h +++ b/lib/include/srsran/rlc/rlc_am_nr.h @@ -122,7 +122,7 @@ public: uint32_t build_status_pdu(byte_buffer_t* payload, uint32_t nof_bytes); // Polling - uint8_t get_pdu_poll(); + uint8_t get_pdu_poll(bool is_retx); void stop() final; @@ -146,13 +146,13 @@ private: * Tx state variables * Ref: 3GPP TS 38.322 version 16.2.0 Section 7.1 ***************************************************************************/ - struct rlc_am_nr_tx_state_t st = {}; + struct rlc_am_nr_tx_state_t st = {}; std::unique_ptr > tx_window; // Queues, buffers and container std::unique_ptr > retx_queue; - uint32_t sdu_under_segmentation_sn = INVALID_RLC_SN; // SN of the SDU currently being segmented. - pdcp_sn_vector_t notify_info_vec; + uint32_t sdu_under_segmentation_sn = INVALID_RLC_SN; // SN of the SDU currently being segmented. + pdcp_sn_vector_t notify_info_vec; rlc_am_nr_status_pdu_t tx_status; // Helper constants @@ -168,8 +168,8 @@ private: public: // Getters/Setters - void set_tx_state(const rlc_am_nr_tx_state_t& st_) { st = st_; } // This should only be used for testing. - rlc_am_nr_tx_state_t get_tx_state() { return st; } // This should only be used for testing. + void set_tx_state(const rlc_am_nr_tx_state_t& st_) { st = st_; } // This should only be used for testing. + rlc_am_nr_tx_state_t get_tx_state() { return st; } // This should only be used for testing. uint32_t get_tx_window_size() { return tx_window->size(); } // This should only be used for testing. // Debug Helper @@ -283,8 +283,8 @@ private: public: // Getters/Setters - void set_rx_state(const rlc_am_nr_rx_state_t& st_) { st = st_; } // This should only be used for testing. - rlc_am_nr_rx_state_t get_rx_state() { return st; } // This should only be used for testing. + void set_rx_state(const rlc_am_nr_rx_state_t& st_) { st = st_; } // This should only be used for testing. + rlc_am_nr_rx_state_t get_rx_state() { return st; } // This should only be used for testing. uint32_t get_rx_window_size() { return rx_window->size(); } // This should only be used for testing. }; diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index c2cf5fc52..22735a4e6 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -195,7 +195,7 @@ uint32_t rlc_am_nr_tx::build_new_pdu(uint8_t* payload, uint32_t nof_bytes) // Prepare header rlc_am_nr_pdu_header_t hdr = {}; hdr.dc = RLC_DC_FIELD_DATA_PDU; - hdr.p = get_pdu_poll(); + hdr.p = get_pdu_poll(false); hdr.si = rlc_nr_si_field_t::full_sdu; hdr.sn_size = cfg.tx_sn_field_length; hdr.sn = st.tx_next; @@ -252,7 +252,7 @@ uint32_t rlc_am_nr_tx::build_new_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, uint8_t* // Prepare header rlc_am_nr_pdu_header_t hdr = {}; hdr.dc = RLC_DC_FIELD_DATA_PDU; - hdr.p = get_pdu_poll(); + hdr.p = get_pdu_poll(false); hdr.si = rlc_nr_si_field_t::first_segment; hdr.sn_size = cfg.tx_sn_field_length; hdr.sn = st.tx_next; @@ -354,7 +354,7 @@ uint32_t rlc_am_nr_tx::build_continuation_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, // Prepare header rlc_am_nr_pdu_header_t hdr = {}; hdr.dc = RLC_DC_FIELD_DATA_PDU; - hdr.p = get_pdu_poll(); + hdr.p = get_pdu_poll(false); hdr.si = si; hdr.sn_size = cfg.tx_sn_field_length; hdr.sn = st.tx_next; @@ -505,7 +505,7 @@ rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8 current_so = retx.current_so; } rlc_am_nr_pdu_header_t new_header = tx_pdu.header; - new_header.p = 0; + new_header.p = get_pdu_poll(true); new_header.si = si; new_header.so = current_so; uint32_t hdr_len = rlc_am_nr_write_data_pdu_header(new_header, payload); @@ -598,6 +598,7 @@ uint32_t rlc_am_nr_tx::build_retx_pdu_with_segmentation(rlc_amd_retx_nr_t& retx, // Write header rlc_am_nr_pdu_header_t hdr = tx_pdu.header; + hdr.p = get_pdu_poll(true); hdr.so = retx.current_so; hdr.si = si; uint32_t hdr_len = rlc_am_nr_write_data_pdu_header(hdr, payload); @@ -900,17 +901,32 @@ void rlc_am_nr_tx::get_buffer_state(uint32_t& n_bytes_new, uint32_t& n_bytes_pri } } -uint8_t rlc_am_nr_tx::get_pdu_poll() +/* + * Check whether the polling bit needs to be set, as specified in + * TS 38.322, section 5.3.3.2 + */ +uint8_t rlc_am_nr_tx::get_pdu_poll(bool is_retx) { uint8_t poll = 0; - if (cfg.poll_pdu > 0) { - if (st.pdu_without_poll >= (uint32_t)cfg.poll_pdu) { - poll = 1; - st.pdu_without_poll = 0; - } else { - st.pdu_without_poll++; + if (!is_retx) { + if (cfg.poll_pdu > 0) { + if (st.pdu_without_poll >= (uint32_t)cfg.poll_pdu) { + poll = 1; + st.pdu_without_poll = 0; + } else { + st.pdu_without_poll++; + } } } + /* + * - if both the transmission buffer and the retransmission buffer becomes empty (excluding transmitted RLC SDUs +or RLC SDU segments awaiting acknowledgements) after the transmission of the AMD PDU; or + * - if no new RLC SDU can be transmitted after the transmission of the AMD PDU (e.g. due to window stalling); + * - include a poll in the AMD PDU as described below. + */ + if (tx_sdu_queue.is_empty() && retx_queue->empty()) { + poll = 1; + } return poll; } From 1d1e6dd83259e28d90de8b32aac97499f05bdd27 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Fri, 11 Mar 2022 15:02:14 +0000 Subject: [PATCH 056/195] lib,rlc_am_nr: added pollBYTE support and a unit test for it --- lib/include/srsran/rlc/rlc_am_nr.h | 2 +- lib/src/rlc/rlc_am_nr.cc | 34 +++++--- lib/test/rlc/rlc_am_nr_test.cc | 132 ++++++++++++++++++++++++++--- 3 files changed, 145 insertions(+), 23 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_am_nr.h b/lib/include/srsran/rlc/rlc_am_nr.h index 1b1a94047..5e588ef50 100644 --- a/lib/include/srsran/rlc/rlc_am_nr.h +++ b/lib/include/srsran/rlc/rlc_am_nr.h @@ -122,7 +122,7 @@ public: uint32_t build_status_pdu(byte_buffer_t* payload, uint32_t nof_bytes); // Polling - uint8_t get_pdu_poll(bool is_retx); + uint8_t get_pdu_poll(bool is_retx, uint32_t sdu_bytes); void stop() final; diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 22735a4e6..6933fb3e0 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -195,7 +195,7 @@ uint32_t rlc_am_nr_tx::build_new_pdu(uint8_t* payload, uint32_t nof_bytes) // Prepare header rlc_am_nr_pdu_header_t hdr = {}; hdr.dc = RLC_DC_FIELD_DATA_PDU; - hdr.p = get_pdu_poll(false); + hdr.p = get_pdu_poll(false, tx_sdu->N_bytes); hdr.si = rlc_nr_si_field_t::full_sdu; hdr.sn_size = cfg.tx_sn_field_length; hdr.sn = st.tx_next; @@ -249,10 +249,12 @@ uint32_t rlc_am_nr_tx::build_new_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, uint8_t* return 0; } + uint32_t segment_payload_len = nof_bytes - min_hdr_size; + // Prepare header rlc_am_nr_pdu_header_t hdr = {}; hdr.dc = RLC_DC_FIELD_DATA_PDU; - hdr.p = get_pdu_poll(false); + hdr.p = get_pdu_poll(false, segment_payload_len); hdr.si = rlc_nr_si_field_t::first_segment; hdr.sn_size = cfg.tx_sn_field_length; hdr.sn = st.tx_next; @@ -268,7 +270,6 @@ uint32_t rlc_am_nr_tx::build_new_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, uint8_t* } // Copy PDU to payload - uint32_t segment_payload_len = nof_bytes - hdr_len; srsran_assert((hdr_len + segment_payload_len) <= nof_bytes, "Error calculating hdr_len and segment_payload_len"); memcpy(&payload[hdr_len], tx_pdu.sdu_buf->msg, segment_payload_len); @@ -354,7 +355,7 @@ uint32_t rlc_am_nr_tx::build_continuation_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, // Prepare header rlc_am_nr_pdu_header_t hdr = {}; hdr.dc = RLC_DC_FIELD_DATA_PDU; - hdr.p = get_pdu_poll(false); + hdr.p = get_pdu_poll(false, segment_payload_len); hdr.si = si; hdr.sn_size = cfg.tx_sn_field_length; hdr.sn = st.tx_next; @@ -505,7 +506,7 @@ rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8 current_so = retx.current_so; } rlc_am_nr_pdu_header_t new_header = tx_pdu.header; - new_header.p = get_pdu_poll(true); + new_header.p = get_pdu_poll(true, 0); new_header.si = si; new_header.so = current_so; uint32_t hdr_len = rlc_am_nr_write_data_pdu_header(new_header, payload); @@ -598,7 +599,7 @@ uint32_t rlc_am_nr_tx::build_retx_pdu_with_segmentation(rlc_amd_retx_nr_t& retx, // Write header rlc_am_nr_pdu_header_t hdr = tx_pdu.header; - hdr.p = get_pdu_poll(true); + hdr.p = get_pdu_poll(true, 0); hdr.so = retx.current_so; hdr.si = si; uint32_t hdr_len = rlc_am_nr_write_data_pdu_header(hdr, payload); @@ -905,16 +906,25 @@ void rlc_am_nr_tx::get_buffer_state(uint32_t& n_bytes_new, uint32_t& n_bytes_pri * Check whether the polling bit needs to be set, as specified in * TS 38.322, section 5.3.3.2 */ -uint8_t rlc_am_nr_tx::get_pdu_poll(bool is_retx) +uint8_t rlc_am_nr_tx::get_pdu_poll(bool is_retx, uint32_t sdu_bytes) { + /* For each AMD PDU or AMD PDU segment that has not been previoulsy tranmitted: + * - increment PDU_WITHOUT_POLL by one; + * - increment BYTE_WITHOUT_POLL by every new byte of Data field element that it maps to the Data field of the AMD + * PDU; + * - if PDU_WITHOUT_POLL >= pollPDU; or + * - if BYTE_WITHOUT_POLL >= pollByte: + * - include a poll in the AMD PDU as described below. + */ uint8_t poll = 0; if (!is_retx) { + st.pdu_without_poll++; + st.byte_without_poll += sdu_bytes; if (cfg.poll_pdu > 0) { - if (st.pdu_without_poll >= (uint32_t)cfg.poll_pdu) { - poll = 1; - st.pdu_without_poll = 0; - } else { - st.pdu_without_poll++; + if (st.pdu_without_poll >= (uint32_t)cfg.poll_pdu || st.byte_without_poll >= (uint32_t)cfg.poll_byte) { + poll = 1; + st.pdu_without_poll = 0; + st.byte_without_poll = 0; } } } diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index ad0f57ffa..93b060518 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -483,7 +483,7 @@ int basic_segmentation_test(rlc_am_nr_sn_size_t sn_size) // Push 1 SDU into RLC1 unique_byte_buffer_t sdu; constexpr uint32_t payload_size = 3; // Give the SDU the size of 3 bytes - sdu = srsran::make_byte_buffer(); + sdu = srsran::make_byte_buffer(); TESTASSERT(nullptr != sdu); sdu->msg[0] = 0; // Write the index into the buffer sdu->N_bytes = payload_size; // Give the SDU the size of 3 bytes @@ -532,7 +532,7 @@ int basic_segmentation_test(rlc_am_nr_sn_size_t sn_size) TESTASSERT_EQ(n_pdus, metrics2.num_rx_pdus); // 3 PDUs TESTASSERT_EQ(0, metrics2.num_tx_pdu_bytes); TESTASSERT_EQ(total_rx_pdu_bytes, metrics2.num_rx_pdu_bytes); // 1 PDU (No SO) + 2 PDUs (with SO) - TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs + TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs // Check state rlc_am_nr_tx_state_t state1_tx = tx1->get_tx_state(); @@ -714,13 +714,13 @@ int segment_retx_test(rlc_am_nr_sn_size_t sn_size) TESTASSERT_EQ(15, metrics2.num_rx_sdu_bytes); // 5 SDUs, 3 bytes each TESTASSERT_EQ(0, metrics2.num_lost_sdus); // SDU metrics - TESTASSERT_EQ(2, metrics2.num_tx_pdus); // Two status PDUs - TESTASSERT_EQ(7, metrics2.num_rx_pdus); // 7 PDUs (8 tx'ed, but one was lost) + TESTASSERT_EQ(2, metrics2.num_tx_pdus); // Two status PDUs + TESTASSERT_EQ(7, metrics2.num_rx_pdus); // 7 PDUs (8 tx'ed, but one was lost) TESTASSERT_EQ(total_tx_pdu_bytes2, metrics2.num_tx_pdu_bytes); TESTASSERT_EQ(total_rx_pdu_bytes2, - metrics2.num_rx_pdu_bytes); // 2 Bytes * (NBUFFS-1) (header size) + (NBUFFS-1) * 3 (data) - // 3 (1 retx no SO) + 2 * 5 (2 retx with SO) = 33 - TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs + metrics2.num_rx_pdu_bytes); // 2 Bytes * (NBUFFS-1) (header size) + (NBUFFS-1) * 3 (data) + // 3 (1 retx no SO) + 2 * 5 (2 retx with SO) = 33 + TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs // Check state rlc_am_nr_rx_state_t state2_rx = rx2->get_rx_state(); @@ -1114,9 +1114,9 @@ int discard_test(rlc_am_nr_sn_size_t sn_size) rlc_am_tester tester; timer_handler timers(8); - auto& test_logger = srslog::fetch_basic_logger("TESTER "); - rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); - rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); test_delimit_logger delimiter("discard test (%d bit SN)", to_number(sn_size)); srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); @@ -1211,6 +1211,111 @@ int discard_test(rlc_am_nr_sn_size_t sn_size) return SRSRAN_SUCCESS; } +// This test checks the correct functioning of RLC TX polling bit setting +int poll_test_poll_pdu() +{ + rlc_am_tester tester; + timer_handler timers(8); + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + test_delimit_logger delimiter("poll test pollPDU"); + + srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); + + rlc_config_t rlc_cnfg = {}; + rlc_cnfg.rat = srsran_rat_t::nr; + rlc_cnfg.rlc_mode = rlc_mode_t::am; + rlc_cnfg.am_nr.poll_pdu = 4; + rlc_cnfg.am_nr.poll_byte = 3000; + rlc_cnfg.am_nr.t_status_prohibit = 8; + rlc_cnfg.am_nr.max_retx_thresh = 8; + rlc_cnfg.am_nr.t_reassembly = 35; + + // Test p bit set on new TX with PollPDU + { + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + if (not rlc1.configure(rlc_cnfg)) { + return SRSRAN_ERROR; + } + // pollPDU == 4 + uint32_t num_tx_sdus = 6; + for (uint32_t i = 0; i < num_tx_sdus; ++i) { + // Write SDU + unique_byte_buffer_t sdu = srsran::make_byte_buffer(); + TESTASSERT(sdu != nullptr); + sdu->N_bytes = 1; + sdu->md.pdcp_sn = i; + rlc1.write_sdu(std::move(sdu)); + } + uint32_t num_tx_pdus = 6; + for (uint32_t i = 0; i < num_tx_pdus; ++i) { + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + TESTASSERT(pdu != nullptr); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, 3); + rlc_am_nr_pdu_header_t hdr; + rlc_am_nr_read_data_pdu_header(pdu.get(), rlc_am_nr_sn_size_t::size18bits, &hdr); + if (i != 3 && i != 5) { // P bit set for PollPDU and for empty TX queue + TESTASSERT_EQ(0, hdr.p); + } else { + TESTASSERT_EQ(1, hdr.p); + } + } + } + return SRSRAN_SUCCESS; +} + +// Test p bit set on new TX with PollBYTE +int poll_test_poll_byte() +{ + rlc_am_tester tester; + timer_handler timers(8); + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + test_delimit_logger delimiter("poll test pollBYTE"); + + srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); + + rlc_config_t rlc_cnfg = {}; + rlc_cnfg.rat = srsran_rat_t::nr; + rlc_cnfg.rlc_mode = rlc_mode_t::am; + rlc_cnfg.am_nr.poll_pdu = 4; + rlc_cnfg.am_nr.poll_byte = 3000; + rlc_cnfg.am_nr.t_status_prohibit = 8; + rlc_cnfg.am_nr.max_retx_thresh = 8; + rlc_cnfg.am_nr.t_reassembly = 35; + + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + if (not rlc1.configure(rlc_cnfg)) { + return SRSRAN_ERROR; + } + // pollByte == 3000 + uint32_t num_tx_sdus = 4; + for (uint32_t i = 0; i < num_tx_sdus; ++i) { + // Write SDU + unique_byte_buffer_t sdu = srsran::make_byte_buffer(); + TESTASSERT(sdu != nullptr); + sdu->N_bytes = i == 0 ? 2999 : 1; + sdu->md.pdcp_sn = i; + rlc1.write_sdu(std::move(sdu)); + } + uint32_t num_tx_pdus = num_tx_sdus; + for (uint32_t i = 0; i < num_tx_pdus; ++i) { + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + TESTASSERT(pdu != nullptr); + uint32_t nof_bytes = i == 0 ? 3001 : 3; + pdu->N_bytes = rlc1.read_pdu(pdu->msg, nof_bytes); + TESTASSERT_EQ(nof_bytes, pdu->N_bytes); + rlc_am_nr_pdu_header_t hdr; + rlc_am_nr_read_data_pdu_header(pdu.get(), rlc_am_nr_sn_size_t::size18bits, &hdr); + if (i != 1 && i != 3) { + TESTASSERT_EQ(0, hdr.p); + } else { + TESTASSERT_EQ(1, hdr.p); + } + } + return SRSRAN_SUCCESS; +} + int main() { // Setup the log message spy to intercept error and warning log entries from RLC @@ -1250,5 +1355,12 @@ int main() TESTASSERT(max_retx_lost_sdu_test(sns) == SRSRAN_SUCCESS); // Fixme TESTASSERT(max_retx_lost_segments_test(sns) == SRSRAN_SUCCESS); // Fixme TESTASSERT(discard_test(sns) == SRSRAN_SUCCESS); // Fixme + TESTASSERT(poll_test_poll_pdu() == SRSRAN_SUCCESS); + TESTASSERT(poll_test_poll_byte() == SRSRAN_SUCCESS); + // Test p bit *not* set on RETX with PollPDU + // Test p bit *not* set on RETX with PollBYTE + // Test p bit set on empty TX queue and empty retx queue + // Test p bit *not* set on empty TX queue and empty retx queue + // Test p bit set on window stall return SRSRAN_SUCCESS; } From 47aea19bd37984800503c1aefd1f47f8617fa9a8 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Fri, 11 Mar 2022 17:09:56 +0000 Subject: [PATCH 057/195] lib,rlc_am_nr: added test for poll setting in RETXes --- lib/src/rlc/rlc_am_nr.cc | 21 +++++-- lib/test/rlc/rlc_am_nr_test.cc | 106 +++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+), 6 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 6933fb3e0..155f46787 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -920,12 +920,11 @@ uint8_t rlc_am_nr_tx::get_pdu_poll(bool is_retx, uint32_t sdu_bytes) if (!is_retx) { st.pdu_without_poll++; st.byte_without_poll += sdu_bytes; - if (cfg.poll_pdu > 0) { - if (st.pdu_without_poll >= (uint32_t)cfg.poll_pdu || st.byte_without_poll >= (uint32_t)cfg.poll_byte) { - poll = 1; - st.pdu_without_poll = 0; - st.byte_without_poll = 0; - } + if (cfg.poll_pdu > 0 && st.pdu_without_poll >= (uint32_t)cfg.poll_pdu) { + poll = 1; + } + if (cfg.poll_byte > 0 && st.byte_without_poll >= (uint32_t)cfg.poll_byte) { + poll = 1; } } /* @@ -937,6 +936,16 @@ or RLC SDU segments awaiting acknowledgements) after the transmission of the AMD if (tx_sdu_queue.is_empty() && retx_queue->empty()) { poll = 1; } + + /* + * - If poll bit is included: + * - set PDU_WITHOUT_POLL to 0; + * - set BYTE_WITHOUT_POLL to 0. + */ + if (poll == 1) { + st.pdu_without_poll = 0; + st.byte_without_poll = 0; + } return poll; } diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index 93b060518..81cde558f 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -1316,6 +1316,111 @@ int poll_test_poll_byte() return SRSRAN_SUCCESS; } +// Test p bit set on RETXes +int poll_test_poll_retx() +{ + rlc_am_tester tester; + timer_handler timers(8); + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + test_delimit_logger delimiter("poll test retx"); + + srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); + + rlc_config_t rlc_cnfg = {}; + rlc_cnfg.rat = srsran_rat_t::nr; + rlc_cnfg.rlc_mode = rlc_mode_t::am; + rlc_cnfg.am_nr.poll_pdu = 4; + rlc_cnfg.am_nr.poll_byte = 3000; + rlc_cnfg.am_nr.t_status_prohibit = 8; + rlc_cnfg.am_nr.max_retx_thresh = 8; + rlc_cnfg.am_nr.t_reassembly = 35; + + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + if (not rlc1.configure(rlc_cnfg)) { + return SRSRAN_ERROR; + } + + // pollPDU == 4 + { + uint32_t num_tx_sdus = 5; + for (uint32_t i = 0; i < num_tx_sdus; ++i) { + // Write SDU + unique_byte_buffer_t sdu = srsran::make_byte_buffer(); + TESTASSERT(sdu != nullptr); + sdu->N_bytes = 1; + sdu->md.pdcp_sn = i; + rlc1.write_sdu(std::move(sdu)); + } + } + { + // Read 3 PDUs and NACK the second one + uint32_t num_tx_pdus = 3; + for (uint32_t i = 0; i < num_tx_pdus; ++i) { + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + TESTASSERT(pdu != nullptr); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, 3); + rlc_am_nr_pdu_header_t hdr; + rlc_am_nr_read_data_pdu_header(pdu.get(), rlc_am_nr_sn_size_t::size12bits, &hdr); + TESTASSERT_EQ(0, hdr.p); + } + } + { + unique_byte_buffer_t status_pdu = srsran::make_byte_buffer(); + TESTASSERT(status_pdu != nullptr); + rlc_am_nr_status_pdu_t status = {}; + status.ack_sn = 2; + status.N_nack = 1; + status.nacks[0].nack_sn = 1; // SN=1 needs RETX + rlc_am_nr_write_status_pdu(status, rlc_cnfg.am_nr.tx_sn_field_length, status_pdu.get()); + rlc1.write_pdu(status_pdu->msg, status_pdu->N_bytes); + } + { + // Read 2 PDUs, + uint32_t num_tx_pdus = 3; + for (uint32_t i = 0; i < num_tx_pdus; ++i) { + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + TESTASSERT(pdu != nullptr); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, 3); + TESTASSERT_EQ(3, pdu->N_bytes); + rlc_am_nr_pdu_header_t hdr; + rlc_am_nr_read_data_pdu_header(pdu.get(), rlc_am_nr_sn_size_t::size12bits, &hdr); + if (i == 0) { + TESTASSERT_EQ(0, hdr.p); // No poll since pollPDU is not incremented for RETX + TESTASSERT_EQ(1, hdr.sn); + } else { + TESTASSERT_EQ(1, hdr.p); // poll set because of pollPDU for SN=3 and empty buffer on SN=4 + } + } + } + { + unique_byte_buffer_t status_pdu = srsran::make_byte_buffer(); + TESTASSERT(status_pdu != nullptr); + rlc_am_nr_status_pdu_t status = {}; + status.ack_sn = 4; + status.N_nack = 1; + status.nacks[0].nack_sn = 1; // SN=1 needs RETX + rlc_am_nr_write_status_pdu(status, rlc_cnfg.am_nr.tx_sn_field_length, status_pdu.get()); + rlc1.write_pdu(status_pdu->msg, status_pdu->N_bytes); + } + { + // Read 1 RETX PDU. Empty retx buffer, so poll should be set + uint32_t num_tx_pdus = 1; + for (uint32_t i = 0; i < num_tx_pdus; ++i) { + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + TESTASSERT(pdu != nullptr); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, 3); + TESTASSERT_EQ(3, pdu->N_bytes); + rlc_am_nr_pdu_header_t hdr; + rlc_am_nr_read_data_pdu_header(pdu.get(), rlc_am_nr_sn_size_t::size12bits, &hdr); + if (i == 0) { + TESTASSERT_EQ(1, hdr.p); // Poll set because of empty retx buffer + TESTASSERT_EQ(1, hdr.sn); + } + } + } + return SRSRAN_SUCCESS; +} int main() { // Setup the log message spy to intercept error and warning log entries from RLC @@ -1357,6 +1462,7 @@ int main() TESTASSERT(discard_test(sns) == SRSRAN_SUCCESS); // Fixme TESTASSERT(poll_test_poll_pdu() == SRSRAN_SUCCESS); TESTASSERT(poll_test_poll_byte() == SRSRAN_SUCCESS); + TESTASSERT(poll_test_poll_retx() == SRSRAN_SUCCESS); // Test p bit *not* set on RETX with PollPDU // Test p bit *not* set on RETX with PollBYTE // Test p bit set on empty TX queue and empty retx queue From b4814bfdc15ad9bb6a0c0be56dc7f87eb713d698 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Fri, 11 Mar 2022 17:56:08 +0000 Subject: [PATCH 058/195] lib,rlc_am_nr: added test for polling bit set when retx queue is empty. Fixed lost PDU test now that we correctly set the polling bit. --- lib/include/srsran/rlc/rlc_am_data_structs.h | 6 ++-- lib/src/rlc/rlc_am_nr.cc | 19 ++++++++----- lib/test/rlc/rlc_am_nr_test.cc | 29 ++++++++++++++------ 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_am_data_structs.h b/lib/include/srsran/rlc/rlc_am_data_structs.h index 499b54626..7f56ba431 100644 --- a/lib/include/srsran/rlc/rlc_am_data_structs.h +++ b/lib/include/srsran/rlc/rlc_am_data_structs.h @@ -203,6 +203,7 @@ struct rlc_ringbuffer_base { virtual T& operator[](size_t sn) = 0; virtual size_t size() const = 0; virtual bool empty() const = 0; + virtual bool full() const = 0; virtual void clear() = 0; virtual bool has_sn(uint32_t sn) const = 0; }; @@ -224,6 +225,7 @@ struct rlc_ringbuffer_t : public rlc_ringbuffer_base { } T& operator[](size_t sn) override { return window[sn]; } size_t size() const override { return window.size(); } + bool full() const override { return window.full(); } bool empty() const override { return window.empty(); } void clear() override { window.clear(); } @@ -354,8 +356,8 @@ public: T& push() override { assert(not full()); - T& p = buffer[wpos]; - wpos = (wpos + 1) % WINDOW_SIZE; + T& p = buffer[wpos]; + wpos = (wpos + 1) % WINDOW_SIZE; return p; } diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 155f46787..047227622 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -493,6 +493,7 @@ rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8 retx.segment_length); // Update & write header + uint32_t retx_sn = retx.sn; uint32_t current_so = 0; rlc_nr_si_field_t si = rlc_nr_si_field_t::full_sdu; if (retx.is_segment) { @@ -506,10 +507,8 @@ rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8 current_so = retx.current_so; } rlc_am_nr_pdu_header_t new_header = tx_pdu.header; - new_header.p = get_pdu_poll(true, 0); new_header.si = si; new_header.so = current_so; - uint32_t hdr_len = rlc_am_nr_write_data_pdu_header(new_header, payload); // Write payload into PDU uint32_t retx_pdu_payload_size = 0; @@ -520,9 +519,12 @@ rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8 // RETX SDU segment retx_pdu_payload_size = (retx.so_start + retx.segment_length - retx.current_so); } + retx_queue->pop(); + new_header.p = get_pdu_poll(true, 0); + uint32_t hdr_len = rlc_am_nr_write_data_pdu_header(new_header, payload); uint32_t pdu_bytes = hdr_len + retx_pdu_payload_size; srsran_assert(pdu_bytes <= nof_bytes, "Error calculating hdr_len and pdu_payload_len"); - memcpy(&payload[hdr_len], &tx_pdu.sdu_buf->msg[retx.current_so], retx_pdu_payload_size); + memcpy(&payload[hdr_len], &tx_pdu.sdu_buf->msg[current_so], retx_pdu_payload_size); // Update RETX queue and log retx_queue->pop(); @@ -533,7 +535,7 @@ rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8 (*tx_window)[retx.sn].sdu_buf->N_bytes, (*tx_window)[retx.sn].retx_count + 1, cfg.max_retx_thresh); - RlcHexInfo(payload, pdu_bytes, "RETX PDU SN=%d (%d B)", retx.sn, pdu_bytes); + RlcHexInfo(payload, pdu_bytes, "RETX PDU SN=%d (%d B)", retx_sn, pdu_bytes); log_rlc_am_nr_pdu_header_to_string(logger.debug, new_header, rb_name); debug_state(); @@ -927,13 +929,16 @@ uint8_t rlc_am_nr_tx::get_pdu_poll(bool is_retx, uint32_t sdu_bytes) poll = 1; } } + /* - * - if both the transmission buffer and the retransmission buffer becomes empty (excluding transmitted RLC SDUs -or RLC SDU segments awaiting acknowledgements) after the transmission of the AMD PDU; or + * - if both the transmission buffer and the retransmission buffer becomes empty + * (excluding transmitted RLC SDUs or RLC SDU segments awaiting acknowledgements) + * after the transmission of the AMD PDU; or * - if no new RLC SDU can be transmitted after the transmission of the AMD PDU (e.g. due to window stalling); * - include a poll in the AMD PDU as described below. */ - if (tx_sdu_queue.is_empty() && retx_queue->empty()) { + + if ((tx_sdu_queue.is_empty() && retx_queue->empty()) || tx_window->full()) { poll = 1; } diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index 81cde558f..c2f959460 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -411,7 +411,21 @@ int lost_pdu_test(rlc_am_nr_sn_size_t sn_size) rlc2.write_pdu(retx_buf.msg, retx_buf.N_bytes); + TESTASSERT_EQ(3, rlc2.get_buffer_state()); // Status report shoud be required, as the TX buffers are now empty. + } + { + // Double check status report + byte_buffer_t status_buf; + int len = rlc2.read_pdu(status_buf.msg, 3); + status_buf.N_bytes = len; + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check = {}; + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. + TESTASSERT_EQ(0, status_check.N_nack); // All PDUs are acked now } // Check statistics @@ -420,8 +434,9 @@ int lost_pdu_test(rlc_am_nr_sn_size_t sn_size) uint32_t total_tx_pdu_bytes1 = (NBUFS + 1) * data_pdu_size; // (NBUFS + 1 RETX) * PDU size uint32_t total_rx_pdu_bytes1 = 2 * status_pdu_ack_size + status_pdu_nack_size; // Two status PDU (one with a NACK) - uint32_t total_tx_pdu_bytes2 = total_rx_pdu_bytes1; // Two status PDU (one with a NACK) - uint32_t total_rx_pdu_bytes2 = (NBUFS)*data_pdu_size; // (NBUFS - 1 Lost + 1 RETX) * PDU size + uint32_t total_tx_pdu_bytes2 = + 3 * status_pdu_ack_size + status_pdu_nack_size; // Three status PDU (one with a NACK, two without) + uint32_t total_rx_pdu_bytes2 = (NBUFS)*data_pdu_size; // (NBUFS - 1 Lost + 1 RETX) * PDU size // SDU metrics TESTASSERT_EQ(5, metrics1.num_tx_sdus); @@ -443,9 +458,9 @@ int lost_pdu_test(rlc_am_nr_sn_size_t sn_size) TESTASSERT_EQ(5, metrics2.num_rx_sdu_bytes); TESTASSERT_EQ(0, metrics2.num_lost_sdus); // SDU metrics - TESTASSERT_EQ(2, metrics2.num_tx_pdus); // Two status PDUs + TESTASSERT_EQ(3, metrics2.num_tx_pdus); // Three status PDUs TESTASSERT_EQ(5, metrics2.num_rx_pdus); // 5 PDUs (6 tx'ed, but one was lost) - TESTASSERT_EQ(total_tx_pdu_bytes2, metrics2.num_tx_pdu_bytes); // Two status PDU (one with a NACK) + TESTASSERT_EQ(total_tx_pdu_bytes2, metrics2.num_tx_pdu_bytes); // Three status PDU (one with a NACK, two without) TESTASSERT_EQ(total_rx_pdu_bytes2, metrics2.num_rx_pdu_bytes); // (NBUFS - 1 Lost + 1 RETX) * PDU size TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs return SRSRAN_SUCCESS; @@ -1421,6 +1436,7 @@ int poll_test_poll_retx() } return SRSRAN_SUCCESS; } + int main() { // Setup the log message spy to intercept error and warning log entries from RLC @@ -1463,10 +1479,5 @@ int main() TESTASSERT(poll_test_poll_pdu() == SRSRAN_SUCCESS); TESTASSERT(poll_test_poll_byte() == SRSRAN_SUCCESS); TESTASSERT(poll_test_poll_retx() == SRSRAN_SUCCESS); - // Test p bit *not* set on RETX with PollPDU - // Test p bit *not* set on RETX with PollBYTE - // Test p bit set on empty TX queue and empty retx queue - // Test p bit *not* set on empty TX queue and empty retx queue - // Test p bit set on window stall return SRSRAN_SUCCESS; } From c023dba175fe3bb70b0e19044d120822cde161d9 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Mon, 14 Mar 2022 15:23:34 +0000 Subject: [PATCH 059/195] lib,rlc_am_nr: cleaned up some test names and some comments. --- lib/src/rlc/rlc_am_nr.cc | 24 +++++++++++++++--------- lib/test/rlc/rlc_am_nr_test.cc | 16 ++++++++-------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 047227622..0dcc13ba0 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -492,7 +492,7 @@ rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8 retx.so_start, retx.segment_length); - // Update & write header + // Get RETX SN, current SO and SI uint32_t retx_sn = retx.sn; uint32_t current_so = 0; rlc_nr_si_field_t si = rlc_nr_si_field_t::full_sdu; @@ -506,11 +506,8 @@ rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8 } current_so = retx.current_so; } - rlc_am_nr_pdu_header_t new_header = tx_pdu.header; - new_header.si = si; - new_header.so = current_so; - // Write payload into PDU + // Get RETX PDU payload size uint32_t retx_pdu_payload_size = 0; if (not retx.is_segment) { // RETX full SDU @@ -519,15 +516,24 @@ rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8 // RETX SDU segment retx_pdu_payload_size = (retx.so_start + retx.segment_length - retx.current_so); } + + // Update RETX queue. This must be done before calculating + // the polling bit, to make sure the poll bit is calculated correctly retx_queue->pop(); - new_header.p = get_pdu_poll(true, 0); - uint32_t hdr_len = rlc_am_nr_write_data_pdu_header(new_header, payload); + + // Write header to payload + rlc_am_nr_pdu_header_t new_header = tx_pdu.header; + new_header.si = si; + new_header.so = current_so; + new_header.p = get_pdu_poll(true, 0); + uint32_t hdr_len = rlc_am_nr_write_data_pdu_header(new_header, payload); + + // Write SDU/SDU segment to payload uint32_t pdu_bytes = hdr_len + retx_pdu_payload_size; srsran_assert(pdu_bytes <= nof_bytes, "Error calculating hdr_len and pdu_payload_len"); memcpy(&payload[hdr_len], &tx_pdu.sdu_buf->msg[current_so], retx_pdu_payload_size); - // Update RETX queue and log - retx_queue->pop(); + // Log RETX RlcHexInfo((*tx_window)[retx.sn].sdu_buf->msg, (*tx_window)[retx.sn].sdu_buf->N_bytes, "Original SDU SN=%d (%d B) (attempt %d/%d)", diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index c2f959460..1ba9d8997 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -1226,8 +1226,8 @@ int discard_test(rlc_am_nr_sn_size_t sn_size) return SRSRAN_SUCCESS; } -// This test checks the correct functioning of RLC TX polling bit setting -int poll_test_poll_pdu() +// Test p bit set on new TX with PollPDU +int poll_pdu() { rlc_am_tester tester; timer_handler timers(8); @@ -1280,7 +1280,7 @@ int poll_test_poll_pdu() } // Test p bit set on new TX with PollBYTE -int poll_test_poll_byte() +int poll_byte() { rlc_am_tester tester; timer_handler timers(8); @@ -1331,8 +1331,8 @@ int poll_test_poll_byte() return SRSRAN_SUCCESS; } -// Test p bit set on RETXes -int poll_test_poll_retx() +// Test p bit set on RETXes that cause an empty retx queue. +int poll_retx() { rlc_am_tester tester; timer_handler timers(8); @@ -1476,8 +1476,8 @@ int main() TESTASSERT(max_retx_lost_sdu_test(sns) == SRSRAN_SUCCESS); // Fixme TESTASSERT(max_retx_lost_segments_test(sns) == SRSRAN_SUCCESS); // Fixme TESTASSERT(discard_test(sns) == SRSRAN_SUCCESS); // Fixme - TESTASSERT(poll_test_poll_pdu() == SRSRAN_SUCCESS); - TESTASSERT(poll_test_poll_byte() == SRSRAN_SUCCESS); - TESTASSERT(poll_test_poll_retx() == SRSRAN_SUCCESS); + TESTASSERT(poll_pdu() == SRSRAN_SUCCESS); + TESTASSERT(poll_byte() == SRSRAN_SUCCESS); + TESTASSERT(poll_retx() == SRSRAN_SUCCESS); return SRSRAN_SUCCESS; } From 12f440145d5156cd8b0fbbb426004f571a59a18c Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Tue, 15 Mar 2022 10:53:55 +0000 Subject: [PATCH 060/195] lib,rlc_am_nr: changed build_retx_pdu_without_segmentation to pass retx info by copy. This is to avoid accidently using retx info by reference after pop'ing the retx from the queue. --- lib/include/srsran/rlc/rlc_am_nr.h | 2 +- lib/src/rlc/rlc_am_nr.cc | 16 +++++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_am_nr.h b/lib/include/srsran/rlc/rlc_am_nr.h index 5e588ef50..99f038fa3 100644 --- a/lib/include/srsran/rlc/rlc_am_nr.h +++ b/lib/include/srsran/rlc/rlc_am_nr.h @@ -107,7 +107,7 @@ public: uint32_t build_new_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, uint8_t* payload, uint32_t nof_bytes); uint32_t build_continuation_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, uint8_t* payload, uint32_t nof_bytes); uint32_t build_retx_pdu(uint8_t* payload, uint32_t nof_bytes); - uint32_t build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8_t* payload, uint32_t nof_bytes); + uint32_t build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t retx, uint8_t* payload, uint32_t nof_bytes); uint32_t build_retx_pdu_with_segmentation(rlc_amd_retx_nr_t& retx, uint8_t* payload, uint32_t nof_bytes); bool is_retx_segmentation_required(const rlc_amd_retx_nr_t& retx, uint32_t nof_bytes); uint32_t get_retx_expected_hdr_len(const rlc_amd_retx_nr_t& retx); diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 0dcc13ba0..2b48f2c6c 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -451,7 +451,8 @@ uint32_t rlc_am_nr_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_bytes) * * The RETX PDU may be transporting a full SDU or an SDU segment. * - * \param [retx] is the retx info contained in the retx_queue. + * \param [retx] is the retx info contained in the retx_queue. This is passed by copy, to avoid + * issues when using retx after pop'ing it from the queue. * \param [payload] is a pointer to the MAC buffer that will hold the PDU segment. * \param [nof_bytes] is the number of bytes the RLC is allowed to fill. * @@ -460,7 +461,7 @@ uint32_t rlc_am_nr_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_bytes) * SDU segment, the SI should already be of the `last_segment` type. */ uint32_t -rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8_t* payload, uint32_t nof_bytes) +rlc_am_nr_tx::build_retx_pdu_without_segmentation(const rlc_amd_retx_nr_t retx, uint8_t* payload, uint32_t nof_bytes) { srsran_assert(tx_window->has_sn(retx.sn), "Called %s without checking retx SN", __FUNCTION__); srsran_assert(not is_retx_segmentation_required(retx, nof_bytes), @@ -493,9 +494,7 @@ rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8 retx.segment_length); // Get RETX SN, current SO and SI - uint32_t retx_sn = retx.sn; - uint32_t current_so = 0; - rlc_nr_si_field_t si = rlc_nr_si_field_t::full_sdu; + rlc_nr_si_field_t si = rlc_nr_si_field_t::full_sdu; if (retx.is_segment) { if (retx.current_so == 0) { si = rlc_nr_si_field_t::first_segment; @@ -504,7 +503,6 @@ rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8 } else { si = rlc_nr_si_field_t::last_segment; } - current_so = retx.current_so; } // Get RETX PDU payload size @@ -524,14 +522,14 @@ rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8 // Write header to payload rlc_am_nr_pdu_header_t new_header = tx_pdu.header; new_header.si = si; - new_header.so = current_so; + new_header.so = retx.current_so; new_header.p = get_pdu_poll(true, 0); uint32_t hdr_len = rlc_am_nr_write_data_pdu_header(new_header, payload); // Write SDU/SDU segment to payload uint32_t pdu_bytes = hdr_len + retx_pdu_payload_size; srsran_assert(pdu_bytes <= nof_bytes, "Error calculating hdr_len and pdu_payload_len"); - memcpy(&payload[hdr_len], &tx_pdu.sdu_buf->msg[current_so], retx_pdu_payload_size); + memcpy(&payload[hdr_len], &tx_pdu.sdu_buf->msg[retx.current_so], retx_pdu_payload_size); // Log RETX RlcHexInfo((*tx_window)[retx.sn].sdu_buf->msg, @@ -541,7 +539,7 @@ rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8 (*tx_window)[retx.sn].sdu_buf->N_bytes, (*tx_window)[retx.sn].retx_count + 1, cfg.max_retx_thresh); - RlcHexInfo(payload, pdu_bytes, "RETX PDU SN=%d (%d B)", retx_sn, pdu_bytes); + RlcHexInfo(payload, pdu_bytes, "RETX PDU SN=%d (%d B)", retx.sn, pdu_bytes); log_rlc_am_nr_pdu_header_to_string(logger.debug, new_header, rb_name); debug_state(); From 329f3e519b303c5d481ba6ed017a3272af1d58b8 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Thu, 17 Mar 2022 11:06:45 +0100 Subject: [PATCH 061/195] lib,rlc_am_nr: reset do_status flag after sending status PDU and extend unit test to fail when redundant status PDUs exist. --- lib/src/rlc/rlc_am_nr.cc | 1 + lib/test/rlc/rlc_am_nr_test.cc | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 2b48f2c6c..d920d5504 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -1343,6 +1343,7 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m if (status_prohibit_timer.is_valid()) { status_prohibit_timer.run(); } + do_status = false; } return tmp_buf.N_bytes; } diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index 1ba9d8997..bf60e7925 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -315,6 +315,7 @@ int basic_test(rlc_am_nr_sn_size_t sn_size) * Test the loss of a single PDU. * NACK should be visible in the status report. * Retx after NACK should be present too. + * No further status reports shall be issued. */ int lost_pdu_test(rlc_am_nr_sn_size_t sn_size) { @@ -336,7 +337,8 @@ int lost_pdu_test(rlc_am_nr_sn_size_t sn_size) return -1; } - if (not rlc2.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { + rlc_config_t rlc2_config = rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)); + if (not rlc2.configure(rlc2_config)) { return -1; } @@ -428,6 +430,15 @@ int lost_pdu_test(rlc_am_nr_sn_size_t sn_size) TESTASSERT_EQ(0, status_check.N_nack); // All PDUs are acked now } + { + // rlc2 should not issue further status PDUs as time passes (even after expiry of t_status_prohibit) + int32_t checktime = 2 * rlc2_config.am_nr.t_status_prohibit; + for (int cnt = 0; cnt < checktime; cnt++) { + timers.step_all(); + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + } + } + // Check statistics rlc_bearer_metrics_t metrics1 = rlc1.get_metrics(); rlc_bearer_metrics_t metrics2 = rlc2.get_metrics(); From 7e13cd071240dcd33e7b34137d7aa09012e18156 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Thu, 17 Mar 2022 10:45:54 +0100 Subject: [PATCH 062/195] lib,rlc_am_nr: rename get_tx_window_size() to get_tx_window_utilization() --- lib/include/srsran/rlc/rlc_am_nr.h | 6 +++--- lib/test/rlc/rlc_am_nr_test.cc | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_am_nr.h b/lib/include/srsran/rlc/rlc_am_nr.h index 99f038fa3..db954dcad 100644 --- a/lib/include/srsran/rlc/rlc_am_nr.h +++ b/lib/include/srsran/rlc/rlc_am_nr.h @@ -168,9 +168,9 @@ private: public: // Getters/Setters - void set_tx_state(const rlc_am_nr_tx_state_t& st_) { st = st_; } // This should only be used for testing. - rlc_am_nr_tx_state_t get_tx_state() { return st; } // This should only be used for testing. - uint32_t get_tx_window_size() { return tx_window->size(); } // This should only be used for testing. + void set_tx_state(const rlc_am_nr_tx_state_t& st_) { st = st_; } // This should only be used for testing. + rlc_am_nr_tx_state_t get_tx_state() { return st; } // This should only be used for testing. + uint32_t get_tx_window_utilization() { return tx_window->size(); } // This should only be used for testing. // Debug Helper void debug_state() const; diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index bf60e7925..c99a0443e 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -262,7 +262,7 @@ int basic_test(rlc_am_nr_sn_size_t sn_size) // Check TX_NEXT_ACK rlc_am_nr_tx_state_t st = tx1->get_tx_state(); TESTASSERT_EQ(5, st.tx_next_ack); - TESTASSERT_EQ(0, tx1->get_tx_window_size()); + TESTASSERT_EQ(0, tx1->get_tx_window_utilization()); // Check PDCP notifications TESTASSERT_EQ(5, tester.notified_counts.size()); From bd6e306c9469b3aca606ea689cefeeae74080eb6 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Wed, 16 Mar 2022 16:59:50 +0100 Subject: [PATCH 063/195] lib,rlc_am_nr: extend unit tests for 18-bit SN --- lib/test/rlc/rlc_am_nr_test.cc | 146 ++++++++++++++++++++------------- 1 file changed, 90 insertions(+), 56 deletions(-) diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index c99a0443e..7042264e1 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -785,15 +785,23 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) // Push 5 SDUs into RLC1 std::vector sdu_bufs(n_sdu_bufs); + constexpr uint32_t payload_size = 3; // Give the SDU the size of 3 bytes + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; for (int i = 0; i < n_sdu_bufs; i++) { sdu_bufs[i] = srsran::make_byte_buffer(); - sdu_bufs[i]->msg[0] = i; // Write the index into the buffer - sdu_bufs[i]->N_bytes = 3; // Give each buffer a size of 3 bytes - sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications + sdu_bufs[i]->msg[0] = i; // Write the index into the buffer + sdu_bufs[i]->N_bytes = payload_size; // Give each buffer a size of 3 bytes + sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications rlc1.write_sdu(std::move(sdu_bufs[i])); } - TESTASSERT(25 == rlc1.get_buffer_state()); // 2 Bytes * NBUFFS (header size) + NBUFFS * 3 (data) = 25 + uint32_t expected_buffer_state = (header_size + payload_size) * n_sdu_bufs; + TESTASSERT(expected_buffer_state == rlc1.get_buffer_state()); + + constexpr uint32_t so_size = 2; + constexpr uint32_t segment_size = 1; + uint32_t pdu_size_first = header_size + segment_size; + uint32_t pdu_size_continued = header_size + so_size + segment_size; // Read 15 PDUs from RLC1 std::vector pdu_bufs(n_pdu_bufs); @@ -801,14 +809,14 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) pdu_bufs[i] = srsran::make_byte_buffer(); if (i == 0 || i == 3 || i == 6 || i == 9 || i == 12) { // First segment, no SO - uint32_t len = rlc1.read_pdu(pdu_bufs[i]->msg, 3); // 2 bytes for header + 1 byte payload + uint32_t len = rlc1.read_pdu(pdu_bufs[i]->msg, pdu_size_first); // 2 bytes for header + 1 byte payload pdu_bufs[i]->N_bytes = len; - TESTASSERT_EQ(3, len); + TESTASSERT_EQ(pdu_size_first, len); } else { // Middle or last segment, SO present - uint32_t len = rlc1.read_pdu(pdu_bufs[i]->msg, 5); // 4 bytes for header + 1 byte payload + uint32_t len = rlc1.read_pdu(pdu_bufs[i]->msg, pdu_size_continued); // 4 bytes for header + 1 byte payload pdu_bufs[i]->N_bytes = len; - TESTASSERT_EQ(5, len); + TESTASSERT_EQ(pdu_size_continued, len); } } @@ -847,11 +855,14 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) // t-reassembly has expired. There should be a NACK in the status report. // There should be 3 NACKs with SO_start and SO_end - TESTASSERT_EQ(21, rlc2.get_buffer_state()); // 3 bytes for fixed header (ACK+E1) + 3 * 6 for NACK with SO = 21. + constexpr uint32_t status_pdu_ack_size = 3; + uint32_t status_pdu_nack_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + constexpr uint32_t status_pdu_so_size = 4; + TESTASSERT_EQ(status_pdu_ack_size + 3 * (status_pdu_nack_size + status_pdu_so_size), rlc2.get_buffer_state()); { // Read status PDU from RLC2 byte_buffer_t status_buf; - int len = rlc2.read_pdu(status_buf.msg, 21); + int len = rlc2.read_pdu(status_buf.msg, status_pdu_ack_size + 3 * (status_pdu_nack_size + status_pdu_so_size)); status_buf.N_bytes = len; TESTASSERT_EQ(0, rlc2.get_buffer_state()); @@ -878,7 +889,7 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); // Check there is an Retx of SN=3 - TESTASSERT_EQ(5, rlc1.get_buffer_state()); + TESTASSERT_EQ(header_size + payload_size, rlc1.get_buffer_state()); } { @@ -887,11 +898,11 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) byte_buffer_t retx_buf; uint32_t len = 0; if (i == 0) { - len = rlc1.read_pdu(retx_buf.msg, 3); - TESTASSERT_EQ(3, len); + len = rlc1.read_pdu(retx_buf.msg, pdu_size_first); + TESTASSERT_EQ(pdu_size_first, len); } else { - len = rlc1.read_pdu(retx_buf.msg, 5); - TESTASSERT_EQ(5, len); + len = rlc1.read_pdu(retx_buf.msg, pdu_size_continued); + TESTASSERT_EQ(pdu_size_continued, len); } retx_buf.N_bytes = len; @@ -918,6 +929,12 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) rlc_bearer_metrics_t metrics1 = rlc1.get_metrics(); rlc_bearer_metrics_t metrics2 = rlc2.get_metrics(); + uint32_t data_pdu_size = header_size + payload_size; + uint32_t total_tx_pdu_bytes1 = 5 * pdu_size_first + 10 * pdu_size_continued + pdu_size_first + 2 * pdu_size_continued; + uint32_t total_rx_pdu_bytes1 = 2 * status_pdu_ack_size + 3 * (status_pdu_nack_size + status_pdu_so_size); + uint32_t total_tx_pdu_bytes2 = total_rx_pdu_bytes1; + uint32_t total_rx_pdu_bytes2 = 4 * pdu_size_first + 8 * pdu_size_continued + pdu_size_first + 2 * pdu_size_continued; + // SDU metrics TESTASSERT_EQ(5, metrics1.num_tx_sdus); TESTASSERT_EQ(0, metrics1.num_rx_sdus); @@ -928,10 +945,12 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) // PDU metrics TESTASSERT_EQ(15 + 3, metrics1.num_tx_pdus); // 15 PDUs + 3 re-transmissions TESTASSERT_EQ(2, metrics1.num_rx_pdus); // Two status PDU - TESTASSERT_EQ(78, metrics1.num_tx_pdu_bytes); // 3 Bytes * 5 (5 PDUs without SO) + 10 * 5 (10 PDUs with SO) - // 3 (1 retx no SO) + 2 * 5 (2 retx with SO) = 78 - TESTASSERT_EQ(24, metrics1.num_rx_pdu_bytes); // Two status PDU. One with just an ack (3 bytes) - // Another with 3 NACKs all with SO. (3 + 3*6 bytes) + TESTASSERT_EQ(total_tx_pdu_bytes1, + metrics1.num_tx_pdu_bytes); // 3 Bytes * 5 (5 PDUs without SO) + 10 * 5 (10 PDUs with SO) + // 3 (1 retx no SO) + 2 * 5 (2 retx with SO) = 78 + TESTASSERT_EQ(total_rx_pdu_bytes1, + metrics1.num_rx_pdu_bytes); // Two status PDU. One with just an ack (3 bytes) + // Another with 3 NACKs all with SO. (3 + 3*6 bytes) = 24 TESTASSERT_EQ(0, metrics1.num_lost_sdus); // No lost SDUs // PDU metrics @@ -943,11 +962,12 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) // SDU metrics TESTASSERT_EQ(2, metrics2.num_tx_pdus); // Two status PDUs TESTASSERT_EQ(15, metrics2.num_rx_pdus); // 15 PDUs (18 tx'ed, but three were lost) - TESTASSERT_EQ(24, metrics2.num_tx_pdu_bytes); // Two status PDU. One with just an ack (3 bytes) - // Another with 3 NACKs all with SO. (3 + 3*6 bytes) - TESTASSERT_EQ(65, metrics2.num_rx_pdu_bytes); // 3 Bytes (header + data size, without SO) * 5 (N PDUs without SO) - // 5 bytes (header + data size, with SO) * 10 (N PDUs with SO) - // = 81 bytes + TESTASSERT_EQ(total_tx_pdu_bytes2, + metrics2.num_tx_pdu_bytes); // Two status PDU. One with just an ack (3 bytes) + // Another with 3 NACKs all with SO. (3 + 3*6 bytes) = 24 + TESTASSERT_EQ(total_rx_pdu_bytes2, + metrics2.num_rx_pdu_bytes); // 3 Bytes * 4 (5-1 PDUs without SO) + 8 * 5 (10-2 PDUs with SO) + // 3 (1 retx no SO) + 2 * 5 (2 retx with SO) = 65 bytes TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs // Check state @@ -979,19 +999,23 @@ int max_retx_lost_sdu_test(rlc_am_nr_sn_size_t sn_size) // Push 2 SDUs into RLC1 const uint32_t n_sdus = 2; unique_byte_buffer_t sdu_bufs[n_sdus]; + constexpr uint32_t payload_size = 1; // Give each buffer a size of 1 byte + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; for (uint32_t i = 0; i < n_sdus; i++) { sdu_bufs[i] = srsran::make_byte_buffer(); - sdu_bufs[i]->msg[0] = i; // Write the index into the buffer - sdu_bufs[i]->N_bytes = 1; // Give each buffer a size of 1 byte - sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications + sdu_bufs[i]->msg[0] = i; // Write the index into the buffer + sdu_bufs[i]->N_bytes = payload_size; // Give each buffer a size of 1 byte + sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications rlc1.write_sdu(std::move(sdu_bufs[i])); } + uint32_t pdu_size = header_size + payload_size; + // Read 2 PDUs from RLC1 (1 byte each) const uint32_t n_pdus = 2; 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 + len = rlc1.read_pdu(pdu_bufs[i].msg, pdu_size); // 2 byte header + 1 byte payload pdu_bufs[i].N_bytes = len; } @@ -1018,7 +1042,7 @@ int max_retx_lost_sdu_test(rlc_am_nr_sn_size_t sn_size) rlc1.write_pdu(status_pdu.msg, status_pdu.N_bytes); byte_buffer_t pdu_buf; - len = rlc1.read_pdu(pdu_buf.msg, 3); // 2 byte header + 1 byte payload + len = rlc1.read_pdu(pdu_buf.msg, pdu_size); // 2 byte header + 1 byte payload } // Now maxRetx should have been triggered @@ -1049,21 +1073,29 @@ int max_retx_lost_segments_test(rlc_am_nr_sn_size_t sn_size) // Push 2 SDUs into RLC1 const uint32_t n_sdus = 2; unique_byte_buffer_t sdu_bufs[n_sdus]; + constexpr uint32_t payload_size = 20; // Give each buffer a size of 20 bytes + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; for (uint32_t i = 0; i < n_sdus; i++) { sdu_bufs[i] = srsran::make_byte_buffer(); - sdu_bufs[i]->msg[0] = i; // Write the index into the buffer - sdu_bufs[i]->N_bytes = 20; // Give each buffer a size of 20 bytes - sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications + sdu_bufs[i]->msg[0] = i; // Write the index into the buffer + sdu_bufs[i]->N_bytes = payload_size; // Give each buffer a size of 20 bytes + sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications rlc1.write_sdu(std::move(sdu_bufs[i])); } + constexpr uint32_t so_size = 2; + constexpr uint32_t segment_size_first = 13; + constexpr uint32_t segment_size_continued = 7; + uint32_t pdu_size_first = header_size + segment_size_first; + uint32_t pdu_size_continued = header_size + so_size + segment_size_continued; + // Read 2*2=4 PDUs from RLC1 and limit to 15 byte to force segmentation in two parts: // Segment 1: 2 byte header + 13 byte payload; space fully used // Segment 2: 4 byte header + 7 byte payload; space not fully used, 4 bytes left over const uint32_t n_pdus = 4; byte_buffer_t pdu_bufs[n_pdus]; for (uint32_t i = 0; i < n_pdus; i++) { - len = rlc1.read_pdu(pdu_bufs[i].msg, 15); + len = rlc1.read_pdu(pdu_bufs[i].msg, pdu_size_first); pdu_bufs[i].N_bytes = len; } @@ -1087,7 +1119,7 @@ int max_retx_lost_segments_test(rlc_am_nr_sn_size_t sn_size) rlc_am_nr_write_status_pdu( status_lost_both_segments, rlc_cfg.am_nr.tx_sn_field_length, &status_pdu_lost_both_segments); - // Fake status PDU that ack SN=1 and nack {SN=0 segment 2} + // Fake status PDU that ack SN=1 and nack {SN=0 segment 1} rlc_am_nr_status_pdu_t status_lost_second_segment = {}; status_lost_second_segment.ack_sn = 2; // delivered up to SN=1 status_lost_second_segment.N_nack = 1; // one SN was lost @@ -1117,14 +1149,14 @@ int max_retx_lost_segments_test(rlc_am_nr_sn_size_t sn_size) rlc1.write_pdu(status_pdu_lost_both_segments.msg, status_pdu_lost_both_segments.N_bytes); // read the retransmitted PDUs - len = rlc1.read_pdu(pdu_buf.msg, 15); // 2 byte header + 13 byte payload - len = rlc1.read_pdu(pdu_buf.msg, 15); // 4 byte header + 7 byte payload + len = rlc1.read_pdu(pdu_buf.msg, pdu_size_first); // 2 byte header + 13 byte payload + len = rlc1.read_pdu(pdu_buf.msg, pdu_size_first); // 4 byte header + 7 byte payload } else { // Send NACK for segment 2 (assume at least segment 1 was finally received) rlc1.write_pdu(status_pdu_lost_second_segment.msg, status_pdu_lost_second_segment.N_bytes); // read the retransmitted PDUs - len = rlc1.read_pdu(pdu_buf.msg, 15); // 4 byte header + 7 byte payload + len = rlc1.read_pdu(pdu_buf.msg, pdu_size_first); // 4 byte header + 7 byte payload } } @@ -1157,14 +1189,16 @@ int discard_test(rlc_am_nr_sn_size_t sn_size) return SRSRAN_ERROR; } + uint32_t num_tx_sdus = 1; + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + uint32_t payload_size = 5; // Give each buffer a size of 5 bytes // Test discarding the single SDU from the queue { - uint32_t num_tx_sdus = 1; for (uint32_t i = 0; i < num_tx_sdus; ++i) { // Write SDU unique_byte_buffer_t sdu = srsran::make_byte_buffer(); TESTASSERT(sdu != nullptr); - sdu->N_bytes = 5; + sdu->N_bytes = payload_size; for (uint32_t k = 0; k < sdu->N_bytes; ++k) { sdu->msg[k] = i; // Write the index into the buffer } @@ -1175,14 +1209,15 @@ int discard_test(rlc_am_nr_sn_size_t sn_size) rlc1.discard_sdu(0); // Try to discard PDCP_SN=0 TESTASSERT(rlc1.has_data() == false); + num_tx_sdus = 10; + payload_size = 7; // Give each buffer a size of 7 bytes // Test discarding two SDUs in the middle (SN=3) and end (SN=9) of the queue and read PDUs after { - uint32_t num_tx_sdus = 10; for (uint32_t i = 0; i < num_tx_sdus; ++i) { // Write SDU unique_byte_buffer_t sdu = srsran::make_byte_buffer(); TESTASSERT(sdu != nullptr); - sdu->N_bytes = 7; + sdu->N_bytes = payload_size; for (uint32_t k = 0; k < sdu->N_bytes; ++k) { sdu->msg[k] = i; // Write the index into the buffer } @@ -1190,21 +1225,21 @@ int discard_test(rlc_am_nr_sn_size_t sn_size) rlc1.write_sdu(std::move(sdu)); } } - TESTASSERT(rlc1.get_buffer_state() == 90); // 10 * (2B Header + 7B Payload) + TESTASSERT(rlc1.get_buffer_state() == num_tx_sdus * (header_size + payload_size)); // 10 * (2B Header + 7B Payload) rlc1.discard_sdu(3); // Try to discard PDCP_SN=3 TESTASSERT(rlc1.has_data() == true); - TESTASSERT(rlc1.get_buffer_state() == 81); + TESTASSERT(rlc1.get_buffer_state() == (num_tx_sdus - 1) * (header_size + payload_size)); rlc1.discard_sdu(9); // Try to discard PDCP_SN=9 TESTASSERT(rlc1.has_data() == true); - TESTASSERT(rlc1.get_buffer_state() == 72); + TESTASSERT(rlc1.get_buffer_state() == (num_tx_sdus - 2) * (header_size + payload_size)); + num_tx_sdus = 8; { - uint32_t num_tx_sdus = 8; for (uint32_t i = 0; i < num_tx_sdus; ++i) { unique_byte_buffer_t pdu = srsran::make_byte_buffer(); uint32_t len = rlc1.read_pdu(pdu->msg, 50); // sufficient space to read without segmentation pdu->N_bytes = len; - TESTASSERT((2 + 7) == len); + TESTASSERT((header_size + payload_size) == len); // Check that we don't have any SN gaps rlc_am_nr_pdu_header_t header = {}; rlc_am_nr_read_data_pdu_header(pdu.get(), sn_size, &header); @@ -1212,17 +1247,17 @@ int discard_test(rlc_am_nr_sn_size_t sn_size) } } TESTASSERT(rlc1.has_data() == false); - srslog::fetch_basic_logger("TEST").info("Received %zd SDUs", tester.sdus.size()); + num_tx_sdus = 3; + payload_size = 7; // Give each buffer a size of 7 bytes // Test discarding non-existing SDU from the queue { - uint32_t num_tx_sdus = 3; for (uint32_t i = 0; i < num_tx_sdus; ++i) { // Write SDU unique_byte_buffer_t sdu = srsran::make_byte_buffer(); TESTASSERT(sdu != nullptr); - sdu->N_bytes = 7; + sdu->N_bytes = payload_size; for (uint32_t k = 0; k < sdu->N_bytes; ++k) { sdu->msg[k] = i; // Write the index into the buffer } @@ -1230,9 +1265,9 @@ int discard_test(rlc_am_nr_sn_size_t sn_size) rlc1.write_sdu(std::move(sdu)); } } - TESTASSERT(rlc1.get_buffer_state() == 27); // 3 * (2B Header + 7B Payload) + TESTASSERT(rlc1.get_buffer_state() == num_tx_sdus * (header_size + payload_size)); // 3 * (2B Header + 7B Payload) rlc1.discard_sdu(8); // Try to discard PDCP_SN=8, which doesn't exist - TESTASSERT(rlc1.get_buffer_state() == 27); // 3 * (2B Header + 7B Payload) + TESTASSERT(rlc1.get_buffer_state() == num_tx_sdus * (header_size + payload_size)); // 3 * (2B Header + 7B Payload) return SRSRAN_SUCCESS; } @@ -1481,12 +1516,11 @@ int main() TESTASSERT(lost_pdu_test(sn_size) == SRSRAN_SUCCESS); TESTASSERT(basic_segmentation_test(sn_size) == SRSRAN_SUCCESS); TESTASSERT(segment_retx_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(retx_segment_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(max_retx_lost_sdu_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(max_retx_lost_segments_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(discard_test(sn_size) == SRSRAN_SUCCESS); } - rlc_am_nr_sn_size_t sns = rlc_am_nr_sn_size_t::size12bits; - TESTASSERT(retx_segment_test(sns) == SRSRAN_SUCCESS); // Fixme - TESTASSERT(max_retx_lost_sdu_test(sns) == SRSRAN_SUCCESS); // Fixme - TESTASSERT(max_retx_lost_segments_test(sns) == SRSRAN_SUCCESS); // Fixme - TESTASSERT(discard_test(sns) == SRSRAN_SUCCESS); // Fixme TESTASSERT(poll_pdu() == SRSRAN_SUCCESS); TESTASSERT(poll_byte() == SRSRAN_SUCCESS); TESTASSERT(poll_retx() == SRSRAN_SUCCESS); From 79e8f7625e3e0b797d8ca367cc47346153285ac1 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Mon, 21 Mar 2022 14:06:38 +0100 Subject: [PATCH 064/195] lib,rlc_am_nr: fix log msg for AM NR PDU header --- lib/include/srsran/rlc/rlc_am_nr_packing.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/include/srsran/rlc/rlc_am_nr_packing.h b/lib/include/srsran/rlc/rlc_am_nr_packing.h index 574d89c17..7f1b47afa 100644 --- a/lib/include/srsran/rlc/rlc_am_nr_packing.h +++ b/lib/include/srsran/rlc/rlc_am_nr_packing.h @@ -178,7 +178,7 @@ inline void log_rlc_am_nr_pdu_header_to_string(srslog::log_channel& log rlc_dc_field_text[header.dc], (header.p ? "1" : "0"), to_string_short(header.si), - header.sn, + to_string(header.sn_size), header.sn, header.so); fmt::format_to(buffer, "]"); From fbdbc81bdd86ec470ce8f3288aa6fc0ac3218f55 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Tue, 22 Mar 2022 13:24:32 +0100 Subject: [PATCH 065/195] lib,pdcp: fix modulus for SN increments --- lib/include/srsran/upper/pdcp_entity_lte.h | 5 +++-- lib/src/pdcp/pdcp_entity_lte.cc | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/include/srsran/upper/pdcp_entity_lte.h b/lib/include/srsran/upper/pdcp_entity_lte.h index a8b96bb03..44fece265 100644 --- a/lib/include/srsran/upper/pdcp_entity_lte.h +++ b/lib/include/srsran/upper/pdcp_entity_lte.h @@ -33,7 +33,7 @@ namespace srsran { class undelivered_sdus_queue { public: - explicit undelivered_sdus_queue(srsran::task_sched_handle task_sched); + explicit undelivered_sdus_queue(srsran::task_sched_handle task_sched, uint32_t sn_mod); bool empty() const { return count == 0; } bool is_full() const { return count >= capacity; } @@ -73,8 +73,9 @@ public: private: const static uint32_t capacity = 4096; const static uint32_t invalid_sn = -1; + uint32_t sn_mod = 0; - static uint32_t increment_sn(uint32_t sn) { return (sn + 1) % capacity; } + uint32_t increment_sn(uint32_t sn) { return (sn + 1) % sn_mod; } struct sdu_data { srsran::unique_byte_buffer_t sdu; diff --git a/lib/src/pdcp/pdcp_entity_lte.cc b/lib/src/pdcp/pdcp_entity_lte.cc index 4e55ca3ca..8047ad6da 100644 --- a/lib/src/pdcp/pdcp_entity_lte.cc +++ b/lib/src/pdcp/pdcp_entity_lte.cc @@ -91,7 +91,7 @@ bool pdcp_entity_lte::configure(const pdcp_config_t& cnfg_) logger.info("Status Report Required: %s", cfg.status_report_required ? "True" : "False"); if (is_drb() and not rlc->rb_is_um(lcid)) { - undelivered_sdus = std::unique_ptr(new undelivered_sdus_queue(task_sched)); + undelivered_sdus = std::unique_ptr(new undelivered_sdus_queue(task_sched, maximum_pdcp_sn)); rx_counts_info.reserve(reordering_window); } @@ -858,7 +858,7 @@ void pdcp_entity_lte::reset_metrics() /**************************************************************************** * Undelivered SDUs queue helpers ***************************************************************************/ -undelivered_sdus_queue::undelivered_sdus_queue(srsran::task_sched_handle task_sched) +undelivered_sdus_queue::undelivered_sdus_queue(srsran::task_sched_handle task_sched, uint32_t sn_mod) : sn_mod(sn_mod) { for (auto& e : sdus) { e.discard_timer = task_sched.get_unique_timer(); From e6f42331baf659b1f0e8bfb94dd61ee245f5e7d8 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Tue, 22 Mar 2022 15:51:38 +0000 Subject: [PATCH 066/195] lib,rlc_am_nr: fix race condition in handle data PDU. This was causing SDUs being removed from the rx_window, while the status report was geing generated. --- lib/src/rlc/rlc_am_nr.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index d920d5504..1de6a85db 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -1064,6 +1064,8 @@ void rlc_am_nr_rx::reestablish() void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) { + std::lock_guard lock(mutex); + // Get AMD PDU Header rlc_am_nr_pdu_header_t header = {}; uint32_t hdr_len = rlc_am_nr_read_data_pdu_header(payload, nof_bytes, cfg.rx_sn_field_length, &header); @@ -1487,7 +1489,7 @@ bool rlc_am_nr_rx::have_all_segments_been_received( */ void rlc_am_nr_rx::debug_state() const { - RlcDebug("RX entity state: Rx_Next %d, Rx_Next_Status_Trigger %d, Rx_Highest_Status %d, Rx_Next_Highest", + RlcDebug("RX entity state: Rx_Next=%d, Rx_Next_Status_Trigger=%d, Rx_Highest_Status=%d, Rx_Next_Highest=%d", st.rx_next, st.rx_next_status_trigger, st.rx_highest_status, From a5c61418d5f01e2c6cdf441e9ee2ef127692853e Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Mon, 21 Mar 2022 10:39:39 +0100 Subject: [PATCH 067/195] lib,rf_imp: load rf plugins when loading this library This fixes race of concurrently loading the same plugins when srsran_rf_open_devname is called by multiple threads Plugins are now loaded when srsran_rf is loaded (for shared lib) or right before main() (for static lib) --- lib/src/phy/rf/rf_imp.c | 13 ++++++++----- lib/src/phy/rf/rf_zmq_test.c | 1 - 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/src/phy/rf/rf_imp.c b/lib/src/phy/rf/rf_imp.c index 30ff775f0..1f754a403 100644 --- a/lib/src/phy/rf/rf_imp.c +++ b/lib/src/phy/rf/rf_imp.c @@ -94,11 +94,6 @@ const char* srsran_rf_get_devname(srsran_rf_t* rf) int srsran_rf_open_devname(srsran_rf_t* rf, const char* devname, char* args, uint32_t nof_channels) { - if (srsran_rf_load_plugins() != SRSRAN_SUCCESS) { - ERROR("Failed to load RF plugins"); - return SRSRAN_ERROR; - } - rf->thread_gain_run = false; bool no_rf_devs_detected = true; @@ -482,3 +477,11 @@ int srsran_rf_load_plugins() #endif /* ENABLE_RF_PLUGINS */ return SRSRAN_SUCCESS; } + +// Search and load plugins when this library is loaded (shared) or right before main (static) +void __attribute__((constructor)) init() +{ + if (srsran_rf_load_plugins() != SRSRAN_SUCCESS) { + ERROR("Failed to load RF plugins"); + } +} diff --git a/lib/src/phy/rf/rf_zmq_test.c b/lib/src/phy/rf/rf_zmq_test.c index be42022e5..70d5704a5 100644 --- a/lib/src/phy/rf/rf_zmq_test.c +++ b/lib/src/phy/rf/rf_zmq_test.c @@ -233,7 +233,6 @@ int param_test(const char* args_param, const int num_channels) int main() { - srsran_rf_load_plugins(); // // two Rx ports // if (param_test("rx_port=ipc://dl0,rx_port1=ipc://dl1", 2)) { // fprintf(stderr, "Param test failed!\n"); From f4ff72bff88d42fc328203765cc872f77c7ac18e Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Mon, 21 Mar 2022 16:58:13 +0000 Subject: [PATCH 068/195] lib,rlc_am_nr: fixes for setting the rx_highest_status incorrectly and incorrect status report generation. --- lib/src/rlc/rlc_am_nr.cc | 131 ++++++++++++++++++++------------- lib/test/rlc/rlc_am_nr_test.cc | 26 ++++++- 2 files changed, 104 insertions(+), 53 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 1de6a85db..d2a604cf2 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -1108,57 +1108,69 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) debug_state(); // 5.2.3.2.3 Actions when an AMD PDU is placed in the reception buffer - // Update Rx_Next_Highest + /* + * - if x >= RX_Next_Highest + * - update RX_Next_Highest to x+ 1. + */ if (rx_mod_base_nr(header.sn) >= rx_mod_base_nr(st.rx_next_highest)) { st.rx_next_highest = (header.sn + 1) % mod_nr; } - // Update RX_Highest_Status /* - * - if x = RX_Highest_Status, - * - update RX_Highest_Status to the SN of the first RLC SDU with SN > current RX_Highest_Status for which not - * all bytes have been received. + * - if all bytes of the RLC SDU with SN = x are received: */ - if (rx_mod_base_nr(header.sn) == rx_mod_base_nr(st.rx_highest_status)) { - uint32_t sn_upd = 0; - uint32_t window_top = st.rx_next + am_window_size(cfg.rx_sn_field_length); - for (sn_upd = st.rx_highest_status; sn_upd < window_top; ++sn_upd) { - if (rx_window->has_sn(sn_upd)) { - if (not(*rx_window)[sn_upd].fully_received) { + if (rx_window->has_sn(header.sn) && (*rx_window)[header.sn].fully_received) { + /* + * - reassemble the RLC SDU from AMD PDU(s) with SN = x, remove RLC headers when doing so and deliver + * the reassembled RLC SDU to upper layer; + */ + write_to_upper_layers(parent->lcid, std::move((*rx_window)[header.sn].buf)); + + /* + * - if x = RX_Highest_Status, + * - update RX_Highest_Status to the SN of the first RLC SDU with SN > current RX_Highest_Status for which not + * all bytes have been received. + */ + if (rx_mod_base_nr(header.sn) == rx_mod_base_nr(st.rx_highest_status)) { + uint32_t sn_upd = 0; + uint32_t window_top = st.rx_next + am_window_size(cfg.rx_sn_field_length); + for (sn_upd = (st.rx_highest_status + 1) % mod_nr; sn_upd < window_top; ++sn_upd) { + if (rx_window->has_sn(sn_upd)) { + if (not(*rx_window)[sn_upd].fully_received) { + break; // first SDU not fully received + } + } else { break; // first SDU not fully received } - } else { - break; // first SDU not fully received } + // Update to the SN of the first SDU with missing bytes. + // If it not exists, update to the end of the rx_window. + st.rx_highest_status = sn_upd; } - // Update to the SN of the first SDU with missing bytes. - // If it not exists, update to the end of the rx_window. - st.rx_highest_status = sn_upd; - } - - /* - * - if x = RX_Next: - * - update RX_Next to the SN of the first RLC SDU with SN > current RX_Next for which not all bytes - * have been received. - */ - if (rx_mod_base_nr(header.sn) == rx_mod_base_nr(st.rx_next)) { - uint32_t sn_upd = 0; - uint32_t window_top = st.rx_next + am_window_size(cfg.rx_sn_field_length); - for (sn_upd = st.rx_next; sn_upd < window_top; ++sn_upd) { - if (rx_window->has_sn(sn_upd)) { - if (not(*rx_window)[sn_upd].fully_received) { + /* + * - if x = RX_Next: + * - update RX_Next to the SN of the first RLC SDU with SN > current RX_Next for which not all bytes + * have been received. + */ + if (rx_mod_base_nr(header.sn) == rx_mod_base_nr(st.rx_next)) { + uint32_t sn_upd = 0; + uint32_t window_top = st.rx_next + am_window_size(cfg.rx_sn_field_length); + for (sn_upd = st.rx_next; sn_upd < window_top; ++sn_upd) { + if (rx_window->has_sn(sn_upd)) { + if (not(*rx_window)[sn_upd].fully_received) { + break; // first SDU not fully received + } + // RX_Next serves as the lower edge of the receiving window + // As such, we remove any SDU from the window if we update this value + rx_window->remove_pdu(sn_upd); + } else { break; // first SDU not fully received } - // RX_Next serves as the lower edge of the receiving window - // As such, we remove any SDU from the window if we update this value - rx_window->remove_pdu(sn_upd); - } else { - break; // first SDU not fully received } + // Update to the SN of the first SDU with missing bytes. + // If it not exists, update to the end of the rx_window. + st.rx_next = sn_upd; } - // Update to the SN of the first SDU with missing bytes. - // If it not exists, update to the end of the rx_window. - st.rx_next = sn_upd; } if (reassembly_timer.is_running()) { @@ -1171,7 +1183,17 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) * to RX_Next + AM_Window_Size: * - stop and reset t-Reassembly. */ - } else { + bool stop_reassembly_timer = false; + if (st.rx_next_status_trigger == st.rx_next) { + stop_reassembly_timer = true; + } + if (stop_reassembly_timer) { + reassembly_timer.stop(); + } + } + + if (not reassembly_timer.is_running()) { + // if t-Reassembly is not running (includes the case t-Reassembly is stopped due to actions above): /* * - if RX_Next_Highest> RX_Next +1; or * - if RX_Next_Highest = RX_Next + 1 and there is at least one missing byte segment of the SDU associated @@ -1183,8 +1205,8 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) if (st.rx_next_highest > st.rx_next + 1) { restart_reassembly_timer = true; } - if (st.rx_next_highest == st.rx_next + 1 && rx_window->has_sn(st.rx_next + 1) && - not(*rx_window)[st.rx_next + 1].fully_received) { + if (st.rx_next_highest == st.rx_next + 1 && rx_window->has_sn(st.rx_next) && + not(*rx_window)[st.rx_next].fully_received) { restart_reassembly_timer = true; } if (restart_reassembly_timer) { @@ -1219,7 +1241,6 @@ int rlc_am_nr_rx::handle_full_data_sdu(const rlc_am_nr_pdu_header_t& header, con memcpy(rx_sdu.buf->msg, payload + hdr_len, nof_bytes - hdr_len); // Don't copy header rx_sdu.buf->N_bytes = nof_bytes - hdr_len; rx_sdu.fully_received = true; - write_to_upper_layers(parent->lcid, std::move((*rx_window)[header.sn].buf)); return SRSRAN_SUCCESS; } @@ -1274,7 +1295,6 @@ int rlc_am_nr_rx::handle_segment_data_sdu(const rlc_am_nr_pdu_header_t& header, memcpy(&rx_sdu.buf->msg[rx_sdu.buf->N_bytes], it.buf->msg, it.buf->N_bytes); rx_sdu.buf->N_bytes += it.buf->N_bytes; } - write_to_upper_layers(parent->lcid, std::move((*rx_window)[header.sn].buf)); } return SRSRAN_SUCCESS; } @@ -1293,10 +1313,16 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m status->ack_sn = st.rx_next; // Start with the lower end of the window byte_buffer_t tmp_buf; + /* + * - for the RLC SDUs with SN such that RX_Next <= SN < RX_Highest_Status that has not been completely + * received yet, in increasing SN order of RLC SDUs and increasing byte segment order within RLC SDUs, + * starting with SN = RX_Next up to the point where the resulting STATUS PDU still fits to the total size of RLC + * PDU(s) indicated by lower layer: + */ uint32_t i = status->ack_sn; - while (rx_mod_base_nr(i) <= rx_mod_base_nr(st.rx_highest_status)) { - if ((rx_window->has_sn(i) && (*rx_window)[i].fully_received) || i == st.rx_highest_status) { - // only update ACK_SN if this SN has been fully received, or if we reached the maximum possible SN + for (i = st.rx_next; rx_mod_base_nr(i) < rx_mod_base_nr(st.rx_highest_status); i = (i + 1) % mod_nr) { + if ((rx_window->has_sn(i) && (*rx_window)[i].fully_received)) { + // only update ACK_SN if this SN has been fully received status->ack_sn = i; } else { if (not rx_window->has_sn(i)) { @@ -1331,14 +1357,17 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m } } } - - // TODO: add check to not exceed status->N_nack >= RLC_AM_NR_MAX_NACKS - // make sure we don't exceed grant size (FIXME) rlc_am_nr_write_status_pdu(*status, cfg.rx_sn_field_length, &tmp_buf); - // TODO - i = (i + 1) % mod_nr; } + /* + * - set the ACK_SN to the SN of the next not received RLC SDU which is not + * indicated as missing in the resulting STATUS PDU. + */ + // FIXME as we do not check the size of status report, the next not received + // RLC SDU has the same SN as RX_HIGHEST_STATUS + status->ack_sn = st.rx_highest_status; + rlc_am_nr_write_status_pdu(*status, cfg.rx_sn_field_length, &tmp_buf); if (max_len != UINT32_MAX) { // UINT32_MAX is used just to query the status PDU length @@ -1387,7 +1416,7 @@ void rlc_am_nr_rx::timer_expired(uint32_t timeout_id) for (uint32_t tmp_sn = st.rx_next_status_trigger; tmp_sn < st.rx_next_status_trigger + am_window_size(cfg.rx_sn_field_length); tmp_sn++) { - if (not rx_window->has_sn(tmp_sn)) { + if (not rx_window->has_sn(tmp_sn) || not(*rx_window)[tmp_sn].fully_received) { st.rx_highest_status = tmp_sn; break; } diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index 7042264e1..3f65ac07d 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -57,7 +57,6 @@ int basic_test_tx(rlc_am* rlc, byte_buffer_t pdu_bufs[NBUFS], rlc_am_nr_sn_size_ /* * Test the limits of the TX/RX window checkers - * */ int window_checker_test(rlc_am_nr_sn_size_t sn_size) { @@ -825,11 +824,25 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) // Write 15 - 3 PDUs into RLC2 for (int i = 0; i < n_pdu_bufs; i++) { if (i != 3 && i != 7 && i != 11) { - rlc2.write_pdu(pdu_bufs[i]->msg, pdu_bufs[i]->N_bytes); // Lose first segment of RLC_SN=1. + // Lose first segment of RLC_SN=1. + // Lose middle segment of RLC_SN=2. + // Lose last segment of RLC_SN=3. + rlc2.write_pdu(pdu_bufs[i]->msg, pdu_bufs[i]->N_bytes); } } + { + // Double check rx state + rlc_am_nr_rx_state_t st = rx2->get_rx_state(); + TESTASSERT_EQ(1, st.rx_next); + TESTASSERT_EQ(1, st.rx_highest_status); + TESTASSERT_EQ(2, st.rx_next_status_trigger); // Rx_Next_Highest + 1, when the t-Reordering was started + TESTASSERT_EQ(5, st.rx_next_highest); // Highest SN received + 1 + } + // Only after t-reassembly has expired, will the status report include NACKs. + // RX_Highest_Status will be updated to to the SN + // of the first RLC SDU with SN >= RX_Next_Status_Trigger TESTASSERT_EQ(3, rlc2.get_buffer_state()); { // Read status PDU from RLC2 @@ -853,6 +866,15 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) timers.step_all(); } + { + // Double check rx state + rlc_am_nr_rx_state_t st = rx2->get_rx_state(); + TESTASSERT_EQ(1, st.rx_next); + TESTASSERT_EQ(1, st.rx_highest_status); + TESTASSERT_EQ(2, st.rx_next_status_trigger); // Rx_Next_Highest + 1, when the t-Reordering was started + TESTASSERT_EQ(5, st.rx_next_highest); // Highest SN received + 1 + } + // t-reassembly has expired. There should be a NACK in the status report. // There should be 3 NACKs with SO_start and SO_end constexpr uint32_t status_pdu_ack_size = 3; From 1e0e451174d7676d210cddc0631ca10695c359a7 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Mon, 21 Mar 2022 21:24:40 +0000 Subject: [PATCH 069/195] lib,rlc_am_nr: fixes for retx_segment_test after changes to RX_Highest_Status --- lib/test/rlc/rlc_am_nr_test.cc | 84 ++++++++++++++++++++++++++-------- 1 file changed, 65 insertions(+), 19 deletions(-) diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index 3f65ac07d..2d3b9952a 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -866,12 +866,58 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) timers.step_all(); } + // After the t-Reassembly expires: + // - RX_Highest_Status is updated to the SN of the first RLC SDU with SN >= RX_Next_Status_Trigger, i.e., SN=2 + // - Because RX_Next_Highest> RX_Highest_Status +1: + // - t-Reassembly is restarted, and + // - RX_Next_Status_Trigger is set to RX_Next_Highest. { // Double check rx state rlc_am_nr_rx_state_t st = rx2->get_rx_state(); TESTASSERT_EQ(1, st.rx_next); - TESTASSERT_EQ(1, st.rx_highest_status); - TESTASSERT_EQ(2, st.rx_next_status_trigger); // Rx_Next_Highest + 1, when the t-Reordering was started + TESTASSERT_EQ(2, st.rx_highest_status); + TESTASSERT_EQ(5, st.rx_next_status_trigger); // Rx_Next_Highest + 1, when the t-Reordering was started + TESTASSERT_EQ(5, st.rx_next_highest); // Highest SN received + 1 + } + + // t-reassembly has expired. Becuse RX_Highest_Status is 2 + // There should be an ACK of SN=2 and a NACK of SN=1 + TESTASSERT_EQ(9, rlc2.get_buffer_state()); // 3 bytes for fixed header (ACK+E1) + 6 for NACK with SO = 9. + { + // Read status PDU from RLC2 + byte_buffer_t status_buf; + int len = rlc2.read_pdu(status_buf.msg, 9); + status_buf.N_bytes = len; + + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check = {}; + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(2, status_check.ack_sn); // 5 is the next expected SN. + TESTASSERT_EQ(1, status_check.N_nack); // We lost one PDU. + TESTASSERT_EQ(1, status_check.nacks[0].nack_sn); // Lost SDU on SN=1. + TESTASSERT_EQ(true, status_check.nacks[0].has_so); // It's a segment. + TESTASSERT_EQ(0, status_check.nacks[0].so_start); // First byte missing is 0. + TESTASSERT_EQ(0, status_check.nacks[0].so_end); // Last byte of the segment. + } + + // Step timers until reassambly timeout expires + for (int cnt = 0; cnt < 35; cnt++) { + timers.step_all(); + } + + // After the t-Reassembly expires: + // - RX_Highest_Status is updated to the SN of the first RLC SDU with SN >= RX_Next_Status_Trigger, i.e., SN=2 + // - Because RX_Next_Highest> RX_Highest_Status +1: + // - t-Reassembly is restarted, and + // - RX_Next_Status_Trigger is set to RX_Next_Highest. + { + // Double check rx state + rlc_am_nr_rx_state_t st = rx2->get_rx_state(); + TESTASSERT_EQ(1, st.rx_next); + TESTASSERT_EQ(5, st.rx_highest_status); + TESTASSERT_EQ(5, st.rx_next_status_trigger); // Rx_Next_Highest + 1, when the t-Reordering was started TESTASSERT_EQ(5, st.rx_next_highest); // Highest SN received + 1 } @@ -953,7 +999,7 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) uint32_t data_pdu_size = header_size + payload_size; uint32_t total_tx_pdu_bytes1 = 5 * pdu_size_first + 10 * pdu_size_continued + pdu_size_first + 2 * pdu_size_continued; - uint32_t total_rx_pdu_bytes1 = 2 * status_pdu_ack_size + 3 * (status_pdu_nack_size + status_pdu_so_size); + uint32_t total_rx_pdu_bytes1 = 3 * status_pdu_ack_size + 4 * (status_pdu_nack_size + status_pdu_so_size); uint32_t total_tx_pdu_bytes2 = total_rx_pdu_bytes1; uint32_t total_rx_pdu_bytes2 = 4 * pdu_size_first + 8 * pdu_size_continued + pdu_size_first + 2 * pdu_size_continued; @@ -965,15 +1011,15 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) TESTASSERT_EQ(0, metrics1.num_lost_sdus); // PDU metrics - TESTASSERT_EQ(15 + 3, metrics1.num_tx_pdus); // 15 PDUs + 3 re-transmissions - TESTASSERT_EQ(2, metrics1.num_rx_pdus); // Two status PDU + TESTASSERT_EQ(15 + 3, metrics1.num_tx_pdus); // 15 PDUs + 3 re-transmissions + TESTASSERT_EQ(2, metrics1.num_rx_pdus); // Two status PDU TESTASSERT_EQ(total_tx_pdu_bytes1, metrics1.num_tx_pdu_bytes); // 3 Bytes * 5 (5 PDUs without SO) + 10 * 5 (10 PDUs with SO) // 3 (1 retx no SO) + 2 * 5 (2 retx with SO) = 78 TESTASSERT_EQ(total_rx_pdu_bytes1, - metrics1.num_rx_pdu_bytes); // Two status PDU. One with just an ack (3 bytes) - // Another with 3 NACKs all with SO. (3 + 3*6 bytes) = 24 - TESTASSERT_EQ(0, metrics1.num_lost_sdus); // No lost SDUs + metrics1.num_rx_pdu_bytes); // Two status PDU. One with just an ack (3 bytes) + // Another with 3 NACKs all with SO. (3 + 3*6 bytes) = 24 + TESTASSERT_EQ(0, metrics1.num_lost_sdus); // No lost SDUs // PDU metrics TESTASSERT_EQ(0, metrics2.num_tx_sdus); @@ -982,15 +1028,15 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) TESTASSERT_EQ(15, metrics2.num_rx_sdu_bytes); // 5 SDUs, 3 bytes each TESTASSERT_EQ(0, metrics2.num_lost_sdus); // SDU metrics - TESTASSERT_EQ(2, metrics2.num_tx_pdus); // Two status PDUs - TESTASSERT_EQ(15, metrics2.num_rx_pdus); // 15 PDUs (18 tx'ed, but three were lost) - TESTASSERT_EQ(total_tx_pdu_bytes2, - metrics2.num_tx_pdu_bytes); // Two status PDU. One with just an ack (3 bytes) - // Another with 3 NACKs all with SO. (3 + 3*6 bytes) = 24 - TESTASSERT_EQ(total_rx_pdu_bytes2, - metrics2.num_rx_pdu_bytes); // 3 Bytes * 4 (5-1 PDUs without SO) + 8 * 5 (10-2 PDUs with SO) - // 3 (1 retx no SO) + 2 * 5 (2 retx with SO) = 65 bytes - TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs + TESTASSERT_EQ(3, metrics2.num_tx_pdus); // 3 status PDUs + TESTASSERT_EQ(15, metrics2.num_rx_pdus); // 15 PDUs (18 tx'ed, but three were lost) + TESTASSERT_EQ(total_tx_pdu_bytes2, // Three status PDU. One with just an ack + metrics2.num_tx_pdu_bytes); // Another with 1 NACK with SO. + // Another with 3 NACKs all with SO. + TESTASSERT_EQ(total_rx_pdu_bytes2, // 3 Bytes (header + data size, without SO) * 5 (N PDUs without SO) + metrics2.num_rx_pdu_bytes); // 5 bytes (header + data size, with SO) * 10 (N PDUs with SO) + // = 81 bytes + TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs // Check state rlc_am_nr_rx_state_t state2_rx = rx2->get_rx_state(); @@ -1248,7 +1294,7 @@ int discard_test(rlc_am_nr_sn_size_t sn_size) } } TESTASSERT(rlc1.get_buffer_state() == num_tx_sdus * (header_size + payload_size)); // 10 * (2B Header + 7B Payload) - rlc1.discard_sdu(3); // Try to discard PDCP_SN=3 + rlc1.discard_sdu(3); // Try to discard PDCP_SN=3 TESTASSERT(rlc1.has_data() == true); TESTASSERT(rlc1.get_buffer_state() == (num_tx_sdus - 1) * (header_size + payload_size)); rlc1.discard_sdu(9); // Try to discard PDCP_SN=9 @@ -1288,7 +1334,7 @@ int discard_test(rlc_am_nr_sn_size_t sn_size) } } TESTASSERT(rlc1.get_buffer_state() == num_tx_sdus * (header_size + payload_size)); // 3 * (2B Header + 7B Payload) - rlc1.discard_sdu(8); // Try to discard PDCP_SN=8, which doesn't exist + rlc1.discard_sdu(8); // Try to discard PDCP_SN=8, which doesn't exist TESTASSERT(rlc1.get_buffer_state() == num_tx_sdus * (header_size + payload_size)); // 3 * (2B Header + 7B Payload) return SRSRAN_SUCCESS; From 981983377d291d65646929461bb4b69c82e29f02 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Tue, 22 Mar 2022 11:47:50 +0000 Subject: [PATCH 070/195] lib,rlc_am_nr: fixes for retx_segment test for 18bits after changes for Rx_Highest_Status --- lib/test/rlc/rlc_am_nr_test.cc | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index 2d3b9952a..8102d7da0 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -761,7 +761,8 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) auto& test_logger = srslog::fetch_basic_logger("TESTER "); rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); - test_delimit_logger delimiter("retx segment PDU (%d bit SN)", to_number(sn_size)); + std::string str = "retx segment PDU (" + std::to_string(to_number(sn_size)) + " bit SN)"; + test_delimit_logger delimiter(str.c_str()); rlc_am_nr_tx* tx1 = dynamic_cast(rlc1.get_tx()); rlc_am_nr_rx* rx1 = dynamic_cast(rlc1.get_rx()); @@ -882,11 +883,15 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) // t-reassembly has expired. Becuse RX_Highest_Status is 2 // There should be an ACK of SN=2 and a NACK of SN=1 - TESTASSERT_EQ(9, rlc2.get_buffer_state()); // 3 bytes for fixed header (ACK+E1) + 6 for NACK with SO = 9. + constexpr uint32_t status_pdu_ack_size = 3; + uint32_t status_pdu_nack_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + constexpr uint32_t status_pdu_so_size = 4; + TESTASSERT_EQ(status_pdu_ack_size + status_pdu_nack_size + status_pdu_so_size, + rlc2.get_buffer_state()); // 3 bytes for fixed header (ACK+E1) + 6 for NACK with SO = 9. { // Read status PDU from RLC2 byte_buffer_t status_buf; - int len = rlc2.read_pdu(status_buf.msg, 9); + int len = rlc2.read_pdu(status_buf.msg, status_pdu_ack_size + status_pdu_nack_size + status_pdu_so_size); status_buf.N_bytes = len; TESTASSERT_EQ(0, rlc2.get_buffer_state()); @@ -923,9 +928,6 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) // t-reassembly has expired. There should be a NACK in the status report. // There should be 3 NACKs with SO_start and SO_end - constexpr uint32_t status_pdu_ack_size = 3; - uint32_t status_pdu_nack_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; - constexpr uint32_t status_pdu_so_size = 4; TESTASSERT_EQ(status_pdu_ack_size + 3 * (status_pdu_nack_size + status_pdu_so_size), rlc2.get_buffer_state()); { // Read status PDU from RLC2 @@ -999,8 +1001,8 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) uint32_t data_pdu_size = header_size + payload_size; uint32_t total_tx_pdu_bytes1 = 5 * pdu_size_first + 10 * pdu_size_continued + pdu_size_first + 2 * pdu_size_continued; - uint32_t total_rx_pdu_bytes1 = 3 * status_pdu_ack_size + 4 * (status_pdu_nack_size + status_pdu_so_size); - uint32_t total_tx_pdu_bytes2 = total_rx_pdu_bytes1; + uint32_t total_rx_pdu_bytes1 = 2 * status_pdu_ack_size + 3 * (status_pdu_nack_size + status_pdu_so_size); + uint32_t total_tx_pdu_bytes2 = 3 * status_pdu_ack_size + 4 * (status_pdu_nack_size + status_pdu_so_size); uint32_t total_rx_pdu_bytes2 = 4 * pdu_size_first + 8 * pdu_size_continued + pdu_size_first + 2 * pdu_size_continued; // SDU metrics From 6a1f6a35c1b8a60b304830afe2d382ff8710d10d Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Thu, 24 Mar 2022 11:46:13 +0000 Subject: [PATCH 071/195] lib,rlc_am_nr: fix loops for updating st.rx_highest_status and st.rx_next to be bounded by st.rx_next_highest, since rx_next_highest holds the value of the highest possible missing SDU. --- lib/src/rlc/rlc_am_nr.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index d2a604cf2..8fdd6bbbb 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -1132,9 +1132,9 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) * all bytes have been received. */ if (rx_mod_base_nr(header.sn) == rx_mod_base_nr(st.rx_highest_status)) { - uint32_t sn_upd = 0; - uint32_t window_top = st.rx_next + am_window_size(cfg.rx_sn_field_length); - for (sn_upd = (st.rx_highest_status + 1) % mod_nr; sn_upd < window_top; ++sn_upd) { + uint32_t sn_upd = 0; + for (sn_upd = (st.rx_highest_status + 1) % mod_nr; rx_mod_base_nr(sn_upd) < rx_mod_base_nr(st.rx_next_highest); + sn_upd = (sn_upd + 1) % mod_nr) { if (rx_window->has_sn(sn_upd)) { if (not(*rx_window)[sn_upd].fully_received) { break; // first SDU not fully received @@ -1155,7 +1155,8 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) if (rx_mod_base_nr(header.sn) == rx_mod_base_nr(st.rx_next)) { uint32_t sn_upd = 0; uint32_t window_top = st.rx_next + am_window_size(cfg.rx_sn_field_length); - for (sn_upd = st.rx_next; sn_upd < window_top; ++sn_upd) { + for (sn_upd = st.rx_next; rx_mod_base_nr(sn_upd) < rx_mod_base_nr(st.rx_next_highest); + sn_upd = (sn_upd + 1) % mod_nr) { if (rx_window->has_sn(sn_upd)) { if (not(*rx_window)[sn_upd].fully_received) { break; // first SDU not fully received From 0ada2adac754447bba8fb2ac6ae1d3661cde1d87 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Thu, 24 Mar 2022 12:09:48 +0000 Subject: [PATCH 072/195] lib,rlc_am_nr: minor fixes to address coments. Re-added comment, deleted useless assignement and fixed incorrect comment --- lib/src/rlc/rlc_am_nr.cc | 4 ++-- lib/test/rlc/rlc_am_nr_test.cc | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 8fdd6bbbb..3ba720aa6 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -1320,8 +1320,7 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m * starting with SN = RX_Next up to the point where the resulting STATUS PDU still fits to the total size of RLC * PDU(s) indicated by lower layer: */ - uint32_t i = status->ack_sn; - for (i = st.rx_next; rx_mod_base_nr(i) < rx_mod_base_nr(st.rx_highest_status); i = (i + 1) % mod_nr) { + for (uint32_t i = st.rx_next; rx_mod_base_nr(i) < rx_mod_base_nr(st.rx_highest_status); i = (i + 1) % mod_nr) { if ((rx_window->has_sn(i) && (*rx_window)[i].fully_received)) { // only update ACK_SN if this SN has been fully received status->ack_sn = i; @@ -1358,6 +1357,7 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m } } } + // TODO: add check to not exceed status->N_nack >= RLC_AM_NR_MAX_NACKS // make sure we don't exceed grant size (FIXME) rlc_am_nr_write_status_pdu(*status, cfg.rx_sn_field_length, &tmp_buf); } diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index 8102d7da0..a86aa684a 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -877,7 +877,7 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) rlc_am_nr_rx_state_t st = rx2->get_rx_state(); TESTASSERT_EQ(1, st.rx_next); TESTASSERT_EQ(2, st.rx_highest_status); - TESTASSERT_EQ(5, st.rx_next_status_trigger); // Rx_Next_Highest + 1, when the t-Reordering was started + TESTASSERT_EQ(5, st.rx_next_status_trigger); // Rx_Next_Highest + 1, when the t-Reassembly was started TESTASSERT_EQ(5, st.rx_next_highest); // Highest SN received + 1 } From a15b4c039ecd7ebf8d20a7579b83b4fea3806cf5 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Thu, 24 Mar 2022 11:01:35 +0000 Subject: [PATCH 073/195] lib,rlc_am_nr: fix state debugging info logging. Also log current TX/RX window size --- lib/src/rlc/rlc_am_nr.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 3ba720aa6..0b637ca1d 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -1001,9 +1001,10 @@ bool rlc_am_nr_tx::inside_tx_window(uint32_t sn) const */ void rlc_am_nr_tx::debug_state() const { - RlcDebug("TX entity state: Tx_Next %d, Rx_Next_Ack %d, POLL_SN %d, PDU_WITHOUT_POLL %d, BYTE_WITHOUT_POLL %d", - st.tx_next, + RlcDebug("TX window state: SDUs %d", tx_window->size()); + RlcDebug("TX entity state: Tx_Next_Ack=%d, Tx_Next=%d, POLL_SN=%d, PDU_WITHOUT_POLL=%d, BYTE_WITHOUT_POLL=%d", st.tx_next_ack, + st.tx_next, st.poll_sn, st.pdu_without_poll, st.byte_without_poll); @@ -1519,6 +1520,7 @@ bool rlc_am_nr_rx::have_all_segments_been_received( */ void rlc_am_nr_rx::debug_state() const { + RlcDebug("RX window state: SDUs %d", rx_window->size()); RlcDebug("RX entity state: Rx_Next=%d, Rx_Next_Status_Trigger=%d, Rx_Highest_Status=%d, Rx_Next_Highest=%d", st.rx_next, st.rx_next_status_trigger, From 46872a8c84c52af5cc087e0faad75915d1a92139 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Wed, 23 Mar 2022 10:52:41 +0000 Subject: [PATCH 074/195] lib,rlc_am_nr: fix unprotected access to rx_window during timer expiration --- lib/src/rlc/rlc_am_nr.cc | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 0b637ca1d..2207f593d 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -1415,20 +1415,22 @@ void rlc_am_nr_rx::timer_expired(uint32_t timeout_id) * - start t-Reassembly; * - set RX_Next_Status_Trigger to RX_Next_Highest. */ - for (uint32_t tmp_sn = st.rx_next_status_trigger; - tmp_sn < st.rx_next_status_trigger + am_window_size(cfg.rx_sn_field_length); - tmp_sn++) { - if (not rx_window->has_sn(tmp_sn) || not(*rx_window)[tmp_sn].fully_received) { - st.rx_highest_status = tmp_sn; + for (uint32_t sn = st.rx_next_status_trigger; rx_mod_base_nr(sn) <= rx_mod_base_nr(st.rx_next_highest); + sn = (sn + 1) % mod_nr) { + if (not rx_window->has_sn(sn) || (rx_window->has_sn(sn) && not(*rx_window)[sn].fully_received)) { + st.rx_highest_status = sn; break; } } bool restart_reassembly_timer = false; - if (st.rx_next_highest > st.rx_highest_status + 1) { + if (rx_mod_base_nr(st.rx_next_highest) > rx_mod_base_nr(st.rx_highest_status + 1)) { restart_reassembly_timer = true; } - if (st.rx_next_highest == st.rx_highest_status + 1 && not(*rx_window)[st.rx_next_highest].fully_received) { - restart_reassembly_timer = true; + if (rx_mod_base_nr(st.rx_next_highest) == rx_mod_base_nr(st.rx_highest_status + 1)) { + if (not rx_window->has_sn(st.rx_next_highest) || + (rx_window->has_sn(st.rx_next_highest) && not(*rx_window)[st.rx_next_highest].fully_received)) { + restart_reassembly_timer = true; + } } if (restart_reassembly_timer) { reassembly_timer.run(); From 8e068611136987735bd40b1220bd3d6707920435 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Thu, 24 Mar 2022 16:23:28 +0000 Subject: [PATCH 075/195] lib,rlc_am_nr: fixup rx_next_highest when t-Reassembly exipres --- lib/src/rlc/rlc_am_nr.cc | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 2207f593d..718f1741c 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -1415,13 +1415,15 @@ void rlc_am_nr_rx::timer_expired(uint32_t timeout_id) * - start t-Reassembly; * - set RX_Next_Status_Trigger to RX_Next_Highest. */ - for (uint32_t sn = st.rx_next_status_trigger; rx_mod_base_nr(sn) <= rx_mod_base_nr(st.rx_next_highest); - sn = (sn + 1) % mod_nr) { - if (not rx_window->has_sn(sn) || (rx_window->has_sn(sn) && not(*rx_window)[sn].fully_received)) { - st.rx_highest_status = sn; + uint32_t sn_upd = {}; + for (sn_upd = st.rx_next_status_trigger; rx_mod_base_nr(sn_upd) < rx_mod_base_nr(st.rx_next_highest); + sn_upd = (sn_upd + 1) % mod_nr) { + if (not rx_window->has_sn(sn_upd) || (rx_window->has_sn(sn_upd) && not(*rx_window)[sn_upd].fully_received)) { break; } } + st.rx_highest_status = sn_upd; + bool restart_reassembly_timer = false; if (rx_mod_base_nr(st.rx_next_highest) > rx_mod_base_nr(st.rx_highest_status + 1)) { restart_reassembly_timer = true; @@ -1443,6 +1445,7 @@ void rlc_am_nr_rx::timer_expired(uint32_t timeout_id) * triggered, but the STATUS report shall be triggered after RX_Highest_Status is updated. */ do_status = true; + debug_state(); return; } } From 9a0d7113bc27b17ef0cd39c9a46974350598c64b Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Fri, 25 Mar 2022 10:34:24 +0000 Subject: [PATCH 076/195] lib,rlc_am_nr: fix checking the wrong SDU rx state on t-Reassembly expiry --- lib/src/rlc/rlc_am_nr.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 718f1741c..5bef44e90 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -1429,8 +1429,8 @@ void rlc_am_nr_rx::timer_expired(uint32_t timeout_id) restart_reassembly_timer = true; } if (rx_mod_base_nr(st.rx_next_highest) == rx_mod_base_nr(st.rx_highest_status + 1)) { - if (not rx_window->has_sn(st.rx_next_highest) || - (rx_window->has_sn(st.rx_next_highest) && not(*rx_window)[st.rx_next_highest].fully_received)) { + if (not rx_window->has_sn(st.rx_highest_status) || + (rx_window->has_sn(st.rx_highest_status) && not(*rx_window)[st.rx_highest_status].fully_received)) { restart_reassembly_timer = true; } } From cf647b1fd5e65209e95be2aa861b1de36c1d8fcb Mon Sep 17 00:00:00 2001 From: Ismael Gomez Date: Fri, 18 Feb 2022 14:57:03 +0100 Subject: [PATCH 077/195] srsenb,metrics: add PUSCH/PUCCH RSSI metrics. Add bearer DL total data metric for RLC UM --- lib/include/srsran/phy/phch/pucch.h | 1 + lib/include/srsran/phy/utils/vector.h | 9 +++++++++ lib/src/pdcp/pdcp_entity_lte.cc | 4 ++++ lib/src/phy/ch_estimation/chest_ul.c | 5 ++++- lib/src/phy/enb/enb_ul.c | 3 ++- srsenb/enb.conf.example | 3 ++- srsenb/hdr/phy/lte/cc_worker.h | 2 +- srsenb/hdr/phy/phy_interfaces.h | 1 + srsenb/hdr/phy/phy_metrics.h | 28 +++++++++++---------------- srsenb/src/main.cc | 1 + srsenb/src/phy/lte/cc_worker.cc | 20 +++++++++++++++---- srsenb/src/phy/lte/sf_worker.cc | 16 ++++++++------- srsenb/src/phy/phy.cc | 22 +++++++++++++-------- 13 files changed, 75 insertions(+), 40 deletions(-) diff --git a/lib/include/srsran/phy/phch/pucch.h b/lib/include/srsran/phy/phch/pucch.h index 73c7ec6a1..5a70a676f 100644 --- a/lib/include/srsran/phy/phch/pucch.h +++ b/lib/include/srsran/phy/phch/pucch.h @@ -75,6 +75,7 @@ typedef struct SRSRAN_API { srsran_uci_value_t uci_data; float dmrs_correlation; float snr_db; + float rsrp_db; float correlation; bool detected; diff --git a/lib/include/srsran/phy/utils/vector.h b/lib/include/srsran/phy/utils/vector.h index 83385638a..d94d640ec 100644 --- a/lib/include/srsran/phy/utils/vector.h +++ b/lib/include/srsran/phy/utils/vector.h @@ -61,6 +61,15 @@ extern "C" { // Proportional moving average #define SRSRAN_VEC_PMA(average1, n1, average2, n2) (((average1) * (n1) + (average2) * (n2)) / ((n1) + (n2))) +// Safe Proportional moving average +#ifdef __cplusplus +#define SRSRAN_VEC_SAFE_PMA(average1, n1, average2, n2) \ + (std::isnormal((n1) + (n2)) ? SRSRAN_VEC_PMA(average1, n1, average2, n2) : (0)) +#else +#define SRSRAN_VEC_SAFE_PMA(average1, n1, average2, n2) \ + (isnormal((n1) + (n2)) ? SRSRAN_VEC_PMA(average1, n1, average2, n2) : (0)) +#endif + // Exponential moving average #define SRSRAN_VEC_EMA(data, average, alpha) ((alpha) * (data) + (1 - alpha) * (average)) diff --git a/lib/src/pdcp/pdcp_entity_lte.cc b/lib/src/pdcp/pdcp_entity_lte.cc index 8047ad6da..0491e246a 100644 --- a/lib/src/pdcp/pdcp_entity_lte.cc +++ b/lib/src/pdcp/pdcp_entity_lte.cc @@ -221,6 +221,10 @@ void pdcp_entity_lte::write_sdu(unique_byte_buffer_t sdu, int upper_sn) // Pass PDU to lower layers metrics.num_tx_pdus++; metrics.num_tx_pdu_bytes += sdu->N_bytes; + // Count TX'd bytes as if they were ACK'd if RLC is UM + if (rlc->rb_is_um(lcid)) { + metrics.num_tx_acked_bytes = metrics.num_tx_pdu_bytes; + } rlc->write_sdu(lcid, std::move(sdu)); } diff --git a/lib/src/phy/ch_estimation/chest_ul.c b/lib/src/phy/ch_estimation/chest_ul.c index 00873f077..056845966 100644 --- a/lib/src/phy/ch_estimation/chest_ul.c +++ b/lib/src/phy/ch_estimation/chest_ul.c @@ -347,13 +347,16 @@ static void chest_ul_estimate(srsran_chest_ul_t* q, } // Estimate received pilot power + float signal_power = srsran_vec_avg_power_cf(q->pilot_recv_signal, nslots * nrefs_sym); if (isnormal(res->noise_estimate)) { - res->snr = srsran_vec_avg_power_cf(q->pilot_recv_signal, nslots * nrefs_sym) / res->noise_estimate; + res->snr = signal_power / res->noise_estimate; } else { res->snr = NAN; } // Convert measurements in logarithm scale + res->rsrp = signal_power; + res->rsrp_dBfs = srsran_convert_power_to_dB(signal_power); res->snr_db = srsran_convert_power_to_dB(res->snr); res->noise_estimate_dbm = srsran_convert_power_to_dBm(res->noise_estimate); } diff --git a/lib/src/phy/enb/enb_ul.c b/lib/src/phy/enb/enb_ul.c index 605fe7295..d8bffa553 100644 --- a/lib/src/phy/enb/enb_ul.c +++ b/lib/src/phy/enb/enb_ul.c @@ -184,7 +184,8 @@ static int get_pucch(srsran_enb_ul_t* q, srsran_ul_sf_cfg_t* ul_sf, srsran_pucch ERROR("Error estimating PUCCH DMRS"); return SRSRAN_ERROR; } - pucch_res.snr_db = q->chest_res.snr_db; + pucch_res.snr_db = q->chest_res.snr_db; + pucch_res.rsrp_db = q->chest_res.rsrp_dBfs; ret = srsran_pucch_decode(&q->pucch, ul_sf, cfg, &q->chest_res, q->sf_symbols, &pucch_res); if (ret < SRSRAN_SUCCESS) { diff --git a/srsenb/enb.conf.example b/srsenb/enb.conf.example index c18387112..505771a05 100644 --- a/srsenb/enb.conf.example +++ b/srsenb/enb.conf.example @@ -387,7 +387,7 @@ nr_pdsch_mcs=28 # rlf_release_timer_ms: Time taken by eNB to release UE context after it detects a RLF # rlf_min_ul_snr_estim: SNR threshold in dB below which the enb is notified with RLF ko # s1_setup_max_retries: Maximum amount of retries to setup the S1AP connection. If this value is exceeded, an alarm is written to the log. -1 means infinity. -# +# rx_gain_offset: RX Gain offset to add to rx_gain to calibrate RSRP readings ##################################################################### [expert] #pusch_max_its = 8 # These are half iterations @@ -423,3 +423,4 @@ nr_pdsch_mcs=28 #rlf_release_timer_ms = 4000 #rlf_min_ul_snr_estim = -2 #s1_setup_max_retries = -1 +#rx_gain_offset = 62 diff --git a/srsenb/hdr/phy/lte/cc_worker.h b/srsenb/hdr/phy/lte/cc_worker.h index 2edbe6547..aa64e6490 100644 --- a/srsenb/hdr/phy/lte/cc_worker.h +++ b/srsenb/hdr/phy/lte/cc_worker.h @@ -100,7 +100,7 @@ private: void metrics_read(phy_metrics_t* metrics); void metrics_dl(uint32_t mcs); void metrics_ul(uint32_t mcs, float rssi, float sinr, float turbo_iters); - void metrics_ul_pucch(float sinr); + void metrics_ul_pucch(float rssi, float sinr); uint32_t get_rnti() const { return rnti; } private: diff --git a/srsenb/hdr/phy/phy_interfaces.h b/srsenb/hdr/phy/phy_interfaces.h index 0caf92528..661860ab8 100644 --- a/srsenb/hdr/phy/phy_interfaces.h +++ b/srsenb/hdr/phy/phy_interfaces.h @@ -50,6 +50,7 @@ struct phy_args_t { std::string type; srsran::phy_log_args_t log; + float rx_gain_offset = 62; float max_prach_offset_us = 10; uint32_t pusch_max_its = 10; uint32_t nr_pusch_max_its = 10; diff --git a/srsenb/hdr/phy/phy_metrics.h b/srsenb/hdr/phy/phy_metrics.h index 0b27a5417..a89d0196a 100644 --- a/srsenb/hdr/phy/phy_metrics.h +++ b/srsenb/hdr/phy/phy_metrics.h @@ -20,28 +20,22 @@ namespace srsenb { // PHY metrics per user struct ul_metrics_t { - float n; - float pusch_sinr; - // Initialize this member with an invalid value as this field is optional. - float pusch_rssi = std::numeric_limits::quiet_NaN(); - // Initialize this member with an invalid value as this field is optional. - int64_t pusch_tpc = 0; + float n; + float pusch_sinr; + float pusch_rssi; + int64_t pusch_tpc; float pucch_sinr; - // Initialize this member with an invalid value as this field is optional. - float pucch_rssi = std::numeric_limits::quiet_NaN(); - // Initialize this member with an invalid value as this field is optional. - float pucch_ni = std::numeric_limits::quiet_NaN(); - float rssi; - float turbo_iters; - float mcs; - int n_samples; - int n_samples_pucch; + float pucch_rssi; + float pucch_ni; + float turbo_iters; + float mcs; + int n_samples; + int n_samples_pucch; }; struct dl_metrics_t { float mcs; - // Initialize this member with an invalid value as this field is optional. - int64_t pucch_tpc = 0; + int64_t pucch_tpc; int n_samples; }; diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index 2118e1048..975abee54 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -259,6 +259,7 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("expert.ts1_reloc_overall_timeout", bpo::value(&args->stack.s1ap.ts1_reloc_overall_timeout)->default_value(10000), "S1AP TS 36.413 TS1RelocOverall Expiry Timeout value in milliseconds.") ("expert.rlf_min_ul_snr_estim", bpo::value(&args->stack.mac.rlf_min_ul_snr_estim)->default_value(-2), "SNR threshold in dB below which the eNB is notified with rlf ko.") ("expert.max_s1_setup_retries", bpo::value(&args->stack.s1ap.max_s1_setup_retries)->default_value(-1), "Max S1 setup retries") + ("expert.rx_gain_offset", bpo::value(&args->phy.rx_gain_offset)->default_value(62), "RX Gain offset to add to rx_gain to calibrate RSRP readings") // eMBMS section ("embms.enable", bpo::value(&args->stack.embms.enable)->default_value(false), "Enables MBMS in the eNB") diff --git a/srsenb/src/phy/lte/cc_worker.cc b/srsenb/src/phy/lte/cc_worker.cc index aa17fb709..1193587da 100644 --- a/srsenb/src/phy/lte/cc_worker.cc +++ b/srsenb/src/phy/lte/cc_worker.cc @@ -363,7 +363,10 @@ bool cc_worker::decode_pusch_rnti(stack_interface_phy_lte::ul_sched_grant_t& ul_ // Save statistics only if data was provided if (ul_grant.data != nullptr) { // Save metrics stats - ue_db[rnti]->metrics_ul(ul_grant.dci.tb.mcs_idx, 0, enb_ul.chest_res.snr_db, pusch_res.avg_iterations_block); + ue_db[rnti]->metrics_ul(ul_grant.dci.tb.mcs_idx, + enb_ul.chest_res.rsrp_dBfs - phy->params.rx_gain_offset, + enb_ul.chest_res.snr_db, + pusch_res.avg_iterations_block); } return true; } @@ -451,7 +454,9 @@ int cc_worker::decode_pucch() } // Save metrics - ue_db[rnti]->metrics_ul_pucch(pucch_res.snr_db); + if (pucch_res.detected) { + ue_db[rnti]->metrics_ul_pucch(pucch_res.rsrp_db - phy->params.rx_gain_offset, pucch_res.snr_db); + } } } } @@ -666,15 +671,22 @@ void cc_worker::ue::metrics_dl(uint32_t mcs) void cc_worker::ue::metrics_ul(uint32_t mcs, float rssi, float sinr, float turbo_iters) { + if (isnan(rssi)) { + rssi = 0; + } metrics.ul.mcs = SRSRAN_VEC_CMA((float)mcs, metrics.ul.mcs, metrics.ul.n_samples); metrics.ul.pusch_sinr = SRSRAN_VEC_CMA((float)sinr, metrics.ul.pusch_sinr, metrics.ul.n_samples); - metrics.ul.rssi = SRSRAN_VEC_CMA((float)rssi, metrics.ul.rssi, metrics.ul.n_samples); + metrics.ul.pusch_rssi = SRSRAN_VEC_CMA((float)rssi, metrics.ul.pusch_rssi, metrics.ul.n_samples); metrics.ul.turbo_iters = SRSRAN_VEC_CMA((float)turbo_iters, metrics.ul.turbo_iters, metrics.ul.n_samples); metrics.ul.n_samples++; } -void cc_worker::ue::metrics_ul_pucch(float sinr) +void cc_worker::ue::metrics_ul_pucch(float rssi, float sinr) { + if (isnan(rssi)) { + rssi = 0; + } + metrics.ul.pucch_rssi = SRSRAN_VEC_CMA((float)rssi, metrics.ul.pucch_rssi, metrics.ul.n_samples_pucch); metrics.ul.pucch_sinr = SRSRAN_VEC_CMA((float)sinr, metrics.ul.pucch_sinr, metrics.ul.n_samples_pucch); metrics.ul.n_samples_pucch++; } diff --git a/srsenb/src/phy/lte/sf_worker.cc b/srsenb/src/phy/lte/sf_worker.cc index 8353d345e..05c09b32f 100644 --- a/srsenb/src/phy/lte/sf_worker.cc +++ b/srsenb/src/phy/lte/sf_worker.cc @@ -268,15 +268,17 @@ uint32_t sf_worker::get_metrics(std::vector& metrics) for (uint32_t r = 0; r < cnt; r++) { phy_metrics_t* m = &metrics[r]; phy_metrics_t* m_ = &metrics_[r]; - m->dl.mcs = SRSRAN_VEC_PMA(m->dl.mcs, m->dl.n_samples, m_->dl.mcs, m_->dl.n_samples); + m->dl.mcs = SRSRAN_VEC_SAFE_PMA(m->dl.mcs, m->dl.n_samples, m_->dl.mcs, m_->dl.n_samples); m->dl.n_samples += m_->dl.n_samples; - m->ul.n = SRSRAN_VEC_PMA(m->ul.n, m->ul.n_samples, m_->ul.n, m_->ul.n_samples); - m->ul.pusch_sinr = SRSRAN_VEC_PMA(m->ul.pusch_sinr, m->ul.n_samples, m_->ul.pusch_sinr, m_->ul.n_samples); + m->ul.n = SRSRAN_VEC_SAFE_PMA(m->ul.n, m->ul.n_samples, m_->ul.n, m_->ul.n_samples); + m->ul.pusch_sinr = SRSRAN_VEC_SAFE_PMA(m->ul.pusch_sinr, m->ul.n_samples, m_->ul.pusch_sinr, m_->ul.n_samples); m->ul.pucch_sinr = - SRSRAN_VEC_PMA(m->ul.pucch_sinr, m->ul.n_samples_pucch, m_->ul.pucch_sinr, m_->ul.n_samples_pucch); - m->ul.mcs = SRSRAN_VEC_PMA(m->ul.mcs, m->ul.n_samples, m_->ul.mcs, m_->ul.n_samples); - m->ul.rssi = SRSRAN_VEC_PMA(m->ul.rssi, m->ul.n_samples, m_->ul.rssi, m_->ul.n_samples); - m->ul.turbo_iters = SRSRAN_VEC_PMA(m->ul.turbo_iters, m->ul.n_samples, m_->ul.turbo_iters, m_->ul.n_samples); + SRSRAN_VEC_SAFE_PMA(m->ul.pucch_sinr, m->ul.n_samples_pucch, m_->ul.pucch_sinr, m_->ul.n_samples_pucch); + m->ul.mcs = SRSRAN_VEC_SAFE_PMA(m->ul.mcs, m->ul.n_samples, m_->ul.mcs, m_->ul.n_samples); + m->ul.pusch_rssi = SRSRAN_VEC_SAFE_PMA(m->ul.pusch_rssi, m->ul.n_samples, m_->ul.pusch_rssi, m_->ul.n_samples); + m->ul.pucch_rssi = + SRSRAN_VEC_SAFE_PMA(m->ul.pucch_rssi, m->ul.n_samples_pucch, m_->ul.pucch_rssi, m_->ul.n_samples_pucch); + m->ul.turbo_iters = SRSRAN_VEC_SAFE_PMA(m->ul.turbo_iters, m->ul.n_samples, m_->ul.turbo_iters, m_->ul.n_samples); m->ul.n_samples += m_->ul.n_samples; m->ul.n_samples_pucch += m_->ul.n_samples_pucch; } diff --git a/srsenb/src/phy/phy.cc b/srsenb/src/phy/phy.cc index 8a185187a..4434d53d4 100644 --- a/srsenb/src/phy/phy.cc +++ b/srsenb/src/phy/phy.cc @@ -252,20 +252,26 @@ void phy::get_metrics(std::vector& metrics) metrics[j].ul.n_samples_pucch += metrics_tmp[j].ul.n_samples_pucch; metrics[j].ul.mcs += metrics_tmp[j].ul.n_samples * metrics_tmp[j].ul.mcs; metrics[j].ul.n += metrics_tmp[j].ul.n_samples * metrics_tmp[j].ul.n; - metrics[j].ul.rssi += metrics_tmp[j].ul.n_samples * metrics_tmp[j].ul.rssi; + metrics[j].ul.pusch_rssi += metrics_tmp[j].ul.n_samples * metrics_tmp[j].ul.pusch_rssi; metrics[j].ul.pusch_sinr += metrics_tmp[j].ul.n_samples * metrics_tmp[j].ul.pusch_sinr; + metrics[j].ul.pucch_rssi += metrics_tmp[j].ul.n_samples_pucch * metrics_tmp[j].ul.pucch_rssi; metrics[j].ul.pucch_sinr += metrics_tmp[j].ul.n_samples_pucch * metrics_tmp[j].ul.pucch_sinr; metrics[j].ul.turbo_iters += metrics_tmp[j].ul.n_samples * metrics_tmp[j].ul.turbo_iters; } } for (uint32_t j = 0; j < metrics.size(); j++) { - metrics[j].dl.mcs /= metrics[j].dl.n_samples; - metrics[j].ul.mcs /= metrics[j].ul.n_samples; - metrics[j].ul.n /= metrics[j].ul.n_samples; - metrics[j].ul.rssi /= metrics[j].ul.n_samples; - metrics[j].ul.pusch_sinr /= metrics[j].ul.n_samples; - metrics[j].ul.pucch_sinr /= metrics[j].ul.n_samples_pucch; - metrics[j].ul.turbo_iters /= metrics[j].ul.n_samples; + if (metrics[j].dl.n_samples > 0) { + metrics[j].dl.mcs /= metrics[j].dl.n_samples; + } + if (metrics[j].ul.n_samples > 0) { + metrics[j].ul.mcs /= metrics[j].ul.n_samples; + metrics[j].ul.n /= metrics[j].ul.n_samples; + metrics[j].ul.pusch_rssi /= metrics[j].ul.n_samples; + metrics[j].ul.pusch_sinr /= metrics[j].ul.n_samples; + metrics[j].ul.pucch_rssi /= metrics[j].ul.n_samples_pucch; + metrics[j].ul.pucch_sinr /= metrics[j].ul.n_samples_pucch; + metrics[j].ul.turbo_iters /= metrics[j].ul.n_samples; + } } } From 2bbeef6068d7bb7d45a1feb4ba17852d3bbdfdf4 Mon Sep 17 00:00:00 2001 From: Ismael Gomez Date: Wed, 23 Feb 2022 22:09:11 +0100 Subject: [PATCH 078/195] srsgnb,metrics: add ul_pucch_ni metric and refactor rssi/epre nomenclature. Fix bug in rsrp_avg computation in pucch --- .../srsran/phy/ch_estimation/chest_ul.h | 2 +- lib/include/srsran/phy/phch/pucch.h | 3 +- lib/src/phy/ch_estimation/chest_ul.c | 44 +++++++++++-------- lib/src/phy/ch_estimation/dmrs_pucch.c | 4 +- .../phy/ch_estimation/test/chest_test_srs.c | 4 +- lib/src/phy/enb/enb_ul.c | 5 ++- lib/src/phy/gnb/gnb_ul.c | 2 +- srsenb/hdr/phy/lte/cc_worker.h | 2 +- srsenb/src/phy/lte/cc_worker.cc | 9 ++-- srsenb/src/phy/lte/sf_worker.cc | 2 + srsenb/src/phy/phy.cc | 2 + 11 files changed, 48 insertions(+), 31 deletions(-) diff --git a/lib/include/srsran/phy/ch_estimation/chest_ul.h b/lib/include/srsran/phy/ch_estimation/chest_ul.h index ade25ea34..1a0bf33bd 100644 --- a/lib/include/srsran/phy/ch_estimation/chest_ul.h +++ b/lib/include/srsran/phy/ch_estimation/chest_ul.h @@ -40,7 +40,7 @@ typedef struct SRSRAN_API { cf_t* ce; uint32_t nof_re; float noise_estimate; - float noise_estimate_dbm; + float noise_estimate_dbFs; float rsrp; float rsrp_dBfs; float epre; diff --git a/lib/include/srsran/phy/phch/pucch.h b/lib/include/srsran/phy/phch/pucch.h index 5a70a676f..20cedae17 100644 --- a/lib/include/srsran/phy/phch/pucch.h +++ b/lib/include/srsran/phy/phch/pucch.h @@ -75,7 +75,8 @@ typedef struct SRSRAN_API { srsran_uci_value_t uci_data; float dmrs_correlation; float snr_db; - float rsrp_db; + float rssi_dbFs; + float ni_dbFs; float correlation; bool detected; diff --git a/lib/src/phy/ch_estimation/chest_ul.c b/lib/src/phy/ch_estimation/chest_ul.c index 056845966..4dcd47702 100644 --- a/lib/src/phy/ch_estimation/chest_ul.c +++ b/lib/src/phy/ch_estimation/chest_ul.c @@ -346,19 +346,30 @@ static void chest_ul_estimate(srsran_chest_ul_t* q, } } - // Estimate received pilot power - float signal_power = srsran_vec_avg_power_cf(q->pilot_recv_signal, nslots * nrefs_sym); + // Measure reference signal RE average power + cf_t corr = srsran_vec_acc_cc(q->pilot_recv_signal, nslots * nrefs_sym) / (nslots * nrefs_sym); + float rsrp_avg = __real__ corr * __real__ corr + __imag__ corr * __imag__ corr; + + // Measure EPRE + float epre = srsran_vec_avg_power_cf(q->pilot_recv_signal, nslots * nrefs_sym); + + // RSRP shall not be greater than EPRE + rsrp_avg = SRSRAN_MIN(rsrp_avg, epre); + + // Calculate SNR if (isnormal(res->noise_estimate)) { - res->snr = signal_power / res->noise_estimate; + res->snr = rsrp_avg / res->noise_estimate; } else { res->snr = NAN; } - // Convert measurements in logarithm scale - res->rsrp = signal_power; - res->rsrp_dBfs = srsran_convert_power_to_dB(signal_power); - res->snr_db = srsran_convert_power_to_dB(res->snr); - res->noise_estimate_dbm = srsran_convert_power_to_dBm(res->noise_estimate); + // Set EPRE and RSRP + res->epre = epre; + res->epre_dBfs = srsran_convert_power_to_dB(res->epre); + res->rsrp = rsrp_avg; + res->rsrp_dBfs = srsran_convert_power_to_dB(res->rsrp); + res->snr_db = srsran_convert_power_to_dB(res->snr); + res->noise_estimate_dbFs = srsran_convert_power_to_dBm(res->noise_estimate); } int srsran_chest_ul_estimate_pusch(srsran_chest_ul_t* q, @@ -473,15 +484,12 @@ int srsran_chest_ul_estimate_pucch(srsran_chest_ul_t* q, srsran_vec_prod_conj_ccc(q->pilot_recv_signal, q->pilot_known_signal, q->pilot_estimates, nrefs_sf); } - // Measure power - float rsrp_avg = 0.0f; - for (int ns = 0; ns < SRSRAN_NOF_SLOTS_PER_SF; ns++) { - for (int i = 0; i < n_rs; i++) { - cf_t corr = srsran_vec_acc_cc(q->pilot_estimates, SRSRAN_NOF_SLOTS_PER_SF * SRSRAN_NRE * n_rs) / (SRSRAN_NRE); - rsrp_avg += __real__ corr * __real__ corr + __imag__ corr * __imag__ corr; - } - } - rsrp_avg /= SRSRAN_NOF_SLOTS_PER_SF * n_rs; + // Measure reference signal RE average power + cf_t corr = srsran_vec_acc_cc(q->pilot_estimates, SRSRAN_NOF_SLOTS_PER_SF * SRSRAN_NRE * n_rs) / + (SRSRAN_NOF_SLOTS_PER_SF * SRSRAN_NRE * n_rs); + float rsrp_avg = __real__ corr * __real__ corr + __imag__ corr * __imag__ corr; + + // Measure EPRE float epre = srsran_vec_avg_power_cf(q->pilot_estimates, SRSRAN_NOF_SLOTS_PER_SF * SRSRAN_NRE * n_rs); // RSRP shall not be greater than EPRE @@ -556,7 +564,7 @@ int srsran_chest_ul_estimate_pucch(srsran_chest_ul_t* q, if (fpclassify(res->noise_estimate) == FP_ZERO) { res->noise_estimate = FLT_MIN; } - res->noise_estimate_dbm = srsran_convert_power_to_dBm(res->noise_estimate); + res->noise_estimate_dbFs = srsran_convert_power_to_dBm(res->noise_estimate); // Estimate SINR if (isnormal(res->noise_estimate)) { diff --git a/lib/src/phy/ch_estimation/dmrs_pucch.c b/lib/src/phy/ch_estimation/dmrs_pucch.c index 49c34e962..3f3ad6217 100644 --- a/lib/src/phy/ch_estimation/dmrs_pucch.c +++ b/lib/src/phy/ch_estimation/dmrs_pucch.c @@ -283,7 +283,7 @@ int srsran_dmrs_pucch_format1_estimate(const srsran_pucch_nr_t* q, res->epre = epre; res->epre_dBfs = srsran_convert_power_to_dB(epre); res->noise_estimate = SRSRAN_MAX(epre - rsrp, 1e-6f); - res->noise_estimate_dbm = srsran_convert_power_to_dB(res->noise_estimate); + res->noise_estimate_dbFs = srsran_convert_power_to_dB(res->noise_estimate); res->snr = rsrp / res->noise_estimate; res->snr_db = srsran_convert_power_to_dB(res->snr); @@ -458,7 +458,7 @@ int srsran_dmrs_pucch_format2_estimate(const srsran_pucch_nr_t* q, res->epre = epre; res->epre_dBfs = srsran_convert_power_to_dB(epre); res->noise_estimate = SRSRAN_MAX(epre - rsrp, 1e-6f); - res->noise_estimate_dbm = srsran_convert_power_to_dB(res->noise_estimate); + res->noise_estimate_dbFs = srsran_convert_power_to_dB(res->noise_estimate); res->snr = rsrp / res->noise_estimate; res->snr_db = srsran_convert_power_to_dB(res->snr); diff --git a/lib/src/phy/ch_estimation/test/chest_test_srs.c b/lib/src/phy/ch_estimation/test/chest_test_srs.c index 9c489c476..649435b8b 100644 --- a/lib/src/phy/ch_estimation/test/chest_test_srs.c +++ b/lib/src/phy/ch_estimation/test/chest_test_srs.c @@ -150,12 +150,12 @@ int srs_test_context_run(srs_test_context_t* q) INFO("RESULTS: tti=%d; snr_db=%+.1f; noise_estimate_dbm=%+.1f; ta_us=%+.1f;", ul_sf_cfg.tti, q->chest_ul_res.snr_db, - q->chest_ul_res.noise_estimate_dbm, + q->chest_ul_res.noise_estimate_dbFs, q->chest_ul_res.ta_us); // Assert SRS measurements TESTASSERT(fabsf(q->chest_ul_res.snr_db - snr_db) < CHEST_TEST_SRS_SNR_DB_TOLERANCE); - TESTASSERT(fabsf(q->chest_ul_res.noise_estimate_dbm - n0_dbm) < CHEST_TEST_SRS_SNR_DB_TOLERANCE); + TESTASSERT(fabsf(q->chest_ul_res.noise_estimate_dbFs - n0_dbm) < CHEST_TEST_SRS_SNR_DB_TOLERANCE); TESTASSERT(fabsf(q->chest_ul_res.ta_us) < CHEST_TEST_SRS_TA_US_TOLERANCE); return SRSRAN_SUCCESS; diff --git a/lib/src/phy/enb/enb_ul.c b/lib/src/phy/enb/enb_ul.c index d8bffa553..9e27ee5dc 100644 --- a/lib/src/phy/enb/enb_ul.c +++ b/lib/src/phy/enb/enb_ul.c @@ -184,8 +184,9 @@ static int get_pucch(srsran_enb_ul_t* q, srsran_ul_sf_cfg_t* ul_sf, srsran_pucch ERROR("Error estimating PUCCH DMRS"); return SRSRAN_ERROR; } - pucch_res.snr_db = q->chest_res.snr_db; - pucch_res.rsrp_db = q->chest_res.rsrp_dBfs; + pucch_res.snr_db = q->chest_res.snr_db; + pucch_res.rssi_dbFs = q->chest_res.epre_dBfs; + pucch_res.ni_dbFs = q->chest_res.noise_estimate_dbFs; ret = srsran_pucch_decode(&q->pucch, ul_sf, cfg, &q->chest_res, q->sf_symbols, &pucch_res); if (ret < SRSRAN_SUCCESS) { diff --git a/lib/src/phy/gnb/gnb_ul.c b/lib/src/phy/gnb/gnb_ul.c index f3f77c4bb..71aaf3b20 100644 --- a/lib/src/phy/gnb/gnb_ul.c +++ b/lib/src/phy/gnb/gnb_ul.c @@ -308,7 +308,7 @@ int srsran_gnb_ul_get_pucch(srsran_gnb_ul_t* q, meas->epre = q->chest_pucch.epre; meas->epre_dB = q->chest_pucch.epre_dBfs; meas->n0 = q->chest_pucch.noise_estimate; - meas->n0_dB = q->chest_pucch.noise_estimate_dbm; + meas->n0_dB = q->chest_pucch.noise_estimate_dbFs; meas->snr_dB = q->chest_pucch.snr_db; meas->cfo_hz = q->chest_pucch.cfo_hz; meas->cfo_hz_max = NAN; // Unavailable diff --git a/srsenb/hdr/phy/lte/cc_worker.h b/srsenb/hdr/phy/lte/cc_worker.h index aa64e6490..e9720e9bc 100644 --- a/srsenb/hdr/phy/lte/cc_worker.h +++ b/srsenb/hdr/phy/lte/cc_worker.h @@ -100,7 +100,7 @@ private: void metrics_read(phy_metrics_t* metrics); void metrics_dl(uint32_t mcs); void metrics_ul(uint32_t mcs, float rssi, float sinr, float turbo_iters); - void metrics_ul_pucch(float rssi, float sinr); + void metrics_ul_pucch(float rssi, float ni, float sinr); uint32_t get_rnti() const { return rnti; } private: diff --git a/srsenb/src/phy/lte/cc_worker.cc b/srsenb/src/phy/lte/cc_worker.cc index 1193587da..78fcd87c9 100644 --- a/srsenb/src/phy/lte/cc_worker.cc +++ b/srsenb/src/phy/lte/cc_worker.cc @@ -364,7 +364,7 @@ bool cc_worker::decode_pusch_rnti(stack_interface_phy_lte::ul_sched_grant_t& ul_ if (ul_grant.data != nullptr) { // Save metrics stats ue_db[rnti]->metrics_ul(ul_grant.dci.tb.mcs_idx, - enb_ul.chest_res.rsrp_dBfs - phy->params.rx_gain_offset, + enb_ul.chest_res.epre_dBfs - phy->params.rx_gain_offset, enb_ul.chest_res.snr_db, pusch_res.avg_iterations_block); } @@ -455,7 +455,9 @@ int cc_worker::decode_pucch() // Save metrics if (pucch_res.detected) { - ue_db[rnti]->metrics_ul_pucch(pucch_res.rsrp_db - phy->params.rx_gain_offset, pucch_res.snr_db); + ue_db[rnti]->metrics_ul_pucch(pucch_res.rssi_dbFs - phy->params.rx_gain_offset, + pucch_res.ni_dbFs - -phy->params.rx_gain_offset, + pucch_res.snr_db); } } } @@ -681,12 +683,13 @@ void cc_worker::ue::metrics_ul(uint32_t mcs, float rssi, float sinr, float turbo metrics.ul.n_samples++; } -void cc_worker::ue::metrics_ul_pucch(float rssi, float sinr) +void cc_worker::ue::metrics_ul_pucch(float rssi, float ni, float sinr) { if (isnan(rssi)) { rssi = 0; } metrics.ul.pucch_rssi = SRSRAN_VEC_CMA((float)rssi, metrics.ul.pucch_rssi, metrics.ul.n_samples_pucch); + metrics.ul.pucch_ni = SRSRAN_VEC_CMA((float)ni, metrics.ul.pucch_ni, metrics.ul.n_samples_pucch); metrics.ul.pucch_sinr = SRSRAN_VEC_CMA((float)sinr, metrics.ul.pucch_sinr, metrics.ul.n_samples_pucch); metrics.ul.n_samples_pucch++; } diff --git a/srsenb/src/phy/lte/sf_worker.cc b/srsenb/src/phy/lte/sf_worker.cc index 05c09b32f..d8e562948 100644 --- a/srsenb/src/phy/lte/sf_worker.cc +++ b/srsenb/src/phy/lte/sf_worker.cc @@ -278,6 +278,8 @@ uint32_t sf_worker::get_metrics(std::vector& metrics) m->ul.pusch_rssi = SRSRAN_VEC_SAFE_PMA(m->ul.pusch_rssi, m->ul.n_samples, m_->ul.pusch_rssi, m_->ul.n_samples); m->ul.pucch_rssi = SRSRAN_VEC_SAFE_PMA(m->ul.pucch_rssi, m->ul.n_samples_pucch, m_->ul.pucch_rssi, m_->ul.n_samples_pucch); + m->ul.pucch_ni = + SRSRAN_VEC_SAFE_PMA(m->ul.pucch_ni, m->ul.n_samples_pucch, m_->ul.pucch_ni, m_->ul.n_samples_pucch); m->ul.turbo_iters = SRSRAN_VEC_SAFE_PMA(m->ul.turbo_iters, m->ul.n_samples, m_->ul.turbo_iters, m_->ul.n_samples); m->ul.n_samples += m_->ul.n_samples; m->ul.n_samples_pucch += m_->ul.n_samples_pucch; diff --git a/srsenb/src/phy/phy.cc b/srsenb/src/phy/phy.cc index 4434d53d4..e211b157c 100644 --- a/srsenb/src/phy/phy.cc +++ b/srsenb/src/phy/phy.cc @@ -255,6 +255,7 @@ void phy::get_metrics(std::vector& metrics) metrics[j].ul.pusch_rssi += metrics_tmp[j].ul.n_samples * metrics_tmp[j].ul.pusch_rssi; metrics[j].ul.pusch_sinr += metrics_tmp[j].ul.n_samples * metrics_tmp[j].ul.pusch_sinr; metrics[j].ul.pucch_rssi += metrics_tmp[j].ul.n_samples_pucch * metrics_tmp[j].ul.pucch_rssi; + metrics[j].ul.pucch_ni += metrics_tmp[j].ul.n_samples_pucch * metrics_tmp[j].ul.pucch_ni; metrics[j].ul.pucch_sinr += metrics_tmp[j].ul.n_samples_pucch * metrics_tmp[j].ul.pucch_sinr; metrics[j].ul.turbo_iters += metrics_tmp[j].ul.n_samples * metrics_tmp[j].ul.turbo_iters; } @@ -269,6 +270,7 @@ void phy::get_metrics(std::vector& metrics) metrics[j].ul.pusch_rssi /= metrics[j].ul.n_samples; metrics[j].ul.pusch_sinr /= metrics[j].ul.n_samples; metrics[j].ul.pucch_rssi /= metrics[j].ul.n_samples_pucch; + metrics[j].ul.pucch_ni /= metrics[j].ul.n_samples_pucch; metrics[j].ul.pucch_sinr /= metrics[j].ul.n_samples_pucch; metrics[j].ul.turbo_iters /= metrics[j].ul.n_samples; } From 65dbac0bd3557b3a9c81090659ef30d394173b5e Mon Sep 17 00:00:00 2001 From: Ismael Gomez Date: Wed, 23 Feb 2022 22:22:09 +0100 Subject: [PATCH 079/195] srsenb,metrics: use epre for snr computation instead of rsrp --- lib/src/phy/ch_estimation/chest_ul.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/phy/ch_estimation/chest_ul.c b/lib/src/phy/ch_estimation/chest_ul.c index 4dcd47702..da744089f 100644 --- a/lib/src/phy/ch_estimation/chest_ul.c +++ b/lib/src/phy/ch_estimation/chest_ul.c @@ -358,7 +358,7 @@ static void chest_ul_estimate(srsran_chest_ul_t* q, // Calculate SNR if (isnormal(res->noise_estimate)) { - res->snr = rsrp_avg / res->noise_estimate; + res->snr = epre / res->noise_estimate; } else { res->snr = NAN; } @@ -568,7 +568,7 @@ int srsran_chest_ul_estimate_pucch(srsran_chest_ul_t* q, // Estimate SINR if (isnormal(res->noise_estimate)) { - res->snr = res->rsrp / res->noise_estimate; + res->snr = res->epre / res->noise_estimate; res->snr_db = srsran_convert_power_to_dB(res->snr); } else { res->snr = NAN; From 6989d435d41feaad8444ec0fac5ef180ea7f8b41 Mon Sep 17 00:00:00 2001 From: Ismael Gomez Date: Tue, 25 Jan 2022 19:40:18 +0100 Subject: [PATCH 080/195] cmake: reduce version requirement --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2262fa370..664cadb94 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ endif(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) ######################################################################## # Project setup ######################################################################## -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.5) project( SRSRAN ) message( STATUS "CMAKE_SYSTEM: " ${CMAKE_SYSTEM} ) message( STATUS "CMAKE_SYSTEM_PROCESSOR: " ${CMAKE_SYSTEM_PROCESSOR} ) From be5c3afc51d28d5e8a80545258502af52cb9508d Mon Sep 17 00:00:00 2001 From: Ismael Gomez Date: Tue, 25 Jan 2022 19:41:10 +0100 Subject: [PATCH 081/195] radio: fix invalid mapping of multiple channels to multiple devices --- lib/src/radio/radio.cc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/src/radio/radio.cc b/lib/src/radio/radio.cc index 25b742117..7ec38258f 100644 --- a/lib/src/radio/radio.cc +++ b/lib/src/radio/radio.cc @@ -656,11 +656,11 @@ void radio::set_rx_freq(const uint32_t& carrier_idx, const double& freq) // 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, + if (device_mapping.channel_idx >= nof_channels_x_dev) { + logger.error("Invalid mapping physical channel %d to logical carrier %d on f_rx=%.1f MHz (nof_channels_x_dev=%d, device_idx=%d)", + device_mapping.channel_idx, carrier_idx, - freq / 1e6); + freq / 1e6, nof_channels_x_dev, device_mapping.device_idx); return; } @@ -675,7 +675,7 @@ 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) { + if (dm.device_idx >= rf_devices.size() or dm.channel_idx >= nof_channels_x_dev) { logger.error("Invalid port mapping %d:%d to logical carrier %d on f_rx=%.1f MHz", dm.device_idx, dm.channel_idx, @@ -795,9 +795,9 @@ void radio::set_tx_freq(const uint32_t& carrier_idx, const double& freq) // 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, + if (device_mapping.channel_idx >= nof_channels_x_dev) { + logger.error("Invalid mapping physical channel %d to logical carrier %d on f_tx=%.1f MHz", + device_mapping.channel_idx, carrier_idx, freq / 1e6); return; @@ -814,7 +814,7 @@ 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) { + if (device_mapping.device_idx >= rf_devices.size() or device_mapping.channel_idx >= nof_channels_x_dev) { logger.error("Invalid port mapping %d:%d to logical carrier %d on f_rx=%.1f MHz", device_mapping.device_idx, device_mapping.channel_idx, From 08b20ba4b2964ede8e61ea4adbeaa1b0ee8d122c Mon Sep 17 00:00:00 2001 From: Ismael Gomez Date: Tue, 25 Jan 2022 19:42:50 +0100 Subject: [PATCH 082/195] srsue,phy: change stop order to avoid locking --- srsue/src/phy/sync.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/srsue/src/phy/sync.cc b/srsue/src/phy/sync.cc index e46ae3f34..58cef2815 100644 --- a/srsue/src/phy/sync.cc +++ b/srsue/src/phy/sync.cc @@ -128,14 +128,14 @@ sync::~sync() void sync::stop() { + running = false; + wait_thread_finish(); + std::lock_guard lock(intra_freq_cfg_mutex); worker_com->semaphore.wait_all(); for (auto& q : intra_freq_meas) { q->stop(); } - running = false; - - wait_thread_finish(); // Reset (stop Rx stream) as soon as possible to avoid base-band Rx buffer overflow radio_h->reset(); From bdbfc3478b62d258abbfbc84c03e1d770e30c86d Mon Sep 17 00:00:00 2001 From: Ismael Gomez Date: Tue, 25 Jan 2022 19:44:27 +0100 Subject: [PATCH 083/195] srsue,phy: do not reestablish if configured more carriers than existing --- srsue/src/phy/phy.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srsue/src/phy/phy.cc b/srsue/src/phy/phy.cc index 3b5c7a3f9..9fca03a4c 100644 --- a/srsue/src/phy/phy.cc +++ b/srsue/src/phy/phy.cc @@ -461,7 +461,7 @@ bool phy::set_config(const srsran::phy_cfg_t& config_, uint32_t cc_idx) if (cc_idx >= args.nof_lte_carriers) { srsran::console("Received SCell configuration for index %d but there are not enough CC workers available\n", cc_idx); - return false; + return true; } Info("Setting configuration"); From 7f8ac07b65a146d4a3e1cfb5e5a1333fcb6ac790 Mon Sep 17 00:00:00 2001 From: Ismael Gomez Date: Tue, 1 Feb 2022 22:54:22 +0100 Subject: [PATCH 084/195] srsue: fix A1/S2 reports with CA. Fix some unstability issues when doing HO with multiple carriers due to frequent call to update_phy --- srsue/hdr/stack/rrc/rrc_cell.h | 7 +++++++ srsue/src/phy/sync.cc | 6 ++++-- srsue/src/stack/rrc/rrc.cc | 5 ++++- srsue/src/stack/rrc/rrc_cell.cc | 19 ++++++++++++++++++ srsue/src/stack/rrc/rrc_meas.cc | 28 +++++++++++++++++++-------- srsue/src/stack/rrc/rrc_procedures.cc | 6 ++++++ 6 files changed, 60 insertions(+), 11 deletions(-) diff --git a/srsue/hdr/stack/rrc/rrc_cell.h b/srsue/hdr/stack/rrc/rrc_cell.h index 783e3a830..d28a45f76 100644 --- a/srsue/hdr/stack/rrc/rrc_cell.h +++ b/srsue/hdr/stack/rrc/rrc_cell.h @@ -247,6 +247,10 @@ public: // serving cell handling int set_serving_cell(phy_cell_t phy_cell, bool discard_serving); + // Set serving cell and earfcn for each cc_idx + void set_scell_cc_idx(uint32_t cc_idx, uint32_t earfcn, uint32_t pci); + bool get_scell_cc_idx(uint32_t earfcn, uint32_t& pci); + T& serving_cell() { return *serv_cell; } const T& serving_cell() const { return *serv_cell; } @@ -263,6 +267,9 @@ private: unique_meas_cell serv_cell; std::vector neighbour_cells; + + // store serving pci and earfcn for each carrier + std::array, SRSRAN_MAX_CARRIERS> current_cell_pci_earfcn = {}; }; } // namespace srsue diff --git a/srsue/src/phy/sync.cc b/srsue/src/phy/sync.cc index 58cef2815..0ceb998e3 100644 --- a/srsue/src/phy/sync.cc +++ b/srsue/src/phy/sync.cc @@ -129,16 +129,18 @@ sync::~sync() void sync::stop() { running = false; - wait_thread_finish(); std::lock_guard lock(intra_freq_cfg_mutex); - worker_com->semaphore.wait_all(); for (auto& q : intra_freq_meas) { q->stop(); } // Reset (stop Rx stream) as soon as possible to avoid base-band Rx buffer overflow radio_h->reset(); + + // let the sync FSM finish before waiting for workers semaphores to avoid workers locking + wait_thread_finish(); + worker_com->semaphore.wait_all(); } void sync::reset() diff --git a/srsue/src/stack/rrc/rrc.cc b/srsue/src/stack/rrc/rrc.cc index 481c62501..64ababa23 100644 --- a/srsue/src/stack/rrc/rrc.cc +++ b/srsue/src/stack/rrc/rrc.cc @@ -457,7 +457,8 @@ void rrc::process_new_cell_meas(const std::vector& meas) bool neighbour_added = meas_cells.process_new_cell_meas(meas, filter); // Instruct measurements subclass to update phy with new cells to measure based on strongest neighbours - if (state == RRC_STATE_CONNECTED && neighbour_added) { + // Avoid updating PHY while HO procedure is busy + if (state == RRC_STATE_CONNECTED && neighbour_added && !ho_handler.is_busy()) { measurements->update_phy(); } } @@ -2482,6 +2483,8 @@ void rrc::apply_phy_scell_config(const scell_to_add_mod_r10_s& scell_config, boo logger.error("Adding SCell cc_idx=%d", scell_config.scell_idx_r10); } else if (!phy_ctrl->set_cell_config(scell_cfg, scell_config.scell_idx_r10)) { logger.error("Setting SCell configuration for cc_idx=%d", scell_config.scell_idx_r10); + } else { + meas_cells.set_scell_cc_idx(scell_config.scell_idx_r10, earfcn, scell.id); } } diff --git a/srsue/src/stack/rrc/rrc_cell.cc b/srsue/src/stack/rrc/rrc_cell.cc index cdd0b6693..94410f0d7 100644 --- a/srsue/src/stack/rrc/rrc_cell.cc +++ b/srsue/src/stack/rrc/rrc_cell.cc @@ -434,6 +434,25 @@ int meas_cell_list::set_serving_cell(phy_cell_t phy_cell, bool discard_servin return SRSRAN_SUCCESS; } +template +void meas_cell_list::set_scell_cc_idx(uint32_t cc_idx, uint32_t earfcn, uint32_t pci) +{ + current_cell_pci_earfcn[cc_idx].first = earfcn; + current_cell_pci_earfcn[cc_idx].second = pci; +} + +template +bool meas_cell_list::get_scell_cc_idx(uint32_t earfcn, uint32_t& pci) +{ + for (auto& cell : current_cell_pci_earfcn) { + if (cell.first == earfcn) { + pci = cell.second; + return true; + } + } + return false; +} + template bool meas_cell_list::process_new_cell_meas(const std::vector& meas, const std::function& filter_meas) diff --git a/srsue/src/stack/rrc/rrc_meas.cc b/srsue/src/stack/rrc/rrc_meas.cc index f9926e89d..985dbf904 100644 --- a/srsue/src/stack/rrc/rrc_meas.cc +++ b/srsue/src/stack/rrc/rrc_meas.cc @@ -118,7 +118,6 @@ bool rrc::rrc_meas::parse_meas_config(const rrc_conn_recfg_r8_ies_s* mob_reconf_ void rrc::rrc_meas::ho_reest_actions(const uint32_t src_earfcn, const uint32_t dst_earfcn) { meas_cfg.ho_reest_finish(src_earfcn, dst_earfcn); - update_phy(); } void rrc::rrc_meas::run_tti() @@ -224,12 +223,10 @@ void rrc::rrc_meas::var_meas_report_list::generate_report_eutra(meas_results_s* for (auto& cell : var_meas.cell_triggered_list) { // report neighbour cells only if (cell.pci == serv_cell->get_pci() && cell.earfcn == serv_cell->get_earfcn()) { - logger.info("MEAS: skipping serving cell in report neighbour=%d, pci=%d, earfcn=%d, rsrp=%+.1f, rsrq=%+.1f", + logger.info("MEAS: skipping serving cell in report neighbour=%d, pci=%d, earfcn=%d", neigh_list.size(), cell.pci, - var_meas.carrier_freq, - rrc_ptr->get_cell_rsrp(var_meas.carrier_freq, cell.pci), - rrc_ptr->get_cell_rsrq(var_meas.carrier_freq, cell.pci)); + var_meas.carrier_freq); continue; } if (neigh_list.size() <= var_meas.report_cfg_eutra.max_report_cells) { @@ -767,16 +764,31 @@ void rrc::rrc_meas::var_meas_cfg::eval_triggers_eutra(uint32_t meas_i } }; + eutra_event_s::event_id_c_ event_id = report_cfg.trigger_type.event().event_id; + + // For A1/A2 events, get serving cell from current carrier + if (event_id.type().value < eutra_event_s::event_id_c_::types::event_a3 && + meas_obj.carrier_freq != serv_cell->get_earfcn()) { + uint32_t scell_pci = 0; + if (!rrc_ptr->meas_cells.get_scell_cc_idx(meas_obj.carrier_freq, scell_pci)) { + logger.error("MEAS: Could not find serving cell for carrier earfcn=%d", meas_obj.carrier_freq); + return; + } + serv_cell = rrc_ptr->meas_cells.get_neighbour_cell_handle(meas_obj.carrier_freq, scell_pci); + if (!serv_cell) { + logger.error( + "MEAS: Could not find serving cell for carrier earfcn=%d and pci=%d", meas_obj.carrier_freq, scell_pci); + return; + } + } + double hyst = 0.5 * report_cfg.trigger_type.event().hysteresis; float Ms = is_rsrp(report_cfg.trigger_quant.value) ? serv_cell->get_rsrp() : serv_cell->get_rsrq(); - if (!std::isnormal(Ms)) { logger.debug("MEAS: Serving cell Ms=%f invalid when evaluating triggers", Ms); return; } - eutra_event_s::event_id_c_ event_id = report_cfg.trigger_type.event().event_id; - if (report_cfg.trigger_type.type() == report_cfg_eutra_s::trigger_type_c_::types::event) { // A1 & A2 are for serving cell only if (event_id.type().value < eutra_event_s::event_id_c_::types::event_a3) { diff --git a/srsue/src/stack/rrc/rrc_procedures.cc b/srsue/src/stack/rrc/rrc_procedures.cc index 23cf181ad..8b954370f 100644 --- a/srsue/src/stack/rrc/rrc_procedures.cc +++ b/srsue/src/stack/rrc/rrc_procedures.cc @@ -1555,6 +1555,8 @@ srsran::proc_outcome_t rrc::connection_reest_proc::react(const asn1::rrc::rrc_co // 1> perform the measurement related actions as specified in 5.5.6.1; rrc_ptr->measurements->ho_reest_actions(rrc_ptr->get_serving_cell()->get_earfcn(), rrc_ptr->get_serving_cell()->get_earfcn()); + // Update PHY measurements after HO Reestablishment actions. + rrc_ptr->measurements->update_phy(); // 1> submit the RRCConnectionReestablishmentComplete message to lower layers for transmission, upon which the // procedure ends; @@ -1738,6 +1740,8 @@ srsran::proc_outcome_t rrc::ho_proc::init(const asn1::rrc::rrc_conn_recfg_s& rrc // perform the measurement related actions as specified in 5.5.6.1; rrc_ptr->measurements->ho_reest_actions(ho_src_cell.earfcn, target_earfcn); + // Do not update PHY measurements here since it will be updated after the HO procedure finishes + // Note: We delay the enqueuing of RRC Reconf Complete message to avoid that the message goes in an UL grant // directed at the old RNTI. rrc_ptr->task_sched.defer_callback(4, [this]() { @@ -1796,6 +1800,8 @@ void rrc::ho_proc::then(const srsran::proc_state_t& result) srsran::console("HO %ssuccessful\n", result.is_success() ? "" : "un"); rrc_ptr->t304.stop(); + + rrc_ptr->measurements->update_phy(); } } // namespace srsue From 12435eae7def92d1c54a88bf8ecf132b2eac456d Mon Sep 17 00:00:00 2001 From: Ismael Gomez Date: Mon, 14 Mar 2022 09:43:39 +0100 Subject: [PATCH 085/195] srsue,phy: avoid changing the sampling rate while sync thread is already receiving --- srsue/src/phy/phy_nr_sa.cc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/srsue/src/phy/phy_nr_sa.cc b/srsue/src/phy/phy_nr_sa.cc index efd480aa5..435bbad28 100644 --- a/srsue/src/phy/phy_nr_sa.cc +++ b/srsue/src/phy/phy_nr_sa.cc @@ -103,10 +103,6 @@ void phy_nr_sa::init_background() } workers.init(args, sync, stack); - // Set fix Tx and Rx sampling rates - radio->set_tx_srate(args.srate_hz); - radio->set_rx_srate(args.srate_hz); - is_configured = true; } From 496b8e2748f02f28bf65e05263039c5faaccfe0a Mon Sep 17 00:00:00 2001 From: Ismael Gomez Date: Mon, 14 Mar 2022 09:44:37 +0100 Subject: [PATCH 086/195] srsue,mac: do not destroy active dl_harq_proc during a reconfiguration --- srsue/src/stack/mac_nr/dl_harq_nr.cc | 32 ++++++++++++++++++---------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/srsue/src/stack/mac_nr/dl_harq_nr.cc b/srsue/src/stack/mac_nr/dl_harq_nr.cc index 174fdf26f..377269057 100644 --- a/srsue/src/stack/mac_nr/dl_harq_nr.cc +++ b/srsue/src/stack/mac_nr/dl_harq_nr.cc @@ -26,6 +26,14 @@ dl_harq_entity_nr::dl_harq_entity_nr(uint8_t cc_idx_, // Init broadcast HARQ process bcch_proc.init(-1); pthread_rwlock_init(&rwlock, NULL); + + // Create default number of processes + for (uint32_t i = 0; i < cfg.nof_procs; i++) { + harq_procs[i] = std::unique_ptr(new dl_harq_process_nr(this)); + if (!harq_procs.at(i)->init(i)) { + logger.error("Error while initializing DL-HARQ process %d", i); + } + } } dl_harq_entity_nr::~dl_harq_entity_nr() @@ -42,17 +50,19 @@ int32_t dl_harq_entity_nr::set_config(const srsran::dl_harq_cfg_nr_t& cfg_) return SRSRAN_ERROR; } - // clear old processees - for (auto& proc : harq_procs) { - proc = nullptr; - } - - // Allocate and init configured HARQ processes - for (uint32_t i = 0; i < cfg.nof_procs; i++) { - harq_procs[i] = std::unique_ptr(new dl_harq_process_nr(this)); - if (!harq_procs.at(i)->init(i)) { - logger.error("Error while initializing DL-HARQ process %d", i); - return SRSRAN_ERROR; + if (cfg_.nof_procs < cfg.nof_procs) { + // clear old processes if not needed + for (uint32_t i = cfg.nof_procs - 1; i < cfg_.nof_procs; i++) { + harq_procs[i] = nullptr; + } + } else { + // Add new processes + for (uint32_t i = cfg.nof_procs; i < cfg_.nof_procs; i++) { + harq_procs[i] = std::unique_ptr(new dl_harq_process_nr(this)); + if (!harq_procs.at(i)->init(i)) { + logger.error("Error while initializing DL-HARQ process %d", i); + return SRSRAN_ERROR; + } } } From 7bb6aa1f50b9eee1d5ba49f2a59ea789266c3d93 Mon Sep 17 00:00:00 2001 From: Ismael Gomez Date: Mon, 14 Mar 2022 09:45:04 +0100 Subject: [PATCH 087/195] srsue,mac: do not add sbsr_ce on Msg3 --- srsue/src/stack/mac_nr/mux_nr.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srsue/src/stack/mac_nr/mux_nr.cc b/srsue/src/stack/mac_nr/mux_nr.cc index 78dc7b090..38d456619 100644 --- a/srsue/src/stack/mac_nr/mux_nr.cc +++ b/srsue/src/stack/mac_nr/mux_nr.cc @@ -76,7 +76,7 @@ srsran::unique_byte_buffer_t mux_nr::get_pdu(uint32_t max_pdu_len) // Pack normal UL data PDU int32_t remaining_len = tx_pdu.get_remaing_len(); // local variable to reserve space for CEs - if (add_bsr_ce == sbsr_ce) { + if (!msg3_is_pending() && add_bsr_ce == sbsr_ce) { // reserve space for SBSR remaining_len -= 2; } From f83557b8913aede6ec316cba82dc32df8697e423 Mon Sep 17 00:00:00 2001 From: Ismael Gomez Date: Mon, 14 Mar 2022 09:46:05 +0100 Subject: [PATCH 088/195] srsue,phy: use common pucch for msg3. Completed table for all pucch_common_idx values --- lib/include/srsran/phy/phch/ra_ul_nr.h | 2 + lib/src/common/phy_cfg_nr.cc | 2 +- lib/src/phy/phch/ra_ul_nr.c | 95 +++++++++++++++++++++----- srsue/src/phy/nr/cc_worker.cc | 17 +---- 4 files changed, 81 insertions(+), 35 deletions(-) diff --git a/lib/include/srsran/phy/phch/ra_ul_nr.h b/lib/include/srsran/phy/phch/ra_ul_nr.h index 958579257..b069b0734 100644 --- a/lib/include/srsran/phy/phch/ra_ul_nr.h +++ b/lib/include/srsran/phy/phch/ra_ul_nr.h @@ -119,11 +119,13 @@ SRSRAN_API int srsran_ra_ul_nr_freq(const srsran_carrier_nr_t* carrier, * @brief Selects a valid PUCCH resource for transmission * @param pucch_cfg PUCCH configuration from upper layers * @param uci_cfg Uplink Control information configuration (and PDCCH context) + * @param N_bwp_sz Uplink BWP size in nof_prb * @param[out] resource Selected resource for transmitting PUCCH * @return SRSRAN_SUCCESS if provided configuration is valid, SRSRAN_ERROR code otherwise */ SRSRAN_API int srsran_ra_ul_nr_pucch_resource(const srsran_pucch_nr_hl_cfg_t* pucch_cfg, const srsran_uci_cfg_nr_t* uci_cfg, + uint32_t N_bwp_sz, srsran_pucch_nr_resource_t* resource); /** diff --git a/lib/src/common/phy_cfg_nr.cc b/lib/src/common/phy_cfg_nr.cc index 98433d21f..fac74521d 100644 --- a/lib/src/common/phy_cfg_nr.cc +++ b/lib/src/common/phy_cfg_nr.cc @@ -321,7 +321,7 @@ bool phy_cfg_nr_t::get_pucch_uci_cfg(const srsran_slot_cfg_t& slot_cfg, srsran_pucch_nr_resource_t& resource) const { // Select PUCCH resource - if (srsran_ra_ul_nr_pucch_resource(&pucch, &uci_cfg, &resource) < SRSRAN_SUCCESS) { + if (srsran_ra_ul_nr_pucch_resource(&pucch, &uci_cfg, carrier.nof_prb, &resource) < SRSRAN_SUCCESS) { ERROR("Selecting PUCCH resource"); return false; } diff --git a/lib/src/phy/phch/ra_ul_nr.c b/lib/src/phy/phch/ra_ul_nr.c index 66567301e..2aaeb660a 100644 --- a/lib/src/phy/phch/ra_ul_nr.c +++ b/lib/src/phy/phch/ra_ul_nr.c @@ -470,31 +470,89 @@ int srsran_ra_ul_nr_freq(const srsran_carrier_nr_t* carrier, return SRSRAN_ERROR; } +typedef struct { + srsran_pucch_nr_format_t format; + uint32_t start_symbol; + uint32_t rb_offset; + uint32_t N_cs; +} pucch_res_common_cfg_t; + +// Table 9.2.1-1 +#define PUCCH_RES_COMMON_CFG_IDX_MAX 15 +static pucch_res_common_cfg_t pucch_res_common_cfg[PUCCH_RES_COMMON_CFG_IDX_MAX + 1] = { + {SRSRAN_PUCCH_NR_FORMAT_0, 12, 0, 2}, + {SRSRAN_PUCCH_NR_FORMAT_0, 12, 0, 3}, + {SRSRAN_PUCCH_NR_FORMAT_0, 12, 3, 3}, + {SRSRAN_PUCCH_NR_FORMAT_1, 10, 0, 2}, + {SRSRAN_PUCCH_NR_FORMAT_1, 10, 0, 3}, + {SRSRAN_PUCCH_NR_FORMAT_1, 10, 2, 3}, + {SRSRAN_PUCCH_NR_FORMAT_1, 10, 4, 3}, + {SRSRAN_PUCCH_NR_FORMAT_1, 4, 0, 2}, + {SRSRAN_PUCCH_NR_FORMAT_1, 4, 0, 3}, + {SRSRAN_PUCCH_NR_FORMAT_1, 4, 2, 3}, + {SRSRAN_PUCCH_NR_FORMAT_1, 4, 4, 3}, + {SRSRAN_PUCCH_NR_FORMAT_1, 0, 0, 2}, + {SRSRAN_PUCCH_NR_FORMAT_1, 0, 0, 4}, + {SRSRAN_PUCCH_NR_FORMAT_1, 0, 2, 4}, + {SRSRAN_PUCCH_NR_FORMAT_1, 0, 4, 4}, + {SRSRAN_PUCCH_NR_FORMAT_1, 0, 0, 4}}; + // Implements TS 38.213 Table 9.2.1-1: PUCCH resource sets before dedicated PUCCH resource configuration -static int ra_ul_nr_pucch_resource_default(uint32_t r_pucch, srsran_pucch_nr_resource_t* resource) +static int ra_ul_nr_pucch_resource_default(uint32_t pucch_res_common_idx, + uint32_t N_size_bwp, + uint32_t r_pucch, + srsran_pucch_nr_resource_t* resource) { - // From BWP config - uint32_t N_size_bwp = 52; + if (pucch_res_common_idx > PUCCH_RES_COMMON_CFG_IDX_MAX) { + ERROR("Invalid pucch_res_common_idx value (%d)\n", pucch_res_common_idx); + return SRSRAN_ERROR; + } - // From row of index 11 - uint32_t rb_offset_bwp = 0; - uint32_t N_cs = 2; - uint32_t cs_v[2] = {0, 6}; + uint32_t cs_v_2[2] = {0, 6}; + uint32_t cs_v_3[3] = {0, 4, 8}; + uint32_t cs_v_4[4] = {0, 3, 6, 9}; - resource->start_symbol_idx = 0; - resource->nof_symbols = 14; + // Table 9.2.1-1 + resource->format = pucch_res_common_cfg[pucch_res_common_idx].format; + resource->start_symbol_idx = pucch_res_common_cfg[pucch_res_common_idx].start_symbol; + resource->nof_symbols = 14 - resource->start_symbol_idx; + uint32_t rb_offset_bwp = pucch_res_common_cfg[pucch_res_common_idx].rb_offset; + uint32_t N_cs = pucch_res_common_cfg[pucch_res_common_idx].N_cs; + + // Special case for cs_v_2 value + if (pucch_res_common_idx == 0) { + cs_v_2[1] = 3; + } + + // Special case for rb_offset + if (pucch_res_common_idx == 15) { + rb_offset_bwp = N_size_bwp / 4; + } + uint32_t csv_idx = 0; if (r_pucch / 8 == 0) { - resource->starting_prb = rb_offset_bwp + SRSRAN_FLOOR(r_pucch, N_cs); - resource->second_hop_prb = N_size_bwp - 1 - resource->starting_prb; - resource->initial_cyclic_shift = cs_v[r_pucch % N_cs]; + resource->starting_prb = rb_offset_bwp + SRSRAN_FLOOR(r_pucch, N_cs); + resource->second_hop_prb = N_size_bwp - 1 - resource->starting_prb; + csv_idx = r_pucch % N_cs; } else { - resource->second_hop_prb = rb_offset_bwp + SRSRAN_FLOOR(r_pucch - 8, N_cs); - resource->starting_prb = N_size_bwp - 1 - resource->second_hop_prb; - resource->initial_cyclic_shift = cs_v[(r_pucch - 8) % N_cs]; + resource->second_hop_prb = rb_offset_bwp + SRSRAN_FLOOR(r_pucch - 8, N_cs); + resource->starting_prb = N_size_bwp - 1 - resource->second_hop_prb; + csv_idx = (r_pucch - 8) % N_cs; } - return SRSRAN_ERROR; + switch (N_cs) { + case 2: + resource->initial_cyclic_shift = cs_v_2[csv_idx]; + break; + case 3: + resource->initial_cyclic_shift = cs_v_3[csv_idx]; + break; + case 4: + resource->initial_cyclic_shift = cs_v_4[csv_idx]; + break; + } + + return SRSRAN_SUCCESS; } static int ra_ul_nr_pucch_resource_hl(const srsran_pucch_nr_hl_cfg_t* cfg, @@ -542,6 +600,7 @@ static int ra_ul_nr_pucch_resource_hl(const srsran_pucch_nr_hl_cfg_t* cfg, int srsran_ra_ul_nr_pucch_resource(const srsran_pucch_nr_hl_cfg_t* pucch_cfg, const srsran_uci_cfg_nr_t* uci_cfg, + uint32_t N_bwp_sz, srsran_pucch_nr_resource_t* resource) { if (pucch_cfg == NULL || uci_cfg == NULL || resource == NULL) { @@ -643,9 +702,9 @@ int srsran_ra_ul_nr_pucch_resource(const srsran_pucch_nr_hl_cfg_t* pucch_cfg, // a PUCCH resource set is provided by pucch-ResourceCommon through an index to a row of Table 9.2.1-1 for size // transmission of HARQ-ACK information on PUCCH in an initial UL BWP of N BWP PRBs. if (!pucch_cfg->enabled) { - uint32_t N_cce = SRSRAN_FLOOR(52, 6); + uint32_t N_cce = SRSRAN_FLOOR(N_bwp_sz, 6); uint32_t r_pucch = ((2 * uci_cfg->pucch.n_cce_0) / N_cce) + 2 * uci_cfg->pucch.resource_id; - return ra_ul_nr_pucch_resource_default(r_pucch, resource); + return ra_ul_nr_pucch_resource_default(pucch_cfg->common.resource_common, N_bwp_sz, r_pucch, resource); } return ra_ul_nr_pucch_resource_hl(pucch_cfg, uci_cfg, uci_cfg->pucch.resource_id, resource); } diff --git a/srsue/src/phy/nr/cc_worker.cc b/srsue/src/phy/nr/cc_worker.cc index c8b3f68e9..99ee6f95b 100644 --- a/srsue/src/phy/nr/cc_worker.cc +++ b/srsue/src/phy/nr/cc_worker.cc @@ -669,24 +669,9 @@ bool cc_worker::work_ul() phy.set_ul_metrics(ul_m); } else if (srsran_uci_nr_total_bits(&uci_data.cfg) > 0) { - // Currently, default PUCCH is not supported, in this case log it and pretend no UCI was available - if (not cfg.pucch.enabled) { - if (logger.info.enabled()) { - std::array str; - srsran_uci_nr_info(&uci_data, str.data(), str.size()); - logger.info( - "PUCCH: No PUCCH resource to transmit UCI cc=%d, %s, tti_tx=%d", cc_idx, str.data(), ul_slot_cfg.idx); - } - - // No NR signal shall be transmitted - srsran_vec_cf_zero(tx_buffer[0], ue_ul.ifft.sf_sz); - - return true; - } - // Get PUCCH resource srsran_pucch_nr_resource_t resource = {}; - if (srsran_ra_ul_nr_pucch_resource(&cfg.pucch, &uci_data.cfg, &resource) < SRSRAN_SUCCESS) { + if (srsran_ra_ul_nr_pucch_resource(&cfg.pucch, &uci_data.cfg, cfg.carrier.nof_prb, &resource) < SRSRAN_SUCCESS) { ERROR("Selecting PUCCH resource"); return false; } From a4f26fa5d6a5058b8883136227897ef7b5881c45 Mon Sep 17 00:00:00 2001 From: Ismael Gomez Date: Wed, 16 Mar 2022 17:45:31 +0100 Subject: [PATCH 089/195] srsue,rrc: send precanned UECapabilityInformation --- srsue/hdr/stack/rrc_nr/rrc_nr.h | 1 + srsue/src/stack/rrc_nr/rrc_nr.cc | 31 ++++++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/srsue/hdr/stack/rrc_nr/rrc_nr.h b/srsue/hdr/stack/rrc_nr/rrc_nr.h index 8696b87fa..e7b230f70 100644 --- a/srsue/hdr/stack/rrc_nr/rrc_nr.h +++ b/srsue/hdr/stack/rrc_nr/rrc_nr.h @@ -149,6 +149,7 @@ private: void handle_dl_info_transfer(const asn1::rrc_nr::dl_info_transfer_s& dl_info_transfer); void handle_security_mode_command(const asn1::rrc_nr::security_mode_cmd_s& smc); void handle_rrc_release(const asn1::rrc_nr::rrc_release_s& rrc_release); + void handle_ue_cap_enquiry(const asn1::rrc_nr::ue_cap_enquiry_s& ue_cap_enquiry); void generate_as_keys(); srsran::task_sched_handle task_sched; diff --git a/srsue/src/stack/rrc_nr/rrc_nr.cc b/srsue/src/stack/rrc_nr/rrc_nr.cc index 107b68af8..f3414bb15 100644 --- a/srsue/src/stack/rrc_nr/rrc_nr.cc +++ b/srsue/src/stack/rrc_nr/rrc_nr.cc @@ -324,6 +324,11 @@ void rrc_nr::decode_dl_dcch(uint32_t lcid, unique_byte_buffer_t pdu) task_sched.defer_task([this, rrc_release]() { handle_rrc_release(rrc_release); }); break; } + case dl_dcch_msg_type_c::c1_c_::types::ue_cap_enquiry: { + ue_cap_enquiry_s rrc_cap_enquiry = c1->ue_cap_enquiry(); + task_sched.defer_task([this, rrc_cap_enquiry]() { handle_ue_cap_enquiry(rrc_cap_enquiry); }); + break; + } default: logger.error("The provided DL-DCCH message type is not recognized or supported."); break; @@ -1894,7 +1899,7 @@ bool rrc_nr::apply_drb_add_mod(const drb_to_add_mod_s& drb_cfg) if (sdap_cfg.sdap_hdr_dl == asn1::rrc_nr::sdap_cfg_s::sdap_hdr_dl_opts::present || sdap_cfg.sdap_hdr_ul == asn1::rrc_nr::sdap_cfg_s::sdap_hdr_ul_opts::present) { logger.error("SDAP currently not supported."); - return false; + // return false; } // TODO: configure SDAP accordingly uint32_t pdu_session_id = drb_cfg.cn_assoc.sdap_cfg().pdu_session; @@ -2085,6 +2090,30 @@ void rrc_nr::handle_rrc_release(const asn1::rrc_nr::rrc_release_s& rrc_release) logger.info("RRC Release not handled yet"); } +void rrc_nr::handle_ue_cap_enquiry(const asn1::rrc_nr::ue_cap_enquiry_s& ue_cap_enquiry) +{ + transaction_id = ue_cap_enquiry.rrc_transaction_id; + + logger.info("Received UECapabilityEnquiry"); + + // Send UECapabilityInformation + ul_dcch_msg_s ul_dcch_msg; + auto& ue_cap_info = ul_dcch_msg.msg.set_c1().set_ue_cap_info().crit_exts.set_ue_cap_info(); + + ue_cap_info.ue_cap_rat_container_list_present = true; + ue_cap_rat_container_s nr_cap = {}; + + nr_cap.rat_type = rat_type_e::nr; + nr_cap.ue_cap_rat_container.from_string( + "E1A01000074F5A03020000C0A0241262C001206A0609B00C39F30C7942C0E098040623809506C4DD608D21A08107CA01165B262A87813E43" + "9F40CF88E3C639F30C7942C0E070F09C0013C0070004F0001601C00140A836036B04690D04083E500892D931541439F11C78C73E618F2858" + "1C0E1E04FE0000003F80000000A00E05"); + + ue_cap_info.ue_cap_rat_container_list.push_back(nr_cap); + + send_ul_dcch_msg(srb_to_lcid(nr_srb::srb1), ul_dcch_msg); +} + // Security helper used by Security Mode Command and Mobility handling routines void rrc_nr::generate_as_keys() { From bfe69deccc8aea25fb40c71202ee0b15b38f4bcc Mon Sep 17 00:00:00 2001 From: Ismael Gomez Date: Wed, 23 Mar 2022 13:07:24 +0100 Subject: [PATCH 090/195] srsue, nr: Add SDAP to the UE. Supports only UL header --- .../srsran/interfaces/ue_pdcp_interfaces.h | 7 ++ .../srsran/interfaces/ue_sdap_interfaces.h | 47 ++++++++++++ srsue/hdr/stack/rrc_nr/rrc_nr.h | 3 + srsue/hdr/stack/ue_stack_lte.h | 27 +++++++ srsue/hdr/stack/ue_stack_nr.h | 2 + srsue/hdr/stack/upper/sdap.h | 56 ++++++++++++++ srsue/src/stack/rrc_nr/rrc_nr.cc | 18 +++-- srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc | 21 +++++ srsue/src/stack/ue_stack_lte.cc | 18 ++++- srsue/src/stack/ue_stack_nr.cc | 1 + srsue/src/stack/upper/CMakeLists.txt | 2 +- srsue/src/stack/upper/sdap.cc | 76 +++++++++++++++++++ 12 files changed, 270 insertions(+), 8 deletions(-) create mode 100644 lib/include/srsran/interfaces/ue_sdap_interfaces.h create mode 100644 srsue/hdr/stack/upper/sdap.h create mode 100644 srsue/src/stack/upper/sdap.cc diff --git a/lib/include/srsran/interfaces/ue_pdcp_interfaces.h b/lib/include/srsran/interfaces/ue_pdcp_interfaces.h index d6e1f8c67..2560d0b73 100644 --- a/lib/include/srsran/interfaces/ue_pdcp_interfaces.h +++ b/lib/include/srsran/interfaces/ue_pdcp_interfaces.h @@ -59,6 +59,13 @@ public: virtual bool is_lcid_enabled(uint32_t lcid) = 0; }; +// SDAP interface +class pdcp_interface_sdap_nr +{ +public: + virtual void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0; +}; + // STACK interface for GW (based on EPS-bearer IDs) class stack_interface_gw { diff --git a/lib/include/srsran/interfaces/ue_sdap_interfaces.h b/lib/include/srsran/interfaces/ue_sdap_interfaces.h new file mode 100644 index 000000000..25b3179c6 --- /dev/null +++ b/lib/include/srsran/interfaces/ue_sdap_interfaces.h @@ -0,0 +1,47 @@ +/** + * + * \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. + * + */ + +/****************************************************************************** + * File: ue_sdap_interfaces.h + * Description: Abstract base class interfaces for SDAP layer + *****************************************************************************/ + +#ifndef SRSRAN_UE_SDAP_INTERFACES_H +#define SRSRAN_UE_SDAP_INTERFACES_H + +/***************************** + * SDAP INTERFACES + ****************************/ +class sdap_interface_pdcp_nr +{ +public: + virtual void write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0; +}; +class sdap_interface_gw_nr +{ +public: + virtual void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0; +}; + +class sdap_interface_rrc +{ +public: + struct bearer_cfg_t { + bool is_data; + bool add_downlink_header; + bool add_uplink_header; + uint32_t qfi; + }; + virtual bool set_bearer_cfg(uint32_t lcid, const bearer_cfg_t& cfg) = 0; +}; + +#endif // SRSRAN_UE_SDAP_INTERFACES_H diff --git a/srsue/hdr/stack/rrc_nr/rrc_nr.h b/srsue/hdr/stack/rrc_nr/rrc_nr.h index e7b230f70..56aa29997 100644 --- a/srsue/hdr/stack/rrc_nr/rrc_nr.h +++ b/srsue/hdr/stack/rrc_nr/rrc_nr.h @@ -28,6 +28,7 @@ #include "srsran/interfaces/ue_nas_interfaces.h" #include "srsran/interfaces/ue_nr_interfaces.h" #include "srsran/interfaces/ue_rrc_interfaces.h" +#include "srsran/interfaces/ue_sdap_interfaces.h" #include "srsue/hdr/stack/upper/gw.h" namespace srsue { @@ -52,6 +53,7 @@ public: mac_interface_rrc_nr* mac_, rlc_interface_rrc* rlc_, pdcp_interface_rrc* pdcp_, + sdap_interface_rrc* sdap_, gw_interface_rrc* gw_, nas_5g_interface_rrc_nr* nas_, rrc_eutra_interface_rrc_nr* rrc_eutra_, @@ -170,6 +172,7 @@ private: mac_interface_rrc_nr* mac = nullptr; rlc_interface_rrc* rlc = nullptr; pdcp_interface_rrc* pdcp = nullptr; + sdap_interface_rrc* sdap = nullptr; gw_interface_rrc* gw = nullptr; nas_5g_interface_rrc_nr* nas = nullptr; rrc_eutra_interface_rrc_nr* rrc_eutra = nullptr; diff --git a/srsue/hdr/stack/ue_stack_lte.h b/srsue/hdr/stack/ue_stack_lte.h index e48e94ead..461866765 100644 --- a/srsue/hdr/stack/ue_stack_lte.h +++ b/srsue/hdr/stack/ue_stack_lte.h @@ -36,6 +36,7 @@ #include "ue_stack_base.h" #include "upper/nas.h" #include "upper/nas_5g.h" +#include "upper/sdap.h" #include "upper/usim.h" #include #include @@ -46,6 +47,28 @@ namespace srsue { class phy_interface_stack_lte; +class sdap_pdcp_adapter : public pdcp_interface_sdap_nr, public gw_interface_pdcp +{ +public: + sdap_pdcp_adapter(pdcp* parent_pdcp_, sdap* parent_sdap_) : parent_pdcp(parent_pdcp_), parent_sdap(parent_sdap_) {} + void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) final + { + parent_pdcp->write_sdu(lcid, std::move(pdu)); + } + void write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) final + { + parent_sdap->write_pdu(lcid, std::move(pdu)); + } + void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) final + { + // not implemented + } + +private: + pdcp* parent_pdcp; + sdap* parent_sdap; +}; + class ue_stack_lte final : public ue_stack_base, public stack_interface_phy_lte, public stack_interface_phy_nr, @@ -231,6 +254,10 @@ private: srsue::nas_5g nas_5g; std::unique_ptr usim; + // SDAP only applies to NR + srsue::sdap sdap; + sdap_pdcp_adapter sdap_pdcp; + ue_bearer_manager bearers; // helper to manage mapping between EPS and radio bearers // Metrics helper diff --git a/srsue/hdr/stack/ue_stack_nr.h b/srsue/hdr/stack/ue_stack_nr.h index 732460dec..367b142c6 100644 --- a/srsue/hdr/stack/ue_stack_nr.h +++ b/srsue/hdr/stack/ue_stack_nr.h @@ -23,6 +23,7 @@ #include "srsran/rlc/rlc.h" #include "srsran/upper/pdcp.h" #include "upper/nas.h" +#include "upper/sdap.h" #include "upper/usim.h" #include "srsran/common/buffer_pool.h" @@ -135,6 +136,7 @@ private: std::unique_ptr rrc; std::unique_ptr rlc; std::unique_ptr pdcp; + std::unique_ptr sdap; // RAT-specific interfaces phy_interface_stack_nr* phy = nullptr; diff --git a/srsue/hdr/stack/upper/sdap.h b/srsue/hdr/stack/upper/sdap.h new file mode 100644 index 000000000..a5975ea6a --- /dev/null +++ b/srsue/hdr/stack/upper/sdap.h @@ -0,0 +1,56 @@ +/** + * + * \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 SRSUE_SDAP_H +#define SRSUE_SDAP_H + +#include "srsran/common/buffer_pool.h" +#include "srsran/common/common.h" +#include "srsran/common/common_nr.h" +#include "srsran/interfaces/ue_gw_interfaces.h" +#include "srsran/interfaces/ue_pdcp_interfaces.h" +#include "srsran/interfaces/ue_sdap_interfaces.h" + +namespace srsue { + +class sdap final : public sdap_interface_pdcp_nr, public sdap_interface_gw_nr, public sdap_interface_rrc +{ +public: + explicit sdap(const char* logname); + bool init(pdcp_interface_sdap_nr* pdcp_, srsue::gw_interface_pdcp* gw_); + void stop(); + + // Interface for GW + void write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) final; + + // Interface for PDCP + void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) final; + + // Interface for RRC + bool set_bearer_cfg(uint32_t lcid, const sdap_interface_rrc::bearer_cfg_t& cfg) final; + +private: + pdcp_interface_sdap_nr* m_pdcp = nullptr; + gw_interface_pdcp* m_gw = nullptr; + + // state + bool running = false; + + // configuration + std::array bearers = {}; + + srslog::basic_logger& logger; +}; + +} // namespace srsue + +#endif // SRSUE_SDAP_H diff --git a/srsue/src/stack/rrc_nr/rrc_nr.cc b/srsue/src/stack/rrc_nr/rrc_nr.cc index f3414bb15..b4a9744f2 100644 --- a/srsue/src/stack/rrc_nr/rrc_nr.cc +++ b/srsue/src/stack/rrc_nr/rrc_nr.cc @@ -45,6 +45,7 @@ int rrc_nr::init(phy_interface_rrc_nr* phy_, mac_interface_rrc_nr* mac_, rlc_interface_rrc* rlc_, pdcp_interface_rrc* pdcp_, + sdap_interface_rrc* sdap_, gw_interface_rrc* gw_, nas_5g_interface_rrc_nr* nas_, rrc_eutra_interface_rrc_nr* rrc_eutra_, @@ -56,6 +57,7 @@ int rrc_nr::init(phy_interface_rrc_nr* phy_, phy = phy_; rlc = rlc_; pdcp = pdcp_; + sdap = sdap_; gw = gw_; nas = nas_; mac = mac_; @@ -1896,12 +1898,18 @@ bool rrc_nr::apply_drb_add_mod(const drb_to_add_mod_s& drb_cfg) stack->add_eps_bearer(eps_bearer_id, srsran::srsran_rat_t::nr, lcid); } else if (drb_cfg.cn_assoc.type() == drb_to_add_mod_s::cn_assoc_c_::types_opts::sdap_cfg) { const auto& sdap_cfg = drb_cfg.cn_assoc.sdap_cfg(); - if (sdap_cfg.sdap_hdr_dl == asn1::rrc_nr::sdap_cfg_s::sdap_hdr_dl_opts::present || - sdap_cfg.sdap_hdr_ul == asn1::rrc_nr::sdap_cfg_s::sdap_hdr_ul_opts::present) { - logger.error("SDAP currently not supported."); - // return false; + + sdap_interface_rrc::bearer_cfg_t sdap_bearer_cfg = {}; + sdap_bearer_cfg.add_downlink_header = sdap_cfg.sdap_hdr_dl.value == sdap_cfg_s::sdap_hdr_dl_opts::present; + sdap_bearer_cfg.add_uplink_header = sdap_cfg.sdap_hdr_ul.value == sdap_cfg_s::sdap_hdr_ul_opts::present; + sdap_bearer_cfg.is_data = true; + sdap_bearer_cfg.qfi = sdap_cfg.mapped_qos_flows_to_add[0]; + + if (not sdap->set_bearer_cfg(lcid, sdap_bearer_cfg)) { + logger.error("Configuring SDAP"); + return false; } - // TODO: configure SDAP accordingly + uint32_t pdu_session_id = drb_cfg.cn_assoc.sdap_cfg().pdu_session; // Register PDU session as "EPS bearer" in bearer manager stack->add_eps_bearer(pdu_session_id, srsran::srsran_rat_t::nr, lcid); diff --git a/srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc b/srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc index da85d7c05..d06597c68 100644 --- a/srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc +++ b/srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc @@ -93,6 +93,13 @@ class dummy_pdcp : public pdcp_interface_rrc void send_status_report(uint32_t lcid){}; }; +class dummy_sdap : public sdap_interface_pdcp_nr, public sdap_interface_gw_nr, public sdap_interface_rrc +{ + void write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) final{}; + void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) final{}; + bool set_bearer_cfg(uint32_t lcid, const sdap_interface_rrc::bearer_cfg_t& cfg) final { return true; }; +}; + class dummy_gw : public gw_interface_rrc { void add_mch_port(uint32_t lcid, uint32_t port){}; @@ -143,6 +150,7 @@ int rrc_nr_cap_request_test() dummy_mac dummy_mac; dummy_rlc dummy_rlc; dummy_pdcp dummy_pdcp; + dummy_sdap dummy_sdap; dummy_gw dummy_gw; dummy_nas dummy_nas; dummy_eutra dummy_eutra; @@ -157,6 +165,7 @@ int rrc_nr_cap_request_test() &dummy_mac, &dummy_rlc, &dummy_pdcp, + &dummy_sdap, &dummy_gw, &dummy_nas, &dummy_eutra, @@ -183,6 +192,7 @@ int rrc_nsa_reconfig_tdd_test() dummy_mac dummy_mac; dummy_rlc dummy_rlc; dummy_pdcp dummy_pdcp; + dummy_sdap dummy_sdap; dummy_gw dummy_gw; dummy_nas dummy_nas; dummy_eutra dummy_eutra; @@ -193,6 +203,7 @@ int rrc_nsa_reconfig_tdd_test() &dummy_mac, &dummy_rlc, &dummy_pdcp, + &dummy_sdap, &dummy_gw, &dummy_nas, &dummy_eutra, @@ -284,6 +295,7 @@ int rrc_nsa_reconfig_fdd_test() dummy_mac dummy_mac; dummy_rlc dummy_rlc; dummy_pdcp dummy_pdcp; + dummy_sdap dummy_sdap; dummy_gw dummy_gw; dummy_nas dummy_nas; dummy_eutra dummy_eutra; @@ -294,6 +306,7 @@ int rrc_nsa_reconfig_fdd_test() &dummy_mac, &dummy_rlc, &dummy_pdcp, + &dummy_sdap, &dummy_gw, &dummy_nas, &dummy_eutra, @@ -386,6 +399,7 @@ int rrc_nr_setup_request_test() dummy_mac dummy_mac; dummy_rlc dummy_rlc; dummy_pdcp dummy_pdcp; + dummy_sdap dummy_sdap; dummy_gw dummy_gw; dummy_nas dummy_nas; dummy_eutra dummy_eutra; @@ -399,6 +413,7 @@ int rrc_nr_setup_request_test() &dummy_mac, &dummy_rlc, &dummy_pdcp, + &dummy_sdap, &dummy_gw, &dummy_nas, &dummy_eutra, @@ -428,6 +443,7 @@ int rrc_nr_sib1_decoding_test() dummy_mac dummy_mac; dummy_rlc dummy_rlc; dummy_pdcp dummy_pdcp; + dummy_sdap dummy_sdap; dummy_gw dummy_gw; dummy_nas dummy_nas; dummy_eutra dummy_eutra; @@ -438,6 +454,7 @@ int rrc_nr_sib1_decoding_test() &dummy_mac, &dummy_rlc, &dummy_pdcp, + &dummy_sdap, &dummy_gw, &dummy_nas, &dummy_eutra, @@ -475,6 +492,7 @@ int rrc_nr_setup_test() dummy_mac dummy_mac; dummy_rlc dummy_rlc; dummy_pdcp dummy_pdcp; + dummy_sdap dummy_sdap; dummy_gw dummy_gw; dummy_nas dummy_nas; dummy_eutra dummy_eutra; @@ -485,6 +503,7 @@ int rrc_nr_setup_test() &dummy_mac, &dummy_rlc, &dummy_pdcp, + &dummy_sdap, &dummy_gw, &dummy_nas, &dummy_eutra, @@ -528,6 +547,7 @@ int rrc_nr_reconfig_test() dummy_mac dummy_mac; dummy_rlc dummy_rlc; dummy_pdcp dummy_pdcp; + dummy_sdap dummy_sdap; dummy_gw dummy_gw; dummy_nas dummy_nas; dummy_eutra dummy_eutra; @@ -538,6 +558,7 @@ int rrc_nr_reconfig_test() &dummy_mac, &dummy_rlc, &dummy_pdcp, + &dummy_sdap, &dummy_gw, &dummy_nas, &dummy_eutra, diff --git a/srsue/src/stack/ue_stack_lte.cc b/srsue/src/stack/ue_stack_lte.cc index 77359c4e2..2b67bed1d 100644 --- a/srsue/src/stack/ue_stack_lte.cc +++ b/srsue/src/stack/ue_stack_lte.cc @@ -47,6 +47,8 @@ ue_stack_lte::ue_stack_lte() : rrc_nr(&task_sched), pdcp(&task_sched, "PDCP"), pdcp_nr(&task_sched, "PDCP-NR"), + sdap("SDAP-NR"), + sdap_pdcp(&pdcp_nr, &sdap), nas(srslog::fetch_basic_logger("NAS", false), &task_sched), nas_5g(srslog::fetch_basic_logger("NAS5G", false), &task_sched), thread("STACK"), @@ -208,9 +210,15 @@ int ue_stack_lte::init(const stack_args_t& args_) mac.init(phy, &rlc, &rrc); rlc.init(&pdcp, &rrc, task_sched.get_timer_handler(), 0 /* RB_ID_SRB0 */); - pdcp.init(&rlc, &rrc, gw); nas.init(usim.get(), &rrc, gw, args.nas); + if (!args.sa_mode) { + pdcp.init(&rlc, &rrc, gw); + } else { + pdcp.init(&rlc, &rrc, &sdap_pdcp); + sdap.init(&sdap_pdcp, gw); + } + mac_nr_args_t mac_nr_args = {}; mac_nr.init(mac_nr_args, phy_nr, &rlc_nr, &rrc_nr); rlc_nr.init(&pdcp_nr, &rrc_nr, task_sched.get_timer_handler(), 0 /* RB_ID_SRB0 */); @@ -219,6 +227,7 @@ int ue_stack_lte::init(const stack_args_t& args_) &mac_nr, &rlc_nr, &pdcp_nr, + &sdap, gw, &nas_5g, args.sa_mode ? nullptr : &rrc, @@ -400,12 +409,17 @@ void ue_stack_lte::remove_eps_bearer(uint8_t eps_bearer_id) void ue_stack_lte::write_sdu(uint32_t eps_bearer_id, srsran::unique_byte_buffer_t sdu) { auto bearer = bearers.get_radio_bearer(eps_bearer_id); + auto task = [this, eps_bearer_id, bearer](srsran::unique_byte_buffer_t& sdu) { // route SDU to PDCP entity if (bearer.rat == srsran_rat_t::lte) { pdcp.write_sdu(bearer.lcid, std::move(sdu)); } else if (bearer.rat == srsran_rat_t::nr) { - pdcp_nr.write_sdu(bearer.lcid, std::move(sdu)); + if (args.sa_mode) { + sdap.write_sdu(bearer.lcid, std::move(sdu)); + } else { + pdcp_nr.write_sdu(bearer.lcid, std::move(sdu)); + } } else { stack_logger.warning("Can't deliver SDU for EPS bearer %d. Dropping it.", eps_bearer_id); } diff --git a/srsue/src/stack/ue_stack_nr.cc b/srsue/src/stack/ue_stack_nr.cc index 826330884..3784096af 100644 --- a/srsue/src/stack/ue_stack_nr.cc +++ b/srsue/src/stack/ue_stack_nr.cc @@ -80,6 +80,7 @@ int ue_stack_nr::init(const stack_args_t& args_) mac.get(), rlc.get(), pdcp.get(), + sdap.get(), gw, nullptr, nullptr, diff --git a/srsue/src/stack/upper/CMakeLists.txt b/srsue/src/stack/upper/CMakeLists.txt index 29c26f205..233294fae 100644 --- a/srsue/src/stack/upper/CMakeLists.txt +++ b/srsue/src/stack/upper/CMakeLists.txt @@ -8,7 +8,7 @@ add_subdirectory(test) -set(SOURCES nas.cc nas_emm_state.cc nas_idle_procedures.cc gw.cc usim_base.cc usim.cc tft_packet_filter.cc nas_base.cc nas_5g_procedures.cc nas_5g.cc nas_5gmm_state.cc) +set(SOURCES nas.cc nas_emm_state.cc nas_idle_procedures.cc gw.cc usim_base.cc usim.cc tft_packet_filter.cc nas_base.cc nas_5g_procedures.cc nas_5g.cc nas_5gmm_state.cc sdap.cc) if(HAVE_PCSC) list(APPEND SOURCES "pcsc_usim.cc") diff --git a/srsue/src/stack/upper/sdap.cc b/srsue/src/stack/upper/sdap.cc new file mode 100644 index 000000000..74f0c65df --- /dev/null +++ b/srsue/src/stack/upper/sdap.cc @@ -0,0 +1,76 @@ +/** + * + * \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 "srsue/hdr/stack/upper/sdap.h" + +namespace srsue { + +sdap::sdap(const char* logname) : logger(srslog::fetch_basic_logger(logname)) {} + +bool sdap::init(pdcp_interface_sdap_nr* pdcp_, srsue::gw_interface_pdcp* gw_) +{ + m_pdcp = pdcp_; + m_gw = gw_; + + running = true; + return true; +} + +void sdap::stop() +{ + if (running) { + running = false; + } +} + +void sdap::write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) +{ + if (!running) { + return; + } + m_gw->write_pdu(lcid, std::move(pdu)); +} + +void sdap::write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) +{ + if (!running) { + return; + } + if (lcid < bearers.size()) { + if (bearers[lcid].add_uplink_header) { + if (pdu->get_headroom() > 1) { + pdu->msg -= 1; + pdu->N_bytes += 1; + pdu->msg[0] = ((bearers[lcid].is_data ? 1 : 0) << 7) | (bearers[lcid].qfi & 0x3f); + } else { + logger.error("Not enough headroom in PDU to add header\n"); + } + } + } + m_pdcp->write_sdu(lcid, std::move(pdu)); +} + +bool sdap::set_bearer_cfg(uint32_t lcid, const sdap_interface_rrc::bearer_cfg_t& cfg) +{ + if (lcid >= bearers.size()) { + logger.error("Error setting configuration: invalid lcid=%d\n", lcid); + return false; + } + if (cfg.add_downlink_header) { + logger.error("Error setting configuration: downlink header not supported\n"); + return false; + } + bearers[lcid] = cfg; + return true; +} + +} // namespace srsue From b3497c4a941b780dd59ad8d9b8ef48f07e9f4dbe Mon Sep 17 00:00:00 2001 From: Ismael Gomez Date: Mon, 28 Mar 2022 22:47:50 +0200 Subject: [PATCH 091/195] srsue,nr: decode SIB1 based on coreset0 configuration in MIB --- lib/include/srsran/asn1/rrc_nr_utils.h | 1 + .../srsran/interfaces/ue_nr_interfaces.h | 3 + lib/src/asn1/rrc_nr_utils.cc | 15 ++ srsue/hdr/stack/mac_nr/demux_nr.h | 2 + srsue/hdr/stack/mac_nr/mac_nr.h | 4 + srsue/hdr/stack/mac_nr/mac_nr_interfaces.h | 1 + srsue/hdr/stack/rrc_nr/rrc_nr_procedures.h | 3 +- srsue/src/phy/nr/cc_worker.cc | 1 + srsue/src/stack/mac_nr/demux_nr.cc | 12 ++ srsue/src/stack/mac_nr/dl_harq_nr.cc | 3 +- srsue/src/stack/mac_nr/mac_nr.cc | 8 +- srsue/src/stack/rrc_nr/rrc_nr.cc | 8 + srsue/src/stack/rrc_nr/rrc_nr_procedures.cc | 180 ++++++++++-------- srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc | 1 + 14 files changed, 162 insertions(+), 80 deletions(-) diff --git a/lib/include/srsran/asn1/rrc_nr_utils.h b/lib/include/srsran/asn1/rrc_nr_utils.h index 8c77e399d..98b3560ac 100644 --- a/lib/include/srsran/asn1/rrc_nr_utils.h +++ b/lib/include/srsran/asn1/rrc_nr_utils.h @@ -94,6 +94,7 @@ bool make_phy_tdd_cfg(const srsran_duplex_config_nr_t& srsran_duplex_config bool make_phy_harq_ack_cfg(const asn1::rrc_nr::phys_cell_group_cfg_s& phys_cell_group_cfg, srsran_harq_ack_cfg_hl_t* srsran_ue_dl_nr_harq_ack_cfg); bool make_phy_coreset_cfg(const asn1::rrc_nr::ctrl_res_set_s& ctrl_res_set, srsran_coreset_t* srsran_coreset); +void make_phy_search_space0_cfg(srsran_search_space_t* in_srsran_search_space); bool make_phy_search_space_cfg(const asn1::rrc_nr::search_space_s& search_space, srsran_search_space_t* srsran_search_space); bool make_phy_csi_report(const asn1::rrc_nr::csi_report_cfg_s& csi_report_cfg, diff --git a/lib/include/srsran/interfaces/ue_nr_interfaces.h b/lib/include/srsran/interfaces/ue_nr_interfaces.h index 8668ce183..c6dc69902 100644 --- a/lib/include/srsran/interfaces/ue_nr_interfaces.h +++ b/lib/include/srsran/interfaces/ue_nr_interfaces.h @@ -197,6 +197,9 @@ public: // RRC informs MAC about new UE identity for contention-free RA virtual bool set_crnti(const uint16_t crnti) = 0; + + // RRC informs MAC to start/stop search for BCCH messages + virtual void bcch_search(bool enabled) = 0; }; struct phy_args_nr_t { diff --git a/lib/src/asn1/rrc_nr_utils.cc b/lib/src/asn1/rrc_nr_utils.cc index f7affbe0e..a452e507a 100644 --- a/lib/src/asn1/rrc_nr_utils.cc +++ b/lib/src/asn1/rrc_nr_utils.cc @@ -503,6 +503,21 @@ bool make_phy_harq_ack_cfg(const phys_cell_group_cfg_s& phys_cell_group_cfg, return true; } +void make_phy_search_space0_cfg(srsran_search_space_t* in_srsran_search_space) +{ + in_srsran_search_space->id = 0; + in_srsran_search_space->coreset_id = 0; + in_srsran_search_space->type = srsran_search_space_type_common_0; + in_srsran_search_space->nof_candidates[0] = 0; + in_srsran_search_space->nof_candidates[1] = 0; + in_srsran_search_space->nof_candidates[2] = 4; + in_srsran_search_space->nof_candidates[3] = 2; + in_srsran_search_space->nof_candidates[4] = 0; + in_srsran_search_space->nof_formats = 1; + in_srsran_search_space->formats[0] = srsran_dci_format_nr_1_0; + in_srsran_search_space->duration = 1; +} + bool make_phy_search_space_cfg(const search_space_s& search_space, srsran_search_space_t* in_srsran_search_space) { srsran_search_space_t srsran_search_space = {}; diff --git a/srsue/hdr/stack/mac_nr/demux_nr.h b/srsue/hdr/stack/mac_nr/demux_nr.h index aae7259e1..fffb26181 100644 --- a/srsue/hdr/stack/mac_nr/demux_nr.h +++ b/srsue/hdr/stack/mac_nr/demux_nr.h @@ -40,6 +40,7 @@ public: void process_pdus(); /// Called by MAC to process received PDUs // HARQ interface + void push_bcch(srsran::unique_byte_buffer_t pdu); void push_pdu(srsran::unique_byte_buffer_t pdu, uint32_t tti); void push_pdu_temp_crnti(srsran::unique_byte_buffer_t pdu, uint32_t tti); uint64_t get_received_crueid(); @@ -55,6 +56,7 @@ private: ///< currently only DCH PDUs supported (add BCH, PCH, etc) srsran::block_queue pdu_queue; + srsran::block_queue bcch_queue; srsran::mac_sch_pdu_nr rx_pdu; srsran::mac_sch_pdu_nr rx_pdu_tcrnti; diff --git a/srsue/hdr/stack/mac_nr/mac_nr.h b/srsue/hdr/stack/mac_nr/mac_nr.h index c6189fe9c..7727337e4 100644 --- a/srsue/hdr/stack/mac_nr/mac_nr.h +++ b/srsue/hdr/stack/mac_nr/mac_nr.h @@ -109,6 +109,7 @@ public: /// RRC void rrc_ra_problem(); void rrc_ra_completed(); + void bcch_search(bool enabled); /// stack interface void process_pdus(); @@ -149,6 +150,9 @@ private: std::atomic started = {false}; + // Boolean to determine if need to decode SI-RNTI + bool search_bcch = false; + ue_rnti rntis; // thread-safe helper to store RNTIs, contention ID, etc bool contention_res_successful; diff --git a/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h b/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h index b894e6509..997b38d49 100644 --- a/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h +++ b/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h @@ -93,6 +93,7 @@ class demux_interface_harq_nr { public: /// Inform demux unit about a newly decoded TB. + virtual void push_bcch(srsran::unique_byte_buffer_t pdu) = 0; virtual void push_pdu(srsran::unique_byte_buffer_t pdu, uint32_t tti) = 0; virtual void push_pdu_temp_crnti(srsran::unique_byte_buffer_t pdu, uint32_t tti) = 0; virtual uint64_t get_received_crueid() = 0; diff --git a/srsue/hdr/stack/rrc_nr/rrc_nr_procedures.h b/srsue/hdr/stack/rrc_nr/rrc_nr_procedures.h index c70dcb3d9..00ed72c15 100644 --- a/srsue/hdr/stack/rrc_nr/rrc_nr_procedures.h +++ b/srsue/hdr/stack/rrc_nr/rrc_nr_procedures.h @@ -25,7 +25,7 @@ namespace srsue { class rrc_nr::cell_selection_proc { public: - enum class state_t { phy_cell_search, phy_cell_select }; + enum class state_t { phy_cell_search, phy_cell_select, sib_acquire }; using cell_selection_complete_ev = srsran::proc_result_t; explicit cell_selection_proc(rrc_nr& parent_); @@ -33,6 +33,7 @@ public: srsran::proc_outcome_t step(); srsran::proc_outcome_t react(const rrc_interface_phy_nr::cell_search_result_t& event); srsran::proc_outcome_t react(const rrc_interface_phy_nr::cell_select_result_t& event); + srsran::proc_outcome_t react(const bool sib1_found); void then(const cell_selection_complete_ev& proc_result) const; diff --git a/srsue/src/phy/nr/cc_worker.cc b/srsue/src/phy/nr/cc_worker.cc index 99ee6f95b..d1139b7fc 100644 --- a/srsue/src/phy/nr/cc_worker.cc +++ b/srsue/src/phy/nr/cc_worker.cc @@ -396,6 +396,7 @@ bool cc_worker::measure_csi() logger.error("PBCH-MIB: NR SFN (%d) does not match current SFN (%d)", mib.sfn, dl_slot_cfg.idx / SRSRAN_NSLOTS_PER_FRAME_NR(cfg.carrier.scs)); + dl_slot_cfg.idx = mib.sfn * SRSRAN_NSLOTS_PER_FRAME_NR(cfg.carrier.scs); } // Log MIB information diff --git a/srsue/src/stack/mac_nr/demux_nr.cc b/srsue/src/stack/mac_nr/demux_nr.cc index a7e5d754a..394854f16 100644 --- a/srsue/src/stack/mac_nr/demux_nr.cc +++ b/srsue/src/stack/mac_nr/demux_nr.cc @@ -38,6 +38,11 @@ void demux_nr::push_pdu(srsran::unique_byte_buffer_t pdu, uint32_t tti) pdu_queue.push(std::move(pdu)); } +void demux_nr::push_bcch(srsran::unique_byte_buffer_t pdu) +{ + bcch_queue.push(std::move(pdu)); +} + /* Demultiplexing of MAC PDU associated with a Temporal C-RNTI. The PDU will * remain in buffer until demultiplex_pending_pdu() is called. * This features is provided to enable the Random Access Procedure to decide @@ -55,6 +60,13 @@ void demux_nr::push_pdu_temp_crnti(srsran::unique_byte_buffer_t pdu, uint32_t tt void demux_nr::process_pdus() { + // Handle first BCCH + while (not bcch_queue.empty()) { + srsran::unique_byte_buffer_t pdu = bcch_queue.wait_pop(); + logger.debug(pdu->msg, pdu->N_bytes, "Handling MAC BCCH PDU (%d B)", pdu->N_bytes); + rlc->write_pdu_bcch_dlsch(pdu->msg, pdu->N_bytes); + } + // Then user PDUs while (not pdu_queue.empty()) { srsran::unique_byte_buffer_t pdu = pdu_queue.wait_pop(); handle_pdu(rx_pdu, std::move(pdu)); diff --git a/srsue/src/stack/mac_nr/dl_harq_nr.cc b/srsue/src/stack/mac_nr/dl_harq_nr.cc index 377269057..68f077fc9 100644 --- a/srsue/src/stack/mac_nr/dl_harq_nr.cc +++ b/srsue/src/stack/mac_nr/dl_harq_nr.cc @@ -229,8 +229,7 @@ void dl_harq_entity_nr::dl_harq_process_nr::tb_decoded(const mac_nr_grant_dl_t& if (acked and result.payload != nullptr) { if (is_bcch) { - logger.warning("Delivering PDU=%d bytes to Dissassemble and Demux unit (BCCH) not implemented", grant.tbs); - reset(); + harq_entity->demux_unit->push_bcch(std::move(result.payload)); } else { if (grant.rnti == harq_entity->mac->get_temp_crnti()) { logger.debug("Delivering PDU=%d bytes to Dissassemble and Demux unit (Temporal C-RNTI) not implemented", diff --git a/srsue/src/stack/mac_nr/mac_nr.cc b/srsue/src/stack/mac_nr/mac_nr.cc index a69a7fad2..d050649b4 100644 --- a/srsue/src/stack/mac_nr/mac_nr.cc +++ b/srsue/src/stack/mac_nr/mac_nr.cc @@ -158,8 +158,7 @@ mac_interface_phy_nr::sched_rnti_t mac_nr::get_ul_sched_rnti_nr(const uint32_t t bool mac_nr::is_si_opportunity() { - // TODO: ask RRC if we need SI - return false; + return search_bcch; } bool mac_nr::is_paging_opportunity() @@ -447,6 +446,11 @@ void mac_nr::set_contention_id(uint64_t ue_identity) rntis.set_contention_id(ue_identity); } +void mac_nr::bcch_search(bool enabled) +{ + search_bcch = enabled; +} + bool mac_nr::set_crnti(const uint16_t c_rnti_) { if (is_valid_crnti(c_rnti_)) { diff --git a/srsue/src/stack/rrc_nr/rrc_nr.cc b/srsue/src/stack/rrc_nr/rrc_nr.cc index b4a9744f2..e8b762e36 100644 --- a/srsue/src/stack/rrc_nr/rrc_nr.cc +++ b/srsue/src/stack/rrc_nr/rrc_nr.cc @@ -391,6 +391,11 @@ void rrc_nr::set_phy_default_config() void rrc_nr::handle_sib1(const sib1_s& sib1) { + if (meas_cells.serving_cell().has_sib1()) { + logger.info("SIB1 already processed"); + return; + } + meas_cells.serving_cell().set_sib1(sib1); logger.info("SIB1 received, CellID=%d", meas_cells.serving_cell().get_cell_id() & 0xfff); @@ -486,6 +491,9 @@ void rrc_nr::handle_sib1(const sib1_s& sib1) logger.warning("Could not set phy config."); return; } + + // Notify cell selector of successful SIB1 reception + cell_selector.trigger(true); } void rrc_nr::write_pdu_pcch(srsran::unique_byte_buffer_t pdu) {} diff --git a/srsue/src/stack/rrc_nr/rrc_nr_procedures.cc b/srsue/src/stack/rrc_nr/rrc_nr_procedures.cc index 8b90b007f..3ee274a3d 100644 --- a/srsue/src/stack/rrc_nr/rrc_nr_procedures.cc +++ b/srsue/src/stack/rrc_nr/rrc_nr_procedures.cc @@ -384,12 +384,12 @@ proc_outcome_t rrc_nr::cell_selection_proc::init() return proc_outcome_t::yield; } -// Skipping SI acquisition procedure proc_outcome_t rrc_nr::cell_selection_proc::step() { switch (state) { case state_t::phy_cell_search: case state_t::phy_cell_select: + case state_t::sib_acquire: // Waits for cell select/search to complete return proc_outcome_t::yield; } @@ -400,84 +400,97 @@ proc_outcome_t rrc_nr::cell_selection_proc::step() proc_outcome_t rrc_nr::cell_selection_proc::handle_cell_search_result(const rrc_interface_phy_nr::cell_search_result_t& result) { - if (result.cell_found) { - // Convert Cell measurement in Text - std::array csi_info_str = {}; - srsran_csi_meas_info_short(&result.measurements, csi_info_str.data(), (uint32_t)csi_info_str.size()); - - // Unpack MIB and convert to text - srsran_mib_nr_t mib = {}; - std::array mib_info_str = {}; - if (srsran_pbch_msg_nr_mib_unpack(&result.pbch_msg, &mib) == SRSASN_SUCCESS) { - // Convert to text - srsran_pbch_msg_nr_mib_info(&mib, mib_info_str.data(), (uint32_t)mib_info_str.size()); - } else { - // It could be the PBCH does not carry MIB - strcpy(mib_info_str.data(), "No MIB found"); - } + if (!result.cell_found) { + Info("Cell search did not find any cell."); + return proc_outcome_t::error; + } - // Logs the PCI, cell measurements and decoded MIB - Info("Cell search found ARFCN=%d PCI=%d %s %s", - result.ssb_arfcn, - result.pci, - csi_info_str.data(), - mib_info_str.data()); - - // Transition to cell selection ignoring the cell search result - state = state_t::phy_cell_select; - - // until cell selection is done, update PHY config to take the last found PCI - rrc_handle.phy_cfg.carrier.pci = result.pci; - - phy_interface_rrc_nr::cell_select_args_t cs_args = {}; - cs_args.carrier = rrc_handle.phy_cfg.carrier; - cs_args.ssb_cfg = rrc_handle.phy_cfg.get_ssb_cfg(); - - { - // Coreset0 configuration - srsran::phy_cfg_nr_t& phy_cfg = rrc_handle.phy_cfg; - - // Get pointA and SSB absolute frequencies - double pointA_abs_freq_Hz = - phy_cfg.carrier.dl_center_frequency_hz - - phy_cfg.carrier.nof_prb * SRSRAN_NRE * SRSRAN_SUBC_SPACING_NR(phy_cfg.carrier.scs) / 2; - double ssb_abs_freq_Hz = phy_cfg.carrier.ssb_center_freq_hz; - // Calculate integer SSB to pointA frequency offset in Hz - uint32_t ssb_pointA_freq_offset_Hz = - (ssb_abs_freq_Hz > pointA_abs_freq_Hz) ? (uint32_t)(ssb_abs_freq_Hz - pointA_abs_freq_Hz) : 0; - - if (srsran_coreset_zero(phy_cfg.carrier.pci, - ssb_pointA_freq_offset_Hz, - phy_cfg.ssb.scs, - phy_cfg.carrier.scs, - 6, - &phy_cfg.pdcch.coreset[0])) { - fprintf(stderr, "Error generating coreset0\n"); - } - phy_cfg.pdcch.coreset_present[0] = true; - } + // Convert Cell measurement in Text + std::array csi_info_str = {}; + srsran_csi_meas_info_short(&result.measurements, csi_info_str.data(), (uint32_t)csi_info_str.size()); - // Until SI acquisition is implemented, provide hard-coded SIB for now - uint8_t msg[] = {0x74, 0x81, 0x01, 0x70, 0x10, 0x40, 0x04, 0x02, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x33, 0x60, 0x38, - 0x05, 0x01, 0x00, 0x40, 0x1a, 0x00, 0x00, 0x06, 0x6c, 0x6d, 0x92, 0x21, 0xf3, 0x70, 0x40, 0x20, - 0x00, 0x00, 0x80, 0x80, 0x00, 0x41, 0x06, 0x80, 0xa0, 0x90, 0x9c, 0x20, 0x08, 0x55, 0x19, 0x40, - 0x00, 0x00, 0x33, 0xa1, 0xc6, 0xd9, 0x22, 0x40, 0x00, 0x00, 0x20, 0xb8, 0x94, 0x63, 0xc0, 0x09, - 0x28, 0x44, 0x1b, 0x7e, 0xad, 0x8e, 0x1d, 0x00, 0x9e, 0x2d, 0xa3, 0x0a}; - srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); - memcpy(pdu->msg, msg, sizeof(msg)); - pdu->N_bytes = sizeof(msg); - rrc_handle.write_pdu_bcch_dlsch(std::move(pdu)); - - if (not rrc_handle.phy->start_cell_select(cs_args)) { - Error("Could not set start cell search."); - return proc_outcome_t::error; - } - return proc_outcome_t::yield; + // Unpack MIB and convert to text + srsran_mib_nr_t mib = {}; + std::array mib_info_str = {}; + if (srsran_pbch_msg_nr_mib_unpack(&result.pbch_msg, &mib) == SRSASN_SUCCESS) { + // Convert to text + srsran_pbch_msg_nr_mib_info(&mib, mib_info_str.data(), (uint32_t)mib_info_str.size()); } else { - Info("Cell search did not find any cell."); + // It could be the PBCH does not carry MIB + strcpy(mib_info_str.data(), "No MIB found"); + Error("No MIB found\n"); + return proc_outcome_t::error; } - return proc_outcome_t::error; + // Check unsupported settings + if (mib.cell_barred) { + Error("Cell barred"); + return proc_outcome_t::error; + } + if (mib.scs_common != srsran_subcarrier_spacing_15kHz) { + Error("Unsupported SCS %s", srsran_subcarrier_spacing_to_str(mib.scs_common)); + return proc_outcome_t::error; + } + // TODO: calculate SSB offset + if (mib.ssb_offset != 6) { + Error("Unsupported SSB offset %d", mib.ssb_offset); + return proc_outcome_t::error; + } + + // Logs the PCI, cell measurements and decoded MIB + Info("Cell search found ARFCN=%d PCI=%d %s %s", + result.ssb_arfcn, + result.pci, + csi_info_str.data(), + mib_info_str.data()); + + // Apply MIB settings + srsran::phy_cfg_nr_t& phy_cfg = rrc_handle.phy_cfg; + phy_cfg.pdsch.typeA_pos = mib.dmrs_typeA_pos; + phy_cfg.pdsch.scs_cfg = mib.scs_common; + phy_cfg.carrier.pci = result.pci; + + // Get pointA and SSB absolute frequencies + double pointA_abs_freq_Hz = phy_cfg.carrier.dl_center_frequency_hz - + phy_cfg.carrier.nof_prb * SRSRAN_NRE * SRSRAN_SUBC_SPACING_NR(phy_cfg.carrier.scs) / 2; + double ssb_abs_freq_Hz = phy_cfg.carrier.ssb_center_freq_hz; + // Calculate integer SSB to pointA frequency offset in Hz + uint32_t ssb_pointA_freq_offset_Hz = + (ssb_abs_freq_Hz > pointA_abs_freq_Hz) ? (uint32_t)(ssb_abs_freq_Hz - pointA_abs_freq_Hz) : 0; + + // Create coreset0 + if (srsran_coreset_zero(phy_cfg.carrier.pci, + ssb_pointA_freq_offset_Hz, + phy_cfg.ssb.scs, + phy_cfg.carrier.scs, + mib.coreset0_idx, + &phy_cfg.pdcch.coreset[0])) { + Error("Error generating coreset0"); + return proc_outcome_t::error; + } + phy_cfg.pdcch.coreset_present[0] = true; + + // Create SearchSpace0 + make_phy_search_space0_cfg(&phy_cfg.pdcch.search_space[0]); + phy_cfg.pdcch.search_space_present[0] = true; + + // Update PHY configuration + if (not rrc_handle.phy->set_config(phy_cfg)) { + Error("Setting PHY configuration"); + return proc_outcome_t::error; + } + + phy_interface_rrc_nr::cell_select_args_t cs_args = {}; + cs_args.carrier = rrc_handle.phy_cfg.carrier; + cs_args.ssb_cfg = rrc_handle.phy_cfg.get_ssb_cfg(); + + // Transition to cell selection ignoring the cell search result + state = state_t::phy_cell_select; + if (not rrc_handle.phy->start_cell_select(cs_args)) { + Error("Could not set start cell search."); + return proc_outcome_t::error; + } + return proc_outcome_t::yield; } proc_outcome_t rrc_nr::cell_selection_proc::react(const rrc_interface_phy_nr::cell_select_result_t& event) @@ -497,7 +510,24 @@ proc_outcome_t rrc_nr::cell_selection_proc::react(const rrc_interface_phy_nr::ce rrc_search_result = rrc_nr::rrc_cell_search_result_t::same_cell; // PHY is now camping on serving cell - Info("Cell search completed."); + Info("Cell selection completed. Starting SIB1 acquisition"); + + // Transition to cell selection ignoring the cell search result + state = state_t::sib_acquire; + rrc_handle.mac->bcch_search(true); + return proc_outcome_t::yield; +} + +proc_outcome_t rrc_nr::cell_selection_proc::react(const bool sib1_found) +{ + if (state != state_t::sib_acquire) { + Warning("Received unexpected cell select result"); + return proc_outcome_t::yield; + } + + Info("SIB1 acquired successfully"); + rrc_handle.mac->bcch_search(false); + return proc_outcome_t::success; } diff --git a/srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc b/srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc index d06597c68..68fd9e178 100644 --- a/srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc +++ b/srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc @@ -41,6 +41,7 @@ class dummy_mac : public mac_interface_rrc_nr int add_tag_config(const srsran::tag_cfg_nr_t& tag_cfg) { return SRSRAN_SUCCESS; } int set_config(const srsran::phr_cfg_nr_t& phr_cfg) { return SRSRAN_SUCCESS; } int remove_tag_config(const uint32_t tag_id) { return SRSRAN_SUCCESS; } + void bcch_search(bool) {} void start_ra_procedure() {} From edeb8e8f18e90f9da1644009715bf770c5978deb Mon Sep 17 00:00:00 2001 From: Ismael Gomez Date: Sun, 20 Mar 2022 22:12:57 +0100 Subject: [PATCH 092/195] srsue,nr: add TA support from RAR and MAC CE --- lib/include/srsran/interfaces/ue_nr_interfaces.h | 6 ++++++ srsue/hdr/phy/nr/sync_sa.h | 6 ++++++ srsue/hdr/phy/phy_nr_sa.h | 3 +++ srsue/hdr/stack/mac_nr/demux_nr.h | 4 +++- srsue/src/phy/phy_nr_sa.cc | 10 ++++++++++ srsue/src/phy/sync_sa.cc | 15 ++++++++++++++- srsue/src/stack/mac_nr/demux_nr.cc | 6 ++++-- srsue/src/stack/mac_nr/mac_nr.cc | 2 +- srsue/src/stack/mac_nr/proc_ra_nr.cc | 5 ++++- srsue/src/stack/mac_nr/test/mac_nr_test.cc | 3 +++ srsue/src/stack/mac_nr/test/proc_ra_nr_test.cc | 2 ++ srsue/src/stack/mac_nr/test/proc_sr_nr_test.cc | 3 +++ 12 files changed, 59 insertions(+), 6 deletions(-) diff --git a/lib/include/srsran/interfaces/ue_nr_interfaces.h b/lib/include/srsran/interfaces/ue_nr_interfaces.h index c6dc69902..edcd93140 100644 --- a/lib/include/srsran/interfaces/ue_nr_interfaces.h +++ b/lib/include/srsran/interfaces/ue_nr_interfaces.h @@ -258,6 +258,12 @@ public: const float preamble_received_target_power, const float ta_base_sec = 0.0f) = 0; + /// Apply TA command after RAR + virtual void set_timeadv_rar(uint32_t tti, uint32_t ta_cmd) = 0; + + /// Apply TA command after MAC CE + virtual void set_timeadv(uint32_t tti, uint32_t ta_cmd) = 0; + /** * @brief Query PHY if there is a valid PUCCH SR resource configured for a given SR identifier * @param sr_id SR identifier diff --git a/srsue/hdr/phy/nr/sync_sa.h b/srsue/hdr/phy/nr/sync_sa.h index 7a27b43bd..1ff410570 100644 --- a/srsue/hdr/phy/nr/sync_sa.h +++ b/srsue/hdr/phy/nr/sync_sa.h @@ -79,6 +79,9 @@ public: void worker_end(const worker_context_t& w_ctx, const bool& tx_enable, srsran::rf_buffer_t& buffer) override; + void add_ta_cmd_rar(uint32_t tti, uint32_t ta_cmd); + void add_ta_cmd_new(uint32_t tti, uint32_t ta_cmd); + private: stack_interface_phy_nr* stack = nullptr; ///< Stand-Alone RRC interface srsran::radio_interface_phy* radio = nullptr; ///< Radio object @@ -107,6 +110,9 @@ private: cell_search searcher; slot_sync slot_synchronizer; + // Time Aligment Controller, internal thread safe + ta_control ta; + // FSM States bool wait_idle(); void run_state_idle(); diff --git a/srsue/hdr/phy/phy_nr_sa.h b/srsue/hdr/phy/phy_nr_sa.h index 3e6dbbc0f..cfc19bd53 100644 --- a/srsue/hdr/phy/phy_nr_sa.h +++ b/srsue/hdr/phy/phy_nr_sa.h @@ -50,6 +50,9 @@ public: const int preamble_index, const float preamble_received_target_power, const float ta_base_sec) final; + void set_timeadv_rar(uint32_t tti, uint32_t ta_cmd) final; + void set_timeadv(uint32_t tti, uint32_t ta_cmd) final; + void set_earfcn(std::vector earfcns); bool has_valid_sr_resource(uint32_t sr_id) final; void clear_pending_grants() final; diff --git a/srsue/hdr/stack/mac_nr/demux_nr.h b/srsue/hdr/stack/mac_nr/demux_nr.h index fffb26181..38e8faa1c 100644 --- a/srsue/hdr/stack/mac_nr/demux_nr.h +++ b/srsue/hdr/stack/mac_nr/demux_nr.h @@ -15,6 +15,7 @@ #include "mac_nr_interfaces.h" #include "srsran/common/block_queue.h" +#include "srsran/interfaces/ue_nr_interfaces.h" #include "srsran/interfaces/ue_rlc_interfaces.h" namespace srsue { @@ -35,7 +36,7 @@ public: demux_nr(srslog::basic_logger& logger_); ~demux_nr(); - int32_t init(rlc_interface_mac* rlc_); + int32_t init(rlc_interface_mac* rlc_, phy_interface_mac_nr* phy_); void process_pdus(); /// Called by MAC to process received PDUs @@ -51,6 +52,7 @@ private: srslog::basic_logger& logger; rlc_interface_mac* rlc = nullptr; + phy_interface_mac_nr* phy = nullptr; uint64_t received_crueid = 0; diff --git a/srsue/src/phy/phy_nr_sa.cc b/srsue/src/phy/phy_nr_sa.cc index 435bbad28..83cc1c319 100644 --- a/srsue/src/phy/phy_nr_sa.cc +++ b/srsue/src/phy/phy_nr_sa.cc @@ -230,6 +230,16 @@ void phy_nr_sa::send_prach(const uint32_t prach_occasion, workers.send_prach(prach_occasion, preamble_index, preamble_received_target_power); } +void phy_nr_sa::set_timeadv_rar(uint32_t tti, uint32_t ta_cmd) +{ + sync.add_ta_cmd_rar(tti, ta_cmd); +} + +void phy_nr_sa::set_timeadv(uint32_t tti, uint32_t ta_cmd) +{ + sync.add_ta_cmd_new(tti, ta_cmd); +} + int phy_nr_sa::set_rar_grant(uint32_t rar_slot_idx, std::array packed_ul_grant, uint16_t rnti, diff --git a/srsue/src/phy/sync_sa.cc b/srsue/src/phy/sync_sa.cc index 23460d3d0..464ceded1 100644 --- a/srsue/src/phy/sync_sa.cc +++ b/srsue/src/phy/sync_sa.cc @@ -16,7 +16,7 @@ namespace srsue { namespace nr { sync_sa::sync_sa(srslog::basic_logger& logger_, worker_pool& workers_) : - logger(logger_), workers(workers_), slot_synchronizer(logger_), searcher(logger_), srsran::thread("SYNC") + logger(logger_), workers(workers_), slot_synchronizer(logger_), searcher(logger_), ta(logger_), srsran::thread("SYNC") {} sync_sa::~sync_sa() @@ -78,10 +78,21 @@ bool sync_sa::reset() { // Wait worker pool to finish any processing tti_semaphore.wait_all(); + ta.set_base_sec(0); return true; } +void sync_sa::add_ta_cmd_rar(uint32_t tti_, uint32_t ta_cmd) +{ + ta.add_ta_cmd_rar(tti_, ta_cmd); +} + +void sync_sa::add_ta_cmd_new(uint32_t tti_, uint32_t ta_cmd) +{ + ta.add_ta_cmd_new(tti_, ta_cmd); +} + void sync_sa::cell_go_idle() { std::unique_lock ul(rrc_mutex); @@ -293,6 +304,8 @@ void sync_sa::run_state_cell_camping() context.last = true; // Set last if standalone last_rx_time.add(FDD_HARQ_DELAY_DL_MS * 1e-3); context.tx_time.copy(last_rx_time); + // Apply current TA + context.tx_time.sub((double)ta.get_sec()); nr_worker->set_context(context); diff --git a/srsue/src/stack/mac_nr/demux_nr.cc b/srsue/src/stack/mac_nr/demux_nr.cc index 394854f16..f7adcfcf8 100644 --- a/srsue/src/stack/mac_nr/demux_nr.cc +++ b/srsue/src/stack/mac_nr/demux_nr.cc @@ -21,9 +21,10 @@ demux_nr::demux_nr(srslog::basic_logger& logger_) : logger(logger_) {} demux_nr::~demux_nr() {} -int32_t demux_nr::init(rlc_interface_mac* rlc_) +int32_t demux_nr::init(rlc_interface_mac* rlc_, phy_interface_mac_nr* phy_) { rlc = rlc_; + phy = phy_; return SRSRAN_SUCCESS; } @@ -104,7 +105,8 @@ void demux_nr::handle_pdu(srsran::mac_sch_pdu_nr& pdu_buffer, srsran::unique_byt logger.info("DRX CE not implemented."); break; case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::TA_CMD: - logger.info("Timing Advance CE not implemented."); + logger.info("Received TA=%d.", subpdu.get_ta().ta_command); + phy->set_timeadv(0, subpdu.get_ta().ta_command); break; case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::CON_RES_ID: received_crueid = subpdu.get_ue_con_res_id_ce_packed(); diff --git a/srsue/src/stack/mac_nr/mac_nr.cc b/srsue/src/stack/mac_nr/mac_nr.cc index d050649b4..99bdccdc3 100644 --- a/srsue/src/stack/mac_nr/mac_nr.cc +++ b/srsue/src/stack/mac_nr/mac_nr.cc @@ -65,7 +65,7 @@ int mac_nr::init(const mac_nr_args_t& args_, return SRSRAN_ERROR; } - if (demux.init(rlc) != SRSRAN_SUCCESS) { + if (demux.init(rlc, phy) != SRSRAN_SUCCESS) { logger.error("Couldn't initialize demux unit."); return SRSRAN_ERROR; } diff --git a/srsue/src/stack/mac_nr/proc_ra_nr.cc b/srsue/src/stack/mac_nr/proc_ra_nr.cc index e52890351..200cc96cd 100644 --- a/srsue/src/stack/mac_nr/proc_ra_nr.cc +++ b/srsue/src/stack/mac_nr/proc_ra_nr.cc @@ -224,10 +224,13 @@ void proc_ra_nr::ra_response_reception(const mac_interface_phy_nr::tb_action_dl_ // Set Temporary-C-RNTI if provided, otherwise C-RNTI is ok phy->set_rar_grant(tb.rx_slot_idx, subpdu.get_ul_grant(), temp_crnti, srsran_rnti_type_ra); + // Apply TA CMD + current_ta = subpdu.get_ta(); + phy->set_timeadv_rar(tb.rx_slot_idx, current_ta); + // reset all parameters that are used before rar rar_rnti = SRSRAN_INVALID_RNTI; mac.msg3_prepare(); - current_ta = subpdu.get_ta(); // Set Backoff parameter if (subpdu.has_backoff()) { 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 fa4a178a3..22d652bf6 100644 --- a/srsue/src/stack/mac_nr/test/mac_nr_test.cc +++ b/srsue/src/stack/mac_nr/test/mac_nr_test.cc @@ -52,6 +52,9 @@ public: bool has_valid_sr_resource(uint32_t sr_id) override { return false; } void clear_pending_grants() override {} + void set_timeadv_rar(uint32_t tti, uint32_t ta_cmd) final{}; + void set_timeadv(uint32_t tti, uint32_t ta_cmd) final{}; + private: uint32_t prach_occasion = 0; uint32_t preamble_index = 0; diff --git a/srsue/src/stack/mac_nr/test/proc_ra_nr_test.cc b/srsue/src/stack/mac_nr/test/proc_ra_nr_test.cc index 420ae38b1..0663bedb3 100644 --- a/srsue/src/stack/mac_nr/test/proc_ra_nr_test.cc +++ b/srsue/src/stack/mac_nr/test/proc_ra_nr_test.cc @@ -46,6 +46,8 @@ public: { return -1; } + void set_timeadv_rar(uint32_t tti, uint32_t ta_cmd) final {} + void set_timeadv(uint32_t tti, uint32_t ta_cmd) final {} private: uint32_t prach_occasion = 0; diff --git a/srsue/src/stack/mac_nr/test/proc_sr_nr_test.cc b/srsue/src/stack/mac_nr/test/proc_sr_nr_test.cc index 4984b0e73..97cbb010d 100644 --- a/srsue/src/stack/mac_nr/test/proc_sr_nr_test.cc +++ b/srsue/src/stack/mac_nr/test/proc_sr_nr_test.cc @@ -47,6 +47,9 @@ public: bool has_valid_sr_resource(uint32_t sr_id) override { return false; } void clear_pending_grants() override {} + void set_timeadv_rar(uint32_t tti, uint32_t ta_cmd) final{}; + void set_timeadv(uint32_t tti, uint32_t ta_cmd) final{}; + private: uint32_t prach_occasion = 0; uint32_t preamble_index = 0; From f8c2fa05bfa405d123d625193b5e38157227741a Mon Sep 17 00:00:00 2001 From: Ismael Gomez Date: Tue, 22 Mar 2022 16:03:01 +0100 Subject: [PATCH 093/195] srsue,phy: support n_ta_offset parameter from sib1 --- lib/include/srsran/common/phy_cfg_nr.h | 3 ++- srsue/hdr/phy/nr/sync_sa.h | 1 + srsue/hdr/phy/ta_control.h | 13 +++++++++++++ srsue/src/phy/phy_nr_sa.cc | 3 +++ srsue/src/phy/sync_sa.cc | 5 +++++ srsue/src/stack/rrc_nr/rrc_nr.cc | 20 ++++++++++++++++++++ 6 files changed, 44 insertions(+), 1 deletion(-) diff --git a/lib/include/srsran/common/phy_cfg_nr.h b/lib/include/srsran/common/phy_cfg_nr.h index 37c1ca499..637b0b114 100644 --- a/lib/include/srsran/common/phy_cfg_nr.h +++ b/lib/include/srsran/common/phy_cfg_nr.h @@ -45,7 +45,8 @@ struct phy_cfg_nr_t { srsran_harq_ack_cfg_hl_t harq_ack = {}; srsran_csi_hl_cfg_t csi = {}; srsran_carrier_nr_t carrier = {}; - ssb_cfg_t ssb; + ssb_cfg_t ssb = {}; + uint32_t t_offset = 0; ///< n-TimingAdvanceOffset phy_cfg_nr_t() {} diff --git a/srsue/hdr/phy/nr/sync_sa.h b/srsue/hdr/phy/nr/sync_sa.h index 1ff410570..59f015a99 100644 --- a/srsue/hdr/phy/nr/sync_sa.h +++ b/srsue/hdr/phy/nr/sync_sa.h @@ -81,6 +81,7 @@ public: void add_ta_cmd_rar(uint32_t tti, uint32_t ta_cmd); void add_ta_cmd_new(uint32_t tti, uint32_t ta_cmd); + void add_ta_offset(uint32_t ta_offset); private: stack_interface_phy_nr* stack = nullptr; ///< Stand-Alone RRC interface diff --git a/srsue/hdr/phy/ta_control.h b/srsue/hdr/phy/ta_control.h index a1891bbcf..6246acd35 100644 --- a/srsue/hdr/phy/ta_control.h +++ b/srsue/hdr/phy/ta_control.h @@ -95,6 +95,19 @@ public: next_base_sec * 1e6f); } + void add_ta_offset(uint32_t ta_offset) + { + std::lock_guard lock(mutex); + + // Assuming numerology 0 + next_base_nta = ta_offset / 64; + + // Update base in seconds + next_base_sec = static_cast(next_base_nta) * SRSRAN_LTE_TS; + + logger.info("PHY: Set TA offset: n_ta_offset: %d, ta_usec: %.1f", next_base_nta, next_base_sec * 1e6f); + } + /** * Increments (delta) the next base time according to time alignment command from a Random Access Response (RAR). * diff --git a/srsue/src/phy/phy_nr_sa.cc b/srsue/src/phy/phy_nr_sa.cc index 83cc1c319..df7334339 100644 --- a/srsue/src/phy/phy_nr_sa.cc +++ b/srsue/src/phy/phy_nr_sa.cc @@ -258,6 +258,9 @@ bool phy_nr_sa::set_config(const srsran::phy_cfg_nr_t& cfg) // Set UE configuration bool ret = workers.set_config(config_nr); + // Pass n_ta_offset to sync + sync.add_ta_offset(config_nr.t_offset); + // Notify PHY config completion if (stack != nullptr) { stack->set_phy_config_complete(ret); diff --git a/srsue/src/phy/sync_sa.cc b/srsue/src/phy/sync_sa.cc index 464ceded1..68d8ade90 100644 --- a/srsue/src/phy/sync_sa.cc +++ b/srsue/src/phy/sync_sa.cc @@ -93,6 +93,11 @@ void sync_sa::add_ta_cmd_new(uint32_t tti_, uint32_t ta_cmd) ta.add_ta_cmd_new(tti_, ta_cmd); } +void sync_sa::add_ta_offset(uint32_t ta_offset) +{ + ta.add_ta_offset(ta_offset); +} + void sync_sa::cell_go_idle() { std::unique_lock ul(rrc_mutex); diff --git a/srsue/src/stack/rrc_nr/rrc_nr.cc b/srsue/src/stack/rrc_nr/rrc_nr.cc index e8b762e36..3119532c7 100644 --- a/srsue/src/stack/rrc_nr/rrc_nr.cc +++ b/srsue/src/stack/rrc_nr/rrc_nr.cc @@ -486,6 +486,26 @@ void rrc_nr::handle_sib1(const sib1_s& sib1) // Apply SSB Config fill_phy_ssb_cfg(sib1.serving_cell_cfg_common, &phy_cfg.ssb); + // Apply n-TimingAdvanceOffset + if (sib1.serving_cell_cfg_common.n_timing_advance_offset_present) { + switch (sib1.serving_cell_cfg_common.n_timing_advance_offset.value) { + case serving_cell_cfg_common_sib_s::n_timing_advance_offset_opts::n0: + phy_cfg.t_offset = 0; + break; + case serving_cell_cfg_common_sib_s::n_timing_advance_offset_opts::n25600: + phy_cfg.t_offset = 25600; + break; + case serving_cell_cfg_common_sib_s::n_timing_advance_offset_opts::n39936: + phy_cfg.t_offset = 39936; + break; + default: + logger.error("Invalid n_ta_offset option"); + break; + } + } else { + phy_cfg.t_offset = 25600; + } + phy_cfg_state = PHY_CFG_STATE_SA_SIB_CFG; if (not phy->set_config(phy_cfg)) { logger.warning("Could not set phy config."); From a1905072b84f42fe7552686b733323b0f61f9fca Mon Sep 17 00:00:00 2001 From: Ismael Gomez Date: Tue, 29 Mar 2022 12:41:55 +0200 Subject: [PATCH 094/195] srsue,rrc_nr: check SDAP configuration before passing to SDAP module --- srsue/src/stack/rrc_nr/rrc_nr.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/srsue/src/stack/rrc_nr/rrc_nr.cc b/srsue/src/stack/rrc_nr/rrc_nr.cc index 3119532c7..093e0181d 100644 --- a/srsue/src/stack/rrc_nr/rrc_nr.cc +++ b/srsue/src/stack/rrc_nr/rrc_nr.cc @@ -1927,6 +1927,14 @@ bool rrc_nr::apply_drb_add_mod(const drb_to_add_mod_s& drb_cfg) } else if (drb_cfg.cn_assoc.type() == drb_to_add_mod_s::cn_assoc_c_::types_opts::sdap_cfg) { const auto& sdap_cfg = drb_cfg.cn_assoc.sdap_cfg(); + // Check supported configuration + if (sdap_cfg.sdap_hdr_dl.value == sdap_cfg_s::sdap_hdr_dl_opts::present || !sdap_cfg.default_drb || + sdap_cfg.mapped_qos_flows_to_add.size() != 1) { + logger.error( + "Configuring SDAP: only UL headder is supported. Default DRB must be set and number of QoS flows must be 1"); + return false; + } + sdap_interface_rrc::bearer_cfg_t sdap_bearer_cfg = {}; sdap_bearer_cfg.add_downlink_header = sdap_cfg.sdap_hdr_dl.value == sdap_cfg_s::sdap_hdr_dl_opts::present; sdap_bearer_cfg.add_uplink_header = sdap_cfg.sdap_hdr_ul.value == sdap_cfg_s::sdap_hdr_ul_opts::present; From 6ffa036b844a5c6f4f4a418106df080ef50a307a Mon Sep 17 00:00:00 2001 From: Ismael Gomez Date: Tue, 29 Mar 2022 12:42:26 +0200 Subject: [PATCH 095/195] srsue,stack: initialize sdap pointers --- srsue/hdr/stack/mac_nr/demux_nr.h | 2 +- srsue/hdr/stack/ue_stack_lte.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/srsue/hdr/stack/mac_nr/demux_nr.h b/srsue/hdr/stack/mac_nr/demux_nr.h index 38e8faa1c..8d896a1da 100644 --- a/srsue/hdr/stack/mac_nr/demux_nr.h +++ b/srsue/hdr/stack/mac_nr/demux_nr.h @@ -56,7 +56,7 @@ private: uint64_t received_crueid = 0; - ///< currently only DCH PDUs supported (add BCH, PCH, etc) + ///< currently only DCH & BCH PDUs supported (add PCH, etc) srsran::block_queue pdu_queue; srsran::block_queue bcch_queue; diff --git a/srsue/hdr/stack/ue_stack_lte.h b/srsue/hdr/stack/ue_stack_lte.h index 461866765..ef8a2b99f 100644 --- a/srsue/hdr/stack/ue_stack_lte.h +++ b/srsue/hdr/stack/ue_stack_lte.h @@ -65,8 +65,8 @@ public: } private: - pdcp* parent_pdcp; - sdap* parent_sdap; + pdcp* parent_pdcp = nullptr; + sdap* parent_sdap = nullptr; }; class ue_stack_lte final : public ue_stack_base, From 718c0ba2cd1bcf4dc4024bd44313b4c5c5e9bb50 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Fri, 25 Mar 2022 17:03:42 +0000 Subject: [PATCH 096/195] lib,rlc: make it easier to save rlc_stress test result log to a file --- lib/test/rlc/rlc_stress_test.cc | 11 +++++++++++ lib/test/rlc/rlc_stress_test.h | 2 ++ 2 files changed, 13 insertions(+) diff --git a/lib/test/rlc/rlc_stress_test.cc b/lib/test/rlc/rlc_stress_test.cc index b1ea4ba29..96cc73fed 100644 --- a/lib/test/rlc/rlc_stress_test.cc +++ b/lib/test/rlc/rlc_stress_test.cc @@ -229,6 +229,17 @@ void rlc_tester::run_thread() void stress_test(stress_test_args_t args) { + auto log_sink = + (args.log_filename == "stdout") ? srslog::create_stdout_sink() : srslog::create_file_sink(args.log_filename); + if (!log_sink) { + return; + } + srslog::log_channel* chan = srslog::create_log_channel("main_channel", *log_sink); + if (!chan) { + return; + } + srslog::set_default_sink(*log_sink); + auto& log1 = srslog::fetch_basic_logger("RLC_1", false); log1.set_level(static_cast(args.log_level)); log1.set_hex_dump_max_size(args.log_hex_limit); diff --git a/lib/test/rlc/rlc_stress_test.h b/lib/test/rlc/rlc_stress_test.h index a904586ac..acd4657e4 100644 --- a/lib/test/rlc/rlc_stress_test.h +++ b/lib/test/rlc/rlc_stress_test.h @@ -73,6 +73,7 @@ typedef struct { uint32_t nof_pdu_tti; uint32_t max_retx; int32_t log_hex_limit; + std::string log_filename; uint32_t min_sdu_size; uint32_t max_sdu_size; } stress_test_args_t; @@ -102,6 +103,7 @@ void parse_args(stress_test_args_t* args, int argc, char* argv[]) ("pdu_cut_rate", bpo::value(&args->pdu_cut_rate)->default_value(0.0), "Rate at which RLC PDUs are chopped in length") ("pdu_duplicate_rate", bpo::value(&args->pdu_duplicate_rate)->default_value(0.0), "Rate at which RLC PDUs are duplicated") ("loglevel", bpo::value(&args->log_level)->default_value((int)srslog::basic_levels::debug), "Log level (1=Error,2=Warning,3=Info,4=Debug)") + ("log_filename", bpo::value(&args->log_filename)->default_value("stdout"), "Filename to save log to") ("singletx", bpo::value(&args->single_tx)->default_value(false), "If set to true, only one node is generating data") ("pcap", bpo::value(&args->write_pcap)->default_value(false), "Whether to write all RLC PDU to PCAP file") ("zeroseed", bpo::value(&args->zero_seed)->default_value(false), "Whether to initialize random seed to zero") From 87a46563077846f6d038d8adebe94b89511c8cf2 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Thu, 24 Mar 2022 18:50:40 +0000 Subject: [PATCH 097/195] lib,rlc_am_nr: fix for transmitting invalid NACKs --- lib/src/rlc/rlc_am_nr.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 5bef44e90..1bda0f389 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -762,7 +762,7 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) // NACK'ing missing bytes in SDU segment. // Retransmit all SDU segments within those missing bytes. if (pdu.segment_list.empty()) { - RlcError("Received NACK with SO, but there is no segment information"); + RlcError("Received NACK with SO, but there is no segment information. SN=%d", nack_sn); } for (std::list::iterator segm = pdu.segment_list.begin(); segm != pdu.segment_list.end(); @@ -1326,9 +1326,11 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m // only update ACK_SN if this SN has been fully received status->ack_sn = i; } else { + status->nacks[status->N_nack] = {}; if (not rx_window->has_sn(i)) { // No segment received, NACK the whole SDU status->nacks[status->N_nack].nack_sn = i; + status->nacks[status->N_nack].has_so = false; status->N_nack++; } else if (not(*rx_window)[i].fully_received) { // Some segments were received, but not all. From 1d4f10fe0d39d6d1e6ea7eeedd567251e446fe02 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Fri, 25 Mar 2022 16:33:50 +0100 Subject: [PATCH 098/195] lib,rlc_am_nr: fix search start point for update of rx_next --- lib/src/rlc/rlc_am_nr.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 1bda0f389..6bf69963f 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -1155,8 +1155,8 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) */ if (rx_mod_base_nr(header.sn) == rx_mod_base_nr(st.rx_next)) { uint32_t sn_upd = 0; - uint32_t window_top = st.rx_next + am_window_size(cfg.rx_sn_field_length); - for (sn_upd = st.rx_next; rx_mod_base_nr(sn_upd) < rx_mod_base_nr(st.rx_next_highest); + // we just completed SDU with x = RX_Next; start search from RX_Next+1 + for (sn_upd = (st.rx_next + 1) % mod_nr; rx_mod_base_nr(sn_upd) < rx_mod_base_nr(st.rx_next_highest); sn_upd = (sn_upd + 1) % mod_nr) { if (rx_window->has_sn(sn_upd)) { if (not(*rx_window)[sn_upd].fully_received) { From 51006bbab81e0837335a9caf7864bd84fb866ae9 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Mon, 28 Mar 2022 11:04:40 +0200 Subject: [PATCH 099/195] lib,rlc_am_nr: fix out-of-window assignment of rx_highest_status Also add reminders for (not yet included) handling of SDUs with segment gaps, i.e. "...and there is at least one missing byte segment of the SDU associated with [...] before the last byte of all received segments of this SDU" --- lib/src/rlc/rlc_am_nr.cc | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 6bf69963f..e26100869 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -1189,6 +1189,15 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) if (st.rx_next_status_trigger == st.rx_next) { stop_reassembly_timer = true; } + // TODO: Finish this condition + // if (rx_mod_base_nr(st.rx_next_status_trigger) == rx_mod_base_nr(st.rx_next + 1)) { + // if (not (*rx_window)[st.rx_next].has_segment_gap) { + // stop_reassembly_timer = true; + // } + // } + if (not inside_rx_window(st.rx_next_status_trigger)) { + stop_reassembly_timer = true; + } if (stop_reassembly_timer) { reassembly_timer.stop(); } @@ -1204,12 +1213,15 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) * - set RX_Next_Status_Trigger to RX_Next_Highest. */ bool restart_reassembly_timer = false; - if (st.rx_next_highest > st.rx_next + 1) { + if (rx_mod_base_nr(st.rx_next_highest) > rx_mod_base_nr(st.rx_next + 1)) { restart_reassembly_timer = true; } - if (st.rx_next_highest == st.rx_next + 1 && rx_window->has_sn(st.rx_next) && - not(*rx_window)[st.rx_next].fully_received) { - restart_reassembly_timer = true; + if (rx_mod_base_nr(st.rx_next_highest) == rx_mod_base_nr(st.rx_next + 1)) { + // TODO: Replace this condition by + // if ((*rx_window)[st.rx_next].has_segment_gap) { + if (rx_window->has_sn(st.rx_next) && not(*rx_window)[st.rx_next].fully_received) { + restart_reassembly_timer = true; + } } if (restart_reassembly_timer) { reassembly_timer.run(); @@ -1425,12 +1437,16 @@ void rlc_am_nr_rx::timer_expired(uint32_t timeout_id) } } st.rx_highest_status = sn_upd; + srsran_assert(rx_mod_base_nr(st.rx_next) < rx_mod_base_nr(sn_upd), + "Error: rx_highest_status assigned outside receive window"); bool restart_reassembly_timer = false; if (rx_mod_base_nr(st.rx_next_highest) > rx_mod_base_nr(st.rx_highest_status + 1)) { restart_reassembly_timer = true; } if (rx_mod_base_nr(st.rx_next_highest) == rx_mod_base_nr(st.rx_highest_status + 1)) { + // TODO: Replace this condition "not(*rx_window)[st.rx_highest_status].fully_received" by + // if ((*rx_window)[st.rx_next].has_segment_gap) { if (not rx_window->has_sn(st.rx_highest_status) || (rx_window->has_sn(st.rx_highest_status) && not(*rx_window)[st.rx_highest_status].fully_received)) { restart_reassembly_timer = true; From d8cb4ec700bcb375cbd9a3e929c1d04b042f9792 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Tue, 29 Mar 2022 09:11:01 +0200 Subject: [PATCH 100/195] lib,rlc_am_nr: detect gaps in sequence of received SDU segments This is required for checks such as "there is at least one missing byte segment [...] before the last byte of all received segments of this SDU" --- lib/include/srsran/rlc/rlc_am_nr.h | 7 +++- lib/include/srsran/rlc/rlc_am_nr_packing.h | 1 + lib/src/rlc/rlc_am_nr.cc | 37 ++++++++++++++-------- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_am_nr.h b/lib/include/srsran/rlc/rlc_am_nr.h index db954dcad..94d57fabf 100644 --- a/lib/include/srsran/rlc/rlc_am_nr.h +++ b/lib/include/srsran/rlc/rlc_am_nr.h @@ -230,7 +230,12 @@ public: bool inside_rx_window(uint32_t sn); void write_to_upper_layers(uint32_t lcid, unique_byte_buffer_t sdu); void insert_received_segment(rlc_amd_rx_pdu_nr segment, rlc_amd_rx_sdu_nr_t::segment_list_t& segment_list) const; - bool have_all_segments_been_received(const rlc_amd_rx_sdu_nr_t::segment_list_t& segment_list) const; + /** + * @brief update_segment_inventory This function updates the flags has_gap and fully_received of an SDU + * according to the current inventory of received SDU segments + * @param rx_sdu The SDU to operate on + */ + void update_segment_inventory(rlc_amd_rx_sdu_nr_t& rx_sdu) const; // Metrics uint32_t get_sdu_rx_latency_ms() final; diff --git a/lib/include/srsran/rlc/rlc_am_nr_packing.h b/lib/include/srsran/rlc/rlc_am_nr_packing.h index 7f1b47afa..5df015542 100644 --- a/lib/include/srsran/rlc/rlc_am_nr_packing.h +++ b/lib/include/srsran/rlc/rlc_am_nr_packing.h @@ -61,6 +61,7 @@ struct rlc_amd_rx_pdu_nr_cmp { struct rlc_amd_rx_sdu_nr_t { uint32_t rlc_sn = 0; bool fully_received = false; + bool has_gap = false; unique_byte_buffer_t buf; using segment_list_t = std::set; segment_list_t segments; diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index e26100869..baa4e86d5 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -1255,6 +1255,7 @@ int rlc_am_nr_rx::handle_full_data_sdu(const rlc_am_nr_pdu_header_t& header, con memcpy(rx_sdu.buf->msg, payload + hdr_len, nof_bytes - hdr_len); // Don't copy header rx_sdu.buf->N_bytes = nof_bytes - hdr_len; rx_sdu.fully_received = true; + rx_sdu.has_gap = false; return SRSRAN_SUCCESS; } @@ -1296,7 +1297,7 @@ int rlc_am_nr_rx::handle_segment_data_sdu(const rlc_am_nr_pdu_header_t& header, insert_received_segment(std::move(pdu_segment), rx_sdu.segments); // Check weather all segments have been received - rx_sdu.fully_received = have_all_segments_been_received(rx_sdu.segments); + update_segment_inventory(rx_sdu); if (rx_sdu.fully_received) { RlcInfo("Fully received segmented SDU. SN=%d.", header.sn); rx_sdu.buf = srsran::make_byte_buffer(); @@ -1305,6 +1306,7 @@ int rlc_am_nr_rx::handle_segment_data_sdu(const rlc_am_nr_pdu_header_t& header, rx_window->remove_pdu(header.sn); return SRSRAN_ERROR; } + // Assemble SDU from segments for (const auto& it : rx_sdu.segments) { memcpy(&rx_sdu.buf->msg[rx_sdu.buf->N_bytes], it.buf->msg, it.buf->N_bytes); rx_sdu.buf->N_bytes += it.buf->N_bytes; @@ -1515,27 +1517,34 @@ void rlc_am_nr_rx::insert_received_segment(rlc_amd_rx_pdu_nr segment_list.insert(std::move(segment)); } -bool rlc_am_nr_rx::have_all_segments_been_received( - const std::set& segment_list) const +void rlc_am_nr_rx::update_segment_inventory(rlc_amd_rx_sdu_nr_t& rx_sdu) const { - if (segment_list.empty()) { - return false; - } - - // Check if we have received the last segment - if (segment_list.rbegin()->header.si != rlc_nr_si_field_t::last_segment) { - return false; + if (rx_sdu.segments.empty()) { + rx_sdu.fully_received = false; + rx_sdu.has_gap = false; + return; } - // Check if all segments have been received + // Check for gaps and if all segments have been received uint32_t next_byte = 0; - for (const auto& it : segment_list) { + for (const auto& it : rx_sdu.segments) { if (it.header.so != next_byte) { - return false; + // Found gap: set flags and return + rx_sdu.has_gap = true; + rx_sdu.fully_received = false; + return; + } + if (it.header.si == rlc_nr_si_field_t::last_segment) { + // Reached last segment without any gaps: set flags and return + rx_sdu.has_gap = false; + rx_sdu.fully_received = true; + return; } next_byte += it.buf->N_bytes; } - return true; + // No gaps, but last segment not yet received + rx_sdu.has_gap = false; + rx_sdu.fully_received = false; } /* From 77ae5182a6cd4967343330acbe8796f8c0644ee9 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Tue, 29 Mar 2022 09:16:07 +0200 Subject: [PATCH 101/195] lib,rlc_am_nr: stop/restart reassembly timer for segmented SDUs with gaps --- lib/src/rlc/rlc_am_nr.cc | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index baa4e86d5..296e4e2c2 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -1189,12 +1189,11 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) if (st.rx_next_status_trigger == st.rx_next) { stop_reassembly_timer = true; } - // TODO: Finish this condition - // if (rx_mod_base_nr(st.rx_next_status_trigger) == rx_mod_base_nr(st.rx_next + 1)) { - // if (not (*rx_window)[st.rx_next].has_segment_gap) { - // stop_reassembly_timer = true; - // } - // } + if (rx_mod_base_nr(st.rx_next_status_trigger) == rx_mod_base_nr(st.rx_next + 1)) { + if (not(*rx_window)[st.rx_next].has_gap) { + stop_reassembly_timer = true; + } + } if (not inside_rx_window(st.rx_next_status_trigger)) { stop_reassembly_timer = true; } @@ -1217,9 +1216,7 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) restart_reassembly_timer = true; } if (rx_mod_base_nr(st.rx_next_highest) == rx_mod_base_nr(st.rx_next + 1)) { - // TODO: Replace this condition by - // if ((*rx_window)[st.rx_next].has_segment_gap) { - if (rx_window->has_sn(st.rx_next) && not(*rx_window)[st.rx_next].fully_received) { + if (rx_window->has_sn(st.rx_next) && (*rx_window)[st.rx_next].has_gap) { restart_reassembly_timer = true; } } @@ -1447,10 +1444,7 @@ void rlc_am_nr_rx::timer_expired(uint32_t timeout_id) restart_reassembly_timer = true; } if (rx_mod_base_nr(st.rx_next_highest) == rx_mod_base_nr(st.rx_highest_status + 1)) { - // TODO: Replace this condition "not(*rx_window)[st.rx_highest_status].fully_received" by - // if ((*rx_window)[st.rx_next].has_segment_gap) { - if (not rx_window->has_sn(st.rx_highest_status) || - (rx_window->has_sn(st.rx_highest_status) && not(*rx_window)[st.rx_highest_status].fully_received)) { + if (rx_window->has_sn(st.rx_highest_status) && (*rx_window)[st.rx_highest_status].has_gap) { restart_reassembly_timer = true; } } From aabd22f4930efb30a3dbd82483be9f8c4216035c Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Tue, 29 Mar 2022 18:02:58 +0200 Subject: [PATCH 102/195] lib,rlc_am_nr: cosmetic change in assert rx_highest_status --- lib/src/rlc/rlc_am_nr.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 296e4e2c2..6334daeb0 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -1436,8 +1436,7 @@ void rlc_am_nr_rx::timer_expired(uint32_t timeout_id) } } st.rx_highest_status = sn_upd; - srsran_assert(rx_mod_base_nr(st.rx_next) < rx_mod_base_nr(sn_upd), - "Error: rx_highest_status assigned outside receive window"); + srsran_assert(inside_rx_window(st.rx_highest_status), "Error: rx_highest_status assigned outside rx window"); bool restart_reassembly_timer = false; if (rx_mod_base_nr(st.rx_next_highest) > rx_mod_base_nr(st.rx_highest_status + 1)) { From dd7bd351b35320e949ae78c05ee16eacfc101ee7 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Tue, 29 Mar 2022 17:45:36 +0200 Subject: [PATCH 103/195] lib,rlc_am_nr: fix pack/unpack of NACKs in status PDUs --- lib/include/srsran/rlc/rlc_am_nr_packing.h | 15 ++--- lib/src/rlc/rlc_am_nr.cc | 36 ++++++----- lib/src/rlc/rlc_am_nr_packing.cc | 58 ++++++++--------- lib/test/rlc/rlc_am_nr_pdu_test.cc | 20 +++--- lib/test/rlc/rlc_am_nr_test.cc | 74 ++++++++++++++-------- 5 files changed, 110 insertions(+), 93 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_am_nr_packing.h b/lib/include/srsran/rlc/rlc_am_nr_packing.h index 5df015542..209323795 100644 --- a/lib/include/srsran/rlc/rlc_am_nr_packing.h +++ b/lib/include/srsran/rlc/rlc_am_nr_packing.h @@ -82,17 +82,16 @@ struct rlc_amd_tx_sdu_nr_t { struct rlc_am_nr_status_pdu_t { rlc_am_nr_control_pdu_type_t cpt; uint32_t ack_sn; ///< SN of the next not received RLC Data PDU - uint16_t N_nack; ///< number of NACKs std::vector nacks; - rlc_am_nr_status_pdu_t() : - cpt(rlc_am_nr_control_pdu_type_t::status_pdu), ack_sn(INVALID_RLC_SN), N_nack(0), nacks(RLC_AM_NR_TYP_NACKS) - {} + rlc_am_nr_status_pdu_t() : cpt(rlc_am_nr_control_pdu_type_t::status_pdu), ack_sn(INVALID_RLC_SN), nacks(0) + { + nacks.reserve(RLC_AM_NR_TYP_NACKS); + } void reset() { cpt = rlc_am_nr_control_pdu_type_t::status_pdu; ack_sn = INVALID_RLC_SN; - N_nack = 0; nacks.clear(); } }; @@ -147,10 +146,10 @@ void log_rlc_am_nr_status_pdu_to_string(srslog::log_channel& log_ch, return; } fmt::memory_buffer buffer; - fmt::format_to(buffer, "ACK_SN = {}, N_nack = {}", status->ack_sn, status->N_nack); - if (status->N_nack > 0) { + fmt::format_to(buffer, "ACK_SN = {}, N_nack = {}", status->ack_sn, status->nacks.size()); + if (status->nacks.size() > 0) { fmt::format_to(buffer, ", NACK_SN = "); - for (uint32_t i = 0; i < status->N_nack; ++i) { + for (uint32_t i = 0; i < status->nacks.size(); ++i) { if (status->nacks[i].has_so) { fmt::format_to( buffer, "[{} {}:{}]", status->nacks[i].nack_sn, status->nacks[i].so_start, status->nacks[i].so_end); diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 6334daeb0..45d7bd8e3 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -719,7 +719,7 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) * retransmission. */ // Process ACKs - uint32_t stop_sn = status.N_nack == 0 + uint32_t stop_sn = status.nacks.size() == 0 ? status.ack_sn : status.nacks[0].nack_sn; // Stop processing ACKs at the first NACK, if it exists. if (stop_sn > st.tx_next) { @@ -745,7 +745,7 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) // Process N_nacks std::set retx_sn_set; // Set of PDU SNs added for retransmission (no duplicates) - for (uint32_t nack_idx = 0; nack_idx < status.N_nack; nack_idx++) { + for (uint32_t nack_idx = 0; nack_idx < status.nacks.size(); nack_idx++) { if (status.nacks[nack_idx].has_nack_range) { RlcError("Handling NACK ranges is not yet implemented. Ignoring NACK across %d SDU(s) starting from SN=%d", status.nacks[nack_idx].nack_range, @@ -1322,7 +1322,7 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m return 0; } - status->N_nack = 0; + status->nacks.clear(); status->ack_sn = st.rx_next; // Start with the lower end of the window byte_buffer_t tmp_buf; @@ -1337,12 +1337,12 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m // only update ACK_SN if this SN has been fully received status->ack_sn = i; } else { - status->nacks[status->N_nack] = {}; if (not rx_window->has_sn(i)) { // No segment received, NACK the whole SDU - status->nacks[status->N_nack].nack_sn = i; - status->nacks[status->N_nack].has_so = false; - status->N_nack++; + rlc_status_nack_t nack; + nack.nack_sn = i; + nack.has_so = false; + status->nacks.push_back(nack); } else if (not(*rx_window)[i].fully_received) { // Some segments were received, but not all. // NACK non consecutive missing bytes @@ -1351,11 +1351,12 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m for (auto segm = (*rx_window)[i].segments.begin(); segm != (*rx_window)[i].segments.end(); segm++) { if (segm->header.so != last_so) { // Some bytes were not received - status->nacks[status->N_nack].nack_sn = i; - status->nacks[status->N_nack].has_so = true; - status->nacks[status->N_nack].so_start = last_so; - status->nacks[status->N_nack].so_end = segm->header.so - 1; // set to last missing byte - status->N_nack++; + rlc_status_nack_t nack; + nack.nack_sn = i; + nack.has_so = true; + nack.so_start = last_so; + nack.so_end = segm->header.so - 1; // set to last missing byte + status->nacks.push_back(nack); } if (segm->header.si == rlc_nr_si_field_t::last_segment) { last_segment_rx = true; @@ -1363,11 +1364,12 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m last_so = segm->header.so + segm->buf->N_bytes; } if (not last_segment_rx) { - status->nacks[status->N_nack].nack_sn = i; - status->nacks[status->N_nack].has_so = true; - status->nacks[status->N_nack].so_start = last_so; - status->nacks[status->N_nack].so_end = so_end_of_sdu; - status->N_nack++; + rlc_status_nack_t nack; + nack.nack_sn = i; + nack.has_so = true; + nack.so_start = last_so; + nack.so_end = so_end_of_sdu; + status->nacks.push_back(nack); } } } diff --git a/lib/src/rlc/rlc_am_nr_packing.cc b/lib/src/rlc/rlc_am_nr_packing.cc index eac502c8e..e0d5d69ba 100644 --- a/lib/src/rlc/rlc_am_nr_packing.cc +++ b/lib/src/rlc/rlc_am_nr_packing.cc @@ -168,6 +168,7 @@ uint32_t rlc_am_nr_read_status_pdu_12bit_sn(const uint8_t* payload, const uint32_t nof_bytes, rlc_am_nr_status_pdu_t* status) { uint8_t* ptr = const_cast(payload); + status->reset(); // fixed part status->cpt = (rlc_am_nr_control_pdu_type_t)((*ptr >> 4) & 0x07); // 3 bits CPT @@ -196,9 +197,6 @@ rlc_am_nr_read_status_pdu_12bit_sn(const uint8_t* payload, const uint32_t nof_by // all good, continue with next byte depending on E1 ptr++; - // reset number of acks - status->N_nack = 0; - while (e1 != 0) { // E1 flag set, read a NACK_SN rlc_status_nack_t nack = {}; @@ -215,26 +213,25 @@ rlc_am_nr_read_status_pdu_12bit_sn(const uint8_t* payload, const uint32_t nof_by return 0; } nack.nack_sn |= (*ptr & 0xF0) >> 4; - status->nacks[status->N_nack] = nack; ptr++; if (e2 != 0) { - status->nacks[status->N_nack].has_so = true; - status->nacks[status->N_nack].so_start = (*ptr) << 8; + nack.has_so = true; + nack.so_start = (*ptr) << 8; ptr++; - status->nacks[status->N_nack].so_start |= (*ptr); + nack.so_start |= (*ptr); ptr++; - status->nacks[status->N_nack].so_end = (*ptr) << 8; + nack.so_end = (*ptr) << 8; ptr++; - status->nacks[status->N_nack].so_end |= (*ptr); + nack.so_end |= (*ptr); ptr++; } if (e3 != 0) { - status->nacks[status->N_nack].has_nack_range = true; - status->nacks[status->N_nack].nack_range = (*ptr); + nack.has_nack_range = true; + nack.nack_range = (*ptr); ptr++; } - status->N_nack++; + status->nacks.push_back(nack); if (uint32_t(ptr - payload) > nof_bytes) { fprintf(stderr, "Malformed PDU, trying to read more bytes than it is available\n"); return 0; @@ -248,6 +245,7 @@ uint32_t rlc_am_nr_read_status_pdu_18bit_sn(const uint8_t* payload, const uint32_t nof_bytes, rlc_am_nr_status_pdu_t* status) { uint8_t* ptr = const_cast(payload); + status->reset(); // fixed part status->cpt = (rlc_am_nr_control_pdu_type_t)((*ptr >> 4) & 0x07); // 3 bits CPT @@ -278,9 +276,6 @@ rlc_am_nr_read_status_pdu_18bit_sn(const uint8_t* payload, const uint32_t nof_by // all good, continue with next byte depending on E1 ptr++; - // reset number of acks - status->N_nack = 0; - while (e1 != 0) { // E1 flag set, read a NACK_SN rlc_status_nack_t nack = {}; @@ -300,26 +295,25 @@ rlc_am_nr_read_status_pdu_18bit_sn(const uint8_t* payload, const uint32_t nof_by fprintf(stderr, "Malformed PDU, reserved bits are set.\n"); return 0; } - status->nacks[status->N_nack] = nack; ptr++; if (e2 != 0) { - status->nacks[status->N_nack].has_so = true; - status->nacks[status->N_nack].so_start = (*ptr) << 8; + nack.has_so = true; + nack.so_start = (*ptr) << 8; ptr++; - status->nacks[status->N_nack].so_start |= (*ptr); + nack.so_start |= (*ptr); ptr++; - status->nacks[status->N_nack].so_end = (*ptr) << 8; + nack.so_end = (*ptr) << 8; ptr++; - status->nacks[status->N_nack].so_end |= (*ptr); + nack.so_end |= (*ptr); ptr++; } if (e3 != 0) { - status->nacks[status->N_nack].has_nack_range = true; - status->nacks[status->N_nack].nack_range = (*ptr); + nack.has_nack_range = true; + nack.nack_range = (*ptr); ptr++; } - status->N_nack++; + status->nacks.push_back(nack); if (uint32_t(ptr - payload) > nof_bytes) { fprintf(stderr, "Malformed PDU, trying to read more bytes than it is available\n"); return 0; @@ -360,15 +354,15 @@ int32_t rlc_am_nr_write_status_pdu_12bit_sn(const rlc_am_nr_status_pdu_t& status ptr++; // write E1 flag in octet 3 - if (status_pdu.N_nack > 0) { + if (status_pdu.nacks.size() > 0) { *ptr = 0x80; } else { *ptr = 0x00; } ptr++; - if (status_pdu.N_nack > 0) { - for (uint32_t i = 0; i < status_pdu.N_nack; i++) { + if (status_pdu.nacks.size() > 0) { + for (uint32_t i = 0; i < status_pdu.nacks.size(); i++) { // write first 8 bit of NACK_SN *ptr = (status_pdu.nacks[i].nack_sn >> 4) & 0xff; ptr++; @@ -376,7 +370,7 @@ int32_t rlc_am_nr_write_status_pdu_12bit_sn(const rlc_am_nr_status_pdu_t& status // write remaining 4 bits of NACK_SN *ptr = (status_pdu.nacks[i].nack_sn & 0x0f) << 4; // Set E1 if necessary - if (i < (uint32_t)(status_pdu.N_nack - 1)) { + if (i < (uint32_t)(status_pdu.nacks.size() - 1)) { *ptr |= 0x08; } @@ -427,20 +421,20 @@ int32_t rlc_am_nr_write_status_pdu_18bit_sn(const rlc_am_nr_status_pdu_t& status *ptr = (status_pdu.ack_sn << 2) & 0xFC; // lower 6 bits of SN // set E1 flag if necessary - if (status_pdu.N_nack > 0) { + if (status_pdu.nacks.size() > 0) { *ptr |= 0x02; } ptr++; - if (status_pdu.N_nack > 0) { - for (uint32_t i = 0; i < status_pdu.N_nack; i++) { + if (status_pdu.nacks.size() > 0) { + for (uint32_t i = 0; i < status_pdu.nacks.size(); i++) { *ptr = (status_pdu.nacks[i].nack_sn >> 10) & 0xFF; // upper 8 bits of SN ptr++; *ptr = (status_pdu.nacks[i].nack_sn >> 2) & 0xFF; // center 8 bits of SN ptr++; *ptr = (status_pdu.nacks[i].nack_sn << 6) & 0xC0; // lower 2 bits of SN - if (i < (uint32_t)(status_pdu.N_nack - 1)) { + if (i < (uint32_t)(status_pdu.nacks.size() - 1)) { *ptr |= 0x20; // Set E1 } if (status_pdu.nacks[i].has_so) { diff --git a/lib/test/rlc/rlc_am_nr_pdu_test.cc b/lib/test/rlc/rlc_am_nr_pdu_test.cc index 00c9d9861..cad7e8bc3 100644 --- a/lib/test/rlc/rlc_am_nr_pdu_test.cc +++ b/lib/test/rlc/rlc_am_nr_pdu_test.cc @@ -236,7 +236,7 @@ int rlc_am_nr_control_pdu_12bit_sn_test1() rlc_am_nr_status_pdu_t status_pdu = {}; TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &status_pdu) == SRSRAN_SUCCESS); TESTASSERT(status_pdu.ack_sn == 2065); - TESTASSERT(status_pdu.N_nack == 0); + TESTASSERT(status_pdu.nacks.size() == 0); // reset status PDU pdu.clear(); @@ -266,7 +266,7 @@ int rlc_am_nr_control_pdu_12bit_sn_test2() rlc_am_nr_status_pdu_t status_pdu = {}; TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &status_pdu) == SRSRAN_SUCCESS); TESTASSERT(status_pdu.ack_sn == 2065); - TESTASSERT(status_pdu.N_nack == 1); + TESTASSERT(status_pdu.nacks.size() == 1); TESTASSERT(status_pdu.nacks[0].nack_sn == 273); // reset status PDU @@ -299,7 +299,7 @@ int rlc_am_nr_control_pdu_12bit_sn_test3() rlc_am_nr_status_pdu_t status_pdu = {}; TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &status_pdu) == SRSRAN_SUCCESS); TESTASSERT(status_pdu.ack_sn == 2065); - TESTASSERT(status_pdu.N_nack == 2); + TESTASSERT(status_pdu.nacks.size() == 2); TESTASSERT(status_pdu.nacks[0].nack_sn == 273); TESTASSERT(status_pdu.nacks[0].so_start == 2); TESTASSERT(status_pdu.nacks[0].so_end == 5); @@ -335,7 +335,7 @@ int rlc_am_nr_control_pdu_12bit_sn_test4() rlc_am_nr_status_pdu_t status_pdu = {}; TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &status_pdu) == SRSRAN_SUCCESS); TESTASSERT(status_pdu.ack_sn == 2065); - TESTASSERT(status_pdu.N_nack == 2); + TESTASSERT(status_pdu.nacks.size() == 2); TESTASSERT(status_pdu.nacks[0].nack_sn == 273); TESTASSERT(status_pdu.nacks[0].has_so == true); TESTASSERT(status_pdu.nacks[0].so_start == 2); @@ -407,7 +407,7 @@ int rlc_am_nr_control_pdu_12bit_sn_test_nack_range() rlc_am_nr_status_pdu_t status_pdu = {}; TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &status_pdu) == SRSRAN_SUCCESS); TESTASSERT(status_pdu.ack_sn == 2065); - TESTASSERT(status_pdu.N_nack == 2); + TESTASSERT(status_pdu.nacks.size() == 2); TESTASSERT(status_pdu.nacks[0].nack_sn == 273); TESTASSERT(status_pdu.nacks[0].has_so == false); TESTASSERT(status_pdu.nacks[0].has_nack_range == true); @@ -448,7 +448,7 @@ int rlc_am_nr_control_pdu_18bit_sn_test1() rlc_am_nr_status_pdu_t status_pdu = {}; TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == SRSRAN_SUCCESS); TESTASSERT(status_pdu.ack_sn == 235929); - TESTASSERT(status_pdu.N_nack == 0); + TESTASSERT(status_pdu.nacks.size() == 0); // reset status PDU pdu.clear(); @@ -479,7 +479,7 @@ int rlc_am_nr_control_pdu_18bit_sn_test2() rlc_am_nr_status_pdu_t status_pdu = {}; TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == SRSRAN_SUCCESS); TESTASSERT(status_pdu.ack_sn == 235929); - TESTASSERT(status_pdu.N_nack == 1); + TESTASSERT(status_pdu.nacks.size() == 1); TESTASSERT(status_pdu.nacks[0].nack_sn == 222822); // reset status PDU @@ -530,7 +530,7 @@ int rlc_am_nr_control_pdu_18bit_sn_test3() rlc_am_nr_status_pdu_t status_pdu = {}; TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == SRSRAN_SUCCESS); TESTASSERT(status_pdu.ack_sn == 235929); - TESTASSERT(status_pdu.N_nack == 2); + TESTASSERT(status_pdu.nacks.size() == 2); TESTASSERT(status_pdu.nacks[0].nack_sn == 222822); TESTASSERT(status_pdu.nacks[0].has_so == true); TESTASSERT(status_pdu.nacks[0].so_start == 2); @@ -582,7 +582,7 @@ int rlc_am_nr_control_pdu_18bit_sn_test4() rlc_am_nr_status_pdu_t status_pdu = {}; TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == SRSRAN_SUCCESS); TESTASSERT(status_pdu.ack_sn == 235929); - TESTASSERT(status_pdu.N_nack == 2); + TESTASSERT(status_pdu.nacks.size() == 2); TESTASSERT(status_pdu.nacks[0].nack_sn == 222822); TESTASSERT(status_pdu.nacks[0].has_so == true); TESTASSERT(status_pdu.nacks[0].so_start == 2); @@ -674,7 +674,7 @@ int rlc_am_nr_control_pdu_18bit_sn_test_nack_range() rlc_am_nr_status_pdu_t status_pdu = {}; TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == SRSRAN_SUCCESS); TESTASSERT(status_pdu.ack_sn == 200977); - TESTASSERT(status_pdu.N_nack == 2); + TESTASSERT(status_pdu.nacks.size() == 2); TESTASSERT(status_pdu.nacks[0].nack_sn == 69905); TESTASSERT(status_pdu.nacks[0].has_so == false); TESTASSERT(status_pdu.nacks[0].has_nack_range == true); diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index a86aa684a..8f3694723 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -393,7 +393,7 @@ int lost_pdu_test(rlc_am_nr_sn_size_t sn_size) rlc_am_nr_status_pdu_t status_check = {}; rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. - TESTASSERT_EQ(1, status_check.N_nack); // We lost one PDU. + TESTASSERT_EQ(1, status_check.nacks.size()); // We lost one PDU. TESTASSERT_EQ(3, status_check.nacks[0].nack_sn); // Lost PDU SN=3. // Write status PDU to RLC1 @@ -426,7 +426,7 @@ int lost_pdu_test(rlc_am_nr_sn_size_t sn_size) rlc_am_nr_status_pdu_t status_check = {}; rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. - TESTASSERT_EQ(0, status_check.N_nack); // All PDUs are acked now + TESTASSERT_EQ(0, status_check.nacks.size()); // All PDUs are acked now } { @@ -664,7 +664,7 @@ int segment_retx_test(rlc_am_nr_sn_size_t sn_size) rlc_am_nr_status_pdu_t status_check = {}; rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. - TESTASSERT_EQ(1, status_check.N_nack); // We lost one PDU. + TESTASSERT_EQ(1, status_check.nacks.size()); // We lost one PDU. TESTASSERT_EQ(3, status_check.nacks[0].nack_sn); // Lost PDU SN=3. // Write status PDU to RLC1 @@ -900,7 +900,7 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) rlc_am_nr_status_pdu_t status_check = {}; rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); TESTASSERT_EQ(2, status_check.ack_sn); // 5 is the next expected SN. - TESTASSERT_EQ(1, status_check.N_nack); // We lost one PDU. + TESTASSERT_EQ(1, status_check.nacks.size()); // We lost one PDU. TESTASSERT_EQ(1, status_check.nacks[0].nack_sn); // Lost SDU on SN=1. TESTASSERT_EQ(true, status_check.nacks[0].has_so); // It's a segment. TESTASSERT_EQ(0, status_check.nacks[0].so_start); // First byte missing is 0. @@ -941,7 +941,7 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) rlc_am_nr_status_pdu_t status_check = {}; rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. - TESTASSERT_EQ(3, status_check.N_nack); // We lost one PDU. + TESTASSERT_EQ(3, status_check.nacks.size()); // We lost one PDU. TESTASSERT_EQ(1, status_check.nacks[0].nack_sn); // Lost SDU on SN=1. TESTASSERT_EQ(true, status_check.nacks[0].has_so); // Lost SDU on SN=1. TESTASSERT_EQ(0, status_check.nacks[0].so_start); // Lost SDU on SN=1. @@ -1094,8 +1094,9 @@ int max_retx_lost_sdu_test(rlc_am_nr_sn_size_t sn_size) // Fake status PDU that ack SN=1 and nack SN=0 rlc_am_nr_status_pdu_t fake_status = {}; fake_status.ack_sn = 2; // delivered up to SN=1 - fake_status.N_nack = 1; // one SN was lost - fake_status.nacks[0].nack_sn = 0; // it was SN=0 that was lost + rlc_status_nack_t nack; // one SN was lost + nack.nack_sn = 0; // it was SN=0 that was lost + fake_status.nacks.push_back(nack); // pack into PDU byte_buffer_t status_pdu; @@ -1174,15 +1175,25 @@ int max_retx_lost_segments_test(rlc_am_nr_sn_size_t sn_size) // Fake status PDU that ack SN=1 and nack {SN=0 segment 0, SN=0 segment 1} rlc_am_nr_status_pdu_t status_lost_both_segments = {}; status_lost_both_segments.ack_sn = 2; // delivered up to SN=1 - status_lost_both_segments.N_nack = 2; // two segments lost - status_lost_both_segments.nacks[0].nack_sn = 0; // it was SN=0 that was lost - status_lost_both_segments.nacks[0].has_so = true; // this NACKs a segment - status_lost_both_segments.nacks[0].so_start = 0; // segment starts at (and includes) byte 0 - status_lost_both_segments.nacks[0].so_end = 12; // segment ends at (and includes) byte 12 - status_lost_both_segments.nacks[1].nack_sn = 0; // it was SN=0 that was lost - status_lost_both_segments.nacks[1].has_so = true; // this NACKs a segment - status_lost_both_segments.nacks[1].so_start = 13; // segment starts at (and includes) byte 13 - status_lost_both_segments.nacks[1].so_end = 19; // segment ends at (and includes) byte 19 + + // two segments lost + { + rlc_status_nack_t nack; + nack.nack_sn = 0; // it was SN=0 that was lost + nack.has_so = true; // this NACKs a segment + nack.so_start = 0; // segment starts at (and includes) byte 0 + nack.so_end = 12; // segment ends at (and includes) byte 12 + status_lost_both_segments.nacks.push_back(nack); + } + + { + rlc_status_nack_t nack; + nack.nack_sn = 0; // it was SN=0 that was lost + nack.has_so = true; // this NACKs a segment + nack.so_start = 13; // segment starts at (and includes) byte 13 + nack.so_end = 19; // segment ends at (and includes) byte 19 + status_lost_both_segments.nacks.push_back(nack); + } // pack into PDU byte_buffer_t status_pdu_lost_both_segments; @@ -1191,12 +1202,17 @@ int max_retx_lost_segments_test(rlc_am_nr_sn_size_t sn_size) // Fake status PDU that ack SN=1 and nack {SN=0 segment 1} rlc_am_nr_status_pdu_t status_lost_second_segment = {}; - status_lost_second_segment.ack_sn = 2; // delivered up to SN=1 - status_lost_second_segment.N_nack = 1; // one SN was lost - status_lost_second_segment.nacks[0].nack_sn = 0; // it was SN=0 that was lost - status_lost_second_segment.nacks[0].has_so = true; // this NACKs a segment - status_lost_second_segment.nacks[0].so_start = 13; // segment starts at (and includes) byte 13 - status_lost_second_segment.nacks[0].so_end = 19; // segment ends at (and includes) byte 19 + status_lost_second_segment.ack_sn = 2; // delivered up to SN=1 + + // one SN was lost + { + rlc_status_nack_t nack; + nack.nack_sn = 0; // it was SN=0 that was lost + nack.has_so = true; // this NACKs a segment + nack.so_start = 13; // segment starts at (and includes) byte 13 + nack.so_end = 19; // segment ends at (and includes) byte 19 + status_lost_second_segment.nacks.push_back(nack); + } // pack into PDU byte_buffer_t status_pdu_lost_second_segment; @@ -1501,8 +1517,11 @@ int poll_retx() TESTASSERT(status_pdu != nullptr); rlc_am_nr_status_pdu_t status = {}; status.ack_sn = 2; - status.N_nack = 1; - status.nacks[0].nack_sn = 1; // SN=1 needs RETX + { + rlc_status_nack_t nack; + nack.nack_sn = 1; // SN=1 needs RETX + status.nacks.push_back(nack); + } rlc_am_nr_write_status_pdu(status, rlc_cnfg.am_nr.tx_sn_field_length, status_pdu.get()); rlc1.write_pdu(status_pdu->msg, status_pdu->N_bytes); } @@ -1529,8 +1548,11 @@ int poll_retx() TESTASSERT(status_pdu != nullptr); rlc_am_nr_status_pdu_t status = {}; status.ack_sn = 4; - status.N_nack = 1; - status.nacks[0].nack_sn = 1; // SN=1 needs RETX + { + rlc_status_nack_t nack; + nack.nack_sn = 1; // SN=1 needs RETX + status.nacks.push_back(nack); + } rlc_am_nr_write_status_pdu(status, rlc_cnfg.am_nr.tx_sn_field_length, status_pdu.get()); rlc1.write_pdu(status_pdu->msg, status_pdu->N_bytes); } From 9ecf66f3135a5bf7e52e85f99dcb44fefd3409f7 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Tue, 29 Mar 2022 16:30:54 +0100 Subject: [PATCH 104/195] lib,rlc_am_nr: make sure that handle control PDU correctly locks the mutex --- lib/src/rlc/rlc_am_nr.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 45d7bd8e3..41daa99cd 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -707,7 +707,8 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) return; } - rlc_am_nr_status_pdu_t status = {}; + std::lock_guard lock(mutex); + rlc_am_nr_status_pdu_t status = {}; RlcHexDebug(payload, nof_bytes, "%s Rx control PDU", parent->rb_name); rlc_am_nr_read_status_pdu(payload, nof_bytes, cfg.tx_sn_field_length, &status); log_rlc_am_nr_status_pdu_to_string(logger.info, "Rx Status PDU: %s", &status, parent->rb_name); From 1180d0f24db5129fedac0275b4fcfeb7afa291de Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Thu, 31 Mar 2022 10:26:54 +0200 Subject: [PATCH 105/195] lib,rlc_am_nr: properly remove all fully received SDUs from rx window --- lib/src/rlc/rlc_am_nr.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 41daa99cd..cac05246a 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -1156,8 +1156,8 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) */ if (rx_mod_base_nr(header.sn) == rx_mod_base_nr(st.rx_next)) { uint32_t sn_upd = 0; - // we just completed SDU with x = RX_Next; start search from RX_Next+1 - for (sn_upd = (st.rx_next + 1) % mod_nr; rx_mod_base_nr(sn_upd) < rx_mod_base_nr(st.rx_next_highest); + // move rx_next forward and remove all fully received SDUs from rx_window + for (sn_upd = (st.rx_next) % mod_nr; rx_mod_base_nr(sn_upd) < rx_mod_base_nr(st.rx_next_highest); sn_upd = (sn_upd + 1) % mod_nr) { if (rx_window->has_sn(sn_upd)) { if (not(*rx_window)[sn_upd].fully_received) { From 711d9e80044ddad92353a22843c531514c3f408c Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Mon, 28 Mar 2022 11:26:10 +0200 Subject: [PATCH 106/195] gnb,rrc: fix deactivation/deletion of bearers fix regression included in f1831d902746c4bd4cd25debe0867f8f60ec0fa2 that marks srb0 to be deactivate/deleted when the UE bearers are configured. further in the code an assert makes sure that srb0 is never removed/reconfigured so we don't want lcid=0 to be included in the list of bearers --- srsgnb/src/stack/rrc/rrc_nr_ue.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srsgnb/src/stack/rrc/rrc_nr_ue.cc b/srsgnb/src/stack/rrc/rrc_nr_ue.cc index 9a7cb2601..65f6fdc1a 100644 --- a/srsgnb/src/stack/rrc/rrc_nr_ue.cc +++ b/srsgnb/src/stack/rrc/rrc_nr_ue.cc @@ -1506,7 +1506,7 @@ int rrc_nr::ue::update_mac(const cell_group_cfg_s& cell_group_config, bool is_co void rrc_nr::ue::deactivate_bearers() { // Iterate over the bearers (MAC LC CH) and set each of them to IDLE - for (uint32_t lcid = 0; lcid < SCHED_NR_MAX_LCID; ++lcid) { + for (uint32_t lcid = 1; lcid < SCHED_NR_MAX_LCID; ++lcid) { uecfg.lc_ch_to_rem.push_back(lcid); } From 1507eed79cb9e31654e4e86696febded1d531c9f Mon Sep 17 00:00:00 2001 From: Ismael Gomez Date: Thu, 31 Mar 2022 21:44:18 +0200 Subject: [PATCH 107/195] srsue,mac: return temp-rnti for UL grants if availabl --- srsue/src/phy/nr/cc_worker.cc | 6 +++--- srsue/src/stack/mac_nr/mac_nr.cc | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/srsue/src/phy/nr/cc_worker.cc b/srsue/src/phy/nr/cc_worker.cc index d1139b7fc..eda91f5d5 100644 --- a/srsue/src/phy/nr/cc_worker.cc +++ b/srsue/src/phy/nr/cc_worker.cc @@ -196,15 +196,15 @@ void cc_worker::decode_pdcch_ul() } // Search for grants - int n_dl = + int n_ul = srsran_ue_dl_nr_find_ul_dci(&ue_dl, &dl_slot_cfg, rnti.id, rnti.type, dci_rx.data(), (uint32_t)dci_rx.size()); - if (n_dl < SRSRAN_SUCCESS) { + if (n_ul < SRSRAN_SUCCESS) { logger.error("Error decoding UL NR-PDCCH"); return; } // Iterate over all received grants - for (int i = 0; i < n_dl; i++) { + for (int i = 0; i < n_ul; i++) { // Log found DCI if (logger.info.enabled()) { std::array str; diff --git a/srsue/src/stack/mac_nr/mac_nr.cc b/srsue/src/stack/mac_nr/mac_nr.cc index 99bdccdc3..e01bf1371 100644 --- a/srsue/src/stack/mac_nr/mac_nr.cc +++ b/srsue/src/stack/mac_nr/mac_nr.cc @@ -153,6 +153,10 @@ void mac_nr::update_buffer_states() mac_interface_phy_nr::sched_rnti_t mac_nr::get_ul_sched_rnti_nr(const uint32_t tti) { + if (proc_ra.has_temp_crnti() && has_crnti() == false) { + logger.debug("SCHED: Searching temp C-RNTI=0x%x (proc_ra)", proc_ra.get_temp_crnti()); + return {proc_ra.get_temp_crnti(), srsran_rnti_type_c}; + } return {rntis.get_crnti(), srsran_rnti_type_c}; } From e5e5266eea2f6458af5faa66bb56c10156257c85 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Mon, 4 Apr 2022 14:31:39 +0200 Subject: [PATCH 108/195] lib,rlc: add stress test support for NR AM18 --- lib/test/rlc/rlc_stress_test.cc | 4 +++- lib/test/rlc/rlc_stress_test.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/test/rlc/rlc_stress_test.cc b/lib/test/rlc/rlc_stress_test.cc index 96cc73fed..7b019555b 100644 --- a/lib/test/rlc/rlc_stress_test.cc +++ b/lib/test/rlc/rlc_stress_test.cc @@ -276,7 +276,9 @@ void stress_test(stress_test_args_t args) } else if (args.mode == "UM12") { cnfg_ = srsran::rlc_config_t::default_rlc_um_nr_config(12); } else if (args.mode == "AM12") { - cnfg_ = srsran::rlc_config_t::default_rlc_am_nr_config(); + cnfg_ = srsran::rlc_config_t::default_rlc_am_nr_config(12); + } else if (args.mode == "AM18") { + cnfg_ = srsran::rlc_config_t::default_rlc_am_nr_config(18); } else { std::cout << "Unsupported RLC mode " << args.mode << ", exiting." << std::endl; exit(-1); diff --git a/lib/test/rlc/rlc_stress_test.h b/lib/test/rlc/rlc_stress_test.h index acd4657e4..7237e56c6 100644 --- a/lib/test/rlc/rlc_stress_test.h +++ b/lib/test/rlc/rlc_stress_test.h @@ -92,7 +92,7 @@ void parse_args(stress_test_args_t* args, int argc, char* argv[]) bpo::options_description common("Configuration options"); common.add_options() ("rat", bpo::value(&args->rat)->default_value("LTE"), "The RLC version to use (LTE/NR)") - ("mode", bpo::value(&args->mode)->default_value("AM"), "Whether to test RLC acknowledged or unacknowledged mode (AM/UM for LTE) (UM6/UM12 for NR)") + ("mode", bpo::value(&args->mode)->default_value("AM"), "Whether to test RLC acknowledged or unacknowledged mode (AM/UM for LTE) (UM6/UM12/AM12/AM18 for NR)") ("duration", bpo::value(&args->test_duration_sec)->default_value(5), "Duration (sec)") ("sdu_size", bpo::value(&args->sdu_size)->default_value(-1), "Size of SDUs (-1 means random)") ("random_opp", bpo::value(&args->random_opp)->default_value(true), "Whether to generate random MAC opportunities") From 1aa13cee9eda2fb4690a51bf8223561ad6d968b6 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Mon, 4 Apr 2022 17:08:20 +0100 Subject: [PATCH 109/195] lib,rlc_am_nr: cleanup logging of configuration --- lib/src/rlc/rlc_am_base.cc | 32 ++++++++++++++++++++++++-------- lib/src/rlc/rlc_am_nr.cc | 5 ++--- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/lib/src/rlc/rlc_am_base.cc b/lib/src/rlc/rlc_am_base.cc index 605b1f494..3310905df 100644 --- a/lib/src/rlc/rlc_am_base.cc +++ b/lib/src/rlc/rlc_am_base.cc @@ -77,14 +77,30 @@ bool rlc_am::configure(const rlc_config_t& cfg_) return false; } - RlcInfo("configured - t_poll_retx=%d, poll_pdu=%d, poll_byte=%d, max_retx_thresh=%d, " - "t_reordering=%d, t_status_prohibit=%d", - cfg.am.t_poll_retx, - cfg.am.poll_pdu, - cfg.am.poll_byte, - cfg.am.max_retx_thresh, - cfg.am.t_reordering, - cfg.am.t_status_prohibit); + if (cfg.rat == srsran_rat_t::lte) { + RlcInfo("AM LTE configured - t_poll_retx=%d, poll_pdu=%d, poll_byte=%d, max_retx_thresh=%d, " + "t_reordering=%d, t_status_prohibit=%d", + cfg.am.t_poll_retx, + cfg.am.poll_pdu, + cfg.am.poll_byte, + cfg.am.max_retx_thresh, + cfg.am.t_reordering, + cfg.am.t_status_prohibit); + } else if (cfg.rat == srsran_rat_t::nr) { + RlcInfo("AM NR configured - tx_sn_field_length=%d, rx_sn_field_length=%d, " + "t_poll_retx=%d, poll_pdu=%d, poll_byte=%d, " + "max_retx_thresh=%d, t_reassembly=%d, t_status_prohibit=%d", + to_number(cfg.am_nr.tx_sn_field_length), + to_number(cfg.am_nr.rx_sn_field_length), + cfg.am_nr.t_poll_retx, + cfg.am_nr.poll_pdu, + cfg.am_nr.poll_byte, + cfg.am_nr.max_retx_thresh, + cfg.am_nr.t_reassembly, + cfg.am_nr.t_status_prohibit); + } else { + RlcError("Invalid RAT at entity configuration"); + } return true; } diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index cac05246a..516dca2cc 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -71,7 +71,7 @@ bool rlc_am_nr_tx::configure(const rlc_config_t& cfg_) tx_enabled = true; - RlcDebug("RLC AM NR tx entity configured."); + RlcDebug("RLC AM NR configured tx entity."); return true; } @@ -1034,7 +1034,6 @@ bool rlc_am_nr_rx::configure(const rlc_config_t& cfg_) // Configure t_reassembly timer if (cfg.t_reassembly > 0) { reassembly_timer.set(static_cast(cfg.t_reassembly), [this](uint32_t timerid) { timer_expired(timerid); }); - RlcInfo("configured reassembly timer. t-Reassembly=%d ms", cfg.t_reassembly); } mod_nr = cardinality(cfg.rx_sn_field_length); @@ -1155,7 +1154,7 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) * have been received. */ if (rx_mod_base_nr(header.sn) == rx_mod_base_nr(st.rx_next)) { - uint32_t sn_upd = 0; + uint32_t sn_upd = 0; // move rx_next forward and remove all fully received SDUs from rx_window for (sn_upd = (st.rx_next) % mod_nr; rx_mod_base_nr(sn_upd) < rx_mod_base_nr(st.rx_next_highest); sn_upd = (sn_upd + 1) % mod_nr) { From 0de1565e523f595b28ceeccec895435f9e03647e Mon Sep 17 00:00:00 2001 From: Francisco Date: Mon, 4 Apr 2022 14:49:35 +0100 Subject: [PATCH 110/195] rrc,s1ap: configurable target eNB TACs during LTE handover. --- .../srsran/interfaces/enb_rrc_interface_types.h | 1 + lib/include/srsran/interfaces/enb_s1ap_interfaces.h | 1 + srsenb/hdr/stack/rrc/rrc_mobility.h | 4 +++- srsenb/hdr/stack/s1ap/s1ap.h | 8 ++++++-- srsenb/src/enb_cfg_parser.cc | 10 +++++++++- srsenb/src/stack/rrc/rrc_mobility.cc | 8 ++++++-- srsenb/src/stack/s1ap/s1ap.cc | 11 ++++++++--- srsenb/test/common/dummy_classes.h | 1 + srsenb/test/rrc/test_helpers.h | 1 + 9 files changed, 36 insertions(+), 9 deletions(-) diff --git a/lib/include/srsran/interfaces/enb_rrc_interface_types.h b/lib/include/srsran/interfaces/enb_rrc_interface_types.h index 00e9f664f..091241445 100644 --- a/lib/include/srsran/interfaces/enb_rrc_interface_types.h +++ b/lib/include/srsran/interfaces/enb_rrc_interface_types.h @@ -34,6 +34,7 @@ struct meas_cell_cfg_t { asn1::rrc::q_offset_range_e cell_individual_offset; uint32_t allowed_meas_bw; bool direct_forward_path_available; + int tac; }; // neigh measurement Cell info diff --git a/lib/include/srsran/interfaces/enb_s1ap_interfaces.h b/lib/include/srsran/interfaces/enb_s1ap_interfaces.h index 41013bc37..b6b2a0741 100644 --- a/lib/include/srsran/interfaces/enb_s1ap_interfaces.h +++ b/lib/include/srsran/interfaces/enb_s1ap_interfaces.h @@ -80,6 +80,7 @@ public: */ virtual bool send_ho_required(uint16_t rnti, uint32_t target_eci, + uint16_t target_tac, srsran::plmn_id_t target_plmn, srsran::span fwd_erabs, srsran::unique_byte_buffer_t rrc_container, diff --git a/srsenb/hdr/stack/rrc/rrc_mobility.h b/srsenb/hdr/stack/rrc/rrc_mobility.h index 8664be0a2..4b706269c 100644 --- a/srsenb/hdr/stack/rrc/rrc_mobility.h +++ b/srsenb/hdr/stack/rrc/rrc_mobility.h @@ -57,7 +57,8 @@ private: asn1::rrc::meas_cfg_s* diff_meas_cfg); // Handover from source cell - bool start_ho_preparation(uint32_t target_eci, uint8_t measobj_id, bool fwd_direct_path_available); + bool + start_ho_preparation(uint32_t target_eci, uint16_t target_tac, uint8_t measobj_id, bool fwd_direct_path_available); // Handover to target cell void fill_mobility_reconf_common(asn1::rrc::dl_dcch_msg_s& msg, @@ -81,6 +82,7 @@ private: // events struct ho_meas_report_ev { uint32_t target_eci = 0; + uint16_t target_tac = 0; const asn1::rrc::meas_obj_to_add_mod_s* meas_obj = nullptr; bool direct_fwd_path = false; }; diff --git a/srsenb/hdr/stack/s1ap/s1ap.h b/srsenb/hdr/stack/s1ap/s1ap.h index 0698dbc08..15951086f 100644 --- a/srsenb/hdr/stack/s1ap/s1ap.h +++ b/srsenb/hdr/stack/s1ap/s1ap.h @@ -81,6 +81,7 @@ public: bool is_mme_connected() override; bool send_ho_required(uint16_t rnti, uint32_t target_eci, + uint16_t target_tac, srsran::plmn_id_t target_plmn, srsran::span fwd_erabs, srsran::unique_byte_buffer_t rrc_container, @@ -107,7 +108,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: @@ -212,6 +213,7 @@ private: struct ts1_reloc_prep_expired {}; ho_prep_proc_t(s1ap::ue* ue_); srsran::proc_outcome_t init(uint32_t target_eci_, + uint16_t target_tac_, srsran::plmn_id_t target_plmn_, srsran::span fwd_erabs, srsran::unique_byte_buffer_t rrc_container, @@ -228,6 +230,7 @@ private: s1ap* s1ap_ptr = nullptr; uint32_t target_eci = 0; + uint16_t target_tac = 0; srsran::plmn_id_t target_plmn; srsran::unique_byte_buffer_t rrc_container; const asn1::s1ap::ho_cmd_s* ho_cmd_msg = nullptr; @@ -260,7 +263,7 @@ private: bool was_uectxtrelease_requested() const { return release_requested; } void - set_state(s1ap_proc_id_t state, const erab_id_list& erabs_updated, const erab_item_list& erabs_failed_to_update); + set_state(s1ap_proc_id_t state, const erab_id_list& erabs_updated, const erab_item_list& erabs_failed_to_update); s1ap_proc_id_t get_state() const { return current_state; } ue_ctxt_t ctxt = {}; @@ -268,6 +271,7 @@ private: private: bool send_ho_required(uint32_t target_eci_, + uint16_t target_tac_, srsran::plmn_id_t target_plmn_, srsran::span fwd_erabs, srsran::unique_byte_buffer_t rrc_container, diff --git a/srsenb/src/enb_cfg_parser.cc b/srsenb/src/enb_cfg_parser.cc index cefade786..15d9e9061 100644 --- a/srsenb/src/enb_cfg_parser.cc +++ b/srsenb/src/enb_cfg_parser.cc @@ -776,6 +776,7 @@ static int parse_meas_cell_list(rrc_meas_cfg_t* meas_cfg, Setting& root) parse_default_field(cell.allowed_meas_bw, root[i], "allowed_meas_bw", 6u); asn1_parsers::default_number_to_enum( cell.cell_individual_offset, root[i], "cell_individual_offset", asn1::rrc::q_offset_range_opts::db0); + parse_default_field(cell.tac, root[i], "tac", -1); srsran_assert(srsran::is_lte_cell_nof_prb(cell.allowed_meas_bw), "Invalid measurement Bandwidth"); } return 0; @@ -1347,7 +1348,7 @@ int set_derived_args(all_args_t* args_, rrc_cfg_t* rrc_cfg_, phy_cfg_t* phy_cfg_ // Create dedicated cell configuration from RRC configuration for (auto it = rrc_cfg_->cell_list.begin(); it != rrc_cfg_->cell_list.end(); ++it) { - auto& cfg = *it; + cell_cfg_t& cfg = *it; phy_cell_cfg_t phy_cell_cfg = {}; phy_cell_cfg.cell = cell_cfg_; phy_cell_cfg.cell.id = cfg.pci; @@ -1396,6 +1397,13 @@ int set_derived_args(all_args_t* args_, rrc_cfg_t* rrc_cfg_, phy_cfg_t* phy_cfg_ } } + for (meas_cell_cfg_t& meas_cell : cfg.meas_cfg.meas_cells) { + if (meas_cell.tac < 0) { + // if meas cell TAC was not set, use current cell TAC. + meas_cell.tac = cfg.tac; + } + } + // Check if the enb cells PCIs won't lead to PSS detection issues auto is_pss_collision = [&cfg](const cell_cfg_t& c) { return c.pci % 3 == cfg.pci % 3 and c.dl_earfcn == cfg.dl_earfcn; diff --git a/srsenb/src/stack/rrc/rrc_mobility.cc b/srsenb/src/stack/rrc/rrc_mobility.cc index 7c85668a5..bdecc72eb 100644 --- a/srsenb/src/stack/rrc/rrc_mobility.cc +++ b/srsenb/src/stack/rrc/rrc_mobility.cc @@ -273,9 +273,11 @@ void rrc::ue::rrc_mobility::handle_ue_meas_report(const meas_report_s& msg, srsr const enb_cell_common* c = rrc_enb->cell_common_list->get_pci(e.pci); if (meas_it != meas_list_cfg.end()) { meas_ev.target_eci = meas_it->eci; + meas_ev.target_tac = meas_it->tac; meas_ev.direct_fwd_path = meas_it->direct_forward_path_available; } else if (c != nullptr) { meas_ev.target_eci = (rrc_enb->cfg.enb_id << 8u) + c->cell_cfg.cell_id; + meas_ev.target_tac = pcell->cell_common->cell_cfg.tac; } else { logger.warning("The PCI=%d inside the MeasReport is not recognized.", e.pci); continue; @@ -296,6 +298,7 @@ void rrc::ue::rrc_mobility::handle_ue_meas_report(const meas_report_s& msg, srsr * - This struct goes in a transparent container to the S1AP */ bool rrc::ue::rrc_mobility::start_ho_preparation(uint32_t target_eci, + uint16_t target_tac, uint8_t measobj_id, bool fwd_direct_path_available) { @@ -405,7 +408,7 @@ bool rrc::ue::rrc_mobility::start_ho_preparation(uint32_t target_eci, } return rrc_enb->s1ap->send_ho_required( - rrc_ue->rnti, target_eci, target_plmn, fwd_erabs, std::move(buffer), fwd_direct_path_available); + rrc_ue->rnti, target_eci, target_tac, target_plmn, fwd_erabs, std::move(buffer), fwd_direct_path_available); } /** @@ -653,7 +656,8 @@ void rrc::ue::rrc_mobility::s1_source_ho_st::enter(rrc_mobility* f, const ho_mea logger.info("Starting S1 Handover of rnti=0x%x to cellid=0x%x.", rrc_ue->rnti, ev.target_eci); report = ev; - if (not parent_fsm()->start_ho_preparation(report.target_eci, report.meas_obj->meas_obj_id, ev.direct_fwd_path)) { + if (not parent_fsm()->start_ho_preparation( + report.target_eci, report.target_tac, report.meas_obj->meas_obj_id, ev.direct_fwd_path)) { trigger(srsran::failure_ev{}); } } diff --git a/srsenb/src/stack/s1ap/s1ap.cc b/srsenb/src/stack/s1ap/s1ap.cc index 6bb4174a8..6fb124728 100644 --- a/srsenb/src/stack/s1ap/s1ap.cc +++ b/srsenb/src/stack/s1ap/s1ap.cc @@ -118,6 +118,7 @@ void fill_erab_failed_setup_list(OutList& output_list, const s1ap::erab_item_lis s1ap::ue::ho_prep_proc_t::ho_prep_proc_t(s1ap::ue* ue_) : ue_ptr(ue_), s1ap_ptr(ue_->s1ap_ptr) {} srsran::proc_outcome_t s1ap::ue::ho_prep_proc_t::init(uint32_t target_eci_, + uint16_t target_tac_, srsran::plmn_id_t target_plmn_, srsran::span fwd_erabs, srsran::unique_byte_buffer_t rrc_container_, @@ -125,11 +126,12 @@ srsran::proc_outcome_t s1ap::ue::ho_prep_proc_t::init(uint32_t { ho_cmd_msg = nullptr; target_eci = target_eci_; + target_tac = target_tac_; target_plmn = target_plmn_; procInfo("Sending HandoverRequired to MME id=%d", ue_ptr->ctxt.mme_ue_s1ap_id.value()); if (not ue_ptr->send_ho_required( - target_eci, target_plmn, fwd_erabs, std::move(rrc_container_), has_direct_fwd_path)) { + target_eci, target_tac, target_plmn, fwd_erabs, std::move(rrc_container_), has_direct_fwd_path)) { procError("Failed to send HORequired to cell 0x%x", target_eci); return srsran::proc_outcome_t::error; } @@ -1821,6 +1823,7 @@ void s1ap::ue::get_erab_addr(uint16_t erab_id, transp_addr_t& transp_addr, asn1: bool s1ap::send_ho_required(uint16_t rnti, uint32_t target_eci, + uint16_t target_tac, srsran::plmn_id_t target_plmn, srsran::span fwd_erabs, srsran::unique_byte_buffer_t rrc_container, @@ -1835,7 +1838,8 @@ bool s1ap::send_ho_required(uint16_t rnti, } // launch procedure - if (not u->ho_prep_proc.launch(target_eci, target_plmn, fwd_erabs, std::move(rrc_container), has_direct_fwd_path)) { + if (not u->ho_prep_proc.launch( + target_eci, target_tac, target_plmn, fwd_erabs, std::move(rrc_container), has_direct_fwd_path)) { logger.error("Failed to initiate an HandoverPreparation procedure for user rnti=0x%x", u->ctxt.rnti); return false; } @@ -2101,6 +2105,7 @@ s1ap::ue::ue(s1ap* s1ap_ptr_) : s1ap_ptr(s1ap_ptr_), ho_prep_proc(this), logger( } bool s1ap::ue::send_ho_required(uint32_t target_eci, + uint16_t target_tac, srsran::plmn_id_t target_plmn, srsran::span fwd_erabs, srsran::unique_byte_buffer_t rrc_container, @@ -2131,7 +2136,7 @@ bool s1ap::ue::send_ho_required(uint32_t target_eci, // set PLMN and TAI of target // NOTE: Only HO without TAU supported. uint16_t tmp16; - tmp16 = htons(s1ap_ptr->args.tac); + tmp16 = htons(target_tac); memcpy(targetenb.sel_tai.tac.data(), &tmp16, sizeof(uint16_t)); target_plmn.to_s1ap_plmn_bytes(targetenb.sel_tai.plm_nid.data()); // NOTE: Only HO to different Macro eNB is supported. diff --git a/srsenb/test/common/dummy_classes.h b/srsenb/test/common/dummy_classes.h index 948f914d6..595a6e3d5 100644 --- a/srsenb/test/common/dummy_classes.h +++ b/srsenb/test/common/dummy_classes.h @@ -67,6 +67,7 @@ public: bool is_mme_connected() override { return true; } bool send_ho_required(uint16_t rnti, uint32_t target_eci, + uint16_t target_tac, srsran::plmn_id_t target_plmn, srsran::span fwd_erabs, srsran::unique_byte_buffer_t rrc_container, diff --git a/srsenb/test/rrc/test_helpers.h b/srsenb/test/rrc/test_helpers.h index 03a164a27..12c27c730 100644 --- a/srsenb/test/rrc/test_helpers.h +++ b/srsenb/test/rrc/test_helpers.h @@ -81,6 +81,7 @@ public: bool send_ho_required(uint16_t rnti, uint32_t target_eci, + uint16_t target_tac, srsran::plmn_id_t target_plmn, srsran::span fwd_erabs, srsran::unique_byte_buffer_t rrc_container, From 9d0bbc45b3db76d3564fa68340874a3efab72750 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Wed, 30 Mar 2022 15:04:30 +0100 Subject: [PATCH 111/195] lib,rlc_am_nr: added debug window function --- lib/include/srsran/rlc/rlc_am_nr.h | 1 + lib/src/rlc/rlc_am_nr.cc | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/include/srsran/rlc/rlc_am_nr.h b/lib/include/srsran/rlc/rlc_am_nr.h index 94d57fabf..bd9c96909 100644 --- a/lib/include/srsran/rlc/rlc_am_nr.h +++ b/lib/include/srsran/rlc/rlc_am_nr.h @@ -246,6 +246,7 @@ public: // Helpers void debug_state() const; + void debug_window() const; private: rlc_am* parent = nullptr; diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 516dca2cc..86c194c5f 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -1461,6 +1461,7 @@ void rlc_am_nr_rx::timer_expired(uint32_t timeout_id) */ do_status = true; debug_state(); + debug_window(); return; } } @@ -1547,11 +1548,16 @@ void rlc_am_nr_rx::update_segment_inventory(rlc_amd_rx_sdu_nr_t& rx_sdu) const */ void rlc_am_nr_rx::debug_state() const { - RlcDebug("RX window state: SDUs %d", rx_window->size()); RlcDebug("RX entity state: Rx_Next=%d, Rx_Next_Status_Trigger=%d, Rx_Highest_Status=%d, Rx_Next_Highest=%d", st.rx_next, st.rx_next_status_trigger, st.rx_highest_status, st.rx_next_highest); } + +void rlc_am_nr_rx::debug_window() const +{ + RlcDebug( + "RX window state: Rx_Next=%d, Rx_Next_Highest=%d, SDUs %d", st.rx_next, st.rx_next_highest, rx_window->size()); +} } // namespace srsran From 330513ae57fec634f685890e33c515101f96046b Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Thu, 31 Mar 2022 13:57:29 +0100 Subject: [PATCH 112/195] lib,rlc_am_nr: updating segment info when segmenting RETX --- lib/src/rlc/rlc_am_nr.cc | 51 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 86c194c5f..f892352cd 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -620,6 +620,51 @@ uint32_t rlc_am_nr_tx::build_retx_pdu_with_segmentation(rlc_amd_retx_nr_t& retx, srsran_assert((hdr_len + retx_pdu_payload_size) <= nof_bytes, "Error calculating hdr_len and segment_payload_len"); memcpy(&payload[hdr_len], tx_pdu.sdu_buf->msg, retx_pdu_payload_size); + // Store PDU segment info into tx_window + RlcDebug("Updating RETX segment info. SN=%d, is_segment=%s", retx.sn, retx.is_segment ? "true" : "false"); + if (!retx.is_segment) { + // Retx is already a segment + rlc_amd_tx_pdu_nr::pdu_segment seg1 = {}; + seg1.so = retx.current_so; + seg1.payload_len = retx_pdu_payload_size; + rlc_amd_tx_pdu_nr::pdu_segment seg2 = {}; + seg2.so = retx.current_so + retx_pdu_payload_size; + seg2.payload_len = retx.segment_length - retx_pdu_payload_size; + tx_pdu.segment_list.push_back(seg1); + tx_pdu.segment_list.push_back(seg2); + RlcDebug("New segment: SN=%d, SO=%d len=%d", retx.sn, seg1.so, seg1.payload_len); + RlcDebug("New segment: SN=%d, SO=%d len=%d", retx.sn, seg2.so, seg2.payload_len); + } else { + RlcDebug("Segmenting retx! it is a segment already. SN=%d", retx.sn); + // Find current segment in segment list. + std::list::iterator it; + for (it = tx_pdu.segment_list.begin(); it != tx_pdu.segment_list.end(); ++it) { + if (it->so == retx.current_so) { + break; + } + } + if (it != tx_pdu.segment_list.end()) { + rlc_amd_tx_pdu_nr::pdu_segment seg1 = {}; + seg1.so = retx.current_so; + seg1.payload_len = retx_pdu_payload_size; + rlc_amd_tx_pdu_nr::pdu_segment seg2 = {}; + seg2.so = retx.current_so + retx_pdu_payload_size; + seg2.payload_len = retx.segment_length - retx_pdu_payload_size; + std::list::iterator begin_it = tx_pdu.segment_list.erase(it); + if (begin_it == tx_pdu.segment_list.end()) { + RlcError("Could not modify segment list. SN=%d, SO=%d len=%d", retx.sn, retx.current_so, retx.segment_length); + } else { + std::list::iterator insert_it = tx_pdu.segment_list.insert(begin_it, seg1); + std::list::iterator insert_it2 = tx_pdu.segment_list.insert(insert_it, seg2); + RlcDebug("Old segment SN=%d, SO=%d len=%d", retx.sn, retx.current_so, retx.segment_length); + RlcDebug("New segment SN=%d, SO=%d len=%d", retx.sn, seg1.so, seg1.payload_len); + RlcDebug("New segment SN=%d, SO=%d len=%d", retx.sn, seg2.so, seg2.payload_len); + } + } else { + RlcDebug("Could not find segment. SN=%d, SO=%d length=%d", retx.sn, retx.current_so, retx.segment_length); + } + } + // Update retx queue retx.is_segment = true; retx.current_so = retx.current_so + retx_pdu_payload_size; @@ -649,8 +694,6 @@ uint32_t rlc_am_nr_tx::build_retx_pdu_with_segmentation(rlc_amd_retx_nr_t& retx, return 0; } - // Update SDU segment info - // TODO return hdr_len + retx_pdu_payload_size; } @@ -691,7 +734,7 @@ uint32_t rlc_am_nr_tx::build_status_pdu(byte_buffer_t* payload, uint32_t nof_byt pdu_len = 0; } else if (pdu_len > 0 && nof_bytes >= static_cast(pdu_len)) { RlcDebug("generated status PDU. Bytes:%d", pdu_len); - log_rlc_am_nr_status_pdu_to_string(logger.info, "tx status PDU - %s", &tx_status, rb_name); + log_rlc_am_nr_status_pdu_to_string(logger.info, "TX status PDU - %s", &tx_status, rb_name); pdu_len = rlc_am_nr_write_status_pdu(tx_status, cfg.tx_sn_field_length, payload); } else { RlcInfo("cannot tx status PDU - %d bytes available, %d bytes required", nof_bytes, pdu_len); @@ -711,7 +754,7 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) rlc_am_nr_status_pdu_t status = {}; RlcHexDebug(payload, nof_bytes, "%s Rx control PDU", parent->rb_name); rlc_am_nr_read_status_pdu(payload, nof_bytes, cfg.tx_sn_field_length, &status); - log_rlc_am_nr_status_pdu_to_string(logger.info, "Rx Status PDU: %s", &status, parent->rb_name); + log_rlc_am_nr_status_pdu_to_string(logger.info, "RX status PDU: %s", &status, parent->rb_name); // Local variables for handling Status PDU will be updated with lock /* * - if the SN of the corresponding RLC SDU falls within the range From b70e6284e623a63369ed50ec36b743578a91ecff Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Thu, 31 Mar 2022 14:01:42 +0100 Subject: [PATCH 113/195] lib,rlc_am_nr: added logging info to to handling NACKs --- lib/src/rlc/rlc_am_nr.cc | 46 +++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index f892352cd..9b45278bd 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -796,6 +796,7 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) status.nacks[nack_idx].nack_sn); continue; } + RlcDebug("Handling NACK for SN=%d", status.nacks[nack_idx].nack_sn); if (st.tx_next_ack <= status.nacks[nack_idx].nack_sn && status.nacks[nack_idx].nack_sn <= st.tx_next) { auto nack = status.nacks[nack_idx]; uint32_t nack_sn = nack.nack_sn; @@ -808,22 +809,32 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) if (pdu.segment_list.empty()) { RlcError("Received NACK with SO, but there is no segment information. SN=%d", nack_sn); } - for (std::list::iterator segm = pdu.segment_list.begin(); - segm != pdu.segment_list.end(); - segm++) { - if (segm->so >= nack.so_start && segm->so <= nack.so_end) { - // TODO: Check if this segment is not already queued for retransmission + bool segment_found = false; + for (const rlc_amd_tx_pdu_nr::pdu_segment& segm : pdu.segment_list) { + if (segm.so >= nack.so_start && segm.so <= nack.so_end) { + // FIXME: Check if this segment is not already queued for retransmission rlc_amd_retx_nr_t& retx = retx_queue->push(); retx.sn = nack_sn; retx.is_segment = true; - retx.so_start = segm->so; - retx.current_so = segm->so; - retx.segment_length = segm->payload_len; + retx.so_start = segm.so; + retx.current_so = segm.so; + retx.segment_length = segm.payload_len; retx_sn_set.insert(nack_sn); RlcInfo("Scheduled RETX of SDU segment SN=%d, so_start=%d, segment_length=%d", retx.sn, retx.so_start, retx.segment_length); + segment_found = true; + } + } + if (!segment_found) { + RlcWarning("Could not find segment for NACK_SN=%d. SO_start=%d, SO_end=%d", + status.nacks[nack_idx].nack_sn, + nack.so_start, + nack.so_end); + for (const rlc_amd_tx_pdu_nr::pdu_segment& segm : pdu.segment_list) { + RlcDebug( + "Segments for SN=%d. SO=%d, SO_end=%d", status.nacks[nack_idx].nack_sn, segm.so, segm.payload_len); } } } else { @@ -838,11 +849,26 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) retx.segment_length = pdu.sdu_buf->N_bytes; retx_sn_set.insert(nack_sn); RlcInfo("Scheduled RETX of SDU SN=%d", retx.sn); + } else { + RlcInfo("RETX queue already has NACK_SN. SDU SN=%d, Tx_Next_Ack=%d, Tx_Next=%d", + status.nacks[nack_idx].nack_sn, + st.tx_next_ack, + st.tx_next); } } + } else { + RlcInfo("TX window does not contain NACK_SN. SDU SN=%d, Tx_Next_Ack=%d, Tx_Next=%d", + status.nacks[nack_idx].nack_sn, + st.tx_next_ack, + st.tx_next); } // TX window containts NACK SN - } // NACK SN within expected range - } // NACK loop + } else { + RlcInfo("RETX not in expected range. SDU SN=%d, Tx_Next_Ack=%d, Tx_Next=%d", + status.nacks[nack_idx].nack_sn, + st.tx_next_ack, + st.tx_next); + } // NACK SN within expected range + } // NACK loop // Process retx_count and inform upper layers if needed for (uint32_t retx_sn : retx_sn_set) { From 02c077106b67f3828894d8d70b8fc46ddf35ba51 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Thu, 31 Mar 2022 14:02:39 +0100 Subject: [PATCH 114/195] lib,rlc_am_nr: slowdown backoff if we run out of buffers in rlc_stress test --- lib/test/rlc/rlc_stress_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/test/rlc/rlc_stress_test.cc b/lib/test/rlc/rlc_stress_test.cc index 7b019555b..a69bc908f 100644 --- a/lib/test/rlc/rlc_stress_test.cc +++ b/lib/test/rlc/rlc_stress_test.cc @@ -201,7 +201,7 @@ void rlc_tester::run_thread() if (pdu == nullptr) { printf("Error: Could not allocate PDU in rlc_tester::run_thread\n\n\n"); // backoff for a bit - std::this_thread::sleep_for(std::chrono::milliseconds(1)); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); continue; } pdu->md.pdcp_sn = pdcp_sn; From 7f7656e2000cf1e7f20caf9be0da5c098159114e Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Thu, 31 Mar 2022 14:58:18 +0100 Subject: [PATCH 115/195] lib,rlc_am_nr: fix missing modulus --- lib/src/rlc/rlc_am_nr.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 9b45278bd..4267391e2 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -766,11 +766,11 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) uint32_t stop_sn = status.nacks.size() == 0 ? status.ack_sn : status.nacks[0].nack_sn; // Stop processing ACKs at the first NACK, if it exists. - if (stop_sn > st.tx_next) { + if (tx_mod_base_nr(stop_sn) > tx_mod_base_nr(st.tx_next)) { RlcError("Received ACK or NACK larger than TX_NEXT. Ignoring status report"); return; } - for (uint32_t sn = st.tx_next_ack; sn < stop_sn; sn++) { + for (uint32_t sn = st.tx_next_ack; tx_mod_base_nr(sn) < tx_mod_base_nr(stop_sn); sn = (sn + 1) % mod_nr) { if (tx_window->has_sn(sn)) { notify_info_vec.push_back((*tx_window)[sn].pdcp_sn); tx_window->remove_pdu(sn); From 865dfe87e74fe913a483d77affe3409174cea3d3 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Thu, 31 Mar 2022 22:11:19 +0100 Subject: [PATCH 116/195] lib,rlc_am_nr: Added assert to check SO_start <= SO_end --- lib/src/rlc/rlc_am_nr.cc | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 4267391e2..a53f2e558 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -780,6 +780,7 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) break; } } + RlcDebug("Processed status report ACKs. ACK_SN=%d. Tx_Next_Ack=%d", status.ack_sn, st.tx_next_ack); // Notify PDCP if (not notify_info_vec.empty()) { @@ -796,8 +797,9 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) status.nacks[nack_idx].nack_sn); continue; } - RlcDebug("Handling NACK for SN=%d", status.nacks[nack_idx].nack_sn); - if (st.tx_next_ack <= status.nacks[nack_idx].nack_sn && status.nacks[nack_idx].nack_sn <= st.tx_next) { + if (tx_mod_base_nr(st.tx_next_ack) <= tx_mod_base_nr(status.nacks[nack_idx].nack_sn) && + tx_mod_base_nr(status.nacks[nack_idx].nack_sn) <= tx_mod_base_nr(st.tx_next)) { + RlcDebug("Handling NACK for SN=%d", status.nacks[nack_idx].nack_sn); auto nack = status.nacks[nack_idx]; uint32_t nack_sn = nack.nack_sn; if (tx_window->has_sn(nack_sn)) { @@ -1401,13 +1403,16 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m * starting with SN = RX_Next up to the point where the resulting STATUS PDU still fits to the total size of RLC * PDU(s) indicated by lower layer: */ + RlcDebug("Generating status PDU"); for (uint32_t i = st.rx_next; rx_mod_base_nr(i) < rx_mod_base_nr(st.rx_highest_status); i = (i + 1) % mod_nr) { if ((rx_window->has_sn(i) && (*rx_window)[i].fully_received)) { // only update ACK_SN if this SN has been fully received status->ack_sn = i; + RlcDebug("Updating ACK_SN. ACK_SN=%d", i); } else { if (not rx_window->has_sn(i)) { // No segment received, NACK the whole SDU + RlcDebug("Updating NACK for full SDU. NACK SN=%d", i); rlc_status_nack_t nack; nack.nack_sn = i; nack.has_so = false; @@ -1415,6 +1420,7 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m } else if (not(*rx_window)[i].fully_received) { // Some segments were received, but not all. // NACK non consecutive missing bytes + RlcDebug("Updating NACK for partial SDU. NACK SN=%d", i); uint32_t last_so = 0; bool last_segment_rx = false; for (auto segm = (*rx_window)[i].segments.begin(); segm != (*rx_window)[i].segments.end(); segm++) { @@ -1426,6 +1432,11 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m nack.so_start = last_so; nack.so_end = segm->header.so - 1; // set to last missing byte status->nacks.push_back(nack); + RlcDebug("First/middle segment missing. NACK_SN=%d. SO_start=%d, SO_end=%d", + nack.nack_sn, + nack.so_start, + nack.so_end); + srsran_assert(nack.so_start <= nack.so_end, "Error: SO_start > SO_end. NACK_SN=%d", nack.nack_sn); } if (segm->header.si == rlc_nr_si_field_t::last_segment) { last_segment_rx = true; @@ -1439,6 +1450,9 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m nack.so_start = last_so; nack.so_end = so_end_of_sdu; status->nacks.push_back(nack); + RlcDebug( + "Final segment missing. NACK_SN=%d. SO_start=%d, SO_end=%d", nack.nack_sn, nack.so_start, nack.so_end); + srsran_assert(nack.so_start <= nack.so_end, "Error: SO_start > SO_end. NACK_SN=%d", nack.nack_sn); } } } @@ -1507,6 +1521,10 @@ void rlc_am_nr_rx::timer_expired(uint32_t timeout_id) } } st.rx_highest_status = sn_upd; + if (not inside_rx_window(st.rx_highest_status)) { + RlcError("Rx_Highest_Status not inside RX window"); + debug_state(); + } srsran_assert(inside_rx_window(st.rx_highest_status), "Error: rx_highest_status assigned outside rx window"); bool restart_reassembly_timer = false; From 4e2f7cf02975be4e927da8c34a2ce6b5c80823bd Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Mon, 4 Apr 2022 11:28:29 +0100 Subject: [PATCH 117/195] lib,rlc_am_nr: make sure that we RETX the segments if we get a NACK for an SDU that was already segmented --- lib/src/rlc/rlc_am_nr.cc | 34 ++++++++++++++++++++++++++-------- lib/test/rlc/rlc_stress_test.h | 3 +++ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index a53f2e558..10e75d054 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -663,6 +663,9 @@ uint32_t rlc_am_nr_tx::build_retx_pdu_with_segmentation(rlc_amd_retx_nr_t& retx, } else { RlcDebug("Could not find segment. SN=%d, SO=%d length=%d", retx.sn, retx.current_so, retx.segment_length); } + for (auto it : tx_pdu.segment_list) { + RlcDebug("Changed segments! SN=%d, SO=%d length=%d", retx.sn, it.so, it.payload_len); + } } // Update retx queue @@ -843,14 +846,29 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) // NACK'ing full SDU. // add to retx queue if it's not already there if (not retx_queue->has_sn(nack_sn)) { - rlc_amd_retx_nr_t& retx = retx_queue->push(); - retx.sn = nack_sn; - retx.is_segment = false; - retx.so_start = 0; - retx.current_so = 0; - retx.segment_length = pdu.sdu_buf->N_bytes; - retx_sn_set.insert(nack_sn); - RlcInfo("Scheduled RETX of SDU SN=%d", retx.sn); + // Have we segmented the SDU alreaty? + if ((*tx_window)[nack_sn].segment_list.empty()) { + rlc_amd_retx_nr_t& retx = retx_queue->push(); + retx.sn = nack_sn; + retx.is_segment = false; + retx.so_start = 0; + retx.current_so = 0; + retx.segment_length = pdu.sdu_buf->N_bytes; + retx_sn_set.insert(nack_sn); + RlcInfo("Scheduled RETX of SDU SN=%d", retx.sn); + } else { + RlcInfo("Scheduled RETX of SDU SN=%d", nack_sn); + retx_sn_set.insert(nack_sn); + for (auto segm : (*tx_window)[nack_sn].segment_list) { + rlc_amd_retx_nr_t& retx = retx_queue->push(); + retx.sn = nack_sn; + retx.is_segment = true; + retx.so_start = segm.so; + retx.current_so = segm.so; + retx.segment_length = segm.payload_len; + RlcInfo("Scheduled RETX of SDU Segment. SN=%d, SO=%d, len=%d", retx.sn, segm.so, segm.payload_len); + } + } } else { RlcInfo("RETX queue already has NACK_SN. SDU SN=%d, Tx_Next_Ack=%d, Tx_Next=%d", status.nacks[nack_idx].nack_sn, diff --git a/lib/test/rlc/rlc_stress_test.h b/lib/test/rlc/rlc_stress_test.h index 7237e56c6..da824c02d 100644 --- a/lib/test/rlc/rlc_stress_test.h +++ b/lib/test/rlc/rlc_stress_test.h @@ -246,6 +246,9 @@ public: // RRC interface void max_retx_attempted() final { + fprintf( + stderr, + "Maximum number of RLC retransmission reached. Consider increasing threshold or lowering channel drop rate."); logger.error( "Maximum number of RLC retransmission reached. Consider increasing threshold or lowering channel drop rate."); std::this_thread::sleep_for(std::chrono::seconds(1)); From 2f1cf8ee110550032db849f4eac7e6f22b7fdc57 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Mon, 4 Apr 2022 14:06:22 +0100 Subject: [PATCH 118/195] lib,rlc_am_nr: make max retx configurable in AM stress test --- lib/src/rlc/rlc_am_nr.cc | 2 +- lib/test/rlc/rlc_stress_test.cc | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 10e75d054..8f42f2b6c 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -846,7 +846,7 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) // NACK'ing full SDU. // add to retx queue if it's not already there if (not retx_queue->has_sn(nack_sn)) { - // Have we segmented the SDU alreaty? + // Have we segmented the SDU already? if ((*tx_window)[nack_sn].segment_list.empty()) { rlc_amd_retx_nr_t& retx = retx_queue->push(); retx.sn = nack_sn; diff --git a/lib/test/rlc/rlc_stress_test.cc b/lib/test/rlc/rlc_stress_test.cc index a69bc908f..016402f63 100644 --- a/lib/test/rlc/rlc_stress_test.cc +++ b/lib/test/rlc/rlc_stress_test.cc @@ -276,9 +276,11 @@ void stress_test(stress_test_args_t args) } else if (args.mode == "UM12") { cnfg_ = srsran::rlc_config_t::default_rlc_um_nr_config(12); } else if (args.mode == "AM12") { - cnfg_ = srsran::rlc_config_t::default_rlc_am_nr_config(12); + cnfg_ = srsran::rlc_config_t::default_rlc_am_nr_config(12); + cnfg_.am_nr.max_retx_thresh = args.max_retx; } else if (args.mode == "AM18") { - cnfg_ = srsran::rlc_config_t::default_rlc_am_nr_config(18); + cnfg_ = srsran::rlc_config_t::default_rlc_am_nr_config(18); + cnfg_.am_nr.max_retx_thresh = args.max_retx; } else { std::cout << "Unsupported RLC mode " << args.mode << ", exiting." << std::endl; exit(-1); From 47caa60b45a422b96d480136111f2e2546bf7cc2 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Mon, 11 Apr 2022 11:45:20 +0100 Subject: [PATCH 119/195] lib,rlc_am_nr: fix up comment --- lib/src/rlc/rlc_am_nr.cc | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 8f42f2b6c..2161089e1 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -623,7 +623,7 @@ uint32_t rlc_am_nr_tx::build_retx_pdu_with_segmentation(rlc_amd_retx_nr_t& retx, // Store PDU segment info into tx_window RlcDebug("Updating RETX segment info. SN=%d, is_segment=%s", retx.sn, retx.is_segment ? "true" : "false"); if (!retx.is_segment) { - // Retx is already a segment + // Retx is not a segment yet rlc_amd_tx_pdu_nr::pdu_segment seg1 = {}; seg1.so = retx.current_so; seg1.payload_len = retx_pdu_payload_size; @@ -635,7 +635,7 @@ uint32_t rlc_am_nr_tx::build_retx_pdu_with_segmentation(rlc_amd_retx_nr_t& retx, RlcDebug("New segment: SN=%d, SO=%d len=%d", retx.sn, seg1.so, seg1.payload_len); RlcDebug("New segment: SN=%d, SO=%d len=%d", retx.sn, seg2.so, seg2.payload_len); } else { - RlcDebug("Segmenting retx! it is a segment already. SN=%d", retx.sn); + // Retx is already a segment // Find current segment in segment list. std::list::iterator it; for (it = tx_pdu.segment_list.begin(); it != tx_pdu.segment_list.end(); ++it) { @@ -663,9 +663,6 @@ uint32_t rlc_am_nr_tx::build_retx_pdu_with_segmentation(rlc_amd_retx_nr_t& retx, } else { RlcDebug("Could not find segment. SN=%d, SO=%d length=%d", retx.sn, retx.current_so, retx.segment_length); } - for (auto it : tx_pdu.segment_list) { - RlcDebug("Changed segments! SN=%d, SO=%d length=%d", retx.sn, it.so, it.payload_len); - } } // Update retx queue From e73acc3a4b022217fccbfd155de64b38df388866 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Fri, 18 Mar 2022 10:28:15 +0100 Subject: [PATCH 120/195] cmake: remove redundant/conflicting cmake_minimum_required() in buildinfo. --- cmake/modules/SRSRANbuildinfo.cmake.in | 1 - 1 file changed, 1 deletion(-) diff --git a/cmake/modules/SRSRANbuildinfo.cmake.in b/cmake/modules/SRSRANbuildinfo.cmake.in index 8cbea7325..e2ce71273 100644 --- a/cmake/modules/SRSRANbuildinfo.cmake.in +++ b/cmake/modules/SRSRANbuildinfo.cmake.in @@ -1,4 +1,3 @@ -cmake_minimum_required(VERSION 2.6) execute_process( COMMAND git rev-parse --abbrev-ref HEAD From 899e4148d4b7a3580199ac19906a95ff7ec2732e Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Thu, 14 Apr 2022 22:46:52 +0200 Subject: [PATCH 121/195] ue,mac_nr: reset DL and UL HARQ when MAC is reset DL and UL harq entities where never reset when the UE was going into IDLE. As a result, the first transmissions where always using the old softbuffer without resetting it, resulting in wrong TB being decoded. In the case of the RAR, the UE decoded a wrong (in fact the old) TCRNTI and the RA procedure failed. --- srsue/src/stack/mac_nr/mac_nr.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/srsue/src/stack/mac_nr/mac_nr.cc b/srsue/src/stack/mac_nr/mac_nr.cc index e01bf1371..c529e9062 100644 --- a/srsue/src/stack/mac_nr/mac_nr.cc +++ b/srsue/src/stack/mac_nr/mac_nr.cc @@ -103,6 +103,16 @@ void mac_nr::reset() proc_sr.reset(); proc_ra.reset(); mux.reset(); + for (const auto& cc : dl_harq) { + if (cc != nullptr) { + cc->reset(); + } + } + for (const auto& cc : ul_harq) { + if (cc != nullptr) { + cc->reset(); + } + } } void mac_nr::run_tti(const uint32_t tti) From 4bf7d8f524c52c2cc0d1ee089e8a70435bc5112d Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Thu, 14 Apr 2022 22:50:31 +0200 Subject: [PATCH 122/195] enb,mac_nr: fix log line --- srsgnb/src/stack/mac/mac_nr.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srsgnb/src/stack/mac/mac_nr.cc b/srsgnb/src/stack/mac/mac_nr.cc index 0cd3f7712..91674f814 100644 --- a/srsgnb/src/stack/mac/mac_nr.cc +++ b/srsgnb/src/stack/mac/mac_nr.cc @@ -675,7 +675,7 @@ srsran::byte_buffer_t* mac_nr::assemble_rar(srsran::const_span Date: Wed, 23 Feb 2022 18:22:22 +0000 Subject: [PATCH 123/195] gnb,config: Added five_qi_cfg to rrc_nr_config_t. Starting to add five_qi_field to parse the rb.conf for 5g configuration. --- srsenb/rb.conf.example | 2 -- srsenb/src/enb_cfg_parser.cc | 34 ++++++++++++++++++++++------ srsenb/src/enb_cfg_parser.h | 21 +++++++++++++---- srsenb/src/main.cc | 2 +- srsgnb/hdr/stack/rrc/rrc_nr_config.h | 8 +++++++ 5 files changed, 53 insertions(+), 14 deletions(-) diff --git a/srsenb/rb.conf.example b/srsenb/rb.conf.example index de1cb8f33..686b18964 100644 --- a/srsenb/rb.conf.example +++ b/srsenb/rb.conf.example @@ -37,7 +37,6 @@ // } qci_config = ( - { qci=7; pdcp_config = { @@ -91,5 +90,4 @@ qci_config = ( dl_max_retx_thresh = 32; }; } - ); diff --git a/srsenb/src/enb_cfg_parser.cc b/srsenb/src/enb_cfg_parser.cc index 15d9e9061..9ff7ae9dd 100644 --- a/srsenb/src/enb_cfg_parser.cc +++ b/srsenb/src/enb_cfg_parser.cc @@ -635,6 +635,22 @@ int field_qci::parse(libconfig::Setting& root) return 0; } +int field_five_qi::parse(libconfig::Setting& root) +{ + uint32_t nof_five_qi = (uint32_t)root.getLength(); + + for (uint32_t i = 0; i < nof_five_qi; i++) { + libconfig::Setting& q = root[i]; + + uint32_t five_qi = q["five_qi"]; + + rrc_nr_cfg_five_qi_t five_qi_cfg; + + cfg.insert(std::make_pair(five_qi, five_qi_cfg)); + } + + return 0; +} namespace rr_sections { int parse_rr(all_args_t* args_, rrc_cfg_t* rrc_cfg_, rrc_nr_cfg_t* rrc_nr_cfg_) @@ -1223,15 +1239,15 @@ int parse_cfg_files(all_args_t* args_, rrc_cfg_t* rrc_cfg_, rrc_nr_cfg_t* rrc_nr } try { - if (drb_sections::parse_drb(args_, rrc_cfg_) != SRSRAN_SUCCESS) { - fprintf(stderr, "Error parsing DRB configuration\n"); + if (rb_sections::parse_rb(args_, rrc_cfg_, rrc_nr_cfg_) != SRSRAN_SUCCESS) { + fprintf(stderr, "Error parsing RB configuration\n"); return SRSRAN_ERROR; } } catch (const SettingTypeException& stex) { - fprintf(stderr, "Error parsing DRB configuration: %s\n", stex.getPath()); + fprintf(stderr, "Error parsing RB configuration: %s\n", stex.getPath()); return SRSRAN_ERROR; } catch (const ConfigException& cex) { - fprintf(stderr, "Error parsing DRB configuration\n"); + fprintf(stderr, "Error parsing RB configuration\n"); return SRSRAN_ERROR; } @@ -2102,9 +2118,9 @@ int parse_sibs(all_args_t* args_, rrc_cfg_t* rrc_cfg_, srsenb::phy_cfg_t* phy_co } // namespace sib_sections -namespace drb_sections { +namespace rb_sections { -int parse_drb(all_args_t* args_, rrc_cfg_t* rrc_cfg_) +int parse_rb(all_args_t* args_, rrc_cfg_t* rrc_cfg_, rrc_nr_cfg_t* rrc_nr_cfg_) { parser::section srb1("srb1_config"); bool srb1_present = false; @@ -2125,11 +2141,15 @@ int parse_drb(all_args_t* args_, rrc_cfg_t* rrc_cfg_) parser::section qci("qci_config"); qci.add_field(new field_qci(rrc_cfg_->qci_cfg)); + parser::section five_qi("five_qi_config"); + five_qi.add_field(new field_five_qi(rrc_nr_cfg_->five_qi_cfg)); + // Run parser with two sections parser p(args_->enb_files.rb_config); p.add_section(&srb1); p.add_section(&srb2); p.add_section(&qci); + p.add_section(&five_qi); int ret = p.parse(); if (not srb1_present) { @@ -2142,6 +2162,6 @@ int parse_drb(all_args_t* args_, rrc_cfg_t* rrc_cfg_) return ret; } -} // namespace drb_sections +} // namespace rb_sections } // namespace srsenb diff --git a/srsenb/src/enb_cfg_parser.h b/srsenb/src/enb_cfg_parser.h index 569cdd4e2..4c52338c9 100644 --- a/srsenb/src/enb_cfg_parser.h +++ b/srsenb/src/enb_cfg_parser.h @@ -22,6 +22,7 @@ #include #include "srsenb/hdr/stack/rrc/rrc.h" +#include "srsgnb/hdr/stack/rrc/rrc_nr_config.h" #include "srsran/asn1/asn1_utils.h" namespace srsenb { @@ -58,11 +59,12 @@ int parse_sibs(all_args_t* args_, rrc_cfg_t* rrc_cfg_, srsenb::phy_cfg_t* phy_co } // namespace sib_sections -// drb.conf parsing -namespace drb_sections { +// rb.conf parsing +namespace rb_sections { -int parse_drb(all_args_t* args, rrc_cfg_t* rrc_cfg); -} // namespace drb_sections +int parse_rb(all_args_t* args, rrc_cfg_t* rrc_cfg, rrc_nr_cfg_t* rrc_nr_cfg); + +} // namespace rb_sections // rr.conf parsing namespace rr_sections { @@ -189,6 +191,17 @@ private: std::map& cfg; }; +class field_five_qi final : public parser::field_itf +{ +public: + explicit field_five_qi(std::map& cfg_) : cfg(cfg_) {} + const char* get_name() override { return "field_5qi"; } + + int parse(Setting& root) override; + +private: + std::map& cfg; +}; // ASN1 parsers class field_asn1 : public parser::field_itf diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index 975abee54..33d739088 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -460,7 +460,7 @@ void parse_args(all_args_t* args, int argc, char* argv[]) } if (!config_exists(args->enb_files.rb_config, "rb.conf")) { - cout << "Failed to read DRB configuration file " << args->enb_files.rb_config << " - exiting" << endl; + cout << "Failed to read RB configuration file " << args->enb_files.rb_config << " - exiting" << endl; exit(1); } diff --git a/srsgnb/hdr/stack/rrc/rrc_nr_config.h b/srsgnb/hdr/stack/rrc/rrc_nr_config.h index 555f58941..b4c7848f5 100644 --- a/srsgnb/hdr/stack/rrc/rrc_nr_config.h +++ b/srsgnb/hdr/stack/rrc/rrc_nr_config.h @@ -45,6 +45,12 @@ struct rrc_cell_cfg_nr_t { typedef std::vector rrc_cell_list_nr_t; +struct rrc_nr_cfg_five_qi_t { + bool configured = false; + asn1::rrc_nr::pdcp_cfg_s pdcp_cfg; + asn1::rrc_nr::rlc_cfg_c rlc_cfg; +}; + struct rrc_nr_cfg_t { rrc_cell_list_nr_t cell_list; uint32_t inactivity_timeout_ms = 100000; @@ -53,6 +59,8 @@ struct rrc_nr_cfg_t { uint16_t mnc; bool is_standalone; + std::map five_qi_cfg; + std::array nea_preference_list; std::array nia_preference_list; From e14efbd95afa5e2d242e59d7df01535d42252403 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Mon, 7 Mar 2022 15:27:33 +0000 Subject: [PATCH 124/195] gnb,config: Added default configuration for the 5G RBs --- srsenb/rb.conf.example | 56 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/srsenb/rb.conf.example b/srsenb/rb.conf.example index 686b18964..e4f13fdb5 100644 --- a/srsenb/rb.conf.example +++ b/srsenb/rb.conf.example @@ -1,5 +1,7 @@ // All times are in ms. Use -1 for infinity, where available +// 4G Section + // srb1_config = { // rlc_config = { // ul_am = { @@ -91,3 +93,57 @@ qci_config = ( }; } ); + +// 5G Section +five_qi_config = ( +{ + five_qi=7; + pdcp_nr_config = { + drb = { + discard_timer = 50; + pdcp_sn_size_ul = 12; + pdcp_sn_size_dl = 12; + }; + t_reordering = 50; + }; + rlc_config = { + am = { + ul_am = { + sn_field_len = 12; + t_poll_retx = 50; + poll_pdu = 4; + poll_byte = 3000; + max_retx_thres = 4; + }; + dl_am = { + sn_field_len = 12; + t_reassembly = 50; + t_status_prohibit = 50; + }; + }; + }; +}, +{ + five_qi=9; + pdcp_nr_config = { + drb = { + discard_timer = 50; + pdcp_sn_size_ul = 12; + pdcp_sn_size_dl = 12; + }; + t_reordering = 50; + }; + rlc_config = { + um_bi_dir = { + ul_um = { + sn_field_len = 12; + }; + dl_um = { + sn_field_len = 12; + t_reassembly = 50; + }; + }; + }; +} +); + From 66bf6fd28c319e95113283b3c5d507ed87a92338 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Thu, 24 Feb 2022 17:20:14 +0000 Subject: [PATCH 125/195] gnb,config: added PDCP-NR configuration parsing --- srsenb/src/enb_cfg_parser.cc | 66 +++++++++++++++++++++++++++++++++- srsenb/src/enb_cfg_parser.h | 4 +-- srsgnb/src/stack/rrc/rrc_nr.cc | 17 ++++----- 3 files changed, 76 insertions(+), 11 deletions(-) diff --git a/srsenb/src/enb_cfg_parser.cc b/srsenb/src/enb_cfg_parser.cc index 9ff7ae9dd..55c64582f 100644 --- a/srsenb/src/enb_cfg_parser.cc +++ b/srsenb/src/enb_cfg_parser.cc @@ -638,7 +638,6 @@ int field_qci::parse(libconfig::Setting& root) int field_five_qi::parse(libconfig::Setting& root) { uint32_t nof_five_qi = (uint32_t)root.getLength(); - for (uint32_t i = 0; i < nof_five_qi; i++) { libconfig::Setting& q = root[i]; @@ -646,6 +645,71 @@ int field_five_qi::parse(libconfig::Setting& root) rrc_nr_cfg_five_qi_t five_qi_cfg; + // Parse PDCP section + if (!q.exists("pdcp_nr_config")) { + fprintf(stderr, "Error section pdcp_nr_config not found for 5qi=%d\n", five_qi); + return SRSRAN_ERROR; + } + libconfig::Setting& pdcp_nr = q["pdcp_nr_config"]; + asn1::rrc_nr::pdcp_cfg_s* pdcp_cfg = &five_qi_cfg.pdcp_cfg; + + // Get PDCP-NR DRB configs + if (!pdcp_nr.exists("drb")) { + fprintf(stderr, "Error section drb not found for 5QI=%d\n", five_qi); + return SRSRAN_ERROR; + } + libconfig::Setting& drb = pdcp_nr["drb"]; + asn1::rrc_nr::pdcp_cfg_s::drb_s_* drb_cfg = &pdcp_cfg->drb; + pdcp_cfg->drb_present = true; + + // Discard timer + field_asn1_enum_number discard_timer("discard_timer", + &drb_cfg->discard_timer); + if (discard_timer.parse(drb) == -1) { + drb_cfg->discard_timer_present = false; + } else { + drb_cfg->discard_timer_present = true; + } + + // PDCP SN size UL + field_asn1_enum_number pdcp_sn_size_ul( + "pdcp_sn_size_ul", &drb_cfg->pdcp_sn_size_ul); + if (pdcp_sn_size_ul.parse(drb) == SRSRAN_ERROR) { + drb_cfg->pdcp_sn_size_ul_present = false; + } else { + drb_cfg->pdcp_sn_size_ul_present = true; + } + + // PDCP SN size DL + field_asn1_enum_number pdcp_sn_size_dl( + "pdcp_sn_size_dl", &drb_cfg->pdcp_sn_size_dl); + if (pdcp_sn_size_dl.parse(drb) == SRSRAN_ERROR) { + drb_cfg->pdcp_sn_size_dl_present = false; + } else { + drb_cfg->pdcp_sn_size_dl_present = true; + } + + parser::field status_report_required("status_report_required", &drb_cfg->status_report_required_present); + status_report_required.parse(drb); + + parser::field out_of_order_delivery("out_of_order_delivery", &drb_cfg->out_of_order_delivery_present); + out_of_order_delivery.parse(drb); + + parser::field integrity_protection("integrity_protection", &drb_cfg->integrity_protection_present); + integrity_protection.parse(drb); + + drb_cfg->hdr_compress.set_not_used(); + // Finish DRB config + + // t_Reordering + field_asn1_enum_number t_reordering("t_reordering", + &pdcp_cfg->t_reordering); + if (t_reordering.parse(pdcp_nr) == SRSRAN_ERROR) { + pdcp_cfg->t_reordering_present = false; + } else { + pdcp_cfg->t_reordering_present = true; + } + cfg.insert(std::make_pair(five_qi, five_qi_cfg)); } diff --git a/srsenb/src/enb_cfg_parser.h b/srsenb/src/enb_cfg_parser.h index 4c52338c9..574cf115b 100644 --- a/srsenb/src/enb_cfg_parser.h +++ b/srsenb/src/enb_cfg_parser.h @@ -183,7 +183,7 @@ class field_qci final : public parser::field_itf { public: explicit field_qci(std::map& cfg_) : cfg(cfg_) {} - const char* get_name() override { return "field_cqi"; } + const char* get_name() override { return "field_qci"; } int parse(Setting& root) override; @@ -195,7 +195,7 @@ class field_five_qi final : public parser::field_itf { public: explicit field_five_qi(std::map& cfg_) : cfg(cfg_) {} - const char* get_name() override { return "field_5qi"; } + const char* get_name() override { return "field_five_qi"; } int parse(Setting& root) override; diff --git a/srsgnb/src/stack/rrc/rrc_nr.cc b/srsgnb/src/stack/rrc/rrc_nr.cc index 9a2ee009a..1954dafa4 100644 --- a/srsgnb/src/stack/rrc/rrc_nr.cc +++ b/srsgnb/src/stack/rrc/rrc_nr.cc @@ -93,14 +93,15 @@ int rrc_nr::init(const rrc_nr_cfg_t& cfg_, config_phy(); // if PHY is not yet initialized, config will be stored and applied on initialization config_mac(); - logger.debug("NIA preference list: NIA%d, NIA%d, NIA%d", - cfg.nia_preference_list[0], - cfg.nia_preference_list[1], - cfg.nia_preference_list[2]); - logger.debug("NEA preference list: NEA%d, NEA%d, NEA%d", - cfg.nea_preference_list[0], - cfg.nea_preference_list[1], - cfg.nea_preference_list[2]); + logger.info("Number of 5QI %d", cfg.five_qi_cfg.size()); + logger.info("NIA preference list: NIA%d, NIA%d, NIA%d", + cfg.nia_preference_list[0], + cfg.nia_preference_list[1], + cfg.nia_preference_list[2]); + logger.info("NEA preference list: NEA%d, NEA%d, NEA%d", + cfg.nea_preference_list[0], + cfg.nea_preference_list[1], + cfg.nea_preference_list[2]); running = true; return SRSRAN_SUCCESS; From a725cb043695907db4b598d682661faf007853f0 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Fri, 25 Feb 2022 18:13:30 +0000 Subject: [PATCH 126/195] gnb,config: Added RLC-NR UM and AM configuration parsing --- srsenb/src/enb_cfg_parser.cc | 97 +++++++++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/srsenb/src/enb_cfg_parser.cc b/srsenb/src/enb_cfg_parser.cc index 55c64582f..0edf9a6b4 100644 --- a/srsenb/src/enb_cfg_parser.cc +++ b/srsenb/src/enb_cfg_parser.cc @@ -710,9 +710,104 @@ int field_five_qi::parse(libconfig::Setting& root) pdcp_cfg->t_reordering_present = true; } + // Parse RLC section + if (!q.exists("rlc_config")) { + fprintf(stderr, "Error section rlc_config not found for 5qi=%d\n", five_qi); + return SRSRAN_ERROR; + } + libconfig::Setting& rlc = q["rlc_config"]; + asn1::rrc_nr::rlc_cfg_c* rlc_cfg = &five_qi_cfg.rlc_cfg; + if (rlc.exists("um_uni_dir_ul") || rlc.exists("um_uni_dir_dl")) { + // Sanity check: RLC UM uni-directional is not supported. + fprintf(stderr, "Error uni-directional UM not supported. 5QI=%d\n", five_qi); + return SRSRAN_ERROR; + } + + if (rlc.exists("am")) { + rlc_cfg->set_am(); + } else if (rlc.exists("um_bi_dir")) { + rlc_cfg->set_um_bi_dir(); + } else { + fprintf(stderr, "Invalid combination of UL/DL UM/AM for 5QI=%d\n", five_qi); + return SRSRAN_ERROR; + } + + // Parse RLC-AM section + if (rlc_cfg->type() == asn1::rrc_nr::rlc_cfg_c::types::am) { + libconfig::Setting& rlc_am = rlc["am"]; + libconfig::Setting& rlc_am_ul = rlc_am["ul_am"]; + libconfig::Setting& rlc_am_dl = rlc_am["dl_am"]; + asn1::rrc_nr::ul_am_rlc_s& ul_am_cfg = rlc_cfg->am().ul_am_rlc; + asn1::rrc_nr::dl_am_rlc_s& dl_am_cfg = rlc_cfg->am().dl_am_rlc; + + // RLC AM UL + // SN length + field_asn1_enum_number rlc_sn_size_ul("sn_field_len", &ul_am_cfg.sn_field_len); + if (rlc_sn_size_ul.parse(rlc_am_ul) == SRSRAN_ERROR) { + ul_am_cfg.sn_field_len_present = false; + } else { + ul_am_cfg.sn_field_len_present = true; + } + // t-PollRetx + field_asn1_enum_number rlc_t_poll_retx("t_poll_retx", &ul_am_cfg.t_poll_retx); + rlc_t_poll_retx.parse(rlc_am_ul); + // pollPDU + field_asn1_enum_number rlc_poll_pdu("poll_pdu", &ul_am_cfg.poll_pdu); + rlc_poll_pdu.parse(rlc_am_ul); + // pollBYTE + field_asn1_enum_number rlc_poll_bytes("poll_byte", &ul_am_cfg.poll_byte); + rlc_poll_bytes.parse(rlc_am_ul); + // maxRetxThreshold + field_asn1_enum_number rlc_max_retx_thres( + "max_retx_thres", &ul_am_cfg.max_retx_thres); + rlc_max_retx_thres.parse(rlc_am_ul); + + // RLC AM DL + // SN length + field_asn1_enum_number rlc_sn_size_dl("sn_field_len", &dl_am_cfg.sn_field_len); + if (rlc_sn_size_dl.parse(rlc_am_dl) == SRSRAN_ERROR) { + dl_am_cfg.sn_field_len_present = false; + } else { + dl_am_cfg.sn_field_len_present = true; + } + // t-reassembly + field_asn1_enum_number rlc_t_reassembly("t_reassembly", &dl_am_cfg.t_reassembly); + rlc_t_reassembly.parse(rlc_am_dl); + // t-statusProhibit + field_asn1_enum_number rlc_status_prohibit("t_status_prohibit", + &dl_am_cfg.t_status_prohibit); + rlc_status_prohibit.parse(rlc_am_dl); + } else if (rlc_cfg->type() == asn1::rrc_nr::rlc_cfg_c::types::um_bi_dir) { + libconfig::Setting& rlc_um = rlc["um_bi_dir"]; + libconfig::Setting& rlc_um_ul = rlc_um["ul_um"]; + libconfig::Setting& rlc_um_dl = rlc_um["dl_um"]; + asn1::rrc_nr::ul_um_rlc_s& ul_um_cfg = rlc_cfg->um_bi_dir().ul_um_rlc; + asn1::rrc_nr::dl_um_rlc_s& dl_um_cfg = rlc_cfg->um_bi_dir().dl_um_rlc; + + // RLC UM UL + // SN field length + field_asn1_enum_number rlc_sn_size_ul("sn_field_len", &ul_um_cfg.sn_field_len); + if (rlc_sn_size_ul.parse(rlc_um_ul) == SRSRAN_ERROR) { + ul_um_cfg.sn_field_len_present = false; + } else { + ul_um_cfg.sn_field_len_present = true; + } + + // RLC UM DL + // SN field length + field_asn1_enum_number rlc_sn_size_dl("sn_field_len", &dl_um_cfg.sn_field_len); + if (rlc_sn_size_dl.parse(rlc_um_dl) == SRSRAN_ERROR) { + dl_um_cfg.sn_field_len_present = false; + } else { + dl_um_cfg.sn_field_len_present = true; + } + // t-reassembly + field_asn1_enum_number rlc_t_reassembly_dl("t_reassembly", &dl_um_cfg.t_reassembly); + rlc_t_reassembly_dl.parse(rlc_um_dl); + } + cfg.insert(std::make_pair(five_qi, five_qi_cfg)); } - return 0; } namespace rr_sections { From 19487c680c5220a6ef6beaa95c89ac0a8fa96020 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Mon, 28 Feb 2022 15:08:05 +0000 Subject: [PATCH 127/195] gnb,config: Added logging about pdcp and rlc configs. --- srsgnb/src/stack/rrc/rrc_nr.cc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/srsgnb/src/stack/rrc/rrc_nr.cc b/srsgnb/src/stack/rrc/rrc_nr.cc index 1954dafa4..5ff6a49a0 100644 --- a/srsgnb/src/stack/rrc/rrc_nr.cc +++ b/srsgnb/src/stack/rrc/rrc_nr.cc @@ -94,6 +94,17 @@ int rrc_nr::init(const rrc_nr_cfg_t& cfg_, config_mac(); logger.info("Number of 5QI %d", cfg.five_qi_cfg.size()); + for (const std::pair& five_qi_cfg : cfg.five_qi_cfg) { + logger.info("5QI configuration. 5QI=%d", five_qi_cfg.first); + if (logger.info.enabled()) { + asn1::json_writer js{}; + five_qi_cfg.second.pdcp_cfg.to_json(js); + logger.info("PDCP NR configuration: %s", js.to_string().c_str()); + js = {}; + five_qi_cfg.second.rlc_cfg.to_json(js); + logger.info("RLC NR configuration: %s", js.to_string().c_str()); + } + } logger.info("NIA preference list: NIA%d, NIA%d, NIA%d", cfg.nia_preference_list[0], cfg.nia_preference_list[1], From ca9b99fb472a8574d6f92285a852863afd9ebf8f Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Fri, 25 Feb 2022 15:00:11 +0000 Subject: [PATCH 128/195] gnb,config: Changed logging for setting not found exception, to make it clearer which setting was not found --- srsenb/src/parser.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srsenb/src/parser.cc b/srsenb/src/parser.cc index ab593c564..d72dd3043 100644 --- a/srsenb/src/parser.cc +++ b/srsenb/src/parser.cc @@ -109,7 +109,7 @@ int parser::section::parse(Setting& root) *enabled_value = false; return 0; } else { - std::cerr << "Error section " << name.c_str() << " not found." << std::endl; + std::cerr << "Error in section " << name.c_str() << ". " << ex.getPath() << " not found." << std::endl; return -1; } } From b8006534f0364528ffc40176e3d0e61f536b366b Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Tue, 8 Mar 2022 15:24:12 +0000 Subject: [PATCH 129/195] gnb,ngap,rrc_nr: Passing 5QI from the NGAP to the RRC when establishing eps bearers. Filling PDCP-NR config from 5QI config. --- .../srsran/interfaces/gnb_rrc_nr_interfaces.h | 25 +++++++++++-------- srsgnb/hdr/stack/rrc/rrc_nr.h | 3 ++- srsgnb/hdr/stack/rrc/rrc_nr_ue.h | 2 +- srsgnb/src/stack/ngap/ngap_ue_proc.cc | 6 ++++- srsgnb/src/stack/ngap/test/ngap_test.cc | 6 ++++- srsgnb/src/stack/rrc/rrc_nr.cc | 8 ++++-- srsgnb/src/stack/rrc/rrc_nr_ue.cc | 20 ++++++--------- .../src/stack/rrc/test/rrc_nr_test_helpers.cc | 2 +- 8 files changed, 41 insertions(+), 31 deletions(-) diff --git a/lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h b/lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h index aaa81a1bf..1717dc238 100644 --- a/lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h +++ b/lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h @@ -20,17 +20,20 @@ namespace srsenb { class rrc_interface_ngap_nr { public: - virtual int ue_set_security_cfg_key(uint16_t rnti, const asn1::fixed_bitstring<256, false, true>& key) = 0; - virtual int ue_set_bitrates(uint16_t rnti, const asn1::ngap::ue_aggregate_maximum_bit_rate_s& rates) = 0; - virtual int set_aggregate_max_bitrate(uint16_t rnti, const asn1::ngap::ue_aggregate_maximum_bit_rate_s& rates) = 0; - virtual int ue_set_security_cfg_capabilities(uint16_t rnti, const asn1::ngap::ue_security_cap_s& caps) = 0; - virtual int start_security_mode_procedure(uint16_t rnti, srsran::unique_byte_buffer_t nas_pdu) = 0; - virtual int - establish_rrc_bearer(uint16_t rnti, uint16_t pdu_session_id, srsran::const_byte_span nas_pdu, uint32_t lcid) = 0; - virtual int allocate_lcid(uint16_t rnti) = 0; - virtual int release_bearers(uint16_t rnti) = 0; - virtual void release_user(uint16_t rnti) = 0; - virtual void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) = 0; + virtual int ue_set_security_cfg_key(uint16_t rnti, const asn1::fixed_bitstring<256, false, true>& key) = 0; + virtual int ue_set_bitrates(uint16_t rnti, const asn1::ngap::ue_aggregate_maximum_bit_rate_s& rates) = 0; + virtual int set_aggregate_max_bitrate(uint16_t rnti, const asn1::ngap::ue_aggregate_maximum_bit_rate_s& rates) = 0; + virtual int ue_set_security_cfg_capabilities(uint16_t rnti, const asn1::ngap::ue_security_cap_s& caps) = 0; + virtual int start_security_mode_procedure(uint16_t rnti, srsran::unique_byte_buffer_t nas_pdu) = 0; + virtual int establish_rrc_bearer(uint16_t rnti, + uint16_t pdu_session_id, + srsran::const_byte_span nas_pdu, + uint32_t lcid, + uint32_t five_qi) = 0; + virtual int allocate_lcid(uint16_t rnti) = 0; + virtual int release_bearers(uint16_t rnti) = 0; + virtual void release_user(uint16_t rnti) = 0; + virtual void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) = 0; }; } // namespace srsenb diff --git a/srsgnb/hdr/stack/rrc/rrc_nr.h b/srsgnb/hdr/stack/rrc/rrc_nr.h index a82aafdc7..fae1cf979 100644 --- a/srsgnb/hdr/stack/rrc/rrc_nr.h +++ b/srsgnb/hdr/stack/rrc/rrc_nr.h @@ -102,7 +102,8 @@ public: int establish_rrc_bearer(uint16_t rnti, uint16_t pdu_session_id, srsran::const_byte_span nas_pdu, - uint32_t lcid) final; + uint32_t lcid, + uint32_t five_qi) final; int release_bearers(uint16_t rnti) final; void release_user(uint16_t rnti) final; void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) final; diff --git a/srsgnb/hdr/stack/rrc/rrc_nr_ue.h b/srsgnb/hdr/stack/rrc/rrc_nr_ue.h index 4240ed82a..37035746a 100644 --- a/srsgnb/hdr/stack/rrc/rrc_nr_ue.h +++ b/srsgnb/hdr/stack/rrc/rrc_nr_ue.h @@ -79,7 +79,7 @@ public: void handle_ul_information_transfer(const asn1::rrc_nr::ul_info_transfer_s& msg); // NGAP interface - void establish_eps_bearer(uint32_t pdu_session_id, srsran::const_byte_span nas_pdu, uint32_t lcid); + void establish_eps_bearer(uint32_t pdu_session_id, srsran::const_byte_span nas_pdu, uint32_t lcid, uint32_t five_qi); /* TS 38.331 - 5.3.4 Initial AS security activation */ void send_security_mode_command(srsran::unique_byte_buffer_t nas_pdu); diff --git a/srsgnb/src/stack/ngap/ngap_ue_proc.cc b/srsgnb/src/stack/ngap/ngap_ue_proc.cc index 17512d922..a18d4fd61 100644 --- a/srsgnb/src/stack/ngap/ngap_ue_proc.cc +++ b/srsgnb/src/stack/ngap/ngap_ue_proc.cc @@ -92,9 +92,13 @@ proc_outcome_t ngap_ue_pdu_session_res_setup_proc::init(const asn1::ngap::pdu_se teid_in, addr_in.to_string()); + uint16_t five_qi = pdu_ses_res_setup_req_trans->qos_flow_setup_request_list.value[0] + .qos_flow_level_qos_params.qos_characteristics.non_dynamic5_qi() + .five_qi; + // QoS parameter mapping in config in LTE enb if (su_req.pdu_session_nas_pdu.size() > 0) { - if (rrc->establish_rrc_bearer(ue_ctxt->rnti, su_req.pdu_session_id, su_req.pdu_session_nas_pdu, lcid) == + if (rrc->establish_rrc_bearer(ue_ctxt->rnti, su_req.pdu_session_id, su_req.pdu_session_nas_pdu, lcid, five_qi) == SRSRAN_SUCCESS) { parent->send_pdu_session_resource_setup_response(su_req.pdu_session_id, teid_in, addr_in); return proc_outcome_t::success; diff --git a/srsgnb/src/stack/ngap/test/ngap_test.cc b/srsgnb/src/stack/ngap/test/ngap_test.cc index 8bd6803ac..93038819e 100644 --- a/srsgnb/src/stack/ngap/test/ngap_test.cc +++ b/srsgnb/src/stack/ngap/test/ngap_test.cc @@ -108,7 +108,11 @@ public: sec_mod_proc_started = true; return SRSRAN_SUCCESS; } - int establish_rrc_bearer(uint16_t rnti, uint16_t pdu_session_id, srsran::const_byte_span nas_pdu, uint32_t lcid) + int establish_rrc_bearer(uint16_t rnti, + uint16_t pdu_session_id, + srsran::const_byte_span nas_pdu, + uint32_t lcid, + uint32_t five_qi) { rrc_logger.info("Establish RRC bearer"); return SRSRAN_SUCCESS; diff --git a/srsgnb/src/stack/rrc/rrc_nr.cc b/srsgnb/src/stack/rrc/rrc_nr.cc index 5ff6a49a0..2b04b7857 100644 --- a/srsgnb/src/stack/rrc/rrc_nr.cc +++ b/srsgnb/src/stack/rrc/rrc_nr.cc @@ -648,14 +648,18 @@ int rrc_nr::start_security_mode_procedure(uint16_t rnti, srsran::unique_byte_buf user_it->second->send_security_mode_command(std::move(nas_pdu)); return SRSRAN_SUCCESS; } -int rrc_nr::establish_rrc_bearer(uint16_t rnti, uint16_t pdu_session_id, srsran::const_byte_span nas_pdu, uint32_t lcid) +int rrc_nr::establish_rrc_bearer(uint16_t rnti, + uint16_t pdu_session_id, + srsran::const_byte_span nas_pdu, + uint32_t lcid, + uint32_t five_qi) { if (not users.contains(rnti)) { logger.error("Establishing RRC bearers for inexistent rnti=0x%x", rnti); return SRSRAN_ERROR; } - users[rnti]->establish_eps_bearer(pdu_session_id, nas_pdu, lcid); + users[rnti]->establish_eps_bearer(pdu_session_id, nas_pdu, lcid, five_qi); // TODO: verify whether this is the best place where to call the RRCReconfig users[rnti]->send_rrc_reconfiguration(); diff --git a/srsgnb/src/stack/rrc/rrc_nr_ue.cc b/srsgnb/src/stack/rrc/rrc_nr_ue.cc index 65f6fdc1a..b6f8a3692 100644 --- a/srsgnb/src/stack/rrc/rrc_nr_ue.cc +++ b/srsgnb/src/stack/rrc/rrc_nr_ue.cc @@ -1316,7 +1316,10 @@ void rrc_nr::ue::handle_ul_information_transfer(const asn1::rrc_nr::ul_info_tran parent->ngap->write_pdu(rnti, msg.crit_exts.ul_info_transfer().ded_nas_msg); } -void rrc_nr::ue::establish_eps_bearer(uint32_t pdu_session_id, srsran::const_byte_span nas_pdu, uint32_t lcid) +void rrc_nr::ue::establish_eps_bearer(uint32_t pdu_session_id, + srsran::const_byte_span nas_pdu, + uint32_t lcid, + uint32_t five_qi) { // Enqueue NAS PDU srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); @@ -1343,18 +1346,9 @@ void rrc_nr::ue::establish_eps_bearer(uint32_t pdu_session_id, srsran::const_byt drb.cn_assoc.sdap_cfg().mapped_qos_flows_to_add.resize(1); drb.cn_assoc.sdap_cfg().mapped_qos_flows_to_add[0] = 1; - drb.drb_id = 1; - drb.pdcp_cfg_present = true; - drb.pdcp_cfg.drb_present = true; - drb.pdcp_cfg.drb.discard_timer_present = true; - drb.pdcp_cfg.drb.discard_timer.value = pdcp_cfg_s::drb_s_::discard_timer_opts::ms100; - drb.pdcp_cfg.drb.pdcp_sn_size_ul_present = true; - drb.pdcp_cfg.drb.pdcp_sn_size_ul.value = asn1::rrc_nr::pdcp_cfg_s::drb_s_::pdcp_sn_size_ul_opts::len18bits; - drb.pdcp_cfg.drb.pdcp_sn_size_dl_present = true; - drb.pdcp_cfg.drb.pdcp_sn_size_dl.value = asn1::rrc_nr::pdcp_cfg_s::drb_s_::pdcp_sn_size_dl_opts::len18bits; - drb.pdcp_cfg.drb.hdr_compress.set_not_used(); - drb.pdcp_cfg.t_reordering_present = true; - drb.pdcp_cfg.t_reordering.value = asn1::rrc_nr::pdcp_cfg_s::t_reordering_opts::ms0; + drb.drb_id = 1; + drb.pdcp_cfg_present = true; + drb.pdcp_cfg = parent->cfg.five_qi_cfg[five_qi].pdcp_cfg; next_radio_bearer_cfg.drb_to_add_mod_list.push_back(drb); diff --git a/srsgnb/src/stack/rrc/test/rrc_nr_test_helpers.cc b/srsgnb/src/stack/rrc/test/rrc_nr_test_helpers.cc index 047693a09..c183437bf 100644 --- a/srsgnb/src/stack/rrc/test/rrc_nr_test_helpers.cc +++ b/srsgnb/src/stack/rrc/test/rrc_nr_test_helpers.cc @@ -315,7 +315,7 @@ void test_rrc_nr_2nd_reconfiguration(srsran::task_scheduler& task_sched, NAS_msg.from_string("c574defc80ba722bffb8eacb6f8a163e3222cf1542ac529f6980bb15e0bf12d9f2b29f11fb458ec9"); // STEP 2 - Trigger and send RRCReconfiguration command (gNB -> UE) - rrc_obj.establish_rrc_bearer(rnti, 1, NAS_msg, srsran::srb_to_lcid(srsran::nr_srb::srb1)); + rrc_obj.establish_rrc_bearer(rnti, 1, NAS_msg, srsran::srb_to_lcid(srsran::nr_srb::srb1), 9); // Test whether there exists the SRB1 initiated in the Connection Establishment // We test this as the SRB1 was set up in a different function From 386b1640a9d5411d5b17c024d5aa5ee44a074d1c Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Tue, 8 Mar 2022 15:48:02 +0000 Subject: [PATCH 130/195] gnb,bearer_mapper: hack to keep 5QI information in bearer mapper --- lib/include/srsran/common/bearer_manager.h | 12 ++++++---- lib/src/common/bearer_manager.cc | 27 ++++++++++++++++++---- srsgnb/src/stack/rrc/rrc_nr_ue.cc | 1 + 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/lib/include/srsran/common/bearer_manager.h b/lib/include/srsran/common/bearer_manager.h index 0e525408c..d7f56a0ce 100644 --- a/lib/include/srsran/common/bearer_manager.h +++ b/lib/include/srsran/common/bearer_manager.h @@ -36,6 +36,7 @@ public: srsran::srsran_rat_t rat; uint32_t lcid; uint32_t eps_bearer_id; + uint32_t five_qi = 0; bool is_valid() const { return rat != srsran_rat_t::nulltype; } }; static const radio_bearer_t invalid_rb; @@ -50,9 +51,11 @@ public: bool has_active_radio_bearer(uint32_t eps_bearer_id); - radio_bearer_t get_radio_bearer(uint32_t eps_bearer_id); + radio_bearer_t get_radio_bearer(uint32_t eps_bearer_id) const; - radio_bearer_t get_eps_bearer_id_for_lcid(uint32_t lcid); + radio_bearer_t get_eps_bearer_id_for_lcid(uint32_t lcid) const; + + bool set_five_qi(uint32_t eps_bearer_id, uint16_t five_qi); private: using eps_rb_map_t = std::map; @@ -147,7 +150,8 @@ public: void rem_user(uint16_t rnti); bool has_active_radio_bearer(uint16_t rnti, uint32_t eps_bearer_id); radio_bearer_t get_radio_bearer(uint16_t rnti, uint32_t eps_bearer_id); - radio_bearer_t get_lcid_bearer(uint16_t rnti, uint32_t lcid); + radio_bearer_t get_lcid_bearer(uint16_t rnti, uint32_t lcid) const; + bool set_five_qi(uint16_t rnti, uint32_t eps_bearer_id, uint16_t five_qi); private: srslog::basic_logger& logger; @@ -157,4 +161,4 @@ private: } // namespace srsenb -#endif // SRSRAN_BEARER_MANAGER_H \ No newline at end of file +#endif // SRSRAN_BEARER_MANAGER_H diff --git a/lib/src/common/bearer_manager.cc b/lib/src/common/bearer_manager.cc index c4ecf960b..1eb74ada8 100644 --- a/lib/src/common/bearer_manager.cc +++ b/lib/src/common/bearer_manager.cc @@ -52,18 +52,28 @@ bool ue_bearer_manager_impl::has_active_radio_bearer(uint32_t eps_bearer_id) return bearers.count(eps_bearer_id) > 0; } -ue_bearer_manager_impl::radio_bearer_t ue_bearer_manager_impl::get_radio_bearer(uint32_t eps_bearer_id) +ue_bearer_manager_impl::radio_bearer_t ue_bearer_manager_impl::get_radio_bearer(uint32_t eps_bearer_id) const { auto it = bearers.find(eps_bearer_id); return it != bearers.end() ? it->second : invalid_rb; } -ue_bearer_manager_impl::radio_bearer_t ue_bearer_manager_impl::get_eps_bearer_id_for_lcid(uint32_t lcid) +ue_bearer_manager_impl::radio_bearer_t ue_bearer_manager_impl::get_eps_bearer_id_for_lcid(uint32_t lcid) const { auto lcid_it = lcid_to_eps_bearer_id.find(lcid); return lcid_it != lcid_to_eps_bearer_id.end() ? bearers.at(lcid_it->second) : invalid_rb; } +bool ue_bearer_manager_impl::set_five_qi(uint32_t eps_bearer_id, uint16_t five_qi) +{ + auto it = bearers.find(eps_bearer_id); + if (it == bearers.end()) { + return false; + } + it->second.five_qi = five_qi; + return true; +} + } // namespace detail } // namespace srsran @@ -176,7 +186,7 @@ bool enb_bearer_manager::has_active_radio_bearer(uint16_t rnti, uint32_t eps_bea return user_it->second.has_active_radio_bearer(eps_bearer_id); } -enb_bearer_manager::radio_bearer_t enb_bearer_manager::get_lcid_bearer(uint16_t rnti, uint32_t lcid) +enb_bearer_manager::radio_bearer_t enb_bearer_manager::get_lcid_bearer(uint16_t rnti, uint32_t lcid) const { auto user_it = users_map.find(rnti); if (user_it == users_map.end()) { @@ -194,4 +204,13 @@ enb_bearer_manager::radio_bearer_t enb_bearer_manager::get_radio_bearer(uint16_t return user_it->second.get_radio_bearer(eps_bearer_id); } -} // namespace srsenb \ No newline at end of file +bool enb_bearer_manager::set_five_qi(uint16_t rnti, uint32_t eps_bearer_id, uint16_t five_qi) +{ + auto user_it = users_map.find(rnti); + if (user_it == users_map.end()) { + return false; + } + return user_it->second.set_five_qi(eps_bearer_id, five_qi); +} + +} // namespace srsenb diff --git a/srsgnb/src/stack/rrc/rrc_nr_ue.cc b/srsgnb/src/stack/rrc/rrc_nr_ue.cc index b6f8a3692..60ac3eeec 100644 --- a/srsgnb/src/stack/rrc/rrc_nr_ue.cc +++ b/srsgnb/src/stack/rrc/rrc_nr_ue.cc @@ -1354,6 +1354,7 @@ void rrc_nr::ue::establish_eps_bearer(uint32_t pdu_session_id, parent->bearer_mapper->add_eps_bearer( rnti, lcid - 3, srsran::srsran_rat_t::nr, lcid); // TODO: configurable bearer id <-> lcid mapping + parent->bearer_mapper->set_five_qi(rnti, lcid - 3, five_qi); logger.info("Established EPS bearer for LCID %u and RNTI 0x%x", lcid, rnti); } From bbae0ce93cbc07b04e57599270dc51f3d461fa38 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Thu, 3 Mar 2022 18:51:05 +0000 Subject: [PATCH 131/195] gnb,rrc_nr: Filling in RLC-NR DRB configs from 5QI config now. --- srsgnb/hdr/stack/rrc/cell_asn1_config.h | 3 +++ srsgnb/src/stack/rrc/cell_asn1_config.cc | 24 ++++++++++++------------ srsgnb/src/stack/rrc/rrc_nr_ue.cc | 9 ++++++--- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/srsgnb/hdr/stack/rrc/cell_asn1_config.h b/srsgnb/hdr/stack/rrc/cell_asn1_config.h index 3ad12601c..2898acd20 100644 --- a/srsgnb/hdr/stack/rrc/cell_asn1_config.h +++ b/srsgnb/hdr/stack/rrc/cell_asn1_config.h @@ -15,6 +15,7 @@ #include "rrc_nr_config.h" #include "srsran/asn1/rrc_nr.h" +#include "srsran/common/bearer_manager.h" #include "srsran/common/common_nr.h" namespace srsenb { @@ -44,6 +45,8 @@ bool compute_diff_radio_bearer_cfg(const rrc_nr_cfg_t& cfg, /// Apply radioBearerConfig updates to CellGroupConfig void fill_cellgroup_with_radio_bearer_cfg(const rrc_nr_cfg_t& cfg, + uint32_t rnti, + const enb_bearer_manager& bearer_mapper, const asn1::rrc_nr::radio_bearer_cfg_s& bearers, asn1::rrc_nr::cell_group_cfg_s& out); diff --git a/srsgnb/src/stack/rrc/cell_asn1_config.cc b/srsgnb/src/stack/rrc/cell_asn1_config.cc index 9da82a9a1..7e2a37e00 100644 --- a/srsgnb/src/stack/rrc/cell_asn1_config.cc +++ b/srsgnb/src/stack/rrc/cell_asn1_config.cc @@ -978,20 +978,17 @@ void fill_srb(const rrc_nr_cfg_t& cfg, srsran::nr_srb srb_id, asn1::rrc_nr::rlc_ } /// Fill DRB with parameters derived from cfg -void fill_drb(const rrc_nr_cfg_t& cfg, uint32_t lcid, srsran::nr_drb drb_id, asn1::rrc_nr::rlc_bearer_cfg_s& out) +void fill_drb(const rrc_nr_cfg_t& cfg, + const enb_bearer_manager::radio_bearer_t& rb, + srsran::nr_drb drb_id, + asn1::rrc_nr::rlc_bearer_cfg_s& out) { - out.lc_ch_id = lcid; + out.lc_ch_id = rb.lcid; out.served_radio_bearer_present = true; out.served_radio_bearer.set_drb_id() = (uint8_t)drb_id; - out.rlc_cfg_present = true; - auto& ul_um = out.rlc_cfg.set_um_bi_dir().ul_um_rlc; - ul_um.sn_field_len_present = true; - ul_um.sn_field_len.value = sn_field_len_um_opts::size12; - auto& dl_um = out.rlc_cfg.um_bi_dir().dl_um_rlc; - dl_um.sn_field_len_present = true; - dl_um.sn_field_len.value = sn_field_len_um_opts::size12; - dl_um.t_reassembly.value = t_reassembly_opts::ms50; + out.rlc_cfg_present = true; + out.rlc_cfg = cfg.five_qi_cfg.at(rb.five_qi).rlc_cfg; // MAC logical channel config out.mac_lc_ch_cfg_present = true; @@ -1319,6 +1316,8 @@ bool compute_diff_radio_bearer_cfg(const rrc_nr_cfg_t& cfg, } void fill_cellgroup_with_radio_bearer_cfg(const rrc_nr_cfg_t& cfg, + const uint32_t rnti, + const enb_bearer_manager& bearer_mapper, const asn1::rrc_nr::radio_bearer_cfg_s& bearers, asn1::rrc_nr::cell_group_cfg_s& out) { @@ -1333,8 +1332,9 @@ void fill_cellgroup_with_radio_bearer_cfg(const rrc_nr_cfg_t& // Add DRBs for (const drb_to_add_mod_s& drb : bearers.drb_to_add_mod_list) { out.rlc_bearer_to_add_mod_list.push_back({}); - uint32_t lcid = drb.drb_id + (int)srsran::nr_srb::count - 1; - fill_drb(cfg, lcid, (srsran::nr_drb)drb.drb_id, out.rlc_bearer_to_add_mod_list.back()); + uint32_t lcid = drb.drb_id + (int)srsran::nr_srb::count - 1; + enb_bearer_manager::radio_bearer_t rb = bearer_mapper.get_lcid_bearer(rnti, lcid); + fill_drb(cfg, rb, (srsran::nr_drb)drb.drb_id, out.rlc_bearer_to_add_mod_list.back()); } // Release DRBs diff --git a/srsgnb/src/stack/rrc/rrc_nr_ue.cc b/srsgnb/src/stack/rrc/rrc_nr_ue.cc index 60ac3eeec..b287f28bb 100644 --- a/srsgnb/src/stack/rrc/rrc_nr_ue.cc +++ b/srsgnb/src/stack/rrc/rrc_nr_ue.cc @@ -968,7 +968,8 @@ void rrc_nr::ue::handle_rrc_reestablishment_request(const asn1::rrc_nr::rrc_rees // compute config and create SRB1 for new user asn1::rrc_nr::radio_bearer_cfg_s dummy_radio_bearer_cfg; // just to compute difference, it's never sent to UE compute_diff_radio_bearer_cfg(parent->cfg, radio_bearer_cfg, next_radio_bearer_cfg, dummy_radio_bearer_cfg); - fill_cellgroup_with_radio_bearer_cfg(parent->cfg, dummy_radio_bearer_cfg, next_cell_group_cfg); + fill_cellgroup_with_radio_bearer_cfg( + parent->cfg, old_rnti, *parent->bearer_mapper, dummy_radio_bearer_cfg, next_cell_group_cfg); // send RRC Reestablishment message and restore bearer configuration send_connection_reest(old_ue->sec_ctx.get_ncc()); @@ -1061,7 +1062,8 @@ void rrc_nr::ue::send_rrc_setup() // - Setup masterCellGroup // - Derive master cell group config bearers - fill_cellgroup_with_radio_bearer_cfg(parent->cfg, setup_ies.radio_bearer_cfg, next_cell_group_cfg); + fill_cellgroup_with_radio_bearer_cfg( + parent->cfg, rnti, *parent->bearer_mapper, setup_ies.radio_bearer_cfg, next_cell_group_cfg); // - Pack masterCellGroup into container srsran::unique_byte_buffer_t pdu = parent->pack_into_pdu(next_cell_group_cfg, __FUNCTION__); if (pdu == nullptr) { @@ -1209,7 +1211,8 @@ void rrc_nr::ue::send_rrc_reconfiguration() // Fill masterCellGroup cell_group_cfg_s master_cell_group; master_cell_group.cell_group_id = 0; - fill_cellgroup_with_radio_bearer_cfg(parent->cfg, ies.radio_bearer_cfg, master_cell_group); + fill_cellgroup_with_radio_bearer_cfg( + parent->cfg, rnti, *parent->bearer_mapper, ies.radio_bearer_cfg, master_cell_group); // Pack masterCellGroup into container srsran::unique_byte_buffer_t pdu = parent->pack_into_pdu(master_cell_group, __FUNCTION__); From c1a565027b04cf14582570e070711ca68687ebc1 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Wed, 9 Mar 2022 14:16:26 +0000 Subject: [PATCH 132/195] gnb,config: Minor fixes to rb.conf.example * added whitespace for QCI/5QI configs * make pdcp nr config default to 18 bits * swapped UM and AM between 5QI 7 and 9 --- srsenb/rb.conf.example | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/srsenb/rb.conf.example b/srsenb/rb.conf.example index e4f13fdb5..8cab02bb6 100644 --- a/srsenb/rb.conf.example +++ b/srsenb/rb.conf.example @@ -40,7 +40,7 @@ qci_config = ( { - qci=7; + qci = 7; pdcp_config = { discard_timer = -1; pdcp_sn_size = 12; @@ -65,7 +65,7 @@ qci_config = ( }; }, { - qci=9; + qci = 9; pdcp_config = { discard_timer = 150; status_report_required = true; @@ -97,50 +97,50 @@ qci_config = ( // 5G Section five_qi_config = ( { - five_qi=7; + five_qi = 7; pdcp_nr_config = { drb = { discard_timer = 50; - pdcp_sn_size_ul = 12; - pdcp_sn_size_dl = 12; + pdcp_sn_size_ul = 18; + pdcp_sn_size_dl = 18; }; t_reordering = 50; }; rlc_config = { - am = { - ul_am = { + um_bi_dir = { + ul_um = { sn_field_len = 12; - t_poll_retx = 50; - poll_pdu = 4; - poll_byte = 3000; - max_retx_thres = 4; }; - dl_am = { + dl_um = { sn_field_len = 12; t_reassembly = 50; - t_status_prohibit = 50; }; }; }; }, { - five_qi=9; + five_qi = 9; pdcp_nr_config = { drb = { discard_timer = 50; - pdcp_sn_size_ul = 12; - pdcp_sn_size_dl = 12; + pdcp_sn_size_ul = 18; + pdcp_sn_size_dl = 18; }; t_reordering = 50; }; rlc_config = { - um_bi_dir = { - ul_um = { + am = { + ul_am = { sn_field_len = 12; + t_poll_retx = 50; + poll_pdu = 4; + poll_byte = 3000; + max_retx_thres = 4; }; - dl_um = { + dl_am = { sn_field_len = 12; t_reassembly = 50; + t_status_prohibit = 50; }; }; }; From efdba8b1c045c1f4fdb8fe4fa0f3c087746e31b5 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Fri, 8 Apr 2022 17:44:39 +0100 Subject: [PATCH 133/195] gnb,rrc_nr: fix up second RRC reconfiguration test --- srsgnb/src/stack/rrc/test/rrc_nr_test.cc | 8 ++++++++ srsgnb/src/stack/rrc/test/rrc_nr_test_helpers.cc | 6 +++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/srsgnb/src/stack/rrc/test/rrc_nr_test.cc b/srsgnb/src/stack/rrc/test/rrc_nr_test.cc index 3d70c1961..259802dbb 100644 --- a/srsgnb/src/stack/rrc/test/rrc_nr_test.cc +++ b/srsgnb/src/stack/rrc/test/rrc_nr_test.cc @@ -147,6 +147,11 @@ void test_rrc_sa_connection() rrc_nr rrc_obj(&task_sched); + // Dummy RLC/PDCP configs + asn1::rrc_nr::rlc_cfg_c rlc_cfg; + rlc_cfg.set_um_bi_dir(); + rlc_cfg.um_bi_dir().dl_um_rlc.t_reassembly = t_reassembly_e::ms50; + // set cfg rrc_nr_cfg_t rrc_cfg_nr; rrc_cfg_nr.cell_list.emplace_back(); @@ -158,6 +163,9 @@ void test_rrc_sa_connection() rrc_cfg_nr.cell_list[0].duplex_mode = SRSRAN_DUPLEX_MODE_FDD; rrc_cfg_nr.is_standalone = true; rrc_cfg_nr.enb_id = 0x19B; + rrc_cfg_nr.five_qi_cfg[9].configured = true; + rrc_cfg_nr.five_qi_cfg[9].rlc_cfg = rlc_cfg; + rrc_cfg_nr.five_qi_cfg[9].pdcp_cfg = {}; srsran::string_to_mcc("001", &rrc_cfg_nr.mcc); srsran::string_to_mnc("01", &rrc_cfg_nr.mnc); set_derived_nr_cell_params(rrc_cfg_nr.is_standalone, rrc_cfg_nr.cell_list[0]); diff --git a/srsgnb/src/stack/rrc/test/rrc_nr_test_helpers.cc b/srsgnb/src/stack/rrc/test/rrc_nr_test_helpers.cc index c183437bf..954e24a9f 100644 --- a/srsgnb/src/stack/rrc/test/rrc_nr_test_helpers.cc +++ b/srsgnb/src/stack/rrc/test/rrc_nr_test_helpers.cc @@ -314,14 +314,14 @@ void test_rrc_nr_2nd_reconfiguration(srsran::task_scheduler& task_sched, asn1::unbounded_octstring NAS_msg; NAS_msg.from_string("c574defc80ba722bffb8eacb6f8a163e3222cf1542ac529f6980bb15e0bf12d9f2b29f11fb458ec9"); - // STEP 2 - Trigger and send RRCReconfiguration command (gNB -> UE) - rrc_obj.establish_rrc_bearer(rnti, 1, NAS_msg, srsran::srb_to_lcid(srsran::nr_srb::srb1), 9); - // Test whether there exists the SRB1 initiated in the Connection Establishment // We test this as the SRB1 was set up in a different function TESTASSERT_EQ(rnti, pdcp.last_sdu_rnti); TESTASSERT_EQ(srsran::srb_to_lcid(srsran::nr_srb::srb1), pdcp.last_sdu_lcid); + // STEP 2 - Trigger and send RRCReconfiguration command (gNB -> UE) + rrc_obj.establish_rrc_bearer(rnti, 1, NAS_msg, 4, 9); + dl_dcch_msg_s dl_dcch_msg; { asn1::cbit_ref bref{pdcp.last_sdu->data(), pdcp.last_sdu->size()}; From 697781f0248d14739ff0bf7128302d01a0f29127 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Tue, 5 Apr 2022 16:38:05 +0200 Subject: [PATCH 134/195] lib,rlc_am_nr: testcase for NACK duplicate (full PDU) --- lib/test/rlc/rlc_am_nr_test.cc | 177 +++++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index 8f3694723..2b8c9055b 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -476,6 +476,182 @@ int lost_pdu_test(rlc_am_nr_sn_size_t sn_size) return SRSRAN_SUCCESS; } +/* + * Test the loss of a single PDU with NACK duplicate + * NACK should be visible in the status report. + * + * Retx after NACK should be present too. + * No further status reports shall be issued. + */ +int lost_pdu_duplicated_nack_test(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester; + timer_handler timers(8); + byte_buffer_t pdu_bufs[NBUFS]; + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + test_delimit_logger delimiter("lost PDU with NACK duplicate (%d bit SN)", to_number(sn_size)); + + constexpr uint32_t payload_size = 1; + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + uint32_t data_pdu_size = header_size + payload_size; + uint32_t expect_buffer_state = NBUFS * data_pdu_size; + + if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { + return -1; + } + + rlc_config_t rlc2_config = rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)); + if (not rlc2.configure(rlc2_config)) { + return -1; + } + + // after configuring entity + TESTASSERT(0 == rlc1.get_buffer_state()); + + basic_test_tx(&rlc1, pdu_bufs, sn_size); + + // Write 5 PDUs into RLC2 + for (int i = 0; i < NBUFS; i++) { + if (i != 3) { + rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); // Don't write RLC_SN=3. + } + } + + // Only after t-reassembly has expired, will the status report include NACKs. + TESTASSERT_EQ(3, rlc2.get_buffer_state()); + { + // Read status PDU from RLC2 + byte_buffer_t status_buf; + int len = rlc2.read_pdu(status_buf.msg, 5); + status_buf.N_bytes = len; + + TESTASSERT(0 == rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check = {}; + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(3, status_check.ack_sn); // 3 is the next expected SN (i.e. the lost packet.) + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + // Write duplicated status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + // Check there is nothing pending in RLC1 + TESTASSERT_EQ(0, rlc1.get_buffer_state()); + } + + // Step timers until reassambly timeout expires + for (int cnt = 0; cnt < 35; cnt++) { + timers.step_all(); + } + + // t-reassembly has expired. There should be a NACK in the status report. + constexpr uint32_t status_pdu_ack_size = 3; + uint32_t status_pdu_nack_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + TESTASSERT_EQ(status_pdu_ack_size + status_pdu_nack_size, rlc2.get_buffer_state()); + { + // Read status PDU from RLC2 + byte_buffer_t status_buf; + uint32_t len = rlc2.read_pdu(status_buf.msg, status_pdu_ack_size + status_pdu_nack_size); + status_buf.N_bytes = len; + + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check = {}; + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. + TESTASSERT_EQ(1, status_check.nacks.size()); // We lost one PDU. + TESTASSERT_EQ(3, status_check.nacks[0].nack_sn); // Lost PDU SN=3. + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + // Write duplicated status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + // Check there is only one Retx of SN=3 + TESTASSERT_EQ(data_pdu_size, rlc1.get_buffer_state()); + } + + { + // Check correct re-transmission + byte_buffer_t retx_buf; + uint32_t len = rlc1.read_pdu(retx_buf.msg, data_pdu_size); + retx_buf.N_bytes = len; + TESTASSERT_EQ(data_pdu_size, len); + + rlc2.write_pdu(retx_buf.msg, retx_buf.N_bytes); + + TESTASSERT_EQ(3, rlc2.get_buffer_state()); // Status report shoud be required, as the TX buffers are now empty. + } + { + // Double check status report + byte_buffer_t status_buf; + int len = rlc2.read_pdu(status_buf.msg, 3); + status_buf.N_bytes = len; + + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check = {}; + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. + TESTASSERT_EQ(0, status_check.nacks.size()); // All PDUs are acked now + } + + { + // rlc2 should not issue further status PDUs as time passes (even after expiry of t_status_prohibit) + int32_t checktime = 2 * rlc2_config.am_nr.t_status_prohibit; + for (int cnt = 0; cnt < checktime; cnt++) { + timers.step_all(); + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + } + } + + // Check statistics + rlc_bearer_metrics_t metrics1 = rlc1.get_metrics(); + rlc_bearer_metrics_t metrics2 = rlc2.get_metrics(); + + uint32_t total_tx_pdu_bytes1 = (NBUFS + 1) * data_pdu_size; // (NBUFS + 1 RETX) * PDU size + uint32_t total_rx_pdu_bytes1 = 4 * status_pdu_ack_size + 2 * status_pdu_nack_size; // 4 status PDU (2 with a NACK) + uint32_t total_tx_pdu_bytes2 = + 3 * status_pdu_ack_size + status_pdu_nack_size; // Three status PDU (one with a NACK, two without) + uint32_t total_rx_pdu_bytes2 = (NBUFS)*data_pdu_size; // (NBUFS - 1 Lost + 1 RETX) * PDU size + + // SDU metrics + TESTASSERT_EQ(5, metrics1.num_tx_sdus); + TESTASSERT_EQ(0, metrics1.num_rx_sdus); + TESTASSERT_EQ(5, metrics1.num_tx_sdu_bytes); + TESTASSERT_EQ(0, metrics1.num_rx_sdu_bytes); + TESTASSERT_EQ(0, metrics1.num_lost_sdus); + // PDU metrics + TESTASSERT_EQ(5 + 1, metrics1.num_tx_pdus); // One re-transmission + TESTASSERT_EQ(4, metrics1.num_rx_pdus); // 4 status PDUs + TESTASSERT_EQ(total_tx_pdu_bytes1, metrics1.num_tx_pdu_bytes); // (NBUFS + 1 RETX) * PDU size + TESTASSERT_EQ(total_rx_pdu_bytes1, metrics1.num_rx_pdu_bytes); // Two status PDU (one with a NACK) + TESTASSERT_EQ(0, metrics1.num_lost_sdus); // No lost SDUs + + // PDU metrics + TESTASSERT_EQ(0, metrics2.num_tx_sdus); + TESTASSERT_EQ(5, metrics2.num_rx_sdus); + TESTASSERT_EQ(0, metrics2.num_tx_sdu_bytes); + TESTASSERT_EQ(5, metrics2.num_rx_sdu_bytes); + TESTASSERT_EQ(0, metrics2.num_lost_sdus); + // SDU metrics + TESTASSERT_EQ(3, metrics2.num_tx_pdus); // Three status PDUs + TESTASSERT_EQ(5, metrics2.num_rx_pdus); // 5 PDUs (6 tx'ed, but one was lost) + TESTASSERT_EQ(total_tx_pdu_bytes2, metrics2.num_tx_pdu_bytes); // Three status PDU (one with a NACK, two without) + TESTASSERT_EQ(total_rx_pdu_bytes2, metrics2.num_rx_pdu_bytes); // (NBUFS - 1 Lost + 1 RETX) * PDU size + TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs + return SRSRAN_SUCCESS; +} + /* * Test the basic segmentation of a single SDU. * A single SDU of 3 bytes is segmented into 3 PDUs @@ -1606,6 +1782,7 @@ int main() TESTASSERT(retx_segmentation_required_checker_test(sn_size) == SRSRAN_SUCCESS); TESTASSERT(basic_test(sn_size) == SRSRAN_SUCCESS); TESTASSERT(lost_pdu_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(lost_pdu_duplicated_nack_test(sn_size) == SRSRAN_SUCCESS); TESTASSERT(basic_segmentation_test(sn_size) == SRSRAN_SUCCESS); TESTASSERT(segment_retx_test(sn_size) == SRSRAN_SUCCESS); TESTASSERT(retx_segment_test(sn_size) == SRSRAN_SUCCESS); From 62558d94da558a60f1ee1ca596b46869c93785bd Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Tue, 5 Apr 2022 16:39:42 +0200 Subject: [PATCH 135/195] lib,rlc_am_nr: fix segmented retx of non-contiguous segments --- lib/src/rlc/rlc_am_nr.cc | 29 ++-- lib/test/rlc/rlc_am_nr_test.cc | 264 +++++++++++++++++++++++++++++++++ 2 files changed, 277 insertions(+), 16 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 2161089e1..ddb0b71d4 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -644,22 +644,19 @@ uint32_t rlc_am_nr_tx::build_retx_pdu_with_segmentation(rlc_amd_retx_nr_t& retx, } } if (it != tx_pdu.segment_list.end()) { - rlc_amd_tx_pdu_nr::pdu_segment seg1 = {}; - seg1.so = retx.current_so; - seg1.payload_len = retx_pdu_payload_size; - rlc_amd_tx_pdu_nr::pdu_segment seg2 = {}; - seg2.so = retx.current_so + retx_pdu_payload_size; - seg2.payload_len = retx.segment_length - retx_pdu_payload_size; - std::list::iterator begin_it = tx_pdu.segment_list.erase(it); - if (begin_it == tx_pdu.segment_list.end()) { - RlcError("Could not modify segment list. SN=%d, SO=%d len=%d", retx.sn, retx.current_so, retx.segment_length); - } else { - std::list::iterator insert_it = tx_pdu.segment_list.insert(begin_it, seg1); - std::list::iterator insert_it2 = tx_pdu.segment_list.insert(insert_it, seg2); - RlcDebug("Old segment SN=%d, SO=%d len=%d", retx.sn, retx.current_so, retx.segment_length); - RlcDebug("New segment SN=%d, SO=%d len=%d", retx.sn, seg1.so, seg1.payload_len); - RlcDebug("New segment SN=%d, SO=%d len=%d", retx.sn, seg2.so, seg2.payload_len); - } + rlc_amd_tx_pdu_nr::pdu_segment seg1 = {}; + seg1.so = it->so; + seg1.payload_len = retx_pdu_payload_size; + rlc_amd_tx_pdu_nr::pdu_segment seg2 = {}; + seg2.so = it->so + retx_pdu_payload_size; + seg2.payload_len = it->payload_len - retx_pdu_payload_size; + + std::list::iterator begin_it = tx_pdu.segment_list.erase(it); + std::list::iterator insert_it = tx_pdu.segment_list.insert(begin_it, seg2); + std::list::iterator insert_it2 = tx_pdu.segment_list.insert(insert_it, seg1); + RlcDebug("Old segment SN=%d, SO=%d len=%d", retx.sn, retx.current_so, retx.segment_length); + RlcDebug("New segment SN=%d, SO=%d len=%d", retx.sn, seg1.so, seg1.payload_len); + RlcDebug("New segment SN=%d, SO=%d len=%d", retx.sn, seg2.so, seg2.payload_len); } else { RlcDebug("Could not find segment. SN=%d, SO=%d length=%d", retx.sn, retx.current_so, retx.segment_length); } diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index 2b8c9055b..b1a947a2a 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -929,6 +929,269 @@ int segment_retx_test(rlc_am_nr_sn_size_t sn_size) return SRSRAN_SUCCESS; } +int segment_retx_and_loose_segments_test(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester; + timer_handler timers(8); + byte_buffer_t pdu_bufs[NBUFS]; + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + test_delimit_logger delimiter("segment retx PDU and loose some segments (%d bit SN)", to_number(sn_size)); + + rlc_am_nr_tx* tx1 = dynamic_cast(rlc1.get_tx()); + rlc_am_nr_rx* rx1 = dynamic_cast(rlc1.get_rx()); + rlc_am_nr_tx* tx2 = dynamic_cast(rlc2.get_tx()); + rlc_am_nr_rx* rx2 = dynamic_cast(rlc2.get_rx()); + + if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { + return -1; + } + + if (not rlc2.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { + return -1; + } + + // after configuring entity + TESTASSERT_EQ(0, rlc1.get_buffer_state()); + + // Push 5 SDUs into RLC1 + unique_byte_buffer_t sdu_bufs[NBUFS]; + constexpr uint32_t payload_size = 3; // Give the SDU the size of 3 bytes + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + for (int i = 0; i < NBUFS; i++) { + sdu_bufs[i] = srsran::make_byte_buffer(); + sdu_bufs[i]->msg[0] = i; // Write the index into the buffer + sdu_bufs[i]->N_bytes = payload_size; // Give each buffer a size of 3 bytes + sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications + rlc1.write_sdu(std::move(sdu_bufs[i])); + } + + uint32_t expected_buffer_state = (header_size + payload_size) * NBUFS; + TESTASSERT_EQ(expected_buffer_state, rlc1.get_buffer_state()); + + // Read 5 PDUs from RLC1 (1 byte each) + for (int i = 0; i < NBUFS; i++) { + uint32_t len = rlc1.read_pdu(pdu_bufs[i].msg, header_size + payload_size); + pdu_bufs[i].N_bytes = len; + TESTASSERT_EQ(header_size + payload_size, len); + } + + TESTASSERT_EQ(0, rlc1.get_buffer_state()); + + // Write 5 - 1 PDUs into RLC2 + for (int i = 0; i < NBUFS; i++) { + if (i != 3) { + rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); // Don't write RLC_SN=3. + } + } + + // Only after t-reassembly has expired, will the status report include NACKs. + TESTASSERT_EQ(3, rlc2.get_buffer_state()); + { + // Read status PDU from RLC2 + byte_buffer_t status_buf; + int len = rlc2.read_pdu(status_buf.msg, 5); + status_buf.N_bytes = len; + + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check = {}; + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(3, status_check.ack_sn); // 3 is the next expected SN (i.e. the lost packet.) + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + } + + // Step timers until reassambly timeout expires + for (int cnt = 0; cnt < 35; cnt++) { + timers.step_all(); + } + + // t-reassembly has expired. There should be a NACK in the status report. + constexpr uint32_t status_pdu_ack_size = 3; + uint32_t status_pdu_nack_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + TESTASSERT_EQ(status_pdu_ack_size + status_pdu_nack_size, rlc2.get_buffer_state()); + { + // Read status PDU from RLC2 + byte_buffer_t status_buf; + int len = rlc2.read_pdu(status_buf.msg, status_pdu_ack_size + status_pdu_nack_size); + status_buf.N_bytes = len; + + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check = {}; + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. + TESTASSERT_EQ(1, status_check.nacks.size()); // We lost one PDU. + TESTASSERT_EQ(3, status_check.nacks[0].nack_sn); // Lost PDU SN=3. + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + // Check there is an Retx of SN=3 + TESTASSERT_EQ(header_size + payload_size, rlc1.get_buffer_state()); + } + + constexpr uint32_t so_size = 2; + constexpr uint32_t segment_size = 1; + uint32_t pdu_size_first = header_size + segment_size; + uint32_t pdu_size_continued = header_size + so_size + segment_size; + { + // Re-transmit PDU in 3 segments + for (int i = 0; i < 3; i++) { + byte_buffer_t retx_buf; + uint32_t len = 0; + if (i == 0) { + len = rlc1.read_pdu(retx_buf.msg, pdu_size_first); + TESTASSERT_EQ(pdu_size_first, len); + } else { + len = rlc1.read_pdu(retx_buf.msg, pdu_size_continued); + TESTASSERT_EQ(pdu_size_continued, len); + } + retx_buf.N_bytes = len; + + rlc_am_nr_pdu_header_t header_check = {}; + uint32_t hdr_len = rlc_am_nr_read_data_pdu_header(&retx_buf, sn_size, &header_check); + // Double check header. + TESTASSERT_EQ(3, header_check.sn); // Double check RETX SN + if (i == 0) { + TESTASSERT_EQ(rlc_nr_si_field_t::first_segment, header_check.si); + } else if (i == 1) { + TESTASSERT_EQ(rlc_nr_si_field_t::neither_first_nor_last_segment, header_check.si); + } else { + TESTASSERT_EQ(rlc_nr_si_field_t::last_segment, header_check.si); + } + + // We loose the first and the last segment + if (i != 0 && i != 2) { + rlc2.write_pdu(retx_buf.msg, retx_buf.N_bytes); + } + } + TESTASSERT(0 == rlc1.get_buffer_state()); + } + + // Step timers until reassambly timeout expires + for (int cnt = 0; cnt < 35; cnt++) { + timers.step_all(); + } + + // t-reassembly has expired. There should be another NACK in the status report. + // constexpr uint32_t status_pdu_ack_size = 3; + // uint32_t status_pdu_nack_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + constexpr uint32_t status_pdu_so_size = 4; + TESTASSERT_EQ(status_pdu_ack_size + 2 * status_pdu_nack_size + 2 * status_pdu_so_size, rlc2.get_buffer_state()); + { + // Read status PDU from RLC2 + byte_buffer_t status_buf; + int len = rlc2.read_pdu(status_buf.msg, status_pdu_ack_size + 2 * status_pdu_nack_size + 2 * status_pdu_so_size); + status_buf.N_bytes = len; + + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check = {}; + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. + TESTASSERT_EQ(2, status_check.nacks.size()); // We lost two PDU segments. + TESTASSERT_EQ(3, status_check.nacks[0].nack_sn); // Lost PDU SN=3. + TESTASSERT_EQ(true, status_check.nacks[0].has_so); // This is a segment missing. + TESTASSERT_EQ(0, status_check.nacks[0].so_start); // Segment offset should be 0 here + TESTASSERT_EQ(0, status_check.nacks[0].so_end); // Segment end should be 0 here + TESTASSERT_EQ(true, status_check.nacks[1].has_so); // This is a segment missing. + TESTASSERT_EQ(2, status_check.nacks[1].so_start); // Segment offset should be 2 here + TESTASSERT_EQ(0xFFFF, status_check.nacks[1].so_end); // Segment end should be 0xFFFF here + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + // Write status PDU duplicate to RLC1 + // rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + // Check there are two Retx segments + TESTASSERT_EQ(header_size + payload_size, rlc1.get_buffer_state()); // Fixme: get_buffer_state() + } + + { + // Re-transmit the lost 2 segments + for (int i = 0; i < 2; i++) { + byte_buffer_t retx_buf; + uint32_t len = 0; + if (i == 0) { + len = rlc1.read_pdu(retx_buf.msg, pdu_size_first); + TESTASSERT_EQ(pdu_size_first, len); + } else { + len = rlc1.read_pdu(retx_buf.msg, pdu_size_continued); + TESTASSERT_EQ(pdu_size_continued, len); + } + retx_buf.N_bytes = len; + + rlc_am_nr_pdu_header_t header_check = {}; + uint32_t hdr_len = rlc_am_nr_read_data_pdu_header(&retx_buf, sn_size, &header_check); + // Double check header. + TESTASSERT_EQ(3, header_check.sn); // Double check RETX SN + if (i == 0) { + TESTASSERT_EQ(rlc_nr_si_field_t::first_segment, header_check.si); + } else { + TESTASSERT_EQ(rlc_nr_si_field_t::last_segment, header_check.si); + } + + rlc2.write_pdu(retx_buf.msg, retx_buf.N_bytes); + } + TESTASSERT(0 == rlc1.get_buffer_state()); + } + + // Check statistics + rlc_bearer_metrics_t metrics1 = rlc1.get_metrics(); + rlc_bearer_metrics_t metrics2 = rlc2.get_metrics(); + + uint32_t data_pdu_size = header_size + payload_size; + uint32_t total_tx_pdu_bytes1 = NBUFS * data_pdu_size + 2 * pdu_size_first + 3 * pdu_size_continued; + uint32_t total_rx_pdu_bytes1 = status_pdu_ack_size + // ACK, no NACK + (status_pdu_ack_size + status_pdu_nack_size) + // ACK + NACK full SDU + (status_pdu_ack_size + 2 * status_pdu_nack_size + // ACK + NACK two segments + 2 * status_pdu_so_size); + uint32_t total_tx_pdu_bytes2 = total_rx_pdu_bytes1; + uint32_t total_rx_pdu_bytes2 = (NBUFS - 1) * data_pdu_size + pdu_size_first + 2 * pdu_size_continued; + + // SDU metrics + TESTASSERT_EQ(5, metrics1.num_tx_sdus); + TESTASSERT_EQ(0, metrics1.num_rx_sdus); + TESTASSERT_EQ(15, metrics1.num_tx_sdu_bytes); + TESTASSERT_EQ(0, metrics1.num_rx_sdu_bytes); + TESTASSERT_EQ(0, metrics1.num_lost_sdus); + // PDU metrics + TESTASSERT_EQ(5 + 3 + 2, metrics1.num_tx_pdus); // 5 + (3 + 2) re-transmissions + TESTASSERT_EQ(3, metrics1.num_rx_pdus); // 3 status PDU + TESTASSERT_EQ(total_tx_pdu_bytes1, metrics1.num_tx_pdu_bytes); + TESTASSERT_EQ(total_rx_pdu_bytes1, metrics1.num_rx_pdu_bytes); + TESTASSERT_EQ(0, metrics1.num_lost_sdus); // No lost SDUs + + // PDU metrics + TESTASSERT_EQ(0, metrics2.num_tx_sdus); + TESTASSERT_EQ(5, metrics2.num_rx_sdus); + TESTASSERT_EQ(0, metrics2.num_tx_sdu_bytes); + TESTASSERT_EQ(15, metrics2.num_rx_sdu_bytes); // 5 SDUs, 3 bytes each + TESTASSERT_EQ(0, metrics2.num_lost_sdus); + // SDU metrics + TESTASSERT_EQ(3, metrics2.num_tx_pdus); // 3 status PDUs + TESTASSERT_EQ(7, metrics2.num_rx_pdus); // 7 PDUs (10 tx'ed, but 3 were lost) + TESTASSERT_EQ(total_tx_pdu_bytes2, metrics2.num_tx_pdu_bytes); + TESTASSERT_EQ(total_rx_pdu_bytes2, + metrics2.num_rx_pdu_bytes); // 2 Bytes * (NBUFFS-1) (header size) + (NBUFFS-1) * 3 (data) + // 3 (1 retx no SO) + 2 * 5 (2 retx with SO) = 33 + TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs + + // Check state + rlc_am_nr_rx_state_t state2_rx = rx2->get_rx_state(); + TESTASSERT_EQ(5, state2_rx.rx_next); + return SRSRAN_SUCCESS; +} + int retx_segment_test(rlc_am_nr_sn_size_t sn_size) { rlc_am_tester tester; @@ -1785,6 +2048,7 @@ int main() TESTASSERT(lost_pdu_duplicated_nack_test(sn_size) == SRSRAN_SUCCESS); TESTASSERT(basic_segmentation_test(sn_size) == SRSRAN_SUCCESS); TESTASSERT(segment_retx_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(segment_retx_and_loose_segments_test(sn_size) == SRSRAN_SUCCESS); TESTASSERT(retx_segment_test(sn_size) == SRSRAN_SUCCESS); TESTASSERT(max_retx_lost_sdu_test(sn_size) == SRSRAN_SUCCESS); TESTASSERT(max_retx_lost_segments_test(sn_size) == SRSRAN_SUCCESS); From 1a5683c226e2deda90749bd02fdc85baa5549abb Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Sat, 9 Apr 2022 10:31:57 +0200 Subject: [PATCH 136/195] lib,rlc_am_nr: add overlap check for segment offsets in rlc_amd_retx and queue --- lib/include/srsran/rlc/rlc_am_data_structs.h | 65 ++++++++++++++++---- 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_am_data_structs.h b/lib/include/srsran/rlc/rlc_am_data_structs.h index 7f56ba431..5c4ce5012 100644 --- a/lib/include/srsran/rlc/rlc_am_data_structs.h +++ b/lib/include/srsran/rlc/rlc_am_data_structs.h @@ -316,20 +316,45 @@ private: uint32_t count = 0; }; -struct rlc_amd_retx_lte_t { - uint32_t sn; - bool is_segment; - uint32_t so_start; // offset to first byte of this segment - uint32_t so_end; // offset to first byte beyond the end of this segment - uint32_t current_so; +struct rlc_amd_retx_base_t { + const static uint32_t invalid_rlc_sn = std::numeric_limits::max(); + + uint32_t sn; ///< sequence number + bool is_segment; ///< flag whether this is a segment or not + uint32_t so_start; ///< offset to first byte of this segment + // so_end or segment_length are different for LTE and NR, hence are defined in subclasses + uint32_t current_so; ///< stores progressing SO during segmentation of this object + + rlc_amd_retx_base_t() : sn(invalid_rlc_sn), is_segment(false), so_start(0), current_so(0) {} + virtual ~rlc_amd_retx_base_t() = default; + + /** + * @brief overlaps implements a check whether the range of this retransmission object includes + * the given segment offset + * @param so the segment offset to check + * @return true if the segment offset is covered by the retransmission object. Otherwise false + */ + virtual bool overlaps(uint32_t so) const = 0; +}; + +struct rlc_amd_retx_lte_t : public rlc_amd_retx_base_t { + uint32_t so_end; ///< offset to first byte beyond the end of this segment + + rlc_amd_retx_lte_t() : rlc_amd_retx_base_t(), so_end(0) {} + bool overlaps(uint32_t segment_offset) const override + { + return (segment_offset >= so_start) && (segment_offset < so_end); + } }; -struct rlc_amd_retx_nr_t { - uint32_t sn; - bool is_segment; - uint32_t so_start; // offset to first byte of this segment - uint32_t segment_length; // number of bytes contained in this segment - uint32_t current_so; +struct rlc_amd_retx_nr_t : public rlc_amd_retx_base_t { + uint32_t segment_length; ///< number of bytes contained in this segment + + rlc_amd_retx_nr_t() : rlc_amd_retx_base_t(), segment_length(0) {} + bool overlaps(uint32_t segment_offset) const override + { + return (segment_offset >= so_start) && (segment_offset < current_so + segment_length); + } }; template @@ -341,10 +366,12 @@ public: virtual void pop() = 0; virtual T& front() = 0; virtual void clear() = 0; - virtual bool has_sn(uint32_t sn) const = 0; virtual size_t size() const = 0; virtual bool empty() const = 0; virtual bool full() const = 0; + + virtual bool has_sn(uint32_t sn) const = 0; + virtual bool has_sn(uint32_t sn, uint32_t so) const = 0; }; template @@ -385,6 +412,18 @@ public: return false; } + bool has_sn(uint32_t sn, uint32_t so) const override + { + for (size_t i = rpos; i != wpos; i = (i + 1) % WINDOW_SIZE) { + if (buffer[i].sn == sn) { + if (buffer[i].overlaps(so)) { + return true; + } + } + } + return false; + } + size_t size() const override { return (wpos >= rpos) ? wpos - rpos : WINDOW_SIZE + wpos - rpos; } bool empty() const override { return wpos == rpos; } bool full() const override { return size() == WINDOW_SIZE - 1; } From 69231f000f040e7d550f63a408f0ddabf8b9aa14 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Sat, 9 Apr 2022 10:32:06 +0200 Subject: [PATCH 137/195] lib,rlc_am_nr: don't enqueue segment retx if already pending for retx. --- lib/src/rlc/rlc_am_nr.cc | 30 ++++++++++++++++++------------ lib/test/rlc/rlc_am_nr_test.cc | 17 ++++++++++------- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index ddb0b71d4..0bcd6b936 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -811,18 +811,24 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) bool segment_found = false; for (const rlc_amd_tx_pdu_nr::pdu_segment& segm : pdu.segment_list) { if (segm.so >= nack.so_start && segm.so <= nack.so_end) { - // FIXME: Check if this segment is not already queued for retransmission - rlc_amd_retx_nr_t& retx = retx_queue->push(); - retx.sn = nack_sn; - retx.is_segment = true; - retx.so_start = segm.so; - retx.current_so = segm.so; - retx.segment_length = segm.payload_len; - retx_sn_set.insert(nack_sn); - RlcInfo("Scheduled RETX of SDU segment SN=%d, so_start=%d, segment_length=%d", - retx.sn, - retx.so_start, - retx.segment_length); + if (not retx_queue->has_sn(nack_sn, segm.so)) { + rlc_amd_retx_nr_t& retx = retx_queue->push(); + retx.sn = nack_sn; + retx.is_segment = true; + retx.so_start = segm.so; + retx.current_so = segm.so; + retx.segment_length = segm.payload_len; + retx_sn_set.insert(nack_sn); + RlcInfo("Scheduled RETX of SDU segment SN=%d, so_start=%d, segment_length=%d", + retx.sn, + retx.so_start, + retx.segment_length); + } else { + RlcInfo("Skip already scheduled RETX of SDU segment SN=%d, so_start=%d, segment_length=%d", + nack_sn, + segm.so, + segm.payload_len); + } segment_found = true; } } diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index b1a947a2a..ac38a2899 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -1110,7 +1110,7 @@ int segment_retx_and_loose_segments_test(rlc_am_nr_sn_size_t sn_size) rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); // Write status PDU duplicate to RLC1 - // rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); // Check there are two Retx segments TESTASSERT_EQ(header_size + payload_size, rlc1.get_buffer_state()); // Fixme: get_buffer_state() @@ -1151,11 +1151,14 @@ int segment_retx_and_loose_segments_test(rlc_am_nr_sn_size_t sn_size) uint32_t data_pdu_size = header_size + payload_size; uint32_t total_tx_pdu_bytes1 = NBUFS * data_pdu_size + 2 * pdu_size_first + 3 * pdu_size_continued; - uint32_t total_rx_pdu_bytes1 = status_pdu_ack_size + // ACK, no NACK - (status_pdu_ack_size + status_pdu_nack_size) + // ACK + NACK full SDU - (status_pdu_ack_size + 2 * status_pdu_nack_size + // ACK + NACK two segments - 2 * status_pdu_so_size); - uint32_t total_tx_pdu_bytes2 = total_rx_pdu_bytes1; + uint32_t total_rx_pdu_bytes1 = status_pdu_ack_size + // ACK, no NACK + (status_pdu_ack_size + status_pdu_nack_size) + // ACK + NACK full SDU + 2 * (status_pdu_ack_size + 2 * status_pdu_nack_size + // 2 * (ACK + NACK two segments) + 2 * status_pdu_so_size); + uint32_t total_tx_pdu_bytes2 = status_pdu_ack_size + // ACK, no NACK + (status_pdu_ack_size + status_pdu_nack_size) + // ACK + NACK full SDU + 1 * (status_pdu_ack_size + 2 * status_pdu_nack_size + // 1 * (ACK + NACK two segments) + 2 * status_pdu_so_size); uint32_t total_rx_pdu_bytes2 = (NBUFS - 1) * data_pdu_size + pdu_size_first + 2 * pdu_size_continued; // SDU metrics @@ -1166,7 +1169,7 @@ int segment_retx_and_loose_segments_test(rlc_am_nr_sn_size_t sn_size) TESTASSERT_EQ(0, metrics1.num_lost_sdus); // PDU metrics TESTASSERT_EQ(5 + 3 + 2, metrics1.num_tx_pdus); // 5 + (3 + 2) re-transmissions - TESTASSERT_EQ(3, metrics1.num_rx_pdus); // 3 status PDU + TESTASSERT_EQ(4, metrics1.num_rx_pdus); // 4 status PDU TESTASSERT_EQ(total_tx_pdu_bytes1, metrics1.num_tx_pdu_bytes); TESTASSERT_EQ(total_rx_pdu_bytes1, metrics1.num_rx_pdu_bytes); TESTASSERT_EQ(0, metrics1.num_lost_sdus); // No lost SDUs From fc38d4f4d9edf5d62e56e15e0a5fd2b3d21e4c20 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Wed, 6 Apr 2022 11:43:42 +0200 Subject: [PATCH 138/195] lib,rlc_am_nr: consider offset when packing retx with segmentation --- lib/src/rlc/rlc_am_nr.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 0bcd6b936..efb8542b3 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -618,7 +618,7 @@ uint32_t rlc_am_nr_tx::build_retx_pdu_with_segmentation(rlc_amd_retx_nr_t& retx, // Copy SDU segment into payload srsran_assert((hdr_len + retx_pdu_payload_size) <= nof_bytes, "Error calculating hdr_len and segment_payload_len"); - memcpy(&payload[hdr_len], tx_pdu.sdu_buf->msg, retx_pdu_payload_size); + memcpy(&payload[hdr_len], &tx_pdu.sdu_buf->msg[retx.current_so], retx_pdu_payload_size); // Store PDU segment info into tx_window RlcDebug("Updating RETX segment info. SN=%d, is_segment=%s", retx.sn, retx.is_segment ? "true" : "false"); From 3a874e78af051a7a2bf7f5ba2c0cbf19fe5cc832 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Fri, 8 Apr 2022 08:59:54 +0200 Subject: [PATCH 139/195] lib,rlc_am_nr_test: fix format string of test delimiter --- lib/test/rlc/rlc_am_nr_test.cc | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index ac38a2899..da1966bd8 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -64,7 +64,7 @@ int window_checker_test(rlc_am_nr_sn_size_t sn_size) timer_handler timers(8); auto& test_logger = srslog::fetch_basic_logger("TESTER "); - test_delimit_logger delimiter("window checkers (%d bit SN)", to_number(sn_size)); + test_delimit_logger delimiter("window checkers ({} bit SN)", to_number(sn_size)); rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); rlc_am_nr_tx* tx = dynamic_cast(rlc1.get_tx()); @@ -128,7 +128,7 @@ int retx_segmentation_required_checker_test(rlc_am_nr_sn_size_t sn_size) timer_handler timers(8); auto& test_logger = srslog::fetch_basic_logger("TESTER "); - test_delimit_logger delimiter("retx segmentation required checkers (%d bit SN)", to_number(sn_size)); + test_delimit_logger delimiter("retx segmentation required checkers ({} bit SN)", to_number(sn_size)); rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); rlc_am_nr_tx* tx = dynamic_cast(rlc1.get_tx()); @@ -215,7 +215,7 @@ int basic_test(rlc_am_nr_sn_size_t sn_size) byte_buffer_t pdu_bufs[NBUFS]; auto& test_logger = srslog::fetch_basic_logger("TESTER "); - test_delimit_logger delimiter("basic tx/rx (%d bit SN)", to_number(sn_size)); + test_delimit_logger delimiter("basic tx/rx ({} bit SN)", to_number(sn_size)); rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); @@ -325,7 +325,7 @@ int lost_pdu_test(rlc_am_nr_sn_size_t sn_size) auto& test_logger = srslog::fetch_basic_logger("TESTER "); rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); - test_delimit_logger delimiter("lost PDU (%d bit SN)", to_number(sn_size)); + test_delimit_logger delimiter("lost PDU ({} bit SN)", to_number(sn_size)); constexpr uint32_t payload_size = 1; uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; @@ -492,7 +492,7 @@ int lost_pdu_duplicated_nack_test(rlc_am_nr_sn_size_t sn_size) auto& test_logger = srslog::fetch_basic_logger("TESTER "); rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); - test_delimit_logger delimiter("lost PDU with NACK duplicate (%d bit SN)", to_number(sn_size)); + test_delimit_logger delimiter("lost PDU with NACK duplicate ({} bit SN)", to_number(sn_size)); constexpr uint32_t payload_size = 1; uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; @@ -661,7 +661,7 @@ int basic_segmentation_test(rlc_am_nr_sn_size_t sn_size) rlc_am_tester tester; timer_handler timers(8); auto& test_logger = srslog::fetch_basic_logger("TESTER "); - test_delimit_logger delimiter("basic segmentation (%d bit SN)", to_number(sn_size)); + test_delimit_logger delimiter("basic segmentation ({} bit SN)", to_number(sn_size)); rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); @@ -751,7 +751,7 @@ int segment_retx_test(rlc_am_nr_sn_size_t sn_size) auto& test_logger = srslog::fetch_basic_logger("TESTER "); rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); - test_delimit_logger delimiter("segment retx PDU (%d bit SN)", to_number(sn_size)); + test_delimit_logger delimiter("segment retx PDU ({} bit SN)", to_number(sn_size)); rlc_am_nr_tx* tx1 = dynamic_cast(rlc1.get_tx()); rlc_am_nr_rx* rx1 = dynamic_cast(rlc1.get_rx()); @@ -938,7 +938,7 @@ int segment_retx_and_loose_segments_test(rlc_am_nr_sn_size_t sn_size) auto& test_logger = srslog::fetch_basic_logger("TESTER "); rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); - test_delimit_logger delimiter("segment retx PDU and loose some segments (%d bit SN)", to_number(sn_size)); + test_delimit_logger delimiter("segment retx PDU and loose some segments ({} bit SN)", to_number(sn_size)); rlc_am_nr_tx* tx1 = dynamic_cast(rlc1.get_tx()); rlc_am_nr_rx* rx1 = dynamic_cast(rlc1.get_rx()); @@ -1501,7 +1501,7 @@ int max_retx_lost_sdu_test(rlc_am_nr_sn_size_t sn_size) rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); srslog::fetch_basic_logger("RLC").set_hex_dump_max_size(100); - test_delimit_logger delimiter("max retx lost SDU (%d bit SN)", to_number(sn_size)); + test_delimit_logger delimiter("max retx lost SDU ({} bit SN)", to_number(sn_size)); const rlc_config_t rlc_cfg = rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)); if (not rlc1.configure(rlc_cfg)) { @@ -1576,7 +1576,7 @@ int max_retx_lost_segments_test(rlc_am_nr_sn_size_t sn_size) rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); srslog::fetch_basic_logger("RLC").set_hex_dump_max_size(100); - test_delimit_logger delimiter("max retx lost SDU segment (%d bit SN)", to_number(sn_size)); + test_delimit_logger delimiter("max retx lost SDU segment ({} bit SN)", to_number(sn_size)); const rlc_config_t rlc_cfg = rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)); if (not rlc1.configure(rlc_cfg)) { @@ -1703,7 +1703,7 @@ int discard_test(rlc_am_nr_sn_size_t sn_size) auto& test_logger = srslog::fetch_basic_logger("TESTER "); rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); - test_delimit_logger delimiter("discard test (%d bit SN)", to_number(sn_size)); + test_delimit_logger delimiter("discard test ({} bit SN)", to_number(sn_size)); srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); srslog::fetch_basic_logger("RLC_AM_2").set_hex_dump_max_size(100); From 25d7258442d8a9585008f349354807851ee7fa0c Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Fri, 8 Apr 2022 09:54:17 +0200 Subject: [PATCH 140/195] lib,rlc_am_nr_test: comment new tests --- lib/test/rlc/rlc_am_nr_test.cc | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index da1966bd8..26b7cc304 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -742,6 +742,12 @@ int basic_segmentation_test(rlc_am_nr_sn_size_t sn_size) return SRSRAN_SUCCESS; } +// This tests correct behaviour of the following flow: +// - Transmit 5 SDUs as whole PDUs +// - Loose 3rd PDU +// - Receive NACK for missing PDU +// - Retransmit lost PDU in 3 segments +// - Check metrics and state int segment_retx_test(rlc_am_nr_sn_size_t sn_size) { rlc_am_tester tester; @@ -929,6 +935,16 @@ int segment_retx_test(rlc_am_nr_sn_size_t sn_size) return SRSRAN_SUCCESS; } +// This tests correct behaviour of the following flow: +// - Transmit 5 SDUs as whole PDUs +// - Loose 3rd PDU +// - Receive NACK for missing PDU +// - Retransmit lost PDU in 3 segments +// - Loose first and last segment +// - Receive NACKs for missing segments +// - Receive duplicate of previous NACKs +// - Retransmit missing segments again, but only once! +// - Check metrics and state int segment_retx_and_loose_segments_test(rlc_am_nr_sn_size_t sn_size) { rlc_am_tester tester; From 87f22bb2948e5b27df12f1cdf07398a651e864ee Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Fri, 8 Apr 2022 10:03:59 +0200 Subject: [PATCH 141/195] lib,rlc_am_nr_test: cosmetic change, deleted commented code --- lib/test/rlc/rlc_am_nr_test.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index 26b7cc304..2a8b9d72a 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -1097,8 +1097,6 @@ int segment_retx_and_loose_segments_test(rlc_am_nr_sn_size_t sn_size) } // t-reassembly has expired. There should be another NACK in the status report. - // constexpr uint32_t status_pdu_ack_size = 3; - // uint32_t status_pdu_nack_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; constexpr uint32_t status_pdu_so_size = 4; TESTASSERT_EQ(status_pdu_ack_size + 2 * status_pdu_nack_size + 2 * status_pdu_so_size, rlc2.get_buffer_state()); { From b157490cb3908be3f6bedcceaa255ea906f3ad78 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Tue, 19 Apr 2022 08:51:59 +0200 Subject: [PATCH 142/195] ue,nr,mac: fix race-condition when accesing temp RNTIs during RA procedure the race was in the prach_nr that stored the temp crnti without proper protection. the fix moves the logic to store the value to the MAC that uses the thread-safe RNTI object for this. --- srsue/hdr/stack/mac_nr/mac_nr.h | 3 ++ srsue/hdr/stack/mac_nr/mac_nr_interfaces.h | 2 + srsue/hdr/stack/mac_nr/proc_ra_nr.h | 3 +- srsue/src/stack/mac_nr/mac_nr.cc | 43 +++++++++++++------ srsue/src/stack/mac_nr/proc_ra_nr.cc | 36 ++++++---------- .../src/stack/mac_nr/test/proc_ra_nr_test.cc | 2 + 6 files changed, 50 insertions(+), 39 deletions(-) diff --git a/srsue/hdr/stack/mac_nr/mac_nr.h b/srsue/hdr/stack/mac_nr/mac_nr.h index 7727337e4..65c65035b 100644 --- a/srsue/hdr/stack/mac_nr/mac_nr.h +++ b/srsue/hdr/stack/mac_nr/mac_nr.h @@ -92,6 +92,8 @@ public: uint16_t get_crnti(); uint16_t get_temp_crnti(); uint16_t get_csrnti() { return SRSRAN_INVALID_RNTI; }; // SPS not supported + void set_temp_crnti(uint16_t temp_crnti); + void set_crnti_to_temp(); /// procedure sr nr interface void start_ra() { proc_ra.start_by_mac(); } @@ -130,6 +132,7 @@ private: bool is_paging_opportunity(); bool has_crnti(); + bool has_temp_crnti(); bool is_valid_crnti(const uint16_t crnti); std::vector logical_channels; // stores the raw configs provide by upper layers diff --git a/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h b/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h index 997b38d49..15f21b2dd 100644 --- a/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h +++ b/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h @@ -26,6 +26,8 @@ public: // Functions for identity handling, e.g., contention id and c-rnti virtual uint16_t get_crnti() = 0; virtual bool set_crnti(uint16_t c_rnti) = 0; + virtual void set_temp_crnti(uint16_t c_rnti) = 0; + virtual void set_crnti_to_temp() = 0; // Functions for msg3 manipulation which shall be transparent to the procedure virtual bool msg3_is_transmitted() = 0; diff --git a/srsue/hdr/stack/mac_nr/proc_ra_nr.h b/srsue/hdr/stack/mac_nr/proc_ra_nr.h index a08df5872..9807f73af 100644 --- a/srsue/hdr/stack/mac_nr/proc_ra_nr.h +++ b/srsue/hdr/stack/mac_nr/proc_ra_nr.h @@ -40,6 +40,7 @@ public: uint16_t get_rar_rnti(); bool has_temp_crnti(); uint16_t get_temp_crnti(); + void set_crnti_to_temp(); void received_contention_resolution(bool is_successful); // PHY interfaces @@ -64,8 +65,6 @@ private: int ra_window_length = -1, ra_window_start = -1; uint16_t rar_rnti = SRSRAN_INVALID_RNTI; - uint16_t temp_crnti = SRSRAN_INVALID_RNTI; - uint16_t transmitted_crnti = SRSRAN_INVALID_RNTI; std::mutex mutex; srsran::rach_cfg_nr_t rach_cfg = {}; diff --git a/srsue/src/stack/mac_nr/mac_nr.cc b/srsue/src/stack/mac_nr/mac_nr.cc index c529e9062..e9c4a3b23 100644 --- a/srsue/src/stack/mac_nr/mac_nr.cc +++ b/srsue/src/stack/mac_nr/mac_nr.cc @@ -163,9 +163,9 @@ void mac_nr::update_buffer_states() mac_interface_phy_nr::sched_rnti_t mac_nr::get_ul_sched_rnti_nr(const uint32_t tti) { - if (proc_ra.has_temp_crnti() && has_crnti() == false) { - logger.debug("SCHED: Searching temp C-RNTI=0x%x (proc_ra)", proc_ra.get_temp_crnti()); - return {proc_ra.get_temp_crnti(), srsran_rnti_type_c}; + if (has_temp_crnti() && has_crnti() == false) { + logger.debug("SCHED: Searching temp C-RNTI=0x%x (proc_ra)", rntis.get_temp_rnti()); + return {rntis.get_temp_rnti(), srsran_rnti_type_c}; } return {rntis.get_crnti(), srsran_rnti_type_c}; } @@ -195,9 +195,9 @@ mac_interface_phy_nr::sched_rnti_t mac_nr::get_dl_sched_rnti_nr(const uint32_t t return {proc_ra.get_rar_rnti(), srsran_rnti_type_ra}; } - if (proc_ra.has_temp_crnti() && has_crnti() == false) { - logger.debug("SCHED: Searching temp C-RNTI=0x%x (proc_ra)", proc_ra.get_temp_crnti()); - return {proc_ra.get_temp_crnti(), srsran_rnti_type_c}; + if (has_temp_crnti() && has_crnti() == false) { + logger.debug("SCHED: Searching temp C-RNTI=0x%x (proc_ra)", rntis.get_temp_rnti()); + return {rntis.get_temp_rnti(), srsran_rnti_type_c}; } if (has_crnti()) { @@ -209,6 +209,26 @@ mac_interface_phy_nr::sched_rnti_t mac_nr::get_dl_sched_rnti_nr(const uint32_t t return {SRSRAN_INVALID_RNTI, srsran_rnti_type_c}; } +bool mac_nr::has_temp_crnti() +{ + return rntis.get_temp_rnti() != SRSRAN_INVALID_RNTI; +} + +uint16_t mac_nr::get_temp_crnti() +{ + return rntis.get_temp_rnti(); +} + +void mac_nr::set_temp_crnti(uint16_t temp_crnti) +{ + rntis.set_temp_rnti(temp_crnti); +} + +void mac_nr::set_crnti_to_temp() +{ + rntis.set_crnti_to_temp(); +} + bool mac_nr::has_crnti() { return rntis.get_crnti() != SRSRAN_INVALID_RNTI; @@ -219,11 +239,6 @@ uint16_t mac_nr::get_crnti() return rntis.get_crnti(); } -uint16_t mac_nr::get_temp_crnti() -{ - return proc_ra.get_temp_crnti(); -} - srsran::mac_sch_subpdu_nr::lcg_bsr_t mac_nr::generate_sbsr() { return proc_bsr.generate_sbsr(); @@ -309,7 +324,7 @@ void mac_nr::new_grant_dl(const uint32_t cc_idx, const mac_nr_grant_dl_t& grant, void mac_nr::tb_decoded(const uint32_t cc_idx, const mac_nr_grant_dl_t& grant, tb_action_dl_result_t result) { - logger.debug("tb_decoded(): cc_idx=%d, tti=%d, rnti=%d, pid=%d, tbs=%d, ndi=%d, rv=%d, result=%s", + logger.debug("tb_decoded(): cc_idx=%d, tti=%d, rnti=0x%X, pid=%d, tbs=%d, ndi=%d, rv=%d, result=%s", cc_idx, grant.tti, grant.rnti, @@ -336,14 +351,14 @@ void mac_nr::tb_decoded(const uint32_t cc_idx, const mac_nr_grant_dl_t& grant, t } // If proc ra is in contention resolution (RA connection request procedure) - if (proc_ra.is_contention_resolution() && grant.rnti == get_temp_crnti()) { + if (proc_ra.is_contention_resolution() && grant.rnti == rntis.get_temp_rnti()) { proc_ra.received_contention_resolution(contention_res_successful); } } void mac_nr::new_grant_ul(const uint32_t cc_idx, const mac_nr_grant_ul_t& grant, tb_action_ul_t* action) { - logger.debug("new_grant_ul(): cc_idx=%d, tti=%d, rnti=%d, pid=%d, tbs=%d, ndi=%d, rv=%d, is_rar=%d", + logger.debug("new_grant_ul(): cc_idx=%d, tti=%d, rnti=0x%X, pid=%d, tbs=%d, ndi=%d, rv=%d, is_rar=%d", cc_idx, grant.tti, grant.rnti, diff --git a/srsue/src/stack/mac_nr/proc_ra_nr.cc b/srsue/src/stack/mac_nr/proc_ra_nr.cc index 200cc96cd..b08818571 100644 --- a/srsue/src/stack/mac_nr/proc_ra_nr.cc +++ b/srsue/src/stack/mac_nr/proc_ra_nr.cc @@ -115,18 +115,6 @@ bool proc_ra_nr::has_rar_rnti() return false; } -bool proc_ra_nr::has_temp_crnti() -{ - std::lock_guard lock(mutex); - return temp_crnti != SRSRAN_INVALID_RNTI; -} - -uint16_t proc_ra_nr::get_temp_crnti() -{ - std::lock_guard lock(mutex); - return temp_crnti; -} - void proc_ra_nr::received_contention_resolution(bool is_successful) { std::lock_guard lock(mutex); @@ -216,13 +204,10 @@ void proc_ra_nr::ra_response_reception(const mac_interface_phy_nr::tb_action_dl_ for (auto& subpdu : pdu.get_subpdus()) { if (subpdu.has_rapid() && subpdu.get_rapid() == preamble_index) { logger.debug("PROC RA NR: Setting UL grant and prepare Msg3"); - temp_crnti = subpdu.get_temp_crnti(); - - // Save transmitted C-RNTI (if any) - transmitted_crnti = mac.get_crnti(); + mac.set_temp_crnti(subpdu.get_temp_crnti()); // Set Temporary-C-RNTI if provided, otherwise C-RNTI is ok - phy->set_rar_grant(tb.rx_slot_idx, subpdu.get_ul_grant(), temp_crnti, srsran_rnti_type_ra); + phy->set_rar_grant(tb.rx_slot_idx, subpdu.get_ul_grant(), subpdu.get_temp_crnti(), srsran_rnti_type_ra); // Apply TA CMD current_ta = subpdu.get_ta(); @@ -280,12 +265,17 @@ void proc_ra_nr::ra_completion() srsran::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, WAITING_FOR_COMPLETION)); return; } + + // Start looking for PDCCH CRNTI + if (!mac.get_crnti()) { + // promote temp RNTI to new C-RNTI + mac.set_crnti_to_temp(); + mac.set_temp_crnti(SRSRAN_INVALID_RNTI); + } + srsran::console("Random Access Complete. c-rnti=0x%x, ta=%d\n", mac.get_crnti(), current_ta); logger.info("Random Access Complete. c-rnti=0x%x, ta=%d", mac.get_crnti(), current_ta); - if (!transmitted_crnti) { - mac.set_crnti(temp_crnti); - } - temp_crnti = SRSRAN_INVALID_RNTI; + mac.rrc_ra_completed(); reset(); } @@ -293,9 +283,9 @@ void proc_ra_nr::ra_completion() void proc_ra_nr::ra_error() { std::lock_guard lock(mutex); - temp_crnti = SRSRAN_INVALID_RNTI; preamble_transmission_counter++; contention_resolution_timer.stop(); + mac.set_temp_crnti(SRSRAN_INVALID_RNTI); uint32_t backoff_wait; bool ra_procedure_completed = false; // true = (unsuccessfully) completed, false = uncompleted @@ -389,10 +379,10 @@ void proc_ra_nr::reset() { state = IDLE; started_by = initiators_t::initiators_t_NULLTYPE; + mac.set_temp_crnti(SRSRAN_INVALID_RNTI); prach_send_timer.stop(); rar_timeout_timer.stop(); contention_resolution_timer.stop(); - transmitted_crnti = SRSRAN_INVALID_RNTI; } } // namespace srsue diff --git a/srsue/src/stack/mac_nr/test/proc_ra_nr_test.cc b/srsue/src/stack/mac_nr/test/proc_ra_nr_test.cc index 0663bedb3..e63ff8f8a 100644 --- a/srsue/src/stack/mac_nr/test/proc_ra_nr_test.cc +++ b/srsue/src/stack/mac_nr/test/proc_ra_nr_test.cc @@ -66,6 +66,8 @@ public: crnti = c_rnti; return true; } + void set_temp_crnti(uint16_t c_rnti) {} + void set_crnti_to_temp() {} bool msg3_is_transmitted() { return true; } void msg3_flush() {} From b2e04818491d373b2036eb256d6c3c921f50313b Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Tue, 19 Apr 2022 12:28:58 +0200 Subject: [PATCH 143/195] ue,mac,nr: fix another race in MAC --- srsue/hdr/stack/mac_nr/mac_nr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srsue/hdr/stack/mac_nr/mac_nr.h b/srsue/hdr/stack/mac_nr/mac_nr.h index 65c65035b..637d5ea41 100644 --- a/srsue/hdr/stack/mac_nr/mac_nr.h +++ b/srsue/hdr/stack/mac_nr/mac_nr.h @@ -154,7 +154,7 @@ private: std::atomic started = {false}; // Boolean to determine if need to decode SI-RNTI - bool search_bcch = false; + std::atomic search_bcch = {false}; ue_rnti rntis; // thread-safe helper to store RNTIs, contention ID, etc bool contention_res_successful; From 07cb60e3f9eb5b8ac07c5849a262fe2ffc57833b Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Tue, 19 Apr 2022 12:29:39 +0200 Subject: [PATCH 144/195] gnb,mac: protect RACH counter --- srsgnb/src/stack/mac/mac_nr.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/srsgnb/src/stack/mac/mac_nr.cc b/srsgnb/src/stack/mac/mac_nr.cc index 91674f814..8423e99eb 100644 --- a/srsgnb/src/stack/mac/mac_nr.cc +++ b/srsgnb/src/stack/mac/mac_nr.cc @@ -339,7 +339,10 @@ void mac_nr::rach_detected(const rach_info_t& rach_info) uint16_t rnti = alloc_ue(enb_cc_idx); // Log this event. - ++detected_rachs[enb_cc_idx]; + { + srsran::rwlock_write_guard lock(rwmutex); + ++detected_rachs[enb_cc_idx]; + } // Trigger scheduler RACH srsenb::sched_nr_interface::rar_info_t rar_info = {}; From 2313365a41243223dcd450007f9c6330a9b303e7 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Tue, 19 Apr 2022 13:29:18 +0200 Subject: [PATCH 145/195] ul_harq_nr,ue: fix format print --- srsue/src/stack/mac_nr/ul_harq_nr.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srsue/src/stack/mac_nr/ul_harq_nr.cc b/srsue/src/stack/mac_nr/ul_harq_nr.cc index a27eb5f5d..fd39f7718 100644 --- a/srsue/src/stack/mac_nr/ul_harq_nr.cc +++ b/srsue/src/stack/mac_nr/ul_harq_nr.cc @@ -206,7 +206,7 @@ void ul_harq_entity_nr::ul_harq_process_nr::new_grant_ul(const mac_interface_phy // retransmission if (harq_buffer == nullptr) { // ignore the UL grant - logger.info("UL %d: HARQ buffer empty. Ignoring grant."); + logger.info("UL %d: HARQ buffer empty. Ignoring grant.", pid); return; } From da2ac3b2c170e80ad681533ef1e34ca27a79b84c Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Tue, 19 Apr 2022 15:26:12 +0200 Subject: [PATCH 146/195] srsue,rrc_nr: fix warning and error logs in SA mode - provide PHY config state to configure MIB values (coreset0). - set PRACH freq_offset to valid value to please config checker. Addresses: 2022-04-19T12:42:38.726040 [PHY-SA ] [E] [ 0] prach_cfg.freq_offset=0 is not compatible with LTE 2022-04-19T12:42:38.726040 [PHY-SA ] [I] [ 0] PHY: Set TA offset: n_ta_offset: 0, ta_usec: 0.0 2022-04-19T12:42:38.726141 [RRC-NR ] [W] PHY configuration completed without a clear state. --- srsue/hdr/stack/rrc_nr/rrc_nr.h | 1 + srsue/src/stack/rrc_nr/rrc_nr.cc | 3 +++ srsue/src/stack/rrc_nr/rrc_nr_procedures.cc | 4 ++++ 3 files changed, 8 insertions(+) diff --git a/srsue/hdr/stack/rrc_nr/rrc_nr.h b/srsue/hdr/stack/rrc_nr/rrc_nr.h index 56aa29997..192a66877 100644 --- a/srsue/hdr/stack/rrc_nr/rrc_nr.h +++ b/srsue/hdr/stack/rrc_nr/rrc_nr.h @@ -201,6 +201,7 @@ private: // Stores the state of the PHY configuration setting enum { PHY_CFG_STATE_NONE = 0, + PHY_CFG_STATE_SA_MIB_CFG, PHY_CFG_STATE_SA_SIB_CFG, PHY_CFG_STATE_SA_FULL_CFG, PHY_CFG_STATE_NSA_APPLY_SP_CELL, diff --git a/srsue/src/stack/rrc_nr/rrc_nr.cc b/srsue/src/stack/rrc_nr/rrc_nr.cc index 093e0181d..c9c14e2e1 100644 --- a/srsue/src/stack/rrc_nr/rrc_nr.cc +++ b/srsue/src/stack/rrc_nr/rrc_nr.cc @@ -2225,6 +2225,9 @@ void rrc_nr::set_phy_config_complete(bool status) case PHY_CFG_STATE_NONE: logger.warning("PHY configuration completed without a clear state."); break; + case PHY_CFG_STATE_SA_MIB_CFG: + logger.info("PHY configuration with MIB parameters completed."); + break; case PHY_CFG_STATE_SA_SIB_CFG: logger.info("PHY configuration with SIB parameters completed."); break; diff --git a/srsue/src/stack/rrc_nr/rrc_nr_procedures.cc b/srsue/src/stack/rrc_nr/rrc_nr_procedures.cc index 3ee274a3d..5bd11d332 100644 --- a/srsue/src/stack/rrc_nr/rrc_nr_procedures.cc +++ b/srsue/src/stack/rrc_nr/rrc_nr_procedures.cc @@ -474,7 +474,11 @@ rrc_nr::cell_selection_proc::handle_cell_search_result(const rrc_interface_phy_n make_phy_search_space0_cfg(&phy_cfg.pdcch.search_space[0]); phy_cfg.pdcch.search_space_present[0] = true; + // Set dummy offset to pass PRACH config check, real value is provided in SIB1 + phy_cfg.prach.freq_offset = 1; + // Update PHY configuration + rrc_handle.phy_cfg_state = PHY_CFG_STATE_SA_MIB_CFG; if (not rrc_handle.phy->set_config(phy_cfg)) { Error("Setting PHY configuration"); return proc_outcome_t::error; From ba092c0e9bdaf4b516b6ef3c7a614f56d5c97e43 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Thu, 31 Mar 2022 11:45:22 +0200 Subject: [PATCH 147/195] asn1,ngap_test: add TC to check unpacking of AMF-UE-ID > 32bits --- lib/test/asn1/ngap_test.cc | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/lib/test/asn1/ngap_test.cc b/lib/test/asn1/ngap_test.cc index ab803b354..e849d4c82 100644 --- a/lib/test/asn1/ngap_test.cc +++ b/lib/test/asn1/ngap_test.cc @@ -216,6 +216,39 @@ int test_dl_nas_transport() return 0; } +// DL NAS transport with AMF-UE-NGAP-ID larger than 32bit +int test_dl_nas_transport2() +{ + uint8_t ngap_msg[] = {0x00, 0x04, 0x40, 0x42, 0x00, 0x00, 0x03, 0x00, 0x0a, 0x00, 0x06, 0x80, 0x03, 0x03, + 0xcf, 0x37, 0xd0, 0x00, 0x55, 0x00, 0x02, 0x00, 0x01, 0x00, 0x26, 0x00, 0x2b, 0x2a, + 0x7e, 0x00, 0x56, 0x00, 0x02, 0x00, 0x00, 0x21, 0xbc, 0x8d, 0xe5, 0x61, 0xf5, 0xb4, + 0xa7, 0x05, 0x8f, 0xdb, 0xe2, 0x3b, 0x4e, 0x21, 0xda, 0x45, 0x20, 0x10, 0x5a, 0xb8, + 0xd1, 0xdb, 0x13, 0x76, 0x80, 0x00, 0x1b, 0x1a, 0x8d, 0x3c, 0x98, 0x4c, 0x01, 0x06}; + // 00044042000003000a0006800303cf37d00055000200010026002b2a7e00560002000021bc8de561f5b4a7058fdbe23b4e21da4520105ab8d1db137680001b1a8d3c984c0106 + + cbit_ref bref(ngap_msg, sizeof(ngap_msg)); + ngap_pdu_c pdu; + TESTASSERT(pdu.unpack(bref) == SRSASN_SUCCESS); + + // Check Fields + TESTASSERT(pdu.type().value == ngap_pdu_c::types_opts::init_msg); + TESTASSERT(pdu.init_msg().proc_code == ASN1_NGAP_ID_DL_NAS_TRANSPORT); + TESTASSERT(pdu.init_msg().crit.value == crit_opts::ignore); + TESTASSERT(pdu.init_msg().value.type().value == ngap_elem_procs_o::init_msg_c::types_opts::dl_nas_transport); + + auto& dl_nas = pdu.init_msg().value.dl_nas_transport(); + // Field 0 + TESTASSERT_EQ(12948813776, dl_nas->amf_ue_ngap_id.value); + + // ... + // Field 2 + TESTASSERT(dl_nas->nas_pdu.value.size() == 42); + + TESTASSERT(ceil(bref.distance(ngap_msg) / 8.0) == sizeof(ngap_msg)); + TESTASSERT(test_pack_unpack_consistency(pdu) == SRSASN_SUCCESS); + return 0; +} + int test_ul_ran_status_transfer() { uint8_t ngap_msg[] = {0x00, 0x2e, 0x40, 0x3c, 0x00, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x02, 0x00, 0x01, 0x00, 0x55, 0x00, @@ -346,6 +379,7 @@ int main() test_ngsetup_response(); test_init_ue_msg(); test_dl_nas_transport(); + test_dl_nas_transport2(); test_ul_ran_status_transfer(); test_ue_context_release(); test_ue_context_release_complete(); From 6984159c6ffbb12c9f39ddcc4310e2729a66c390 Mon Sep 17 00:00:00 2001 From: Francisco Date: Mon, 11 Apr 2022 21:56:56 +0100 Subject: [PATCH 148/195] asn1: fix amf-ngap-ue-id unpacking overflow bug --- lib/src/asn1/asn1_utils.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/asn1/asn1_utils.cc b/lib/src/asn1/asn1_utils.cc index 70f4e174d..8b9a53708 100644 --- a/lib/src/asn1/asn1_utils.cc +++ b/lib/src/asn1/asn1_utils.cc @@ -186,7 +186,7 @@ SRSASN_CODE unpack_bits(T& val, Ptr& ptr, uint8_t& offset, const uint8_t* max_pt n_bits = 0; } else { auto mask = static_cast((1u << (8u - offset)) - 1u); - val += ((uint32_t)((*ptr) & mask)) << (n_bits - 8 + offset); + val += static_cast((*ptr) & mask) << (n_bits - 8 + offset); n_bits -= 8 - offset; offset = 0; ptr++; From e2efb884155d7f8ce7b8a345d220f04979509b73 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Wed, 20 Apr 2022 15:08:09 +0200 Subject: [PATCH 149/195] enb,ngap: extend type to store AMF id to 64bit AMF is up to 40bit long --- srsgnb/hdr/stack/ngap/ngap.h | 4 ++-- srsgnb/hdr/stack/ngap/ngap_ue_utils.h | 2 +- srsgnb/src/stack/ngap/ngap.cc | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/srsgnb/hdr/stack/ngap/ngap.h b/srsgnb/hdr/stack/ngap/ngap.h index 6c8a2d12f..65700a5b3 100644 --- a/srsgnb/hdr/stack/ngap/ngap.h +++ b/srsgnb/hdr/stack/ngap/ngap.h @@ -154,7 +154,7 @@ private: ue* find_ue_rnti(uint16_t rnti); ue* find_ue_gnbid(uint32_t gnbid); - ue* find_ue_amfid(uint32_t amfid); + ue* find_ue_amfid(uint64_t amfid); ue* add_user(value_type user); void erase(ue* ue_ptr); iterator begin() { return users.begin(); } @@ -190,7 +190,7 @@ private: ngap* ngap_ptr = nullptr; }; - ue* handle_ngapmsg_ue_id(uint32_t gnb_id, uint32_t amf_id); + ue* handle_ngapmsg_ue_id(uint32_t gnb_id, uint64_t amf_id); srsran::proc_t ngsetup_proc; diff --git a/srsgnb/hdr/stack/ngap/ngap_ue_utils.h b/srsgnb/hdr/stack/ngap/ngap_ue_utils.h index f0067ef50..a1a455919 100644 --- a/srsgnb/hdr/stack/ngap/ngap_ue_utils.h +++ b/srsgnb/hdr/stack/ngap/ngap_ue_utils.h @@ -24,7 +24,7 @@ struct ngap_ue_ctxt_t { uint16_t rnti = SRSRAN_INVALID_RNTI; uint32_t ran_ue_ngap_id = invalid_gnb_id; - srsran::optional amf_ue_ngap_id; + srsran::optional amf_ue_ngap_id; uint32_t gnb_cc_idx = 0; struct timeval init_timestamp = {}; diff --git a/srsgnb/src/stack/ngap/ngap.cc b/srsgnb/src/stack/ngap/ngap.cc index b6e15cb0d..423af12ab 100644 --- a/srsgnb/src/stack/ngap/ngap.cc +++ b/srsgnb/src/stack/ngap/ngap.cc @@ -302,7 +302,7 @@ ngap::ue* ngap::user_list::find_ue_gnbid(uint32_t gnbid) return (it != users.end()) ? it->second.get() : nullptr; } -ngap::ue* ngap::user_list::find_ue_amfid(uint32_t amfid) +ngap::ue* ngap::user_list::find_ue_amfid(uint64_t amfid) { auto it = std::find_if(users.begin(), users.end(), [amfid](const user_list::pair_type& v) { return v.second->ctxt.amf_ue_ngap_id == amfid; @@ -758,7 +758,7 @@ bool ngap::sctp_send_ngap_pdu(const asn1::ngap::ngap_pdu_c& tx_pdu, uint32_t rnt * @param amf_id amf_ue_ngap_id value stored in NGAP message * @return pointer to user if it has been found */ -ngap::ue* ngap::handle_ngapmsg_ue_id(uint32_t gnb_id, uint32_t amf_id) +ngap::ue* ngap::handle_ngapmsg_ue_id(uint32_t gnb_id, uint64_t amf_id) { ue* user_ptr = users.find_ue_gnbid(gnb_id); ue* user_amf_ptr = nullptr; From e43d3f32fd9a1e08fc4c455e2b3c0f87bc1ff076 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Wed, 20 Apr 2022 16:46:06 +0200 Subject: [PATCH 150/195] cmake,soapy: fix finding of Soapy headers when installed in SOAPY_DIR env path --- cmake/modules/FindSoapySDR.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/modules/FindSoapySDR.cmake b/cmake/modules/FindSoapySDR.cmake index cf3512dda..f6c1e3496 100644 --- a/cmake/modules/FindSoapySDR.cmake +++ b/cmake/modules/FindSoapySDR.cmake @@ -11,11 +11,11 @@ if(NOT SOAPYSDR_FOUND) pkg_check_modules (SOAPYSDR_PKG SoapySDR) find_path(SOAPYSDR_INCLUDE_DIRS - NAMES Device.h + NAMES SoapySDR/Device.h HINTS $ENV{SOAPY_DIR}/include PATHS ${SOAPYSDR_PKG_INCLUDE_DIRS} - /usr/include/SoapySDR - /usr/local/include/SoapySDR + /usr/include + /usr/local/include ) find_library(SOAPYSDR_LIBRARIES From 68695078f07bc19ed808b4b8257d160680f4a551 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Wed, 20 Apr 2022 14:59:54 +0200 Subject: [PATCH 151/195] phy_common: add basic test for sample rate for different bandwidths --- lib/src/phy/common/test/CMakeLists.txt | 9 +++++ lib/src/phy/common/test/phy_common_test.c | 47 +++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 lib/src/phy/common/test/phy_common_test.c diff --git a/lib/src/phy/common/test/CMakeLists.txt b/lib/src/phy/common/test/CMakeLists.txt index ae08699a5..869570ba7 100644 --- a/lib/src/phy/common/test/CMakeLists.txt +++ b/lib/src/phy/common/test/CMakeLists.txt @@ -25,3 +25,12 @@ target_link_libraries(sliv_test srsran_phy) add_test(sliv_test_14 sliv_test 14) add_test(sliv_test_52 sliv_test 48) add_test(sliv_test_52 sliv_test 52) + +######################################################################## +# PHY COMMON TEST +######################################################################## + +add_executable(phy_common_test phy_common_test.c) +target_link_libraries(phy_common_test srsran_phy) + +add_test(phy_common_test phy_common_test) \ No newline at end of file diff --git a/lib/src/phy/common/test/phy_common_test.c b/lib/src/phy/common/test/phy_common_test.c new file mode 100644 index 000000000..a4046e063 --- /dev/null +++ b/lib/src/phy/common/test/phy_common_test.c @@ -0,0 +1,47 @@ +/** + * + * \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/common/phy_common.h" + +int srsran_default_rates_test() +{ + // Verify calculated sample rates for all valid PRB sizes. + // By default we use the reduced 3/4 sampling to save bandwidth on the fronthaul. + TESTASSERT(srsran_sampling_freq_hz(6) == 1920000); + TESTASSERT(srsran_sampling_freq_hz(15) == 3840000); + TESTASSERT(srsran_sampling_freq_hz(25) == 5760000); + TESTASSERT(srsran_sampling_freq_hz(50) == 11520000); + TESTASSERT(srsran_sampling_freq_hz(75) == 15360000); // need to use default rate for 15 MHz BW + TESTASSERT(srsran_sampling_freq_hz(100) == 23040000); + return SRSRAN_SUCCESS; +} + +int lte_standard_rates_test() +{ + // Verify calculated sample rates for all valid PRB sizes. + // Enable standard LTE rates (required by some RF HW). + srsran_use_standard_symbol_size(true); + TESTASSERT(srsran_sampling_freq_hz(6) == 1920000); + TESTASSERT(srsran_sampling_freq_hz(15) == 3840000); + TESTASSERT(srsran_sampling_freq_hz(25) == 7680000); + TESTASSERT(srsran_sampling_freq_hz(50) == 15360000); + TESTASSERT(srsran_sampling_freq_hz(75) == 23040000); + TESTASSERT(srsran_sampling_freq_hz(100) == 30720000); + return SRSRAN_SUCCESS; +} + +int main(int argc, char** argv) +{ + TESTASSERT(srsran_default_rates_test() == SRSRAN_SUCCESS); + TESTASSERT(lte_standard_rates_test() == SRSRAN_SUCCESS); + return SRSRAN_SUCCESS; +} \ No newline at end of file From eec4a395d08e0e106f009ab1a513c9eb5e3cfab1 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Thu, 7 Apr 2022 13:13:41 +0200 Subject: [PATCH 152/195] lib,rlc_am_nr: status PDU to string prints NACK range --- lib/include/srsran/rlc/rlc_am_nr_packing.h | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_am_nr_packing.h b/lib/include/srsran/rlc/rlc_am_nr_packing.h index 209323795..fb6d846b8 100644 --- a/lib/include/srsran/rlc/rlc_am_nr_packing.h +++ b/lib/include/srsran/rlc/rlc_am_nr_packing.h @@ -150,11 +150,24 @@ void log_rlc_am_nr_status_pdu_to_string(srslog::log_channel& log_ch, if (status->nacks.size() > 0) { fmt::format_to(buffer, ", NACK_SN = "); for (uint32_t i = 0; i < status->nacks.size(); ++i) { - if (status->nacks[i].has_so) { - fmt::format_to( - buffer, "[{} {}:{}]", status->nacks[i].nack_sn, status->nacks[i].so_start, status->nacks[i].so_end); + if (status->nacks[i].has_nack_range) { + if (status->nacks[i].has_so) { + fmt::format_to(buffer, + "[{} {}:{} r{}]", + status->nacks[i].nack_sn, + status->nacks[i].so_start, + status->nacks[i].so_end, + status->nacks[i].nack_range); + } else { + fmt::format_to(buffer, "[{} r{}]", status->nacks[i].nack_sn, status->nacks[i].nack_range); + } } else { - fmt::format_to(buffer, "[{}]", status->nacks[i].nack_sn); + if (status->nacks[i].has_so) { + fmt::format_to( + buffer, "[{} {}:{}]", status->nacks[i].nack_sn, status->nacks[i].so_start, status->nacks[i].so_end); + } else { + fmt::format_to(buffer, "[{}]", status->nacks[i].nack_sn); + } } } } From 489259dc7832208f664ee521ea3ef17c11e39913 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Thu, 7 Apr 2022 13:31:12 +0200 Subject: [PATCH 153/195] lib,rlc_am_nr: extend status PDU struct with self-contained packed size info This change refactors the rlc_am_nr_status_pdu_t from a simple container into a class with integrated counter of the packed size. As this depends on the SN length, the SN length must be passed via constructor. --- lib/include/srsran/rlc/rlc_am_nr.h | 1 - lib/include/srsran/rlc/rlc_am_nr_packing.h | 47 +++++++++++------- lib/src/rlc/rlc_am_nr.cc | 35 +++++++------- lib/src/rlc/rlc_am_nr_packing.cc | 56 +++++++++++++++++++++- lib/test/rlc/rlc_am_nr_pdu_test.cc | 24 +++++----- lib/test/rlc/rlc_am_nr_test.cc | 56 +++++++++++----------- 6 files changed, 144 insertions(+), 75 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_am_nr.h b/lib/include/srsran/rlc/rlc_am_nr.h index bd9c96909..91ce60dea 100644 --- a/lib/include/srsran/rlc/rlc_am_nr.h +++ b/lib/include/srsran/rlc/rlc_am_nr.h @@ -153,7 +153,6 @@ private: std::unique_ptr > retx_queue; uint32_t sdu_under_segmentation_sn = INVALID_RLC_SN; // SN of the SDU currently being segmented. pdcp_sn_vector_t notify_info_vec; - rlc_am_nr_status_pdu_t tx_status; // Helper constants uint32_t min_hdr_size = 2; diff --git a/lib/include/srsran/rlc/rlc_am_nr_packing.h b/lib/include/srsran/rlc/rlc_am_nr_packing.h index fb6d846b8..e427bf5d8 100644 --- a/lib/include/srsran/rlc/rlc_am_nr_packing.h +++ b/lib/include/srsran/rlc/rlc_am_nr_packing.h @@ -78,22 +78,27 @@ struct rlc_amd_tx_sdu_nr_t { explicit rlc_amd_tx_sdu_nr_t(uint32_t rlc_sn_) : rlc_sn(rlc_sn_) {} }; -///< AM NR Status PDU header (perhaps merge with LTE version) -struct rlc_am_nr_status_pdu_t { - rlc_am_nr_control_pdu_type_t cpt; - uint32_t ack_sn; ///< SN of the next not received RLC Data PDU - std::vector nacks; - - rlc_am_nr_status_pdu_t() : cpt(rlc_am_nr_control_pdu_type_t::status_pdu), ack_sn(INVALID_RLC_SN), nacks(0) - { - nacks.reserve(RLC_AM_NR_TYP_NACKS); - } - void reset() - { - cpt = rlc_am_nr_control_pdu_type_t::status_pdu; - ack_sn = INVALID_RLC_SN; - nacks.clear(); - } +///< AM NR Status PDU header +class rlc_am_nr_status_pdu_t +{ +private: + rlc_am_nr_sn_size_t sn_size; ///< Stored SN size required to compute the packed size + std::vector nacks_; ///< Internal NACK container; keep in sync with packed_size_ + uint32_t packed_size_; ///< Stores the current packed size; sync on each change of nacks_ + + void refresh_packed_size(); + +public: + rlc_am_nr_control_pdu_type_t cpt; ///< CPT header + uint32_t ack_sn; ///< SN of the next not received RLC Data PDU + const std::vector& nacks; ///< Read-only reference to NACKs + const uint32_t& packed_size; ///< Read-only reference to packed size + + rlc_am_nr_status_pdu_t(rlc_am_nr_sn_size_t sn_size); + void reset(); + void push_nack(const rlc_status_nack_t& nack); + const std::vector& get_nacks() const { return nacks_; } + uint32_t get_packed_size() const { return packed_size; } }; /**************************************************************************** @@ -114,6 +119,16 @@ uint32_t rlc_am_nr_write_data_pdu_header(const rlc_am_nr_pdu_header_t& header, b uint32_t rlc_am_nr_packed_length(const rlc_am_nr_pdu_header_t& header); +/**************************************************************************** + * Status PDU pack/unpack helper functions for NR + * Ref: 3GPP TS 38.322 v16.2.0 Section 6.2.2.5 + ***************************************************************************/ +constexpr uint32_t rlc_am_nr_status_pdu_sizeof_header_ack_sn = 3; // header fixed part and ACK SN +constexpr uint32_t rlc_am_nr_status_pdu_sizeof_nack_sn_ext_12bit_sn = 2; // NACK SN and extension fields (12 bit SN) +constexpr uint32_t rlc_am_nr_status_pdu_sizeof_nack_sn_ext_18bit_sn = 3; // NACK SN and extension fields (18 bit SN) +constexpr uint32_t rlc_am_nr_status_pdu_sizeof_nack_so = 4; // NACK segment offsets (start and end) +constexpr uint32_t rlc_am_nr_status_pdu_sizeof_nack_range = 1; // NACK range (nof consecutively lost SDUs) + uint32_t rlc_am_nr_read_status_pdu(const byte_buffer_t* pdu, const rlc_am_nr_sn_size_t sn_size, rlc_am_nr_status_pdu_t* status); diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index efb8542b3..49ea68591 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -724,15 +724,16 @@ uint32_t rlc_am_nr_tx::get_retx_expected_hdr_len(const rlc_amd_retx_nr_t& retx) uint32_t rlc_am_nr_tx::build_status_pdu(byte_buffer_t* payload, uint32_t nof_bytes) { RlcInfo("generating status PDU. Bytes available:%d", nof_bytes); - tx_status.reset(); - int pdu_len = rx->get_status_pdu(&tx_status, nof_bytes); + rlc_am_nr_status_pdu_t status(cfg.rx_sn_field_length); // carries status of RX entity, hence use SN length of RX + status.reset(); + int pdu_len = rx->get_status_pdu(&status, nof_bytes); if (pdu_len == SRSRAN_ERROR) { RlcDebug("deferred status PDU. Cause: Failed to acquire rx lock"); pdu_len = 0; } else if (pdu_len > 0 && nof_bytes >= static_cast(pdu_len)) { RlcDebug("generated status PDU. Bytes:%d", pdu_len); - log_rlc_am_nr_status_pdu_to_string(logger.info, "TX status PDU - %s", &tx_status, rb_name); - pdu_len = rlc_am_nr_write_status_pdu(tx_status, cfg.tx_sn_field_length, payload); + log_rlc_am_nr_status_pdu_to_string(logger.info, "TX status PDU - %s", &status, rb_name); + pdu_len = rlc_am_nr_write_status_pdu(status, cfg.tx_sn_field_length, payload); } else { RlcInfo("cannot tx status PDU - %d bytes available, %d bytes required", nof_bytes, pdu_len); pdu_len = 0; @@ -748,7 +749,7 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) } std::lock_guard lock(mutex); - rlc_am_nr_status_pdu_t status = {}; + rlc_am_nr_status_pdu_t status(cfg.tx_sn_field_length); RlcHexDebug(payload, nof_bytes, "%s Rx control PDU", parent->rb_name); rlc_am_nr_read_status_pdu(payload, nof_bytes, cfg.tx_sn_field_length, &status); log_rlc_am_nr_status_pdu_to_string(logger.info, "RX status PDU: %s", &status, parent->rb_name); @@ -1408,11 +1409,11 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m { std::unique_lock lock(mutex, std::try_to_lock); if (not lock.owns_lock()) { - return 0; + return SRSRAN_ERROR; } - status->nacks.clear(); - status->ack_sn = st.rx_next; // Start with the lower end of the window + status->reset(); + byte_buffer_t tmp_buf; /* @@ -1430,15 +1431,15 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m } else { if (not rx_window->has_sn(i)) { // No segment received, NACK the whole SDU - RlcDebug("Updating NACK for full SDU. NACK SN=%d", i); + RlcDebug("Adding NACK for full SDU. NACK SN=%d", i); rlc_status_nack_t nack; nack.nack_sn = i; nack.has_so = false; - status->nacks.push_back(nack); + status->push_nack(nack); } else if (not(*rx_window)[i].fully_received) { // Some segments were received, but not all. // NACK non consecutive missing bytes - RlcDebug("Updating NACK for partial SDU. NACK SN=%d", i); + RlcDebug("Adding NACKs for segmented SDU. NACK SN=%d", i); uint32_t last_so = 0; bool last_segment_rx = false; for (auto segm = (*rx_window)[i].segments.begin(); segm != (*rx_window)[i].segments.end(); segm++) { @@ -1449,7 +1450,7 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m nack.has_so = true; nack.so_start = last_so; nack.so_end = segm->header.so - 1; // set to last missing byte - status->nacks.push_back(nack); + status->push_nack(nack); RlcDebug("First/middle segment missing. NACK_SN=%d. SO_start=%d, SO_end=%d", nack.nack_sn, nack.so_start, @@ -1460,14 +1461,14 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m last_segment_rx = true; } last_so = segm->header.so + segm->buf->N_bytes; - } + } // Segment loop if (not last_segment_rx) { rlc_status_nack_t nack; nack.nack_sn = i; nack.has_so = true; nack.so_start = last_so; nack.so_end = so_end_of_sdu; - status->nacks.push_back(nack); + status->push_nack(nack); RlcDebug( "Final segment missing. NACK_SN=%d. SO_start=%d, SO_end=%d", nack.nack_sn, nack.so_start, nack.so_end); srsran_assert(nack.so_start <= nack.so_end, "Error: SO_start > SO_end. NACK_SN=%d", nack.nack_sn); @@ -1499,8 +1500,10 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m uint32_t rlc_am_nr_rx::get_status_pdu_length() { - rlc_am_nr_status_pdu_t tmp_status; - return get_status_pdu(&tmp_status, UINT32_MAX); + rlc_am_nr_status_pdu_t tmp_status(cfg.rx_sn_field_length); + tmp_status.reset(); + get_status_pdu(&tmp_status, UINT32_MAX); + return tmp_status.get_packed_size(); } bool rlc_am_nr_rx::get_do_status() diff --git a/lib/src/rlc/rlc_am_nr_packing.cc b/lib/src/rlc/rlc_am_nr_packing.cc index e0d5d69ba..df8785015 100644 --- a/lib/src/rlc/rlc_am_nr_packing.cc +++ b/lib/src/rlc/rlc_am_nr_packing.cc @@ -15,6 +15,58 @@ namespace srsran { +/**************************************************************************** + * Container implementation for pack/unpack functions + ***************************************************************************/ + +void rlc_am_nr_status_pdu_t::refresh_packed_size() +{ + uint32_t packed_size = rlc_am_nr_status_pdu_sizeof_header_ack_sn; + for (auto nack : nacks_) { + packed_size += sn_size == rlc_am_nr_sn_size_t::size12bits ? rlc_am_nr_status_pdu_sizeof_nack_sn_ext_12bit_sn + : rlc_am_nr_status_pdu_sizeof_nack_sn_ext_18bit_sn; + if (nack.has_so) { + packed_size += rlc_am_nr_status_pdu_sizeof_nack_so; + } + if (nack.has_nack_range) { + packed_size += rlc_am_nr_status_pdu_sizeof_nack_range; + } + } +} + +rlc_am_nr_status_pdu_t::rlc_am_nr_status_pdu_t(rlc_am_nr_sn_size_t sn_size) : + sn_size(sn_size), + nacks_(0), + packed_size_(rlc_am_nr_status_pdu_sizeof_header_ack_sn), + cpt(rlc_am_nr_control_pdu_type_t::status_pdu), + ack_sn(INVALID_RLC_SN), + nacks(nacks_), + packed_size(packed_size_) +{ + nacks_.reserve(RLC_AM_NR_TYP_NACKS); +} + +void rlc_am_nr_status_pdu_t::reset() +{ + cpt = rlc_am_nr_control_pdu_type_t::status_pdu; + ack_sn = INVALID_RLC_SN; + nacks_.clear(); + packed_size_ = rlc_am_nr_status_pdu_sizeof_header_ack_sn; +} + +void rlc_am_nr_status_pdu_t::push_nack(const rlc_status_nack_t& nack) +{ + nacks_.push_back(nack); + packed_size_ += sn_size == rlc_am_nr_sn_size_t::size12bits ? rlc_am_nr_status_pdu_sizeof_nack_sn_ext_12bit_sn + : rlc_am_nr_status_pdu_sizeof_nack_sn_ext_18bit_sn; + if (nack.has_so) { + packed_size_ += rlc_am_nr_status_pdu_sizeof_nack_so; + } + if (nack.has_nack_range) { + packed_size_ += rlc_am_nr_status_pdu_sizeof_nack_range; + } +} + /**************************************************************************** * Header pack/unpack helper functions * Ref: 3GPP TS 38.322 v15.3.0 Section 6.2.2.4 @@ -231,7 +283,7 @@ rlc_am_nr_read_status_pdu_12bit_sn(const uint8_t* payload, const uint32_t nof_by nack.nack_range = (*ptr); ptr++; } - status->nacks.push_back(nack); + status->push_nack(nack); if (uint32_t(ptr - payload) > nof_bytes) { fprintf(stderr, "Malformed PDU, trying to read more bytes than it is available\n"); return 0; @@ -313,7 +365,7 @@ rlc_am_nr_read_status_pdu_18bit_sn(const uint8_t* payload, const uint32_t nof_by nack.nack_range = (*ptr); ptr++; } - status->nacks.push_back(nack); + status->push_nack(nack); if (uint32_t(ptr - payload) > nof_bytes) { fprintf(stderr, "Malformed PDU, trying to read more bytes than it is available\n"); return 0; diff --git a/lib/test/rlc/rlc_am_nr_pdu_test.cc b/lib/test/rlc/rlc_am_nr_pdu_test.cc index cad7e8bc3..d826ef896 100644 --- a/lib/test/rlc/rlc_am_nr_pdu_test.cc +++ b/lib/test/rlc/rlc_am_nr_pdu_test.cc @@ -233,7 +233,7 @@ int rlc_am_nr_control_pdu_12bit_sn_test1() TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); // unpack PDU - rlc_am_nr_status_pdu_t status_pdu = {}; + rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size12bits); TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &status_pdu) == SRSRAN_SUCCESS); TESTASSERT(status_pdu.ack_sn == 2065); TESTASSERT(status_pdu.nacks.size() == 0); @@ -263,7 +263,7 @@ int rlc_am_nr_control_pdu_12bit_sn_test2() TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); // unpack PDU - rlc_am_nr_status_pdu_t status_pdu = {}; + rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size12bits); TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &status_pdu) == SRSRAN_SUCCESS); TESTASSERT(status_pdu.ack_sn == 2065); TESTASSERT(status_pdu.nacks.size() == 1); @@ -296,7 +296,7 @@ int rlc_am_nr_control_pdu_12bit_sn_test3() TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); // unpack PDU - rlc_am_nr_status_pdu_t status_pdu = {}; + rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size12bits); TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &status_pdu) == SRSRAN_SUCCESS); TESTASSERT(status_pdu.ack_sn == 2065); TESTASSERT(status_pdu.nacks.size() == 2); @@ -332,7 +332,7 @@ int rlc_am_nr_control_pdu_12bit_sn_test4() TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); // unpack PDU - rlc_am_nr_status_pdu_t status_pdu = {}; + rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size12bits); TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &status_pdu) == SRSRAN_SUCCESS); TESTASSERT(status_pdu.ack_sn == 2065); TESTASSERT(status_pdu.nacks.size() == 2); @@ -370,7 +370,7 @@ int rlc_am_nr_control_pdu_12bit_sn_test5() TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); // unpack PDU - rlc_am_nr_status_pdu_t status_pdu = {}; + rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size12bits); TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &status_pdu) == 0); return SRSRAN_SUCCESS; @@ -404,7 +404,7 @@ int rlc_am_nr_control_pdu_12bit_sn_test_nack_range() TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); // unpack PDU - rlc_am_nr_status_pdu_t status_pdu = {}; + rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size12bits); TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &status_pdu) == SRSRAN_SUCCESS); TESTASSERT(status_pdu.ack_sn == 2065); TESTASSERT(status_pdu.nacks.size() == 2); @@ -445,7 +445,7 @@ int rlc_am_nr_control_pdu_18bit_sn_test1() TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); // unpack PDU - rlc_am_nr_status_pdu_t status_pdu = {}; + rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size18bits); TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == SRSRAN_SUCCESS); TESTASSERT(status_pdu.ack_sn == 235929); TESTASSERT(status_pdu.nacks.size() == 0); @@ -476,7 +476,7 @@ int rlc_am_nr_control_pdu_18bit_sn_test2() TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); // unpack PDU - rlc_am_nr_status_pdu_t status_pdu = {}; + rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size18bits); TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == SRSRAN_SUCCESS); TESTASSERT(status_pdu.ack_sn == 235929); TESTASSERT(status_pdu.nacks.size() == 1); @@ -527,7 +527,7 @@ int rlc_am_nr_control_pdu_18bit_sn_test3() TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); // unpack PDU - rlc_am_nr_status_pdu_t status_pdu = {}; + rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size18bits); TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == SRSRAN_SUCCESS); TESTASSERT(status_pdu.ack_sn == 235929); TESTASSERT(status_pdu.nacks.size() == 2); @@ -579,7 +579,7 @@ int rlc_am_nr_control_pdu_18bit_sn_test4() TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); // unpack PDU - rlc_am_nr_status_pdu_t status_pdu = {}; + rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size18bits); TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == SRSRAN_SUCCESS); TESTASSERT(status_pdu.ack_sn == 235929); TESTASSERT(status_pdu.nacks.size() == 2); @@ -635,7 +635,7 @@ int rlc_am_nr_control_pdu_18bit_sn_test5() TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); // unpack PDU - rlc_am_nr_status_pdu_t status_pdu = {}; + rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size18bits); TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == 0); return SRSRAN_SUCCESS; @@ -671,7 +671,7 @@ int rlc_am_nr_control_pdu_18bit_sn_test_nack_range() TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); // unpack PDU - rlc_am_nr_status_pdu_t status_pdu = {}; + rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size18bits); TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == SRSRAN_SUCCESS); TESTASSERT(status_pdu.ack_sn == 200977); TESTASSERT(status_pdu.nacks.size() == 2); diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index 2a8b9d72a..1ab1d84a1 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -251,7 +251,7 @@ int basic_test(rlc_am_nr_sn_size_t sn_size) TESTASSERT_EQ(0, rlc2.get_buffer_state()); // Assert status is correct - rlc_am_nr_status_pdu_t status_check = {}; + rlc_am_nr_status_pdu_t status_check(sn_size); rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the last SN that was not received. @@ -364,7 +364,7 @@ int lost_pdu_test(rlc_am_nr_sn_size_t sn_size) TESTASSERT(0 == rlc2.get_buffer_state()); // Assert status is correct - rlc_am_nr_status_pdu_t status_check = {}; + rlc_am_nr_status_pdu_t status_check(sn_size); rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); TESTASSERT_EQ(3, status_check.ack_sn); // 3 is the next expected SN (i.e. the lost packet.) @@ -390,7 +390,7 @@ int lost_pdu_test(rlc_am_nr_sn_size_t sn_size) TESTASSERT_EQ(0, rlc2.get_buffer_state()); // Assert status is correct - rlc_am_nr_status_pdu_t status_check = {}; + rlc_am_nr_status_pdu_t status_check(sn_size); rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. TESTASSERT_EQ(1, status_check.nacks.size()); // We lost one PDU. @@ -423,7 +423,7 @@ int lost_pdu_test(rlc_am_nr_sn_size_t sn_size) TESTASSERT_EQ(0, rlc2.get_buffer_state()); // Assert status is correct - rlc_am_nr_status_pdu_t status_check = {}; + rlc_am_nr_status_pdu_t status_check(sn_size); rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. TESTASSERT_EQ(0, status_check.nacks.size()); // All PDUs are acked now @@ -531,7 +531,7 @@ int lost_pdu_duplicated_nack_test(rlc_am_nr_sn_size_t sn_size) TESTASSERT(0 == rlc2.get_buffer_state()); // Assert status is correct - rlc_am_nr_status_pdu_t status_check = {}; + rlc_am_nr_status_pdu_t status_check(sn_size); rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); TESTASSERT_EQ(3, status_check.ack_sn); // 3 is the next expected SN (i.e. the lost packet.) @@ -563,7 +563,7 @@ int lost_pdu_duplicated_nack_test(rlc_am_nr_sn_size_t sn_size) TESTASSERT_EQ(0, rlc2.get_buffer_state()); // Assert status is correct - rlc_am_nr_status_pdu_t status_check = {}; + rlc_am_nr_status_pdu_t status_check(sn_size); rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. TESTASSERT_EQ(1, status_check.nacks.size()); // We lost one PDU. @@ -599,7 +599,7 @@ int lost_pdu_duplicated_nack_test(rlc_am_nr_sn_size_t sn_size) TESTASSERT_EQ(0, rlc2.get_buffer_state()); // Assert status is correct - rlc_am_nr_status_pdu_t status_check = {}; + rlc_am_nr_status_pdu_t status_check(sn_size); rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. TESTASSERT_EQ(0, status_check.nacks.size()); // All PDUs are acked now @@ -817,7 +817,7 @@ int segment_retx_test(rlc_am_nr_sn_size_t sn_size) TESTASSERT_EQ(0, rlc2.get_buffer_state()); // Assert status is correct - rlc_am_nr_status_pdu_t status_check = {}; + rlc_am_nr_status_pdu_t status_check(sn_size); rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); TESTASSERT_EQ(3, status_check.ack_sn); // 3 is the next expected SN (i.e. the lost packet.) @@ -843,7 +843,7 @@ int segment_retx_test(rlc_am_nr_sn_size_t sn_size) TESTASSERT_EQ(0, rlc2.get_buffer_state()); // Assert status is correct - rlc_am_nr_status_pdu_t status_check = {}; + rlc_am_nr_status_pdu_t status_check(sn_size); rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. TESTASSERT_EQ(1, status_check.nacks.size()); // We lost one PDU. @@ -1014,7 +1014,7 @@ int segment_retx_and_loose_segments_test(rlc_am_nr_sn_size_t sn_size) TESTASSERT_EQ(0, rlc2.get_buffer_state()); // Assert status is correct - rlc_am_nr_status_pdu_t status_check = {}; + rlc_am_nr_status_pdu_t status_check(sn_size); rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); TESTASSERT_EQ(3, status_check.ack_sn); // 3 is the next expected SN (i.e. the lost packet.) @@ -1040,7 +1040,7 @@ int segment_retx_and_loose_segments_test(rlc_am_nr_sn_size_t sn_size) TESTASSERT_EQ(0, rlc2.get_buffer_state()); // Assert status is correct - rlc_am_nr_status_pdu_t status_check = {}; + rlc_am_nr_status_pdu_t status_check(sn_size); rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. TESTASSERT_EQ(1, status_check.nacks.size()); // We lost one PDU. @@ -1108,7 +1108,7 @@ int segment_retx_and_loose_segments_test(rlc_am_nr_sn_size_t sn_size) TESTASSERT_EQ(0, rlc2.get_buffer_state()); // Assert status is correct - rlc_am_nr_status_pdu_t status_check = {}; + rlc_am_nr_status_pdu_t status_check(sn_size); rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. TESTASSERT_EQ(2, status_check.nacks.size()); // We lost two PDU segments. @@ -1310,7 +1310,7 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) TESTASSERT_EQ(0, rlc2.get_buffer_state()); // Assert status is correct - rlc_am_nr_status_pdu_t status_check = {}; + rlc_am_nr_status_pdu_t status_check(sn_size); rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); TESTASSERT_EQ(1, status_check.ack_sn); // 1 is the next expected SN (i.e. the first lost packet.) @@ -1353,7 +1353,7 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) TESTASSERT_EQ(0, rlc2.get_buffer_state()); // Assert status is correct - rlc_am_nr_status_pdu_t status_check = {}; + rlc_am_nr_status_pdu_t status_check(sn_size); rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); TESTASSERT_EQ(2, status_check.ack_sn); // 5 is the next expected SN. TESTASSERT_EQ(1, status_check.nacks.size()); // We lost one PDU. @@ -1394,7 +1394,7 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) TESTASSERT_EQ(0, rlc2.get_buffer_state()); // Assert status is correct - rlc_am_nr_status_pdu_t status_check = {}; + rlc_am_nr_status_pdu_t status_check(sn_size); rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. TESTASSERT_EQ(3, status_check.nacks.size()); // We lost one PDU. @@ -1548,11 +1548,11 @@ int max_retx_lost_sdu_test(rlc_am_nr_sn_size_t sn_size) TESTASSERT(0 == rlc1.get_buffer_state()); // Fake status PDU that ack SN=1 and nack SN=0 - rlc_am_nr_status_pdu_t fake_status = {}; + rlc_am_nr_status_pdu_t fake_status(sn_size); fake_status.ack_sn = 2; // delivered up to SN=1 rlc_status_nack_t nack; // one SN was lost nack.nack_sn = 0; // it was SN=0 that was lost - fake_status.nacks.push_back(nack); + fake_status.push_nack(nack); // pack into PDU byte_buffer_t status_pdu; @@ -1629,8 +1629,8 @@ int max_retx_lost_segments_test(rlc_am_nr_sn_size_t sn_size) TESTASSERT(0 == rlc1.get_buffer_state()); // Fake status PDU that ack SN=1 and nack {SN=0 segment 0, SN=0 segment 1} - rlc_am_nr_status_pdu_t status_lost_both_segments = {}; - status_lost_both_segments.ack_sn = 2; // delivered up to SN=1 + rlc_am_nr_status_pdu_t status_lost_both_segments(sn_size); + status_lost_both_segments.ack_sn = 2; // delivered up to SN=1 // two segments lost { @@ -1639,7 +1639,7 @@ int max_retx_lost_segments_test(rlc_am_nr_sn_size_t sn_size) nack.has_so = true; // this NACKs a segment nack.so_start = 0; // segment starts at (and includes) byte 0 nack.so_end = 12; // segment ends at (and includes) byte 12 - status_lost_both_segments.nacks.push_back(nack); + status_lost_both_segments.push_nack(nack); } { @@ -1648,7 +1648,7 @@ int max_retx_lost_segments_test(rlc_am_nr_sn_size_t sn_size) nack.has_so = true; // this NACKs a segment nack.so_start = 13; // segment starts at (and includes) byte 13 nack.so_end = 19; // segment ends at (and includes) byte 19 - status_lost_both_segments.nacks.push_back(nack); + status_lost_both_segments.push_nack(nack); } // pack into PDU @@ -1657,8 +1657,8 @@ int max_retx_lost_segments_test(rlc_am_nr_sn_size_t sn_size) status_lost_both_segments, rlc_cfg.am_nr.tx_sn_field_length, &status_pdu_lost_both_segments); // Fake status PDU that ack SN=1 and nack {SN=0 segment 1} - rlc_am_nr_status_pdu_t status_lost_second_segment = {}; - status_lost_second_segment.ack_sn = 2; // delivered up to SN=1 + rlc_am_nr_status_pdu_t status_lost_second_segment(sn_size); + status_lost_second_segment.ack_sn = 2; // delivered up to SN=1 // one SN was lost { @@ -1667,7 +1667,7 @@ int max_retx_lost_segments_test(rlc_am_nr_sn_size_t sn_size) nack.has_so = true; // this NACKs a segment nack.so_start = 13; // segment starts at (and includes) byte 13 nack.so_end = 19; // segment ends at (and includes) byte 19 - status_lost_second_segment.nacks.push_back(nack); + status_lost_second_segment.push_nack(nack); } // pack into PDU @@ -1971,12 +1971,12 @@ int poll_retx() { unique_byte_buffer_t status_pdu = srsran::make_byte_buffer(); TESTASSERT(status_pdu != nullptr); - rlc_am_nr_status_pdu_t status = {}; + rlc_am_nr_status_pdu_t status(rlc_am_nr_sn_size_t::size12bits); status.ack_sn = 2; { rlc_status_nack_t nack; nack.nack_sn = 1; // SN=1 needs RETX - status.nacks.push_back(nack); + status.push_nack(nack); } rlc_am_nr_write_status_pdu(status, rlc_cnfg.am_nr.tx_sn_field_length, status_pdu.get()); rlc1.write_pdu(status_pdu->msg, status_pdu->N_bytes); @@ -2002,12 +2002,12 @@ int poll_retx() { unique_byte_buffer_t status_pdu = srsran::make_byte_buffer(); TESTASSERT(status_pdu != nullptr); - rlc_am_nr_status_pdu_t status = {}; + rlc_am_nr_status_pdu_t status(rlc_am_nr_sn_size_t::size12bits); status.ack_sn = 4; { rlc_status_nack_t nack; nack.nack_sn = 1; // SN=1 needs RETX - status.nacks.push_back(nack); + status.push_nack(nack); } rlc_am_nr_write_status_pdu(status, rlc_cnfg.am_nr.tx_sn_field_length, status_pdu.get()); rlc1.write_pdu(status_pdu->msg, status_pdu->N_bytes); From 48dce0aab030388bf515db9ee06af592afd290cf Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Thu, 7 Apr 2022 15:27:41 +0200 Subject: [PATCH 154/195] lib,rlc_am_nr: truncate status PDUs if necessary --- lib/include/srsran/rlc/rlc_am_nr_packing.h | 2 + lib/src/rlc/rlc_am_nr.cc | 30 +++++++------ lib/src/rlc/rlc_am_nr_packing.cc | 51 ++++++++++++++++------ 3 files changed, 56 insertions(+), 27 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_am_nr_packing.h b/lib/include/srsran/rlc/rlc_am_nr_packing.h index e427bf5d8..8a7484a45 100644 --- a/lib/include/srsran/rlc/rlc_am_nr_packing.h +++ b/lib/include/srsran/rlc/rlc_am_nr_packing.h @@ -87,6 +87,7 @@ private: uint32_t packed_size_; ///< Stores the current packed size; sync on each change of nacks_ void refresh_packed_size(); + uint32_t nack_size(const rlc_status_nack_t& nack) const; public: rlc_am_nr_control_pdu_type_t cpt; ///< CPT header @@ -99,6 +100,7 @@ public: void push_nack(const rlc_status_nack_t& nack); const std::vector& get_nacks() const { return nacks_; } uint32_t get_packed_size() const { return packed_size; } + bool trim(uint32_t max_packed_size); }; /**************************************************************************** diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 49ea68591..4fc7b3a0d 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -1414,8 +1414,6 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m status->reset(); - byte_buffer_t tmp_buf; - /* * - for the RLC SDUs with SN such that RX_Next <= SN < RX_Highest_Status that has not been completely * received yet, in increasing SN order of RLC SDUs and increasing byte segment order within RLC SDUs, @@ -1425,9 +1423,7 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m RlcDebug("Generating status PDU"); for (uint32_t i = st.rx_next; rx_mod_base_nr(i) < rx_mod_base_nr(st.rx_highest_status); i = (i + 1) % mod_nr) { if ((rx_window->has_sn(i) && (*rx_window)[i].fully_received)) { - // only update ACK_SN if this SN has been fully received - status->ack_sn = i; - RlcDebug("Updating ACK_SN. ACK_SN=%d", i); + RlcDebug("SDU SN=%d is fully received", i); } else { if (not rx_window->has_sn(i)) { // No segment received, NACK the whole SDU @@ -1475,18 +1471,25 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m } } } - // TODO: add check to not exceed status->N_nack >= RLC_AM_NR_MAX_NACKS - // make sure we don't exceed grant size (FIXME) - rlc_am_nr_write_status_pdu(*status, cfg.rx_sn_field_length, &tmp_buf); - } + } // NACK loop + /* * - set the ACK_SN to the SN of the next not received RLC SDU which is not * indicated as missing in the resulting STATUS PDU. */ - // FIXME as we do not check the size of status report, the next not received - // RLC SDU has the same SN as RX_HIGHEST_STATUS status->ack_sn = st.rx_highest_status; - rlc_am_nr_write_status_pdu(*status, cfg.rx_sn_field_length, &tmp_buf); + + // trim PDU if necessary + if (status->packed_size > max_len) { + RlcInfo("Trimming status PDU with %d NACKs and packed_size=%d into max_len=%d", + status->nacks.size(), + status->packed_size, + max_len); + log_rlc_am_nr_status_pdu_to_string(logger.debug, "Untrimmed status PDU - %s", status, rb_name); + if (not status->trim(max_len)) { + RlcError("Failed to trim status PDU into provided space: max_len=%d", max_len); + } + } if (max_len != UINT32_MAX) { // UINT32_MAX is used just to query the status PDU length @@ -1495,7 +1498,8 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m } do_status = false; } - return tmp_buf.N_bytes; + + return status->packed_size; } uint32_t rlc_am_nr_rx::get_status_pdu_length() diff --git a/lib/src/rlc/rlc_am_nr_packing.cc b/lib/src/rlc/rlc_am_nr_packing.cc index df8785015..31cd2ba4a 100644 --- a/lib/src/rlc/rlc_am_nr_packing.cc +++ b/lib/src/rlc/rlc_am_nr_packing.cc @@ -23,15 +23,21 @@ void rlc_am_nr_status_pdu_t::refresh_packed_size() { uint32_t packed_size = rlc_am_nr_status_pdu_sizeof_header_ack_sn; for (auto nack : nacks_) { - packed_size += sn_size == rlc_am_nr_sn_size_t::size12bits ? rlc_am_nr_status_pdu_sizeof_nack_sn_ext_12bit_sn - : rlc_am_nr_status_pdu_sizeof_nack_sn_ext_18bit_sn; - if (nack.has_so) { - packed_size += rlc_am_nr_status_pdu_sizeof_nack_so; - } - if (nack.has_nack_range) { - packed_size += rlc_am_nr_status_pdu_sizeof_nack_range; - } + packed_size += nack_size(nack); + } +} + +uint32_t rlc_am_nr_status_pdu_t::nack_size(const rlc_status_nack_t& nack) const +{ + uint32_t result = sn_size == rlc_am_nr_sn_size_t::size12bits ? rlc_am_nr_status_pdu_sizeof_nack_sn_ext_12bit_sn + : rlc_am_nr_status_pdu_sizeof_nack_sn_ext_18bit_sn; + if (nack.has_so) { + result += rlc_am_nr_status_pdu_sizeof_nack_so; + } + if (nack.has_nack_range) { + result += rlc_am_nr_status_pdu_sizeof_nack_range; } + return result; } rlc_am_nr_status_pdu_t::rlc_am_nr_status_pdu_t(rlc_am_nr_sn_size_t sn_size) : @@ -57,14 +63,31 @@ void rlc_am_nr_status_pdu_t::reset() void rlc_am_nr_status_pdu_t::push_nack(const rlc_status_nack_t& nack) { nacks_.push_back(nack); - packed_size_ += sn_size == rlc_am_nr_sn_size_t::size12bits ? rlc_am_nr_status_pdu_sizeof_nack_sn_ext_12bit_sn - : rlc_am_nr_status_pdu_sizeof_nack_sn_ext_18bit_sn; - if (nack.has_so) { - packed_size_ += rlc_am_nr_status_pdu_sizeof_nack_so; + packed_size_ += nack_size(nack); +} + +bool rlc_am_nr_status_pdu_t::trim(uint32_t max_packed_size) +{ + if (max_packed_size >= packed_size_) { + // no trimming required + return true; } - if (nack.has_nack_range) { - packed_size_ += rlc_am_nr_status_pdu_sizeof_nack_range; + if (max_packed_size <= rlc_am_nr_status_pdu_sizeof_header_ack_sn) { + // too little space for smallest possible status PDU (only header + ACK). + return false; + } + + // remove NACKs (starting from the back) until it fits into given space + // note: when removing a NACK for a segment, we have to remove all other NACKs with the same SN as well, + // see TS 38.322 Sec. 5.3.4: + // "set the ACK_SN to the SN of the next not received RLC SDU + // which is not indicated as missing in the resulting STATUS PDU." + while (nacks_.size() > 0 && (max_packed_size >= packed_size_ || nacks_.back().nack_sn == ack_sn)) { + packed_size_ -= nack_size(nacks_.back()); + ack_sn = nacks_.back().nack_sn; + nacks_.pop_back(); } + return true; } /**************************************************************************** From d9d3bfde6324822530250fec532fc98836292836 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Fri, 8 Apr 2022 10:31:51 +0200 Subject: [PATCH 155/195] lib,rlc_am_nr: remove redundant function calls. --- lib/src/rlc/rlc_am_nr.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 4fc7b3a0d..8f9ffa1e2 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -725,7 +725,6 @@ uint32_t rlc_am_nr_tx::build_status_pdu(byte_buffer_t* payload, uint32_t nof_byt { RlcInfo("generating status PDU. Bytes available:%d", nof_bytes); rlc_am_nr_status_pdu_t status(cfg.rx_sn_field_length); // carries status of RX entity, hence use SN length of RX - status.reset(); int pdu_len = rx->get_status_pdu(&status, nof_bytes); if (pdu_len == SRSRAN_ERROR) { RlcDebug("deferred status PDU. Cause: Failed to acquire rx lock"); @@ -1505,7 +1504,6 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m uint32_t rlc_am_nr_rx::get_status_pdu_length() { rlc_am_nr_status_pdu_t tmp_status(cfg.rx_sn_field_length); - tmp_status.reset(); get_status_pdu(&tmp_status, UINT32_MAX); return tmp_status.get_packed_size(); } From 872e55a84eeb6c237f368e99bbdadc8d51fa8041 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Fri, 8 Apr 2022 11:41:56 +0200 Subject: [PATCH 156/195] lib,rlc_am_nr_packing: reorder functions by importance --- lib/src/rlc/rlc_am_nr_packing.cc | 42 ++++++++++++++++---------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr_packing.cc b/lib/src/rlc/rlc_am_nr_packing.cc index 31cd2ba4a..0d718809b 100644 --- a/lib/src/rlc/rlc_am_nr_packing.cc +++ b/lib/src/rlc/rlc_am_nr_packing.cc @@ -19,27 +19,6 @@ namespace srsran { * Container implementation for pack/unpack functions ***************************************************************************/ -void rlc_am_nr_status_pdu_t::refresh_packed_size() -{ - uint32_t packed_size = rlc_am_nr_status_pdu_sizeof_header_ack_sn; - for (auto nack : nacks_) { - packed_size += nack_size(nack); - } -} - -uint32_t rlc_am_nr_status_pdu_t::nack_size(const rlc_status_nack_t& nack) const -{ - uint32_t result = sn_size == rlc_am_nr_sn_size_t::size12bits ? rlc_am_nr_status_pdu_sizeof_nack_sn_ext_12bit_sn - : rlc_am_nr_status_pdu_sizeof_nack_sn_ext_18bit_sn; - if (nack.has_so) { - result += rlc_am_nr_status_pdu_sizeof_nack_so; - } - if (nack.has_nack_range) { - result += rlc_am_nr_status_pdu_sizeof_nack_range; - } - return result; -} - rlc_am_nr_status_pdu_t::rlc_am_nr_status_pdu_t(rlc_am_nr_sn_size_t sn_size) : sn_size(sn_size), nacks_(0), @@ -90,6 +69,27 @@ bool rlc_am_nr_status_pdu_t::trim(uint32_t max_packed_size) return true; } +void rlc_am_nr_status_pdu_t::refresh_packed_size() +{ + uint32_t packed_size = rlc_am_nr_status_pdu_sizeof_header_ack_sn; + for (auto nack : nacks_) { + packed_size += nack_size(nack); + } +} + +uint32_t rlc_am_nr_status_pdu_t::nack_size(const rlc_status_nack_t& nack) const +{ + uint32_t result = sn_size == rlc_am_nr_sn_size_t::size12bits ? rlc_am_nr_status_pdu_sizeof_nack_sn_ext_12bit_sn + : rlc_am_nr_status_pdu_sizeof_nack_sn_ext_18bit_sn; + if (nack.has_so) { + result += rlc_am_nr_status_pdu_sizeof_nack_so; + } + if (nack.has_nack_range) { + result += rlc_am_nr_status_pdu_sizeof_nack_range; + } + return result; +} + /**************************************************************************** * Header pack/unpack helper functions * Ref: 3GPP TS 38.322 v15.3.0 Section 6.2.2.4 From a2332d10f27242aec52914b008ef5e2a159868a9 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Fri, 8 Apr 2022 14:08:07 +0200 Subject: [PATCH 157/195] lib,rlc_am_nr_packing: unit test + bugfix for trimming of status PDUs --- lib/src/rlc/rlc_am_nr_packing.cc | 4 +- lib/test/rlc/rlc_am_nr_pdu_test.cc | 236 +++++++++++++++++++++++++++++ 2 files changed, 238 insertions(+), 2 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr_packing.cc b/lib/src/rlc/rlc_am_nr_packing.cc index 0d718809b..ed642aa79 100644 --- a/lib/src/rlc/rlc_am_nr_packing.cc +++ b/lib/src/rlc/rlc_am_nr_packing.cc @@ -51,7 +51,7 @@ bool rlc_am_nr_status_pdu_t::trim(uint32_t max_packed_size) // no trimming required return true; } - if (max_packed_size <= rlc_am_nr_status_pdu_sizeof_header_ack_sn) { + if (max_packed_size < rlc_am_nr_status_pdu_sizeof_header_ack_sn) { // too little space for smallest possible status PDU (only header + ACK). return false; } @@ -61,7 +61,7 @@ bool rlc_am_nr_status_pdu_t::trim(uint32_t max_packed_size) // see TS 38.322 Sec. 5.3.4: // "set the ACK_SN to the SN of the next not received RLC SDU // which is not indicated as missing in the resulting STATUS PDU." - while (nacks_.size() > 0 && (max_packed_size >= packed_size_ || nacks_.back().nack_sn == ack_sn)) { + while (nacks_.size() > 0 && (max_packed_size < packed_size_ || nacks_.back().nack_sn == ack_sn)) { packed_size_ -= nack_size(nacks_.back()); ack_sn = nacks_.back().nack_sn; nacks_.pop_back(); diff --git a/lib/test/rlc/rlc_am_nr_pdu_test.cc b/lib/test/rlc/rlc_am_nr_pdu_test.cc index d826ef896..977337b4a 100644 --- a/lib/test/rlc/rlc_am_nr_pdu_test.cc +++ b/lib/test/rlc/rlc_am_nr_pdu_test.cc @@ -433,6 +433,232 @@ int rlc_am_nr_control_pdu_12bit_sn_test_nack_range() return SRSRAN_SUCCESS; } +// Test status PDU for correct trimming and estimation of packed size +// 1) Test init, copy and reset +// 2) Test step-wise growth and trimming of status PDU while covering several corner cases +int rlc_am_nr_control_pdu_test_trimming(rlc_am_nr_sn_size_t sn_size) +{ + test_delimit_logger delimiter("Control PDU ({} bit SN) test trimming", to_number(sn_size)); + + // status PDU with no NACKs + { + constexpr uint32_t min_size = 3; + srsran::byte_buffer_t pdu; + rlc_am_nr_status_pdu_t status_pdu(sn_size); + + status_pdu.ack_sn = 99; + TESTASSERT_EQ(status_pdu.packed_size, min_size); // minimum size + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, min_size); + + rlc_am_nr_status_pdu_t status_pdu_copy = status_pdu; + TESTASSERT_EQ(status_pdu_copy.ack_sn, 99); + TESTASSERT_EQ(status_pdu_copy.packed_size, min_size); // minimum size + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu_copy, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, min_size); + + status_pdu.reset(); + + status_pdu.ack_sn = 77; + TESTASSERT_EQ(status_pdu.packed_size, min_size); // should still have minimum size + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, min_size); + + TESTASSERT_EQ(status_pdu_copy.ack_sn, 99); // shouldn't have changed + TESTASSERT_EQ(status_pdu_copy.packed_size, min_size); // minimum size + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu_copy, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, min_size); + } + + // status PDU with multiple NACKs + // expect: ACK=77, NACKs=[12][14][17 50:99][17 150:199][17 250:299][19][21 333:111 r5][27 444:666 r3] + { + constexpr uint32_t min_size = 3; + const uint32_t nack_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + constexpr uint32_t so_size = 4; + constexpr uint32_t range_size = 1; + uint32_t expected_size = min_size; + srsran::byte_buffer_t pdu; + rlc_am_nr_status_pdu_t status_pdu(sn_size); + + status_pdu.ack_sn = 77; + { + rlc_status_nack_t nack; + nack.nack_sn = 12; + status_pdu.push_nack(nack); + } + expected_size += nack_size; + TESTASSERT_EQ(status_pdu.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + { + rlc_status_nack_t nack; + nack.nack_sn = 14; + status_pdu.push_nack(nack); + } + expected_size += nack_size; + TESTASSERT_EQ(status_pdu.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + { + rlc_status_nack_t nack; + nack.nack_sn = 17; + nack.has_so = true; + nack.so_start = 50; + nack.so_end = 99; + status_pdu.push_nack(nack); + } + expected_size += nack_size + so_size; + TESTASSERT_EQ(status_pdu.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + { + rlc_status_nack_t nack; + nack.nack_sn = 17; + nack.has_so = true; + nack.so_start = 150; + nack.so_end = 199; + status_pdu.push_nack(nack); + } + expected_size += nack_size + so_size; + TESTASSERT_EQ(status_pdu.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + { + rlc_status_nack_t nack; + nack.nack_sn = 17; + nack.has_so = true; + nack.so_start = 250; + nack.so_end = 299; + status_pdu.push_nack(nack); + } + expected_size += nack_size + so_size; + TESTASSERT_EQ(status_pdu.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + { + rlc_status_nack_t nack; + nack.nack_sn = 19; + status_pdu.push_nack(nack); + } + expected_size += nack_size; + TESTASSERT_EQ(status_pdu.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + { + rlc_status_nack_t nack; + nack.nack_sn = 21; + nack.has_so = true; + nack.so_start = 333; + nack.so_end = 111; + nack.has_nack_range = true; + nack.nack_range = 5; + status_pdu.push_nack(nack); + } + expected_size += nack_size + so_size + range_size; + TESTASSERT_EQ(status_pdu.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + { + rlc_status_nack_t nack; + nack.nack_sn = 27; + nack.has_so = true; + nack.so_start = 444; + nack.so_end = 666; + nack.has_nack_range = true; + nack.nack_range = 3; + status_pdu.push_nack(nack); + } + expected_size += nack_size + so_size + range_size; + TESTASSERT_EQ(status_pdu.ack_sn, 77); + TESTASSERT_EQ(status_pdu.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + + // current state: ACK=77, NACKs=[12][14][17 50:99][17 150:199][17 250:299][19][21 333:111 r5][27 444:666 r3] + + // create a copy, check content + rlc_am_nr_status_pdu_t status_pdu_copy = status_pdu; + TESTASSERT_EQ(status_pdu_copy.ack_sn, 77); + TESTASSERT_EQ(status_pdu_copy.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu_copy, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + + // current state: ACK=77, NACKs=[12][14][17 50:99][17 150:199][17 250:299][19][21 333:111 r5][27 444:666 r3] + + // trim to much larger size: nothing should change + TESTASSERT_EQ(status_pdu.trim(expected_size * 2), true); + TESTASSERT_EQ(status_pdu.ack_sn, 77); + TESTASSERT_EQ(status_pdu.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + + // trim to exact size: nothing should change + TESTASSERT_EQ(status_pdu.trim(expected_size), true); + TESTASSERT_EQ(status_pdu.ack_sn, 77); + TESTASSERT_EQ(status_pdu.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + + // trim to (expected_size - 1): this should remove the last NACK and update ACK accordingly + TESTASSERT_EQ(status_pdu.trim(expected_size - 1), true); + expected_size -= nack_size + so_size + range_size; + TESTASSERT_EQ(status_pdu.ack_sn, 27); + TESTASSERT_EQ(status_pdu.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + + // current state: ACK=27, NACKs=[12][14][17 50:99][17 150:199][17 250:299][19][21 333:111 r5] + + // trim to (expected_size - last two NACKs): this should remove the last NACK and update ACK accordingly + TESTASSERT_EQ(status_pdu.trim(expected_size - (2 * nack_size + so_size + range_size)), true); + expected_size -= 2 * nack_size + so_size + range_size; + TESTASSERT_EQ(status_pdu.ack_sn, 19); + TESTASSERT_EQ(status_pdu.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + + // current state: ACK=19, NACKs=[12][14][17 50:99][17 150:199][17 250:299] + + // trim to (expected_size - 1): this should remove the last NACK and all other NACKs with the same SN + TESTASSERT_EQ(status_pdu.trim(expected_size - 1), true); + expected_size -= 3 * (nack_size + so_size); + TESTASSERT_EQ(status_pdu.ack_sn, 17); + TESTASSERT_EQ(status_pdu.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + + // current state: ACK=17, NACKs=[12][14] + + // trim to impossible size = 1: this should report a failure without changes of the PDU + TESTASSERT_EQ(status_pdu.trim(1), false); + TESTASSERT_EQ(status_pdu.ack_sn, 17); + TESTASSERT_EQ(status_pdu.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + + // current state: ACK=17, NACKs=[12][14] + + // trim to minimum size: this should remove all NACKs and update ACK to the SN of the first NACK + expected_size = min_size; + TESTASSERT_EQ(status_pdu.trim(expected_size), true); + TESTASSERT_EQ(status_pdu.ack_sn, 12); + TESTASSERT_EQ(status_pdu.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + + // current state: ACK=12, NACKs empty + + // check the copy again - should be unchanged if not a shallow copy + TESTASSERT_EQ(status_pdu_copy.ack_sn, 77); + TESTASSERT_EQ(status_pdu_copy.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu_copy, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + } + + return SRSRAN_SUCCESS; +} + ///< Control PDU tests (18bit SN) // Status PDU for 18bit SN with ACK_SN=235929=0x39999=0b11 1001 1001 1001 1001 and no further NACK_SN (E1 bit not set) int rlc_am_nr_control_pdu_18bit_sn_test1() @@ -786,6 +1012,11 @@ int main(int argc, char** argv) return SRSRAN_ERROR; } + if (rlc_am_nr_control_pdu_test_trimming(rlc_am_nr_sn_size_t::size12bits)) { + fprintf(stderr, "rlc_am_nr_control_pdu_test_trimming(size12bits) failed.\n"); + return SRSRAN_ERROR; + } + if (rlc_am_nr_control_pdu_18bit_sn_test1()) { fprintf(stderr, "rlc_am_nr_control_pdu_18bit_sn_test1() failed.\n"); return SRSRAN_ERROR; @@ -816,5 +1047,10 @@ int main(int argc, char** argv) return SRSRAN_ERROR; } + if (rlc_am_nr_control_pdu_test_trimming(rlc_am_nr_sn_size_t::size18bits)) { + fprintf(stderr, "rlc_am_nr_control_pdu_test_trimming(size18bits) failed.\n"); + return SRSRAN_ERROR; + } + return SRSRAN_SUCCESS; } From 85c95d94b2594947a7f9957b064d23ea69db05e6 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Fri, 8 Apr 2022 15:50:34 +0200 Subject: [PATCH 158/195] lib,rlc_am_nr_test: add retx test with trimmed status PDU --- lib/test/rlc/rlc_am_nr_test.cc | 214 +++++++++++++++++++++++++++++++++ 1 file changed, 214 insertions(+) diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index 1ab1d84a1..fd816dcb8 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -652,6 +652,219 @@ int lost_pdu_duplicated_nack_test(rlc_am_nr_sn_size_t sn_size) return SRSRAN_SUCCESS; } +/* + * Test the loss of multiple PDUs. + * NACKs for all missing PDUs should be visible in buffer state -- but we enforce + * a trimmed status PDU by providing little space for the whole status PDU. + * Retx after NACK should be present too. + * Further status report shall contain the trimmed NACK. + * Another Retx after NACK should be present. + * No further status reports shall be issued. + */ +int lost_pdus_trimmed_nack_test(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester; + timer_handler timers(8); + byte_buffer_t pdu_bufs[NBUFS]; + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + test_delimit_logger delimiter("lost PDUs and trimmed NACKs ({} bit SN)", to_number(sn_size)); + + constexpr uint32_t payload_size = 1; + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + uint32_t data_pdu_size = header_size + payload_size; + uint32_t expect_buffer_state = NBUFS * data_pdu_size; + + if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { + return -1; + } + + rlc_config_t rlc2_config = rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)); + if (not rlc2.configure(rlc2_config)) { + return -1; + } + + // after configuring entity + TESTASSERT(0 == rlc1.get_buffer_state()); + + basic_test_tx(&rlc1, pdu_bufs, sn_size); + + // Write 5 PDUs into RLC2 + for (int i = 0; i < NBUFS; i++) { + if (i != 1 && i != 3) { + rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); // Don't write RLC_SN=1 and 3. + } + } + + // Only after t-reassembly has expired, will the status report include NACKs. + TESTASSERT_EQ(3, rlc2.get_buffer_state()); + { + // Read status PDU from RLC2 + byte_buffer_t status_buf; + int len = rlc2.read_pdu(status_buf.msg, 5); + status_buf.N_bytes = len; + + TESTASSERT(0 == rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check(sn_size); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(1, status_check.ack_sn); // 1 is the next expected SN (i.e. the first lost packet.) + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + } + + // Step timers until reassambly timeout expires + for (int cnt = 0; cnt < 35; cnt++) { + timers.step_all(); + } + + // Step timers until reassambly timeout expires + for (int cnt = 0; cnt < 35; cnt++) { + timers.step_all(); + } + + // t-reassembly has expired. There should be two NACKs in the status report. + constexpr uint32_t status_pdu_ack_size = 3; + uint32_t status_pdu_nack_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + uint32_t expected_size = status_pdu_ack_size + 2 * status_pdu_nack_size; + TESTASSERT_EQ(expected_size, rlc2.get_buffer_state()); + { + // Read status PDU from RLC2, enforce to trimming by providing little space (expected_size - 1) + // to drop the second NACK. + byte_buffer_t status_buf; + uint32_t len = rlc2.read_pdu(status_buf.msg, expected_size - 1); + status_buf.N_bytes = len; + expected_size = status_pdu_ack_size + 1 * status_pdu_nack_size; // only one NACK left + TESTASSERT_EQ(len, expected_size); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check(sn_size); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(3, status_check.ack_sn); // 3 is the next expected SN (after trimming) + TESTASSERT_EQ(1, status_check.nacks.size()); // Expect only one NACK left + TESTASSERT_EQ(1, status_check.nacks[0].nack_sn); // The NACK'ed SN is 1. + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + // Check there is an Retx of SN=1 + TESTASSERT_EQ(data_pdu_size, rlc1.get_buffer_state()); + } + + { + // Check correct re-transmission + byte_buffer_t retx_buf; + uint32_t len = rlc1.read_pdu(retx_buf.msg, data_pdu_size); + retx_buf.N_bytes = len; + TESTASSERT_EQ(data_pdu_size, len); + + rlc2.write_pdu(retx_buf.msg, retx_buf.N_bytes); + + expected_size = status_pdu_ack_size + 1 * status_pdu_nack_size; + TESTASSERT_EQ(expected_size, rlc2.get_buffer_state()); // Status report should now include the chopped NACK + } + { + // Double check status report + byte_buffer_t status_buf; + uint32_t len = rlc2.read_pdu(status_buf.msg, expected_size); + status_buf.N_bytes = len; + expected_size = status_pdu_ack_size + 1 * status_pdu_nack_size; // the remaining NACK + TESTASSERT_EQ(len, expected_size); + + // Nothing else pending + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check(sn_size); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. + TESTASSERT_EQ(1, status_check.nacks.size()); // Expect only the second NACK + TESTASSERT_EQ(3, status_check.nacks[0].nack_sn); // The NACK'ed SN is 3. + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + // Check there is an Retx of SN=3 + TESTASSERT_EQ(data_pdu_size, rlc1.get_buffer_state()); + } + { + // Check correct re-transmission + byte_buffer_t retx_buf; + uint32_t len = rlc1.read_pdu(retx_buf.msg, data_pdu_size); + retx_buf.N_bytes = len; + TESTASSERT_EQ(data_pdu_size, len); + + rlc2.write_pdu(retx_buf.msg, retx_buf.N_bytes); + + expected_size = status_pdu_ack_size; + TESTASSERT_EQ(expected_size, rlc2.get_buffer_state()); // Status report should have no NACKs + } + { + // Double check status report + byte_buffer_t status_buf; + int len = rlc2.read_pdu(status_buf.msg, expected_size); + status_buf.N_bytes = len; + + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check(sn_size); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. + TESTASSERT_EQ(0, status_check.nacks.size()); // All PDUs are acked now + } + + { + // rlc2 should not issue further status PDUs as time passes (even after expiry of t_status_prohibit) + int32_t checktime = 2 * rlc2_config.am_nr.t_status_prohibit; + for (int cnt = 0; cnt < checktime; cnt++) { + timers.step_all(); + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + } + } + + // Check statistics + rlc_bearer_metrics_t metrics1 = rlc1.get_metrics(); + rlc_bearer_metrics_t metrics2 = rlc2.get_metrics(); + + uint32_t total_tx_pdu_bytes1 = (NBUFS + 2) * data_pdu_size; // (NBUFS + 2 RETX) * PDU size + uint32_t total_rx_pdu_bytes1 = 3 * status_pdu_ack_size + 2 * status_pdu_nack_size; // 3 status PDUs (2 with one NACK) + uint32_t total_tx_pdu_bytes2 = + 4 * status_pdu_ack_size + 2 * status_pdu_nack_size; // 4 status PDUs (2 with one NACK, two without) + uint32_t total_rx_pdu_bytes2 = (NBUFS)*data_pdu_size; // (NBUFS - 2 Lost + 2 RETX) * PDU size + + // SDU metrics + TESTASSERT_EQ(5, metrics1.num_tx_sdus); + TESTASSERT_EQ(0, metrics1.num_rx_sdus); + TESTASSERT_EQ(5, metrics1.num_tx_sdu_bytes); + TESTASSERT_EQ(0, metrics1.num_rx_sdu_bytes); + TESTASSERT_EQ(0, metrics1.num_lost_sdus); + // PDU metrics + TESTASSERT_EQ(5 + 2, metrics1.num_tx_pdus); // One re-transmission + TESTASSERT_EQ(3, metrics1.num_rx_pdus); // 3 status PDUs + TESTASSERT_EQ(total_tx_pdu_bytes1, metrics1.num_tx_pdu_bytes); // (NBUFS + 2 RETX) * PDU size + TESTASSERT_EQ(total_rx_pdu_bytes1, metrics1.num_rx_pdu_bytes); // Two status PDU (one with a NACK) + TESTASSERT_EQ(0, metrics1.num_lost_sdus); // No lost SDUs + + // PDU metrics + TESTASSERT_EQ(0, metrics2.num_tx_sdus); + TESTASSERT_EQ(5, metrics2.num_rx_sdus); + TESTASSERT_EQ(0, metrics2.num_tx_sdu_bytes); + TESTASSERT_EQ(5, metrics2.num_rx_sdu_bytes); + TESTASSERT_EQ(0, metrics2.num_lost_sdus); + // SDU metrics + TESTASSERT_EQ(4, metrics2.num_tx_pdus); // 4 status PDUs + TESTASSERT_EQ(5, metrics2.num_rx_pdus); // 5 PDUs (7 tx'ed, but 2 were lost) + TESTASSERT_EQ(total_tx_pdu_bytes2, metrics2.num_tx_pdu_bytes); // Three status PDU (one with a NACK, two without) + TESTASSERT_EQ(total_rx_pdu_bytes2, metrics2.num_rx_pdu_bytes); // (NBUFS - 2 Lost + 2 RETX) * PDU size + TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs + return SRSRAN_SUCCESS; +} + /* * Test the basic segmentation of a single SDU. * A single SDU of 3 bytes is segmented into 3 PDUs @@ -2063,6 +2276,7 @@ int main() TESTASSERT(basic_test(sn_size) == SRSRAN_SUCCESS); TESTASSERT(lost_pdu_test(sn_size) == SRSRAN_SUCCESS); TESTASSERT(lost_pdu_duplicated_nack_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(lost_pdus_trimmed_nack_test(sn_size) == SRSRAN_SUCCESS); TESTASSERT(basic_segmentation_test(sn_size) == SRSRAN_SUCCESS); TESTASSERT(segment_retx_test(sn_size) == SRSRAN_SUCCESS); TESTASSERT(segment_retx_and_loose_segments_test(sn_size) == SRSRAN_SUCCESS); From ca37eed6642a42ecfc02e6ce59a9f7e4c853676e Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Wed, 20 Apr 2022 09:20:39 +0200 Subject: [PATCH 159/195] lib,rlc_am_nr: default initialize member in class definition --- lib/include/srsran/rlc/rlc_am_nr_packing.h | 35 +++++++++++++--------- lib/src/rlc/rlc_am_nr_packing.cc | 9 +----- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_am_nr_packing.h b/lib/include/srsran/rlc/rlc_am_nr_packing.h index 8a7484a45..f70bf6c14 100644 --- a/lib/include/srsran/rlc/rlc_am_nr_packing.h +++ b/lib/include/srsran/rlc/rlc_am_nr_packing.h @@ -78,22 +78,35 @@ struct rlc_amd_tx_sdu_nr_t { explicit rlc_amd_tx_sdu_nr_t(uint32_t rlc_sn_) : rlc_sn(rlc_sn_) {} }; -///< AM NR Status PDU header +constexpr uint32_t rlc_am_nr_status_pdu_sizeof_header_ack_sn = 3; ///< header fixed part and ACK SN +constexpr uint32_t rlc_am_nr_status_pdu_sizeof_nack_sn_ext_12bit_sn = 2; ///< NACK SN and extension fields (12 bit SN) +constexpr uint32_t rlc_am_nr_status_pdu_sizeof_nack_sn_ext_18bit_sn = 3; ///< NACK SN and extension fields (18 bit SN) +constexpr uint32_t rlc_am_nr_status_pdu_sizeof_nack_so = 4; ///< NACK segment offsets (start and end) +constexpr uint32_t rlc_am_nr_status_pdu_sizeof_nack_range = 1; ///< NACK range (nof consecutively lost SDUs) + +/// AM NR Status PDU header class rlc_am_nr_status_pdu_t { private: - rlc_am_nr_sn_size_t sn_size; ///< Stored SN size required to compute the packed size - std::vector nacks_; ///< Internal NACK container; keep in sync with packed_size_ - uint32_t packed_size_; ///< Stores the current packed size; sync on each change of nacks_ + /// Stored SN size required to compute the packed size + rlc_am_nr_sn_size_t sn_size = rlc_am_nr_sn_size_t::nulltype; + /// Internal NACK container; keep in sync with packed_size_ + std::vector nacks_ = {}; + /// Stores the current packed size; sync on each change of nacks_ + uint32_t packed_size_ = rlc_am_nr_status_pdu_sizeof_header_ack_sn; void refresh_packed_size(); uint32_t nack_size(const rlc_status_nack_t& nack) const; public: - rlc_am_nr_control_pdu_type_t cpt; ///< CPT header - uint32_t ack_sn; ///< SN of the next not received RLC Data PDU - const std::vector& nacks; ///< Read-only reference to NACKs - const uint32_t& packed_size; ///< Read-only reference to packed size + /// CPT header + rlc_am_nr_control_pdu_type_t cpt = rlc_am_nr_control_pdu_type_t::status_pdu; + /// SN of the next not received RLC Data PDU + uint32_t ack_sn = INVALID_RLC_SN; + /// Read-only reference to NACKs + const std::vector& nacks = nacks_; + /// Read-only reference to packed size + const uint32_t& packed_size = packed_size_; rlc_am_nr_status_pdu_t(rlc_am_nr_sn_size_t sn_size); void reset(); @@ -125,12 +138,6 @@ uint32_t rlc_am_nr_packed_length(const rlc_am_nr_pdu_header_t& header); * Status PDU pack/unpack helper functions for NR * Ref: 3GPP TS 38.322 v16.2.0 Section 6.2.2.5 ***************************************************************************/ -constexpr uint32_t rlc_am_nr_status_pdu_sizeof_header_ack_sn = 3; // header fixed part and ACK SN -constexpr uint32_t rlc_am_nr_status_pdu_sizeof_nack_sn_ext_12bit_sn = 2; // NACK SN and extension fields (12 bit SN) -constexpr uint32_t rlc_am_nr_status_pdu_sizeof_nack_sn_ext_18bit_sn = 3; // NACK SN and extension fields (18 bit SN) -constexpr uint32_t rlc_am_nr_status_pdu_sizeof_nack_so = 4; // NACK segment offsets (start and end) -constexpr uint32_t rlc_am_nr_status_pdu_sizeof_nack_range = 1; // NACK range (nof consecutively lost SDUs) - uint32_t rlc_am_nr_read_status_pdu(const byte_buffer_t* pdu, const rlc_am_nr_sn_size_t sn_size, rlc_am_nr_status_pdu_t* status); diff --git a/lib/src/rlc/rlc_am_nr_packing.cc b/lib/src/rlc/rlc_am_nr_packing.cc index ed642aa79..72054c26c 100644 --- a/lib/src/rlc/rlc_am_nr_packing.cc +++ b/lib/src/rlc/rlc_am_nr_packing.cc @@ -19,14 +19,7 @@ namespace srsran { * Container implementation for pack/unpack functions ***************************************************************************/ -rlc_am_nr_status_pdu_t::rlc_am_nr_status_pdu_t(rlc_am_nr_sn_size_t sn_size) : - sn_size(sn_size), - nacks_(0), - packed_size_(rlc_am_nr_status_pdu_sizeof_header_ack_sn), - cpt(rlc_am_nr_control_pdu_type_t::status_pdu), - ack_sn(INVALID_RLC_SN), - nacks(nacks_), - packed_size(packed_size_) +rlc_am_nr_status_pdu_t::rlc_am_nr_status_pdu_t(rlc_am_nr_sn_size_t sn_size) : sn_size(sn_size) { nacks_.reserve(RLC_AM_NR_TYP_NACKS); } From 319a185eba7d7e98a2e95c9a338715c7c549b0e4 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Fri, 22 Apr 2022 11:55:59 +0200 Subject: [PATCH 160/195] phy_common_test: fix test when compiled with USE_LTE_RATES --- lib/src/phy/common/test/phy_common_test.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/src/phy/common/test/phy_common_test.c b/lib/src/phy/common/test/phy_common_test.c index a4046e063..5465ffdc4 100644 --- a/lib/src/phy/common/test/phy_common_test.c +++ b/lib/src/phy/common/test/phy_common_test.c @@ -16,6 +16,9 @@ int srsran_default_rates_test() { // Verify calculated sample rates for all valid PRB sizes. // By default we use the reduced 3/4 sampling to save bandwidth on the fronthaul. +#ifdef FORCE_STANDARD_RATE + srsran_use_standard_symbol_size(false); +#endif TESTASSERT(srsran_sampling_freq_hz(6) == 1920000); TESTASSERT(srsran_sampling_freq_hz(15) == 3840000); TESTASSERT(srsran_sampling_freq_hz(25) == 5760000); From d854bbc22e676c0d3f1c4f3c9fc81810f555174a Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Thu, 21 Apr 2022 12:25:11 +0200 Subject: [PATCH 161/195] enb,rrc: remove hard-coded SCG bearer config Use 5QI config when configuring secondary cell group bearer for NSA. The 5QI used for this needs to match with the QCI used for the initial LTE DRB. This allows to use RLC AM over NSA. --- .../srsran/interfaces/enb_x2_interfaces.h | 1 + srsenb/hdr/stack/rrc/rrc_endc.h | 1 - srsenb/src/stack/rrc/rrc_endc.cc | 23 ++++- srsgnb/hdr/stack/rrc/rrc_nr_ue.h | 3 +- srsgnb/src/stack/rrc/rrc_nr_ue.cc | 88 +++---------------- 5 files changed, 33 insertions(+), 83 deletions(-) diff --git a/lib/include/srsran/interfaces/enb_x2_interfaces.h b/lib/include/srsran/interfaces/enb_x2_interfaces.h index 9c28f0840..e7287acaf 100644 --- a/lib/include/srsran/interfaces/enb_x2_interfaces.h +++ b/lib/include/srsran/interfaces/enb_x2_interfaces.h @@ -30,6 +30,7 @@ class rrc_nr_interface_rrc public: struct sgnb_addition_req_params_t { uint32_t eps_bearer_id; + uint32_t five_qi; // add configuration check // E-RAB Parameters, Tunnel address (IP address, TEID) // QCI, security, etc diff --git a/srsenb/hdr/stack/rrc/rrc_endc.h b/srsenb/hdr/stack/rrc/rrc_endc.h index d835a4842..fcb6472e3 100644 --- a/srsenb/hdr/stack/rrc/rrc_endc.h +++ b/srsenb/hdr/stack/rrc/rrc_endc.h @@ -101,7 +101,6 @@ private: asn1::rrc::rrc_conn_recfg_complete_s pending_recfg_complete; // fixed ENDC variables - const uint32_t eutra_drb_id = 1; // The DRB ID that converted to NR const uint32_t lcid_drb_nr = 4; // internal events diff --git a/srsenb/src/stack/rrc/rrc_endc.cc b/srsenb/src/stack/rrc/rrc_endc.cc index 49f3676b2..c6b39f4b6 100644 --- a/srsenb/src/stack/rrc/rrc_endc.cc +++ b/srsenb/src/stack/rrc/rrc_endc.cc @@ -310,8 +310,27 @@ void rrc::ue::rrc_endc::start_sgnb_addition() { // Start EN-DC activation using EPS bearer of EUTRA DRB1 rrc_nr_interface_rrc::sgnb_addition_req_params_t params = {}; - params.eps_bearer_id = - rrc_enb->bearer_manager.get_lcid_bearer(rrc_ue->rnti, drb_to_lcid((lte_drb)eutra_drb_id)).eps_bearer_id; + + const auto& drb_list = rrc_ue->bearer_list.get_established_drbs(); + if (drb_list.size() > 0) { + // move first establised DRB to NR cell + const auto& drb1 = drb_list[0]; + const auto& erab_list = rrc_ue->bearer_list.get_erabs(); + auto erab_it = erab_list.find(drb1.eps_bearer_id); + if (erab_it != erab_list.end()) { + params.eps_bearer_id = drb1.eps_bearer_id; + params.five_qi = erab_it->second.qos_params.qci; // use QCI as 5QI + } else { + logger.error("Couldn't find ERAB config for DRB%d. Aborting SgNB addition for E-UTRA rnti=0x%x", + drb1.drb_id, + rrc_ue->rnti); + return; + } + } else { + logger.error("No LTE DRB established. Aborting SgNB addition for E-UTRA rnti=0x%x", rrc_ue->rnti); + return; + } + logger.info("Triggering SgNB addition for E-UTRA rnti=0x%x", rrc_ue->rnti); rrc_enb->rrc_nr->sgnb_addition_request(rrc_ue->rnti, params); diff --git a/srsgnb/hdr/stack/rrc/rrc_nr_ue.h b/srsgnb/hdr/stack/rrc/rrc_nr_ue.h index 37035746a..502a52f88 100644 --- a/srsgnb/hdr/stack/rrc/rrc_nr_ue.h +++ b/srsgnb/hdr/stack/rrc/rrc_nr_ue.h @@ -114,7 +114,6 @@ private: int pack_rrc_reconfiguration(asn1::dyn_octstring& packed_rrc_reconfig); int pack_secondary_cell_group_cfg(asn1::dyn_octstring& packed_secondary_cell_config); - int pack_secondary_cell_group_rlc_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); int pack_secondary_cell_group_mac_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); int pack_secondary_cell_group_sp_cell_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); @@ -149,7 +148,7 @@ private: int pack_nr_radio_bearer_config(asn1::dyn_octstring& packed_nr_bearer_config); - int add_drb(); + int add_drb(uint32_t five_qi); bool init_pucch(); diff --git a/srsgnb/src/stack/rrc/rrc_nr_ue.cc b/srsgnb/src/stack/rrc/rrc_nr_ue.cc index b287f28bb..a5e14e743 100644 --- a/srsgnb/src/stack/rrc/rrc_nr_ue.cc +++ b/srsgnb/src/stack/rrc/rrc_nr_ue.cc @@ -163,55 +163,6 @@ int rrc_nr::ue::send_dl_dcch(srsran::nr_srb srb, const asn1::rrc_nr::dl_dcch_msg return SRSRAN_SUCCESS; } -int rrc_nr::ue::pack_secondary_cell_group_rlc_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) -{ - // RLC for DRB1 (with fixed LCID) - cell_group_cfg_pack.rlc_bearer_to_add_mod_list.resize(1); - auto& rlc_bearer = cell_group_cfg_pack.rlc_bearer_to_add_mod_list[0]; - rlc_bearer.lc_ch_id = drb1_lcid; - rlc_bearer.served_radio_bearer_present = true; - rlc_bearer.served_radio_bearer.set_drb_id(); - rlc_bearer.served_radio_bearer.drb_id() = 1; - rlc_bearer.rlc_cfg_present = true; - -#ifdef USE_RLC_AM_NR - rlc_bearer.rlc_cfg.set_am(); - rlc_bearer.rlc_cfg.am().ul_am_rlc.sn_field_len_present = true; - rlc_bearer.rlc_cfg.am().ul_am_rlc.sn_field_len = sn_field_len_am_opts::size12; - rlc_bearer.rlc_cfg.am().dl_am_rlc.sn_field_len_present = true; - rlc_bearer.rlc_cfg.am().dl_am_rlc.sn_field_len = sn_field_len_am_opts::size12; - - rlc_bearer.rlc_cfg.am().ul_am_rlc.t_poll_retx = t_poll_retx_opts::ms500; - rlc_bearer.rlc_cfg.am().ul_am_rlc.poll_pdu = poll_pdu_opts::p8; - rlc_bearer.rlc_cfg.am().ul_am_rlc.poll_byte = poll_byte_opts::infinity; - rlc_bearer.rlc_cfg.am().ul_am_rlc.max_retx_thres = ul_am_rlc_s::max_retx_thres_opts::t8; - rlc_bearer.rlc_cfg.am().dl_am_rlc.t_reassembly = t_reassembly_opts::ms50; - rlc_bearer.rlc_cfg.am().dl_am_rlc.t_status_prohibit = t_status_prohibit_opts::ms50; -#else - rlc_bearer.rlc_cfg.set_um_bi_dir(); - rlc_bearer.rlc_cfg.um_bi_dir().ul_um_rlc.sn_field_len_present = true; - rlc_bearer.rlc_cfg.um_bi_dir().ul_um_rlc.sn_field_len = sn_field_len_um_opts::size12; - rlc_bearer.rlc_cfg.um_bi_dir().dl_um_rlc.sn_field_len_present = true; - rlc_bearer.rlc_cfg.um_bi_dir().dl_um_rlc.sn_field_len = sn_field_len_um_opts::size12; - rlc_bearer.rlc_cfg.um_bi_dir().dl_um_rlc.t_reassembly = t_reassembly_opts::ms50; -#endif - - // MAC logical channel config - rlc_bearer.mac_lc_ch_cfg_present = true; - rlc_bearer.mac_lc_ch_cfg.ul_specific_params_present = true; - rlc_bearer.mac_lc_ch_cfg.ul_specific_params.prio = 11; - rlc_bearer.mac_lc_ch_cfg.ul_specific_params.prioritised_bit_rate = - asn1::rrc_nr::lc_ch_cfg_s::ul_specific_params_s_::prioritised_bit_rate_opts::kbps0; - rlc_bearer.mac_lc_ch_cfg.ul_specific_params.bucket_size_dur = - asn1::rrc_nr::lc_ch_cfg_s::ul_specific_params_s_::bucket_size_dur_opts::ms100; - rlc_bearer.mac_lc_ch_cfg.ul_specific_params.lc_ch_group_present = true; - rlc_bearer.mac_lc_ch_cfg.ul_specific_params.lc_ch_group = 6; - rlc_bearer.mac_lc_ch_cfg.ul_specific_params.sched_request_id_present = true; - rlc_bearer.mac_lc_ch_cfg.ul_specific_params.sched_request_id = 0; - - return SRSRAN_SUCCESS; -} - int rrc_nr::ue::pack_secondary_cell_group_mac_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) { // mac-CellGroup-Config for BSR and SR @@ -624,7 +575,6 @@ int rrc_nr::ue::pack_secondary_cell_group_cfg(asn1::dyn_octstring& packed_second { auto& cell_group_cfg_pack = cell_group_cfg; - pack_secondary_cell_group_rlc_cfg(cell_group_cfg_pack); pack_secondary_cell_group_mac_cfg(cell_group_cfg_pack); pack_secondary_cell_group_sp_cell_cfg(cell_group_cfg_pack); @@ -700,7 +650,7 @@ int rrc_nr::ue::pack_nr_radio_bearer_config(asn1::dyn_octstring& packed_nr_beare int rrc_nr::ue::handle_sgnb_addition_request(uint16_t eutra_rnti_, const sgnb_addition_req_params_t& req_params) { // Add DRB1 to RLC and PDCP - if (add_drb() != SRSRAN_SUCCESS) { + if (add_drb(req_params.five_qi) != SRSRAN_SUCCESS) { parent->logger.error("Failed to configure DRB"); parent->rrc_eutra->sgnb_addition_reject(eutra_rnti_); return SRSRAN_ERROR; @@ -773,10 +723,16 @@ void rrc_nr::ue::crnti_ce_received() * The function sets and configures all relavant fields for the DRB configuration (MAC, RLC, PDCP) in the * cellGroupConfig and also adds the bearer to the local RLC and PDCP entities. * + * @param int 5QI of the DRB to be added * @return int SRSRAN_SUCCESS on success */ -int rrc_nr::ue::add_drb() +int rrc_nr::ue::add_drb(uint32_t five_qi) { + if (parent->cfg.five_qi_cfg.find(five_qi) == parent->cfg.five_qi_cfg.end()) { + parent->logger.error("No bearer config for 5QI %d present. Aborting DRB addition.", five_qi); + return SRSRAN_ERROR; + } + // RLC for DRB1 (with fixed LCID) inside cell_group_cfg auto& cell_group_cfg_pack = cell_group_cfg; @@ -787,21 +743,7 @@ int rrc_nr::ue::add_drb() rlc_bearer.served_radio_bearer.set_drb_id(); rlc_bearer.served_radio_bearer.drb_id() = 1; rlc_bearer.rlc_cfg_present = true; - -#ifdef USE_RLC_AM_NR - rlc_bearer.rlc_cfg.set_am(); - rlc_bearer.rlc_cfg.am().ul_am_rlc.sn_field_len_present = true; - rlc_bearer.rlc_cfg.am().ul_am_rlc.sn_field_len = sn_field_len_am_opts::size12; - rlc_bearer.rlc_cfg.am().dl_am_rlc.sn_field_len_present = true; - rlc_bearer.rlc_cfg.am().dl_am_rlc.sn_field_len = sn_field_len_am_opts::size12; -#else - rlc_bearer.rlc_cfg.set_um_bi_dir(); - rlc_bearer.rlc_cfg.um_bi_dir().ul_um_rlc.sn_field_len_present = true; - rlc_bearer.rlc_cfg.um_bi_dir().ul_um_rlc.sn_field_len = sn_field_len_um_opts::size12; - rlc_bearer.rlc_cfg.um_bi_dir().dl_um_rlc.sn_field_len_present = true; - rlc_bearer.rlc_cfg.um_bi_dir().dl_um_rlc.sn_field_len = sn_field_len_um_opts::size12; - rlc_bearer.rlc_cfg.um_bi_dir().dl_um_rlc.t_reassembly = t_reassembly_opts::ms50; -#endif + rlc_bearer.rlc_cfg = parent->cfg.five_qi_cfg[five_qi].rlc_cfg; // add RLC bearer srsran::rlc_config_t rlc_cfg; @@ -838,17 +780,7 @@ int rrc_nr::ue::add_drb() drb_item.cn_assoc_present = true; drb_item.cn_assoc.set_eps_bearer_id() = 5; drb_item.pdcp_cfg_present = true; - drb_item.pdcp_cfg.ciphering_disabled_present = true; - drb_item.pdcp_cfg.drb_present = true; - drb_item.pdcp_cfg.drb.pdcp_sn_size_dl_present = true; - drb_item.pdcp_cfg.drb.pdcp_sn_size_dl = asn1::rrc_nr::pdcp_cfg_s::drb_s_::pdcp_sn_size_dl_opts::len18bits; - drb_item.pdcp_cfg.drb.pdcp_sn_size_ul_present = true; - drb_item.pdcp_cfg.drb.pdcp_sn_size_ul = asn1::rrc_nr::pdcp_cfg_s::drb_s_::pdcp_sn_size_ul_opts::len18bits; - drb_item.pdcp_cfg.drb.discard_timer_present = true; - drb_item.pdcp_cfg.drb.discard_timer = asn1::rrc_nr::pdcp_cfg_s::drb_s_::discard_timer_opts::ms100; - drb_item.pdcp_cfg.drb.hdr_compress.set_not_used(); - drb_item.pdcp_cfg.t_reordering_present = true; - drb_item.pdcp_cfg.t_reordering = asn1::rrc_nr::pdcp_cfg_s::t_reordering_opts::ms0; + drb_item.pdcp_cfg = parent->cfg.five_qi_cfg[five_qi].pdcp_cfg; // Add DRB1 to PDCP srsran::pdcp_config_t pdcp_cnfg = srsran::make_drb_pdcp_config_t(drb_item.drb_id, false, drb_item.pdcp_cfg); From 225a7741b3bfcb45c0cf17518cec44f92711cd5c Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Thu, 14 Apr 2022 15:59:58 +0200 Subject: [PATCH 162/195] pssch_ue: fix uninitialized variables --- lib/examples/pssch_ue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/examples/pssch_ue.c b/lib/examples/pssch_ue.c index 1b5b60b24..e72e24a01 100644 --- a/lib/examples/pssch_ue.c +++ b/lib/examples/pssch_ue.c @@ -294,7 +294,7 @@ int main(int argc, char** argv) cf_t* equalized_sf_buffer = srsran_vec_malloc(sizeof(cf_t) * sf_n_re); // RX - srsran_ofdm_t fft[SRSRAN_MAX_PORTS]; + srsran_ofdm_t fft[SRSRAN_MAX_PORTS] = {}; srsran_ofdm_cfg_t ofdm_cfg = {}; ofdm_cfg.nof_prb = cell_sl.nof_prb; ofdm_cfg.cp = SRSRAN_CP_NORM; From 8505523928932309cf2ce5056e859b9ae1c74a7c Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Thu, 14 Apr 2022 16:00:34 +0200 Subject: [PATCH 163/195] ue,ue_sync: fix CP setting for find and track objects move setting of CP for find and track objects further down after the respective objects have been resized and updated there frame_len, fft_size, etc. members. Doing this too early cause a segfault with div by zero because the values were all zero --- lib/src/phy/ue/ue_sync.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/src/phy/ue/ue_sync.c b/lib/src/phy/ue/ue_sync.c index cd5e38fe2..71a3d2b34 100644 --- a/lib/src/phy/ue/ue_sync.c +++ b/lib/src/phy/ue/ue_sync.c @@ -330,8 +330,7 @@ int srsran_ue_sync_set_cell(srsran_ue_sync_t* q, srsran_cell_t cell) q->cell = cell; q->fft_size = srsran_symbol_sz(q->cell.nof_prb); q->sf_len = SRSRAN_SF_LEN(q->fft_size); - srsran_sync_set_cp(&q->sfind, q->cell.cp); - srsran_sync_set_cp(&q->strack, q->cell.cp); + if (cell.id == 1000) { /* If the cell is unkown, we search PSS/SSS in 5 ms */ q->nof_recv_sf = 5; @@ -367,6 +366,10 @@ int srsran_ue_sync_set_cell(srsran_ue_sync_t* q, srsran_cell_t cell) } } + // Set CP for find and track objects + srsran_sync_set_cp(&q->sfind, cell.cp); + srsran_sync_set_cp(&q->strack, cell.cp); + // When Cell ID is 1000, ue_sync receives nof_avg_find_frames frames in find state and does not go to tracking // state and is used to search a cell if (cell.id == 1000) { @@ -384,9 +387,6 @@ int srsran_ue_sync_set_cell(srsran_ue_sync_t* q, srsran_cell_t cell) srsran_sync_set_cfo_ema_alpha(&q->strack, 0.1); } else { - q->sfind.cp = cell.cp; - q->strack.cp = cell.cp; - srsran_sync_set_frame_type(&q->sfind, cell.frame_type); srsran_sync_set_frame_type(&q->strack, cell.frame_type); From 47749350f01b17b565a9815c571dbcca95ff3afb Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Fri, 22 Apr 2022 10:12:55 +0100 Subject: [PATCH 164/195] gnb,ngap: make sure we log the received 5QI --- srsgnb/src/stack/ngap/ngap_ue_proc.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/srsgnb/src/stack/ngap/ngap_ue_proc.cc b/srsgnb/src/stack/ngap/ngap_ue_proc.cc index a18d4fd61..2efd60d7d 100644 --- a/srsgnb/src/stack/ngap/ngap_ue_proc.cc +++ b/srsgnb/src/stack/ngap/ngap_ue_proc.cc @@ -86,16 +86,16 @@ proc_outcome_t ngap_ue_pdu_session_res_setup_proc::init(const asn1::ngap::pdu_se return proc_outcome_t::error; } - logger.info("Added PDU Session with LCID %d, teid_out %d, teid_in %d, addr_in %s", - lcid, - teid_out, - teid_in, - addr_in.to_string()); - uint16_t five_qi = pdu_ses_res_setup_req_trans->qos_flow_setup_request_list.value[0] .qos_flow_level_qos_params.qos_characteristics.non_dynamic5_qi() .five_qi; + logger.info("Added PDU Session with LCID %d, 5QI %d, teid_out %d, teid_in %d, addr_in %s", + lcid, + five_qi, + teid_out, + teid_in, + addr_in.to_string()); // QoS parameter mapping in config in LTE enb if (su_req.pdu_session_nas_pdu.size() > 0) { if (rrc->establish_rrc_bearer(ue_ctxt->rnti, su_req.pdu_session_id, su_req.pdu_session_nas_pdu, lcid, five_qi) == From 10c921573f4705c472d2eb639a3a0605ac7da859 Mon Sep 17 00:00:00 2001 From: Fabian Eckermann Date: Fri, 22 Apr 2022 16:19:15 +0200 Subject: [PATCH 165/195] Revert "Add NSSAI slice differentiator, currently only 0 supported" This reverts commit 66ed8b9065d4946ff81e9bd2f9b4a577335bb591. --- srsue/src/stack/upper/nas_5g.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/srsue/src/stack/upper/nas_5g.cc b/srsue/src/stack/upper/nas_5g.cc index 01c9fc668..d88d44b7f 100644 --- a/srsue/src/stack/upper/nas_5g.cc +++ b/srsue/src/stack/upper/nas_5g.cc @@ -576,9 +576,8 @@ int nas_5g::send_pdu_session_establishment_request(uint32_t tran ul_nas_msg.request_type.request_type_value = request_type_t::Request_type_value_type_::options::initial_request; ul_nas_msg.s_nssai_present = true; - ul_nas_msg.s_nssai.type = s_nssai_t::SST_type_::options::sst_and_sd; + ul_nas_msg.s_nssai.type = s_nssai_t::SST_type_::options::sst; ul_nas_msg.s_nssai.sst = 1; - ul_nas_msg.s_nssai.sd = 0; ul_nas_msg.dnn_present = true; ul_nas_msg.dnn.dnn_value.resize(pdu_session_cfg.apn_name.size() + 1); From 63877ba209ae43ffdf9702e94c06850f91d5b4eb Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Fri, 22 Apr 2022 13:15:46 +0100 Subject: [PATCH 166/195] lib,rlc_am_nr: make sure that tx_buffer_queue size is configurable --- lib/include/srsran/rlc/rlc_am_nr.h | 5 +++-- lib/src/rlc/rlc_am_base.cc | 10 ++++++---- lib/src/rlc/rlc_am_nr.cc | 19 +++++++++++++++++-- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_am_nr.h b/lib/include/srsran/rlc/rlc_am_nr.h index 91ce60dea..af1988f95 100644 --- a/lib/include/srsran/rlc/rlc_am_nr.h +++ b/lib/include/srsran/rlc/rlc_am_nr.h @@ -101,6 +101,7 @@ public: int write_sdu(unique_byte_buffer_t sdu); void empty_queue() final; + void empty_queue_no_lock(); // Data PDU helpers uint32_t build_new_pdu(uint8_t* payload, uint32_t nof_bytes); @@ -151,8 +152,8 @@ private: // Queues, buffers and container std::unique_ptr > retx_queue; - uint32_t sdu_under_segmentation_sn = INVALID_RLC_SN; // SN of the SDU currently being segmented. - pdcp_sn_vector_t notify_info_vec; + uint32_t sdu_under_segmentation_sn = INVALID_RLC_SN; // SN of the SDU currently being segmented. + pdcp_sn_vector_t notify_info_vec; // Helper constants uint32_t min_hdr_size = 2; diff --git a/lib/src/rlc/rlc_am_base.cc b/lib/src/rlc/rlc_am_base.cc index 3310905df..049998e2b 100644 --- a/lib/src/rlc/rlc_am_base.cc +++ b/lib/src/rlc/rlc_am_base.cc @@ -79,17 +79,18 @@ bool rlc_am::configure(const rlc_config_t& cfg_) if (cfg.rat == srsran_rat_t::lte) { RlcInfo("AM LTE configured - t_poll_retx=%d, poll_pdu=%d, poll_byte=%d, max_retx_thresh=%d, " - "t_reordering=%d, t_status_prohibit=%d", + "t_reordering=%d, t_status_prohibit=%d, tx_queue_length=%d", cfg.am.t_poll_retx, cfg.am.poll_pdu, cfg.am.poll_byte, cfg.am.max_retx_thresh, cfg.am.t_reordering, - cfg.am.t_status_prohibit); + cfg.am.t_status_prohibit, + cfg.tx_queue_length); } else if (cfg.rat == srsran_rat_t::nr) { RlcInfo("AM NR configured - tx_sn_field_length=%d, rx_sn_field_length=%d, " "t_poll_retx=%d, poll_pdu=%d, poll_byte=%d, " - "max_retx_thresh=%d, t_reassembly=%d, t_status_prohibit=%d", + "max_retx_thresh=%d, t_reassembly=%d, t_status_prohibit=%di, tx_queue_length=%d", to_number(cfg.am_nr.tx_sn_field_length), to_number(cfg.am_nr.rx_sn_field_length), cfg.am_nr.t_poll_retx, @@ -97,7 +98,8 @@ bool rlc_am::configure(const rlc_config_t& cfg_) cfg.am_nr.poll_byte, cfg.am_nr.max_retx_thresh, cfg.am_nr.t_reassembly, - cfg.am_nr.t_status_prohibit); + cfg.am_nr.t_status_prohibit, + cfg.tx_queue_length); } else { RlcError("Invalid RAT at entity configuration"); } diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 8f9ffa1e2..f496c6032 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -69,6 +69,10 @@ bool rlc_am_nr_tx::configure(const rlc_config_t& cfg_) max_hdr_size = min_hdr_size + so_size; + // make sure Tx queue is empty before attempting to resize + empty_queue_no_lock(); + tx_sdu_queue.resize(cfg_.tx_queue_length); + tx_enabled = true; RlcDebug("RLC AM NR configured tx entity."); @@ -725,7 +729,7 @@ uint32_t rlc_am_nr_tx::build_status_pdu(byte_buffer_t* payload, uint32_t nof_byt { RlcInfo("generating status PDU. Bytes available:%d", nof_bytes); rlc_am_nr_status_pdu_t status(cfg.rx_sn_field_length); // carries status of RX entity, hence use SN length of RX - int pdu_len = rx->get_status_pdu(&status, nof_bytes); + int pdu_len = rx->get_status_pdu(&status, nof_bytes); if (pdu_len == SRSRAN_ERROR) { RlcDebug("deferred status PDU. Cause: Failed to acquire rx lock"); pdu_len = 0; @@ -1063,8 +1067,19 @@ bool rlc_am_nr_tx::sdu_queue_is_full() return false; } -void rlc_am_nr_tx::empty_queue() {} +void rlc_am_nr_tx::empty_queue() +{ + std::lock_guard lock(mutex); + empty_queue_no_lock(); +} +void rlc_am_nr_tx::empty_queue_no_lock() +{ + // deallocate all SDUs in transmit queue + while (tx_sdu_queue.size() > 0) { + unique_byte_buffer_t buf = tx_sdu_queue.read(); + } +} void rlc_am_nr_tx::stop() {} /* From 8a27cdf45ae4cb979d1dfcf52771ed77ae5607db Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Fri, 22 Apr 2022 15:58:35 +0100 Subject: [PATCH 167/195] lib,rlc_am_nr: fix incorrectly stopping the t-StatusProhibit --- lib/src/rlc/rlc_am_nr.cc | 1 - lib/test/rlc/rlc_am_nr_test.cc | 47 ++++++++++++++++++++++++++-------- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index f496c6032..5fa34da3d 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -1207,7 +1207,6 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) if (header.p) { RlcInfo("status packet requested through polling bit"); do_status = true; - status_prohibit_timer.stop(); } debug_state(); diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index fd816dcb8..db83c5310 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -410,10 +410,18 @@ int lost_pdu_test(rlc_am_nr_sn_size_t sn_size) retx_buf.N_bytes = len; TESTASSERT_EQ(data_pdu_size, len); + // Polling bit on the RETX should be required, as the buffers are not empty. rlc2.write_pdu(retx_buf.msg, retx_buf.N_bytes); - TESTASSERT_EQ(3, rlc2.get_buffer_state()); // Status report shoud be required, as the TX buffers are now empty. + TESTASSERT_EQ(0, rlc2.get_buffer_state()); // t-StatusProhibit is still running } + + // Step timers until t-StatusProhibit expires + for (int cnt = 0; cnt < 8; cnt++) { + timers.step_all(); + } + TESTASSERT_EQ(3, rlc2.get_buffer_state()); // t-StatusProhibit no longer running + { // Double check status report byte_buffer_t status_buf; @@ -425,7 +433,7 @@ int lost_pdu_test(rlc_am_nr_sn_size_t sn_size) // Assert status is correct rlc_am_nr_status_pdu_t status_check(sn_size); rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); - TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. + TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. TESTASSERT_EQ(0, status_check.nacks.size()); // All PDUs are acked now } @@ -588,8 +596,15 @@ int lost_pdu_duplicated_nack_test(rlc_am_nr_sn_size_t sn_size) rlc2.write_pdu(retx_buf.msg, retx_buf.N_bytes); - TESTASSERT_EQ(3, rlc2.get_buffer_state()); // Status report shoud be required, as the TX buffers are now empty. + TESTASSERT_EQ(0, rlc2.get_buffer_state()); // Status report shoud be required, as the TX buffers are now empty. + } + + // Step timers until t-StatusProhibit expires + for (int cnt = 0; cnt < 8; cnt++) { + timers.step_all(); } + TESTASSERT_EQ(3, rlc2.get_buffer_state()); // t-StatusProhibit no longer running + { // Double check status report byte_buffer_t status_buf; @@ -765,8 +780,15 @@ int lost_pdus_trimmed_nack_test(rlc_am_nr_sn_size_t sn_size) rlc2.write_pdu(retx_buf.msg, retx_buf.N_bytes); expected_size = status_pdu_ack_size + 1 * status_pdu_nack_size; - TESTASSERT_EQ(expected_size, rlc2.get_buffer_state()); // Status report should now include the chopped NACK + TESTASSERT_EQ(0, rlc2.get_buffer_state()); // Status report should now include the chopped NACK + } + + // Step timers until t-StatusProhibit expires + for (int cnt = 0; cnt < 8; cnt++) { + timers.step_all(); } + TESTASSERT_EQ(expected_size, rlc2.get_buffer_state()); // t-StatusProhibit no longer running + { // Double check status report byte_buffer_t status_buf; @@ -801,8 +823,13 @@ int lost_pdus_trimmed_nack_test(rlc_am_nr_sn_size_t sn_size) rlc2.write_pdu(retx_buf.msg, retx_buf.N_bytes); expected_size = status_pdu_ack_size; - TESTASSERT_EQ(expected_size, rlc2.get_buffer_state()); // Status report should have no NACKs + TESTASSERT_EQ(0, rlc2.get_buffer_state()); // Status report should have no NACKs + } + // Step timers until t-StatusProhibit expires + for (int cnt = 0; cnt < 8; cnt++) { + timers.step_all(); } + TESTASSERT_EQ(expected_size, rlc2.get_buffer_state()); // t-StatusProhibit no longer running { // Double check status report byte_buffer_t status_buf; @@ -1762,9 +1789,9 @@ int max_retx_lost_sdu_test(rlc_am_nr_sn_size_t sn_size) // Fake status PDU that ack SN=1 and nack SN=0 rlc_am_nr_status_pdu_t fake_status(sn_size); - fake_status.ack_sn = 2; // delivered up to SN=1 - rlc_status_nack_t nack; // one SN was lost - nack.nack_sn = 0; // it was SN=0 that was lost + fake_status.ack_sn = 2; // delivered up to SN=1 + rlc_status_nack_t nack; // one SN was lost + nack.nack_sn = 0; // it was SN=0 that was lost fake_status.push_nack(nack); // pack into PDU @@ -2185,7 +2212,7 @@ int poll_retx() unique_byte_buffer_t status_pdu = srsran::make_byte_buffer(); TESTASSERT(status_pdu != nullptr); rlc_am_nr_status_pdu_t status(rlc_am_nr_sn_size_t::size12bits); - status.ack_sn = 2; + status.ack_sn = 2; { rlc_status_nack_t nack; nack.nack_sn = 1; // SN=1 needs RETX @@ -2216,7 +2243,7 @@ int poll_retx() unique_byte_buffer_t status_pdu = srsran::make_byte_buffer(); TESTASSERT(status_pdu != nullptr); rlc_am_nr_status_pdu_t status(rlc_am_nr_sn_size_t::size12bits); - status.ack_sn = 4; + status.ack_sn = 4; { rlc_status_nack_t nack; nack.nack_sn = 1; // SN=1 needs RETX From 647882d03e9f0ef1a8d439567accec743d42af50 Mon Sep 17 00:00:00 2001 From: Bedran Karakoc Date: Fri, 17 Dec 2021 01:42:00 +0100 Subject: [PATCH 168/195] nr,gnb,rrc: Add initial UE capability transfer --- srsgnb/hdr/stack/rrc/rrc_nr_ue.h | 6 +++++ srsgnb/src/stack/rrc/rrc_nr.cc | 3 +++ srsgnb/src/stack/rrc/rrc_nr_ue.cc | 45 ++++++++++++++++++++++++++++++- 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/srsgnb/hdr/stack/rrc/rrc_nr_ue.h b/srsgnb/hdr/stack/rrc/rrc_nr_ue.h index 502a52f88..450226888 100644 --- a/srsgnb/hdr/stack/rrc/rrc_nr_ue.h +++ b/srsgnb/hdr/stack/rrc/rrc_nr_ue.h @@ -69,6 +69,9 @@ public: void handle_rrc_reestablishment_request(const asn1::rrc_nr::rrc_reest_request_s& msg); void handle_rrc_reestablishment_complete(const asn1::rrc_nr::rrc_reest_complete_s& msg); + /* TS 38.331 - 5.6.1 UE capability transfer */ + void handle_ue_capability_information(const asn1::rrc_nr::ue_cap_info_s& msg); + /** TS 38.331 - 5.3.8 Connection Release */ void send_rrc_release(); @@ -87,6 +90,9 @@ public: /* TS 38.331 - 5.3.5 RRC reconfiguration */ void send_rrc_reconfiguration(); + /* TS 38.331 - 5.6.1 UE capability transfer */ + void send_ue_capability_enquiry(); + private: int send_dl_ccch(const asn1::rrc_nr::dl_ccch_msg_s& dl_ccch_msg); int send_dl_dcch(srsran::nr_srb srb, const asn1::rrc_nr::dl_dcch_msg_s& dl_dcch_msg); diff --git a/srsgnb/src/stack/rrc/rrc_nr.cc b/srsgnb/src/stack/rrc/rrc_nr.cc index 2b04b7857..8b20dd4c6 100644 --- a/srsgnb/src/stack/rrc/rrc_nr.cc +++ b/srsgnb/src/stack/rrc/rrc_nr.cc @@ -554,6 +554,9 @@ void rrc_nr::handle_ul_dcch(uint16_t rnti, uint32_t lcid, srsran::const_byte_spa case ul_dcch_msg_type_c::c1_c_::types_opts::rrc_reest_complete: u.handle_rrc_reestablishment_complete(ul_dcch_msg.msg.c1().rrc_reest_complete()); break; + case ul_dcch_msg_type_c::c1_c_::types_opts::ue_cap_info: + u.handle_ue_capability_information(ul_dcch_msg.msg.c1().ue_cap_info()); + break; default: log_rx_pdu_fail(rnti, srb_to_lcid(lte_srb::srb0), pdu, "Unsupported UL-CCCH message type", false); // TODO Remove user diff --git a/srsgnb/src/stack/rrc/rrc_nr_ue.cc b/srsgnb/src/stack/rrc/rrc_nr_ue.cc index a5e14e743..8a99120c5 100644 --- a/srsgnb/src/stack/rrc/rrc_nr_ue.cc +++ b/srsgnb/src/stack/rrc/rrc_nr_ue.cc @@ -1116,8 +1116,8 @@ void rrc_nr::ue::handle_security_mode_complete(const asn1::rrc_nr::security_mode // finally, also enable ciphering on SRB1 update_as_security(srb_to_lcid(srsran::nr_srb::srb1), false, true); + send_ue_capability_enquiry(); send_rrc_reconfiguration(); - // Note: Skip UE capabilities // Send RRCReconfiguration if necessary if (not nas_pdu_queue.empty()) { @@ -1189,6 +1189,49 @@ void rrc_nr::ue::send_rrc_reconfiguration() } } +void rrc_nr::ue::send_ue_capability_enquiry() +{ + dl_dcch_msg_s dl_dcch_msg; + dl_dcch_msg.msg.set_c1().set_ue_cap_enquiry().rrc_transaction_id = (uint8_t)((transaction_id++) % 4); + ue_cap_enquiry_ies_s& ies = dl_dcch_msg.msg.c1().ue_cap_enquiry().crit_exts.set_ue_cap_enquiry(); + + // ue-CapabilityRAT-RequestList + ue_cap_rat_request_s cap_rat_request; + cap_rat_request.rat_type.value = rat_type_opts::nr; + cap_rat_request.cap_request_filt_present = true; + + // capabilityRequestFilter + ue_cap_request_filt_nr_s request_filter; + + // frequencyBandListFilter + request_filter.freq_band_list_filt_present = true; + freq_band_info_c freq_band_info; + freq_band_info_nr_s& freq_band_info_nr = freq_band_info.set_band_info_nr(); + + // Iterate through cell list and assign bandInformationNR items + for (auto& iter : parent->cfg.cell_list) { + freq_band_info_nr.band_nr = iter.band; + request_filter.freq_band_list_filt.push_back(freq_band_info); + } + + // Pack capabilityRequestFilter + cap_rat_request.cap_request_filt.resize(128); + asn1::bit_ref bref_pack(cap_rat_request.cap_request_filt.data(), cap_rat_request.cap_request_filt.size()); + if (request_filter.pack(bref_pack) != asn1::SRSASN_SUCCESS) { + logger.error("Failed to pack capabilityRequestFilter in UE Capability Enquiry"); + } + cap_rat_request.cap_request_filt.resize(bref_pack.distance_bytes()); + + ies.ue_cap_rat_request_list.push_back(cap_rat_request); + + send_dl_dcch(srsran::nr_srb::srb1, dl_dcch_msg); +} + +void rrc_nr::ue::handle_ue_capability_information(const asn1::rrc_nr::ue_cap_info_s& msg) +{ + logger.info("UECapabilityInformation transaction ID: %d", msg.rrc_transaction_id); +} + void rrc_nr::ue::handle_rrc_reconfiguration_complete(const asn1::rrc_nr::rrc_recfg_complete_s& msg) { update_mac(next_cell_group_cfg, true); From d769192463e7b7704d428caf32913602ac702a33 Mon Sep 17 00:00:00 2001 From: Bedran Karakoc Date: Thu, 13 Jan 2022 14:04:46 +0100 Subject: [PATCH 169/195] gnb,rrc: Wait for UE Capability Information before sending RRC Reconfiguration --- srsgnb/src/stack/rrc/rrc_nr_ue.cc | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/srsgnb/src/stack/rrc/rrc_nr_ue.cc b/srsgnb/src/stack/rrc/rrc_nr_ue.cc index 8a99120c5..3d5b88835 100644 --- a/srsgnb/src/stack/rrc/rrc_nr_ue.cc +++ b/srsgnb/src/stack/rrc/rrc_nr_ue.cc @@ -1117,12 +1117,6 @@ void rrc_nr::ue::handle_security_mode_complete(const asn1::rrc_nr::security_mode update_as_security(srb_to_lcid(srsran::nr_srb::srb1), false, true); send_ue_capability_enquiry(); - send_rrc_reconfiguration(); - - // Send RRCReconfiguration if necessary - if (not nas_pdu_queue.empty()) { - send_rrc_reconfiguration(); - } } /// TS 38.331, RRCReconfiguration @@ -1230,6 +1224,13 @@ void rrc_nr::ue::send_ue_capability_enquiry() void rrc_nr::ue::handle_ue_capability_information(const asn1::rrc_nr::ue_cap_info_s& msg) { logger.info("UECapabilityInformation transaction ID: %d", msg.rrc_transaction_id); + + send_rrc_reconfiguration(); + + // Send RRCReconfiguration if necessary + if (not nas_pdu_queue.empty()) { + send_rrc_reconfiguration(); + } } void rrc_nr::ue::handle_rrc_reconfiguration_complete(const asn1::rrc_nr::rrc_recfg_complete_s& msg) From 29bd06f58a8cd567fb9cfe043976f08c0f9cf455 Mon Sep 17 00:00:00 2001 From: Bedran Karakoc Date: Mon, 17 Jan 2022 15:18:51 +0100 Subject: [PATCH 170/195] gnb,rrc: Added test for UE Capability Exchange procedure --- srsgnb/hdr/stack/rrc/rrc_nr_ue.h | 8 ++--- srsgnb/src/stack/rrc/rrc_nr_ue.cc | 9 ++--- srsgnb/src/stack/rrc/test/rrc_nr_test.cc | 1 + .../src/stack/rrc/test/rrc_nr_test_helpers.cc | 35 +++++++++++++++++++ .../src/stack/rrc/test/rrc_nr_test_helpers.h | 5 +++ 5 files changed, 50 insertions(+), 8 deletions(-) diff --git a/srsgnb/hdr/stack/rrc/rrc_nr_ue.h b/srsgnb/hdr/stack/rrc/rrc_nr_ue.h index 450226888..87bf9a8da 100644 --- a/srsgnb/hdr/stack/rrc/rrc_nr_ue.h +++ b/srsgnb/hdr/stack/rrc/rrc_nr_ue.h @@ -69,12 +69,12 @@ public: void handle_rrc_reestablishment_request(const asn1::rrc_nr::rrc_reest_request_s& msg); void handle_rrc_reestablishment_complete(const asn1::rrc_nr::rrc_reest_complete_s& msg); - /* TS 38.331 - 5.6.1 UE capability transfer */ - void handle_ue_capability_information(const asn1::rrc_nr::ue_cap_info_s& msg); - /** TS 38.331 - 5.3.8 Connection Release */ void send_rrc_release(); + /* TS 38.331 - 5.6.1 UE capability transfer */ + void handle_ue_capability_information(const asn1::rrc_nr::ue_cap_info_s& msg); + /** TS 38.331 - 5.7.1 DL information transfer */ void send_dl_information_transfer(srsran::unique_byte_buffer_t sdu); @@ -91,7 +91,7 @@ public: void send_rrc_reconfiguration(); /* TS 38.331 - 5.6.1 UE capability transfer */ - void send_ue_capability_enquiry(); + int send_ue_capability_enquiry(); private: int send_dl_ccch(const asn1::rrc_nr::dl_ccch_msg_s& dl_ccch_msg); diff --git a/srsgnb/src/stack/rrc/rrc_nr_ue.cc b/srsgnb/src/stack/rrc/rrc_nr_ue.cc index 3d5b88835..75c03c2bc 100644 --- a/srsgnb/src/stack/rrc/rrc_nr_ue.cc +++ b/srsgnb/src/stack/rrc/rrc_nr_ue.cc @@ -1183,7 +1183,7 @@ void rrc_nr::ue::send_rrc_reconfiguration() } } -void rrc_nr::ue::send_ue_capability_enquiry() +int rrc_nr::ue::send_ue_capability_enquiry() { dl_dcch_msg_s dl_dcch_msg; dl_dcch_msg.msg.set_c1().set_ue_cap_enquiry().rrc_transaction_id = (uint8_t)((transaction_id++) % 4); @@ -1191,14 +1191,12 @@ void rrc_nr::ue::send_ue_capability_enquiry() // ue-CapabilityRAT-RequestList ue_cap_rat_request_s cap_rat_request; - cap_rat_request.rat_type.value = rat_type_opts::nr; - cap_rat_request.cap_request_filt_present = true; + cap_rat_request.rat_type.value = rat_type_opts::nr; // capabilityRequestFilter ue_cap_request_filt_nr_s request_filter; // frequencyBandListFilter - request_filter.freq_band_list_filt_present = true; freq_band_info_c freq_band_info; freq_band_info_nr_s& freq_band_info_nr = freq_band_info.set_band_info_nr(); @@ -1213,12 +1211,15 @@ void rrc_nr::ue::send_ue_capability_enquiry() asn1::bit_ref bref_pack(cap_rat_request.cap_request_filt.data(), cap_rat_request.cap_request_filt.size()); if (request_filter.pack(bref_pack) != asn1::SRSASN_SUCCESS) { logger.error("Failed to pack capabilityRequestFilter in UE Capability Enquiry"); + return SRSRAN_ERROR; } cap_rat_request.cap_request_filt.resize(bref_pack.distance_bytes()); ies.ue_cap_rat_request_list.push_back(cap_rat_request); send_dl_dcch(srsran::nr_srb::srb1, dl_dcch_msg); + + return SRSRAN_SUCCESS; } void rrc_nr::ue::handle_ue_capability_information(const asn1::rrc_nr::ue_cap_info_s& msg) diff --git a/srsgnb/src/stack/rrc/test/rrc_nr_test.cc b/srsgnb/src/stack/rrc/test/rrc_nr_test.cc index 259802dbb..ea4521d4a 100644 --- a/srsgnb/src/stack/rrc/test/rrc_nr_test.cc +++ b/srsgnb/src/stack/rrc/test/rrc_nr_test.cc @@ -180,6 +180,7 @@ void test_rrc_sa_connection() test_rrc_nr_connection_establishment(task_sched, rrc_obj, rlc_obj, mac_obj, ngap_obj, 0x4601); test_rrc_nr_info_transfer(task_sched, rrc_obj, pdcp_obj, ngap_obj, 0x4601); test_rrc_nr_security_mode_cmd(task_sched, rrc_obj, pdcp_obj, 0x4601); + test_rrc_nr_ue_capability_enquiry(task_sched, rrc_obj, pdcp_obj, 0x4601); test_rrc_nr_reconfiguration(task_sched, rrc_obj, pdcp_obj, ngap_obj, 0x4601); test_rrc_nr_2nd_reconfiguration(task_sched, rrc_obj, pdcp_obj, ngap_obj, 0x4601); } diff --git a/srsgnb/src/stack/rrc/test/rrc_nr_test_helpers.cc b/srsgnb/src/stack/rrc/test/rrc_nr_test_helpers.cc index 954e24a9f..2af126e8a 100644 --- a/srsgnb/src/stack/rrc/test/rrc_nr_test_helpers.cc +++ b/srsgnb/src/stack/rrc/test/rrc_nr_test_helpers.cc @@ -248,6 +248,41 @@ void test_rrc_nr_security_mode_cmd(srsran::task_scheduler& task_sched, rrc_obj.write_pdu(rnti, 1, std::move(pdu)); } +void test_rrc_nr_ue_capability_enquiry(srsran::task_scheduler& task_sched, + rrc_nr& rrc_obj, + pdcp_nr_rrc_tester& pdcp, + uint16_t rnti) +{ + dl_dcch_msg_s dl_dcch_msg; + { + asn1::cbit_ref bref{pdcp.last_sdu->data(), pdcp.last_sdu->size()}; + TESTASSERT_SUCCESS(dl_dcch_msg.unpack(bref)); + } + + // Check if unpacked message is correct (ueCapabilityEnquiry | gNB -> UE) + TESTASSERT_EQ(dl_dcch_msg_type_c::types_opts::c1, dl_dcch_msg.msg.type().value); + TESTASSERT_EQ(dl_dcch_msg_type_c::c1_c_::types_opts::ue_cap_enquiry, dl_dcch_msg.msg.c1().type().value); + TESTASSERT_EQ(ue_cap_enquiry_s::crit_exts_c_::types_opts::ue_cap_enquiry, + dl_dcch_msg.msg.c1().ue_cap_enquiry().crit_exts.type().value); + + // Send response (ueCapabilityInformation | UE -> gNB) + ul_dcch_msg_s ul_dcch_msg; + auto& ue_capability_information = ul_dcch_msg.msg.set_c1().set_ue_cap_info(); + ue_capability_information.rrc_transaction_id = dl_dcch_msg.msg.c1().ue_cap_enquiry().rrc_transaction_id; + ue_capability_information.crit_exts.set_ue_cap_info(); + + srsran::unique_byte_buffer_t pdu; + { + pdu = srsran::make_byte_buffer(); + asn1::bit_ref bref{pdu->data(), pdu->get_tailroom()}; + TESTASSERT_SUCCESS(ul_dcch_msg.pack(bref)); + pdu->N_bytes = bref.distance_bytes(); + } + + // send message to RRC + rrc_obj.write_pdu(rnti, 1, std::move(pdu)); +} + void test_rrc_nr_reconfiguration(srsran::task_scheduler& task_sched, rrc_nr& rrc_obj, pdcp_nr_rrc_tester& pdcp, diff --git a/srsgnb/src/stack/rrc/test/rrc_nr_test_helpers.h b/srsgnb/src/stack/rrc/test/rrc_nr_test_helpers.h index e1032ac09..664ef587f 100644 --- a/srsgnb/src/stack/rrc/test/rrc_nr_test_helpers.h +++ b/srsgnb/src/stack/rrc/test/rrc_nr_test_helpers.h @@ -106,6 +106,11 @@ void test_rrc_nr_security_mode_cmd(srsran::task_scheduler& task_sched, pdcp_nr_rrc_tester& pdcp, uint16_t rnti); +void test_rrc_nr_ue_capability_enquiry(srsran::task_scheduler& task_sched, + rrc_nr& rrc_obj, + pdcp_nr_rrc_tester& pdcp, + uint16_t rnti); + void test_rrc_nr_reconfiguration(srsran::task_scheduler& task_sched, rrc_nr& rrc_obj, pdcp_nr_rrc_tester& pdcp, From 2cccad9d285fbcf2a90a6ff0ce1387190483ccbc Mon Sep 17 00:00:00 2001 From: Bedran Karakoc Date: Mon, 7 Feb 2022 15:54:35 +0100 Subject: [PATCH 171/195] ue,rrc_nr: Added initial UE Capability Information --- srsue/hdr/stack/rrc_nr/rrc_nr.h | 2 + srsue/src/stack/rrc_nr/rrc_nr.cc | 99 +++++++++++++++++++++++++++++++- 2 files changed, 99 insertions(+), 2 deletions(-) diff --git a/srsue/hdr/stack/rrc_nr/rrc_nr.h b/srsue/hdr/stack/rrc_nr/rrc_nr.h index 192a66877..40e39fcf1 100644 --- a/srsue/hdr/stack/rrc_nr/rrc_nr.h +++ b/srsue/hdr/stack/rrc_nr/rrc_nr.h @@ -138,6 +138,7 @@ private: void send_setup_request(srsran::nr_establishment_cause_t cause); void send_con_setup_complete(srsran::unique_byte_buffer_t nas_msg); void send_rrc_reconfig_complete(); + int send_ue_capability_info(const asn1::rrc_nr::ue_cap_enquiry_s& msg); void send_ul_info_transfer(srsran::unique_byte_buffer_t nas_msg); void send_ul_ccch_msg(const asn1::rrc_nr::ul_ccch_msg_s& msg); void send_ul_dcch_msg(uint32_t lcid, const asn1::rrc_nr::ul_dcch_msg_s& msg); @@ -148,6 +149,7 @@ private: void handle_sib1(const asn1::rrc_nr::sib1_s& sib1); bool handle_rrc_setup(const asn1::rrc_nr::rrc_setup_s& setup); void handle_rrc_reconfig(const asn1::rrc_nr::rrc_recfg_s& reconfig); + void handle_ue_capability_enquiry(const asn1::rrc_nr::ue_cap_enquiry_s& ue_cap_enquiry); void handle_dl_info_transfer(const asn1::rrc_nr::dl_info_transfer_s& dl_info_transfer); void handle_security_mode_command(const asn1::rrc_nr::security_mode_cmd_s& smc); void handle_rrc_release(const asn1::rrc_nr::rrc_release_s& rrc_release); diff --git a/srsue/src/stack/rrc_nr/rrc_nr.cc b/srsue/src/stack/rrc_nr/rrc_nr.cc index c9c14e2e1..dcdcbde4f 100644 --- a/srsue/src/stack/rrc_nr/rrc_nr.cc +++ b/srsue/src/stack/rrc_nr/rrc_nr.cc @@ -327,8 +327,8 @@ void rrc_nr::decode_dl_dcch(uint32_t lcid, unique_byte_buffer_t pdu) break; } case dl_dcch_msg_type_c::c1_c_::types::ue_cap_enquiry: { - ue_cap_enquiry_s rrc_cap_enquiry = c1->ue_cap_enquiry(); - task_sched.defer_task([this, rrc_cap_enquiry]() { handle_ue_cap_enquiry(rrc_cap_enquiry); }); + ue_cap_enquiry_s ue_cap_enquiry = c1->ue_cap_enquiry(); + task_sched.defer_task([this, ue_cap_enquiry]() { handle_ue_capability_enquiry(ue_cap_enquiry); }); break; } default: @@ -699,6 +699,96 @@ void rrc_nr::send_rrc_reconfig_complete() send_ul_dcch_msg(srb_to_lcid(nr_srb::srb1), ul_dcch_msg); } +int rrc_nr::send_ue_capability_info(const asn1::rrc_nr::ue_cap_enquiry_s& msg) +{ + ue_cap_enquiry_ies_s ue_cap_enquiry_ies = msg.crit_exts.ue_cap_enquiry(); + + asn1::rrc_nr::ul_dcch_msg_s ul_dcch_msg; + + auto& ue_cap_info = ul_dcch_msg.msg.set_c1().set_ue_cap_info().crit_exts.set_ue_cap_info(); + ul_dcch_msg.msg.c1().ue_cap_info().rrc_transaction_id = msg.rrc_transaction_id; + + for (auto ue_cap_rat_request : ue_cap_enquiry_ies.ue_cap_rat_request_list) { + if (ue_cap_rat_request.rat_type.value == rat_type_opts::nr) { + ue_cap_info.ue_cap_rat_container_list_present = true; + ue_cap_rat_container_s ue_cap_rat_container; + ue_cap_rat_container.rat_type.value = rat_type_opts::nr; + + ue_nr_cap_s ue_cap; + ue_cap.access_stratum_release = access_stratum_release_opts::rel15; + + // PDCP parameters + ue_cap.pdcp_params.supported_rohc_profiles.profile0x0000 = false; + ue_cap.pdcp_params.supported_rohc_profiles.profile0x0001 = false; + ue_cap.pdcp_params.supported_rohc_profiles.profile0x0002 = false; + ue_cap.pdcp_params.supported_rohc_profiles.profile0x0003 = false; + ue_cap.pdcp_params.supported_rohc_profiles.profile0x0004 = false; + ue_cap.pdcp_params.supported_rohc_profiles.profile0x0006 = false; + ue_cap.pdcp_params.supported_rohc_profiles.profile0x0101 = false; + ue_cap.pdcp_params.supported_rohc_profiles.profile0x0102 = false; + ue_cap.pdcp_params.supported_rohc_profiles.profile0x0103 = false; + ue_cap.pdcp_params.supported_rohc_profiles.profile0x0104 = false; + ue_cap.pdcp_params.max_num_rohc_context_sessions.value = pdcp_params_s::max_num_rohc_context_sessions_opts::cs2; + + if (args.pdcp_short_sn_support) { + ue_cap.pdcp_params.short_sn_present = true; + } + // PHY Parameters + ue_cap.phy_params.phy_params_common_present = true; + + // RF Parameters + for (const auto band : args.supported_bands_nr) { + band_nr_s band_nr; + band_nr.band_nr = band; + ue_cap.rf_params.supported_band_list_nr.push_back(band_nr); + + // supportedBandCombinationList + band_combination_s band_combination; + band_params_c band_params; + band_params.set_nr().band_nr = band; + band_combination.band_list.push_back(band_params); + ue_cap.rf_params.supported_band_combination_list.push_back(band_combination); + } + // featureSets + ue_cap.feature_sets_present = true; + feature_set_dl_per_cc_s feature_set_dl_per_cc; + feature_set_ul_per_cc_s feature_set_ul_per_cc; + + feature_set_dl_per_cc.supported_bw_dl.set_fr1().value = supported_bw_c::fr1_opts::mhz10; + feature_set_ul_per_cc.supported_bw_ul.set_fr1().value = supported_bw_c::fr1_opts::mhz10; + + switch (args.scs) { + case srsran_subcarrier_spacing_15kHz: + feature_set_dl_per_cc.supported_subcarrier_spacing_dl = subcarrier_spacing_opts::khz15; + feature_set_ul_per_cc.supported_subcarrier_spacing_ul = subcarrier_spacing_opts::khz15; + break; + case srsran_subcarrier_spacing_30kHz: + feature_set_dl_per_cc.supported_subcarrier_spacing_dl = subcarrier_spacing_opts::khz30; + feature_set_ul_per_cc.supported_subcarrier_spacing_ul = subcarrier_spacing_opts::khz30; + break; + default: + logger.warning("Unsupported subcarrier spacing value"); + } + + ue_cap.feature_sets.feature_sets_dl_per_cc.push_back(feature_set_dl_per_cc); + ue_cap.feature_sets.feature_sets_ul_per_cc.push_back(feature_set_ul_per_cc); + + ue_cap_rat_container.ue_cap_rat_container.resize(512); + asn1::bit_ref bref_pack(ue_cap_rat_container.ue_cap_rat_container.data(), + ue_cap_rat_container.ue_cap_rat_container.size()); + + if (ue_cap.pack(bref_pack) != asn1::SRSASN_SUCCESS) { + logger.error("Failed to pack UE NR Capabilities in UE Capability Info"); + return SRSRAN_ERROR; + } + ue_cap_rat_container.ue_cap_rat_container.resize(bref_pack.distance_bytes()); + ue_cap_info.ue_cap_rat_container_list.push_back(ue_cap_rat_container); + } + } + send_ul_dcch_msg(srb_to_lcid(nr_srb::srb1), ul_dcch_msg); + return SRSASN_SUCCESS; +} + // EUTRA-RRC interface int rrc_nr::get_eutra_nr_capabilities(srsran::byte_buffer_t* eutra_nr_caps_pdu) { @@ -2080,6 +2170,11 @@ void rrc_nr::handle_rrc_reconfig(const rrc_recfg_s& reconfig) } callback_list.add_proc(conn_recfg_proc); } +void rrc_nr::handle_ue_capability_enquiry(const ue_cap_enquiry_s& ue_cap_enquiry) +{ + logger.info("Received UE Capability Enquiry"); + send_ue_capability_info(ue_cap_enquiry); +} void rrc_nr::handle_dl_info_transfer(const dl_info_transfer_s& dl_info_transfer) { From 6db48353028741c0a1a66b0c724b783dbc22202b Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Sun, 24 Apr 2022 11:08:19 +0200 Subject: [PATCH 172/195] ue,rrc_nr: use minimal NR capabilities by default keep hard-coded caps as compile-time option but don't use them by default. --- srsue/hdr/stack/rrc_nr/rrc_nr.h | 1 - srsue/src/stack/rrc_nr/rrc_nr.cc | 36 +++++++++++--------------------- 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/srsue/hdr/stack/rrc_nr/rrc_nr.h b/srsue/hdr/stack/rrc_nr/rrc_nr.h index 40e39fcf1..edb223cb8 100644 --- a/srsue/hdr/stack/rrc_nr/rrc_nr.h +++ b/srsue/hdr/stack/rrc_nr/rrc_nr.h @@ -153,7 +153,6 @@ private: void handle_dl_info_transfer(const asn1::rrc_nr::dl_info_transfer_s& dl_info_transfer); void handle_security_mode_command(const asn1::rrc_nr::security_mode_cmd_s& smc); void handle_rrc_release(const asn1::rrc_nr::rrc_release_s& rrc_release); - void handle_ue_cap_enquiry(const asn1::rrc_nr::ue_cap_enquiry_s& ue_cap_enquiry); void generate_as_keys(); srsran::task_sched_handle task_sched; diff --git a/srsue/src/stack/rrc_nr/rrc_nr.cc b/srsue/src/stack/rrc_nr/rrc_nr.cc index dcdcbde4f..581b31ac1 100644 --- a/srsue/src/stack/rrc_nr/rrc_nr.cc +++ b/srsue/src/stack/rrc_nr/rrc_nr.cc @@ -701,6 +701,8 @@ void rrc_nr::send_rrc_reconfig_complete() int rrc_nr::send_ue_capability_info(const asn1::rrc_nr::ue_cap_enquiry_s& msg) { + transaction_id = msg.rrc_transaction_id; + ue_cap_enquiry_ies_s ue_cap_enquiry_ies = msg.crit_exts.ue_cap_enquiry(); asn1::rrc_nr::ul_dcch_msg_s ul_dcch_msg; @@ -773,6 +775,7 @@ int rrc_nr::send_ue_capability_info(const asn1::rrc_nr::ue_cap_enquiry_s& msg) ue_cap.feature_sets.feature_sets_dl_per_cc.push_back(feature_set_dl_per_cc); ue_cap.feature_sets.feature_sets_ul_per_cc.push_back(feature_set_ul_per_cc); +#if 1 ue_cap_rat_container.ue_cap_rat_container.resize(512); asn1::bit_ref bref_pack(ue_cap_rat_container.ue_cap_rat_container.data(), ue_cap_rat_container.ue_cap_rat_container.size()); @@ -782,6 +785,15 @@ int rrc_nr::send_ue_capability_info(const asn1::rrc_nr::ue_cap_enquiry_s& msg) return SRSRAN_ERROR; } ue_cap_rat_container.ue_cap_rat_container.resize(bref_pack.distance_bytes()); +#else + // hard-coded capabilities from third-party + ue_cap_rat_container.ue_cap_rat_container.from_string("E1A01000074F5A03020000C0A0241262C001206A0609B00C39F30C7942" + "C0E098040623809506C4DD608D21A08107CA01165B262A87813E43" + "9F40CF88E3C639F30C7942C0E070F09C0013C0070004F0001601C00140" + "A836036B04690D04083E500892D931541439F11C78C73E618F2858" + "1C0E1E04FE0000003F80000000A00E05"); +#endif + ue_cap_info.ue_cap_rat_container_list.push_back(ue_cap_rat_container); } } @@ -2229,30 +2241,6 @@ void rrc_nr::handle_rrc_release(const asn1::rrc_nr::rrc_release_s& rrc_release) logger.info("RRC Release not handled yet"); } -void rrc_nr::handle_ue_cap_enquiry(const asn1::rrc_nr::ue_cap_enquiry_s& ue_cap_enquiry) -{ - transaction_id = ue_cap_enquiry.rrc_transaction_id; - - logger.info("Received UECapabilityEnquiry"); - - // Send UECapabilityInformation - ul_dcch_msg_s ul_dcch_msg; - auto& ue_cap_info = ul_dcch_msg.msg.set_c1().set_ue_cap_info().crit_exts.set_ue_cap_info(); - - ue_cap_info.ue_cap_rat_container_list_present = true; - ue_cap_rat_container_s nr_cap = {}; - - nr_cap.rat_type = rat_type_e::nr; - nr_cap.ue_cap_rat_container.from_string( - "E1A01000074F5A03020000C0A0241262C001206A0609B00C39F30C7942C0E098040623809506C4DD608D21A08107CA01165B262A87813E43" - "9F40CF88E3C639F30C7942C0E070F09C0013C0070004F0001601C00140A836036B04690D04083E500892D931541439F11C78C73E618F2858" - "1C0E1E04FE0000003F80000000A00E05"); - - ue_cap_info.ue_cap_rat_container_list.push_back(nr_cap); - - send_ul_dcch_msg(srb_to_lcid(nr_srb::srb1), ul_dcch_msg); -} - // Security helper used by Security Mode Command and Mobility handling routines void rrc_nr::generate_as_keys() { From 20201cd89b2d24aec96d5d97e2d6a33b7a1ccffd Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Thu, 21 Apr 2022 14:07:25 +0200 Subject: [PATCH 173/195] lib,rlc_am_nr: cosmetic change, add comment --- lib/include/srsran/rlc/rlc_am_nr.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_am_nr.h b/lib/include/srsran/rlc/rlc_am_nr.h index af1988f95..dbeb40fa0 100644 --- a/lib/include/srsran/rlc/rlc_am_nr.h +++ b/lib/include/srsran/rlc/rlc_am_nr.h @@ -156,9 +156,9 @@ private: pdcp_sn_vector_t notify_info_vec; // Helper constants - uint32_t min_hdr_size = 2; + uint32_t min_hdr_size = 2; // Pre-initialized for 12 bit SN, updated by configure() uint32_t so_size = 2; - uint32_t max_hdr_size = 4; + uint32_t max_hdr_size = 4; // Pre-initialized for 12 bit SN, updated by configure() /**************************************************************************** * Tx constants From e0acb7b81caf11e3151ed3351357534e94447896 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Fri, 22 Apr 2022 07:32:01 +0200 Subject: [PATCH 174/195] lib,rlc_am_nr: update buffer-state-related unit test --- lib/src/rlc/rlc_am_nr.cc | 2 +- lib/test/rlc/rlc_am_nr_test.cc | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 5fa34da3d..ce8b2b1e1 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -977,7 +977,7 @@ void rlc_am_nr_tx::get_buffer_state(uint32_t& n_bytes_new, uint32_t& n_bytes_pri retx.so_start + retx.segment_length - 1); if (tx_window->has_sn(retx.sn)) { int req_bytes = retx.segment_length; - int hdr_req_bytes = retx.is_segment ? max_hdr_size : min_hdr_size; // Segmentation not supported yet + int hdr_req_bytes = (retx.is_segment && retx.current_so != 0) ? max_hdr_size : min_hdr_size; if (req_bytes <= 0) { RlcError("in get_buffer_state(): Removing retx with SN=%d from queue", retx.sn); retx_queue->pop(); diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index db83c5310..a452f78b2 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -1366,8 +1366,9 @@ int segment_retx_and_loose_segments_test(rlc_am_nr_sn_size_t sn_size) // Write status PDU duplicate to RLC1 rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); - // Check there are two Retx segments - TESTASSERT_EQ(header_size + payload_size, rlc1.get_buffer_state()); // Fixme: get_buffer_state() + // Check there are two Retx segments (a first one and a continued one) + TESTASSERT_EQ(pdu_size_first, rlc1.get_buffer_state()); // Fixme: get_buffer_state() + // TESTASSERT_EQ(pdu_size_first + pdu_size_continued, rlc1.get_buffer_state()); // Should be this } { @@ -1654,8 +1655,9 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) // Write status PDU to RLC1 rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); - // Check there is an Retx of SN=3 - TESTASSERT_EQ(header_size + payload_size, rlc1.get_buffer_state()); + // Check there are 3 Retx segments (a first one and two continued ones) + TESTASSERT_EQ(pdu_size_first, rlc1.get_buffer_state()); // Fixme: get_buffer_state() + // TESTASSERT_EQ(pdu_size_first + 2 * pdu_size_continued, rlc1.get_buffer_state()); // Should be this } { From ced6cf6e4068733c7902434049fe4698d47421bb Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Fri, 22 Apr 2022 07:46:19 +0200 Subject: [PATCH 175/195] lib,rlc_am_nr: consider SDU under segmentation in buffer state --- lib/src/rlc/rlc_am_nr.cc | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index ce8b2b1e1..b1fcdc51d 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -988,7 +988,36 @@ void rlc_am_nr_tx::get_buffer_state(uint32_t& n_bytes_new, uint32_t& n_bytes_pri } } - // Bytes needed for tx SDUs + // Bytes needed for tx of the rest of the SDU that is currently under segmentation (if any) + if (sdu_under_segmentation_sn != INVALID_RLC_SN) { + if (tx_window->has_sn(sdu_under_segmentation_sn)) { + rlc_amd_tx_pdu_nr& seg_pdu = (*tx_window)[sdu_under_segmentation_sn]; + if (not seg_pdu.segment_list.empty()) { + // obtain amount of already transmitted Bytes + const rlc_amd_tx_pdu_nr::pdu_segment& seg = seg_pdu.segment_list.back(); + uint32_t last_byte = seg.so + seg.payload_len; + if (last_byte <= seg_pdu.sdu_buf->N_bytes) { + // compute remaining bytes pending for transmission + uint32_t remaining_bytes = seg_pdu.sdu_buf->N_bytes - last_byte; + n_bytes_new += remaining_bytes + max_hdr_size; + } else { + RlcError( + "buffer state - last segment of SDU under segmentation exceeds SDU len. SDU len=%d B, last_byte=%d B", + seg_pdu.sdu_buf->N_bytes, + last_byte); + } + } else { + RlcError("buffer state - SDU under segmentation has empty segment list. Ignoring SN=%d", + sdu_under_segmentation_sn); + } + } else { + sdu_under_segmentation_sn = INVALID_RLC_SN; + RlcError("buffer state - SDU under segmentation does not exist in tx_window. Aborting segmentation SN=%d", + sdu_under_segmentation_sn); + } + } + + // Bytes needed for tx SDUs in queue uint32_t n_sdus = tx_sdu_queue.get_n_sdus(); n_bytes_new += tx_sdu_queue.size_bytes(); From d66c76b37453a6ee4be90cc13fa42ddffbde30e7 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Fri, 22 Apr 2022 10:29:57 +0200 Subject: [PATCH 176/195] lib,rlc_am_nr: consider size of all pending retx in buffer state --- lib/include/srsran/rlc/rlc_am_data_structs.h | 15 +++++++++++++++ lib/src/rlc/rlc_am_nr.cc | 8 ++++---- lib/test/rlc/rlc_am_nr_test.cc | 6 ++---- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_am_data_structs.h b/lib/include/srsran/rlc/rlc_am_data_structs.h index 5c4ce5012..98ac017a7 100644 --- a/lib/include/srsran/rlc/rlc_am_data_structs.h +++ b/lib/include/srsran/rlc/rlc_am_data_structs.h @@ -370,6 +370,9 @@ public: virtual bool empty() const = 0; virtual bool full() const = 0; + virtual T& operator[](size_t idx) = 0; + virtual const T& operator[](size_t idx) const = 0; + virtual bool has_sn(uint32_t sn) const = 0; virtual bool has_sn(uint32_t sn, uint32_t so) const = 0; }; @@ -396,6 +399,18 @@ public: return buffer[rpos]; } + T& operator[](size_t idx) override + { + srsran_assert(idx < size(), "Out-of-bounds access to element idx=%zd", idx); + return buffer[(rpos + idx) % WINDOW_SIZE]; + } + + const T& operator[](size_t idx) const override + { + srsran_assert(idx < size(), "Out-of-bounds access to element idx=%zd", idx); + return buffer[(rpos + idx) % WINDOW_SIZE]; + } + void clear() override { wpos = 0; diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index b1fcdc51d..6091694e0 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -968,8 +968,9 @@ void rlc_am_nr_tx::get_buffer_state(uint32_t& n_bytes_new, uint32_t& n_bytes_pri } // Bytes needed for retx - if (not retx_queue->empty()) { - rlc_amd_retx_nr_t& retx = retx_queue->front(); + size_t n_retx = retx_queue->size(); + for (size_t i = 0; i < n_retx; i++) { + rlc_amd_retx_nr_t& retx = (*retx_queue)[i]; RlcDebug("buffer state - retx - SN=%d, Segment: %s, %d:%d", retx.sn, retx.is_segment ? "true" : "false", @@ -979,8 +980,7 @@ void rlc_am_nr_tx::get_buffer_state(uint32_t& n_bytes_new, uint32_t& n_bytes_pri int req_bytes = retx.segment_length; int hdr_req_bytes = (retx.is_segment && retx.current_so != 0) ? max_hdr_size : min_hdr_size; if (req_bytes <= 0) { - RlcError("in get_buffer_state(): Removing retx with SN=%d from queue", retx.sn); - retx_queue->pop(); + RlcError("buffer state - retx - invalid length=%d for SN=%d", req_bytes, retx.sn); } else { n_bytes_prio += (req_bytes + hdr_req_bytes); RlcDebug("buffer state - retx: %d bytes", n_bytes_prio); diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index a452f78b2..1488bece1 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -1367,8 +1367,7 @@ int segment_retx_and_loose_segments_test(rlc_am_nr_sn_size_t sn_size) rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); // Check there are two Retx segments (a first one and a continued one) - TESTASSERT_EQ(pdu_size_first, rlc1.get_buffer_state()); // Fixme: get_buffer_state() - // TESTASSERT_EQ(pdu_size_first + pdu_size_continued, rlc1.get_buffer_state()); // Should be this + TESTASSERT_EQ(pdu_size_first + pdu_size_continued, rlc1.get_buffer_state()); } { @@ -1656,8 +1655,7 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); // Check there are 3 Retx segments (a first one and two continued ones) - TESTASSERT_EQ(pdu_size_first, rlc1.get_buffer_state()); // Fixme: get_buffer_state() - // TESTASSERT_EQ(pdu_size_first + 2 * pdu_size_continued, rlc1.get_buffer_state()); // Should be this + TESTASSERT_EQ(pdu_size_first + 2 * pdu_size_continued, rlc1.get_buffer_state()); } { From c16071a3ac249729505796046a68dde0c29924ea Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Fri, 22 Apr 2022 12:32:21 +0200 Subject: [PATCH 177/195] lib,rlc_am_nr: extend unit tests to check buffer state --- lib/test/rlc/rlc_am_nr_test.cc | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index 1488bece1..a6c371d65 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -1492,7 +1492,7 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) } uint32_t expected_buffer_state = (header_size + payload_size) * n_sdu_bufs; - TESTASSERT(expected_buffer_state == rlc1.get_buffer_state()); + TESTASSERT_EQ(expected_buffer_state, rlc1.get_buffer_state()); constexpr uint32_t so_size = 2; constexpr uint32_t segment_size = 1; @@ -1502,6 +1502,16 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) // Read 15 PDUs from RLC1 std::vector pdu_bufs(n_pdu_bufs); for (int i = 0; i < n_pdu_bufs; i++) { + // First also test buffer state + uint32_t remaining_total_bytes = (payload_size * n_sdu_bufs) - (i * segment_size); + uint32_t remaining_full_sdus = remaining_total_bytes / payload_size; + uint32_t remaining_seg_bytes = remaining_total_bytes % payload_size; + + uint32_t buffer_state_full_sdus = (header_size + payload_size) * remaining_full_sdus; + uint32_t buffer_state_seg_sdu = remaining_seg_bytes == 0 ? 0 : (header_size + so_size + remaining_seg_bytes); + expected_buffer_state = buffer_state_full_sdus + buffer_state_seg_sdu; + TESTASSERT_EQ(expected_buffer_state, rlc1.get_buffer_state()); + pdu_bufs[i] = srsran::make_byte_buffer(); if (i == 0 || i == 3 || i == 6 || i == 9 || i == 12) { // First segment, no SO @@ -1661,6 +1671,14 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) { // Re-transmit the 3 lost segments for (int i = 0; i < 3; i++) { + // First also test buffer state + uint32_t remaining_segments = 3 - i; + expected_buffer_state = remaining_segments * (header_size + so_size + segment_size); + if (i == 0) { // subtract so_size, because in this setup the first retx is a "first_segment" without SO. + expected_buffer_state -= so_size; + } + TESTASSERT_EQ(expected_buffer_state, rlc1.get_buffer_state()); + byte_buffer_t retx_buf; uint32_t len = 0; if (i == 0) { From 4028b26274694bc9fb22189a78af43ff13c09af7 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Fri, 22 Apr 2022 14:00:09 +0200 Subject: [PATCH 178/195] lib,rlc_am_nr: add info_state() --- lib/include/srsran/rlc/rlc_am_nr.h | 1 + lib/src/rlc/rlc_am_nr.cc | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/lib/include/srsran/rlc/rlc_am_nr.h b/lib/include/srsran/rlc/rlc_am_nr.h index dbeb40fa0..0baadcd68 100644 --- a/lib/include/srsran/rlc/rlc_am_nr.h +++ b/lib/include/srsran/rlc/rlc_am_nr.h @@ -174,6 +174,7 @@ public: // Debug Helper void debug_state() const; + void info_state() const; }; /**************************************************************************** diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 6091694e0..bd151af6d 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -1143,6 +1143,16 @@ void rlc_am_nr_tx::debug_state() const st.pdu_without_poll, st.byte_without_poll); } +void rlc_am_nr_tx::info_state() const +{ + RlcInfo("TX window state: SDUs %d", tx_window->size()); + RlcInfo("TX entity state: Tx_Next_Ack=%d, Tx_Next=%d, POLL_SN=%d, PDU_WITHOUT_POLL=%d, BYTE_WITHOUT_POLL=%d", + st.tx_next_ack, + st.tx_next, + st.poll_sn, + st.pdu_without_poll, + st.byte_without_poll); +} /**************************************************************************** * Rx subclass implementation ***************************************************************************/ From c0546b86344f8cc5604329d05d84db6f3be2b159 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Fri, 22 Apr 2022 13:22:46 +0200 Subject: [PATCH 179/195] lib,rlc_am_nr: increase verbosity on invalid ACK --- lib/src/rlc/rlc_am_nr.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index bd151af6d..e05ae4cf2 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -768,7 +768,8 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) ? status.ack_sn : status.nacks[0].nack_sn; // Stop processing ACKs at the first NACK, if it exists. if (tx_mod_base_nr(stop_sn) > tx_mod_base_nr(st.tx_next)) { - RlcError("Received ACK or NACK larger than TX_NEXT. Ignoring status report"); + RlcError("Received ACK or NACK with SN=%d larger than TX_NEXT=%d. Ignoring status report", stop_sn, st.tx_next); + info_state(); return; } for (uint32_t sn = st.tx_next_ack; tx_mod_base_nr(sn) < tx_mod_base_nr(stop_sn); sn = (sn + 1) % mod_nr) { From 5c59cbfa755e3e5f4a6ca0b2c21ff2e33f7b5d90 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Mon, 25 Apr 2022 13:35:08 +0200 Subject: [PATCH 180/195] ue,rrc_nr: include RLC capabilities in NR cap info this is needed to enable RLC UM DRBs with Amarisoft --- srsue/src/stack/rrc_nr/rrc_nr.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/srsue/src/stack/rrc_nr/rrc_nr.cc b/srsue/src/stack/rrc_nr/rrc_nr.cc index 581b31ac1..9a4186891 100644 --- a/srsue/src/stack/rrc_nr/rrc_nr.cc +++ b/srsue/src/stack/rrc_nr/rrc_nr.cc @@ -719,6 +719,12 @@ int rrc_nr::send_ue_capability_info(const asn1::rrc_nr::ue_cap_enquiry_s& msg) ue_nr_cap_s ue_cap; ue_cap.access_stratum_release = access_stratum_release_opts::rel15; + // RLC params + ue_cap.rlc_params_present = true; + ue_cap.rlc_params.am_with_short_sn_present = true; + ue_cap.rlc_params.um_with_short_sn_present = true; + ue_cap.rlc_params.um_with_long_sn_present = true; + // PDCP parameters ue_cap.pdcp_params.supported_rohc_profiles.profile0x0000 = false; ue_cap.pdcp_params.supported_rohc_profiles.profile0x0001 = false; From b3edced44aef56167d855ac8c3fac028d8c666f9 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Mon, 14 Mar 2022 16:02:32 +0000 Subject: [PATCH 181/195] lib,rlc_am_nr: added t_poll_retransmit to tx entity --- lib/include/srsran/rlc/rlc_am_nr.h | 11 ++++++++++- lib/src/rlc/rlc_am_nr.cc | 28 +++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_am_nr.h b/lib/include/srsran/rlc/rlc_am_nr.h index 0baadcd68..d64aabc27 100644 --- a/lib/include/srsran/rlc/rlc_am_nr.h +++ b/lib/include/srsran/rlc/rlc_am_nr.h @@ -98,6 +98,7 @@ public: bool sdu_queue_is_full() final; void reestablish() final; + void stop() final; int write_sdu(unique_byte_buffer_t sdu); void empty_queue() final; @@ -125,8 +126,10 @@ public: // Polling uint8_t get_pdu_poll(bool is_retx, uint32_t sdu_bytes); - void stop() final; + // Timers + void timer_expired(uint32_t timeout_id); + // Window helpers bool inside_tx_window(uint32_t sn) const; private: @@ -166,6 +169,12 @@ private: ***************************************************************************/ inline uint32_t tx_window_size() const; + /**************************************************************************** + * TX timers + * Ref: 3GPP TS 38.322 version 16.2.0 Section 7.3 + ***************************************************************************/ + srsran::timer_handler::unique_timer poll_retransmit_timer; + public: // Getters/Setters void set_tx_state(const rlc_am_nr_tx_state_t& st_) { st = st_; } // This should only be used for testing. diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index e05ae4cf2..836feaa03 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -32,7 +32,9 @@ const static uint32_t so_end_of_sdu = 0xFFFF; /*************************************************************************** * Tx subclass implementation ***************************************************************************/ -rlc_am_nr_tx::rlc_am_nr_tx(rlc_am* parent_) : parent(parent_), rlc_am_base_tx(parent_->logger) {} +rlc_am_nr_tx::rlc_am_nr_tx(rlc_am* parent_) : + parent(parent_), rlc_am_base_tx(parent_->logger), poll_retransmit_timer(parent->timers->get_unique_timer()) +{} bool rlc_am_nr_tx::configure(const rlc_config_t& cfg_) { @@ -73,6 +75,19 @@ bool rlc_am_nr_tx::configure(const rlc_config_t& cfg_) empty_queue_no_lock(); tx_sdu_queue.resize(cfg_.tx_queue_length); + // Check timers are valid + if (not poll_retransmit_timer.is_valid()) { + RlcError("Configuring TX: timers not configured"); + return false; + } + + // Configure t_poll_retransmission timer + if (cfg.t_poll_retx > 0) { + poll_retransmit_timer.set(static_cast(cfg.t_poll_retx), + [this](uint32_t timerid) { timer_expired(timerid); }); + RlcInfo("configured poll retransmission timer. t-pollRetransmission=%d ms", cfg.t_poll_retx); + } + tx_enabled = true; RlcDebug("RLC AM NR configured tx entity."); @@ -1112,6 +1127,17 @@ void rlc_am_nr_tx::empty_queue_no_lock() } void rlc_am_nr_tx::stop() {} +void rlc_am_nr_tx::timer_expired(uint32_t timeout_id) +{ + std::unique_lock lock(mutex); + + // Status Prohibit + if (poll_retransmit_timer.is_valid() && poll_retransmit_timer.id() == timeout_id) { + RlcDebug("Status prohibit timer expired after %dms", poll_retransmit_timer.duration()); + return; + } +} + /* * Window helpers */ From f93d699abc7ecbb4a68a81e5d92a607cf716e22c Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Mon, 14 Mar 2022 17:29:17 +0000 Subject: [PATCH 182/195] lib,rlc_am_nr: retransmit first un-acked SDU when t-Poll_retransmit expires --- lib/src/rlc/rlc_am_nr.cc | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 836feaa03..ebee6b6be 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -1134,6 +1134,32 @@ void rlc_am_nr_tx::timer_expired(uint32_t timeout_id) // Status Prohibit if (poll_retransmit_timer.is_valid() && poll_retransmit_timer.id() == timeout_id) { RlcDebug("Status prohibit timer expired after %dms", poll_retransmit_timer.duration()); + /* + * - if both the transmission buffer and the retransmission buffer are empty + * (excluding transmitted RLC SDU or RLC SDU segment awaiting acknowledgements); or + * - if no new RLC SDU or RLC SDU segment can be transmitted (e.g. due to window stalling): + * - consider the RLC SDU with the highest SN among the RLC SDUs submitted to lower layer for + * retransmission; or + * - consider any RLC SDU which has not been positively acknowledged for retransmission. + * - include a poll in an AMD PDU as described in section 5.3.3.2. + */ + if ((tx_sdu_queue.is_empty() && retx_queue.empty()) || tx_window.full()) { + if (tx_window.empty()) { + // Nothing to RETX + return; + } + // Fully RETX first RLC SDU that has not been acked + if (tx_window.has_sn(st.tx_next_ack)) { + RlcError("TX window not empty, but TX_NEXT_ACK not in TX_WINDOW"); + return; + } + rlc_amd_retx_nr_t& retx = retx_queue.push(); + retx.sn = st.tx_next_ack; + retx.is_segment = false; + retx.so_start = 0; + retx.segment_length = tx_window[st.tx_next_ack].sdu_buf->N_bytes; + retx.current_so = 0; + } return; } } From 515f2099be476c6793c273c689656bbb09ca3eea Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Mon, 14 Mar 2022 17:50:20 +0000 Subject: [PATCH 183/195] lib,rlc_am_nr: added SN to get pdu poll function. Setting POLL_SN now. --- lib/include/srsran/rlc/rlc_am_nr.h | 2 +- lib/src/rlc/rlc_am_nr.cc | 34 ++++++++++++++++++++---------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_am_nr.h b/lib/include/srsran/rlc/rlc_am_nr.h index d64aabc27..7e5d97873 100644 --- a/lib/include/srsran/rlc/rlc_am_nr.h +++ b/lib/include/srsran/rlc/rlc_am_nr.h @@ -124,7 +124,7 @@ public: uint32_t build_status_pdu(byte_buffer_t* payload, uint32_t nof_bytes); // Polling - uint8_t get_pdu_poll(bool is_retx, uint32_t sdu_bytes); + uint8_t get_pdu_poll(uint32_t sn, bool is_retx, uint32_t sdu_bytes); // Timers void timer_expired(uint32_t timeout_id); diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index ebee6b6be..41ea9ad5d 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -214,7 +214,7 @@ uint32_t rlc_am_nr_tx::build_new_pdu(uint8_t* payload, uint32_t nof_bytes) // Prepare header rlc_am_nr_pdu_header_t hdr = {}; hdr.dc = RLC_DC_FIELD_DATA_PDU; - hdr.p = get_pdu_poll(false, tx_sdu->N_bytes); + hdr.p = get_pdu_poll(st.tx_next, false, tx_sdu->N_bytes); hdr.si = rlc_nr_si_field_t::full_sdu; hdr.sn_size = cfg.tx_sn_field_length; hdr.sn = st.tx_next; @@ -273,7 +273,7 @@ uint32_t rlc_am_nr_tx::build_new_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, uint8_t* // Prepare header rlc_am_nr_pdu_header_t hdr = {}; hdr.dc = RLC_DC_FIELD_DATA_PDU; - hdr.p = get_pdu_poll(false, segment_payload_len); + hdr.p = get_pdu_poll(st.tx_next, false, segment_payload_len); hdr.si = rlc_nr_si_field_t::first_segment; hdr.sn_size = cfg.tx_sn_field_length; hdr.sn = st.tx_next; @@ -374,7 +374,7 @@ uint32_t rlc_am_nr_tx::build_continuation_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, // Prepare header rlc_am_nr_pdu_header_t hdr = {}; hdr.dc = RLC_DC_FIELD_DATA_PDU; - hdr.p = get_pdu_poll(false, segment_payload_len); + hdr.p = get_pdu_poll(st.tx_next, false, segment_payload_len); hdr.si = si; hdr.sn_size = cfg.tx_sn_field_length; hdr.sn = st.tx_next; @@ -542,7 +542,7 @@ rlc_am_nr_tx::build_retx_pdu_without_segmentation(const rlc_amd_retx_nr_t retx, rlc_am_nr_pdu_header_t new_header = tx_pdu.header; new_header.si = si; new_header.so = retx.current_so; - new_header.p = get_pdu_poll(true, 0); + new_header.p = get_pdu_poll(retx.sn, true, 0); uint32_t hdr_len = rlc_am_nr_write_data_pdu_header(new_header, payload); // Write SDU/SDU segment to payload @@ -624,7 +624,7 @@ uint32_t rlc_am_nr_tx::build_retx_pdu_with_segmentation(rlc_amd_retx_nr_t& retx, // Write header rlc_am_nr_pdu_header_t hdr = tx_pdu.header; - hdr.p = get_pdu_poll(true, 0); + hdr.p = get_pdu_poll(retx.sn, true, 0); hdr.so = retx.current_so; hdr.si = si; uint32_t hdr_len = rlc_am_nr_write_data_pdu_header(hdr, payload); @@ -1051,7 +1051,7 @@ void rlc_am_nr_tx::get_buffer_state(uint32_t& n_bytes_new, uint32_t& n_bytes_pri * Check whether the polling bit needs to be set, as specified in * TS 38.322, section 5.3.3.2 */ -uint8_t rlc_am_nr_tx::get_pdu_poll(bool is_retx, uint32_t sdu_bytes) +uint8_t rlc_am_nr_tx::get_pdu_poll(uint32_t sn, bool is_retx, uint32_t sdu_bytes) { /* For each AMD PDU or AMD PDU segment that has not been previoulsy tranmitted: * - increment PDU_WITHOUT_POLL by one; @@ -1093,6 +1093,18 @@ uint8_t rlc_am_nr_tx::get_pdu_poll(bool is_retx, uint32_t sdu_bytes) if (poll == 1) { st.pdu_without_poll = 0; st.byte_without_poll = 0; + /* + * - set POLL_SN to the highest SN of the AMD PDU among the AMD PDUs submitted to lower layer; + * - if t-PollRetransmit is not running: + * - start t-PollRetransmit. + * - else: + * - restart t-PollRetransmit. + */ + if (sn > st.poll_sn) { + st.poll_sn = sn; + poll_retransmit_timer.stop(); + poll_retransmit_timer.run(); + } } return poll; } @@ -1143,21 +1155,21 @@ void rlc_am_nr_tx::timer_expired(uint32_t timeout_id) * - consider any RLC SDU which has not been positively acknowledged for retransmission. * - include a poll in an AMD PDU as described in section 5.3.3.2. */ - if ((tx_sdu_queue.is_empty() && retx_queue.empty()) || tx_window.full()) { - if (tx_window.empty()) { + if ((tx_sdu_queue.is_empty() && retx_queue->empty()) || tx_window->full()) { + if (tx_window->empty()) { // Nothing to RETX return; } // Fully RETX first RLC SDU that has not been acked - if (tx_window.has_sn(st.tx_next_ack)) { + if (tx_window->has_sn(st.tx_next_ack)) { RlcError("TX window not empty, but TX_NEXT_ACK not in TX_WINDOW"); return; } - rlc_amd_retx_nr_t& retx = retx_queue.push(); + rlc_amd_retx_nr_t& retx = retx_queue->push(); retx.sn = st.tx_next_ack; retx.is_segment = false; retx.so_start = 0; - retx.segment_length = tx_window[st.tx_next_ack].sdu_buf->N_bytes; + retx.segment_length = (*tx_window)[st.tx_next_ack].sdu_buf->N_bytes; retx.current_so = 0; } return; From ffb0c2190345c7b1bcc9b19d1c95039ca2b2f53c Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Mon, 14 Mar 2022 18:17:20 +0000 Subject: [PATCH 184/195] lib,rlc_am_nr: stop poll retransmission if POLL_SN is ACKed/NACKed --- lib/src/rlc/rlc_am_nr.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 41ea9ad5d..23f09c913 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -771,14 +771,13 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) RlcHexDebug(payload, nof_bytes, "%s Rx control PDU", parent->rb_name); rlc_am_nr_read_status_pdu(payload, nof_bytes, cfg.tx_sn_field_length, &status); log_rlc_am_nr_status_pdu_to_string(logger.info, "RX status PDU: %s", &status, parent->rb_name); - // Local variables for handling Status PDU will be updated with lock + /* * - if the SN of the corresponding RLC SDU falls within the range * TX_Next_Ack <= SN < = the highest SN of the AMD PDU among the AMD PDUs submitted to lower layer: * - consider the RLC SDU or the RLC SDU segment for which a negative acknowledgement was received for * retransmission. */ - // Process ACKs uint32_t stop_sn = status.nacks.size() == 0 ? status.ack_sn : status.nacks[0].nack_sn; // Stop processing ACKs at the first NACK, if it exists. @@ -787,6 +786,8 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) info_state(); return; } + + // Process ACKs for (uint32_t sn = st.tx_next_ack; tx_mod_base_nr(sn) < tx_mod_base_nr(stop_sn); sn = (sn + 1) % mod_nr) { if (tx_window->has_sn(sn)) { notify_info_vec.push_back((*tx_window)[sn].pdcp_sn); @@ -933,6 +934,11 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) * - if t-PollRetransmit is running: * - stop and reset t-PollRetransmit. */ + if (st.poll_sn <= status.ack_sn) { + if (poll_retransmit_timer.is_running()) { + poll_retransmit_timer.stop(); + } + } } /** From d2d3c4140aeb86c33f91a49762ec704c6d0b456c Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Tue, 29 Mar 2022 14:41:56 +0100 Subject: [PATCH 185/195] lib,rlc_am_nr: make sure that sdu_under_segmentation is set before calculating the polling bit --- lib/src/rlc/rlc_am_nr.cc | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 23f09c913..2a0d21855 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -270,6 +270,11 @@ uint32_t rlc_am_nr_tx::build_new_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, uint8_t* uint32_t segment_payload_len = nof_bytes - min_hdr_size; + // Save SDU currently being segmented + // This needs to be done before calculating the polling bit + // To make sure we check correctly that the buffers are empty. + sdu_under_segmentation_sn = st.tx_next; + // Prepare header rlc_am_nr_pdu_header_t hdr = {}; hdr.dc = RLC_DC_FIELD_DATA_PDU; @@ -292,9 +297,6 @@ uint32_t rlc_am_nr_tx::build_new_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, uint8_t* srsran_assert((hdr_len + segment_payload_len) <= nof_bytes, "Error calculating hdr_len and segment_payload_len"); memcpy(&payload[hdr_len], tx_pdu.sdu_buf->msg, segment_payload_len); - // Save SDU currently being segmented - sdu_under_segmentation_sn = st.tx_next; - // Store Segment Info rlc_amd_tx_pdu_nr::pdu_segment segment_info; segment_info.payload_len = segment_payload_len; @@ -368,7 +370,8 @@ uint32_t rlc_am_nr_tx::build_continuation_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, "SDU bytes left %d, nof_bytes %d, ", segment_payload_full_len, nof_bytes); - si = rlc_nr_si_field_t::last_segment; + si = rlc_nr_si_field_t::last_segment; + sdu_under_segmentation_sn = INVALID_RLC_SN; } // Prepare header @@ -405,7 +408,6 @@ uint32_t rlc_am_nr_tx::build_continuation_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, } else { RlcInfo("grant is large enough for full SDU." "Removing current SDU info"); - sdu_under_segmentation_sn = INVALID_RLC_SN; // SDU is fully TX'ed. Increment TX_NEXT st.tx_next = (st.tx_next + 1) % mod_nr; } @@ -1073,9 +1075,11 @@ uint8_t rlc_am_nr_tx::get_pdu_poll(uint32_t sn, bool is_retx, uint32_t sdu_bytes st.byte_without_poll += sdu_bytes; if (cfg.poll_pdu > 0 && st.pdu_without_poll >= (uint32_t)cfg.poll_pdu) { poll = 1; + RlcInfo("Setting poll bit due to PollPDU. SN=%d", sn); } if (cfg.poll_byte > 0 && st.byte_without_poll >= (uint32_t)cfg.poll_byte) { poll = 1; + RlcInfo("Setting poll bit due to PollBYTE. SN=%d", sn); } } @@ -1086,8 +1090,9 @@ uint8_t rlc_am_nr_tx::get_pdu_poll(uint32_t sn, bool is_retx, uint32_t sdu_bytes * - if no new RLC SDU can be transmitted after the transmission of the AMD PDU (e.g. due to window stalling); * - include a poll in the AMD PDU as described below. */ - - if ((tx_sdu_queue.is_empty() && retx_queue->empty()) || tx_window->full()) { + if ((tx_sdu_queue.is_empty() && retx_queue->empty() && sdu_under_segmentation_sn == INVALID_RLC_SN) || + tx_window->full()) { + RlcInfo("Setting poll bit due to empty buffers/inablity to TX. SN=%d", sn); poll = 1; } @@ -1110,6 +1115,7 @@ uint8_t rlc_am_nr_tx::get_pdu_poll(uint32_t sn, bool is_retx, uint32_t sdu_bytes st.poll_sn = sn; poll_retransmit_timer.stop(); poll_retransmit_timer.run(); + RlcInfo("Setting POLL_SN=%d", sn); } } return poll; From 6ff18272e06c41638648ad7f0123f370edbb2b6b Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Tue, 29 Mar 2022 13:56:12 +0100 Subject: [PATCH 186/195] lib,rlc_am_nr: added debug_window function --- lib/include/srsran/rlc/rlc_am_nr.h | 3 ++- lib/src/rlc/rlc_am_nr.cc | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/include/srsran/rlc/rlc_am_nr.h b/lib/include/srsran/rlc/rlc_am_nr.h index 7e5d97873..c48eb270f 100644 --- a/lib/include/srsran/rlc/rlc_am_nr.h +++ b/lib/include/srsran/rlc/rlc_am_nr.h @@ -181,9 +181,10 @@ public: rlc_am_nr_tx_state_t get_tx_state() { return st; } // This should only be used for testing. uint32_t get_tx_window_utilization() { return tx_window->size(); } // This should only be used for testing. - // Debug Helper + // Debug Helpers void debug_state() const; void info_state() const; + void debug_window() const; }; /**************************************************************************** diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 2a0d21855..374701e2c 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -1220,6 +1220,7 @@ void rlc_am_nr_tx::debug_state() const st.pdu_without_poll, st.byte_without_poll); } + void rlc_am_nr_tx::info_state() const { RlcInfo("TX window state: SDUs %d", tx_window->size()); @@ -1230,6 +1231,11 @@ void rlc_am_nr_tx::info_state() const st.pdu_without_poll, st.byte_without_poll); } + +void rlc_am_nr_tx::debug_window() const +{ + RlcDebug("TX window state: Tx_Next_Ack=%d, Tx_Next=%d, SDUs=%d", st.tx_next_ack, st.tx_next, tx_window->size()); +} /**************************************************************************** * Rx subclass implementation ***************************************************************************/ From 0551244d6486f4cfe2d4ff2dd6eb8126c87093e2 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Tue, 29 Mar 2022 14:26:11 +0100 Subject: [PATCH 187/195] lib,rlc_am_nr: make sure has_data returns true when there are retxs. Make sure t-PollRetransmit is not started if it is set to infinity. --- lib/src/rlc/rlc_am_nr.cc | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 374701e2c..f1d256829 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -96,8 +96,8 @@ bool rlc_am_nr_tx::configure(const rlc_config_t& cfg_) bool rlc_am_nr_tx::has_data() { - return do_status() || // if we have a status PDU to transmit - tx_sdu_queue.get_n_sdus() != 0; // or if there is a SDU queued up for transmission + return do_status() || // if we have a status PDU to transmit + tx_sdu_queue.get_n_sdus() != 0 || !retx_queue->empty(); // or if there is a SDU queued up for transmission } /** @@ -1111,11 +1111,15 @@ uint8_t rlc_am_nr_tx::get_pdu_poll(uint32_t sn, bool is_retx, uint32_t sdu_bytes * - else: * - restart t-PollRetransmit. */ - if (sn > st.poll_sn) { + if (tx_mod_base_nr(sn) > tx_mod_base_nr(st.poll_sn)) { st.poll_sn = sn; + } + if (poll_retransmit_timer.is_running()) { poll_retransmit_timer.stop(); + } + if (cfg.t_poll_retx > 0) { poll_retransmit_timer.run(); - RlcInfo("Setting POLL_SN=%d", sn); + RlcInfo("Started t-PollRetransmit. POLL_SN=%d", sn); } } return poll; @@ -1157,7 +1161,8 @@ void rlc_am_nr_tx::timer_expired(uint32_t timeout_id) // Status Prohibit if (poll_retransmit_timer.is_valid() && poll_retransmit_timer.id() == timeout_id) { - RlcDebug("Status prohibit timer expired after %dms", poll_retransmit_timer.duration()); + RlcDebug("Poll retransmission timer expired after %dms", poll_retransmit_timer.duration()); + debug_state(); /* * - if both the transmission buffer and the retransmission buffer are empty * (excluding transmitted RLC SDU or RLC SDU segment awaiting acknowledgements); or @@ -1173,8 +1178,8 @@ void rlc_am_nr_tx::timer_expired(uint32_t timeout_id) return; } // Fully RETX first RLC SDU that has not been acked - if (tx_window->has_sn(st.tx_next_ack)) { - RlcError("TX window not empty, but TX_NEXT_ACK not in TX_WINDOW"); + if (not tx_window->has_sn(st.tx_next_ack)) { + RlcError("TX window not empty, but TX_NEXT_ACK=%d not in TX_WINDOW", st.tx_next_ack); return; } rlc_amd_retx_nr_t& retx = retx_queue->push(); @@ -1212,7 +1217,6 @@ bool rlc_am_nr_tx::inside_tx_window(uint32_t sn) const */ void rlc_am_nr_tx::debug_state() const { - RlcDebug("TX window state: SDUs %d", tx_window->size()); RlcDebug("TX entity state: Tx_Next_Ack=%d, Tx_Next=%d, POLL_SN=%d, PDU_WITHOUT_POLL=%d, BYTE_WITHOUT_POLL=%d", st.tx_next_ack, st.tx_next, From 2425985ab22c74b98ff3bb67d8ba0de241efc4d4 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Tue, 15 Mar 2022 14:23:37 +0000 Subject: [PATCH 188/195] lib,rlc_am_nr: added test for poll retx timer --- .../srsran/interfaces/rlc_interface_types.h | 1 + lib/test/rlc/rlc_am_nr_test.cc | 150 +++++++++++++++++- 2 files changed, 147 insertions(+), 4 deletions(-) diff --git a/lib/include/srsran/interfaces/rlc_interface_types.h b/lib/include/srsran/interfaces/rlc_interface_types.h index 903dc3f81..9a1a8027c 100644 --- a/lib/include/srsran/interfaces/rlc_interface_types.h +++ b/lib/include/srsran/interfaces/rlc_interface_types.h @@ -254,6 +254,7 @@ public: rlc_cnfg.am_nr.max_retx_thresh = 4; rlc_cnfg.am_nr.t_reassembly = 35; rlc_cnfg.am_nr.poll_pdu = 4; + rlc_cnfg.am_nr.t_poll_retx = 45; return rlc_cnfg; } static rlc_config_t default_rlc_um_nr_config(uint32_t sn_size = 6) diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index a6c371d65..3f4b4a9cc 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -1004,11 +1004,13 @@ int segment_retx_test(rlc_am_nr_sn_size_t sn_size) rlc_am_nr_tx* tx2 = dynamic_cast(rlc2.get_tx()); rlc_am_nr_rx* rx2 = dynamic_cast(rlc2.get_rx()); - if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { + auto rlc_cnfg = rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)); + rlc_cnfg.am_nr.t_poll_retx = -1; + if (not rlc1.configure(rlc_cnfg)) { return -1; } - if (not rlc2.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { + if (not rlc2.configure(rlc_cnfg)) { return -1; } @@ -1465,11 +1467,13 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) rlc_am_nr_tx* tx2 = dynamic_cast(rlc2.get_tx()); rlc_am_nr_rx* rx2 = dynamic_cast(rlc2.get_rx()); - if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { + auto rlc_cnfg = rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)); + rlc_cnfg.am_nr.t_poll_retx = -1; + if (not rlc1.configure(rlc_cnfg)) { return -1; } - if (not rlc2.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { + if (not rlc2.configure(rlc_cnfg)) { return -1; } @@ -2289,6 +2293,143 @@ int poll_retx() return SRSRAN_SUCCESS; } +// This test checks whether re-transmissions are triggered correctly in case the t-PollRetranmission expires. +// It checks if the poll retx timer is re-armed upon receiving an ACK for POLL_SN +bool poll_retx_expiry() +{ + rlc_am_tester tester; + timer_handler timers(8); + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + test_delimit_logger delimiter("poll test retx expiry"); + + srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); + srslog::fetch_basic_logger("RLC_AM_2").set_hex_dump_max_size(100); + + rlc_config_t rlc_cnfg = rlc_config_t::default_rlc_am_nr_config(); + + rlc_cnfg.am_nr.t_poll_retx = 65; + rlc_cnfg.am_nr.poll_pdu = -1; + rlc_cnfg.am_nr.poll_byte = -1; + rlc_cnfg.am_nr.max_retx_thresh = 6; + rlc_cnfg.am_nr.t_status_prohibit = 55; + + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + if (not rlc1.configure(rlc_cnfg)) { + return SRSRAN_ERROR; + } + + rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + if (not rlc2.configure(rlc_cnfg)) { + return SRSRAN_ERROR; + } + + unsigned hdr_no_so = 2; + unsigned hdr_with_so = 4; + // [I] SRB1 Tx SDU (135 B, tx_sdu_queue_len=1) + // [I] SRB1 Tx PDU SN=3 (91 B) + // [I] SRB1 Tx PDU SN=4 (48 B) + { + // TX a single SDU + unique_byte_buffer_t sdu = srsran::make_byte_buffer(); + TESTASSERT(sdu != nullptr); + sdu->N_bytes = 135; + for (uint32_t k = 0; k < sdu->N_bytes; ++k) { + sdu->msg[k] = 0; // Write the index into the buffer + } + sdu->md.pdcp_sn = 0; + rlc1.write_sdu(std::move(sdu)); + + // Read two PDUs. The last PDU should trigger polling, as it + // is the last SDU segment in the buffer. + unique_byte_buffer_t pdu1 = srsran::make_byte_buffer(); + TESTASSERT(pdu1 != nullptr); + pdu1->N_bytes = rlc1.read_pdu(pdu1->msg, 91); // 91 bytes PDU, 89 bytes payload + + unique_byte_buffer_t pdu2 = srsran::make_byte_buffer(); + TESTASSERT(pdu2 != nullptr); + pdu2->N_bytes = rlc1.read_pdu(pdu2->msg, 50); // 50 bytes PDU, 46 bytes payload + + // Deliver PDU2 to RLC2. PDU1 is lost + rlc2.write_pdu(pdu2->msg, pdu2->N_bytes); + + // Double-check polling status in PDUs + rlc_am_nr_pdu_header_t hdr1 = {}; + rlc_am_nr_read_data_pdu_header(pdu1.get(), srsran::rlc_am_nr_sn_size_t::size12bits, &hdr1); + rlc_am_nr_pdu_header_t hdr2 = {}; + rlc_am_nr_read_data_pdu_header(pdu2.get(), srsran::rlc_am_nr_sn_size_t::size12bits, &hdr2); + TESTASSERT_EQ(0, hdr1.p); + TESTASSERT_EQ(1, hdr2.p); + } + + // Step timers until t-PollRetransmit timer expires on RLC1 + // t-Reordering timer also will expire on RLC2, so we can get an status report. + // [I] SRB1 Schedule SN=3 for reTx + for (int cnt = 0; cnt < 65; cnt++) { + timers.step_all(); + } + + uint32_t status_size = rlc2.get_buffer_state(); + TESTASSERT_EQ(9, status_size); + + // Read status PDU from RLC2 + unique_byte_buffer_t status_buf = srsran::make_byte_buffer(); + TESTASSERT(status_buf != nullptr); + int len = rlc2.read_pdu(status_buf->msg, status_size); + status_buf->N_bytes = len; + + TESTASSERT(0 == rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check(rlc_am_nr_sn_size_t::size12bits); + rlc_am_nr_read_status_pdu(status_buf.get(), rlc_am_nr_sn_size_t::size12bits, &status_check); + TESTASSERT(status_check.ack_sn == 1); // SN=1 is first SN missing without a NACK + TESTASSERT(status_check.nacks.size() == 1); // 1 PDU lost + + // [I] SRB1 Retx PDU segment SN=3 [so=0] (83 B) (attempt 2/6) + { + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + TESTASSERT(pdu != nullptr); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, 83); + } + + // [I] SRB1 Retx PDU segment SN=3 [so=79] (14 B) (attempt 2/6) + { + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + TESTASSERT(pdu != nullptr); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, 79); + } + + // Deliver status PDU after ReTX to RLC1. This should restart t-PollRetransmission + TESTASSERT_EQ(false, rlc1.has_data()); + rlc1.write_pdu(status_buf->msg, status_buf->N_bytes); + TESTASSERT_EQ(true, rlc1.has_data()); + + // [I] SRB1 Retx PDU segment SN=3 [so=0] (83 B) (attempt 3/6) (received a NACK and retx...) + // [I] SRB1 Retx PDU segment SN=3 [so=79] (14 B) (attempt 3/6) + { + unique_byte_buffer_t pdu1 = srsran::make_byte_buffer(); + TESTASSERT(pdu1 != nullptr); + pdu1->N_bytes = rlc1.read_pdu(pdu1->msg, 83); + + unique_byte_buffer_t pdu2 = srsran::make_byte_buffer(); + TESTASSERT(pdu2 != nullptr); + pdu2->N_bytes = rlc1.read_pdu(pdu2->msg, 14); + } + + TESTASSERT_EQ(false, rlc1.has_data()); + + // Step timers until t-PollRetransmission timer expires on RLC1 + // [I] SRB1 Schedule SN=3 for reTx + for (int cnt = 0; cnt < 66; cnt++) { + timers.step_all(); + } + TESTASSERT_EQ(true, rlc1.has_data()); + srslog::fetch_basic_logger("TEST").info("t-Poll Retransmssion successfully restarted."); + + return SRSRAN_SUCCESS; +} + int main() { // Setup the log message spy to intercept error and warning log entries from RLC @@ -2333,5 +2474,6 @@ int main() TESTASSERT(poll_pdu() == SRSRAN_SUCCESS); TESTASSERT(poll_byte() == SRSRAN_SUCCESS); TESTASSERT(poll_retx() == SRSRAN_SUCCESS); + TESTASSERT(poll_retx_expiry() == SRSRAN_SUCCESS); return SRSRAN_SUCCESS; } From 0ee20d7a0c618bb1fb5226c26f619388cca08355 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Mon, 4 Apr 2022 17:25:22 +0100 Subject: [PATCH 189/195] lib,rlc_am_nr: addressing reviewers comments: * Tweaked logs * Addressed a missing modulus in handling a state variable. * made restarting t-PollRetransmission look more like the comment. --- lib/src/rlc/rlc_am_nr.cc | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index f1d256829..527c84b7f 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -85,7 +85,6 @@ bool rlc_am_nr_tx::configure(const rlc_config_t& cfg_) if (cfg.t_poll_retx > 0) { poll_retransmit_timer.set(static_cast(cfg.t_poll_retx), [this](uint32_t timerid) { timer_expired(timerid); }); - RlcInfo("configured poll retransmission timer. t-pollRetransmission=%d ms", cfg.t_poll_retx); } tx_enabled = true; @@ -936,7 +935,7 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) * - if t-PollRetransmit is running: * - stop and reset t-PollRetransmit. */ - if (st.poll_sn <= status.ack_sn) { + if (tx_mod_base_nr(st.poll_sn) <= tx_mod_base_nr(status.ack_sn)) { if (poll_retransmit_timer.is_running()) { poll_retransmit_timer.stop(); } @@ -1075,11 +1074,11 @@ uint8_t rlc_am_nr_tx::get_pdu_poll(uint32_t sn, bool is_retx, uint32_t sdu_bytes st.byte_without_poll += sdu_bytes; if (cfg.poll_pdu > 0 && st.pdu_without_poll >= (uint32_t)cfg.poll_pdu) { poll = 1; - RlcInfo("Setting poll bit due to PollPDU. SN=%d", sn); + RlcDebug("Setting poll bit due to PollPDU. SN=%d", sn); } if (cfg.poll_byte > 0 && st.byte_without_poll >= (uint32_t)cfg.poll_byte) { poll = 1; - RlcInfo("Setting poll bit due to PollBYTE. SN=%d", sn); + RlcDebug("Setting poll bit due to PollBYTE. SN=%d", sn); } } @@ -1092,7 +1091,7 @@ uint8_t rlc_am_nr_tx::get_pdu_poll(uint32_t sn, bool is_retx, uint32_t sdu_bytes */ if ((tx_sdu_queue.is_empty() && retx_queue->empty() && sdu_under_segmentation_sn == INVALID_RLC_SN) || tx_window->full()) { - RlcInfo("Setting poll bit due to empty buffers/inablity to TX. SN=%d", sn); + RlcDebug("Setting poll bit due to empty buffers/inablity to TX. SN=%d", sn); poll = 1; } @@ -1114,11 +1113,13 @@ uint8_t rlc_am_nr_tx::get_pdu_poll(uint32_t sn, bool is_retx, uint32_t sdu_bytes if (tx_mod_base_nr(sn) > tx_mod_base_nr(st.poll_sn)) { st.poll_sn = sn; } - if (poll_retransmit_timer.is_running()) { - poll_retransmit_timer.stop(); - } if (cfg.t_poll_retx > 0) { - poll_retransmit_timer.run(); + if (not poll_retransmit_timer.is_running()) { + poll_retransmit_timer.run(); + } else { + poll_retransmit_timer.stop(); + poll_retransmit_timer.run(); + } RlcInfo("Started t-PollRetransmit. POLL_SN=%d", sn); } } @@ -1173,13 +1174,9 @@ void rlc_am_nr_tx::timer_expired(uint32_t timeout_id) * - include a poll in an AMD PDU as described in section 5.3.3.2. */ if ((tx_sdu_queue.is_empty() && retx_queue->empty()) || tx_window->full()) { - if (tx_window->empty()) { - // Nothing to RETX - return; - } // Fully RETX first RLC SDU that has not been acked if (not tx_window->has_sn(st.tx_next_ack)) { - RlcError("TX window not empty, but TX_NEXT_ACK=%d not in TX_WINDOW", st.tx_next_ack); + RlcError("Tx_Next_Ack not in tx_widow. Tx_Next_Ack=%d, tx_window_size=%d", st.tx_next_ack, tx_window->size()); return; } rlc_amd_retx_nr_t& retx = retx_queue->push(); From 42f7094a0e472b5bbf19abd5e4a6438dc5f8ff1b Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Mon, 11 Apr 2022 17:17:11 +0100 Subject: [PATCH 190/195] lib,rlc_am_nr: changed order in which the ack of poll SN is checked --- lib/src/rlc/rlc_am_nr.cc | 51 +++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 527c84b7f..52278d49d 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -773,6 +773,21 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) rlc_am_nr_read_status_pdu(payload, nof_bytes, cfg.tx_sn_field_length, &status); log_rlc_am_nr_status_pdu_to_string(logger.info, "RX status PDU: %s", &status, parent->rb_name); + /** + * Section 5.3.3.3: Reception of a STATUS report + * - if the STATUS report comprises a positive or negative acknowledgement for the RLC SDU with sequence + * number equal to POLL_SN: + * - if t-PollRetransmit is running: + * - stop and reset t-PollRetransmit. + * + */ + if (tx_mod_base_nr(st.poll_sn) <= tx_mod_base_nr(status.ack_sn)) { + if (poll_retransmit_timer.is_running()) { + RlcDebug("Received ACK or NACK for POLL_SN=%d. Stopping t-PollRetransmit", st.poll_sn); + poll_retransmit_timer.stop(); + } + } + /* * - if the SN of the corresponding RLC SDU falls within the range * TX_Next_Ack <= SN < = the highest SN of the AMD PDU among the AMD PDUs submitted to lower layer: @@ -801,12 +816,6 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) } RlcDebug("Processed status report ACKs. ACK_SN=%d. Tx_Next_Ack=%d", status.ack_sn, st.tx_next_ack); - // Notify PDCP - if (not notify_info_vec.empty()) { - parent->pdcp->notify_delivery(parent->lcid, notify_info_vec); - } - notify_info_vec.clear(); - // Process N_nacks std::set retx_sn_set; // Set of PDU SNs added for retransmission (no duplicates) for (uint32_t nack_idx = 0; nack_idx < status.nacks.size(); nack_idx++) { @@ -928,18 +937,11 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) check_sn_reached_max_retx(retx_sn); } - /** - * Section 5.3.3.3: Reception of a STATUS report - * - if the STATUS report comprises a positive or negative acknowledgement for the RLC SDU with sequence - * number equal to POLL_SN: - * - if t-PollRetransmit is running: - * - stop and reset t-PollRetransmit. - */ - if (tx_mod_base_nr(st.poll_sn) <= tx_mod_base_nr(status.ack_sn)) { - if (poll_retransmit_timer.is_running()) { - poll_retransmit_timer.stop(); - } + // Notify PDCP + if (not notify_info_vec.empty()) { + parent->pdcp->notify_delivery(parent->lcid, notify_info_vec); } + notify_info_vec.clear(); } /** @@ -1174,11 +1176,22 @@ void rlc_am_nr_tx::timer_expired(uint32_t timeout_id) * - include a poll in an AMD PDU as described in section 5.3.3.2. */ if ((tx_sdu_queue.is_empty() && retx_queue->empty()) || tx_window->full()) { - // Fully RETX first RLC SDU that has not been acked + if (tx_window->empty()) { + RlcError("t-PollRetransmit expired, but the tx_window is empty. POLL_SN=%d, Tx_Next_Ack=%d, tx_window_size=%d", + st.poll_sn, + st.tx_next_ack, + tx_window->size()); + return; + } if (not tx_window->has_sn(st.tx_next_ack)) { - RlcError("Tx_Next_Ack not in tx_widow. Tx_Next_Ack=%d, tx_window_size=%d", st.tx_next_ack, tx_window->size()); + RlcError("t-PollRetransmit expired, but Tx_Next_Ack is not in the tx_widow. POLL_SN=%d, Tx_Next_Ack=%d, " + "tx_window_size=%d", + st.poll_sn, + st.tx_next_ack, + tx_window->size()); return; } + // Fully RETX first RLC SDU that has not been acked rlc_amd_retx_nr_t& retx = retx_queue->push(); retx.sn = st.tx_next_ack; retx.is_segment = false; From 361d3f46e065fc0514d701c9528aa98328fb542c Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Wed, 13 Apr 2022 11:40:19 +0100 Subject: [PATCH 191/195] lib,rlc_am_nr: starting to fix retx poll expiry test --- lib/src/rlc/rlc_am_nr.cc | 2 +- lib/test/rlc/rlc_am_nr_test.cc | 152 +++++++++++++++++++-------------- 2 files changed, 88 insertions(+), 66 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 52278d49d..6e90bb1db 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -1162,7 +1162,7 @@ void rlc_am_nr_tx::timer_expired(uint32_t timeout_id) { std::unique_lock lock(mutex); - // Status Prohibit + // t-PollRetransmit if (poll_retransmit_timer.is_valid() && poll_retransmit_timer.id() == timeout_id) { RlcDebug("Poll retransmission timer expired after %dms", poll_retransmit_timer.duration()); debug_state(); diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index 3f4b4a9cc..205a9b4bf 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -2077,7 +2077,7 @@ int discard_test(rlc_am_nr_sn_size_t sn_size) } // Test p bit set on new TX with PollPDU -int poll_pdu() +int poll_pdu(rlc_am_nr_sn_size_t sn_size) { rlc_am_tester tester; timer_handler timers(8); @@ -2087,14 +2087,16 @@ int poll_pdu() srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); - rlc_config_t rlc_cnfg = {}; - rlc_cnfg.rat = srsran_rat_t::nr; - rlc_cnfg.rlc_mode = rlc_mode_t::am; - rlc_cnfg.am_nr.poll_pdu = 4; - rlc_cnfg.am_nr.poll_byte = 3000; - rlc_cnfg.am_nr.t_status_prohibit = 8; - rlc_cnfg.am_nr.max_retx_thresh = 8; - rlc_cnfg.am_nr.t_reassembly = 35; + rlc_config_t rlc_cnfg = {}; + rlc_cnfg.rat = srsran_rat_t::nr; + rlc_cnfg.rlc_mode = rlc_mode_t::am; + rlc_cnfg.am_nr.tx_sn_field_length = sn_size; // Number of bits used for tx (UL) sequence number + rlc_cnfg.am_nr.rx_sn_field_length = sn_size; // Number of bits used for rx (DL) sequence number + rlc_cnfg.am_nr.poll_pdu = 4; + rlc_cnfg.am_nr.poll_byte = 3000; + rlc_cnfg.am_nr.t_status_prohibit = 8; + rlc_cnfg.am_nr.max_retx_thresh = 8; + rlc_cnfg.am_nr.t_reassembly = 35; // Test p bit set on new TX with PollPDU { @@ -2113,12 +2115,13 @@ int poll_pdu() rlc1.write_sdu(std::move(sdu)); } uint32_t num_tx_pdus = 6; + uint32_t pdu_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 3 : 4; for (uint32_t i = 0; i < num_tx_pdus; ++i) { unique_byte_buffer_t pdu = srsran::make_byte_buffer(); TESTASSERT(pdu != nullptr); - pdu->N_bytes = rlc1.read_pdu(pdu->msg, 3); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, pdu_size); rlc_am_nr_pdu_header_t hdr; - rlc_am_nr_read_data_pdu_header(pdu.get(), rlc_am_nr_sn_size_t::size18bits, &hdr); + rlc_am_nr_read_data_pdu_header(pdu.get(), sn_size, &hdr); if (i != 3 && i != 5) { // P bit set for PollPDU and for empty TX queue TESTASSERT_EQ(0, hdr.p); } else { @@ -2130,7 +2133,7 @@ int poll_pdu() } // Test p bit set on new TX with PollBYTE -int poll_byte() +int poll_byte(rlc_am_nr_sn_size_t sn_size) { rlc_am_tester tester; timer_handler timers(8); @@ -2140,14 +2143,16 @@ int poll_byte() srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); - rlc_config_t rlc_cnfg = {}; - rlc_cnfg.rat = srsran_rat_t::nr; - rlc_cnfg.rlc_mode = rlc_mode_t::am; - rlc_cnfg.am_nr.poll_pdu = 4; - rlc_cnfg.am_nr.poll_byte = 3000; - rlc_cnfg.am_nr.t_status_prohibit = 8; - rlc_cnfg.am_nr.max_retx_thresh = 8; - rlc_cnfg.am_nr.t_reassembly = 35; + rlc_config_t rlc_cnfg = {}; + rlc_cnfg.rat = srsran_rat_t::nr; + rlc_cnfg.rlc_mode = rlc_mode_t::am; + rlc_cnfg.am_nr.tx_sn_field_length = sn_size; // Number of bits used for tx (UL) sequence number + rlc_cnfg.am_nr.rx_sn_field_length = sn_size; // Number of bits used for rx (DL) sequence number + rlc_cnfg.am_nr.poll_pdu = 4; + rlc_cnfg.am_nr.poll_byte = 3000; + rlc_cnfg.am_nr.t_status_prohibit = 8; + rlc_cnfg.am_nr.max_retx_thresh = 8; + rlc_cnfg.am_nr.t_reassembly = 35; rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); if (not rlc1.configure(rlc_cnfg)) { @@ -2163,11 +2168,13 @@ int poll_byte() sdu->md.pdcp_sn = i; rlc1.write_sdu(std::move(sdu)); } - uint32_t num_tx_pdus = num_tx_sdus; + uint32_t num_tx_pdus = num_tx_sdus; + uint32_t small_pdu_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 3 : 4; + uint32_t large_pdu_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 3001 : 3002; for (uint32_t i = 0; i < num_tx_pdus; ++i) { unique_byte_buffer_t pdu = srsran::make_byte_buffer(); TESTASSERT(pdu != nullptr); - uint32_t nof_bytes = i == 0 ? 3001 : 3; + uint32_t nof_bytes = i == 0 ? large_pdu_size : small_pdu_size; pdu->N_bytes = rlc1.read_pdu(pdu->msg, nof_bytes); TESTASSERT_EQ(nof_bytes, pdu->N_bytes); rlc_am_nr_pdu_header_t hdr; @@ -2182,7 +2189,7 @@ int poll_byte() } // Test p bit set on RETXes that cause an empty retx queue. -int poll_retx() +int poll_retx(rlc_am_nr_sn_size_t sn_size) { rlc_am_tester tester; timer_handler timers(8); @@ -2192,14 +2199,16 @@ int poll_retx() srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); - rlc_config_t rlc_cnfg = {}; - rlc_cnfg.rat = srsran_rat_t::nr; - rlc_cnfg.rlc_mode = rlc_mode_t::am; - rlc_cnfg.am_nr.poll_pdu = 4; - rlc_cnfg.am_nr.poll_byte = 3000; - rlc_cnfg.am_nr.t_status_prohibit = 8; - rlc_cnfg.am_nr.max_retx_thresh = 8; - rlc_cnfg.am_nr.t_reassembly = 35; + rlc_config_t rlc_cnfg = {}; + rlc_cnfg.rat = srsran_rat_t::nr; + rlc_cnfg.rlc_mode = rlc_mode_t::am; + rlc_cnfg.am_nr.tx_sn_field_length = sn_size; // Number of bits used for tx (UL) sequence number + rlc_cnfg.am_nr.rx_sn_field_length = sn_size; // Number of bits used for rx (DL) sequence number + rlc_cnfg.am_nr.poll_pdu = 4; + rlc_cnfg.am_nr.poll_byte = 3000; + rlc_cnfg.am_nr.t_status_prohibit = 8; + rlc_cnfg.am_nr.max_retx_thresh = 8; + rlc_cnfg.am_nr.t_reassembly = 35; rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); if (not rlc1.configure(rlc_cnfg)) { @@ -2221,12 +2230,13 @@ int poll_retx() { // Read 3 PDUs and NACK the second one uint32_t num_tx_pdus = 3; + uint32_t pdu_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 3 : 4; for (uint32_t i = 0; i < num_tx_pdus; ++i) { unique_byte_buffer_t pdu = srsran::make_byte_buffer(); TESTASSERT(pdu != nullptr); - pdu->N_bytes = rlc1.read_pdu(pdu->msg, 3); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, pdu_size); rlc_am_nr_pdu_header_t hdr; - rlc_am_nr_read_data_pdu_header(pdu.get(), rlc_am_nr_sn_size_t::size12bits, &hdr); + rlc_am_nr_read_data_pdu_header(pdu.get(), sn_size, &hdr); TESTASSERT_EQ(0, hdr.p); } } @@ -2246,13 +2256,14 @@ int poll_retx() { // Read 2 PDUs, uint32_t num_tx_pdus = 3; + uint32_t pdu_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 3 : 4; for (uint32_t i = 0; i < num_tx_pdus; ++i) { unique_byte_buffer_t pdu = srsran::make_byte_buffer(); TESTASSERT(pdu != nullptr); - pdu->N_bytes = rlc1.read_pdu(pdu->msg, 3); - TESTASSERT_EQ(3, pdu->N_bytes); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, pdu_size); + TESTASSERT_EQ(pdu_size, pdu->N_bytes); rlc_am_nr_pdu_header_t hdr; - rlc_am_nr_read_data_pdu_header(pdu.get(), rlc_am_nr_sn_size_t::size12bits, &hdr); + rlc_am_nr_read_data_pdu_header(pdu.get(), sn_size, &hdr); if (i == 0) { TESTASSERT_EQ(0, hdr.p); // No poll since pollPDU is not incremented for RETX TESTASSERT_EQ(1, hdr.sn); @@ -2277,13 +2288,14 @@ int poll_retx() { // Read 1 RETX PDU. Empty retx buffer, so poll should be set uint32_t num_tx_pdus = 1; + uint32_t pdu_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 3 : 4; for (uint32_t i = 0; i < num_tx_pdus; ++i) { unique_byte_buffer_t pdu = srsran::make_byte_buffer(); TESTASSERT(pdu != nullptr); - pdu->N_bytes = rlc1.read_pdu(pdu->msg, 3); - TESTASSERT_EQ(3, pdu->N_bytes); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, pdu_size); + TESTASSERT_EQ(pdu_size, pdu->N_bytes); rlc_am_nr_pdu_header_t hdr; - rlc_am_nr_read_data_pdu_header(pdu.get(), rlc_am_nr_sn_size_t::size12bits, &hdr); + rlc_am_nr_read_data_pdu_header(pdu.get(), sn_size, &hdr); if (i == 0) { TESTASSERT_EQ(1, hdr.p); // Poll set because of empty retx buffer TESTASSERT_EQ(1, hdr.sn); @@ -2295,7 +2307,7 @@ int poll_retx() // This test checks whether re-transmissions are triggered correctly in case the t-PollRetranmission expires. // It checks if the poll retx timer is re-armed upon receiving an ACK for POLL_SN -bool poll_retx_expiry() +bool poll_retx_expiry(rlc_am_nr_sn_size_t sn_size) { rlc_am_tester tester; timer_handler timers(8); @@ -2308,11 +2320,13 @@ bool poll_retx_expiry() rlc_config_t rlc_cnfg = rlc_config_t::default_rlc_am_nr_config(); - rlc_cnfg.am_nr.t_poll_retx = 65; - rlc_cnfg.am_nr.poll_pdu = -1; - rlc_cnfg.am_nr.poll_byte = -1; - rlc_cnfg.am_nr.max_retx_thresh = 6; - rlc_cnfg.am_nr.t_status_prohibit = 55; + rlc_cnfg.am_nr.tx_sn_field_length = sn_size; // Number of bits used for tx (UL) sequence number + rlc_cnfg.am_nr.rx_sn_field_length = sn_size; // Number of bits used for rx (DL) sequence number + rlc_cnfg.am_nr.t_poll_retx = 65; + rlc_cnfg.am_nr.poll_pdu = -1; + rlc_cnfg.am_nr.poll_byte = -1; + rlc_cnfg.am_nr.max_retx_thresh = 6; + rlc_cnfg.am_nr.t_status_prohibit = 55; rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); if (not rlc1.configure(rlc_cnfg)) { @@ -2326,9 +2340,9 @@ bool poll_retx_expiry() unsigned hdr_no_so = 2; unsigned hdr_with_so = 4; - // [I] SRB1 Tx SDU (135 B, tx_sdu_queue_len=1) - // [I] SRB1 Tx PDU SN=3 (91 B) - // [I] SRB1 Tx PDU SN=4 (48 B) + // Tx SDU with 135 B of data + // Read it in two PDU segments, so=0 (89B of data) + // and so=89 (46B of data) { // TX a single SDU unique_byte_buffer_t sdu = srsran::make_byte_buffer(); @@ -2342,22 +2356,24 @@ bool poll_retx_expiry() // Read two PDUs. The last PDU should trigger polling, as it // is the last SDU segment in the buffer. - unique_byte_buffer_t pdu1 = srsran::make_byte_buffer(); + uint32_t pdu1_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 91 : 92; + unique_byte_buffer_t pdu1 = srsran::make_byte_buffer(); TESTASSERT(pdu1 != nullptr); - pdu1->N_bytes = rlc1.read_pdu(pdu1->msg, 91); // 91 bytes PDU, 89 bytes payload + pdu1->N_bytes = rlc1.read_pdu(pdu1->msg, pdu1_size); // 91 bytes PDU, 89 bytes payload - unique_byte_buffer_t pdu2 = srsran::make_byte_buffer(); + uint32_t pdu2_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 50 : 51; + unique_byte_buffer_t pdu2 = srsran::make_byte_buffer(); TESTASSERT(pdu2 != nullptr); - pdu2->N_bytes = rlc1.read_pdu(pdu2->msg, 50); // 50 bytes PDU, 46 bytes payload + pdu2->N_bytes = rlc1.read_pdu(pdu2->msg, pdu2_size); // 50 bytes PDU, 46 bytes payload // Deliver PDU2 to RLC2. PDU1 is lost rlc2.write_pdu(pdu2->msg, pdu2->N_bytes); // Double-check polling status in PDUs rlc_am_nr_pdu_header_t hdr1 = {}; - rlc_am_nr_read_data_pdu_header(pdu1.get(), srsran::rlc_am_nr_sn_size_t::size12bits, &hdr1); + rlc_am_nr_read_data_pdu_header(pdu1.get(), sn_size, &hdr1); rlc_am_nr_pdu_header_t hdr2 = {}; - rlc_am_nr_read_data_pdu_header(pdu2.get(), srsran::rlc_am_nr_sn_size_t::size12bits, &hdr2); + rlc_am_nr_read_data_pdu_header(pdu2.get(), sn_size, &hdr2); TESTASSERT_EQ(0, hdr1.p); TESTASSERT_EQ(1, hdr2.p); } @@ -2386,27 +2402,32 @@ bool poll_retx_expiry() TESTASSERT(status_check.ack_sn == 1); // SN=1 is first SN missing without a NACK TESTASSERT(status_check.nacks.size() == 1); // 1 PDU lost - // [I] SRB1 Retx PDU segment SN=3 [so=0] (83 B) (attempt 2/6) + // Fully RETX first RLC SDU that has not been acked + test_logger.info("buf=%d", rlc1.get_buffer_state()); + TESTASSERT((135 + 2) == rlc1.get_buffer_state()); + + // Retx first SDU segment (81B of data) { unique_byte_buffer_t pdu = srsran::make_byte_buffer(); TESTASSERT(pdu != nullptr); pdu->N_bytes = rlc1.read_pdu(pdu->msg, 83); } - // [I] SRB1 Retx PDU segment SN=3 [so=79] (14 B) (attempt 2/6) + // Retx second SDU segment (54B of data) { unique_byte_buffer_t pdu = srsran::make_byte_buffer(); TESTASSERT(pdu != nullptr); - pdu->N_bytes = rlc1.read_pdu(pdu->msg, 79); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, 58); } // Deliver status PDU after ReTX to RLC1. This should restart t-PollRetransmission + // It NACKs SDU segment 0:81 and partially 81:135 TESTASSERT_EQ(false, rlc1.has_data()); rlc1.write_pdu(status_buf->msg, status_buf->N_bytes); TESTASSERT_EQ(true, rlc1.has_data()); - // [I] SRB1 Retx PDU segment SN=3 [so=0] (83 B) (attempt 3/6) (received a NACK and retx...) - // [I] SRB1 Retx PDU segment SN=3 [so=79] (14 B) (attempt 3/6) + // [I] SRB1 Retx SDU segment (81 B of data) + // [I] SRB1 Retx PDU segment (10 B of data) { unique_byte_buffer_t pdu1 = srsran::make_byte_buffer(); TESTASSERT(pdu1 != nullptr); @@ -2417,7 +2438,8 @@ bool poll_retx_expiry() pdu2->N_bytes = rlc1.read_pdu(pdu2->msg, 14); } - TESTASSERT_EQ(false, rlc1.has_data()); + TESTASSERT_EQ(true, rlc1.has_data()); // We still have 44 bytes of data + TESTASSERT_EQ(48, rlc1.get_buffer_state()); // We still have 44 bytes of data // Step timers until t-PollRetransmission timer expires on RLC1 // [I] SRB1 Schedule SN=3 for reTx @@ -2425,7 +2447,7 @@ bool poll_retx_expiry() timers.step_all(); } TESTASSERT_EQ(true, rlc1.has_data()); - srslog::fetch_basic_logger("TEST").info("t-Poll Retransmssion successfully restarted."); + srslog::fetch_basic_logger("TEST").info("t-PollRetransmssion successfully restarted."); return SRSRAN_SUCCESS; } @@ -2470,10 +2492,10 @@ int main() TESTASSERT(max_retx_lost_sdu_test(sn_size) == SRSRAN_SUCCESS); TESTASSERT(max_retx_lost_segments_test(sn_size) == SRSRAN_SUCCESS); TESTASSERT(discard_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(poll_pdu(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(poll_byte(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(poll_retx(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(poll_retx_expiry(sn_size) == SRSRAN_SUCCESS); } - TESTASSERT(poll_pdu() == SRSRAN_SUCCESS); - TESTASSERT(poll_byte() == SRSRAN_SUCCESS); - TESTASSERT(poll_retx() == SRSRAN_SUCCESS); - TESTASSERT(poll_retx_expiry() == SRSRAN_SUCCESS); return SRSRAN_SUCCESS; } From d7a32a0ea5d7e552c7ff69616bd26f2c502d37aa Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Thu, 21 Apr 2022 12:38:47 +0100 Subject: [PATCH 192/195] lib,rlc_am_nr: sending first SDU segment if t-PollRetransmit expires instead of full SDU. Adressing test accordingly --- lib/src/rlc/rlc_am_nr.cc | 25 ++++++++--- lib/test/rlc/rlc_am_nr_test.cc | 81 ++++++++++++++++++---------------- 2 files changed, 64 insertions(+), 42 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 6e90bb1db..35657c281 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -1191,13 +1191,28 @@ void rlc_am_nr_tx::timer_expired(uint32_t timeout_id) tx_window->size()); return; } - // Fully RETX first RLC SDU that has not been acked + // RETX first RLC SDU that has not been ACKed + // or first SDU segment of the first RLC SDU + // that has not been acked rlc_amd_retx_nr_t& retx = retx_queue->push(); retx.sn = st.tx_next_ack; - retx.is_segment = false; - retx.so_start = 0; - retx.segment_length = (*tx_window)[st.tx_next_ack].sdu_buf->N_bytes; - retx.current_so = 0; + if ((*tx_window)[st.tx_next_ack].segment_list.empty()) { + // Full SDU + retx.is_segment = false; + retx.so_start = 0; + retx.segment_length = (*tx_window)[st.tx_next_ack].sdu_buf->N_bytes; + retx.current_so = 0; + } else { + // To make sure we do not mess up the segment list + // We RETX an SDU segment instead of the full SDU + // if the SDU has been segmented before. + // As we cannot know which segments have been ACKed before + // we simply RETX the first one. + retx.is_segment = true; + retx.so_start = 0; + retx.current_so = 0; + retx.segment_length = (*tx_window)[st.tx_next_ack].segment_list.begin()->payload_len; + } } return; } diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index 205a9b4bf..a70a769cc 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -2083,7 +2083,7 @@ int poll_pdu(rlc_am_nr_sn_size_t sn_size) timer_handler timers(8); auto& test_logger = srslog::fetch_basic_logger("TESTER "); - test_delimit_logger delimiter("poll test pollPDU"); + test_delimit_logger delimiter("pollPDU test ({} bit SN)", to_number(sn_size)); srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); @@ -2139,7 +2139,7 @@ int poll_byte(rlc_am_nr_sn_size_t sn_size) timer_handler timers(8); auto& test_logger = srslog::fetch_basic_logger("TESTER "); - test_delimit_logger delimiter("poll test pollBYTE"); + test_delimit_logger delimiter("pollBYTE test ({} bit SN)", to_number(sn_size)); srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); @@ -2195,7 +2195,7 @@ int poll_retx(rlc_am_nr_sn_size_t sn_size) timer_handler timers(8); auto& test_logger = srslog::fetch_basic_logger("TESTER "); - test_delimit_logger delimiter("poll test retx"); + test_delimit_logger delimiter("poll retx test ({} bit SN)", to_number(sn_size)); srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); @@ -2313,7 +2313,7 @@ bool poll_retx_expiry(rlc_am_nr_sn_size_t sn_size) timer_handler timers(8); auto& test_logger = srslog::fetch_basic_logger("TESTER "); - test_delimit_logger delimiter("poll test retx expiry"); + test_delimit_logger delimiter("poll retx expiry test ({} bit SN)", to_number(sn_size)); srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); srslog::fetch_basic_logger("RLC_AM_2").set_hex_dump_max_size(100); @@ -2338,8 +2338,8 @@ bool poll_retx_expiry(rlc_am_nr_sn_size_t sn_size) return SRSRAN_ERROR; } - unsigned hdr_no_so = 2; - unsigned hdr_with_so = 4; + unsigned hdr_no_so = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + unsigned hdr_with_so = sn_size == rlc_am_nr_sn_size_t::size12bits ? 4 : 5; // Tx SDU with 135 B of data // Read it in two PDU segments, so=0 (89B of data) // and so=89 (46B of data) @@ -2356,15 +2356,15 @@ bool poll_retx_expiry(rlc_am_nr_sn_size_t sn_size) // Read two PDUs. The last PDU should trigger polling, as it // is the last SDU segment in the buffer. - uint32_t pdu1_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 91 : 92; + uint32_t pdu1_size = 89 + hdr_no_so; unique_byte_buffer_t pdu1 = srsran::make_byte_buffer(); TESTASSERT(pdu1 != nullptr); - pdu1->N_bytes = rlc1.read_pdu(pdu1->msg, pdu1_size); // 91 bytes PDU, 89 bytes payload + pdu1->N_bytes = rlc1.read_pdu(pdu1->msg, pdu1_size); // 89 bytes payload - uint32_t pdu2_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 50 : 51; + uint32_t pdu2_size = 46 + hdr_with_so; unique_byte_buffer_t pdu2 = srsran::make_byte_buffer(); TESTASSERT(pdu2 != nullptr); - pdu2->N_bytes = rlc1.read_pdu(pdu2->msg, pdu2_size); // 50 bytes PDU, 46 bytes payload + pdu2->N_bytes = rlc1.read_pdu(pdu2->msg, pdu2_size); // 46 bytes payload // Deliver PDU2 to RLC2. PDU1 is lost rlc2.write_pdu(pdu2->msg, pdu2->N_bytes); @@ -2379,12 +2379,34 @@ bool poll_retx_expiry(rlc_am_nr_sn_size_t sn_size) } // Step timers until t-PollRetransmit timer expires on RLC1 - // t-Reordering timer also will expire on RLC2, so we can get an status report. - // [I] SRB1 Schedule SN=3 for reTx + // t-PollRetransmit will schedule SN=0, so=0, payload_len=89 for RETX + // t-Reordering timer also will expire on RLC2, meaning we will also get a status report. + TESTASSERT_EQ(false, rlc1.has_data()); for (int cnt = 0; cnt < 65; cnt++) { timers.step_all(); } + // Make sure that the SDU segment was scheduled for RETX + TESTASSERT_EQ(89 + hdr_no_so, rlc1.get_buffer_state()); + + // Further segment RETX segment + // First SDU segment (81B of data) + { + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + TESTASSERT(pdu != nullptr); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, 81 + hdr_no_so); + } + // Second SDU segment (7B of data) + { + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + TESTASSERT(pdu != nullptr); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, 7 + hdr_with_so); + } + TESTASSERT_EQ(0, rlc1.get_buffer_state()); + + // Read status PDU from RLC2 (triggered previously from t-Reordering) + // ACK=1, NACKs=1 + // NACK_SN[0].sn=0, NACK_SN[0].so_start=0, NACK_SN[0].so_end=89 uint32_t status_size = rlc2.get_buffer_state(); TESTASSERT_EQ(9, status_size); @@ -2397,31 +2419,17 @@ bool poll_retx_expiry(rlc_am_nr_sn_size_t sn_size) TESTASSERT(0 == rlc2.get_buffer_state()); // Assert status is correct - rlc_am_nr_status_pdu_t status_check(rlc_am_nr_sn_size_t::size12bits); - rlc_am_nr_read_status_pdu(status_buf.get(), rlc_am_nr_sn_size_t::size12bits, &status_check); - TESTASSERT(status_check.ack_sn == 1); // SN=1 is first SN missing without a NACK - TESTASSERT(status_check.nacks.size() == 1); // 1 PDU lost - - // Fully RETX first RLC SDU that has not been acked - test_logger.info("buf=%d", rlc1.get_buffer_state()); - TESTASSERT((135 + 2) == rlc1.get_buffer_state()); - - // Retx first SDU segment (81B of data) - { - unique_byte_buffer_t pdu = srsran::make_byte_buffer(); - TESTASSERT(pdu != nullptr); - pdu->N_bytes = rlc1.read_pdu(pdu->msg, 83); - } - - // Retx second SDU segment (54B of data) - { - unique_byte_buffer_t pdu = srsran::make_byte_buffer(); - TESTASSERT(pdu != nullptr); - pdu->N_bytes = rlc1.read_pdu(pdu->msg, 58); - } + rlc_am_nr_status_pdu_t status_check(sn_size); + rlc_am_nr_read_status_pdu(status_buf.get(), sn_size, &status_check); + TESTASSERT(status_check.ack_sn == 1); // SN=1 is first SN missing without a NACK + TESTASSERT(status_check.nacks.size() == 1); // 1 PDU lost + TESTASSERT(status_check.nacks[0].nack_sn == 0); // SN=0 + TESTASSERT(status_check.nacks[0].so_start == 0); // SN=0 + TESTASSERT_EQ(88, status_check.nacks[0].so_end); // SN=0 + TESTASSERT_EQ(0, rlc1.get_buffer_state()); // Deliver status PDU after ReTX to RLC1. This should restart t-PollRetransmission - // It NACKs SDU segment 0:81 and partially 81:135 + // It NACKs SDU segment 0:81 and 81:89 TESTASSERT_EQ(false, rlc1.has_data()); rlc1.write_pdu(status_buf->msg, status_buf->N_bytes); TESTASSERT_EQ(true, rlc1.has_data()); @@ -2438,8 +2446,7 @@ bool poll_retx_expiry(rlc_am_nr_sn_size_t sn_size) pdu2->N_bytes = rlc1.read_pdu(pdu2->msg, 14); } - TESTASSERT_EQ(true, rlc1.has_data()); // We still have 44 bytes of data - TESTASSERT_EQ(48, rlc1.get_buffer_state()); // We still have 44 bytes of data + TESTASSERT_EQ(false, rlc1.has_data()); // We don't have any more data // Step timers until t-PollRetransmission timer expires on RLC1 // [I] SRB1 Schedule SN=3 for reTx From abfa1135762ca2a54f365c287e336d52cfc40a55 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Thu, 21 Apr 2022 15:28:02 +0100 Subject: [PATCH 193/195] lib,rlc_am_nr: fix wrong buffer state calculation for segment RETXes when SO=0 --- lib/test/rlc/rlc_am_nr_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index a70a769cc..77c40353e 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -1229,7 +1229,7 @@ int segment_retx_and_loose_segments_test(rlc_am_nr_sn_size_t sn_size) uint32_t expected_buffer_state = (header_size + payload_size) * NBUFS; TESTASSERT_EQ(expected_buffer_state, rlc1.get_buffer_state()); - // Read 5 PDUs from RLC1 (1 byte each) + // Read 5 PDUs from RLC1 (each with a full SDU) for (int i = 0; i < NBUFS; i++) { uint32_t len = rlc1.read_pdu(pdu_bufs[i].msg, header_size + payload_size); pdu_bufs[i].N_bytes = len; From 4d59989f728b95b6915daf17ca196d09bf1811b6 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Thu, 21 Apr 2022 16:10:17 +0100 Subject: [PATCH 194/195] lib,rlc_am_nr: fix t-PollRetransmion timer test to work with 18bit SN --- lib/test/rlc/rlc_am_nr_test.cc | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index 77c40353e..2529e0b5f 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -2338,8 +2338,11 @@ bool poll_retx_expiry(rlc_am_nr_sn_size_t sn_size) return SRSRAN_ERROR; } - unsigned hdr_no_so = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; - unsigned hdr_with_so = sn_size == rlc_am_nr_sn_size_t::size12bits ? 4 : 5; + unsigned hdr_no_so = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + unsigned hdr_with_so = sn_size == rlc_am_nr_sn_size_t::size12bits ? 4 : 5; + unsigned ack_size = 3; + unsigned nack_size_no_so = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + unsigned nack_size_with_so = sn_size == rlc_am_nr_sn_size_t::size12bits ? (2 + 4) : (3 + 4); // Tx SDU with 135 B of data // Read it in two PDU segments, so=0 (89B of data) // and so=89 (46B of data) @@ -2396,11 +2399,11 @@ bool poll_retx_expiry(rlc_am_nr_sn_size_t sn_size) TESTASSERT(pdu != nullptr); pdu->N_bytes = rlc1.read_pdu(pdu->msg, 81 + hdr_no_so); } - // Second SDU segment (7B of data) + // Second SDU segment (8B of data) { unique_byte_buffer_t pdu = srsran::make_byte_buffer(); TESTASSERT(pdu != nullptr); - pdu->N_bytes = rlc1.read_pdu(pdu->msg, 7 + hdr_with_so); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, 8 + hdr_with_so); } TESTASSERT_EQ(0, rlc1.get_buffer_state()); @@ -2408,7 +2411,7 @@ bool poll_retx_expiry(rlc_am_nr_sn_size_t sn_size) // ACK=1, NACKs=1 // NACK_SN[0].sn=0, NACK_SN[0].so_start=0, NACK_SN[0].so_end=89 uint32_t status_size = rlc2.get_buffer_state(); - TESTASSERT_EQ(9, status_size); + TESTASSERT_EQ(ack_size + nack_size_with_so, status_size); // Read status PDU from RLC2 unique_byte_buffer_t status_buf = srsran::make_byte_buffer(); @@ -2435,15 +2438,15 @@ bool poll_retx_expiry(rlc_am_nr_sn_size_t sn_size) TESTASSERT_EQ(true, rlc1.has_data()); // [I] SRB1 Retx SDU segment (81 B of data) - // [I] SRB1 Retx PDU segment (10 B of data) + // [I] SRB1 Retx PDU segment (8 B of data) { unique_byte_buffer_t pdu1 = srsran::make_byte_buffer(); TESTASSERT(pdu1 != nullptr); - pdu1->N_bytes = rlc1.read_pdu(pdu1->msg, 83); + pdu1->N_bytes = rlc1.read_pdu(pdu1->msg, 81 + hdr_no_so); unique_byte_buffer_t pdu2 = srsran::make_byte_buffer(); TESTASSERT(pdu2 != nullptr); - pdu2->N_bytes = rlc1.read_pdu(pdu2->msg, 14); + pdu2->N_bytes = rlc1.read_pdu(pdu2->msg, 8 + hdr_with_so); } TESTASSERT_EQ(false, rlc1.has_data()); // We don't have any more data @@ -2453,7 +2456,7 @@ bool poll_retx_expiry(rlc_am_nr_sn_size_t sn_size) for (int cnt = 0; cnt < 66; cnt++) { timers.step_all(); } - TESTASSERT_EQ(true, rlc1.has_data()); + TESTASSERT_EQ(81 + hdr_no_so, rlc1.get_buffer_state()); srslog::fetch_basic_logger("TEST").info("t-PollRetransmssion successfully restarted."); return SRSRAN_SUCCESS; From 0c562336d2baefc67c804b98412940f8ae467389 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Thu, 21 Apr 2022 18:23:54 +0100 Subject: [PATCH 195/195] lib,rlc_am_nr: change update of POLL SN to be the currently being transmitted SDU (if not an RETX) --- lib/src/rlc/rlc_am_nr.cc | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 35657c281..9b6607040 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -785,7 +785,11 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) if (poll_retransmit_timer.is_running()) { RlcDebug("Received ACK or NACK for POLL_SN=%d. Stopping t-PollRetransmit", st.poll_sn); poll_retransmit_timer.stop(); + } else { + RlcDebug("Received ACK or NACK for POLL_SN=%d. t-PollRetransmit already stopped", st.poll_sn); } + } else { + RlcDebug("POLL_SN=%d > ACK_SN=%d. Not stopping t-PollRetransmit ", st.poll_sn, status.ack_sn); } /* @@ -1062,6 +1066,11 @@ void rlc_am_nr_tx::get_buffer_state(uint32_t& n_bytes_new, uint32_t& n_bytes_pri */ uint8_t rlc_am_nr_tx::get_pdu_poll(uint32_t sn, bool is_retx, uint32_t sdu_bytes) { + RlcDebug("Checking poll bit requirements for PDU. SN=%d, retx=%s, sdu_bytes=%d, POLL_SN=%d", + sn, + is_retx ? "true" : "false", + sdu_bytes, + st.poll_sn); /* For each AMD PDU or AMD PDU segment that has not been previoulsy tranmitted: * - increment PDU_WITHOUT_POLL by one; * - increment BYTE_WITHOUT_POLL by every new byte of Data field element that it maps to the Data field of the AMD @@ -1076,11 +1085,11 @@ uint8_t rlc_am_nr_tx::get_pdu_poll(uint32_t sn, bool is_retx, uint32_t sdu_bytes st.byte_without_poll += sdu_bytes; if (cfg.poll_pdu > 0 && st.pdu_without_poll >= (uint32_t)cfg.poll_pdu) { poll = 1; - RlcDebug("Setting poll bit due to PollPDU. SN=%d", sn); + RlcDebug("Setting poll bit due to PollPDU. SN=%d, POLL_SN=%d", sn, st.poll_sn); } if (cfg.poll_byte > 0 && st.byte_without_poll >= (uint32_t)cfg.poll_byte) { poll = 1; - RlcDebug("Setting poll bit due to PollBYTE. SN=%d", sn); + RlcDebug("Setting poll bit due to PollBYTE. SN=%d, POLL_SN=%d", sn, st.poll_sn); } } @@ -1093,7 +1102,7 @@ uint8_t rlc_am_nr_tx::get_pdu_poll(uint32_t sn, bool is_retx, uint32_t sdu_bytes */ if ((tx_sdu_queue.is_empty() && retx_queue->empty() && sdu_under_segmentation_sn == INVALID_RLC_SN) || tx_window->full()) { - RlcDebug("Setting poll bit due to empty buffers/inablity to TX. SN=%d", sn); + RlcDebug("Setting poll bit due to empty buffers/inablity to TX. SN=%d, POLL_SN=%d", sn, st.poll_sn); poll = 1; } @@ -1112,8 +1121,11 @@ uint8_t rlc_am_nr_tx::get_pdu_poll(uint32_t sn, bool is_retx, uint32_t sdu_bytes * - else: * - restart t-PollRetransmit. */ - if (tx_mod_base_nr(sn) > tx_mod_base_nr(st.poll_sn)) { + if (!is_retx) { + // This is not an RETX, but a new transmission + // As such it should be the highest SN submitted to the lower layers st.poll_sn = sn; + RlcDebug("Setting new POLL_SN. POLL_SN=%d", sn); } if (cfg.t_poll_retx > 0) { if (not poll_retransmit_timer.is_running()) { @@ -1122,7 +1134,7 @@ uint8_t rlc_am_nr_tx::get_pdu_poll(uint32_t sn, bool is_retx, uint32_t sdu_bytes poll_retransmit_timer.stop(); poll_retransmit_timer.run(); } - RlcInfo("Started t-PollRetransmit. POLL_SN=%d", sn); + RlcInfo("Started t-PollRetransmit. POLL_SN=%d", st.poll_sn); } } return poll;