FR2: GPIO configuration for Interdigital Radio Unit
For FR2 and including analog beamforming, we control the beams via GPIO on radio units. In this commit, introduce a configuration option to switch the GPIO control for different devices: "generic" is the current GPIO control, "interdigital" configures the GPIO configuration to be used with an InterDigital device.
This commit is contained in:
committed by
Robert Schmidt
parent
fe61354173
commit
76ccc2f5f3
+96
-36
@@ -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; 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);
|
||||
|
||||
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
|
||||
@@ -1873,6 +1916,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);
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
+48
-15
@@ -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<std::string> 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;
|
||||
|
||||
Reference in New Issue
Block a user