diff --git a/lib/src/phy/ue/ue_ul.c b/lib/src/phy/ue/ue_ul.c index ab7e2e125..544700d4e 100644 --- a/lib/src/phy/ue/ue_ul.c +++ b/lib/src/phy/ue/ue_ul.c @@ -996,9 +996,6 @@ int srslte_ue_ul_encode(srslte_ue_ul_t* q, srslte_ul_sf_cfg_t* sf, srslte_ue_ul_ cfg->cc_idx == 0) { // Send PUCCH over PCell only if (!cfg->ul_cfg.pucch.rnti) { cfg->ul_cfg.pucch.rnti = q->current_rnti; - if (!q->current_rnti) { - printf("PUCCH: Warning PUCCH rnti or current_rnti are not set\n"); - } } ret = pucch_encode(q, sf, cfg, &data->uci) ? -1 : 1; } else if (srs_tx_enabled(&cfg->ul_cfg.srs, sf->tti)) { diff --git a/srsue/hdr/stack/mac/dl_harq.h b/srsue/hdr/stack/mac/dl_harq.h index 6711f08cb..4dafa52ac 100644 --- a/srsue/hdr/stack/mac/dl_harq.h +++ b/srsue/hdr/stack/mac/dl_harq.h @@ -62,6 +62,7 @@ private: dl_harq_process(); bool init(int pid, dl_harq_entity* parent); void reset(void); + void reset_ndi(); void new_grant_dl(mac_interface_phy_lte::mac_grant_dl_t grant, mac_interface_phy_lte::tb_action_dl_t* action); void tb_decoded(mac_interface_phy_lte::mac_grant_dl_t grant, bool ack[SRSLTE_MAX_CODEWORDS]); @@ -78,6 +79,7 @@ private: bool init(int pid, dl_harq_entity* parent, uint32_t tb_idx); void reset(bool lock = true); + void reset_ndi(); void new_grant_dl(mac_interface_phy_lte::mac_grant_dl_t grant, mac_interface_phy_lte::tb_action_dl_t* action); void tb_decoded(mac_interface_phy_lte::mac_grant_dl_t grant, bool* ack_ptr); diff --git a/srsue/hdr/stack/mac/mac.h b/srsue/hdr/stack/mac/mac.h index 6b15cbd71..18e373453 100644 --- a/srsue/hdr/stack/mac/mac.h +++ b/srsue/hdr/stack/mac/mac.h @@ -62,7 +62,7 @@ public: void new_grant_dl(uint32_t cc_idx, mac_grant_dl_t grant, tb_action_dl_t* action); void new_mch_dl(srslte_pdsch_grant_t phy_grant, tb_action_dl_t* action); void tb_decoded(uint32_t cc_idx, mac_grant_dl_t grant, bool ack[SRSLTE_MAX_CODEWORDS]); - void bch_decoded_ok(uint8_t *payload, uint32_t len); + void bch_decoded_ok(uint8_t* payload, uint32_t len); uint16_t get_dl_sched_rnti(uint32_t tti); uint16_t get_ul_sched_rnti(uint32_t tti); diff --git a/srsue/hdr/stack/mac/mux.h b/srsue/hdr/stack/mac/mux.h index 8282b79c0..c30bd4fe8 100644 --- a/srsue/hdr/stack/mac/mux.h +++ b/srsue/hdr/stack/mac/mux.h @@ -69,9 +69,9 @@ public: void msg3_flush(); bool msg3_is_transmitted(); - void msg3_prepare(); bool msg3_is_pending(); + bool msg3_is_empty(); void append_crnti_ce_next_tx(uint16_t crnti); @@ -103,8 +103,10 @@ private: /* PDU Buffer */ srslte::sch_pdu pdu_msg; - bool msg3_has_been_transmitted = false; - bool msg3_pending = false; + + srslte::byte_buffer_t msg3_buff; + bool msg3_has_been_transmitted = false; + bool msg3_pending = false; }; } // namespace srsue diff --git a/srsue/hdr/stack/mac/proc_ra.h b/srsue/hdr/stack/mac/proc_ra.h index fd82198a4..2292cae01 100644 --- a/srsue/hdr/stack/mac/proc_ra.h +++ b/srsue/hdr/stack/mac/proc_ra.h @@ -44,24 +44,21 @@ public: bzero(&softbuffer_rar, sizeof(srslte_softbuffer_rx_t)); pcap = NULL; backoff_interval_start = 0; - backoff_inteval = 0; + backoff_interval = 0; received_target_power_dbm = 0; ra_rnti = 0; current_ta = 0; state = IDLE; last_msg3_group = RA_GROUP_A; msg3_transmitted = false; - first_rar_received = false; phy_h = NULL; log_h = NULL; mux_unit = NULL; rrc = NULL; transmitted_contention_id = 0; transmitted_crnti = 0; - pdcch_to_crnti_received = PDCCH_CRNTI_NOT_RECEIVED; started_by_pdcch = false; rar_grant_nbytes = 0; - msg3_flushed = false; noncontention_enabled = false; next_preamble_idx = 0; @@ -89,11 +86,11 @@ public: void start_mac_order(uint32_t msg_len_bits = 56, bool is_ho = false); void step(uint32_t tti); - bool is_rar_window(int* rar_window_start, int* rar_window_length); + bool update_rar_window(int* rar_window_start, int* rar_window_length); bool is_contention_resolution(); void harq_retx(); void harq_max_retx(); - void pdcch_to_crnti(bool contains_uplink_grant); + void pdcch_to_crnti(bool is_new_uplink_transmission); void timer_expired(uint32_t timer_id); void new_grant_dl(mac_interface_phy_lte::mac_grant_dl_t grant, mac_interface_phy_lte::tb_action_dl_t* action); void tb_decoded_ok(); @@ -103,16 +100,18 @@ public: void start_pcap(srslte::mac_pcap* pcap); private: + void state_pdcch_setup(); + void state_response_reception(uint32_t tti); + void state_backoff_wait(uint32_t tti); + void state_contention_resolution(); + void state_completition(); + void process_timeadv_cmd(uint32_t ta_cmd); - void step_initialization(); - void step_resource_selection(); - void step_preamble_transmission(); - void step_pdcch_setup(); - void step_response_reception(uint32_t tti); - void step_response_error(uint32_t tti); - void step_backoff_wait(uint32_t tti); - void step_contention_resolution(); - void step_completition(); + void initialization(); + void resource_selection(); + void preamble_transmission(); + void response_error(); + void complete(); // Buffer to receive RAR PDU static const uint32_t MAX_RAR_PDU_LEN = 2048; @@ -136,34 +135,21 @@ private: uint32_t backoff_param_ms; uint32_t sel_maskIndex; uint32_t sel_preamble; - uint32_t backoff_interval_start; - uint32_t backoff_inteval; + int backoff_interval_start; + uint32_t backoff_interval; int received_target_power_dbm; uint32_t ra_rnti; + uint32_t ra_tti; uint32_t current_ta; srslte_softbuffer_rx_t softbuffer_rar; - enum { - IDLE = 0, - INITIALIZATION, // Section 5.1.1 - RESOURCE_SELECTION, // Section 5.1.2 - PREAMBLE_TRANSMISSION, // Section 5.1.3 - PDCCH_SETUP, - RESPONSE_RECEPTION, // Section 5.1.4 - RESPONSE_ERROR, - BACKOFF_WAIT, - CONTENTION_RESOLUTION, // Section 5.1.5 - COMPLETION, // Section 5.1.6 - COMPLETION_DONE, - RA_PROBLEM // Section 5.1.5 last part - } state; + enum { IDLE = 0, PDCCH_SETUP, RESPONSE_RECEPTION, BACKOFF_WAIT, CONTENTION_RESOLUTION, COMPLETITION } state; typedef enum { RA_GROUP_A, RA_GROUP_B } ra_group_t; ra_group_t last_msg3_group; bool msg3_transmitted; - bool first_rar_received; uint32_t rar_window_st; @@ -185,12 +171,9 @@ private: std::mutex mutex; - enum { PDCCH_CRNTI_NOT_RECEIVED = 0, PDCCH_CRNTI_UL_GRANT, PDCCH_CRNTI_DL_GRANT } pdcch_to_crnti_received; - bool ra_is_ho; bool started_by_pdcch; uint32_t rar_grant_nbytes; - bool msg3_flushed; bool rar_received; }; diff --git a/srsue/src/phy/cc_worker.cc b/srsue/src/phy/cc_worker.cc index 3bdca95b9..d15e7cd29 100644 --- a/srsue/src/phy/cc_worker.cc +++ b/srsue/src/phy/cc_worker.cc @@ -867,10 +867,12 @@ bool cc_worker::encode_uplink(mac_interface_phy_lte::tb_action_ul_t* action, srs void cc_worker::set_uci_sr(srslte_uci_data_t* uci_data) { + Debug("set_uci_sr() query: sr_enabled=%d, last_tx_tti=%d\n", phy->sr_enabled, phy->sr_last_tx_tti); if (srslte_ue_ul_gen_sr(&ue_ul_cfg, &sf_cfg_ul, uci_data, phy->sr_enabled)) { if (phy->sr_enabled) { phy->sr_last_tx_tti = CURRENT_TTI_TX; phy->sr_enabled = false; + Debug("set_uci_sr() sending SR: sr_enabled=%d, last_tx_tti=%d\n", phy->sr_enabled, phy->sr_last_tx_tti); } } } diff --git a/srsue/src/phy/phy.cc b/srsue/src/phy/phy.cc index cd361434c..ad1ba2fb1 100644 --- a/srsue/src/phy/phy.cc +++ b/srsue/src/phy/phy.cc @@ -387,6 +387,7 @@ void phy::sr_send() { common.sr_enabled = true; common.sr_last_tx_tti = -1; + Debug("sr_send(): sr_enabled=%d, last_tx_tti=%d\n", common.sr_enabled, common.sr_last_tx_tti); } int phy::sr_last_tx_tti() diff --git a/srsue/src/stack/mac/demux.cc b/srsue/src/stack/mac/demux.cc index 6967d1f09..8c957db19 100644 --- a/srsue/src/stack/mac/demux.cc +++ b/srsue/src/stack/mac/demux.cc @@ -104,12 +104,13 @@ void demux::push_pdu_temp_crnti(uint8_t *buff, uint32_t nof_bytes) is_uecrid_successful = mac->contention_resolution_id_rcv(pending_mac_msg.get()->get_con_res_id()); } } - pending_mac_msg.reset(); - - Debug("Saved MAC PDU with Temporal C-RNTI in buffer\n"); - - pdus.push(buff, nof_bytes, srslte::pdu_queue::DCH); + if (is_uecrid_successful) { + Debug("Saved MAC PDU with Temporal C-RNTI in buffer\n"); + pdus.push(buff, nof_bytes, srslte::pdu_queue::DCH); + } else { + pdus.deallocate(buff); + } } else { Warning("Trying to push PDU with payload size zero\n"); } diff --git a/srsue/src/stack/mac/dl_harq.cc b/srsue/src/stack/mac/dl_harq.cc index 3fd5f0f9a..c60196f61 100644 --- a/srsue/src/stack/mac/dl_harq.cc +++ b/srsue/src/stack/mac/dl_harq.cc @@ -81,14 +81,11 @@ void dl_harq_entity::new_grant_dl(mac_interface_phy_lte::mac_grant_dl_t grant, } proc_ptr = &proc[grant.pid]; } + // Consider the NDI to have been toggled if (grant.rnti == rntis->temp_rnti && last_temporal_crnti != rntis->temp_rnti) { - grant.tb[0].ndi = true; last_temporal_crnti = rntis->temp_rnti; - Info("Set NDI=1 for Temp-RNTI DL dci\n"); - } - if (grant.rnti == rntis->crnti && proc_ptr->is_sps()) { - grant.tb[0].ndi = true; - Info("Set NDI=1 for C-RNTI DL dci\n"); + proc_ptr->reset_ndi(); + Info("Considering NDI in pid=%d to be toggled for first Temporal C-RNTI\n", grant.pid); } proc_ptr->new_grant_dl(grant, action); } else { @@ -152,6 +149,13 @@ void dl_harq_entity::dl_harq_process::reset(void) } } +void dl_harq_entity::dl_harq_process::reset_ndi() +{ + for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { + subproc[tb].reset_ndi(); + } +} + void dl_harq_entity::dl_harq_process::new_grant_dl(mac_interface_phy_lte::mac_grant_dl_t grant, mac_interface_phy_lte::tb_action_dl_t* action) { @@ -245,6 +249,11 @@ void dl_harq_entity::dl_harq_process::dl_tb_process::reset(bool lock) } } +void dl_harq_entity::dl_harq_process::dl_tb_process::reset_ndi() +{ + is_first_tb = true; +} + void dl_harq_entity::dl_harq_process::dl_tb_process::new_grant_dl(mac_interface_phy_lte::mac_grant_dl_t grant, mac_interface_phy_lte::tb_action_dl_t* action) { diff --git a/srsue/src/stack/mac/mac.cc b/srsue/src/stack/mac/mac.cc index 987f7e8c3..9131f4480 100644 --- a/srsue/src/stack/mac/mac.cc +++ b/srsue/src/stack/mac/mac.cc @@ -121,6 +121,7 @@ void mac::start_pcap(srslte::mac_pcap* pcap_) ra_procedure.start_pcap(pcap); } +// FIXME: Change the function name and implement reconfiguration as in specs // Implement Section 5.8 void mac::reconfiguration(const uint32_t& cc_idx, const bool& enable) { @@ -229,8 +230,9 @@ void mac::run_tti(const uint32_t tti) } ra_procedure.step(tti); - ra_window_start = 0; - ra_procedure.is_rar_window(&ra_window_start, &ra_window_length); + ra_window_start = -1; + ra_window_length = -1; + ra_procedure.update_rar_window(&ra_window_start, &ra_window_length); timers.step_all(); } @@ -261,7 +263,8 @@ void mac::clear_rntis() { p_window_start = 0; si_window_start = 0; - ra_window_start = 0; + ra_window_start = -1; + ra_window_length = -1; bzero(&uernti, sizeof(ue_rnti_t)); } diff --git a/srsue/src/stack/mac/mux.cc b/srsue/src/stack/mac/mux.cc index 7a7001d31..6f0868741 100644 --- a/srsue/src/stack/mac/mux.cc +++ b/srsue/src/stack/mac/mux.cc @@ -355,6 +355,7 @@ void mux::msg3_flush() if (log_h) { Debug("Msg3 buffer flushed\n"); } + msg3_buff.clear(); msg3_has_been_transmitted = false; msg3_pending = false; } @@ -365,6 +366,7 @@ bool mux::msg3_is_transmitted() } void mux::msg3_prepare() { + msg3_has_been_transmitted = false; msg3_pending = true; } @@ -372,15 +374,29 @@ bool mux::msg3_is_pending() { return msg3_pending; } +bool mux::msg3_is_empty() +{ + return msg3_buff.N_bytes == 0; +} + /* Returns a pointer to the Msg3 buffer */ uint8_t* mux::msg3_get(srslte::byte_buffer_t* payload, uint32_t pdu_sz) { - if (pdu_get(payload, pdu_sz) != nullptr) { - msg3_pending = false; + if (pdu_sz < msg3_buff.get_tailroom()) { + if (msg3_is_empty()) { + if (!pdu_get(&msg3_buff, pdu_sz)) { + Error("Moving PDU from Mux unit to Msg3 buffer\n"); + return NULL; + } + msg3_pending = false; + } + *payload = msg3_buff; msg3_has_been_transmitted = true; return payload->msg; + } else { + Error("Msg3 size exceeds buffer\n"); + return NULL; } - return nullptr; } } diff --git a/srsue/src/stack/mac/proc_bsr.cc b/srsue/src/stack/mac/proc_bsr.cc index 5707aac8b..501307fe9 100644 --- a/srsue/src/stack/mac/proc_bsr.cc +++ b/srsue/src/stack/mac/proc_bsr.cc @@ -378,8 +378,9 @@ bool bsr_proc::generate_padding_bsr(uint32_t nof_padding_bytes, bsr_t* bsr) // This function is called by MUX only if Regular BSR has not been triggered before if (nof_padding_bytes >= 2) { // generate padding BSR - triggered_bsr_type = PADDING; + set_trigger(PADDING); generate_bsr(bsr, nof_padding_bytes); + set_trigger(NONE); ret = true; } diff --git a/srsue/src/stack/mac/proc_ra.cc b/srsue/src/stack/mac/proc_ra.cc index 84443778f..aed4a57ef 100644 --- a/srsue/src/stack/mac/proc_ra.cc +++ b/srsue/src/stack/mac/proc_ra.cc @@ -38,12 +38,20 @@ namespace srsue { +const char* state_str[6] = { + "RA: INIT: ", "RA: PDCCH: ", "RA: Rx: ", "RA: Backof: ", "RA: ConRes: ", "RA: Complt: "}; + +#define rError(fmt, ...) Error("%s" fmt, state_str[state], ##__VA_ARGS__) +#define rInfo(fmt, ...) Info("%s" fmt, state_str[state], ##__VA_ARGS__) +#define rDebug(fmt, ...) Debug("%s" fmt, state_str[state], ##__VA_ARGS__) + // Table 7.2-1. Backoff Parameter values uint32_t backoff_table[16] = {0, 10, 20, 30, 40, 60, 80, 120, 160, 240, 320, 480, 960, 960, 960, 960}; // Table 7.6-1: DELTA_PREAMBLE values. int delta_preamble_db_table[5] = {0, 0, -3, -3, 8}; +// Initializes memory and pointers to other objects void ra_proc::init(phy_interface_mac_lte* phy_h_, rrc_interface_mac* rrc_, srslte::log* log_h_, @@ -80,15 +88,17 @@ void ra_proc::reset() { void ra_proc::start_pcap(srslte::mac_pcap* pcap_) { - pcap = pcap_; + pcap = pcap_; } +/* Sets a new configuration. The configuration is applied by initialization() function */ void ra_proc::set_config(srsue::mac_interface_rrc::rach_cfg_t& rach_cfg) { std::unique_lock ul(mutex); new_cfg = rach_cfg; } +/* Reads the configuration and configures internal variables */ void ra_proc::read_params() { mutex.lock(); @@ -117,75 +127,129 @@ void ra_proc::read_params() } } -bool ra_proc::is_contention_resolution() { - return state == CONTENTION_RESOLUTION; +/* Function called by MAC every TTI. Runs a state function until it changes to a different state + */ +void ra_proc::step(uint32_t tti_) +{ + switch (state) { + case IDLE: + break; + case PDCCH_SETUP: + state_pdcch_setup(); + break; + case RESPONSE_RECEPTION: + state_response_reception(tti_); + break; + case BACKOFF_WAIT: + state_backoff_wait(tti_); + break; + case CONTENTION_RESOLUTION: + state_contention_resolution(); + break; + case COMPLETITION: + state_completition(); + break; + } } -const char* state_str[12] = {"Idle", - "RA: INIT: ", - "RA: Select: ", - "RA: TX: ", - "RA: PDCCH: ", - "RA: Rx: ", - "RA: RxErr: ", - "RA: Backof: ", - "RA: ConRes: ", - "RA: Done: ", - "RA: Done: ", - "RA: Error: "}; - - -#define rError(fmt, ...) Error("%s" fmt, state_str[state], ##__VA_ARGS__) -#define rInfo(fmt, ...) Info("%s" fmt, state_str[state], ##__VA_ARGS__) -#define rDebug(fmt, ...) Debug("%s" fmt, state_str[state], ##__VA_ARGS__) +/* Waits for PRACH to be transmitted by PHY. Once it's transmitted, configure RA-RNTI and wait for RAR reception + */ +void ra_proc::state_pdcch_setup() +{ - -// Process Timing Advance Command as defined in Section 5.2 -void ra_proc::process_timeadv_cmd(uint32_t ta) { - if (preambleIndex == 0) { - // Preamble not selected by UE MAC - phy_h->set_timeadv_rar(ta); - // Only if timer is running reset the timer - if (time_alignment_timer->is_running()) { - time_alignment_timer->reset(); - time_alignment_timer->run(); - } - Debug("Applying RAR TA CMD %d\n", ta); + phy_interface_mac_lte::prach_info_t info = phy_h->prach_get_info(); + if (info.is_transmitted) { + ra_tti = info.tti_ra; + ra_rnti = 1 + (ra_tti % 10) + info.f_id; + rInfo("seq=%d, ra-rnti=0x%x, ra-tti=%d, f_id=%d\n", sel_preamble, ra_rnti, info.tti_ra, info.f_id); + log_h->console("Random Access Transmission: seq=%d, ra-rnti=0x%x\n", sel_preamble, ra_rnti); + rar_window_st = ra_tti + 3; + rntis->rar_rnti = ra_rnti; + state = RESPONSE_RECEPTION; } else { - // Preamble selected by UE MAC - if (!time_alignment_timer->is_running()) { - phy_h->set_timeadv_rar(ta); - time_alignment_timer->run(); - Debug("Applying RAR TA CMD %d\n", ta); + rDebug("preamble not yet transmitted\n"); + } +} + +/* Waits for RAR reception. rar_received variable will be set by tb_decoded_ok() function which is called when a DL + * TB assigned to RA-RNTI is received + */ +void ra_proc::state_response_reception(uint32_t tti) +{ + // do nothing. Processing done in tb_decoded_ok() + if (!rar_received) { + uint32_t interval = srslte_tti_interval(tti, ra_tti + 3 + rach_cfg.responseWindowSize - 1); + if (interval > 0 && interval < 100) { + Error("RA response not received within the response window\n"); + response_error(); + } + } +} + +/* Waits for given backoff interval to expire + */ +void ra_proc::state_backoff_wait(uint32_t tti) +{ + if (backoff_interval > 0) { + // Backoff_interval = 0 is handled before entering here + // When we arrive to this state, there is already 1 TTI delay + if (backoff_interval == 1) { + resource_selection(); } else { - // Ignore TA CMD - Warning("Ignoring RAR TA CMD because timeAlignmentTimer still running\n"); + // If it's the first time, save TTI + if (backoff_interval_start == -1) { + backoff_interval_start = tti; + backoff_interval--; + } + if (srslte_tti_interval(tti, backoff_interval_start) >= backoff_interval) { + backoff_interval = 0; + resource_selection(); + } } } } -void ra_proc::step_initialization() { +/* Actions during contention resolution state as defined in 5.1.5 + * Resolution of the Contention is made by contention_resolution_id_received() and pdcch_to_crnti() + */ +void ra_proc::state_contention_resolution() +{ + // Once Msg3 is transmitted, start contention resolution timer + if (mux_unit->msg3_is_transmitted() && !contention_resolution_timer->is_running()) { + // Start contention resolution timer + rInfo("Starting ContentionResolutionTimer=%d ms\n", contention_resolution_timer->get_timeout()); + contention_resolution_timer->reset(); + contention_resolution_timer->run(); + } +} + +/* This step just configures the PHY to generate the C-RNTI. It is called from a state because it takes a long time to + * compute + */ +void ra_proc::state_completition() +{ + phy_h->set_crnti(rntis->crnti); + state = IDLE; +} + +/* RA procedure initialization as defined in 5.1.1 */ +void ra_proc::initialization() +{ read_params(); - pdcch_to_crnti_received = PDCCH_CRNTI_NOT_RECEIVED; - transmitted_contention_id = 0; + transmitted_contention_id = 0; preambleTransmissionCounter = 1; - first_rar_received = true; mux_unit->msg3_flush(); - msg3_flushed = false; - backoff_param_ms = 0; + backoff_param_ms = 0; - // FIXME: This is because RA in Connected state not working in amarisoft - transmitted_crnti = rntis->crnti; - if(transmitted_crnti) { - state = RESPONSE_ERROR; - } - // Instruct phy to configure PRACH phy_h->configure_prach_params(); - state = RESOURCE_SELECTION; + + // Jump directly to Resource selection + resource_selection(); } -void ra_proc::step_resource_selection() +/* Resource selection as defined in 5.1.2 */ +void ra_proc::resource_selection() { ra_group_t sel_group; @@ -217,7 +281,7 @@ void ra_proc::step_resource_selection() sel_preamble = rand() % rach_cfg.nof_groupA_preambles; } else { rError("Selected group preamble A but nof_groupA_preambles=0\n"); - state = RA_PROBLEM; + state = IDLE; return; } } else { @@ -226,7 +290,7 @@ void ra_proc::step_resource_selection() sel_preamble = rach_cfg.nof_groupA_preambles + rand() % nof_groupB_preambles; } else { rError("Selected group preamble B but nof_groupA_preambles=0\n"); - state = RA_PROBLEM; + state = IDLE; return; } } @@ -238,51 +302,55 @@ void ra_proc::step_resource_selection() sel_maskIndex, rach_cfg.nof_groupA_preambles, nof_groupB_preambles); - state = PREAMBLE_TRANSMISSION; + + // Jump directly to transmission + preamble_transmission(); } -void ra_proc::step_preamble_transmission() { +/* Preamble transmission as defined in 5.1.3 */ +void ra_proc::preamble_transmission() +{ received_target_power_dbm = rach_cfg.iniReceivedTargetPower + delta_preamble_db + (preambleTransmissionCounter - 1) * rach_cfg.powerRampingStep; - rar_received = false; phy_h->prach_send(sel_preamble, sel_maskIndex - 1, received_target_power_dbm); + rntis->rar_rnti = 0; + ra_tti = 0; + rar_received = false; + backoff_interval_start = -1; + state = PDCCH_SETUP; } -bool ra_proc::is_rar_window(int* rar_window_start, int* rar_window_length) +// Process Timing Advance Command as defined in Section 5.2 +void ra_proc::process_timeadv_cmd(uint32_t ta) { - if (state == RESPONSE_RECEPTION) { - if (rar_window_length) { - *rar_window_length = rach_cfg.responseWindowSize; - } - if (rar_window_start) { - *rar_window_start = rar_window_st; + if (preambleIndex == 0) { + // Preamble not selected by UE MAC + phy_h->set_timeadv_rar(ta); + // Only if timer is running reset the timer + if (time_alignment_timer->is_running()) { + time_alignment_timer->reset(); + time_alignment_timer->run(); } - return true; + Debug("Applying RAR TA CMD %d\n", ta); } else { - if (rar_window_length) { - *rar_window_length = -1; + // Preamble selected by UE MAC + if (!time_alignment_timer->is_running()) { + phy_h->set_timeadv_rar(ta); + time_alignment_timer->run(); + Debug("Applying RAR TA CMD %d\n", ta); + } else { + // Ignore TA CMD + Warning("Ignoring RAR TA CMD because timeAlignmentTimer still running\n"); } - return false; - } -} - -void ra_proc::step_pdcch_setup() -{ - - phy_interface_mac_lte::prach_info_t info = phy_h->prach_get_info(); - if (info.is_transmitted) { - ra_rnti = 1 + info.tti_ra % 10 + info.f_id; - rInfo("seq=%d, ra-rnti=0x%x, ra-tti=%d, f_id=%d\n", sel_preamble, ra_rnti, info.tti_ra, info.f_id); - log_h->console("Random Access Transmission: seq=%d, ra-rnti=0x%x\n", sel_preamble, ra_rnti); - rar_window_st = info.tti_ra + 3; - rntis->rar_rnti = ra_rnti; - state = RESPONSE_RECEPTION; } } +/* Called upon the reception of a DL grant for RA-RNTI + * Configures the action and softbuffer for the reception of the associated TB + */ void ra_proc::new_grant_dl(mac_interface_phy_lte::mac_grant_dl_t grant, mac_interface_phy_lte::tb_action_dl_t* action) { bzero(action, sizeof(mac_interface_phy_lte::tb_action_dl_t)); @@ -299,10 +367,12 @@ void ra_proc::new_grant_dl(mac_interface_phy_lte::mac_grant_dl_t grant, mac_inte } } else { rError("Received RAR dci exceeds buffer length (%d>%d)\n", grant.tb[0].tbs, MAX_RAR_PDU_LEN); - state = RESPONSE_ERROR; } } +/* Called upon the successful decoding of a TB addressed to RA-RNTI. + * Processes the reception of a RAR as defined in 5.1.4 + */ void ra_proc::tb_decoded_ok() { if (pcap) { pcap->write_dl_ranti(rar_pdu_buffer, rar_grant_nbytes, ra_rnti, true, 0); @@ -312,6 +382,7 @@ void ra_proc::tb_decoded_ok() { rar_pdu_msg.init_rx(rar_grant_nbytes); rar_pdu_msg.parse_packet(rar_pdu_buffer); + // Set Backoff parameter if (rar_pdu_msg.has_backoff()) { backoff_param_ms = backoff_table[rar_pdu_msg.get_backoff()%16]; @@ -345,14 +416,14 @@ void ra_proc::tb_decoded_ok() { if (preambleIndex > 0) { // Preamble selected by Network - state = COMPLETION; + complete(); } else { // Preamble selected by UE MAC mux_unit->msg3_prepare(); rntis->temp_rnti = rar_pdu_msg.get()->get_temp_crnti(); - if (first_rar_received) { - first_rar_received = false; + // If this is the first successfully received RAR within this procedure, Msg3 is empty + if (mux_unit->msg3_is_empty()) { // Save transmitted C-RNTI (if any) transmitted_crnti = rntis->crnti; @@ -361,16 +432,14 @@ void ra_proc::tb_decoded_ok() { if (transmitted_crnti) { rInfo("Appending C-RNTI MAC CE 0x%x in next transmission\n", transmitted_crnti); mux_unit->append_crnti_ce_next_tx(transmitted_crnti); - rntis->crnti = transmitted_crnti; } } - rDebug("Going to Contention Resolution state\n"); - state = CONTENTION_RESOLUTION; - // Start contention resolution timer - rInfo("Starting ContentionResolutionTimer=%d ms\n", contention_resolution_timer->get_timeout()); - contention_resolution_timer->reset(); - contention_resolution_timer->run(); + // Save transmitted UE contention id, as defined by higher layers + transmitted_contention_id = rntis->contention_id; + + rDebug("Waiting for Contention Resolution\n"); + state = CONTENTION_RESOLUTION; } } else { if (rar_pdu_msg.get()->has_rapid()) { @@ -380,170 +449,61 @@ void ra_proc::tb_decoded_ok() { } } -void ra_proc::step_response_reception(uint32_t tti) -{ - // do nothing. Processing done in tb_decoded_ok() - phy_interface_mac_lte::prach_info_t prach_info = phy_h->prach_get_info(); - if (prach_info.is_transmitted && !rar_received) { - uint32_t interval = srslte_tti_interval(tti, prach_info.tti_ra + 3 + rach_cfg.responseWindowSize); - if (interval > 1 && interval < 100) { - Error("RA response not received within the response window\n"); - state = RESPONSE_ERROR; - } - } -} - -void ra_proc::step_response_error(uint32_t tti) +/* Called after RA response window expiration without a valid RAPID or after a reception of an invalid + * Contention Resolution ID + */ +void ra_proc::response_error() { + rntis->temp_rnti = 0; preambleTransmissionCounter++; if (preambleTransmissionCounter >= rach_cfg.preambleTransMax + 1) { rError("Maximum number of transmissions reached (%d)\n", rach_cfg.preambleTransMax); rrc->ra_problem(); - state = RA_PROBLEM; + state = IDLE; if (ra_is_ho) { rrc->ho_ra_completed(false); } } else { - backoff_interval_start = tti; + backoff_interval_start = -1; if (backoff_param_ms) { - backoff_inteval = rand() % backoff_param_ms; + backoff_interval = rand() % backoff_param_ms; } else { - backoff_inteval = 0; + backoff_interval = 0; } - if (backoff_inteval) { - rDebug("Backoff wait interval %d\n", backoff_inteval); + if (backoff_interval) { + rDebug("Backoff wait interval %d\n", backoff_interval); state = BACKOFF_WAIT; } else { rDebug("Transmitting inmediatly (%d/%d)\n", preambleTransmissionCounter, rach_cfg.preambleTransMax); - state = RESOURCE_SELECTION; + resource_selection(); } } } -void ra_proc::step_backoff_wait(uint32_t tti) +bool ra_proc::is_contention_resolution() { - if (srslte_tti_interval(tti, backoff_interval_start) >= backoff_inteval) { - state = RESOURCE_SELECTION; - } -} - -// Random Access initiated by RRC by the transmission of CCCH SDU -bool ra_proc::contention_resolution_id_received(uint64_t rx_contention_id) { - bool uecri_successful = false; - - rDebug("MAC PDU Contains Contention Resolution ID CE\n"); - - // MAC PDU successfully decoded and contains MAC CE contention Id - contention_resolution_timer->stop(); - - if (transmitted_contention_id == rx_contention_id) - { - // UE Contention Resolution ID included in MAC CE matches the CCCH SDU transmitted in Msg3 - uecri_successful = true; - state = COMPLETION; - } else { - rInfo("Transmitted UE Contention Id differs from received Contention ID (0x%" PRIu64 " != 0x%" PRIu64 ")\n", - transmitted_contention_id, rx_contention_id); - // Discard MAC PDU - uecri_successful = false; - - // Contention Resolution not successfully is like RAR not successful - // FIXME: Need to flush Msg3 HARQ buffer. Why? - state = RESPONSE_ERROR; - } - - return uecri_successful; -} - -void ra_proc::step_contention_resolution() { - // If Msg3 has been sent - if (mux_unit->msg3_is_transmitted()) - { - msg3_transmitted = true; - if (transmitted_crnti) { - // Random Access with transmission of MAC C-RNTI CE - if ((!started_by_pdcch && pdcch_to_crnti_received == PDCCH_CRNTI_UL_GRANT) || - (started_by_pdcch && pdcch_to_crnti_received != PDCCH_CRNTI_NOT_RECEIVED)) - { - rDebug("PDCCH for C-RNTI received\n"); - contention_resolution_timer->stop(); - state = COMPLETION; - } - pdcch_to_crnti_received = PDCCH_CRNTI_NOT_RECEIVED; - } else { - // RA with transmission of CCCH SDU is resolved in contention_resolution_id_received() callback function - if (!transmitted_contention_id) { - // Save transmitted UE contention id, as defined by higher layers - transmitted_contention_id = rntis->contention_id; - rntis->contention_id = 0; - } - } - } else { - rDebug("Msg3 not yet transmitted\n"); - } + return state == CONTENTION_RESOLUTION; } -void ra_proc::step_completition() +/* Perform the actions upon completition of the RA procedure as defined in 5.1.6 */ +void ra_proc::complete() { - - // Start looking for PDCCH CRNTI + /* Start looking for PDCCH CRNTI */ if (!transmitted_crnti) { rntis->crnti = rntis->temp_rnti; } rntis->temp_rnti = 0; - log_h->console("Random Access Complete. c-rnti=0x%x, ta=%d\n", rntis->crnti, current_ta); - rInfo("Random Access Complete. c-rnti=0x%x, ta=%d\n", rntis->crnti, current_ta); - - if (!msg3_flushed) { - mux_unit->msg3_flush(); - msg3_flushed = true; - } - - phy_h->set_crnti(rntis->crnti); + mux_unit->msg3_flush(); msg3_transmitted = false; - state = COMPLETION_DONE; if (ra_is_ho) { rrc->ho_ra_completed(true); } -} + log_h->console("Random Access Complete. c-rnti=0x%x, ta=%d\n", rntis->crnti, current_ta); + rInfo("Random Access Complete. c-rnti=0x%x, ta=%d\n", rntis->crnti, current_ta); -void ra_proc::step(uint32_t tti_) -{ - switch(state) { - case IDLE: - break; - case INITIALIZATION: - step_initialization(); - break; - case RESOURCE_SELECTION: - step_resource_selection(); - break; - case PREAMBLE_TRANSMISSION: - step_preamble_transmission(); - break; - case PDCCH_SETUP: - step_pdcch_setup(); - break; - case RESPONSE_RECEPTION: - step_response_reception(tti_); - break; - case RESPONSE_ERROR: - step_response_error(tti_); - break; - case BACKOFF_WAIT: - step_backoff_wait(tti_); - break; - case CONTENTION_RESOLUTION: - step_contention_resolution(); - break; - case COMPLETION: - step_completition(); - case COMPLETION_DONE: - case RA_PROBLEM: - break; - } + state = COMPLETITION; } void ra_proc::start_noncont(uint32_t preamble_index, uint32_t prach_mask) { @@ -555,21 +515,25 @@ void ra_proc::start_noncont(uint32_t preamble_index, uint32_t prach_mask) { void ra_proc::start_mac_order(uint32_t msg_len_bits, bool is_ho) { - if (state == IDLE || state == COMPLETION_DONE || state == RA_PROBLEM) { + if (state == IDLE) { ra_is_ho = is_ho; started_by_pdcch = false; - new_ra_msg_len = msg_len_bits; - state = INITIALIZATION; + new_ra_msg_len = msg_len_bits; rInfo("Starting PRACH by MAC order\n"); + initialization(); + } else { + Warning("Trying to start PRACH by MAC order in invalid state (%s)\n", state_str[state]); } } void ra_proc::start_pdcch_order() { - if (state == IDLE || state == COMPLETION_DONE || state == RA_PROBLEM) { + if (state == IDLE) { started_by_pdcch = true; - state = INITIALIZATION; rInfo("Starting PRACH by PDCCH order\n"); + initialization(); + } else { + Warning("Trying to start PRACH by MAC order in invalid state (%s)\n", state_str[state]); } } @@ -577,29 +541,79 @@ void ra_proc::start_pdcch_order() void ra_proc::timer_expired(uint32_t timer_id) { rInfo("Contention Resolution Timer expired. Stopping PDCCH Search and going to Response Error\n"); - bzero(rntis, sizeof(mac_interface_rrc::ue_rnti_t)); - state = RESPONSE_ERROR; + response_error(); +} + +/* Function called by MAC when a Contention Resolution ID CE is received. + * Performs the actions defined in 5.1.5 for Temporal C-RNTI Contention Resolution + */ +bool ra_proc::contention_resolution_id_received(uint64_t rx_contention_id) +{ + bool uecri_successful = false; + + rDebug("MAC PDU Contains Contention Resolution ID CE\n"); + + // MAC PDU successfully decoded and contains MAC CE contention Id + contention_resolution_timer->stop(); + + if (transmitted_contention_id == rx_contention_id) { + // UE Contention Resolution ID included in MAC CE matches the CCCH SDU transmitted in Msg3 + uecri_successful = true; + complete(); + } else { + rInfo("Transmitted UE Contention Id differs from received Contention ID (0x%lx != 0x%lx)\n", + transmitted_contention_id, + rx_contention_id); + + // Discard MAC PDU + uecri_successful = false; + + // Contention Resolution not successfully is like RAR not successful + response_error(); + } + + return uecri_successful; } -void ra_proc::pdcch_to_crnti(bool contains_uplink_grant) { - rDebug("PDCCH to C-RNTI received %s UL dci\n", contains_uplink_grant ? "with" : "without"); - if (contains_uplink_grant) { - pdcch_to_crnti_received = PDCCH_CRNTI_UL_GRANT; - } else if (pdcch_to_crnti_received == PDCCH_CRNTI_NOT_RECEIVED) { - pdcch_to_crnti_received = PDCCH_CRNTI_DL_GRANT; +void ra_proc::pdcch_to_crnti(bool is_new_uplink_transmission) +{ + rDebug("PDCCH to C-RNTI received %s new UL transmission\n", is_new_uplink_transmission ? "with" : "without"); + if ((!started_by_pdcch && is_new_uplink_transmission) || started_by_pdcch) { + rDebug("PDCCH for C-RNTI received\n"); + contention_resolution_timer->stop(); + complete(); + } +} + +bool ra_proc::update_rar_window(int* rar_window_start, int* rar_window_length) +{ + if (state == RESPONSE_RECEPTION) { + if (rar_window_length) { + *rar_window_length = rach_cfg.responseWindowSize; + } + if (rar_window_start) { + *rar_window_start = rar_window_st; + } + return true; + } else { + if (rar_window_length) { + *rar_window_length = -1; + } + return false; } } +// Restart timer at each Msg3 HARQ retransmission (5.1.5) void ra_proc::harq_retx() { + rInfo("Restarting ContentionResolutionTimer=%d ms\n", contention_resolution_timer->get_timeout()); contention_resolution_timer->reset(); } void ra_proc::harq_max_retx() { Warning("Contention Resolution is considered not successful. Stopping PDCCH Search and going to Response Error\n"); - bzero(rntis, sizeof(mac_interface_rrc::ue_rnti_t)); - state = RESPONSE_ERROR; + response_error(); } } diff --git a/srsue/src/stack/mac/proc_sr.cc b/srsue/src/stack/mac/proc_sr.cc index 44b27b58a..9ab439a17 100644 --- a/srsue/src/stack/mac/proc_sr.cc +++ b/srsue/src/stack/mac/proc_sr.cc @@ -49,7 +49,8 @@ void sr_proc::reset() bool sr_proc::need_tx(uint32_t tti) { - int last_tx_tti = phy_h->sr_last_tx_tti(); + int last_tx_tti = phy_h->sr_last_tx_tti(); + Info("SR: need_tx(): last_tx_tti=%d, tti=%d\n", last_tx_tti, tti); if (last_tx_tti >= 0) { if (tti > (uint32_t)last_tx_tti) { if (tti - last_tx_tti > 8) { diff --git a/srsue/src/stack/mac/ul_harq.cc b/srsue/src/stack/mac/ul_harq.cc index 375d0ec0f..a2bca04a2 100644 --- a/srsue/src/stack/mac/ul_harq.cc +++ b/srsue/src/stack/mac/ul_harq.cc @@ -199,10 +199,6 @@ void ul_harq_entity::ul_harq_process::new_grant_ul(mac_interface_phy_lte::mac_gr harq_feedback = grant.hi_value; } - if (grant.rnti == harq_entity->rntis->crnti && harq_entity->ra_procedure->is_contention_resolution()) { - harq_entity->ra_procedure->pdcch_to_crnti(true); - } - // Get maximum retransmissions uint32_t max_retx; if (grant_is_rar()) { @@ -218,7 +214,7 @@ void ul_harq_entity::ul_harq_process::new_grant_ul(mac_interface_phy_lte::mac_gr harq_entity->ra_procedure->harq_max_retx(); } reset(); - } else if (grant_is_rar()) { + } else if (grant_is_rar() && current_tx_nb) { harq_entity->ra_procedure->harq_retx(); } } @@ -244,6 +240,10 @@ void ul_harq_entity::ul_harq_process::new_grant_ul(mac_interface_phy_lte::mac_gr // New transmission reset(); + if (grant.rnti == harq_entity->rntis->crnti && harq_entity->ra_procedure->is_contention_resolution()) { + harq_entity->ra_procedure->pdcch_to_crnti(true); + } + // Check buffer size if (grant.tb.tbs > payload_buffer_len) { Error("Grant size exceeds payload buffer size (%d > %d)\n", grant.tb.tbs, payload_buffer_len); diff --git a/srsue/test/mac_test.cc b/srsue/test/mac_test.cc index 3048f5759..8be24dfc8 100644 --- a/srsue/test/mac_test.cc +++ b/srsue/test/mac_test.cc @@ -31,7 +31,7 @@ using namespace srsue; using namespace srslte; -#define HAVE_PCAP 0 +#define HAVE_PCAP 1 static std::unique_ptr pcap_handle = nullptr; @@ -57,7 +57,7 @@ public: uint32_t len = SRSLTE_MIN(ul_queues[lcid], nof_bytes); // set payload bytes to LCID so we can check later if the scheduling was correct - memset(payload, lcid, len); + memset(payload, lcid > 0 ? lcid : 0xf, len); // remove from UL queue ul_queues[lcid] -= len; @@ -88,24 +88,74 @@ class phy_dummy : public phy_interface_mac_lte { public: phy_dummy() : scell_cmd(0){}; + + void set_log(srslte::log* log_h) { this->log_h = log_h; } + void reset() + { + last_preamble_idx = 0; + last_target_power = 0; + prach_delay_cnt = 0; + prach_tti = 0; + nof_rar_grants = 0; + rar_temp_rnti = 0; + rar_time_adv = 0; + last_crnti = 0; + prach_transmitted = false; + prach_info_tx = false; + } + + void set_prach_tti(uint32_t tti, bool reset_transmitted = true) + { + this->prach_tti = tti; + if (reset_transmitted) { + prach_transmitted = false; + } + } + // phy_interface_mac_lte void configure_prach_params(){}; - virtual void prach_send(uint32_t preamble_idx, int allowed_subframe, float target_power_dbm){}; + + void prach_send(uint32_t preamble_idx, int allowed_subframe, float target_power_dbm) + { + prach_delay_cnt = 0; + last_preamble_idx = preamble_idx; + last_target_power = target_power_dbm; + prach_transmitted = true; + prach_info_tx = true; + log_h->info("PRACH will be transmitted at tti=%d, preamble_idx=%d\n", prach_tti, preamble_idx); + } + prach_info_t prach_get_info() { prach_info_t info = {}; + if (prach_info_tx) { + prach_delay_cnt++; + if (prach_delay_cnt > prach_delay) { + info.tti_ra = prach_tti; + prach_info_tx = false; + info.is_transmitted = true; + log_h->info("PRACH has been transmitted\n"); + } + } return info; }; + void sr_send(){}; int sr_last_tx_tti() { return 0; }; void set_mch_period_stop(uint32_t stop){}; // phy_interface_mac_common - void set_crnti(uint16_t rnti){}; - void set_timeadv_rar(uint32_t ta_cmd){}; + void set_crnti(uint16_t rnti) { last_crnti = rnti; } + void set_timeadv_rar(uint32_t ta_cmd) { rar_time_adv = ta_cmd; } void set_timeadv(uint32_t ta_cmd){}; void set_activation_deactivation_scell(uint32_t cmd) { scell_cmd = cmd; }; - void set_rar_grant(uint8_t grant_payload[SRSLTE_RAR_GRANT_LEN], uint16_t rnti){}; + void set_rar_grant(uint8_t grant_payload[SRSLTE_RAR_GRANT_LEN], uint16_t rnti) + { + memcpy(rar_payload, grant_payload, SRSLTE_RAR_GRANT_LEN); + rar_temp_rnti = rnti; + nof_rar_grants++; + } + uint32_t get_current_tti() { return 0; } float get_phr() { return 0; }; float get_pathloss_db() { return 0; }; @@ -113,17 +163,161 @@ public: // getter for test execution uint32_t get_scell_cmd() { return scell_cmd; } + // Testing methods + int dl_grant(mac* mac_h, bool ack, uint16_t rnti, uint32_t len, const uint8_t* payload) + { + + bool ack_v[SRSLTE_MAX_CODEWORDS] = {ack, 0}; + + mac_interface_phy_lte::tb_action_dl_t dl_action = {}; + mac_interface_phy_lte::mac_grant_dl_t dl_mac_grant = {}; + + // Send grant to MAC + dl_mac_grant.rnti = rnti; + dl_mac_grant.tb[0].ndi = dl_ndi; + dl_mac_grant.tb[0].ndi_present = true; + dl_mac_grant.tb[0].tbs = len; + mac_h->new_grant_dl(0, dl_mac_grant, &dl_action); + + if (ack && !SRSLTE_RNTI_ISRAR(rnti)) { + dl_ndi = !dl_ndi; + } + + TESTASSERT(dl_action.tb[0].enabled); + TESTASSERT((int)dl_action.tb[0].rv == dl_mac_grant.tb[0].rv); + + // Copy data and send tb_decoded + memcpy(dl_action.tb[0].payload, payload, len); + + // print generated PDU + log_h->info_hex( + dl_action.tb[0].payload, dl_mac_grant.tb[0].tbs, "Generated DL PDU (%d B)\n", dl_mac_grant.tb[0].tbs); + +#if HAVE_PCAP + pcap_handle->write_dl_crnti(dl_action.tb[0].payload, dl_mac_grant.tb[0].tbs, rnti, true, 1); +#endif + + mac_h->tb_decoded(0, dl_mac_grant, ack_v); + + return 0; + } + + int rar_and_check(mac* mac_h, bool preamble_matches, uint32_t temp_rnti) + { + + // Generate RAR to MAC + uint8_t grant[SRSLTE_RAR_GRANT_LEN] = {}; + memset(grant, 1, SRSLTE_RAR_GRANT_LEN); + + uint32_t rar_timeadv = 16; + + srslte::rar_pdu rar_pdu_msg; + byte_buffer.clear(); + rar_pdu_msg.init_tx(&byte_buffer, 7); + if (rar_pdu_msg.new_subh()) { + rar_pdu_msg.get()->set_rapid(preamble_matches ? last_preamble_idx : (last_preamble_idx + 1)); + rar_pdu_msg.get()->set_ta_cmd(rar_timeadv); + rar_pdu_msg.get()->set_temp_crnti(temp_rnti); + rar_pdu_msg.get()->set_sched_grant(grant); + } + rar_pdu_msg.write_packet(byte_buffer.msg); + + // Send RAR grant to MAC + dl_grant(mac_h, true, get_rar_rnti(), 7, byte_buffer.msg); + + // Check MAC passes RAR grant and TA cmd to PHY + if (preamble_matches) { + TESTASSERT(!memcmp(rar_payload, grant, SRSLTE_RAR_GRANT_LEN)); + TESTASSERT(rar_temp_rnti == temp_rnti); + TESTASSERT(rar_time_adv == rar_timeadv); + } + + return 0; + } + + int ul_grant_and_check_tv(mac* mac_h, bool ack, uint16_t rnti, uint32_t len, const uint8_t* tv) + { + + mac_interface_phy_lte::tb_action_ul_t ul_action = {}; + mac_interface_phy_lte::mac_grant_ul_t ul_mac_grant = {}; + + // Generate UL Grant + ul_mac_grant.phich_available = !ack; + ul_mac_grant.rnti = rnti; + ul_mac_grant.tb.ndi = ul_ndi; + ul_mac_grant.tb.ndi_present = ack; + ul_mac_grant.tb.tbs = len; + + if (ack) { + ul_ndi = !ul_ndi; + } + + // Send grant to MAC and get action for this TB, then call tb_decoded to unlock MAC + mac_h->new_grant_ul(0, ul_mac_grant, &ul_action); + + // print generated PDU + log_h->info_hex(ul_action.tb.payload, ul_mac_grant.tb.tbs, "Generated UL PDU (%d B)\n", ul_mac_grant.tb.tbs); + +#if HAVE_PCAP + pcap_handle->write_ul_crnti(ul_action.tb.payload, ul_mac_grant.tb.tbs, rnti, true, 1); +#endif + + if (tv && ul_action.tb.payload) { + return memcmp(ul_action.tb.payload, tv, len); + } else { + return 0; + } + } + + int get_last_preamble() { return last_preamble_idx; } + + uint32_t is_prach_transmitted() { return prach_transmitted; } + + uint32_t get_rar_rnti() { return (prach_tti % 10) + 1; } + + uint16_t get_crnti() { return last_crnti; } + + const static uint32_t prach_delay = 5; + private: - uint32_t scell_cmd; + uint32_t scell_cmd = 0; + + uint32_t prach_delay_cnt = 0; + uint32_t prach_tti = 0; + bool prach_info_tx = false; + bool prach_transmitted = false; + float last_target_power = 0; + int last_preamble_idx = -1; + + uint16_t last_crnti = 0; + + srslte::log* log_h; + + bool ul_ndi = false; + bool dl_ndi = false; + + byte_buffer_t byte_buffer; + + uint32_t nof_rar_grants = 0; + uint32_t rar_time_adv = 0; + uint16_t rar_temp_rnti = 0; + uint8_t rar_payload[SRSLTE_RAR_GRANT_LEN]; }; class rrc_dummy : public rrc_interface_mac { public: - void ho_ra_completed(bool ra_successful) { printf("%s\n", __FUNCTION__); } + void ho_ra_completed(bool ra_successful) + { + ho_finish = true; + ho_finish_successful = ra_successful; + } void release_pucch_srs() { printf("%s\n", __FUNCTION__); } void run_tti(uint32_t tti) { printf("%s\n", __FUNCTION__); } - void ra_problem() { printf("%s\n", __FUNCTION__); } + void ra_problem() { rach_problem++; } + bool ho_finish = false; + bool ho_finish_successful = false; + uint32_t rach_problem = 0; }; } // namespace srslte @@ -873,10 +1067,185 @@ int mac_ul_sch_pdu_three_byte_test() return SRSLTE_SUCCESS; } -// UL-SCH with Trucated BSR and 6 B Msg3 -int mac_ul_sch_pdu_msg3_test() +struct ra_test { + uint32_t nof_prachs; + uint32_t rar_nof_rapid; // set to zero to don't transmit RAR + uint32_t rar_nof_invalid_rapid; + uint16_t crnti; + uint16_t temp_rnti; + uint32_t nof_msg3_retx; + uint32_t preamble_idx; + int assume_prach_transmitted; + bool send_valid_ul_grant; + bool msg4_enable; + bool msg4_valid_conres; + bool check_ra_successful; + asn1::rrc::rach_cfg_common_s rach_cfg; +}; + +struct ra_test test; + +int run_mac_ra_test(struct ra_test test, mac* mac, phy_dummy* phy, uint32_t* tti_state) +{ + uint32_t tti = *tti_state; + + const uint8_t tv_msg3[] = {0x3c, 0x00, 0xd0, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f}; + const uint8_t tv_msg3_ce[] = {0x1b, 0x00, 0x65}; + + uint32_t msg4_len = 7; + const uint8_t tv_msg4_nocontres[] = {0x1f, 0x1f}; + const uint8_t tv_msg4_valid[] = {0x1c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f}; + const uint8_t tv_msg4_invalid[] = {0x1c, 0x0f, 0x0a, 0x0f, 0x0f, 0x0f, 0x0f}; + + uint16_t temp_rnti = test.temp_rnti; + + bool new_prach = false; + + for (uint32_t j = 0; j < test.nof_prachs; j++) { + + // In the next TTI, a BSR shall be triggered which triggers SR which triggers PRACH + if (test.assume_prach_transmitted != (int)j) { + phy->set_prach_tti(tti + phy->prach_delay); + mac->run_tti(tti++); + } + + // Check MAC instructs PHY to transmit PRACH + TESTASSERT(phy->is_prach_transmitted()); + + // Test preamble index + if (test.preamble_idx) { + TESTASSERT(phy->get_last_preamble() == (int)test.preamble_idx); + } else { + TESTASSERT(phy->get_last_preamble() < test.rach_cfg.preamb_info.nof_ra_preambs.to_number()); + } + + // TODO: Test power ramping + + // Check MAC does not schedule RA-RNTI before window starts + for (uint32_t i = 0; i < phy->prach_delay + 3 - 1; i++) { + mac->run_tti(tti); + TESTASSERT(!SRSLTE_RNTI_ISRAR(mac->get_dl_sched_rnti(tti))); + tti++; + } + + bool rapid_found = false; + // Check MAC schedules correct RA-RNTI during window + for (uint32_t i = 0; i < test.rach_cfg.ra_supervision_info.ra_resp_win_size.to_number() && !rapid_found; i++) { + mac->run_tti(tti); + TESTASSERT(mac->get_dl_sched_rnti(tti) == phy->get_rar_rnti()); + tti++; + + // Receive RAR + if (test.rar_nof_rapid > 0) { + rapid_found = i >= test.rar_nof_invalid_rapid; + if (phy->rar_and_check(mac, rapid_found, temp_rnti)) { + return -1; + } + } + } + + // Run Contention Resolution if received correct RAPID + if (rapid_found) { + // Skip Contention resolution if preamble chosen by network + if (test.preamble_idx) { + break; + } + // Request Msg3 (re)-transmission + for (uint32_t i = 0; i < test.nof_msg3_retx + 1; i++) { + + // Step to contention resolution. Make sure timer does not start until Msg3 is transmitted + // and restarts on every retx + for (int j = 0; j < test.rach_cfg.ra_supervision_info.mac_contention_resolution_timer.to_number() - 1; j++) { + mac->run_tti(tti); + TESTASSERT(mac->get_dl_sched_rnti(tti) == (test.crnti ? test.crnti : test.temp_rnti)); + tti++; + } + + if (i == test.rach_cfg.max_harq_msg3_tx) { + phy->set_prach_tti(tti + phy->prach_delay, false); + } + + if (test.crnti) { + TESTASSERT(!phy->ul_grant_and_check_tv(mac, i == 0, temp_rnti, 3, tv_msg3_ce)); + } else { + TESTASSERT(!phy->ul_grant_and_check_tv(mac, i == 0, temp_rnti, 9, tv_msg3)); + } + } + + if (test.nof_msg3_retx == test.rach_cfg.max_harq_msg3_tx) { + TESTASSERT(mac->get_dl_sched_rnti(tti) != temp_rnti); + break; + } + + for (int i = 0; i < test.rach_cfg.ra_supervision_info.mac_contention_resolution_timer.to_number() - 1; i++) { + mac->run_tti(tti); + TESTASSERT(mac->get_dl_sched_rnti(tti) == (test.crnti ? test.crnti : test.temp_rnti)); + tti++; + + if (test.msg4_enable) { + if (test.crnti) { + // Test a DL grant does not resolve the contention resolution + if (phy->dl_grant(mac, true, test.crnti, 2, tv_msg4_nocontres)) { + return -1; + } + TESTASSERT(phy->get_crnti() != test.crnti); + + // UL grant is checked later + if (test.send_valid_ul_grant) { + if (phy->ul_grant_and_check_tv(mac, true, test.crnti, 2, NULL)) { + return -1; + } + break; + } else if ((int)i == test.rach_cfg.ra_supervision_info.mac_contention_resolution_timer.to_number() - 2) { + new_prach = true; + } + } else { + if (phy->dl_grant( + mac, true, temp_rnti, msg4_len, test.msg4_valid_conres ? tv_msg4_valid : tv_msg4_invalid)) { + return -1; + } + if (!test.msg4_valid_conres) { + new_prach = true; + } + break; + } + } + } + } + if (new_prach) { + test.assume_prach_transmitted = (int)j + 1; + phy->set_prach_tti(tti + phy->prach_delay, false); + TESTASSERT(mac->get_dl_sched_rnti(tti) != temp_rnti); + mac->run_tti(tti++); + } + } + + // RA procedure should be completed here + if (test.check_ra_successful) { + mac->run_tti(tti); + TESTASSERT(phy->get_crnti() == (test.crnti ? test.crnti : test.temp_rnti)); + TESTASSERT(mac->get_dl_sched_rnti(tti) == (test.crnti ? test.crnti : test.temp_rnti)); + tti++; + } + + *tti_state = tti; + return 0; +} + +/* Tests MAC RA procedure specified in 5.1 of 36.321 + * Currently not covered: + * - Selection of groupA/groupB sequences + * - Backoff timer + * - PDCCH order RACH initiation + * - Ignore RAR TA cmd when TA-Timer is running + */ +int mac_random_access_test() { - const uint8_t tv[] = {0x3c, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint64_t contention_id = 0xf0f0f0f0f0f; + + srslte::log_filter phy_log("PHY"); + phy_log.set_level(srslte::LOG_LEVEL_DEBUG); + phy_log.set_hex_limit(100000); srslte::log_filter mac_log("MAC"); mac_log.set_level(srslte::LOG_LEVEL_DEBUG); @@ -888,14 +1257,26 @@ int mac_ul_sch_pdu_msg3_test() // dummy layers phy_dummy phy; + phy.set_log(&phy_log); rlc_dummy rlc(&rlc_log); rrc_dummy rrc; - // the actual MAC + // Configure default RACH parameters + asn1::rrc::rach_cfg_common_s rach_cfg = {}; + rach_cfg.preamb_info.nof_ra_preambs = asn1::rrc::rach_cfg_common_s::preamb_info_s_::nof_ra_preambs_opts::n12; + rach_cfg.ra_supervision_info.preamb_trans_max = asn1::rrc::preamb_trans_max_opts::n8; + rach_cfg.ra_supervision_info.ra_resp_win_size = + asn1::rrc::rach_cfg_common_s::ra_supervision_info_s_::ra_resp_win_size_e_::sf4; + rach_cfg.max_harq_msg3_tx = 2; + rach_cfg.ra_supervision_info.mac_contention_resolution_timer = + asn1::rrc::rach_cfg_common_s::ra_supervision_info_s_::mac_contention_resolution_timer_opts::sf8; + + // Configure MAC mac mac(&mac_log); mac.init(&phy, &rlc, &rrc); - const uint16_t crnti = 0x1001; - mac.set_ho_rnti(crnti, 0); + mac_interface_rrc::mac_cfg_t mac_cfg; + mac_cfg.set_rach_cfg_common(rach_cfg); + mac.set_config(mac_cfg); // generate config for LCIDs in different LCGs than CCCH std::vector lcids; @@ -913,41 +1294,148 @@ int mac_ul_sch_pdu_msg3_test() mac.setup_lcid(channel.lcid, channel.lcg, channel.priority, channel.PBR, channel.BSD); } - // write dummy data + // Generate Msg3 + mac.set_contention_id(contention_id); rlc.write_sdu(0, 6); // UL-CCCH with Msg3 rlc.write_sdu(3, 100); // DRB data on other LCG - // generate TTI uint32 tti = 0; - mac.run_tti(tti++); - usleep(100); - - // create UL action and grant and push MAC PDU - { - mac_interface_phy_lte::tb_action_ul_t ul_action = {}; - mac_interface_phy_lte::mac_grant_ul_t mac_grant = {}; - - mac_grant.rnti = crnti; // make sure MAC picks it up as valid UL grant - mac_grant.tb.ndi_present = true; - mac_grant.tb.ndi = true; - mac_grant.tb.tbs = 9; // give room for MAC subheader, SDU and one padding byte - int cc_idx = 0; - - // Send grant to MAC and get action for this TB, then call tb_decoded to unlock MAC - mac.new_grant_ul(cc_idx, mac_grant, &ul_action); - // print generated PDU - mac_log.info_hex(ul_action.tb.payload, mac_grant.tb.tbs, "Generated PDU (%d B)\n", mac_grant.tb.tbs); -#if HAVE_PCAP - pcap_handle->write_ul_crnti(ul_action.tb.payload, mac_grant.tb.tbs, 0x1001, true, 1); -#endif + // Structure that defines the test to be executed + struct ra_test my_test; + uint32_t test_id = 1; + ZERO_OBJECT(my_test); + my_test.temp_rnti = 100; + my_test.assume_prach_transmitted = -1; + + // Test 1: No RAR is received. + // According to end of 5.1.5, UE sends up to preamb_trans_max upon which indicates RA problem to higher layers + mac_log.info("\n=========== Test %d =============\n", test_id++); + my_test.rach_cfg = rach_cfg; + my_test.nof_prachs = rach_cfg.ra_supervision_info.preamb_trans_max.to_number(); + TESTASSERT(!run_mac_ra_test(my_test, &mac, &phy, &tti)); + + // Make sure it triggers RRC signal + mac.run_tti(tti++); + TESTASSERT(rrc.rach_problem == 1); + + // Reset MAC + mac.reset(); + phy.reset(); + mac.set_contention_id(contention_id); + + // Test 2: RAR received but no matching RAPID + // The UE receives a RAR without a matching RAPID on every RAR response window TTI. + // According to 5.1.5, the RA procedure is considered non successful and tries again + mac_log.info("\n=========== Test %d =============\n", test_id++); + my_test.rar_nof_rapid = 1; + my_test.nof_prachs = 1; + my_test.rar_nof_invalid_rapid = rach_cfg.ra_supervision_info.ra_resp_win_size.to_number(); + TESTASSERT(!run_mac_ra_test(my_test, &mac, &phy, &tti)); + + // Test 3: RAR received but no matching RAPID. Test Msg3 retransmissions + // On each HARQ retx, contention resolution timer must be restarted (5.1.5) + // When max-HARQ-msg3-retx, contention not successful + mac_log.info("\n=========== Test %d =============\n", test_id++); + my_test.rar_nof_invalid_rapid = 0; + my_test.nof_msg3_retx = rach_cfg.max_harq_msg3_tx; + TESTASSERT(!run_mac_ra_test(my_test, &mac, &phy, &tti)); + + // Test 4: RAR with valid RAPID. Msg3 transmitted, Msg4 received but invalid ConRes + // Contention resolution is defined in 5.1.5. If ConResID does not match, the ConRes is considered + // not successful and tries again + mac_log.info("\n=========== Test %d =============\n", test_id++); + phy.reset(); + my_test.nof_msg3_retx = 0; + my_test.msg4_enable = true; + TESTASSERT(!run_mac_ra_test(my_test, &mac, &phy, &tti)); + + // Test 5: Msg4 received and valid ConRes. In this case a valid ConResID is received and RA procedure is successful + mac_log.info("\n=========== Test %d =============\n", test_id++); + my_test.temp_rnti++; // Temporal C-RNTI has to change to avoid duplicate + my_test.msg4_valid_conres = true; + my_test.check_ra_successful = true; + my_test.assume_prach_transmitted = 0; + TESTASSERT(!run_mac_ra_test(my_test, &mac, &phy, &tti)); + + // Test 6: RA with existing C-RNTI (Sends C-RNTI MAC CE) + // The transmission of C-RNTI MAC CE is only done if no CCCH is present (5.1.4). + // To trigger a new RA we have to either generate more data for DRB or wait until BSR-reTX is triggered + rlc.write_sdu(3, 100); + phy.set_crnti(0); + mac_log.info("\n=========== Test %d =============\n", test_id++); + my_test.crnti = my_test.temp_rnti; + my_test.temp_rnti++; // Temporal C-RNTI has to change to avoid duplicate + my_test.assume_prach_transmitted = -1; + my_test.send_valid_ul_grant = true; + TESTASSERT(!run_mac_ra_test(my_test, &mac, &phy, &tti)); + + // Test 7: Test Contention based Random Access. This is used eg in HO where preamble is chosen by UE. + // It is similar to Test 5 because C-RNTI is available to the UE when start the RA but + // In this case we will let the procedure expire the Contention Resolution window and make sure + // and RRC HO fail signal is sent to RRC. + mac_log.info("\n=========== Test %d =============\n", test_id++); + phy.set_prach_tti(tti + phy.prach_delay); + phy.set_crnti(0); + mac.start_cont_ho(); + mac.run_tti(tti++); + rrc.ho_finish = false; + my_test.nof_prachs = rach_cfg.ra_supervision_info.preamb_trans_max.to_number(); + my_test.temp_rnti++; // Temporal C-RNTI has to change to avoid duplicate + my_test.msg4_valid_conres = false; + my_test.assume_prach_transmitted = 0; + my_test.check_ra_successful = false; + my_test.send_valid_ul_grant = false; + TESTASSERT(!run_mac_ra_test(my_test, &mac, &phy, &tti)); + TESTASSERT(!rrc.ho_finish_successful && rrc.ho_finish); + + // Test 8: Test Contention based Random Access. Same as above but we let the procedure finish successfully. + mac_log.info("\n=========== Test %d =============\n", test_id++); + phy.set_prach_tti(tti + phy.prach_delay); + phy.set_crnti(0); + mac.start_cont_ho(); + mac.run_tti(tti++); + rrc.ho_finish = false; + my_test.nof_prachs = 1; + my_test.temp_rnti++; // Temporal C-RNTI has to change to avoid duplicate + my_test.send_valid_ul_grant = true; + TESTASSERT(!run_mac_ra_test(my_test, &mac, &phy, &tti)); + TESTASSERT(rrc.ho_finish_successful && rrc.ho_finish); + + // Test 9: Test non-Contention based HO. Used in HO but preamble is given by the network. In addition to checking + // that the given preamble is correctly passed to the PHY, in this case there is no contention. + // In this first test, no RAR is received and RA procedure fails + mac_log.info("\n=========== Test %d =============\n", test_id++); + phy.set_prach_tti(tti + phy.prach_delay); + mac.run_tti(tti++); + phy.set_crnti(0); + rrc.ho_finish = false; + my_test.preamble_idx = 3; + mac.start_noncont_ho(my_test.preamble_idx, 0); + my_test.nof_prachs = rach_cfg.ra_supervision_info.preamb_trans_max.to_number(); + my_test.rar_nof_invalid_rapid = rach_cfg.ra_supervision_info.ra_resp_win_size.to_number(); + my_test.temp_rnti++; // Temporal C-RNTI has to change to avoid duplicate + TESTASSERT(!run_mac_ra_test(my_test, &mac, &phy, &tti)); + mac.run_tti(tti++); + TESTASSERT(!rrc.ho_finish_successful && rrc.ho_finish); - TESTASSERT(memcmp(ul_action.tb.payload, tv, sizeof(tv)) == 0); - } + // Test 10: Test non-Contention based HO. Used in HO but preamble is given by the network. We check that + // the procedure is considered successful without waiting for contention + mac_log.info("\n=========== Test %d =============\n", test_id++); + phy.set_prach_tti(tti + phy.prach_delay); + mac.run_tti(tti++); + phy.set_crnti(0); + rrc.ho_finish = false; + my_test.preamble_idx = 3; + mac.start_noncont_ho(my_test.preamble_idx, 0); + my_test.nof_prachs = 1; + my_test.rar_nof_invalid_rapid = 0; + my_test.check_ra_successful = true; + my_test.temp_rnti++; // Temporal C-RNTI has to change to avoid duplicate + TESTASSERT(!run_mac_ra_test(my_test, &mac, &phy, &tti)); + mac.run_tti(tti++); + TESTASSERT(rrc.ho_finish_successful && rrc.ho_finish); - // make sure MAC PDU thread picks up before stopping - sleep(1); - mac.run_tti(0); mac.stop(); return SRSLTE_SUCCESS; @@ -1010,7 +1498,7 @@ int main(int argc, char** argv) return -1; } - if (mac_ul_sch_pdu_msg3_test()) { + if (mac_random_access_test()) { printf("mac_ul_sch_pdu_msg3_test() test failed.\n"); return -1; }