Fixed multiple issues in Random Access procedure and extended MAC test

master
Ismael Gomez 5 years ago committed by Andre Puschmann
parent 5dcf3b3563
commit 773c2a9721

@ -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)) {

@ -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);

@ -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);

@ -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

@ -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;
};

@ -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);
}
}
}

@ -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()

@ -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");
}

@ -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)
{

@ -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));
}

@ -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;
}
}

@ -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;
}

@ -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<std::mutex> 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();
}
}

@ -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) {

@ -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);

@ -31,7 +31,7 @@
using namespace srsue;
using namespace srslte;
#define HAVE_PCAP 0
#define HAVE_PCAP 1
static std::unique_ptr<srslte::mac_pcap> 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<logical_channel_config_t> 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;
}

Loading…
Cancel
Save