diff options
Diffstat (limited to 'mpm/lib')
| -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 | 
4 files changed, 128 insertions, 133 deletions
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  | 
