diff options
author | Mark Meserve <mark.meserve@ni.com> | 2017-08-09 15:17:44 -0700 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2017-12-22 15:04:00 -0800 |
commit | 309a2f04e3da6667bc44c78121064b2d03e24e75 (patch) | |
tree | 08b453917053ac0a445ee261c694a706d9f5e180 /mpm | |
parent | 8ee3da3ef9b812eda2ac3269927191e09916dc3c (diff) | |
download | uhd-309a2f04e3da6667bc44c78121064b2d03e24e75.tar.gz uhd-309a2f04e3da6667bc44c78121064b2d03e24e75.tar.bz2 uhd-309a2f04e3da6667bc44c78121064b2d03e24e75.zip |
mg: General fixes
- Make DB probe-able
- Add RPC client
Diffstat (limited to 'mpm')
-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 |
6 files changed, 136 insertions, 140 deletions
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() |