diff --git a/executables/nr-ru.c b/executables/nr-ru.c index 2d864abb8e..7d2b11cf65 100644 --- a/executables/nr-ru.c +++ b/executables/nr-ru.c @@ -726,6 +726,49 @@ void rx_rf(RU_t *ru,int *frame,int *slot) { stop_meas(&ru->rx_fhaul); } +static radio_tx_gpio_flag_t get_gpio_flags(RU_t *ru, int slot) +{ + radio_tx_gpio_flag_t flags_gpio = 0; + NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; + openair0_config_t *cfg0 = &ru->openair0_cfg; + + switch (cfg0->gpio_controller) { + case RU_GPIO_CONTROL_GENERIC: + // currently we switch beams at the beginning of a slot and we take the beam index of the first symbol of this slot + // we only send the beam to the gpio if the beam is different from the previous slot + + if (ru->common.beam_id) { + int prev_slot = (slot - 1 + fp->slots_per_frame) % fp->slots_per_frame; + const uint8_t *beam_ids = ru->common.beam_id[0]; + int prev_beam = beam_ids[prev_slot * fp->symbols_per_slot]; + int beam = beam_ids[slot * fp->symbols_per_slot]; + if (prev_beam != beam) { + flags_gpio = beam | TX_GPIO_CHANGE; // enable change of gpio + LOG_I(HW, "slot %d, beam %d\n", slot, ru->common.beam_id[0][slot * fp->symbols_per_slot]); + } + } + break; + case RU_GPIO_CONTROL_INTERDIGITAL: { + // the beam index is written in bits 8-10 of the flags + // bit 11 enables the gpio programming + int beam = 0; + if ((slot % 10 == 0) && ru->common.beam_id && (ru->common.beam_id[0][slot * fp->symbols_per_slot] < 64)) { + // beam = ru->common.beam_id[0][slot*fp->symbols_per_slot] | 64; + beam = 1024; // hardcoded now for beam32 boresight + // beam = 127; //for the sake of trying beam63 + LOG_D(HW, "slot %d, beam %d\n", slot, beam); + } + flags_gpio = beam | TX_GPIO_CHANGE; + // flags_gpio |= beam << 8; // MSB 8 bits are used for beam + LOG_I(HW, "slot %d, beam %d, flags_gpio %d\n", slot, beam, flags_gpio); + break; + } + default: + AssertFatal(false, "illegal GPIO controller %d\n", cfg0->gpio_controller); + } + + return flags_gpio; +} void tx_rf(RU_t *ru,int frame,int slot, uint64_t timestamp) { RU_proc_t *proc = &ru->proc; @@ -783,47 +826,47 @@ void tx_rf(RU_t *ru,int frame,int slot, uint64_t timestamp) { flags_burst = proc->first_tx == 1 ? TX_BURST_START : TX_BURST_MIDDLE; } - if (fp->freq_range==nr_FR2) { - // currently we switch beams at the beginning of a slot and we take the beam index of the first symbol of this slot - // we only send the beam to the gpio if the beam is different from the previous slot + if (fp->freq_range == nr_FR2) + flags_gpio = get_gpio_flags(ru, slot); - if ( ru->common.beam_id) { - int prev_slot = (slot - 1 + fp->slots_per_frame) % fp->slots_per_frame; - const uint8_t *beam_ids = ru->common.beam_id[0]; - int prev_beam = beam_ids[prev_slot * fp->symbols_per_slot]; - int beam = beam_ids[slot * fp->symbols_per_slot]; - if (prev_beam != beam) { - flags_gpio = beam | TX_GPIO_CHANGE; // enable change of gpio - LOG_D(HW, "slot %d, beam %d\n", slot, ru->common.beam_id[0][slot * fp->symbols_per_slot]); - } - } - } + const int flags = flags_burst | (flags_gpio << 4); - const int flags = flags_burst | flags_gpio << 4; + if (proc->first_tx == 1) + proc->first_tx = 0; - if (proc->first_tx == 1) proc->first_tx = 0; + VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_TRX_WRITE_FLAGS, flags); + VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_TX0_RU, frame); + VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_TTI_NUMBER_TX0_RU, slot); - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_WRITE_FLAGS, flags ); - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_TX0_RU, frame ); - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TTI_NUMBER_TX0_RU, slot ); + for (i = 0; i < ru->nb_tx; i++) + txp[i] = (void *)&ru->common.txdata[i][fp->get_samples_slot_timestamp(slot, fp, 0)] - sf_extension * sizeof(int32_t); - for (i=0; inb_tx; i++) - txp[i] = (void *)&ru->common.txdata[i][fp->get_samples_slot_timestamp(slot,fp,0)]-sf_extension*sizeof(int32_t); - - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST, (timestamp+ru->ts_offset-ru->openair0_cfg.tx_sample_advance)&0xffffffff ); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_WRITE, 1 ); - // prepare tx buffer pointers - txs = ru->rfdevice.trx_write_func(&ru->rfdevice, - timestamp+ru->ts_offset-ru->openair0_cfg.tx_sample_advance-sf_extension, - txp, - siglen+sf_extension, - ru->nb_tx, - flags); - LOG_D(PHY,"[TXPATH] RU %d aa %d tx_rf, writing to TS %llu, %d.%d, unwrapped_frame %d, slot %d, flags %d, siglen+sf_extension %d, returned %d, E %f\n",ru->idx,i, - (long long unsigned int)(timestamp+ru->ts_offset-ru->openair0_cfg.tx_sample_advance-sf_extension),frame,slot,proc->frame_tx_unwrap,slot, flags, siglen+sf_extension, txs,10*log10((double)signal_energy(txp[0],siglen+sf_extension))); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_WRITE, 0 ); - //AssertFatal(txs == 0,"trx write function error %d\n", txs); - + VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST, + (timestamp + ru->ts_offset - ru->openair0_cfg.tx_sample_advance) & 0xffffffff); + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_WRITE, 1); + // prepare tx buffer pointers + txs = ru->rfdevice.trx_write_func(&ru->rfdevice, + timestamp + ru->ts_offset - ru->openair0_cfg.tx_sample_advance - sf_extension, + txp, + siglen + sf_extension, + ru->nb_tx, + flags); + LOG_D(PHY, + "[TXPATH] RU %d aa %d tx_rf, writing to TS %llu, %d.%d, unwrapped_frame %d, slot %d, flags %d, siglen+sf_extension %d, " + "returned %d, E %f\n", + ru->idx, + i, + (long long unsigned int)(timestamp + ru->ts_offset - ru->openair0_cfg.tx_sample_advance - sf_extension), + frame, + slot, + proc->frame_tx_unwrap, + slot, + flags, + siglen + sf_extension, + txs, + 10 * log10((double)signal_energy(txp[0], siglen + sf_extension))); + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_WRITE, 0); + // AssertFatal(txs == 0,"trx write function error %d\n", txs); } // this is for RU with local RF unit @@ -1881,6 +1924,23 @@ static void NRRCconfig_RU(void) RC.ru[j]->openair0_cfg.sdr_addrs = strdup(*(RUParamList.paramarray[j][RU_SDR_ADDRS].strptr)); } + if (config_isparamset(RUParamList.paramarray[j], RU_GPIO_CONTROL)) { + if (strcmp(*RUParamList.paramarray[j][RU_GPIO_CONTROL].strptr, "generic") == 0) { + RC.ru[j]->openair0_cfg.gpio_controller = RU_GPIO_CONTROL_GENERIC; + LOG_I(PHY, "RU GPIO control set as 'generic'\n"); + } else if (strcmp(*RUParamList.paramarray[j][RU_GPIO_CONTROL].strptr, "interdigital") == 0) { + RC.ru[j]->openair0_cfg.gpio_controller = RU_GPIO_CONTROL_INTERDIGITAL; + LOG_I(PHY, "RU GPIO control set as 'interdigital'\n"); + } else { + AssertFatal(false, + "bad GPIO controller in configuration file: '%s'\n", + *(RUParamList.paramarray[j][RU_GPIO_CONTROL].strptr)); + } + } else { + RC.ru[j]->openair0_cfg.gpio_controller = RU_GPIO_CONTROL_GENERIC; + LOG_I(PHY, "RU GPIO control set as 'generic'\n"); + } + if (config_isparamset(RUParamList.paramarray[j], RU_TX_SUBDEV)) { RC.ru[j]->openair0_cfg.tx_subdev = strdup(*(RUParamList.paramarray[j][RU_TX_SUBDEV].strptr)); LOG_I(PHY, "RU USRP tx subdev == %s\n", RC.ru[j]->openair0_cfg.tx_subdev); diff --git a/openair2/ENB_APP/enb_paramdef.h b/openair2/ENB_APP/enb_paramdef.h index 58eebba278..6417d3cb75 100644 --- a/openair2/ENB_APP/enb_paramdef.h +++ b/openair2/ENB_APP/enb_paramdef.h @@ -112,6 +112,7 @@ typedef enum { #define CONFIG_STRING_RU_NUM_INTERFACES "num_interfaces" #define CONFIG_STRING_RU_HALF_SLOT_PARALLELIZATION "half_slot_parallelization" #define CONFIG_STRING_RU_RU_THREAD_CORE "ru_thread_core" +#define CONFIG_STRING_RU_GPIO_CONTROL "gpio_controller" #define HLP_RU_SF_AHEAD "LTE TX processing advance" #define HLP_RU_SL_AHEAD "NR TX processing advance" @@ -124,6 +125,7 @@ typedef enum { #define HLP_RU_NUM_INTERFACES "Number of network interfaces for RU" #define HLP_RU_HALF_SLOT_PARALLELIZATION "run half slots in parallel in RU FEP" #define HLP_RU_RU_THREAD_CORE "id of core to pin ru_thread, -1 is default" +#define HLP_RU_GPIO_CONTROL "set the GPIO control type for the RU" #define RU_LOCAL_IF_NAME_IDX 0 #define RU_LOCAL_ADDRESS_IDX 1 @@ -167,6 +169,7 @@ typedef enum { #define RU_NUM_INTERFACES 39 #define RU_HALF_SLOT_PARALLELIZATION 40 #define RU_RU_THREAD_CORE 41 +#define RU_GPIO_CONTROL 42 /*-----------------------------------------------------------------------------------------------------------------------------------------*/ /* RU configuration parameters */ /* optname helpstr paramflags XXXptr defXXXval type numelt */ @@ -215,6 +218,7 @@ typedef enum { {CONFIG_STRING_RU_NUM_INTERFACES, HLP_RU_NUM_INTERFACES, 0, .uptr=NULL, .defintval=1, TYPE_UINT, 0}, \ {CONFIG_STRING_RU_HALF_SLOT_PARALLELIZATION, HLP_RU_HALF_SLOT_PARALLELIZATION, 0, .uptr=NULL, .defintval=1, TYPE_UINT, 0}, \ {CONFIG_STRING_RU_RU_THREAD_CORE, HLP_RU_RU_THREAD_CORE, 0, .uptr=NULL, .defintval=-1, TYPE_UINT, 0}, \ + {CONFIG_STRING_RU_GPIO_CONTROL, HLP_RU_GPIO_CONTROL, 0, .strptr=NULL, .defstrval="generic", TYPE_STRING, 0}, \ } // clang-format on diff --git a/radio/COMMON/common_lib.h b/radio/COMMON/common_lib.h index 2e06744aaa..19fe3e468b 100644 --- a/radio/COMMON/common_lib.h +++ b/radio/COMMON/common_lib.h @@ -173,6 +173,10 @@ typedef struct { notifiedFIFO_t *resp; } udp_ctx_t; +typedef enum { + RU_GPIO_CONTROL_GENERIC, + RU_GPIO_CONTROL_INTERDIGITAL, +} gpio_control_t; /*! \brief RF frontend parameters set by application */ typedef struct { @@ -275,6 +279,8 @@ typedef struct { int rxfh_cores[4]; //! Core IDs for TX FH int txfh_cores[4]; + //! select the GPIO control method + gpio_control_t gpio_controller; } openair0_config_t; /*! \brief RF mapping */ diff --git a/radio/USRP/usrp_lib.cpp b/radio/USRP/usrp_lib.cpp index 3c5d1e2009..06b0c9bace 100644 --- a/radio/USRP/usrp_lib.cpp +++ b/radio/USRP/usrp_lib.cpp @@ -263,7 +263,42 @@ static int sync_to_gps(openair0_device *device) { #define ATR_MASK 0x7f //pins controlled by ATR #define ATR_RX 0x50 //data[4] and data[6] #define ATR_XX 0x20 //data[5] -#define MAN_MASK ATR_MASK^0xFFF //manually controlled pins +#define MAN_MASK ATR_MASK ^ 0xFFF // manually controlled pins + +static void trx_usrp_start_interdigital_gpio(openair0_device *device, usrp_state_t *s) +{ + AssertFatal(device->type == USRP_X400_DEV, + "interdigital frontend device for beam management can only be used together with an X400\n"); + + // s->gpio_bank="GPIO0"; + std::vector sxx{12, "DB0_RF0"}; + // set every pin on GPIO0 to be ocntrolled by DB1_RF0 + s->usrp->set_gpio_src(s->gpio_bank, sxx); + // set data direction register (DDR) to output + s->usrp->set_gpio_attr(s->gpio_bank, "DDR", 0xfff, 0xfff); + // set lower GPIO#1 to be controlled automatically by ATR (the rest bits are controlled manually) + s->usrp->set_gpio_attr(s->gpio_bank, "CTRL", (1 << 1), (1 << 1)); + // set GPIO1 (Tx/Rx1) to 1 for MHU1 for transmistting + s->usrp->set_gpio_attr(s->gpio_bank, "ATR_XX", (1 << 1), (1 << 1)); + // set GPIO4 (ID0) to 1 and GPIO2 (TX/RX2) &GPIO3 (ID1) to 0 + s->usrp->set_gpio_attr(s->gpio_bank, "OUT", (1 << 4), 0x1c); +} + +static void trx_usrp_start_generic_gpio(openair0_device *device, usrp_state_t *s) +{ + // setup GPIO for TDD, GPIO(4) = ATR_RX + // set data direction register (DDR) to output + s->usrp->set_gpio_attr(s->gpio_bank, "DDR", 0xfff, 0xfff); + // set bits to be controlled automatically by ATR + s->usrp->set_gpio_attr(s->gpio_bank, "CTRL", ATR_MASK, 0xfff); + // set bits to 1 when the radio is only receiving (ATR_RX) + s->usrp->set_gpio_attr(s->gpio_bank, "ATR_RX", ATR_RX, ATR_MASK); + // set bits to 1 when the radio is transmitting and receiveing (ATR_XX) + // (we use full duplex here, because our RX is on all the time - this might need to change later) + s->usrp->set_gpio_attr(s->gpio_bank, "ATR_XX", ATR_XX, ATR_MASK); + // set all other pins to manual + s->usrp->set_gpio_attr(s->gpio_bank, "OUT", MAN_MASK, 0xfff); +} /*! \brief Called to start the USRP transceiver. Return 0 if OK, < 0 if error @param device pointer to the device structure specific to the RF hardware target @@ -280,21 +315,19 @@ static int trx_usrp_start(openair0_device *device) { s->gpio_bank = (char *) "GPIO0"; s->usrp->set_gpio_src(s->gpio_bank, sxx); } -#endif +#endif + + switch (device->openair0_cfg->gpio_controller) { + case RU_GPIO_CONTROL_GENERIC: + trx_usrp_start_generic_gpio(device, s); + break; + case RU_GPIO_CONTROL_INTERDIGITAL: + trx_usrp_start_interdigital_gpio(device, s); + break; + default: + AssertFatal(false, "illegal GPIO controller %d\n", device->openair0_cfg->gpio_controller); + } - // setup GPIO for TDD, GPIO(4) = ATR_RX - //set data direction register (DDR) to output - s->usrp->set_gpio_attr(s->gpio_bank, "DDR", 0xfff, 0xfff); - //set bits to be controlled automatically by ATR - s->usrp->set_gpio_attr(s->gpio_bank, "CTRL", ATR_MASK, 0xfff); - //set bits to 1 when the radio is only receiving (ATR_RX) - s->usrp->set_gpio_attr(s->gpio_bank, "ATR_RX", ATR_RX, ATR_MASK); - // set bits to 1 when the radio is transmitting and receiveing (ATR_XX) - // (we use full duplex here, because our RX is on all the time - this might need to change later) - s->usrp->set_gpio_attr(s->gpio_bank, "ATR_XX", ATR_XX, ATR_MASK); - // set all other pins to manual - s->usrp->set_gpio_attr(s->gpio_bank, "OUT", MAN_MASK, 0xfff); - s->wait_for_first_pps = 1; s->rx_count = 0; s->tx_count = 0;