diff --git a/lib/include/srslte/common/pcap.h b/lib/include/srslte/common/pcap.h index d52547312..18b66a141 100644 --- a/lib/include/srslte/common/pcap.h +++ b/lib/include/srslte/common/pcap.h @@ -34,6 +34,7 @@ #define MAC_LTE_DLT 147 #define NAS_LTE_DLT 148 +#define RLC_LTE_DLT 149 // UDP needs to be selected as protocol /* This structure gets written to the start of the file */ @@ -104,6 +105,67 @@ typedef struct NAS_Context_Info_s { } NAS_Context_Info_t; +/* RLC-LTE disector */ + +/* rlcMode */ +#define RLC_TM_MODE 1 +#define RLC_UM_MODE 2 +#define RLC_AM_MODE 4 +#define RLC_PREDEF 8 + +/* priority ? */ + +/* channelType */ +#define CHANNEL_TYPE_CCCH 1 +#define CHANNEL_TYPE_BCCH_BCH 2 +#define CHANNEL_TYPE_PCCH 3 +#define CHANNEL_TYPE_SRB 4 +#define CHANNEL_TYPE_DRB 5 +#define CHANNEL_TYPE_BCCH_DL_SCH 6 +#define CHANNEL_TYPE_MCCH 7 +#define CHANNEL_TYPE_MTCH 8 + +/* sequenceNumberLength */ +#define UM_SN_LENGTH_5_BITS 5 +#define UM_SN_LENGTH_10_BITS 10 +#define AM_SN_LENGTH_10_BITS 10 +#define AM_SN_LENGTH_16_BITS 16 + +/* Narrow band mode */ +typedef enum { + rlc_no_nb_mode = 0, + rlc_nb_mode = 1 +} rlc_lte_nb_mode; + +/* Context information for every RLC PDU that will be logged */ +typedef struct { + unsigned char rlcMode; + unsigned char direction; + unsigned char priority; + unsigned char sequenceNumberLength; + unsigned short ueid; + unsigned short channelType; + unsigned short channelId; /* for SRB: 1=SRB1, 2=SRB2, 3=SRB1bis; for DRB: DRB ID */ + unsigned short pduLength; + bool extendedLiField; + rlc_lte_nb_mode nbMode; +} RLC_Context_Info_t; + + +// See Wireshark's packet-rlc-lte.h for details +#define RLC_LTE_START_STRING "rlc-lte" +#define RLC_LTE_SN_LENGTH_TAG 0x02 +#define RLC_LTE_DIRECTION_TAG 0x03 +#define RLC_LTE_PRIORITY_TAG 0x04 +#define RLC_LTE_UEID_TAG 0x05 +#define RLC_LTE_CHANNEL_TYPE_TAG 0x06 +#define RLC_LTE_CHANNEL_ID_TAG 0x07 +#define RLC_LTE_EXT_LI_FIELD_TAG 0x08 +#define RLC_LTE_NB_MODE_TAG 0x09 +#define RLC_LTE_PAYLOAD_TAG 0x01 + + + /************************************************************************** * API functions for opening/closing LTE PCAP files * **************************************************************************/ @@ -247,4 +309,93 @@ inline int LTE_PCAP_NAS_WritePDU(FILE *fd, NAS_Context_Info_t *context, return 1; } + +/************************************************************************** + * API functions for writing RLC-LTE PCAP files * + **************************************************************************/ + +/* Write an individual RLC PDU (PCAP packet header + UDP header + rlc-context + rlc-pdu) */ +inline int LTE_PCAP_RLC_WritePDU(FILE *fd, RLC_Context_Info_t *context, + const unsigned char *PDU, unsigned int length) +{ + pcaprec_hdr_t packet_header; + char context_header[256]; + int offset = 0; + uint16_t tmp16; + + /* Can't write if file wasn't successfully opened */ + if (fd == NULL) { + printf("Error: Can't write to empty file handle\n"); + return 0; + } + + /*****************************************************************/ + + // Add dummy UDP header, start with src and dest port + context_header[offset++] = 0xde; + context_header[offset++] = 0xad; + context_header[offset++] = 0xbe; + context_header[offset++] = 0xef; + // length + tmp16 = length + 12; + memcpy(context_header+offset, &tmp16, 2); + offset += 2; + // dummy CRC + context_header[offset++] = 0xde; + context_header[offset++] = 0xad; + + // Start magic string + memcpy(&context_header[offset], RLC_LTE_START_STRING, strlen(RLC_LTE_START_STRING)); + offset += strlen(RLC_LTE_START_STRING); + + // Fixed field RLC mode + context_header[offset++] = context->rlcMode; + + // Conditional fields + if (context->rlcMode == RLC_UM_MODE) { + context_header[offset++] = RLC_LTE_SN_LENGTH_TAG; + context_header[offset++] = context->sequenceNumberLength; + } + + // Optional fields + context_header[offset++] = RLC_LTE_DIRECTION_TAG; + context_header[offset++] = context->direction; + + context_header[offset++] = RLC_LTE_PRIORITY_TAG; + context_header[offset++] = context->priority; + + context_header[offset++] = RLC_LTE_UEID_TAG; + tmp16 = htons(context->ueid); + memcpy(context_header+offset, &tmp16, 2); + offset += 2; + + context_header[offset++] = RLC_LTE_CHANNEL_TYPE_TAG; + tmp16 = htons(context->channelType); + memcpy(context_header+offset, &tmp16, 2); + offset += 2; + + context_header[offset++] = RLC_LTE_CHANNEL_ID_TAG; + tmp16 = htons(context->channelId); + memcpy(context_header+offset, &tmp16, 2); + offset += 2; + + // Now the actual PDU + context_header[offset++] = RLC_LTE_PAYLOAD_TAG; + + // PCAP header + struct timeval t; + gettimeofday(&t, NULL); + packet_header.ts_sec = t.tv_sec; + packet_header.ts_usec = t.tv_usec; + packet_header.incl_len = offset + length; + packet_header.orig_len = offset + length; + + // Write everything to file + fwrite(&packet_header, sizeof(pcaprec_hdr_t), 1, fd); + fwrite(context_header, 1, offset, fd); + fwrite(PDU, 1, length, fd); + + return 1; +} + #endif // SRSLTE_PCAP_H diff --git a/lib/include/srslte/common/rlc_pcap.h b/lib/include/srslte/common/rlc_pcap.h new file mode 100644 index 000000000..3a506f6ef --- /dev/null +++ b/lib/include/srslte/common/rlc_pcap.h @@ -0,0 +1,65 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsUE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef RLCPCAP_H +#define RLCPCAP_H + +#include +#include "srslte/common/pcap.h" + +namespace srslte { + +class rlc_pcap +{ +public: + rlc_pcap() {enable_write=false; ue_id=0; pcap_file = NULL; }; + void enable(bool en); + void open(const char *filename, uint32_t ue_id = 0); + void close(); + + void set_ue_id(uint16_t ue_id); + + void write_dl_am_ccch(uint8_t* pdu, uint32_t pdu_len_bytes); + void write_ul_am_ccch(uint8_t* pdu, uint32_t pdu_len_bytes); + +private: + bool enable_write; + FILE *pcap_file; + uint32_t ue_id; + void pack_and_write(uint8_t* pdu, + uint32_t pdu_len_bytes, + uint8_t mode, + uint8_t direction, + uint8_t priority, + uint8_t seqnumberlength, + uint16_t ueid, + uint16_t channel_type, + uint16_t channel_id); +}; + +} // namespace srsue + +#endif // RLCPCAP_H diff --git a/lib/include/srslte/upper/rlc_am.h b/lib/include/srslte/upper/rlc_am.h index be6c1f669..3a8c2cc16 100644 --- a/lib/include/srslte/upper/rlc_am.h +++ b/lib/include/srslte/upper/rlc_am.h @@ -217,8 +217,10 @@ bool rlc_am_is_control_pdu(byte_buffer_t *pdu); bool rlc_am_is_control_pdu(uint8_t *payload); bool rlc_am_is_pdu_segment(uint8_t *payload); std::string rlc_am_to_string(rlc_status_pdu_t *status); -bool rlc_am_start_aligned(uint8_t fi); -bool rlc_am_end_aligned(uint8_t fi); +bool rlc_am_start_aligned(const uint8_t fi); +bool rlc_am_end_aligned(const uint8_t fi); +bool rlc_am_is_unaligned(const uint8_t fi); +bool rlc_am_not_start_aligned(const uint8_t fi); } // namespace srslte diff --git a/lib/include/srslte/upper/rlc_interface.h b/lib/include/srslte/upper/rlc_interface.h index 8c869ceb9..a28d3d9bd 100644 --- a/lib/include/srslte/upper/rlc_interface.h +++ b/lib/include/srslte/upper/rlc_interface.h @@ -83,6 +83,9 @@ public: srslte_rlc_am_config_t am; srslte_rlc_um_config_t um; + // Default ctor + srslte_rlc_config_t(): rlc_mode(LIBLTE_RRC_RLC_MODE_AM), am(), um() {}; + // Constructor based on liblte's RLC config srslte_rlc_config_t(LIBLTE_RRC_RLC_CONFIG_STRUCT *cnfg) : rlc_mode(cnfg->rlc_mode), am(), um() { diff --git a/lib/src/common/rlc_pcap.cc b/lib/src/common/rlc_pcap.cc new file mode 100644 index 000000000..e6bcbe642 --- /dev/null +++ b/lib/src/common/rlc_pcap.cc @@ -0,0 +1,90 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsUE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#include +#include "srslte/srslte.h" +#include "srslte/common/pcap.h" +#include "srslte/common/rlc_pcap.h" + +namespace srslte { + +void rlc_pcap::enable(bool en) +{ + enable_write = true; +} +void rlc_pcap::open(const char* filename, uint32_t ue_id) +{ + fprintf(stdout, "Opening RLC PCAP with DLT=%d\n", RLC_LTE_DLT); + pcap_file = LTE_PCAP_Open(RLC_LTE_DLT, filename); + this->ue_id = ue_id; + enable_write = true; +} +void rlc_pcap::close() +{ + fprintf(stdout, "Saving RLC PCAP file\n"); + LTE_PCAP_Close(pcap_file); +} + +void rlc_pcap::set_ue_id(uint16_t ue_id) { + this->ue_id = ue_id; +} + +void rlc_pcap::pack_and_write(uint8_t* pdu, uint32_t pdu_len_bytes, uint8_t mode, uint8_t direction, uint8_t priority, uint8_t seqnumberlength, uint16_t ueid, uint16_t channel_type, uint16_t channel_id) +{ + if (enable_write) { + RLC_Context_Info_t context; + context.rlcMode = mode; + context.direction = direction; + context.priority = priority; + context.sequenceNumberLength = seqnumberlength; + context.ueid = ueid; + context.channelType = channel_type; + context.channelId = channel_id; + context.pduLength = pdu_len_bytes; + if (pdu) { + LTE_PCAP_RLC_WritePDU(pcap_file, &context, pdu, pdu_len_bytes); + } + } +} + +void rlc_pcap::write_dl_am_ccch(uint8_t* pdu, uint32_t pdu_len_bytes) +{ + uint8_t priority = 0; + uint8_t seqnumberlength = 0; // normal length of 10bit + uint8_t channel_id = 0; + pack_and_write(pdu, pdu_len_bytes, RLC_AM_MODE, DIRECTION_DOWNLINK, priority, seqnumberlength, ue_id, CHANNEL_TYPE_CCCH, channel_id); +} + +void rlc_pcap::write_ul_am_ccch(uint8_t* pdu, uint32_t pdu_len_bytes) +{ + uint8_t priority = 0; + uint8_t seqnumberlength = 0; // normal length of 10bit + uint8_t channel_id = 0; + pack_and_write(pdu, pdu_len_bytes, RLC_AM_MODE, DIRECTION_UPLINK, priority, seqnumberlength, ue_id, CHANNEL_TYPE_CCCH, channel_id); +} + +} diff --git a/lib/src/phy/phch/test/CMakeLists.txt b/lib/src/phy/phch/test/CMakeLists.txt index 33f236061..c93770a84 100644 --- a/lib/src/phy/phch/test/CMakeLists.txt +++ b/lib/src/phy/phch/test/CMakeLists.txt @@ -224,7 +224,76 @@ add_test(pmch_file_test pmch_file_test -i ${CMAKE_CURRENT_SOURCE_DIR}/pmch_100p add_executable(pusch_test pusch_test.c) target_link_libraries(pusch_test srslte_phy) -add_test(pusch_test pusch_test) +if (NOT DEFINED TEST_EXTENSION) + set(TEST_EXTENSION Normal) +endif(NOT DEFINED TEST_EXTENSION) + +if (TEST_EXTENSION STREQUAL Paranoid) + # All valid number of PRBs for PUSCH + set(cell_n_prb_valid 1 2 3 4 5 6 8 9 10 12 15 16 18 20 24 25 27 30 32 36 40 45 48 50 54 60 64 72 75 80 81 90 96 100) + + set(pusch_min_mcs 0) + set(pusch_max_mcs 28) + set(pusch_step_mcs 1) + + set(pusch_acks -1 0 1) + + set(pusch_cqi none wideband) + +else (TEST_EXTENSION STREQUAL Paranoid) + set(cell_n_prb_valid 6 15 25 50 100) + + set(pusch_min_mcs 0) + set(pusch_max_mcs 28) + set(pusch_step_mcs 10) + + set(pusch_acks -1 0) + + set(pusch_cqi none wideband) + +endif (TEST_EXTENSION STREQUAL Paranoid) + +foreach (cell_n_prb 6 15 25 50 75 100) + set(pusch_cell_n_prb) + foreach (n_prb ${cell_n_prb_valid}) + if (NOT (${n_prb} GREATER ${cell_n_prb})) + set(pusch_cell_n_prb ${pusch_cell_n_prb} ${n_prb}) + endif (NOT (${n_prb} GREATER ${cell_n_prb})) + endforeach (n_prb) + + foreach (n_prb ${pusch_cell_n_prb}) + foreach (mcs RANGE ${pusch_min_mcs} ${pusch_max_mcs} ${pusch_step_mcs}) + foreach (ack ${pusch_acks}) + foreach (cqi ${pusch_cqi}) + set(pusch_test_args "") + + set(pusch_test_args ${pusch_test_args} -n ${cell_n_prb}) + set(pusch_test_args ${pusch_test_args} -L ${n_prb}) + + if (NOT (${ack} EQUAL -1)) + set(pusch_test_args ${pusch_test_args} -p uci_ack ${ack}) + if (mcs EQUAL 28) + set(mcs 27) + endif (mcs EQUAL 28) + endif (NOT (${ack} EQUAL -1)) + + if (NOT (${cqi} STREQUAL none)) + set(pusch_test_args ${pusch_test_args} -p uci_cqi ${cqi}) + #if (mcs EQUAL 28) + # set(mcs 27) + #endif (mcs EQUAL 28) + endif (NOT (${cqi} STREQUAL none)) + + set(pusch_test_args ${pusch_test_args} -m ${mcs}) + + string(REGEX REPLACE "\ " "" test_name_args ${pusch_test_args}) + + add_test(pusch_test${test_name_args} pusch_test ${pusch_test_args}) + endforeach (cqi) + endforeach (ack) + endforeach (mcs) + endforeach (n_prb) +endforeach (cell_n_prb) ######################################################################## # PUCCH TEST diff --git a/lib/src/phy/phch/test/pusch_test.c b/lib/src/phy/phch/test/pusch_test.c index 1bcf6202e..4e377b42a 100644 --- a/lib/src/phy/phch/test/pusch_test.c +++ b/lib/src/phy/phch/test/pusch_test.c @@ -34,12 +34,32 @@ #include "srslte/srslte.h" srslte_cell_t cell = { - 6, // nof_prb - 1, // nof_ports - 0, // cell_id - SRSLTE_CP_NORM, // cyclic prefix - SRSLTE_PHICH_R_1_6, // PHICH resources - SRSLTE_PHICH_NORM // PHICH length + .nof_prb = 6, // nof_prb + .nof_ports = 1, // nof_ports + .id = 0, // cell_id + .cp = SRSLTE_CP_NORM, // cyclic prefix + .phich_length = SRSLTE_PHICH_NORM, // PHICH length + .phich_resources = SRSLTE_PHICH_R_1_6 // PHICH resources +}; + +srslte_uci_cfg_t uci_cfg = { + .I_offset_cqi = 6, + .I_offset_ri = 2, + .I_offset_ack = 9, +}; + +srslte_uci_data_t uci_data_tx = { + .uci_cqi = {0}, + .uci_cqi_len = 0, + .uci_ri = 0, + .uci_ri_len = 0, + .uci_ack = 0, + .uci_ack_2 = 0, + .uci_ack_len = 0, + .ri_periodic_report = false, + .scheduling_request = false, + .channel_selection = false, + .cqi_ack = false }; uint32_t cfi = 2; @@ -52,25 +72,110 @@ uint32_t n_prb = 0; int freq_hop = -1; int riv = -1; uint32_t mcs_idx = 0; +srslte_cqi_value_t cqi_value; void usage(char *prog) { - printf("Usage: %s [csrnfvmtLNF] -m MCS \n", prog); - printf("\t-m MCS index [Default %d]\n", mcs_idx); - printf("\t-c cell id [Default %d]\n", cell.id); - printf("\t-s subframe [Default %d]\n", subframe); - printf("\t-L L_prb [Default %d]\n", L_prb); - printf("\t-N n_prb [Default %d]\n", n_prb); - printf("\t-F frequency hopping [Default %d]\n", freq_hop); - printf("\t-R RIV [Default %d]\n", riv); - printf("\t-r rv_idx [Default %d]\n", rv_idx); - printf("\t-f cfi [Default %d]\n", cfi); - printf("\t-n cell.nof_prb [Default %d]\n", cell.nof_prb); + printf("Usage: %s [csrnfvmtLNF] \n", prog); + printf("\n\tCell specific parameters:\n"); + printf("\t\t-n number of PRB [Default %d]\n", cell.nof_prb); + printf("\t\t-c cell id [Default %d]\n", cell.id); + + printf("\n\tGrant parameters:\n"); + printf("\t\t-m MCS index (0-28) [Default %d]\n", mcs_idx); + printf("\t\t-L L_prb [Default %d]\n", L_prb); + printf("\t\t-N n_prb [Default %d]\n", n_prb); + printf("\t\t-F frequency hopping [Default %d]\n", freq_hop); + printf("\t\t-R RIV [Default %d]\n", riv); + printf("\t\t-r rv_idx (0-3) [Default %d]\n", rv_idx); + printf("\t\t-f cfi [Default %d]\n", cfi); + + printf("\n\tCQI/RI/ACK Reporting indexes parameters:\n"); + printf("\t\t-p I_offset_cqi (0-15) [Default %d]\n", uci_cfg.I_offset_cqi); + printf("\t\t-p I_offset_ri (0-15) [Default %d]\n", uci_cfg.I_offset_ri); + printf("\t\t-p I_offset_ack (0-15) [Default %d]\n", uci_cfg.I_offset_ack); + + printf("\n\tCQI/RI/ACK Reporting contents:\n"); + printf("\t\t-p uci_cqi (zeros, ones, random) [Default zeros]\n"); + printf("\t\t-p uci_cqi_len (0-64) [Default %d]\n", uci_data_tx.uci_cqi_len); + printf("\t\t-p uci_ri (0-1) (zeros, ones, random) [Default none]\n"); + printf("\t\t-p uci_ack (0-1) [Default none]\n"); + printf("\t\t-p uci_ack_2 (0-1) [Default none]\n"); + + printf("\n\tOther parameters:\n"); + printf("\t\t-s subframe [Default %d]\n", subframe); printf("\t-v [set srslte_verbose to debug, default none]\n"); } +void parse_extensive_param (char *param, char *arg) { + int ext_code = SRSLTE_SUCCESS; + if (!strcmp(param, "I_offset_cqi")) { + uci_cfg.I_offset_cqi = (uint32_t) atoi(arg); + if (uci_cfg.I_offset_cqi > 15) { + ext_code = SRSLTE_ERROR; + } + } else if (!strcmp(param, "I_offset_ri")) { + uci_cfg.I_offset_ri = (uint32_t) atoi(arg); + if (uci_cfg.I_offset_ri > 15) { + ext_code = SRSLTE_ERROR; + } + } else if (!strcmp(param, "I_offset_ack")) { + uci_cfg.I_offset_ack = (uint32_t) atoi(arg); + if (uci_cfg.I_offset_ack > 15) { + ext_code = SRSLTE_ERROR; + } + } else if (!strcmp(param, "uci_cqi")) { + if (!strcmp(arg, "wideband")) { + cqi_value.type = SRSLTE_CQI_TYPE_WIDEBAND; + cqi_value.wideband.wideband_cqi = (uint8_t) (rand() & 0x03); + uci_data_tx.uci_cqi_len = (uint32_t) srslte_cqi_value_unpack(uci_data_tx.uci_cqi, &cqi_value); + } else { + ext_code = SRSLTE_ERROR; + } + } else if (!strcmp(param, "uci_cqi_len")) { + uci_data_tx.uci_cqi_len = (uint32_t) atol(arg); + if (uci_data_tx.uci_cqi_len >= SRSLTE_CQI_MAX_BITS) { + ext_code = SRSLTE_ERROR; + } + } else if (!strcmp(param, "uci_ri")) { + uci_data_tx.uci_ri = (uint8_t) atol(arg); + if (uci_data_tx.uci_ri > 1) { + ext_code = SRSLTE_ERROR; + } else { + uci_data_tx.uci_ri_len = 1; + } + } else if (!strcmp(param, "uci_ack")) { + uci_data_tx.uci_ack = (uint8_t) atol(arg); + if (uci_data_tx.uci_ack > 1) { + ext_code = SRSLTE_ERROR; + } else { + uci_data_tx.uci_ack_len++; + if (uci_data_tx.uci_ack_len > 2) { + uci_data_tx.uci_ack_len = 2; + } + } + } else if (!strcmp(param, "uci_ack_2")) { + uci_data_tx.uci_ack_2 = (uint8_t) atol(arg); + if (uci_data_tx.uci_ack_2 > 1) { + ext_code = SRSLTE_ERROR; + } else { + uci_data_tx.uci_ack_len++; + if (uci_data_tx.uci_ack_len > 2) { + uci_data_tx.uci_ack_len = 2; + } + } + } else { + ext_code = SRSLTE_ERROR; + } + + if (ext_code) { + fprintf(stderr, "Error parsing parameter '%s' and argument '%s'\n", param, arg); + exit(ext_code); + } +} + void parse_args(int argc, char **argv) { int opt; - while ((opt = getopt(argc, argv, "cnfvmtsrLNFR")) != -1) { + while ((opt = getopt(argc, argv, "msLNRFrncpv")) != -1) { switch(opt) { case 'm': mcs_idx = atoi(argv[optind]); @@ -99,6 +204,10 @@ void parse_args(int argc, char **argv) { case 'c': cell.id = atoi(argv[optind]); break; + case 'p': + parse_extensive_param(argv[optind], argv[optind + 1]); + optind++; + break; case 'v': srslte_verbose++; break; @@ -125,6 +234,8 @@ int main(int argc, char **argv) { bzero(&cfg, sizeof(srslte_pusch_cfg_t)); + srslte_dft_load(); + srslte_ra_ul_dci_t dci; dci.freq_hop_fl = freq_hop; if (riv < 0) { @@ -165,13 +276,6 @@ int main(int argc, char **argv) { /* Configure PUSCH */ - printf("Encoding rv_idx=%d\n",rv_idx); - - srslte_uci_cfg_t uci_cfg; - uci_cfg.I_offset_cqi = 6; - uci_cfg.I_offset_ri = 2; - uci_cfg.I_offset_ack = 4; - if (srslte_pusch_cfg(&pusch_tx, &cfg, &grant, &uci_cfg, &ul_hopping, NULL, subframe, 0, 0)) { fprintf(stderr, "Error configuring PDSCH\n"); exit(-1); @@ -185,21 +289,9 @@ int main(int argc, char **argv) { srslte_pusch_set_rnti(&pusch_tx, rnti); srslte_pusch_set_rnti(&pusch_rx, rnti); - srslte_uci_data_t uci_data_tx; - srslte_uci_data_t uci_data_rx; - bzero(&uci_data_tx, sizeof(srslte_uci_data_t)); - uci_data_tx.uci_cqi_len = 4; - uci_data_tx.uci_ri_len = 0; - uci_data_tx.uci_ack_len = 1; + srslte_uci_data_t uci_data_rx; memcpy(&uci_data_rx, &uci_data_tx, sizeof(srslte_uci_data_t)); - for (uint32_t i=0;iget_rb_name(lcid).c_str(), header.sn, nof_bytes, pdu.buf->get_tailroom()); + pool->deallocate(pdu.buf); + return; + } memcpy(pdu.buf->msg, payload, nof_bytes); pdu.buf->N_bytes = nof_bytes; memcpy(&pdu.header, &header, sizeof(rlc_amd_pdu_header_t)); @@ -1173,6 +1187,11 @@ void rlc_am::reassemble_rx_sdus() for(uint32_t i=0; iget_tailroom() >= len) { memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_r].buf->msg, len); rx_sdu->N_bytes += len; @@ -1345,7 +1364,13 @@ bool rlc_am::add_segment_and_check(rlc_amd_rx_pdu_segments_t *pdu, rlc_amd_rx_pd count += it->header.li[i]; } } - carryover = it->buf->N_bytes - count; + + // accumulate segment sizes until end aligned PDU is received + if (rlc_am_not_start_aligned(it->header.fi)) { + carryover += it->buf->N_bytes - count; + } else { + carryover = it->buf->N_bytes - count; + } tmpit = it; if(rlc_am_end_aligned(it->header.fi) && ++tmpit != pdu->segments.end()) { header.li[header.N_li++] = carryover; @@ -1741,14 +1766,24 @@ std::string rlc_am_to_string(rlc_status_pdu_t *status) return ss.str(); } -bool rlc_am_start_aligned(uint8_t fi) +bool rlc_am_start_aligned(const uint8_t fi) { return (fi == RLC_FI_FIELD_START_AND_END_ALIGNED || fi == RLC_FI_FIELD_NOT_END_ALIGNED); } -bool rlc_am_end_aligned(uint8_t fi) +bool rlc_am_end_aligned(const uint8_t fi) { return (fi == RLC_FI_FIELD_START_AND_END_ALIGNED || fi == RLC_FI_FIELD_NOT_START_ALIGNED); } +bool rlc_am_is_unaligned(const uint8_t fi) +{ + return (fi == RLC_FI_FIELD_NOT_START_OR_END_ALIGNED); +} + +bool rlc_am_not_start_aligned(const uint8_t fi) +{ + return (fi == RLC_FI_FIELD_NOT_START_ALIGNED || fi == RLC_FI_FIELD_NOT_START_OR_END_ALIGNED); +} + } // namespace srsue diff --git a/lib/src/upper/rlc_tm.cc b/lib/src/upper/rlc_tm.cc index 423f88785..0dd16bf4d 100644 --- a/lib/src/upper/rlc_tm.cc +++ b/lib/src/upper/rlc_tm.cc @@ -52,7 +52,7 @@ void rlc_tm::init(srslte::log *log_, void rlc_tm::configure(srslte_rlc_config_t cnfg) { - log->error("Attempted to configure TM RLC entity"); + log->error("Attempted to configure TM RLC entity\n"); } void rlc_tm::empty_queue() diff --git a/lib/src/upper/rlc_um.cc b/lib/src/upper/rlc_um.cc index 6bad2a2cb..b1b2d91be 100644 --- a/lib/src/upper/rlc_um.cc +++ b/lib/src/upper/rlc_um.cc @@ -31,7 +31,7 @@ namespace srslte { -rlc_um::rlc_um() : tx_sdu_queue(16) +rlc_um::rlc_um() : tx_sdu_queue(32) { log = NULL; pdcp = NULL; @@ -467,6 +467,17 @@ void rlc_um::reassemble_rx_sdus() for(uint32_t i=0; iN_bytes == 0 && i == 0 && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) { + log->warning("Dropping PDU %d due to lost start segment\n", vr_ur); + // Advance data pointers and continue with next segment + rx_window[vr_ur].buf->msg += len; + rx_window[vr_ur].buf->N_bytes -= len; + rx_sdu->reset(); + break; + } + memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_ur].buf->msg, len); rx_sdu->N_bytes += len; rx_window[vr_ur].buf->msg += len; @@ -488,27 +499,30 @@ void rlc_um::reassemble_rx_sdus() } // Handle last segment - memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_ur].buf->msg, rx_window[vr_ur].buf->N_bytes); - rx_sdu->N_bytes += rx_window[vr_ur].buf->N_bytes; - log->debug("Writting last segment in SDU buffer. Lower edge vr_ur=%d, Buffer size=%d, segment size=%d\n", - vr_ur, rx_sdu->N_bytes, rx_window[vr_ur].buf->N_bytes); - vr_ur_in_rx_sdu = vr_ur; - if(rlc_um_end_aligned(rx_window[vr_ur].header.fi)) - { - if(pdu_lost && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) { - log->warning("Dropping remainder of lost PDU (lower edge last segments)\n"); - rx_sdu->reset(); - } else { - log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d (lower edge last segments)", rrc->get_rb_name(lcid).c_str(), vr_ur); - rx_sdu->set_timestamp(); - pdcp->write_pdu(lcid, rx_sdu); - rx_sdu = pool_allocate; - if (!rx_sdu) { - log->error("Fatal Error: Couldn't allocate buffer in rlc_um::reassemble_rx_sdus().\n"); - return; + if (rx_sdu->N_bytes > 0 || rlc_um_start_aligned(rx_window[vr_ur].header.fi)) { + log->debug("Writing last segment in SDU buffer. Lower edge vr_ur=%d, Buffer size=%d, segment size=%d\n", + vr_ur, rx_sdu->N_bytes, rx_window[vr_ur].buf->N_bytes); + + memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_ur].buf->msg, rx_window[vr_ur].buf->N_bytes); + rx_sdu->N_bytes += rx_window[vr_ur].buf->N_bytes; + vr_ur_in_rx_sdu = vr_ur; + if(rlc_um_end_aligned(rx_window[vr_ur].header.fi)) + { + if(pdu_lost && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) { + log->warning("Dropping remainder of lost PDU (lower edge last segments)\n"); + rx_sdu->reset(); + } else { + log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d (lower edge last segments)", rrc->get_rb_name(lcid).c_str(), vr_ur); + rx_sdu->set_timestamp(); + pdcp->write_pdu(lcid, rx_sdu); + rx_sdu = pool_allocate; + if (!rx_sdu) { + log->error("Fatal Error: Couldn't allocate buffer in rlc_um::reassemble_rx_sdus().\n"); + return; + } } + pdu_lost = false; } - pdu_lost = false; } // Clean up rx_window @@ -527,10 +541,21 @@ void rlc_um::reassemble_rx_sdus() for(uint32_t i=0; imsg[rx_sdu->N_bytes], rx_window[vr_ur].buf->msg, len); + + // Check if the first part of the PDU is a middle or end segment + if (rx_sdu->N_bytes == 0 && i == 0 && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) { + log->warning("Dropping PDU %d due to lost start segment\n", vr_ur); + // Advance data pointers and continue with next segment + rx_window[vr_ur].buf->msg += len; + rx_window[vr_ur].buf->N_bytes -= len; + rx_sdu->reset(); + break; + } + log->debug("Concatenating %d bytes in to current length %d. rx_window remaining bytes=%d, vr_ur_in_rx_sdu=%d, vr_ur=%d, rx_mod=%d, last_mod=%d\n", len, rx_sdu->N_bytes, rx_window[vr_ur].buf->N_bytes, vr_ur_in_rx_sdu, vr_ur, cfg.rx_mod, (vr_ur_in_rx_sdu+1)%cfg.rx_mod); - rx_sdu->N_bytes += len; + memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_ur].buf->msg, len); + rx_sdu->N_bytes += len; rx_window[vr_ur].buf->msg += len; rx_window[vr_ur].buf->N_bytes -= len; if((pdu_lost && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) || (vr_ur != ((vr_ur_in_rx_sdu+1)%cfg.rx_mod))) { @@ -548,8 +573,14 @@ void rlc_um::reassemble_rx_sdus() } pdu_lost = false; } - + // Handle last segment + if (rx_sdu->N_bytes == 0 && rx_window[vr_ur].header.N_li == 0 && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) { + log->warning("Dropping PDU %d due to lost start segment\n", vr_ur); + rx_sdu->reset(); + goto clean_up_rx_window; + } + if (rx_sdu->N_bytes < SRSLTE_MAX_BUFFER_SIZE_BYTES && rx_window[vr_ur].buf->N_bytes < SRSLTE_MAX_BUFFER_SIZE_BYTES && rx_window[vr_ur].buf->N_bytes + rx_sdu->N_bytes < SRSLTE_MAX_BUFFER_SIZE_BYTES) @@ -557,7 +588,7 @@ void rlc_um::reassemble_rx_sdus() memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_ur].buf->msg, rx_window[vr_ur].buf->N_bytes); rx_sdu->N_bytes += rx_window[vr_ur].buf->N_bytes; - log->debug("Writting last segment in SDU buffer. Updating vr_ur=%d, Buffer size=%d, segment size=%d\n", + log->debug("Writing last segment in SDU buffer. Updating vr_ur=%d, Buffer size=%d, segment size=%d\n", vr_ur, rx_sdu->N_bytes, rx_window[vr_ur].buf->N_bytes); } else { log->error("Out of bounds while reassembling SDU buffer in UM: sdu_len=%d, window_buffer_len=%d, vr_ur=%d\n", @@ -582,6 +613,8 @@ void rlc_um::reassemble_rx_sdus() pdu_lost = false; } +clean_up_rx_window: + // Clean up rx_window pool->deallocate(rx_window[vr_ur].buf); rx_window.erase(vr_ur); diff --git a/lib/test/upper/CMakeLists.txt b/lib/test/upper/CMakeLists.txt index 79bbb8331..07d0a777f 100644 --- a/lib/test/upper/CMakeLists.txt +++ b/lib/test/upper/CMakeLists.txt @@ -30,9 +30,11 @@ add_executable(rlc_am_test rlc_am_test.cc) target_link_libraries(rlc_am_test srslte_upper srslte_phy srslte_common) add_test(rlc_am_test rlc_am_test) -add_executable(rlc_am_stress_test rlc_am_stress_test.cc) -target_link_libraries(rlc_am_stress_test srslte_upper srslte_phy srslte_common ${Boost_LIBRARIES}) -add_test(rlc_am_stress_test rlc_am_stress_test --duration 10) +add_executable(rlc_stress_test rlc_stress_test.cc) +target_link_libraries(rlc_stress_test srslte_upper srslte_phy srslte_common ${Boost_LIBRARIES}) +add_test(rlc_am_stress_test rlc_stress_test --mode=AM) +add_test(rlc_um_stress_test rlc_stress_test --mode=UM) +add_test(rlc_tm_stress_test rlc_stress_test --mode=TM --opp_sdu_ratio=1.0) add_executable(rlc_um_data_test rlc_um_data_test.cc) target_link_libraries(rlc_um_data_test srslte_upper srslte_phy srslte_common) diff --git a/lib/test/upper/rlc_am_data_test.cc b/lib/test/upper/rlc_am_data_test.cc index d2dfe5949..ad3146582 100644 --- a/lib/test/upper/rlc_am_data_test.cc +++ b/lib/test/upper/rlc_am_data_test.cc @@ -40,6 +40,20 @@ uint32_t PDU2_LEN = 5; uint8_t pdu3[] = {0x8C, 0x00, 0xDD, 0xCD, 0xDC, 0x5D, 0xC0}; uint32_t PDU3_LEN = 7; +// D/C = 1 = Data PDU +// RF = 0 = AMD PDU +// P = 0 = Status PDU is not requested +// FI = 11 = First byte of the Data field does not corresponds to the first byte of a RLC SDU, +// Last byte of the Data field does not corresponds to the last byte of a RLC SDU +// E = 1 = A set of E field and LI field follows from the octet following the fixed part of the header +// SN = 0000000010 -> SN 2 +// E = 1 +// LI1 = 1010011 1110 (1342 Dec) +// E = 0 +// LI2 = 10111011100 (1500 Dec) +uint8_t pdu4[] = {0x9C, 0x02, 0xD3, 0xE5, 0xDC }; +uint32_t PDU4_LEN = 5; + using namespace srslte; int main(int argc, char **argv) { @@ -106,4 +120,26 @@ int main(int argc, char **argv) { assert(b2.N_bytes == PDU3_LEN); for(uint32_t i=0;i #define NBUFS 5 +#define HAVE_PCAP 0 using namespace srsue; using namespace srslte; @@ -54,9 +56,11 @@ class rlc_am_tester ,public rrc_interface_rlc { public: - rlc_am_tester(){ + rlc_am_tester(rlc_pcap *pcap_ = NULL) + { bzero(sdus, sizeof(sdus)); n_sdus = 0; + pcap = pcap_; } ~rlc_am_tester(){ @@ -83,6 +87,7 @@ public: byte_buffer_t *sdus[10]; int n_sdus; + rlc_pcap *pcap; }; void basic_test() @@ -1056,6 +1061,311 @@ void resegment_test_6() } } +// Retransmission of PDU segments of the same size +void resegment_test_7() +{ + // SDUs: | 30 | 30 | + // PDUs: | 13 | 13 | 11 | 13 | 10 | + // Rxed PDUs | 13 | 13 | | 13 | 10 | + // Retx PDU segments: | 4 | 7 | + // Retx PDU segments: |3|3]3|2| + const uint32_t N_SDU_BUFS = 2; + const uint32_t N_PDU_BUFS = 5; + const uint32_t sdu_size = 30; + + srslte::log_filter log1("RLC_AM_1"); + srslte::log_filter log2("RLC_AM_2"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(100); + log2.set_hex_limit(100); + +#if HAVE_PCAP + rlc_pcap pcap; + pcap.open("rlc_am_test7.pcap", 0); + rlc_am_tester tester(&pcap); +#else + rlc_am_tester tester(NULL); +#endif + mac_dummy_timers timers; + + rlc_am rlc1; + rlc_am rlc2; + + int len; + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 1, &tester, &tester, &timers); + rlc2.init(&log2, 1, &tester, &tester, &timers); + + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; + cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; + cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS5; + cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4; + cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25; + cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4; + cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5; + + rlc1.configure(&cnfg); + rlc2.configure(&cnfg); + + // Push 2 SDUs into RLC1 + byte_buffer_t sdu_bufs[N_SDU_BUFS]; + for(uint32_t i=0;i 1) { + rlc2.write_pdu(retx[i].msg, retx[i].N_bytes); +#if HAVE_PCAP + pcap.write_dl_am_ccch(retx[i].msg, retx[i].N_bytes); +#endif + } + } + + usleep(10000); + + // Read status PDU from RLC2 + assert(rlc2.get_buffer_state()); + byte_buffer_t status_buf; + status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); +#if HAVE_PCAP + pcap.write_ul_am_ccch(status_buf.msg, status_buf.N_bytes); +#endif + + assert(15 == rlc1.get_buffer_state()); + + + // second round of retx, forcing resegmentation + byte_buffer_t retx2[4]; + for (uint32_t i = 0; i < 4; i++) { + assert(rlc1.get_buffer_state() != 0); + retx2[i].N_bytes = rlc1.read_pdu(retx2[i].msg, 7); + assert(retx2[i].N_bytes != 0); + + rlc2.write_pdu(retx2[i].msg, retx2[i].N_bytes); +#if HAVE_PCAP + pcap.write_dl_am_ccch(retx[i].msg, retx[i].N_bytes); +#endif + } + + // check buffer states + assert(0 == rlc1.get_buffer_state()); + assert(0 == rlc2.get_buffer_state()); + + // Check number of SDUs and their content + assert(tester.n_sdus == N_SDU_BUFS); + for(int i=0; iN_bytes == sdu_size); + for(uint32_t j=0;jmsg[j] == i); + } + } + +#if HAVE_PCAP + pcap.close(); +#endif +} + + +// Retransmission of PDU segments with different size +void resegment_test_8() +{ + // SDUs: | 30 | 30 | + // PDUs: | 15 | 15 | 15 | 15 | 15 | + // Rxed PDUs | 15 | | 15 | 15 | + // Retx PDU segments: | 7 | 7 | 7 | 7 | + // Retx PDU segments: | 6 | 6 ] 6 | 6 | 6 | 6 | 6 | 6 | + const uint32_t N_SDU_BUFS = 2; + const uint32_t N_PDU_BUFS = 5; + const uint32_t sdu_size = 30; + + srslte::log_filter log1("RLC_AM_1"); + srslte::log_filter log2("RLC_AM_2"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(100); + log2.set_hex_limit(100); + +#if HAVE_PCAP + rlc_pcap pcap; + pcap.open("rlc_am_test8.pcap", 0); + rlc_am_tester tester(&pcap); +#else + rlc_am_tester tester(NULL); +#endif + mac_dummy_timers timers; + + rlc_am rlc1; + rlc_am rlc2; + + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 1, &tester, &tester, &timers); + rlc2.init(&log2, 1, &tester, &tester, &timers); + + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; + cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; + cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS5; + cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4; + cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25; + cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4; + cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5; + + rlc1.configure(&cnfg); + rlc2.configure(&cnfg); + + // Push 2 SDUs into RLC1 + byte_buffer_t sdu_bufs[N_SDU_BUFS]; + for(uint32_t i=0;i 2) { + rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); +#if HAVE_PCAP + pcap.write_dl_am_ccch(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); +#endif + } + } + + // Sleep to let reordering timeout expire + usleep(10000); + + assert(12 == rlc1.get_buffer_state()); + + // first round of retx, forcing resegmentation + byte_buffer_t retx[4]; + for (uint32_t i = 0; i < 3; i++) { + assert(rlc1.get_buffer_state()); + retx[i].N_bytes = rlc1.read_pdu(retx[i].msg, 8); + assert(retx[i].N_bytes); + + // Write the last two segments to RLC2 + if (i > 1) { + rlc2.write_pdu(retx[i].msg, retx[i].N_bytes); +#if HAVE_PCAP + pcap.write_dl_am_ccch(retx[i].msg, retx[i].N_bytes); +#endif + } + } + + usleep(20000); + + // Read status PDU from RLC2 + assert(rlc2.get_buffer_state()); + byte_buffer_t status_buf; + status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); +#if HAVE_PCAP + pcap.write_ul_am_ccch(status_buf.msg, status_buf.N_bytes); +#endif + + assert(15 == rlc1.get_buffer_state()); + + // second round of retx, reduce grant size to force different segment sizes + byte_buffer_t retx2[20]; + for (uint32_t i = 0; i < 9; i++) { + assert(rlc1.get_buffer_state() != 0); + retx2[i].N_bytes = rlc1.read_pdu(retx2[i].msg, 7); + assert(retx2[i].N_bytes != 0); + rlc2.write_pdu(retx2[i].msg, retx2[i].N_bytes); +#if HAVE_PCAP + pcap.write_dl_am_ccch(retx[i].msg, retx[i].N_bytes); +#endif + } + + // check buffer states + assert(0 == rlc1.get_buffer_state()); + assert(0 == rlc2.get_buffer_state()); + + // Check number of SDUs and their content + assert(tester.n_sdus == N_SDU_BUFS); + for(int i=0; iN_bytes == sdu_size); + for(uint32_t j=0;jmsg[j] == i); + } + } + +#if HAVE_PCAP + pcap.close(); +#endif +} + + void reset_test() { srslte::log_filter log1("RLC_AM_1"); @@ -1141,6 +1451,12 @@ int main(int argc, char **argv) { resegment_test_6(); byte_buffer_pool::get_instance()->cleanup(); + resegment_test_7(); + byte_buffer_pool::get_instance()->cleanup(); + + resegment_test_8(); + byte_buffer_pool::get_instance()->cleanup(); + reset_test(); byte_buffer_pool::get_instance()->cleanup(); } diff --git a/lib/test/upper/rlc_am_stress_test.cc b/lib/test/upper/rlc_stress_test.cc similarity index 55% rename from lib/test/upper/rlc_am_stress_test.cc rename to lib/test/upper/rlc_stress_test.cc index fb83e4cfd..0def725c1 100644 --- a/lib/test/upper/rlc_am_stress_test.cc +++ b/lib/test/upper/rlc_stress_test.cc @@ -30,10 +30,14 @@ #include "srslte/common/log_filter.h" #include "srslte/common/logger_stdout.h" #include "srslte/common/threads.h" +#include "srslte/common/rlc_pcap.h" #include "srslte/upper/rlc.h" #include #include #include +#include + +#define SDU_SIZE 1500 using namespace std; using namespace srsue; @@ -41,12 +45,16 @@ using namespace srslte; namespace bpo = boost::program_options; typedef struct { - uint32_t test_duration_sec; - float error_rate; - uint32_t sdu_gen_delay_usec; - uint32_t pdu_tx_delay_usec; - bool reestablish; - uint32_t log_level; + std::string mode; + uint32_t test_duration_sec; + float error_rate; + uint32_t sdu_gen_delay_usec; + uint32_t pdu_tx_delay_usec; + bool reestablish; + uint32_t log_level; + bool single_tx; + bool write_pcap; + float opp_sdu_ratio; } stress_test_args_t; void parse_args(stress_test_args_t *args, int argc, char *argv[]) { @@ -61,12 +69,16 @@ void parse_args(stress_test_args_t *args, int argc, char *argv[]) { // Command line or config file options bpo::options_description common("Configuration options"); common.add_options() - ("duration", bpo::value(&args->test_duration_sec)->default_value(10), "Duration (sec)") - ("sdu_gen_delay", bpo::value(&args->sdu_gen_delay_usec)->default_value(10), "SDU generation delay (usec)") - ("pdu_tx_delay", bpo::value(&args->pdu_tx_delay_usec)->default_value(10), "Delay in MAC for transfering PDU from tx'ing RLC to rx'ing RLC (usec)") + ("mode", bpo::value(&args->mode)->default_value("AM"), "Whether to test RLC acknowledged or unacknowledged mode (AM/UM)") + ("duration", bpo::value(&args->test_duration_sec)->default_value(5), "Duration (sec)") + ("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)") ("error_rate", bpo::value(&args->error_rate)->default_value(0.1), "Rate at which RLC PDUs are dropped") + ("opp_sdu_ratio", bpo::value(&args->opp_sdu_ratio)->default_value(0.0), "Ratio between MAC opportunity and SDU size (0==random)") ("reestablish", bpo::value(&args->reestablish)->default_value(false), "Mimic RLC reestablish during execution") - ("loglevel", bpo::value(&args->log_level)->default_value(srslte::LOG_LEVEL_DEBUG), "Log level (1=Error,2=Warning,3=Info,4=Debug"); + ("loglevel", bpo::value(&args->log_level)->default_value(srslte::LOG_LEVEL_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"); // these options are allowed on the command line bpo::options_description cmdline_options; @@ -94,14 +106,18 @@ class mac_reader :public thread { public: - mac_reader(rlc_interface_mac *rlc1_, rlc_interface_mac *rlc2_, float fail_rate_, uint32_t pdu_tx_delay_usec_) + mac_reader(rlc_interface_mac *rlc1_, rlc_interface_mac *rlc2_, float fail_rate_, float opp_sdu_ratio_, uint32_t pdu_tx_delay_usec_, rlc_pcap *pcap_, uint32_t lcid_, bool is_dl_ = true) { rlc1 = rlc1_; rlc2 = rlc2_; fail_rate = fail_rate_; + opp_sdu_ratio = opp_sdu_ratio_; run_enable = true; running = false; pdu_tx_delay_usec = pdu_tx_delay_usec_; + pcap = pcap_; + is_dl = is_dl_; + lcid = lcid_; } void stop() @@ -129,14 +145,23 @@ private: } while(run_enable) { - float r = (float)rand()/RAND_MAX; - int opp_size = r*1500; - rlc1->get_buffer_state(1); - int read = rlc1->read_pdu(1, pdu->msg, opp_size); - if(((float)rand()/RAND_MAX > fail_rate) && read>0) { - rlc2->write_pdu(1, pdu->msg, opp_size); + // generate MAC opportunities of random size or with fixed ratio + float r = opp_sdu_ratio ? opp_sdu_ratio : (float)rand()/RAND_MAX; + int opp_size = r*SDU_SIZE; + uint32_t buf_state = rlc1->get_buffer_state(lcid); + if (buf_state) { + int read = rlc1->read_pdu(lcid, pdu->msg, opp_size); + if (pdu_tx_delay_usec) usleep(pdu_tx_delay_usec); + if(((float)rand()/RAND_MAX > fail_rate) && read>0) { + pdu->N_bytes = read; + rlc2->write_pdu(lcid, pdu->msg, pdu->N_bytes); + if (is_dl) { + pcap->write_dl_am_ccch(pdu->msg, pdu->N_bytes); + } else { + pcap->write_ul_am_ccch(pdu->msg, pdu->N_bytes); + } + } } - usleep(pdu_tx_delay_usec); } running = false; byte_buffer_pool::get_instance()->deallocate(pdu); @@ -145,7 +170,11 @@ private: rlc_interface_mac *rlc1; rlc_interface_mac *rlc2; float fail_rate; + float opp_sdu_ratio; uint32_t pdu_tx_delay_usec; + rlc_pcap *pcap; + uint32_t lcid; + bool is_dl; bool run_enable; bool running; @@ -155,9 +184,9 @@ class mac_dummy :public srslte::mac_interface_timers { public: - mac_dummy(rlc_interface_mac *rlc1_, rlc_interface_mac *rlc2_, float fail_rate_, uint32_t pdu_tx_delay) - :r1(rlc1_, rlc2_, fail_rate_, pdu_tx_delay) - ,r2(rlc2_, rlc1_, fail_rate_, pdu_tx_delay) + mac_dummy(rlc_interface_mac *rlc1_, rlc_interface_mac *rlc2_, float fail_rate_, float opp_sdu_ratio_, int32_t pdu_tx_delay, uint32_t lcid, rlc_pcap* pcap = NULL) + :r1(rlc1_, rlc2_, fail_rate_, opp_sdu_ratio_, pdu_tx_delay, pcap, lcid, true) + ,r2(rlc2_, rlc1_, fail_rate_, opp_sdu_ratio_, pdu_tx_delay, pcap, lcid, false) { } @@ -189,19 +218,20 @@ private: -class rlc_am_tester +class rlc_tester :public pdcp_interface_rlc ,public rrc_interface_rlc ,public thread { public: - rlc_am_tester(rlc_interface_pdcp *rlc_, std::string name_, uint32_t sdu_gen_delay_usec_){ + rlc_tester(rlc_interface_pdcp *rlc_, std::string name_, uint32_t sdu_gen_delay_usec_, uint32_t lcid_){ rlc = rlc_; run_enable = true; running = false; rx_pdus = 0; name = name_; sdu_gen_delay_usec = sdu_gen_delay_usec_; + lcid = lcid_; } void stop() @@ -219,11 +249,16 @@ public: } // PDCP interface - void write_pdu(uint32_t lcid, byte_buffer_t *sdu) + void write_pdu(uint32_t rx_lcid, byte_buffer_t *sdu) { - assert(lcid == 1); + assert(rx_lcid == lcid); + if (sdu->N_bytes != SDU_SIZE) { + printf("Received PDU with size %d, expected %d. Exiting.\n", sdu->N_bytes, SDU_SIZE); + exit(-1); + } + byte_buffer_pool::get_instance()->deallocate(sdu); - std::cout << "rlc_am_tester " << name << " received " << rx_pdus++ << " PDUs" << std::endl; + rx_pdus++; } void write_pdu_bcch_bch(byte_buffer_t *sdu) {} void write_pdu_bcch_dlsch(byte_buffer_t *sdu) {} @@ -231,7 +266,9 @@ public: // RRC interface void max_retx_attempted(){} - std::string get_rb_name(uint32_t lcid) { return std::string(""); } + std::string get_rb_name(uint32_t rx_lcid) { return std::string(""); } + + int get_nof_rx_pdus() { return rx_pdus; } private: void run_thread() @@ -239,15 +276,18 @@ private: uint8_t sn = 0; running = true; while(run_enable) { - byte_buffer_t *pdu = byte_buffer_pool::get_instance()->allocate("rlc_am_tester::run_thread"); + byte_buffer_t *pdu = byte_buffer_pool::get_instance()->allocate("rlc_tester::run_thread"); if (!pdu) { - printf("Fatal Error: Could not allocate PDU in rlc_am_tester::run_thread\n"); + printf("Fatal Error: Could not allocate PDU in rlc_tester::run_thread\n"); exit(-1); } - pdu->N_bytes = 1500; - pdu->msg[0] = sn++; - rlc->write_sdu(1, pdu); - usleep(sdu_gen_delay_usec); + for (uint32_t i = 0; i < SDU_SIZE; i++) { + pdu->msg[i] = sn; + } + sn++; + pdu->N_bytes = SDU_SIZE; + rlc->write_sdu(lcid, pdu); + if (sdu_gen_delay_usec) usleep(sdu_gen_delay_usec); } running = false; } @@ -255,6 +295,7 @@ private: bool run_enable; bool running; long rx_pdus; + uint32_t lcid; std::string name; @@ -265,40 +306,67 @@ private: void stress_test(stress_test_args_t args) { - srslte::log_filter log1("RLC_AM_1"); - srslte::log_filter log2("RLC_AM_2"); + srslte::log_filter log1("RLC_1"); + srslte::log_filter log2("RLC_2"); log1.set_level((LOG_LEVEL_ENUM)args.log_level); log2.set_level((LOG_LEVEL_ENUM)args.log_level); log1.set_hex_limit(-1); log2.set_hex_limit(-1); + rlc_pcap pcap; + uint32_t lcid = 1; + + if (args.write_pcap) { + pcap.open("rlc_stress_test.pcap", 0); + } + + srslte_rlc_config_t cnfg_; + if (args.mode == "AM") { + // config RLC AM bearer + cnfg_.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; + cnfg_.am.max_retx_thresh = 4; + cnfg_.am.poll_byte = 25*1000; + cnfg_.am.poll_pdu = 4; + cnfg_.am.t_poll_retx = 5; + cnfg_.am.t_reordering = 5; + cnfg_.am.t_status_prohibit = 5; + } else if (args.mode == "UM") { + // config UM bearer + cnfg_.rlc_mode = LIBLTE_RRC_RLC_MODE_UM_BI; + cnfg_.um.t_reordering = 5; + cnfg_.um.rx_mod = 32; + cnfg_.um.rx_sn_field_length = RLC_UMD_SN_SIZE_5_BITS; + cnfg_.um.rx_window_size = 16; + cnfg_.um.tx_sn_field_length = RLC_UMD_SN_SIZE_5_BITS; + cnfg_.um.tx_mod = 32; + } else if (args.mode == "TM") { + // use default LCID in TM + lcid = 0; + } else { + cout << "Unsupported RLC mode " << args.mode << ", exiting." << endl; + exit(-1); + } rlc rlc1; rlc rlc2; - rlc_am_tester tester1(&rlc1, "tester1", args.sdu_gen_delay_usec); - rlc_am_tester tester2(&rlc2, "tester2", args.sdu_gen_delay_usec); - mac_dummy mac(&rlc1, &rlc2, args.error_rate, args.pdu_tx_delay_usec); + rlc_tester tester1(&rlc1, "tester1", args.sdu_gen_delay_usec, lcid); + rlc_tester tester2(&rlc2, "tester2", args.sdu_gen_delay_usec, lcid); + mac_dummy mac(&rlc1, &rlc2, args.error_rate, args.opp_sdu_ratio, args.pdu_tx_delay_usec, lcid, &pcap); ue_interface ue; rlc1.init(&tester1, &tester1, &ue, &log1, &mac, 0); rlc2.init(&tester2, &tester2, &ue, &log2, &mac, 0); - LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; - cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; - cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; - cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS5; - cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4; - cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25; - cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4; - cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5; - - srslte_rlc_config_t cnfg_(&cnfg); - - rlc1.add_bearer(1, cnfg_); - rlc2.add_bearer(1, cnfg_); + // only add AM and UM bearers + if (args.mode != "TM") { + rlc1.add_bearer(lcid, cnfg_); + rlc2.add_bearer(lcid, cnfg_); + } tester1.start(7); - tester2.start(7); + if (!args.single_tx) { + tester2.start(7); + } mac.start(); for (uint32_t i = 0; i < args.test_duration_sec; i++) { @@ -313,6 +381,19 @@ void stress_test(stress_test_args_t args) tester1.stop(); tester2.stop(); mac.stop(); + if (args.write_pcap) { + pcap.close(); + } + + printf("RLC1 received %d SDUs in %ds (%.2f PDU/s)\n", + tester1.get_nof_rx_pdus(), + args.test_duration_sec, + (float)tester1.get_nof_rx_pdus()/args.test_duration_sec); + + printf("RLC2 received %d SDUs in %ds (%.2f PDU/s)\n", + tester2.get_nof_rx_pdus(), + args.test_duration_sec, + (float)tester2.get_nof_rx_pdus()/args.test_duration_sec); } @@ -322,4 +403,6 @@ int main(int argc, char **argv) { stress_test(args); byte_buffer_pool::get_instance()->cleanup(); + + exit(0); } diff --git a/lib/test/upper/rlc_um_test.cc b/lib/test/upper/rlc_um_test.cc index 3755c1174..2a4ffac0b 100644 --- a/lib/test/upper/rlc_um_test.cc +++ b/lib/test/upper/rlc_um_test.cc @@ -29,6 +29,7 @@ #include "srslte/upper/rlc_um.h" #include +#define MAX_NBUFS 100 #define NBUFS 5 using namespace srslte; @@ -60,6 +61,7 @@ public: rlc_um_tester(){ bzero(sdus, sizeof(sdus)); n_sdus = 0; + expected_sdu_len = 0; } ~rlc_um_tester(){ @@ -74,6 +76,10 @@ public: void write_pdu(uint32_t lcid, byte_buffer_t *sdu) { assert(lcid == 3); + if (sdu->N_bytes != expected_sdu_len) { + printf("Received PDU with size %d, expected %d. Exiting.\n", sdu->N_bytes, expected_sdu_len); + exit(-1); + } sdus[n_sdus++] = sdu; } void write_pdu_bcch_bch(byte_buffer_t *sdu) {} @@ -83,9 +89,11 @@ public: // RRC interface void max_retx_attempted(){} std::string get_rb_name(uint32_t lcid) { return std::string(""); } + void set_expected_sdu_len(uint32_t len) { expected_sdu_len = len; } - byte_buffer_t *sdus[5]; + byte_buffer_t *sdus[MAX_NBUFS]; int n_sdus; + uint32_t expected_sdu_len; }; void basic_test() @@ -119,6 +127,8 @@ void basic_test() rlc1.configure(&cnfg); rlc2.configure(&cnfg); + tester.set_expected_sdu_len(1); + // Push 5 SDUs into RLC1 byte_buffer_t sdu_bufs[NBUFS]; for(int i=0;iN_bytes == sdu_len); + } +} + +// This reassmble test checks the reassembly routines when a PDU +// is lost that _only_ contains the beginning of SDU segment, +// while the next PDU contains the middle part of this SDU (and +// yet another PDU the end part). +// On reassembly of the SDUs, the missing start segment +// should be detected and the complete SDU be discarded +// Therefore, one SDU less should be received than was tx'ed. +void reassmble_test2() +{ + srslte::log_filter log1("RLC_UM_1"); + srslte::log_filter log2("RLC_UM_2"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(-1); + log2.set_hex_limit(-1); + rlc_um_tester tester; + mac_dummy_timers timers; + + rlc_um rlc1; + rlc_um rlc2; + + int len; + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 3, &tester, &tester, &timers); + rlc2.init(&log2, 3, &tester, &tester, &timers); + + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_UM_BI; + cnfg.dl_um_bi_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; + cnfg.dl_um_bi_rlc.sn_field_len = LIBLTE_RRC_SN_FIELD_LENGTH_SIZE5; + cnfg.ul_um_bi_rlc.sn_field_len = LIBLTE_RRC_SN_FIELD_LENGTH_SIZE5; + + rlc1.configure(&cnfg); + rlc2.configure(&cnfg); + + // Push SDUs into RLC1 + const int n_sdus = 25; + const int sdu_len = 100; + + tester.set_expected_sdu_len(sdu_len); + + byte_buffer_t sdu_bufs[n_sdus]; + const int n_sdu_first_batch = 17; + + for(int i=0;iN_bytes == sdu_len); + } +} + + int main(int argc, char **argv) { basic_test(); byte_buffer_pool::get_instance()->cleanup(); + loss_test(); byte_buffer_pool::get_instance()->cleanup(); + + reassmble_test(); + byte_buffer_pool::get_instance()->cleanup(); + + reassmble_test2(); + byte_buffer_pool::get_instance()->cleanup(); } diff --git a/srsue/src/phy/phch_recv.cc b/srsue/src/phy/phch_recv.cc index 93c27b810..f2ef205c9 100644 --- a/srsue/src/phy/phch_recv.cc +++ b/srsue/src/phy/phch_recv.cc @@ -366,7 +366,6 @@ void phch_recv::run_thread() bool is_end_of_burst = false; cf_t *dummy_buffer[SRSLTE_MAX_PORTS]; - for (int i=0;imsg, pdu->N_bytes); if(n > 0 && (pdu->N_bytes != (uint32_t)n)) { - gw_log->warning("DL TUN/TAP write failure\n"); + gw_log->warning("DL TUN/TAP write failure. Wanted to write %d B but only wrote %d B.\n", pdu->N_bytes, n); } } pool->deallocate(pdu);