//
// Copyright 2013-2014,2016 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//
#include "n230_frontend_ctrl.hpp"
#include
#include
#include
#include
#include
#include "n230_fpga_defs.h"
namespace uhd { namespace usrp { namespace n230 {
/* ATR Control Bits */
static const uint32_t TX_ENABLE = (1 << 7);
static const uint32_t SFDX_RX = (1 << 6);
static const uint32_t SFDX_TX = (1 << 5);
static const uint32_t SRX_RX = (1 << 4);
static const uint32_t SRX_TX = (1 << 3);
static const uint32_t LED_RX = (1 << 2);
static const uint32_t LED_TXRX_RX = (1 << 1);
static const uint32_t LED_TXRX_TX = (1 << 0);
/* ATR State Definitions. */
static const uint32_t STATE_OFF = 0x00;
static const uint32_t STATE_RX_RX2 = (SFDX_RX
| SFDX_TX
| LED_RX);
static const uint32_t STATE_RX_TXRX = (SRX_RX
| SRX_TX
| LED_TXRX_RX);
static const uint32_t STATE_FDX_TXRX = (TX_ENABLE
| SFDX_RX
| SFDX_TX
| LED_TXRX_TX
| LED_RX);
static const uint32_t STATE_TX_TXRX = (TX_ENABLE
| SFDX_RX
| SFDX_TX
| LED_TXRX_TX);
using namespace uhd::usrp;
class n230_frontend_ctrl_impl : public n230_frontend_ctrl
{
public:
n230_frontend_ctrl_impl(
radio_ctrl_core_3000::sptr core_ctrl,
fpga::core_misc_reg_t& core_misc_reg,
ad9361_ctrl::sptr codec_ctrl,
const std::vector& gpio_cores
): _core_ctrl(core_ctrl),
_codec_ctrl(codec_ctrl),
_gpio_cores(gpio_cores),
_core_misc_reg(core_misc_reg)
{
}
virtual ~n230_frontend_ctrl_impl()
{
}
void set_antenna_sel(const size_t which, const std::string &ant)
{
if (ant != "TX/RX" and ant != "RX2")
throw uhd::value_error("n230: unknown RX antenna option: " + ant);
_fe_states[which].rx_ant = ant;
_flush_atr_state();
}
void set_stream_state(const fe_state_t fe0_state_, const fe_state_t fe1_state_)
{
//Update soft-state
_fe_states[0].state = fe0_state_;
_fe_states[1].state = fe1_state_;
const fe_state_t fe0_state = _fe_states[0].state;
const fe_state_t fe1_state = (_gpio_cores.size() > 1) ? _fe_states[1].state : NONE_STREAMING;
const size_t num_tx = (_is_tx(fe0_state) ? 1 : 0) + (_is_tx(fe1_state) ? 1 : 0);
const size_t num_rx = (_is_rx(fe0_state) ? 1 : 0) + (_is_rx(fe1_state) ? 1 : 0);
//setup the active chains in the codec
if ((num_rx + num_tx) == 0) {
_codec_ctrl->set_active_chains(
true, false,
true, false); //enable something
} else {
_codec_ctrl->set_active_chains(
_is_tx(fe0_state), _is_tx(fe1_state),
_is_rx(fe0_state), _is_rx(fe1_state));
}
_core_misc_reg.flush();
//atrs change based on enables
_flush_atr_state();
}
void set_stream_state(const size_t which, const fe_state_t state)
{
if (which == 0) {
set_stream_state(state, _fe_states[1].state);
} else if (which == 1) {
set_stream_state(_fe_states[0].state, state);
} else {
throw uhd::value_error(
str(boost::format("n230: unknown stream index option: %d") % which)
);
}
}
void set_bandsel(const std::string& which, double freq)
{
using namespace n230::fpga;
if(which[0] == 'R') {
if(freq < 2.2e9) {
_core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_A, 0);
_core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_B, 0);
_core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_C, 1);
} else if((freq >= 2.2e9) && (freq < 4e9)) {
_core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_A, 0);
_core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_B, 1);
_core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_C, 0);
} else if((freq >= 4e9) && (freq <= 6e9)) {
_core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_A, 1);
_core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_B, 0);
_core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_C, 0);
} else {
UHD_THROW_INVALID_CODE_PATH();
}
} else if(which[0] == 'T') {
if(freq < 2.5e9) {
_core_misc_reg.set(core_misc_reg_t::TX_BANDSEL_A, 0);
_core_misc_reg.set(core_misc_reg_t::TX_BANDSEL_B, 1);
} else if((freq >= 2.5e9) && (freq <= 6e9)) {
_core_misc_reg.set(core_misc_reg_t::TX_BANDSEL_A, 1);
_core_misc_reg.set(core_misc_reg_t::TX_BANDSEL_B, 0);
} else {
UHD_THROW_INVALID_CODE_PATH();
}
} else {
UHD_THROW_INVALID_CODE_PATH();
}
_core_misc_reg.flush();
}
void set_self_test_mode(self_test_mode_t mode)
{
switch (mode) {
case LOOPBACK_RADIO: {
_core_ctrl->poke32(fpga::sr_addr(fpga::SR_CORE_LOOPBACK), 0x1);
} break;
case LOOPBACK_CODEC: {
_core_ctrl->poke32(fpga::sr_addr(fpga::SR_CORE_LOOPBACK), 0x0);
_codec_ctrl->data_port_loopback(true);
} break;
//Default = disable
default:
case LOOPBACK_DISABLED: {
_core_ctrl->poke32(fpga::sr_addr(fpga::SR_CORE_LOOPBACK), 0x0);
_codec_ctrl->data_port_loopback(false);
} break;
}
}
private:
void _flush_atr_state()
{
for (size_t i = 0; i < _gpio_cores.size(); i++) {
const fe_state_cache_t& fe_state_cache = _fe_states[i];
const bool enb_rx = _is_rx(fe_state_cache.state);
const bool enb_tx = _is_tx(fe_state_cache.state);
const bool is_rx2 = (fe_state_cache.rx_ant == "RX2");
const size_t rxonly = (enb_rx)? ((is_rx2)? STATE_RX_RX2 : STATE_RX_TXRX) : STATE_OFF;
const size_t txonly = (enb_tx)? (STATE_TX_TXRX) : STATE_OFF;
size_t fd = STATE_OFF;
if (enb_rx and enb_tx) fd = STATE_FDX_TXRX;
if (enb_rx and not enb_tx) fd = rxonly;
if (not enb_rx and enb_tx) fd = txonly;
_gpio_cores[i]->set_atr_reg(gpio_atr::ATR_REG_IDLE, STATE_OFF);
_gpio_cores[i]->set_atr_reg(gpio_atr::ATR_REG_RX_ONLY, rxonly);
_gpio_cores[i]->set_atr_reg(gpio_atr::ATR_REG_TX_ONLY, txonly);
_gpio_cores[i]->set_atr_reg(gpio_atr::ATR_REG_FULL_DUPLEX, fd);
}
}
inline static bool _is_tx(const fe_state_t state)
{
return state == TX_STREAMING || state == TXRX_STREAMING;
}
inline static bool _is_rx(const fe_state_t state)
{
return state == RX_STREAMING || state == TXRX_STREAMING;
}
private:
struct fe_state_cache_t {
fe_state_cache_t() : state(NONE_STREAMING), rx_ant("RX2")
{}
fe_state_t state;
std::string rx_ant;
};
radio_ctrl_core_3000::sptr _core_ctrl;
ad9361_ctrl::sptr _codec_ctrl;
std::vector _gpio_cores;
fpga::core_misc_reg_t& _core_misc_reg;
uhd::dict _fe_states;
};
}}} //namespace
using namespace uhd::usrp::n230;
n230_frontend_ctrl::sptr n230_frontend_ctrl::make(
radio_ctrl_core_3000::sptr core_ctrl,
fpga::core_misc_reg_t& core_misc_reg,
ad9361_ctrl::sptr codec_ctrl,
const std::vector& gpio_cores)
{
return sptr(new n230_frontend_ctrl_impl(core_ctrl, core_misc_reg, codec_ctrl, gpio_cores));
}