aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_init.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_init.cpp')
-rw-r--r--host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_init.cpp487
1 files changed, 487 insertions, 0 deletions
diff --git a/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_init.cpp b/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_init.cpp
new file mode 100644
index 000000000..ed5cb61c3
--- /dev/null
+++ b/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_init.cpp
@@ -0,0 +1,487 @@
+//
+// Copyright 2017 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0
+//
+
+#include "magnesium_radio_ctrl_impl.hpp"
+#include "magnesium_constants.hpp"
+#include "spi_core_3000.hpp"
+#include <uhd/utils/log.hpp>
+#include <uhd/types/eeprom.hpp>
+#include <vector>
+#include <string>
+
+using namespace uhd;
+using namespace uhd::rfnoc;
+
+namespace {
+ enum slave_select_t {
+ SEN_CPLD = 1,
+ SEN_TX_LO = 2,
+ SEN_RX_LO = 4,
+ SEN_PHASE_DAC = 8
+ };
+}
+
+void magnesium_radio_ctrl_impl::_init_peripherals()
+{
+ UHD_LOG_TRACE(unique_id(), "Initializing peripherals...");
+ fs_path cpld_path = _root_path.branch_path()
+ / str(boost::format("Radio_%d") % ((get_block_id().get_block_count()/2)*2))
+ / "cpld";
+ fs_path rx_lo_path = _root_path.branch_path()
+ / str(boost::format("Radio_%d") % ((get_block_id().get_block_count()/2)*2))
+ / "rx_lo";
+ fs_path tx_lo_path = _root_path.branch_path()
+ / str(boost::format("Radio_%d") % ((get_block_id().get_block_count()/2)*2))
+ / "tx_lo";
+ // TODO: When we move back to 2 chans per RFNoC block, this needs to be
+ // non-conditional, and the else-branch goes away:
+ if (_master) {
+ UHD_LOG_TRACE(unique_id(), "Initializing SPI core...");
+ _spi = spi_core_3000::make(_get_ctrl(0),
+ radio_ctrl_impl::regs::sr_addr(radio_ctrl_impl::regs::SPI),
+ radio_ctrl_impl::regs::RB_SPI);
+ } else {
+ UHD_LOG_TRACE(unique_id(), "Not a master radio, no SPI core.");
+ }
+
+ UHD_LOG_TRACE(unique_id(), "Initializing CPLD...");
+ UHD_LOG_TRACE(unique_id(), "CPLD path: " << cpld_path);
+ if (not _tree->exists(cpld_path)) {
+ UHD_LOG_TRACE(unique_id(), "Creating new CPLD object...");
+ spi_config_t spi_config;
+ spi_config.use_custom_divider = true;
+ spi_config.divider = 125;
+ spi_config.mosi_edge = spi_config_t::EDGE_RISE;
+ spi_config.miso_edge = spi_config_t::EDGE_FALL;
+ UHD_LOG_TRACE(unique_id(), "Making CPLD object...");
+ _cpld = std::make_shared<magnesium_cpld_ctrl>(
+ [this, spi_config](const uint32_t transaction){ // Write functor
+ this->_spi->write_spi(
+ SEN_CPLD,
+ spi_config,
+ transaction,
+ 24
+ );
+ },
+ [this, spi_config](const uint32_t transaction){ // Read functor
+ return this->_spi->read_spi(
+ SEN_CPLD,
+ spi_config,
+ transaction,
+ 24
+ );
+ }
+ );
+ _tree->create<magnesium_cpld_ctrl::sptr>(cpld_path).set(_cpld);
+ } else {
+ UHD_LOG_TRACE(unique_id(), "Reusing someone else's CPLD object...");
+ _cpld = _tree->access<magnesium_cpld_ctrl::sptr>(cpld_path).get();
+ }
+
+ // TODO: Same comment as above applies
+ if (_master) {
+ UHD_LOG_TRACE(unique_id(), "Initializing TX LO...");
+ _tx_lo = adf435x_iface::make_adf4351(
+ [this](const std::vector<uint32_t> transactions){
+ for (const uint32_t transaction: transactions) {
+ this->_spi->write_spi(
+ SEN_TX_LO,
+ spi_config_t::EDGE_RISE,
+ transaction,
+ 32
+ );
+ }
+ }
+ );
+ UHD_LOG_TRACE(unique_id(), "Initializing RX LO...");
+ _rx_lo = adf435x_iface::make_adf4351(
+ [this](const std::vector<uint32_t> transactions){
+ for (const uint32_t transaction: transactions) {
+ this->_spi->write_spi(
+ SEN_RX_LO,
+ spi_config_t::EDGE_RISE,
+ transaction,
+ 32
+ );
+ }
+ }
+ );
+ } else {
+ UHD_LOG_TRACE(unique_id(), "Not a master radio, no LOs.");
+ }
+ if (not _tree->exists(rx_lo_path)) {
+ _tree->create<adf435x_iface::sptr>(rx_lo_path).set(_rx_lo);
+ } else {
+ UHD_LOG_TRACE(unique_id(), "Not a master radio. Getting LO from master" );
+ _rx_lo = _tree->access<adf435x_iface::sptr>(rx_lo_path).get();
+ }
+ if (not _tree->exists(tx_lo_path)) {
+ _tree->create<adf435x_iface::sptr>(tx_lo_path).set(_tx_lo);
+ } else {
+ UHD_LOG_TRACE(unique_id(), "Not a master radio. Getting LO from master" );
+ _tx_lo = _tree->access<adf435x_iface::sptr>(tx_lo_path).get();
+ }
+
+ _gpio.clear(); // Following the as-if rule, this can get optimized out
+ for (size_t radio_idx = 0; radio_idx < _get_num_radios(); radio_idx++) {
+ UHD_LOG_TRACE(unique_id(),
+ "Initializing GPIOs for channel " << radio_idx);
+ _gpio.emplace_back(
+ usrp::gpio_atr::gpio_atr_3000::make(
+ _get_ctrl(radio_idx),
+ regs::sr_addr(regs::GPIO),
+ regs::RB_DB_GPIO
+ )
+ );
+ // DSA and AD9371 gain bits do *not* toggle on ATR modes. If we ever
+ // connect anything else to this core, we might need to set_atr_mode()
+ // to MODE_ATR on those bits. For now, all bits simply do what they're
+ // told, and don't toggle on RX/TX state changes.
+ _gpio.back()->set_atr_mode(
+ usrp::gpio_atr::MODE_GPIO, // Disable ATR mode
+ usrp::gpio_atr::gpio_atr_3000::MASK_SET_ALL
+ );
+ _gpio.back()->set_gpio_ddr(
+ usrp::gpio_atr::DDR_OUTPUT, // Make all GPIOs outputs
+ usrp::gpio_atr::gpio_atr_3000::MASK_SET_ALL
+ );
+ }
+ if (get_block_id().get_block_count() == FPGPIO_MASTER_RADIO) {
+ UHD_LOG_TRACE(unique_id(), "Initializing front-panel GPIO control...")
+ _fp_gpio = usrp::gpio_atr::gpio_atr_3000::make(
+ _get_ctrl(0), regs::sr_addr(regs::FP_GPIO), regs::RB_FP_GPIO);
+ }
+}
+
+void magnesium_radio_ctrl_impl::_init_defaults()
+{
+ UHD_LOG_TRACE(unique_id(), "Initializing defaults...");
+ const size_t num_rx_chans = get_output_ports().size();
+ const size_t num_tx_chans = get_input_ports().size();
+
+ UHD_LOG_TRACE(unique_id(),
+ "Num TX chans: " << num_tx_chans
+ << " Num RX chans: " << num_rx_chans);
+ UHD_LOG_TRACE(unique_id(),
+ "Setting tick rate to " << MAGNESIUM_TICK_RATE / 1e6 << " MHz");
+ radio_ctrl_impl::set_rate(MAGNESIUM_TICK_RATE);
+
+ for (size_t chan = 0; chan < num_rx_chans; chan++) {
+ radio_ctrl_impl::set_rx_frequency(MAGNESIUM_CENTER_FREQ, chan);
+ radio_ctrl_impl::set_rx_gain(0, chan);
+ radio_ctrl_impl::set_rx_antenna(MAGNESIUM_DEFAULT_RX_ANTENNA, chan);
+ radio_ctrl_impl::set_rx_bandwidth(MAGNESIUM_DEFAULT_BANDWIDTH, chan);
+ }
+
+ for (size_t chan = 0; chan < num_tx_chans; chan++) {
+ radio_ctrl_impl::set_tx_frequency(MAGNESIUM_CENTER_FREQ, chan);
+ radio_ctrl_impl::set_tx_gain(0, chan);
+ radio_ctrl_impl::set_tx_antenna(MAGNESIUM_DEFAULT_TX_ANTENNA, chan);
+ }
+}
+
+void magnesium_radio_ctrl_impl::_init_frontend_subtree(
+ uhd::property_tree::sptr subtree,
+ const size_t fe_chan_idx,
+ const size_t chan_idx
+) {
+ const fs_path tx_fe_path = fs_path("tx_frontends") / fe_chan_idx;
+ const fs_path rx_fe_path = fs_path("rx_frontends") / fe_chan_idx;
+ UHD_LOG_TRACE(unique_id(),
+ "Adding non-RFNoC block properties for channel " << chan_idx <<
+ " to prop tree path " << tx_fe_path << " and " << rx_fe_path);
+ // TX Standard attributes
+ subtree->create<std::string>(tx_fe_path / "name")
+ .set(str(boost::format("Magnesium")))
+ ;
+ subtree->create<std::string>(tx_fe_path / "connection")
+ .set("IQ")
+ ;
+ // RX Standard attributes
+ subtree->create<std::string>(rx_fe_path / "name")
+ .set(str(boost::format("Magnesium")))
+ ;
+ subtree->create<std::string>(rx_fe_path / "connection")
+ .set("IQ")
+ ;
+ // TX Antenna
+ subtree->create<std::string>(tx_fe_path / "antenna" / "value")
+ .add_coerced_subscriber([this, chan_idx](const std::string &ant){
+ this->set_tx_antenna(ant, chan_idx);
+ })
+ .set_publisher([this, chan_idx](){
+ return this->get_tx_antenna(chan_idx);
+ })
+ ;
+ subtree->create<std::vector<std::string>>(tx_fe_path / "antenna" / "options")
+ .set({MAGNESIUM_DEFAULT_TX_ANTENNA})
+ .add_coerced_subscriber([](const std::vector<std::string> &){
+ throw uhd::runtime_error(
+ "Attempting to update antenna options!");
+ })
+ ;
+ // RX Antenna
+ subtree->create<std::string>(rx_fe_path / "antenna" / "value")
+ .add_coerced_subscriber([this, chan_idx](const std::string &ant){
+ this->set_rx_antenna(ant, chan_idx);
+ })
+ .set_publisher([this, chan_idx](){
+ return this->get_rx_antenna(chan_idx);
+ })
+ ;
+ subtree->create<std::vector<std::string>>(rx_fe_path / "antenna" / "options")
+ .set(MAGNESIUM_RX_ANTENNAS)
+ .add_coerced_subscriber([](const std::vector<std::string> &){
+ throw uhd::runtime_error(
+ "Attempting to update antenna options!");
+ })
+ ;
+ // TX frequency
+ subtree->create<double>(tx_fe_path / "freq" / "value")
+ .set_coercer([this, chan_idx](const double freq){
+ return this->set_tx_frequency(freq, chan_idx);
+ })
+ .set_publisher([this, chan_idx](){
+ return this->get_tx_frequency(chan_idx);
+ })
+ ;
+ subtree->create<meta_range_t>(tx_fe_path / "freq" / "range")
+ .set(meta_range_t(MAGNESIUM_MIN_FREQ, MAGNESIUM_MAX_FREQ, 1.0))
+ .add_coerced_subscriber([](const meta_range_t &){
+ throw uhd::runtime_error(
+ "Attempting to update freq range!");
+ })
+ ;
+ // RX frequency
+ subtree->create<double>(rx_fe_path / "freq" / "value")
+ .set_coercer([this, chan_idx](const double freq){
+ return this->set_rx_frequency(freq, chan_idx);
+ })
+ .set_publisher([this, chan_idx](){
+ return this->get_rx_frequency(chan_idx);
+ })
+ ;
+ subtree->create<meta_range_t>(rx_fe_path / "freq" / "range")
+ .set(meta_range_t(MAGNESIUM_MIN_FREQ, MAGNESIUM_MAX_FREQ, 1.0))
+ .add_coerced_subscriber([](const meta_range_t &){
+ throw uhd::runtime_error(
+ "Attempting to update freq range!");
+ })
+ ;
+ // TX bandwidth
+ subtree->create<double>(tx_fe_path / "bandwidth" / "value")
+ .set_coercer([this, chan_idx](const double bw){
+ return this->set_tx_bandwidth(bw, chan_idx);
+ })
+ .set_publisher([this, chan_idx](){
+ //return this->get_tx_bandwidth(chan_idx);
+ return 0.0; // FIXME
+ })
+ ;
+ subtree->create<meta_range_t>(tx_fe_path / "bandwidth" / "range")
+ .set(meta_range_t(0.0, 0.0, 0.0)) // FIXME
+ .add_coerced_subscriber([](const meta_range_t &){
+ throw uhd::runtime_error(
+ "Attempting to update bandwidth range!");
+ })
+ ;
+ // RX bandwidth
+ subtree->create<double>(rx_fe_path / "bandwidth" / "value")
+ .set_coercer([this, chan_idx](const double bw){
+ return this->set_rx_bandwidth(bw, chan_idx);
+ })
+ .set_publisher([this, chan_idx](){
+ return this->get_rx_bandwidth(chan_idx);
+ })
+ ;
+ subtree->create<meta_range_t>(rx_fe_path / "bandwidth" / "range")
+ .set(meta_range_t(0.0, 0.0, 0.0)) // FIXME
+ .add_coerced_subscriber([](const meta_range_t &){
+ throw uhd::runtime_error(
+ "Attempting to update bandwidth range!");
+ })
+ ;
+ // TX gains
+ subtree->create<double>(tx_fe_path / "gains" / "all" / "value")
+ .set_coercer([this, chan_idx](const double gain){
+ return this->set_tx_gain(gain, chan_idx);
+ })
+ .set_publisher([this, chan_idx](){
+ return this->get_tx_gain(chan_idx);
+ })
+ ;
+ subtree->create<meta_range_t>(tx_fe_path / "gains" / "all" / "range")
+ .set(meta_range_t(0.0, 60.0, 1.0)) // FIXME
+ .add_coerced_subscriber([](const meta_range_t &){
+ throw uhd::runtime_error(
+ "Attempting to update bandwidth range!");
+ })
+ ;
+ // RX gains
+ subtree->create<double>(rx_fe_path / "gains" / "all" / "value")
+ .set_coercer([this, chan_idx](const double gain){
+ UHD_VAR(gain);
+ return this->set_rx_gain(gain, chan_idx);
+ })
+ .set_publisher([this, chan_idx](){
+ return this->get_rx_gain(chan_idx);
+ })
+ ;
+ subtree->create<meta_range_t>(rx_fe_path / "gains" / "all" / "range")
+ .set(meta_range_t(0.0, 60.0, 1.0)) // FIXME
+ .add_coerced_subscriber([](const meta_range_t &){
+ throw uhd::runtime_error(
+ "Attempting to update bandwidth range!");
+ })
+ ;
+ // FIXME separate DSA and Myk gains
+}
+
+void magnesium_radio_ctrl_impl::_init_prop_tree()
+{
+ fs_path gain_mode_path =
+ _root_path.branch_path()
+ / str(boost::format("Radio_%d") % ((get_block_id().get_block_count()/2)*2))
+ / "args/0/gain_mode/value";
+ UHD_LOG_DEBUG("GAIN_MODE_STRING","Gain mode path " << gain_mode_path);
+ std::string gain_mode = _tree->access<std::string>(gain_mode_path).get();
+ UHD_LOG_DEBUG("GAIN_MODE_STRING","Gain mode string" << gain_mode);
+
+ /**** Set up legacy compatible properties ******************************/
+ // For use with multi_usrp APIs etc.
+ // For legacy prop tree init:
+ // TODO: determine DB number
+ const fs_path fe_base = fs_path("dboards") / _radio_slot;
+ const std::vector<uhd::direction_t> dir({ RX_DIRECTION, TX_DIRECTION });
+ const std::vector<std::string> fe({ "rx_frontends", "tx_frontends" });
+ const std::vector<std::string> ant({ "RX" , "TX" });
+ //const size_t RX_IDX = 0;
+ // const size_t TX_IDX = 1;
+ //this->_dsa_set_gain(0.5,0,RX_DIRECTION);
+ for (size_t fe_idx = 0; fe_idx < fe.size(); ++fe_idx)
+ {
+ const fs_path fe_direction_path = fe_base / fe[fe_idx];
+ for (size_t chan = 0; chan < MAGNESIUM_NUM_CHANS; ++chan)
+ {
+ const fs_path fe_path = fe_direction_path / chan;
+ UHD_LOG_TRACE(unique_id(), "Adding FE at " << fe_path);
+ // Shared TX/RX attributes
+ //{
+ //auto ad9371_min_gain = (fe_idx == RX_IDX) ? AD9371_MIN_RX_GAIN : AD9371_MIN_TX_GAIN;
+ //auto ad9371_max_gain = (fe_idx == RX_IDX) ? AD9371_MAX_RX_GAIN : AD9371_MAX_TX_GAIN;
+ //auto ad9371_gain_step = (fe_idx == RX_IDX) ? AD9371_RX_GAIN_STEP : AD9371_TX_GAIN_STEP;
+ //auto dsa_min_gain = DSA_MIN_GAIN;
+ //auto dsa_max_gain = DSA_MAX_GAIN;
+ //auto dsa_gain_step = DSA_GAIN_STEP;
+ //auto all_min_gain = (fe_idx == RX_IDX) ? ALL_RX_MIN_GAIN : ALL_TX_MIN_GAIN;
+ //auto all_max_gain = (fe_idx == RX_IDX) ? ALL_TX_MAX_GAIN : ALL_TX_MAX_GAIN;
+ //auto all_gain_step = 0.5;
+ //if (gain_mode == "auto"){
+ //ad9371_min_gain = 0;
+ //ad9371_max_gain = 0;
+ //ad9371_gain_step = 0;
+ //dsa_min_gain = 0;
+ //dsa_max_gain = 0;
+ //dsa_gain_step = 0;
+ //}
+ //if (gain_mode == "manual")
+ //{
+ //all_min_gain = 0 ;
+ //all_max_gain = 0 ;
+ //all_gain_step = 0 ;
+
+ //}
+ //auto dir_ = dir[fe_idx];
+ ////Create gain property for mykonos
+ //auto myk_set_gain_func = [this, chan, dir_](const double gain)
+ //{
+ //return this->_myk_set_gain(gain, chan, dir_);
+ //};
+ //auto myk_get_gain_func = [this, chan, dir_]()
+ //{
+ //return this->_myk_get_gain(chan, dir_);
+ //};
+
+ //_tree->create<double>(fe_path / "gains" / "ad9371" / "value")
+ //.set(0)
+ //.set_coercer(myk_set_gain_func)
+ //.set_publisher(myk_get_gain_func);
+ //_tree->create<meta_range_t>(fe_path / "gains" / "ad9371" / "range")
+ //.set(meta_range_t(ad9371_min_gain, ad9371_max_gain, ad9371_gain_step));
+ //// Create gain property for DSA
+ //auto dsa_set_gain_func = [this, chan, dir_](const double gain)
+ //{
+ //return this->_dsa_set_gain(gain, chan, dir_);
+ //};
+ //auto dsa_get_gain_func = [this, chan, dir_]()
+ //{
+ //return this->_dsa_get_gain(chan, dir_);
+ //};
+ //_tree->create<double>(fe_path / "gains" / "dsa" / "value")
+ //.set(0)
+ //.set_coercer(dsa_set_gain_func)
+ //.set_publisher(dsa_get_gain_func);
+ //_tree->create<meta_range_t>(fe_path / "gains" / "dsa" / "range")
+ //.set(meta_range_t(dsa_min_gain, dsa_max_gain, dsa_gain_step));
+
+ //// Create gain property for all gains
+ //auto set_all_gain_func = [this, chan, dir_](const double gain)
+ //{
+ //return this->_set_all_gain(gain, chan, dir_);
+ //};
+ //auto get_all_gain_func = [this, chan, dir_]()
+ //{
+ //return this->_get_all_gain(chan, dir_);
+ //};
+ //_tree->create<double>(fe_path / "gains" / "all" / "value")
+ //.set(0)
+ //.set_coercer(set_all_gain_func)
+ //.set_publisher(get_all_gain_func);
+ //_tree->create<meta_range_t>(fe_path / "gains" / "all" / "range")
+ //.set(meta_range_t(all_min_gain, all_max_gain, all_gain_step));
+
+ //}
+ }
+ }
+
+
+ // TODO this might be wrong
+ if (_master) {
+ this->_init_frontend_subtree(_tree->subtree(fe_base), 0, 0);
+ std::string slave_slot = (_radio_slot == "A") ? "B" : "D";
+ UHD_LOG_TRACE(unique_id(),
+ "Also registering props for slave radio " << slave_slot);
+ this->_init_frontend_subtree(
+ _tree->subtree(fs_path("dboards") / slave_slot), 0, 1);
+ }
+ // TODO: When we go to one radio per dboard, the above if statement goes
+ // away, and instead we have something like this:
+ /*
+ *for (chan_idx = 0; chan_idx < MAGNESIUM_NUM_CHANS; chan_idx++) {
+ * this->_init_frontend_subtree(
+ * _tree->get_subtree(fe_base), chan_idx, chan_idx);
+ *}
+ */
+
+
+
+ // EEPROM paths subject to change FIXME
+ _tree->create<eeprom_map_t>(_root_path / "eeprom")
+ .set(eeprom_map_t());
+
+ // TODO change codec names
+ _tree->create<int>("rx_codecs" / _radio_slot / "gains");
+ _tree->create<int>("tx_codecs" / _radio_slot / "gains");
+ _tree->create<std::string>("rx_codecs" / _radio_slot / "name").set("AD9371 Dual ADC");
+ _tree->create<std::string>("tx_codecs" / _radio_slot / "name").set("AD9371 Dual DAC");
+
+ // TODO remove this dirty hack
+ if (not _tree->exists("tick_rate"))
+ {
+ _tree->create<double>("tick_rate").set(MAGNESIUM_TICK_RATE);
+ }
+}
+