diff options
-rw-r--r-- | host/include/uhd/rfnoc/blocks/radio_magnesium.xml | 2 | ||||
-rw-r--r-- | host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_impl.cpp | 200 | ||||
-rw-r--r-- | host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_impl.hpp | 21 | ||||
-rw-r--r-- | host/lib/usrp/mpmd/mpmd_impl.cpp | 7 | ||||
-rw-r--r-- | mpm/include/mpm/ad937x/ad937x_ctrl.hpp | 5 | ||||
-rw-r--r-- | mpm/lib/mykonos/ad937x_ctrl.cpp | 17 | ||||
-rw-r--r-- | mpm/lib/mykonos/ad937x_device.cpp | 222 | ||||
-rw-r--r-- | mpm/lib/mykonos/ad937x_device.hpp | 20 | ||||
-rw-r--r-- | mpm/lib/mykonos/config/ad937x_default_config.hpp | 2 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/dboard_manager/magnesium.py | 10 |
10 files changed, 332 insertions, 174 deletions
diff --git a/host/include/uhd/rfnoc/blocks/radio_magnesium.xml b/host/include/uhd/rfnoc/blocks/radio_magnesium.xml index 55065d0d3..a67e2722f 100644 --- a/host/include/uhd/rfnoc/blocks/radio_magnesium.xml +++ b/host/include/uhd/rfnoc/blocks/radio_magnesium.xml @@ -5,7 +5,7 @@ <key>MagnesiumRadio</key> <!--There can be several of these:--> <ids> - <id revision="0">12AD100000000003</id> + <id revision="0">12AD100000000310</id> </ids> <!-- Registers --> <registers> diff --git a/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_impl.cpp b/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_impl.cpp index b659902ae..cc51562aa 100644 --- a/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_impl.cpp +++ b/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_impl.cpp @@ -23,90 +23,246 @@ #include <uhd/utils/math.hpp> #include <boost/algorithm/string.hpp> #include <boost/make_shared.hpp> -#include <boost/date_time/posix_time/posix_time_io.hpp> +#include <boost/format.hpp> +#include <sstream> using namespace uhd; using namespace uhd::usrp; using namespace uhd::rfnoc; static const size_t IO_MASTER_RADIO = 0; +static const double MAGNESIUM_TICK_RATE = 125e6; // Hz +static const double MAGNESIUM_RADIO_RATE = 125e6; // Hz +static const double MAGNESIUM_CENTER_FREQ = 2.5e9; // Hz +static const double MAGNESIUM_DEFAULT_GAIN = 0.0; // dB +static const double MAGNESIUM_DEFAULT_BANDWIDTH = 40e6; // Hz TODO: fix +static const size_t MAGNESIUM_NUM_TX_CHANS = 2; +static const size_t MAGNESIUM_NUM_RX_CHANS = 2; + +std::string _get_which(direction_t dir, size_t chan) +{ + std::stringstream ss; + switch (dir) + { + case RX_DIRECTION: + ss << "RX"; + break; + case TX_DIRECTION: + ss << "TX"; + break; + default: + UHD_THROW_INVALID_CODE_PATH(); + } + + switch (chan) + { + case 0: + ss << "1"; + break; + case 1: + ss << "2"; + break; + default: + throw uhd::runtime_error("invalid channel number"); + } + + return ss.str(); +} UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(magnesium_radio_ctrl) { - std::cout << "magnesium_radio_ctrl_impl::ctor() " << std::endl; + UHD_LOG_TRACE("MAGNESIUM", "magnesium_radio_ctrl_impl::ctor() "); + _radio_slot = (get_block_id().get_block_count() == IO_MASTER_RADIO) ? "A" : "B"; + _slot_prefix = (get_block_id().get_block_count() == IO_MASTER_RADIO) ? "db_0_" : "db_1_"; + UHD_LOG_TRACE("MAGNESIUM", "Radio slot: " << _radio_slot); + + + const size_t num_rx_chans = get_output_ports().size(); + UHD_ASSERT_THROW(num_rx_chans == MAGNESIUM_NUM_RX_CHANS); + const size_t num_tx_chans = get_input_ports().size(); + UHD_ASSERT_THROW(num_tx_chans == MAGNESIUM_NUM_TX_CHANS); + + UHD_LOG_TRACE("MAGNESIUM", "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(MAGNESIUM_DEFAULT_GAIN, chan); + // TODO: fix antenna name + radio_ctrl_impl::set_rx_antenna(str(boost::format("RX%d") % (chan+1)), 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(MAGNESIUM_DEFAULT_GAIN, chan); + // TODO: fix antenna name + radio_ctrl_impl::set_tx_antenna(str(boost::format("TX%d") % (chan + 1)), chan); + } + + /**** Set up legacy compatible properties ******************************/ + // For use with multi_usrp APIs etc. + // For legacy prop tree init: + // TODO: determine DB number + fs_path fe_base = fs_path("dboards") / _radio_slot; + std::vector<std::string> fe({ "rx_frontends" , "tx_frontends" }); + std::vector<std::string> ant({ "RX" , "TX" }); + + UHD_ASSERT_THROW(MAGNESIUM_NUM_RX_CHANS == MAGNESIUM_NUM_TX_CHANS); + for (size_t fe_idx = 0; fe_idx < fe.size(); ++fe_idx) + { + fs_path fe_direction_path = fe_base / fe[fe_idx]; + for (size_t chan = 0; chan < MAGNESIUM_NUM_RX_CHANS; ++chan) { + fs_path fe_path = fe_direction_path / chan; + UHD_LOG_TRACE("MAGNESIUM", "Adding FE at " << fe_path); + _tree->create<std::string>(fe_path / "name") + .set(str(boost::format("Magnesium %s %d") % ant[fe_idx] % chan)) + ; + _tree->create<std::string>(fe_path / "connection") + .set("IQ") + ; + // TODO: fix antenna name + _tree->create<std::string>(fe_path / "antenna" / "value") + .set(str(boost::format("%s%d") % ant[fe_idx] % (chan+1))) + .add_coerced_subscriber(boost::bind(&magnesium_radio_ctrl_impl::set_rx_antenna, this, _1, chan)) + .set_publisher(boost::bind(&radio_ctrl_impl::get_rx_antenna, this, chan)) + ; + _tree->create<std::vector<std::string>>(fe_path / "antenna" / "options") + .set(std::vector<std::string>(1, str(boost::format("%s%d") % ant[fe_idx] % (chan + 1)))) + ; + _tree->create<double>(fe_path / "freq" / "value") + .set(MAGNESIUM_CENTER_FREQ) + .set_coercer(boost::bind(&magnesium_radio_ctrl_impl::set_rx_frequency, this, _1, chan)) + .set_publisher(boost::bind(&radio_ctrl_impl::get_rx_frequency, this, chan)) + ; + _tree->create<meta_range_t>(fe_path / "freq" / "range") + .set(meta_range_t(MAGNESIUM_CENTER_FREQ, MAGNESIUM_CENTER_FREQ)) + ; + _tree->create<double>(fe_path / "gains" / "null" / "value") + .set(MAGNESIUM_DEFAULT_GAIN) + .set_coercer(boost::bind(&magnesium_radio_ctrl_impl::set_rx_gain, this, _1, chan)) + .set_publisher(boost::bind(&radio_ctrl_impl::get_rx_gain, this, chan)) + ; + _tree->create<meta_range_t>(fe_path / "gains" / "null" / "range") + .set(meta_range_t(MAGNESIUM_DEFAULT_GAIN, MAGNESIUM_DEFAULT_GAIN)) + ; + _tree->create<double>(fe_path / "bandwidth" / "value") + .set(MAGNESIUM_DEFAULT_BANDWIDTH) + .set_coercer(boost::bind(&magnesium_radio_ctrl_impl::set_rx_bandwidth, this, _1, chan)) + .set_publisher(boost::bind(&radio_ctrl_impl::get_rx_bandwidth, this, chan)) + ; + _tree->create<meta_range_t>(fe_path / "bandwidth" / "range") + .set(meta_range_t(MAGNESIUM_DEFAULT_BANDWIDTH, MAGNESIUM_DEFAULT_BANDWIDTH)) + ; + } + } + + // 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("AD9361 Dual ADC"); + _tree->create<std::string>("tx_codecs" / _radio_slot / "name").set("AD9361 Dual DAC"); + + // TODO remove this dirty hack + if (not _tree->exists("tick_rate")) + { + _tree->create<double>("tick_rate").set(MAGNESIUM_TICK_RATE); + } } magnesium_radio_ctrl_impl::~magnesium_radio_ctrl_impl() { + UHD_LOG_TRACE("MAGNESIUM", "magnesium_radio_ctrl_impl::dtor() "); } double magnesium_radio_ctrl_impl::set_rate(double rate) { - return 0; + // TODO: implement + if (rate != get_rate()) { + UHD_LOG_WARNING("MAGNESIUM", "Attempting to set sampling rate to invalid value " << rate); + } + return get_rate(); } void magnesium_radio_ctrl_impl::set_tx_antenna(const std::string &ant, const size_t chan) { + // TODO: implement + UHD_LOG_WARNING("MAGNESIUM", "Ignoring attempt to set Tx antenna"); + // CPLD control? } void magnesium_radio_ctrl_impl::set_rx_antenna(const std::string &ant, const size_t chan) { + // TODO: implement + UHD_LOG_WARNING("MAGNESIUM", "Ignoring attempt to set Rx antenna"); + // CPLD control? } double magnesium_radio_ctrl_impl::set_tx_frequency(const double freq, const size_t chan) { - return 0; + return _set_freq(freq, chan, TX_DIRECTION); } double magnesium_radio_ctrl_impl::set_rx_frequency(const double freq, const size_t chan) { - return 0; + return _set_freq(freq, chan, RX_DIRECTION); } double magnesium_radio_ctrl_impl::set_rx_bandwidth(const double bandwidth, const size_t chan) { - return 0; + // TODO: implement + return get_rx_bandwidth(chan); } -double magnesium_radio_ctrl_impl::get_tx_frequency(const size_t chan) +double magnesium_radio_ctrl_impl::set_tx_gain(const double gain, const size_t chan) { - return 0; + return _set_gain(gain, chan, TX_DIRECTION); } -double magnesium_radio_ctrl_impl::get_rx_frequency(const size_t chan) +double magnesium_radio_ctrl_impl::set_rx_gain(const double gain, const size_t chan) { - return 0; + return _set_gain(gain, chan, RX_DIRECTION); } -double magnesium_radio_ctrl_impl::get_rx_bandwidth(const size_t chan) +size_t magnesium_radio_ctrl_impl::get_chan_from_dboard_fe(const std::string &fe, const direction_t dir) { - return 0; + UHD_LOG_TRACE("MAGNESIUM", "get_chan_from_dboard_fe " << fe << " returns " << boost::lexical_cast<size_t>(fe)); + return boost::lexical_cast<size_t>(fe); } -double magnesium_radio_ctrl_impl::set_tx_gain(const double gain, const size_t chan) +std::string magnesium_radio_ctrl_impl::get_dboard_fe_from_chan(const size_t chan, const direction_t dir) { - return 0; + UHD_LOG_TRACE("MAGNESIUM", "get_dboard_fe_from_chan " << chan << " returns " << std::to_string(chan)); + return std::to_string(chan); } -double magnesium_radio_ctrl_impl::set_rx_gain(const double gain, const size_t chan) +double magnesium_radio_ctrl_impl::get_output_samp_rate(size_t port) { - return 0; + return 125e6; } -size_t magnesium_radio_ctrl_impl::get_chan_from_dboard_fe(const std::string &fe, const direction_t dir) +void magnesium_radio_ctrl_impl::set_rpc_client( + uhd::rpc_client::sptr rpcc, + const uhd::device_addr_t &block_args +) { - return 0; + _rpcc = rpcc; + _block_args = block_args; } -std::string magnesium_radio_ctrl_impl::get_dboard_fe_from_chan(const size_t chan, const direction_t dir) +double magnesium_radio_ctrl_impl::_set_freq(const double freq, const size_t chan, const direction_t dir) { - return ""; + auto which = _get_which(dir, chan); + UHD_LOG_TRACE("MAGNESIUM", "calling " << _slot_prefix << "set_freq on " << which << " with " << freq); + return _rpcc->request_with_token<double>(_slot_prefix + "set_freq", which, freq, false); } -double magnesium_radio_ctrl_impl::get_output_samp_rate(size_t port) +double magnesium_radio_ctrl_impl::_set_gain(const double gain, const size_t chan, const direction_t dir) { - return 0; + auto which = _get_which(dir, chan); + UHD_LOG_TRACE("MAGNESIUM", "calling " << _slot_prefix << "set_gain on " << which << " with " << gain); + return _rpcc->request_with_token<double>(_slot_prefix + "set_gain", which, gain); } UHD_RFNOC_BLOCK_REGISTER(magnesium_radio_ctrl, "MagnesiumRadio"); diff --git a/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_impl.hpp b/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_impl.hpp index ea8282532..0bb1b26b8 100644 --- a/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_impl.hpp +++ b/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_impl.hpp @@ -19,6 +19,7 @@ #define INCLUDED_LIBUHD_RFNOC_MAGNESIUM_RADIO_CTRL_IMPL_HPP #include "radio_ctrl_impl.hpp" +#include "rpc_block_ctrl.hpp" #include <uhd/usrp/dboard_manager.hpp> #include <uhd/usrp/gpio_defs.hpp> @@ -27,7 +28,7 @@ namespace uhd { /*! \brief Provide access to an Magnesium radio. */ -class magnesium_radio_ctrl_impl : public radio_ctrl_impl +class magnesium_radio_ctrl_impl : public radio_ctrl_impl, public rpc_block_ctrl { public: typedef boost::shared_ptr<magnesium_radio_ctrl_impl> sptr; @@ -49,9 +50,6 @@ public: double set_tx_frequency(const double freq, const size_t chan); double set_rx_frequency(const double freq, const size_t chan); double set_rx_bandwidth(const double bandwidth, const size_t chan); - double get_tx_frequency(const size_t chan); - double get_rx_frequency(const size_t chan); - double get_rx_bandwidth(const size_t chan); double set_tx_gain(const double gain, const size_t chan); double set_rx_gain(const double gain, const size_t chan); @@ -61,7 +59,22 @@ public: double get_output_samp_rate(size_t port); + void set_rpc_client( + uhd::rpc_client::sptr rpcc, + const uhd::device_addr_t &block_args + ); +private: + double _set_freq(const double freq, const size_t chan, const direction_t dir); + double _set_gain(const double gain, const size_t chan, const direction_t dir); + + std::string _radio_slot; + std::string _slot_prefix; + //! Additional block args; gets set during set_rpc_client() + uhd::device_addr_t _block_args; + + //! Reference to the RPC client + uhd::rpc_client::sptr _rpcc; }; /* class radio_ctrl_impl */ diff --git a/host/lib/usrp/mpmd/mpmd_impl.cpp b/host/lib/usrp/mpmd/mpmd_impl.cpp index c8c860c00..38a089788 100644 --- a/host/lib/usrp/mpmd/mpmd_impl.cpp +++ b/host/lib/usrp/mpmd/mpmd_impl.cpp @@ -106,13 +106,6 @@ namespace { ); }) ; - tree->create<int>( - mb_path / "rx_codecs" / "A" / "gains") - .set_publisher([](){ - return 1 // FIXME: Remove hardcoding - ; - }) - ; } void reset_time_synchronized(uhd::property_tree::sptr tree) diff --git a/mpm/include/mpm/ad937x/ad937x_ctrl.hpp b/mpm/include/mpm/ad937x/ad937x_ctrl.hpp index ee3b5d6f3..f26eee92d 100644 --- a/mpm/include/mpm/ad937x/ad937x_ctrl.hpp +++ b/mpm/include/mpm/ad937x/ad937x_ctrl.hpp @@ -74,7 +74,6 @@ public: virtual uint8_t get_framer_status() = 0; virtual uint8_t get_deframer_status() = 0; - virtual uint8_t get_deframer_irq() = 0; virtual uint16_t get_ilas_config_match() = 0; virtual void enable_jesd_loopback(uint8_t enable) = 0; @@ -140,9 +139,10 @@ public: * Sets the RF frequency. This is a per direction setting. * \param which frontend string to specify direction to tune * \param value target frequency + * \param wait_for_lock wait after tuning for the PLL to lock * \return actual frequency */ - virtual double set_freq(const std::string &which, double value) = 0; + virtual double set_freq(const std::string &which, double value, bool wait_for_lock) = 0; /*! \brief get the RF frequency for the direction specified in which * @@ -189,7 +189,6 @@ void export_mykonos(){ .def("get_multichip_sync_status", &ad937x_ctrl::get_multichip_sync_status) .def("get_framer_status", &ad937x_ctrl::get_framer_status) .def("get_deframer_status", &ad937x_ctrl::get_deframer_status) - //.def("get_deframer_irq", &ad937x_ctrl::get_deframer_irq) .def("get_ilas_config_match", &ad937x_ctrl::get_ilas_config_match) .def("enable_jesd_loopback", &ad937x_ctrl::enable_jesd_loopback) .def("get_rf_freq_range", &ad937x_ctrl::get_rf_freq_range) diff --git a/mpm/lib/mykonos/ad937x_ctrl.cpp b/mpm/lib/mykonos/ad937x_ctrl.cpp index 6bd8b5ce1..55479fb1d 100644 --- a/mpm/lib/mykonos/ad937x_ctrl.cpp +++ b/mpm/lib/mykonos/ad937x_ctrl.cpp @@ -157,41 +157,30 @@ public: device.start_jesd_tx(); } - // TODO: interpret the status byte - // or provide means to do so virtual uint8_t get_multichip_sync_status() { std::lock_guard<std::mutex> lock(*spi_mutex); return device.get_multichip_sync_status(); } - // TODO: interpret the status byte - // or provide means to do so virtual uint8_t get_framer_status() { std::lock_guard<std::mutex> lock(*spi_mutex); return device.get_framer_status(); } - // TODO: interpret the status byte - // or provide means to do so virtual uint8_t get_deframer_status() { std::lock_guard<std::mutex> lock(*spi_mutex); return device.get_deframer_status(); } - virtual uint8_t get_deframer_irq() - { - std::lock_guard<std::mutex> lock(*spi_mutex); - return device.get_deframer_irq(); - } - virtual uint16_t get_ilas_config_match() { std::lock_guard<std::mutex> lock(*spi_mutex); return device.get_ilas_config_match(); } + virtual void enable_jesd_loopback(uint8_t enable) { std::lock_guard<std::mutex> lock(*spi_mutex); @@ -297,13 +286,13 @@ public: return device.enable_channel(dir, chain, enable); } - virtual double set_freq(const std::string &which, double value) + virtual double set_freq(const std::string &which, double value, bool wait_for_lock) { auto dir = _get_direction_from_antenna(which); auto clipped_value = get_rf_freq_range().clip(value); std::lock_guard<std::mutex> lock(*spi_mutex); - return device.tune(dir, clipped_value); + return device.tune(dir, clipped_value, wait_for_lock); } virtual double get_freq(const std::string &which) diff --git a/mpm/lib/mykonos/ad937x_device.cpp b/mpm/lib/mykonos/ad937x_device.cpp index 0e0851f41..e9815112e 100644 --- a/mpm/lib/mykonos/ad937x_device.cpp +++ b/mpm/lib/mykonos/ad937x_device.cpp @@ -25,6 +25,7 @@ #include <functional> #include <iostream> #include <thread> +#include <fstream> using namespace mpm::ad937x::device; using namespace mpm::ad937x::gpio; @@ -39,8 +40,10 @@ const double ad937x_device::MIN_TX_GAIN = 0.0; const double ad937x_device::MAX_TX_GAIN = 41.95; const double ad937x_device::TX_GAIN_STEP = 0.05; -static const double RX_DEFAULT_FREQ = 1e9; -static const double TX_DEFAULT_FREQ = 1e9; +static const double RX_DEFAULT_FREQ = 2.5e9; +static const double TX_DEFAULT_FREQ = 2.5e9; +static const double RX_DEFAULT_GAIN = 0; +static const double TX_DEFAULT_GAIN = 0; // TODO: get the actual device ID static const uint32_t AD9371_PRODUCT_ID = 0x1; @@ -112,89 +115,59 @@ void ad937x_device::_call_gpio_api_function(std::function<mykonosGpioErr_t()> fu } } -//void ad937x_device::_call_debug_api_function(std::function<mykonosDbgErr_t()> func) -//{ - //auto error = func(); - //if (error != MYKONOS_ERR_DBG_OK) - //{ - //std::cout << getDbgJesdMykonosErrorMessage(error); - //// TODO: make UHD exception - ////throw std::exception(getMykonosErrorMessage(error)); - //} -//} - -// TODO: delete this comment closer to release -/* -EX 1 Preconditions -EX 1. Check revision register -EX 2. Initialize Clocking -EX 3. Initialize FPGA JESD - -begin_initialize() -IN 2 Start -IN 1. Reset Myk -IN 2. Init Myk -IN 3. Check base PLL -IN 4. Start Multichip Sync - -EX 3 Multichip Pulses -EX 1. Send 2 SYSREF pulses - -finish_initialize() -IN 4 Verify Multichip -IN 1. Verify Multichip -IN 2. Complete Init - ---skipping this for now using special hack from jepson ---IN 3. Load ARM ---IN 4. RF Start ---IN Set RF Freq ---IN Check RF PLLs ---IN Set GPIO controls ---IN Set gain ---IN Init TX attenuations ---IN Initialization Calibrations ---IN External LOL Calibration (do we need this ???) - ---separate functions here for reusability-- - -start_jesd_rx() -IN 5 Start Myk JESD RX -IN 1. Reset Myk JESD RX (???) -IN 2. Enable Myk JESD RX Transmitter - -EX 6 Start FPGA CGS -EX 1. Reset and Ready RX JESD for CGS -EX 2. Reset and Ready TX JESD for CGS - -start_jesd_tx() -IN 7 Start Myk JESD TX -IN 1. Disable Myk JESD Receiver -IN 2. Reset Myk JESD Receiver -IN 3. Enable Myk JESD Receiver - -EX 8 Finish CGS -EX 1. Enable FPGA LMFC Generator -EX 2. Send SYSREF Pulse -EX 3. Wait (200 ms ???) -EX 4. Check TX Core is Synced -EX 5. Check RX Core is Synced - -OTHER FUNCTIONS THAT SHOULD BE WRITTEN -get_framer_status() get_deframer_status() Read framer/deframer status -get_deframer_irq() Read Deframer IRQ -get_ilas_config_match() Check ILAS Config Match -set_jesd_loopback() Enable Loopback -stop_jesd() Stop Link - -*/ +std::string ad937x_device::_get_arm_binary_path() +{ + // TODO: possibly add more options here, for now it's always in /lib/firmware or we explode + return "/lib/firmware/Mykonos_M3.bin"; +} -void ad937x_device::begin_initialization() +std::vector<uint8_t> ad937x_device::_get_arm_binary() { - // TODO: make this reset actually do something (implement CMB_HardReset or replace) - _call_api_function(std::bind(MYKONOS_resetDevice, mykonos_config.device)); - _call_api_function(std::bind(MYKONOS_initialize, mykonos_config.device)); + auto path = _get_arm_binary_path(); + std::ifstream file(path, std::ios::binary); + if (!file.is_open()) + { + throw mpm::runtime_error("Could not open AD9371 ARM binary at path " + path); + return{}; + } + + std::vector<uint8_t> binary(ARM_BINARY_SIZE); + file.read(reinterpret_cast<char*>(binary.data()), ARM_BINARY_SIZE); + if (file.bad()) + { + throw mpm::runtime_error("Error reading AD9371 ARM binary at path " + path); + } + return binary; +} + +void ad937x_device::_initialize_rf() +{ + // Set frequencies + tune(uhd::RX_DIRECTION, RX_DEFAULT_FREQ, true); + tune(uhd::TX_DIRECTION, TX_DEFAULT_FREQ, true); + + if (!get_pll_lock_status(pll_t::CLK_SYNTH)) + { + throw mpm::runtime_error("CLK SYNTH PLL became unlocked!"); + } + // Set gain control GPIO pins + _apply_gain_pins(uhd::RX_DIRECTION, chain_t::ONE); + _apply_gain_pins(uhd::RX_DIRECTION, chain_t::TWO); + _apply_gain_pins(uhd::TX_DIRECTION, chain_t::ONE); + _apply_gain_pins(uhd::TX_DIRECTION, chain_t::TWO); + + // Set manual gain values + set_gain(uhd::RX_DIRECTION, chain_t::ONE, RX_DEFAULT_GAIN); + set_gain(uhd::RX_DIRECTION, chain_t::TWO, RX_DEFAULT_GAIN); + set_gain(uhd::TX_DIRECTION, chain_t::ONE, TX_DEFAULT_GAIN); + set_gain(uhd::TX_DIRECTION, chain_t::TWO, TX_DEFAULT_GAIN); + + // TODO: add calibration stuff +} + +void ad937x_device::_verify_product_id() +{ uint8_t product_id = get_product_id(); if (product_id != AD9371_PRODUCT_ID) { @@ -203,10 +176,30 @@ void ad937x_device::begin_initialization() % int(product_id) % int(AD9371_PRODUCT_ID) )); } +} + +void ad937x_device::_verify_multichip_sync_status(multichip_sync_t mcs) +{ + uint8_t status_expected = (mcs == multichip_sync_t::FULL) ? 0x0B : 0x0A; + uint8_t status_mask = status_expected; // all 1s expected, mask is the same + + uint8_t mcs_status = get_multichip_sync_status(); + if ((mcs_status & status_mask) != status_expected) + { + throw mpm::runtime_error(str(boost::format("Multichip sync failed! Read: %X Expected: %X") + % int(mcs_status) % int(status_expected))); + } +} + +void ad937x_device::begin_initialization() +{ + _call_api_function(std::bind(MYKONOS_initialize, mykonos_config.device)); + + _verify_product_id(); if (!get_pll_lock_status(pll_t::CLK_SYNTH)) { - throw mpm::runtime_error("AD937x CLK_SYNTH PLL failed to lock in initialize()"); + throw mpm::runtime_error("AD937x CLK_SYNTH PLL failed to lock"); } uint8_t mcs_status = 0; @@ -215,33 +208,24 @@ void ad937x_device::begin_initialization() void ad937x_device::finish_initialization() { - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - // to check status, just call the same function with a 0 instead of a 1, seems good - uint8_t mcs_status = 0; - _call_api_function(std::bind(MYKONOS_enableMultichipSync, mykonos_config.device, 0, &mcs_status)); + _verify_multichip_sync_status(multichip_sync_t::PARTIAL); - if ((mcs_status & 0x0A) != 0x0A) - { - throw mpm::runtime_error(str(boost::format("Multichip sync failed! Read: %X Expected: %X") - % int(mcs_status) % int(0x0A))); - } - - _call_api_function(std::bind(MYKONOS_initSubRegisterTables, mykonos_config.device)); - // according to djepson, we can call only this function and avoid loading the ARM or - // doing an RF stuff - // TODO: fix all this once we want to more than just loopback + _call_api_function(std::bind(MYKONOS_initArm, mykonos_config.device)); + auto binary = _get_arm_binary(); + _call_api_function(std::bind(MYKONOS_loadArmFromBinary, + mykonos_config.device, + binary.data(), + binary.size())); - // load ARM - // ARM init - // RF setup + _initialize_rf(); } -void ad937x_device::start_jesd_rx() +void ad937x_device::start_jesd_tx() { _call_api_function(std::bind(MYKONOS_enableSysrefToRxFramer, mykonos_config.device, 1)); } -void ad937x_device::start_jesd_tx() +void ad937x_device::start_jesd_rx() { _call_api_function(std::bind(MYKONOS_enableSysrefToDeframer, mykonos_config.device, 0)); _call_api_function(std::bind(MYKONOS_resetDeframer, mykonos_config.device)); @@ -251,6 +235,7 @@ void ad937x_device::start_jesd_tx() uint8_t ad937x_device::get_multichip_sync_status() { uint8_t mcs_status = 0; + // to check status, just call the enable function with a 0 instead of a 1, seems good _call_api_function(std::bind(MYKONOS_enableMultichipSync, mykonos_config.device, 0, &mcs_status)); return mcs_status; } @@ -269,19 +254,11 @@ uint8_t ad937x_device::get_deframer_status() return status; } -uint8_t ad937x_device::get_deframer_irq() -{ - uint8_t irq_status = 0; - //_call_debug_api_function(std::bind(MYKONOS_deframerGetIrq, mykonos_config.device, &irq_status)); - return irq_status; -} - uint16_t ad937x_device::get_ilas_config_match() { uint16_t ilas_status = 0; _call_api_function(std::bind(MYKONOS_jesd204bIlasCheck, mykonos_config.device, &ilas_status)); return ilas_status; - } void ad937x_device::enable_jesd_loopback(uint8_t enable) @@ -351,21 +328,24 @@ void ad937x_device::enable_channel(direction_t direction, chain_t chain, bool en // is _initialize(). Need to figure out how to deal with this. } -double ad937x_device::tune(direction_t direction, double value) +double ad937x_device::tune(direction_t direction, double value, bool wait_for_lock = false) { // I'm not sure why we set the PLL value in the config AND as a function parameter // but here it is mykonosRfPllName_t pll; + pll_t locked_pll; uint64_t integer_value = static_cast<uint64_t>(value); switch (direction) { case TX_DIRECTION: pll = TX_PLL; + locked_pll = pll_t::TX_SYNTH; mykonos_config.device->tx->txPllLoFrequency_Hz = integer_value; break; case RX_DIRECTION: pll = RX_PLL; + locked_pll = pll_t::RX_SYNTH; mykonos_config.device->rx->rxPllLoFrequency_Hz = integer_value; break; default: @@ -373,11 +353,30 @@ double ad937x_device::tune(direction_t direction, double value) } _call_api_function(std::bind(MYKONOS_setRfPllFrequency, mykonos_config.device, pll, integer_value)); + auto lock_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(200); // TODO: coercion here causes extra device accesses, when the formula is provided on pg 119 of the user guide // Furthermore, because coerced is returned as an integer, it's not even accurate uint64_t coerced_pll; _call_api_function(std::bind(MYKONOS_getRfPllFrequency, mykonos_config.device, pll, &coerced_pll)); + + if (wait_for_lock) + { + bool locked = false; + while (not locked and lock_time > std::chrono::steady_clock::now()) + { + locked = get_pll_lock_status(locked_pll); + } + + if (!locked) + { + if (!get_pll_lock_status(locked_pll)) // last chance + { + throw mpm::runtime_error("RF PLL did not lock"); + } + } + } + return static_cast<double>(coerced_pll); } @@ -403,6 +402,7 @@ bool ad937x_device::get_pll_lock_status(pll_t pll) { uint8_t pll_status; _call_api_function(std::bind(MYKONOS_checkPllsLockStatus, mykonos_config.device, &pll_status)); + switch (pll) { case pll_t::CLK_SYNTH: diff --git a/mpm/lib/mykonos/ad937x_device.hpp b/mpm/lib/mykonos/ad937x_device.hpp index 17a09f249..b20feb3f7 100644 --- a/mpm/lib/mykonos/ad937x_device.hpp +++ b/mpm/lib/mykonos/ad937x_device.hpp @@ -30,6 +30,7 @@ #include <mpm/spi/spi_iface.hpp> #include <mpm/exception.hpp> #include <boost/noncopyable.hpp> +#include <boost/filesystem.hpp> #include <memory> #include <functional> @@ -37,7 +38,7 @@ class ad937x_device : public boost::noncopyable { public: enum class gain_mode_t { MANUAL, AUTOMATIC, HYBRID }; - enum class pll_t {CLK_SYNTH, RX_SYNTH, TX_SYNTH, SNIFF_SYNTH, CALPLL_SDM}; + enum class pll_t { CLK_SYNTH, RX_SYNTH, TX_SYNTH, SNIFF_SYNTH, CALPLL_SDM }; ad937x_device( mpm::types::regs_iface* iface, @@ -48,13 +49,10 @@ public: void finish_initialization(); void start_jesd_rx(); void start_jesd_tx(); + uint8_t get_multichip_sync_status(); uint8_t get_framer_status(); uint8_t get_deframer_status(); - - // debug functions for JESD - // TODO: make these returns useful - uint8_t get_deframer_irq(); uint16_t get_ilas_config_match(); void enable_jesd_loopback(uint8_t enable); @@ -68,7 +66,7 @@ public: void set_agc_mode(uhd::direction_t direction, gain_mode_t mode); double set_clock_rate(double value); void enable_channel(uhd::direction_t direction, mpm::ad937x::device::chain_t chain, bool enable); - double tune(uhd::direction_t direction, double value); + double tune(uhd::direction_t direction, double value, bool wait_for_lock); double get_freq(uhd::direction_t direction); bool get_pll_lock_status(pll_t pll); @@ -91,6 +89,8 @@ public: const static double TX_GAIN_STEP; private: + enum class multichip_sync_t { PARTIAL, FULL }; + ad9371_spiSettings_t full_spi_settings; ad937x_config_t mykonos_config; ad937x_gain_ctrl_config_t gain_ctrl; @@ -99,7 +99,13 @@ private: void _call_api_function(std::function<mykonosErr_t()> func); void _call_gpio_api_function(std::function<mykonosGpioErr_t()> func); - //void _call_debug_api_function(std::function<mykonosDbgErr_t()> func); + + std::string _get_arm_binary_path(); + std::vector<uint8_t> _get_arm_binary(); + + void _initialize_rf(); + void _verify_product_id(); + void _verify_multichip_sync_status(multichip_sync_t mcs); static uint8_t _convert_rx_gain(double gain); static uint16_t _convert_tx_gain(double gain); diff --git a/mpm/lib/mykonos/config/ad937x_default_config.hpp b/mpm/lib/mykonos/config/ad937x_default_config.hpp index b0f254308..fdf777795 100644 --- a/mpm/lib/mykonos/config/ad937x_default_config.hpp +++ b/mpm/lib/mykonos/config/ad937x_default_config.hpp @@ -199,7 +199,7 @@ static const mykonosObsRxSettings_t DEFAULT_ORX_SETTINGS = nullptr, // Sniffer datapath profile, 3dB corner frequencies, and digital filter enables nullptr, // SnRx gain control settings structure nullptr, // ObsRx JESD204b framer configuration structure - MYK_OBS_RXOFF, // obsRxChannel + MYK_ORX1, // obsRxChannel TODO: fix this garbage please OBSLO_TX_PLL, // (obsRxLoSource) The Obs Rx mixer can use the Tx Synth(TX_PLL) or Sniffer Synth (SNIFFER_PLL) 2600000000U, // SnRx PLL LO frequency in Hz 0, // Flag to choose if complex baseband or real IF data are selected for Rx and ObsRx paths. Where if > 0 = real IF data, '0' = complex data diff --git a/mpm/python/usrp_mpm/dboard_manager/magnesium.py b/mpm/python/usrp_mpm/dboard_manager/magnesium.py index b779607c8..536503b62 100644 --- a/mpm/python/usrp_mpm/dboard_manager/magnesium.py +++ b/mpm/python/usrp_mpm/dboard_manager/magnesium.py @@ -64,8 +64,9 @@ class Magnesium(DboardManagerBase): spi_chipselect = {"lmk": 0, "mykonos": 1} def __init__(self, slot_idx, **kwargs): - super(Magnesium, self).__init__(*args, **kwargs) - self.log = get_logger("Magnesium") + super(Magnesium, self).__init__(slot_idx, **kwargs) + self.log = get_logger("Magnesium-{}".format(slot_idx)) + self.log.trace("Initializing Magnesium daughterboard, slot index {}".format(self.slot_idx)) def init(self, args): """ @@ -88,6 +89,7 @@ class Magnesium(DboardManagerBase): self.radio_regs = UIO(label="jesd204b-regs", read_only=False) self.log.info("Radio-register UIO object successfully generated!") self.init_jesd(self.radio_regs) + return True def init_jesd(self, uio): """ @@ -117,14 +119,14 @@ class Magnesium(DboardManagerBase): self.mykonos.finish_initialization() self.log.trace("Starting Mykonos framer...") - self.mykonos.start_jesd_rx() + self.mykonos.start_jesd_tx() self.jesdcore.send_sysref_pulse() self.log.trace("Resetting FPGA deframer...") self.jesdcore.init_deframer() self.log.trace("Resetting FPGA framer...") self.jesdcore.init_framer() self.log.trace("Starting Mykonos deframer...") - self.mykonos.start_jesd_tx() + self.mykonos.start_jesd_rx() self.log.trace("Enable LMFC and send") self.jesdcore.enable_lmfc() |